diff options
Diffstat (limited to 'core')
79 files changed, 4517 insertions, 872 deletions
diff --git a/core/SCsub b/core/SCsub index c508ecc37e..c8e2e10b9f 100644 --- a/core/SCsub +++ b/core/SCsub @@ -2,7 +2,9 @@ Import('env') -import methods +import core_builders +import make_binders +from platform_methods import run_in_subprocess env.core_sources = [] @@ -21,7 +23,7 @@ gd_cpp += gd_inc gd_cpp += "void ProjectSettings::register_global_defaults() {\n" + gd_call + "\n}\n" with open("global_defaults.gen.cpp", "w") as f: - f.write(gd_cpp) + f.write(gd_cpp) # Generate AES256 script encryption key @@ -48,26 +50,27 @@ if ("SCRIPT_AES256_ENCRYPTION_KEY" in os.environ): txt = "0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0" print("Invalid AES256 encryption key, not 64 bits hex: " + e) +# NOTE: It is safe to generate this file here, since this is still executed serially with open("script_encryption_key.gen.cpp", "w") as f: - f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") + f.write("#include \"project_settings.h\"\nuint8_t script_encryption_key[32]={" + txt + "};\n") # Add required thirdparty code. Header paths are hardcoded, we don't need to append # to the include path (saves a few chars on the compiler invocation for touchy MSVC...) thirdparty_dir = "#thirdparty/misc/" thirdparty_sources = [ - # C sources - "base64.c", - "fastlz.c", - "sha256.c", - "smaz.c", - - # C++ sources - "aes256.cpp", - "hq2x.cpp", - "md5.cpp", - "pcg.cpp", - "triangulator.cpp", + # C sources + "base64.c", + "fastlz.c", + "sha256.c", + "smaz.c", + + # C++ sources + "aes256.cpp", + "hq2x.cpp", + "md5.cpp", + "pcg.cpp", + "triangulator.cpp", ] thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env.add_source_files(env.core_sources, thirdparty_sources) @@ -76,9 +79,9 @@ env.add_source_files(env.core_sources, thirdparty_sources) # However, our version has some custom modifications, so it won't compile with the system one thirdparty_minizip_dir = "#thirdparty/minizip/" thirdparty_minizip_sources = [ - "ioapi.c", - "unzip.c", - "zip.c", + "ioapi.c", + "unzip.c", + "zip.c", ] thirdparty_minizip_sources = [thirdparty_minizip_dir + file for file in thirdparty_minizip_sources] env.add_source_files(env.core_sources, thirdparty_minizip_sources) @@ -92,20 +95,19 @@ env.add_source_files(env.core_sources, "*.cpp") # Make binders -import make_binders -env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', make_binders.run) +env.CommandNoCache(['method_bind.gen.inc', 'method_bind_ext.gen.inc'], 'make_binders.py', run_in_subprocess(make_binders.run)) # Authors env.Depends('#core/authors.gen.h', "../AUTHORS.md") -env.CommandNoCache('#core/authors.gen.h', "../AUTHORS.md", methods.make_authors_header) +env.CommandNoCache('#core/authors.gen.h', "../AUTHORS.md", run_in_subprocess(core_builders.make_authors_header)) # Donors env.Depends('#core/donors.gen.h', "../DONORS.md") -env.CommandNoCache('#core/donors.gen.h', "../DONORS.md", methods.make_donors_header) +env.CommandNoCache('#core/donors.gen.h', "../DONORS.md", run_in_subprocess(core_builders.make_donors_header)) # License env.Depends('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"]) -env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], methods.make_license_header) +env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"], run_in_subprocess(core_builders.make_license_header)) # Chain load SCsubs SConscript('os/SCsub') diff --git a/core/array.cpp b/core/array.cpp index 9e3250fd47..44c553e4eb 100644 --- a/core/array.cpp +++ b/core/array.cpp @@ -72,7 +72,7 @@ void Array::_unref() const { Variant &Array::operator[](int p_idx) { - return _p->array[p_idx]; + return _p->array.write[p_idx]; } const Variant &Array::operator[](int p_idx) const { @@ -259,7 +259,7 @@ Array &Array::sort_custom(Object *p_obj, const StringName &p_function) { ERR_FAIL_NULL_V(p_obj, *this); - SortArray<Variant, _ArrayVariantSortCustom> avs; + SortArray<Variant, _ArrayVariantSortCustom, true> avs; avs.compare.obj = p_obj; avs.compare.func = p_function; avs.sort(_p->array.ptrw(), _p->array.size()); diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 7a14e85f20..626d41dca0 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -112,11 +112,22 @@ PoolStringArray _ResourceLoader::get_dependencies(const String &p_path) { return ret; }; +#ifndef DISABLE_DEPRECATED bool _ResourceLoader::has(const String &p_path) { + WARN_PRINTS("ResourceLoader.has() is deprecated, please replace it with the equivalent has_cached() or the new exists()."); + return has_cached(p_path); +} +#endif // DISABLE_DEPRECATED + +bool _ResourceLoader::has_cached(const String &p_path) { String local_path = ProjectSettings::get_singleton()->localize_path(p_path); return ResourceCache::has(local_path); -}; +} + +bool _ResourceLoader::exists(const String &p_path, const String &p_type_hint) { + return ResourceLoader::exists(p_path, p_type_hint); +} void _ResourceLoader::_bind_methods() { @@ -125,7 +136,11 @@ void _ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_recognized_extensions_for_type", "type"), &_ResourceLoader::get_recognized_extensions_for_type); ClassDB::bind_method(D_METHOD("set_abort_on_missing_resources", "abort"), &_ResourceLoader::set_abort_on_missing_resources); ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies); + ClassDB::bind_method(D_METHOD("has_cached", "path"), &_ResourceLoader::has_cached); + ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &_ResourceLoader::exists, DEFVAL("")); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("has", "path"), &_ResourceLoader::has); +#endif // DISABLE_DEPRECATED } _ResourceLoader::_ResourceLoader() { @@ -221,6 +236,10 @@ String _OS::get_audio_driver_name(int p_driver) const { return OS::get_singleton()->get_audio_driver_name(p_driver); } +PoolStringArray _OS::get_connected_midi_inputs() { + return OS::get_singleton()->get_connected_midi_inputs(); +} + void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeable, int p_screen) { OS::VideoMode vm; @@ -1058,6 +1077,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name); ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count); ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name); + ClassDB::bind_method(D_METHOD("get_connected_midi_inputs"), &_OS::get_connected_midi_inputs); ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count); ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 48b7b74005..311372aeca 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -55,7 +55,11 @@ public: PoolVector<String> get_recognized_extensions_for_type(const String &p_type); void set_abort_on_missing_resources(bool p_abort); PoolStringArray get_dependencies(const String &p_path); +#ifndef DISABLE_DEPRECATED bool has(const String &p_path); +#endif // DISABLE_DEPRECATED + bool has_cached(const String &p_path); + bool exists(const String &p_path, const String &p_type_hint = ""); _ResourceLoader(); }; @@ -152,6 +156,8 @@ public: virtual int get_audio_driver_count() const; virtual String get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual int get_screen_count() const; virtual int get_current_screen() const; virtual void set_current_screen(int p_screen); diff --git a/core/class_db.cpp b/core/class_db.cpp index f97eaf6099..03b214aa41 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -58,8 +58,8 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(2); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); return md; } @@ -68,9 +68,9 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(3); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); return md; } @@ -79,10 +79,10 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(4); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); return md; } @@ -91,11 +91,11 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(5); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); return md; } @@ -104,12 +104,12 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(6); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); return md; } @@ -118,13 +118,13 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(7); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); return md; } @@ -133,14 +133,14 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(8); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); return md; } @@ -149,15 +149,15 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(9); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); return md; } @@ -166,16 +166,16 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(10); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); return md; } @@ -184,17 +184,17 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(11); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); return md; } @@ -203,18 +203,18 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(12); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); - md.args[11] = StaticCString::create(p_arg12); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); + md.args.write[11] = StaticCString::create(p_arg12); return md; } @@ -223,19 +223,19 @@ MethodDefinition D_METHOD(const char *p_name, const char *p_arg1, const char *p_ MethodDefinition md; md.name = StaticCString::create(p_name); md.args.resize(13); - md.args[0] = StaticCString::create(p_arg1); - md.args[1] = StaticCString::create(p_arg2); - md.args[2] = StaticCString::create(p_arg3); - md.args[3] = StaticCString::create(p_arg4); - md.args[4] = StaticCString::create(p_arg5); - md.args[5] = StaticCString::create(p_arg6); - md.args[6] = StaticCString::create(p_arg7); - md.args[7] = StaticCString::create(p_arg8); - md.args[8] = StaticCString::create(p_arg9); - md.args[9] = StaticCString::create(p_arg10); - md.args[10] = StaticCString::create(p_arg11); - md.args[11] = StaticCString::create(p_arg12); - md.args[12] = StaticCString::create(p_arg13); + md.args.write[0] = StaticCString::create(p_arg1); + md.args.write[1] = StaticCString::create(p_arg2); + md.args.write[2] = StaticCString::create(p_arg3); + md.args.write[3] = StaticCString::create(p_arg4); + md.args.write[4] = StaticCString::create(p_arg5); + md.args.write[5] = StaticCString::create(p_arg6); + md.args.write[6] = StaticCString::create(p_arg7); + md.args.write[7] = StaticCString::create(p_arg8); + md.args.write[8] = StaticCString::create(p_arg9); + md.args.write[9] = StaticCString::create(p_arg10); + md.args.write[10] = StaticCString::create(p_arg11); + md.args.write[11] = StaticCString::create(p_arg12); + md.args.write[12] = StaticCString::create(p_arg13); return md; } @@ -1246,7 +1246,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c defvals.resize(p_defcount); for (int i = 0; i < p_defcount; i++) { - defvals[i] = *p_defs[p_defcount - i - 1]; + defvals.write[i] = *p_defs[p_defcount - i - 1]; } p_bind->set_default_arguments(defvals); diff --git a/core/color.cpp b/core/color.cpp index 88e57ec6e2..fcfcf20355 100644 --- a/core/color.cpp +++ b/core/color.cpp @@ -49,6 +49,7 @@ uint32_t Color::to_argb32() const { } uint32_t Color::to_abgr32() const { + uint32_t c = (uint8_t)Math::round(a * 255); c <<= 8; c |= (uint8_t)Math::round(b * 255); @@ -73,6 +74,45 @@ uint32_t Color::to_rgba32() const { return c; } +uint64_t Color::to_abgr64() const { + + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + + return c; +} + +uint64_t Color::to_argb64() const { + + uint64_t c = (uint16_t)Math::round(a * 65535); + c <<= 16; + c |= (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + + return c; +} + +uint64_t Color::to_rgba64() const { + + uint64_t c = (uint16_t)Math::round(r * 65535); + c <<= 16; + c |= (uint16_t)Math::round(g * 65535); + c <<= 16; + c |= (uint16_t)Math::round(b * 65535); + c <<= 16; + c |= (uint16_t)Math::round(a * 65535); + + return c; +} + float Color::get_h() const { float min = MIN(r, g); @@ -200,6 +240,19 @@ Color Color::hex(uint32_t p_hex) { return Color(r, g, b, a); } +Color Color::hex64(uint64_t p_hex) { + + float a = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float b = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float g = (p_hex & 0xFFFF) / 65535.0; + p_hex >>= 16; + float r = (p_hex & 0xFFFF) / 65535.0; + + return Color(r, g, b, a); +} + static float _parse_col(const String &p_str, int p_ofs) { int ig = 0; diff --git a/core/color.h b/core/color.h index a2015a34d6..c0516e55fe 100644 --- a/core/color.h +++ b/core/color.h @@ -55,6 +55,9 @@ struct Color { uint32_t to_rgba32() const; uint32_t to_argb32() const; uint32_t to_abgr32() const; + uint64_t to_rgba64() const; + uint64_t to_argb64() const; + uint64_t to_abgr64() const; float gray() const; float get_h() const; float get_s() const; @@ -186,6 +189,7 @@ struct Color { } static Color hex(uint32_t p_hex); + static Color hex64(uint64_t p_hex); static Color html(const String &p_color); static bool html_is_valid(const String &p_color); static Color named(const String &p_name); diff --git a/core/compressed_translation.cpp b/core/compressed_translation.cpp index 266d793af7..5c1fd26e2a 100644 --- a/core/compressed_translation.cpp +++ b/core/compressed_translation.cpp @@ -73,7 +73,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { Pair<int, CharString> p; p.first = idx; p.second = cs; - buckets[h % size].push_back(p); + buckets.write[h % size].push_back(p); //compress string CharString src_s = p_from->get_message(E->get()).operator String().utf8(); @@ -100,7 +100,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { ps.compressed[0] = 0; } - compressed[idx] = ps; + compressed.write[idx] = ps; total_compression_size += ps.compressed.size(); total_string_size += src_s.size(); idx++; @@ -111,8 +111,8 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { for (int i = 0; i < size; i++) { - Vector<Pair<int, CharString> > &b = buckets[i]; - Map<uint32_t, int> &t = table[i]; + const Vector<Pair<int, CharString> > &b = buckets[i]; + Map<uint32_t, int> &t = table.write[i]; if (b.size() == 0) continue; @@ -136,7 +136,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { } } - hfunc_table[i] = d; + hfunc_table.write[i] = d; bucket_table_size += 2 + b.size() * 4; } @@ -157,7 +157,7 @@ void PHashTranslation::generate(const Ref<Translation> &p_from) { for (int i = 0; i < size; i++) { - Map<uint32_t, int> &t = table[i]; + const Map<uint32_t, int> &t = table[i]; if (t.size() == 0) { htw[i] = 0xFFFFFFFF; //nothing continue; diff --git a/core/core_builders.py b/core/core_builders.py new file mode 100644 index 0000000000..90e505aab9 --- /dev/null +++ b/core/core_builders.py @@ -0,0 +1,236 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. + +""" +from platform_methods import subprocess_main +from compat import iteritems, itervalues, open_utf8, escape_string + + +def make_authors_header(target, source, env): + sections = ["Project Founders", "Lead Developer", "Project Manager", "Developers"] + sections_id = ["AUTHORS_FOUNDERS", "AUTHORS_LEAD_DEVELOPERS", "AUTHORS_PROJECT_MANAGERS", "AUTHORS_DEVELOPERS"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_AUTHORS_H\n") + g.write("#define _EDITOR_AUTHORS_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_donors_header(target, source, env): + sections = ["Platinum sponsors", "Gold sponsors", "Mini sponsors", + "Gold donors", "Silver donors", "Bronze donors"] + sections_id = ["DONORS_SPONSOR_PLAT", "DONORS_SPONSOR_GOLD", "DONORS_SPONSOR_MINI", + "DONORS_GOLD", "DONORS_SILVER", "DONORS_BRONZE"] + + src = source[0] + dst = target[0] + f = open_utf8(src, "r") + g = open_utf8(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef _EDITOR_DONORS_H\n") + g.write("#define _EDITOR_DONORS_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading >= 0: + if line.startswith(" "): + g.write("\t\"" + escape_string(line.strip()) + "\",\n") + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break + + if reading: + close_section() + + g.write("#endif\n") + + g.close() + f.close() + + +def make_license_header(target, source, env): + src_copyright = source[0] + src_license = source[1] + dst = target[0] + + class LicenseReader: + def __init__(self, license_file): + self._license_file = license_file + self.line_num = 0 + self.current = self.next_line() + + def next_line(self): + line = self._license_file.readline() + self.line_num += 1 + while line.startswith("#"): + line = self._license_file.readline() + self.line_num += 1 + self.current = line + return line + + def next_tag(self): + if not ':' in self.current: + return ('', []) + tag, line = self.current.split(":", 1) + lines = [line.strip()] + while self.next_line() and self.current.startswith(" "): + lines.append(self.current.strip()) + return (tag, lines) + + from collections import OrderedDict + projects = OrderedDict() + license_list = [] + + with open_utf8(src_copyright, "r") as copyright_file: + reader = LicenseReader(copyright_file) + part = {} + while reader.current: + tag, content = reader.next_tag() + if tag in ("Files", "Copyright", "License"): + part[tag] = content[:] + elif tag == "Comment": + # attach part to named project + projects[content[0]] = projects.get(content[0], []) + [part] + + if not tag or not reader.current: + # end of a paragraph start a new part + if "License" in part and not "Files" in part: + # no Files tag in this one, so assume standalone license + license_list.append(part["License"]) + part = {} + reader.next_line() + + data_list = [] + for project in itervalues(projects): + for part in project: + part["file_index"] = len(data_list) + data_list += part["Files"] + part["copyright_index"] = len(data_list) + data_list += part["Copyright"] + + with open_utf8(dst, "w") as f: + + f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + f.write("#ifndef _EDITOR_LICENSE_H\n") + f.write("#define _EDITOR_LICENSE_H\n") + f.write("const char *const GODOT_LICENSE_TEXT =") + + with open_utf8(src_license, "r") as license_file: + for line in license_file: + escaped_string = escape_string(line.strip()) + f.write("\n\t\t\"" + escaped_string + "\\n\"") + f.write(";\n\n") + + f.write("struct ComponentCopyrightPart {\n" + "\tconst char *license;\n" + "\tconst char *const *files;\n" + "\tconst char *const *copyright_statements;\n" + "\tint file_count;\n" + "\tint copyright_count;\n" + "};\n\n") + + f.write("struct ComponentCopyright {\n" + "\tconst char *name;\n" + "\tconst ComponentCopyrightPart *parts;\n" + "\tint part_count;\n" + "};\n\n") + + f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n") + for line in data_list: + f.write("\t\"" + escape_string(line) + "\",\n") + f.write("};\n\n") + + f.write("const ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n") + part_index = 0 + part_indexes = {} + for project_name, project in iteritems(projects): + part_indexes[project_name] = part_index + for part in project: + f.write("\t{ \"" + escape_string(part["License"][0]) + "\", " + + "©RIGHT_INFO_DATA[" + str(part["file_index"]) + "], " + + "©RIGHT_INFO_DATA[" + str(part["copyright_index"]) + "], " + + str(len(part["Files"])) + ", " + + str(len(part["Copyright"])) + " },\n") + part_index += 1 + f.write("};\n\n") + + f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n") + + f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n") + for project_name, project in iteritems(projects): + f.write("\t{ \"" + escape_string(project_name) + "\", " + + "©RIGHT_PROJECT_PARTS[" + str(part_indexes[project_name]) + "], " + + str(len(project)) + " },\n") + f.write("};\n\n") + + f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") + + f.write("const char *const LICENSE_NAMES[] = {\n") + for l in license_list: + f.write("\t\"" + escape_string(l[0]) + "\",\n") + f.write("};\n\n") + + f.write("const char *const LICENSE_BODIES[] = {\n\n") + for l in license_list: + for line in l[1:]: + if line == ".": + f.write("\t\"\\n\"\n") + else: + f.write("\t\"" + escape_string(line) + "\\n\"\n") + f.write("\t\"\",\n\n") + f.write("};\n\n") + + f.write("#endif\n") + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/core/cowdata.h b/core/cowdata.h new file mode 100644 index 0000000000..6a8f644d53 --- /dev/null +++ b/core/cowdata.h @@ -0,0 +1,338 @@ +/*************************************************************************/ +/* cowdata.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef COWDATA_H_ +#define COWDATA_H_ + +#include "os/memory.h" +#include "safe_refcount.h" + +template <class T> +class Vector; +class String; +class CharString; +template <class T, class V> +class VMap; + +template <class T> +class CowData { + template <class TV> + friend class Vector; + friend class String; + friend class CharString; + template <class TV, class VV> + friend class VMap; + +private: + mutable T *_ptr; + + // internal helpers + + _FORCE_INLINE_ uint32_t *_get_refcount() const { + + if (!_ptr) + return NULL; + + return reinterpret_cast<uint32_t *>(_ptr) - 2; + } + + _FORCE_INLINE_ uint32_t *_get_size() const { + + if (!_ptr) + return NULL; + + return reinterpret_cast<uint32_t *>(_ptr) - 1; + } + + _FORCE_INLINE_ T *_get_data() const { + + if (!_ptr) + return NULL; + return reinterpret_cast<T *>(_ptr); + } + + _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { + //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); + return next_power_of_2(p_elements * sizeof(T)); + } + + _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { +#if defined(_add_overflow) && defined(_mul_overflow) + size_t o; + size_t p; + if (_mul_overflow(p_elements, sizeof(T), &o)) return false; + *out = next_power_of_2(o); + if (_add_overflow(o, static_cast<size_t>(32), &p)) return false; //no longer allocated here + return true; +#else + // Speed is more important than correctness here, do the operations unchecked + // and hope the best + *out = _get_alloc_size(p_elements); + return true; +#endif + } + + void _unref(void *p_data); + void _ref(const CowData *p_from); + void _ref(const CowData &p_from); + void _copy_on_write(); + +public: + void operator=(const CowData<T> &p_from) { _ref(p_from); } + + _FORCE_INLINE_ T *ptrw() { + _copy_on_write(); + return (T *)_get_data(); + } + + _FORCE_INLINE_ const T *ptr() const { + return _get_data(); + } + + _FORCE_INLINE_ int size() const { + uint32_t *size = (uint32_t *)_get_size(); + if (size) + return *size; + else + return 0; + } + + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _ptr == 0; } + + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { + + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + _get_data()[p_index] = p_elem; + } + + _FORCE_INLINE_ T &get_m(int p_index) { + + CRASH_BAD_INDEX(p_index, size()); + _copy_on_write(); + return _get_data()[p_index]; + } + + _FORCE_INLINE_ const T &get(int p_index) const { + + CRASH_BAD_INDEX(p_index, size()); + + return _get_data()[p_index]; + } + + Error resize(int p_size); + + _FORCE_INLINE_ void remove(int p_index) { + + ERR_FAIL_INDEX(p_index, size()); + T *p = ptrw(); + int len = size(); + for (int i = p_index; i < len - 1; i++) { + + p[i] = p[i + 1]; + }; + + resize(len - 1); + }; + + Error insert(int p_pos, const T &p_val) { + + ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); + resize(size() + 1); + for (int i = (size() - 1); i > p_pos; i--) + set(i, get(i - 1)); + set(p_pos, p_val); + + return OK; + }; + + _FORCE_INLINE_ CowData(); + _FORCE_INLINE_ ~CowData(); + _FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); }; +}; + +template <class T> +void CowData<T>::_unref(void *p_data) { + + if (!p_data) + return; + + uint32_t *refc = _get_refcount(); + + if (atomic_decrement(refc) > 0) + return; // still in use + // clean up + + uint32_t *count = _get_size(); + T *data = (T *)(count + 1); + + for (uint32_t i = 0; i < *count; ++i) { + // call destructors + data[i].~T(); + } + + // free mem + Memory::free_static((uint8_t *)p_data, true); +} + +template <class T> +void CowData<T>::_copy_on_write() { + + if (!_ptr) + return; + + uint32_t *refc = _get_refcount(); + + if (unlikely(*refc > 1)) { + /* in use by more than me */ + uint32_t current_size = *_get_size(); + + uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); + + *(mem_new - 2) = 1; //refcount + *(mem_new - 1) = current_size; //size + + T *_data = (T *)(mem_new); + + // initialize new elements + for (uint32_t i = 0; i < current_size; i++) { + + memnew_placement(&_data[i], T(_get_data()[i])); + } + + _unref(_ptr); + _ptr = _data; + } +} + +template <class T> +Error CowData<T>::resize(int p_size) { + + ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); + + if (p_size == size()) + return OK; + + if (p_size == 0) { + // wants to clean up + _unref(_ptr); + _ptr = NULL; + return OK; + } + + // possibly changing size, copy on write + _copy_on_write(); + + size_t alloc_size; + ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); + + if (p_size > size()) { + + if (size() == 0) { + // alloc from scratch + uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); + ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); + *(ptr - 1) = 0; //size, currently none + *(ptr - 2) = 1; //refcount + + _ptr = (T *)ptr; + + } else { + void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + _ptr = (T *)(_ptrnew); + } + + // construct the newly created elements + T *elems = _get_data(); + + for (int i = *_get_size(); i < p_size; i++) { + + memnew_placement(&elems[i], T); + } + + *_get_size() = p_size; + + } else if (p_size < size()) { + + // deinitialize no longer needed elements + for (uint32_t i = p_size; i < *_get_size(); i++) { + + T *t = &_get_data()[i]; + t->~T(); + } + + void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); + ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); + + _ptr = (T *)(_ptrnew); + + *_get_size() = p_size; + } + + return OK; +} + +template <class T> +void CowData<T>::_ref(const CowData *p_from) { + _ref(*p_from); +} + +template <class T> +void CowData<T>::_ref(const CowData &p_from) { + + if (_ptr == p_from._ptr) + return; // self assign, do nothing. + + _unref(_ptr); + _ptr = NULL; + + if (!p_from._ptr) + return; //nothing to do + + if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference + _ptr = p_from._ptr; + } +} + +template <class T> +CowData<T>::CowData() { + + _ptr = NULL; +} + +template <class T> +CowData<T>::~CowData() { + + _unref(_ptr); +} + +#endif /* COW_H_ */ diff --git a/core/dictionary.cpp b/core/dictionary.cpp index d68411a572..42d9eab310 100644 --- a/core/dictionary.cpp +++ b/core/dictionary.cpp @@ -140,6 +140,11 @@ void Dictionary::erase(const Variant &p_key) { _p->variant_map.erase(p_key); } +bool Dictionary::erase_checked(const Variant &p_key) { + + return _p->variant_map.erase(p_key); +} + bool Dictionary::operator==(const Dictionary &p_dictionary) const { return _p == p_dictionary._p; diff --git a/core/dictionary.h b/core/dictionary.h index 84a5cafe1d..00ec67fb99 100644 --- a/core/dictionary.h +++ b/core/dictionary.h @@ -66,6 +66,7 @@ public: bool has_all(const Array &p_keys) const; void erase(const Variant &p_key); + bool erase_checked(const Variant &p_key); bool operator==(const Dictionary &p_dictionary) const; diff --git a/core/error_macros.h b/core/error_macros.h index 3587e01d54..bee738ceea 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -313,7 +313,7 @@ extern bool _err_error_exists; #define WARN_DEPRECATED \ { \ - static bool warning_shown = false; \ + static volatile bool warning_shown = false; \ if (!warning_shown) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \ _err_error_exists = false; \ diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 5b4dd05dbf..187813f9d0 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -89,6 +89,7 @@ VARIANT_ENUM_CAST(KeyList); VARIANT_ENUM_CAST(KeyModifierMask); VARIANT_ENUM_CAST(ButtonList); VARIANT_ENUM_CAST(JoystickList); +VARIANT_ENUM_CAST(MidiMessageList); void register_global_constants() { @@ -458,6 +459,15 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_L2); BIND_GLOBAL_ENUM_CONSTANT(JOY_ANALOG_R2); + // midi + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_OFF); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_NOTE_ON); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_AFTERTOUCH); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CONTROL_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PROGRAM_CHANGE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_CHANNEL_PRESSURE); + BIND_GLOBAL_ENUM_CONSTANT(MIDI_MESSAGE_PITCH_BEND); + // error list BIND_GLOBAL_ENUM_CONSTANT(OK); diff --git a/core/image.cpp b/core/image.cpp index b9386a1b6d..65905c83e8 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -33,6 +33,7 @@ #include "core/io/image_loader.h" #include "core/os/copymem.h" #include "hash_map.h" +#include "math_funcs.h" #include "print_string.h" #include "thirdparty/misc/hq2x.h" @@ -525,7 +526,7 @@ static double _bicubic_interp_kernel(double x) { } template <int CC> -static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { // get source image size int width = p_src_width; @@ -555,7 +556,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi // initial pixel value - uint8_t *dst = p_dst + (y * p_dst_width + x) * CC; + uint8_t *__restrict dst = p_dst + (y * p_dst_width + x) * CC; double color[CC]; for (int i = 0; i < CC; i++) { @@ -583,7 +584,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi ox2 = xmax; // get pixel of original image - const uint8_t *p = p_src + (oy2 * p_src_width + ox2) * CC; + const uint8_t *__restrict p = p_src + (oy2 * p_src_width + ox2) * CC; for (int i = 0; i < CC; i++) { @@ -600,7 +601,7 @@ static void _scale_cubic(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_wi } template <int CC> -static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { enum { FRAC_BITS = 8, @@ -655,7 +656,7 @@ static void _scale_bilinear(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src } template <int CC> -static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { +static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { for (uint32_t i = 0; i < p_dst_height; i++) { @@ -676,6 +677,16 @@ static void _scale_nearest(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_src_ } } +static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) { + + uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256); + + for (uint32_t i = 0; i < p_width * p_height * p_pixel_size; i++) { + + p_dst[i] = (p_dst[i] * (256 - alpha) + p_src[i] * alpha) >> 8; + } +} + void Image::resize_to_po2(bool p_square) { if (!_can_modify(format)) { @@ -707,6 +718,8 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { ERR_FAIL(); } + bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; + ERR_FAIL_COND(p_width <= 0); ERR_FAIL_COND(p_height <= 0); ERR_FAIL_COND(p_width > MAX_WIDTH); @@ -717,6 +730,32 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { Image dst(p_width, p_height, 0, format); + // Setup mipmap-aware scaling + Image dst2; + int mip1; + int mip2; + float mip1_weight; + if (mipmap_aware) { + float avg_scale = ((float)p_width / width + (float)p_height / height) * 0.5f; + if (avg_scale >= 1.0f) { + mipmap_aware = false; + } else { + float level = Math::log(1.0f / avg_scale) / Math::log(2.0f); + mip1 = CLAMP((int)Math::floor(level), 0, get_mipmap_count()); + mip2 = CLAMP((int)Math::ceil(level), 0, get_mipmap_count()); + mip1_weight = 1.0f - (level - mip1); + } + } + bool interpolate_mipmaps = mipmap_aware && mip1 != mip2; + if (interpolate_mipmaps) { + dst2.create(p_width, p_height, 0, format); + } + bool had_mipmaps = mipmaps; + if (interpolate_mipmaps && !had_mipmaps) { + generate_mipmaps(); + } + // -- + PoolVector<uint8_t>::Read r = data.read(); const unsigned char *r_ptr = r.ptr(); @@ -734,13 +773,57 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { case 4: _scale_nearest<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; } } break; - case INTERPOLATE_BILINEAR: { + case INTERPOLATE_BILINEAR: + case INTERPOLATE_TRILINEAR: { + + for (int i = 0; i < 2; ++i) { + int src_width; + int src_height; + const unsigned char *src_ptr; + + if (!mipmap_aware) { + if (i == 0) { + // Standard behavior + src_width = width; + src_height = height; + src_ptr = r_ptr; + } else { + // No need for a second iteration + break; + } + } else { + if (i == 0) { + // Read from the first mipmap that will be interpolated + // (if both levels are the same, we will not interpolate, but at least we'll sample from the right level) + int offs; + _get_mipmap_offset_and_size(mip1, offs, src_width, src_height); + src_ptr = r_ptr + offs; + } else if (!interpolate_mipmaps) { + // No need generate a second image + break; + } else { + // Switch to read from the second mipmap that will be interpolated + int offs; + _get_mipmap_offset_and_size(mip2, offs, src_width, src_height); + src_ptr = r_ptr + offs; + // Switch to write to the second destination image + w = dst2.data.write(); + w_ptr = w.ptr(); + } + } - switch (get_format_pixel_size(format)) { - case 1: _scale_bilinear<1>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 2: _scale_bilinear<2>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 3: _scale_bilinear<3>(r_ptr, w_ptr, width, height, p_width, p_height); break; - case 4: _scale_bilinear<4>(r_ptr, w_ptr, width, height, p_width, p_height); break; + switch (get_format_pixel_size(format)) { + case 1: _scale_bilinear<1>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 2: _scale_bilinear<2>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 3: _scale_bilinear<3>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + case 4: _scale_bilinear<4>(src_ptr, w_ptr, src_width, src_height, p_width, p_height); break; + } + } + + if (interpolate_mipmaps) { + // Switch to read again from the first scaled mipmap to overlay it over the second + r = dst.data.read(); + _overlay(r.ptr(), w.ptr(), mip1_weight, p_width, p_height, get_format_pixel_size(format)); } } break; @@ -759,7 +842,11 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { r = PoolVector<uint8_t>::Read(); w = PoolVector<uint8_t>::Write(); - if (mipmaps > 0) + if (interpolate_mipmaps) { + dst._copy_internals_from(dst2); + } + + if (had_mipmaps) dst.generate_mipmaps(); _copy_internals_from(dst); @@ -900,8 +987,10 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & int pixsize = get_format_pixel_size(p_format); int pixshift = get_format_pixel_rshift(p_format); int block = get_format_block_size(p_format); - int minw, minh; - get_format_min_pixel_size(p_format, minw, minh); + //technically, you can still compress up to 1 px no matter the format, so commenting this + //int minw, minh; + //get_format_min_pixel_size(p_format, minw, minh); + int minw = 1, minh = 1; while (true) { @@ -1217,7 +1306,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0); if (size != p_data.size()) { - ERR_EXPLAIN("Expected data size of " + itos(size) + " in Image::create()"); + ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes."); ERR_FAIL_COND(p_data.size() != size); } @@ -1505,10 +1594,10 @@ Error Image::save_png(const String &p_path) const { return save_png_func(p_path, Ref<Image>((Image *)this)); } -int Image::get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps) { +int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) { int mm; - return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps); + return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0); } int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format) { @@ -2289,6 +2378,17 @@ Image::DetectChannels Image::get_detected_channels() { return DETECTED_RGBA; } +void Image::optimize_channels() { + switch (get_detected_channels()) { + case DETECTED_L: convert(FORMAT_L8); break; + case DETECTED_LA: convert(FORMAT_LA8); break; + case DETECTED_R: convert(FORMAT_R8); break; + case DETECTED_RG: convert(FORMAT_RG8); break; + case DETECTED_RGB: convert(FORMAT_RGB8); break; + case DETECTED_RGBA: convert(FORMAT_RGBA8); break; + } +} + void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("get_width"), &Image::get_width); @@ -2404,6 +2504,7 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST); BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR); BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC); + BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR); BIND_ENUM_CONSTANT(ALPHA_NONE); BIND_ENUM_CONSTANT(ALPHA_BIT); diff --git a/core/image.h b/core/image.h index 3792103a87..c8dd647c31 100644 --- a/core/image.h +++ b/core/image.h @@ -108,13 +108,16 @@ public: INTERPOLATE_NEAREST, INTERPOLATE_BILINEAR, INTERPOLATE_CUBIC, + INTERPOLATE_TRILINEAR, + /* INTERPOLATE_TRICUBIC, */ /* INTERPOLATE GAUSS */ }; enum CompressSource { COMPRESS_SOURCE_GENERIC, COMPRESS_SOURCE_SRGB, - COMPRESS_SOURCE_NORMAL + COMPRESS_SOURCE_NORMAL, + COMPRESS_SOURCE_LAYERED, }; //some functions provided by something else @@ -270,7 +273,7 @@ public: static int get_format_block_size(Format p_format); static void get_format_min_pixel_size(Format p_format, int &r_w, int &r_h); - static int get_image_data_size(int p_width, int p_height, Format p_format, int p_mipmaps = 0); + static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false); static int get_image_required_mipmaps(int p_width, int p_height, Format p_format); enum CompressMode { @@ -327,6 +330,7 @@ public: }; DetectChannels get_detected_channels(); + void optimize_channels(); Color get_pixelv(const Point2 &p_src) const; Color get_pixel(int p_x, int p_y) const; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 221f457b78..bb7a444ccc 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -89,7 +89,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8 for (size_t i = 0; i < ds; i += 16) { - aes256_decrypt_ecb(&ctx, &data[i]); + aes256_decrypt_ecb(&ctx, &data.write[i]); } aes256_done(&ctx); @@ -117,7 +117,7 @@ Error FileAccessEncrypted::open_and_parse_password(FileAccess *p_base, const Str key.resize(32); for (int i = 0; i < 32; i++) { - key[i] = cs[i]; + key.write[i] = cs[i]; } return open_and_parse(p_base, key, p_mode); @@ -148,7 +148,7 @@ void FileAccessEncrypted::close() { compressed.resize(len); zeromem(compressed.ptrw(), len); for (int i = 0; i < data.size(); i++) { - compressed[i] = data[i]; + compressed.write[i] = data[i]; } aes256_context ctx; @@ -156,7 +156,7 @@ void FileAccessEncrypted::close() { for (size_t i = 0; i < len; i += 16) { - aes256_encrypt_ecb(&ctx, &compressed[i]); + aes256_encrypt_ecb(&ctx, &compressed.write[i]); } aes256_done(&ctx); @@ -263,7 +263,7 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { data.resize(pos + p_length); for (int i = 0; i < p_length; i++) { - data[pos + i] = p_src[i]; + data.write[pos + i] = p_src[i]; } pos += p_length; } @@ -280,7 +280,7 @@ void FileAccessEncrypted::store_8(uint8_t p_dest) { ERR_FAIL_COND(!writing); if (pos < data.size()) { - data[pos] = p_dest; + data.write[pos] = p_dest; pos++; } else if (pos == data.size()) { data.push_back(p_dest); diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 1aa1e4a595..c4eb2848b1 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -92,7 +92,7 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { Map<String, Vector<uint8_t> >::Element *E = files->find(name); ERR_FAIL_COND_V(!E, ERR_FILE_NOT_FOUND); - data = &(E->get()[0]); + data = E->get().ptrw(); length = E->get().size(); pos = 0; diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 21e3a4172b..e0a2dbf507 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -258,8 +258,8 @@ void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) } buffer_mutex->lock(); - pages[page].buffer = p_block; - pages[page].queued = false; + pages.write[page].buffer = p_block; + pages.write[page].queued = false; buffer_mutex->unlock(); if (waiting_on_page == page) { @@ -389,7 +389,7 @@ void FileAccessNetwork::_queue_page(int p_page) const { br.offset = size_t(p_page) * page_size; br.size = page_size; nc->block_requests.push_back(br); - pages[p_page].queued = true; + pages.write[p_page].queued = true; nc->blockrequest_mutex->unlock(); DEBUG_PRINT("QUEUE PAGE POST"); nc->sem->post(); @@ -433,12 +433,12 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { _queue_page(page + j); } - buff = pages[page].buffer.ptrw(); + buff = pages.write[page].buffer.ptrw(); //queue pages buffer_mutex->unlock(); } - buff = pages[page].buffer.ptrw(); + buff = pages.write[page].buffer.ptrw(); last_page_buff = buff; last_page = page; } diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index f1620f1493..2425bb6d69 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -552,7 +552,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } else { int rec = 0; - err = _get_http_data(&chunk[chunk.size() - chunk_left], chunk_left, rec); + err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec); if (rec == 0) { break; } diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 8ebd9d6cd9..b8fd13d67c 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -107,3 +107,83 @@ void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { ERR_FAIL_COND(loader_count >= MAX_LOADERS); loader[loader_count++] = p_loader; } + +///////////////// + +RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error) { + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + if (r_error) { + *r_error = ERR_CANT_OPEN; + } + memdelete(f); + return RES(); + } + + uint8_t header[4] = { 0, 0, 0, 0 }; + f->get_buffer(header, 4); + + bool unrecognized = header[0] != 'G' || header[1] != 'D' || header[2] != 'I' || header[3] != 'M'; + if (unrecognized) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + String extension = f->get_pascal_string(); + + int idx = -1; + + for (int i = 0; i < ImageLoader::loader_count; i++) { + if (ImageLoader::loader[i]->recognize(extension)) { + idx = i; + break; + } + } + + if (idx == -1) { + memdelete(f); + if (r_error) { + *r_error = ERR_FILE_UNRECOGNIZED; + } + ERR_FAIL_V(RES()); + } + + Ref<Image> image; + image.instance(); + + Error err = ImageLoader::loader[idx]->load_image(image, f, false, 1.0); + + memdelete(f); + + if (err != OK) { + if (r_error) { + *r_error = err; + } + return RES(); + } + + if (r_error) { + *r_error = OK; + } + + return image; +} + +void ResourceFormatLoaderImage::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("image"); +} + +bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { + + return p_type == "Image"; +} + +String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { + + return p_path.get_extension().to_lower() == "image" ? "Image" : String(); +} diff --git a/core/io/image_loader.h b/core/io/image_loader.h index 052a8b8a40..fbb654c326 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -32,9 +32,11 @@ #define IMAGE_LOADER_H #include "image.h" +#include "io/resource_loader.h" #include "list.h" #include "os/file_access.h" #include "ustring.h" + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -55,6 +57,7 @@ class ImageLoader; class ImageFormatLoader { friend class ImageLoader; + friend class ResourceFormatLoaderImage; protected: virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) = 0; @@ -70,7 +73,7 @@ class ImageLoader { enum { MAX_LOADERS = 8 }; - + friend class ResourceFormatLoaderImage; static ImageFormatLoader *loader[MAX_LOADERS]; static int loader_count; @@ -83,4 +86,12 @@ public: static void add_image_format_loader(ImageFormatLoader *p_loader); }; +class ResourceFormatLoaderImage : public ResourceFormatLoader { +public: + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String &p_type) const; + virtual String get_resource_type(const String &p_path) const; +}; + #endif diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 0a3a6c1ba1..e97df0c261 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -32,8 +32,13 @@ #include "os/keyboard.h" #include "print_string.h" #include "reference.h" +#include <limits.h> #include <stdio.h> +#define _S(a) ((int32_t)a) +#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) +#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) + void EncodedObjectAsID::_bind_methods() { ClassDB::bind_method(D_METHOD("set_object_id", "id"), &EncodedObjectAsID::set_object_id); ClassDB::bind_method(D_METHOD("get_object_id"), &EncodedObjectAsID::get_object_id); @@ -60,23 +65,31 @@ EncodedObjectAsID::EncodedObjectAsID() { 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); - uint32_t strlen = decode_uint32(buf); + int32_t strlen = decode_uint32(buf); + int32_t pad = 0; + + // Handle padding + if (strlen % 4) { + pad = 4 - strlen % 4; + } + buf += 4; len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_FILE_EOF); + + // Ensure buffer is big enough + ERR_FAIL_ADD_OF(strlen, pad, ERR_FILE_EOF); + ERR_FAIL_COND_V(strlen < 0 || strlen + pad > len, ERR_FILE_EOF); String str; - str.parse_utf8((const char *)buf, strlen); + ERR_FAIL_COND_V(str.parse_utf8((const char *)buf, strlen), ERR_INVALID_DATA); r_string = str; - //handle padding - if (strlen % 4) { - strlen += 4 - strlen % 4; - } + // Add padding + strlen += pad; + // Update buffer pos, left data count, and return size buf += strlen; len -= strlen; - if (r_len) { (*r_len) += 4 + strlen; } @@ -119,14 +132,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::INT: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); if (type & ENCODE_FLAG_64) { + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); int64_t val = decode_uint64(buf); r_variant = val; if (r_len) (*r_len) += 8; } else { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t val = decode_uint32(buf); r_variant = val; if (r_len) @@ -136,14 +150,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::REAL: { - ERR_FAIL_COND_V(len < (int)4, ERR_INVALID_DATA); - if (type & ENCODE_FLAG_64) { + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); double val = decode_double(buf); r_variant = val; if (r_len) (*r_len) += 8; } else { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); float val = decode_float(buf); r_variant = val; if (r_len) @@ -164,7 +178,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // math types case Variant::VECTOR2: { - ERR_FAIL_COND_V(len < (int)4 * 2, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA); Vector2 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -176,7 +190,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; // 5 case Variant::RECT2: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Rect2 val; val.position.x = decode_float(&buf[0]); val.position.y = decode_float(&buf[4]); @@ -190,7 +204,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::VECTOR3: { - ERR_FAIL_COND_V(len < (int)4 * 3, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA); Vector3 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -203,7 +217,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM2D: { - ERR_FAIL_COND_V(len < (int)4 * 6, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); Transform2D val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { @@ -220,7 +234,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PLANE: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Plane val; val.normal.x = decode_float(&buf[0]); val.normal.y = decode_float(&buf[4]); @@ -234,7 +248,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::QUAT: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Quat val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); @@ -248,7 +262,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::AABB: { - ERR_FAIL_COND_V(len < (int)4 * 6, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); AABB val; val.position.x = decode_float(&buf[0]); val.position.y = decode_float(&buf[4]); @@ -264,7 +278,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::BASIS: { - ERR_FAIL_COND_V(len < (int)4 * 9, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 9, ERR_INVALID_DATA); Basis val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -281,7 +295,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM: { - ERR_FAIL_COND_V(len < (int)4 * 12, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 12, ERR_INVALID_DATA); Transform val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -303,7 +317,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // misc types case Variant::COLOR: { - ERR_FAIL_COND_V(len < (int)4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Color val; val.r = decode_float(&buf[0]); val.g = decode_float(&buf[4]); @@ -318,7 +332,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::NODE_PATH: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); + int32_t strlen = decode_uint32(buf); if (strlen & 0x80000000) { //new format @@ -343,31 +357,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int for (uint32_t i = 0; i < total; i++) { - ERR_FAIL_COND_V((int)len < 4, ERR_INVALID_DATA); - strlen = decode_uint32(buf); - - int pad = 0; - - if (strlen % 4) - pad += 4 - strlen % 4; - - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen + pad > len, ERR_INVALID_DATA); - String str; - str.parse_utf8((const char *)buf, strlen); + Error err = _decode_string(buf, len, r_len, str); + if (err) + return err; if (i < namecount) names.push_back(str); else subnames.push_back(str); - - buf += strlen + pad; - len -= strlen + pad; - - if (r_len) - (*r_len) += 4 + strlen + pad; } r_variant = NodePath(names, subnames, flags & 1); @@ -375,17 +373,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } else { //old format, just a string - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_INVALID_DATA); - - String str; - str.parse_utf8((const char *)buf, strlen); - - r_variant = NodePath(str); - - if (r_len) - (*r_len) += 4 + strlen; + ERR_FAIL_V(ERR_INVALID_DATA); } } break; @@ -402,6 +390,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (type & ENCODE_FLAG_OBJECT_AS_ID) { //this _is_ allowed + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); ObjectID val = decode_uint64(buf); if (r_len) (*r_len) += 8; @@ -475,7 +464,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::DICTIONARY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -488,7 +477,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Dictionary d; - for (uint32_t i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { Variant key, value; @@ -520,7 +509,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -533,7 +522,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Array varr; - for (uint32_t i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { int used = 0; Variant v; @@ -555,17 +544,17 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_BYTE_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count > len, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count > len, ERR_INVALID_DATA); PoolVector<uint8_t> data; if (count) { data.resize(count); PoolVector<uint8_t>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = buf[i]; } @@ -585,10 +574,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_INT_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); PoolVector<int> data; @@ -596,7 +586,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int //const int*rbuf=(const int*)buf; data.resize(count); PoolVector<int>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = decode_uint32(&buf[i * 4]); } @@ -612,10 +602,11 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_REAL_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); PoolVector<float> data; @@ -623,7 +614,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int //const float*rbuf=(const float*)buf; data.resize(count); PoolVector<float>::Write w = data.write(); - for (uint32_t i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { w[i] = decode_float(&buf[i * 4]); } @@ -640,7 +631,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_STRING_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); PoolVector<String> strings; buf += 4; @@ -650,35 +641,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += 4; //printf("string count: %i\n",count); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t strlen = decode_uint32(buf); - - buf += 4; - len -= 4; - ERR_FAIL_COND_V((int)strlen > len, ERR_INVALID_DATA); - - //printf("loaded string: %s\n",(const char*)buf); String str; - str.parse_utf8((const char *)buf, strlen); + Error err = _decode_string(buf, len, r_len, str); + if (err) + return err; strings.push_back(str); - - buf += strlen; - len -= strlen; - - if (r_len) - (*r_len) += 4 + strlen; - - if (strlen % 4) { - int pad = 4 - (strlen % 4); - buf += pad; - len -= pad; - if (r_len) { - (*r_len) += pad; - } - } } r_variant = strings; @@ -687,11 +657,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_VECTOR2_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 2 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 2, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 2 > len, ERR_INVALID_DATA); PoolVector<Vector2> varray; if (r_len) { @@ -702,7 +673,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int varray.resize(count); PoolVector<Vector2>::Write w = varray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].x = decode_float(buf + i * 4 * 2 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 2 + 4 * 1); @@ -722,11 +693,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_VECTOR3_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 3 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 3, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 3 > len, ERR_INVALID_DATA); + PoolVector<Vector3> varray; if (r_len) { @@ -737,7 +710,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int varray.resize(count); PoolVector<Vector3>::Write w = varray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].x = decode_float(buf + i * 4 * 3 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 3 + 4 * 1); @@ -758,11 +731,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int case Variant::POOL_COLOR_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t count = decode_uint32(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; - ERR_FAIL_COND_V((int)count * 4 * 4 > len, ERR_INVALID_DATA); + ERR_FAIL_MUL_OF(count, 4 * 4, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 4 * 4 > len, ERR_INVALID_DATA); + PoolVector<Color> carray; if (r_len) { @@ -773,7 +748,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int carray.resize(count); PoolVector<Color>::Write w = carray.write(); - for (int i = 0; i < (int)count; i++) { + for (int32_t i = 0; i < count; i++) { w[i].r = decode_float(buf + i * 4 * 4 + 4 * 0); w[i].g = decode_float(buf + i * 4 * 4 + 4 * 1); @@ -1323,7 +1298,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo while (r_len % 4) { r_len++; //pad if (buf) - buf++; + *(buf++) = 0; } } diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index ffd3ecaed0..8e67f1c97a 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -282,10 +282,10 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_ ERR_FAIL_COND(p_offset >= p_packet_len); int vlen; - Error err = decode_variant(args[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); ERR_FAIL_COND(err != OK); //args[i]=p_packet[3+i]; - argp[i] = &args[i]; + argp.write[i] = &args[i]; p_offset += vlen; } @@ -354,8 +354,8 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, Vector<uint8_t> packet; packet.resize(1 + len); - packet[0] = NETWORK_COMMAND_CONFIRM_PATH; - encode_cstring(pname.get_data(), &packet[1]); + packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; + encode_cstring(pname.get_data(), &packet.write[1]); network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_target_peer(p_from); @@ -415,9 +415,9 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int Vector<uint8_t> packet; packet.resize(1 + 4 + len); - packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH; - encode_uint32(psc->id, &packet[1]); - encode_cstring(pname.get_data(), &packet[5]); + packet.write[0] = NETWORK_COMMAND_SIMPLIFY_PATH; + encode_uint32(psc->id, &packet.write[1]); + encode_cstring(pname.get_data(), &packet.write[5]); network_peer->set_target_peer(E->get()); //to all of you network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); @@ -482,19 +482,19 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p //encode type MAKE_ROOM(1); - packet_cache[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; ofs += 1; //encode ID MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &(packet_cache[ofs])); + encode_uint32(psc->id, &(packet_cache.write[ofs])); ofs += 4; //encode function name CharString name = String(p_name).utf8(); int len = encode_cstring(name.get_data(), NULL); MAKE_ROOM(ofs + len); - encode_cstring(name.get_data(), &(packet_cache[ofs])); + encode_cstring(name.get_data(), &(packet_cache.write[ofs])); ofs += len; if (p_set) { @@ -502,19 +502,19 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p Error err = encode_variant(*p_arg[0], NULL, len); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[0], &(packet_cache[ofs]), len); + encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len); ofs += len; } else { //call arguments MAKE_ROOM(ofs + 1); - packet_cache[ofs] = p_argcount; + packet_cache.write[ofs] = p_argcount; ofs += 1; for (int i = 0; i < p_argcount; i++) { Error err = encode_variant(*p_arg[i], NULL, len); ERR_FAIL_COND(err != OK); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[i], &(packet_cache[ofs]), len); + encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len); ofs += len; } } @@ -537,7 +537,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p CharString pname = String(from_path).utf8(); int path_len = encode_cstring(pname.get_data(), NULL); MAKE_ROOM(ofs + path_len); - encode_cstring(pname.get_data(), &(packet_cache[ofs])); + encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { @@ -554,11 +554,11 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p if (F->get() == true) { //this one confirmed path, so use id - encode_uint32(psc->id, &(packet_cache[1])); + encode_uint32(psc->id, &(packet_cache.write[1])); network_peer->put_packet(packet_cache.ptr(), ofs); } else { //this one did not confirm path yet, so use entire path (sorry!) - encode_uint32(0x80000000 | ofs, &(packet_cache[1])); //offset to path and flag + encode_uint32(0x80000000 | ofs, &(packet_cache.write[1])); //offset to path and flag network_peer->put_packet(packet_cache.ptr(), ofs + path_len); } } @@ -712,8 +712,8 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, Networked MAKE_ROOM(p_data.size() + 1); PoolVector<uint8_t>::Read r = p_data.read(); - packet_cache[0] = NETWORK_COMMAND_RAW; - memcpy(&packet_cache[1], &r[0], p_data.size()); + packet_cache.write[0] = NETWORK_COMMAND_RAW; + memcpy(&packet_cache.write[1], &r[0], p_data.size()); network_peer->set_target_peer(p_to); network_peer->set_transfer_mode(p_mode); @@ -773,7 +773,7 @@ Vector<int> MultiplayerAPI::get_network_connected_peers() const { void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); - ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); + ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index b777a9f960..dc4997dfc2 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -173,7 +173,7 @@ Error PacketPeerStream::_poll_buffer() const { int read = 0; ERR_FAIL_COND_V(input_buffer.size() < ring_buffer.space_left(), ERR_UNAVAILABLE); - Error err = peer->get_partial_data(&input_buffer[0], ring_buffer.space_left(), read); + Error err = peer->get_partial_data(input_buffer.ptrw(), ring_buffer.space_left(), read); if (err) return err; if (read == 0) @@ -226,7 +226,7 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) ERR_FAIL_COND_V(input_buffer.size() < len, ERR_UNAVAILABLE); ring_buffer.read(lbuf, 4); //get rid of first 4 bytes - ring_buffer.read(&input_buffer[0], len); // read packet + ring_buffer.read(input_buffer.ptrw(), len); // read packet *r_buffer = &input_buffer[0]; r_buffer_size = len; @@ -247,8 +247,8 @@ Error PacketPeerStream::put_packet(const uint8_t *p_buffer, int p_buffer_size) { ERR_FAIL_COND_V(p_buffer_size < 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_buffer_size + 4 > output_buffer.size(), ERR_INVALID_PARAMETER); - encode_uint32(p_buffer_size, &output_buffer[0]); - uint8_t *dst = &output_buffer[4]; + encode_uint32(p_buffer_size, output_buffer.ptrw()); + uint8_t *dst = &output_buffer.write[4]; for (int i = 0; i < p_buffer_size; i++) dst[i] = p_buffer[i]; diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index b6377662de..2fd73db27d 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -120,7 +120,7 @@ Error PCKPacker::flush(bool p_verbose) { for (int i = 0; i < files.size(); i++) { file->store_pascal_string(files[i].path); - files[i].offset_offset = file->get_position(); + files.write[i].offset_offset = file->get_position(); file->store_64(0); // offset file->store_64(files[i].size); // size diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 0c626c197b..02c2c6ce1a 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -894,7 +894,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { for (uint32_t i = 0; i < string_table_size; i++) { StringName s = get_unicode_string(); - string_map[i] = s; + string_map.write[i] = s; } print_bl("strings: " + itos(string_table_size)); @@ -1834,7 +1834,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p save_order.resize(external_resources.size()); for (Map<RES, int>::Element *E = external_resources.front(); E; E = E->next()) { - save_order[E->get()] = E->key(); + save_order.write[E->get()] = E->key(); } for (int i = 0; i < save_order.size(); i++) { diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 1351030d1e..8b0655deb0 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -123,6 +123,10 @@ Ref<ResourceInteractiveLoader> ResourceFormatLoader::load_interactive(const Stri return ril; } +bool ResourceFormatLoader::exists(const String &p_path) const { + return FileAccess::exists(p_path); //by default just check file +} + RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error) { String path = p_path; @@ -239,6 +243,36 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p return res; } +bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { + + String local_path; + if (p_path.is_rel_path()) + local_path = "res://" + p_path; + else + local_path = ProjectSettings::get_singleton()->localize_path(p_path); + + if (ResourceCache::has(local_path)) { + + return true; // If cached, it probably exists + } + + bool xl_remapped = false; + String path = _path_remap(local_path, &xl_remapped); + + // Try all loaders and pick the first match for the type hint + for (int i = 0; i < loader_count; i++) { + + if (!loader[i]->recognize_path(path, p_type_hint)) { + continue; + } + + if (loader[i]->exists(path)) + return true; + } + + return false; +} + Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { if (r_error) @@ -556,7 +590,7 @@ void ResourceLoader::load_translation_remaps() { Vector<String> lang_remaps; lang_remaps.resize(langs.size()); for (int i = 0; i < langs.size(); i++) { - lang_remaps[i] = langs[i]; + lang_remaps.write[i] = langs[i]; } translation_remaps[String(E->get())] = lang_remaps; diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 9be82abb42..f78464ef0c 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -60,6 +60,7 @@ class ResourceFormatLoader { public: virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual bool exists(const String &p_path) const; virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const; @@ -106,6 +107,7 @@ class ResourceLoader { public: static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); static RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); + static bool exists(const String &p_path, const String &p_type_hint = ""); static void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions); static void add_resource_format_loader(ResourceFormatLoader *p_format_loader, bool p_at_front = false); diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 927b9f6366..3e0ee088c2 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -331,7 +331,7 @@ String StreamPeer::get_string(int p_bytes) { ERR_FAIL_COND_V(err != OK, String()); err = get_data((uint8_t *)&buf[0], p_bytes); ERR_FAIL_COND_V(err != OK, String()); - buf[p_bytes] = 0; + buf.write[p_bytes] = 0; return buf.ptr(); } String StreamPeer::get_utf8_string(int p_bytes) { diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index c71af6b641..e7e9662d24 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -92,7 +92,7 @@ PoolByteArray StreamPeerSSL::get_project_cert_array() { void StreamPeerSSL::_bind_methods() { ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll); - ClassDB::bind_method(D_METHOD("accept_stream"), &StreamPeerSSL::accept_stream); + ClassDB::bind_method(D_METHOD("accept_stream", "base"), &StreamPeerSSL::accept_stream); ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String())); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status); ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream); diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 16d5e3c282..85c1fc5ddf 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -54,32 +54,25 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S int line = 1; bool skip_this = false; bool skip_next = false; + bool is_eof = false; - while (true) { + while (!is_eof) { - String l = f->get_line(); + String l = f->get_line().strip_edges(); + is_eof = f->eof_reached(); - if (f->eof_reached()) { + // If we reached last line and it's not a content line, break, otherwise let processing that last loop + if (is_eof && l.empty()) { - if (status == STATUS_READING_STRING) { - - if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") - config = msg_str; - break; - - } else if (status == STATUS_NONE) + if (status == STATUS_READING_ID) { + memdelete(f); + ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V(RES()); + } else { break; - - memdelete(f); - ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); - ERR_FAIL_V(RES()); + } } - l = l.strip_edges(); - if (l.begins_with("msgid")) { if (status == STATUS_READING_ID) { @@ -160,6 +153,15 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S f->close(); memdelete(f); + if (status == STATUS_READING_STRING) { + + if (msg_id != "") { + if (!skip_this) + translation->add_message(msg_id, msg_str); + } else if (config == "") + config = msg_str; + } + if (config == "") { ERR_EXPLAIN("No config found in file: " + p_path); ERR_FAIL_V(RES()); diff --git a/core/make_binders.py b/core/make_binders.py index 6a7602cd5d..4c61b90d99 100644 --- a/core/make_binders.py +++ b/core/make_binders.py @@ -1,6 +1,5 @@ # -*- coding: ibm850 -*- - template_typed = """ #ifdef TYPED_METHOD_BIND template<class T $ifret ,class R$ $ifargs ,$ $arg, class P@$> @@ -265,8 +264,13 @@ def run(target, source, env): else: text += t - with open(target[0].path, "w") as f: + with open(target[0], "w") as f: f.write(text) - with open(target[1].path, "w") as f: + with open(target[1], "w") as f: f.write(text_ext) + + +if __name__ == '__main__': + from platform_methods import subprocess_main + subprocess_main(globals()) diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index cff19f990c..e2e71dda92 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -245,7 +245,6 @@ Vector3 AABB::get_longest_axis() const { if (size.z > max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -262,7 +261,6 @@ int AABB::get_longest_axis_index() const { if (size.z > max_size) { axis = 2; - max_size = size.z; } return axis; @@ -280,7 +278,6 @@ Vector3 AABB::get_shortest_axis() const { if (size.z < max_size) { axis = Vector3(0, 0, 1); - max_size = size.z; } return axis; @@ -297,7 +294,6 @@ int AABB::get_shortest_axis_index() const { if (size.z < max_size) { axis = 2; - max_size = size.z; } return axis; diff --git a/core/math/bsp_tree.cpp b/core/math/bsp_tree.cpp index b1424e1d78..24096de551 100644 --- a/core/math/bsp_tree.cpp +++ b/core/math/bsp_tree.cpp @@ -244,10 +244,8 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { const Node *nodesptr = &nodes[0]; const Plane *planesptr = &planes[0]; - int plane_count = planes.size(); int idx = node_count - 1; - int steps = 0; while (true) { @@ -259,21 +257,19 @@ bool BSP_Tree::point_is_inside(const Vector3 &p_point) const { return true; } - uint16_t plane = nodesptr[idx].plane; #ifdef DEBUG_ENABLED - + int plane_count = planes.size(); + uint16_t plane = nodesptr[idx].plane; ERR_FAIL_INDEX_V(plane, plane_count, false); #endif + bool over = planesptr[nodesptr[idx].plane].is_point_over(p_point); idx = over ? nodes[idx].over : nodes[idx].under; #ifdef DEBUG_ENABLED - ERR_FAIL_COND_V(idx < MAX_NODES && idx >= node_count, false); #endif - - steps++; } return false; @@ -453,10 +449,10 @@ BSP_Tree::operator Variant() const { for (int i = 0; i < planes.size(); i++) { - plane_values[i * 4 + 0] = planes[i].normal.x; - plane_values[i * 4 + 1] = planes[i].normal.y; - plane_values[i * 4 + 2] = planes[i].normal.z; - plane_values[i * 4 + 3] = planes[i].d; + plane_values.write[i * 4 + 0] = planes[i].normal.x; + plane_values.write[i * 4 + 1] = planes[i].normal.y; + plane_values.write[i * 4 + 2] = planes[i].normal.z; + plane_values.write[i * 4 + 3] = planes[i].d; } d["planes"] = plane_values; @@ -502,10 +498,10 @@ BSP_Tree::BSP_Tree(const Variant &p_variant) { PoolVector<real_t>::Read r = src_planes.read(); for (int i = 0; i < plane_count / 4; i++) { - planes[i].normal.x = r[i * 4 + 0]; - planes[i].normal.y = r[i * 4 + 1]; - planes[i].normal.z = r[i * 4 + 2]; - planes[i].d = r[i * 4 + 3]; + planes.write[i].normal.x = r[i * 4 + 0]; + planes.write[i].normal.y = r[i * 4 + 1]; + planes.write[i].normal.z = r[i * 4 + 2]; + planes.write[i].d = r[i * 4 + 3]; } } @@ -524,9 +520,9 @@ BSP_Tree::BSP_Tree(const Variant &p_variant) { for (int i = 0; i < nodes.size(); i++) { - nodes[i].over = r[i * 3 + 0]; - nodes[i].under = r[i * 3 + 1]; - nodes[i].plane = r[i * 3 + 2]; + nodes.write[i].over = r[i * 3 + 0]; + nodes.write[i].under = r[i * 3 + 1]; + nodes.write[i].plane = r[i * 3 + 2]; } } diff --git a/core/math/delaunay.h b/core/math/delaunay.h index 09aebc773f..13fbc0c6ae 100644 --- a/core/math/delaunay.h +++ b/core/math/delaunay.h @@ -92,7 +92,7 @@ public: for (int j = 0; j < triangles.size(); j++) { if (circum_circle_contains(points, triangles[j], i)) { - triangles[j].bad = true; + triangles.write[j].bad = true; polygon.push_back(Edge(triangles[j].points[0], triangles[j].points[1])); polygon.push_back(Edge(triangles[j].points[1], triangles[j].points[2])); polygon.push_back(Edge(triangles[j].points[2], triangles[j].points[0])); @@ -109,8 +109,8 @@ public: for (int j = 0; j < polygon.size(); j++) { for (int k = j + 1; k < polygon.size(); k++) { if (edge_compare(points, polygon[j], polygon[k])) { - polygon[j].bad = true; - polygon[k].bad = true; + polygon.write[j].bad = true; + polygon.write[k].bad = true; } } } diff --git a/core/math/expression.cpp b/core/math/expression.cpp new file mode 100644 index 0000000000..a161dbddba --- /dev/null +++ b/core/math/expression.cpp @@ -0,0 +1,2136 @@ +#include "expression.h" + +#include "class_db.h" +#include "func_ref.h" +#include "io/marshalls.h" +#include "math_funcs.h" +#include "os/os.h" +#include "reference.h" +#include "variant_parser.h" + +const char *Expression::func_name[Expression::FUNC_MAX] = { + "sin", + "cos", + "tan", + "sinh", + "cosh", + "tanh", + "asin", + "acos", + "atan", + "atan2", + "sqrt", + "fmod", + "fposmod", + "floor", + "ceil", + "round", + "abs", + "sign", + "pow", + "log", + "exp", + "is_nan", + "is_inf", + "ease", + "decimals", + "stepify", + "lerp", + "inverse_lerp", + "range_lerp", + "dectime", + "randomize", + "randi", + "randf", + "rand_range", + "seed", + "rand_seed", + "deg2rad", + "rad2deg", + "linear2db", + "db2linear", + "polar2cartesian", + "cartesian2polar", + "wrapi", + "wrapf", + "max", + "min", + "clamp", + "nearest_po2", + "weakref", + "funcref", + "convert", + "typeof", + "type_exists", + "char", + "str", + "print", + "printerr", + "printraw", + "var2str", + "str2var", + "var2bytes", + "bytes2var", + "color_named", +}; + +Expression::BuiltinFunc Expression::find_function(const String &p_string) { + + for (int i = 0; i < FUNC_MAX; i++) { + if (p_string == func_name[i]) + return BuiltinFunc(i); + } + + return FUNC_MAX; +} + +String Expression::get_func_name(BuiltinFunc p_func) { + + ERR_FAIL_INDEX_V(p_func, FUNC_MAX, String()); + return func_name[p_func]; +} + +int Expression::get_func_argument_count(BuiltinFunc p_func) { + + switch (p_func) { + + case MATH_RANDOMIZE: + case MATH_RAND: + case MATH_RANDF: + return 0; + case MATH_SIN: + case MATH_COS: + case MATH_TAN: + case MATH_SINH: + case MATH_COSH: + case MATH_TANH: + case MATH_ASIN: + case MATH_ACOS: + case MATH_ATAN: + case MATH_SQRT: + case MATH_FLOOR: + case MATH_CEIL: + case MATH_ROUND: + case MATH_ABS: + case MATH_SIGN: + case MATH_LOG: + case MATH_EXP: + case MATH_ISNAN: + case MATH_ISINF: + case MATH_DECIMALS: + case MATH_SEED: + case MATH_RANDSEED: + case MATH_DEG2RAD: + case MATH_RAD2DEG: + case MATH_LINEAR2DB: + case MATH_DB2LINEAR: + case LOGIC_NEAREST_PO2: + case OBJ_WEAKREF: + case TYPE_OF: + case TEXT_CHAR: + case TEXT_STR: + case TEXT_PRINT: + case TEXT_PRINTERR: + case TEXT_PRINTRAW: + case VAR_TO_STR: + case STR_TO_VAR: + case VAR_TO_BYTES: + case BYTES_TO_VAR: + case TYPE_EXISTS: + return 1; + case MATH_ATAN2: + case MATH_FMOD: + case MATH_FPOSMOD: + case MATH_POW: + case MATH_EASE: + case MATH_STEPIFY: + case MATH_RANDOM: + case MATH_POLAR2CARTESIAN: + case MATH_CARTESIAN2POLAR: + case LOGIC_MAX: + case LOGIC_MIN: + case FUNC_FUNCREF: + case TYPE_CONVERT: + case COLORN: + return 2; + case MATH_LERP: + case MATH_INVERSE_LERP: + case MATH_DECTIME: + case MATH_WRAP: + case MATH_WRAPF: + case LOGIC_CLAMP: + return 3; + case MATH_RANGE_LERP: + return 5; + case FUNC_MAX: { + } + } + return 0; +} + +#define VALIDATE_ARG_NUM(m_arg) \ + if (!p_inputs[m_arg]->is_num()) { \ + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; \ + r_error.argument = m_arg; \ + r_error.expected = Variant::REAL; \ + return; \ + } + +void Expression::exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str) { + + switch (p_func) { + case MATH_SIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sin((double)*p_inputs[0]); + } break; + case MATH_COS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cos((double)*p_inputs[0]); + } break; + case MATH_TAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tan((double)*p_inputs[0]); + } break; + case MATH_SINH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sinh((double)*p_inputs[0]); + } break; + case MATH_COSH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::cosh((double)*p_inputs[0]); + } break; + case MATH_TANH: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::tanh((double)*p_inputs[0]); + } break; + case MATH_ASIN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::asin((double)*p_inputs[0]); + } break; + case MATH_ACOS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::acos((double)*p_inputs[0]); + } break; + case MATH_ATAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::atan((double)*p_inputs[0]); + } break; + case MATH_ATAN2: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::atan2((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SQRT: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::sqrt((double)*p_inputs[0]); + } break; + case MATH_FMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FPOSMOD: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::fposmod((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_FLOOR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::floor((double)*p_inputs[0]); + } break; + case MATH_CEIL: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::ceil((double)*p_inputs[0]); + } break; + case MATH_ROUND: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::round((double)*p_inputs[0]); + } break; + case MATH_ABS: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = ABS(i); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = Math::abs(r); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_SIGN: { + + if (p_inputs[0]->get_type() == Variant::INT) { + + int64_t i = *p_inputs[0]; + *r_return = i < 0 ? -1 : (i > 0 ? +1 : 0); + } else if (p_inputs[0]->get_type() == Variant::REAL) { + + real_t r = *p_inputs[0]; + *r_return = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0); + } else { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::REAL; + } + } break; + case MATH_POW: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::pow((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LOG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::log((double)*p_inputs[0]); + } break; + case MATH_EXP: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::exp((double)*p_inputs[0]); + } break; + case MATH_ISNAN: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_nan((double)*p_inputs[0]); + } break; + case MATH_ISINF: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::is_inf((double)*p_inputs[0]); + } break; + case MATH_EASE: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::ease((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_DECIMALS: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::step_decimals((double)*p_inputs[0]); + } break; + case MATH_STEPIFY: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::stepify((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_INVERSE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::inverse_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANGE_LERP: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + VALIDATE_ARG_NUM(3); + VALIDATE_ARG_NUM(4); + *r_return = Math::range_lerp((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2], (double)*p_inputs[3], (double)*p_inputs[4]); + } break; + case MATH_DECTIME: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::dectime((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case MATH_RANDOMIZE: { + Math::randomize(); + + } break; + case MATH_RAND: { + *r_return = Math::rand(); + } break; + case MATH_RANDF: { + *r_return = Math::randf(); + } break; + case MATH_RANDOM: { + + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + *r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]); + } break; + case MATH_SEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + Math::seed(seed); + + } break; + case MATH_RANDSEED: { + + VALIDATE_ARG_NUM(0); + uint64_t seed = *p_inputs[0]; + int ret = Math::rand_from_seed(&seed); + Array reta; + reta.push_back(ret); + reta.push_back(seed); + *r_return = reta; + + } break; + case MATH_DEG2RAD: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::deg2rad((double)*p_inputs[0]); + } break; + case MATH_RAD2DEG: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::rad2deg((double)*p_inputs[0]); + } break; + case MATH_LINEAR2DB: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::linear2db((double)*p_inputs[0]); + } break; + case MATH_DB2LINEAR: { + + VALIDATE_ARG_NUM(0); + *r_return = Math::db2linear((double)*p_inputs[0]); + } break; + case MATH_POLAR2CARTESIAN: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double r = *p_inputs[0]; + double th = *p_inputs[1]; + *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); + } break; + case MATH_CARTESIAN2POLAR: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + double x = *p_inputs[0]; + double y = *p_inputs[1]; + *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); + } break; + case MATH_WRAP: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapi((int64_t)*p_inputs[0], (int64_t)*p_inputs[1], (int64_t)*p_inputs[2]); + } break; + case MATH_WRAPF: { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + *r_return = Math::wrapf((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]); + } break; + case LOGIC_MAX: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MAX(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MAX(a, b); + } + + } break; + case LOGIC_MIN: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + *r_return = MIN(a, b); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + + *r_return = MIN(a, b); + } + } break; + case LOGIC_CLAMP: { + + if (p_inputs[0]->get_type() == Variant::INT && p_inputs[1]->get_type() == Variant::INT && p_inputs[2]->get_type() == Variant::INT) { + + int64_t a = *p_inputs[0]; + int64_t b = *p_inputs[1]; + int64_t c = *p_inputs[2]; + *r_return = CLAMP(a, b, c); + } else { + VALIDATE_ARG_NUM(0); + VALIDATE_ARG_NUM(1); + VALIDATE_ARG_NUM(2); + + real_t a = *p_inputs[0]; + real_t b = *p_inputs[1]; + real_t c = *p_inputs[2]; + + *r_return = CLAMP(a, b, c); + } + } break; + case LOGIC_NEAREST_PO2: { + + VALIDATE_ARG_NUM(0); + int64_t num = *p_inputs[0]; + *r_return = next_power_of_2(num); + } break; + case OBJ_WEAKREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + + if (p_inputs[0]->is_ref()) { + + REF r = *p_inputs[0]; + if (!r.is_valid()) { + + return; + } + + Ref<WeakRef> wref = memnew(WeakRef); + wref->set_ref(r); + *r_return = wref; + } else { + Object *obj = *p_inputs[0]; + if (!obj) { + + return; + } + Ref<WeakRef> wref = memnew(WeakRef); + wref->set_obj(obj); + *r_return = wref; + } + + } break; + case FUNC_FUNCREF: { + + if (p_inputs[0]->get_type() != Variant::OBJECT) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + + return; + } + if (p_inputs[1]->get_type() != Variant::STRING && p_inputs[1]->get_type() != Variant::NODE_PATH) { + + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING; + + return; + } + + Ref<FuncRef> fr = memnew(FuncRef); + + fr->set_instance(*p_inputs[0]); + fr->set_function(*p_inputs[1]); + + *r_return = fr; + + } break; + case TYPE_CONVERT: { + + VALIDATE_ARG_NUM(1); + int type = *p_inputs[1]; + if (type < 0 || type >= Variant::VARIANT_MAX) { + + r_error_str = RTR("Invalid type argument to convert(), use TYPE_* constants."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::INT; + return; + + } else { + + *r_return = Variant::construct(Variant::Type(type), p_inputs, 1, r_error); + } + } break; + case TYPE_OF: { + + *r_return = p_inputs[0]->get_type(); + + } break; + case TYPE_EXISTS: { + + *r_return = ClassDB::class_exists(*p_inputs[0]); + + } break; + case TEXT_CHAR: { + + CharType result[2] = { *p_inputs[0], 0 }; + + *r_return = String(result); + + } break; + case TEXT_STR: { + + String str = *p_inputs[0]; + + *r_return = str; + + } break; + case TEXT_PRINT: { + + String str = *p_inputs[0]; + print_line(str); + + } break; + + case TEXT_PRINTERR: { + + String str = *p_inputs[0]; + + //str+="\n"; + print_error(str); + + } break; + case TEXT_PRINTRAW: { + String str = *p_inputs[0]; + + //str+="\n"; + OS::get_singleton()->print("%s", str.utf8().get_data()); + + } break; + case VAR_TO_STR: { + + String vars; + VariantWriter::write_to_string(*p_inputs[0], vars); + *r_return = vars; + } break; + case STR_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::STRING) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + + return; + } + + VariantParser::StreamString ss; + ss.s = *p_inputs[0]; + + String errs; + int line; + Error err = VariantParser::parse(&ss, *r_return, errs, line); + + if (err != OK) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::STRING; + *r_return = "Parse error at line " + itos(line) + ": " + errs; + return; + } + + } break; + case VAR_TO_BYTES: { + + PoolByteArray barr; + int len; + Error err = encode_variant(*p_inputs[0], NULL, len); + if (err) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + r_error_str = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID)."; + return; + } + + barr.resize(len); + { + PoolByteArray::Write w = barr.write(); + encode_variant(*p_inputs[0], w.ptr(), len); + } + *r_return = barr; + } break; + case BYTES_TO_VAR: { + + if (p_inputs[0]->get_type() != Variant::POOL_BYTE_ARRAY) { + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + + return; + } + + PoolByteArray varr = *p_inputs[0]; + Variant ret; + { + PoolByteArray::Read r = varr.read(); + Error err = decode_variant(ret, r.ptr(), varr.size(), NULL); + if (err != OK) { + r_error_str = RTR("Not enough bytes for decoding bytes, or invalid format."); + r_error.error = Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::POOL_BYTE_ARRAY; + return; + } + } + + *r_return = ret; + + } break; + case COLORN: { + + VALIDATE_ARG_NUM(1); + + Color color = Color::named(*p_inputs[0]); + color.a = *p_inputs[1]; + + *r_return = String(color); + + } break; + default: {} + } +} + +//////// + +Error Expression::_get_token(Token &r_token) { + + while (true) { +#define GET_CHAR() (str_ofs >= expression.length() ? 0 : expression[str_ofs++]) + + CharType cchar = GET_CHAR(); + if (cchar == 0) { + r_token.type = TK_EOF; + return OK; + } + + switch (cchar) { + + case 0: { + r_token.type = TK_EOF; + return OK; + } break; + case '{': { + + r_token.type = TK_CURLY_BRACKET_OPEN; + return OK; + }; + case '}': { + + r_token.type = TK_CURLY_BRACKET_CLOSE; + return OK; + }; + case '[': { + + r_token.type = TK_BRACKET_OPEN; + return OK; + }; + case ']': { + + r_token.type = TK_BRACKET_CLOSE; + return OK; + }; + case '(': { + + r_token.type = TK_PARENTHESIS_OPEN; + return OK; + }; + case ')': { + + r_token.type = TK_PARENTHESIS_CLOSE; + return OK; + }; + case ',': { + + r_token.type = TK_COMMA; + return OK; + }; + case ':': { + + r_token.type = TK_COLON; + return OK; + }; + case '.': { + + r_token.type = TK_PERIOD; + return OK; + }; + case '$': { + + r_token.type = TK_INPUT; + int index = 0; + do { + if (expression[str_ofs] < '0' || expression[str_ofs] > '9') { + _set_error("Expected number after '$'"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + index *= 10; + index += expression[str_ofs] - '0'; + str_ofs++; + + } while (expression[str_ofs] >= '0' && expression[str_ofs] <= '9'); + + r_token.value = index; + return OK; + }; + case '=': { + + cchar = GET_CHAR(); + if (cchar == '=') { + r_token.type = TK_OP_EQUAL; + } else { + _set_error("Expected '='"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + return OK; + }; + case '!': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_NOT_EQUAL; + str_ofs++; + } else { + r_token.type = TK_OP_NOT; + } + return OK; + }; + case '>': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_GREATER_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '>') { + r_token.type = TK_OP_SHIFT_RIGHT; + str_ofs++; + } else { + r_token.type = TK_OP_GREATER; + } + return OK; + }; + case '<': { + + if (expression[str_ofs] == '=') { + r_token.type = TK_OP_LESS_EQUAL; + str_ofs++; + } else if (expression[str_ofs] == '<') { + r_token.type = TK_OP_SHIFT_LEFT; + str_ofs++; + } else { + r_token.type = TK_OP_LESS; + } + return OK; + }; + case '+': { + r_token.type = TK_OP_ADD; + return OK; + }; + case '-': { + r_token.type = TK_OP_SUB; + return OK; + }; + case '/': { + r_token.type = TK_OP_DIV; + return OK; + }; + case '*': { + r_token.type = TK_OP_MUL; + return OK; + }; + case '%': { + r_token.type = TK_OP_MOD; + return OK; + }; + case '&': { + + if (expression[str_ofs] == '&') { + r_token.type = TK_OP_AND; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_AND; + } + return OK; + }; + case '|': { + + if (expression[str_ofs] == '|') { + r_token.type = TK_OP_OR; + str_ofs++; + } else { + r_token.type = TK_OP_BIT_OR; + } + return OK; + }; + case '^': { + + r_token.type = TK_OP_BIT_XOR; + + return OK; + }; + case '~': { + + r_token.type = TK_OP_BIT_INVERT; + + return OK; + }; + case '"': { + + String str; + while (true) { + + CharType ch = GET_CHAR(); + + if (ch == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } else if (ch == '"') { + break; + } else if (ch == '\\') { + //escaped characters... + + CharType next = GET_CHAR(); + if (next == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType res = 0; + + switch (next) { + + case 'b': res = 8; break; + case 't': res = 9; break; + case 'n': res = 10; break; + case 'f': res = 12; break; + case 'r': res = 13; break; + case 'u': { + //hexnumbarh - oct is deprecated + + for (int j = 0; j < 4; j++) { + CharType c = GET_CHAR(); + + if (c == 0) { + _set_error("Unterminated String"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + + _set_error("Malformed hex constant in string"); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + CharType v; + if (c >= '0' && c <= '9') { + v = c - '0'; + } else if (c >= 'a' && c <= 'f') { + v = c - 'a'; + v += 10; + } else if (c >= 'A' && c <= 'F') { + v = c - 'A'; + v += 10; + } else { + ERR_PRINT("BUG"); + v = 0; + } + + res <<= 4; + res |= v; + } + + } break; + //case '\"': res='\"'; break; + //case '\\': res='\\'; break; + //case '/': res='/'; break; + default: { + res = next; + //r_err_str="Invalid escape sequence"; + //return ERR_PARSE_ERROR; + } break; + } + + str += res; + + } else { + str += ch; + } + } + + r_token.type = TK_CONSTANT; + r_token.value = str; + return OK; + + } break; + default: { + + if (cchar <= 32) { + break; + } + + if (cchar >= '0' && cchar <= '9') { + //a number + + String num; +#define READING_SIGN 0 +#define READING_INT 1 +#define READING_DEC 2 +#define READING_EXP 3 +#define READING_DONE 4 + int reading = READING_INT; + + CharType c = cchar; + bool exp_sign = false; + bool exp_beg = false; + bool is_float = false; + + while (true) { + + switch (reading) { + case READING_INT: { + + if (c >= '0' && c <= '9') { + //pass + } else if (c == '.') { + reading = READING_DEC; + is_float = true; + } else if (c == 'e') { + reading = READING_EXP; + } else { + reading = READING_DONE; + } + + } break; + case READING_DEC: { + + if (c >= '0' && c <= '9') { + + } else if (c == 'e') { + reading = READING_EXP; + + } else { + reading = READING_DONE; + } + + } break; + case READING_EXP: { + + if (c >= '0' && c <= '9') { + exp_beg = true; + + } else if ((c == '-' || c == '+') && !exp_sign && !exp_beg) { + if (c == '-') + is_float = true; + exp_sign = true; + + } else { + reading = READING_DONE; + } + } break; + } + + if (reading == READING_DONE) + break; + num += String::chr(c); + c = GET_CHAR(); + } + + str_ofs--; + + r_token.type = TK_CONSTANT; + + if (is_float) + r_token.value = num.to_double(); + else + r_token.value = num.to_int(); + return OK; + + } else if ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_') { + + String id; + bool first = true; + + while ((cchar >= 'A' && cchar <= 'Z') || (cchar >= 'a' && cchar <= 'z') || cchar == '_' || (!first && cchar >= '0' && cchar <= '9')) { + + id += String::chr(cchar); + cchar = GET_CHAR(); + first = false; + } + + str_ofs--; //go back one + + if (id == "in") { + r_token.type = TK_OP_IN; + } else if (id == "null") { + r_token.type = TK_CONSTANT; + r_token.value = Variant(); + } else if (id == "true") { + r_token.type = TK_CONSTANT; + r_token.value = true; + } else if (id == "false") { + r_token.type = TK_CONSTANT; + r_token.value = false; + } else if (id == "PI") { + r_token.type = TK_CONSTANT; + r_token.value = Math_PI; + } else if (id == "TAU") { + r_token.type = TK_CONSTANT; + r_token.value = Math_TAU; + } else if (id == "INF") { + r_token.type = TK_CONSTANT; + r_token.value = Math_INF; + } else if (id == "NAN") { + r_token.type = TK_CONSTANT; + r_token.value = Math_NAN; + } else if (id == "not") { + r_token.type = TK_OP_NOT; + } else if (id == "or") { + r_token.type = TK_OP_OR; + } else if (id == "and") { + r_token.type = TK_OP_AND; + } else if (id == "self") { + r_token.type = TK_SELF; + } else { + + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (id == Variant::get_type_name(Variant::Type(i))) { + r_token.type = TK_BASIC_TYPE; + r_token.value = i; + return OK; + } + } + + BuiltinFunc bifunc = find_function(id); + if (bifunc != FUNC_MAX) { + r_token.type = TK_BUILTIN_FUNC; + r_token.value = bifunc; + return OK; + } + + r_token.type = TK_IDENTIFIER; + r_token.value = id; + } + + return OK; + } else { + _set_error("Unexpected character."); + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; + } + } + } + } + + r_token.type = TK_ERROR; + return ERR_PARSE_ERROR; +} + +const char *Expression::token_name[TK_MAX] = { + "CURLY BRACKET OPEN", + "CURLY BRACKET CLOSE", + "BRACKET OPEN", + "BRACKET CLOSE", + "PARENTHESIS OPEN", + "PARENTHESIS CLOSE", + "IDENTIFIER", + "BUILTIN FUNC", + "SELF", + "CONSTANT", + "BASIC TYPE", + "COLON", + "COMMA", + "PERIOD", + "OP IN", + "OP EQUAL", + "OP NOT EQUAL", + "OP LESS", + "OP LESS EQUAL", + "OP GREATER", + "OP GREATER EQUAL", + "OP AND", + "OP OR", + "OP NOT", + "OP ADD", + "OP SUB", + "OP MUL", + "OP DIV", + "OP MOD", + "OP SHIFT LEFT", + "OP SHIFT RIGHT", + "OP BIT AND", + "OP BIT OR", + "OP BIT XOR", + "OP BIT INVERT", + "OP INPUT", + "EOF", + "ERROR" +}; + +Expression::ENode *Expression::_parse_expression() { + + Vector<ExpressionNode> expression; + + while (true) { + //keep appending stuff to expression + ENode *expr = NULL; + + Token tk; + _get_token(tk); + if (error_set) + return NULL; + + switch (tk.type) { + case TK_CURLY_BRACKET_OPEN: { + //a dictionary + DictionaryNode *dn = alloc_node<DictionaryNode>(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_CURLY_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + dn->dict.push_back(expr); + + _get_token(tk); + if (tk.type != TK_COLON) { + _set_error("Expected ':'"); + return NULL; + } + + expr = _parse_expression(); + if (!expr) + return NULL; + + dn->dict.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_CURLY_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or '}'"); + } + } + + expr = dn; + } break; + case TK_BRACKET_OPEN: { + //an array + + ArrayNode *an = alloc_node<ArrayNode>(); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_BRACKET_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + an->array.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_BRACKET_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ']'"); + } + } + + expr = an; + } break; + case TK_PARENTHESIS_OPEN: { + //a suexpression + ENode *e = _parse_expression(); + if (error_set) + return NULL; + _get_token(tk); + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ')'"); + return NULL; + } + + expr = e; + + } break; + case TK_IDENTIFIER: { + + String identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method = identifier; + SelfNode *self_node = alloc_node<SelfNode>(); + func_call->base = self_node; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + int input_index = -1; + for (int i = 0; i < input_names.size(); i++) { + if (input_names[i] == identifier) { + input_index = i; + break; + } + } + + if (input_index != -1) { + InputNode *input = alloc_node<InputNode>(); + input->index = input_index; + expr = input; + } else { + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + SelfNode *self_node = alloc_node<SelfNode>(); + index->base = self_node; + index->name = identifier; + expr = index; + } + } + } break; + case TK_INPUT: { + + InputNode *input = alloc_node<InputNode>(); + input->index = tk.value; + expr = input; + } break; + case TK_SELF: { + + SelfNode *self = alloc_node<SelfNode>(); + expr = self; + } break; + case TK_CONSTANT: { + ConstantNode *constant = alloc_node<ConstantNode>(); + constant->value = tk.value; + expr = constant; + } break; + case TK_BASIC_TYPE: { + //constructor.. + + Variant::Type bt = Variant::Type(int(tk.value)); + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + ConstructorNode *constructor = alloc_node<ConstructorNode>(); + constructor->data_type = bt; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + constructor->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = constructor; + + } break; + case TK_BUILTIN_FUNC: { + //builtin function + + _get_token(tk); + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '('"); + return NULL; + } + + BuiltinFuncNode *bifunc = alloc_node<BuiltinFuncNode>(); + bifunc->func = BuiltinFunc(int(tk.value)); + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + bifunc->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + int expected_args = get_func_argument_count(bifunc->func); + if (bifunc->arguments.size() != expected_args) { + _set_error("Builtin func '" + get_func_name(bifunc->func) + "' expects " + itos(expected_args) + " arguments."); + } + + expr = bifunc; + + } break; + case TK_OP_SUB: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NEGATE; + expression.push_back(e); + continue; + } break; + case TK_OP_NOT: { + + ExpressionNode e; + e.is_op = true; + e.op = Variant::OP_NOT; + expression.push_back(e); + continue; + } break; + + default: { + _set_error("Expected expression."); + return NULL; + } break; + } + + //before going to operators, must check indexing! + + while (true) { + int cofs2 = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + bool done = false; + + switch (tk.type) { + case TK_BRACKET_OPEN: { + //value indexing + + IndexNode *index = alloc_node<IndexNode>(); + index->base = expr; + + ENode *what = _parse_expression(); + if (!what) + return NULL; + + index->index = what; + + _get_token(tk); + if (tk.type != TK_BRACKET_CLOSE) { + _set_error("Expected ']' at end of index."); + return NULL; + } + expr = index; + + } break; + case TK_PERIOD: { + //named indexing or function call + _get_token(tk); + if (tk.type != TK_IDENTIFIER) { + _set_error("Expected identifier after '.'"); + return NULL; + } + + StringName identifier = tk.value; + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_OPEN) { + //function call + CallNode *func_call = alloc_node<CallNode>(); + func_call->method = identifier; + func_call->base = expr; + + while (true) { + + int cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_PARENTHESIS_CLOSE) { + break; + } + str_ofs = cofs; //revert + //parse an expression + ENode *expr = _parse_expression(); + if (!expr) + return NULL; + + func_call->arguments.push_back(expr); + + cofs = str_ofs; + _get_token(tk); + if (tk.type == TK_COMMA) { + //all good + } else if (tk.type == TK_PARENTHESIS_CLOSE) { + str_ofs = cofs; + } else { + _set_error("Expected ',' or ')'"); + } + } + + expr = func_call; + } else { + //named indexing + str_ofs = cofs; + + NamedIndexNode *index = alloc_node<NamedIndexNode>(); + index->base = expr; + index->name = identifier; + expr = index; + } + + } break; + default: { + str_ofs = cofs2; + done = true; + } break; + } + + if (done) + break; + } + + //push expression + { + ExpressionNode e; + e.is_op = false; + e.node = expr; + expression.push_back(e); + } + + //ok finally look for an operator + + int cofs = str_ofs; + _get_token(tk); + if (error_set) + return NULL; + + Variant::Operator op = Variant::OP_MAX; + + switch (tk.type) { + case TK_OP_IN: op = Variant::OP_IN; break; + case TK_OP_EQUAL: op = Variant::OP_EQUAL; break; + case TK_OP_NOT_EQUAL: op = Variant::OP_NOT_EQUAL; break; + case TK_OP_LESS: op = Variant::OP_LESS; break; + case TK_OP_LESS_EQUAL: op = Variant::OP_LESS_EQUAL; break; + case TK_OP_GREATER: op = Variant::OP_GREATER; break; + case TK_OP_GREATER_EQUAL: op = Variant::OP_GREATER_EQUAL; break; + case TK_OP_AND: op = Variant::OP_AND; break; + case TK_OP_OR: op = Variant::OP_OR; break; + case TK_OP_NOT: op = Variant::OP_NOT; break; + case TK_OP_ADD: op = Variant::OP_ADD; break; + case TK_OP_SUB: op = Variant::OP_SUBTRACT; break; + case TK_OP_MUL: op = Variant::OP_MULTIPLY; break; + case TK_OP_DIV: op = Variant::OP_DIVIDE; break; + case TK_OP_MOD: op = Variant::OP_MODULE; break; + case TK_OP_SHIFT_LEFT: op = Variant::OP_SHIFT_LEFT; break; + case TK_OP_SHIFT_RIGHT: op = Variant::OP_SHIFT_RIGHT; break; + case TK_OP_BIT_AND: op = Variant::OP_BIT_AND; break; + case TK_OP_BIT_OR: op = Variant::OP_BIT_OR; break; + case TK_OP_BIT_XOR: op = Variant::OP_BIT_XOR; break; + case TK_OP_BIT_INVERT: op = Variant::OP_BIT_NEGATE; break; + default: {}; + } + + if (op == Variant::OP_MAX) { //stop appending stuff + str_ofs = cofs; + break; + } + + //push operator and go on + { + ExpressionNode e; + e.is_op = true; + e.op = op; + expression.push_back(e); + } + } + + /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */ + + while (expression.size() > 1) { + + int next_op = -1; + int min_priority = 0xFFFFF; + bool is_unary = false; + + for (int i = 0; i < expression.size(); i++) { + + if (!expression[i].is_op) { + + continue; + } + + int priority; + + bool unary = false; + + switch (expression[i].op) { + + case Variant::OP_BIT_NEGATE: + priority = 0; + unary = true; + break; + case Variant::OP_NEGATE: + priority = 1; + unary = true; + break; + + case Variant::OP_MULTIPLY: priority = 2; break; + case Variant::OP_DIVIDE: priority = 2; break; + case Variant::OP_MODULE: priority = 2; break; + + case Variant::OP_ADD: priority = 3; break; + case Variant::OP_SUBTRACT: priority = 3; break; + + case Variant::OP_SHIFT_LEFT: priority = 4; break; + case Variant::OP_SHIFT_RIGHT: priority = 4; break; + + case Variant::OP_BIT_AND: priority = 5; break; + case Variant::OP_BIT_XOR: priority = 6; break; + case Variant::OP_BIT_OR: priority = 7; break; + + case Variant::OP_LESS: priority = 8; break; + case Variant::OP_LESS_EQUAL: priority = 8; break; + case Variant::OP_GREATER: priority = 8; break; + case Variant::OP_GREATER_EQUAL: priority = 8; break; + + case Variant::OP_EQUAL: priority = 8; break; + case Variant::OP_NOT_EQUAL: priority = 8; break; + + case Variant::OP_IN: priority = 10; break; + + case Variant::OP_NOT: + priority = 11; + unary = true; + break; + case Variant::OP_AND: priority = 12; break; + case Variant::OP_OR: priority = 13; break; + + default: { + _set_error("Parser bug, invalid operator in expression: " + itos(expression[i].op)); + return NULL; + } + } + + if (priority < min_priority) { + // < is used for left to right (default) + // <= is used for right to left + + next_op = i; + min_priority = priority; + is_unary = unary; + } + } + + if (next_op == -1) { + + _set_error("Yet another parser bug...."); + ERR_FAIL_COND_V(next_op == -1, NULL); + } + + // OK! create operator.. + if (is_unary) { + + int expr_pos = next_op; + while (expression[expr_pos].is_op) { + + expr_pos++; + if (expr_pos == expression.size()) { + //can happen.. + _set_error("Unexpected end of expression..."); + return NULL; + } + } + + //consecutively do unary opeators + for (int i = expr_pos - 1; i >= next_op; i--) { + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[i].op; + op->nodes[0] = expression[i + 1].node; + op->nodes[1] = NULL; + expression.write[i].is_op = false; + expression.write[i].node = op; + expression.remove(i + 1); + } + + } else { + + if (next_op < 1 || next_op >= (expression.size() - 1)) { + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + OperatorNode *op = alloc_node<OperatorNode>(); + op->op = expression[next_op].op; + + if (expression[next_op - 1].is_op) { + + _set_error("Parser bug..."); + ERR_FAIL_V(NULL); + } + + if (expression[next_op + 1].is_op) { + // this is not invalid and can really appear + // but it becomes invalid anyway because no binary op + // can be followed by a unary op in a valid combination, + // due to how precedence works, unaries will always disappear first + + _set_error("Unexpected two consecutive operators."); + return NULL; + } + + op->nodes[0] = expression[next_op - 1].node; //expression goes as left + op->nodes[1] = expression[next_op + 1].node; //next expression goes as right + + //replace all 3 nodes by this operator and make it an expression + expression.write[next_op - 1].node = op; + expression.remove(next_op); + expression.remove(next_op); + } + } + + return expression[0].node; +} + +bool Expression::_compile_expression() { + + if (!expression_dirty) + return error_set; + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return true; + } + + expression_dirty = false; + return false; +} + +bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) { + + switch (p_node->type) { + case Expression::ENode::TYPE_INPUT: { + + const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node); + if (in->index < 0 || in->index >= p_inputs.size()) { + r_error_str = vformat(RTR("Invalid input %i (not passed) in expression"), in->index); + return true; + } + r_ret = p_inputs[in->index]; + } break; + case Expression::ENode::TYPE_CONSTANT: { + + const Expression::ConstantNode *c = static_cast<const Expression::ConstantNode *>(p_node); + r_ret = c->value; + + } break; + case Expression::ENode::TYPE_SELF: { + + if (!p_instance) { + r_error_str = RTR("self can't be used because instance is null (not passed)"); + return true; + } + r_ret = p_instance; + } break; + case Expression::ENode::TYPE_OPERATOR: { + + const Expression::OperatorNode *op = static_cast<const Expression::OperatorNode *>(p_node); + + Variant a; + bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str); + if (ret) + return true; + + Variant b; + + if (op->nodes[1]) { + bool ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str); + if (ret) + return true; + } + + bool valid = true; + Variant::evaluate(op->op, a, b, r_ret, valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid operands to operator %s, %s and %s."), Variant::get_operator_name(op->op), Variant::get_type_name(a.get_type()), Variant::get_type_name(b.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_INDEX: { + + const Expression::IndexNode *index = static_cast<const Expression::IndexNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + Variant idx; + + ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get(idx, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid index of type %s for base type %s"), Variant::get_type_name(idx.get_type()), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_NAMED_INDEX: { + + const Expression::NamedIndexNode *index = static_cast<const Expression::NamedIndexNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str); + if (ret) + return true; + + bool valid; + r_ret = base.get_named(index->name, &valid); + if (!valid) { + r_error_str = vformat(RTR("Invalid named index '%s' for base type "), String(index->name), Variant::get_type_name(base.get_type())); + return true; + } + + } break; + case Expression::ENode::TYPE_ARRAY: { + const Expression::ArrayNode *array = static_cast<const Expression::ArrayNode *>(p_node); + + Array arr; + arr.resize(array->array.size()); + for (int i = 0; i < array->array.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str); + + if (ret) + return true; + arr[i] = value; + } + + r_ret = arr; + + } break; + case Expression::ENode::TYPE_DICTIONARY: { + const Expression::DictionaryNode *dictionary = static_cast<const Expression::DictionaryNode *>(p_node); + + Dictionary d; + for (int i = 0; i < dictionary->dict.size(); i += 2) { + + Variant key; + bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str); + + if (ret) + return true; + + Variant value; + ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str); + if (ret) + return true; + + d[key] = value; + } + + r_ret = d; + } break; + case Expression::ENode::TYPE_CONSTRUCTOR: { + + const Expression::ConstructorNode *constructor = static_cast<const Expression::ConstructorNode *>(p_node); + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(constructor->arguments.size()); + argp.resize(constructor->arguments.size()); + + for (int i = 0; i < constructor->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("Invalid arguments to construct '%s'"), Variant::get_type_name(constructor->data_type)); + return true; + } + + } break; + case Expression::ENode::TYPE_BUILTIN_FUNC: { + + const Expression::BuiltinFuncNode *bifunc = static_cast<const Expression::BuiltinFuncNode *>(p_node); + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(bifunc->arguments.size()); + argp.resize(bifunc->arguments.size()); + + for (int i = 0; i < bifunc->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str); + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + exec_func(bifunc->func, (const Variant **)argp.ptr(), &r_ret, ce, r_error_str); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = "Builtin Call Failed. " + r_error_str; + return true; + } + + } break; + case Expression::ENode::TYPE_CALL: { + + const Expression::CallNode *call = static_cast<const Expression::CallNode *>(p_node); + + Variant base; + bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str); + + if (ret) + return true; + + Vector<Variant> arr; + Vector<const Variant *> argp; + arr.resize(call->arguments.size()); + argp.resize(call->arguments.size()); + + for (int i = 0; i < call->arguments.size(); i++) { + + Variant value; + bool ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str); + + if (ret) + return true; + arr.write[i] = value; + argp.write[i] = &arr[i]; + } + + Variant::CallError ce; + r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce); + + if (ce.error != Variant::CallError::CALL_OK) { + r_error_str = vformat(RTR("On call to '%s':"), String(call->method)); + return true; + } + + } break; + } + return false; +} + +Error Expression::parse(const String &p_expression, const Vector<String> &p_input_names) { + + if (nodes) { + memdelete(nodes); + nodes = NULL; + root = NULL; + } + + error_str = String(); + error_set = false; + str_ofs = 0; + input_names = p_input_names; + + expression = p_expression; + root = _parse_expression(); + + if (error_set) { + root = NULL; + if (nodes) { + memdelete(nodes); + } + nodes = NULL; + return ERR_INVALID_PARAMETER; + } + + return OK; +} + +Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) { + + execution_error = false; + Variant output; + String error_txt; + bool err = _execute(p_inputs, p_base, root, output, error_txt); + if (err) { + execution_error = true; + error_str = error_txt; + if (p_show_error) { + ERR_EXPLAIN(error_str); + ERR_FAIL_V(Variant()); + } + } + + return output; +} + +bool Expression::has_execute_failed() const { + return execution_error; +} + +String Expression::get_error_text() const { + return error_str; +} + +void Expression::_bind_methods() { + + ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector<String>())); + ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed); + ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text); +} + +Expression::Expression() { + output_type = Variant::NIL; + error_set = true; + root = NULL; + nodes = NULL; + sequenced = false; + execution_error = false; +} + +Expression::~Expression() { + + if (nodes) { + memdelete(nodes); + } +} diff --git a/core/math/expression.h b/core/math/expression.h new file mode 100644 index 0000000000..7a7639cf0b --- /dev/null +++ b/core/math/expression.h @@ -0,0 +1,325 @@ +#ifndef EXPRESSION_H +#define EXPRESSION_H + +#include "core/reference.h" + +class Expression : public Reference { + GDCLASS(Expression, Reference) +public: + enum BuiltinFunc { + MATH_SIN, + MATH_COS, + MATH_TAN, + MATH_SINH, + MATH_COSH, + MATH_TANH, + MATH_ASIN, + MATH_ACOS, + MATH_ATAN, + MATH_ATAN2, + MATH_SQRT, + MATH_FMOD, + MATH_FPOSMOD, + MATH_FLOOR, + MATH_CEIL, + MATH_ROUND, + MATH_ABS, + MATH_SIGN, + MATH_POW, + MATH_LOG, + MATH_EXP, + MATH_ISNAN, + MATH_ISINF, + MATH_EASE, + MATH_DECIMALS, + MATH_STEPIFY, + MATH_LERP, + MATH_INVERSE_LERP, + MATH_RANGE_LERP, + MATH_DECTIME, + MATH_RANDOMIZE, + MATH_RAND, + MATH_RANDF, + MATH_RANDOM, + MATH_SEED, + MATH_RANDSEED, + MATH_DEG2RAD, + MATH_RAD2DEG, + MATH_LINEAR2DB, + MATH_DB2LINEAR, + MATH_POLAR2CARTESIAN, + MATH_CARTESIAN2POLAR, + MATH_WRAP, + MATH_WRAPF, + LOGIC_MAX, + LOGIC_MIN, + LOGIC_CLAMP, + LOGIC_NEAREST_PO2, + OBJ_WEAKREF, + FUNC_FUNCREF, + TYPE_CONVERT, + TYPE_OF, + TYPE_EXISTS, + TEXT_CHAR, + TEXT_STR, + TEXT_PRINT, + TEXT_PRINTERR, + TEXT_PRINTRAW, + VAR_TO_STR, + STR_TO_VAR, + VAR_TO_BYTES, + BYTES_TO_VAR, + COLORN, + FUNC_MAX + }; + + static int get_func_argument_count(BuiltinFunc p_func); + static String get_func_name(BuiltinFunc p_func); + static void exec_func(BuiltinFunc p_func, const Variant **p_inputs, Variant *r_return, Variant::CallError &r_error, String &r_error_str); + static BuiltinFunc find_function(const String &p_string); + +private: + static const char *func_name[FUNC_MAX]; + + struct Input { + + Variant::Type type; + String name; + + Input() { type = Variant::NIL; } + }; + + Vector<Input> inputs; + Variant::Type output_type; + + String expression; + + bool sequenced; + int str_ofs; + bool expression_dirty; + + bool _compile_expression(); + + enum TokenType { + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_PARENTHESIS_OPEN, + TK_PARENTHESIS_CLOSE, + TK_IDENTIFIER, + TK_BUILTIN_FUNC, + TK_SELF, + TK_CONSTANT, + TK_BASIC_TYPE, + TK_COLON, + TK_COMMA, + TK_PERIOD, + TK_OP_IN, + TK_OP_EQUAL, + TK_OP_NOT_EQUAL, + TK_OP_LESS, + TK_OP_LESS_EQUAL, + TK_OP_GREATER, + TK_OP_GREATER_EQUAL, + TK_OP_AND, + TK_OP_OR, + TK_OP_NOT, + TK_OP_ADD, + TK_OP_SUB, + TK_OP_MUL, + TK_OP_DIV, + TK_OP_MOD, + TK_OP_SHIFT_LEFT, + TK_OP_SHIFT_RIGHT, + TK_OP_BIT_AND, + TK_OP_BIT_OR, + TK_OP_BIT_XOR, + TK_OP_BIT_INVERT, + TK_INPUT, + TK_EOF, + TK_ERROR, + TK_MAX + }; + + static const char *token_name[TK_MAX]; + struct Token { + + TokenType type; + Variant value; + }; + + void _set_error(const String &p_err) { + if (error_set) + return; + error_str = p_err; + error_set = true; + } + + Error _get_token(Token &r_token); + + String error_str; + bool error_set; + + struct ENode { + + enum Type { + TYPE_INPUT, + TYPE_CONSTANT, + TYPE_SELF, + TYPE_OPERATOR, + TYPE_INDEX, + TYPE_NAMED_INDEX, + TYPE_ARRAY, + TYPE_DICTIONARY, + TYPE_CONSTRUCTOR, + TYPE_BUILTIN_FUNC, + TYPE_CALL + }; + + ENode *next; + + Type type; + + ENode() { next = NULL; } + virtual ~ENode() { + if (next) { + memdelete(next); + } + } + }; + + struct ExpressionNode { + + bool is_op; + union { + Variant::Operator op; + ENode *node; + }; + }; + + ENode *_parse_expression(); + + struct InputNode : public ENode { + + int index; + InputNode() { + type = TYPE_INPUT; + } + }; + + struct ConstantNode : public ENode { + + Variant value; + ConstantNode() { + type = TYPE_CONSTANT; + } + }; + + struct OperatorNode : public ENode { + + Variant::Operator op; + + ENode *nodes[2]; + + OperatorNode() { + type = TYPE_OPERATOR; + } + }; + + struct SelfNode : public ENode { + + SelfNode() { + type = TYPE_SELF; + } + }; + + struct IndexNode : public ENode { + ENode *base; + ENode *index; + + IndexNode() { + type = TYPE_INDEX; + } + }; + + struct NamedIndexNode : public ENode { + ENode *base; + StringName name; + + NamedIndexNode() { + type = TYPE_NAMED_INDEX; + } + }; + + struct ConstructorNode : public ENode { + Variant::Type data_type; + Vector<ENode *> arguments; + + ConstructorNode() { + type = TYPE_CONSTRUCTOR; + } + }; + + struct CallNode : public ENode { + ENode *base; + StringName method; + Vector<ENode *> arguments; + + CallNode() { + type = TYPE_CALL; + } + }; + + struct ArrayNode : public ENode { + Vector<ENode *> array; + ArrayNode() { + type = TYPE_ARRAY; + } + }; + + struct DictionaryNode : public ENode { + Vector<ENode *> dict; + DictionaryNode() { + type = TYPE_DICTIONARY; + } + }; + + struct BuiltinFuncNode : public ENode { + BuiltinFunc func; + Vector<ENode *> arguments; + BuiltinFuncNode() { + type = TYPE_BUILTIN_FUNC; + } + }; + + template <class T> + T *alloc_node() { + T *node = memnew(T); + node->next = nodes; + nodes = node; + return node; + } + + ENode *root; + ENode *nodes; + + Vector<String> input_names; + + bool execution_error; + bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str); + +protected: + static void _bind_methods(); + +public: + Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>()); + Variant execute(Array p_inputs, Object *p_base = NULL, bool p_show_error = true); + bool has_execute_failed() const; + String get_error_text() const; + + Expression(); + ~Expression(); +}; + +#endif // EXPRESSION_H diff --git a/core/math/geometry.cpp b/core/math/geometry.cpp index 24f077a4ca..7ab28daf20 100644 --- a/core/math/geometry.cpp +++ b/core/math/geometry.cpp @@ -56,7 +56,7 @@ void Geometry::MeshData::optimize_vertices() { vtx_remap[idx] = ni; } - faces[i].indices[j] = vtx_remap[idx]; + faces.write[i].indices.write[j] = vtx_remap[idx]; } } @@ -74,8 +74,8 @@ void Geometry::MeshData::optimize_vertices() { vtx_remap[b] = ni; } - edges[i].a = vtx_remap[a]; - edges[i].b = vtx_remap[b]; + edges.write[i].a = vtx_remap[a]; + edges.write[i].b = vtx_remap[b]; } Vector<Vector3> new_vertices; @@ -84,7 +84,7 @@ void Geometry::MeshData::optimize_vertices() { for (int i = 0; i < vertices.size(); i++) { if (vtx_remap.has(i)) - new_vertices[vtx_remap[i]] = vertices[i]; + new_vertices.write[vtx_remap[i]] = vertices[i]; } vertices = new_vertices; } @@ -1014,8 +1014,8 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu Vector<_AtlasWorkRect> wrects; wrects.resize(p_rects.size()); for (int i = 0; i < p_rects.size(); i++) { - wrects[i].s = p_rects[i]; - wrects[i].idx = i; + wrects.write[i].s = p_rects[i]; + wrects.write[i].idx = i; } wrects.sort(); int widest = wrects[0].s.width; @@ -1033,7 +1033,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu Vector<int> hmax; hmax.resize(w); for (int j = 0; j < w; j++) - hmax[j] = 0; + hmax.write[j] = 0; //place them int ofs = 0; @@ -1052,8 +1052,8 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu from_y = hmax[ofs + k]; } - wrects[j].p.x = ofs; - wrects[j].p.y = from_y; + wrects.write[j].p.x = ofs; + wrects.write[j].p.y = from_y; int end_h = from_y + wrects[j].s.height; int end_w = ofs + wrects[j].s.width; if (ofs == 0) @@ -1061,7 +1061,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu for (int k = 0; k < wrects[j].s.width; k++) { - hmax[ofs + k] = end_h; + hmax.write[ofs + k] = end_h; } if (end_h > max_h) @@ -1101,7 +1101,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu for (int i = 0; i < p_rects.size(); i++) { - r_result[results[best].result[i].idx] = results[best].result[i].p; + r_result.write[results[best].result[i].idx] = results[best].result[i].p; } r_size = Size2(results[best].max_w, results[best].max_h); diff --git a/core/math/geometry.h b/core/math/geometry.h index be998aef0b..186a05fb37 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -890,14 +890,14 @@ public: for (int i = 0; i < n; ++i) { while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; - H[k++] = P[i]; + H.write[k++] = P[i]; } // Build upper hull for (int i = n - 2, t = k + 1; i >= 0; i--) { while (k >= t && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0) k--; - H[k++] = P[i]; + H.write[k++] = P[i]; } H.resize(k); diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp index 2371f49561..7db41756ed 100644 --- a/core/math/matrix3.cpp +++ b/core/math/matrix3.cpp @@ -242,18 +242,11 @@ void Basis::scale_local(const Vector3 &p_scale) { Basis Basis::scaled_local(const Vector3 &p_scale) const { Basis b; - b.set_scale(p_scale); + b.set_diagonal(p_scale); return (*this) * b; } -void Basis::set_scale(const Vector3 &p_scale) { - - set_axis(0, get_axis(0).normalized() * p_scale.x); - set_axis(1, get_axis(1).normalized() * p_scale.y); - set_axis(2, get_axis(2).normalized() * p_scale.z); -} - Vector3 Basis::get_scale_abs() const { return Vector3( diff --git a/core/math/matrix3.h b/core/math/matrix3.h index cd1b51baa6..9ff1a97dc9 100644 --- a/core/math/matrix3.h +++ b/core/math/matrix3.h @@ -112,7 +112,6 @@ public: void scale_local(const Vector3 &p_scale); Basis scaled_local(const Vector3 &p_scale) const; - void set_scale(const Vector3 &p_scale); Vector3 get_scale() const; Vector3 get_scale_abs() const; Vector3 get_scale_local() const; @@ -232,10 +231,13 @@ public: operator Quat() const { return get_quat(); } Basis(const Quat &p_quat) { set_quat(p_quat); }; + Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } + Basis(const Vector3 &p_euler) { set_euler(p_euler); } + Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); } + Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); } Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); } - Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); } _FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) { elements[0] = row0; diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index fc90417413..cb923d264e 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -61,10 +61,10 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Vector3 sp = p_points[i].snapped(Vector3(0.0001, 0.0001, 0.0001)); if (valid_cache.has(sp)) { - valid_points[i] = false; + valid_points.write[i] = false; //print_line("INVALIDATED: "+itos(i)); } else { - valid_points[i] = true; + valid_points.write[i] = true; valid_cache.insert(sp); } } @@ -452,7 +452,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me int idx = 0; for (List<Geometry::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { - r_mesh.faces[idx++] = E->get(); + r_mesh.faces.write[idx++] = E->get(); } r_mesh.edges.resize(ret_edges.size()); idx = 0; @@ -461,7 +461,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Geometry::MeshData::Edge e; e.a = E->key().vertices[0]; e.b = E->key().vertices[1]; - r_mesh.edges[idx++] = e; + r_mesh.edges.write[idx++] = e; } r_mesh.vertices = p_points; diff --git a/core/math/transform.cpp b/core/math/transform.cpp index d1e190f4b9..976e0f174e 100644 --- a/core/math/transform.cpp +++ b/core/math/transform.cpp @@ -127,12 +127,11 @@ Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c) Quat dst_rot = p_transform.basis.get_rotation_quat(); Vector3 dst_loc = p_transform.origin; - Transform dst; //this could be made faster by using a single function in Basis.. - dst.basis = src_rot.slerp(dst_rot, p_c).normalized(); - dst.basis.set_scale(src_scale.linear_interpolate(dst_scale, p_c)); - dst.origin = src_loc.linear_interpolate(dst_loc, p_c); + Transform interp; + interp.basis.set_quat_scale(src_rot.slerp(dst_rot, p_c).normalized(), src_scale.linear_interpolate(dst_scale, p_c)); + interp.origin = src_loc.linear_interpolate(dst_loc, p_c); - return dst; + return interp; } void Transform::scale(const Vector3 &p_scale) { diff --git a/core/math/triangulate.cpp b/core/math/triangulate.cpp index 563ba7268f..0edc0ea039 100644 --- a/core/math/triangulate.cpp +++ b/core/math/triangulate.cpp @@ -128,10 +128,10 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul if (0.0 < get_area(contour)) for (int v = 0; v < n; v++) - V[v] = v; + V.write[v] = v; else for (int v = 0; v < n; v++) - V[v] = (n - 1) - v; + V.write[v] = (n - 1) - v; bool relaxed = false; @@ -182,7 +182,7 @@ bool Triangulate::triangulate(const Vector<Vector2> &contour, Vector<int> &resul /* remove v from remaining polygon */ for (s = v, t = v + 1; t < nv; s++, t++) - V[s] = V[t]; + V.write[s] = V[t]; nv--; diff --git a/core/method_bind.h b/core/method_bind.h index 41b500c401..7ee687ee40 100644 --- a/core/method_bind.h +++ b/core/method_bind.h @@ -354,7 +354,7 @@ public: for (int i = 0; i < p_info.arguments.size(); i++) { at[i + 1] = p_info.arguments[i].type; - names[i] = p_info.arguments[i].name; + names.write[i] = p_info.arguments[i].name; } set_argument_names(names); diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h index 677e8e1fb2..2f6dcb3178 100644 --- a/core/method_ptrcall.h +++ b/core/method_ptrcall.h @@ -180,7 +180,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -207,7 +207,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -225,7 +225,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -252,7 +252,7 @@ struct PtrToArg<const T *> { { \ PoolVector<m_type>::Read r = dvs->read(); \ for (int i = 0; i < len; i++) { \ - ret[i] = r[i]; \ + ret.write[i] = r[i]; \ } \ } \ return ret; \ @@ -277,7 +277,7 @@ MAKE_VECARG_ALT(String, StringName); int len = arr->size(); \ ret.resize(len); \ for (int i = 0; i < len; i++) { \ - ret[i] = (*arr)[i]; \ + ret.write[i] = (*arr)[i]; \ } \ return ret; \ } \ @@ -298,7 +298,7 @@ MAKE_VECARG_ALT(String, StringName); int len = arr->size(); \ ret.resize(len); \ for (int i = 0; i < len; i++) { \ - ret[i] = (*arr)[i]; \ + ret.write[i] = (*arr)[i]; \ } \ return ret; \ } \ diff --git a/core/node_path.cpp b/core/node_path.cpp index 487d5ee8c6..7d4116fa1e 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -427,7 +427,7 @@ NodePath::NodePath(const String &p_path) { String name = path.substr(from, i - from); ERR_FAIL_INDEX(slice, data->path.size()); - data->path[slice++] = name; + data->path.write[slice++] = name; } from = i + 1; last_is_slash = true; diff --git a/core/object.cpp b/core/object.cpp index d86c60a3b8..a0c64feb09 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -818,8 +818,8 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) { argptrs.resize(p_args.size()); for (int i = 0; i < p_args.size(); i++) { - args[i] = p_args[i]; - argptrs[i] = &args[i]; + args.write[i] = p_args[i]; + argptrs.write[i] = &args[i]; } Variant::CallError ce; @@ -1182,10 +1182,10 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int bind_mem.resize(p_argcount + c.binds.size()); for (int j = 0; j < p_argcount; j++) { - bind_mem[j] = p_args[j]; + bind_mem.write[j] = p_args[j]; } for (int j = 0; j < c.binds.size(); j++) { - bind_mem[p_argcount + j] = &c.binds[j]; + bind_mem.write[p_argcount + j] = &c.binds[j]; } args = (const Variant **)bind_mem.ptr(); @@ -1209,7 +1209,15 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int } } - if (c.flags & CONNECT_ONESHOT) { + bool disconnect = c.flags & CONNECT_ONESHOT; +#ifdef TOOLS_ENABLED + if (disconnect && (c.flags & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) { + //this signal was connected from the editor, and is being edited. just dont disconnect for now + disconnect = false; + } +#endif + if (disconnect) { + _ObjectSignalDisconnectData dd; dd.signal = p_name; dd.target = target; diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 033b4b12b9..59f07c03e7 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -262,15 +262,14 @@ String FileAccess::get_token() const { while (!eof_reached()) { if (c <= ' ') { - if (!token.empty()) + if (token.length()) break; } else { - token.push_back(c); + token += c; } c = get_8(); } - token.push_back(0); return String::utf8(token.get_data()); } @@ -293,7 +292,7 @@ class CharBuffer { for (int i = 0; i < written; i++) { - vector[i] = stack_buffer[i]; + vector.write[i] = stack_buffer[i]; } } diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index ca6446d015..12c6ef7d3b 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -949,6 +949,14 @@ bool InputEventAction::is_pressed() const { return pressed; } +bool InputEventAction::shortcut_match(const Ref<InputEvent> &p_event) const { + Ref<InputEventKey> event = p_event; + if (event.is_null()) + return false; + + return event->is_action(action); +} + bool InputEventAction::is_action(const StringName &p_action) const { return action == p_action; @@ -1080,3 +1088,122 @@ InputEventPanGesture::InputEventPanGesture() { delta = Vector2(0, 0); } +///////////////////////////// + +void InputEventMIDI::set_channel(const int p_channel) { + + channel = p_channel; +} + +int InputEventMIDI::get_channel() const { + return channel; +} + +void InputEventMIDI::set_message(const int p_message) { + + message = p_message; +} + +int InputEventMIDI::get_message() const { + return message; +} + +void InputEventMIDI::set_pitch(const int p_pitch) { + + pitch = p_pitch; +} + +int InputEventMIDI::get_pitch() const { + return pitch; +} + +void InputEventMIDI::set_velocity(const int p_velocity) { + + velocity = p_velocity; +} + +int InputEventMIDI::get_velocity() const { + return velocity; +} + +void InputEventMIDI::set_instrument(const int p_instrument) { + + instrument = p_instrument; +} + +int InputEventMIDI::get_instrument() const { + return instrument; +} + +void InputEventMIDI::set_pressure(const int p_pressure) { + + pressure = p_pressure; +} + +int InputEventMIDI::get_pressure() const { + return pressure; +} + +void InputEventMIDI::set_controller_number(const int p_controller_number) { + + controller_number = p_controller_number; +} + +int InputEventMIDI::get_controller_number() const { + return controller_number; +} + +void InputEventMIDI::set_controller_value(const int p_controller_value) { + + controller_value = p_controller_value; +} + +int InputEventMIDI::get_controller_value() const { + return controller_value; +} + +String InputEventMIDI::as_text() const { + + return "InputEventMIDI : channel=(" + itos(get_channel()) + "), message=(" + itos(get_message()) + ")"; +} + +void InputEventMIDI::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_channel", "channel"), &InputEventMIDI::set_channel); + ClassDB::bind_method(D_METHOD("get_channel"), &InputEventMIDI::get_channel); + ClassDB::bind_method(D_METHOD("set_message", "message"), &InputEventMIDI::set_message); + ClassDB::bind_method(D_METHOD("get_message"), &InputEventMIDI::get_message); + ClassDB::bind_method(D_METHOD("set_pitch", "pitch"), &InputEventMIDI::set_pitch); + ClassDB::bind_method(D_METHOD("get_pitch"), &InputEventMIDI::get_pitch); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventMIDI::set_velocity); + ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventMIDI::get_velocity); + ClassDB::bind_method(D_METHOD("set_instrument", "instrument"), &InputEventMIDI::set_instrument); + ClassDB::bind_method(D_METHOD("get_instrument"), &InputEventMIDI::get_instrument); + ClassDB::bind_method(D_METHOD("set_pressure", "pressure"), &InputEventMIDI::set_pressure); + ClassDB::bind_method(D_METHOD("get_pressure"), &InputEventMIDI::get_pressure); + ClassDB::bind_method(D_METHOD("set_controller_number", "controller_number"), &InputEventMIDI::set_controller_number); + ClassDB::bind_method(D_METHOD("get_controller_number"), &InputEventMIDI::get_controller_number); + ClassDB::bind_method(D_METHOD("set_controller_value", "controller_value"), &InputEventMIDI::set_controller_value); + ClassDB::bind_method(D_METHOD("get_controller_value"), &InputEventMIDI::get_controller_value); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "channel"), "set_channel", "get_channel"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "message"), "set_message", "get_message"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "pitch"), "set_pitch", "get_pitch"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "velocity"), "set_velocity", "get_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "instrument"), "set_instrument", "get_instrument"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "pressure"), "set_pressure", "get_pressure"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value"); +} + +InputEventMIDI::InputEventMIDI() { + + channel = 0; + message = 0; + pitch = 0; + velocity = 0; + instrument = 0; + pressure = 0; + controller_number = 0; + controller_value = 0; +} diff --git a/core/os/input_event.h b/core/os/input_event.h index bd1a85ce29..07df81488b 100644 --- a/core/os/input_event.h +++ b/core/os/input_event.h @@ -140,6 +140,16 @@ enum JoystickList { JOY_ANALOG_R2 = JOY_AXIS_7, }; +enum MidiMessageList { + MIDI_MESSAGE_NOTE_OFF = 0x8, + MIDI_MESSAGE_NOTE_ON = 0x9, + MIDI_MESSAGE_AFTERTOUCH = 0xA, + MIDI_MESSAGE_CONTROL_CHANGE = 0xB, + MIDI_MESSAGE_PROGRAM_CHANGE = 0xC, + MIDI_MESSAGE_CHANNEL_PRESSURE = 0xD, + MIDI_MESSAGE_PITCH_BEND = 0xE, +}; + /** * Input Modifier Status * for keyboard/mouse events. @@ -470,6 +480,7 @@ public: virtual bool is_action(const StringName &p_action) const; + virtual bool shortcut_match(const Ref<InputEvent> &p_event) const; virtual bool is_action_type() const { return true; } virtual String as_text() const; @@ -525,4 +536,50 @@ public: InputEventPanGesture(); }; + +class InputEventMIDI : public InputEvent { + GDCLASS(InputEventMIDI, InputEvent) + + int channel; + int message; + int pitch; + int velocity; + int instrument; + int pressure; + int controller_number; + int controller_value; + +protected: + static void _bind_methods(); + +public: + void set_channel(const int p_channel); + int get_channel() const; + + void set_message(const int p_message); + int get_message() const; + + void set_pitch(const int p_pitch); + int get_pitch() const; + + void set_velocity(const int p_velocity); + int get_velocity() const; + + void set_instrument(const int p_instrument); + int get_instrument() const; + + void set_pressure(const int p_pressure); + int get_pressure() const; + + void set_controller_number(const int p_controller_number); + int get_controller_number() const; + + void set_controller_value(const int p_controller_value); + int get_controller_value() const; + + virtual String as_text() const; + + InputEventMIDI(); +}; + #endif diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp new file mode 100644 index 0000000000..7b4f84473c --- /dev/null +++ b/core/os/midi_driver.cpp @@ -0,0 +1,107 @@ +/*************************************************************************/ +/* midi_driver.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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 "midi_driver.h" + +#include "main/input_default.h" +#include "os/os.h" + +MIDIDriver *MIDIDriver::singleton = NULL; +MIDIDriver *MIDIDriver::get_singleton() { + + return singleton; +} + +void MIDIDriver::set_singleton() { + + singleton = this; +} + +void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) { + + Ref<InputEventMIDI> event; + event.instance(); + + if (length >= 1) { + event->set_channel(data[0] & 0xF); + event->set_message(data[0] >> 4); + } + + switch (event->get_message()) { + case MIDI_MESSAGE_AFTERTOUCH: + if (length >= 3) { + event->set_pitch(data[1]); + event->set_pressure(data[2]); + } + break; + + case MIDI_MESSAGE_CONTROL_CHANGE: + if (length >= 3) { + event->set_controller_number(data[1]); + event->set_controller_value(data[2]); + } + break; + + case MIDI_MESSAGE_NOTE_ON: + case MIDI_MESSAGE_NOTE_OFF: + case MIDI_MESSAGE_PITCH_BEND: + if (length >= 3) { + event->set_pitch(data[1]); + event->set_velocity(data[2]); + } + break; + + case MIDI_MESSAGE_PROGRAM_CHANGE: + if (length >= 2) { + event->set_instrument(data[1]); + } + break; + + case MIDI_MESSAGE_CHANNEL_PRESSURE: + if (length >= 2) { + event->set_pressure(data[1]); + } + break; + } + + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + id->parse_input_event(event); +} + +PoolStringArray MIDIDriver::get_connected_inputs() { + + PoolStringArray list; + return list; +} + +MIDIDriver::MIDIDriver() { + + set_singleton(); +} diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h new file mode 100644 index 0000000000..1a3a67a411 --- /dev/null +++ b/core/os/midi_driver.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* midi_driver.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef MIDI_DRIVER_H +#define MIDI_DRIVER_H + +#include "core/variant.h" +#include "typedefs.h" +/** + * Multi-Platform abstraction for accessing to MIDI. + */ + +class MIDIDriver { + + static MIDIDriver *singleton; + +public: + static MIDIDriver *get_singleton(); + void set_singleton(); + + virtual Error open() = 0; + virtual void close() = 0; + + virtual PoolStringArray get_connected_inputs(); + + static void receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length); + + MIDIDriver(); + virtual ~MIDIDriver() {} +}; + +#endif diff --git a/core/os/os.cpp b/core/os/os.cpp index 89866e4044..97dae05919 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -33,6 +33,7 @@ #include "dir_access.h" #include "input.h" #include "os/file_access.h" +#include "os/midi_driver.h" #include "project_settings.h" #include "servers/audio_server.h" #include "version_generated.gen.h" @@ -576,6 +577,13 @@ bool OS::has_feature(const String &p_feature) { if (p_feature == "release") return true; #endif +#ifdef TOOLS_ENABLED + if (p_feature == "editor") + return true; +#else + if (p_feature == "standalone") + return true; +#endif if (sizeof(void *) == 8 && p_feature == "64") { return true; @@ -672,6 +680,15 @@ List<String> OS::get_restart_on_exit_arguments() const { return restart_commandline; } +PoolStringArray OS::get_connected_midi_inputs() { + + if (MIDIDriver::get_singleton()) + return MIDIDriver::get_singleton()->get_connected_inputs(); + + PoolStringArray list; + return list; +} + OS::OS() { void *volatile stack_bottom; diff --git a/core/os/os.h b/core/os/os.h index 4e0cb003fb..dd783408e8 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -189,6 +189,8 @@ public: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual PoolStringArray get_connected_midi_inputs(); + virtual int get_screen_count() const { return 1; } virtual int get_current_screen() const { return 0; } virtual void set_current_screen(int p_screen) {} diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h index e0fb589767..3ff7db2a44 100644 --- a/core/os/threaded_array_processor.h +++ b/core/os/threaded_array_processor.h @@ -80,7 +80,7 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us threads.resize(OS::get_singleton()->get_processor_count()); for (int i = 0; i < threads.size(); i++) { - threads[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U> >, &data); + threads.write[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U> >, &data); } for (int i = 0; i < threads.size(); i++) { diff --git a/core/packed_data_container.cpp b/core/packed_data_container.cpp index eaccdba9bf..45e060fa4a 100644 --- a/core/packed_data_container.cpp +++ b/core/packed_data_container.cpp @@ -251,7 +251,7 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd int len; encode_variant(p_data, NULL, len); tmpdata.resize(tmpdata.size() + len); - encode_variant(p_data, &tmpdata[pos], len); + encode_variant(p_data, &tmpdata.write[pos], len); return pos; } break; @@ -268,8 +268,8 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd uint32_t pos = tmpdata.size(); int len = d.size(); tmpdata.resize(tmpdata.size() + len * 12 + 8); - encode_uint32(TYPE_DICT, &tmpdata[pos + 0]); - encode_uint32(len, &tmpdata[pos + 4]); + encode_uint32(TYPE_DICT, &tmpdata.write[pos + 0]); + encode_uint32(len, &tmpdata.write[pos + 4]); List<Variant> keys; d.get_key_list(&keys); @@ -288,11 +288,11 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd int idx = 0; for (List<DictKey>::Element *E = sortk.front(); E; E = E->next()) { - encode_uint32(E->get().hash, &tmpdata[pos + 8 + idx * 12 + 0]); + encode_uint32(E->get().hash, &tmpdata.write[pos + 8 + idx * 12 + 0]); uint32_t ofs = _pack(E->get().key, tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + idx * 12 + 4]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 4]); ofs = _pack(d[E->get().key], tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + idx * 12 + 8]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 8]); idx++; } @@ -306,13 +306,13 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd uint32_t pos = tmpdata.size(); int len = a.size(); tmpdata.resize(tmpdata.size() + len * 4 + 8); - encode_uint32(TYPE_ARRAY, &tmpdata[pos + 0]); - encode_uint32(len, &tmpdata[pos + 4]); + encode_uint32(TYPE_ARRAY, &tmpdata.write[pos + 0]); + encode_uint32(len, &tmpdata.write[pos + 4]); for (int i = 0; i < len; i++) { uint32_t ofs = _pack(a[i], tmpdata, string_cache); - encode_uint32(ofs, &tmpdata[pos + 8 + i * 4]); + encode_uint32(ofs, &tmpdata.write[pos + 8 + i * 4]); } return pos; diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 60e8933751..146b4870e8 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -627,7 +627,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(p_custom_features, &buff[0], len); + err = encode_variant(p_custom_features, buff.ptrw(), len); if (err != OK) { memdelete(file); ERR_FAIL_V(err); @@ -664,7 +664,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str Vector<uint8_t> buff; buff.resize(len); - err = encode_variant(value, &buff[0], len); + err = encode_variant(value, buff.ptrw(), len); if (err != OK) memdelete(file); ERR_FAIL_COND_V(err != OK, ERR_INVALID_DATA); @@ -959,7 +959,7 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("application/config/name", ""); GLOBAL_DEF("application/run/main_scene", ""); - custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "tscn,scn,res"); + custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"); GLOBAL_DEF("application/run/disable_stdout", false); GLOBAL_DEF("application/run/disable_stderr", false); GLOBAL_DEF("application/config/use_custom_user_dir", false); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 9bcc2d4530..859015f44b 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -41,6 +41,7 @@ #include "input_map.h" #include "io/config_file.h" #include "io/http_client.h" +#include "io/image_loader.h" #include "io/marshalls.h" #include "io/multiplayer_api.h" #include "io/networked_multiplayer_peer.h" @@ -53,6 +54,7 @@ #include "io/tcp_server.h" #include "io/translation_loader_po.h" #include "math/a_star.h" +#include "math/expression.h" #include "math/triangle_mesh.h" #include "os/input.h" #include "os/main_loop.h" @@ -60,11 +62,14 @@ #include "path_remap.h" #include "project_settings.h" #include "translation.h" + #include "undo_redo.h" static ResourceFormatSaverBinary *resource_saver_binary = NULL; static ResourceFormatLoaderBinary *resource_loader_binary = NULL; static ResourceFormatImporter *resource_format_importer = NULL; +static ResourceFormatLoaderImage *resource_format_image = NULL; + static _ResourceLoader *_resource_loader = NULL; static _ResourceSaver *_resource_saver = NULL; static _OS *_os = NULL; @@ -111,6 +116,9 @@ void register_core_types() { resource_format_importer = memnew(ResourceFormatImporter); ResourceLoader::add_resource_format_loader(resource_format_importer); + resource_format_image = memnew(ResourceFormatLoaderImage); + ResourceLoader::add_resource_format_loader(resource_format_image); + ClassDB::register_class<Object>(); ClassDB::register_virtual_class<Script>(); @@ -209,6 +217,7 @@ void register_core_singletons() { ClassDB::register_virtual_class<Input>(); ClassDB::register_class<InputMap>(); ClassDB::register_class<_JSON>(); + ClassDB::register_class<Expression>(); Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton())); @@ -237,6 +246,8 @@ void unregister_core_types() { memdelete(_geometry); + if (resource_format_image) + memdelete(resource_format_image); if (resource_saver_binary) memdelete(resource_saver_binary); if (resource_loader_binary) diff --git a/core/ring_buffer.h b/core/ring_buffer.h index de4757612a..00628a4ab3 100644 --- a/core/ring_buffer.h +++ b/core/ring_buffer.h @@ -137,7 +137,7 @@ public: Error write(const T &p_v) { ERR_FAIL_COND_V(space_left() < 1, FAILED); - data[inc(write_pos, 1)] = p_v; + data.write[inc(write_pos, 1)] = p_v; return OK; }; @@ -156,7 +156,7 @@ public: int total = end - pos; for (int i = 0; i < total; i++) { - data[pos + i] = p_buf[src++]; + data.write[pos + i] = p_buf[src++]; }; to_write -= total; pos = 0; @@ -196,7 +196,7 @@ public: data.resize(1 << p_power); if (old_size < new_size && read_pos > write_pos) { for (int i = 0; i < write_pos; i++) { - data[(old_size + i) & mask] = data[i]; + data.write[(old_size + i) & mask] = data[i]; }; write_pos = (old_size + write_pos) & mask; } else { diff --git a/core/safe_refcount.cpp b/core/safe_refcount.cpp index 3b203f6977..692ff722f3 100644 --- a/core/safe_refcount.cpp +++ b/core/safe_refcount.cpp @@ -57,113 +57,113 @@ return m_val; \ } -_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(register uint32_t *pw){ +_ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(volatile uint32_t *pw){ ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t) } -_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(register uint32_t *pw) { +_ALWAYS_INLINE_ uint32_t _atomic_decrement_impl(volatile uint32_t *pw) { return InterlockedDecrement((LONG volatile *)pw); } -_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(register uint32_t *pw) { +_ALWAYS_INLINE_ uint32_t _atomic_increment_impl(volatile uint32_t *pw) { return InterlockedIncrement((LONG volatile *)pw); } -_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(register uint32_t *pw, register uint32_t val) { +_ALWAYS_INLINE_ uint32_t _atomic_sub_impl(volatile uint32_t *pw, volatile uint32_t val) { return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val; } -_ALWAYS_INLINE_ uint32_t _atomic_add_impl(register uint32_t *pw, register uint32_t val) { +_ALWAYS_INLINE_ uint32_t _atomic_add_impl(volatile uint32_t *pw, volatile uint32_t val) { return InterlockedAdd((LONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(register uint32_t *pw, register uint32_t val){ +_ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(volatile uint32_t *pw, volatile uint32_t val){ ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t) } -_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(register uint64_t *pw){ +_ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(volatile uint64_t *pw){ ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t) } -_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(register uint64_t *pw) { +_ALWAYS_INLINE_ uint64_t _atomic_decrement_impl(volatile uint64_t *pw) { return InterlockedDecrement64((LONGLONG volatile *)pw); } -_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(register uint64_t *pw) { +_ALWAYS_INLINE_ uint64_t _atomic_increment_impl(volatile uint64_t *pw) { return InterlockedIncrement64((LONGLONG volatile *)pw); } -_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(register uint64_t *pw, register uint64_t val) { +_ALWAYS_INLINE_ uint64_t _atomic_sub_impl(volatile uint64_t *pw, volatile uint64_t val) { return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val; } -_ALWAYS_INLINE_ uint64_t _atomic_add_impl(register uint64_t *pw, register uint64_t val) { +_ALWAYS_INLINE_ uint64_t _atomic_add_impl(volatile uint64_t *pw, volatile uint64_t val) { return InterlockedAdd64((LONGLONG volatile *)pw, val); } -_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(register uint64_t *pw, register uint64_t val){ +_ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(volatile uint64_t *pw, volatile uint64_t val){ ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t) } // The actual advertised functions; they'll call the right implementation -uint32_t atomic_conditional_increment(register uint32_t *pw) { +uint32_t atomic_conditional_increment(volatile uint32_t *pw) { return _atomic_conditional_increment_impl(pw); } -uint32_t atomic_decrement(register uint32_t *pw) { +uint32_t atomic_decrement(volatile uint32_t *pw) { return _atomic_decrement_impl(pw); } -uint32_t atomic_increment(register uint32_t *pw) { +uint32_t atomic_increment(volatile uint32_t *pw) { return _atomic_increment_impl(pw); } -uint32_t atomic_sub(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_sub_impl(pw, val); } -uint32_t atomic_add(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_add_impl(pw, val); } -uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val) { +uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val) { return _atomic_exchange_if_greater_impl(pw, val); } -uint64_t atomic_conditional_increment(register uint64_t *pw) { +uint64_t atomic_conditional_increment(volatile uint64_t *pw) { return _atomic_conditional_increment_impl(pw); } -uint64_t atomic_decrement(register uint64_t *pw) { +uint64_t atomic_decrement(volatile uint64_t *pw) { return _atomic_decrement_impl(pw); } -uint64_t atomic_increment(register uint64_t *pw) { +uint64_t atomic_increment(volatile uint64_t *pw) { return _atomic_increment_impl(pw); } -uint64_t atomic_sub(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_sub_impl(pw, val); } -uint64_t atomic_add(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_add_impl(pw, val); } -uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val) { +uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val) { return _atomic_exchange_if_greater_impl(pw, val); } #endif diff --git a/core/safe_refcount.h b/core/safe_refcount.h index eff209c2db..36bcf5e576 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -44,7 +44,7 @@ /* Bogus implementation unaware of multiprocessing */ template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { if (*pw == 0) return 0; @@ -55,7 +55,7 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { +static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { (*pw)--; @@ -63,7 +63,7 @@ static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { (*pw)++; @@ -71,7 +71,7 @@ static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { (*pw) -= val; @@ -79,7 +79,7 @@ static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { (*pw) += val; @@ -87,7 +87,7 @@ static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { if (val > *pw) *pw = val; @@ -103,7 +103,7 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V v // Clang states it supports GCC atomic builtins. template <class T> -static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_conditional_increment(volatile T *pw) { while (true) { T tmp = static_cast<T const volatile &>(*pw); @@ -115,31 +115,31 @@ static _ALWAYS_INLINE_ T atomic_conditional_increment(register T *pw) { } template <class T> -static _ALWAYS_INLINE_ T atomic_decrement(register T *pw) { +static _ALWAYS_INLINE_ T atomic_decrement(volatile T *pw) { return __sync_sub_and_fetch(pw, 1); } template <class T> -static _ALWAYS_INLINE_ T atomic_increment(register T *pw) { +static _ALWAYS_INLINE_ T atomic_increment(volatile T *pw) { return __sync_add_and_fetch(pw, 1); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_sub(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_sub(volatile T *pw, volatile V val) { return __sync_sub_and_fetch(pw, val); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_add(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_add(volatile T *pw, volatile V val) { return __sync_add_and_fetch(pw, val); } template <class T, class V> -static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V val) { +static _ALWAYS_INLINE_ T atomic_exchange_if_greater(volatile T *pw, volatile V val) { while (true) { T tmp = static_cast<T const volatile &>(*pw); @@ -153,19 +153,19 @@ static _ALWAYS_INLINE_ T atomic_exchange_if_greater(register T *pw, register V v #elif defined(_MSC_VER) // For MSVC use a separate compilation unit to prevent windows.h from polluting // the global namespace. -uint32_t atomic_conditional_increment(register uint32_t *pw); -uint32_t atomic_decrement(register uint32_t *pw); -uint32_t atomic_increment(register uint32_t *pw); -uint32_t atomic_sub(register uint32_t *pw, register uint32_t val); -uint32_t atomic_add(register uint32_t *pw, register uint32_t val); -uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val); - -uint64_t atomic_conditional_increment(register uint64_t *pw); -uint64_t atomic_decrement(register uint64_t *pw); -uint64_t atomic_increment(register uint64_t *pw); -uint64_t atomic_sub(register uint64_t *pw, register uint64_t val); -uint64_t atomic_add(register uint64_t *pw, register uint64_t val); -uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val); +uint32_t atomic_conditional_increment(volatile uint32_t *pw); +uint32_t atomic_decrement(volatile uint32_t *pw); +uint32_t atomic_increment(volatile uint32_t *pw); +uint32_t atomic_sub(volatile uint32_t *pw, volatile uint32_t val); +uint32_t atomic_add(volatile uint32_t *pw, volatile uint32_t val); +uint32_t atomic_exchange_if_greater(volatile uint32_t *pw, volatile uint32_t val); + +uint64_t atomic_conditional_increment(volatile uint64_t *pw); +uint64_t atomic_decrement(volatile uint64_t *pw); +uint64_t atomic_increment(volatile uint64_t *pw); +uint64_t atomic_sub(volatile uint64_t *pw, volatile uint64_t val); +uint64_t atomic_add(volatile uint64_t *pw, volatile uint64_t val); +uint64_t atomic_exchange_if_greater(volatile uint64_t *pw, volatile uint64_t val); #else //no threads supported? diff --git a/core/script_debugger_local.cpp b/core/script_debugger_local.cpp index 55d7270473..6949b5802b 100644 --- a/core/script_debugger_local.cpp +++ b/core/script_debugger_local.cpp @@ -325,7 +325,7 @@ void ScriptDebuggerLocal::idle_poll() { int ofs = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo[ofs], pinfo.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs); } SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort; @@ -377,7 +377,7 @@ void ScriptDebuggerLocal::profiling_end() { int ofs = 0; for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo[ofs], pinfo.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs); } SortArray<ScriptLanguage::ProfilingInfo, _ScriptDebuggerLocalProfileInfoSort> sort; diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 3955f222f9..c5daaeea47 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -783,13 +783,13 @@ void ScriptDebuggerRemote::_send_profiling_data(bool p_for_frame) { for (int i = 0; i < ScriptServer::get_language_count(); i++) { if (p_for_frame) - ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info[ofs], profile_info.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&profile_info.write[ofs], profile_info.size() - ofs); else - ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info[ofs], profile_info.size() - ofs); + ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&profile_info.write[ofs], profile_info.size() - ofs); } for (int i = 0; i < ofs; i++) { - profile_info_ptrs[i] = &profile_info[i]; + profile_info_ptrs.write[i] = &profile_info.write[i]; } SortArray<ScriptLanguage::ProfilingInfo *, ProfileInfoSort> sa; @@ -1054,7 +1054,7 @@ void ScriptDebuggerRemote::add_profiling_frame_data(const StringName &p_name, co if (idx == -1) { profile_frame_data.push_back(fd); } else { - profile_frame_data[idx] = fd; + profile_frame_data.write[idx] = fd; } } diff --git a/core/script_language.h b/core/script_language.h index 4e81b9b626..71d550d404 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -207,13 +207,20 @@ public: virtual void finish() = 0; /* EDITOR FUNCTIONS */ + struct Warning { + int line; + int code; + String string_code; + String message; + }; + virtual void get_reserved_words(List<String> *p_words) const = 0; virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0; virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const = 0; virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {} virtual bool is_using_templates() { return false; } - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const = 0; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, List<Warning> *r_warnings = NULL, Set<int> *r_safe_lines = NULL) const = 0; virtual String validate_path(const String &p_path) const { return ""; } virtual Script *create_script() const = 0; virtual bool has_named_classes() const = 0; diff --git a/core/sort.h b/core/sort.h index a6780309d8..97983829e1 100644 --- a/core/sort.h +++ b/core/sort.h @@ -36,13 +36,25 @@ @author ,,, <red@lunatea> */ +#define ERR_BAD_COMPARE(cond) \ + if (unlikely(cond)) { \ + ERR_PRINT("bad comparison function; sorting will be broken"); \ + break; \ + } + template <class T> struct _DefaultComparator { - inline bool operator()(const T &a, const T &b) const { return (a < b); } + _FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); } }; -template <class T, class Comparator = _DefaultComparator<T> > +#ifdef DEBUG_ENABLED +#define SORT_ARRAY_VALIDATE_ENABLED true +#else +#define SORT_ARRAY_VALIDATE_ENABLED false +#endif + +template <class T, class Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED> class SortArray { enum { @@ -164,12 +176,23 @@ public: inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const { + const int unmodified_first = p_first; + const int unmodified_last = p_last; + while (true) { - while (compare(p_array[p_first], p_pivot)) + while (compare(p_array[p_first], p_pivot)) { + if (Validate) { + ERR_BAD_COMPARE(p_first == unmodified_last - 1) + } p_first++; + } p_last--; - while (compare(p_pivot, p_array[p_last])) + while (compare(p_pivot, p_array[p_last])) { + if (Validate) { + ERR_BAD_COMPARE(p_last == unmodified_first) + } p_last--; + } if (!(p_first < p_last)) return p_first; @@ -238,6 +261,9 @@ public: int next = p_last - 1; while (compare(p_value, p_array[next])) { + if (Validate) { + ERR_BAD_COMPARE(next == 0) + } p_array[p_last] = p_array[next]; p_last = next; next--; diff --git a/core/string_buffer.h b/core/string_buffer.h index 7e9b151bea..5d3be0ecf1 100644 --- a/core/string_buffer.h +++ b/core/string_buffer.h @@ -42,7 +42,7 @@ class StringBuffer { int string_length; _FORCE_INLINE_ CharType *current_buffer_ptr() { - return static_cast<Vector<CharType> &>(buffer).empty() ? short_buffer : buffer.ptrw(); + return static_cast<String &>(buffer).empty() ? short_buffer : buffer.ptrw(); } public: diff --git a/core/translation.cpp b/core/translation.cpp index aaa4de5912..78115c3749 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -1171,13 +1171,11 @@ void TranslationServer::_bind_methods() { void TranslationServer::load_translations() { String locale = get_locale(); - bool found = _load_translations("locale/translations"); //all + _load_translations("locale/translations"); //all + _load_translations("locale/translations_" + locale.substr(0, 2)); - if (_load_translations("locale/translations_" + locale.substr(0, 2))) - found = true; if (locale.substr(0, 2) != locale) { - if (_load_translations("locale/translations_" + locale)) - found = true; + _load_translations("locale/translations_" + locale); } } diff --git a/core/typedefs.h b/core/typedefs.h index 71771ea4e6..57afa3bdb4 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -58,12 +58,8 @@ #endif #ifndef _FORCE_INLINE_ -#ifdef DEBUG_ENABLED -#define _FORCE_INLINE_ inline -#else #define _FORCE_INLINE_ _ALWAYS_INLINE_ #endif -#endif //custom, gcc-safe offsetof, because gcc complains a lot. template <class T> @@ -267,7 +263,7 @@ static inline uint64_t BSWAP64(uint64_t x) { template <class T> struct Comparator { - inline bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } + _ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } }; void _global_lock(); diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index b9a2fdd0ac..3d90608dd7 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -39,7 +39,7 @@ void UndoRedo::_discard_redo() { for (int i = current_action + 1; i < actions.size(); i++) { - for (List<Operation>::Element *E = actions[i].do_ops.front(); E; E = E->next()) { + for (List<Operation>::Element *E = actions.write[i].do_ops.front(); E; E = E->next()) { if (E->get().type == Operation::TYPE_REFERENCE) { @@ -70,7 +70,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { if (p_mode == MERGE_ENDS) { // Clear all do ops from last action, and delete all object references - List<Operation>::Element *E = actions[current_action + 1].do_ops.front(); + List<Operation>::Element *E = actions.write[current_action + 1].do_ops.front(); while (E) { @@ -83,11 +83,11 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { } E = E->next(); - actions[current_action + 1].do_ops.pop_front(); + actions.write[current_action + 1].do_ops.pop_front(); } } - actions[actions.size() - 1].last_tick = ticks; + actions.write[actions.size() - 1].last_tick = ticks; merge_mode = p_mode; @@ -122,7 +122,7 @@ void UndoRedo::add_do_method(Object *p_object, const String &p_method, VARIANT_A for (int i = 0; i < VARIANT_ARG_MAX; i++) { do_op.args[i] = *argptr[i]; } - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_method(Object *p_object, const String &p_method, VARIANT_ARG_DECLARE) { @@ -147,7 +147,7 @@ void UndoRedo::add_undo_method(Object *p_object, const String &p_method, VARIANT for (int i = 0; i < VARIANT_ARG_MAX; i++) { undo_op.args[i] = *argptr[i]; } - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::add_do_property(Object *p_object, const String &p_property, const Variant &p_value) { @@ -162,7 +162,7 @@ void UndoRedo::add_do_property(Object *p_object, const String &p_property, const do_op.type = Operation::TYPE_PROPERTY; do_op.name = p_property; do_op.args[0] = p_value; - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_property(Object *p_object, const String &p_property, const Variant &p_value) { @@ -182,7 +182,7 @@ void UndoRedo::add_undo_property(Object *p_object, const String &p_property, con undo_op.type = Operation::TYPE_PROPERTY; undo_op.name = p_property; undo_op.args[0] = p_value; - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::add_do_reference(Object *p_object) { @@ -195,7 +195,7 @@ void UndoRedo::add_do_reference(Object *p_object) { do_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); do_op.type = Operation::TYPE_REFERENCE; - actions[current_action + 1].do_ops.push_back(do_op); + actions.write[current_action + 1].do_ops.push_back(do_op); } void UndoRedo::add_undo_reference(Object *p_object) { @@ -213,7 +213,7 @@ void UndoRedo::add_undo_reference(Object *p_object) { undo_op.resref = Ref<Resource>(Object::cast_to<Resource>(p_object)); undo_op.type = Operation::TYPE_REFERENCE; - actions[current_action + 1].undo_ops.push_back(undo_op); + actions.write[current_action + 1].undo_ops.push_back(undo_op); } void UndoRedo::_pop_history_tail() { @@ -223,7 +223,7 @@ void UndoRedo::_pop_history_tail() { if (!actions.size()) return; - for (List<Operation>::Element *E = actions[0].undo_ops.front(); E; E = E->next()) { + for (List<Operation>::Element *E = actions.write[0].undo_ops.front(); E; E = E->next()) { if (E->get().type == Operation::TYPE_REFERENCE) { @@ -307,7 +307,7 @@ bool UndoRedo::redo() { return false; //nothing to redo current_action++; - _process_operation_list(actions[current_action].do_ops.front()); + _process_operation_list(actions.write[current_action].do_ops.front()); version++; return true; @@ -318,7 +318,7 @@ bool UndoRedo::undo() { ERR_FAIL_COND_V(action_level > 0, false); if (current_action < 0) return false; //nothing to redo - _process_operation_list(actions[current_action].undo_ops.front()); + _process_operation_list(actions.write[current_action].undo_ops.front()); current_action--; version--; diff --git a/core/ustring.cpp b/core/ustring.cpp index bee5f5ffdb..96d142d85b 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -102,6 +102,15 @@ bool CharString::operator<(const CharString &p_right) const { return is_str_less(get_data(), p_right.get_data()); } +CharString &CharString::operator+=(char p_char) { + + resize(size() ? size() + 1 : 2); + set(length(), 0); + set(length() - 1, p_char); + + return *this; +} + const char *CharString::get_data() const { if (size()) @@ -161,14 +170,21 @@ void String::copy_from(const CharType *p_cstr, int p_clip_to) { return; } - resize(len + 1); - set(len, 0); + copy_from_unchecked(p_cstr, len); +} - CharType *dst = &operator[](0); +// assumes the following have already been validated: +// p_char != NULL +// p_length > 0 +// p_length <= p_char strlen +void String::copy_from_unchecked(const CharType *p_char, int p_length) { + resize(p_length + 1); + set(p_length, 0); - for (int i = 0; i < len; i++) { + CharType *dst = &operator[](0); - dst[i] = p_cstr[i]; + for (int i = 0; i < p_length; i++) { + dst[i] = p_char[i]; } } @@ -921,8 +937,8 @@ String String::to_upper() const { for (int i = 0; i < upper.size(); i++) { - const char s = upper[i]; - const char t = _find_upper(s); + const CharType s = upper[i]; + const CharType t = _find_upper(s); if (s != t) // avoid copy on write upper[i] = t; } @@ -936,8 +952,8 @@ String String::to_lower() const { for (int i = 0; i < lower.size(); i++) { - const char s = lower[i]; - const char t = _find_lower(s); + const CharType s = lower[i]; + const CharType t = _find_lower(s); if (s != t) // avoid copy on write lower[i] = t; } @@ -1571,6 +1587,7 @@ String::String(const char *p_str) { copy_from(p_str); } + String::String(const CharType *p_str, int p_clip_to_len) { copy_from(p_str, p_clip_to_len); @@ -2190,7 +2207,7 @@ Vector<uint8_t> String::md5_buffer() const { Vector<uint8_t> ret; ret.resize(16); for (int i = 0; i < 16; i++) { - ret[i] = ctx.digest[i]; + ret.write[i] = ctx.digest[i]; }; return ret; @@ -2207,7 +2224,7 @@ Vector<uint8_t> String::sha256_buffer() const { Vector<uint8_t> ret; ret.resize(32); for (int i = 0; i < 32; i++) { - ret[i] = hash[i]; + ret.write[i] = hash[i]; } return ret; @@ -2246,7 +2263,9 @@ String String::substr(int p_from, int p_chars) const { return String(*this); } - return String(&c_str()[p_from], p_chars); + String s = String(); + s.copy_from_unchecked(&c_str()[p_from], p_chars); + return s; } int String::find_last(const String &p_str) const { @@ -2664,7 +2683,7 @@ Vector<String> String::bigrams() const { } b.resize(n_pairs); for (int i = 0; i < n_pairs; i++) { - b[i] = substr(i, 2); + b.write[i] = substr(i, 2); } return b; } @@ -2759,7 +2778,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", key), val); + new_string = new_string.replace(placeholder.replace("_", key), val); } else { ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data()); } @@ -2772,7 +2791,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", i_as_str), val); + new_string = new_string.replace(placeholder.replace("_", i_as_str), val); } } } else if (values.get_type() == Variant::DICTIONARY) { @@ -2792,7 +2811,7 @@ String String::format(const Variant &values, String placeholder) const { val = val.substr(1, val.length() - 2); } - new_string = new_string.replacen(placeholder.replace("_", key), val); + new_string = new_string.replace(placeholder.replace("_", key), val); } } else { ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data()); @@ -3023,14 +3042,14 @@ String String::strip_escapes() const { return substr(beg, end - beg); } -String String::lstrip(const Vector<CharType> &p_chars) const { +String String::lstrip(const String &p_chars) const { int len = length(); int beg; for (beg = 0; beg < len; beg++) { - if (p_chars.find(operator[](beg)) == -1) + if (p_chars.find(&ptr()[beg]) == -1) break; } @@ -3040,14 +3059,14 @@ String String::lstrip(const Vector<CharType> &p_chars) const { return substr(beg, len - beg); } -String String::rstrip(const Vector<CharType> &p_chars) const { +String String::rstrip(const String &p_chars) const { int len = length(); int end; for (end = len - 1; end >= 0; end--) { - if (p_chars.find(operator[](end)) == -1) + if (p_chars.find(&ptr()[end]) == -1) break; } @@ -3859,11 +3878,9 @@ String String::percent_decode() const { c += d; i += 2; } - pe.push_back(c); + pe += c; } - pe.push_back(0); - return String::utf8(pe.ptr()); } diff --git a/core/ustring.h b/core/ustring.h index b57e9629d9..3b4405833c 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -32,6 +32,7 @@ #define RSTRING_H #include "array.h" +#include "cowdata.h" #include "typedefs.h" #include "vector.h" @@ -39,9 +40,27 @@ @author red <red@killy> */ -class CharString : public Vector<char> { +class CharString { + + CowData<char> _cowdata; + public: + _FORCE_INLINE_ char *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const char *ptr() const { return _cowdata.ptr(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + + _FORCE_INLINE_ char get(int p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const char get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const char &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ char &operator[](int p_index) { return _cowdata.get_m(p_index); } + _FORCE_INLINE_ const char &operator[](int p_index) const { return _cowdata.get(p_index); } + + _FORCE_INLINE_ CharString() {} + _FORCE_INLINE_ CharString(const CharString &p_str) { _cowdata._ref(p_str._cowdata); } + bool operator<(const CharString &p_right) const; + CharString &operator+=(char p_char); int length() const { return size() ? size() - 1 : 0; } const char *get_data() const; operator const char *() { return get_data(); }; @@ -60,11 +79,14 @@ struct StrRange { } }; -class String : public Vector<CharType> { +class String { + + CowData<CharType> _cowdata; void copy_from(const char *p_cstr); void copy_from(const CharType *p_cstr, int p_clip_to = -1); void copy_from(const CharType &p_char); + void copy_from_unchecked(const CharType *p_char, int p_length); bool _base_is_subsequence_of(const String &p_string, bool case_insensitive) const; public: @@ -73,6 +95,21 @@ public: npos = -1 ///<for "some" compatibility with std::string (npos is a huge value in std::string) }; + _FORCE_INLINE_ CharType *ptrw() { return _cowdata.ptrw(); } + _FORCE_INLINE_ const CharType *ptr() const { return _cowdata.ptr(); } + + void remove(int p_index) { _cowdata.remove(p_index); } + + _FORCE_INLINE_ void clear() { resize(0); } + + _FORCE_INLINE_ CharType get(int p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const CharType get(int p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(int p_index, const CharType &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + Error resize(int p_size) { return _cowdata.resize(p_size); } + _FORCE_INLINE_ CharType &operator[](int p_index) { return _cowdata.get_m(p_index); } + _FORCE_INLINE_ const CharType &operator[](int p_index) const { return _cowdata.get(p_index); } + bool operator==(const String &p_str) const; bool operator!=(const String &p_str) const; String operator+(const String &p_str) const; @@ -191,8 +228,8 @@ public: String dedent() const; String strip_edges(bool left = true, bool right = true) const; String strip_escapes() const; - String lstrip(const Vector<CharType> &p_chars) const; - String rstrip(const Vector<CharType> &p_chars) const; + String lstrip(const String &p_chars) const; + String rstrip(const String &p_chars) const; String get_extension() const; String get_basename() const; String plus_file(const String &p_file) const; @@ -253,9 +290,10 @@ public: * The constructors must not depend on other overloads */ /* String(CharType p_char);*/ - inline String() {} - inline String(const String &p_str) : - Vector(p_str) {} + + _FORCE_INLINE_ String() {} + _FORCE_INLINE_ String(const String &p_str) { _cowdata._ref(p_str._cowdata); } + String(const char *p_str); String(const CharType *p_str, int p_clip_to_len = -1); String(const StrRange &p_range); diff --git a/core/variant.cpp b/core/variant.cpp index c48aa57652..e4be5520bc 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -1878,7 +1878,7 @@ Variant::operator Vector<RID>() const { Vector<RID> rids; rids.resize(va.size()); for (int i = 0; i < rids.size(); i++) - rids[i] = va[i]; + rids.write[i] = va[i]; return rids; } @@ -1891,7 +1891,7 @@ Variant::operator Vector<Vector2>() const { return Vector<Vector2>(); to.resize(len); PoolVector<Vector2>::Read r = from.read(); - Vector2 *w = &to[0]; + Vector2 *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; @@ -1945,7 +1945,7 @@ Variant::operator Vector<Plane>() const { planes.resize(va_size); for (int i = 0; i < va_size; i++) - planes[i] = va[i]; + planes.write[i] = va[i]; return planes; } @@ -1958,7 +1958,7 @@ Variant::operator Vector<Variant>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1971,7 +1971,7 @@ Variant::operator Vector<uint8_t>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1983,7 +1983,7 @@ Variant::operator Vector<int>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -1995,7 +1995,7 @@ Variant::operator Vector<real_t>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -2008,7 +2008,7 @@ Variant::operator Vector<String>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -2020,7 +2020,7 @@ Variant::operator Vector<StringName>() const { to.resize(len); for (int i = 0; i < len; i++) { - to[i] = from[i]; + to.write[i] = from[i]; } return to; } @@ -2034,7 +2034,7 @@ Variant::operator Vector<Vector3>() const { return Vector<Vector3>(); to.resize(len); PoolVector<Vector3>::Read r = from.read(); - Vector3 *w = &to[0]; + Vector3 *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; @@ -2050,7 +2050,7 @@ Variant::operator Vector<Color>() const { return Vector<Color>(); to.resize(len); PoolVector<Color>::Read r = from.read(); - Color *w = &to[0]; + Color *w = to.ptrw(); for (int i = 0; i < len; i++) { w[i] = r[i]; diff --git a/core/variant.h b/core/variant.h index 4b245d25e6..b48a0b3e73 100644 --- a/core/variant.h +++ b/core/variant.h @@ -398,9 +398,9 @@ public: void static_assign(const Variant &p_variant); static void get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_list); - static void get_numeric_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); - static bool has_numeric_constant(Variant::Type p_type, const StringName &p_value); - static int get_numeric_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = NULL); + static void get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants); + static bool has_constant(Variant::Type p_type, const StringName &p_value); + static Variant get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = NULL); typedef String (*ObjectDeConstruct)(const Variant &p_object, void *ud); typedef void (*ObjectConstruct)(const String &p_text, void *ud, Variant &r_value); diff --git a/core/variant_call.cpp b/core/variant_call.cpp index e6f36ecbf1..19308ff683 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -30,6 +30,7 @@ #include "variant.h" +#include "core/color_names.inc" #include "core_string_names.h" #include "io/compression.h" #include "object.h" @@ -64,7 +65,7 @@ struct _VariantCall { if (arg_count == 0) return true; - Variant::Type *tptr = &arg_types[0]; + const Variant::Type *tptr = &arg_types[0]; for (int i = 0; i < arg_count; i++) { @@ -446,8 +447,12 @@ struct _VariantCall { VCALL_LOCALMEM1(Quat, set_euler); VCALL_LOCALMEM2(Quat, set_axis_angle); - VCALL_LOCALMEM0R(Color, to_rgba32); VCALL_LOCALMEM0R(Color, to_argb32); + VCALL_LOCALMEM0R(Color, to_abgr32); + VCALL_LOCALMEM0R(Color, to_rgba32); + VCALL_LOCALMEM0R(Color, to_argb64); + VCALL_LOCALMEM0R(Color, to_abgr64); + VCALL_LOCALMEM0R(Color, to_rgba64); VCALL_LOCALMEM0R(Color, gray); VCALL_LOCALMEM0R(Color, inverted); VCALL_LOCALMEM0R(Color, contrasted); @@ -987,6 +992,7 @@ struct _VariantCall { #ifdef DEBUG_ENABLED List<StringName> value_ordered; #endif + Map<StringName, Variant> variant_value; }; static ConstantData *constant_data; @@ -998,6 +1004,11 @@ struct _VariantCall { constant_data[p_type].value_ordered.push_back(p_constant_name); #endif } + + static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) { + + constant_data[p_type].variant_value[p_constant_name] = p_constant_value; + } }; _VariantCall::TypeFunc *_VariantCall::type_funcs = NULL; @@ -1350,7 +1361,7 @@ void Variant::get_constructor_list(Variant::Type p_type, List<MethodInfo> *p_lis } } -void Variant::get_numeric_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { +void Variant::get_constants_for_type(Variant::Type p_type, List<StringName> *p_constants) { ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); @@ -1366,16 +1377,21 @@ void Variant::get_numeric_constants_for_type(Variant::Type p_type, List<StringNa p_constants->push_back(E->key()); #endif } + + for (Map<StringName, Variant>::Element *E = cd.variant_value.front(); E; E = E->next()) { + + p_constants->push_back(E->key()); + } } -bool Variant::has_numeric_constant(Variant::Type p_type, const StringName &p_value) { +bool Variant::has_constant(Variant::Type p_type, const StringName &p_value) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); _VariantCall::ConstantData &cd = _VariantCall::constant_data[p_type]; - return cd.value.has(p_value); + return cd.value.has(p_value) || cd.variant_value.has(p_value); } -int Variant::get_numeric_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { +Variant Variant::get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid) { if (r_valid) *r_valid = false; @@ -1385,7 +1401,14 @@ int Variant::get_numeric_constant_value(Variant::Type p_type, const StringName & Map<StringName, int>::Element *E = cd.value.find(p_value); if (!E) { - return -1; + Map<StringName, Variant>::Element *E = cd.variant_value.find(p_value); + if (E) { + if (r_valid) + *r_valid = true; + return E->get(); + } else { + return -1; + } } if (r_valid) *r_valid = true; @@ -1613,8 +1636,12 @@ void register_variant_methods() { ADDFUNC1(QUAT, NIL, Quat, set_euler, VECTOR3, "euler", varray()); ADDFUNC2(QUAT, NIL, Quat, set_axis_angle, VECTOR3, "axis", REAL, "angle", varray()); - ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray()); ADDFUNC0R(COLOR, INT, Color, to_argb32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_abgr32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_rgba32, varray()); + ADDFUNC0R(COLOR, INT, Color, to_argb64, varray()); + ADDFUNC0R(COLOR, INT, Color, to_abgr64, varray()); + ADDFUNC0R(COLOR, INT, Color, to_rgba64, varray()); ADDFUNC0R(COLOR, REAL, Color, gray, varray()); ADDFUNC0R(COLOR, COLOR, Color, inverted, varray()); ADDFUNC0R(COLOR, COLOR, Color, contrasted, varray()); @@ -1850,9 +1877,62 @@ void register_variant_methods() { /* REGISTER CONSTANTS */ + _populate_named_colors(); + for (Map<String, Color>::Element *color = _named_colors.front(); color; color = color->next()) { + _VariantCall::add_variant_constant(Variant::COLOR, color->key(), color->value()); + } + _VariantCall::add_constant(Variant::VECTOR3, "AXIS_X", Vector3::AXIS_X); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Y", Vector3::AXIS_Y); _VariantCall::add_constant(Variant::VECTOR3, "AXIS_Z", Vector3::AXIS_Z); + + _VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "DOWN", Vector3(0, -1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "FORWARD", Vector3(0, 0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "BACK", Vector3(0, 0, 1)); + + _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "DOWN", Vector2(0, 1)); + + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D(1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0)); + _VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0)); + + Transform identity_transform, transform_x, transform_y, transform_z; + identity_transform.set(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "IDENTITY", identity_transform); + transform_x.set(-1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_X", transform_x); + transform_x.set(1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Y", transform_y); + transform_x.set(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0); + _VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", transform_z); + + _VariantCall::add_variant_constant(Variant::PLANE, "X", Plane(Vector3(1, 0, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "Y", Plane(Vector3(0, 1, 0), 0)); + _VariantCall::add_variant_constant(Variant::PLANE, "Z", Plane(Vector3(0, 0, 1), 0)); + + _VariantCall::add_variant_constant(Variant::QUAT, "IDENTITY", Quat(0, 0, 0, 1)); + + CharType black_circle[2] = { 0x25CF, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "BLACK_CIRCLE", String(black_circle)); + CharType white_circle[2] = { 0x25CB, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "WHITE_CIRCLE", String(white_circle)); + CharType black_diamond[2] = { 0x25C6, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "BLACK_DIAMOND", String(black_diamond)); + CharType white_diamond[2] = { 0x25C7, 0 }; + _VariantCall::add_variant_constant(Variant::STRING, "WHITE_DIAMOND", String(white_diamond)); + + _VariantCall::add_variant_constant(Variant::NODE_PATH, "CURRENT", String(".")); + _VariantCall::add_variant_constant(Variant::NODE_PATH, "PARENT", String("..")); } void unregister_variant_methods() { diff --git a/core/vector.h b/core/vector.h index f586471e27..52e8758f9b 100644 --- a/core/vector.h +++ b/core/vector.h @@ -36,129 +36,68 @@ * @author Juan Linietsky * Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use PoolVector for large arrays. */ +#include "cowdata.h" #include "error_macros.h" #include "os/memory.h" -#include "safe_refcount.h" #include "sort.h" template <class T> -class Vector { - - mutable T *_ptr; - - // internal helpers +class VectorWriteProxy { + friend class Vector<T>; + CowData<T> *_parent; - _FORCE_INLINE_ uint32_t *_get_refcount() const { + _FORCE_INLINE_ VectorWriteProxy(CowData<T> *parent) : + _parent(parent){}; - if (!_ptr) - return NULL; - - return reinterpret_cast<uint32_t *>(_ptr) - 2; - } - - _FORCE_INLINE_ uint32_t *_get_size() const { - - if (!_ptr) - return NULL; - - return reinterpret_cast<uint32_t *>(_ptr) - 1; - } - _FORCE_INLINE_ T *_get_data() const { - - if (!_ptr) - return NULL; - return reinterpret_cast<T *>(_ptr); - } - - _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { - //return nearest_power_of_2_templated(p_elements*sizeof(T)+sizeof(SafeRefCount)+sizeof(int)); - return next_power_of_2(p_elements * sizeof(T)); - } +public: + _FORCE_INLINE_ T &operator[](int p_index) { + CRASH_BAD_INDEX(p_index, _parent->size()); - _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { -#if defined(_add_overflow) && defined(_mul_overflow) - size_t o; - size_t p; - if (_mul_overflow(p_elements, sizeof(T), &o)) return false; - *out = next_power_of_2(o); - if (_add_overflow(o, static_cast<size_t>(32), &p)) return false; //no longer allocated here - return true; -#else - // Speed is more important than correctness here, do the operations unchecked - // and hope the best - *out = _get_alloc_size(p_elements); - return true; -#endif + return _parent->ptrw()[p_index]; } +}; - void _unref(void *p_data); +template <class T> +class Vector { + friend class VectorWriteProxy<T>; - void _copy_from(const Vector &p_from); - void _copy_on_write(); + CowData<T> *_cowdata; public: - _FORCE_INLINE_ T *ptrw() { - if (!_ptr) return NULL; - _copy_on_write(); - return (T *)_get_data(); - } - _FORCE_INLINE_ const T *ptr() const { - if (!_ptr) return NULL; - return _get_data(); - } - - _FORCE_INLINE_ void clear() { resize(0); } + VectorWriteProxy<T> write; - _FORCE_INLINE_ int size() const { - uint32_t *size = (uint32_t *)_get_size(); - if (size) - return *size; - else - return 0; - } - _FORCE_INLINE_ bool empty() const { return _ptr == 0; } - Error resize(int p_size); bool push_back(const T &p_elem); - void remove(int p_index); + void remove(int p_index) { _cowdata->remove(p_index); } void erase(const T &p_val) { int idx = find(p_val); if (idx >= 0) remove(idx); }; void invert(); - template <class T_val> - int find(const T_val &p_val, int p_from = 0) const; - - void set(int p_index, const T &p_elem); - T get(int p_index) const; - - inline T &operator[](int p_index) { - - CRASH_BAD_INDEX(p_index, size()); - - _copy_on_write(); // wants to write, so copy on write. - - return _get_data()[p_index]; - } - - inline const T &operator[](int p_index) const { - - CRASH_BAD_INDEX(p_index, size()); + _FORCE_INLINE_ T *ptrw() { return _cowdata->ptrw(); } + _FORCE_INLINE_ const T *ptr() const { return _cowdata->ptr(); } + _FORCE_INLINE_ void clear() { resize(0); } + _FORCE_INLINE_ bool empty() const { return _cowdata->empty(); } - // no cow needed, since it's reading - return _get_data()[p_index]; - } + _FORCE_INLINE_ T get(int p_index) { return _cowdata->get(p_index); } + _FORCE_INLINE_ const T get(int p_index) const { return _cowdata->get(p_index); } + _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata->set(p_index, p_elem); } + _FORCE_INLINE_ int size() const { return _cowdata->size(); } + Error resize(int p_size) { return _cowdata->resize(p_size); } + _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata->get(p_index); } + Error insert(int p_pos, const T &p_val) { return _cowdata->insert(p_pos, p_val); } - Error insert(int p_pos, const T &p_val); + void append_array(const Vector<T> &p_other); template <class C> void sort_custom() { - int len = size(); + int len = _cowdata->size(); if (len == 0) return; - T *data = &operator[](0); + + T *data = ptrw(); SortArray<T, C> sorter; sorter.sort(data, len); } @@ -170,7 +109,7 @@ public: void ordered_insert(const T &p_val) { int i; - for (i = 0; i < size(); i++) { + for (i = 0; i < _cowdata->size(); i++) { if (p_val < operator[](i)) { break; @@ -179,173 +118,56 @@ public: insert(i, p_val); } - void operator=(const Vector &p_from); - Vector(const Vector &p_from); + int find(const T &p_val, int p_from = 0) const { + int ret = -1; + if (p_from < 0 || size() == 0) + return ret; - _FORCE_INLINE_ Vector(); - _FORCE_INLINE_ ~Vector(); -}; - -template <class T> -void Vector<T>::_unref(void *p_data) { - - if (!p_data) - return; - - uint32_t *refc = _get_refcount(); - - if (atomic_decrement(refc) > 0) - return; // still in use - // clean up - - uint32_t *count = _get_size(); - T *data = (T *)(count + 1); - - for (uint32_t i = 0; i < *count; i++) { - // call destructors - data[i].~T(); - } - - // free mem - Memory::free_static((uint8_t *)p_data, true); -} - -template <class T> -void Vector<T>::_copy_on_write() { - - if (!_ptr) - return; - - uint32_t *refc = _get_refcount(); + for (int i = p_from; i < size(); i++) { - if (*refc > 1) { - /* in use by more than me */ - uint32_t current_size = *_get_size(); - - uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); - - *(mem_new - 2) = 1; //refcount - *(mem_new - 1) = current_size; //size - - T *_data = (T *)(mem_new); - - // initialize new elements - for (uint32_t i = 0; i < current_size; i++) { - - memnew_placement(&_data[i], T(_get_data()[i])); - } - - _unref(_ptr); - _ptr = _data; - } -} - -template <class T> -template <class T_val> -int Vector<T>::find(const T_val &p_val, int p_from) const { - - int ret = -1; - if (p_from < 0 || size() == 0) - return ret; - - for (int i = p_from; i < size(); i++) { - - if (operator[](i) == p_val) { - ret = i; - break; + if (ptr()[i] == p_val) { + ret = i; + break; + }; }; - }; - return ret; -} - -template <class T> -Error Vector<T>::resize(int p_size) { - - ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); - - if (p_size == size()) - return OK; - - if (p_size == 0) { - // wants to clean up - _unref(_ptr); - _ptr = NULL; - return OK; + return ret; } - // possibly changing size, copy on write - _copy_on_write(); - - size_t alloc_size; - ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); - - if (p_size > size()) { - - if (size() == 0) { - // alloc from scratch - uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); - ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY); - *(ptr - 1) = 0; //size, currently none - *(ptr - 2) = 1; //refcount - - _ptr = (T *)ptr; - - } else { - void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); - _ptr = (T *)(_ptrnew); - } - - // construct the newly created elements - T *elems = _get_data(); - - for (int i = *_get_size(); i < p_size; i++) { - - memnew_placement(&elems[i], T); - } - - *_get_size() = p_size; - - } else if (p_size < size()) { - - // deinitialize no longer needed elements - for (uint32_t i = p_size; i < *_get_size(); i++) { - - T *t = &_get_data()[i]; - t->~T(); - } - - void *_ptrnew = (T *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY); - - _ptr = (T *)(_ptrnew); - - *_get_size() = p_size; + _FORCE_INLINE_ Vector() : + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) {} + _FORCE_INLINE_ Vector(const Vector &p_from) : + _cowdata(new CowData<T>()), + write(VectorWriteProxy<T>(_cowdata)) { _cowdata->_ref(p_from._cowdata); } + inline Vector &operator=(const Vector &p_from) { + _cowdata->_ref(p_from._cowdata); + return *this; } - return OK; -} + _FORCE_INLINE_ ~Vector() { + delete _cowdata; + } +}; template <class T> void Vector<T>::invert() { for (int i = 0; i < size() / 2; i++) { - - SWAP(operator[](i), operator[](size() - i - 1)); + T *p = ptrw(); + SWAP(p[i], p[size() - i - 1]); } } template <class T> -void Vector<T>::set(int p_index, const T &p_elem) { - - operator[](p_index) = p_elem; -} - -template <class T> -T Vector<T>::get(int p_index) const { - - return operator[](p_index); +void Vector<T>::append_array(const Vector<T> &p_other) { + const int ds = p_other.size(); + if (ds == 0) + return; + const int bs = size(); + resize(bs + ds); + for (int i = 0; i < ds; ++i) + ptrw()[bs + i] = p_other[i]; } template <class T> @@ -358,72 +180,4 @@ bool Vector<T>::push_back(const T &p_elem) { return false; } -template <class T> -void Vector<T>::remove(int p_index) { - - ERR_FAIL_INDEX(p_index, size()); - T *p = ptrw(); - int len = size(); - for (int i = p_index; i < len - 1; i++) { - - p[i] = p[i + 1]; - }; - - resize(len - 1); -}; - -template <class T> -void Vector<T>::_copy_from(const Vector &p_from) { - - if (_ptr == p_from._ptr) - return; // self assign, do nothing. - - _unref(_ptr); - _ptr = NULL; - - if (!p_from._ptr) - return; //nothing to do - - if (atomic_conditional_increment(p_from._get_refcount()) > 0) { // could reference - _ptr = p_from._ptr; - } -} - -template <class T> -void Vector<T>::operator=(const Vector &p_from) { - - _copy_from(p_from); -} - -template <class T> -Error Vector<T>::insert(int p_pos, const T &p_val) { - - ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); - resize(size() + 1); - for (int i = (size() - 1); i > p_pos; i--) - set(i, get(i - 1)); - set(p_pos, p_val); - - return OK; -} - -template <class T> -Vector<T>::Vector(const Vector &p_from) { - - _ptr = NULL; - _copy_from(p_from); -} - -template <class T> -Vector<T>::Vector() { - - _ptr = NULL; -} - -template <class T> -Vector<T>::~Vector() { - - _unref(_ptr); -} - #endif diff --git a/core/vmap.h b/core/vmap.h index 8636c02997..ce0ddc4ec6 100644 --- a/core/vmap.h +++ b/core/vmap.h @@ -31,8 +31,8 @@ #ifndef VMAP_H #define VMAP_H +#include "cowdata.h" #include "typedefs.h" -#include "vector.h" template <class T, class V> class VMap { @@ -51,17 +51,17 @@ class VMap { } }; - Vector<_Pair> _data; + CowData<_Pair> _cowdata; _FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const { r_exact = false; - if (_data.empty()) + if (_cowdata.empty()) return 0; int low = 0; - int high = _data.size() - 1; - const _Pair *a = &_data[0]; + int high = _cowdata.size() - 1; + const _Pair *a = _cowdata.ptr(); int middle = 0; #if DEBUG_ENABLED @@ -89,13 +89,13 @@ class VMap { _FORCE_INLINE_ int _find_exact(const T &p_val) const { - if (_data.empty()) + if (_cowdata.empty()) return -1; int low = 0; - int high = _data.size() - 1; + int high = _cowdata.size() - 1; int middle; - const _Pair *a = &_data[0]; + const _Pair *a = _cowdata.ptr(); while (low <= high) { middle = (low + high) / 2; @@ -118,10 +118,10 @@ public: bool exact; int pos = _find(p_key, exact); if (exact) { - _data[pos].value = p_val; + _cowdata.get_m(pos).value = p_val; return pos; } - _data.insert(pos, _Pair(p_key, p_val)); + _cowdata.insert(pos, _Pair(p_key, p_val)); return pos; } @@ -135,7 +135,7 @@ public: int pos = _find_exact(p_val); if (pos < 0) return; - _data.remove(pos); + _cowdata.remove(pos); } int find(const T &p_val) const { @@ -149,37 +149,37 @@ public: return _find(p_val, exact); } - _FORCE_INLINE_ int size() const { return _data.size(); } - _FORCE_INLINE_ bool empty() const { return _data.empty(); } + _FORCE_INLINE_ int size() const { return _cowdata.size(); } + _FORCE_INLINE_ bool empty() const { return _cowdata.empty(); } const _Pair *get_array() const { - return _data.ptr(); + return _cowdata.ptr(); } _Pair *get_array() { - return _data.ptr(); + return _cowdata.ptrw(); } const V &getv(int p_index) const { - return _data[p_index].value; + return _cowdata.get(p_index).value; } V &getv(int p_index) { - return _data[p_index].value; + return _cowdata.get_m(p_index).value; } const T &getk(int p_index) const { - return _data[p_index].key; + return _cowdata.get(p_index).key; } T &getk(int p_index) { - return _data[p_index].key; + return _cowdata.get_m(p_index).key; } inline const V &operator[](const T &p_key) const { @@ -188,7 +188,7 @@ public: CRASH_COND(pos < 0); - return _data[pos].value; + return _cowdata.get(pos).value; } inline V &operator[](const T &p_key) { @@ -199,7 +199,14 @@ public: pos = insert(p_key, val); } - return _data[pos].value; + return _cowdata.get_m(pos).value; + } + + _FORCE_INLINE_ VMap(){}; + _FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); } + inline VMap &operator=(const VMap &p_from) { + _cowdata._ref(p_from._cowdata); + return *this; } }; #endif // VMAP_H diff --git a/core/vset.h b/core/vset.h index 449943b4a1..7f4d8e7f62 100644 --- a/core/vset.h +++ b/core/vset.h @@ -133,7 +133,7 @@ public: inline T &operator[](int p_index) { - return _data[p_index]; + return _data.write[p_index]; } inline const T &operator[](int p_index) const { |