From 8e74c1bd31acadb04c2600952bb00a6d2dc7d06d Mon Sep 17 00:00:00 2001 From: nikitalita <69168929+nikitalita@users.noreply.github.com> Date: Sat, 18 Jun 2022 22:20:59 -0700 Subject: [PATCH] Fixes for recovery on extracted folder --- README.md | 5 +- standalone/gdre_main.gd | 141 ++++++++++++-------------------- utility/gdre_cli_main.cpp | 14 ++++ utility/gdre_cli_main.h | 2 + utility/gdre_settings.cpp | 14 +++- utility/gdre_settings.h | 6 +- utility/import_exporter.cpp | 159 ++++++++++++++++++++++++++++++++---- utility/import_exporter.h | 8 +- utility/import_info.cpp | 1 + utility/import_info.h | 2 + utility/pcfg_loader.cpp | 16 +++- utility/pcfg_loader.h | 4 + 12 files changed, 257 insertions(+), 115 deletions(-) diff --git a/README.md b/README.md index 0cf4f7da..b61acb83 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,11 @@ This module has support for decompiling Godot 4.x, 3.x, and 2.x projects. - To perform full project recovery from the command line: ```bash -gdre_tools --recover=game.pck [--output-dir=game-extract/] +gdre_tools --recover=game.pck ``` +Optional arguments: + -`--output-dir=` : Output directory, defaults to `_extracted`, or the project directory if one is specified + -`--key=` : The Key to use if PAK/EXE is encrypted (hex string) Use the same Godot tools version that the original game was compiled in to edit the project. In order to detect this, see [To detect Godot version](#to-detect-godot-version) diff --git a/standalone/gdre_main.gd b/standalone/gdre_main.gd index 4a6e0777..35ffa7f8 100644 --- a/standalone/gdre_main.gd +++ b/standalone/gdre_main.gd @@ -1,7 +1,9 @@ extends Control -var ver_major = 0; -var ver_minor = 0; +var ver_major = 0 +var ver_minor = 0 +var main : GDRECLIMain + func _ready(): $menu_background/version_lbl.text = $re_editor_standalone.get_version() # test_functions() @@ -26,8 +28,8 @@ func get_arg_value(arg): func export_imports(output_dir:String): var importer:ImportExporter = ImportExporter.new() importer.load_import_files(output_dir, ver_major, ver_minor) + importer.decompile_scripts(output_dir) var arr = importer.get_import_files() - var failed_files = [] print("Number of import files: " + str(arr.size())) importer.export_imports(output_dir) importer.reset() @@ -54,13 +56,13 @@ func test_decomp(fname): print("error failed to save "+ f) -func dump_files(exe_file:String, output_dir:String, enc_key:String = "") -> int: +func dump_files(input_file:String, output_dir:String, enc_key:String = "") -> int: var err:int = OK; var pckdump = PckDumper.new() - print(exe_file) + print(input_file) if (enc_key != ""): pckdump.set_key(enc_key) - err = pckdump.load_pck(exe_file) + err = pckdump.load_pck(input_file) if err == OK: print("Successfully loaded PCK!") ver_major = pckdump.get_engine_version().split(".")[0].to_int() @@ -77,57 +79,6 @@ func dump_files(exe_file:String, output_dir:String, enc_key:String = "") -> int: print("error dumping to dir") pckdump.clear_data() return err - - var decomp; - # TODO: instead of doing this, run the detect bytecode script - if version.begins_with("1.0"): - decomp = GDScriptDecomp_e82dc40.new() - elif version.begins_with("1.1"): - decomp = GDScriptDecomp_65d48d6.new() - elif version.begins_with("2.0"): - decomp = GDScriptDecomp_23441ec.new() - elif version.begins_with("2.1"): - decomp = GDScriptDecomp_ed80f45.new() - elif version.begins_with("3.0"): - decomp = GDScriptDecomp_054a2ac.new() - elif version.begins_with("3.1"): - decomp = GDScriptDecomp_514a3fb.new() - elif version.begins_with("3.2"): - decomp = GDScriptDecomp_5565f55.new() - elif version.begins_with("3.3"): - decomp = GDScriptDecomp_5565f55.new() - elif version.begins_with("3.4"): - decomp = GDScriptDecomp_5565f55.new() - elif version.begins_with("3.5"): - decomp = GDScriptDecomp_5565f55.new() - else: - print("Unknown version, no decomp") - pckdump.clear_data() - return err - - print("Script version "+ version.substr(0,3)+".x detected") - - for f in pckdump.get_loaded_files(): - var da:Directory = Directory.new() - da.open(output_dir) - f = f.replace("res://", "") - if f.get_extension() == "gdc": - print("decompiling " + f) - if decomp.decompile_byte_code(output_dir.plus_file(f)) != OK: - print("error decompiling " + f) - else: - var text = decomp.get_script_text() - var gdfile:File = File.new() - if gdfile.open(output_dir.plus_file(f.replace(".gdc",".gd")), File.WRITE) == OK: - gdfile.store_string(text) - gdfile.close() - da.remove(f) - if da.file_exists(f.replace(".gdc",".gd.remap")): - da.remove(f.replace(".gdc",".gd.remap")) - print("successfully decompiled " + f) - else: - print("error failed to save "+ f) - decomp.free() else: print("ERROR: failed to load exe") pckdump.clear_data() @@ -178,18 +129,55 @@ func print_usage(): print("\nGeneral options:") print(" -h, --help: Display this help message") print("\nFull Project Recovery options:") - print("Usage: GDRE_Tools.exe --headless --recover= --output-dir= [options]") + print("Usage: GDRE_Tools.exe --headless --recover= [options]") print("") print("--recover=\t\tThe Pak, EXE, or extracted project directory to perform full project recovery on") - print("--output-dir=\t\tOutput directory") print("\nOptions:\n") print("--key=\t\tThe Key to use if PAK/EXE is encrypted (hex string)") + print("--output-dir=\t\tOutput directory, defaults to , or the project directory if one of specified") + #print("View Godot assets, extract Godot PAK files, and export Godot projects") +func recovery(input_file:String, output_dir:String, enc_key:String): + var da:Directory = Directory.new() + input_file = main.get_cli_abs_path(input_file) + print(input_file) + if output_dir == "": + output_dir = input_file.get_basename() + if output_dir.get_extension(): + output_dir += "_recovery" + else: + output_dir = main.get_cli_abs_path(output_dir) + #debugging + #print_import_info(output_dir) + #print_import_info_from_pak(input_file) + #actually an directory, just run export_imports + if da.dir_exists(input_file): + + if !da.dir_exists(input_file.plus_file(".import")): + print("Error: This does not appear to be a project directory") + else: + if output_dir != input_file: + if main.copy_dir(input_file, output_dir) != OK: + print("Error: failed to copy " + input_file + " to " + output_dir) + return + main.open_log(output_dir) + export_imports(output_dir) + elif da.file_exists(input_file): + main.open_log(output_dir) + var err = dump_files(input_file, output_dir, enc_key) + if (err == OK): + export_imports(output_dir) + else: + print("Error: failed to extract PAK file, not exporting assets") + else: + print("Error: failed to load " + input_file) + + func handle_cli(): var args = OS.get_cmdline_args() - var exe_file:String = "" + var input_file:String = "" var output_dir: String = "" var enc_key: String = "" for i in range(args.size()): @@ -198,39 +186,14 @@ func handle_cli(): print_usage() get_tree().quit() if arg.begins_with("--recover"): - exe_file = normalize_path(get_arg_value(arg)) + input_file = normalize_path(get_arg_value(arg)) elif arg.begins_with("--output-dir"): output_dir = normalize_path(get_arg_value(arg)) elif arg.begins_with("--key"): enc_key = get_arg_value(arg) - if exe_file != "": - var main = GDRECLIMain.new() - var da:Directory = Directory.new() - exe_file = main.get_cli_abs_path(exe_file) - print(exe_file) - if output_dir == "": - output_dir = exe_file.get_basename() + "_recovery" - else: - output_dir = main.get_cli_abs_path(output_dir) - main.open_log(output_dir) - #debugging - #print_import_info(output_dir) - #print_import_info_from_pak(exe_file) - #actually an directory, just run export_imports - if da.dir_exists(exe_file): - if !da.dir_exists(exe_file.plus_file(".import")): - print("Error: This does not appear to be a project directory") - else: - export_imports(exe_file) - elif da.file_exists(exe_file): - var err = dump_files(exe_file, output_dir, enc_key) - if (err == OK): - export_imports(output_dir) - else: - print("Error: failed to extract PAK file, not exporting assets") - else: - print("Error: failed to load " + exe_file) - + if input_file != "": + main = GDRECLIMain.new() + recovery(input_file, output_dir, enc_key) main.close_log() get_tree().quit() else: diff --git a/utility/gdre_cli_main.cpp b/utility/gdre_cli_main.cpp index 101ea9f7..2b189a5c 100644 --- a/utility/gdre_cli_main.cpp +++ b/utility/gdre_cli_main.cpp @@ -5,6 +5,13 @@ Error GDRECLIMain::open_log(const String &path) { } Error GDRECLIMain::close_log() { + String path = GDRESettings::get_singleton()->get_log_file_path(); + if (path == "") { + return OK; + } + GDRESettings::get_singleton()->close_log_file(); + print_line("Log file written to " + path); + print_line("Please include this file when reporting an issue!"); return GDRESettings::get_singleton()->close_log_file(); } @@ -15,8 +22,15 @@ String GDRECLIMain::get_cli_abs_path(const String &path) { String exec_path = GDRESettings::get_singleton()->get_exec_dir(); return exec_path.plus_file(path).replace("\\", "/"); } +Error GDRECLIMain::copy_dir(const String &src_path, const String dst_path) { + Ref f = DirAccess::open(src_path); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Can't open " + src_path); + Error err = f->copy_dir(src_path, dst_path); + return err; +} void GDRECLIMain::_bind_methods() { + ClassDB::bind_method(D_METHOD("copy_dir"), &GDRECLIMain::copy_dir); ClassDB::bind_method(D_METHOD("open_log"), &GDRECLIMain::open_log); ClassDB::bind_method(D_METHOD("close_log"), &GDRECLIMain::close_log); ClassDB::bind_method(D_METHOD("get_cli_abs_path"), &GDRECLIMain::get_cli_abs_path); diff --git a/utility/gdre_cli_main.h b/utility/gdre_cli_main.h index 0f5fb23b..4a57213a 100644 --- a/utility/gdre_cli_main.h +++ b/utility/gdre_cli_main.h @@ -16,6 +16,8 @@ class GDRECLIMain : public RefCounted { Error open_log(const String &path); Error close_log(); String get_cli_abs_path(const String &path); + Error copy_dir(const String &src_path, const String dst_path); + GDRECLIMain() { gdres_singleton = memnew(GDRESettings); } diff --git a/utility/gdre_settings.cpp b/utility/gdre_settings.cpp index bdf7e23f..36fc30c2 100644 --- a/utility/gdre_settings.cpp +++ b/utility/gdre_settings.cpp @@ -410,6 +410,10 @@ void GDRELogger::logv(const char *p_format, va_list p_list, bool p_err) { } } +String GDRESettings::get_log_file_path() { + return logger->get_path(); +} + Error GDRESettings::open_log_file(const String &output_dir) { String logfile = output_dir.plus_file("gdre_export.log"); Error err = logger->open_file(logfile); @@ -423,17 +427,18 @@ Error GDRESettings::close_log_file() { return OK; } -Error GDRELogger::open_file(const String &base_path) { +Error GDRELogger::open_file(const String &p_base_path) { if (file.is_valid()) { return ERR_ALREADY_IN_USE; } Ref da = DirAccess::create(DirAccess::ACCESS_USERDATA); if (da.is_valid()) { - da->make_dir_recursive(base_path.get_base_dir()); + da->make_dir_recursive(p_base_path.get_base_dir()); } Error err; - file = FileAccess::open(base_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(file.is_null(), err, "Failed to open log file " + base_path + " for writing."); + file = FileAccess::open(p_base_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V_MSG(file.is_null(), err, "Failed to open log file " + p_base_path + " for writing."); + base_path = p_base_path; return OK; } @@ -441,6 +446,7 @@ void GDRELogger::close_file() { if (file.is_valid()) { file->flush(); file = Ref(); + base_path = ""; } } diff --git a/utility/gdre_settings.h b/utility/gdre_settings.h index dfb2aa25..4b0f0609 100644 --- a/utility/gdre_settings.h +++ b/utility/gdre_settings.h @@ -39,11 +39,11 @@ class GDREOS : public T { }; class GDRELogger : public Logger { - String base_path; - Ref file; + String base_path; public: + String get_path() { return base_path; }; GDRELogger(); Error open_file(const String &p_base_path); void close_file(); @@ -134,6 +134,8 @@ class GDRESettings : public Object { String get_res_path(const String &p_path, const String &resource_dir = ""); bool has_res_path(const String &p_path, const String &resource_dir = ""); Error open_log_file(const String &output_dir); + String get_log_file_path(); + bool is_fs_path(const String &p_path); Error close_log_file(); String get_cwd() { return GDRESettings::_get_cwd(); }; diff --git a/utility/import_exporter.cpp b/utility/import_exporter.cpp index a3d5ecf6..4fdaa0f0 100644 --- a/utility/import_exporter.cpp +++ b/utility/import_exporter.cpp @@ -24,6 +24,27 @@ Array ImportExporter::get_import_files() { return files; } +bool ImportExporter::check_if_dir_is_v4(const String &dir) { + Vector wildcards; + // these are files that will only show up in version 4 + wildcards.push_back("*.ctex"); + if (gdreutil::get_recursive_dir_list(dir, wildcards).size() > 0) { + return true; + } else { + return false; + } +} + +bool ImportExporter::check_if_dir_is_v3(const String &dir) { + Vector wildcards; + // these are files that will only show up in version 3 + wildcards.push_back("*.stex"); + if (gdreutil::get_recursive_dir_list(dir, wildcards).size() > 0) { + return true; + } else { + return false; + } +} bool ImportExporter::check_if_dir_is_v2(const String &dir) { Vector wildcards; @@ -54,11 +75,10 @@ Vector ImportExporter::get_v2_wildcards() { wildcards.push_back("*.xl"); wildcards.push_back("*.cbm"); wildcards.push_back("*.pbm"); - wildcards.push_back("*engine.cfb"); + wildcards.push_back("*.gdc"); return wildcards; } - Error ImportExporter::load_import_files(const String &dir, const uint32_t p_ver_major, const uint32_t p_ver_minor = 0) { project_dir = dir; Vector file_names; @@ -67,9 +87,38 @@ Error ImportExporter::load_import_files(const String &dir, const uint32_t p_ver_ if (dir != "" && !dir.begins_with("res://")) { GDRESettings::get_singleton()->set_project_path(dir); } + String bin_proj_file; + bool ver_major_not_input = false; + //TODO: Get version number from AndroidManifest.xml if this is an APK if (ver_major == 0) { - ver_major = check_if_dir_is_v2(dir) ? 2 : 3; // we just assume 3 for now + ver_major = check_if_dir_is_v2(dir) ? 2 : 0; + if (ver_major == 0) { + ver_major = check_if_dir_is_v3(dir) ? 3 : 0; + } + if (ver_major == 0) { + ver_major = check_if_dir_is_v4(dir) ? 4 : 0; + } + ERR_FAIL_COND_V_MSG(ver_major == 0, ERR_FILE_UNRECOGNIZED, "Can't determine project version, cannot load"); + //for the sake of decompiling scripts + if (ver_major == 2) { + ver_minor = 1; + } else if (ver_major == 3) { + ver_minor = 3; + } + } + + if (!FileAccess::exists(project_dir.plus_file("project.godot")) && !FileAccess::exists(project_dir.plus_file("engine.cfg"))) { + pcfg_loader.instantiate(); + if (FileAccess::exists(project_dir.plus_file("project.binary"))) { + bin_proj_file = project_dir.plus_file("project.binary"); + } else if (FileAccess::exists(project_dir.plus_file("engine.cfb"))) { + bin_proj_file = project_dir.plus_file("engine.cfb"); + } + // We fail hard here because we may have guessed the version wrong + ERR_FAIL_COND_V_MSG(bin_proj_file.is_empty(), ERR_CANT_OPEN, "Could not load project file"); + Error err = pcfg_loader->load_cfb(bin_proj_file, ver_major, ver_minor); + ERR_FAIL_COND_V_MSG(err, err, "Could not load project file"); } if (ver_major <= 2) { @@ -81,7 +130,8 @@ Error ImportExporter::load_import_files(const String &dir, const uint32_t p_ver_ } else { Vector wildcards; wildcards.push_back("*.import"); - wildcards.push_back("*project.binary"); + wildcards.push_back("*.gdc"); + if ((dir == "" || dir.begins_with("res://")) && GDRESettings::get_singleton()->is_pack_loaded()) { file_names = GDRESettings::get_singleton()->get_file_list(wildcards); } else { @@ -90,17 +140,9 @@ Error ImportExporter::load_import_files(const String &dir, const uint32_t p_ver_ } for (int i = 0; i < file_names.size(); i++) { - if (file_names[i] == "project.binary" || file_names[i] == "engine.cfb") { - // we wont replace it if it already exists - if (FileAccess::exists("project.godot") || FileAccess::exists("engine.cfg")) { - continue; - } - Ref pcfg_loader; - pcfg_loader.instantiate(); - pcfg_loader->load_cfb(file_names[i], ver_major, ver_minor); - pcfg_loader->save_cfb(dir, ver_major, ver_minor); - } - if (load_import_file(file_names[i]) != OK) { + if (file_names[i].get_extension() == "gdc") { + code_files.push_back(file_names[i]); + } else if (load_import_file(file_names[i]) != OK) { WARN_PRINT("Can't load import file: " + file_names[i]); } } @@ -124,6 +166,11 @@ Error ImportExporter::export_imports(const String &p_out_dir) { } recreate_plugin_configs(output_dir); + if (pcfg_loader.is_valid() && pcfg_loader->is_loaded()) { + pcfg_loader->save_cfb(output_dir, ver_major, ver_minor); + } + Ref dir = DirAccess::open(output_dir); + for (int i = 0; i < files.size(); i++) { Ref iinfo = files[i]; String path = iinfo->get_path(); @@ -181,6 +228,9 @@ Error ImportExporter::export_imports(const String &p_out_dir) { (importer == "scene" && (iinfo->source_file.get_extension() == "tscn" || iinfo->source_file.get_extension() == "escn"))) { iinfo->preferred_dest = iinfo->source_file; err = convert_res_bin_2_txt(output_dir, iinfo->import_path, iinfo->source_file); + if (ver_major == 2 && !err && iinfo->is_auto_converted()) { + dir->remove(iinfo->import_path.replace("res://", "")); + } } else { WARN_PRINT_ONCE("Conversion for " + type + " not implemented"); print_line("Did not convert " + type + " resource " + path); @@ -212,6 +262,82 @@ Error ImportExporter::export_imports(const String &p_out_dir) { return OK; } +Error ImportExporter::decompile_scripts(const String &p_out_dir) { + GDScriptDecomp *decomp; + // TODO: instead of doing this, run the detect bytecode script + switch (ver_major) { + case 1: + switch (ver_minor) { + case 0: + decomp = memnew(GDScriptDecomp_e82dc40); + break; + case 1: + decomp = memnew(GDScriptDecomp_e82dc40); + break; + } + break; + case 2: + switch (ver_minor) { + case 0: + decomp = memnew(GDScriptDecomp_23441ec); + break; + case 1: + decomp = memnew(GDScriptDecomp_ed80f45); + break; + } + break; + case 3: + switch (ver_minor) { + case 0: + decomp = memnew(GDScriptDecomp_054a2ac); + break; + case 1: + decomp = memnew(GDScriptDecomp_514a3fb); + break; + case 2: + decomp = memnew(GDScriptDecomp_5565f55); + break; + case 3: + case 4: + case 5: + decomp = memnew(GDScriptDecomp_5565f55); + break; + } + break; + case 4: + decomp = memnew(GDScriptDecomp_5565f55); + break; + default: + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown version, failed to decompile"); + } + print_line("Script version " + itos(ver_major) + "." + itos(ver_minor) + ".x detected"); + + for (String f : code_files) { + Ref da = DirAccess::open(p_out_dir); + print_line("decompiling " + f); + Error err = decomp->decompile_byte_code(project_dir.plus_file(f)); + if (err) { + memdelete(decomp); + ERR_FAIL_V_MSG(err, "error decompiling " + f); + } else { + String text = decomp->get_script_text(); + Ref fa = FileAccess::open(p_out_dir.plus_file(f.replace(".gdc", ".gd")), FileAccess::WRITE); + if (fa.is_null()) { + memdelete(decomp); + ERR_FAIL_V_MSG(ERR_FILE_CANT_WRITE, "error failed to save " + f); + } + fa->store_string(text); + da->remove(f); + if (da->file_exists(f.replace(".gdc", ".gd.remap"))) { + da->remove(f.replace(".gdc", ".gd.remap")); + } + print_line("successfully decompiled " + f); + } + } + memdelete(decomp); + return OK; +} + Error ImportExporter::recreate_plugin_config(const String &output_dir, const String &plugin_dir) { Error err; Vector wildcards; @@ -589,7 +715,7 @@ void ImportExporter::print_report() { print_line("Lossy not converted: " + itos(lossy_imports.size())); } print_line("Rewrote metadata: " + itos(rewrote_metadata.size())); - print_line("Require rewritten metadata but weren't: " + itos(failed_rewrite_md.size())); + print_line("Failed to rewrite metadata: " + itos(failed_rewrite_md.size())); print_line("Not converted: " + itos(not_converted.size())); print_line("Failed conversions: " + itos(failed.size())); print_line("-------------\n"); @@ -637,6 +763,7 @@ void ImportExporter::print_report() { } void ImportExporter::_bind_methods() { + ClassDB::bind_method(D_METHOD("decompile_scripts"), &ImportExporter::decompile_scripts); ClassDB::bind_method(D_METHOD("load_import_files"), &ImportExporter::load_import_files); ClassDB::bind_method(D_METHOD("get_import_files"), &ImportExporter::get_import_files); ClassDB::bind_method(D_METHOD("export_imports"), &ImportExporter::export_imports); diff --git a/utility/import_exporter.h b/utility/import_exporter.h index 8b18b35c..d7d2c861 100644 --- a/utility/import_exporter.h +++ b/utility/import_exporter.h @@ -8,11 +8,13 @@ #include "core/object/ref_counted.h" #include "core/templates/rb_map.h" #include "import_info.h" +#include "pcfg_loader.h" #include "resource_import_metadatav2.h" - class ImportExporter : public RefCounted { GDCLASS(ImportExporter, RefCounted) Array files; + Ref pcfg_loader; + Vector code_files; String project_dir; bool opt_bin2text = true; bool opt_export_textures = true; @@ -49,6 +51,8 @@ class ImportExporter : public RefCounted { static Error ensure_dir(const String &dst_dir); static bool check_if_dir_is_v2(const String &dir); + static bool check_if_dir_is_v3(const String &dir); + static bool check_if_dir_is_v4(const String &dir); static Vector get_v2_wildcards(); String _get_path(const String &output_dir, const String &p_path); @@ -64,7 +68,9 @@ class ImportExporter : public RefCounted { Error convert_sample_to_wav(const String &output_dir, const String &p_path, const String &p_dst); Error convert_oggstr_to_ogg(const String &output_dir, const String &p_path, const String &p_dst); Error convert_mp3str_to_mp3(const String &output_dir, const String &p_path, const String &p_dst); + Error decompile_scripts(const String &output_dir); Error load_import_files(const String &dir, const uint32_t ver_major, const uint32_t p_ver_minor); + Array get_import_files(); Ref get_import_info(const String &p_path); Error export_imports(const String &output_dir = ""); diff --git a/utility/import_info.cpp b/utility/import_info.cpp index e657e8d9..d0af87c9 100644 --- a/utility/import_info.cpp +++ b/utility/import_info.cpp @@ -220,6 +220,7 @@ Error ImportInfo::load_from_file_v2(const String &p_path) { //This is a converted file if (p_path.get_file().find(".converted.") != -1) { + auto_converted_export = true; // if this doesn't match "filename.ext.converted.newext" ERR_FAIL_COND_V_MSG(spl.size() != 4, ERR_CANT_RESOLVE, "Can't open imported file " + p_path); dest = p_path.get_base_dir().plus_file(spl[0] + "." + spl[1]); diff --git a/utility/import_info.h b/utility/import_info.h index 965dec05..14ca16cb 100644 --- a/utility/import_info.h +++ b/utility/import_info.h @@ -69,6 +69,7 @@ class ImportInfo : public RefCounted { Ref cf; // raw v3-v4 import data Ref v2metadata; // Raw v2 import metadata Dictionary v3metadata_prop; // 'metadata' property of "remap" tag in an import file + bool auto_converted_export; void _init(); Error load_from_file_v2(const String &p_path); @@ -100,6 +101,7 @@ class ImportInfo : public RefCounted { virtual String to_string() override; int get_import_loss_type() const; Error rename_source(const String &p_new_source); + bool is_auto_converted() { return auto_converted_export; } protected: static void _bind_methods() { diff --git a/utility/pcfg_loader.cpp b/utility/pcfg_loader.cpp index c848658e..d4024c0d 100644 --- a/utility/pcfg_loader.cpp +++ b/utility/pcfg_loader.cpp @@ -12,14 +12,19 @@ Error ProjectConfigLoader::load_cfb(const String path, const uint32_t ver_major, const uint32_t ver_minor) { cfb_path = path; - Error err; + Error err = OK; Ref f = FileAccess::open(path, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(f.is_null(), err, "Could not open " + path); err = _load_settings_binary(f, path, ver_major); - return err; + ERR_FAIL_COND_V(err, err); + loaded = true; + return OK; } Error ProjectConfigLoader::save_cfb(const String dir, const uint32_t ver_major, const uint32_t ver_minor) { + if (cfb_path.is_empty()) { + return ERR_UNAVAILABLE; + } String file; if (ver_major > 2) { file = "project.godot"; @@ -30,6 +35,13 @@ Error ProjectConfigLoader::save_cfb(const String dir, const uint32_t ver_major, return save_custom(dir.plus_file(file), ver_major, ver_minor); } +Variant ProjectConfigLoader::get_setting(String p_var, Variant default_value) const { + if (props.has(p_var)) { + return props[p_var].variant; + } + return default_value; +} + Error ProjectConfigLoader::_load_settings_binary(Ref f, const String &p_path, uint32_t ver_major) { Error err; uint8_t hdr[4]; diff --git a/utility/pcfg_loader.h b/utility/pcfg_loader.h index 42472638..8b279b62 100644 --- a/utility/pcfg_loader.h +++ b/utility/pcfg_loader.h @@ -40,6 +40,7 @@ class ProjectConfigLoader : public RefCounted { RBMap custom_prop_info; String cfb_path; int last_builtin_order; + bool loaded; public: Error load_cfb(const String path, uint32_t ver_major, uint32_t ver_minor); @@ -49,7 +50,10 @@ class ProjectConfigLoader : public RefCounted { Error save_custom(const String &p_path, const uint32_t ver_major, const uint32_t ver_minor); Error _save_settings_text(const String &p_file, const RBMap> &props, const uint32_t ver_major, const uint32_t ver_minor); Error _save_settings_text(const String &p_file); + bool is_loaded() const { return loaded; }; bool has_setting(String p_var) const; + Variant get_setting(String p_var, Variant default_value) const; + int get_major_version() { return (int)get_setting("config_version", 0); } Variant g_set(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false); ProjectConfigLoader(); ~ProjectConfigLoader();