From db03f8612d2a07e0e9a8891c9ca2ab690f54b177 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 17 Aug 2022 20:39:11 +0200 Subject: [PATCH] [godot] Related to #2134, validate .json and .skel files before import. --- spine-godot/spine_godot/SpineEditorPlugin.cpp | 10 +- spine-godot/spine_godot/SpineEditorPlugin.h | 6 ++ .../spine_godot/SpineSkeletonFileResource.cpp | 93 ++++++++++++++++++- 3 files changed, 100 insertions(+), 9 deletions(-) diff --git a/spine-godot/spine_godot/SpineEditorPlugin.cpp b/spine-godot/spine_godot/SpineEditorPlugin.cpp index c71424d20..0bb424cae 100644 --- a/spine-godot/spine_godot/SpineEditorPlugin.cpp +++ b/spine-godot/spine_godot/SpineEditorPlugin.cpp @@ -71,11 +71,12 @@ Error SpineJsonResourceImportPlugin::import(const String &source_file, const Str Error SpineJsonResourceImportPlugin::import(const String &source_file, const String &save_path, const Map &options, List *platform_variants, List *gen_files, Variant *metadata) { #endif Ref skeleton_file_res(memnew(SpineSkeletonFileResource)); - skeleton_file_res->load_from_file(source_file); + Error error = skeleton_file_res->load_from_file(source_file); + if (error != OK) return error; String file_name = vformat("%s.%s", save_path, get_save_extension()); #if VERSION_MAJOR > 3 - auto error = ResourceSaver::save(skeleton_file_res, file_name); + error = ResourceSaver::save(skeleton_file_res, file_name); #else auto error = ResourceSaver::save(file_name, skeleton_file_res); #endif @@ -88,11 +89,12 @@ Error SpineBinaryResourceImportPlugin::import(const String &source_file, const S Error SpineBinaryResourceImportPlugin::import(const String &source_file, const String &save_path, const Map &options, List *platform_variants, List *gen_files, Variant *metadata) { #endif Ref skeleton_file_res(memnew(SpineSkeletonFileResource)); - skeleton_file_res->load_from_file(source_file); + Error error = skeleton_file_res->load_from_file(source_file); + if (error != OK) return error; String file_name = vformat("%s.%s", save_path, get_save_extension()); #if VERSION_MAJOR > 3 - auto error = ResourceSaver::save(skeleton_file_res, file_name); + error = ResourceSaver::save(skeleton_file_res, file_name); #else auto error = ResourceSaver::save(file_name, skeleton_file_res); #endif diff --git a/spine-godot/spine_godot/SpineEditorPlugin.h b/spine-godot/spine_godot/SpineEditorPlugin.h index ca29b0df5..720cef48b 100644 --- a/spine-godot/spine_godot/SpineEditorPlugin.h +++ b/spine-godot/spine_godot/SpineEditorPlugin.h @@ -57,6 +57,8 @@ public: #if VERSION_MAJOR > 3 int get_import_order() const override { return IMPORT_ORDER_DEFAULT; } + float get_priority() const override { return 1; } + void get_import_options(const String &path, List *options, int preset) const override; virtual bool get_option_visibility(const String &path, const String &option, const HashMap &options) const override { return true; } @@ -92,6 +94,8 @@ public: #if VERSION_MAJOR > 3 int get_import_order() const override { return IMPORT_ORDER_DEFAULT; } + float get_priority() const override { return 1; } + void get_import_options(const String &path, List *options, int preset) const override {} bool get_option_visibility(const String &path, const String &option, const HashMap &options) const override { return true; } @@ -127,6 +131,8 @@ public: #if VERSION_MAJOR > 3 int get_import_order() const override { return IMPORT_ORDER_DEFAULT; } + float get_priority() const override { return 1; } + void get_import_options(const String &path, List *options, int preset) const override {} bool get_option_visibility(const String &path, const String &option, const HashMap &options) const override { return true; } diff --git a/spine-godot/spine_godot/SpineSkeletonFileResource.cpp b/spine-godot/spine_godot/SpineSkeletonFileResource.cpp index ad5395eb1..f9f5275c8 100644 --- a/spine-godot/spine_godot/SpineSkeletonFileResource.cpp +++ b/spine-godot/spine_godot/SpineSkeletonFileResource.cpp @@ -33,16 +33,99 @@ #else #include "core/os/file_access.h" #endif +#include +#include +#include + + +struct BinaryInput { + const unsigned char *cursor; + const unsigned char *end; +}; + +static unsigned char readByte(BinaryInput *input) { + return *input->cursor++; +} + +static int readVarint(BinaryInput *input, bool optimizePositive) { + unsigned char b = readByte(input); + int value = b & 0x7F; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 7; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 14; + if (b & 0x80) { + b = readByte(input); + value |= (b & 0x7F) << 21; + if (b & 0x80) value |= (readByte(input) & 0x7F) << 28; + } + } + } + + if (!optimizePositive) { + value = (((unsigned int) value >> 1) ^ -(value & 1)); + } + + return value; +} + +static char *readString(BinaryInput *input) { + int length = readVarint(input, true); + char *string; + if (length == 0) { + return NULL; + } + string = spine::SpineExtension::alloc(length, __FILE__, __LINE__); + memcpy(string, input->cursor, length - 1); + input->cursor += length - 1; + string[length - 1] = '\0'; + return string; +} void SpineSkeletonFileResource::_bind_methods() { } +static bool checkVersion(const char *version) { + if (!version) return false; + char *result = (char *) (strstr(version, SPINE_VERSION_STRING) - version); + return result == 0; +} + +static bool checkJson(const char *jsonData) { + spine::Json json(jsonData); + spine::Json *skeleton = spine::Json::getItem(&json, "skeleton"); + if (!skeleton) return false; + const char *version = spine::Json::getString(skeleton, "spine", 0); + if (!version) return false; + + return checkVersion(version); +} + +static bool checkBinary(const char *binaryData, int length) { + BinaryInput input; + input.cursor = (const unsigned char *) binaryData; + input.end = (const unsigned char *) binaryData + length; + // Skip hash + input.cursor += 8; + char *version = readString(&input); + bool result = checkVersion(version); + spine::SpineExtension::free(version, __FILE__, __LINE__); + return result; +} + Error SpineSkeletonFileResource::load_from_file(const String &path) { - Error error; - if (path.ends_with("spjson")) - json = FileAccess::get_file_as_string(path, &error); - else - binary = FileAccess::get_file_as_array(path, &error); + Error error = OK; + if (path.ends_with(".spjson") || path.ends_with(".json")) { + json = FileAccess::get_file_as_string(path, &error); + if (error != OK) return error; + if (!checkJson(json.utf8())) return ERR_INVALID_DATA; + } else { + binary = FileAccess::get_file_as_array(path, &error); + if (error != OK) return error; + if (!checkBinary((const char*)binary.ptr(), binary.size())) return ERR_INVALID_DATA; + } return error; }