diff --git a/.github/workflows/all_builds.yml b/.github/workflows/all_builds.yml index 0e5f4785..51b51535 100644 --- a/.github/workflows/all_builds.yml +++ b/.github/workflows/all_builds.yml @@ -28,7 +28,7 @@ env: # TODO: change this back to godotengine/godot and target master when #109685 and #109475 are merged GODOT_REPOSITORY: nikitalita/godot # Change the README too - GODOT_MAIN_SYNC_REF: gdre-wb-63227bb + GODOT_MAIN_SYNC_REF: gdre-wb-1559ab34c6 SCONSFLAGS: verbose=yes warnings=all werror=no module_text_server_fb_enabled=yes minizip=yes deprecated=yes SCONSFLAGS_TEMPLATE: no_editor_splash=yes module_camera_enabled=no module_mobile_vr_enabled=no module_upnp_enabled=no module_websocket_enabled=no module_csg_enabled=yes module_gridmap_enabled=yes use_static_cpp=yes builtin_freetype=yes builtin_libpng=yes builtin_zlib=yes builtin_libwebp=yes builtin_libvorbis=yes builtin_libogg=yes disable_3d=no SCONS_CACHE_MSVC_CONFIG: true @@ -224,7 +224,7 @@ jobs: - name: setup-dotnet uses: actions/setup-dotnet@v4 with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Download pre-built Android Swappy Frame Pacing Library if: matrix.platform == 'android' diff --git a/.gitignore b/.gitignore index 415cea50..305547f9 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,6 @@ gmon.out *.swp # QT project files -*.config *.creator *.creator.* *.files @@ -76,7 +75,6 @@ logs/ *.suo *.user *.sln.docstates -*.sln *.vcxproj* # Build results @@ -323,4 +321,4 @@ platform/windows/godot_res.res standalone/spriteTest.* standalone/character* .tmpcache/ -external_install/ \ No newline at end of file +external_install/ diff --git a/.scripts/rebase_godot.sh b/.scripts/rebase_godot.sh index 0595d55f..a095036b 100755 --- a/.scripts/rebase_godot.sh +++ b/.scripts/rebase_godot.sh @@ -1,6 +1,7 @@ #!/bin/sh SCRIPT_PATH=$(dirname "$0") +GDRE_PATH=$(realpath "$SCRIPT_PATH/..") cd "$SCRIPT_PATH/../../.." git fetch --all @@ -8,6 +9,8 @@ git checkout master git pull HEAD=$(git rev-parse HEAD) +SHORT_HASH=$(git rev-parse --short HEAD) +NEW_BRANCH_NAME="gdre-wb-$SHORT_HASH" # check for the existence of the 'nikitalita' remote if ! git remote | grep -q "nikitalita"; then @@ -15,7 +18,13 @@ if ! git remote | grep -q "nikitalita"; then git fetch nikitalita fi -git checkout working-branch +# check if the branch already exists +if git branch -a | grep -q $NEW_BRANCH_NAME; then + git branch -D $NEW_BRANCH_NAME +fi +git branch -C $NEW_BRANCH_NAME + +git checkout $NEW_BRANCH_NAME git reset --hard $HEAD BRANCHES_TO_MERGE=( @@ -26,9 +35,29 @@ BRANCHES_TO_MERGE=( convert-3.x-escn fix-svg fix-compat-array-shapes + gltf-buffer-view-encode-fix ) +# set fail on error for branch in "${BRANCHES_TO_MERGE[@]}"; do # merge branch, use a merge commit git merge nikitalita/$branch -m "Merge branch '$branch'" + if [ $? -ne 0 ]; then + echo "Error: Failed to merge branch '$branch'" + exit 1 + fi done + +# detect OS for cross-platform sed compatibility +# macOS (BSD sed) requires -i '' while Linux (GNU sed) uses -i +if [ "$(uname)" = "Darwin" ]; then + sed_in_place() { sed -i '' "$@"; } +else + sed_in_place() { sed -i "$@"; } +fi + +git push nikitalita $NEW_BRANCH_NAME --set-upstream + +# change the branch name in .github/workflows/all_builds.yml and the README.md +sed_in_place "s/GODOT_MAIN_SYNC_REF: .*/GODOT_MAIN_SYNC_REF: $NEW_BRANCH_NAME/" "$GDRE_PATH/.github/workflows/all_builds.yml" +sed_in_place "s/ @ branch \`.*\`/ @ branch \`$NEW_BRANCH_NAME\`/" "$GDRE_PATH/README.md" diff --git a/README.md b/README.md index 6d5ca06f..d0d182bb 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ Note: Make sure to build the editor build first, and to launch the editor to edi ### Requirements -[Our fork of godot](https://github.com/nikitalita/godot/tree/working-branch) @ `gdre-wb-63227bb` +[Our fork of godot](https://github.com/nikitalita/godot) @ branch `gdre-wb-1559ab34c6` - 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 18c095fe..83889aa9 100644 --- a/SCsub +++ b/SCsub @@ -43,6 +43,7 @@ if env["platform"] == "android" or (is_using_clang and env["platform"] == "macos # force shared on macos with clang++, because `ld` does not support multiple definitions of the same symbol mono_native_lib_type = "Shared" +mbedtlsthirdparty_dir = "#thirdparty/mbedtls/include/" mmp3thirdparty_dir = "#thirdparty/minimp3/" liboggthirdparty_dir = "#thirdparty/libogg/" libtheorathirdparty_dir = "#thirdparty/libtheora/" @@ -82,6 +83,7 @@ env_gdsdecomp.Append(CPPPATH=[godot_mono_decomp_include_dir]) env_gdsdecomp.Append(CPPPATH=["#modules/gdsdecomp/"]) env_gdsdecomp.Append(CPPPATH=["#thirdparty/thorsvg/"]) env_gdsdecomp.Append(CPPPATH=[libtheorathirdparty_dir]) +env_gdsdecomp.Append(CPPPATH=[mbedtlsthirdparty_dir]) if env["disable_exceptions"]: env_gdsdecomp.Append(CPPDEFINES=["DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS"]) @@ -850,6 +852,7 @@ module_obj = [] env_gdsdecomp.add_source_files(module_obj, "*.cpp") env_gdsdecomp.add_source_files(module_obj, "bytecode/*.cpp") env_gdsdecomp.add_source_files(module_obj, "compat/*.cpp") +env_gdsdecomp.add_source_files(module_obj, "crypto/*.cpp") if env["target"] == "editor": env_gdsdecomp.add_source_files(module_obj, "editor/*.cpp") env_gdsdecomp.add_source_files(module_obj, "exporters/*.cpp") diff --git a/compat/fake_csharp_script.cpp b/compat/fake_csharp_script.cpp index 4a223e48..681ce159 100644 --- a/compat/fake_csharp_script.cpp +++ b/compat/fake_csharp_script.cpp @@ -2,10 +2,8 @@ #include "compat/fake_script.h" #include "compat/resource_loader_compat.h" -#include "compat/variant_decoder_compat.h" #include "compat/variant_writer_compat.h" #include "core/error/error_list.h" -#include "core/io/missing_resource.h" #include "core/math/expression.h" #include "core/object/object.h" #include "core/string/ustring.h" @@ -108,106 +106,154 @@ bool FakeCSharpScript::instance_has(const Object *p_this) const { PropertyHint string_to_property_hint(const String &p_string) { String name = p_string.to_upper(); - if (name == "NONE") + if (name == "NONE") { return PROPERTY_HINT_NONE; ///< no hint provided. - if (name == "RANGE") + } + if (name == "RANGE") { return PROPERTY_HINT_RANGE; ///< hint_text = "min,max[,step][,or_greater][,or_less][,hide_slider][,radians_as_degrees][,degrees][,exp][,suffix:] range. - if (name == "ENUM") + } + if (name == "ENUM") { return PROPERTY_HINT_ENUM; ///< hint_text= "val1,val2,val3,etc" - if (name == "ENUM_SUGGESTION") + } + if (name == "ENUM_SUGGESTION") { return PROPERTY_HINT_ENUM_SUGGESTION; ///< hint_text= "val1,val2,val3,etc" - if (name == "EXP_EASING") + } + if (name == "EXP_EASING") { return PROPERTY_HINT_EXP_EASING; /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "positive_only" to exclude in-out and out-in. (ie: "attenuation,positive_only") - if (name == "LINK") + } + if (name == "LINK") { return PROPERTY_HINT_LINK; - if (name == "FLAGS") + } + if (name == "FLAGS") { return PROPERTY_HINT_FLAGS; ///< hint_text= "flag1,flag2,etc" (as bit flags) - if (name == "LAYERS_2D_RENDER") + } + if (name == "LAYERS_2D_RENDER") { return PROPERTY_HINT_LAYERS_2D_RENDER; - if (name == "LAYERS_2D_PHYSICS") + } + if (name == "LAYERS_2D_PHYSICS") { return PROPERTY_HINT_LAYERS_2D_PHYSICS; - if (name == "LAYERS_2D_NAVIGATION") + } + if (name == "LAYERS_2D_NAVIGATION") { return PROPERTY_HINT_LAYERS_2D_NAVIGATION; - if (name == "LAYERS_3D_RENDER") + } + if (name == "LAYERS_3D_RENDER") { return PROPERTY_HINT_LAYERS_3D_RENDER; - if (name == "LAYERS_3D_PHYSICS") + } + if (name == "LAYERS_3D_PHYSICS") { return PROPERTY_HINT_LAYERS_3D_PHYSICS; - if (name == "LAYERS_3D_NAVIGATION") + } + if (name == "LAYERS_3D_NAVIGATION") { return PROPERTY_HINT_LAYERS_3D_NAVIGATION; - if (name == "FILE") + } + if (name == "FILE") { return PROPERTY_HINT_FILE; ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," - if (name == "DIR") + } + if (name == "DIR") { return PROPERTY_HINT_DIR; ///< a directory path must be passed - if (name == "GLOBAL_FILE") + } + if (name == "GLOBAL_FILE") { return PROPERTY_HINT_GLOBAL_FILE; ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc," - if (name == "GLOBAL_DIR") + } + if (name == "GLOBAL_DIR") { return PROPERTY_HINT_GLOBAL_DIR; ///< a directory path must be passed - if (name == "RESOURCE_TYPE") + } + if (name == "RESOURCE_TYPE") { return PROPERTY_HINT_RESOURCE_TYPE; ///< a comma-separated resource object type, e.g. "NoiseTexture,GradientTexture2D". Subclasses can be excluded with a "-" prefix if placed *after* the base class, e.g. "Texture2D,-MeshTexture". - if (name == "MULTILINE_TEXT") + } + if (name == "MULTILINE_TEXT") { return PROPERTY_HINT_MULTILINE_TEXT; ///< used for string properties that can contain multiple lines - if (name == "EXPRESSION") + } + if (name == "EXPRESSION") { return PROPERTY_HINT_EXPRESSION; ///< used for string properties that can contain multiple lines - if (name == "PLACEHOLDER_TEXT") + } + if (name == "PLACEHOLDER_TEXT") { return PROPERTY_HINT_PLACEHOLDER_TEXT; ///< used to set a placeholder text for string properties - if (name == "COLOR_NO_ALPHA") + } + if (name == "COLOR_NO_ALPHA") { return PROPERTY_HINT_COLOR_NO_ALPHA; ///< used for ignoring alpha component when editing a color - if (name == "OBJECT_ID") + } + if (name == "OBJECT_ID") { return PROPERTY_HINT_OBJECT_ID; - if (name == "TYPE_STRING") + } + if (name == "TYPE_STRING") { return PROPERTY_HINT_TYPE_STRING; ///< a type string, the hint is the base type to choose - if (name == "NODE_PATH_TO_EDITED_NODE") + } + if (name == "NODE_PATH_TO_EDITED_NODE") { return PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE; // Deprecated. - if (name == "OBJECT_TOO_BIG") + } + if (name == "OBJECT_TOO_BIG") { return PROPERTY_HINT_OBJECT_TOO_BIG; ///< object is too big to send - if (name == "NODE_PATH_VALID_TYPES") + } + if (name == "NODE_PATH_VALID_TYPES") { return PROPERTY_HINT_NODE_PATH_VALID_TYPES; - if (name == "SAVE_FILE") + } + if (name == "SAVE_FILE") { return PROPERTY_HINT_SAVE_FILE; ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog - if (name == "GLOBAL_SAVE_FILE") + } + if (name == "GLOBAL_SAVE_FILE") { return PROPERTY_HINT_GLOBAL_SAVE_FILE; ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog - if (name == "INT_IS_OBJECTID") + } + if (name == "INT_IS_OBJECTID") { return PROPERTY_HINT_INT_IS_OBJECTID; // Deprecated. - if (name == "INT_IS_POINTER") + } + if (name == "INT_IS_POINTER") { return PROPERTY_HINT_INT_IS_POINTER; - if (name == "ARRAY_TYPE") + } + if (name == "ARRAY_TYPE") { return PROPERTY_HINT_ARRAY_TYPE; - if (name == "LOCALE_ID") + } + if (name == "LOCALE_ID") { return PROPERTY_HINT_LOCALE_ID; - if (name == "LOCALIZABLE_STRING") + } + if (name == "LOCALIZABLE_STRING") { return PROPERTY_HINT_LOCALIZABLE_STRING; - if (name == "NODE_TYPE") + } + if (name == "NODE_TYPE") { return PROPERTY_HINT_NODE_TYPE; ///< a node object type - if (name == "HIDE_QUATERNION_EDIT") + } + if (name == "HIDE_QUATERNION_EDIT") { return PROPERTY_HINT_HIDE_QUATERNION_EDIT; /// Only Node3D::transform should hide the quaternion editor. - if (name == "PASSWORD") + } + if (name == "PASSWORD") { return PROPERTY_HINT_PASSWORD; - if (name == "LAYERS_AVOIDANCE") + } + if (name == "LAYERS_AVOIDANCE") { return PROPERTY_HINT_LAYERS_AVOIDANCE; - if (name == "DICTIONARY_TYPE") + } + if (name == "DICTIONARY_TYPE") { return PROPERTY_HINT_DICTIONARY_TYPE; - if (name == "TOOL_BUTTON") + } + if (name == "TOOL_BUTTON") { return PROPERTY_HINT_TOOL_BUTTON; - if (name == "ONESHOT") + } + if (name == "ONESHOT") { return PROPERTY_HINT_ONESHOT; ///< the property will be changed by self after setting, such as AudioStreamPlayer.playing, Particles.emitting. - if (name == "NO_NODEPATH") + } + if (name == "NO_NODEPATH") { return PROPERTY_HINT_NO_NODEPATH; /// < this property will not contain a NodePath, regardless of type (Array, Dictionary, List, etc.). Needed for SceneTreeDock. - if (name == "GROUP_ENABLE") + } + if (name == "GROUP_ENABLE") { return PROPERTY_HINT_GROUP_ENABLE; ///< used to make the property's group checkable. Only use for boolean types. - if (name == "INPUT_NAME") + } + if (name == "INPUT_NAME") { return PROPERTY_HINT_INPUT_NAME; - if (name == "FILE_PATH") + } + if (name == "FILE_PATH") { return PROPERTY_HINT_FILE_PATH; - if (name == "MAX") + } + if (name == "MAX") { return PROPERTY_HINT_MAX; + } return PROPERTY_HINT_NONE; } Variant::Type string_to_variant_type(const String &name) { - if (name == "Variant") + if (name == "Variant") { return Variant::NIL; - if (name == "Nil" || name == "null" || name == "None" || name == "void") + } + if (name == "Nil" || name == "null" || name == "None" || name == "void") { return Variant::NIL; + } auto tp = Variant::get_type_by_name(name); if (tp == Variant::VARIANT_MAX) { if (name.begins_with("Array")) { diff --git a/compat/file_access_encrypted_v3.h b/compat/file_access_encrypted_v3.h index 126623fd..f7b4e0ed 100644 --- a/compat/file_access_encrypted_v3.h +++ b/compat/file_access_encrypted_v3.h @@ -30,8 +30,7 @@ #include -#ifndef FILE_ACCESS_ENCRYPTED_V3_H -#define FILE_ACCESS_ENCRYPTED_V3_H +#pragma once #include "core/io/file_access.h" @@ -48,7 +47,7 @@ class FileAccessEncryptedv3 : public FileAccess { }; private: - Mode mode; + Mode mode = MODE_READ; Vector key; bool writing = false; Ref file; @@ -104,5 +103,3 @@ class FileAccessEncryptedv3 : public FileAccess { FileAccessEncryptedv3() {} ~FileAccessEncryptedv3(); }; - -#endif //FILE_ACCESS_ENCRYPTED_V3_H \ No newline at end of file diff --git a/compat/image_parser_v2.cpp b/compat/image_parser_v2.cpp index c09c9207..f499788f 100644 --- a/compat/image_parser_v2.cpp +++ b/compat/image_parser_v2.cpp @@ -99,8 +99,9 @@ String ImageParserV2::image_v2_to_string(const Variant &r_v, bool is_pcfg) { if (!is_pcfg) { int len = data.size(); for (int i = 0; i < len; i++) { - if (i > 0) + if (i > 0) { s += ", "; + } s += itos(data[i]); } } else { diff --git a/compat/image_parser_v2.h b/compat/image_parser_v2.h index 042987ab..e464cd22 100644 --- a/compat/image_parser_v2.h +++ b/compat/image_parser_v2.h @@ -1,5 +1,4 @@ -#ifndef IMAGE_PARSER_V2_H -#define IMAGE_PARSER_V2_H +#pragma once #include "core/io/file_access.h" #include "core/io/image.h" @@ -17,5 +16,3 @@ class ImageParserV2 { static Error decode_image_v2(Ref f, Variant &r_v, bool convert_indexed = true); static Error write_image_v2_to_bin(Ref f, const Variant &r_v, bool compress_lossless = true); }; - -#endif // IMAGE_PARSER_V2_H diff --git a/compat/input_event_parser_v2.h b/compat/input_event_parser_v2.h index 5bbaec19..e3be1f9f 100644 --- a/compat/input_event_parser_v2.h +++ b/compat/input_event_parser_v2.h @@ -1,5 +1,4 @@ -#ifndef INPUT_EVENT_PARSER_V2_H -#define INPUT_EVENT_PARSER_V2_H +#pragma once #include "compat/resource_loader_compat.h" #include "core/input/input_event.h" @@ -420,5 +419,3 @@ class InputEventConverterCompat : public ResourceCompatConverter { virtual Ref convert_back(const Ref &res, int ver_major, Error *r_error = nullptr) override; static bool handles_type_static(const String &p_type, int ver_major); }; - -#endif // INPUT_EVENT_PARSER_V2_H diff --git a/compat/oggstr_loader_compat.h b/compat/oggstr_loader_compat.h index 181ef9af..b186e38b 100644 --- a/compat/oggstr_loader_compat.h +++ b/compat/oggstr_loader_compat.h @@ -1,16 +1,10 @@ +#pragma once #include "compat/resource_loader_compat.h" -#include "core/io/file_access.h" -#include "core/io/image.h" #include "core/io/resource.h" -#include "core/io/resource_loader.h" #include "core/object/ref_counted.h" -#include "core/templates/vector.h" #include "modules/vorbis/audio_stream_ogg_vorbis.h" -#include "scene/resources/texture.h" -#ifndef OGGSTR_LOADER_COMPAT_H -#define OGGSTR_LOADER_COMPAT_H class OggStreamConverterCompat : public ResourceCompatConverter { GDCLASS(OggStreamConverterCompat, ResourceCompatConverter); @@ -18,5 +12,3 @@ class OggStreamConverterCompat : public ResourceCompatConverter { virtual Ref convert(const Ref &res, ResourceInfo::LoadType p_type, int ver_major, Error *r_error = nullptr) override; virtual bool handles_type(const String &p_type, int ver_major) const override; }; - -#endif //OGGSTR_LOADER_COMPAT_H diff --git a/compat/resource_compat_binary.h b/compat/resource_compat_binary.h index 36ff4aef..94c5cda7 100644 --- a/compat/resource_compat_binary.h +++ b/compat/resource_compat_binary.h @@ -28,8 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef RESOURCE_COMPAT_BINARY_H -#define RESOURCE_COMPAT_BINARY_H +#pragma once #include "compat/resource_import_metadatav2.h" #include "compat/resource_loader_compat.h" @@ -274,5 +273,3 @@ class ResourceFormatSaverCompatBinary : public ResourceFormatSaver { ResourceFormatSaverCompatBinary(); }; - -#endif // RESOURCE_COMPAT_BINARY_H diff --git a/compat/resource_compat_text.h b/compat/resource_compat_text.h index 86747456..02af856b 100644 --- a/compat/resource_compat_text.h +++ b/compat/resource_compat_text.h @@ -28,8 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef RESOURCE_COMPAT_TEXT_H -#define RESOURCE_COMPAT_TEXT_H +#pragma once #include "compat/resource_loader_compat.h" #include "utility/resource_info.h" @@ -274,5 +273,3 @@ class ResourceFormatSaverCompatText : public ResourceFormatSaver { ResourceFormatSaverCompatText(); }; - -#endif // RESOURCE_COMPAT_TEXT_H diff --git a/compat/resource_format_xml.cpp b/compat/resource_format_xml.cpp index 3f3c81a1..e1bab4a0 100644 --- a/compat/resource_format_xml.cpp +++ b/compat/resource_format_xml.cpp @@ -31,11 +31,9 @@ #include "resource_format_xml.h" #include "compat/image_enum_compat.h" #include "compat/input_event_parser_v2.h" -#include "core/config/project_settings.h" -#include "core/io/dir_access.h" #include "core/io/image.h" #include "core/string/ustring.h" -#include "core/version.h" + #include "utility/gdre_settings.h" typedef uint8_t CharType; @@ -2054,8 +2052,9 @@ void ResourceFormatSaverXMLInstance::write_tabs(int p_diff) { void ResourceFormatSaverXMLInstance::write_string(String p_str, bool p_escape) { /* write an UTF8 string */ - if (p_escape) + if (p_escape) { escape(p_str); + } f->store_string(p_str); ; @@ -2149,8 +2148,9 @@ static bool _check_type(const Variant& p_property) { }*/ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const Variant &p_property, bool *r_ok) { - if (r_ok) + if (r_ok) { *r_ok = false; + } const char *type; String params; @@ -2212,8 +2212,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const write_tabs(); enter_tag(type, "name=\"" + p_name + "\""); exit_tag(type); - if (r_ok) + if (r_ok) { *r_ok = true; + } return; // don't save it } @@ -2224,8 +2225,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const write_tabs(); enter_tag(type, "name=\"" + p_name + "\""); exit_tag(type); - if (r_ok) + if (r_ok) { *r_ok = true; + } return; } params += "encoding=\"raw\""; @@ -2377,21 +2379,24 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const write_tabs(); if (p_name != "") { - if (params.length()) + if (params.length()) { enter_tag(type, "name=\"" + p_name + "\" " + params); - else + } else { enter_tag(type, "name=\"" + p_name + "\""); + } } else { - if (params.length()) + if (params.length()) { enter_tag(type, " " + params); - else + } else { enter_tag(type, String()); + } } - if (!oneliner) + if (!oneliner) { f->store_8('\n'); - else + } else { f->store_8(' '); + } switch (p_property.get_type()) { case Variant::NIL: { @@ -2444,8 +2449,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const Transform2D m3 = p_property; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtoss(m3.columns[i][j]); } } @@ -2458,8 +2464,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const Basis m3 = p_property; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtoss(m3.rows[i][j]); } } @@ -2473,8 +2480,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const Basis &m3 = t.basis; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtoss(m3.rows[i][j]); } } @@ -2573,8 +2581,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const ERR_CONTINUE(!ok); write_property("", dict[key], &ok); - if (!ok) + if (!ok) { write_property("", Variant()); //at least make the file consistent.. + } } } break; @@ -2613,8 +2622,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const write_tabs(); for (int i = 0; i < len; i++) { - if (i > 0) + if (i > 0) { write_string(", ", false); + } write_string(itos(ptr[i]), false); } @@ -2630,8 +2640,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const String cm = ", "; for (int i = 0; i < len; i++) { - if (i > 0) + if (i > 0) { write_string(cm, false); + } write_string(rtoss(ptr[i]), false); } @@ -2661,8 +2672,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const write_tabs(); for (int i = 0; i < len; i++) { - if (i > 0) + if (i > 0) { write_string(", ", false); + } write_string(rtoss(ptr[i].x), false); write_string(", " + rtoss(ptr[i].y), false); } @@ -2677,8 +2689,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const write_tabs(); for (int i = 0; i < len; i++) { - if (i > 0) + if (i > 0) { write_string(", ", false); + } write_string(rtoss(ptr[i].x), false); write_string(", " + rtoss(ptr[i].y), false); write_string(", " + rtoss(ptr[i].z), false); @@ -2694,8 +2707,9 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const write_tabs(); for (int i = 0; i < len; i++) { - if (i > 0) + if (i > 0) { write_string(", ", false); + } write_string(rtoss(ptr[i].r), false); write_string(", " + rtoss(ptr[i].g), false); @@ -2707,16 +2721,18 @@ void ResourceFormatSaverXMLInstance::write_property(const String &p_name, const default: { } } - if (oneliner) + if (oneliner) { f->store_8(' '); - else + } else { write_tabs(-1); + } exit_tag(type); f->store_8('\n'); - if (r_ok) + if (r_ok) { *r_ok = true; + } } void ResourceFormatSaverXMLInstance::_find_resources(const Variant &p_variant, bool p_main) { @@ -2724,8 +2740,9 @@ void ResourceFormatSaverXMLInstance::_find_resources(const Variant &p_variant, b case Variant::OBJECT: { Ref res = p_variant; - if (res.is_null() || external_resources.has(res)) + if (res.is_null() || external_resources.has(res)) { return; + } if (!CompatFormatLoader::resource_is_resource(res, ver_major)) { return; } @@ -2736,8 +2753,9 @@ void ResourceFormatSaverXMLInstance::_find_resources(const Variant &p_variant, b return; } - if (resource_set.has(res)) + if (resource_set.has(res)) { return; + } List property_list; @@ -2855,11 +2873,11 @@ Error ResourceFormatSaverXMLInstance::save_to_file(const Ref &p_f, c write_tabs(); - if (main) + if (main) { enter_tag("main_resource", ""); //bundled - else if (res->get_path().length() && res->get_path().find("::") == -1) + } else if (res->get_path().length() && res->get_path().find("::") == -1) { enter_tag("resource", "type=\"" + res->get_save_class() + "\" path=\"" + res->get_path() + "\""); //bundled - else { + } else { if (res->get_scene_unique_id() == "0") { int new_subindex = 1; if (used_indices.size()) { @@ -2882,8 +2900,9 @@ Error ResourceFormatSaverXMLInstance::save_to_file(const Ref &p_f, c res->get_property_list(&property_list); // property_list.sort(); for (List::Element *PE = property_list.front(); PE; PE = PE->next()) { - if (skip_editor && PE->get().name.begins_with("__editor")) + if (skip_editor && PE->get().name.begins_with("__editor")) { continue; + } if (PE->get().usage & PROPERTY_USAGE_STORAGE) { // 2.x didn't have these @@ -2914,10 +2933,11 @@ Error ResourceFormatSaverXMLInstance::save_to_file(const Ref &p_f, c write_string("\n", false); write_tabs(-1); - if (main) + if (main) { exit_tag("main_resource"); - else + } else { exit_tag("resource"); + } write_string("\n", false); } diff --git a/compat/resource_format_xml.h b/compat/resource_format_xml.h index 5c310b6c..fdaa1fbe 100644 --- a/compat/resource_format_xml.h +++ b/compat/resource_format_xml.h @@ -28,8 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef RESOURCE_FORMAT_XML_H -#define RESOURCE_FORMAT_XML_H +#pragma once #include "compat/resource_loader_compat.h" #include "core/io/file_access.h" @@ -251,5 +250,3 @@ class ResourceFormatSaverXML : public ResourceFormatSaver { ResourceFormatSaverXML(); }; - -#endif // RESOURCE_FORMAT_XML_H diff --git a/compat/resource_import_metadatav2.h b/compat/resource_import_metadatav2.h index 872da747..2004da8e 100644 --- a/compat/resource_import_metadatav2.h +++ b/compat/resource_import_metadatav2.h @@ -1,8 +1,6 @@ -#ifndef RESOURCE_IMPORT_METADATAV2_H -#define RESOURCE_IMPORT_METADATAV2_H +#pragma once -#include "core/io/resource.h" #include "core/object/ref_counted.h" #include "core/templates/rb_map.h" @@ -46,5 +44,3 @@ class ResourceImportMetadatav2 : public RefCounted { static Ref from_json(const Dictionary &p_dict); ResourceImportMetadatav2(); }; - -#endif //RESOURCE_IMPORT_METADATAV2_H diff --git a/compat/resource_loader_compat.cpp b/compat/resource_loader_compat.cpp index e2e94e0a..3ecc5104 100644 --- a/compat/resource_loader_compat.cpp +++ b/compat/resource_loader_compat.cpp @@ -4,7 +4,6 @@ #include "compat/resource_format_xml.h" #include "core/error/error_list.h" #include "core/error/error_macros.h" -#include "core/io/file_access_memory.h" #include "core/io/resource_loader.h" #include "core/object/class_db.h" #include "utility/common.h" diff --git a/compat/script_loader.cpp b/compat/script_loader.cpp index 944ea776..a7cf7fcd 100644 --- a/compat/script_loader.cpp +++ b/compat/script_loader.cpp @@ -1,11 +1,7 @@ #include "script_loader.h" -#include "core/io/file_access.h" #include "core/io/resource_loader.h" -#include "core/io/resource_saver.h" -#include "core/os/os.h" -#include "core/variant/variant_parser.h" #include "bytecode/bytecode_base.h" #include "compat/fake_csharp_script.h" diff --git a/compat/texture_loader_compat.h b/compat/texture_loader_compat.h index 1501b8da..e973caab 100644 --- a/compat/texture_loader_compat.h +++ b/compat/texture_loader_compat.h @@ -1,5 +1,4 @@ -#ifndef TEXTURE_LOADER_COMPAT_H -#define TEXTURE_LOADER_COMPAT_H +#pragma once #include "compat/resource_loader_compat.h" #include "core/io/file_access.h" @@ -160,5 +159,3 @@ class ResourceFormatLoaderImageTextureCompat : public CompatFormatLoader { virtual Ref get_resource_info(const String &p_path, Error *r_error) const override; virtual bool handles_fake_load() const override { return false; } }; - -#endif // TEXTURE_LOADER_COMPAT_H diff --git a/compat/variant_decoder_compat.cpp b/compat/variant_decoder_compat.cpp index cd79e6a1..f321aadc 100644 --- a/compat/variant_decoder_compat.cpp +++ b/compat/variant_decoder_compat.cpp @@ -1415,8 +1415,9 @@ Error VariantDecoderCompat::decode_variant_2(Variant &r_variant, const uint8_t * } r_variant = img; if (r_len) { - if (datalen % 4) + if (datalen % 4) { (*r_len) += 4 - datalen % 4; + } (*r_len) += 4 * 5 + datalen; } @@ -1479,8 +1480,9 @@ Error VariantDecoderCompat::decode_variant_2(Variant &r_variant, const uint8_t * r_variant = NodePath(str); - if (r_len) + if (r_len) { (*r_len) += 4 + strlen; + } ERR_FAIL_V(ERR_INVALID_DATA); } @@ -2347,8 +2349,9 @@ Error VariantDecoderCompat::encode_variant_3(const Variant &p_variant, uint8_t * if (buf) { encode_uint32(datalen, buf); buf += 4; - for (int i = 0; i < datalen; i++) + for (int i = 0; i < datalen; i++) { encode_uint32(data[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; @@ -2363,8 +2366,9 @@ Error VariantDecoderCompat::encode_variant_3(const Variant &p_variant, uint8_t * if (buf) { encode_uint32(datalen, buf); buf += 4; - for (int i = 0; i < datalen; i++) + for (int i = 0; i < datalen; i++) { encode_float(data[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; @@ -2559,8 +2563,9 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * } int pad = 0; - if (data.size() % 4) + if (data.size() % 4) { pad = 4 - data.size() % 4; + } r_len += data.size() + 5 * 4 + pad; return OK; @@ -2616,10 +2621,12 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); //for compatibility with the old format encode_uint32(snc, buf + 4); uint32_t flags = 0; - if (np.is_absolute()) + if (np.is_absolute()) { flags |= 1; - if (property_idx != -1) + } + if (property_idx != -1) { flags |= 2; + } encode_uint32(flags, buf + 8); @@ -2629,18 +2636,20 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * r_len += 12; int total = np.get_name_count() + snc; - if (property_idx != -1) + if (property_idx != -1) { total++; + } for (int i = 0; i < total; i++) { String str; - if (i < np.get_name_count()) + if (i < np.get_name_count()) { str = np.get_name(i); - else if (i < np.get_name_count() + snc) + } else if (i < np.get_name_count() + snc) { str = np.get_subname(i - np.get_name_count()); - else // property + } else { // property str = np.get_subname(property_idx); + } _encode_string(str, buf, r_len); } @@ -2818,13 +2827,15 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * encode_variant_2(E, buf, len); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } encode_variant_2(d[E], buf, len); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } } } break; @@ -2845,8 +2856,9 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * encode_variant_2(v.get(i), buf, len); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } } } break; @@ -2863,8 +2875,9 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * } r_len += 4 + datalen * datasize; - while (r_len % 4) + while (r_len % 4) { r_len++; + } } break; // compat @@ -2876,8 +2889,9 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * if (buf) { encode_uint32(datalen, buf); buf += 4; - for (int i = 0; i < datalen; i++) + for (int i = 0; i < datalen; i++) { encode_uint32(data[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; @@ -2891,8 +2905,9 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * if (buf) { encode_uint32(datalen, buf); buf += 4; - for (int i = 0; i < datalen; i++) + for (int i = 0; i < datalen; i++) { encode_uint32(data[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; @@ -2910,8 +2925,9 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * if (buf) { encode_uint32(datalen, buf); buf += 4; - for (int i = 0; i < datalen; i++) + for (int i = 0; i < datalen; i++) { encode_float(data[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; @@ -2925,8 +2941,9 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * if (buf) { encode_uint32(datalen, buf); buf += 4; - for (int i = 0; i < datalen; i++) + for (int i = 0; i < datalen; i++) { encode_float(data[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; @@ -2956,8 +2973,9 @@ Error VariantDecoderCompat::encode_variant_2(const Variant &p_variant, uint8_t * r_len += 4 + utf8.length() + 1; while (r_len % 4) { r_len++; //pad - if (buf) + if (buf) { *(buf++) = 0; + } } } diff --git a/compat/variant_decoder_compat.h b/compat/variant_decoder_compat.h index 1021f8db..8b5d1690 100644 --- a/compat/variant_decoder_compat.h +++ b/compat/variant_decoder_compat.h @@ -1,5 +1,4 @@ -#ifndef VARIANT_DECODER_COMPAT_H -#define VARIANT_DECODER_COMPAT_H +#pragma once #include "core/string/ustring.h" #include @@ -114,5 +113,4 @@ class VariantDecoderCompat { static Error encode_variant_3(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false, int p_depth = 0); static Error encode_variant_2(const Variant &p_variant, uint8_t *r_buffer, int &r_len); static Error encode_variant_compat(int ver_major, const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false, int p_depth = 0); -}; -#endif // VARIANT_DECODER_COMPAT_H \ No newline at end of file +}; \ No newline at end of file diff --git a/compat/variant_writer_compat.cpp b/compat/variant_writer_compat.cpp index d140c9e8..ef8057ab 100644 --- a/compat/variant_writer_compat.cpp +++ b/compat/variant_writer_compat.cpp @@ -1544,8 +1544,9 @@ Error VarWriter::write_compa case Variant::FLOAT: { // "REAL" in v2 and v3 String s = rtosfix(p_variant.operator double()); if (!float_special_values.has(s)) { - if (s.find(".") == -1 && s.find("e") == -1) + if (s.find(".") == -1 && s.find("e") == -1) { s += ".0"; + } } p_store_string_func(p_store_string_ud, s); } break; @@ -1573,8 +1574,9 @@ Error VarWriter::write_compa Transform2D m3 = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtosfix(m3.columns[i][j]); } } @@ -1604,8 +1606,9 @@ Error VarWriter::write_compa Basis m3 = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtosfix(m3.rows[i][j]); } } @@ -1619,8 +1622,9 @@ Error VarWriter::write_compa Basis &m3 = t.basis; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - if (i != 0 || j != 0) + if (i != 0 || j != 0) { s += ", "; + } s += rtosfix(m3.rows[i][j]); } } @@ -1742,8 +1746,9 @@ Error VarWriter::write_compa write_compat_v2_v3(E, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); p_store_string_func(p_store_string_ud, ": "); write_compat_v2_v3(dict[E], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); - if (i < keys.size() - 1) + if (i < keys.size() - 1) { p_store_string_func(p_store_string_ud, is_v2_pcfg ? ", " : ",\n"); + } } p_store_string_func(p_store_string_ud, is_v2_pcfg ? "}" : "\n}"); diff --git a/compat/webp_compat.h b/compat/webp_compat.h index b9d2d76f..655579fd 100644 --- a/compat/webp_compat.h +++ b/compat/webp_compat.h @@ -1,10 +1,8 @@ -#ifndef WEBP_COMMON_H -#define WEBP_COMMON_H +#pragma once #include "core/io/image.h" namespace WebPCompat { Ref webp_unpack_v2v3(const Vector &p_buffer); -} -#endif //WEBP_COMMON_H \ No newline at end of file +} \ No newline at end of file diff --git a/config.py b/config.py index 38574590..3b466f88 100644 --- a/config.py +++ b/config.py @@ -88,67 +88,23 @@ def configure(env): def get_doc_classes(): return [ "GDScriptDecomp", - "GDScriptDecomp_0b806ee", - "GDScriptDecomp_8c1731b", - "GDScriptDecomp_31ce3c5", - "GDScriptDecomp_703004f", - "GDScriptDecomp_8cab401", - "GDScriptDecomp_e82dc40", - "GDScriptDecomp_2185c01", - "GDScriptDecomp_97f34a1", - "GDScriptDecomp_be46be7", - "GDScriptDecomp_65d48d6", - "GDScriptDecomp_48f1d02", - "GDScriptDecomp_30c1229", - "GDScriptDecomp_7d2d144", - "GDScriptDecomp_64872ca", - "GDScriptDecomp_6174585", - "GDScriptDecomp_23441ec", - "GDScriptDecomp_7124599", - "GDScriptDecomp_1add52b", - "GDScriptDecomp_4ee82a2", - "GDScriptDecomp_513c026", - "GDScriptDecomp_23381a5", - "GDScriptDecomp_85585c7", - "GDScriptDecomp_ed80f45", - "GDScriptDecomp_8b912d1", - "GDScriptDecomp_62273e5", - "GDScriptDecomp_f8a7c46", - "GDScriptDecomp_c24c739", - "GDScriptDecomp_5e938f0", - "GDScriptDecomp_015d36d", - "GDScriptDecomp_c6120e7", - "GDScriptDecomp_d28da86", - "GDScriptDecomp_216a8aa", - "GDScriptDecomp_91ca725", - "GDScriptDecomp_054a2ac", - "GDScriptDecomp_ff1e7cf", - "GDScriptDecomp_a56d6ff", - "GDScriptDecomp_3ea6d9f", - "GDScriptDecomp_8e35d93", - "GDScriptDecomp_a3f1ee5", - "GDScriptDecomp_8aab9a0", - "GDScriptDecomp_d6b31da", - "GDScriptDecomp_1ca61a3", - "GDScriptDecomp_1a36141", - "GDScriptDecomp_514a3fb", - "GDScriptDecomp_7f7d97f", - "GDScriptDecomp_620ec47", - "GDScriptDecomp_c00427a", - "GDScriptDecomp_a60f242", - "GDScriptDecomp_6694c11", - "GDScriptDecomp_506df14", - "GDScriptDecomp_5565f55", - "GDScriptDecomp_f3f05dc", - "GDRECLIMain", + "Glob", + "GodotVer", "GodotREEditorStandalone", "ImportExporter", "ImportInfo", - "NewPackDialog", - "PackDialog", "PckDumper", - "ScriptCompDialog", - "ScriptDecompDialog", + "ImportExporter", + "ResourceCompatLoader", + "Exporter", + "ResourceExporter", + "CustomDecryptor", + "AESContextGDRE", + "CamelliaContext", + "AriaContext", + "GDRESettings", + "GDREConfig", + "PckCreator", ] diff --git a/crypto/crypto_core_gdre.cpp b/crypto/crypto_core_gdre.cpp new file mode 100644 index 00000000..d7d2eed3 --- /dev/null +++ b/crypto/crypto_core_gdre.cpp @@ -0,0 +1,112 @@ +#include "crypto_core_gdre.h" + +#include "core/os/memory.h" + +#include +#include + +// AES256 +CryptoCoreGdre::CamelliaContext::CamelliaContext() { + ctx = memalloc(sizeof(mbedtls_camellia_context)); + mbedtls_camellia_init((mbedtls_camellia_context *)ctx); +} + +CryptoCoreGdre::CamelliaContext::~CamelliaContext() { + mbedtls_camellia_free((mbedtls_camellia_context *)ctx); + memfree((mbedtls_camellia_context *)ctx); +} + +Error CryptoCoreGdre::CamelliaContext::set_encode_key(const uint8_t *p_key, size_t p_bits) { + int ret = mbedtls_camellia_setkey_enc((mbedtls_camellia_context *)ctx, p_key, p_bits); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::CamelliaContext::set_decode_key(const uint8_t *p_key, size_t p_bits) { + int ret = mbedtls_camellia_setkey_dec((mbedtls_camellia_context *)ctx, p_key, p_bits); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::CamelliaContext::encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { + int ret = mbedtls_camellia_crypt_ecb((mbedtls_camellia_context *)ctx, MBEDTLS_CAMELLIA_ENCRYPT, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::CamelliaContext::encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + int ret = mbedtls_camellia_crypt_cbc((mbedtls_camellia_context *)ctx, MBEDTLS_CAMELLIA_ENCRYPT, p_length, r_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::CamelliaContext::encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + size_t iv_off = 0; // Ignore and assume 16-byte alignment. + int ret = mbedtls_camellia_crypt_cfb128((mbedtls_camellia_context *)ctx, MBEDTLS_CAMELLIA_ENCRYPT, p_length, &iv_off, p_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::CamelliaContext::decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { + int ret = mbedtls_camellia_crypt_ecb((mbedtls_camellia_context *)ctx, MBEDTLS_CAMELLIA_DECRYPT, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::CamelliaContext::decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + int ret = mbedtls_camellia_crypt_cbc((mbedtls_camellia_context *)ctx, MBEDTLS_CAMELLIA_DECRYPT, p_length, r_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::CamelliaContext::decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + size_t iv_off = 0; // Ignore and assume 16-byte alignment. + int ret = mbedtls_camellia_crypt_cfb128((mbedtls_camellia_context *)ctx, MBEDTLS_CAMELLIA_DECRYPT, p_length, &iv_off, p_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +// ARIA +CryptoCoreGdre::AriaContext::AriaContext() { + ctx = memalloc(sizeof(mbedtls_aria_context)); + mbedtls_aria_init((mbedtls_aria_context *)ctx); +} + +CryptoCoreGdre::AriaContext::~AriaContext() { + mbedtls_aria_free((mbedtls_aria_context *)ctx); + memfree((mbedtls_aria_context *)ctx); +} + +Error CryptoCoreGdre::AriaContext::set_encode_key(const uint8_t *p_key, size_t p_bits) { + int ret = mbedtls_aria_setkey_enc((mbedtls_aria_context *)ctx, p_key, p_bits); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::AriaContext::set_decode_key(const uint8_t *p_key, size_t p_bits) { + int ret = mbedtls_aria_setkey_dec((mbedtls_aria_context *)ctx, p_key, p_bits); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::AriaContext::encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { + int ret = mbedtls_aria_crypt_ecb((mbedtls_aria_context *)ctx, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::AriaContext::encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + int ret = mbedtls_aria_crypt_cbc((mbedtls_aria_context *)ctx, MBEDTLS_ARIA_ENCRYPT, p_length, r_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::AriaContext::encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + size_t iv_off = 0; // Ignore and assume 16-byte alignment. + int ret = mbedtls_aria_crypt_cfb128((mbedtls_aria_context *)ctx, MBEDTLS_ARIA_ENCRYPT, p_length, &iv_off, p_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::AriaContext::decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { + int ret = mbedtls_aria_crypt_ecb((mbedtls_aria_context *)ctx, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::AriaContext::decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + int ret = mbedtls_aria_crypt_cbc((mbedtls_aria_context *)ctx, MBEDTLS_ARIA_DECRYPT, p_length, r_iv, p_src, r_dst); + return ret ? FAILED : OK; +} + +Error CryptoCoreGdre::AriaContext::decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { + size_t iv_off = 0; // Ignore and assume 16-byte alignment. + int ret = mbedtls_aria_crypt_cfb128((mbedtls_aria_context *)ctx, MBEDTLS_ARIA_DECRYPT, p_length, &iv_off, p_iv, p_src, r_dst); + return ret ? FAILED : OK; +} diff --git a/crypto/crypto_core_gdre.h b/crypto/crypto_core_gdre.h new file mode 100644 index 00000000..0497473d --- /dev/null +++ b/crypto/crypto_core_gdre.h @@ -0,0 +1,42 @@ +#pragma once + +#include "core/typedefs.h" + +class CryptoCoreGdre { +public: + class CamelliaContext { + private: + void *ctx = nullptr; + + public: + CamelliaContext(); + ~CamelliaContext(); + + Error set_encode_key(const uint8_t *p_key, size_t p_bits); + Error set_decode_key(const uint8_t *p_key, size_t p_bits); + Error encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); + Error decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); + Error encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst); + }; + + class AriaContext { + private: + void *ctx = nullptr; + + public: + AriaContext(); + ~AriaContext(); + + Error set_encode_key(const uint8_t *p_key, size_t p_bits); + Error set_decode_key(const uint8_t *p_key, size_t p_bits); + Error encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); + Error decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); + Error encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst); + Error decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst); + }; +}; diff --git a/crypto/crypto_core_gdre_contexts.cpp b/crypto/crypto_core_gdre_contexts.cpp new file mode 100644 index 00000000..67dfde7e --- /dev/null +++ b/crypto/crypto_core_gdre_contexts.cpp @@ -0,0 +1,309 @@ +/**************************************************************************/ +/* crypto_core_gdre_contexts.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "crypto_core_gdre_contexts.h" + +// AESContext with CFB support +Error AESContextGDRE::start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv) { + ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AESContextGDRE already started. Call 'finish' before starting a new one."); + ERR_FAIL_COND_V_MSG(p_mode < 0 || p_mode >= MODE_MAX, ERR_INVALID_PARAMETER, "Invalid mode requested."); + // Key check. + int key_bits = p_key.size() << 3; + ERR_FAIL_COND_V_MSG(key_bits != 128 && key_bits != 192 && key_bits != 256, ERR_INVALID_PARAMETER, "Camellia key must be either 16, 24, or 32 bytes"); + // Initialization vector. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_CBC_DECRYPT || p_mode == MODE_CFB_ENCRYPT || p_mode == MODE_CFB_DECRYPT) { + ERR_FAIL_COND_V_MSG(p_iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes."); + iv.resize(0); + iv.append_array(p_iv); + } + // Encryption/decryption key. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_ECB_ENCRYPT || p_mode == MODE_CFB_ENCRYPT || p_mode == MODE_CFB_DECRYPT) { + ctx.set_encode_key(p_key.ptr(), key_bits); + } else { + ctx.set_decode_key(p_key.ptr(), key_bits); + } + mode = p_mode; + return OK; +} + +PackedByteArray AESContextGDRE::update(const PackedByteArray &p_src) { + ERR_FAIL_COND_V_MSG(mode < 0 || mode >= MODE_MAX, PackedByteArray(), "AESContextGDRE not started. Call 'start' before calling 'update'."); + int len = p_src.size(); + ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); + PackedByteArray out; + out.resize(len); + const uint8_t *src_ptr = p_src.ptr(); + uint8_t *out_ptr = out.ptrw(); + switch (mode) { + case MODE_ECB_ENCRYPT: { + for (int i = 0; i < len; i += 16) { + Error err = ctx.encrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_ECB_DECRYPT: { + for (int i = 0; i < len; i += 16) { + Error err = ctx.decrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_CBC_ENCRYPT: { + Error err = ctx.encrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CBC_DECRYPT: { + Error err = ctx.decrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CFB_ENCRYPT: { + Error err = ctx.encrypt_cfb(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CFB_DECRYPT: { + Error err = ctx.decrypt_cfb(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + default: + ERR_FAIL_V_MSG(PackedByteArray(), "Bug!"); + } + return out; +} + +PackedByteArray AESContextGDRE::get_iv_state() { + ERR_FAIL_COND_V_MSG(mode != MODE_CBC_ENCRYPT && mode != MODE_CBC_DECRYPT && mode != MODE_CFB_ENCRYPT && mode != MODE_CFB_DECRYPT, PackedByteArray(), "Calling 'get_iv_state' only makes sense when the context is started in CBC or CFB mode."); + PackedByteArray out; + out.append_array(iv); + return out; +} + +void AESContextGDRE::finish() { + mode = MODE_MAX; + iv.resize(0); +} + +void AESContextGDRE::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "mode", "key", "iv"), &AESContextGDRE::start, DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("update", "src"), &AESContextGDRE::update); + ClassDB::bind_method(D_METHOD("get_iv_state"), &AESContextGDRE::get_iv_state); + ClassDB::bind_method(D_METHOD("finish"), &AESContextGDRE::finish); + BIND_ENUM_CONSTANT(MODE_ECB_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_ECB_DECRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_DECRYPT); + BIND_ENUM_CONSTANT(MODE_CFB_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_CFB_DECRYPT); + BIND_ENUM_CONSTANT(MODE_MAX); +} + +Error CamelliaContext::start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv) { + ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "CamelliaContext already started. Call 'finish' before starting a new one."); + ERR_FAIL_COND_V_MSG(p_mode < 0 || p_mode >= MODE_MAX, ERR_INVALID_PARAMETER, "Invalid mode requested."); + // Key check. + int key_bits = p_key.size() << 3; + ERR_FAIL_COND_V_MSG(key_bits != 128 && key_bits != 192 && key_bits != 256, ERR_INVALID_PARAMETER, "Camellia key must be either 16, 24, or 32 bytes"); + // Initialization vector. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_CBC_DECRYPT || p_mode == MODE_CFB_ENCRYPT || p_mode == MODE_CFB_DECRYPT) { + ERR_FAIL_COND_V_MSG(p_iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes."); + iv.resize(0); + iv.append_array(p_iv); + } + // Encryption/decryption key. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_ECB_ENCRYPT || p_mode == MODE_CFB_ENCRYPT || p_mode == MODE_CFB_DECRYPT) { + ctx.set_encode_key(p_key.ptr(), key_bits); + } else { + ctx.set_decode_key(p_key.ptr(), key_bits); + } + mode = p_mode; + return OK; +} + +PackedByteArray CamelliaContext::update(const PackedByteArray &p_src) { + ERR_FAIL_COND_V_MSG(mode < 0 || mode >= MODE_MAX, PackedByteArray(), "CamelliaContext not started. Call 'start' before calling 'update'."); + int len = p_src.size(); + ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); + PackedByteArray out; + out.resize(len); + const uint8_t *src_ptr = p_src.ptr(); + uint8_t *out_ptr = out.ptrw(); + switch (mode) { + case MODE_ECB_ENCRYPT: { + for (int i = 0; i < len; i += 16) { + Error err = ctx.encrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_ECB_DECRYPT: { + for (int i = 0; i < len; i += 16) { + Error err = ctx.decrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_CBC_ENCRYPT: { + Error err = ctx.encrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CBC_DECRYPT: { + Error err = ctx.decrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CFB_ENCRYPT: { + Error err = ctx.encrypt_cfb(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CFB_DECRYPT: { + Error err = ctx.decrypt_cfb(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + default: + ERR_FAIL_V_MSG(PackedByteArray(), "Bug!"); + } + return out; +} + +PackedByteArray CamelliaContext::get_iv_state() { + ERR_FAIL_COND_V_MSG(mode != MODE_CBC_ENCRYPT && mode != MODE_CBC_DECRYPT && mode != MODE_CFB_ENCRYPT && mode != MODE_CFB_DECRYPT, PackedByteArray(), "Calling 'get_iv_state' only makes sense when the context is started in CBC or CFB mode."); + PackedByteArray out; + out.append_array(iv); + return out; +} + +void CamelliaContext::finish() { + mode = MODE_MAX; + iv.resize(0); +} + +void CamelliaContext::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "mode", "key", "iv"), &CamelliaContext::start, DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("update", "src"), &CamelliaContext::update); + ClassDB::bind_method(D_METHOD("get_iv_state"), &CamelliaContext::get_iv_state); + ClassDB::bind_method(D_METHOD("finish"), &CamelliaContext::finish); + BIND_ENUM_CONSTANT(MODE_ECB_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_ECB_DECRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_DECRYPT); + BIND_ENUM_CONSTANT(MODE_CFB_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_CFB_DECRYPT); + BIND_ENUM_CONSTANT(MODE_MAX); +} + +// ARIA +Error AriaContext::start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv) { + ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AriaContext already started. Call 'finish' before starting a new one."); + ERR_FAIL_COND_V_MSG(p_mode < 0 || p_mode >= MODE_MAX, ERR_INVALID_PARAMETER, "Invalid mode requested."); + // Key check. + int key_bits = p_key.size() << 3; + ERR_FAIL_COND_V_MSG(key_bits != 128 && key_bits != 192 && key_bits != 256, ERR_INVALID_PARAMETER, "ARIA key must be 16, 24, or 32 bytes (128, 192, or 256 bits)"); + // Initialization vector. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_CBC_DECRYPT || p_mode == MODE_CFB_ENCRYPT || p_mode == MODE_CFB_DECRYPT) { + ERR_FAIL_COND_V_MSG(p_iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes."); + iv.resize(0); + iv.append_array(p_iv); + } + // Encryption/decryption key. + if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_ECB_ENCRYPT || p_mode == MODE_CFB_ENCRYPT || p_mode == MODE_CFB_DECRYPT) { + ctx.set_encode_key(p_key.ptr(), key_bits); + } else { + ctx.set_decode_key(p_key.ptr(), key_bits); + } + mode = p_mode; + return OK; +} + +PackedByteArray AriaContext::update(const PackedByteArray &p_src) { + ERR_FAIL_COND_V_MSG(mode < 0 || mode >= MODE_MAX, PackedByteArray(), "AriaContext not started. Call 'start' before calling 'update'."); + int len = p_src.size(); + PackedByteArray out; + out.resize(len); + const uint8_t *src_ptr = p_src.ptr(); + uint8_t *out_ptr = out.ptrw(); + switch (mode) { + case MODE_ECB_ENCRYPT: { + ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); + for (int i = 0; i < len; i += 16) { + Error err = ctx.encrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_ECB_DECRYPT: { + ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be decrypted must be multiple of 16. Add padding if needed"); + for (int i = 0; i < len; i += 16) { + Error err = ctx.decrypt_ecb(src_ptr + i, out_ptr + i); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } + } break; + case MODE_CBC_ENCRYPT: { + ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); + Error err = ctx.encrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CBC_DECRYPT: { + ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be decrypted must be multiple of 16. Add padding if needed"); + Error err = ctx.decrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CFB_ENCRYPT: { + Error err = ctx.encrypt_cfb(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + case MODE_CFB_DECRYPT: { + Error err = ctx.decrypt_cfb(len, iv.ptrw(), p_src.ptr(), out.ptrw()); + ERR_FAIL_COND_V(err != OK, PackedByteArray()); + } break; + default: + ERR_FAIL_V_MSG(PackedByteArray(), "Bug!"); + } + return out; +} + +PackedByteArray AriaContext::get_iv_state() { + ERR_FAIL_COND_V_MSG(mode != MODE_CBC_ENCRYPT && mode != MODE_CBC_DECRYPT && mode != MODE_CFB_ENCRYPT && mode != MODE_CFB_DECRYPT, PackedByteArray(), "Calling 'get_iv_state' only makes sense when the context is started in CBC or CFB mode."); + PackedByteArray out; + out.append_array(iv); + return out; +} + +void AriaContext::finish() { + mode = MODE_MAX; + iv.resize(0); +} + +void AriaContext::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "mode", "key", "iv"), &AriaContext::start, DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("update", "src"), &AriaContext::update); + ClassDB::bind_method(D_METHOD("get_iv_state"), &AriaContext::get_iv_state); + ClassDB::bind_method(D_METHOD("finish"), &AriaContext::finish); + BIND_ENUM_CONSTANT(MODE_ECB_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_ECB_DECRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_CBC_DECRYPT); + BIND_ENUM_CONSTANT(MODE_CFB_ENCRYPT); + BIND_ENUM_CONSTANT(MODE_CFB_DECRYPT); + BIND_ENUM_CONSTANT(MODE_MAX); +} diff --git a/crypto/crypto_core_gdre_contexts.h b/crypto/crypto_core_gdre_contexts.h new file mode 100644 index 00000000..1418e916 --- /dev/null +++ b/crypto/crypto_core_gdre_contexts.h @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* crypto_core_gdre_contexts.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include "core/crypto/crypto_core.h" +#include "core/object/ref_counted.h" +#include "crypto/crypto_core_gdre.h" + +class AESContextGDRE : public RefCounted { + GDCLASS(AESContextGDRE, RefCounted); + +public: + enum Mode : int32_t { + MODE_ECB_ENCRYPT, + MODE_ECB_DECRYPT, + MODE_CBC_ENCRYPT, + MODE_CBC_DECRYPT, + MODE_CFB_ENCRYPT, + MODE_CFB_DECRYPT, + MODE_MAX + }; + +private: + Mode mode = MODE_MAX; + CryptoCore::AESContext ctx; + PackedByteArray iv; + +protected: + static void _bind_methods(); + +public: + Error start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv = PackedByteArray()); + PackedByteArray update(const PackedByteArray &p_src); + PackedByteArray get_iv_state(); + void finish(); +}; + +VARIANT_ENUM_CAST(AESContextGDRE::Mode); + +class CamelliaContext : public RefCounted { + GDCLASS(CamelliaContext, RefCounted); + +public: + enum Mode : int32_t { + MODE_ECB_ENCRYPT, + MODE_ECB_DECRYPT, + MODE_CBC_ENCRYPT, + MODE_CBC_DECRYPT, + MODE_CFB_ENCRYPT, + MODE_CFB_DECRYPT, + MODE_MAX + }; + +private: + Mode mode = MODE_MAX; + CryptoCoreGdre::CamelliaContext ctx; + PackedByteArray iv; + +protected: + static void _bind_methods(); + +public: + Error start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv = PackedByteArray()); + PackedByteArray update(const PackedByteArray &p_src); + PackedByteArray get_iv_state(); + void finish(); +}; + +VARIANT_ENUM_CAST(CamelliaContext::Mode); + +class AriaContext : public RefCounted { + GDCLASS(AriaContext, RefCounted); + +public: + enum Mode : int32_t { + MODE_ECB_ENCRYPT, + MODE_ECB_DECRYPT, + MODE_CBC_ENCRYPT, + MODE_CBC_DECRYPT, + MODE_CFB_ENCRYPT, + MODE_CFB_DECRYPT, + MODE_MAX + }; + +private: + Mode mode = MODE_MAX; + CryptoCoreGdre::AriaContext ctx; + PackedByteArray iv; + +protected: + static void _bind_methods(); + +public: + Error start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv = PackedByteArray()); + PackedByteArray update(const PackedByteArray &p_src); + PackedByteArray get_iv_state(); + void finish(); +}; + +VARIANT_ENUM_CAST(AriaContext::Mode); diff --git a/crypto/custom_decryptor.cpp b/crypto/custom_decryptor.cpp new file mode 100644 index 00000000..dddff8ae --- /dev/null +++ b/crypto/custom_decryptor.cpp @@ -0,0 +1,13 @@ +#include "custom_decryptor.h" +#include "core/variant/variant.h" + +void CustomDecryptor::_bind_methods() { + ClassDB::bind_method(D_METHOD("parse_and_decrypt", "file", "key", "non_pack_file"), &CustomDecryptor::parse_and_decrypt); + GDVIRTUAL_BIND(_parse_and_decrypt, "file", "key", "non_pack_file"); +} + +Dictionary CustomDecryptor::parse_and_decrypt(Ref p_file, const Vector &p_key, bool p_non_pack_file) { + Dictionary result; + GDVIRTUAL_CALL(_parse_and_decrypt, p_file, p_key, p_non_pack_file, result); + return result; +} diff --git a/crypto/custom_decryptor.h b/crypto/custom_decryptor.h new file mode 100644 index 00000000..dccdc2ef --- /dev/null +++ b/crypto/custom_decryptor.h @@ -0,0 +1,21 @@ +#pragma once +#include "core/io/file_access.h" +#include "core/object/gdvirtual.gen.inc" +#include "core/object/ref_counted.h" +#include "core/variant/variant.h" + +class CustomDecryptor : public RefCounted { + GDCLASS(CustomDecryptor, RefCounted); + +protected: + static void _bind_methods(); + +public: + virtual Dictionary parse_and_decrypt(Ref p_file, const Vector &p_key, bool p_non_pack_file); + virtual ~CustomDecryptor() {} + // Dictionary result must have the following keys: + // - error: Error + // - length: int + // - data: PackedByteArray + GDVIRTUAL3R(Dictionary, _parse_and_decrypt, Ref, const Vector &, bool); +}; diff --git a/crypto/file_access_encrypted_custom.cpp b/crypto/file_access_encrypted_custom.cpp new file mode 100644 index 00000000..4d68c30d --- /dev/null +++ b/crypto/file_access_encrypted_custom.cpp @@ -0,0 +1,272 @@ +/**************************************************************************/ +/* file_access_encrypted.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "file_access_encrypted_custom.h" + +#include "core/error/error_list.h" +#include "core/variant/variant.h" + +#include + +#include "crypto/crypto_core_gdre.h" + +CryptoCore::RandomGenerator *FileAccessEncryptedCustom::_fae_static_rng = nullptr; + +void FileAccessEncryptedCustom::deinitialize() { + if (_fae_static_rng) { + memdelete(_fae_static_rng); + _fae_static_rng = nullptr; + } +} + +Error FileAccessEncryptedCustom::open_and_parse(Ref p_base, const Vector &p_key, Mode p_mode, bool p_with_magic, const Vector &p_iv) { + ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, vformat("Can't open file while another file from path '%s' is open.", file->get_path_absolute())); + ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); + + pos = 0; + eofed = false; + use_magic = p_with_magic; + + if (p_mode == MODE_WRITE_CUSTOM) { + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Writing to a custom encrypted file is not supported."); + } else if (p_mode == MODE_READ) { + ERR_FAIL_NULL_V(decryptor, ERR_UNCONFIGURED); + writing = false; + key = p_key; + Dictionary result = decryptor->parse_and_decrypt(p_base, key, use_magic); + ERR_FAIL_COND_V_MSG(result.is_empty(), ERR_BUG, "Decryptor did not return a result."); + ERR_FAIL_COND_V_MSG(!result.has("error") || !result.has("length") || !result.has("data"), ERR_BUG, "Decryptor result is missing required keys;\nEnsure that the decryptor returns a Dictionary with the following keys: 'error', 'length', and 'data'."); + Error err = result["error"]; + ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to decrypt data."); + length = result["length"]; + data = result["data"]; + ERR_FAIL_COND_V_MSG(length != data.size(), ERR_UNAUTHORIZED, "Decrypted data size mismatch; 'length' does not match data.size()"); + file = p_base; + } + + return OK; +} + +Error FileAccessEncryptedCustom::open_and_parse_password(Ref p_base, const String &p_key, Mode p_mode) { + String cs = p_key.md5_text(); + ERR_FAIL_COND_V(cs.length() != 32, ERR_INVALID_PARAMETER); + Vector key_md5; + key_md5.resize(32); + for (int i = 0; i < 32; i++) { + key_md5.write[i] = cs[i]; + } + + return open_and_parse(p_base, key_md5, p_mode); +} + +Error FileAccessEncryptedCustom::open_internal(const String &p_path, int p_mode_flags) { + return OK; +} + +void FileAccessEncryptedCustom::_close() { + if (file.is_null()) { + return; + } + + file.unref(); +} + +bool FileAccessEncryptedCustom::is_open() const { + return file.is_valid(); +} + +String FileAccessEncryptedCustom::get_path() const { + if (file.is_valid()) { + return file->get_path(); + } else { + return ""; + } +} + +String FileAccessEncryptedCustom::get_path_absolute() const { + if (file.is_valid()) { + return file->get_path_absolute(); + } else { + return ""; + } +} + +void FileAccessEncryptedCustom::seek(uint64_t p_position) { + if (p_position > get_length()) { + p_position = get_length(); + } + + pos = p_position; + eofed = false; +} + +void FileAccessEncryptedCustom::seek_end(int64_t p_position) { + seek(get_length() + p_position); +} + +uint64_t FileAccessEncryptedCustom::get_position() const { + return pos; +} + +uint64_t FileAccessEncryptedCustom::get_length() const { + return data.size(); +} + +bool FileAccessEncryptedCustom::eof_reached() const { + return eofed; +} + +uint64_t FileAccessEncryptedCustom::get_buffer(uint8_t *p_dst, uint64_t p_length) const { + ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode."); + + if (!p_length) { + return 0; + } + + ERR_FAIL_NULL_V(p_dst, -1); + + uint64_t to_copy = MIN(p_length, get_length() - pos); + + memcpy(p_dst, data.ptr() + pos, to_copy); + pos += to_copy; + + if (to_copy < p_length) { + eofed = true; + } + + return to_copy; +} + +Error FileAccessEncryptedCustom::get_error() const { + return eofed ? ERR_FILE_EOF : OK; +} + +bool FileAccessEncryptedCustom::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_V_MSG(false, "Writing to a custom encrypted file is not supported."); +} + +void FileAccessEncryptedCustom::flush() { + ERR_FAIL_MSG("Writing to a custom encrypted file is not supported."); + + // encrypted files keep data in memory till close() +} + +bool FileAccessEncryptedCustom::file_exists(const String &p_name) { + Ref fa = FileAccess::open(p_name, FileAccess::READ); + if (fa.is_null()) { + return false; + } + return true; +} + +uint64_t FileAccessEncryptedCustom::_get_modified_time(const String &p_file) { + if (file.is_valid()) { + return file->get_modified_time(p_file); + } else { + return 0; + } +} + +uint64_t FileAccessEncryptedCustom::_get_access_time(const String &p_file) { + if (file.is_valid()) { + return file->get_access_time(p_file); + } else { + return 0; + } +} + +int64_t FileAccessEncryptedCustom::_get_size(const String &p_file) { + if (file.is_valid()) { + return file->get_size(p_file); + } else { + return -1; + } +} + +BitField FileAccessEncryptedCustom::_get_unix_permissions(const String &p_file) { + if (file.is_valid()) { + return file->_get_unix_permissions(p_file); + } + return 0; +} + +Error FileAccessEncryptedCustom::_set_unix_permissions(const String &p_file, BitField p_permissions) { + if (file.is_valid()) { + return file->_set_unix_permissions(p_file, p_permissions); + } + return FAILED; +} + +bool FileAccessEncryptedCustom::_get_hidden_attribute(const String &p_file) { + if (file.is_valid()) { + return file->_get_hidden_attribute(p_file); + } + return false; +} + +Error FileAccessEncryptedCustom::_set_hidden_attribute(const String &p_file, bool p_hidden) { + if (file.is_valid()) { + return file->_set_hidden_attribute(p_file, p_hidden); + } + return FAILED; +} + +bool FileAccessEncryptedCustom::_get_read_only_attribute(const String &p_file) { + if (file.is_valid()) { + return file->_get_read_only_attribute(p_file); + } + return false; +} + +Error FileAccessEncryptedCustom::_set_read_only_attribute(const String &p_file, bool p_ro) { + if (file.is_valid()) { + return file->_set_read_only_attribute(p_file, p_ro); + } + return FAILED; +} + +void FileAccessEncryptedCustom::set_decryptor(Ref p_decryptor) { + decryptor = p_decryptor; +} + +void FileAccessEncryptedCustom::close() { + _close(); +} + +Ref FileAccessEncryptedCustom::create(Ref p_decryptor) { + Ref fae; + fae.instantiate(); + fae->set_decryptor(p_decryptor); + return fae; +} + +FileAccessEncryptedCustom::~FileAccessEncryptedCustom() { + _close(); +} diff --git a/crypto/file_access_encrypted_custom.h b/crypto/file_access_encrypted_custom.h new file mode 100644 index 00000000..9b88933e --- /dev/null +++ b/crypto/file_access_encrypted_custom.h @@ -0,0 +1,125 @@ +/**************************************************************************/ +/* file_access_encrypted_custom.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#pragma once + +#include "core/crypto/crypto_core.h" +#include "core/io/file_access.h" +#include "crypto/custom_decryptor.h" + +#define ENCRYPTED_HEADER_MAGIC 0x43454447 + +class FileAccessEncryptedCustom : public FileAccess { + GDSOFTCLASS(FileAccessEncryptedCustom, FileAccess); + +public: + enum Mode : int32_t { + MODE_READ, + MODE_WRITE_CUSTOM, + MODE_MAX + }; + +private: + Vector iv; + Vector key; + bool writing = false; + Ref file; + uint64_t length = 0; + Vector data; + mutable uint64_t pos = 0; + mutable bool eofed = false; + bool use_magic = true; + Ref decryptor; + + void _close(); + + static CryptoCore::RandomGenerator *_fae_static_rng; + +public: + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file + virtual bool is_open() const override; ///< true when file is open + + virtual String get_path() const override; /// returns the path for the current open file + virtual String get_path_absolute() const override; /// returns the absolute path for the current open file + + virtual void seek(uint64_t p_position) override; ///< seek to a given position + virtual void seek_end(int64_t p_position = 0) override; ///< seek from the end of file + virtual uint64_t get_position() const override; ///< get position in the file + virtual uint64_t get_length() const override; ///< get size of the file + + virtual bool eof_reached() const override; ///< reading passed EOF + + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; + + virtual Error get_error() const override; ///< get last error + + virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } + virtual void flush() override; + virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes + + virtual bool file_exists(const String &p_name) override; ///< return true if a file exists + + virtual uint64_t _get_modified_time(const String &p_file) override; + virtual uint64_t _get_access_time(const String &p_file) override; + virtual int64_t _get_size(const String &p_file) override; + virtual BitField _get_unix_permissions(const String &p_file) override; + virtual Error _set_unix_permissions(const String &p_file, BitField p_permissions) override; + + virtual bool _get_hidden_attribute(const String &p_file) override; + virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override; + virtual bool _get_read_only_attribute(const String &p_file) override; + virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override; + + virtual void close() override; + + Error open_and_parse(Ref p_base, const Vector &p_key, Mode p_mode, bool p_with_magic = true, const Vector &p_iv = Vector()); + Error open_and_parse_password(Ref p_base, const String &p_key, Mode p_mode); + + Vector get_iv() const { return iv; } + void set_iv(const Vector &p_iv) { iv = p_iv; } + + Ref get_base_file() const { return file; } + void set_base_file(Ref p_file) { file = p_file; } + + Vector get_key() const { return key; } + void set_key(const Vector &p_key) { key = p_key; } + + void set_length(uint64_t p_length) { length = p_length; } + + bool get_use_magic() const { return use_magic; } + + static void deinitialize(); + + void set_decryptor(Ref p_decryptor); + + static Ref create(Ref p_decryptor); + + ~FileAccessEncryptedCustom(); +}; diff --git a/doc_classes/AESContextGDRE.xml b/doc_classes/AESContextGDRE.xml new file mode 100644 index 00000000..121c01ef --- /dev/null +++ b/doc_classes/AESContextGDRE.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/AriaContext.xml b/doc_classes/AriaContext.xml new file mode 100644 index 00000000..604064ca --- /dev/null +++ b/doc_classes/AriaContext.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/CamelliaContext.xml b/doc_classes/CamelliaContext.xml new file mode 100644 index 00000000..99aa6f1a --- /dev/null +++ b/doc_classes/CamelliaContext.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/CustomDecryptor.xml b/doc_classes/CustomDecryptor.xml new file mode 100644 index 00000000..7a2d38f0 --- /dev/null +++ b/doc_classes/CustomDecryptor.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/Exporter.xml b/doc_classes/Exporter.xml new file mode 100644 index 00000000..37bf857d --- /dev/null +++ b/doc_classes/Exporter.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/GDREConfig.xml b/doc_classes/GDREConfig.xml new file mode 100644 index 00000000..30d87e88 --- /dev/null +++ b/doc_classes/GDREConfig.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/GDRESettings.xml b/doc_classes/GDRESettings.xml new file mode 100644 index 00000000..8eae3348 --- /dev/null +++ b/doc_classes/GDRESettings.xml @@ -0,0 +1,440 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/GDScriptDecomp.xml b/doc_classes/GDScriptDecomp.xml index 4f45e212..e0367e36 100644 --- a/doc_classes/GDScriptDecomp.xml +++ b/doc_classes/GDScriptDecomp.xml @@ -1,5 +1,5 @@ - + @@ -7,16 +7,73 @@ + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -25,10 +82,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/GDScriptDecomp_015d36d.xml b/doc_classes/GDScriptDecomp_015d36d.xml deleted file mode 100644 index 1bc3fbe6..00000000 --- a/doc_classes/GDScriptDecomp_015d36d.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_054a2ac.xml b/doc_classes/GDScriptDecomp_054a2ac.xml deleted file mode 100644 index da961027..00000000 --- a/doc_classes/GDScriptDecomp_054a2ac.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_0b806ee.xml b/doc_classes/GDScriptDecomp_0b806ee.xml deleted file mode 100644 index 0fec35e4..00000000 --- a/doc_classes/GDScriptDecomp_0b806ee.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_1a36141.xml b/doc_classes/GDScriptDecomp_1a36141.xml deleted file mode 100644 index e1f1e600..00000000 --- a/doc_classes/GDScriptDecomp_1a36141.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_1add52b.xml b/doc_classes/GDScriptDecomp_1add52b.xml deleted file mode 100644 index 93dc25e6..00000000 --- a/doc_classes/GDScriptDecomp_1add52b.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_1ca61a3.xml b/doc_classes/GDScriptDecomp_1ca61a3.xml deleted file mode 100644 index 06df413d..00000000 --- a/doc_classes/GDScriptDecomp_1ca61a3.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_216a8aa.xml b/doc_classes/GDScriptDecomp_216a8aa.xml deleted file mode 100644 index 87b5ad92..00000000 --- a/doc_classes/GDScriptDecomp_216a8aa.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_2185c01.xml b/doc_classes/GDScriptDecomp_2185c01.xml deleted file mode 100644 index 2ee1bfcd..00000000 --- a/doc_classes/GDScriptDecomp_2185c01.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_23381a5.xml b/doc_classes/GDScriptDecomp_23381a5.xml deleted file mode 100644 index 1880cb67..00000000 --- a/doc_classes/GDScriptDecomp_23381a5.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_23441ec.xml b/doc_classes/GDScriptDecomp_23441ec.xml deleted file mode 100644 index ed38dc4d..00000000 --- a/doc_classes/GDScriptDecomp_23441ec.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_30c1229.xml b/doc_classes/GDScriptDecomp_30c1229.xml deleted file mode 100644 index 6de0c39f..00000000 --- a/doc_classes/GDScriptDecomp_30c1229.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_31ce3c5.xml b/doc_classes/GDScriptDecomp_31ce3c5.xml deleted file mode 100644 index 64727a9d..00000000 --- a/doc_classes/GDScriptDecomp_31ce3c5.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_3ea6d9f.xml b/doc_classes/GDScriptDecomp_3ea6d9f.xml deleted file mode 100644 index 6ee5a06d..00000000 --- a/doc_classes/GDScriptDecomp_3ea6d9f.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_48f1d02.xml b/doc_classes/GDScriptDecomp_48f1d02.xml deleted file mode 100644 index ceb2e399..00000000 --- a/doc_classes/GDScriptDecomp_48f1d02.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_4ee82a2.xml b/doc_classes/GDScriptDecomp_4ee82a2.xml deleted file mode 100644 index 87c3c567..00000000 --- a/doc_classes/GDScriptDecomp_4ee82a2.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_506df14.xml b/doc_classes/GDScriptDecomp_506df14.xml deleted file mode 100644 index 7e518207..00000000 --- a/doc_classes/GDScriptDecomp_506df14.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_513c026.xml b/doc_classes/GDScriptDecomp_513c026.xml deleted file mode 100644 index 2669f06d..00000000 --- a/doc_classes/GDScriptDecomp_513c026.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_514a3fb.xml b/doc_classes/GDScriptDecomp_514a3fb.xml deleted file mode 100644 index ccad19b7..00000000 --- a/doc_classes/GDScriptDecomp_514a3fb.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_5565f55.xml b/doc_classes/GDScriptDecomp_5565f55.xml deleted file mode 100644 index f94c6043..00000000 --- a/doc_classes/GDScriptDecomp_5565f55.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_5e938f0.xml b/doc_classes/GDScriptDecomp_5e938f0.xml deleted file mode 100644 index fcbf93d0..00000000 --- a/doc_classes/GDScriptDecomp_5e938f0.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_6174585.xml b/doc_classes/GDScriptDecomp_6174585.xml deleted file mode 100644 index 9c693f7e..00000000 --- a/doc_classes/GDScriptDecomp_6174585.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_620ec47.xml b/doc_classes/GDScriptDecomp_620ec47.xml deleted file mode 100644 index d9b183b7..00000000 --- a/doc_classes/GDScriptDecomp_620ec47.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_62273e5.xml b/doc_classes/GDScriptDecomp_62273e5.xml deleted file mode 100644 index 98edabd2..00000000 --- a/doc_classes/GDScriptDecomp_62273e5.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_64872ca.xml b/doc_classes/GDScriptDecomp_64872ca.xml deleted file mode 100644 index a96502f6..00000000 --- a/doc_classes/GDScriptDecomp_64872ca.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_65d48d6.xml b/doc_classes/GDScriptDecomp_65d48d6.xml deleted file mode 100644 index 23cfa7d9..00000000 --- a/doc_classes/GDScriptDecomp_65d48d6.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_6694c11.xml b/doc_classes/GDScriptDecomp_6694c11.xml deleted file mode 100644 index 15f1719d..00000000 --- a/doc_classes/GDScriptDecomp_6694c11.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_703004f.xml b/doc_classes/GDScriptDecomp_703004f.xml deleted file mode 100644 index 5c03b4ad..00000000 --- a/doc_classes/GDScriptDecomp_703004f.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_7124599.xml b/doc_classes/GDScriptDecomp_7124599.xml deleted file mode 100644 index 2f5a2e6a..00000000 --- a/doc_classes/GDScriptDecomp_7124599.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_7d2d144.xml b/doc_classes/GDScriptDecomp_7d2d144.xml deleted file mode 100644 index 45be41cb..00000000 --- a/doc_classes/GDScriptDecomp_7d2d144.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_7f7d97f.xml b/doc_classes/GDScriptDecomp_7f7d97f.xml deleted file mode 100644 index d4a0824b..00000000 --- a/doc_classes/GDScriptDecomp_7f7d97f.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_85585c7.xml b/doc_classes/GDScriptDecomp_85585c7.xml deleted file mode 100644 index 970d554d..00000000 --- a/doc_classes/GDScriptDecomp_85585c7.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_8aab9a0.xml b/doc_classes/GDScriptDecomp_8aab9a0.xml deleted file mode 100644 index 292ad9c9..00000000 --- a/doc_classes/GDScriptDecomp_8aab9a0.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_8b912d1.xml b/doc_classes/GDScriptDecomp_8b912d1.xml deleted file mode 100644 index 8f01d5ab..00000000 --- a/doc_classes/GDScriptDecomp_8b912d1.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_8c1731b.xml b/doc_classes/GDScriptDecomp_8c1731b.xml deleted file mode 100644 index 4ab77c2e..00000000 --- a/doc_classes/GDScriptDecomp_8c1731b.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_8cab401.xml b/doc_classes/GDScriptDecomp_8cab401.xml deleted file mode 100644 index 8ce86bce..00000000 --- a/doc_classes/GDScriptDecomp_8cab401.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_8e35d93.xml b/doc_classes/GDScriptDecomp_8e35d93.xml deleted file mode 100644 index 0eab4758..00000000 --- a/doc_classes/GDScriptDecomp_8e35d93.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_91ca725.xml b/doc_classes/GDScriptDecomp_91ca725.xml deleted file mode 100644 index c198f879..00000000 --- a/doc_classes/GDScriptDecomp_91ca725.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_97f34a1.xml b/doc_classes/GDScriptDecomp_97f34a1.xml deleted file mode 100644 index 771a98ea..00000000 --- a/doc_classes/GDScriptDecomp_97f34a1.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_a3f1ee5.xml b/doc_classes/GDScriptDecomp_a3f1ee5.xml deleted file mode 100644 index 4cfc9d29..00000000 --- a/doc_classes/GDScriptDecomp_a3f1ee5.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_a56d6ff.xml b/doc_classes/GDScriptDecomp_a56d6ff.xml deleted file mode 100644 index 14ff37b5..00000000 --- a/doc_classes/GDScriptDecomp_a56d6ff.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_a60f242.xml b/doc_classes/GDScriptDecomp_a60f242.xml deleted file mode 100644 index 74f3e0db..00000000 --- a/doc_classes/GDScriptDecomp_a60f242.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_a7aad78.xml b/doc_classes/GDScriptDecomp_a7aad78.xml deleted file mode 100644 index 61145e6d..00000000 --- a/doc_classes/GDScriptDecomp_a7aad78.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_be46be7.xml b/doc_classes/GDScriptDecomp_be46be7.xml deleted file mode 100644 index 7400cfcf..00000000 --- a/doc_classes/GDScriptDecomp_be46be7.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_c00427a.xml b/doc_classes/GDScriptDecomp_c00427a.xml deleted file mode 100644 index 9d881fc1..00000000 --- a/doc_classes/GDScriptDecomp_c00427a.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_c24c739.xml b/doc_classes/GDScriptDecomp_c24c739.xml deleted file mode 100644 index e4057e13..00000000 --- a/doc_classes/GDScriptDecomp_c24c739.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_c6120e7.xml b/doc_classes/GDScriptDecomp_c6120e7.xml deleted file mode 100644 index 6f20caf7..00000000 --- a/doc_classes/GDScriptDecomp_c6120e7.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_d28da86.xml b/doc_classes/GDScriptDecomp_d28da86.xml deleted file mode 100644 index c445fd11..00000000 --- a/doc_classes/GDScriptDecomp_d28da86.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_d6b31da.xml b/doc_classes/GDScriptDecomp_d6b31da.xml deleted file mode 100644 index 273a1c12..00000000 --- a/doc_classes/GDScriptDecomp_d6b31da.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_e82dc40.xml b/doc_classes/GDScriptDecomp_e82dc40.xml deleted file mode 100644 index 3413e261..00000000 --- a/doc_classes/GDScriptDecomp_e82dc40.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_ed80f45.xml b/doc_classes/GDScriptDecomp_ed80f45.xml deleted file mode 100644 index 73db5613..00000000 --- a/doc_classes/GDScriptDecomp_ed80f45.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_f3f05dc.xml b/doc_classes/GDScriptDecomp_f3f05dc.xml deleted file mode 100644 index cf81d00f..00000000 --- a/doc_classes/GDScriptDecomp_f3f05dc.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_f8a7c46.xml b/doc_classes/GDScriptDecomp_f8a7c46.xml deleted file mode 100644 index c275252b..00000000 --- a/doc_classes/GDScriptDecomp_f8a7c46.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/GDScriptDecomp_ff1e7cf.xml b/doc_classes/GDScriptDecomp_ff1e7cf.xml deleted file mode 100644 index daeec276..00000000 --- a/doc_classes/GDScriptDecomp_ff1e7cf.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/Glob.xml b/doc_classes/Glob.xml new file mode 100644 index 00000000..6b139253 --- /dev/null +++ b/doc_classes/Glob.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/GodotREEditorStandalone.xml b/doc_classes/GodotREEditorStandalone.xml index 94759f9e..998e4c3d 100644 --- a/doc_classes/GodotREEditorStandalone.xml +++ b/doc_classes/GodotREEditorStandalone.xml @@ -1,5 +1,5 @@ - + @@ -12,12 +12,72 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/GodotVer.xml b/doc_classes/GodotVer.xml new file mode 100644 index 00000000..4f02eebd --- /dev/null +++ b/doc_classes/GodotVer.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/ImportExporter.xml b/doc_classes/ImportExporter.xml index a407f6cd..53b6c290 100644 --- a/doc_classes/ImportExporter.xml +++ b/doc_classes/ImportExporter.xml @@ -1,5 +1,5 @@ - + @@ -7,66 +7,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + - - - - + + - - + + + diff --git a/doc_classes/ImportInfo.xml b/doc_classes/ImportInfo.xml index 2b44dc83..be0ffc2f 100644 --- a/doc_classes/ImportInfo.xml +++ b/doc_classes/ImportInfo.xml @@ -1,5 +1,5 @@ - + @@ -7,45 +7,170 @@ - - + + + - - + + + + + + + + + - + - - + + + + - - + + - + - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/NewPackDialog.xml b/doc_classes/NewPackDialog.xml deleted file mode 100644 index 8b4ad46c..00000000 --- a/doc_classes/NewPackDialog.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/doc_classes/OggStreamLoaderCompat.xml b/doc_classes/OggStreamLoaderCompat.xml deleted file mode 100644 index 32eec51d..00000000 --- a/doc_classes/OggStreamLoaderCompat.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/doc_classes/PackDialog.xml b/doc_classes/PackDialog.xml deleted file mode 100644 index cca4cb03..00000000 --- a/doc_classes/PackDialog.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc_classes/PckCreator.xml b/doc_classes/PckCreator.xml new file mode 100644 index 00000000..c4e6fbf2 --- /dev/null +++ b/doc_classes/PckCreator.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/PckDumper.xml b/doc_classes/PckDumper.xml index 2fa58fec..733a4da5 100644 --- a/doc_classes/PckDumper.xml +++ b/doc_classes/PckDumper.xml @@ -1,5 +1,5 @@ - + @@ -12,47 +12,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/doc_classes/ResourceCompatLoader.xml b/doc_classes/ResourceCompatLoader.xml new file mode 100644 index 00000000..821a1d09 --- /dev/null +++ b/doc_classes/ResourceCompatLoader.xml @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/ResourceExporter.xml b/doc_classes/ResourceExporter.xml new file mode 100644 index 00000000..0ed4e040 --- /dev/null +++ b/doc_classes/ResourceExporter.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc_classes/ScriptCompDialog.xml b/doc_classes/ScriptCompDialog.xml deleted file mode 100644 index 87100741..00000000 --- a/doc_classes/ScriptCompDialog.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc_classes/ScriptDecompDialog.xml b/doc_classes/ScriptDecompDialog.xml deleted file mode 100644 index be387623..00000000 --- a/doc_classes/ScriptDecompDialog.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/custom_decryptors.md b/docs/custom_decryptors.md new file mode 100644 index 00000000..05823f9e --- /dev/null +++ b/docs/custom_decryptors.md @@ -0,0 +1,114 @@ +# A Guide to Writing Custom Decryptor scripts + +If the game you are attempting to recover assets from uses a custom encryption scheme (i.e. not using the standard AES-256-CFB encryption in Godot), you will need to write a custom decryptor script in order to use GDRE Tools to extract and recover the assets. + +## When should you write a custom decryptor? +**In the vast maority of cases, this is not necessary**, and the standard decryption will work with the correct encryption key. + +*Most issues with decryption involve not having the correct key*. The game in question may be applying permutations to the key after it has been loaded to further obscure it, so a key provided by a key extraction program won't work. + +Only pursue this *after* ensuring you have the correct encryption key, and you have verified that the game is not using the standard Godot encryption scheme. This can be done by reverse-engineering the binary in a decompiler like [Ghidra](https://ghidra-sre.org/). + +**Support will not be provided for recovering the correct encryption key**. + +# Writing the script: +The encryption script should be written in Godot 4.5+ GDScript; support for other languages is not provided. + +Each script must extend the `CustomDecryptor` class, and must implement the `_parse_and_decrypt()` method. + +Implementing the `_parse_and_decrypt()` method will require a knowledge of the encryption scheme used by the game, and the file structure of the encrypted files. + +For reference, a custom decryption script that implements the standard Godot encryption scheme is provided in the [`gdre_standard_encryption.gd`](./gdre_standard_encryption.gd) file. + +## File structure: +In most cases, the file structure will be something like this: +- magic (32-bit int, 4 bytes) (optional, not present if it's a file in a PCK) +- md5 (16 bytes) +- length (64-bit int, 8 bytes) +- iv (16 bytes) +- data (`length` bytes + padding to an alignment, usually 16 bytes; all encryption contexts that we provide require 16 byte alignment) + +## Encryption scheme: +In most cases, the encryption scheme will be something like this: +- AES-256-CFB +- Camellia-256-CFB +- Aria-256-CFB + +## Basic decryption flow: +1. Read the magic number (if present) +2. Read the md5 hash, length, and iv +3. Read the data (length bytes + padding) +4. Decrypt the data using the encryption context +5. Verify the md5 hash of the decrypted data +6. Return the decrypted data + +## Script ingestion: +A path to the custom decryption script to be used for project recovery can be set using either the `--custom-decryption-script` command line argument, or by setting it in the GUI in the "Set Encryption Key" menu option. + +## IMPORTANT NOTE: +If you are intending to distribute your decryption script, it is **strongly** recommended to not include any secrets (encryption keys, xor keys, etc.) in the script. +In US jurisdictions, this is illegal as it is a violation of the DMCA, and may be illegal in other jurisdictions as well. + +# CLASS REFERENCE: +## CustomDecryptor class +The CustomDecryptor class is the base class for all custom decryptors. A custom decryption script must inherit from this class. + +### methods: +#### _parse_and_decrypt(file: FileAccess, key: PackedByteArray, non_pack_file: bool) -> Dictionary: +`_parse_and_decrypt()` is a virtual method that must be implemented by the custom decryptor script that decrypts the file. +*Parameters*: +- `file`: `FileAccess` + - The `FileAccess` handle to the file to decrypt. + - NOTE: In most cases, this is a handle that points to an offset in the PCK file, + and `FileAccess` methods like `get_length()` will return the length of the PCK file, not the length of the data in the packed file. +- `key`: `PackedByteArray` + - The key to use for decryption. +- `non_pack_file`: `bool` + - `false` means that the file being loaded is part of a PCK, which means that the file does not have a magic number at the beginning (99% of cases) + - `true` means that the file being loaded is not part of a PCK, which means that the file will have a magic number at the beginning + +*Returns*: + a `Dictionary` that must contain the following keys: + - `error`: `Error` + - `OK` if successful, otherwise an error code + - `length`: `int` + - the length of the data in the file in bytes + - NOTE: This is the length of the decrypted data, not the length of the data in the file padded to 16 bytes alignment. + - i.e. `data.size()` should be equal to `length`, not `length + padding to 16 byte alignment` + - `data`: `PackedByteArray` + - The decrypted data + + +# Encryption contexts: +For conveniences sake, we have provided additional encryption contexts that can be used to decrypt data. +These behave like the [AESContext class in Godot](https://docs.godotengine.org/en/stable/classes/class_aescontext.html), but with support for additional Ciphers, and CFB mode. +These are: +- `AESContextGDRE` + - Behaves like the [AESContext class in Godot](https://docs.godotengine.org/en/stable/classes/class_aescontext.html), but with CFB support. +- `CamelliaContext` + - Implements the Camellia cipher. +- `AriaContext` + - Implements the Aria cipher. + +## methods: +- `start(mode: int, key: PackedByteArray, iv: PackedByteArray = PackedByteArray()) -> Error`: + - Starts the encryption context. + - Returns an error code if the context could not be started. +- `update(src: PackedByteArray) -> PackedByteArray`: + - Updates the encryption context with the data to encrypt/decrypt. + - Returns the encrypted/decrypted data. +- `get_iv_state() -> PackedByteArray`: + - Returns the current IV state of the encryption context. + - NOTE: This is only meaningful when the context is started in CBC or CFB mode. +- `finish()`: + - Finishes the encryption context. + +## constants: +All three support the following modes: +- `MODE_ECB_ENCRYPT` +- `MODE_ECB_DECRYPT` +- `MODE_CBC_ENCRYPT` +- `MODE_CBC_DECRYPT` +- `MODE_CFB_ENCRYPT` +- `MODE_CFB_DECRYPT` + diff --git a/docs/gdre_standard_encryption.gd b/docs/gdre_standard_encryption.gd new file mode 100644 index 00000000..478ee96f --- /dev/null +++ b/docs/gdre_standard_encryption.gd @@ -0,0 +1,120 @@ +class_name CustomDecryptorStandard +extends CustomDecryptor + +# This script implements the standard decryption scheme used by Godot. + + +# PLEASE NOTE: If you are intending to distribute your decryption script, it is *strongly* recommended to not include any secrets (encryption keys, xor keys, etc.) in the script. +# In US jurisdictions, this is illegal as it is a violation of the DMCA and may be illegal in other jurisdictions as well. + +# _parse_and_decrypt() is a virtual method that must be implemented by the custom decryptor. +# *Parameters*: +# - file: FileAccess +# - the file to decrypt. +# - NOTE: In most cases, this is a handle that points to an offset in the PCK file, +# and FileAccess methods like `get_length()` will return the length of the PCK file, not the length of the data in the packed file. +# - key: PackedByteArray +# - the key to use for decryption. +# - non_pack_file: bool +# - false means that the file being loaded is part of a PCK, which means that the file does not have a magic number at the beginning (99% of cases) +# - true means that the file being loaded is not part of a PCK, which means that the file will have a magic number at the beginning +# +# *Returns*: +# a Dictionary that must contain the following keys: +# - error: Error +# - OK if successful, otherwise an error code +# - length: int +# - the length of the data in the file in bytes +# - NOTE: This is the length of the decrypted data, not the length of the data in the file padded to 16 bytes alignment. +# - i.e. `data.size()` should be equal to `length`, not `length + padding to 16 byte alignment` +# - data: PackedByteArray +# - the decrypted data +func _parse_and_decrypt(file: FileAccess, key: PackedByteArray, non_pack_file: bool) -> Dictionary: + # file structure is: + # - magic (32-bit int, 4 bytes) (optional, not present if it's a file in a PCK) + # - md5 (16 bytes) + # - length (64-bit int, 8 bytes) + # - iv (16 bytes) + # - data (`length` bytes + padding to 16 byte alignment) + + var result: Dictionary = { + "error": OK, + "length": 0, + "data": PackedByteArray(), + } + + # Godot normally uses 'GDEC' in ASCII as the magic number found in the header of non-packed encrypted files. + const GODOT_ENCRYPTED_HEADER_MAGIC = 0x43454447 # little-endian + + # if this isn't a file in a PCK, check that the magic number is correct + if non_pack_file: + var magic = file.get_32() + if magic != GODOT_ENCRYPTED_HEADER_MAGIC: + result["error"] = ERR_INVALID_PARAMETER + printerr("Invalid magic") + return result + + + # The standard Godot encryption scheme uses AES-256-CFB, so check that the key is 32 bytes long + if key.size() != 32: + result["error"] = ERR_INVALID_PARAMETER + printerr("Invalid key length; key must be 32 bytes long for AES-256-CFB") + return result + + # get the md5, length, iv + var md5 = file.get_buffer(16) + var length = file.get_64() + var iv = file.get_buffer(16) + result["length"] = length + + # check if the file is long enough to contain the data of the given length + var base = file.get_position() + if file.get_length() < base + length: + result["error"] = ERR_FILE_CORRUPT + printerr("File is too short to contain the data") + return result + + # data has to be padded to 16 bytes, so the length of the data in file is actually a multiple of 16 + var ds = length; + if ds % 16 != 0: + ds += 16 - (ds % 16) + var data = file.get_buffer(ds) + # check that the data we read matches the expected size + if (data.size() != ds): + result["error"] = ERR_FILE_CORRUPT + printerr("Data size mismatch") + return result + + # decrypt the data + # AES in CFB mode is used for the standard encryption scheme + var ctx = AESContextGDRE.new() + ctx.start(AESContextGDRE.MODE_CFB_DECRYPT, key, iv) + var decrypted = ctx.update(data) + ctx.finish() + + # truncate the data to the original length + decrypted.resize(length) + + # MD5 hash the decrypted data + var md5_ctx = HashingContext.new() + var err = md5_ctx.start(HashingContext.HASH_MD5) + if err != OK: + result["error"] = ERR_BUG + printerr("Failed to start MD5 hashing") + return result + # don't update the hashing context if the data is empty; we get the default MD5 hash of an empty array + if decrypted.size() > 0: + err = md5_ctx.update(decrypted) + if err != OK: + result["error"] = ERR_BUG + printerr("Failed to update MD5 hashing") + return result + var md5_hash = md5_ctx.finish() + + # Check that the md5 of the decrypted data matches the expected md5 + if md5_hash.hex_encode() != md5.hex_encode(): + result["error"] = ERR_FILE_CORRUPT + printerr("MD5 mismatch, incorrect decryption key?") + return result + result["data"] = decrypted + return result diff --git a/editor/gdre_cmp_dlg.h b/editor/gdre_cmp_dlg.h index cc0fe5c4..d523e482 100644 --- a/editor/gdre_cmp_dlg.h +++ b/editor/gdre_cmp_dlg.h @@ -2,8 +2,7 @@ /* gdre_cmp_dlg.h */ /*************************************************************************/ -#ifndef GODOT_RE_CMP_DLG_H -#define GODOT_RE_CMP_DLG_H +#pragma once #include "core/io/resource.h" #include "core/templates/rb_map.h" @@ -61,5 +60,3 @@ class ScriptCompDialog : public AcceptDialog { int get_bytecode_version() const; ~ScriptCompDialog(); }; - -#endif diff --git a/editor/gdre_dec_dlg.h b/editor/gdre_dec_dlg.h index 629c0142..d9553692 100644 --- a/editor/gdre_dec_dlg.h +++ b/editor/gdre_dec_dlg.h @@ -2,8 +2,7 @@ /* gdre_dec_dlg.h */ /*************************************************************************/ -#ifndef GODOT_RE_DEC_DLG_H -#define GODOT_RE_DEC_DLG_H +#pragma once #include "core/io/resource.h" #include "core/templates/rb_map.h" @@ -62,5 +61,3 @@ class ScriptDecompDialog : public AcceptDialog { ScriptDecompDialog(); ~ScriptDecompDialog(); }; - -#endif diff --git a/editor/gdre_editor.h b/editor/gdre_editor.h index 51700079..be121249 100644 --- a/editor/gdre_editor.h +++ b/editor/gdre_editor.h @@ -2,8 +2,7 @@ /* gdre_editor.h */ /*************************************************************************/ -#ifndef GODOT_RE_EDITOR_H -#define GODOT_RE_EDITOR_H +#pragma once #include "utility/packed_file_info.h" #include "core/io/resource.h" @@ -189,5 +188,3 @@ class GodotREEditor : public Node { }; #endif // TOOLS_ENABLED /*************************************************************************/ - -#endif // GODOT_RE_EDITOR_H diff --git a/editor/gdre_enc_key.h b/editor/gdre_enc_key.h index 4a103275..a1a731c0 100644 --- a/editor/gdre_enc_key.h +++ b/editor/gdre_enc_key.h @@ -2,8 +2,7 @@ /* gdre_enc_key.h */ /*************************************************************************/ -#ifndef GODOT_RE_ENC_KEY_H -#define GODOT_RE_ENC_KEY_H +#pragma once #ifdef TOOLS_ENABLED #include "core/io/resource.h" #include "core/templates/rb_map.h" @@ -39,4 +38,3 @@ class EncKeyDialog : public AcceptDialog { }; #endif // TOOLS_ENABLED -#endif // GODOT_RE_ENC_KEY_H diff --git a/editor/gdre_npck_dlg.h b/editor/gdre_npck_dlg.h index a720d948..e4521e9c 100644 --- a/editor/gdre_npck_dlg.h +++ b/editor/gdre_npck_dlg.h @@ -2,8 +2,7 @@ /* gdre_npck_dlg.h */ /*************************************************************************/ -#ifndef GODOT_RE_NPCK_DLG_H -#define GODOT_RE_NPCK_DLG_H +#pragma once #ifdef TOOLS_ENABLED #include "core/io/resource.h" @@ -67,4 +66,3 @@ class NewPackDialog : public AcceptDialog { }; #endif // TOOLS_ENABLED -#endif // GODOT_RE_NPCK_DLG_H diff --git a/editor/gdre_pck_dlg.h b/editor/gdre_pck_dlg.h index 085026ba..e2ac67e0 100644 --- a/editor/gdre_pck_dlg.h +++ b/editor/gdre_pck_dlg.h @@ -2,8 +2,7 @@ /* gdre_pck_dlg.h */ /*************************************************************************/ -#ifndef GODOT_RE_PCK_DLG_H -#define GODOT_RE_PCK_DLG_H +#pragma once #ifdef TOOLS_ENABLED #include "core/io/resource.h" @@ -82,4 +81,3 @@ class PackDialog : public AcceptDialog { }; #endif // TOOLS_ENABLED -#endif // GODOT_RE_PCK_DLG_H diff --git a/exporters/gdre_test_macros.h b/exporters/gdre_test_macros.h index 252d1dfb..503976bf 100644 --- a/exporters/gdre_test_macros.h +++ b/exporters/gdre_test_macros.h @@ -104,7 +104,6 @@ } #include "core/string/ustring.h" -#include "core/templates/vector.h" namespace gdre_test { template diff --git a/exporters/mp3str_exporter.cpp b/exporters/mp3str_exporter.cpp index 71027670..f36b8e5a 100644 --- a/exporters/mp3str_exporter.cpp +++ b/exporters/mp3str_exporter.cpp @@ -1,8 +1,11 @@ #include "mp3str_exporter.h" + +#include "core/io/file_access.h" +#include "modules/mp3/audio_stream_mp3.h" + #include "compat/resource_loader_compat.h" #include "exporters/export_report.h" -#include "gdre_test_macros.h" -#include "modules/mp3/audio_stream_mp3.h" +#include "exporters/gdre_test_macros.h" #include "utility/common.h" Error Mp3StrExporter::export_file(const String &p_dest_path, const String &p_src_path) { diff --git a/exporters/obj_exporter.h b/exporters/obj_exporter.h index 1852eb1d..c336049a 100644 --- a/exporters/obj_exporter.h +++ b/exporters/obj_exporter.h @@ -1,5 +1,4 @@ -#ifndef OBJ_EXPORTER_H -#define OBJ_EXPORTER_H +#pragma once #include "resource_exporter.h" #include "scene/resources/material.h" @@ -41,5 +40,3 @@ class ObjExporter : public ResourceExporter { virtual String get_default_export_extension(const String &res_path) const override; virtual Vector get_export_extensions(const String &res_path) const override; }; - -#endif // OBJ_EXPORTER_H diff --git a/exporters/oggstr_exporter.cpp b/exporters/oggstr_exporter.cpp index 03c596e8..b498d578 100644 --- a/exporters/oggstr_exporter.cpp +++ b/exporters/oggstr_exporter.cpp @@ -1,5 +1,4 @@ #include "oggstr_exporter.h" -#include "compat/oggstr_loader_compat.h" #include "compat/resource_loader_compat.h" #include "core/error/error_list.h" #include "core/error/error_macros.h" diff --git a/exporters/sample_exporter.h b/exporters/sample_exporter.h index bb16138d..3ae0288a 100644 --- a/exporters/sample_exporter.h +++ b/exporters/sample_exporter.h @@ -1,5 +1,6 @@ #pragma once #include "exporters/resource_exporter.h" + class AudioStreamWAV; class SampleExporter : public ResourceExporter { GDCLASS(SampleExporter, ResourceExporter); diff --git a/exporters/scene_exporter.cpp b/exporters/scene_exporter.cpp index 95fccab6..7febe2d2 100644 --- a/exporters/scene_exporter.cpp +++ b/exporters/scene_exporter.cpp @@ -29,17 +29,21 @@ #include "utility/gdre_logger.h" #include "utility/gdre_settings.h" +#include "core/crypto/crypto_core.h" #include "core/error/error_list.h" #include "core/error/error_macros.h" #include "main/main.h" #include "scene/resources/compressed_texture.h" #include "scene/resources/packed_scene.h" -#include "servers/navigation_3d/navigation_server_3d.h" #include "utility/task_manager.h" #ifndef MAKE_GLTF_COPY +#ifdef DEBUG_ENABLED +#define MAKE_GLTF_COPY 1 +#else #define MAKE_GLTF_COPY 0 #endif +#endif #ifndef PRINT_PERF_CSV #define PRINT_PERF_CSV 0 #endif @@ -2408,6 +2412,85 @@ void GLTFDocumentExtensionPhysicsRemover::convert_scene_node(Ref p_st } } +Ref GLBExporterInstance::_parse_image_bytes_into_image(const Ref &p_state, const Vector &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension) { + Ref r_image; + r_image.instantiate(); + // Check if any GLTFDocumentExtensions want to import this data as an image. + for (auto &ext : GLTFDocument::get_all_gltf_document_extensions()) { + ERR_CONTINUE(ext.is_null()); + Error err = ext->parse_image_data(p_state, p_bytes, p_mime_type, r_image); + ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->get_filename() + ". Continuing."); + if (!r_image->is_empty()) { + r_file_extension = ext->get_image_file_extension(); + return r_image; + } + } + // If no extension wanted to import this data as an image, try to load a PNG or JPEG. + // First we honor the mime types if they were defined. + if (p_mime_type == "image/png") { // Load buffer as PNG. + r_image->load_png_from_buffer(p_bytes); + r_file_extension = ".png"; + } else if (p_mime_type == "image/jpeg") { // Loader buffer as JPEG. + r_image->load_jpg_from_buffer(p_bytes); + r_file_extension = ".jpg"; + } + // If we didn't pass the above tests, we attempt loading as PNG and then JPEG directly. + // This covers URIs with base64-encoded data with application/* type but + // no optional mimeType property, or bufferViews with a bogus mimeType + // (e.g. `image/jpeg` but the data is actually PNG). + // That's not *exactly* what the spec mandates but this lets us be + // lenient with bogus glb files which do exist in production. + if (r_image->is_empty()) { // Try PNG first. + r_image->load_png_from_buffer(p_bytes); + } + if (r_image->is_empty()) { // And then JPEG. + r_image->load_jpg_from_buffer(p_bytes); + } + // If it still can't be loaded, give up and insert an empty image as placeholder. + if (r_image->is_empty()) { + ERR_PRINT(vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", p_index, p_mime_type)); + } + return r_image; +} + +String GLBExporterInstance::get_gltf_image_hash(const Ref &p_state, const Dictionary &dict, int p_index) { + String image_hash; + String mime_type; + if (dict.has("mimeType")) { // Should be "image/png", "image/jpeg", or something handled by an extension. + mime_type = dict["mimeType"]; + } + + String resource_uri; + // We only need the image hash if it's embedded in the glTF file, so if it's not a bufferView, we can return early. + Vector data; + if (dict.has("bufferView")) { + // Handles the third bullet point from the spec (bufferView). + ERR_FAIL_COND_V_MSG(mime_type.is_empty(), "", vformat("glTF: Image index '%d' specifies 'bufferView' but no 'mimeType', which is invalid.", p_index)); + const GLTFBufferViewIndex bvi = dict["bufferView"]; + auto buffer_views = p_state->get_buffer_views(); + ERR_FAIL_INDEX_V(bvi, buffer_views.size(), ""); + Ref bv = buffer_views[bvi]; + const GLTFBufferIndex bi = bv->get_buffer(); + auto buffers = p_state->get_buffers(); + ERR_FAIL_INDEX_V(bi, buffers.size(), ""); + ERR_FAIL_COND_V(bv->get_byte_offset() + bv->get_byte_length() > buffers[bi].size(), ""); + const PackedByteArray &buffer = buffers[bi]; + data = buffer.slice(bv->get_byte_offset(), bv->get_byte_offset() + bv->get_byte_length()); + } + if (data.is_empty()) { + return image_hash; + } + String ext; + Ref img = _parse_image_bytes_into_image(p_state, data, mime_type, p_index, ext); + if (img.is_valid()) { + auto img_data = img->get_data(); + unsigned char md5_hash[16]; + CryptoCore::md5(img_data.ptr(), img_data.size(), md5_hash); + image_hash = String::hex_encode_buffer(md5_hash, 16); + } + return image_hash; +} + Error GLBExporterInstance::_export_instanced_scene(Node *root, const String &p_dest_path) { { GDRE_SCN_EXP_CHECK_CANCEL(); @@ -2564,7 +2647,9 @@ Error GLBExporterInstance::_export_instanced_scene(Node *root, const String &p_d } else { name = path.get_file().get_basename(); external_deps_updated.insert(path); - image_path_to_data_hash[path] = FileAccess::get_md5(path); + if (updating_import_info && is_batch_export) { + image_path_to_data_hash[path] = get_gltf_image_hash(state, image_dict, i); + } } check_unique(name, image_map); @@ -3008,7 +3093,9 @@ void GLBExporterInstance::_update_import_params(const String &p_dest_path) { iinfo->set_param("_subresources", _subresources_dict); Dictionary extra_info = report->get_extra_info(); - extra_info["image_path_to_data_hash"] = image_path_to_data_hash; + if (!image_path_to_data_hash.is_empty()) { + extra_info["image_path_to_data_hash"] = image_path_to_data_hash; + } report->set_extra_info(extra_info); } @@ -3447,18 +3534,17 @@ struct BatchExportToken : public TaskRunnerStruct { if (check_unsupported()) { err = ERR_UNAVAILABLE; _set_unsupported(report, ver_major, is_obj_output()); - preload_done = true; + after_preload(); return false; } if (is_text_output()) { - preload_done = true; return false; } err = _check_cancelled(); if (err != OK) { report->set_error(err); - preload_done = true; + after_preload(); return false; } instance._initial_set(p_src_path, report); @@ -3474,7 +3560,7 @@ struct BatchExportToken : public TaskRunnerStruct { } if (err != OK) { report->set_error(err); - preload_done = true; + after_preload(); return false; } _scene = scene; @@ -3484,7 +3570,7 @@ struct BatchExportToken : public TaskRunnerStruct { _scene = nullptr; err = ERR_CANT_ACQUIRE_RESOURCE; report->set_error(err); - preload_done = true; + after_preload(); return false; } root = instance._set_stuff_from_instanced_scene(root); @@ -3501,10 +3587,19 @@ struct BatchExportToken : public TaskRunnerStruct { surface_count = get_total_surface_count(meshes); } // print_line("Preloaded scene " + p_src_path); - preload_done = true; + after_preload(); return do_on_main_thread; } + void after_preload() { + if (Thread::is_main_thread() && _scene.is_valid()) { + // We have to flush the message queue after the scene is loaded; + // Certain resources like NoiseTexture2D can queue up deferred calls that will cause crashes if not flushed before the scene is manipulated or freed + GDRESettings::main_iteration(); + } + preload_done = true; + } + void batch_export_instanced_scene() { while (!preload_done && _check_cancelled() == OK) { OS::get_singleton()->delay_usec(10000); diff --git a/exporters/scene_exporter.h b/exporters/scene_exporter.h index b9c2f3db..cde34118 100644 --- a/exporters/scene_exporter.h +++ b/exporters/scene_exporter.h @@ -198,6 +198,9 @@ class GLBExporterInstance { String demangle_name(const String &obj_name); + static Ref _parse_image_bytes_into_image(const Ref &p_state, const Vector &p_bytes, const String &p_mime_type, int p_index, String &r_file_extension); + static String get_gltf_image_hash(const Ref &p_state, const Dictionary &dict, int p_index); + public: GLBExporterInstance(String p_output_dir, Dictionary curr_options = {}, bool p_project_recovery = false); diff --git a/exporters/texture_exporter.cpp b/exporters/texture_exporter.cpp index e74b9cfb..dcf5fe4a 100644 --- a/exporters/texture_exporter.cpp +++ b/exporters/texture_exporter.cpp @@ -17,6 +17,7 @@ #include "scene/resources/texture.h" #include "utility/image_saver.h" #include "utility/resource_info.h" + #include namespace { bool get_bit(const Vector &bitmask, int width, int p_x, int p_y) { diff --git a/godot-mono-decomp/GodotMonoDecomp/Common.cs b/godot-mono-decomp/GodotMonoDecomp/Common.cs index 4682a424..f17549c2 100644 --- a/godot-mono-decomp/GodotMonoDecomp/Common.cs +++ b/godot-mono-decomp/GodotMonoDecomp/Common.cs @@ -246,7 +246,7 @@ public static string[] GetEnumValueNames(IType type) .Select(field => field.Name).ToArray(); } - public static string GetEnumValueName(IType type, int value, string defaultValue = "") + public static string GetEnumValueName(IType type, long value, string defaultValue = "") { var names = GetEnumValueNames(type); if (names.Length == 0 || value >= names.Length) @@ -255,4 +255,28 @@ public static string GetEnumValueName(IType type, int value, string defaultValue } return names[value]; } + + public static Guid GenerateDeterministicGuidFromString(string input) + { + const byte Variant10xxMask = 0xC0; + const byte Variant10xxValue = 0x80; + + const ushort VersionMask = 0xF000; + const ushort Version4Value = 0x4000; + using (var md5 = System.Security.Cryptography.MD5.Create()) + { + byte[] inputBytes = Encoding.UTF8.GetBytes(input); + byte[] hashBytes = md5.ComputeHash(inputBytes); + short _c = (short)(hashBytes[3] | hashBytes[4] << 8); + byte _d = hashBytes[5]; + _c = (short)((_c & ~VersionMask) | Version4Value); + _d = (byte)((_d & ~Variant10xxMask) | Variant10xxValue); + + hashBytes[3] = (byte)(_c & 0xFF); + hashBytes[4] = (byte)((_c >> 8) & 0xFF); + hashBytes[5] = _d; + + return new Guid(hashBytes); + } + } } diff --git a/godot-mono-decomp/GodotMonoDecomp/GodotExpressionTokenWriter.cs b/godot-mono-decomp/GodotMonoDecomp/GodotExpressionTokenWriter.cs index 4d42eeed..c6e92710 100644 --- a/godot-mono-decomp/GodotMonoDecomp/GodotExpressionTokenWriter.cs +++ b/godot-mono-decomp/GodotMonoDecomp/GodotExpressionTokenWriter.cs @@ -36,17 +36,21 @@ public class GodotExpressionOutputVisitor : CSharpOutputVisitor TextWriter tw; private bool isDict = false; - public GodotExpressionOutputVisitor(TextWriter w, CSharpFormattingOptions formattingOptions) + private GodotProjectDecompiler? godotDecompiler; + + public GodotExpressionOutputVisitor(TextWriter w, CSharpFormattingOptions formattingOptions, GodotProjectDecompiler? godotDecompiler = null) : base(new GodotExpressionTokenWriter(w), formattingOptions) { tw = w; + this.godotDecompiler = godotDecompiler; } - public GodotExpressionOutputVisitor(TextWriter w) - : this(w, SingleLineFormattingOptions) - {} + public GodotExpressionOutputVisitor(TextWriter w, GodotProjectDecompiler? godotDecompiler = null) + : this(w, SingleLineFormattingOptions, godotDecompiler) + { + } public static readonly CSharpFormattingOptions SingleLineFormattingOptions = GetSingleLineFormattingOptions(); @@ -231,10 +235,10 @@ private static CSharpFormattingOptions GetSingleLineFormattingOptions() return settings.CSharpFormattingOptions; } - public static string GetString(AstNode node) + public static string GetString(AstNode node, GodotProjectDecompiler? godotDecompiler = null) { var stringWriter = new StringWriter(); - var visitor = new GodotExpressionOutputVisitor(stringWriter); + var visitor = new GodotExpressionOutputVisitor(stringWriter, godotDecompiler); node.AcceptVisitor(visitor); return stringWriter.ToString(); } @@ -501,7 +505,7 @@ bool IsComplexExpression(Expression ex) } public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression) { - string? text = GodotStuff.ReplaceMemberReference(memberReferenceExpression); + string? text = GodotStuff.ReplaceMemberReference(memberReferenceExpression, godotDecompiler); if (text != null) { StartNode(memberReferenceExpression); diff --git a/godot-mono-decomp/GodotMonoDecomp/GodotModuleDecompiler.cs b/godot-mono-decomp/GodotMonoDecomp/GodotModuleDecompiler.cs index 040f91dd..fed06699 100644 --- a/godot-mono-decomp/GodotMonoDecomp/GodotModuleDecompiler.cs +++ b/godot-mono-decomp/GodotMonoDecomp/GodotModuleDecompiler.cs @@ -23,6 +23,7 @@ public class GodotModule public readonly PEFile Module; public readonly DotNetCoreDepInfo? depInfo; public readonly LanguageVersion languageVersion; + public readonly Guid moduleGuid; public readonly IDebugInfoProvider? debugInfoProvider; public readonly string? SubDirectory; public Dictionary fileMap; @@ -54,7 +55,11 @@ public GodotModule(PEFile module, DotNetCoreDepInfo? depInfo, string? subdir, Go var moduleSettings = p_settings!.Clone(); moduleSettings.SetLanguageVersion(languageVersion); - ProjectDecompiler = new GodotProjectDecompiler(moduleSettings, assemblyResolver, assemblyResolver, debugInfoProvider); + + // generate a UUID for the module based on its name + moduleGuid = Common.GenerateDeterministicGuidFromString(Module.FileName); + + ProjectDecompiler = new GodotProjectDecompiler(moduleSettings, moduleGuid, assemblyResolver, assemblyResolver, debugInfoProvider); TypeSystem = ProjectDecompiler.CreateTypeSystem(Module); } @@ -325,6 +330,20 @@ ProjectItem decompileFile(GodotModule module, string csprojPath) foreach (var module in AdditionalModules) { projectIDs.Add(decompileFile(module, moduleToCsProjPath[module.Name])); + var dir = Path.GetDirectoryName(moduleToCsProjPath[module.Name]); + if (dir != null) + { + Common.EnsureDir(dir); + // create a .gitignore file in the directory + var gitignorePath = Path.Combine(dir, ".gitignore"); + if (!File.Exists(gitignorePath)) + { + var gitignore = File.CreateText(gitignorePath); + gitignore.WriteLine("bin/*"); + gitignore.WriteLine("obj/*"); + gitignore.Close(); + } + } } var solutionPath = Path.ChangeExtension(outputCSProjectPath, ".sln"); removeIfExists(solutionPath); @@ -447,7 +466,18 @@ private string GetPathForType(ITypeDefinition? typeDef){ string propUsage = ""; if (exportAttr?.FixedArguments.Length >= 2) { - int hintValue = exportAttr.FixedArguments[0].Value as int? ?? 0; + long hintValue = 0; + if (exportAttr.FixedArguments[0].Value != null) + { + if (exportAttr.FixedArguments[0].Value is int intVal) + { + hintValue = intVal; + } + else if (exportAttr.FixedArguments[0].Value is long longVal) + { + hintValue = longVal; + } + } propHint = Common.GetEnumValueName(exportAttr.FixedArguments[0].Type, hintValue, "None"); propUsage = exportAttr.FixedArguments[1].Value?.ToString() ?? ""; } diff --git a/godot-mono-decomp/GodotMonoDecomp/GodotMonoDecomp.csproj b/godot-mono-decomp/GodotMonoDecomp/GodotMonoDecomp.csproj index 51cdb67f..1dc943aa 100644 --- a/godot-mono-decomp/GodotMonoDecomp/GodotMonoDecomp.csproj +++ b/godot-mono-decomp/GodotMonoDecomp/GodotMonoDecomp.csproj @@ -2,15 +2,15 @@ Library - net9.0 + net10.0 GodotMonoDecomp enable enable true - - + + diff --git a/godot-mono-decomp/GodotMonoDecomp/GodotProjectDecompiler.cs b/godot-mono-decomp/GodotMonoDecomp/GodotProjectDecompiler.cs index 95ad2104..822051d5 100644 --- a/godot-mono-decomp/GodotMonoDecomp/GodotProjectDecompiler.cs +++ b/godot-mono-decomp/GodotMonoDecomp/GodotProjectDecompiler.cs @@ -86,7 +86,7 @@ public LanguageVersion LanguageVersion { }; - // bool IProjectInfoProvider.CheckForOverflowUnderflow => Settings.CheckForOverflowUnderflow; + bool IProjectInfoProvider.CheckForOverflowUnderflow => Settings.CheckForOverflowUnderflow; public IAssemblyResolver AssemblyResolver { get; } @@ -130,7 +130,7 @@ public GodotProjectDecompiler( { } - protected GodotProjectDecompiler( + public GodotProjectDecompiler( GodotMonoDecompSettings settings, Guid projectGuid, IAssemblyResolver assemblyResolver, diff --git a/godot-mono-decomp/GodotMonoDecomp/GodotStuff.cs b/godot-mono-decomp/GodotMonoDecomp/GodotStuff.cs index d65de56f..11db2a97 100644 --- a/godot-mono-decomp/GodotMonoDecomp/GodotStuff.cs +++ b/godot-mono-decomp/GodotMonoDecomp/GodotStuff.cs @@ -91,7 +91,7 @@ public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclar } else if (init is InterpolatedStringExpression interpolatedStringExpression) { - value = GodotExpressionOutputVisitor.GetString(interpolatedStringExpression); + value = GodotExpressionOutputVisitor.GetString(interpolatedStringExpression, godotDecompiler); } else if (init is IdentifierExpression identifierExpression) { @@ -134,45 +134,13 @@ public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclar } else { - var sr = GodotExpressionOutputVisitor.GetString(oce); + var sr = GodotExpressionOutputVisitor.GetString(oce, godotDecompiler); value = Common.TrimPrefix(sr, "new").Trim(); } } - else if (init is MemberReferenceExpression memberReferenceExpression) - { - value = GodotStuff.ReplaceMemberReference(memberReferenceExpression); - if (value == null) - { - var s = memberReferenceExpression.GetSymbol(); - if (s is IProperty || s is IField) - { - IMember prop = (IMember)s; - var declaringType = prop.DeclaringType.GetDefinition(); - if (declaringType == null || declaringType.ParentModule == null || declaringType.ParentModule.MetadataFile == null) - { - return GodotExpressionOutputVisitor.GetString(memberReferenceExpression); - } - var decompiler = godotDecompiler.CreateDecompilerWithPartials(declaringType.ParentModule.MetadataFile, [(TypeDefinitionHandle)declaringType.MetadataToken]); - var tree = decompiler.DecompileTypes([(TypeDefinitionHandle)declaringType.MetadataToken]); - GetFieldInitializerValueVisitor vis; - if (memberReferenceExpression.GetSymbol() is IMember m) - { - vis = new GetFieldInitializerValueVisitor(m, godotDecompiler); - tree.AcceptVisitor(vis); - value = vis.strVal; - } - - if (value == null) - { - value = GodotExpressionOutputVisitor.GetString(memberReferenceExpression); - } - } - } - } - else { - value = GodotExpressionOutputVisitor.GetString(init); + value = GodotExpressionOutputVisitor.GetString(init, godotDecompiler); } } return value; @@ -1222,7 +1190,7 @@ public static string CSharpTypeToGodotType(string _type) return newType; } - public static string? ReplaceMemberReference(MemberReferenceExpression memberReferenceExpression) + public static string? ReplaceMemberReference(MemberReferenceExpression memberReferenceExpression, GodotProjectDecompiler? godotDecompiler = null) { string? text = null; if (memberReferenceExpression.GetSymbol() is IMember ne) @@ -1257,6 +1225,28 @@ public static string CSharpTypeToGodotType(string _type) { text = "\"\""; } + else if (godotDecompiler != null) + { + var s = memberReferenceExpression.GetSymbol(); + if (s is IProperty || s is IField) + { + IMember prop = (IMember)s; + var declaringType = prop.DeclaringType.GetDefinition(); + if (declaringType == null || declaringType.ParentModule == null || declaringType.ParentModule.MetadataFile == null) + { + return null; + } + var decompiler = godotDecompiler.CreateDecompilerWithPartials(declaringType.ParentModule.MetadataFile, [(TypeDefinitionHandle)declaringType.MetadataToken]); + var tree = decompiler.DecompileTypes([(TypeDefinitionHandle)declaringType.MetadataToken]); + GetFieldInitializerValueVisitor vis; + if (memberReferenceExpression.GetSymbol() is IMember m) + { + vis = new GetFieldInitializerValueVisitor(m, godotDecompiler); + tree.AcceptVisitor(vis); + text = vis.strVal; + } + } + } } return text; diff --git a/godot-mono-decomp/GodotMonoDecomp/ProjectFileWriterGodotStyle.cs b/godot-mono-decomp/GodotMonoDecomp/ProjectFileWriterGodotStyle.cs index 20c16aae..bf709792 100644 --- a/godot-mono-decomp/GodotMonoDecomp/ProjectFileWriterGodotStyle.cs +++ b/godot-mono-decomp/GodotMonoDecomp/ProjectFileWriterGodotStyle.cs @@ -396,6 +396,7 @@ static void WriteProjectInfo(XmlTextWriter xml, IProjectInfoProvider project) xml.WriteElementString("LangVersion", project.LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.')); xml.WriteElementString("AllowUnsafeBlocks", TrueString); + xml.WriteElementString("CheckForOverflowUnderflow", project.CheckForOverflowUnderflow ? TrueString : FalseString); if (project.StrongNameKeyFile != null) { diff --git a/godot-mono-decomp/GodotMonoDecompCLI/GodotMonoDecompCLI.csproj b/godot-mono-decomp/GodotMonoDecompCLI/GodotMonoDecompCLI.csproj index ac9ae367..d35810ee 100644 --- a/godot-mono-decomp/GodotMonoDecompCLI/GodotMonoDecompCLI.csproj +++ b/godot-mono-decomp/GodotMonoDecompCLI/GodotMonoDecompCLI.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 enable enable ConsoleApp1 diff --git a/godot-mono-decomp/GodotMonoDecompNativeAOT/GodotMonoDecompNativeAOT.csproj b/godot-mono-decomp/GodotMonoDecompNativeAOT/GodotMonoDecompNativeAOT.csproj index 9d377344..05daeecb 100644 --- a/godot-mono-decomp/GodotMonoDecompNativeAOT/GodotMonoDecompNativeAOT.csproj +++ b/godot-mono-decomp/GodotMonoDecompNativeAOT/GodotMonoDecompNativeAOT.csproj @@ -2,7 +2,7 @@ libGodotMonoDecompNativeAOT - net9.0 + net10.0 enable enable GodotMonoDecomp.NativeLibrary diff --git a/godot-mono-decomp/global.json b/godot-mono-decomp/global.json new file mode 100644 index 00000000..a11f48e1 --- /dev/null +++ b/godot-mono-decomp/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "10.0.0", + "rollForward": "latestMajor", + "allowPrerelease": true + } +} \ No newline at end of file diff --git a/godot-mono-decomp/godot-mono-decomp.sln b/godot-mono-decomp/godot-mono-decomp.sln new file mode 100644 index 00000000..3022c9d0 --- /dev/null +++ b/godot-mono-decomp/godot-mono-decomp.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotMonoDecomp", "GodotMonoDecomp\GodotMonoDecomp.csproj", "{13D29FFC-3A83-452F-8749-A8EA47686A03}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotMonoDecompCLI", "GodotMonoDecompCLI\GodotMonoDecompCLI.csproj", "{1D65910F-3A1A-4857-A747-F72203D6E3B2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotMonoDecompNativeAOT", "GodotMonoDecompNativeAOT\GodotMonoDecompNativeAOT.csproj", "{2F613AB3-32B2-49E2-B7EC-A58B7C5B0756}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {13D29FFC-3A83-452F-8749-A8EA47686A03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13D29FFC-3A83-452F-8749-A8EA47686A03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13D29FFC-3A83-452F-8749-A8EA47686A03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13D29FFC-3A83-452F-8749-A8EA47686A03}.Release|Any CPU.Build.0 = Release|Any CPU + {1D65910F-3A1A-4857-A747-F72203D6E3B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D65910F-3A1A-4857-A747-F72203D6E3B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D65910F-3A1A-4857-A747-F72203D6E3B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D65910F-3A1A-4857-A747-F72203D6E3B2}.Release|Any CPU.Build.0 = Release|Any CPU + {2F613AB3-32B2-49E2-B7EC-A58B7C5B0756}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2F613AB3-32B2-49E2-B7EC-A58B7C5B0756}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2F613AB3-32B2-49E2-B7EC-A58B7C5B0756}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2F613AB3-32B2-49E2-B7EC-A58B7C5B0756}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/godot-mono-decomp/nuget.config b/godot-mono-decomp/nuget.config new file mode 100644 index 00000000..6b267fba --- /dev/null +++ b/godot-mono-decomp/nuget.config @@ -0,0 +1,5 @@ + + + + + diff --git a/gui/find_replace_bar.cpp b/gui/find_replace_bar.cpp index 21971252..fd9e4076 100644 --- a/gui/find_replace_bar.cpp +++ b/gui/find_replace_bar.cpp @@ -5,7 +5,6 @@ #include "core/os/keyboard.h" #include "core/variant/array.h" #include "gui/gui_icons.h" -#include "scene/resources/image_texture.h" #include "utility/gdre_settings.h" #include "gui/gdre_icons.gen.h" diff --git a/gui/find_replace_bar.h b/gui/find_replace_bar.h index a8cbdade..17e8673d 100644 --- a/gui/find_replace_bar.h +++ b/gui/find_replace_bar.h @@ -6,7 +6,6 @@ #include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/panel_container.h" -#include "scene/gui/text_edit.h" class GDREFindReplaceBar : public PanelContainer { GDCLASS(GDREFindReplaceBar, PanelContainer); diff --git a/gui/gdre_audio_stream_preview.h b/gui/gdre_audio_stream_preview.h index dda1dc06..f8657f86 100644 --- a/gui/gdre_audio_stream_preview.h +++ b/gui/gdre_audio_stream_preview.h @@ -28,8 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef AUDIO_STREAM_PREVIEW_H -#define AUDIO_STREAM_PREVIEW_H +#pragma once #include "core/os/thread.h" #include "core/templates/safe_refcount.h" @@ -119,5 +118,3 @@ class GDREAudioStreamPreviewGenerator : public Object { GDREAudioStreamPreviewGenerator(); }; - -#endif // AUDIO_STREAM_PREVIEW_H diff --git a/gui/gdre_progress.cpp b/gui/gdre_progress.cpp index 4aaf9b4c..d7da21d0 100644 --- a/gui/gdre_progress.cpp +++ b/gui/gdre_progress.cpp @@ -31,8 +31,6 @@ #include "gui/gdre_progress.h" #include "core/os/os.h" -#include "gdre_standalone.h" -#include "main/main.h" #include "scene/gui/file_dialog.h" #include "servers/display/display_server.h" #include "utility/gdre_logger.h" diff --git a/gui/gdre_progress.h b/gui/gdre_progress.h index 84a19b42..d5e0d890 100644 --- a/gui/gdre_progress.h +++ b/gui/gdre_progress.h @@ -28,8 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef GDRE_PROGRESS_DIALOG_H -#define GDRE_PROGRESS_DIALOG_H +#pragma once #include "scene/gui/box_container.h" #include "scene/gui/button.h" @@ -175,5 +174,3 @@ struct EditorProgressGDDC : public RefCounted { EditorProgressGDDC(Node *p_parent, const String &p_task, const String &p_label, int p_amount, bool p_can_cancel = false); ~EditorProgressGDDC(); }; - -#endif // GDRE_PROGRESS_H diff --git a/gui/gui_icons.h b/gui/gui_icons.h index 777b149b..171a9a27 100644 --- a/gui/gui_icons.h +++ b/gui/gui_icons.h @@ -1,11 +1,12 @@ #include "core/object/object.h" -class ImageTexture; +#include "scene/resources/image_texture.h" + class Control; class GDREGuiIcons { static GDREGuiIcons *singleton; HashMap>> icons; - bool initialized; + bool initialized = false; Mutex init_lock; void init_for_scale(float scale); diff --git a/gui/scene_previewer.cpp b/gui/scene_previewer.cpp index ac5e00be..2ba0c02d 100644 --- a/gui/scene_previewer.cpp +++ b/gui/scene_previewer.cpp @@ -30,10 +30,7 @@ #include "scene_previewer.h" -#include "compat/input_event_parser_v2.h" #include "core/config/project_settings.h" -#include "main/main.h" -#include "scene/2d/camera_2d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" diff --git a/gui/scene_previewer.h b/gui/scene_previewer.h index 10b27251..33b11da3 100644 --- a/gui/scene_previewer.h +++ b/gui/scene_previewer.h @@ -47,8 +47,8 @@ class ScenePreviewer3D : public SubViewportContainer { friend class ScenePreviewer; - float rot_x; - float rot_y; + float rot_x = 0.0f; + float rot_y = 0.0f; Vector3 center; Vector3 scale = Vector3(1.0, 1.0, 1.0); diff --git a/plugin_manager/asset_library_source.h b/plugin_manager/asset_library_source.h index 89f271bf..7f0dc0e1 100644 --- a/plugin_manager/asset_library_source.h +++ b/plugin_manager/asset_library_source.h @@ -1,5 +1,4 @@ -#ifndef ASSET_LIBRARY_SOURCE_H -#define ASSET_LIBRARY_SOURCE_H +#pragma once #include "core/variant/dictionary.h" #include "plugin_source.h" @@ -52,5 +51,3 @@ class AssetLibrarySource : public PluginSource { String get_plugin_name() override; Vector find_release_infos_by_tag(const String &plugin_name, const String &tag, Error &r_error) override; }; - -#endif // ASSET_LIBRARY_SOURCE_H diff --git a/plugin_manager/codeberg_source.h b/plugin_manager/codeberg_source.h index 079e6205..7b26c87e 100644 --- a/plugin_manager/codeberg_source.h +++ b/plugin_manager/codeberg_source.h @@ -1,5 +1,4 @@ -#ifndef GITLAB_SOURCE_H -#define GITLAB_SOURCE_H +#pragma once #include "core/object/object.h" #include "core/string/ustring.h" @@ -27,5 +26,3 @@ class CodebergSource : public GitHubSource { CodebergSource(); ~CodebergSource(); }; - -#endif // GITLAB_SOURCE_H diff --git a/plugin_manager/github_source.h b/plugin_manager/github_source.h index 165391ac..e362a4f3 100644 --- a/plugin_manager/github_source.h +++ b/plugin_manager/github_source.h @@ -1,5 +1,4 @@ -#ifndef GITHUB_SOURCE_H -#define GITHUB_SOURCE_H +#pragma once #include "core/object/object.h" #include "core/string/ustring.h" @@ -97,5 +96,3 @@ class GitHubSource : public PluginSource { // void load_cache_data(const String &plugin_name, const Dictionary &data) override; // Deprecated Vector find_release_infos_by_tag(const String &plugin_name, const String &tag, Error &r_error) override; }; - -#endif // GITHUB_SOURCE_H diff --git a/plugin_manager/plugin_info.h b/plugin_manager/plugin_info.h index 35473bfa..9026245c 100644 --- a/plugin_manager/plugin_info.h +++ b/plugin_manager/plugin_info.h @@ -1,4 +1,5 @@ #pragma once + #include "core/variant/variant.h" #include "utility/godotver.h" diff --git a/plugin_manager/plugin_manager.h b/plugin_manager/plugin_manager.h index 2274b003..dcbb8997 100644 --- a/plugin_manager/plugin_manager.h +++ b/plugin_manager/plugin_manager.h @@ -1,5 +1,4 @@ -#ifndef PLUGIN_MANAGER_H -#define PLUGIN_MANAGER_H +#pragma once #include "core/object/object.h" #include "core/object/ref_counted.h" @@ -55,5 +54,3 @@ class PluginManager : public Object { static void unregister_source(Ref source); static void print_plugin_cache(); }; - -#endif // PLUGIN_MANAGER_H diff --git a/plugin_manager/plugin_source.h b/plugin_manager/plugin_source.h index 477dda76..623f5572 100644 --- a/plugin_manager/plugin_source.h +++ b/plugin_manager/plugin_source.h @@ -1,5 +1,4 @@ -#ifndef PLUGIN_SOURCE_H -#define PLUGIN_SOURCE_H +#pragma once #include "core/object/object.h" #include "core/object/ref_counted.h" @@ -35,5 +34,3 @@ class PluginSource : public RefCounted { return retrieved_time + EXPIRY_TIME <= OS::get_singleton()->get_unix_time(); } }; - -#endif // PLUGIN_SOURCE_H diff --git a/register_types.cpp b/register_types.cpp index 09c06333..c30cf379 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -35,6 +35,9 @@ #include "compat/script_loader.h" #include "compat/texture_loader_compat.h" #include "compat/video_stream_compat.h" +#include "crypto/crypto_core_gdre_contexts.h" +#include "crypto/custom_decryptor.h" +#include "crypto/file_access_encrypted_custom.h" #include "exporters/autoconverted_exporter.h" #include "exporters/csharp_exporter.h" #include "exporters/dialogue_exporter.h" @@ -484,6 +487,13 @@ void initialize_gdsdecomp_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(); + // crypto classes + ClassDB::register_class(true); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); diff --git a/register_types.h b/register_types.h index d1df2281..f825df43 100644 --- a/register_types.h +++ b/register_types.h @@ -2,8 +2,7 @@ /* register_types.h */ /*************************************************************************/ -#ifndef GDSDECOMP_REGISTER_TYPES_H -#define GDSDECOMP_REGISTER_TYPES_H +#pragma once #include "modules/register_module_types.h" @@ -11,4 +10,3 @@ void initialize_gdsdecomp_module(ModuleInitializationLevel p_level); void uninitialize_gdsdecomp_module(ModuleInitializationLevel p_level); void init_ver_regex(); void free_ver_regex(); -#endif // GDSDECOMP_REGISTER_TYPES_H diff --git a/standalone/gdre_icons/gdre_Close.svg b/standalone/gdre_icons/gdre_Close.svg new file mode 100644 index 00000000..204616b0 --- /dev/null +++ b/standalone/gdre_icons/gdre_Close.svg @@ -0,0 +1 @@ + diff --git a/standalone/gdre_icons/gdre_Close.svg.import b/standalone/gdre_icons/gdre_Close.svg.import new file mode 100644 index 00000000..ce587227 --- /dev/null +++ b/standalone/gdre_icons/gdre_Close.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="DPITexture" +uid="uid://ctf2qc5l62xbw" +path="res://.godot/imported/gdre_Close.svg-6a694d57a40b6b67ed00f3d03bcb58db.dpitex" + +[deps] + +source_file="res://gdre_icons/gdre_Close.svg" +dest_files=["res://.godot/imported/gdre_Close.svg-6a694d57a40b6b67ed00f3d03bcb58db.dpitex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/standalone/gdre_main.gd b/standalone/gdre_main.gd index afe6e118..44e7a025 100644 --- a/standalone/gdre_main.gd +++ b/standalone/gdre_main.gd @@ -126,6 +126,13 @@ func end_recovery(): func extract_and_recover(files_to_extract: PackedStringArray, output_dir: String, extract_only: bool): %GdreRecover.hide_win() if not extract_only: + if GDREConfig.get_setting("Recovery/clear_output_dir_except_git_before_full_recovery"): + if files_to_extract.size() >= GDRESettings.get_file_info_array().size(): + var err = GDRECommon.clear_dir_except_for(output_dir, [".git", ".gitignore"]) + if err != OK: + print("Error: failed to clear output directory except for git") + return 1 + GDRESettings.open_log_file(output_dir) var log_path = GDRESettings.get_log_file_path() GDRESettings.get_errors() @@ -230,6 +237,8 @@ func open_about_window(): $LegalNoticeWindow.popup_centered() func open_setenc_window(): + %KeyText.text = GDRESettings.get_encryption_key_string() + %EncryptionScriptPathText.text = GDRESettings.get_custom_decryption_script_path() $SetEncryptionKeyWindow.popup_centered() @@ -447,6 +456,15 @@ func _on_setenc_key_ok_pressed(): # pop up an accept dialog $SetEncryptionKeyWindow.popup_error_box("Invalid key!\nKey must be a hex string with 64 characters", "Error") return + + if %EncryptionScriptPathText.text.length() > 0: + GDRESettings.get_recent_error_string() + var err:int = GDRESettings.set_custom_decryption_script(%EncryptionScriptPathText.text) + if (err != OK): + $SetEncryptionKeyWindow.popup_error_box("Invalid encryption script!\n" + GDRESettings.get_recent_error_string(), "Error") + return + else: + GDRESettings.reset_custom_decryptor() # close the window $SetEncryptionKeyWindow.hide() @@ -750,6 +768,7 @@ var RECOVER_OPTS_NOTES = """Recover/Extract Options: --load-custom-bytecode= Load a custom bytecode definition file from the specified JSON file and use it for the recovery session --translation-hint= Load a translation key hint file (.csv, .txt, .po, .mo) and use it during translation recovery --skip-loading-resource-strings Skip loading resource strings from all resources during translation recovery +--custom-decryption-script= Load a custom decryption script from the specified path and use it for the recovery session """ # todo: handle --key option var COMPILE_OPTS_NOTES = """Decompile/Compile Options: @@ -973,6 +992,15 @@ func recovery( input_files:PackedStringArray, if (da.file_exists(output_dir)): print("Error: output dir appears to be a file, not extracting...") return 1 + + var not_full_recovery = extract_only or translation_only or scripts_only or includes.size() > 0 or excludes.size() > 0 + if !not_full_recovery and GDREConfig.get_setting("Recovery/clear_output_dir_except_git_before_full_recovery"): + var log_file = GDRESettings.get_log_file_path().get_file() + err = GDRECommon.clear_dir_except_for(output_dir, [".git", ".gitignore", log_file]) + if (err != OK): + print("Error: failed to clear output directory except for git") + return 1 + if is_dir: if extract_only: print("Why did you open a folder to extract it??? What's wrong with you?!!?") @@ -1495,6 +1523,24 @@ func handle_cli(args: PackedStringArray) -> bool: scripts_only = true elif arg.begins_with("--key"): enc_key = get_arg_value(arg) + elif arg.begins_with("--custom-decryption-script"): + var decryptor_script_path = get_cli_abs_path(get_arg_value(arg)) + if decryptor_script_path.is_empty(): + print_usage() + print("Error: path is required for --custom-decryption-script") + ret_code = 1 + return true + decryptor_script_path = get_cli_abs_path(decryptor_script_path) + if not FileAccess.file_exists(decryptor_script_path): + print_usage() + print("Error: custom encryption script file '" + decryptor_script_path + "' does not exist") + ret_code = 1 + return true + if GDRESettings.set_custom_decryption_script(decryptor_script_path) != OK: + print_usage() + print("Error: failed to set custom encryption script: " + decryptor_script_path) + ret_code = 1 + return true elif arg.begins_with("--ignore-checksum-errors"): ignore_md5 = true elif arg.begins_with("--skip-checksum-check"): @@ -1815,3 +1861,16 @@ func _on_gdre_patch_pck_do_patch_pck(dest_pck: String, file_map: Dictionary[Stri return popup_error_box("PCK patching complete", "Success") pass # Replace with function body. + + +func _on_encryption_script_chooser_pressed() -> void: + %EncryptionScriptFileDialog.popup_centered() + pass # Replace with function body. + + +func _on_encryption_script_clear_pressed() -> void: + %EncryptionScriptPathText.text = "" + + +func _on_encryption_script_file_dialog_file_selected(path: String) -> void: + %EncryptionScriptPathText.text = path diff --git a/standalone/gdre_main.tscn b/standalone/gdre_main.tscn index d443b8db..c5e4a48c 100644 --- a/standalone/gdre_main.tscn +++ b/standalone/gdre_main.tscn @@ -2,7 +2,9 @@ [ext_resource type="Theme" uid="uid://crq1fwn0ajw7b" path="res://gdre_theme.tres" id="1"] [ext_resource type="Script" uid="uid://bqpxqjfn2qt1g" path="res://gdre_main.gd" id="2"] +[ext_resource type="Texture2D" uid="uid://bwbdnwnpkyycv" path="res://gdre_icons/gdre_FileBrowse.svg" id="3_6hx8r"] [ext_resource type="Texture2D" uid="uid://drxn4ollkvprv" path="res://gdre_icons/gdre_Logo.svg" id="3_bc0t5"] +[ext_resource type="Texture2D" uid="uid://ctf2qc5l62xbw" path="res://gdre_icons/gdre_Close.svg" id="4_jf1ob"] [ext_resource type="Texture2D" uid="uid://bp8yf7a2dd6nu" path="res://gdre_icons/gdre_Pack.svg" id="4_nesr8"] [ext_resource type="Texture2D" uid="uid://b3l2iqhf34x5c" path="res://gdre_icons/gdre_LogoBig.svg" id="4_vsto2"] [ext_resource type="Texture2D" uid="uid://ditgs1iq4qkup" path="res://gdre_icons/gdre_Translation.svg" id="5_mjm1x"] @@ -36,8 +38,9 @@ script = ExtResource("2") oversampling_override = 1.0 title = "Set Encryption Key" position = Vector2i(144, 24) -size = Vector2i(800, 150) +size = Vector2i(800, 220) visible = false +exclusive = true [node name="VBoxContainer" type="VBoxContainer" parent="SetEncryptionKeyWindow" unique_id=1675930274] anchors_preset = 5 @@ -55,10 +58,37 @@ layout_mode = 2 text = "Set Encryption Key (64 char hex string...)" [node name="KeyText" type="TextEdit" parent="SetEncryptionKeyWindow/VBoxContainer" unique_id=931078826] +unique_name_in_owner = true custom_minimum_size = Vector2(2.08165e-12, 36) layout_mode = 2 placeholder_text = "" +[node name="Label2" type="Label" parent="SetEncryptionKeyWindow/VBoxContainer" unique_id=1933890505] +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..." + +[node name="HBoxContainer2" type="HBoxContainer" parent="SetEncryptionKeyWindow/VBoxContainer" unique_id=1842488725] +layout_mode = 2 +tooltip_text = "Optional decryption script for games with custom encryption" + +[node name="EncryptionScriptPathText" type="TextEdit" parent="SetEncryptionKeyWindow/VBoxContainer/HBoxContainer2" unique_id=1717632378] +unique_name_in_owner = true +custom_minimum_size = Vector2(2.08165e-12, 36) +layout_mode = 2 +size_flags_horizontal = 3 +tooltip_text = "Optional decryption script for games with custom encryption" +editable = false + +[node name="EncryptionScriptChooser" type="Button" parent="SetEncryptionKeyWindow/VBoxContainer/HBoxContainer2" unique_id=2083068586] +layout_mode = 2 +icon = ExtResource("3_6hx8r") + +[node name="EncryptionScriptClear" type="Button" parent="SetEncryptionKeyWindow/VBoxContainer/HBoxContainer2" unique_id=1347551438] +layout_mode = 2 +icon = ExtResource("4_jf1ob") + [node name="Container" type="Container" parent="SetEncryptionKeyWindow/VBoxContainer" unique_id=1558141142] custom_minimum_size = Vector2(2.08165e-12, 10) layout_mode = 2 @@ -332,6 +362,19 @@ filters = PackedStringArray("*.sample;WAV Sample files") show_hidden_files = true use_native_dialog = true +[node name="EncryptionScriptFileDialog" type="FileDialog" parent="." unique_id=1281606175] +unique_name_in_owner = true +title = "Open a File" +dialog_hide_on_ok = true +file_mode = 0 +display_mode = 1 +access = 2 +filters = PackedStringArray("*.gd;GDScript files") +show_hidden_files = true +use_native_dialog = true + +[connection signal="pressed" from="SetEncryptionKeyWindow/VBoxContainer/HBoxContainer2/EncryptionScriptChooser" to="." method="_on_encryption_script_chooser_pressed"] +[connection signal="pressed" from="SetEncryptionKeyWindow/VBoxContainer/HBoxContainer2/EncryptionScriptClear" to="." method="_on_encryption_script_clear_pressed"] [connection signal="pressed" from="SetEncryptionKeyWindow/VBoxContainer/HBoxContainer/CancelButton" to="." method="_on_setenc_key_cancel_pressed"] [connection signal="pressed" from="SetEncryptionKeyWindow/VBoxContainer/HBoxContainer/OK Button" to="." method="_on_setenc_key_ok_pressed"] [connection signal="about_to_popup" from="MenuContainer/REToolsMenu" to="." method="_menubutton_about_to_popup"] @@ -347,3 +390,4 @@ use_native_dialog = true [connection signal="files_selected" from="OggStreamToOGGFileDialog" to="." method="_on_ogg_file_dialog_files_selected"] [connection signal="files_selected" from="MP3StreamToMP3FileDialog" to="." method="_on_mp_3_stream_to_mp_3_file_dialog_files_selected"] [connection signal="files_selected" from="SampleToWAVFileDialog" to="." method="_on_sample_file_dialog_files_selected"] +[connection signal="file_selected" from="EncryptionScriptFileDialog" to="." method="_on_encryption_script_file_dialog_file_selected"] diff --git a/standalone/gdre_theme.tres b/standalone/gdre_theme.tres index b34c0900..388f47cd 100644 --- a/standalone/gdre_theme.tres +++ b/standalone/gdre_theme.tres @@ -160,6 +160,15 @@ font_names = PackedStringArray("Cascadia Code", "Consolas", "Monospace") bg_color = Color(0.0965684, 0.120984, 0.166773, 1) [sub_resource type="FontFile" id="FontFile_w2kt3"] +cache/0/variation_coordinates = {} +cache/0/face_index = 0 +cache/0/embolden = 0.0 +cache/0/transform = Transform2D(1, 0, 0, 1, 0, 0) +cache/0/spacing_top = 0 +cache/0/spacing_bottom = 0 +cache/0/spacing_space = 0 +cache/0/spacing_glyph = 0 +cache/0/baseline_offset = 0.0 cache/0/16/0/ascent = 0.0 cache/0/16/0/descent = 0.0 cache/0/16/0/underline_position = 0.0 diff --git a/tests/test_bytecode.h b/tests/test_bytecode.h index 23faff65..0df597ff 100644 --- a/tests/test_bytecode.h +++ b/tests/test_bytecode.h @@ -1,5 +1,4 @@ -#ifndef TEST_BYTECODE_H -#define TEST_BYTECODE_H +#pragma once #include "../bytecode/bytecode_base.h" #include "bytecode/bytecode_versions.h" @@ -471,5 +470,3 @@ TEST_CASE("[GDSDecomp][Bytecode][EOFNewline] Test indented newline at EOF") { } } //namespace TestBytecode - -#endif // TEST_BYTECODE_H diff --git a/tests/test_common.cpp b/tests/test_common.cpp index 9bd78d82..8e1ef30f 100644 --- a/tests/test_common.cpp +++ b/tests/test_common.cpp @@ -1,9 +1,5 @@ #include "test_common.h" -#include "bytecode/bytecode_base.h" -#include "bytecode/bytecode_versions.h" -#include "compat/fake_gdscript.h" #include "core/os/main_loop.h" -#include "modules/gdscript/gdscript_tokenizer_buffer.h" #include "servers/audio/audio_server.h" #ifdef WINDOWS_ENABLED diff --git a/tests/test_resource_export.cpp b/tests/test_resource_export.cpp index b0dab3b8..92a107ee 100644 --- a/tests/test_resource_export.cpp +++ b/tests/test_resource_export.cpp @@ -1,6 +1,6 @@ #include "test_resource_export.h" -#include "compat/oggstr_loader_compat.h" +#include "modules/vorbis/audio_stream_ogg_vorbis.h" #include "test_common.h" #include "tests/test_macros.h" #include diff --git a/tests/test_resource_loading.h b/tests/test_resource_loading.h index 4c38dd57..b9f434f1 100644 --- a/tests/test_resource_loading.h +++ b/tests/test_resource_loading.h @@ -1,5 +1,4 @@ -#ifndef TEST_RESOURCE_LOADING_H -#define TEST_RESOURCE_LOADING_H +#pragma once #include #include @@ -735,5 +734,3 @@ TEST_CASE("[GDSDecomp][ResourceSaving] Test external resources") { } } //namespace TestResourceLoading - -#endif diff --git a/tests/test_variant_compat.h b/tests/test_variant_compat.h index fad3ee27..a8d08878 100644 --- a/tests/test_variant_compat.h +++ b/tests/test_variant_compat.h @@ -1,6 +1,5 @@ -#ifndef TEST_VARIANT_COMPAT_H -#define TEST_VARIANT_COMPAT_H +#pragma once #include "compat/image_enum_compat.h" #include "compat/image_parser_v2.h" #include "compat/input_event_parser_v2.h" @@ -1081,4 +1080,3 @@ TEST_CASE("[GDSDecomp][VariantCompat] Writer recursive dictionary") { } } //namespace TestVariantCompat -#endif //TEST_VARIANT_COMPAT_H diff --git a/utility/axml_parser.h b/utility/axml_parser.h index f69864da..52ae5051 100644 --- a/utility/axml_parser.h +++ b/utility/axml_parser.h @@ -1,5 +1,4 @@ -#ifndef __AXML_PARSER_H__ -#define __AXML_PARSER_H__ +#pragma once #include #include @@ -37,7 +36,6 @@ class AXMLParser { Vector perms; public: - String get_godot_library_version_string() { return godot_library_version; }; + String get_godot_library_version_string() { return godot_library_version; } Error parse_manifest(Vector &p_manifest); -}; -#endif // __AMXL_PARSER_H__ \ No newline at end of file +}; \ No newline at end of file diff --git a/utility/common.cpp b/utility/common.cpp index 7729ddcd..73eb1e1a 100644 --- a/utility/common.cpp +++ b/utility/common.cpp @@ -1537,6 +1537,29 @@ Ref gdre::load_image_from_file(const String &p_path) { return image; } +Error gdre::clear_dir_except_for(const String &p_dir, const Vector &p_files_or_dirs) { + if (!DirAccess::dir_exists_absolute(p_dir)) { + return OK; + } + HashSet files_or_dirs_set = gdre::vector_to_hashset(p_files_or_dirs); + auto da = DirAccess::create_for_path(p_dir); + if (da.is_null()) { + return ERR_FILE_CANT_OPEN; + } + da->change_dir(p_dir); + da->list_dir_begin(); + String file = da->get_next(); + while (!file.is_empty()) { + if (file == "." || file == ".." || files_or_dirs_set.has(file)) { + file = da->get_next(); + continue; + } + rimraf(p_dir.path_join(file)); + file = da->get_next(); + } + return OK; +} + void GDRECommon::_bind_methods() { // ClassDB::bind_static_method("GLTFCamera", D_METHOD("from_node", "camera_node"), &GLTFCamera::from_node); @@ -1565,4 +1588,5 @@ void GDRECommon::_bind_methods() { ClassDB::bind_static_method("GDRECommon", D_METHOD("get_dirs_at", "dir", "wildcards", "absolute"), &gdre::get_dirs_at); ClassDB::bind_static_method("GDRECommon", D_METHOD("get_recursive_dir_list_multithread", "dir", "wildcards", "absolute", "include_hidden", "exclude_filters", "files_first", "exclude_dot_prefix_and_gdignore", "show_progress"), &gdre::get_recursive_dir_list_multithread, DEFVAL(PackedStringArray()), DEFVAL(true), DEFVAL(true), DEFVAL(PackedStringArray()), DEFVAL(false), DEFVAL(false), DEFVAL(false)); ClassDB::bind_static_method("GDRECommon", D_METHOD("get_safe_dir_name", "dir_name", "allow_paths"), &gdre::get_safe_dir_name, DEFVAL(false)); + ClassDB::bind_static_method("GDRECommon", D_METHOD("clear_dir_except_for", "dir", "files_or_dirs"), &gdre::clear_dir_except_for); } diff --git a/utility/common.h b/utility/common.h index 10636d72..a0518cd3 100644 --- a/utility/common.h +++ b/utility/common.h @@ -375,6 +375,7 @@ String path_to_uri(const String &p_path); bool is_zip_file(const String &p_path); String get_safe_dir_name(const String &p_dir_name, bool p_allow_paths = false); Ref load_image_from_file(const String &p_path); +Error clear_dir_except_for(const String &p_dir, const Vector &p_files_or_dirs); } // namespace gdre class GDRECommon : public Object { diff --git a/utility/diff_result.h b/utility/diff_result.h index 35a6855f..0625e839 100644 --- a/utility/diff_result.h +++ b/utility/diff_result.h @@ -1,5 +1,4 @@ -#ifndef DIFF_RESULT_H -#define DIFF_RESULT_H +#pragma once #include "core/object/ref_counted.h" #include "core/variant/array.h" @@ -167,5 +166,3 @@ class PropertyDiffResult : public RefCounted { Object *get_new_object() const; PropertyDiffResult(const String &p_name, const String &p_change_type, const Variant &p_old_value, const Variant &p_new_value, Object *p_old_object, Object *p_new_object); }; - -#endif // DIFF_RESULT_H diff --git a/utility/file_access_apk.h b/utility/file_access_apk.h index 36482928..c08bae8d 100644 --- a/utility/file_access_apk.h +++ b/utility/file_access_apk.h @@ -28,8 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef FILE_ACCESS_APK_H -#define FILE_ACCESS_APK_H +#pragma once #ifdef MINIZIP_ENABLED @@ -128,5 +127,3 @@ class FileAccessAPK : public FileAccess { }; #endif // MINIZIP_ENABLED - -#endif // FILE_ACCESS_ZIP_H diff --git a/utility/file_access_gdre.cpp b/utility/file_access_gdre.cpp index 902db41a..f9b751eb 100644 --- a/utility/file_access_gdre.cpp +++ b/utility/file_access_gdre.cpp @@ -933,10 +933,11 @@ DirAccessGDRE::~DirAccessGDRE() { #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" #else // UNIX_ENABLED -- covers OSX, Linux, FreeBSD, Web. -#include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" #ifdef MACOS_ENABLED #include "platform/macos/dir_access_macos.h" +#else +#include "drivers/unix/dir_access_unix.h" #endif #ifdef ANDROID_ENABLED #include "platform/android/dir_access_jandroid.h" diff --git a/utility/gd_parallel_hashmap.h b/utility/gd_parallel_hashmap.h index 54f7520d..0dcc7486 100644 --- a/utility/gd_parallel_hashmap.h +++ b/utility/gd_parallel_hashmap.h @@ -1,7 +1,4 @@ - -#ifndef GD_PARALLEL_HASHMAP_H -#define GD_PARALLEL_HASHMAP_H - +#pragma once #include "core/os/mutex.h" #include "external/parallel_hashmap/phmap.h" #include "std_allocator.h" @@ -73,5 +70,3 @@ template // use std::mutex to enable internal locks using ParallelNodeHashMap = phmap::parallel_node_hash_map; - -#endif //GD_PARALLEL_HASHMAP_H diff --git a/utility/gd_parallel_queue.h b/utility/gd_parallel_queue.h index 122eb6c8..3ad9c9d7 100644 --- a/utility/gd_parallel_queue.h +++ b/utility/gd_parallel_queue.h @@ -1,10 +1,6 @@ - -#ifndef GD_PARALLEL_QUEUE_H -#define GD_PARALLEL_QUEUE_H - +#pragma once #include "external/atomic_queue/atomic_queue.h" #include "std_allocator.h" -#include "std_hash.h" template using ParallelQueueAllocator = GodotStdAllocator; @@ -14,4 +10,3 @@ using StaticParallelQueue = atomic_queue::AtomicQueue2, bool MAXIMIZE_THROUGHPUT = true, bool TOTAL_ORDER = false, bool SPSC = false> using ParalellQueue = atomic_queue::AtomicQueueB2; -#endif //GD_PARALLEL_QUEUE_H diff --git a/utility/gdre_config.cpp b/utility/gdre_config.cpp index fd45d335..d722dee3 100644 --- a/utility/gdre_config.cpp +++ b/utility/gdre_config.cpp @@ -2,6 +2,7 @@ #include "bytecode/bytecode_base.h" #include "bytecode/bytecode_versions.h" #include "common.h" +#include "core/io/config_file.h" #include "core/io/json.h" #include "gdre_logger.h" #include "gdre_settings.h" @@ -249,6 +250,12 @@ Vector> GDREConfig::_init_default_settings() { "Use scene view by default", "Use scene view by default instead of the text preview.\nWARNING: Scene view is still experimental and certain scenes may cause the program to become unresponsive.", false)), + memnew(GDREConfigSetting( + "Recovery/clear_output_dir_except_git_before_full_recovery", + "Clear output directory except git before full recovery", + "Clear the output directory except for the .git directory before running a full recovery (i.e. no includes/excludes)", + false, + true)), memnew(GDREConfigSetting( "Recovery/git/create_git_repo", "Create git repo in recovered project", diff --git a/utility/gdre_config.h b/utility/gdre_config.h index db43fd76..bf689e83 100644 --- a/utility/gdre_config.h +++ b/utility/gdre_config.h @@ -3,7 +3,7 @@ #include "core/object/object.h" #include "core/object/ref_counted.h" #include "core/string/ustring.h" -#include "core/variant/typed_dictionary.h" +#include "core/variant/typed_array.h" #include "gd_parallel_hashmap.h" class GDREConfig; diff --git a/utility/gdre_packed_source.cpp b/utility/gdre_packed_source.cpp index e6a5803f..00cb0b9e 100644 --- a/utility/gdre_packed_source.cpp +++ b/utility/gdre_packed_source.cpp @@ -1,4 +1,6 @@ #include "gdre_packed_source.h" +#include "core/version_generated.gen.h" +#include "crypto/file_access_encrypted_custom.h" #include "file_access_apk.h" #include "file_access_gdre.h" #include "gdre_settings.h" @@ -229,7 +231,7 @@ bool GDREPackedSource::_get_exe_embedded_pck_info(Ref f, const Strin return false; } -bool GDREPackedSource::seek_offset_from_exe(Ref f, const String &p_path) { +bool GDREPackedSource::seek_offset_from_exe(Ref f, const String &p_path, uint64_t &r_pck_size) { EXEPCKInfo info; auto ret = _get_exe_embedded_pck_info(f, p_path, info); #ifdef DEBUG_ENABLED @@ -247,6 +249,7 @@ bool GDREPackedSource::seek_offset_from_exe(Ref f, const String &p_p print_verbose("Embedded PCK not found in executable."); } #endif + r_pck_size = info.pck_embed_size; return ret; } @@ -278,11 +281,30 @@ bool GDREPackedSource::has_embedded_pck(const String &p_path) { if (f.is_null()) { return false; } - return seek_offset_from_exe(f, p_path); + uint64_t file_length; + return seek_offset_from_exe(f, p_path, file_length); +} + +bool GDREPackedSource::is_magic_ascii(uint32_t magic) { + for (int i = 0; i < 4; i++) { + // printable ASCII characters + uint32_t c = magic & 0xFF; + if (c < 32 || c > 127) { + return false; + } + magic >>= 8; + } + return true; +} + +String GDREPackedSource::get_magic_ascii(uint32_t magic) { + // little-endian to ASCII + return String::chr(magic & 0xFF) + String::chr((magic >> 8) & 0xFF) + String::chr((magic >> 16) & 0xFF) + String::chr((magic >> 24) & 0xFF); } bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) { - if (p_path.get_extension().to_lower() == "apk" || p_path.get_extension().to_lower() == "zip") { + String ext = p_path.get_extension().to_lower(); + if (ext == "apk" || ext == "zip") { return false; } String pck_path = p_path.replace("_GDRE_a_really_dumb_hack", ""); @@ -293,20 +315,32 @@ bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, bool is_exe = false; uint32_t magic = f->get_32(); + bool suspect_magic = false; + String non_standard_header; + + uint64_t pck_size = f->get_length(); if (magic != PACK_HEADER_MAGIC) { - // Loading with offset feature not supported for self contained exe files. - if (p_offset != 0) { - ERR_FAIL_V_MSG(false, "Loading self-contained executable with offset not supported."); - } + if (ext == "pck" && is_magic_ascii(magic)) { + suspect_magic = true; + non_standard_header = get_magic_ascii(magic); + WARN_PRINT("PCK has suspect magic: " + get_magic_ascii(magic)); + } else { + // Loading with offset feature not supported for self contained exe files. + if (p_offset != 0) { + ERR_FAIL_V_MSG(false, "Loading self-contained executable with offset not supported."); + } - if (!seek_offset_from_exe(f, pck_path)) { - return false; + if (!seek_offset_from_exe(f, pck_path, pck_size)) { + ERR_FAIL_COND_V_MSG(ext == "pck", false, "PCK header not found in pck file: " + pck_path); + return false; + } + is_exe = true; } - is_exe = true; } int64_t pck_start_pos = f->get_position() - 4; + uint64_t pck_end_pos = pck_start_pos + pck_size; uint32_t version = f->get_32(); uint32_t ver_major = f->get_32(); @@ -317,6 +351,12 @@ bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, ERR_FAIL_V_MSG(false, "Pack version unsupported: " + itos(version) + ". (engine version: " + itos(ver_major) + "." + itos(ver_minor) + "." + itos(ver_patch) + ")"); } + if (suspect_magic) { + if (ver_major > GODOT_VERSION_MAJOR || (ver_major == 0)) { + ERR_FAIL_V_MSG(false, "PCK ver_major is suspicious, not continuing: " + itos(version) + ". (engine version: " + itos(ver_major) + "." + itos(ver_minor) + "." + itos(ver_patch) + ")"); + } + } + uint32_t pack_flags = 0; uint64_t file_base = 0; @@ -346,6 +386,7 @@ bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, uint64_t dir_offset = f->get_64() + pck_start_pos; ERR_FAIL_COND_V_MSG(dir_offset == 0, false, "Directory offset is 0, this is not a valid PCK file\n" + DEBUG_PCK_INFO()); + ERR_FAIL_COND_V_MSG(dir_offset >= pck_end_pos, false, "Directory offset is out of bounds: " + String::num_int64(dir_offset) + " (file length: " + String::num_int64(pck_end_pos) + ")"); f->seek(dir_offset); } else if (version <= PACK_FORMAT_VERSION_V2) { // V2: Directory directly after the header. @@ -356,25 +397,27 @@ bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, #undef DEBUG_PCK_INFO uint32_t file_count = f->get_32(); + ERR_FAIL_COND_V_MSG(file_count > 0 && file_base >= pck_end_pos, false, "file_base is out of bounds: " + String::num_int64(file_base) + " (file length: " + String::num_int64(pck_size) + ")"); if (enc_directory) { - Ref fae = memnew(FileAccessEncrypted); - if (fae.is_null()) { - GDRESettings::get_singleton()->_set_error_encryption(true); - ERR_FAIL_V_MSG(false, "Failed to instance FileAccessEncrypted??????."); - } - Vector key; key.resize(32); for (int i = 0; i < key.size(); i++) { key.write[i] = script_encryption_key[i]; } - - Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); + Error err = OK; + if (GDRESettings::get_singleton()->get_custom_decryptor().is_valid()) { + Ref fae = FileAccessEncryptedCustom::create(GDRESettings::get_singleton()->get_custom_decryptor()); + err = fae->open_and_parse(f, key, FileAccessEncryptedCustom::MODE_READ, false); + f = fae; + } else { + Ref fae = memnew(FileAccessEncrypted); + err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); + f = fae; + } if (err) { GDRESettings::get_singleton()->_set_error_encryption(true); ERR_FAIL_V_MSG(false, "Can't open encrypted pack directory (PCK format version " + itos(version) + ", engine version " + itos(ver_major) + "." + itos(ver_minor) + "." + itos(ver_patch) + ")."); } - f = fae; } // Set Pack info before reading the file list. @@ -401,9 +444,12 @@ bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, Ref pckinfo; pckinfo.instantiate(); pckinfo->init( - pck_path, godot_ver, version, pack_flags, file_base, file_count, is_exe ? GDRESettings::PackInfo::EXE : GDRESettings::PackInfo::PCK, enc_directory, suspect_version); + pck_path, godot_ver, version, pack_flags, file_base, file_count, is_exe ? GDRESettings::PackInfo::EXE : GDRESettings::PackInfo::PCK, enc_directory, suspect_version, non_standard_header); GDRESettings::get_singleton()->add_pack_info(pckinfo); + bool opened_encrypted_file = false; + bool open_encrypted_success = false; + int64_t encrypted_file_count = 0; // Read the file list. for (uint32_t i = 0; i < file_count; i++) { uint32_t sl = f->get_32(); @@ -422,6 +468,8 @@ bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, uint64_t size = f->get_64(); uint8_t md5[16]; uint32_t flags = 0; + // check if the file offset is out of bounds for pcks with suspect magic + ERR_FAIL_COND_V_MSG(suspect_magic && (ofs + size > pck_end_pos), false, "File offset is out of bounds: " + String::num_int64(ofs) + " (file length: " + String::num_int64(pck_end_pos) + ")"); f->get_buffer(md5, 16); if (version >= PACK_FORMAT_VERSION_V2) { flags = f->get_32(); @@ -429,9 +477,37 @@ bool GDREPackedSource::try_open_pack(const String &p_path, bool p_replace_files, if (flags & PACK_FILE_REMOVAL) { // The file was removed. GDREPackedData::get_singleton()->remove_path(path); } else { - GDREPackedData::get_singleton()->add_path(pck_path, path, ofs, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED), sparse_bundle, (flags & PACK_FILE_DELTA)); + bool encrypted = (flags & PACK_FILE_ENCRYPTED); + bool delta = (flags & PACK_FILE_DELTA); + GDREPackedData::get_singleton()->add_path(pck_path, path, ofs, size, md5, this, p_replace_files, encrypted, sparse_bundle, delta); + if (encrypted) { + encrypted_file_count++; + if (!opened_encrypted_file && size > 0 && !delta) { + opened_encrypted_file = true; + // try opening the file + PackedData::PackedFile pf; + pf.pack = pck_path; + pf.offset = ofs; + pf.size = size; + memcpy(pf.md5, md5, 16); + pf.src = this; + pf.encrypted = encrypted; + pf.bundle = sparse_bundle; + pf.delta = delta; + Ref fa = get_file(path, &pf); + if (fa.is_null() || fa->get_error() != OK) { + WARN_PRINT("Can't open encrypted files in PCK!"); + GDRESettings::get_singleton()->_set_error_encryption(true); + } else { + open_encrypted_success = true; + } + } + } } } + if (opened_encrypted_file && !open_encrypted_success) { + WARN_PRINT("Can't decrypt " + itos(encrypted_file_count) + " encrypted files in PCK!"); + } return true; } @@ -471,8 +547,25 @@ Ref GDREPackedSource::get_file(const String &p_path, PackedData::Pac file = fae; } } else { - // otherwise... - file = Ref(memnew(FileAccessPack(p_path, *p_file))); + if (p_file->encrypted && GDRESettings::get_singleton()->get_custom_decryptor().is_valid()) { + Ref base = FileAccess::open(p_file->pack, FileAccess::READ); + ERR_FAIL_COND_V_MSG(base.is_null(), nullptr, vformat("Can't open pack-referenced file '%s'.", String(p_path))); + base->seek(p_file->offset); + + Ref fae = FileAccessEncryptedCustom::create(GDRESettings::get_singleton()->get_custom_decryptor()); + ERR_FAIL_COND_V_MSG(fae.is_null(), nullptr, vformat("Can't open encrypted pack-referenced file '%s'.", String(p_path))); + Vector key; + key.resize(32); + for (int i = 0; i < key.size(); i++) { + key.write[i] = script_encryption_key[i]; + } + Error err = fae->open_and_parse(base, key, FileAccessEncryptedCustom::MODE_READ, false); + ERR_FAIL_COND_V_MSG(err, nullptr, vformat("Can't open encrypted pack-referenced file '%s'.", String(p_path))); + file = fae; + } else { + // otherwise... + file = Ref(memnew(FileAccessPack(p_path, *p_file))); + } } if (GDREPackedData::get_singleton()->has_delta_patches(p_path)) { diff --git a/utility/gdre_packed_source.h b/utility/gdre_packed_source.h index b1fda1fe..7c376a4d 100644 --- a/utility/gdre_packed_source.h +++ b/utility/gdre_packed_source.h @@ -26,7 +26,9 @@ class GDREPackedSource : public PackSource { static bool get_pck_section_info_unix(Ref f, GDREPackedSource::EXEPCKInfo &info); static bool seek_after_magic_windows(Ref f); static bool get_pck_section_info_windows(Ref f, GDREPackedSource::EXEPCKInfo &r_info); - static bool seek_offset_from_exe(Ref f, const String &p_path); + static bool seek_offset_from_exe(Ref f, const String &p_path, uint64_t &r_pck_size); + static bool is_magic_ascii(uint32_t magic); + static String get_magic_ascii(uint32_t magic); public: static constexpr int CURRENT_PACK_FORMAT_VERSION = 3; diff --git a/utility/gdre_settings.cpp b/utility/gdre_settings.cpp index 7b412bb6..5619f611 100644 --- a/utility/gdre_settings.cpp +++ b/utility/gdre_settings.cpp @@ -9,11 +9,12 @@ #include "core/error/error_macros.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" -#include "core/io/file_access_encrypted.h" #include "core/object/class_db.h" #include "core/string/print_string.h" +#include "crypto/custom_decryptor.h" #include "exporters/translation_exporter.h" #include "main/main.h" +#include "modules/gdscript/gdscript.h" #include "modules/zip/zip_reader.h" #include "plugin_manager/plugin_manager.h" #include "utility/common.h" @@ -120,10 +121,12 @@ int GDRESettings::get_ver_major_from_dir() { if (check_if_dir_is_v2() && (FileAccess::exists("res://engine.cfb") || FileAccess::exists("res://engine.cfg"))) { return 2; } - if (check_if_dir_is_v4()) + if (check_if_dir_is_v4()) { return 4; - if (check_if_dir_is_v3()) + } + if (check_if_dir_is_v3()) { return 3; + } bool not_v2 = !check_if_dir_is_v2() || FileAccess::exists("res://project.binary") || FileAccess::exists("res://project.godot"); // deeper checking; we know it's not v2, so we don't need to check that. @@ -313,6 +316,20 @@ uint32_t GDRESettings::get_file_count() const { return count; } +bool GDRESettings::uses_nonstandard_headers() const { + if (!is_pack_loaded()) { + return false; + } + return !current_project->non_standard_header.is_empty(); +} + +String GDRESettings::get_non_standard_header() const { + if (!is_pack_loaded()) { + return ""; + } + return current_project->non_standard_header; +} + void GDRESettings::set_project_path(const String &p_path) { project_path = p_path; } @@ -685,6 +702,7 @@ Error GDRESettings::_add_pack(const String &path) { Error GDRESettings::load_project(const Vector &p_paths, bool _cmd_line_extract, const String &csharp_assembly_override) { GDRELogger::clear_error_queues(); + error_encryption = false; if (is_pack_loaded()) { return ERR_ALREADY_IN_USE; } @@ -1144,6 +1162,11 @@ 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(); + // 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; + } if (!is_pack_loaded()) { return ERR_DOES_NOT_EXIST; } @@ -1151,7 +1174,6 @@ Error GDRESettings::unload_project(bool p_no_reset_ephemeral) { error_encryption = false; remove_current_pack(); - GDREPackedData::get_singleton()->clear(); reset_uid_cache(); reset_gdscript_cache(); if (!p_no_reset_ephemeral && GDREConfig::get_singleton()) { @@ -1169,6 +1191,7 @@ void GDRESettings::add_pack_info(Ref packinfo) { current_project->pack_file = packinfo->pack_file; current_project->type = packinfo->type; current_project->suspect_version = packinfo->suspect_version; + current_project->non_standard_header = packinfo->non_standard_header; } else { if (!current_project->version->eq(packinfo->version)) { if ((!current_project->version->is_valid_semver() || current_project->version->get_major() == 0) && @@ -1286,19 +1309,21 @@ Error GDRESettings::set_encryption_key_string(const String &key_str) { int v = 0; if (i * 2 < skey.length()) { char32_t ct = skey.to_lower()[i * 2]; - if (ct >= '0' && ct <= '9') + if (ct >= '0' && ct <= '9') { ct = ct - '0'; - else if (ct >= 'a' && ct <= 'f') + } else if (ct >= 'a' && ct <= 'f') { ct = 10 + ct - 'a'; + } v |= ct << 4; } if (i * 2 + 1 < skey.length()) { char32_t ct = skey.to_lower()[i * 2 + 1]; - if (ct >= '0' && ct <= '9') + if (ct >= '0' && ct <= '9') { ct = ct - '0'; - else if (ct >= 'a' && ct <= 'f') + } else if (ct >= 'a' && ct <= 'f') { ct = 10 + ct - 'a'; + } v |= ct; } key.write[i] = v; @@ -1307,6 +1332,42 @@ Error GDRESettings::set_encryption_key_string(const String &key_str) { return OK; } +Error GDRESettings::set_custom_decryption_script(const String &p_decryptor_script_path) { + ERR_FAIL_COND_V_MSG(!FileAccess::exists(p_decryptor_script_path), ERR_FILE_NOT_FOUND, "Custom encryption script file '" + p_decryptor_script_path + "' does not exist"); + ERR_FAIL_COND_V_MSG(p_decryptor_script_path.get_extension().to_lower() != "gd", ERR_INVALID_PARAMETER, "Custom encryption script file must be a GDScript!"); + Error err = OK; + ResourceFormatLoaderGDScript loader; + Ref