diff --git a/.github/workflows/all_builds.yml b/.github/workflows/all_builds.yml index bfa45c83c..8ecd1b43d 100644 --- a/.github/workflows/all_builds.yml +++ b/.github/workflows/all_builds.yml @@ -26,7 +26,7 @@ on: env: GODOT_BASE_BRANCH: master # Change the README too - GODOT_MAIN_SYNC_REF: 92e51fca7247c932f95a1662aefc28aca96e8de6 + GODOT_MAIN_SYNC_REF: ef8d981267702de38ffc24136f9d823d31781c60 SCONSFLAGS: verbose=yes warnings=all werror=no module_text_server_fb_enabled=yes minizip=yes debug_symbols=no deprecated=yes SCONSFLAGS_TEMPLATE: no_editor_splash=yes module_camera_enabled=no module_mbedtls_enabled=no module_enet_enabled=no module_mobile_vr_enabled=no module_upnp_enabled=no module_noise_enabled=no module_websocket_enabled=no use_static_cpp=yes builtin_freetype=yes builtin_libpng=yes builtin_zlib=yes builtin_libwebp=yes builtin_libvorbis=yes builtin_libogg=yes module_csg_enabled=yes module_gridmap_enabled=yes disable_3d=no SCONS_CACHE_MSVC_CONFIG: true diff --git a/README.md b/README.md index e77ea22d7..b9aee2c02 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,8 @@ For ease of bootstrapping development, we have included launch, build, and setti ### Requirements -Godot 4.0 (master branch) @ 92e51fca7247c932f95a1662aefc28aca96e8de6 +Godot 4.0 (master branch) @ ef8d981267702de38ffc24136f9d823d31781c60 + - Support for building on 3.x has been dropped and no new features are being pushed - Godot RE Tools still retains the ability to decompile 3.x and 2.x projects, however. diff --git a/SCsub b/SCsub index 7dd4d33bb..4b468bc11 100644 --- a/SCsub +++ b/SCsub @@ -96,12 +96,10 @@ def write_version_header(): prerelease_tag = f"{prerelease_name}.{prerelease_num}" else: prerelease_tag += ".1" - new_version_info = f"{major}.{minor}.{patch}-{prerelease_tag}+{dev_stuff}" + new_version_info = f"{major}.{minor}.{patch}-{prerelease_tag}+{dev_stuff.replace('+', '-')}" else: patch = str(int(patch) + 1) if patch.isdigit() else 0 new_version_info = f"{major}.{minor}.{patch}-{dev_stuff}" - if build_metadata: - new_version_info += "+" + build_metadata version_info = new_version_info else: version_info = res diff --git a/bytecode/bytecode_base.cpp b/bytecode/bytecode_base.cpp index a0b9f20ba..b6e5b7364 100644 --- a/bytecode/bytecode_base.cpp +++ b/bytecode/bytecode_base.cpp @@ -1458,6 +1458,13 @@ bool GDScriptDecomp::test_built_in_func_arg_count(const Vector &tokens Ref GDScriptDecomp::get_godot_ver() const { return GodotVer::parse(get_engine_version()); } +Ref GDScriptDecomp::get_max_godot_ver() const { + auto max_ver = get_max_engine_version(); + if (max_ver.is_empty()) { + return nullptr; + } + return GodotVer::parse(max_ver); +} Vector GDScriptDecomp::get_compile_errors(const Vector &p_buffer) { Vector identifiers; diff --git a/bytecode/bytecode_base.h b/bytecode/bytecode_base.h index dff6c8855..afe539aac 100644 --- a/bytecode/bytecode_base.h +++ b/bytecode/bytecode_base.h @@ -192,6 +192,7 @@ class GDScriptDecomp : public RefCounted { virtual String get_engine_version() const = 0; virtual String get_max_engine_version() const = 0; Ref get_godot_ver() const; + Ref get_max_godot_ver() const; Error decompile_byte_code_encrypted(const String &p_path, Vector p_key); Error decompile_byte_code(const String &p_path); diff --git a/bytecode/bytecode_tester.cpp b/bytecode/bytecode_tester.cpp index e01aaae7e..dc0b96b58 100644 --- a/bytecode/bytecode_tester.cpp +++ b/bytecode/bytecode_tester.cpp @@ -1,4 +1,5 @@ #include "bytecode/bytecode_tester.h" +#include "bytecode/bytecode_base.h" #include "bytecode/bytecode_versions.h" #include "core/io/file_access.h" #include "utility/gdre_settings.h" @@ -145,72 +146,38 @@ built-in func divergence for bytecode version 13 (3.1.x only) // TODO: add this */ -uint64_t generic_test(const Vector &p_paths, const Vector &p_key, int ver_major_hint, int ver_minor_hint, bool include_dev = false) { - ERR_FAIL_COND_V_MSG(p_paths.size() == 0, 0, "No files to test"); - int detected_bytecode_version; - if (p_key.size() > 0) { - if (ver_major_hint >= 0 && ver_major_hint < 3) { - ERR_FAIL_V_MSG(0, "Encrypted scripts were not supported in Godot 1.x or 2.x."); - } else { - detected_bytecode_version = GDScriptDecomp::read_bytecode_version_encrypted(p_paths[0], ver_major_hint <= 0 ? 3 : ver_major_hint, p_key); - ERR_FAIL_COND_V_MSG(detected_bytecode_version <= 0, 0, "Failed to detect bytecode version for encrypted script (did you set the correct key?):" + p_paths[0]); - } - } else { - detected_bytecode_version = GDScriptDecomp::read_bytecode_version(p_paths[0]); +int get_bytecode_version(Vector bytecode_files) { + int bytecode_version = 0; + if (bytecode_files.size() == 0) { + ERR_FAIL_V_MSG({}, "No bytecode files provided."); } - ERR_FAIL_COND_V_MSG(detected_bytecode_version <= 0, 0, "Failed to detect bytecode version for script " + p_paths[0]); - - Vector> decomp_versions = get_decomps_for_bytecode_ver(detected_bytecode_version, include_dev); - Vector passed_versions; - for (String path : p_paths) { - Vector data; - Vector failed_version_idxs; - if (p_key.size() > 0) { - // We should never have scripts with bytecodes of differing versions in the same project. - ERR_FAIL_COND_V_MSG( - detected_bytecode_version != GDScriptDecomp::read_bytecode_version_encrypted(path, ver_major_hint <= 0 ? 3 : ver_major_hint, p_key), - 0, "Detected bytecode version mismatch for script " + path); - Error err = GDScriptDecomp::get_buffer_encrypted(path, ver_major_hint <= 0 ? 3 : ver_major_hint, p_key, data); - ERR_FAIL_COND_V_MSG(err == ERR_UNAUTHORIZED, 0, "Failed to decrypt file " + path + " (Did you set the correct key?)"); - ERR_FAIL_COND_V_MSG(err != OK, 0, "Failed to read file " + path); + for (const String &file : bytecode_files) { + int this_ver = 0; + if (file.get_extension().to_lower() == "gde") { + this_ver = GDScriptDecomp::read_bytecode_version_encrypted(file, 3, GDRESettings::get_singleton()->get_encryption_key()); } else { - ERR_FAIL_COND_V_MSG(detected_bytecode_version != GDScriptDecomp::read_bytecode_version(path), 0, "Detected bytecode version mismatch for script " + path); - data = FileAccess::get_file_as_bytes(path); + this_ver = GDScriptDecomp::read_bytecode_version(file); } - if (data.size() == 0) { - WARN_PRINT("File is empty: " + path); + if (this_ver == -1) { + WARN_PRINT("Could not read bytecode version from file: " + file); continue; } - for (int i = 0; i < decomp_versions.size(); i++) { - auto test_result = decomp_versions[i]->test_bytecode(data); - switch (test_result) { - case GDScriptDecomp::BytecodeTestResult::BYTECODE_TEST_FAIL: { - failed_version_idxs.push_back(i); - } break; - case GDScriptDecomp::BytecodeTestResult::BYTECODE_TEST_PASS: { - if (passed_versions.has(i)) { - break; - } - passed_versions.push_back(decomp_versions[i]->get_bytecode_rev()); - } break; - case GDScriptDecomp::BytecodeTestResult::BYTECODE_TEST_CORRUPT: { - WARN_PRINT("BYTECODE_TEST_CORRUPT test result for " + vformat("%07x", decomp_versions[i]->get_bytecode_rev()) + ", script " + path); - failed_version_idxs.push_back(i); - } break; - default: - break; - } - } - failed_version_idxs.sort(); - // remove failed versions from the list in reverse order - for (int i = failed_version_idxs.size() - 1; i >= 0; i--) { - decomp_versions.remove_at(failed_version_idxs[i]); - } - failed_version_idxs.clear(); - if (decomp_versions.size() == 0) { - break; + if (bytecode_version == 0) { + bytecode_version = this_ver; + } else if (this_ver != bytecode_version) { + // Hell no. + return -1; } } + return bytecode_version; +} + +uint64_t generic_test(const Vector &p_paths, int ver_major_hint, int ver_minor_hint, bool include_dev = false) { + int detected_bytecode_version = get_bytecode_version(p_paths); + ERR_FAIL_COND_V_MSG(detected_bytecode_version == -1, {}, "Inconsistent byecode versions across files!!!"); + ERR_FAIL_COND_V_MSG(detected_bytecode_version == 0, {}, "Could not read bytecode version from files."); + + Vector> decomp_versions = BytecodeTester::get_possible_decomps(p_paths, include_dev); if (decomp_versions.size() == 1) { // easy return decomp_versions[0]->get_bytecode_rev(); @@ -218,74 +185,18 @@ uint64_t generic_test(const Vector &p_paths, const Vector &p_ke if (decomp_versions.size() == 0) { if (!include_dev) { // try again with the dev versions - return generic_test(p_paths, p_key, ver_major_hint, ver_minor_hint, true); + return generic_test(p_paths, ver_major_hint, ver_minor_hint, true); } // else fail ERR_FAIL_V_MSG(0, "Failed to detect GDScript revision for bytecode version " + vformat("%d", detected_bytecode_version) + ", engine version " + vformat("%d.%d", ver_major_hint, ver_minor_hint) + ", please report this issue on GitHub."); } - // otherwise, we have more than 1. - if (passed_versions.size() == 1) { - // only one version passed, so we'll use that - return passed_versions[0]; - } // otherwise... - Vector candidates; - if (ver_major_hint > 0 && ver_minor_hint <= 0) { - for (int i = 0; i < decomp_versions.size(); i++) { - if (decomp_versions[i]->get_engine_ver_major() == ver_major_hint) { - int rev = decomp_versions[i]->get_bytecode_rev(); - if (!passed_versions.is_empty()) { - if (passed_versions.has(rev)) { - candidates.push_back(rev); - } - } else { - candidates.push_back(rev); - } - } - } - } else if (ver_major_hint > 0 && ver_minor_hint > 0) { - for (int i = 0; i < decomp_versions.size(); i++) { - if (decomp_versions[i]->get_engine_ver_major() == ver_major_hint) { - bool good = false; - if (decomp_versions[i]->get_max_engine_version() != "") { - Ref min_ver = GodotVer::parse(decomp_versions[i]->get_engine_version()); - Ref max_ver = GodotVer::parse(decomp_versions[i]->get_max_engine_version()); - if (max_ver->get_minor() >= ver_minor_hint && min_ver->get_minor() <= ver_minor_hint) { - good = true; - } - } else { - Ref ver = memnew(GodotVer(decomp_versions[i]->get_engine_version())); - if (ver->get_minor() == ver_minor_hint) { - good = true; - } - } - if (good) { - int rev = decomp_versions[i]->get_bytecode_rev(); - if (!passed_versions.is_empty()) { - if (passed_versions.has(rev)) { - candidates.push_back(rev); - } - } else { - candidates.push_back(rev); - } - } - } - } - } else { - // no version hint - for (int i = 0; i < decomp_versions.size(); i++) { - candidates.push_back(decomp_versions[i]->get_bytecode_rev()); - } - } - ERR_FAIL_COND_V_MSG(candidates.is_empty(), 0, "Failed to detect GDScript revision for bytecode version " + vformat("%d", detected_bytecode_version) + ", engine version " + vformat("%d.%d", ver_major_hint, ver_minor_hint) + ", please report this issue on GitHub."); - if (candidates.size() == 1) { - return candidates[0]; - } + auto candidates = BytecodeTester::filter_decomps(decomp_versions, ver_major_hint, ver_minor_hint); // otherwise, we have multiple candidates, fail with an error message that contains the candidates String candidates_str; for (int i = 0; i < candidates.size(); i++) { - candidates_str += vformat("%07x", candidates[i]); + candidates_str += vformat("%07x", candidates[i]->get_bytecode_rev()); if (i < candidates.size() - 1) { candidates_str += ", "; } @@ -404,7 +315,7 @@ uint64_t test_files_2_1(const Vector &p_paths) { } if (rev == 0) { // try it with the dev versions. - return generic_test(p_paths, Vector(), 2, 1, true); + return generic_test(p_paths, 2, 1, true); } } return rev; @@ -533,7 +444,7 @@ uint64_t test_files_3_1(const Vector &p_paths, const Vector &p_ rev = 0x1ca61a3; } else { // Try it with the dev versions. - return generic_test(p_paths, p_key, 3, 1, true); + return generic_test(p_paths, 3, 1, true); } } @@ -553,20 +464,85 @@ uint64_t BytecodeTester::test_files(const Vector &p_paths, int ver_major } else if (ver_major_hint == 2 && ver_minor_hint == 1) { rev = test_files_2_1(p_paths); } else { - rev = generic_test(p_paths, key, ver_major_hint, ver_minor_hint); + rev = generic_test(p_paths, ver_major_hint, ver_minor_hint); } return rev; } -uint64_t BytecodeTester::test_files_encrypted(const Vector &p_paths, const Vector &p_key, int ver_major_hint, int ver_minor_hint) { - uint64_t rev = 0; - if (ver_major_hint > 0 && ver_major_hint <= 2) { - // 1-2 didn't have encrypted scripts....??? - ERR_FAIL_V_MSG(0, "Encrypted scripts were not supported in Godot 1.x or 2.x."); - } else if (ver_major_hint == 3 && ver_minor_hint == 1) { - return test_files_3_1(p_paths, p_key); +Vector> get_possibles_from_set(const Vector &bytecode_files, const Vector> &decomps) { + Vector> passed; + for (const auto &decomp : decomps) { + bool failed = false; + for (const String &file : bytecode_files) { + Vector buffer; + if (file.get_extension().to_lower() == "gde") { + Error err = GDScriptDecomp::get_buffer_encrypted(file, 3, GDRESettings::get_singleton()->get_encryption_key(), buffer); + if (err) { + WARN_PRINT("Could not read encrypted bytecode file: " + file); + continue; + } + } else { + buffer = FileAccess::get_file_as_bytes(file); + if (buffer.size() == 0) { + WARN_PRINT("Could not read bytecode file: " + file); + continue; + } + } + auto result = decomp->test_bytecode(buffer); + if (result == GDScriptDecomp::BYTECODE_TEST_FAIL || result == GDScriptDecomp::BYTECODE_TEST_CORRUPT) { + failed = true; + break; + } + } + if (!failed) { + passed.append(decomp); + } + } + return passed; +} + +Vector> BytecodeTester::get_possible_decomps(Vector bytecode_files, bool include_dev) { + int bytecode_version = get_bytecode_version(bytecode_files); + ERR_FAIL_COND_V_MSG(bytecode_version == -1, {}, "Inconsistent byecode versions across files!!!"); + ERR_FAIL_COND_V_MSG(bytecode_version == 0, {}, "Could not read bytecode version from files."); + auto decomps = get_decomps_for_bytecode_ver(bytecode_version, include_dev); + return get_possibles_from_set(bytecode_files, decomps); +} + +Vector> BytecodeTester::filter_decomps(const Vector> &decomp_versions, int ver_major_hint, int ver_minor_hint) { + Vector> candidates; + if (ver_major_hint > 0 && ver_minor_hint < 0) { + for (int i = 0; i < decomp_versions.size(); i++) { + if (decomp_versions[i]->get_engine_ver_major() == ver_major_hint) { + candidates.push_back(decomp_versions[i]); + } + } + } else if (ver_major_hint > 0 && ver_minor_hint >= 0) { + for (int i = 0; i < decomp_versions.size(); i++) { + if (decomp_versions[i]->get_engine_ver_major() == ver_major_hint) { + bool good = false; + if (decomp_versions[i]->get_max_engine_version() != "") { + Ref min_ver = GodotVer::parse(decomp_versions[i]->get_engine_version()); + Ref max_ver = GodotVer::parse(decomp_versions[i]->get_max_engine_version()); + if (max_ver->get_minor() >= ver_minor_hint && min_ver->get_minor() <= ver_minor_hint) { + good = true; + } + } else { + Ref ver = memnew(GodotVer(decomp_versions[i]->get_engine_version())); + if (ver->get_minor() == ver_minor_hint) { + good = true; + } + } + if (good) { + candidates.push_back(decomp_versions[i]); + } + } + } } else { - return generic_test(p_paths, p_key, ver_major_hint, ver_minor_hint); + // no version hint + for (int i = 0; i < decomp_versions.size(); i++) { + candidates.push_back(decomp_versions[i]); + } } - return rev; + return candidates; } \ No newline at end of file diff --git a/bytecode/bytecode_tester.h b/bytecode/bytecode_tester.h index c4b5c6f37..9fae19b27 100644 --- a/bytecode/bytecode_tester.h +++ b/bytecode/bytecode_tester.h @@ -8,7 +8,6 @@ class BytecodeTester { public: // NOTE: This function is only implemented for testing 2.1 or 3.1 scripts. This returns 0 for everything else. static uint64_t test_files(const Vector &p_paths, int ver_major_hint = -1, int ver_minor_hint = -1); - - // NOTE: This function is only implemented for testing 3.1 scripts (2.1 had no encryption scheme). This returns 0 for everything else. - static uint64_t test_files_encrypted(const Vector &p_paths, const Vector &p_key, int ver_major_hint = -1, int ver_minor_hint = -1); + static Vector> filter_decomps(const Vector> &decomps, int ver_major_hint, int ver_minor_hint); + static Vector> get_possible_decomps(Vector bytecode_files, bool include_dev = false); }; \ No newline at end of file diff --git a/compat/resource_compat_text.cpp b/compat/resource_compat_text.cpp index bfbb8f2c0..54eafc8a7 100644 --- a/compat/resource_compat_text.cpp +++ b/compat/resource_compat_text.cpp @@ -2225,9 +2225,12 @@ void ResourceFormatSaverCompatTextInstance::_find_resources(const Variant &p_var } break; case Variant::PACKED_BYTE_ARRAY: { // Balance between compatibility and performance. + // compat: We should only use the newest format if it was explicitly set or it has PackedVector4Arrays. +#if 0 if (use_compat && p_variant.operator PackedByteArray().size() > 64) { use_compat = false; } +#endif } break; case Variant::PACKED_VECTOR4_ARRAY: { use_compat = false; @@ -2325,7 +2328,7 @@ Error ResourceFormatSaverCompatTextInstance::save(const String &p_path, const Re } // Save resources. - use_compat = true; // _find_resources() changes this. + use_compat = format_version < 4; // _find_resources() changes this. _find_resources(p_resource, true); if (!use_compat && format_version >= 3) { format_version = 4; diff --git a/editor/gdre_editor.cpp b/editor/gdre_editor.cpp index 154e9ee99..5faff62ac 100644 --- a/editor/gdre_editor.cpp +++ b/editor/gdre_editor.cpp @@ -235,12 +235,12 @@ void GodotREEditor::init_gui(Control *p_control, HBoxContainer *p_menu, bool p_l pck_file_selection = memnew(FileDialog); pck_file_selection->set_access(FileDialog::ACCESS_FILESYSTEM); - pck_file_selection->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE); + pck_file_selection->set_file_mode(FileDialog::FILE_MODE_OPEN_FILES); pck_file_selection->add_filter("*.pck;PCK archive files"); pck_file_selection->add_filter("*.exe,*.bin,*.32,*.64;Self contained executable files"); pck_file_selection->add_filter("*.apk;Android application files"); pck_file_selection->add_filter("*.zip;Zipped Godot project files"); - pck_file_selection->connect("file_selected", callable_mp(this, &GodotREEditor::_pck_select_request)); + pck_file_selection->connect("files_selected", callable_mp(this, &GodotREEditor::_pck_select_request)); pck_file_selection->set_show_hidden_files(true); p_control->add_child(pck_file_selection); @@ -326,7 +326,7 @@ void GodotREEditor::init_gui(Control *p_control, HBoxContainer *p_menu, bool p_l p_menu->set_anchor(Side::SIDE_TOP, 0); menu_button = memnew(MenuButton); menu_button->set_text(RTR("RE Tools")); - menu_button->set_icon(icons["RELogo"]); + menu_button->set_button_icon(icons["RELogo"]); menu_popup = menu_button->get_popup(); menu_popup->add_icon_item(icons["RELogo"], RTR("Recover project..."), MENU_EXT_PCK); menu_popup->add_separator(); @@ -342,7 +342,7 @@ void GodotREEditor::init_gui(Control *p_control, HBoxContainer *p_menu, bool p_l menu_button = memnew(MenuButton); menu_button->set_text(RTR("PCK")); - menu_button->set_icon(icons["REPack"]); + menu_button->set_button_icon(icons["REPack"]); menu_popup = menu_button->get_popup(); menu_popup->add_icon_item(icons["REPack"], RTR("Create PCK archive from folder..."), MENU_CREATE_PCK); menu_button->set_anchor(Side::SIDE_TOP, 0); @@ -351,7 +351,7 @@ void GodotREEditor::init_gui(Control *p_control, HBoxContainer *p_menu, bool p_l menu_button = memnew(MenuButton); menu_button->set_text(RTR("GDScript")); - menu_button->set_icon(icons["REScript"]); + menu_button->set_button_icon(icons["REScript"]); menu_popup = menu_button->get_popup(); menu_popup->add_icon_item(icons["REScript"], RTR("Decompile .GDC/.GDE script files..."), MENU_DECOMP_GDS); menu_popup->add_icon_item(icons["REScript"], RTR("Compile .GD script files..."), MENU_COMP_GDS); @@ -361,7 +361,7 @@ void GodotREEditor::init_gui(Control *p_control, HBoxContainer *p_menu, bool p_l menu_button = memnew(MenuButton); menu_button->set_text(RTR("Resources")); - menu_button->set_icon(icons["REResBT"]); + menu_button->set_button_icon(icons["REResBT"]); menu_popup = menu_button->get_popup(); menu_popup->add_icon_item(icons["REResBT"], RTR("Convert binary resources to text..."), MENU_CONV_TO_TXT); menu_popup->add_icon_item(icons["REResTB"], RTR("Convert text resources to binary..."), MENU_CONV_TO_BIN); @@ -375,7 +375,7 @@ void GodotREEditor::init_gui(Control *p_control, HBoxContainer *p_menu, bool p_l } else { menu_button = memnew(MenuButton); menu_button->set_text(RTR("RE Tools")); - menu_button->set_icon(icons["RELogo"]); + menu_button->set_button_icon(icons["RELogo"]); menu_popup = menu_button->get_popup(); menu_popup->add_icon_item(icons["RELogo"], RTR("Recover project..."), MENU_EXT_PCK); @@ -724,20 +724,20 @@ void GodotREEditor::_compile_process() { /* PCK explorer */ /*************************************************************************/ -void GodotREEditor::_pck_select_request(const String &p_path) { +void GodotREEditor::_pck_select_request(const Vector &p_paths) { pck_dialog->clear(); pck_files.clear(); - pck_file = p_path; + pck_file = p_paths[0]; if (key_dialog->get_key().size() > 0) { GDRESettings::get_singleton()->set_encryption_key(key_dialog->get_key()); } - Error err = GDRESettings::get_singleton()->load_pack(p_path); + Error err = GDRESettings::get_singleton()->load_project(p_paths); if (err) { if (err == ERR_PRINTER_ON_FIRE) { - show_warning(RTR("Error opening encrypted PCK file: ") + p_path + "\nSet correct encryption key and try again.", RTR("Read PCK")); + show_warning(RTR("Error opening encrypted PCK file: ") + pck_file + "\nSet correct encryption key and try again.", RTR("Read PCK")); } else { - show_warning(RTR("Error opening PCK file: ") + p_path, RTR("Read PCK")); + show_warning(RTR("Error opening PCK file: ") + pck_file, RTR("Read PCK")); } return; } @@ -787,13 +787,13 @@ void GodotREEditor::_pck_select_request(const String &p_path) { pck_dialog->add_file(file->get_path(), file->get_size(), icon, error_string, file->is_malformed(), file->is_encrypted()); } String user_desktop = OS::get_singleton()->get_system_dir(OS::SYSTEM_DIR_DESKTOP); - String proj_name = p_path.get_file().get_basename(); + String proj_name = pck_file.get_file().get_basename(); pck_dialog->set_target_dir(user_desktop.path_join(proj_name)); pck_dialog->popup_centered(Size2(600, 400)); } void GodotREEditor::_pck_unload() { - GDRESettings::get_singleton()->unload_pack(); + GDRESettings::get_singleton()->unload_project(); } void GodotREEditor::_pck_extract_files() { @@ -1741,7 +1741,7 @@ String GodotREEditorStandalone::get_version() { return String(GDRE_VERSION); } -void GodotREEditorStandalone::pck_select_request(const String &p_path) { +void GodotREEditorStandalone::pck_select_request(const Vector &p_path) { if (editor_ctx) { editor_ctx->_pck_select_request(p_path); } diff --git a/editor/gdre_editor.h b/editor/gdre_editor.h index 8fb0afd63..2312ddcbd 100644 --- a/editor/gdre_editor.h +++ b/editor/gdre_editor.h @@ -130,7 +130,7 @@ class GodotREEditor : public Node { void _compile_files(); void _compile_process(); - void _pck_select_request(const String &p_path); + void _pck_select_request(const Vector &p_paths); void _pck_unload(); void _pck_extract_files(); void _pck_extract_files_process(); @@ -213,7 +213,7 @@ class GodotREEditorStandalone : public Control { public: void show_about_dialog(); - void pck_select_request(const String &p_path); + void pck_select_request(const Vector &p_path); void _write_log_message(String p_message); String get_version(); diff --git a/exporters/translation_exporter.cpp b/exporters/translation_exporter.cpp index cf32e53ab..744dcc4ca 100644 --- a/exporters/translation_exporter.cpp +++ b/exporters/translation_exporter.cpp @@ -90,7 +90,7 @@ Ref TranslationExporter::export_resource(const String &output_dir, } } else { // We have a real translation class, get the keys - if (locale == default_locale) { + if (locale.to_lower() == default_locale.to_lower()) { List key_list; tr->get_message_list(&key_list); for (auto key : key_list) { @@ -103,7 +103,7 @@ Ref TranslationExporter::export_resource(const String &output_dir, messages.push_back(values[i]); } } - if (locale == default_locale) { + if (locale.to_lower() == default_locale.to_lower()) { default_messages = messages; default_translation = tr; } diff --git a/standalone/gdre_main.gd b/standalone/gdre_main.gd index 12df63290..ed1ff7507 100644 --- a/standalone/gdre_main.gd +++ b/standalone/gdre_main.gd @@ -16,7 +16,7 @@ func test_text_to_bin(txt_to_bin: String, output_dir: String): func _on_re_editor_standalone_dropped_files(files: PackedStringArray): if files.size() == 0: return - $re_editor_standalone.pck_select_request(files[0]) + $re_editor_standalone.pck_select_request(files) func _on_re_editor_standalone_write_log_message(message): $log_window.text += message @@ -60,6 +60,9 @@ func is_new_version(new_version: String): var curr_semver = SemVer.parse_semver(curr_version) var new_semver = SemVer.parse_semver(new_version) if curr_semver == null or new_semver == null: + print("Error: invalid semver format") + print("Current version: " + curr_version) + print("New version: " + new_version) return false if new_semver.gt(curr_semver): return true @@ -76,7 +79,7 @@ func _on_version_check_completed(_result, response_code, _headers, body): var prerelease = json["prerelease"] var curr_version = GDRESettings.get_gdre_version() - if draft or prerelease or not is_new_version(checked_version): + if draft or (prerelease and not ("-" in curr_version)) or not is_new_version(checked_version): return var update_str = "Update available! Click here! " + curr_version @@ -124,7 +127,7 @@ func save_config(): func handle_quit(save_cfg = true): if GDRESettings.is_pack_loaded(): - GDRESettings.unload_pack() + GDRESettings.unload_project() if save_cfg: var ret = save_config() if ret != OK and ret != ERR_DOES_NOT_EXIST: @@ -318,20 +321,32 @@ func normalize_cludes(cludes: PackedStringArray, dir = "res://") -> PackedString new_cludes.append(clude.simplify_path()) return new_cludes -func recovery( input_file:String, +func recovery( input_files:PackedStringArray, output_dir:String, enc_key:String, extract_only: bool, ignore_checksum_errors: bool = false, excludes: PackedStringArray = [], includes: PackedStringArray = []): + var _new_files = [] + for file in input_files: + file = get_cli_abs_path(file) + var _files = Glob.rglob(file) + if _files.size() > 0: + _new_files.append_array(_files) + else: + print_usage() + print("Error: failed to locate " + file) + return + print("Input files: ", str(_new_files)) + input_files = _new_files + var input_file = input_files[0] var da:DirAccess var is_dir:bool = false var err: int = OK var parent_dir = "res://" # get the current time var start_time = Time.get_ticks_msec() - input_file = get_cli_abs_path(input_file) if output_dir == "": output_dir = input_file.get_basename() if output_dir.get_extension(): @@ -348,6 +363,10 @@ func recovery( input_file:String, return #directory if da.dir_exists(input_file): + if input_files.size() > 1: + print_usage() + print("Error: cannot specify multiple directories") + return if input_file.get_extension().to_lower() == "app": is_dir = false elif !da.dir_exists(input_file.path_join(".import")) && !da.dir_exists(input_file.path_join(".godot")): @@ -371,10 +390,10 @@ func recovery( input_file:String, print("Error: failed to set key!") return - err = GDRESettings.load_pack(input_file, extract_only) + err = GDRESettings.load_project(input_files, extract_only) if (err != OK): print_usage() - print("Error: failed to open " + input_file) + print("Error: failed to open ", (input_files)) return print("Successfully loaded PCK!") @@ -540,8 +559,8 @@ func bin_to_text(files: PackedStringArray, output_dir: String): importer.convert_res_bin_2_txt(output_dir, file, dst_file) func handle_cli(args: PackedStringArray) -> bool: - var input_extract_file:String = "" - var input_file: String = "" + var input_extract_file:PackedStringArray = [] + var input_file:PackedStringArray = [] var output_dir: String = "" var enc_key: String = "" var txt_to_bin = PackedStringArray() @@ -549,8 +568,7 @@ func handle_cli(args: PackedStringArray) -> bool: var ignore_md5: bool = false var compile_files = PackedStringArray() var bytecode_version: String = "" - var main_args_cnt = 0 - var compile_cnt = 0 + var main_cmds = {} var excludes: PackedStringArray = [] var includes: PackedStringArray = [] if (args.size() == 0): @@ -578,15 +596,17 @@ func handle_cli(args: PackedStringArray) -> bool: print_version() return true elif arg.begins_with("--extract"): - input_extract_file = get_arg_value(arg).simplify_path() - main_args_cnt += 1 + input_extract_file.append(get_arg_value(arg).simplify_path()) + main_cmds["extract"] = true elif arg.begins_with("--recover"): - input_file = get_arg_value(arg).simplify_path() - main_args_cnt += 1 + input_file.append(get_arg_value(arg).simplify_path()) + main_cmds["recover"] = true elif arg.begins_with("--txt-to-bin"): txt_to_bin.append(get_arg_value(arg).simplify_path()) + main_cmds["txt-to-bin"] = true elif arg.begins_with("--bin-to-txt"): bin_to_txt.append(get_arg_value(arg).simplify_path()) + main_cmds["bin-to-txt"] = true elif arg.begins_with("--output-dir"): output_dir = get_arg_value(arg).simplify_path() elif arg.begins_with("--scripts-only"): @@ -606,8 +626,7 @@ func handle_cli(args: PackedStringArray) -> bool: elif arg.begins_with("--bytecode"): bytecode_version = get_arg_value(arg) elif arg.begins_with("--compile"): - if compile_files.size() == 0: - main_args_cnt += 1 + main_cmds["compile"] = true compile_files.append(get_arg_value(arg)) elif arg.begins_with("--exclude"): excludes.append(get_arg_value(arg)) @@ -621,19 +640,19 @@ func handle_cli(args: PackedStringArray) -> bool: print_usage() print(last_error) return true - if main_args_cnt > 1: + if main_cmds.size() > 1: print_usage() print("ERROR: invalid option! Must specify only one of " + ", ".join(MAIN_COMMANDS)) return true if compile_files.size() > 0: compile(compile_files, bytecode_version, output_dir) - elif input_file != "": + elif not input_file.is_empty(): recovery(input_file, output_dir, enc_key, false, ignore_md5, excludes, includes) - GDRESettings.unload_pack() + GDRESettings.unload_project() close_log() - elif input_extract_file != "": + elif not input_extract_file.is_empty(): recovery(input_extract_file, output_dir, enc_key, true, ignore_md5, excludes, includes) - GDRESettings.unload_pack() + GDRESettings.unload_project() close_log() elif txt_to_bin.is_empty() == false: text_to_bin(txt_to_bin, output_dir) diff --git a/utility/gdre_settings.cpp b/utility/gdre_settings.cpp index 09b090a3e..8603ed32e 100644 --- a/utility/gdre_settings.cpp +++ b/utility/gdre_settings.cpp @@ -1,4 +1,5 @@ #include "gdre_settings.h" +#include "bytecode/bytecode_base.h" #include "bytecode/bytecode_tester.h" #include "compat/resource_compat_binary.h" #include "compat/resource_loader_compat.h" @@ -25,6 +26,7 @@ #include "modules/regex/regex.h" #include "servers/rendering_server.h" #include "utility/glob.h" +#include #if defined(WINDOWS_ENABLED) #include @@ -268,35 +270,33 @@ String GDRESettings::get_encryption_key_string() { return enc_key_str; } bool GDRESettings::is_pack_loaded() const { - return current_pack.is_valid(); + return current_project.is_valid(); } void GDRESettings::add_pack_file(const Ref &f_info) { file_map.insert(f_info->path, f_info); } bool GDRESettings::has_valid_version() const { - return is_pack_loaded() && current_pack->version.is_valid() && current_pack->version->is_valid_semver(); + return is_pack_loaded() && current_project->version.is_valid() && current_project->version->is_valid_semver(); } + GDRESettings::PackInfo::PackType GDRESettings::get_pack_type() const { - return is_pack_loaded() ? current_pack->type : PackInfo::UNKNOWN; + return is_pack_loaded() ? current_project->type : PackInfo::UNKNOWN; } String GDRESettings::get_pack_path() const { - return is_pack_loaded() ? current_pack->pack_file : ""; -} -uint32_t GDRESettings::get_pack_format() const { - return is_pack_loaded() ? current_pack->fmt_version : 0; + return is_pack_loaded() ? current_project->pack_file : ""; } String GDRESettings::get_version_string() const { - return has_valid_version() ? current_pack->version->as_text() : String(); + return has_valid_version() ? current_project->version->as_text() : String(); } uint32_t GDRESettings::get_ver_major() const { - return has_valid_version() ? current_pack->version->get_major() : 0; + return has_valid_version() ? current_project->version->get_major() : 0; } uint32_t GDRESettings::get_ver_minor() const { - return has_valid_version() ? current_pack->version->get_minor() : 0; + return has_valid_version() ? current_project->version->get_minor() : 0; } uint32_t GDRESettings::get_ver_rev() const { - return has_valid_version() ? current_pack->version->get_patch() : 0; + return has_valid_version() ? current_project->version->get_patch() : 0; } uint32_t GDRESettings::get_file_count() const { if (!is_pack_loaded()) { @@ -312,11 +312,11 @@ uint32_t GDRESettings::get_file_count() const { void GDRESettings::set_ver_rev(uint32_t p_rev) { if (is_pack_loaded()) { if (!has_valid_version()) { - current_pack->version = Ref(memnew(GodotVer(0, 0, p_rev))); + current_project->version = Ref(memnew(GodotVer(0, 0, p_rev))); } else { - current_pack->version->set_patch(p_rev); - if (current_pack->version->get_build_metadata() == "x") { - current_pack->version->set_build_metadata(""); + current_project->version->set_patch(p_rev); + if (current_project->version->get_build_metadata() == "x") { + current_project->version->set_build_metadata(""); } } } @@ -331,16 +331,15 @@ bool GDRESettings::is_project_config_loaded() const { if (!is_pack_loaded()) { return false; } - bool is_loaded = current_pack->pcfg->is_loaded(); + bool is_loaded = current_project->pcfg->is_loaded(); return is_loaded; } void GDRESettings::remove_current_pack() { - current_pack = Ref(); + current_project = Ref(); packs.clear(); file_map.clear(); import_files.clear(); - code_files.clear(); remap_iinfo.clear(); reset_encryption_key(); } @@ -373,10 +372,11 @@ Error GDRESettings::fix_patch_number() { return OK; } // This is only implemented for 3.1 and 2.1; they started writing the correct patch number to the pck in 3.2 - if (!((get_ver_major() == 3 || get_ver_major() == 2) && get_ver_minor() == 1)) { + if (!((get_ver_major() == 3 || get_ver_major() == 2) && get_ver_minor() == 1 && get_ver_rev() == 0)) { return OK; } - auto revision = BytecodeTester::test_files(code_files, get_ver_major(), get_ver_minor()); + auto bytecode_files = get_file_list({ "*.gdc", "*.gde" }); + auto revision = BytecodeTester::test_files(bytecode_files, get_ver_major(), get_ver_minor()); switch (revision) { case 0xed80f45: // 2.1.{3-6} set_ver_rev(6); @@ -394,7 +394,7 @@ Error GDRESettings::fix_patch_number() { set_ver_rev(1); break; case 0x1ca61a3: // 3.1.beta - current_pack->version = GodotVer::parse("3.1.0-beta5"); + current_project->version = GodotVer::parse("3.1.0-beta5"); break; default: ERR_FAIL_COND_V_MSG(true, ERR_CANT_RESOLVE, "Could not determine patch number!"); @@ -433,34 +433,11 @@ Error GDRESettings::load_dir(const String &p_path) { pckinfo.instantiate(); pckinfo->init( p_path, Ref(memnew(GodotVer)), 1, 0, 0, pa.size(), PackInfo::DIR); - // Need to get version number from binary resources - add_pack_info(pckinfo); - Error err = get_version_from_bin_resources(); - err = load_import_files(); - ERR_FAIL_COND_V_MSG(err, err, "FATAL ERROR: Could not load imported binary files!"); - // this is a catastrophic failure, unload the pack - if (err) { - unload_pack(); - ERR_FAIL_V_MSG(err, "FATAL ERROR: Can't determine engine version of project directory!"); - } - if (!pack_has_project_config()) { - WARN_PRINT("Could not find project configuration in directory, may be a seperate resource directory..."); - } else { - err = load_project_config(); - ERR_FAIL_COND_V_MSG(err, err, "FATAL ERROR: Can't open project config!"); - } - err = fix_patch_number(); - if (err) { // this only fails on 2.1 and 3.1, where it's crucial to determine the patch number; if this fails, we can't continue - unload_pack(); - ERR_FAIL_V_MSG(err, "FATAL ERROR: Could not determine patch number to decompile scripts!"); - } - load_pack_uid_cache(); return OK; } Error GDRESettings::unload_dir() { - remove_current_pack(); ProjectSettings *settings_singleton = ProjectSettings::get_singleton(); GDREPackSettings *new_singleton = static_cast(settings_singleton); new_singleton->set_resource_path(gdre_resource_path); @@ -509,12 +486,33 @@ Error check_embedded(String &p_path) { return OK; } -Error GDRESettings::load_pack(const String &p_path, bool _cmd_line_extract) { +Error GDRESettings::load_pck(const String &p_path) { + // Check if the path is already loaded + for (const auto &pack : packs) { + if (pack->pack_file == p_path) { + return ERR_ALREADY_IN_USE; + } + } + print_line("Opening file: " + p_path); + Error err = GDREPackedData::get_singleton()->add_pack(p_path, false, 0); + ERR_FAIL_COND_V_MSG(err, err, "FATAL ERROR: Can't open pack!"); + ERR_FAIL_COND_V_MSG(!is_pack_loaded(), ERR_FILE_CANT_READ, "FATAL ERROR: loaded project pack, but didn't load files from it!"); + return OK; +} + +Error GDRESettings::load_project(const Vector &p_paths, bool _cmd_line_extract) { if (is_pack_loaded()) { return ERR_ALREADY_IN_USE; } - String path = p_path; + if (p_paths.is_empty()) { + ERR_FAIL_V_MSG(ERR_FILE_NOT_FOUND, "No valid paths provided!"); + } + String p_path = p_paths[0]; + Error err = ERR_CANT_OPEN; if (DirAccess::exists(p_path)) { + if (p_paths.size() > 1) { + ERR_FAIL_V_MSG(ERR_FILE_NOT_FOUND, "Cannot specify multiple directories!"); + } // This may be a ".app" bundle, so we need to check if it's a valid Godot app // and if so, load the pck from inside the bundle if (p_path.get_extension().to_lower() == "app") { @@ -522,42 +520,60 @@ Error GDRESettings::load_pack(const String &p_path, bool _cmd_line_extract) { if (DirAccess::exists(resources_path)) { auto list = gdreutil::get_recursive_dir_list(resources_path, { "*.pck" }, true); if (!list.is_empty()) { - if (list.size() > 1) { - WARN_PRINT("Found multiple pck files in bundle, using first one!!!"); - } - return load_pack(list[0]); + return load_project(list, _cmd_line_extract); } else { ERR_FAIL_V_MSG(ERR_FILE_NOT_FOUND, "Can't find pck file in .app bundle!"); } } } - return load_dir(p_path); - } - print_line("Opening file: " + path); - Error err = check_embedded(path); - if (err != OK) { - String parent_path = path.get_base_dir(); - if (parent_path.is_empty()) { - parent_path = GDRESettings::get_exec_dir(); - } - if (parent_path.get_file().to_lower() == "macos") { - // we want to get ../Resources - parent_path = parent_path.get_base_dir().path_join("Resources"); - String pck_path = parent_path.path_join(path.get_file().get_basename() + ".pck"); - if (FileAccess::exists(pck_path)) { - path = pck_path; - err = OK; + err = load_dir(p_path); + ERR_FAIL_COND_V_MSG(err, err, "FATAL ERROR: Can't load project directory!"); + } else { + for (auto path : p_paths) { + print_line("Opening file: " + path); + if (check_embedded(path) != OK) { + String new_path = path; + String parent_path = path.get_base_dir(); + if (parent_path.is_empty()) { + parent_path = GDRESettings::get_exec_dir(); + } + if (parent_path.get_file().to_lower() == "macos") { + // we want to get ../Resources + parent_path = parent_path.get_base_dir().path_join("Resources"); + String pck_path = parent_path.path_join(path.get_file().get_basename() + ".pck"); + if (FileAccess::exists(pck_path)) { + new_path = pck_path; + err = OK; + } + if (p_paths.has(new_path)) { + // we already tried this path + WARN_PRINT("EXE does not have an embedded pck, not loading " + path); + continue; + } + } + if (err != OK) { + String pck_path = path.get_basename() + ".pck"; + bool only_1_path = p_paths.size() == 1; + bool already_has_path = p_paths.has(pck_path); + bool exists = FileAccess::exists(pck_path); + if (!only_1_path && (already_has_path || !exists)) { + // we already tried this path + WARN_PRINT("EXE does not have an embedded pck, not loading " + path); + continue; + } + ERR_FAIL_COND_V_MSG(!exists, err, "Can't find embedded pck file in executable and cannot find pck file in same directory!"); + new_path = pck_path; + } + path = new_path; + WARN_PRINT("Could not find embedded pck in EXE, found pck file, loading from: " + path); + } + err = load_pck(path); + if (err) { + unload_project(); + ERR_FAIL_COND_V_MSG(err, err, "FATAL ERROR: Can't open pack file: " + path); } } - if (err != OK) { - String pck_path = path.get_basename() + ".pck"; - ERR_FAIL_COND_V_MSG(!FileAccess::exists(pck_path), err, "Can't find embedded pck file in executable and cannot find pck file in same directory!"); - path = pck_path; - } - WARN_PRINT("Could not find embedded pck in EXE, found pck file, loading from: " + path); } - err = GDREPackedData::get_singleton()->add_pack(path, false, 0); - ERR_FAIL_COND_V_MSG(err, err, "FATAL ERROR: Can't open pack!"); 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. @@ -565,23 +581,11 @@ Error GDRESettings::load_pack(const String &p_path, bool _cmd_line_extract) { } if (!has_valid_version()) { - // check if this is sonic colors unlimited - if (path.get_file().begins_with("sonic")) { - // get the other files that are like sonic*.pck - Vector sonic_files = Glob::glob(path.get_base_dir().path_join("sonic*.pck")); - for (const String &sonic_file : sonic_files) { - if (sonic_file.to_lower() != path.to_lower()) { - Error err = GDREPackedData::get_singleton()->add_pack(sonic_file, false, 0); - if (err) { - WARN_PRINT("Failed to load " + sonic_file); - } - } - } - } + // We need to get the version from the binary resources. err = get_version_from_bin_resources(); // this is a catastrophic failure, unload the pack if (err) { - unload_pack(); + unload_project(); ERR_FAIL_V_MSG(err, "FATAL ERROR: Can't determine engine version of project pack!"); } } @@ -598,7 +602,7 @@ Error GDRESettings::load_pack(const String &p_path, bool _cmd_line_extract) { err = fix_patch_number(); if (err) { // this only fails on 2.1 and 3.1, where it's crucial to determine the patch number; if this fails, we can't continue - unload_pack(); + unload_project(); ERR_FAIL_V_MSG(err, "FATAL ERROR: Could not determine patch number to decompile scripts, please report this on the GitHub page!"); } load_pack_uid_cache(); @@ -613,6 +617,38 @@ Error GDRESettings::get_version_from_bin_resources() { int i; int version_from_dir = get_ver_major_from_dir(); + + // only test the bytecode on non-encrypted 3.x files + Vector bytecode_files = get_file_list({ "*.gdc" }); + Vector> decomps; + + auto check_if_same_minor_major = [&](Ref version, Ref max_ver) { + if (!(max_ver->get_major() == version->get_major() && max_ver->get_minor() == version->get_minor())) { + return false; + } + return true; + }; + + auto do_thing = [&]() { + if (decomps.size() == 1) { + auto version = decomps[0]->get_godot_ver(); + auto max_version = decomps[0]->get_max_godot_ver(); + if (version->get_major() != 4 && (max_version.is_null() || check_if_same_minor_major(version, max_version))) { + current_project->version = max_version.is_valid() ? max_version : version; + return true; + } + } + return false; + }; + + if (!bytecode_files.is_empty()) { + decomps = BytecodeTester::get_possible_decomps(bytecode_files); + ERR_FAIL_COND_V_MSG(decomps.is_empty(), ERR_FILE_NOT_FOUND, "Cannot determine version from bin resources: decomp testing failed!"); + if (do_thing()) { + return OK; + } + } + List exts; ResourceCompatLoader::get_base_extensions(&exts, version_from_dir); Vector wildcards; @@ -668,8 +704,8 @@ Error GDRESettings::get_version_from_bin_resources() { ERR_FAIL_COND_V_MSG(ver_major == 0, ERR_CANT_ACQUIRE_RESOURCE, "Can't find version from directory!"); } - current_pack->version = GodotVer::create(ver_major, ver_minor, 0); - return OK; + current_project->version = GodotVer::create(ver_major, ver_minor, 0); + return fix_patch_number(); } Error GDRESettings::load_project_config() { @@ -678,10 +714,10 @@ Error GDRESettings::load_project_config() { ERR_FAIL_COND_V_MSG(is_project_config_loaded(), ERR_ALREADY_IN_USE, "Project config is already loaded!"); ERR_FAIL_COND_V_MSG(!pack_has_project_config(), ERR_FILE_NOT_FOUND, "Could not find project config!"); if (get_ver_major() == 2) { - err = current_pack->pcfg->load_cfb("res://engine.cfb", get_ver_major(), get_ver_minor()); + err = current_project->pcfg->load_cfb("res://engine.cfb", get_ver_major(), get_ver_minor()); ERR_FAIL_COND_V_MSG(err, err, "Failed to load project config!"); } else if (get_ver_major() == 3 || get_ver_major() == 4) { - err = current_pack->pcfg->load_cfb("res://project.binary", get_ver_major(), get_ver_minor()); + err = current_project->pcfg->load_cfb("res://project.binary", get_ver_major(), get_ver_minor()); ERR_FAIL_COND_V_MSG(err, err, "Failed to load project config!"); } else { ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, @@ -695,16 +731,16 @@ Error GDRESettings::save_project_config(const String &p_out_dir = "") { if (output_dir.is_empty()) { output_dir = project_path; } - return current_pack->pcfg->save_cfb(output_dir, get_ver_major(), get_ver_minor()); + return current_project->pcfg->save_cfb(output_dir, get_ver_major(), get_ver_minor()); } -Error GDRESettings::unload_pack() { +Error GDRESettings::unload_project() { if (!is_pack_loaded()) { return ERR_DOES_NOT_EXIST; } reset_uid_cache(); if (get_pack_type() == PackInfo::DIR) { - return unload_dir(); + unload_dir(); } remove_current_pack(); @@ -713,12 +749,16 @@ Error GDRESettings::unload_pack() { } void GDRESettings::add_pack_info(Ref packinfo) { + ERR_FAIL_COND_MSG(!packinfo.is_valid(), "Invalid pack info!"); packs.push_back(packinfo); - if (!current_pack.is_valid()) { // only set if we don't have a current pack - current_pack = packinfo; + if (!current_project.is_valid()) { // only set if we don't have a current pack + current_project = Ref(memnew(ProjectInfo)); + current_project->version = packinfo->version; + current_project->pack_file = packinfo->pack_file; + current_project->type = packinfo->type; } else { - if ((!current_pack->version.is_valid() || !current_pack->version->is_valid_semver()) && packinfo->version.is_valid() && packinfo->version->is_valid_semver()) { - current_pack = packinfo; + if (!current_project->version->eq(packinfo->version)) { + WARN_PRINT("Warning: Pack version mismatch!"); } } } @@ -981,11 +1021,11 @@ bool GDRESettings::has_any_remaps() const { if (remap_iinfo.size() > 0) { return true; } - if (current_pack->pcfg->is_loaded() && current_pack->pcfg->has_setting("path_remap/remapped_paths")) { + if (current_project->pcfg->is_loaded() && current_project->pcfg->has_setting("path_remap/remapped_paths")) { return true; } } else { // version 1-2 - if (current_pack->pcfg->is_loaded() && current_pack->pcfg->has_setting("remap/all")) { + if (current_project->pcfg->is_loaded() && current_project->pcfg->has_setting("remap/all")) { return true; } } @@ -1000,15 +1040,15 @@ Dictionary GDRESettings::get_remaps(bool include_imports) const { for (auto E : remap_iinfo) { ret[E.key] = E.value->get_path(); } - if (current_pack->pcfg->is_loaded() && current_pack->pcfg->has_setting("path_remap/remapped_paths")) { - PackedStringArray v3remaps = current_pack->pcfg->get_setting("path_remap/remapped_paths", PackedStringArray()); + if (current_project->pcfg->is_loaded() && current_project->pcfg->has_setting("path_remap/remapped_paths")) { + PackedStringArray v3remaps = current_project->pcfg->get_setting("path_remap/remapped_paths", PackedStringArray()); for (int i = 0; i < v3remaps.size(); i += 2) { ret[v3remaps[i]] = v3remaps[i + 1]; } } } else { - if (current_pack->pcfg->is_loaded() && current_pack->pcfg->has_setting("remap/all")) { - PackedStringArray v2remaps = current_pack->pcfg->get_setting("remap/all", PackedStringArray()); + if (current_project->pcfg->is_loaded() && current_project->pcfg->has_setting("remap/all")) { + PackedStringArray v2remaps = current_project->pcfg->get_setting("remap/all", PackedStringArray()); for (int i = 0; i < v2remaps.size(); i += 2) { ret[v2remaps[i]] = v2remaps[i + 1]; } @@ -1065,8 +1105,8 @@ String GDRESettings::get_remap(const String &src) const { } } String setting = get_ver_major() < 3 ? "remap/all" : "path_remap/remapped_paths"; - if (is_project_config_loaded() && current_pack->pcfg->has_setting(setting)) { - PackedStringArray remaps = current_pack->pcfg->get_setting(setting, PackedStringArray()); + if (is_project_config_loaded() && current_project->pcfg->has_setting(setting)) { + PackedStringArray remaps = current_project->pcfg->get_setting(setting, PackedStringArray()); int idx = remaps.find(local_src); if (idx != -1 && idx + 1 < remaps.size()) { return remaps[idx + 1]; @@ -1091,8 +1131,8 @@ bool GDRESettings::has_remap(const String &src, const String &dst) const { } } String setting = get_ver_major() < 3 ? "remap/all" : "path_remap/remapped_paths"; - if (is_project_config_loaded() && current_pack->pcfg->has_setting(setting)) { - return has_old_remap(current_pack->pcfg->get_setting(setting, PackedStringArray()), local_src, local_dst); + if (is_project_config_loaded() && current_project->pcfg->has_setting(setting)) { + return has_old_remap(current_project->pcfg->get_setting(setting, PackedStringArray()), local_src, local_dst); } } return false; @@ -1106,7 +1146,7 @@ Error GDRESettings::add_remap(const String &src, const String &dst) { } ERR_FAIL_COND_V_MSG(!is_project_config_loaded(), ERR_DATABASE_CANT_READ, "project config not loaded!"); String setting = get_ver_major() < 3 ? "remap/all" : "path_remap/remapped_paths"; - PackedStringArray v2remaps = current_pack->pcfg->get_setting(setting, PackedStringArray()); + PackedStringArray v2remaps = current_project->pcfg->get_setting(setting, PackedStringArray()); String local_src = localize_path(src); String local_dst = localize_path(dst); int idx = v2remaps.find(local_src); @@ -1116,7 +1156,7 @@ Error GDRESettings::add_remap(const String &src, const String &dst) { v2remaps.push_back(local_src); v2remaps.push_back(local_dst); } - current_pack->pcfg->set_setting(setting, v2remaps); + current_project->pcfg->set_setting(setting, v2remaps); return OK; } @@ -1148,17 +1188,17 @@ Error GDRESettings::remove_remap(const String &src, const String &dst, const Str ERR_FAIL_V_MSG(ERR_DOES_NOT_EXIST, "Remap between" + src + " and " + dst + " does not exist!"); } String setting = get_ver_major() < 3 ? "remap/all" : "path_remap/remapped_paths"; - ERR_FAIL_COND_V_MSG(!current_pack->pcfg->has_setting(setting), ERR_DOES_NOT_EXIST, "Remap between" + src + " and " + dst + " does not exist!"); - PackedStringArray v2remaps = current_pack->pcfg->get_setting(setting, PackedStringArray()); + ERR_FAIL_COND_V_MSG(!current_project->pcfg->has_setting(setting), ERR_DOES_NOT_EXIST, "Remap between" + src + " and " + dst + " does not exist!"); + PackedStringArray v2remaps = current_project->pcfg->get_setting(setting, PackedStringArray()); String local_src = localize_path(src); String local_dst = localize_path(dst); if (has_old_remap(v2remaps, local_src, local_dst)) { v2remaps.erase(local_src); v2remaps.erase(local_dst); if (v2remaps.size()) { - err = current_pack->pcfg->set_setting("remap/all", v2remaps); + err = current_project->pcfg->set_setting("remap/all", v2remaps); } else { - err = current_pack->pcfg->remove_setting("remap/all"); + err = current_project->pcfg->remove_setting("remap/all"); } return err; } @@ -1171,18 +1211,18 @@ bool GDRESettings::has_project_setting(const String &p_setting) { WARN_PRINT("Attempted to check project setting " + p_setting + ", but no project config loaded"); return false; } - return current_pack->pcfg->has_setting(p_setting); + return current_project->pcfg->has_setting(p_setting); } Variant GDRESettings::get_project_setting(const String &p_setting) { ERR_FAIL_COND_V_MSG(!is_pack_loaded(), Variant(), "Pack not loaded!"); ERR_FAIL_COND_V_MSG(!is_project_config_loaded(), Variant(), "project config not loaded!"); - return current_pack->pcfg->get_setting(p_setting, Variant()); + return current_project->pcfg->get_setting(p_setting, Variant()); } String GDRESettings::get_project_config_path() { ERR_FAIL_COND_V_MSG(!is_project_config_loaded(), String(), "project config not loaded!"); - return current_pack->pcfg->get_cfg_path(); + return current_project->pcfg->get_cfg_path(); } String GDRESettings::get_log_file_path() { @@ -1248,7 +1288,7 @@ Error GDRESettings::load_pack_uid_cache(bool p_reset) { //"application/config/use_hidden_project_data_directory" if (is_project_config_loaded()) { // if this is set, we want to load the cache from the hidden directory - cache_file = current_pack->pcfg->get_setting("application/config/use_hidden_project_data_directory", true) ? cache_file : "res://godot/uid_cache.bin"; + cache_file = current_project->pcfg->get_setting("application/config/use_hidden_project_data_directory", true) ? cache_file : "res://godot/uid_cache.bin"; } Ref f = FileAccess::open(cache_file, FileAccess::READ); @@ -1320,7 +1360,6 @@ Error GDRESettings::load_import_files() { } else { ERR_FAIL_V_MSG(ERR_BUG, "Can't determine major version!"); } - code_files.append_array(get_file_list({ "*.gdc", "*.gde" })); Vector tokens; for (int i = 0; i < resource_files.size(); i++) { tokens.push_back({ resource_files[i], nullptr, (int)get_ver_major(), (int)get_ver_minor() }); @@ -1423,7 +1462,7 @@ Ref GDRESettings::get_import_info_by_dest(const String &p_path) { } Vector GDRESettings::get_code_files() { - return code_files; + return get_file_list({ "*.gdc", "*.gde" }); } bool GDRESettings::pack_has_project_config() { @@ -1464,8 +1503,8 @@ String GDRESettings::get_disclaimer_body() { } void GDRESettings::_bind_methods() { - ClassDB::bind_method(D_METHOD("load_pack", "p_path", "cmd_line_extract"), &GDRESettings::load_pack, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("unload_pack"), &GDRESettings::unload_pack); + ClassDB::bind_method(D_METHOD("load_project", "p_paths", "cmd_line_extract"), &GDRESettings::load_project, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("unload_project"), &GDRESettings::unload_project); ClassDB::bind_method(D_METHOD("get_gdre_resource_path"), &GDRESettings::get_gdre_resource_path); ClassDB::bind_method(D_METHOD("get_encryption_key"), &GDRESettings::get_encryption_key); ClassDB::bind_method(D_METHOD("get_encryption_key_string"), &GDRESettings::get_encryption_key_string); @@ -1480,7 +1519,6 @@ void GDRESettings::_bind_methods() { ClassDB::bind_method(D_METHOD("get_file_info_array", "filters"), &GDRESettings::get_file_info_array, DEFVAL(Vector())); ClassDB::bind_method(D_METHOD("get_pack_type"), &GDRESettings::get_pack_type); ClassDB::bind_method(D_METHOD("get_pack_path"), &GDRESettings::get_pack_path); - ClassDB::bind_method(D_METHOD("get_pack_format"), &GDRESettings::get_pack_format); ClassDB::bind_method(D_METHOD("get_version_string"), &GDRESettings::get_version_string); ClassDB::bind_method(D_METHOD("get_ver_major"), &GDRESettings::get_ver_major); ClassDB::bind_method(D_METHOD("get_ver_minor"), &GDRESettings::get_ver_minor); diff --git a/utility/gdre_settings.h b/utility/gdre_settings.h index 8a29b460e..67ca9f541 100644 --- a/utility/gdre_settings.h +++ b/utility/gdre_settings.h @@ -67,8 +67,6 @@ class GDRESettings : public Object { // copy the version, or set it to null if it's invalid if (godot_ver.is_valid() && godot_ver->is_valid_semver()) { version = GodotVer::create(godot_ver->get_major(), godot_ver->get_minor(), godot_ver->get_patch(), godot_ver->get_prerelease(), godot_ver->get_build_metadata()); - } else { - version.instantiate(); } fmt_version = fver; pack_flags = flags; @@ -82,16 +80,32 @@ class GDRESettings : public Object { } void set_project_config() { } + PackInfo() { + version.instantiate(); + pcfg.instantiate(); + } + }; + class ProjectInfo : public RefCounted { + GDCLASS(ProjectInfo, RefCounted); + + public: + Ref version; + Ref pcfg; + PackInfo::PackType type = PackInfo::PCK; + String pack_file; + ProjectInfo() { + pcfg.instantiate(); + } + // String project_path; }; private: Vector> packs; HashMap> file_map; - Ref current_pack = Ref(); + Ref current_project = Ref(); GDREPackedData *gdre_packeddata_singleton = nullptr; GDRELogger *logger; Array import_files; - Vector code_files; HashMap> remap_iinfo; String gdre_resource_path = ""; @@ -138,12 +152,16 @@ class GDRESettings : public Object { Error load_pack_uid_cache(bool p_reset = false); Error reset_uid_cache(); + static bool need_correct_patch(int ver_major, int ver_minor); + protected: static void _bind_methods(); public: - Error load_pack(const String &p_path, bool cmd_line_extract = false); - Error unload_pack(); + Error load_project(const Vector &p_paths, bool cmd_line_extract = false); + Error load_pck(const String &p_path); + + Error unload_project(); String get_gdre_resource_path() const; Vector get_encryption_key(); @@ -162,7 +180,6 @@ class GDRESettings : public Object { Vector> get_file_info_list(const Vector &filters = Vector()); PackInfo::PackType get_pack_type() const; String get_pack_path() const; - uint32_t get_pack_format() const; String get_version_string() const; uint32_t get_ver_major() const; uint32_t get_ver_minor() const; diff --git a/utility/import_exporter.cpp b/utility/import_exporter.cpp index f6d59be75..31c4567dd 100644 --- a/utility/import_exporter.cpp +++ b/utility/import_exporter.cpp @@ -222,14 +222,14 @@ Error ImportExporter::_export_imports(const String &p_out_dir, const Vector 0; // check if res://addons exists Ref res_da = DirAccess::open("res://"); Vector addon_first_level_dirs = Glob::glob("res://addons/*", true); if (res_da->dir_exists("res://addons")) { // Only recreate plugin configs if we are exporting files within the addons directory if (files_to_export.size() != 0) { - Vector addon_code_files = Glob::rglob_list({ "res://addons/**/*.gdc", "res://addons/**/*.gde", "res://addons/**/*.gd" }); + Vector addon_code_files = Glob::rglob_list({ "res://addons/**/*.gdc", "res://addons/**/*.gde" }); addon_first_level_dirs = Glob::dirs_in_names(files_to_export, addon_first_level_dirs); auto new_code_files = Glob::names_in_dirs(addon_code_files, addon_first_level_dirs); for (auto &code_file : new_code_files) { @@ -240,7 +240,7 @@ Error ImportExporter::_export_imports(const String &p_out_dir, const Vector 0) { + if (has_non_compiled_scripts || to_decompile.size() > 0) { decompile_scripts(output_dir, to_decompile); // This only works if we decompile the scripts first recreate_plugin_configs(output_dir, addon_first_level_dirs); @@ -263,7 +263,6 @@ Error ImportExporter::_export_imports(const String &p_out_dir, const Vector tokens; Vector non_multithreaded_tokens; Vector paths_to_export; - paths_to_export.resize(_files.size()); HashSet files_to_export_set = vector_to_hashset(files_to_export); for (int i = 0; i < _files.size(); i++) { Ref iinfo = _files[i]; @@ -310,7 +309,7 @@ Error ImportExporter::_export_imports(const String &p_out_dir, const Vectorget_path(); + paths_to_export.push_back(iinfo->get_path()); if (supports_multithreading) { tokens.push_back({ iinfo, nullptr, output_dir, supports_multithreading, opt_rewrite_imd_v2, opt_rewrite_imd_v3, opt_write_md5_files }); } else { @@ -318,7 +317,6 @@ Error ImportExporter::_export_imports(const String &p_out_dir, const Vector 0) { last_completed = -1; diff --git a/utility/pck_dumper.cpp b/utility/pck_dumper.cpp index 4e1df156c..9b0741beb 100644 --- a/utility/pck_dumper.cpp +++ b/utility/pck_dumper.cpp @@ -227,8 +227,6 @@ Error PckDumper::_pck_dump_to_dir( if (opt_multi_thread) { Vector tokens; Vector paths_to_extract; - tokens.resize(files_to_extract.is_empty() ? files.size() : files_to_extract.size()); - paths_to_extract.resize(files_to_extract.is_empty() ? files.size() : files_to_extract.size()); int actual = 0; HashSet files_to_extract_set; for (const String &f : files_to_extract) { @@ -240,11 +238,9 @@ Error PckDumper::_pck_dump_to_dir( } actual++; if (pr) { - paths_to_extract.write[i] = files.get(i)->get_path(); + paths_to_extract.push_back(files.get(i)->get_path()); } - tokens.write[i].file = files.get(i); - tokens.write[i].output_dir = dir; - tokens.write[i].err = OK; + tokens.push_back({ files.get(i), dir, OK }); } tokens.resize(actual); paths_to_extract.resize(actual);