Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
46d1728
add gdrepreview class
nikitalita Oct 8, 2026
a8d2257
Add Texture3D previewing support to TextureLayeredPreviewer
nikitalita Oct 8, 2026
0862a68
more wip previewer stuff
nikitalita Oct 8, 2026
64b0424
media player previewer wip
nikitalita Oct 8, 2026
ca335ab
made media player a standalone script class
nikitalita Oct 8, 2026
2a3f832
Refactor resource_to_string methods, use FileAccessString for perform…
nikitalita Oct 8, 2026
2e8a0b0
more wip gdrepreview
nikitalita Oct 8, 2026
38a8d43
ResourceCompatLoader::resource_file_to_string fixes
nikitalita Oct 8, 2026
1864eb4
refactor gdre_resource_previewer to use new previewers with asynchron…
nikitalita Oct 8, 2026
4d96f04
fix mesh_previewer
nikitalita Oct 9, 2026
89926cd
fix resetting texture layered preview
nikitalita Oct 9, 2026
77be61f
clear dummy sources on packed data clear
nikitalita Oct 9, 2026
563fcd5
add GDREPackedSource::open_encrypted_file, fix opening bundled files
nikitalita Oct 9, 2026
5ba2bd7
Fix bytecode revision guessing for dev versions when using `force`
nikitalita Oct 9, 2026
3240c10
bind PackSourceCustom::open_encrypted_file, use in docs
nikitalita Oct 9, 2026
cceffbb
Don't show salted file paths in file listing
nikitalita Oct 9, 2026
ba1a168
remove debug stuff
nikitalita Oct 9, 2026
38a1d1e
Fix custom pack source being unable to get salt string from salt bytes
nikitalita Oct 9, 2026
2f8bf63
fix individual mesh export not working when using `auto`
nikitalita Oct 9, 2026
bca22b7
update ilspy to 11.1
nikitalita Oct 9, 2026
a867287
Speed up script caching by only loading global C# scripts
nikitalita Oct 9, 2026
0af5aa0
fix static checks
nikitalita Oct 10, 2026
a29bcc4
fix custom encryption not working
nikitalita Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bytecode/bytecode_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1673,7 +1673,7 @@ Ref<GDScriptDecomp> GDScriptDecomp::create_decomp_for_version(String str_ver, bo
return Ref<GDScriptDecomp>(create_decomp_for_commit(v.commit));
}
}
ERR_FAIL_COND_V_MSG(include_dev, Ref<GDScriptDecomp>(), "No version found for: " + str_ver);
ERR_FAIL_COND_V_MSG(!p_force, Ref<GDScriptDecomp>(), "No version found for: " + str_ver);
}
if (p_force && ver->get_major() == 4 && ver->get_minor() < 3 && (ver->get_minor() != 0 || !ver->is_prerelease())) {
return Ref<GDScriptDecomp>(create_decomp_for_version("4.3.0"));
Expand Down
37 changes: 31 additions & 6 deletions compat/resource_loader_compat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
#include "core/io/resource_loader.h"
#include "core/object/class_db.h"
#include "core/version_generated.gen.h"
#include "scene/resources/text_file.h"
#include "utility/common.h"
#include "utility/file_access_buffer.h"
#include "utility/file_access_string.h"
#include "utility/gdre_settings.h"
#include "utility/godot_mono_decomp_wrapper.h"
#include "utility/resource_info.h"

Ref<CompatFormatLoader> ResourceCompatLoader::loaders[ResourceCompatLoader::MAX_LOADERS];
Expand Down Expand Up @@ -555,7 +557,7 @@ Error ResourceCompatLoader::to_text(const String &p_path, const String &p_dst, u
return saver.save(p_dst, res, p_flags);
}

String ResourceCompatLoader::resource_to_string(const String &p_path, bool p_skip_cr) {
String ResourceCompatLoader::resource_file_to_string(const String &p_path) {
ERR_FAIL_COND_V_MSG(p_path.is_empty(), "", "Path is empty");
String orig_path;
String path = p_path;
Expand All @@ -570,7 +572,12 @@ String ResourceCompatLoader::resource_to_string(const String &p_path, bool p_ski
return ProjectConfigLoader::get_project_settings_as_string(path);
}
String ext = path.get_extension().to_lower();
if (ext == "tres" || ext == "tscn" || !handles_resource(path, "")) {
if (ext == "cs" && GDRESettings::get_singleton()->has_loaded_dotnet_assembly()) {
String code = GDRESettings::get_singleton()->get_dotnet_decompiler()->decompile_individual_file(path);
return code;
}

if (FileAccess::exists(path) && (ext == "tres" || ext == "tscn" || ext == "gd" || ext == "cs" || !handles_resource(path, ""))) {
Error err;
Ref<FileAccess> f = FileAccess::open(path, FileAccess::READ, &err);
if (f.is_null() || err != OK) {
Expand All @@ -580,6 +587,9 @@ String ResourceCompatLoader::resource_to_string(const String &p_path, bool p_ski
return "ERROR: Failed to open file: " + path;
}
auto buf = f->get_buffer(f->get_length());
if (buf.size() == 0 || (buf.size() == 1 && buf[0] == 0)) {
return "";
}
if (ext == "tres" || ext == "tscn" || gdre::detect_utf8(buf)) {
String str;
str.append_utf8((const char *)buf.ptr(), buf.size());
Expand All @@ -597,15 +607,29 @@ String ResourceCompatLoader::resource_to_string(const String &p_path, bool p_ski
return "ERROR: Failed to load " + path;
}

int64_t length = FileAccess::get_size(path);
return resource_to_string(res);
}

String ResourceCompatLoader::resource_to_string(const Ref<Resource> &res) {
ERR_FAIL_COND_V_MSG(res.is_null(), "", "Resource is null");
int64_t length = 1024;
String path = res->get_path();
if (!path.is_empty() && FileAccess::exists(path)) {
length = FileAccess::get_size(path);
}
Ref<Script> script = res;
if (script.is_valid()) {
return script->get_source_code();
}
Ref<TextFile> text_file = res;
if (text_file.is_valid()) {
return text_file->get_text();
}

Ref<FileAccessBuffer> f = FileAccessBuffer::create(FileAccessBuffer::RESIZE_OPTIMIZED);
Ref<FileAccessString> f = FileAccessString::create();
f->reserve(length * 3);

Error err = OK;
String save_path;
String base_ext = path.get_basename().get_basename().get_extension();
bool optimized = path.contains(".optimized.");
Expand Down Expand Up @@ -895,7 +919,8 @@ void ResourceCompatLoader::_bind_methods() {
ClassDB::bind_static_method(get_class_static(), D_METHOD("get_resource_script_class", "path"), &::ResourceCompatLoader::get_resource_script_class);
ClassDB::bind_static_method(get_class_static(), D_METHOD("get_resource_type", "path"), &::ResourceCompatLoader::get_resource_type);
ClassDB::bind_static_method(get_class_static(), D_METHOD("save_custom", "resource", "path", "ver_major", "ver_minor"), &::ResourceCompatLoader::save_custom);
ClassDB::bind_static_method(get_class_static(), D_METHOD("resource_to_string", "path", "skip_cr"), &::ResourceCompatLoader::resource_to_string, DEFVAL(true));
ClassDB::bind_static_method(get_class_static(), D_METHOD("resource_file_to_string", "path"), &::ResourceCompatLoader::resource_file_to_string);
ClassDB::bind_static_method(get_class_static(), D_METHOD("resource_to_string", "resource"), &::ResourceCompatLoader::resource_to_string);
ClassDB::bind_integer_constant(get_class_static(), "LoadType", "FAKE_LOAD", ResourceInfo::FAKE_LOAD);
ClassDB::bind_integer_constant(get_class_static(), "LoadType", "NON_GLOBAL_LOAD", ResourceInfo::NON_GLOBAL_LOAD);
ClassDB::bind_integer_constant(get_class_static(), "LoadType", "GLTF_LOAD", ResourceInfo::GLTF_LOAD);
Expand Down
3 changes: 2 additions & 1 deletion compat/resource_loader_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ class ResourceCompatLoader {
static bool exists(const String &p_path);
static bool has_custom_uid_support(const String &p_path);

static String resource_to_string(const String &p_path, bool p_skip_cr = true);
static String resource_file_to_string(const String &p_path);
static String resource_to_string(const Ref<Resource> &p_resource);

static void set_default_gltf_load(bool p_enable);
static bool is_default_gltf_load();
Expand Down
11 changes: 3 additions & 8 deletions docs/gdre_custom_pack_source.gd
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ const PACK_FILE_ENCRYPTED: int = 1 << 0
const PACK_FILE_REMOVAL: int = 1 << 1
const PACK_FILE_DELTA: int = 1 << 2

func open_encrypted_file(base: FileAccess, key: PackedByteArray) -> FileAccess:
if GDRESettings.get_custom_decryptor():
return FileAccessEncryptedCustom.create_and_parse_custom(GDRESettings.get_custom_decryptor(), base, key, FileAccessEncryptedCustom.MODE_READ, false)
return FileAccessEncryptedCustom.create_and_parse_non_custom(base, key, FileAccessEncryptedCustom.MODE_READ, false)

func _try_open_pack(pck_path: String, p_replace_files: bool, p_offset: int, p_decryption_key: PackedByteArray) -> bool:
var ext: String = pck_path.get_extension().to_lower()
if ext == "apk" or ext == "zip":
Expand Down Expand Up @@ -89,7 +84,7 @@ func _try_open_pack(pck_path: String, p_replace_files: bool, p_offset: int, p_de
if sparse_bundle and enc_directory and version == PACK_FORMAT_VERSION_V4:
# V4: Read encrypted directory salt.
var salt_data: PackedByteArray = f.get_buffer(32)
salt = salt_data.get_string_from_utf8()
salt = GDRECommon.get_latin1_string_from_bytes(salt_data)
if dir_offset == 0:
printerr("Directory offset is 0, this is not a valid PCK file")
return false
Expand All @@ -113,7 +108,7 @@ func _try_open_pack(pck_path: String, p_replace_files: bool, p_offset: int, p_de
if enc_directory:
if p_decryption_key.is_empty():
p_decryption_key = GDRESettings.get_encryption_key()
var file = open_encrypted_file(f, p_decryption_key)
var file = PackSourceCustom.open_encrypted_file(f, p_decryption_key, FileAccess.READ, false)
if not file or file.get_error() != OK:
printerr("Failed to open encrypted pack directory: " + str(file.get_error() if file else "Unknown error"))
return false
Expand Down Expand Up @@ -176,7 +171,7 @@ func _get_file(p_path: String, p_file: PackedFile, p_decryption_key: PackedByteA
printerr("Failed to open pack-referenced file: " + str(base.get_error() if base else "Unknown error"))
return null
base.seek(p_file.offset)
file = open_encrypted_file(base, p_decryption_key)
file = PackSourceCustom.open_encrypted_file(base, p_decryption_key, FileAccess.READ, false)
if not file or file.get_error() != OK:
printerr("Failed to open encrypted pack-referenced file: " + str(file.get_error() if file else "Unknown error"))
return null
Expand Down
17 changes: 17 additions & 0 deletions godot-mono-decomp/GodotMonoDecomp/GodotModuleDecompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,5 +1025,22 @@ public void DumpStrings(string outputFile)
Console.WriteLine($"Dumped {strings.Count} strings to {outputFile}");
}

public string[] GetGlobalClassFiles() {
var godotObjectFiles = new List<string>();
List<GodotModule> list = [MainModule];
foreach (var module in list.Concat(AdditionalModules))
{
var decompiler = module.CreateCSharpDecompilerWithPartials(module.fileMap.SelectMany(kv => kv.Value).ToHashSet());
foreach (var kv in module.fileMap){
if (kv.Value.Any(type => {
var typeDef = decompiler.TypeSystem.MainModule.GetDefinition(type);
return typeDef != null && GodotStuff.IsGodotClass(typeDef) && typeDef.GetAttributes().Any(a => a.AttributeType.Name == "GlobalClassAttribute");
})){
godotObjectFiles.Add(kv.Key);
}
}
}
return godotObjectFiles.ToArray();
}

}
4 changes: 2 additions & 2 deletions godot-mono-decomp/GodotMonoDecomp/GodotMonoDecomp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ICSharpCode.Decompiler" Version="11.0.0.9375" />
<PackageReference Include="ICSharpCode.ILSpyX" Version="11.0.0.9375" />
<PackageReference Include="ICSharpCode.Decompiler" Version="11.1.0.9782" />
<PackageReference Include="ICSharpCode.ILSpyX" Version="11.1.0.9782" />
<PackageReference Include="NuGet.Packaging" Version="6.14.0" />
</ItemGroup>

Expand Down
21 changes: 21 additions & 0 deletions godot-mono-decomp/GodotMonoDecompNativeAOT/Lib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,25 @@ IntPtr file
return false;
}
}

[UnmanagedCallersOnly(EntryPoint = "GodotMonoDecomp_GetGlobalClassFiles")]
public static IntPtr AOTGetGlobalClassFiles(
IntPtr decompilerHandle,
IntPtr r_num_files
)
{
var decompiler = GCHandle.FromIntPtr(decompilerHandle).Target as GodotModuleDecompiler;
if (decompiler == null)
{
return IntPtr.Zero;
}
var files = decompiler.GetGlobalClassFiles();
Marshal.WriteInt32(r_num_files, files.Length);
var arrayPtr = Marshal.AllocHGlobal(files.Length * IntPtr.Size);
for (int i = 0; i < files.Length; i++)
{
Marshal.WriteIntPtr(arrayPtr + i * IntPtr.Size, StringToHGlobalUtf8(files[i]));
}
return arrayPtr;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ int GodotMonoDecomp_IsCustomVersionDetected(void *decompilerHandle);

bool GodotMonoDecomp_CheckFileIsAssembly(const char* file);

const char** GodotMonoDecomp_GetGlobalClassFiles(void* decompilerHandle, int* r_num_files);

#ifdef __cplusplus
}
#endif
Expand Down
73 changes: 73 additions & 0 deletions gui/gdre_previewer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "gdre_previewer.h"
#include "compat/resource_loader_compat.h"
#include "core/object/class_db.h"

String GDREPreviewer::get_previewer_name() const {
String ret = "";
GDVIRTUAL_CALL(_get_previewer_name, ret);
ERR_FAIL_COND_V_MSG(ret.is_empty(), "", "_get_previewer_name virtual method is not implemented");
return ret;
}

Error GDREPreviewer::edit(Ref<Resource> p_resource) {
Error ret = ERR_UNAVAILABLE;
GDVIRTUAL_CALL(_edit, p_resource, ret);
return ret;
}

Error GDREPreviewer::edit_from_path(const String &p_resource_path) {
Error ret = ERR_UNAVAILABLE;
if (GDVIRTUAL_CALL(_edit_from_path, p_resource_path, ret)) {
return ret;
}
Ref<Resource> resource = ResourceCompatLoader::custom_load(p_resource_path, "", get_load_type(), &ret);
ERR_FAIL_COND_V_MSG(ret, ret, "Failed to load resource from path: " + p_resource_path);
return edit(resource);
}

bool GDREPreviewer::can_edit(const String &p_resource_path, const String &p_resource_type) const {
bool ret = false;
GDVIRTUAL_CALL(_can_edit, p_resource_path, p_resource_type, ret);
return ret;
}

String GDREPreviewer::get_edited_resource_path() const {
String ret = "";
GDVIRTUAL_CALL(_get_edited_resource_path, ret);
return ret;
}

void GDREPreviewer::reset() {
GDVIRTUAL_CALL(_reset);
}

ResourceInfo::LoadType GDREPreviewer::get_load_type() const {
ResourceInfo::LoadType ret = ResourceInfo::LoadType::REAL_LOAD;
GDVIRTUAL_CALL(_get_load_type, ret);
return ret;
}

bool GDREPreviewer::can_switch_to_text() const {
bool ret = false;
GDVIRTUAL_CALL(_can_switch_to_text, ret);
return ret;
}

void GDREPreviewer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_previewer_name"), &GDREPreviewer::get_previewer_name);
ClassDB::bind_method(D_METHOD("edit", "resource"), &GDREPreviewer::edit);
ClassDB::bind_method(D_METHOD("edit_from_path", "resource_path"), &GDREPreviewer::edit_from_path);
ClassDB::bind_method(D_METHOD("can_edit", "resource_path"), &GDREPreviewer::can_edit);
ClassDB::bind_method(D_METHOD("get_edited_resource_path"), &GDREPreviewer::get_edited_resource_path);
ClassDB::bind_method(D_METHOD("reset"), &GDREPreviewer::reset);
ClassDB::bind_method(D_METHOD("get_load_type"), &GDREPreviewer::get_load_type);
ClassDB::bind_method(D_METHOD("can_switch_to_text"), &GDREPreviewer::can_switch_to_text);
GDVIRTUAL_BIND(_get_previewer_name);
GDVIRTUAL_BIND(_edit, "resource");
GDVIRTUAL_BIND(_edit_from_path, "resource_path");
GDVIRTUAL_BIND(_can_edit, "resource_path", "resource_type");
GDVIRTUAL_BIND(_get_edited_resource_path);
GDVIRTUAL_BIND(_reset);
GDVIRTUAL_BIND(_get_load_type);
GDVIRTUAL_BIND(_can_switch_to_text);
}
32 changes: 32 additions & 0 deletions gui/gdre_previewer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include "core/io/resource.h"
#include "scene/gui/control.h"

#include "utility/resource_info.h"

class GDREPreviewer : public Control {
GDCLASS(GDREPreviewer, Control);

protected:
static void _bind_methods();

public:
virtual String get_previewer_name() const;
virtual Error edit(Ref<Resource> p_scene);
virtual Error edit_from_path(const String &p_resource_path);
virtual bool can_edit(const String &p_resource_path, const String &p_resource_type) const;
virtual String get_edited_resource_path() const;
virtual void reset();
virtual ResourceInfo::LoadType get_load_type() const;
virtual bool can_switch_to_text() const;

GDVIRTUAL0RC(String, _get_previewer_name);
GDVIRTUAL1R(Error, _edit, Ref<Resource>);
GDVIRTUAL1R(Error, _edit_from_path, String);
GDVIRTUAL2RC(bool, _can_edit, String, String);
GDVIRTUAL0RC(String, _get_edited_resource_path);
GDVIRTUAL0(_reset);
GDVIRTUAL0RC(ResourceInfo::LoadType, _get_load_type);
GDVIRTUAL0RC(bool, _can_switch_to_text);
};
Loading
Loading