|
@@ -33,8 +33,6 @@
|
|
#include "core/io/resource_loader.h"
|
|
#include "core/io/resource_loader.h"
|
|
#include "core/object/ref_counted.h"
|
|
#include "core/object/ref_counted.h"
|
|
#include "core/object/script_language.h"
|
|
#include "core/object/script_language.h"
|
|
-#include "core/os/keyboard.h"
|
|
|
|
-#include "core/string/print_string.h"
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdio.h>
|
|
@@ -69,10 +67,31 @@ ObjectID EncodedObjectAsID::get_object_id() const {
|
|
// For `Variant::ARRAY`.
|
|
// For `Variant::ARRAY`.
|
|
// Occupies bits 16 and 17.
|
|
// Occupies bits 16 and 17.
|
|
#define HEADER_DATA_FIELD_TYPED_ARRAY_MASK (0b11 << 16)
|
|
#define HEADER_DATA_FIELD_TYPED_ARRAY_MASK (0b11 << 16)
|
|
-#define HEADER_DATA_FIELD_TYPED_ARRAY_NONE (0b00 << 16)
|
|
|
|
-#define HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN (0b01 << 16)
|
|
|
|
-#define HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME (0b10 << 16)
|
|
|
|
-#define HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT (0b11 << 16)
|
|
|
|
|
|
+#define HEADER_DATA_FIELD_TYPED_ARRAY_SHIFT 16
|
|
|
|
+
|
|
|
|
+// For `Variant::DICTIONARY`.
|
|
|
|
+// Occupies bits 16 and 17.
|
|
|
|
+#define HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_MASK (0b11 << 16)
|
|
|
|
+#define HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_SHIFT 16
|
|
|
|
+// Occupies bits 18 and 19.
|
|
|
|
+#define HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_MASK (0b11 << 18)
|
|
|
|
+#define HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_SHIFT 18
|
|
|
|
+
|
|
|
|
+enum ContainerTypeKind {
|
|
|
|
+ CONTAINER_TYPE_KIND_NONE = 0b00,
|
|
|
|
+ CONTAINER_TYPE_KIND_BUILTIN = 0b01,
|
|
|
|
+ CONTAINER_TYPE_KIND_CLASS_NAME = 0b10,
|
|
|
|
+ CONTAINER_TYPE_KIND_SCRIPT = 0b11,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+struct ContainerType {
|
|
|
|
+ Variant::Type builtin_type = Variant::NIL;
|
|
|
|
+ StringName class_name;
|
|
|
|
+ Ref<Script> script;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define GET_CONTAINER_TYPE_KIND(m_header, m_field) \
|
|
|
|
+ ((ContainerTypeKind)(((m_header) & HEADER_DATA_FIELD_##m_field##_MASK) >> HEADER_DATA_FIELD_##m_field##_SHIFT))
|
|
|
|
|
|
static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) {
|
|
static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) {
|
|
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
@@ -80,7 +99,7 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
|
|
int32_t strlen = decode_uint32(buf);
|
|
int32_t strlen = decode_uint32(buf);
|
|
int32_t pad = 0;
|
|
int32_t pad = 0;
|
|
|
|
|
|
- // Handle padding
|
|
|
|
|
|
+ // Handle padding.
|
|
if (strlen % 4) {
|
|
if (strlen % 4) {
|
|
pad = 4 - strlen % 4;
|
|
pad = 4 - strlen % 4;
|
|
}
|
|
}
|
|
@@ -88,7 +107,7 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
|
|
buf += 4;
|
|
buf += 4;
|
|
len -= 4;
|
|
len -= 4;
|
|
|
|
|
|
- // Ensure buffer is big enough
|
|
|
|
|
|
+ // Ensure buffer is big enough.
|
|
ERR_FAIL_ADD_OF(strlen, pad, ERR_FILE_EOF);
|
|
ERR_FAIL_ADD_OF(strlen, pad, ERR_FILE_EOF);
|
|
ERR_FAIL_COND_V(strlen < 0 || strlen + pad > len, ERR_FILE_EOF);
|
|
ERR_FAIL_COND_V(strlen < 0 || strlen + pad > len, ERR_FILE_EOF);
|
|
|
|
|
|
@@ -96,10 +115,10 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
|
|
ERR_FAIL_COND_V(str.parse_utf8((const char *)buf, strlen) != OK, ERR_INVALID_DATA);
|
|
ERR_FAIL_COND_V(str.parse_utf8((const char *)buf, strlen) != OK, ERR_INVALID_DATA);
|
|
r_string = str;
|
|
r_string = str;
|
|
|
|
|
|
- // Add padding
|
|
|
|
|
|
+ // Add padding.
|
|
strlen += pad;
|
|
strlen += pad;
|
|
|
|
|
|
- // Update buffer pos, left data count, and return size
|
|
|
|
|
|
+ // Update buffer pos, left data count, and return size.
|
|
buf += strlen;
|
|
buf += strlen;
|
|
len -= strlen;
|
|
len -= strlen;
|
|
if (r_len) {
|
|
if (r_len) {
|
|
@@ -109,6 +128,65 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
|
|
return OK;
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static Error _decode_container_type(const uint8_t *&buf, int &len, int *r_len, bool p_allow_objects, ContainerTypeKind p_type_kind, ContainerType &r_type) {
|
|
|
|
+ switch (p_type_kind) {
|
|
|
|
+ case CONTAINER_TYPE_KIND_NONE: {
|
|
|
|
+ return OK;
|
|
|
|
+ } break;
|
|
|
|
+ case CONTAINER_TYPE_KIND_BUILTIN: {
|
|
|
|
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
|
|
+
|
|
|
|
+ int32_t bt = decode_uint32(buf);
|
|
|
|
+ buf += 4;
|
|
|
|
+ len -= 4;
|
|
|
|
+ if (r_len) {
|
|
|
|
+ (*r_len) += 4;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
|
|
|
|
+ r_type.builtin_type = (Variant::Type)bt;
|
|
|
|
+ if (!p_allow_objects && r_type.builtin_type == Variant::OBJECT) {
|
|
|
|
+ r_type.class_name = EncodedObjectAsID::get_class_static();
|
|
|
|
+ }
|
|
|
|
+ return OK;
|
|
|
|
+ } break;
|
|
|
|
+ case CONTAINER_TYPE_KIND_CLASS_NAME: {
|
|
|
|
+ String str;
|
|
|
|
+ Error err = _decode_string(buf, len, r_len, str);
|
|
|
|
+ if (err) {
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ r_type.builtin_type = Variant::OBJECT;
|
|
|
|
+ if (p_allow_objects) {
|
|
|
|
+ r_type.class_name = str;
|
|
|
|
+ } else {
|
|
|
|
+ r_type.class_name = EncodedObjectAsID::get_class_static();
|
|
|
|
+ }
|
|
|
|
+ return OK;
|
|
|
|
+ } break;
|
|
|
|
+ case CONTAINER_TYPE_KIND_SCRIPT: {
|
|
|
|
+ String path;
|
|
|
|
+ Error err = _decode_string(buf, len, r_len, path);
|
|
|
|
+ if (err) {
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ r_type.builtin_type = Variant::OBJECT;
|
|
|
|
+ if (p_allow_objects) {
|
|
|
|
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path \"%s\".", path));
|
|
|
|
+ r_type.script = ResourceLoader::load(path, "Script");
|
|
|
|
+ ERR_FAIL_COND_V_MSG(r_type.script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path \"%s\".", path));
|
|
|
|
+ r_type.class_name = r_type.script->get_instance_base_type();
|
|
|
|
+ } else {
|
|
|
|
+ r_type.class_name = EncodedObjectAsID::get_class_static();
|
|
|
|
+ }
|
|
|
|
+ return OK;
|
|
|
|
+ } break;
|
|
|
|
+ }
|
|
|
|
+ ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Invalid container type kind."); // Future proofing.
|
|
|
|
+}
|
|
|
|
+
|
|
Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_objects, int p_depth) {
|
|
Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_objects, int p_depth) {
|
|
ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Variant is too deep. Bailing.");
|
|
ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Variant is too deep. Bailing.");
|
|
const uint8_t *buf = p_buffer;
|
|
const uint8_t *buf = p_buffer;
|
|
@@ -126,7 +204,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
*r_len = 4;
|
|
*r_len = 4;
|
|
}
|
|
}
|
|
|
|
|
|
- // Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded.
|
|
|
|
|
|
+ // NOTE: We cannot use `sizeof(real_t)` for decoding, in case a different size is encoded.
|
|
// Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
|
|
// Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
|
|
// This does lead to some code duplication for decoding, but compatibility is the priority.
|
|
// This does lead to some code duplication for decoding, but compatibility is the priority.
|
|
switch (header & HEADER_TYPE_MASK) {
|
|
switch (header & HEADER_TYPE_MASK) {
|
|
@@ -188,7 +266,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
|
|
|
|
} break;
|
|
} break;
|
|
|
|
|
|
- // math types
|
|
|
|
|
|
+ // Math types.
|
|
case Variant::VECTOR2: {
|
|
case Variant::VECTOR2: {
|
|
Vector2 val;
|
|
Vector2 val;
|
|
if (header & HEADER_DATA_FLAG_64) {
|
|
if (header & HEADER_DATA_FLAG_64) {
|
|
@@ -539,7 +617,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
r_variant = val;
|
|
r_variant = val;
|
|
|
|
|
|
} break;
|
|
} break;
|
|
- // misc types
|
|
|
|
|
|
+
|
|
|
|
+ // Misc types.
|
|
case Variant::COLOR: {
|
|
case Variant::COLOR: {
|
|
ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
|
|
ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
|
|
Color val;
|
|
Color val;
|
|
@@ -568,7 +647,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
int32_t strlen = decode_uint32(buf);
|
|
int32_t strlen = decode_uint32(buf);
|
|
|
|
|
|
if (strlen & 0x80000000) {
|
|
if (strlen & 0x80000000) {
|
|
- //new format
|
|
|
|
|
|
+ // New format.
|
|
ERR_FAIL_COND_V(len < 12, ERR_INVALID_DATA);
|
|
ERR_FAIL_COND_V(len < 12, ERR_INVALID_DATA);
|
|
Vector<StringName> names;
|
|
Vector<StringName> names;
|
|
Vector<StringName> subnames;
|
|
Vector<StringName> subnames;
|
|
@@ -607,8 +686,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
r_variant = NodePath(names, subnames, np_flags & 1);
|
|
r_variant = NodePath(names, subnames, np_flags & 1);
|
|
|
|
|
|
} else {
|
|
} else {
|
|
- //old format, just a string
|
|
|
|
-
|
|
|
|
|
|
+ // Old format, just a string.
|
|
ERR_FAIL_V(ERR_INVALID_DATA);
|
|
ERR_FAIL_V(ERR_INVALID_DATA);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -698,9 +776,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
if (str == "script" && value.get_type() != Variant::NIL) {
|
|
if (str == "script" && value.get_type() != Variant::NIL) {
|
|
ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String.");
|
|
ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String.");
|
|
String path = value;
|
|
String path = value;
|
|
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path: '%s'.", path));
|
|
|
|
|
|
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path \"%s\".", path));
|
|
Ref<Script> script = ResourceLoader::load(path, "Script");
|
|
Ref<Script> script = ResourceLoader::load(path, "Script");
|
|
- ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path: '%s'.", path));
|
|
|
|
|
|
+ ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path \"%s\".", path));
|
|
obj->set_script(script);
|
|
obj->set_script(script);
|
|
} else {
|
|
} else {
|
|
obj->set(str, value);
|
|
obj->set(str, value);
|
|
@@ -731,9 +809,30 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
r_variant = Signal(id, StringName(name));
|
|
r_variant = Signal(id, StringName(name));
|
|
} break;
|
|
} break;
|
|
case Variant::DICTIONARY: {
|
|
case Variant::DICTIONARY: {
|
|
|
|
+ ContainerType key_type;
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ ContainerTypeKind key_type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_DICTIONARY_KEY);
|
|
|
|
+ Error err = _decode_container_type(buf, len, r_len, p_allow_objects, key_type_kind, key_type);
|
|
|
|
+ if (err) {
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ContainerType value_type;
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ ContainerTypeKind value_type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_DICTIONARY_VALUE);
|
|
|
|
+ Error err = _decode_container_type(buf, len, r_len, p_allow_objects, value_type_kind, value_type);
|
|
|
|
+ if (err) {
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
|
|
+
|
|
int32_t count = decode_uint32(buf);
|
|
int32_t count = decode_uint32(buf);
|
|
- // bool shared = count&0x80000000;
|
|
|
|
|
|
+ //bool shared = count & 0x80000000;
|
|
count &= 0x7FFFFFFF;
|
|
count &= 0x7FFFFFFF;
|
|
|
|
|
|
buf += 4;
|
|
buf += 4;
|
|
@@ -743,7 +842,10 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
(*r_len) += 4; // Size of count number.
|
|
(*r_len) += 4; // Size of count number.
|
|
}
|
|
}
|
|
|
|
|
|
- Dictionary d;
|
|
|
|
|
|
+ Dictionary dict;
|
|
|
|
+ if (key_type.builtin_type != Variant::NIL || value_type.builtin_type != Variant::NIL) {
|
|
|
|
+ dict.set_typed(key_type.builtin_type, key_type.class_name, key_type.script, value_type.builtin_type, value_type.class_name, value_type.script);
|
|
|
|
+ }
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
for (int i = 0; i < count; i++) {
|
|
Variant key, value;
|
|
Variant key, value;
|
|
@@ -767,75 +869,27 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
(*r_len) += used;
|
|
(*r_len) += used;
|
|
}
|
|
}
|
|
|
|
|
|
- d[key] = value;
|
|
|
|
|
|
+ dict[key] = value;
|
|
}
|
|
}
|
|
|
|
|
|
- r_variant = d;
|
|
|
|
|
|
+ r_variant = dict;
|
|
|
|
|
|
} break;
|
|
} break;
|
|
case Variant::ARRAY: {
|
|
case Variant::ARRAY: {
|
|
- Variant::Type builtin_type = Variant::VARIANT_MAX;
|
|
|
|
- StringName class_name;
|
|
|
|
- Ref<Script> script;
|
|
|
|
-
|
|
|
|
- switch (header & HEADER_DATA_FIELD_TYPED_ARRAY_MASK) {
|
|
|
|
- case HEADER_DATA_FIELD_TYPED_ARRAY_NONE:
|
|
|
|
- break; // Untyped array.
|
|
|
|
- case HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN: {
|
|
|
|
- ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
|
|
-
|
|
|
|
- int32_t bt = decode_uint32(buf);
|
|
|
|
- buf += 4;
|
|
|
|
- len -= 4;
|
|
|
|
- if (r_len) {
|
|
|
|
- (*r_len) += 4;
|
|
|
|
- }
|
|
|
|
|
|
+ ContainerType type;
|
|
|
|
|
|
- ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
|
|
|
|
- builtin_type = (Variant::Type)bt;
|
|
|
|
- if (!p_allow_objects && builtin_type == Variant::OBJECT) {
|
|
|
|
- class_name = EncodedObjectAsID::get_class_static();
|
|
|
|
- }
|
|
|
|
- } break;
|
|
|
|
- case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: {
|
|
|
|
- String str;
|
|
|
|
- Error err = _decode_string(buf, len, r_len, str);
|
|
|
|
- if (err) {
|
|
|
|
- return err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- builtin_type = Variant::OBJECT;
|
|
|
|
- if (p_allow_objects) {
|
|
|
|
- class_name = str;
|
|
|
|
- } else {
|
|
|
|
- class_name = EncodedObjectAsID::get_class_static();
|
|
|
|
- }
|
|
|
|
- } break;
|
|
|
|
- case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: {
|
|
|
|
- String path;
|
|
|
|
- Error err = _decode_string(buf, len, r_len, path);
|
|
|
|
- if (err) {
|
|
|
|
- return err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- builtin_type = Variant::OBJECT;
|
|
|
|
- if (p_allow_objects) {
|
|
|
|
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path: '%s'.", path));
|
|
|
|
- script = ResourceLoader::load(path, "Script");
|
|
|
|
- ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path: '%s'.", path));
|
|
|
|
- class_name = script->get_instance_base_type();
|
|
|
|
- } else {
|
|
|
|
- class_name = EncodedObjectAsID::get_class_static();
|
|
|
|
- }
|
|
|
|
- } break;
|
|
|
|
- default:
|
|
|
|
- ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing.
|
|
|
|
|
|
+ {
|
|
|
|
+ ContainerTypeKind type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_ARRAY);
|
|
|
|
+ Error err = _decode_container_type(buf, len, r_len, p_allow_objects, type_kind, type);
|
|
|
|
+ if (err) {
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
|
|
|
|
int32_t count = decode_uint32(buf);
|
|
int32_t count = decode_uint32(buf);
|
|
- // bool shared = count&0x80000000;
|
|
|
|
|
|
+ //bool shared = count & 0x80000000;
|
|
count &= 0x7FFFFFFF;
|
|
count &= 0x7FFFFFFF;
|
|
|
|
|
|
buf += 4;
|
|
buf += 4;
|
|
@@ -845,29 +899,29 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
(*r_len) += 4; // Size of count number.
|
|
(*r_len) += 4; // Size of count number.
|
|
}
|
|
}
|
|
|
|
|
|
- Array varr;
|
|
|
|
- if (builtin_type != Variant::VARIANT_MAX) {
|
|
|
|
- varr.set_typed(builtin_type, class_name, script);
|
|
|
|
|
|
+ Array array;
|
|
|
|
+ if (type.builtin_type != Variant::NIL) {
|
|
|
|
+ array.set_typed(type.builtin_type, type.class_name, type.script);
|
|
}
|
|
}
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
for (int i = 0; i < count; i++) {
|
|
int used = 0;
|
|
int used = 0;
|
|
- Variant v;
|
|
|
|
- Error err = decode_variant(v, buf, len, &used, p_allow_objects, p_depth + 1);
|
|
|
|
|
|
+ Variant elem;
|
|
|
|
+ Error err = decode_variant(elem, buf, len, &used, p_allow_objects, p_depth + 1);
|
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant.");
|
|
ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant.");
|
|
buf += used;
|
|
buf += used;
|
|
len -= used;
|
|
len -= used;
|
|
- varr.push_back(v);
|
|
|
|
|
|
+ array.push_back(elem);
|
|
if (r_len) {
|
|
if (r_len) {
|
|
(*r_len) += used;
|
|
(*r_len) += used;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- r_variant = varr;
|
|
|
|
|
|
+ r_variant = array;
|
|
|
|
|
|
} break;
|
|
} break;
|
|
|
|
|
|
- // arrays
|
|
|
|
|
|
+ // Packed arrays.
|
|
case Variant::PACKED_BYTE_ARRAY: {
|
|
case Variant::PACKED_BYTE_ARRAY: {
|
|
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
|
int32_t count = decode_uint32(buf);
|
|
int32_t count = decode_uint32(buf);
|
|
@@ -906,7 +960,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
Vector<int32_t> data;
|
|
Vector<int32_t> data;
|
|
|
|
|
|
if (count) {
|
|
if (count) {
|
|
- //const int*rbuf=(const int*)buf;
|
|
|
|
|
|
+ //const int *rbuf = (const int *)buf;
|
|
data.resize(count);
|
|
data.resize(count);
|
|
int32_t *w = data.ptrw();
|
|
int32_t *w = data.ptrw();
|
|
for (int32_t i = 0; i < count; i++) {
|
|
for (int32_t i = 0; i < count; i++) {
|
|
@@ -930,7 +984,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
Vector<int64_t> data;
|
|
Vector<int64_t> data;
|
|
|
|
|
|
if (count) {
|
|
if (count) {
|
|
- //const int*rbuf=(const int*)buf;
|
|
|
|
|
|
+ //const int *rbuf = (const int *)buf;
|
|
data.resize(count);
|
|
data.resize(count);
|
|
int64_t *w = data.ptrw();
|
|
int64_t *w = data.ptrw();
|
|
for (int64_t i = 0; i < count; i++) {
|
|
for (int64_t i = 0; i < count; i++) {
|
|
@@ -954,7 +1008,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|
Vector<float> data;
|
|
Vector<float> data;
|
|
|
|
|
|
if (count) {
|
|
if (count) {
|
|
- //const float*rbuf=(const float*)buf;
|
|
|
|
|
|
+ //const float *rbuf = (const float *)buf;
|
|
data.resize(count);
|
|
data.resize(count);
|
|
float *w = data.ptrw();
|
|
float *w = data.ptrw();
|
|
for (int32_t i = 0; i < count; i++) {
|
|
for (int32_t i = 0; i < count; i++) {
|
|
@@ -1265,13 +1319,50 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) {
|
|
|
|
|
|
r_len += 4 + utf8.length();
|
|
r_len += 4 + utf8.length();
|
|
while (r_len % 4) {
|
|
while (r_len % 4) {
|
|
- r_len++; //pad
|
|
|
|
|
|
+ r_len++; // Pad.
|
|
if (buf) {
|
|
if (buf) {
|
|
*(buf++) = 0;
|
|
*(buf++) = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void _encode_container_type_header(const ContainerType &p_type, uint32_t &header, uint32_t p_shift, bool p_full_objects) {
|
|
|
|
+ if (p_type.builtin_type != Variant::NIL) {
|
|
|
|
+ if (p_type.script.is_valid()) {
|
|
|
|
+ header |= (p_full_objects ? CONTAINER_TYPE_KIND_SCRIPT : CONTAINER_TYPE_KIND_CLASS_NAME) << p_shift;
|
|
|
|
+ } else if (p_type.class_name != StringName()) {
|
|
|
|
+ header |= CONTAINER_TYPE_KIND_CLASS_NAME << p_shift;
|
|
|
|
+ } else {
|
|
|
|
+ // No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`.
|
|
|
|
+ header |= CONTAINER_TYPE_KIND_BUILTIN << p_shift;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static Error _encode_container_type(const ContainerType &p_type, uint8_t *&buf, int &r_len, bool p_full_objects) {
|
|
|
|
+ if (p_type.builtin_type != Variant::NIL) {
|
|
|
|
+ if (p_type.script.is_valid()) {
|
|
|
|
+ if (p_full_objects) {
|
|
|
|
+ String path = p_type.script->get_path();
|
|
|
|
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for a container type.");
|
|
|
|
+ _encode_string(path, buf, r_len);
|
|
|
|
+ } else {
|
|
|
|
+ _encode_string(EncodedObjectAsID::get_class_static(), buf, r_len);
|
|
|
|
+ }
|
|
|
|
+ } else if (p_type.class_name != StringName()) {
|
|
|
|
+ _encode_string(p_full_objects ? p_type.class_name.operator String() : EncodedObjectAsID::get_class_static(), buf, r_len);
|
|
|
|
+ } else {
|
|
|
|
+ // No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`.
|
|
|
|
+ if (buf) {
|
|
|
|
+ encode_uint32(p_type.builtin_type, buf);
|
|
|
|
+ buf += 4;
|
|
|
|
+ }
|
|
|
|
+ r_len += 4;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return OK;
|
|
|
|
+}
|
|
|
|
+
|
|
Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) {
|
|
Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) {
|
|
ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential infinite recursion detected. Bailing.");
|
|
ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential infinite recursion detected. Bailing.");
|
|
uint8_t *buf = r_buffer;
|
|
uint8_t *buf = r_buffer;
|
|
@@ -1310,20 +1401,32 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
header |= HEADER_DATA_FLAG_OBJECT_AS_ID;
|
|
header |= HEADER_DATA_FLAG_OBJECT_AS_ID;
|
|
}
|
|
}
|
|
} break;
|
|
} break;
|
|
|
|
+ case Variant::DICTIONARY: {
|
|
|
|
+ Dictionary dict = p_variant;
|
|
|
|
+
|
|
|
|
+ ContainerType key_type;
|
|
|
|
+ key_type.builtin_type = (Variant::Type)dict.get_typed_key_builtin();
|
|
|
|
+ key_type.class_name = dict.get_typed_key_class_name();
|
|
|
|
+ key_type.script = dict.get_typed_key_script();
|
|
|
|
+
|
|
|
|
+ _encode_container_type_header(key_type, header, HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_SHIFT, p_full_objects);
|
|
|
|
+
|
|
|
|
+ ContainerType value_type;
|
|
|
|
+ value_type.builtin_type = (Variant::Type)dict.get_typed_value_builtin();
|
|
|
|
+ value_type.class_name = dict.get_typed_value_class_name();
|
|
|
|
+ value_type.script = dict.get_typed_value_script();
|
|
|
|
+
|
|
|
|
+ _encode_container_type_header(value_type, header, HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_SHIFT, p_full_objects);
|
|
|
|
+ } break;
|
|
case Variant::ARRAY: {
|
|
case Variant::ARRAY: {
|
|
Array array = p_variant;
|
|
Array array = p_variant;
|
|
- if (array.is_typed()) {
|
|
|
|
- Ref<Script> script = array.get_typed_script();
|
|
|
|
- if (script.is_valid()) {
|
|
|
|
- header |= p_full_objects ? HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT : HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
|
|
|
|
- } else if (array.get_typed_class_name() != StringName()) {
|
|
|
|
- header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
|
|
|
|
- } else {
|
|
|
|
- // No need to check `p_full_objects` since for `Variant::OBJECT`
|
|
|
|
- // `array.get_typed_class_name()` should be non-empty.
|
|
|
|
- header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ ContainerType type;
|
|
|
|
+ type.builtin_type = (Variant::Type)array.get_typed_builtin();
|
|
|
|
+ type.class_name = array.get_typed_class_name();
|
|
|
|
+ type.script = array.get_typed_script();
|
|
|
|
+
|
|
|
|
+ _encode_container_type_header(type, header, HEADER_DATA_FIELD_TYPED_ARRAY_SHIFT, p_full_objects);
|
|
} break;
|
|
} break;
|
|
#ifdef REAL_T_IS_DOUBLE
|
|
#ifdef REAL_T_IS_DOUBLE
|
|
case Variant::VECTOR2:
|
|
case Variant::VECTOR2:
|
|
@@ -1344,7 +1447,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
} break;
|
|
} break;
|
|
#endif // REAL_T_IS_DOUBLE
|
|
#endif // REAL_T_IS_DOUBLE
|
|
default: {
|
|
default: {
|
|
- } // nothing to do at this stage
|
|
|
|
|
|
+ // Nothing to do at this stage.
|
|
|
|
+ } break;
|
|
}
|
|
}
|
|
|
|
|
|
if (buf) {
|
|
if (buf) {
|
|
@@ -1355,7 +1459,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
|
|
|
|
switch (p_variant.get_type()) {
|
|
switch (p_variant.get_type()) {
|
|
case Variant::NIL: {
|
|
case Variant::NIL: {
|
|
- //nothing to do
|
|
|
|
|
|
+ // Nothing to do.
|
|
} break;
|
|
} break;
|
|
case Variant::BOOL: {
|
|
case Variant::BOOL: {
|
|
if (buf) {
|
|
if (buf) {
|
|
@@ -1367,7 +1471,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
} break;
|
|
} break;
|
|
case Variant::INT: {
|
|
case Variant::INT: {
|
|
if (header & HEADER_DATA_FLAG_64) {
|
|
if (header & HEADER_DATA_FLAG_64) {
|
|
- //64 bits
|
|
|
|
|
|
+ // 64 bits.
|
|
if (buf) {
|
|
if (buf) {
|
|
encode_uint64(p_variant.operator int64_t(), buf);
|
|
encode_uint64(p_variant.operator int64_t(), buf);
|
|
}
|
|
}
|
|
@@ -1401,7 +1505,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
case Variant::NODE_PATH: {
|
|
case Variant::NODE_PATH: {
|
|
NodePath np = p_variant;
|
|
NodePath np = p_variant;
|
|
if (buf) {
|
|
if (buf) {
|
|
- encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); //for compatibility with the old format
|
|
|
|
|
|
+ encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); // For compatibility with the old format.
|
|
encode_uint32(np.get_subname_count(), buf + 4);
|
|
encode_uint32(np.get_subname_count(), buf + 4);
|
|
uint32_t np_flags = 0;
|
|
uint32_t np_flags = 0;
|
|
if (np.is_absolute()) {
|
|
if (np.is_absolute()) {
|
|
@@ -1451,7 +1555,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
|
|
|
|
} break;
|
|
} break;
|
|
|
|
|
|
- // math types
|
|
|
|
|
|
+ // Math types.
|
|
case Variant::VECTOR2: {
|
|
case Variant::VECTOR2: {
|
|
if (buf) {
|
|
if (buf) {
|
|
Vector2 v2 = p_variant;
|
|
Vector2 v2 = p_variant;
|
|
@@ -1635,7 +1739,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
|
|
|
|
} break;
|
|
} break;
|
|
|
|
|
|
- // misc types
|
|
|
|
|
|
+ // Misc types.
|
|
case Variant::COLOR: {
|
|
case Variant::COLOR: {
|
|
if (buf) {
|
|
if (buf) {
|
|
Color c = p_variant;
|
|
Color c = p_variant;
|
|
@@ -1746,29 +1850,53 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
r_len += 8;
|
|
r_len += 8;
|
|
} break;
|
|
} break;
|
|
case Variant::DICTIONARY: {
|
|
case Variant::DICTIONARY: {
|
|
- Dictionary d = p_variant;
|
|
|
|
|
|
+ Dictionary dict = p_variant;
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ ContainerType key_type;
|
|
|
|
+ key_type.builtin_type = (Variant::Type)dict.get_typed_key_builtin();
|
|
|
|
+ key_type.class_name = dict.get_typed_key_class_name();
|
|
|
|
+ key_type.script = dict.get_typed_key_script();
|
|
|
|
+
|
|
|
|
+ Error err = _encode_container_type(key_type, buf, r_len, p_full_objects);
|
|
|
|
+ if (err) {
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ {
|
|
|
|
+ ContainerType value_type;
|
|
|
|
+ value_type.builtin_type = (Variant::Type)dict.get_typed_value_builtin();
|
|
|
|
+ value_type.class_name = dict.get_typed_value_class_name();
|
|
|
|
+ value_type.script = dict.get_typed_value_script();
|
|
|
|
+
|
|
|
|
+ Error err = _encode_container_type(value_type, buf, r_len, p_full_objects);
|
|
|
|
+ if (err) {
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
if (buf) {
|
|
if (buf) {
|
|
- encode_uint32(uint32_t(d.size()), buf);
|
|
|
|
|
|
+ encode_uint32(uint32_t(dict.size()), buf);
|
|
buf += 4;
|
|
buf += 4;
|
|
}
|
|
}
|
|
r_len += 4;
|
|
r_len += 4;
|
|
|
|
|
|
List<Variant> keys;
|
|
List<Variant> keys;
|
|
- d.get_key_list(&keys);
|
|
|
|
|
|
+ dict.get_key_list(&keys);
|
|
|
|
|
|
- for (const Variant &E : keys) {
|
|
|
|
|
|
+ for (const Variant &key : keys) {
|
|
int len;
|
|
int len;
|
|
- Error err = encode_variant(E, buf, len, p_full_objects, p_depth + 1);
|
|
|
|
|
|
+ Error err = encode_variant(key, buf, len, p_full_objects, p_depth + 1);
|
|
ERR_FAIL_COND_V(err, err);
|
|
ERR_FAIL_COND_V(err, err);
|
|
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
|
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
|
r_len += len;
|
|
r_len += len;
|
|
if (buf) {
|
|
if (buf) {
|
|
buf += len;
|
|
buf += len;
|
|
}
|
|
}
|
|
- Variant *v = d.getptr(E);
|
|
|
|
- ERR_FAIL_NULL_V(v, ERR_BUG);
|
|
|
|
- err = encode_variant(*v, buf, len, p_full_objects, p_depth + 1);
|
|
|
|
|
|
+ Variant *value = dict.getptr(key);
|
|
|
|
+ ERR_FAIL_NULL_V(value, ERR_BUG);
|
|
|
|
+ err = encode_variant(*value, buf, len, p_full_objects, p_depth + 1);
|
|
ERR_FAIL_COND_V(err, err);
|
|
ERR_FAIL_COND_V(err, err);
|
|
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
|
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
|
r_len += len;
|
|
r_len += len;
|
|
@@ -1781,27 +1909,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
case Variant::ARRAY: {
|
|
case Variant::ARRAY: {
|
|
Array array = p_variant;
|
|
Array array = p_variant;
|
|
|
|
|
|
- if (array.is_typed()) {
|
|
|
|
- Variant variant = array.get_typed_script();
|
|
|
|
- Ref<Script> script = variant;
|
|
|
|
- if (script.is_valid()) {
|
|
|
|
- if (p_full_objects) {
|
|
|
|
- String path = script->get_path();
|
|
|
|
- ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type.");
|
|
|
|
- _encode_string(path, buf, r_len);
|
|
|
|
- } else {
|
|
|
|
- _encode_string(EncodedObjectAsID::get_class_static(), buf, r_len);
|
|
|
|
- }
|
|
|
|
- } else if (array.get_typed_class_name() != StringName()) {
|
|
|
|
- _encode_string(p_full_objects ? array.get_typed_class_name().operator String() : EncodedObjectAsID::get_class_static(), buf, r_len);
|
|
|
|
- } else {
|
|
|
|
- // No need to check `p_full_objects` since for `Variant::OBJECT`
|
|
|
|
- // `array.get_typed_class_name()` should be non-empty.
|
|
|
|
- if (buf) {
|
|
|
|
- encode_uint32(array.get_typed_builtin(), buf);
|
|
|
|
- buf += 4;
|
|
|
|
- }
|
|
|
|
- r_len += 4;
|
|
|
|
|
|
+ {
|
|
|
|
+ ContainerType type;
|
|
|
|
+ type.builtin_type = (Variant::Type)array.get_typed_builtin();
|
|
|
|
+ type.class_name = array.get_typed_class_name();
|
|
|
|
+ type.script = array.get_typed_script();
|
|
|
|
+
|
|
|
|
+ Error err = _encode_container_type(type, buf, r_len, p_full_objects);
|
|
|
|
+ if (err) {
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1811,9 +1927,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
}
|
|
}
|
|
r_len += 4;
|
|
r_len += 4;
|
|
|
|
|
|
- for (const Variant &var : array) {
|
|
|
|
|
|
+ for (const Variant &elem : array) {
|
|
int len;
|
|
int len;
|
|
- Error err = encode_variant(var, buf, len, p_full_objects, p_depth + 1);
|
|
|
|
|
|
+ Error err = encode_variant(elem, buf, len, p_full_objects, p_depth + 1);
|
|
ERR_FAIL_COND_V(err, err);
|
|
ERR_FAIL_COND_V(err, err);
|
|
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
|
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
|
if (buf) {
|
|
if (buf) {
|
|
@@ -1823,7 +1939,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
}
|
|
}
|
|
|
|
|
|
} break;
|
|
} break;
|
|
- // arrays
|
|
|
|
|
|
+
|
|
|
|
+ // Packed arrays.
|
|
case Variant::PACKED_BYTE_ARRAY: {
|
|
case Variant::PACKED_BYTE_ARRAY: {
|
|
Vector<uint8_t> data = p_variant;
|
|
Vector<uint8_t> data = p_variant;
|
|
int datalen = data.size();
|
|
int datalen = data.size();
|
|
@@ -1939,7 +2056,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
|
|
|
|
r_len += 4 + utf8.length() + 1;
|
|
r_len += 4 + utf8.length() + 1;
|
|
while (r_len % 4) {
|
|
while (r_len % 4) {
|
|
- r_len++; //pad
|
|
|
|
|
|
+ r_len++; // Pad.
|
|
if (buf) {
|
|
if (buf) {
|
|
*(buf++) = 0;
|
|
*(buf++) = 0;
|
|
}
|
|
}
|
|
@@ -2057,9 +2174,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|
}
|
|
}
|
|
|
|
|
|
Vector<float> vector3_to_float32_array(const Vector3 *vecs, size_t count) {
|
|
Vector<float> vector3_to_float32_array(const Vector3 *vecs, size_t count) {
|
|
- // We always allocate a new array, and we don't memcpy.
|
|
|
|
- // We also don't consider returning a pointer to the passed vectors when sizeof(real_t) == 4.
|
|
|
|
- // One reason is that we could decide to put a 4th component in Vector3 for SIMD/mobile performance,
|
|
|
|
|
|
+ // We always allocate a new array, and we don't `memcpy()`.
|
|
|
|
+ // We also don't consider returning a pointer to the passed vectors when `sizeof(real_t) == 4`.
|
|
|
|
+ // One reason is that we could decide to put a 4th component in `Vector3` for SIMD/mobile performance,
|
|
// which would cause trouble with these optimizations.
|
|
// which would cause trouble with these optimizations.
|
|
Vector<float> floats;
|
|
Vector<float> floats;
|
|
if (count == 0) {
|
|
if (count == 0) {
|