From dbc7df2d054eb01d7cdf484df354d2b2b7bd24bf Mon Sep 17 00:00:00 2001 From: nikitalita <69168929+nikitalita@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:40:58 -0700 Subject: [PATCH 1/6] add test for custom pack source --- tests/test_gdre_project_loading.cpp | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_gdre_project_loading.cpp b/tests/test_gdre_project_loading.cpp index d6780a2b4..cf6480355 100644 --- a/tests/test_gdre_project_loading.cpp +++ b/tests/test_gdre_project_loading.cpp @@ -229,6 +229,39 @@ TEST_CASE("[GDSDecomp] Check opening encrypted PCKs") { gdre::rimraf(tmp_pck_path); } +TEST_CASE("[GDSDecomp] Check opening PCK with custom pack source") { + REQUIRE(GDRESettings::get_singleton()); + CHECK(gdre::ensure_dir(get_tmp_path()) == OK); + auto tmp_pck_path = get_tmp_path().path_join("test.pck"); + auto tmp_test_file = get_tmp_path().path_join("test.txt"); + auto tmp_project_path = get_tmp_path().path_join("project.binary"); + REQUIRE(ProjectSettings::get_singleton()); + ProjectSettings::get_singleton()->save_custom(tmp_project_path); + + CHECK(store_file_as_string(tmp_test_file, "dummy") == OK); + HashMap files = { + { "res://test.txt", tmp_test_file }, + { "res://project.binary", tmp_project_path } + }; + CHECK(create_test_pck(tmp_pck_path, files) == OK); + + auto settings = GDRESettings::get_singleton(); + REQUIRE(settings); + + GDScriptLanguage::get_singleton()->init(); + CHECK(ClassDB::class_exists(StringName("PackSourceCustom"))); + String custom_pack_source_script_path = get_gdsdecomp_path().path_join("docs/gdre_custom_pack_source.gd"); + CHECK(settings->add_custom_pack_source_script(custom_pack_source_script_path) == OK); + CHECK(settings->load_project({ tmp_pck_path }, false) == OK); + + test_pck_files(files); + + CHECK(settings->unload_project() == OK); + settings->clear_custom_pack_source_script(); + + gdre::rimraf(tmp_test_file); + gdre::rimraf(tmp_pck_path); +} // Disabling this for now; fragile and kind of redundant. #if 0 static constexpr const char *const export_presets = From ed47011a10ab9ba6039716464ebf79156c8098dd Mon Sep 17 00:00:00 2001 From: nikitalita <69168929+nikitalita@users.noreply.github.com> Date: Fri, 9 Oct 2026 22:31:51 -0700 Subject: [PATCH 2/6] add link to docs in encryption key dialog --- standalone/gdre_main.tscn | 13 +++++++++++-- standalone/gdre_theme.tres | 11 +++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/standalone/gdre_main.tscn b/standalone/gdre_main.tscn index f68ecf212..9e877e895 100644 --- a/standalone/gdre_main.tscn +++ b/standalone/gdre_main.tscn @@ -21,6 +21,12 @@ [ext_resource type="PackedScene" uid="uid://bhxdw5q2etvki" path="res://gdre_export_report.tscn" id="16_htlpu"] [ext_resource type="PackedScene" uid="uid://8tgbmg7tg5ys" path="res://gdre_patch_translation.tscn" id="16_mjm1x"] +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6hx8r"] +content_margin_left = 4.0 +content_margin_top = 4.0 +content_margin_right = 4.0 +content_margin_bottom = 4.0 + [sub_resource type="ButtonGroup" id="ButtonGroup_x51e8"] [node name="root" type="GodotREEditorStandalone" unique_id=1603667437] @@ -63,11 +69,14 @@ custom_minimum_size = Vector2(2.08165e-12, 36) layout_mode = 2 placeholder_text = "" -[node name="Label2" type="Label" parent="SetEncryptionKeyWindow/VBoxContainer" unique_id=1933890505] +[node name="Label2" type="RichTextLabel" parent="SetEncryptionKeyWindow/VBoxContainer" unique_id=1547524909] custom_minimum_size = Vector2(2.08165e-12, 2.08165e-12) layout_mode = 2 tooltip_text = "Optional decryption script for games with custom encryption" -text = "Set Custom Decryption Script..." +theme_override_styles/normal = SubResource("StyleBoxEmpty_6hx8r") +bbcode_enabled = true +text = "Set Custom Decryption Script... [url=https://github.com/GDRETools/gdsdecomp/blob/master/docs/custom_decryptors.md](What's this?)[/url]" +fit_content = true [node name="HBoxContainer2" type="HBoxContainer" parent="SetEncryptionKeyWindow/VBoxContainer" unique_id=1842488725] layout_mode = 2 diff --git a/standalone/gdre_theme.tres b/standalone/gdre_theme.tres index 545bf419a..50db53f24 100644 --- a/standalone/gdre_theme.tres +++ b/standalone/gdre_theme.tres @@ -437,6 +437,8 @@ image = SubResource("Image_1dhk2") [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_qm34x"] +[sub_resource type="StyleBoxEmpty" id="1395"] + [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_0mbvf"] content_margin_left = 6.0 content_margin_top = 4.0 @@ -973,8 +975,6 @@ content_margin_bottom = 1.0 texture = SubResource("529") region_rect = Rect2(0, 0, 16, 16) -[sub_resource type="StyleBoxEmpty" id="1395"] - [sub_resource type="Image" id="Image_c0mi8"] data = { "data": PackedByteArray(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 0, 112, 112, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 167, 167, 16, 223, 223, 223, 152, 223, 223, 223, 151, 167, 167, 167, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 167, 167, 16, 223, 223, 223, 164, 222, 222, 222, 199, 222, 222, 222, 199, 223, 223, 223, 164, 167, 167, 167, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 167, 167, 16, 223, 223, 223, 163, 222, 222, 222, 199, 222, 222, 222, 164, 222, 222, 222, 164, 222, 222, 222, 199, 223, 223, 223, 163, 167, 167, 167, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 167, 167, 16, 223, 223, 223, 163, 222, 222, 222, 199, 222, 222, 222, 164, 165, 165, 165, 17, 165, 165, 165, 17, 222, 222, 222, 164, 222, 222, 222, 199, 223, 223, 223, 163, 167, 167, 167, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 0, 223, 223, 223, 156, 222, 222, 222, 199, 222, 222, 222, 165, 166, 166, 166, 17, 0, 0, 0, 0, 0, 0, 0, 0, 166, 166, 166, 17, 222, 222, 222, 165, 222, 222, 222, 199, 223, 223, 223, 156, 111, 111, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 0, 222, 222, 222, 154, 222, 222, 222, 159, 166, 166, 166, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 166, 166, 18, 222, 222, 222, 159, 222, 222, 222, 154, 111, 111, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 110, 110, 0, 111, 111, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 0, 110, 110, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 0, 111, 111, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 112, 112, 0, 111, 111, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 0, 222, 222, 222, 150, 223, 223, 223, 152, 167, 167, 167, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 167, 167, 167, 16, 223, 223, 223, 153, 222, 222, 222, 151, 111, 111, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 0, 222, 222, 222, 156, 222, 222, 222, 199, 223, 223, 223, 164, 167, 167, 167, 16, 0, 0, 0, 0, 0, 0, 0, 0, 167, 167, 167, 16, 223, 223, 223, 164, 222, 222, 222, 199, 222, 222, 222, 156, 111, 111, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165, 165, 165, 17, 222, 222, 222, 164, 222, 222, 222, 199, 223, 223, 223, 163, 167, 167, 167, 16, 167, 167, 167, 16, 223, 223, 223, 163, 222, 222, 222, 199, 222, 222, 222, 164, 165, 165, 165, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 166, 166, 17, 222, 222, 222, 165, 222, 222, 222, 199, 223, 223, 223, 163, 223, 223, 223, 163, 222, 222, 222, 199, 222, 222, 222, 165, 166, 166, 166, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 166, 166, 17, 223, 223, 223, 165, 222, 222, 222, 199, 222, 222, 222, 199, 223, 223, 223, 165, 166, 166, 166, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, 166, 166, 18, 222, 222, 222, 153, 222, 222, 222, 153, 166, 166, 166, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 111, 111, 0, 111, 111, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), @@ -1438,6 +1438,13 @@ FlatMenuButton/colors/icon_hover_color = Color(0.93845, 0.93845, 0.93845, 1) FlatMenuButton/colors/icon_hover_pressed_color = Color(0.443137, 0.737255, 0.992157, 1) FlatMenuButton/colors/icon_normal_color = Color(0.847474, 0.847474, 0.847474, 1) FlatMenuButton/colors/icon_pressed_color = Color(0.443137, 0.737255, 0.992157, 1) +FlatRichTextLabel/colors/default_color = Color(0.79902, 0.807843, 0.827451, 1) +FlatRichTextLabel/colors/font_color_shadow = Color(0, 0, 0, 0) +FlatRichTextLabel/constants/shadow_as_outline = 0 +FlatRichTextLabel/constants/shadow_offset_x = 1 +FlatRichTextLabel/constants/shadow_offset_y = 1 +FlatRichTextLabel/styles/focus = SubResource("1359") +FlatRichTextLabel/styles/normal = SubResource("1359") FoldableContainer/styles/focus = SubResource("StyleBoxFlat_0mbvf") FoldableContainer/styles/panel = SubResource("StyleBoxFlat_twpvv") FoldableContainer/styles/title_collapsed_hover_panel = SubResource("StyleBoxFlat_e2oel") From ddd8c1a9afb78d2ff69d7297253f6bd2905ca0d9 Mon Sep 17 00:00:00 2001 From: nikitalita <69168929+nikitalita@users.noreply.github.com> Date: Sat, 10 Oct 2026 11:25:51 -0700 Subject: [PATCH 3/6] Fix loading encrypted files on template_release, fix tests --- tests/test_gdre_project_loading.cpp | 25 ++++++----- utility/file_access_gdre.cpp | 64 +++++++++++++++++++++++------ utility/file_access_gdre.h | 3 ++ utility/gdre_packed_source.cpp | 2 +- utility/gdre_settings.cpp | 14 ++++++- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/tests/test_gdre_project_loading.cpp b/tests/test_gdre_project_loading.cpp index cf6480355..a071d5e85 100644 --- a/tests/test_gdre_project_loading.cpp +++ b/tests/test_gdre_project_loading.cpp @@ -1,9 +1,7 @@ -#include "../compat/resource_compat_binary.h" -#include "../compat/resource_compat_text.h" - #include "test_common.h" #include "tests/test_macros.h" +#include "bytecode/bytecode_base.h" #include "utility/common.h" #include "utility/gdre_settings.h" @@ -25,12 +23,12 @@ namespace TestGdreProjectLoading { // 32 0s static const String empty_key = "0000000000000000000000000000000000000000000000000000000000000000"; -inline Error create_test_pck(const String &pck_path, const HashMap &paths, const String &key = empty_key, bool encrypt_directory = false) { +inline Error create_test_pck(const String &pck_path, const HashMap &paths, const String &key = empty_key, bool encrypt_directory = false, bool encrypt_files = false) { PCKPacker pck; Error err = pck.pck_start(pck_path, 32, key, encrypt_directory); ERR_FAIL_COND_V(err, err); for (const auto &path : paths) { - err = pck.add_file(path.key, path.value); + err = pck.add_file(path.key, path.value, encrypt_files); ERR_FAIL_COND_V(err, err); } err = pck.flush(false); @@ -112,7 +110,7 @@ TEST_CASE("[GDSDecomp][ProjectConfigLoader] loading example from current engine" gdre::rimraf(binary_project_path); } -TEST_CASE("[GDSDecomp] GDRESettings project loading") { +TEST_CASE("[GDSDecomp][ProjectLoading] GDRESettings project loading") { REQUIRE(gdre::ensure_dir(get_tmp_path()) == OK); auto tmp_pck_path = get_tmp_path().path_join("test.pck"); auto tmp_project_path = get_tmp_path().path_join("project.binary"); @@ -147,7 +145,7 @@ TEST_CASE("[GDSDecomp] GDRESettings project loading") { gdre::rimraf(tmp_pck_path); } -TEST_CASE("[GDSDecomp] FileAccessGDRE tests") { +TEST_CASE("[GDSDecomp][ProjectLoading] FileAccessGDRE tests") { CHECK(gdre::ensure_dir(get_tmp_path()) == OK); auto tmp_pck_path = get_tmp_path().path_join("FileAccessGDRETest.pck"); auto tmp_test_file = get_tmp_path().path_join("test.txt"); @@ -179,7 +177,7 @@ TEST_CASE("[GDSDecomp] FileAccessGDRE tests") { gdre::rimraf(tmp_pck_path); } -TEST_CASE("[GDSDecomp] Check opening encrypted PCKs") { +TEST_CASE("[GDSDecomp][ProjectLoading] Check opening encrypted PCKs") { REQUIRE(GDRESettings::get_singleton()); CHECK(gdre::ensure_dir(get_tmp_path()) == OK); auto tmp_pck_path = get_tmp_path().path_join("test.pck"); @@ -195,7 +193,7 @@ TEST_CASE("[GDSDecomp] Check opening encrypted PCKs") { }; static const String key = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; - CHECK(create_test_pck(tmp_pck_path, files, key, true) == OK); + CHECK(create_test_pck(tmp_pck_path, files, key, true, true) == OK); auto settings = GDRESettings::get_singleton(); REQUIRE(settings); @@ -206,6 +204,10 @@ TEST_CASE("[GDSDecomp] Check opening encrypted PCKs") { test_pck_files(files); + Ref fa = FileAccess::open("res://test.txt", FileAccess::READ); + REQUIRE(fa.is_valid()); + CHECK(fa->get_proxy_class_name() == "FileAccessEncrypted"); + CHECK(settings->unload_project() == OK); settings->reset_encryption_key(); } @@ -219,6 +221,9 @@ TEST_CASE("[GDSDecomp] Check opening encrypted PCKs") { CHECK(settings->load_project({ tmp_pck_path }, false) == OK); test_pck_files(files); + Ref fa = FileAccess::open("res://test.txt", FileAccess::READ); + REQUIRE(fa.is_valid()); + CHECK(fa->get_proxy_class_name() == "FileAccessEncryptedCustom"); CHECK(settings->unload_project() == OK); settings->reset_custom_decryptor(); @@ -229,7 +234,7 @@ TEST_CASE("[GDSDecomp] Check opening encrypted PCKs") { gdre::rimraf(tmp_pck_path); } -TEST_CASE("[GDSDecomp] Check opening PCK with custom pack source") { +TEST_CASE("[GDSDecomp][ProjectLoading] Check opening PCK with custom pack source") { REQUIRE(GDRESettings::get_singleton()); CHECK(gdre::ensure_dir(get_tmp_path()) == OK); auto tmp_pck_path = get_tmp_path().path_join("test.pck"); diff --git a/utility/file_access_gdre.cpp b/utility/file_access_gdre.cpp index 0b21f4849..dd3798e92 100644 --- a/utility/file_access_gdre.cpp +++ b/utility/file_access_gdre.cpp @@ -843,6 +843,13 @@ int64_t FileAccessGDRE::_get_size(const String &p_file) { return 0; } +String FileAccessGDRE::get_proxy_class_name() const { + if (proxy.is_valid()) { + return FileAccessGDRE::get_class_name_from_file_access(proxy); + } + return ""; +} + // DirAccessGDRE // This is a copy/paste implementation of DirAccessPack, with the exception of the proxying // DirAccessPack accessed the PackedData singleton, which we don't want to do. @@ -1148,6 +1155,11 @@ DirAccessGDRE::~DirAccessGDRE() { } // At the end so that the header files that they pull in don't screw up the above. +#include "core/io/file_access_compressed.h" +#include "core/io/file_access_encrypted.h" +#include "core/io/file_access_zip.h" +#include "utility/file_access_buffer.h" +#include "utility/file_access_string.h" #ifdef WINDOWS_ENABLED #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" @@ -1206,22 +1218,50 @@ String FileAccessGDRE::fix_path(const String &p_path) const { String DirAccessGDRE::fix_path(const String &p_path) const { return PathFinder::_fix_path_file_access(p_path); } -//get_current_file_access_class -String GDREPackedData::get_current_file_access_class(FileAccess::AccessType p_access_type) { - Ref fa = FileAccess::create(p_access_type); - Ref fa_os = fa; - if (fa_os.is_valid()) { - return FILE_ACCESS_OS_STR; + +String FileAccessGDRE::get_class_name_from_file_access(const Ref &p_file_access) { + ERR_FAIL_COND_V_MSG(p_file_access.is_null(), "", "FileAccess is null"); + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessGDRE"; } - Ref fa_pack = fa; - if (fa_pack.is_valid()) { + if (Object::cast_to(p_file_access.ptr()) != nullptr) { return "FileAccessPack"; } - Ref fa_gdre = fa; - if (fa_gdre.is_valid()) { - return "FileAccessGDRE"; + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return FILE_ACCESS_OS_STR; } - return ""; + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessEncrypted"; + } + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessEncryptedCustom"; + } + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessArray"; + } + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessMemory"; + } + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessCompressed"; + } + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessZip"; + } + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessBuffer"; + } + if (Object::cast_to(p_file_access.ptr()) != nullptr) { + return "FileAccessBuffer"; + } + + return p_file_access->get_class_name(); +} + +//get_current_file_access_class +String GDREPackedData::get_current_file_access_class(FileAccess::AccessType p_access_type) { + Ref fa = FileAccess::create(p_access_type); + return FileAccessGDRE::get_class_name_from_file_access(fa); } String GDREPackedData::get_current_dir_access_class(DirAccess::AccessType p_access_type) { diff --git a/utility/file_access_gdre.h b/utility/file_access_gdre.h index bd70bfda3..95a055af7 100644 --- a/utility/file_access_gdre.h +++ b/utility/file_access_gdre.h @@ -278,6 +278,9 @@ class FileAccessGDRE : public FileAccess { virtual bool file_exists(const String &p_name) override; ///< return true if a file exists virtual void close() override; + + static String get_class_name_from_file_access(const Ref &p_file_access); + String get_proxy_class_name() const; }; class DirAccessGDRE : public DirAccess { diff --git a/utility/gdre_packed_source.cpp b/utility/gdre_packed_source.cpp index eace51fda..1a1c80cba 100644 --- a/utility/gdre_packed_source.cpp +++ b/utility/gdre_packed_source.cpp @@ -599,7 +599,7 @@ Ref GDREPackedSource::get_file(const String &p_path, PackedData::Pac file = get_bundled_file(p_path, p_file, decryption_key); ERR_FAIL_COND_V_MSG(file.is_null(), nullptr, vformat("Can't open bundled pack-referenced file '%s'.", String(p_path))); } else { - if (p_file->encrypted && GDRESettings::get_singleton()->get_custom_decryptor().is_valid()) { + if (p_file->encrypted) { file = open_encrypted_packed_file(p_file, p_path, decryption_key); ERR_FAIL_COND_V_MSG(file.is_null(), nullptr, vformat("Can't open encrypted pack-referenced file '%s'.", String(p_path))); } else { diff --git a/utility/gdre_settings.cpp b/utility/gdre_settings.cpp index f5d787146..20901fad1 100644 --- a/utility/gdre_settings.cpp +++ b/utility/gdre_settings.cpp @@ -724,6 +724,11 @@ Error GDRESettings::load_project(const Vector &p_paths, bool _cmd_line_e ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "No valid paths provided!"); } + // set encryption key + if (!enc_key.is_empty()) { + memcpy(script_encryption_key, enc_key.ptr(), MIN(enc_key.size(), 32)); + } + for (int i = 0; i < pck_files.size(); i++) { err = _add_pack(pck_files[i]); if (err) { @@ -739,8 +744,11 @@ Error GDRESettings::load_project(const Vector &p_paths, bool _cmd_line_e ERR_FAIL_COND_V_MSG(err, err, "Can't load project!"); } } + if (!is_pack_loaded()) { + unload_project(true); + ERR_FAIL_V_MSG(err, "FATAL ERROR: loaded project pack, but didn't load files from it!"); + } - ERR_FAIL_COND_V_MSG(!is_pack_loaded(), ERR_FILE_CANT_READ, "FATAL ERROR: loaded project pack, but didn't load files from it!"); if (_cmd_line_extract) { // we don't want to load the imports and project config if we're just extracting. return OK; @@ -1224,6 +1232,10 @@ Error GDRESettings::save_project_config_binary(const String &p_out_dir = "") { Error GDRESettings::unload_project(bool p_no_reset_ephemeral) { logger->stop_prebuffering(); GDREPackedData::get_singleton()->clear(); + // reset encryption key + for (int i = 0; i < 32; i++) { + script_encryption_key[i] = 0; + } // If this wasn't a custom decryptor set by the user, unload it. if (custom_decryptor.is_valid() && custom_decryption_script_path.is_empty()) { custom_decryptor = nullptr; From 30c0d25d9e1ffffef2a378d1acd5f968d5362b50 Mon Sep 17 00:00:00 2001 From: nikitalita <69168929+nikitalita@users.noreply.github.com> Date: Sat, 10 Oct 2026 12:41:30 -0700 Subject: [PATCH 4/6] animation_library: Fix crash when we fail to generate a debug mesh --- exporters/scene_exporter.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/exporters/scene_exporter.cpp b/exporters/scene_exporter.cpp index a9b162009..9c1e0a323 100644 --- a/exporters/scene_exporter.cpp +++ b/exporters/scene_exporter.cpp @@ -3816,6 +3816,7 @@ Error _create_packed_scene_from_animation_library(const String &p_original_path, skeleton_path_to_skeleton.insert(node_name, skeleton); } } + Vector skeleton_path_meshes_added; for (auto &[skeleton_path, skeleton] : skeleton_path_to_skeleton) { String skeleton_name = skeleton->get_name(); @@ -3831,9 +3832,10 @@ Error _create_packed_scene_from_animation_library(const String &p_original_path, String child_name = child ? child->get_name().operator String() : skeleton_name + "_mesh_instance"; mesh_instance = memnew(MeshInstance3D); Ref debug_bones_mesh = generate_debug_bones_mesh(skeleton); + ERR_CONTINUE_MSG(debug_bones_mesh.is_null(), vformat("Failed to generate debug bones mesh for skeleton %s in animation library %s", skeleton_name, p_original_path)); + ERR_CONTINUE_MSG(debug_bones_mesh->get_surface_count() == 0, vformat("Generated debug bones mesh for skeleton %s in animation library %s has no surfaces!", skeleton_name, p_original_path)); Ref debug_bones_material = debug_bones_mesh->surface_get_material(0); debug_bones_mesh->set_name(skeleton_name + "_debug_bones_mesh"); - debug_bones_material->set_name(skeleton_name + "_debug_bones_material"); mesh_instance->set_mesh(debug_bones_mesh); mesh_instance->set_name(child_name); if (child) { @@ -3844,10 +3846,17 @@ Error _create_packed_scene_from_animation_library(const String &p_original_path, mesh_instance->set_owner(root); } mesh_instance->set_skeleton_path(NodePath("..")); - mesh_instance->set_surface_override_material(0, debug_bones_material); + if (debug_bones_material.is_valid()) { + debug_bones_material->set_name(skeleton_name + "_debug_bones_material"); + mesh_instance->set_surface_override_material(0, debug_bones_material); + } else { + WARN_PRINT(vformat("No material found for debug bones mesh for skeleton %s in animation library %s", skeleton_name, p_original_path)); + } } mesh_instance->set_skin(skin); + skeleton_path_meshes_added.append(skeleton_path); } + ERR_FAIL_COND_V_MSG(skeleton_path_meshes_added.size() == 0, ERR_FILE_CORRUPT, vformat("No meshes added for skeleton paths in animation library %s", p_original_path)); AnimationPlayer *animation_player = memnew(AnimationPlayer); animation_library->set_path_cache(p_original_path); animation_player->set_name("AnimationPlayer"); From a4daa4747c02cb62c433f38a9325b8e878da3027 Mon Sep 17 00:00:00 2001 From: nikitalita <69168929+nikitalita@users.noreply.github.com> Date: Sat, 10 Oct 2026 12:42:50 -0700 Subject: [PATCH 5/6] scene: Fix error messages not being filtered when GLTF validation fails --- exporters/scene_exporter.cpp | 29 ++++++++++++++++++++--------- exporters/scene_exporter.h | 3 ++- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/exporters/scene_exporter.cpp b/exporters/scene_exporter.cpp index 9c1e0a323..86952e1dc 100644 --- a/exporters/scene_exporter.cpp +++ b/exporters/scene_exporter.cpp @@ -674,7 +674,7 @@ String GLBExporterInstance::add_errors_to_report(Error p_err, const String &err_ step = "serializing GLTF document"; break; case ERR_FILE_CORRUPT: - step = "GLTF is empty or corrupt"; + step = "GLTF validation: scene is empty or corrupt"; break; case ERR_BUG: step = "GLTF conversion"; @@ -726,6 +726,10 @@ String GLBExporterInstance::add_errors_to_report(Error p_err, const String &err_ errors.append("** Errors during import parameter setting:"); errors.append_array(import_param_error_messages); } + if (validation_error_messages.size() > 0) { + errors.append("** Errors during GLTF validation:"); + errors.append_array(validation_error_messages); + } other_error_messages.append_array(_get_logged_error_messages()); if (other_error_messages.size() > 0) { errors.append("** Other errors:"); @@ -2540,7 +2544,7 @@ Error GLBExporterInstance::_check_model_can_load(const String &p_dest_path) { String error_string; Error load_err = load_model(p_dest_path, model, error_string); if (load_err != OK) { - gltf_serialization_error_messages.append(error_string); + validation_error_messages.append(error_string); return ERR_FILE_CORRUPT; } return OK; @@ -2948,7 +2952,7 @@ void GLBExporterInstance::cancel() { canceled = true; } -Error GLBExporterInstance::_get_return_error() { +Error GLBExporterInstance::_get_return_error(const String &p_dest_path) { bool set_all_externals = !updating_import_info; if (updating_import_info) { set_all_externals = true; @@ -2967,6 +2971,8 @@ Error GLBExporterInstance::_get_return_error() { Ref animation_error_re = RegEx::create_from_string(ANIMATION_ERROR_PATTERN); static const Vector errors_to_ignore = { + "Can't set_pixel() on compressed image, sorry", // GLTFDocument couldn't decompress image, but forgot to fail + "Can't get_pixel() on compressed image, sorry", // GLTFDocument couldn't decompress image, but forgot to fail "A node was animated, but it wasn't found in the GLTFState", // animation track not found because it's not exported "Parameter \"p_target_object\" is null.", // object property animated by track not found because it's not exportable by the exporter "Image width specified (0 pixels) must be greater than 0 pixels", // texture export failure caused by zero-sized source images @@ -3059,6 +3065,15 @@ Error GLBExporterInstance::_get_return_error() { } } + // Check if the model can be loaded; minimum validation to ensure the model is valid + { + other_error_messages.append_array(_get_logged_error_messages()); + if (_check_model_can_load(p_dest_path) != OK) { + validation_error_messages.append_array(_get_logged_error_messages()); + err = ERR_FILE_CORRUPT; + GDRE_SCN_EXP_FAIL_V_MSG(ERR_FILE_CORRUPT, ""); + } + } if (had_gltf_serialization_errors) { String _ = add_errors_to_report(ERR_BUG, ""); } else if ((!set_all_externals || missing_dependencies.size() > 0) && err == OK) { @@ -4471,12 +4486,8 @@ Error GLBExporterInstance::_batch_export_instanced_scene(Node *root, const Strin if (updating_import_info) { _update_import_params(p_dest_path); } - // Check if the model can be loaded; minimum validation to ensure the model is valid - err = _check_model_can_load(p_dest_path); - if (err) { - GDRE_SCN_EXP_FAIL_V_MSG(ERR_FILE_CORRUPT, ""); - } - err = _get_return_error(); + + err = _get_return_error(p_dest_path); return err; } diff --git a/exporters/scene_exporter.h b/exporters/scene_exporter.h index 13265f84b..f9a02aa84 100644 --- a/exporters/scene_exporter.h +++ b/exporters/scene_exporter.h @@ -153,6 +153,7 @@ class GLBExporterInstance { Vector import_param_error_messages; Vector dependency_resolution_list; Vector other_error_messages; + Vector validation_error_messages; constexpr static const char *const COPYRIGHT_STRING_FORMAT = "The Creators of '%s'"; @@ -180,7 +181,7 @@ class GLBExporterInstance { void convert_animation_tracks_to_v4_for_player(AnimationPlayer *p_player); void _unload_deps(); - Error _get_return_error(); + Error _get_return_error(const String &p_dest_path); Node *_instantiate_scene(Ref scene); void set_path_options(Dictionary &import_opts, const String &path, const String &prefix = "save_to_file"); From a60f18cb0bee11ae1adf6796805d25a66ba8a28d Mon Sep 17 00:00:00 2001 From: nikitalita <69168929+nikitalita@users.noreply.github.com> Date: Sat, 10 Oct 2026 13:36:16 -0700 Subject: [PATCH 6/6] fix finding assembly when assembly is in a PCK --- utility/gdre_settings.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/utility/gdre_settings.cpp b/utility/gdre_settings.cpp index 20901fad1..6f330766c 100644 --- a/utility/gdre_settings.cpp +++ b/utility/gdre_settings.cpp @@ -2915,9 +2915,6 @@ Error GDRESettings::load_project_dotnet_assembly() { //res://.mono/assemblies//.dll search_dirs.push_back("res://.mono/assemblies/Release"); search_dirs.push_back("res://.mono/assemblies/Debug"); - } else if (current_project->type == PackInfo::APK || current_project->type == PackInfo::ZIP || current_project->type == PackInfo::DIR) { - // Exported APKs have the assembly in the .godot/mono directory - search_dirs.push_back("res://.godot/mono"); } // Godot 4.x games have the assembly in a sub-directory named "data___" in the game directory Vector directories = DirAccess::get_directories_at(project_dir); @@ -2926,6 +2923,11 @@ Error GDRESettings::load_project_dotnet_assembly() { search_dirs.push_back(project_dir.path_join(directory)); } } + // Godot 4.x games can also have the assembly in the .godot/mono directory + if (get_ver_major() >= 4) { + search_dirs.push_back("res://.godot/mono"); + } + // Finally, check the game directory search_dirs.push_back(project_dir); String assembly_name = get_dotnet_assembly_name_from_settings(); String base_name = get_pack_path().get_file().get_basename();