diff options
Diffstat (limited to 'core')
61 files changed, 1342 insertions, 1836 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 6275502378..15f807017f 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -101,10 +101,15 @@ const PackedStringArray ProjectSettings::_get_supported_features() { features.append(VERSION_BRANCH "." _MKSTR(VERSION_PATCH)); features.append(VERSION_FULL_CONFIG); features.append(VERSION_FULL_BUILD); - // For now, assume Vulkan is always supported. - // This should be removed if it's possible to build the editor without Vulkan. - features.append("Vulkan Clustered"); - features.append("Vulkan Mobile"); + +#ifdef VULKAN_ENABLED + features.append("Forward Plus"); + features.append("Mobile"); +#endif + +#ifdef GLES3_ENABLED + features.append("GL Compatibility"); +#endif return features; } @@ -909,7 +914,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust project_features = ProjectSettings::get_required_features(); } // Check the rendering API. - const String rendering_api = has_setting("rendering/quality/driver/driver_name") ? (String)get_setting("rendering/quality/driver/driver_name") : String(); + const String rendering_api = has_setting("rendering/renderer/rendering_method") ? (String)get_setting("rendering/renderer/rendering_method") : String(); if (!rendering_api.is_empty()) { // Add the rendering API as a project feature if it doesn't already exist. if (!project_features.has(rendering_api)) { @@ -1139,6 +1144,7 @@ void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("get_order", "name"), &ProjectSettings::get_order); ClassDB::bind_method(D_METHOD("set_initial_value", "name", "value"), &ProjectSettings::set_initial_value); ClassDB::bind_method(D_METHOD("add_property_info", "hint"), &ProjectSettings::_add_property_info_bind); + ClassDB::bind_method(D_METHOD("set_restart_if_changed", "name", "restart"), &ProjectSettings::set_restart_if_changed); ClassDB::bind_method(D_METHOD("clear", "name"), &ProjectSettings::clear); ClassDB::bind_method(D_METHOD("localize_path", "path"), &ProjectSettings::localize_path); ClassDB::bind_method(D_METHOD("globalize_path", "path"), &ProjectSettings::globalize_path); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index a9af63ee39..960dfe0395 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -45,9 +45,10 @@ public: static const String PROJECT_DATA_DIR_NAME_SUFFIX; enum { - //properties that are not for built in values begin from this value, so builtin ones are displayed first + // Properties that are not for built in values begin from this value, so builtin ones are displayed first. NO_BUILTIN_ORDER_BASE = 1 << 16 }; + #ifdef TOOLS_ENABLED const static PackedStringArray get_required_features(); const static PackedStringArray get_unsupported_features(const PackedStringArray &p_project_features); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 9daf58cb71..2149ee4512 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -322,6 +322,14 @@ String OS::get_name() const { return ::OS::get_singleton()->get_name(); } +String OS::get_distribution_name() const { + return ::OS::get_singleton()->get_distribution_name(); +} + +String OS::get_version() const { + return ::OS::get_singleton()->get_version(); +} + Vector<String> OS::get_cmdline_args() { List<String> cmdline = ::OS::get_singleton()->get_cmdline_args(); Vector<String> cmdlinev; @@ -535,6 +543,8 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment); ClassDB::bind_method(D_METHOD("get_name"), &OS::get_name); + ClassDB::bind_method(D_METHOD("get_distribution_name"), &OS::get_distribution_name); + ClassDB::bind_method(D_METHOD("get_version"), &OS::get_version); ClassDB::bind_method(D_METHOD("get_cmdline_args"), &OS::get_cmdline_args); ClassDB::bind_method(D_METHOD("get_cmdline_user_args"), &OS::get_cmdline_user_args); @@ -800,14 +810,13 @@ Dictionary Geometry2D::make_atlas(const Vector<Size2> &p_rects) { ::Geometry2D::make_atlas(rects, result, size); - Size2 r_size = size; Vector<Point2> r_result; for (int i = 0; i < result.size(); i++) { r_result.push_back(result[i]); } ret["points"] = r_result; - ret["size"] = r_size; + ret["size"] = size; return ret; } @@ -982,636 +991,6 @@ void Geometry3D::_bind_methods() { ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &Geometry3D::clip_polygon); } -////// File ////// - -Error File::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { - Error err = open(p_path, p_mode_flags); - if (err) { - return err; - } - - Ref<FileAccessEncrypted> fae; - fae.instantiate(); - err = fae->open_and_parse(f, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ); - if (err) { - close(); - return err; - } - f = fae; - return OK; -} - -Error File::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) { - Error err = open(p_path, p_mode_flags); - if (err) { - return err; - } - - Ref<FileAccessEncrypted> fae; - fae.instantiate(); - err = fae->open_and_parse_password(f, p_pass, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ); - if (err) { - close(); - return err; - } - - f = fae; - return OK; -} - -Error File::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) { - Ref<FileAccessCompressed> fac; - fac.instantiate(); - fac->configure("GCPF", (Compression::Mode)p_compress_mode); - - Error err = fac->_open(p_path, p_mode_flags); - - if (err) { - return err; - } - - f = fac; - return OK; -} - -Error File::open(const String &p_path, ModeFlags p_mode_flags) { - Error err; - f = FileAccess::open(p_path, p_mode_flags, &err); - if (f.is_valid()) { - f->set_big_endian(big_endian); - } - return err; -} - -void File::flush() { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before flushing."); - f->flush(); -} - -void File::close() { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened."); - f.unref(); -} - -bool File::is_open() const { - return f != nullptr; -} - -String File::get_path() const { - ERR_FAIL_COND_V_MSG(f.is_null(), "", "File must be opened before use, or is lacking read-write permission."); - return f->get_path(); -} - -String File::get_path_absolute() const { - ERR_FAIL_COND_V_MSG(f.is_null(), "", "File must be opened before use, or is lacking read-write permission."); - return f->get_path_absolute(); -} - -void File::seek(int64_t p_position) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - ERR_FAIL_COND_MSG(p_position < 0, "Seek position must be a positive integer."); - f->seek(p_position); -} - -void File::seek_end(int64_t p_position) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - f->seek_end(p_position); -} - -uint64_t File::get_position() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_position(); -} - -uint64_t File::get_length() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_length(); -} - -bool File::eof_reached() const { - ERR_FAIL_COND_V_MSG(f.is_null(), false, "File must be opened before use, or is lacking read-write permission."); - return f->eof_reached(); -} - -uint8_t File::get_8() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_8(); -} - -uint16_t File::get_16() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_16(); -} - -uint32_t File::get_32() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_32(); -} - -uint64_t File::get_64() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_64(); -} - -float File::get_float() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_float(); -} - -double File::get_double() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_double(); -} - -real_t File::get_real() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use, or is lacking read-write permission."); - return f->get_real(); -} - -Vector<uint8_t> File::get_buffer(int64_t p_length) const { - Vector<uint8_t> data; - ERR_FAIL_COND_V_MSG(f.is_null(), data, "File must be opened before use, or is lacking read-write permission."); - - ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0."); - if (p_length == 0) { - return data; - } - - Error err = data.resize(p_length); - ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements."); - - uint8_t *w = data.ptrw(); - int64_t len = f->get_buffer(&w[0], p_length); - - if (len < p_length) { - data.resize(len); - } - - return data; -} - -String File::get_as_text(bool p_skip_cr) const { - ERR_FAIL_COND_V_MSG(f.is_null(), String(), "File must be opened before use, or is lacking read-write permission."); - - uint64_t original_pos = f->get_position(); - const_cast<FileAccess *>(*f)->seek(0); - - String text = f->get_as_utf8_string(p_skip_cr); - - const_cast<FileAccess *>(*f)->seek(original_pos); - - return text; -} - -String File::get_md5(const String &p_path) const { - return FileAccess::get_md5(p_path); -} - -String File::get_sha256(const String &p_path) const { - return FileAccess::get_sha256(p_path); -} - -String File::get_line() const { - ERR_FAIL_COND_V_MSG(f.is_null(), String(), "File must be opened before use, or is lacking read-write permission."); - return f->get_line(); -} - -Vector<String> File::get_csv_line(const String &p_delim) const { - ERR_FAIL_COND_V_MSG(f.is_null(), Vector<String>(), "File must be opened before use, or is lacking read-write permission."); - return f->get_csv_line(p_delim); -} - -/**< use this for files WRITTEN in _big_ endian machines (i.e. amiga/mac) - * It's not about the current CPU type but file formats. - * These flags get reset to false (little endian) on each open - */ - -void File::set_big_endian(bool p_big_endian) { - big_endian = p_big_endian; - if (f.is_valid()) { - f->set_big_endian(p_big_endian); - } -} - -bool File::is_big_endian() { - return big_endian; -} - -Error File::get_error() const { - if (f.is_null()) { - return ERR_UNCONFIGURED; - } - return f->get_error(); -} - -void File::store_8(uint8_t p_dest) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_8(p_dest); -} - -void File::store_16(uint16_t p_dest) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_16(p_dest); -} - -void File::store_32(uint32_t p_dest) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_32(p_dest); -} - -void File::store_64(uint64_t p_dest) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_64(p_dest); -} - -void File::store_float(float p_dest) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_float(p_dest); -} - -void File::store_double(double p_dest) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_double(p_dest); -} - -void File::store_real(real_t p_real) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_real(p_real); -} - -void File::store_string(const String &p_string) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_string(p_string); -} - -void File::store_pascal_string(const String &p_string) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - f->store_pascal_string(p_string); -} - -String File::get_pascal_string() { - ERR_FAIL_COND_V_MSG(f.is_null(), "", "File must be opened before use, or is lacking read-write permission."); - - return f->get_pascal_string(); -} - -void File::store_line(const String &p_string) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - f->store_line(p_string); -} - -void File::store_csv_line(const Vector<String> &p_values, const String &p_delim) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - f->store_csv_line(p_values, p_delim); -} - -void File::store_buffer(const Vector<uint8_t> &p_buffer) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - - uint64_t len = p_buffer.size(); - if (len == 0) { - return; - } - - const uint8_t *r = p_buffer.ptr(); - - f->store_buffer(&r[0], len); -} - -bool File::file_exists(const String &p_name) { - return FileAccess::exists(p_name); -} - -void File::store_var(const Variant &p_var, bool p_full_objects) { - ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use, or is lacking read-write permission."); - int len; - Error err = encode_variant(p_var, nullptr, len, p_full_objects); - ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); - - Vector<uint8_t> buff; - buff.resize(len); - - uint8_t *w = buff.ptrw(); - err = encode_variant(p_var, &w[0], len, p_full_objects); - ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); - - store_32(len); - store_buffer(buff); -} - -Variant File::get_var(bool p_allow_objects) const { - ERR_FAIL_COND_V_MSG(f.is_null(), Variant(), "File must be opened before use, or is lacking read-write permission."); - uint32_t len = get_32(); - Vector<uint8_t> buff = get_buffer(len); - ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant()); - - const uint8_t *r = buff.ptr(); - - Variant v; - Error err = decode_variant(v, &r[0], len, nullptr, p_allow_objects); - ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to encode Variant."); - - return v; -} - -uint64_t File::get_modified_time(const String &p_file) const { - return FileAccess::get_modified_time(p_file); -} - -void File::_bind_methods() { - ClassDB::bind_method(D_METHOD("open_encrypted", "path", "mode_flags", "key"), &File::open_encrypted); - ClassDB::bind_method(D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &File::open_encrypted_pass); - ClassDB::bind_method(D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &File::open_compressed, DEFVAL(0)); - - ClassDB::bind_method(D_METHOD("open", "path", "flags"), &File::open); - ClassDB::bind_method(D_METHOD("flush"), &File::flush); - ClassDB::bind_method(D_METHOD("close"), &File::close); - ClassDB::bind_method(D_METHOD("get_path"), &File::get_path); - ClassDB::bind_method(D_METHOD("get_path_absolute"), &File::get_path_absolute); - ClassDB::bind_method(D_METHOD("is_open"), &File::is_open); - ClassDB::bind_method(D_METHOD("seek", "position"), &File::seek); - ClassDB::bind_method(D_METHOD("seek_end", "position"), &File::seek_end, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("get_position"), &File::get_position); - ClassDB::bind_method(D_METHOD("get_length"), &File::get_length); - ClassDB::bind_method(D_METHOD("eof_reached"), &File::eof_reached); - ClassDB::bind_method(D_METHOD("get_8"), &File::get_8); - ClassDB::bind_method(D_METHOD("get_16"), &File::get_16); - ClassDB::bind_method(D_METHOD("get_32"), &File::get_32); - ClassDB::bind_method(D_METHOD("get_64"), &File::get_64); - ClassDB::bind_method(D_METHOD("get_float"), &File::get_float); - ClassDB::bind_method(D_METHOD("get_double"), &File::get_double); - ClassDB::bind_method(D_METHOD("get_real"), &File::get_real); - ClassDB::bind_method(D_METHOD("get_buffer", "length"), &File::get_buffer); - ClassDB::bind_method(D_METHOD("get_line"), &File::get_line); - ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &File::get_csv_line, DEFVAL(",")); - ClassDB::bind_method(D_METHOD("get_as_text", "skip_cr"), &File::get_as_text, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_md5", "path"), &File::get_md5); - ClassDB::bind_method(D_METHOD("get_sha256", "path"), &File::get_sha256); - ClassDB::bind_method(D_METHOD("is_big_endian"), &File::is_big_endian); - ClassDB::bind_method(D_METHOD("set_big_endian", "big_endian"), &File::set_big_endian); - ClassDB::bind_method(D_METHOD("get_error"), &File::get_error); - ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &File::get_var, DEFVAL(false)); - - ClassDB::bind_method(D_METHOD("store_8", "value"), &File::store_8); - ClassDB::bind_method(D_METHOD("store_16", "value"), &File::store_16); - ClassDB::bind_method(D_METHOD("store_32", "value"), &File::store_32); - ClassDB::bind_method(D_METHOD("store_64", "value"), &File::store_64); - ClassDB::bind_method(D_METHOD("store_float", "value"), &File::store_float); - ClassDB::bind_method(D_METHOD("store_double", "value"), &File::store_double); - ClassDB::bind_method(D_METHOD("store_real", "value"), &File::store_real); - ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &File::store_buffer); - ClassDB::bind_method(D_METHOD("store_line", "line"), &File::store_line); - ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &File::store_csv_line, DEFVAL(",")); - ClassDB::bind_method(D_METHOD("store_string", "string"), &File::store_string); - ClassDB::bind_method(D_METHOD("store_var", "value", "full_objects"), &File::store_var, DEFVAL(false)); - - ClassDB::bind_method(D_METHOD("store_pascal_string", "string"), &File::store_pascal_string); - ClassDB::bind_method(D_METHOD("get_pascal_string"), &File::get_pascal_string); - - ClassDB::bind_static_method("File", D_METHOD("file_exists", "path"), &File::file_exists); - ClassDB::bind_method(D_METHOD("get_modified_time", "file"), &File::get_modified_time); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian"); - - BIND_ENUM_CONSTANT(READ); - BIND_ENUM_CONSTANT(WRITE); - BIND_ENUM_CONSTANT(READ_WRITE); - BIND_ENUM_CONSTANT(WRITE_READ); - - BIND_ENUM_CONSTANT(COMPRESSION_FASTLZ); - BIND_ENUM_CONSTANT(COMPRESSION_DEFLATE); - BIND_ENUM_CONSTANT(COMPRESSION_ZSTD); - BIND_ENUM_CONSTANT(COMPRESSION_GZIP); -} - -////// Directory ////// - -Error Directory::open(const String &p_path) { - Error err; - Ref<DirAccess> alt = DirAccess::open(p_path, &err); - if (alt.is_null()) { - return err; - } - d = alt; - dir_open = true; - - return OK; -} - -bool Directory::is_open() const { - return d.is_valid() && dir_open; -} - -Error Directory::list_dir_begin() { - ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); - return d->list_dir_begin(); -} - -String Directory::get_next() { - ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); - - String next = d->get_next(); - while (!next.is_empty() && ((!include_navigational && (next == "." || next == "..")) || (!include_hidden && d->current_is_hidden()))) { - next = d->get_next(); - } - return next; -} - -bool Directory::current_is_dir() const { - ERR_FAIL_COND_V_MSG(!is_open(), false, "Directory must be opened before use."); - return d->current_is_dir(); -} - -void Directory::list_dir_end() { - ERR_FAIL_COND_MSG(!is_open(), "Directory must be opened before use."); - d->list_dir_end(); -} - -PackedStringArray Directory::get_files() { - return _get_contents(false); -} - -PackedStringArray Directory::get_directories() { - return _get_contents(true); -} - -PackedStringArray Directory::_get_contents(bool p_directories) { - PackedStringArray ret; - ERR_FAIL_COND_V_MSG(!is_open(), ret, "Directory must be opened before use."); - - list_dir_begin(); - String s = get_next(); - while (!s.is_empty()) { - if (current_is_dir() == p_directories) { - ret.append(s); - } - s = get_next(); - } - - ret.sort(); - return ret; -} - -void Directory::set_include_navigational(bool p_enable) { - include_navigational = p_enable; -} - -bool Directory::get_include_navigational() const { - return include_navigational; -} - -void Directory::set_include_hidden(bool p_enable) { - include_hidden = p_enable; -} - -bool Directory::get_include_hidden() const { - return include_hidden; -} - -int Directory::get_drive_count() { - ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); - return d->get_drive_count(); -} - -String Directory::get_drive(int p_drive) { - ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); - return d->get_drive(p_drive); -} - -int Directory::get_current_drive() { - ERR_FAIL_COND_V_MSG(!is_open(), 0, "Directory must be opened before use."); - return d->get_current_drive(); -} - -Error Directory::change_dir(String p_dir) { - ERR_FAIL_COND_V_MSG(d.is_null(), ERR_UNCONFIGURED, "Directory is not configured properly."); - Error err = d->change_dir(p_dir); - - if (err != OK) { - return err; - } - dir_open = true; - - return OK; -} - -String Directory::get_current_dir() { - ERR_FAIL_COND_V_MSG(!is_open(), "", "Directory must be opened before use."); - return d->get_current_dir(); -} - -Error Directory::make_dir(String p_dir) { - ERR_FAIL_COND_V_MSG(d.is_null(), ERR_UNCONFIGURED, "Directory is not configured properly."); - if (!p_dir.is_relative_path()) { - Ref<DirAccess> da = DirAccess::create_for_path(p_dir); - return da->make_dir(p_dir); - } - return d->make_dir(p_dir); -} - -Error Directory::make_dir_recursive(String p_dir) { - ERR_FAIL_COND_V_MSG(d.is_null(), ERR_UNCONFIGURED, "Directory is not configured properly."); - if (!p_dir.is_relative_path()) { - Ref<DirAccess> da = DirAccess::create_for_path(p_dir); - return da->make_dir_recursive(p_dir); - } - return d->make_dir_recursive(p_dir); -} - -bool Directory::file_exists(String p_file) { - ERR_FAIL_COND_V_MSG(d.is_null(), false, "Directory is not configured properly."); - if (!p_file.is_relative_path()) { - return FileAccess::exists(p_file); - } - return d->file_exists(p_file); -} - -bool Directory::dir_exists(String p_dir) { - ERR_FAIL_COND_V_MSG(d.is_null(), false, "Directory is not configured properly."); - if (!p_dir.is_relative_path()) { - return DirAccess::exists(p_dir); - } - return d->dir_exists(p_dir); -} - -uint64_t Directory::get_space_left() { - ERR_FAIL_COND_V_MSG(d.is_null(), 0, "Directory must be opened before use."); - return d->get_space_left() / 1024 * 1024; // Truncate to closest MiB. -} - -Error Directory::copy(String p_from, String p_to) { - ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); - return d->copy(p_from, p_to); -} - -Error Directory::rename(String p_from, String p_to) { - ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); - ERR_FAIL_COND_V_MSG(p_from.is_empty() || p_from == "." || p_from == "..", ERR_INVALID_PARAMETER, "Invalid path to rename."); - - if (!p_from.is_relative_path()) { - Ref<DirAccess> da = DirAccess::create_for_path(p_from); - ERR_FAIL_COND_V_MSG(!da->file_exists(p_from) && !da->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist."); - return da->rename(p_from, p_to); - } - - ERR_FAIL_COND_V_MSG(!d->file_exists(p_from) && !d->dir_exists(p_from), ERR_DOES_NOT_EXIST, "File or directory does not exist."); - return d->rename(p_from, p_to); -} - -Error Directory::remove(String p_name) { - ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use."); - if (!p_name.is_relative_path()) { - Ref<DirAccess> da = DirAccess::create_for_path(p_name); - return da->remove(p_name); - } - - return d->remove(p_name); -} - -void Directory::_bind_methods() { - ClassDB::bind_method(D_METHOD("open", "path"), &Directory::open); - ClassDB::bind_method(D_METHOD("list_dir_begin"), &Directory::list_dir_begin, DEFVAL(false), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_next"), &Directory::get_next); - ClassDB::bind_method(D_METHOD("current_is_dir"), &Directory::current_is_dir); - ClassDB::bind_method(D_METHOD("list_dir_end"), &Directory::list_dir_end); - ClassDB::bind_method(D_METHOD("get_files"), &Directory::get_files); - ClassDB::bind_method(D_METHOD("get_directories"), &Directory::get_directories); - ClassDB::bind_method(D_METHOD("get_drive_count"), &Directory::get_drive_count); - ClassDB::bind_method(D_METHOD("get_drive", "idx"), &Directory::get_drive); - ClassDB::bind_method(D_METHOD("get_current_drive"), &Directory::get_current_drive); - ClassDB::bind_method(D_METHOD("change_dir", "todir"), &Directory::change_dir); - ClassDB::bind_method(D_METHOD("get_current_dir"), &Directory::get_current_dir); - ClassDB::bind_method(D_METHOD("make_dir", "path"), &Directory::make_dir); - ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &Directory::make_dir_recursive); - ClassDB::bind_method(D_METHOD("file_exists", "path"), &Directory::file_exists); - ClassDB::bind_method(D_METHOD("dir_exists", "path"), &Directory::dir_exists); - ClassDB::bind_method(D_METHOD("get_space_left"), &Directory::get_space_left); - ClassDB::bind_method(D_METHOD("copy", "from", "to"), &Directory::copy); - ClassDB::bind_method(D_METHOD("rename", "from", "to"), &Directory::rename); - ClassDB::bind_method(D_METHOD("remove", "path"), &Directory::remove); - - ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &Directory::set_include_navigational); - ClassDB::bind_method(D_METHOD("get_include_navigational"), &Directory::get_include_navigational); - ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &Directory::set_include_hidden); - ClassDB::bind_method(D_METHOD("get_include_hidden"), &Directory::get_include_hidden); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden"); -} - -Directory::Directory() { - d = DirAccess::create(DirAccess::ACCESS_RESOURCES); -} - ////// Marshalls ////// Marshalls *Marshalls::singleton = nullptr; diff --git a/core/core_bind.h b/core/core_bind.h index cd382d2915..345c517b99 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -193,6 +193,8 @@ public: bool set_environment(const String &p_var, const String &p_value) const; String get_name() const; + String get_distribution_name() const; + String get_version() const; Vector<String> get_cmdline_args(); Vector<String> get_cmdline_user_args(); @@ -353,156 +355,6 @@ public: Geometry3D() { singleton = this; } }; -class File : public RefCounted { - GDCLASS(File, RefCounted); - - Ref<FileAccess> f; - bool big_endian = false; - -protected: - static void _bind_methods(); - -public: - enum ModeFlags { - READ = 1, - WRITE = 2, - READ_WRITE = 3, - WRITE_READ = 7, - }; - - enum CompressionMode { - COMPRESSION_FASTLZ = Compression::MODE_FASTLZ, - COMPRESSION_DEFLATE = Compression::MODE_DEFLATE, - COMPRESSION_ZSTD = Compression::MODE_ZSTD, - COMPRESSION_GZIP = Compression::MODE_GZIP - }; - - Error open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key); - Error open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass); - Error open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ); - - Error open(const String &p_path, ModeFlags p_mode_flags); // open a file. - void flush(); // Flush a file (write its buffer to disk). - void close(); // Close a file. - bool is_open() const; // True when file is open. - - String get_path() const; // Returns the path for the current open file. - String get_path_absolute() const; // Returns the absolute path for the current open file. - - void seek(int64_t p_position); // Seek to a given position. - void seek_end(int64_t p_position = 0); // Seek from the end of file. - uint64_t get_position() const; // Get position in the file. - uint64_t get_length() const; // Get size of the file. - - bool eof_reached() const; // Reading passed EOF. - - uint8_t get_8() const; // Get a byte. - uint16_t get_16() const; // Get 16 bits uint. - uint32_t get_32() const; // Get 32 bits uint. - uint64_t get_64() const; // Get 64 bits uint. - - float get_float() const; - double get_double() const; - real_t get_real() const; - - Variant get_var(bool p_allow_objects = false) const; - - Vector<uint8_t> get_buffer(int64_t p_length) const; // Get an array of bytes. - String get_line() const; - Vector<String> get_csv_line(const String &p_delim = ",") const; - String get_as_text(bool p_skip_cr = false) const; - String get_md5(const String &p_path) const; - String get_sha256(const String &p_path) const; - - /* - * Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac). - * It's not about the current CPU type but file formats. - * This flag gets reset to `false` (little endian) on each open. - */ - void set_big_endian(bool p_big_endian); - bool is_big_endian(); - - Error get_error() const; // Get last error. - - void store_8(uint8_t p_dest); // Store a byte. - void store_16(uint16_t p_dest); // Store 16 bits uint. - void store_32(uint32_t p_dest); // Store 32 bits uint. - void store_64(uint64_t p_dest); // Store 64 bits uint. - - void store_float(float p_dest); - void store_double(double p_dest); - void store_real(real_t p_real); - - void store_string(const String &p_string); - void store_line(const String &p_string); - void store_csv_line(const Vector<String> &p_values, const String &p_delim = ","); - - virtual void store_pascal_string(const String &p_string); - virtual String get_pascal_string(); - - void store_buffer(const Vector<uint8_t> &p_buffer); // Store an array of bytes. - - void store_var(const Variant &p_var, bool p_full_objects = false); - - static bool file_exists(const String &p_name); // Return true if a file exists. - - uint64_t get_modified_time(const String &p_file) const; - - File() {} -}; - -class Directory : public RefCounted { - GDCLASS(Directory, RefCounted); - Ref<DirAccess> d; - - bool dir_open = false; - bool include_navigational = false; - bool include_hidden = false; - -protected: - static void _bind_methods(); - -public: - Error open(const String &p_path); - - bool is_open() const; - - Error list_dir_begin(); - String get_next(); - bool current_is_dir() const; - void list_dir_end(); - - PackedStringArray get_files(); - PackedStringArray get_directories(); - PackedStringArray _get_contents(bool p_directories); - - void set_include_navigational(bool p_enable); - bool get_include_navigational() const; - void set_include_hidden(bool p_enable); - bool get_include_hidden() const; - - int get_drive_count(); - String get_drive(int p_drive); - int get_current_drive(); - - Error change_dir(String p_dir); // Can be relative or absolute, return false on success. - String get_current_dir(); // Return current dir location. - - Error make_dir(String p_dir); - Error make_dir_recursive(String p_dir); - - bool file_exists(String p_file); - bool dir_exists(String p_dir); - - uint64_t get_space_left(); - - Error copy(String p_from, String p_to); - Error rename(String p_from, String p_to); - Error remove(String p_name); - - Directory(); -}; - class Marshalls : public Object { GDCLASS(Marshalls, Object); @@ -736,9 +588,6 @@ VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyBooleanOperation); VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyJoinType); VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyEndType); -VARIANT_ENUM_CAST(core_bind::File::ModeFlags); -VARIANT_ENUM_CAST(core_bind::File::CompressionMode); - VARIANT_ENUM_CAST(core_bind::Thread::Priority); #endif // CORE_BIND_H diff --git a/core/core_constants.cpp b/core/core_constants.cpp index c4d83d4765..2f8b28363b 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -433,11 +433,11 @@ void register_global_constants() { BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(KeyModifierMask, KEY_CODE_MASK, CODE_MASK); BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(KeyModifierMask, KEY_MODIFIER_MASK, MODIFIER_MASK); + BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, CMD_OR_CTRL); BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, SHIFT); BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, ALT); BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, META); BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, CTRL); - BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(KeyModifierMask, KEY_MASK, CMD); BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, KPAD); BIND_CORE_ENUM_CLASS_CONSTANT(KeyModifierMask, KEY_MASK, GROUP_SWITCH); diff --git a/core/doc_data.h b/core/doc_data.h index 1d8d2483e0..3b7bf149b4 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -66,6 +66,8 @@ public: String return_enum; String qualifiers; String description; + bool is_deprecated = false; + bool is_experimental = false; Vector<ArgumentDoc> arguments; Vector<int> errors_returned; bool operator<(const MethodDoc &p_method) const { @@ -105,6 +107,8 @@ public: String enumeration; bool is_bitfield = false; String description; + bool is_deprecated = false; + bool is_experimental = false; bool operator<(const ConstantDoc &p_const) const { return name < p_const.name; } @@ -126,6 +130,8 @@ public: String default_value; bool overridden = false; String overrides; + bool is_deprecated = false; + bool is_experimental = false; bool operator<(const PropertyDoc &p_prop) const { return name < p_prop.name; } @@ -167,6 +173,8 @@ public: Vector<PropertyDoc> properties; Vector<MethodDoc> annotations; Vector<ThemeItemDoc> theme_properties; + bool is_deprecated = false; + bool is_experimental = false; bool is_script_doc = false; String script_path; bool operator<(const ClassDoc &p_class) const { diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 5cf951a93c..bda1a5cdc1 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -462,7 +462,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { d["indexing_return_type"] = index_type == Variant::NIL ? String("Variant") : Variant::get_type_name(index_type); } - d["is_keyed"] = Variant::ValidatedKeyedSetter(type); + d["is_keyed"] = Variant::is_keyed(type); { //members diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index ef0b590030..67dc55bdb7 100644 --- a/core/extension/gdnative_interface.cpp +++ b/core/extension/gdnative_interface.cpp @@ -251,27 +251,6 @@ static GDNativeBool gdnative_variant_booleanize(const GDNativeVariantPtr p_self) return self->booleanize(); } -static void gdnative_variant_sub(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_dst) { - const Variant *a = (const Variant *)p_a; - const Variant *b = (const Variant *)p_b; - memnew_placement(r_dst, Variant); - Variant::sub(*a, *b, *(Variant *)r_dst); -} - -static void gdnative_variant_blend(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) { - const Variant *a = (const Variant *)p_a; - const Variant *b = (const Variant *)p_b; - memnew_placement(r_dst, Variant); - Variant::blend(*a, *b, p_c, *(Variant *)r_dst); -} - -static void gdnative_variant_interpolate(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst) { - const Variant *a = (const Variant *)p_a; - const Variant *b = (const Variant *)p_b; - memnew_placement(r_dst, Variant); - Variant::interpolate(*a, *b, p_c, *(Variant *)r_dst); -} - static void gdnative_variant_duplicate(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep) { const Variant *self = (const Variant *)p_self; memnew_placement(r_ret, Variant(self->duplicate(p_deep))); @@ -905,7 +884,7 @@ static GDNativeMethodBindPtr gdnative_classdb_get_method_bind(const char *p_clas MethodBind *mb = ClassDB::get_method(StringName(p_classname), StringName(p_methodname)); ERR_FAIL_COND_V(!mb, nullptr); if (mb->get_hash() != p_hash) { - ERR_PRINT_ONCE("Hash mismatch for method '" + String(p_classname) + "." + String(p_methodname) + "'."); + ERR_PRINT("Hash mismatch for method '" + String(p_classname) + "." + String(p_methodname) + "'."); return nullptr; } // MethodBind *mb = ClassDB::get_method("Node", "get_name"); @@ -970,9 +949,6 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { gdni.variant_recursive_hash = gdnative_variant_recursive_hash; gdni.variant_hash_compare = gdnative_variant_hash_compare; gdni.variant_booleanize = gdnative_variant_booleanize; - gdni.variant_sub = gdnative_variant_sub; - gdni.variant_blend = gdnative_variant_blend; - gdni.variant_interpolate = gdnative_variant_interpolate; gdni.variant_duplicate = gdnative_variant_duplicate; gdni.variant_stringify = gdnative_variant_stringify; diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index cb2adcb562..39378d8261 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -427,9 +427,6 @@ typedef struct { GDNativeInt (*variant_recursive_hash)(const GDNativeVariantPtr p_self, GDNativeInt p_recursion_count); GDNativeBool (*variant_hash_compare)(const GDNativeVariantPtr p_self, const GDNativeVariantPtr p_other); GDNativeBool (*variant_booleanize)(const GDNativeVariantPtr p_self); - void (*variant_sub)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, GDNativeVariantPtr r_dst); - void (*variant_blend)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst); - void (*variant_interpolate)(const GDNativeVariantPtr p_a, const GDNativeVariantPtr p_b, float p_c, GDNativeVariantPtr r_dst); void (*variant_duplicate)(const GDNativeVariantPtr p_self, GDNativeVariantPtr r_ret, GDNativeBool p_deep); void (*variant_stringify)(const GDNativeVariantPtr p_self, GDNativeStringPtr r_ret); diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 3f02d80c26..596b704732 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -142,13 +142,33 @@ int64_t InputEventFromWindow::get_window_id() const { /////////////////////////////////// -void InputEventWithModifiers::set_store_command(bool p_enabled) { - store_command = p_enabled; +void InputEventWithModifiers::set_command_or_control_autoremap(bool p_enabled) { + command_or_control_autoremap = p_enabled; + if (command_or_control_autoremap) { +#ifdef MACOS_ENABLED + ctrl_pressed = false; + meta_pressed = true; +#else + ctrl_pressed = true; + meta_pressed = false; +#endif + } else { + ctrl_pressed = false; + meta_pressed = false; + } emit_changed(); } -bool InputEventWithModifiers::is_storing_command() const { - return store_command; +bool InputEventWithModifiers::is_command_or_control_autoremap() const { + return command_or_control_autoremap; +} + +bool InputEventWithModifiers::is_command_or_control_pressed() const { +#ifdef MACOS_ENABLED + return meta_pressed; +#else + return ctrl_pressed; +#endif } void InputEventWithModifiers::set_shift_pressed(bool p_enabled) { @@ -170,6 +190,7 @@ bool InputEventWithModifiers::is_alt_pressed() const { } void InputEventWithModifiers::set_ctrl_pressed(bool p_enabled) { + ERR_FAIL_COND_MSG(command_or_control_autoremap, "Command/Control autoremaping is enabled, cannot set Control directly!"); ctrl_pressed = p_enabled; emit_changed(); } @@ -179,6 +200,7 @@ bool InputEventWithModifiers::is_ctrl_pressed() const { } void InputEventWithModifiers::set_meta_pressed(bool p_enabled) { + ERR_FAIL_COND_MSG(command_or_control_autoremap, "Command/Control autoremaping is enabled, cannot set Meta directly!"); meta_pressed = p_enabled; emit_changed(); } @@ -187,15 +209,6 @@ bool InputEventWithModifiers::is_meta_pressed() const { return meta_pressed; } -void InputEventWithModifiers::set_command_pressed(bool p_enabled) { - command_pressed = p_enabled; - emit_changed(); -} - -bool InputEventWithModifiers::is_command_pressed() const { - return command_pressed; -} - void InputEventWithModifiers::set_modifiers_from_event(const InputEventWithModifiers *event) { set_alt_pressed(event->is_alt_pressed()); set_shift_pressed(event->is_shift_pressed()); @@ -217,6 +230,13 @@ Key InputEventWithModifiers::get_modifiers_mask() const { if (is_meta_pressed()) { mask |= KeyModifierMask::META; } + if (is_command_or_control_autoremap()) { +#ifdef MACOS_ENABLED + mask |= KeyModifierMask::META; +#else + mask |= KeyModifierMask::CTRL; +#endif + } return mask; } @@ -248,8 +268,10 @@ String InputEventWithModifiers::to_string() { } void InputEventWithModifiers::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_store_command", "enable"), &InputEventWithModifiers::set_store_command); - ClassDB::bind_method(D_METHOD("is_storing_command"), &InputEventWithModifiers::is_storing_command); + ClassDB::bind_method(D_METHOD("set_command_or_control_autoremap", "enable"), &InputEventWithModifiers::set_command_or_control_autoremap); + ClassDB::bind_method(D_METHOD("is_command_or_control_autoremap"), &InputEventWithModifiers::is_command_or_control_autoremap); + + ClassDB::bind_method(D_METHOD("is_command_or_control_pressed"), &InputEventWithModifiers::is_command_or_control_pressed); ClassDB::bind_method(D_METHOD("set_alt_pressed", "pressed"), &InputEventWithModifiers::set_alt_pressed); ClassDB::bind_method(D_METHOD("is_alt_pressed"), &InputEventWithModifiers::is_alt_pressed); @@ -263,34 +285,24 @@ void InputEventWithModifiers::_bind_methods() { ClassDB::bind_method(D_METHOD("set_meta_pressed", "pressed"), &InputEventWithModifiers::set_meta_pressed); ClassDB::bind_method(D_METHOD("is_meta_pressed"), &InputEventWithModifiers::is_meta_pressed); - ClassDB::bind_method(D_METHOD("set_command_pressed", "pressed"), &InputEventWithModifiers::set_command_pressed); - ClassDB::bind_method(D_METHOD("is_command_pressed"), &InputEventWithModifiers::is_command_pressed); - - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "store_command"), "set_store_command", "is_storing_command"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "command_or_control_autoremap"), "set_command_or_control_autoremap", "is_command_or_control_autoremap"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "alt_pressed"), "set_alt_pressed", "is_alt_pressed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shift_pressed"), "set_shift_pressed", "is_shift_pressed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ctrl_pressed"), "set_ctrl_pressed", "is_ctrl_pressed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_pressed"), "set_meta_pressed", "is_meta_pressed"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "command_pressed"), "set_command_pressed", "is_command_pressed"); } void InputEventWithModifiers::_validate_property(PropertyInfo &p_property) const { - if (store_command) { - // If we only want to Store "Command". -#ifdef APPLE_STYLE_KEYS - // Don't store "Meta" on Mac. + if (command_or_control_autoremap) { + // Cannot be used with Meta/Command or Control! if (p_property.name == "meta_pressed") { p_property.usage ^= PROPERTY_USAGE_STORAGE; } -#else - // Don't store "Ctrl". if (p_property.name == "ctrl_pressed") { p_property.usage ^= PROPERTY_USAGE_STORAGE; } -#endif } else { - // We don't want to store command, only ctrl or meta (on mac). - if (p_property.name == "command_pressed") { + if (p_property.name == "command_or_control_autoremap") { p_property.usage ^= PROPERTY_USAGE_STORAGE; } } @@ -399,14 +411,18 @@ Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode) { if ((p_keycode & KeyModifierMask::ALT) != Key::NONE) { ie->set_alt_pressed(true); } - if ((p_keycode & KeyModifierMask::CTRL) != Key::NONE) { - ie->set_ctrl_pressed(true); - } - if ((p_keycode & KeyModifierMask::CMD) != Key::NONE) { - ie->set_command_pressed(true); - } - if ((p_keycode & KeyModifierMask::META) != Key::NONE) { - ie->set_meta_pressed(true); + if ((p_keycode & KeyModifierMask::CMD_OR_CTRL) != Key::NONE) { + ie->set_command_or_control_autoremap(true); + if ((p_keycode & KeyModifierMask::CTRL) != Key::NONE || (p_keycode & KeyModifierMask::META) != Key::NONE) { + WARN_PRINT("Invalid Key Modifiers: Command or Control autoremapping is enabled, Meta and Control values are ignored!"); + } + } else { + if ((p_keycode & KeyModifierMask::CTRL) != Key::NONE) { + ie->set_ctrl_pressed(true); + } + if ((p_keycode & KeyModifierMask::META) != Key::NONE) { + ie->set_meta_pressed(true); + } } return ie; diff --git a/core/input/input_event.h b/core/input/input_event.h index 6cfc031c8a..bc3ec3e7ac 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -107,32 +107,22 @@ public: class InputEventWithModifiers : public InputEventFromWindow { GDCLASS(InputEventWithModifiers, InputEventFromWindow); - bool store_command = true; + bool command_or_control_autoremap = false; bool shift_pressed = false; bool alt_pressed = false; -#ifdef APPLE_STYLE_KEYS - union { - bool command_pressed; - bool meta_pressed = false; //< windows/mac key - }; - + bool meta_pressed = false; // "Command" on macOS, "Meta/Win" key on other platforms. bool ctrl_pressed = false; -#else - union { - bool command_pressed; //< windows/mac key - bool ctrl_pressed = false; - }; - bool meta_pressed = false; //< windows/mac key -#endif protected: static void _bind_methods(); void _validate_property(PropertyInfo &p_property) const; public: - void set_store_command(bool p_enabled); - bool is_storing_command() const; + void set_command_or_control_autoremap(bool p_enabled); + bool is_command_or_control_autoremap() const; + + bool is_command_or_control_pressed() const; void set_shift_pressed(bool p_pressed); bool is_shift_pressed() const; @@ -146,9 +136,6 @@ public: void set_meta_pressed(bool p_pressed); bool is_meta_pressed() const; - void set_command_pressed(bool p_pressed); - bool is_command_pressed() const; - void set_modifiers_from_event(const InputEventWithModifiers *event); Key get_modifiers_mask() const; diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index 702e257fb4..ce76d11b6e 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -429,27 +429,27 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { // ///// UI basic Shortcuts ///// inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::X | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::X | KeyModifierMask::CMD_OR_CTRL)); inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::SHIFT)); default_builtin_cache.insert("ui_cut", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::C | KeyModifierMask::CMD)); - inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::C | KeyModifierMask::CMD_OR_CTRL)); + inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_copy", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::V | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::V | KeyModifierMask::CMD_OR_CTRL)); inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::SHIFT)); default_builtin_cache.insert("ui_paste", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_undo", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD | KeyModifierMask::SHIFT)); - inputs.push_back(InputEventKey::create_reference(Key::Y | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT)); + inputs.push_back(InputEventKey::create_reference(Key::Y | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_redo", inputs); // ///// UI Text Input Shortcuts ///// @@ -474,13 +474,13 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::CMD)); - inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::CMD_OR_CTRL)); + inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_newline_blank", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD)); - inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL)); + inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_newline_above", inputs); // Indentation @@ -499,7 +499,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_backspace", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_backspace_word", inputs); inputs = List<Ref<InputEvent>>(); @@ -510,7 +510,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_backspace_all_to_left", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_backspace_all_to_left.macos", inputs); inputs = List<Ref<InputEvent>>(); @@ -518,7 +518,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_delete", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_delete_word", inputs); inputs = List<Ref<InputEvent>>(); @@ -529,7 +529,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_delete_all_to_right", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_delete_all_to_right.macos", inputs); // Text Caret Movement Left/Right @@ -539,7 +539,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_caret_left", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_caret_word_left", inputs); inputs = List<Ref<InputEvent>>(); @@ -551,7 +551,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_caret_right", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_caret_word_right", inputs); inputs = List<Ref<InputEvent>>(); @@ -576,7 +576,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CTRL)); - inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_caret_line_start.macos", inputs); inputs = List<Ref<InputEvent>>(); @@ -585,7 +585,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(Key::E | KeyModifierMask::CTRL)); - inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_caret_line_end.macos", inputs); // Text Caret Movement Page Up/Down @@ -601,47 +601,47 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { // Text Caret Movement Document Start/End inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_caret_document_start", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_caret_document_start.macos", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_caret_document_end", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs); // Text Scrolling inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_scroll_up", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD | KeyModifierMask::ALT)); + inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT)); default_builtin_cache.insert("ui_text_scroll_up.macos", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_scroll_down", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD | KeyModifierMask::ALT)); + inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT)); default_builtin_cache.insert("ui_text_scroll_down.macos", inputs); // Text Misc inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_select_all", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_text_select_word_under_caret", inputs); inputs = List<Ref<InputEvent>>(); @@ -660,7 +660,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { // ///// UI Graph Shortcuts ///// inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_graph_duplicate", inputs); inputs = List<Ref<InputEvent>>(); @@ -681,7 +681,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_filedialog_show_hidden", inputs); inputs = List<Ref<InputEvent>>(); - inputs.push_back(InputEventKey::create_reference(Key::QUOTELEFT | KeyModifierMask::CMD)); + inputs.push_back(InputEventKey::create_reference(Key::QUOTELEFT | KeyModifierMask::CMD_OR_CTRL)); default_builtin_cache.insert("ui_swap_input_direction", inputs); return default_builtin_cache; diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index bed41b8d89..f86dfe8057 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -36,6 +36,8 @@ #include "core/os/os.h" #include "core/templates/local_vector.h" +thread_local Error DirAccess::last_dir_open_error = OK; + String DirAccess::_get_root_path() const { switch (_access_type) { case ACCESS_RESOURCES: @@ -249,6 +251,61 @@ Ref<DirAccess> DirAccess::open(const String &p_path, Error *r_error) { return da; } +Ref<DirAccess> DirAccess::_open(const String &p_path) { + Error err = OK; + Ref<DirAccess> da = open(p_path, &err); + last_dir_open_error = err; + if (err) { + return Ref<DirAccess>(); + } + return da; +} + +int DirAccess::_get_drive_count() { + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + return d->get_drive_count(); +} + +String DirAccess::get_drive_name(int p_idx) { + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + return d->get_drive(p_idx); +} + +Error DirAccess::make_dir_absolute(const String &p_dir) { + Ref<DirAccess> d = DirAccess::create_for_path(p_dir); + return d->make_dir(p_dir); +} + +Error DirAccess::make_dir_recursive_absolute(const String &p_dir) { + Ref<DirAccess> d = DirAccess::create_for_path(p_dir); + return d->make_dir_recursive(p_dir); +} + +bool DirAccess::dir_exists_absolute(const String &p_dir) { + Ref<DirAccess> d = DirAccess::create_for_path(p_dir); + return d->dir_exists(p_dir); +} + +Error DirAccess::copy_absolute(const String &p_from, const String &p_to, int p_chmod_flags) { + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + // Support copying from res:// to user:// etc. + String from = ProjectSettings::get_singleton()->globalize_path(p_from); + String to = ProjectSettings::get_singleton()->globalize_path(p_to); + return d->copy(from, to, p_chmod_flags); +} + +Error DirAccess::rename_absolute(const String &p_from, const String &p_to) { + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + String from = ProjectSettings::get_singleton()->globalize_path(p_from); + String to = ProjectSettings::get_singleton()->globalize_path(p_to); + return d->rename(from, to); +} + +Error DirAccess::remove_absolute(const String &p_path) { + Ref<DirAccess> d = DirAccess::create_for_path(p_path); + return d->remove(p_path); +} + Ref<DirAccess> DirAccess::create(AccessType p_access) { Ref<DirAccess> da = create_func[p_access] ? create_func[p_access]() : nullptr; if (da.is_valid()) { @@ -266,6 +323,10 @@ Ref<DirAccess> DirAccess::create(AccessType p_access) { return da; } +Error DirAccess::get_open_error() { + return last_dir_open_error; +} + String DirAccess::get_full_path(const String &p_path, AccessType p_access) { Ref<DirAccess> d = DirAccess::create(p_access); if (d.is_null()) { @@ -424,3 +485,102 @@ bool DirAccess::exists(String p_dir) { Ref<DirAccess> da = DirAccess::create_for_path(p_dir); return da->change_dir(p_dir) == OK; } + +PackedStringArray DirAccess::get_files() { + return _get_contents(false); +} + +PackedStringArray DirAccess::get_files_at(const String &p_path) { + Ref<DirAccess> da = DirAccess::open(p_path); + return da->get_files(); +} + +PackedStringArray DirAccess::get_directories() { + return _get_contents(true); +} + +PackedStringArray DirAccess::get_directories_at(const String &p_path) { + Ref<DirAccess> da = DirAccess::open(p_path); + return da->get_directories(); +} + +PackedStringArray DirAccess::_get_contents(bool p_directories) { + PackedStringArray ret; + + list_dir_begin(); + String s = _get_next(); + while (!s.is_empty()) { + if (current_is_dir() == p_directories) { + ret.append(s); + } + s = _get_next(); + } + + ret.sort(); + return ret; +} + +String DirAccess::_get_next() { + String next = get_next(); + while (!next.is_empty() && ((!include_navigational && (next == "." || next == "..")) || (!include_hidden && current_is_hidden()))) { + next = get_next(); + } + return next; +} + +void DirAccess::set_include_navigational(bool p_enable) { + include_navigational = p_enable; +} + +bool DirAccess::get_include_navigational() const { + return include_navigational; +} + +void DirAccess::set_include_hidden(bool p_enable) { + include_hidden = p_enable; +} + +bool DirAccess::get_include_hidden() const { + return include_hidden; +} + +void DirAccess::_bind_methods() { + ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open); + ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error); + + ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin, DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_next"), &DirAccess::_get_next); + ClassDB::bind_method(D_METHOD("current_is_dir"), &DirAccess::current_is_dir); + ClassDB::bind_method(D_METHOD("list_dir_end"), &DirAccess::list_dir_end); + ClassDB::bind_method(D_METHOD("get_files"), &DirAccess::get_files); + ClassDB::bind_static_method("DirAccess", D_METHOD("get_files_at", "path"), &DirAccess::get_files_at); + ClassDB::bind_method(D_METHOD("get_directories"), &DirAccess::get_directories); + ClassDB::bind_static_method("DirAccess", D_METHOD("get_directories_at", "path"), &DirAccess::get_directories_at); + ClassDB::bind_static_method("DirAccess", D_METHOD("get_drive_count"), &DirAccess::_get_drive_count); + ClassDB::bind_static_method("DirAccess", D_METHOD("get_drive_name", "idx"), &DirAccess::get_drive_name); + ClassDB::bind_method(D_METHOD("get_current_drive"), &DirAccess::get_current_drive); + ClassDB::bind_method(D_METHOD("change_dir", "to_dir"), &DirAccess::change_dir); + ClassDB::bind_method(D_METHOD("get_current_dir", "include_drive"), &DirAccess::get_current_dir, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("make_dir", "path"), &DirAccess::make_dir); + ClassDB::bind_static_method("DirAccess", D_METHOD("make_dir_absolute", "path"), &DirAccess::make_dir_absolute); + ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &DirAccess::make_dir_recursive); + ClassDB::bind_static_method("DirAccess", D_METHOD("make_dir_recursive_absolute", "path"), &DirAccess::make_dir_recursive_absolute); + ClassDB::bind_method(D_METHOD("file_exists", "path"), &DirAccess::file_exists); + ClassDB::bind_method(D_METHOD("dir_exists", "path"), &DirAccess::dir_exists); + ClassDB::bind_static_method("DirAccess", D_METHOD("dir_exists_absolute", "path"), &DirAccess::dir_exists_absolute); + ClassDB::bind_method(D_METHOD("get_space_left"), &DirAccess::get_space_left); + ClassDB::bind_method(D_METHOD("copy", "from", "to", "chmod_flags"), &DirAccess::copy, DEFVAL(-1)); + ClassDB::bind_static_method("DirAccess", D_METHOD("copy_absolute", "from", "to", "chmod_flags"), &DirAccess::copy_absolute, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("rename", "from", "to"), &DirAccess::rename); + ClassDB::bind_static_method("DirAccess", D_METHOD("rename_absolute", "from", "to"), &DirAccess::rename_absolute); + ClassDB::bind_method(D_METHOD("remove", "path"), &DirAccess::remove); + ClassDB::bind_static_method("DirAccess", D_METHOD("remove_absolute", "path"), &DirAccess::remove_absolute); + + ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational); + ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational); + ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden); + ClassDB::bind_method(D_METHOD("get_include_hidden"), &DirAccess::get_include_hidden); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden"); +} diff --git a/core/io/dir_access.h b/core/io/dir_access.h index 2469c2a080..ee675f1c89 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -37,6 +37,8 @@ //@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies class DirAccess : public RefCounted { + GDCLASS(DirAccess, RefCounted); + public: enum AccessType { ACCESS_RESOURCES, @@ -53,7 +55,13 @@ private: Error _copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod_flags, bool p_copy_links); + thread_local static Error last_dir_open_error; + bool include_navigational = false; + bool include_hidden = false; + protected: + static void _bind_methods(); + String _get_root_path() const; virtual String _get_root_string() const; @@ -118,6 +126,7 @@ public: static Ref<DirAccess> create_for_path(const String &p_path); static Ref<DirAccess> create(AccessType p_access); + static Error get_open_error(); template <class T> static void make_default(AccessType p_access) { @@ -125,6 +134,30 @@ public: } static Ref<DirAccess> open(const String &p_path, Error *r_error = nullptr); + static Ref<DirAccess> _open(const String &p_path); + + static int _get_drive_count(); + static String get_drive_name(int p_idx); + + static Error make_dir_absolute(const String &p_dir); + static Error make_dir_recursive_absolute(const String &p_dir); + static bool dir_exists_absolute(const String &p_dir); + + static Error copy_absolute(const String &p_from, const String &p_to, int p_chmod_flags = -1); + static Error rename_absolute(const String &p_from, const String &p_to); + static Error remove_absolute(const String &p_path); + + PackedStringArray get_files(); + static PackedStringArray get_files_at(const String &p_path); + PackedStringArray get_directories(); + static PackedStringArray get_directories_at(const String &p_path); + PackedStringArray _get_contents(bool p_directories); + String _get_next(); + + void set_include_navigational(bool p_enable); + bool get_include_navigational() const; + void set_include_hidden(bool p_enable); + bool get_include_hidden() const; DirAccess() {} virtual ~DirAccess() {} diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index 72c00bd678..499f083f51 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -32,6 +32,8 @@ #include "core/config/project_settings.h" #include "core/crypto/crypto_core.h" +#include "core/io/file_access_compressed.h" +#include "core/io/file_access_encrypted.h" #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" #include "core/os/os.h" @@ -41,6 +43,7 @@ FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { nullptr, nullptr FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr; bool FileAccess::backup_save = false; +thread_local Error FileAccess::last_file_open_error = OK; Ref<FileAccess> FileAccess::create(AccessType p_access) { ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr); @@ -81,7 +84,7 @@ Ref<FileAccess> FileAccess::create_for_path(const String &p_path) { } Error FileAccess::reopen(const String &p_path, int p_mode_flags) { - return _open(p_path, p_mode_flags); + return open_internal(p_path, p_mode_flags); } Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) { @@ -99,7 +102,7 @@ Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error * } ret = create_for_path(p_path); - Error err = ret->_open(p_path, p_mode_flags); + Error err = ret->open_internal(p_path, p_mode_flags); if (r_error) { *r_error = err; @@ -111,6 +114,66 @@ Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error * return ret; } +Ref<FileAccess> FileAccess::_open(const String &p_path, ModeFlags p_mode_flags) { + Error err = OK; + Ref<FileAccess> fa = open(p_path, p_mode_flags, &err); + last_file_open_error = err; + if (err) { + return Ref<FileAccess>(); + } + return fa; +} + +Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) { + Ref<FileAccess> fa = _open(p_path, p_mode_flags); + if (fa.is_null()) { + return fa; + } + + Ref<FileAccessEncrypted> fae; + fae.instantiate(); + Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ); + if (err) { + last_file_open_error = err; + return Ref<FileAccess>(); + } + return fae; +} + +Ref<FileAccess> FileAccess::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) { + Ref<FileAccess> fa = _open(p_path, p_mode_flags); + if (fa.is_null()) { + return fa; + } + + Ref<FileAccessEncrypted> fae; + fae.instantiate(); + Error err = fae->open_and_parse_password(fa, p_pass, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ); + if (err) { + last_file_open_error = err; + return Ref<FileAccess>(); + } + return fae; +} + +Ref<FileAccess> FileAccess::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) { + Ref<FileAccessCompressed> fac; + fac.instantiate(); + fac->configure("GCPF", (Compression::Mode)p_compress_mode); + Error err = fac->open_internal(p_path, p_mode_flags); + + if (err) { + last_file_open_error = err; + return Ref<FileAccess>(); + } + + return fac; +} + +Error FileAccess::get_open_error() { + return last_file_open_error; +} + FileAccess::CreateFunc FileAccess::get_create_func(AccessType p_access) { return create_func[p_access]; } @@ -227,6 +290,20 @@ real_t FileAccess::get_real() const { } } +Variant FileAccess::get_var(bool p_allow_objects) const { + uint32_t len = get_32(); + Vector<uint8_t> buff = _get_buffer(len); + ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant()); + + const uint8_t *r = buff.ptr(); + + Variant v; + Error err = decode_variant(v, &r[0], len, nullptr, p_allow_objects); + ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to encode Variant."); + + return v; +} + double FileAccess::get_double() const { MarshallDouble m; m.l = get_64(); @@ -370,6 +447,17 @@ Vector<String> FileAccess::get_csv_line(const String &p_delim) const { return strings; } +String FileAccess::get_as_text(bool p_skip_cr) const { + uint64_t original_pos = get_position(); + const_cast<FileAccess *>(this)->seek(0); + + String text = get_as_utf8_string(p_skip_cr); + + const_cast<FileAccess *>(this)->seek(original_pos); + + return text; +} + uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); @@ -381,6 +469,27 @@ uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const { return i; } +Vector<uint8_t> FileAccess::_get_buffer(int64_t p_length) const { + Vector<uint8_t> data; + + ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0."); + if (p_length == 0) { + return data; + } + + Error err = data.resize(p_length); + ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements."); + + uint8_t *w = data.ptrw(); + int64_t len = get_buffer(&w[0], p_length); + + if (len < p_length) { + data.resize(len); + } + + return data; +} + String FileAccess::get_as_utf8_string(bool p_skip_cr) const { Vector<uint8_t> sourcef; uint64_t len = get_length(); @@ -554,6 +663,33 @@ void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) { } } +void FileAccess::_store_buffer(const Vector<uint8_t> &p_buffer) { + uint64_t len = p_buffer.size(); + if (len == 0) { + return; + } + + const uint8_t *r = p_buffer.ptr(); + + store_buffer(&r[0], len); +} + +void FileAccess::store_var(const Variant &p_var, bool p_full_objects) { + int len; + Error err = encode_variant(p_var, nullptr, len, p_full_objects); + ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); + + Vector<uint8_t> buff; + buff.resize(len); + + uint8_t *w = buff.ptrw(); + err = encode_variant(p_var, &w[0], len, p_full_objects); + ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant."); + + store_32(len); + _store_buffer(buff); +} + Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) { Ref<FileAccess> f = FileAccess::open(p_path, READ, r_error); if (f.is_null()) { @@ -666,3 +802,69 @@ String FileAccess::get_sha256(const String &p_file) { return String::hex_encode_buffer(hash, 32); } + +void FileAccess::_bind_methods() { + ClassDB::bind_static_method("FileAccess", D_METHOD("open", "path", "flags"), &FileAccess::_open); + ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::open_encrypted); + ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass); + ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0)); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error); + + ClassDB::bind_method(D_METHOD("flush"), &FileAccess::flush); + ClassDB::bind_method(D_METHOD("get_path"), &FileAccess::get_path); + ClassDB::bind_method(D_METHOD("get_path_absolute"), &FileAccess::get_path_absolute); + ClassDB::bind_method(D_METHOD("is_open"), &FileAccess::is_open); + ClassDB::bind_method(D_METHOD("seek", "position"), &FileAccess::seek); + ClassDB::bind_method(D_METHOD("seek_end", "position"), &FileAccess::seek_end, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("get_position"), &FileAccess::get_position); + ClassDB::bind_method(D_METHOD("get_length"), &FileAccess::get_length); + ClassDB::bind_method(D_METHOD("eof_reached"), &FileAccess::eof_reached); + ClassDB::bind_method(D_METHOD("get_8"), &FileAccess::get_8); + ClassDB::bind_method(D_METHOD("get_16"), &FileAccess::get_16); + ClassDB::bind_method(D_METHOD("get_32"), &FileAccess::get_32); + ClassDB::bind_method(D_METHOD("get_64"), &FileAccess::get_64); + ClassDB::bind_method(D_METHOD("get_float"), &FileAccess::get_float); + ClassDB::bind_method(D_METHOD("get_double"), &FileAccess::get_double); + ClassDB::bind_method(D_METHOD("get_real"), &FileAccess::get_real); + ClassDB::bind_method(D_METHOD("get_buffer", "length"), &FileAccess::_get_buffer); + ClassDB::bind_method(D_METHOD("get_line"), &FileAccess::get_line); + ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &FileAccess::get_csv_line, DEFVAL(",")); + ClassDB::bind_method(D_METHOD("get_as_text", "skip_cr"), &FileAccess::get_as_text, DEFVAL(false)); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_md5", "path"), &FileAccess::get_md5); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_sha256", "path"), &FileAccess::get_sha256); + ClassDB::bind_method(D_METHOD("is_big_endian"), &FileAccess::is_big_endian); + ClassDB::bind_method(D_METHOD("set_big_endian", "big_endian"), &FileAccess::set_big_endian); + ClassDB::bind_method(D_METHOD("get_error"), &FileAccess::get_error); + ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &FileAccess::get_var, DEFVAL(false)); + + ClassDB::bind_method(D_METHOD("store_8", "value"), &FileAccess::store_8); + ClassDB::bind_method(D_METHOD("store_16", "value"), &FileAccess::store_16); + ClassDB::bind_method(D_METHOD("store_32", "value"), &FileAccess::store_32); + ClassDB::bind_method(D_METHOD("store_64", "value"), &FileAccess::store_64); + ClassDB::bind_method(D_METHOD("store_float", "value"), &FileAccess::store_float); + ClassDB::bind_method(D_METHOD("store_double", "value"), &FileAccess::store_double); + ClassDB::bind_method(D_METHOD("store_real", "value"), &FileAccess::store_real); + ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &FileAccess::_store_buffer); + ClassDB::bind_method(D_METHOD("store_line", "line"), &FileAccess::store_line); + ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line, DEFVAL(",")); + ClassDB::bind_method(D_METHOD("store_string", "string"), &FileAccess::store_string); + ClassDB::bind_method(D_METHOD("store_var", "value", "full_objects"), &FileAccess::store_var, DEFVAL(false)); + + ClassDB::bind_method(D_METHOD("store_pascal_string", "string"), &FileAccess::store_pascal_string); + ClassDB::bind_method(D_METHOD("get_pascal_string"), &FileAccess::get_pascal_string); + + ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists); + ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian"); + + BIND_ENUM_CONSTANT(READ); + BIND_ENUM_CONSTANT(WRITE); + BIND_ENUM_CONSTANT(READ_WRITE); + BIND_ENUM_CONSTANT(WRITE_READ); + + BIND_ENUM_CONSTANT(COMPRESSION_FASTLZ); + BIND_ENUM_CONSTANT(COMPRESSION_DEFLATE); + BIND_ENUM_CONSTANT(COMPRESSION_ZSTD); + BIND_ENUM_CONSTANT(COMPRESSION_GZIP); +} diff --git a/core/io/file_access.h b/core/io/file_access.h index fc0eb95d44..f8c42d3c0d 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -31,6 +31,7 @@ #ifndef FILE_ACCESS_H #define FILE_ACCESS_H +#include "core/io/compression.h" #include "core/math/math_defs.h" #include "core/object/ref_counted.h" #include "core/os/memory.h" @@ -42,6 +43,8 @@ */ class FileAccess : public RefCounted { + GDCLASS(FileAccess, RefCounted); + public: enum AccessType { ACCESS_RESOURCES, @@ -60,15 +63,18 @@ public: virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0; protected: + static void _bind_methods(); + AccessType get_access_type() const; String fix_path(const String &p_path) const; - virtual Error _open(const String &p_path, int p_mode_flags) = 0; ///< open a file + virtual Error open_internal(const String &p_path, int p_mode_flags) = 0; ///< open a file virtual uint64_t _get_modified_time(const String &p_file) = 0; static FileCloseFailNotify close_fail_notify; private: static bool backup_save; + thread_local static Error last_file_open_error; AccessType _access_type = ACCESS_FILESYSTEM; static CreateFunc create_func[ACCESS_MAX]; /** default file access creation function for a platform */ @@ -89,6 +95,13 @@ public: WRITE_READ = 7, }; + enum CompressionMode { + COMPRESSION_FASTLZ = Compression::MODE_FASTLZ, + COMPRESSION_DEFLATE = Compression::MODE_DEFLATE, + COMPRESSION_ZSTD = Compression::MODE_ZSTD, + COMPRESSION_GZIP = Compression::MODE_GZIP + }; + virtual bool is_open() const = 0; ///< true when file is open virtual String get_path() const { return ""; } /// returns the path for the current open file @@ -110,10 +123,14 @@ public: virtual double get_double() const; virtual real_t get_real() const; + Variant get_var(bool p_allow_objects = false) const; + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes + Vector<uint8_t> _get_buffer(int64_t p_length) const; virtual String get_line() const; virtual String get_token() const; virtual Vector<String> get_csv_line(const String &p_delim = ",") const; + String get_as_text(bool p_skip_cr = false) const; virtual String get_as_utf8_string(bool p_skip_cr = false) const; /** @@ -144,6 +161,9 @@ public: virtual String get_pascal_string(); virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes + void _store_buffer(const Vector<uint8_t> &p_buffer); + + void store_var(const Variant &p_var, bool p_full_objects = false); virtual bool file_exists(const String &p_name) = 0; ///< return true if a file exists @@ -152,6 +172,13 @@ public: static Ref<FileAccess> create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files. static Ref<FileAccess> create_for_path(const String &p_path); static Ref<FileAccess> open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files. + + static Ref<FileAccess> _open(const String &p_path, ModeFlags p_mode_flags); + static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key); + static Ref<FileAccess> open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass); + static Ref<FileAccess> open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ); + static Error get_open_error(); + static CreateFunc get_create_func(AccessType p_access); static bool exists(const String &p_name); ///< return true if a file exists static uint64_t get_modified_time(const String &p_file); @@ -177,4 +204,7 @@ public: virtual ~FileAccess() {} }; +VARIANT_ENUM_CAST(FileAccess::CompressionMode); +VARIANT_ENUM_CAST(FileAccess::ModeFlags); + #endif // FILE_ACCESS_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 1d0a718166..d2c8a88269 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -95,7 +95,7 @@ Error FileAccessCompressed::open_after_magic(Ref<FileAccess> p_base) { return ret == -1 ? ERR_FILE_CORRUPT : OK; } -Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { +Error FileAccessCompressed::open_internal(const String &p_path, int p_mode_flags) { ERR_FAIL_COND_V(p_mode_flags == READ_WRITE, ERR_UNAVAILABLE); _close(); diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index e41491a92c..ee114c2c65 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -70,7 +70,7 @@ public: Error open_after_magic(Ref<FileAccess> p_base); - virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file virtual bool is_open() const override; ///< true when file is open virtual void seek(uint64_t p_position) override; ///< seek to a given position diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index d1b014a0be..be502dacd9 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -111,7 +111,7 @@ Error FileAccessEncrypted::open_and_parse_password(Ref<FileAccess> p_base, const return open_and_parse(p_base, key, p_mode); } -Error FileAccessEncrypted::_open(const String &p_path, int p_mode_flags) { +Error FileAccessEncrypted::open_internal(const String &p_path, int p_mode_flags) { return OK; } diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 6200f87a7a..6b4588841d 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -60,7 +60,7 @@ public: Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true); Error open_and_parse_password(Ref<FileAccess> p_base, const String &p_key, Mode p_mode); - virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file virtual bool is_open() const override; ///< true when file is open virtual String get_path() const override; /// returns the path for the current open file diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 499d001234..21ded4247f 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -78,7 +78,7 @@ Error FileAccessMemory::open_custom(const uint8_t *p_data, uint64_t p_len) { return OK; } -Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { +Error FileAccessMemory::open_internal(const String &p_path, int p_mode_flags) { ERR_FAIL_COND_V(!files, ERR_FILE_NOT_FOUND); String name = fix_path(p_path); diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index f2bd2aa832..b1f408eb98 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -45,7 +45,7 @@ public: static void cleanup(); virtual Error open_custom(const uint8_t *p_data, uint64_t p_len); ///< open a file - virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file virtual bool is_open() const override; ///< true when file is open virtual void seek(uint64_t p_position) override; ///< seek to a given position diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 1365b4b593..13730518bf 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -252,7 +252,7 @@ void FileAccessNetwork::_respond(uint64_t p_len, Error p_status) { pages.resize(pc); } -Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) { +Error FileAccessNetwork::open_internal(const String &p_path, int p_mode_flags) { ERR_FAIL_COND_V(p_mode_flags != READ, ERR_UNAVAILABLE); _close(); diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index ceadc715a1..ee92d3b9db 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -132,7 +132,7 @@ public: RESPONSE_GET_MODTIME, }; - virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file virtual bool is_open() const override; ///< true when file is open virtual void seek(uint64_t p_position) override; ///< seek to a given position diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index adae0db0f4..dfcce30ab5 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -259,7 +259,7 @@ Ref<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::Pack ////////////////////////////////////////////////////////////////// -Error FileAccessPack::_open(const String &p_path, int p_mode_flags) { +Error FileAccessPack::open_internal(const String &p_path, int p_mode_flags) { ERR_FAIL_V(ERR_UNAVAILABLE); return ERR_UNAVAILABLE; } diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 023758ac0f..4b9b49a161 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -148,7 +148,7 @@ class FileAccessPack : public FileAccess { uint64_t off; Ref<FileAccess> f; - virtual Error _open(const String &p_path, int p_mode_flags) override; + virtual Error open_internal(const String &p_path, int p_mode_flags) override; virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; } virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; } diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index 17f2335a8e..2af6f370cf 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -234,7 +234,7 @@ ZipArchive::~ZipArchive() { packages.clear(); } -Error FileAccessZip::_open(const String &p_path, int p_mode_flags) { +Error FileAccessZip::open_internal(const String &p_path, int p_mode_flags) { _close(); ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, FAILED); diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index 74a48192f3..6d61b9a291 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -85,7 +85,7 @@ class FileAccessZip : public FileAccess { void _close(); public: - virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file virtual bool is_open() const override; ///< true when file is open virtual void seek(uint64_t p_position) override; ///< seek to a given position diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 52b1120b2a..93a310e83b 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -138,7 +138,7 @@ PackedStringArray HTTPClient::_get_response_headers() { } void HTTPClient::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_ssl", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_tls", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_connection", "connection"), &HTTPClient::set_connection); ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection); ClassDB::bind_method(D_METHOD("request_raw", "method", "url", "headers", "body"), &HTTPClient::_request_raw); @@ -190,7 +190,7 @@ void HTTPClient::_bind_methods() { BIND_ENUM_CONSTANT(STATUS_REQUESTING); // Request in progress BIND_ENUM_CONSTANT(STATUS_BODY); // Request resulted in body which must be read BIND_ENUM_CONSTANT(STATUS_CONNECTION_ERROR); - BIND_ENUM_CONSTANT(STATUS_SSL_HANDSHAKE_ERROR); + BIND_ENUM_CONSTANT(STATUS_TLS_HANDSHAKE_ERROR); BIND_ENUM_CONSTANT(RESPONSE_CONTINUE); BIND_ENUM_CONSTANT(RESPONSE_SWITCHING_PROTOCOLS); diff --git a/core/io/http_client.h b/core/io/http_client.h index de6045f647..0524b010f4 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -138,7 +138,7 @@ public: STATUS_REQUESTING, // Request in progress STATUS_BODY, // Request resulted in body, which must be read STATUS_CONNECTION_ERROR, - STATUS_SSL_HANDSHAKE_ERROR, + STATUS_TLS_HANDSHAKE_ERROR, }; @@ -168,7 +168,7 @@ public: Error verify_headers(const Vector<String> &p_headers); virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0; - virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) = 0; + virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) = 0; virtual void set_connection(const Ref<StreamPeer> &p_connection) = 0; virtual Ref<StreamPeer> get_connection() const = 0; diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index 7afab9ea09..5c1d00a330 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -39,7 +39,7 @@ HTTPClient *HTTPClientTCP::_create_func() { return memnew(HTTPClientTCP); } -Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) { +Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_tls, bool p_verify_host) { close(); conn_port = p_port; @@ -47,21 +47,21 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss ip_candidates.clear(); - ssl = p_ssl; - ssl_verify_host = p_verify_host; + tls = p_tls; + tls_verify_host = p_verify_host; String host_lower = conn_host.to_lower(); if (host_lower.begins_with("http://")) { conn_host = conn_host.substr(7, conn_host.length() - 7); } else if (host_lower.begins_with("https://")) { - ssl = true; + tls = true; conn_host = conn_host.substr(8, conn_host.length() - 8); } ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER); if (conn_port < 0) { - if (ssl) { + if (tls) { conn_port = PORT_HTTPS; } else { conn_port = PORT_HTTP; @@ -70,11 +70,11 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss connection = tcp_connection; - if (ssl && https_proxy_port != -1) { + if (tls && https_proxy_port != -1) { proxy_client.instantiate(); // Needs proxy negotiation. server_host = https_proxy_host; server_port = https_proxy_port; - } else if (!ssl && http_proxy_port != -1) { + } else if (!tls && http_proxy_port != -1) { server_host = http_proxy_host; server_port = http_proxy_port; } else { @@ -107,7 +107,7 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss void HTTPClientTCP::set_connection(const Ref<StreamPeer> &p_connection) { ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object."); - if (ssl) { + if (tls) { ERR_FAIL_NULL_MSG(Object::cast_to<StreamPeerTLS>(p_connection.ptr()), "Connection is not a reference to a valid StreamPeerTLS object."); } @@ -156,7 +156,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< } String uri = p_url; - if (!ssl && http_proxy_port != -1) { + if (!tls && http_proxy_port != -1) { uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url); } @@ -181,7 +181,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< } } if (add_host) { - if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) { + if ((tls && conn_port == PORT_HTTPS) || (!tls && conn_port == PORT_HTTP)) { // Don't append the standard ports. request += "Host: " + conn_host + "\r\n"; } else { @@ -316,7 +316,7 @@ Error HTTPClientTCP::poll() { return OK; } break; case StreamPeerTCP::STATUS_CONNECTED: { - if (ssl && proxy_client.is_valid()) { + if (tls && proxy_client.is_valid()) { Error err = proxy_client->poll(); if (err == ERR_UNCONFIGURED) { proxy_client->set_connection(tcp_connection); @@ -357,42 +357,42 @@ Error HTTPClientTCP::poll() { return ERR_CANT_CONNECT; } break; } - } else if (ssl) { - Ref<StreamPeerTLS> ssl; + } else if (tls) { + Ref<StreamPeerTLS> tls; if (!handshaking) { // Connect the StreamPeerTLS and start handshaking. - ssl = Ref<StreamPeerTLS>(StreamPeerTLS::create()); - ssl->set_blocking_handshake_enabled(false); - Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); + tls = Ref<StreamPeerTLS>(StreamPeerTLS::create()); + tls->set_blocking_handshake_enabled(false); + Error err = tls->connect_to_stream(tcp_connection, tls_verify_host, conn_host); if (err != OK) { close(); - status = STATUS_SSL_HANDSHAKE_ERROR; + status = STATUS_TLS_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - connection = ssl; + connection = tls; handshaking = true; } else { - // We are already handshaking, which means we can use your already active SSL connection. - ssl = static_cast<Ref<StreamPeerTLS>>(connection); - if (ssl.is_null()) { + // We are already handshaking, which means we can use your already active TLS connection. + tls = static_cast<Ref<StreamPeerTLS>>(connection); + if (tls.is_null()) { close(); - status = STATUS_SSL_HANDSHAKE_ERROR; + status = STATUS_TLS_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - ssl->poll(); // Try to finish the handshake. + tls->poll(); // Try to finish the handshake. } - if (ssl->get_status() == StreamPeerTLS::STATUS_CONNECTED) { + if (tls->get_status() == StreamPeerTLS::STATUS_CONNECTED) { // Handshake has been successful. handshaking = false; ip_candidates.clear(); status = STATUS_CONNECTED; return OK; - } else if (ssl->get_status() != StreamPeerTLS::STATUS_HANDSHAKING) { + } else if (tls->get_status() != StreamPeerTLS::STATUS_HANDSHAKING) { // Handshake has failed. close(); - status = STATUS_SSL_HANDSHAKE_ERROR; + status = STATUS_TLS_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } // ... we will need to poll more for handshake to finish. @@ -421,7 +421,7 @@ Error HTTPClientTCP::poll() { case STATUS_BODY: case STATUS_CONNECTED: { // Check if we are still connected. - if (ssl) { + if (tls) { Ref<StreamPeerTLS> tmp = connection; tmp->poll(); if (tmp->get_status() != StreamPeerTLS::STATUS_CONNECTED) { @@ -548,7 +548,7 @@ Error HTTPClientTCP::poll() { return ERR_UNCONFIGURED; } break; case STATUS_CONNECTION_ERROR: - case STATUS_SSL_HANDSHAKE_ERROR: { + case STATUS_TLS_HANDSHAKE_ERROR: { return ERR_CONNECTION_ERROR; } break; case STATUS_CANT_CONNECT: { diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h index c10e0b1eca..744c15f7ab 100644 --- a/core/io/http_client_tcp.h +++ b/core/io/http_client_tcp.h @@ -46,8 +46,8 @@ private: String http_proxy_host; int https_proxy_port = -1; // Proxy server for https requests. String https_proxy_host; - bool ssl = false; - bool ssl_verify_host = false; + bool tls = false; + bool tls_verify_host = false; bool blocking = false; bool handshaking = false; bool head_request = false; @@ -79,7 +79,7 @@ public: Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override; - Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) override; + Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) override; void set_connection(const Ref<StreamPeer> &p_connection) override; Ref<StreamPeer> get_connection() const override; void close() override; diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index d09697b951..d6854666c0 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -32,6 +32,12 @@ #include "core/string/print_string.h" +void ImageFormatLoader::_bind_methods() { + BIND_BITFIELD_FLAG(FLAG_NONE); + BIND_BITFIELD_FLAG(FLAG_FORCE_LINEAR); + BIND_BITFIELD_FLAG(FLAG_CONVERT_COLORS); +} + bool ImageFormatLoader::recognize(const String &p_extension) const { List<String> extensions; get_recognized_extensions(&extensions); @@ -44,7 +50,39 @@ bool ImageFormatLoader::recognize(const String &p_extension) const { return false; } -Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, uint32_t p_flags, float p_scale) { +Error ImageFormatLoaderExtension::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) { + Error err; + if (GDVIRTUAL_CALL(_load_image, p_image, p_fileaccess, p_flags, p_scale, err)) { + return err; + } + return ERR_UNAVAILABLE; +} + +void ImageFormatLoaderExtension::get_recognized_extensions(List<String> *p_extension) const { + PackedStringArray ext; + if (GDVIRTUAL_CALL(_get_recognized_extensions, ext)) { + for (int i = 0; i < ext.size(); i++) { + p_extension->push_back(ext[i]); + } + } +} + +void ImageFormatLoaderExtension::add_format_loader() { + ImageLoader::add_image_format_loader(this); +} + +void ImageFormatLoaderExtension::remove_format_loader() { + ImageLoader::remove_image_format_loader(this); +} + +void ImageFormatLoaderExtension::_bind_methods() { + GDVIRTUAL_BIND(_get_recognized_extensions); + GDVIRTUAL_BIND(_load_image, "image", "fileaccess", "flags", "scale"); + ClassDB::bind_method(D_METHOD("add_format_loader"), &ImageFormatLoaderExtension::add_format_loader); + ClassDB::bind_method(D_METHOD("remove_format_loader"), &ImageFormatLoaderExtension::remove_format_loader); +} + +Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) { ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "It's not a reference to a valid Image object."); Ref<FileAccess> f = p_custom; @@ -60,7 +98,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> if (!loader[i]->recognize(extension)) { continue; } - Error err = loader[i]->load_image(p_image, f, p_flags, p_scale); + Error err = loader.write[i]->load_image(p_image, f, p_flags, p_scale); if (err != OK) { ERR_PRINT("Error loading image: " + p_file); } @@ -79,7 +117,7 @@ void ImageLoader::get_recognized_extensions(List<String> *p_extensions) { } } -ImageFormatLoader *ImageLoader::recognize(const String &p_extension) { +Ref<ImageFormatLoader> ImageLoader::recognize(const String &p_extension) { for (int i = 0; i < loader.size(); i++) { if (loader[i]->recognize(p_extension)) { return loader[i]; @@ -89,17 +127,17 @@ ImageFormatLoader *ImageLoader::recognize(const String &p_extension) { return nullptr; } -Vector<ImageFormatLoader *> ImageLoader::loader; +Vector<Ref<ImageFormatLoader>> ImageLoader::loader; -void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { +void ImageLoader::add_image_format_loader(Ref<ImageFormatLoader> p_loader) { loader.push_back(p_loader); } -void ImageLoader::remove_image_format_loader(ImageFormatLoader *p_loader) { +void ImageLoader::remove_image_format_loader(Ref<ImageFormatLoader> p_loader) { loader.erase(p_loader); } -const Vector<ImageFormatLoader *> &ImageLoader::get_image_format_loaders() { +const Vector<Ref<ImageFormatLoader>> &ImageLoader::get_image_format_loaders() { return loader; } @@ -152,7 +190,7 @@ Ref<Resource> ResourceFormatLoaderImage::load(const String &p_path, const String Ref<Image> image; image.instantiate(); - Error err = ImageLoader::loader[idx]->load_image(image, f); + Error err = ImageLoader::loader.write[idx]->load_image(image, f); if (err != OK) { if (r_error) { diff --git a/core/io/image_loader.h b/core/io/image_loader.h index cb64d2310e..f70fdf22aa 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -31,46 +31,74 @@ #ifndef IMAGE_LOADER_H #define IMAGE_LOADER_H +#include "core/core_bind.h" #include "core/io/file_access.h" #include "core/io/image.h" #include "core/io/resource_loader.h" +#include "core/object/gdvirtual.gen.inc" #include "core/string/ustring.h" #include "core/templates/list.h" +#include "core/variant/binder_common.h" class ImageLoader; -class ImageFormatLoader { +class ImageFormatLoader : public RefCounted { + GDCLASS(ImageFormatLoader, RefCounted); + friend class ImageLoader; friend class ResourceFormatLoaderImage; -protected: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags = (uint32_t)FLAG_NONE, float p_scale = 1.0) = 0; - virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; - bool recognize(const String &p_extension) const; - public: enum LoaderFlags { FLAG_NONE = 0, FLAG_FORCE_LINEAR = 1, + FLAG_CONVERT_COLORS = 2, }; +protected: + static void _bind_methods(); + + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags = FLAG_NONE, float p_scale = 1.0) = 0; + virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; + bool recognize(const String &p_extension) const; + +public: virtual ~ImageFormatLoader() {} }; +VARIANT_BITFIELD_CAST(ImageFormatLoader::LoaderFlags); + +class ImageFormatLoaderExtension : public ImageFormatLoader { + GDCLASS(ImageFormatLoaderExtension, ImageFormatLoader); + +protected: + static void _bind_methods(); + +public: + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags = FLAG_NONE, float p_scale = 1.0) override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + + void add_format_loader(); + void remove_format_loader(); + + GDVIRTUAL0RC(PackedStringArray, _get_recognized_extensions); + GDVIRTUAL4R(Error, _load_image, Ref<Image>, Ref<FileAccess>, BitField<ImageFormatLoader::LoaderFlags>, float); +}; + class ImageLoader { - static Vector<ImageFormatLoader *> loader; + static Vector<Ref<ImageFormatLoader>> loader; friend class ResourceFormatLoaderImage; protected: public: - static Error load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), uint32_t p_flags = (uint32_t)ImageFormatLoader::FLAG_NONE, float p_scale = 1.0); + static Error load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), BitField<ImageFormatLoader::LoaderFlags> p_flags = ImageFormatLoader::FLAG_NONE, float p_scale = 1.0); static void get_recognized_extensions(List<String> *p_extensions); - static ImageFormatLoader *recognize(const String &p_extension); + static Ref<ImageFormatLoader> recognize(const String &p_extension); - static void add_image_format_loader(ImageFormatLoader *p_loader); - static void remove_image_format_loader(ImageFormatLoader *p_loader); + static void add_image_format_loader(Ref<ImageFormatLoader> p_loader); + static void remove_image_format_loader(Ref<ImageFormatLoader> p_loader); - static const Vector<ImageFormatLoader *> &get_image_format_loaders(); + static const Vector<Ref<ImageFormatLoader>> &get_image_format_loaders(); static void cleanup(); }; diff --git a/core/io/json.cpp b/core/io/json.cpp index 91500ff3d5..7e267d35d4 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -56,6 +56,8 @@ String JSON::_make_indent(const String &p_indent, int p_size) { } String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision) { + ERR_FAIL_COND_V_MSG(p_cur_indent > Variant::MAX_RECURSION_DEPTH, "...", "JSON structure is too deep. Bailing."); + String colon = ":"; String end_statement = ""; @@ -357,17 +359,22 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to return ERR_PARSE_ERROR; } -Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { +Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) { + if (p_depth > Variant::MAX_RECURSION_DEPTH) { + r_err_str = "JSON structure is too deep. Bailing."; + return ERR_OUT_OF_MEMORY; + } + if (token.type == TK_CURLY_BRACKET_OPEN) { Dictionary d; - Error err = _parse_object(d, p_str, index, p_len, line, r_err_str); + Error err = _parse_object(d, p_str, index, p_len, line, p_depth + 1, r_err_str); if (err) { return err; } value = d; } else if (token.type == TK_BRACKET_OPEN) { Array a; - Error err = _parse_array(a, p_str, index, p_len, line, r_err_str); + Error err = _parse_array(a, p_str, index, p_len, line, p_depth + 1, r_err_str); if (err) { return err; } @@ -396,7 +403,7 @@ Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, in return OK; } -Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { +Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) { Token token; bool need_comma = false; @@ -421,7 +428,7 @@ Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_ } Variant v; - err = _parse_value(v, token, p_str, index, p_len, line, r_err_str); + err = _parse_value(v, token, p_str, index, p_len, line, p_depth, r_err_str); if (err) { return err; } @@ -434,7 +441,7 @@ Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_ return ERR_PARSE_ERROR; } -Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) { +Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) { bool at_key = true; String key; Token token; @@ -483,7 +490,7 @@ Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, } Variant v; - err = _parse_value(v, token, p_str, index, p_len, line, r_err_str); + err = _parse_value(v, token, p_str, index, p_len, line, p_depth, r_err_str); if (err) { return err; } @@ -497,6 +504,10 @@ Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, return ERR_PARSE_ERROR; } +void JSON::set_data(const Variant &p_data) { + data = p_data; +} + Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) { const char32_t *str = p_json.ptr(); int idx = 0; @@ -510,7 +521,7 @@ Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_st return err; } - err = _parse_value(r_ret, token, str, idx, len, r_err_line, r_err_str); + err = _parse_value(r_ret, token, str, idx, len, r_err_line, 0, r_err_str); // Check if EOF is reached // or it's a type of the next token. @@ -557,6 +568,88 @@ void JSON::_bind_methods() { ClassDB::bind_method(D_METHOD("parse", "json_string"), &JSON::parse); ClassDB::bind_method(D_METHOD("get_data"), &JSON::get_data); + ClassDB::bind_method(D_METHOD("set_data", "data"), &JSON::set_data); ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line); ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message); + + ADD_PROPERTY(PropertyInfo(Variant::NIL, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), "set_data", "get_data"); // Ensures that it can be serialized as binary. +} + +//// + +//////////// + +Ref<Resource> ResourceFormatLoaderJSON::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { + if (r_error) { + *r_error = ERR_FILE_CANT_OPEN; + } + + if (!FileAccess::exists(p_path)) { + *r_error = ERR_FILE_NOT_FOUND; + return Ref<Resource>(); + } + + Ref<JSON> json; + json.instantiate(); + + Error err = json->parse(FileAccess::get_file_as_string(p_path)); + if (err != OK) { + if (r_error) { + *r_error = err; + } + ERR_PRINT("Error parsing JSON file at '" + p_path + "', on line " + itos(json->get_error_line()) + ": " + json->get_error_message()); + return Ref<Resource>(); + } + + if (r_error) { + *r_error = OK; + } + + return json; +} + +void ResourceFormatLoaderJSON::get_recognized_extensions(List<String> *p_extensions) const { + p_extensions->push_back("json"); +} + +bool ResourceFormatLoaderJSON::handles_type(const String &p_type) const { + return (p_type == "JSON"); +} + +String ResourceFormatLoaderJSON::get_resource_type(const String &p_path) const { + String el = p_path.get_extension().to_lower(); + if (el == "json") { + return "JSON"; + } + return ""; +} + +Error ResourceFormatSaverJSON::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { + Ref<JSON> json = p_resource; + ERR_FAIL_COND_V(json.is_null(), ERR_INVALID_PARAMETER); + + String source = JSON::stringify(json->get_data(), "\t", false, true); + + Error err; + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); + + ERR_FAIL_COND_V_MSG(err, err, "Cannot save json '" + p_path + "'."); + + file->store_string(source); + if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { + return ERR_CANT_CREATE; + } + + return OK; +} + +void ResourceFormatSaverJSON::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { + Ref<JSON> json = p_resource; + if (json.is_valid()) { + p_extensions->push_back("json"); + } +} + +bool ResourceFormatSaverJSON::recognize(const Ref<Resource> &p_resource) const { + return p_resource->get_class_name() == "JSON"; //only json, not inherited } diff --git a/core/io/json.h b/core/io/json.h index 840b1cc08a..829a5f922b 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -31,7 +31,9 @@ #ifndef JSON_H #define JSON_H -#include "core/object/ref_counted.h" +#include "core/io/resource.h" +#include "core/io/resource_loader.h" +#include "core/io/resource_saver.h" #include "core/variant/variant.h" class JSON : public RefCounted { @@ -72,9 +74,9 @@ class JSON : public RefCounted { static String _make_indent(const String &p_indent, int p_size); static String _stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision = false); static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str); - static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); - static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); - static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); + static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str); + static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str); + static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str); static Error _parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line); protected: @@ -86,8 +88,24 @@ public: static Variant parse_string(const String &p_json_string); inline Variant get_data() const { return data; } + void set_data(const Variant &p_data); inline int get_error_line() const { return err_line; } inline String get_error_message() const { return err_str; } }; +class ResourceFormatLoaderJSON : public ResourceFormatLoader { +public: + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); + 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; +}; + +class ResourceFormatSaverJSON : public ResourceFormatSaver { +public: + virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual bool recognize(const Ref<Resource> &p_resource) const; +}; + #endif // JSON_H diff --git a/core/io/resource.cpp b/core/io/resource.cpp index d117f86f39..553698f8a6 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -93,15 +93,14 @@ String Resource::get_path() const { String Resource::generate_scene_unique_id() { // Generate a unique enough hash, but still user-readable. // If it's not unique it does not matter because the saver will try again. - OS::Date date = OS::get_singleton()->get_date(); - OS::Time time = OS::get_singleton()->get_time(); + OS::DateTime dt = OS::get_singleton()->get_datetime(); uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec()); - hash = hash_murmur3_one_32(date.year, hash); - hash = hash_murmur3_one_32(date.month, hash); - hash = hash_murmur3_one_32(date.day, hash); - hash = hash_murmur3_one_32(time.hour, hash); - hash = hash_murmur3_one_32(time.minute, hash); - hash = hash_murmur3_one_32(time.second, hash); + hash = hash_murmur3_one_32(dt.year, hash); + hash = hash_murmur3_one_32(dt.month, hash); + hash = hash_murmur3_one_32(dt.day, hash); + hash = hash_murmur3_one_32(dt.hour, hash); + hash = hash_murmur3_one_32(dt.minute, hash); + hash = hash_murmur3_one_32(dt.second, hash); hash = hash_murmur3_one_32(Math::rand(), hash); static constexpr uint32_t characters = 5; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 4f1204fc48..06649aba5b 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -1206,7 +1206,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons Ref<FileAccessCompressed> facw; facw.instantiate(); facw->configure("RSCC"); - err = facw->_open(p_path + ".depren", FileAccess::WRITE); + err = facw->open_internal(p_path + ".depren", FileAccess::WRITE); ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".depren'."); fw = facw; @@ -1986,7 +1986,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re fac.instantiate(); fac->configure("RSCC"); f = fac; - err = fac->_open(p_path, FileAccess::WRITE); + err = fac->open_internal(p_path, FileAccess::WRITE); } else { f = FileAccess::open(p_path, FileAccess::WRITE, &err); } diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index aa7f96a047..d923522317 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -108,6 +108,15 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy } } +#ifdef TOOLS_ENABLED + if (r_path_and_type.metadata && !r_path_and_type.path.is_empty()) { + Dictionary metadata = r_path_and_type.metadata; + if (metadata.has("has_editor_variant")) { + r_path_and_type.path = r_path_and_type.path.get_basename() + ".editor." + r_path_and_type.path.get_extension(); + } + } +#endif + if (r_path_and_type.path.is_empty() || r_path_and_type.type.is_empty()) { return ERR_FILE_CORRUPT; } diff --git a/core/io/stream_peer_gzip.cpp b/core/io/stream_peer_gzip.cpp new file mode 100644 index 0000000000..ca8be2d62e --- /dev/null +++ b/core/io/stream_peer_gzip.cpp @@ -0,0 +1,209 @@ +/*************************************************************************/ +/* stream_peer_gzip.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 "core/io/stream_peer_gzip.h" + +#include "core/io/zip_io.h" +#include <zlib.h> + +void StreamPeerGZIP::_bind_methods() { + ClassDB::bind_method(D_METHOD("start_compression", "use_deflate", "buffer_size"), &StreamPeerGZIP::start_compression, DEFVAL(false), DEFVAL(65535)); + ClassDB::bind_method(D_METHOD("start_decompression", "use_deflate", "buffer_size"), &StreamPeerGZIP::start_decompression, DEFVAL(false), DEFVAL(65535)); + ClassDB::bind_method(D_METHOD("finish"), &StreamPeerGZIP::finish); + ClassDB::bind_method(D_METHOD("clear"), &StreamPeerGZIP::clear); +} + +StreamPeerGZIP::StreamPeerGZIP() { +} + +StreamPeerGZIP::~StreamPeerGZIP() { + _close(); +} + +void StreamPeerGZIP::_close() { + if (ctx) { + z_stream *strm = (z_stream *)ctx; + if (compressing) { + deflateEnd(strm); + } else { + inflateEnd(strm); + } + memfree(strm); + ctx = nullptr; + } +} + +void StreamPeerGZIP::clear() { + _close(); + rb.clear(); + buffer.clear(); +} + +Error StreamPeerGZIP::start_compression(bool p_is_deflate, int buffer_size) { + return _start(true, p_is_deflate, buffer_size); +} + +Error StreamPeerGZIP::start_decompression(bool p_is_deflate, int buffer_size) { + return _start(false, p_is_deflate, buffer_size); +} + +Error StreamPeerGZIP::_start(bool p_compress, bool p_is_deflate, int buffer_size) { + ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE); + clear(); + compressing = p_compress; + rb.resize(nearest_shift(buffer_size - 1)); + buffer.resize(1024); + + // Create ctx. + ctx = memalloc(sizeof(z_stream)); + z_stream &strm = *(z_stream *)ctx; + strm.next_in = Z_NULL; + strm.avail_in = 0; + strm.zalloc = zipio_alloc; + strm.zfree = zipio_free; + strm.opaque = Z_NULL; + int window_bits = p_is_deflate ? 15 : (15 + 16); + int err = Z_OK; + int level = Z_DEFAULT_COMPRESSION; + if (compressing) { + err = deflateInit2(&strm, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY); + } else { + err = inflateInit2(&strm, window_bits); + } + ERR_FAIL_COND_V(err != Z_OK, FAILED); + return OK; +} + +Error StreamPeerGZIP::_process(uint8_t *p_dst, int p_dst_size, const uint8_t *p_src, int p_src_size, int &r_consumed, int &r_out, bool p_close) { + ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED); + z_stream &strm = *(z_stream *)ctx; + strm.avail_in = p_src_size; + strm.avail_out = p_dst_size; + strm.next_in = (Bytef *)p_src; + strm.next_out = (Bytef *)p_dst; + int flush = p_close ? Z_FINISH : Z_NO_FLUSH; + if (compressing) { + int err = deflate(&strm, flush); + ERR_FAIL_COND_V(err != (p_close ? Z_STREAM_END : Z_OK), FAILED); + } else { + int err = inflate(&strm, flush); + ERR_FAIL_COND_V(err != Z_OK && err != Z_STREAM_END, FAILED); + } + r_out = p_dst_size - strm.avail_out; + r_consumed = p_src_size - strm.avail_in; + return OK; +} + +Error StreamPeerGZIP::put_data(const uint8_t *p_data, int p_bytes) { + int wrote = 0; + Error err = put_partial_data(p_data, p_bytes, wrote); + if (err != OK) { + return err; + } + ERR_FAIL_COND_V(p_bytes != wrote, ERR_OUT_OF_MEMORY); + return OK; +} + +Error StreamPeerGZIP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { + ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED); + ERR_FAIL_COND_V(p_bytes < 0, ERR_INVALID_PARAMETER); + + // Ensure we have enough space in temporary buffer. + if (buffer.size() < p_bytes) { + buffer.resize(p_bytes); + } + + r_sent = 0; + while (r_sent < p_bytes && rb.space_left() > 1024) { // Keep the ring buffer size meaningful. + int sent = 0; + int to_write = 0; + // Compress or decompress + Error err = _process(buffer.ptrw(), MIN(buffer.size(), rb.space_left()), p_data + r_sent, p_bytes - r_sent, sent, to_write); + if (err != OK) { + return err; + } + // When decompressing, we might need to do another round. + r_sent += sent; + + // We can't write more than this buffer is full. + if (sent == 0 && to_write == 0) { + return OK; + } + if (to_write) { + // Copy to ring buffer. + int wrote = rb.write(buffer.ptr(), to_write); + ERR_FAIL_COND_V(wrote != to_write, ERR_BUG); + } + } + return OK; +} + +Error StreamPeerGZIP::get_data(uint8_t *p_buffer, int p_bytes) { + int received = 0; + Error err = get_partial_data(p_buffer, p_bytes, received); + if (err != OK) { + return err; + } + ERR_FAIL_COND_V(p_bytes != received, ERR_UNAVAILABLE); + return OK; +} + +Error StreamPeerGZIP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { + ERR_FAIL_COND_V(p_bytes < 0, ERR_INVALID_PARAMETER); + + r_received = MIN(p_bytes, rb.data_left()); + if (r_received == 0) { + return OK; + } + int received = rb.read(p_buffer, r_received); + ERR_FAIL_COND_V(received != r_received, ERR_BUG); + return OK; +} + +int StreamPeerGZIP::get_available_bytes() const { + return rb.data_left(); +} + +Error StreamPeerGZIP::finish() { + ERR_FAIL_COND_V(!ctx || !compressing, ERR_UNAVAILABLE); + // Ensure we have enough space in temporary buffer. + if (buffer.size() < 1024) { + buffer.resize(1024); // 1024 should be more than enough. + } + int consumed = 0; + int to_write = 0; + Error err = _process(buffer.ptrw(), 1024, nullptr, 0, consumed, to_write, true); // compress + if (err != OK) { + return err; + } + int wrote = rb.write(buffer.ptr(), to_write); + ERR_FAIL_COND_V(wrote != to_write, ERR_OUT_OF_MEMORY); + return OK; +} diff --git a/core/io/stream_peer_gzip.h b/core/io/stream_peer_gzip.h new file mode 100644 index 0000000000..5bafdbca9b --- /dev/null +++ b/core/io/stream_peer_gzip.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* stream_peer_gzip.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 STREAM_PEER_GZIP_H +#define STREAM_PEER_GZIP_H + +#include "core/io/stream_peer.h" + +#include "core/core_bind.h" +#include "core/io/compression.h" +#include "core/templates/ring_buffer.h" + +class StreamPeerGZIP : public StreamPeer { + GDCLASS(StreamPeerGZIP, StreamPeer); + +private: + void *ctx = nullptr; // Will hold our z_stream instance. + bool compressing = true; + + RingBuffer<uint8_t> rb; + Vector<uint8_t> buffer; + + Error _process(uint8_t *p_dst, int p_dst_size, const uint8_t *p_src, int p_src_size, int &r_consumed, int &r_out, bool p_close = false); + void _close(); + Error _start(bool p_compress, bool p_is_deflate, int buffer_size = 65535); + +protected: + static void _bind_methods(); + +public: + Error start_compression(bool p_is_deflate, int buffer_size = 65535); + Error start_decompression(bool p_is_deflate, int buffer_size = 65535); + + Error finish(); + void clear(); + + virtual Error put_data(const uint8_t *p_data, int p_bytes) override; + virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; + + virtual Error get_data(uint8_t *p_buffer, int p_bytes) override; + virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; + + virtual int get_available_bytes() const override; + + StreamPeerGZIP(); + ~StreamPeerGZIP(); +}; + +#endif // STREAM_PEER_GZIP_H diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 154b55f5e7..abae48fdd8 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -137,7 +137,7 @@ bool XMLParser::_parse_cdata() { next_char(); } - if (cDataEnd) { + if (!cDataEnd) { cDataEnd = P; } node_name = String::utf8(cDataBegin, (int)(cDataEnd - cDataBegin)); diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 23d7e379ee..ad67cfa852 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -30,16 +30,16 @@ #include "a_star_grid_2d.h" -static real_t heuristic_manhattan(const Vector2i &p_from, const Vector2i &p_to) { +static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) { real_t dx = (real_t)ABS(p_to.x - p_from.x); real_t dy = (real_t)ABS(p_to.y - p_from.y); - return dx + dy; + return (real_t)Math::sqrt(dx * dx + dy * dy); } -static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) { +static real_t heuristic_manhattan(const Vector2i &p_from, const Vector2i &p_to) { real_t dx = (real_t)ABS(p_to.x - p_from.x); real_t dy = (real_t)ABS(p_to.y - p_from.y); - return (real_t)Math::sqrt(dx * dx + dy * dy); + return dx + dy; } static real_t heuristic_octile(const Vector2i &p_from, const Vector2i &p_to) { @@ -55,7 +55,7 @@ static real_t heuristic_chebyshev(const Vector2i &p_from, const Vector2i &p_to) return MAX(dx, dy); } -static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_manhattan, heuristic_euclidian, heuristic_octile, heuristic_chebyshev }; +static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_euclidian, heuristic_manhattan, heuristic_octile, heuristic_chebyshev }; void AStarGrid2D::set_size(const Size2i &p_size) { ERR_FAIL_COND(p_size.x < 0 || p_size.y < 0); @@ -572,7 +572,7 @@ void AStarGrid2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size"), "set_cell_size", "get_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "jumping_enabled"), "set_jumping_enabled", "is_jumping_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "default_heuristic", PROPERTY_HINT_ENUM, "Manhattan,Euclidean,Octile,Chebyshev,Max"), "set_default_heuristic", "get_default_heuristic"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "default_heuristic", PROPERTY_HINT_ENUM, "Euclidean,Manhattan,Octile,Chebyshev,Max"), "set_default_heuristic", "get_default_heuristic"); ADD_PROPERTY(PropertyInfo(Variant::INT, "diagonal_mode", PROPERTY_HINT_ENUM, "Never,Always,At Least One Walkable,Only If No Obstacles,Max"), "set_diagonal_mode", "get_diagonal_mode"); BIND_ENUM_CONSTANT(HEURISTIC_EUCLIDEAN); diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 0eb6320ac6..4b163409ce 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -754,29 +754,28 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { #ifdef MATH_CHECKS ERR_FAIL_COND(!is_rotation()); #endif -*/ - real_t angle, x, y, z; // variables for result - real_t angle_epsilon = 0.1; // margin to distinguish between 0 and 180 degrees - - if ((Math::abs(rows[1][0] - rows[0][1]) < CMP_EPSILON) && (Math::abs(rows[2][0] - rows[0][2]) < CMP_EPSILON) && (Math::abs(rows[2][1] - rows[1][2]) < CMP_EPSILON)) { - // singularity found - // first check for identity matrix which must have +1 for all terms - // in leading diagonal and zero in other terms - if ((Math::abs(rows[1][0] + rows[0][1]) < angle_epsilon) && (Math::abs(rows[2][0] + rows[0][2]) < angle_epsilon) && (Math::abs(rows[2][1] + rows[1][2]) < angle_epsilon) && (Math::abs(rows[0][0] + rows[1][1] + rows[2][2] - 3) < angle_epsilon)) { - // this singularity is identity matrix so angle = 0 + */ + + // https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + real_t x, y, z; // Variables for result. + if (Math::is_zero_approx(rows[0][1] - rows[1][0]) && Math::is_zero_approx(rows[0][2] - rows[2][0]) && Math::is_zero_approx(rows[1][2] - rows[2][1])) { + // Singularity found. + // First check for identity matrix which must have +1 for all terms in leading diagonal and zero in other terms. + if (is_diagonal() && (Math::abs(rows[0][0] + rows[1][1] + rows[2][2] - 3) < 3 * CMP_EPSILON)) { + // This singularity is identity matrix so angle = 0. r_axis = Vector3(0, 1, 0); r_angle = 0; return; } - // otherwise this singularity is angle = 180 - angle = Math_PI; + // Otherwise this singularity is angle = 180. real_t xx = (rows[0][0] + 1) / 2; real_t yy = (rows[1][1] + 1) / 2; real_t zz = (rows[2][2] + 1) / 2; - real_t xy = (rows[1][0] + rows[0][1]) / 4; - real_t xz = (rows[2][0] + rows[0][2]) / 4; - real_t yz = (rows[2][1] + rows[1][2]) / 4; - if ((xx > yy) && (xx > zz)) { // rows[0][0] is the largest diagonal term + real_t xy = (rows[0][1] + rows[1][0]) / 4; + real_t xz = (rows[0][2] + rows[2][0]) / 4; + real_t yz = (rows[1][2] + rows[2][1]) / 4; + + if ((xx > yy) && (xx > zz)) { // rows[0][0] is the largest diagonal term. if (xx < CMP_EPSILON) { x = 0; y = Math_SQRT12; @@ -786,7 +785,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { y = xy / x; z = xz / x; } - } else if (yy > zz) { // rows[1][1] is the largest diagonal term + } else if (yy > zz) { // rows[1][1] is the largest diagonal term. if (yy < CMP_EPSILON) { x = Math_SQRT12; y = 0; @@ -796,7 +795,7 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { x = xy / y; z = yz / y; } - } else { // rows[2][2] is the largest diagonal term so base result on this + } else { // rows[2][2] is the largest diagonal term so base result on this. if (zz < CMP_EPSILON) { x = Math_SQRT12; y = Math_SQRT12; @@ -808,22 +807,24 @@ void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const { } } r_axis = Vector3(x, y, z); - r_angle = angle; + r_angle = Math_PI; return; } - // as we have reached here there are no singularities so we can handle normally - real_t s = Math::sqrt((rows[1][2] - rows[2][1]) * (rows[1][2] - rows[2][1]) + (rows[2][0] - rows[0][2]) * (rows[2][0] - rows[0][2]) + (rows[0][1] - rows[1][0]) * (rows[0][1] - rows[1][0])); // s=|axis||sin(angle)|, used to normalise + // As we have reached here there are no singularities so we can handle normally. + double s = Math::sqrt((rows[2][1] - rows[1][2]) * (rows[2][1] - rows[1][2]) + (rows[0][2] - rows[2][0]) * (rows[0][2] - rows[2][0]) + (rows[1][0] - rows[0][1]) * (rows[1][0] - rows[0][1])); // Used to normalise. - angle = Math::acos((rows[0][0] + rows[1][1] + rows[2][2] - 1) / 2); - if (angle < 0) { - s = -s; + if (Math::abs(s) < CMP_EPSILON) { + // Prevent divide by zero, should not happen if matrix is orthogonal and should be caught by singularity test above. + s = 1; } + x = (rows[2][1] - rows[1][2]) / s; y = (rows[0][2] - rows[2][0]) / s; z = (rows[1][0] - rows[0][1]) / s; r_axis = Vector3(x, y, z); - r_angle = angle; + // CLAMP to avoid NaN if the value passed to acos is not in [0,1]. + r_angle = Math::acos(CLAMP((rows[0][0] + rows[1][1] + rows[2][2] - 1) / 2, (real_t)0.0, (real_t)1.0)); } void Basis::set_quaternion(const Quaternion &p_quaternion) { diff --git a/core/math/math_fieldwise.cpp b/core/math/math_fieldwise.cpp index 208f89f449..f36b228543 100644 --- a/core/math/math_fieldwise.cpp +++ b/core/math/math_fieldwise.cpp @@ -76,6 +76,7 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const return target; } + case Variant::VECTOR3I: { SETUP_TYPE(Vector3i) @@ -85,6 +86,7 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const return target; } + case Variant::VECTOR4: { SETUP_TYPE(Vector4) @@ -95,6 +97,7 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const return target; } + case Variant::VECTOR4I: { SETUP_TYPE(Vector4i) @@ -106,7 +109,6 @@ Variant fieldwise_assign(const Variant &p_target, const Variant &p_source, const return target; } - case Variant::PLANE: { SETUP_TYPE(Plane) diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 656fc9f798..7fa674a23d 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -267,6 +267,7 @@ public: return cubic_interpolate(from_rot, to_rot, pre_rot, post_rot, p_weight); } + static _ALWAYS_INLINE_ float cubic_interpolate_angle(float p_from, float p_to, float p_pre, float p_post, float p_weight) { float from_rot = fmod(p_from, (float)Math_TAU); @@ -293,6 +294,7 @@ public: double b2 = Math::lerp(a2, a3, p_post_t == 0 ? 1.0 : t / p_post_t); return Math::lerp(b1, b2, p_to_t == 0 ? 0.5 : t / p_to_t); } + static _ALWAYS_INLINE_ float cubic_interpolate_in_time(float p_from, float p_to, float p_pre, float p_post, float p_weight, float p_to_t, float p_pre_t, float p_post_t) { /* Barry-Goldman method */ @@ -320,6 +322,7 @@ public: return cubic_interpolate_in_time(from_rot, to_rot, pre_rot, post_rot, p_weight, p_to_t, p_pre_t, p_post_t); } + static _ALWAYS_INLINE_ float cubic_interpolate_angle_in_time(float p_from, float p_to, float p_pre, float p_post, float p_weight, float p_to_t, float p_pre_t, float p_post_t) { float from_rot = fmod(p_from, (float)Math_TAU); @@ -346,6 +349,7 @@ public: return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3; } + static _ALWAYS_INLINE_ float bezier_interpolate(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) { /* Formula from Wikipedia article on Bezier curves. */ float omt = (1.0f - p_t); @@ -368,11 +372,19 @@ public: return p_from + distance * p_weight; } - static _ALWAYS_INLINE_ double inverse_lerp(double p_from, double p_to, double p_value) { return (p_value - p_from) / (p_to - p_from); } - static _ALWAYS_INLINE_ float inverse_lerp(float p_from, float p_to, float p_value) { return (p_value - p_from) / (p_to - p_from); } + static _ALWAYS_INLINE_ double inverse_lerp(double p_from, double p_to, double p_value) { + return (p_value - p_from) / (p_to - p_from); + } + static _ALWAYS_INLINE_ float inverse_lerp(float p_from, float p_to, float p_value) { + return (p_value - p_from) / (p_to - p_from); + } - static _ALWAYS_INLINE_ double remap(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } - static _ALWAYS_INLINE_ float remap(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); } + static _ALWAYS_INLINE_ double remap(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { + return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); + } + static _ALWAYS_INLINE_ float remap(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { + return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); + } static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) { if (is_equal_approx(p_from, p_to)) { @@ -388,14 +400,26 @@ public: float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f); return s * s * (3.0f - 2.0f * s); } - static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta; } - static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta; } + static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { + return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta; + } + static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { + return abs(p_to - p_from) <= p_delta ? p_to : p_from + SIGN(p_to - p_from) * p_delta; + } - static _ALWAYS_INLINE_ double linear_to_db(double p_linear) { return Math::log(p_linear) * 8.6858896380650365530225783783321; } - static _ALWAYS_INLINE_ float linear_to_db(float p_linear) { return Math::log(p_linear) * (float)8.6858896380650365530225783783321; } + static _ALWAYS_INLINE_ double linear_to_db(double p_linear) { + return Math::log(p_linear) * 8.6858896380650365530225783783321; + } + static _ALWAYS_INLINE_ float linear_to_db(float p_linear) { + return Math::log(p_linear) * (float)8.6858896380650365530225783783321; + } - static _ALWAYS_INLINE_ double db_to_linear(double p_db) { return Math::exp(p_db * 0.11512925464970228420089957273422); } - static _ALWAYS_INLINE_ float db_to_linear(float p_db) { return Math::exp(p_db * (float)0.11512925464970228420089957273422); } + static _ALWAYS_INLINE_ double db_to_linear(double p_db) { + return Math::exp(p_db * 0.11512925464970228420089957273422); + } + static _ALWAYS_INLINE_ float db_to_linear(float p_db) { + return Math::exp(p_db * (float)0.11512925464970228420089957273422); + } static _ALWAYS_INLINE_ double round(double p_val) { return ::round(p_val); } static _ALWAYS_INLINE_ float round(float p_val) { return ::roundf(p_val); } diff --git a/core/math/vector2i.h b/core/math/vector2i.h index 0245900a3b..e131bdea94 100644 --- a/core/math/vector2i.h +++ b/core/math/vector2i.h @@ -38,6 +38,8 @@ class String; struct Vector2; struct _NO_DISCARD_ Vector2i { + static const int AXIS_COUNT = 2; + enum Axis { AXIS_X, AXIS_Y, diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 825ce40318..c6d03cd031 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -38,6 +38,8 @@ class String; struct Vector3; struct _NO_DISCARD_ Vector3i { + static const int AXIS_COUNT = 3; + enum Axis { AXIS_X, AXIS_Y, diff --git a/core/math/vector4.h b/core/math/vector4.h index f964264108..d89f3ddb05 100644 --- a/core/math/vector4.h +++ b/core/math/vector4.h @@ -37,6 +37,8 @@ #include "core/string/ustring.h" struct _NO_DISCARD_ Vector4 { + static const int AXIS_COUNT = 4; + enum Axis { AXIS_X, AXIS_Y, diff --git a/core/math/vector4i.h b/core/math/vector4i.h index d08e40d754..fdf33b9569 100644 --- a/core/math/vector4i.h +++ b/core/math/vector4i.h @@ -38,6 +38,8 @@ class String; struct Vector4; struct _NO_DISCARD_ Vector4i { + static const int AXIS_COUNT = 4; + enum Axis { AXIS_X, AXIS_Y, diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index d3c48853f1..aa66e86bc0 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -126,27 +126,28 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode) { force_keep_in_merge_ends = false; } -void UndoRedo::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { - ERR_FAIL_COND(p_object == nullptr); +void UndoRedo::add_do_method(const Callable &p_callable) { + ERR_FAIL_COND(p_callable.is_null()); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); + + Object *object = p_callable.get_object(); + ERR_FAIL_NULL(object); + Operation do_op; - do_op.object = p_object->get_instance_id(); - if (Object::cast_to<RefCounted>(p_object)) { - do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); + do_op.callable = p_callable; + do_op.object = p_callable.get_object_id(); + if (Object::cast_to<RefCounted>(object)) { + do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object)); } - do_op.type = Operation::TYPE_METHOD; - do_op.name = p_method; + do_op.name = p_callable.get_method(); - for (int i = 0; i < p_argcount; i++) { - do_op.args.push_back(*p_args[i]); - } actions.write[current_action + 1].do_ops.push_back(do_op); } -void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { - ERR_FAIL_COND(p_object == nullptr); +void UndoRedo::add_undo_method(const Callable &p_callable) { + ERR_FAIL_COND(p_callable.is_null()); ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); @@ -155,19 +156,19 @@ void UndoRedo::add_undo_methodp(Object *p_object, const StringName &p_method, co return; } + Object *object = p_callable.get_object(); + ERR_FAIL_NULL(object); + Operation undo_op; - undo_op.object = p_object->get_instance_id(); - if (Object::cast_to<RefCounted>(p_object)) { - undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(p_object)); + undo_op.callable = p_callable; + undo_op.object = p_callable.get_object_id(); + if (Object::cast_to<RefCounted>(object)) { + undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object)); } - undo_op.type = Operation::TYPE_METHOD; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; - undo_op.name = p_method; + undo_op.name = p_callable.get_method(); - for (int i = 0; i < p_argcount; i++) { - undo_op.args.push_back(*p_args[i]); - } actions.write[current_action + 1].undo_ops.push_back(undo_op); } @@ -183,7 +184,7 @@ void UndoRedo::add_do_property(Object *p_object, const StringName &p_property, c do_op.type = Operation::TYPE_PROPERTY; do_op.name = p_property; - do_op.args.push_back(p_value); + do_op.value = p_value; actions.write[current_action + 1].do_ops.push_back(do_op); } @@ -206,7 +207,7 @@ void UndoRedo::add_undo_property(Object *p_object, const StringName &p_property, undo_op.type = Operation::TYPE_PROPERTY; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.name = p_property; - undo_op.args.push_back(p_value); + undo_op.value = p_value; actions.write[current_action + 1].undo_ops.push_back(undo_op); } @@ -312,33 +313,42 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { switch (op.type) { case Operation::TYPE_METHOD: { - int argc = op.args.size(); - Vector<const Variant *> argptrs; - argptrs.resize(argc); - - for (int i = 0; i < argc; i++) { - argptrs.write[i] = &op.args[i]; - } - Callable::CallError ce; - obj->callp(op.name, (const Variant **)argptrs.ptr(), argc, ce); + Variant ret; + op.callable.callp(nullptr, 0, ret, ce); if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, (const Variant **)argptrs.ptr(), argc, ce)); + ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce)); } #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); if (res) { res->set_edited(true); } - #endif if (method_callback) { - method_callback(method_callback_ud, obj, op.name, (const Variant **)argptrs.ptr(), argc); + Vector<Variant> binds; + if (op.callable.is_custom()) { + CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(op.callable.get_custom()); + if (ccb) { + binds = ccb->get_binds(); + } + } + + if (binds.is_empty()) { + method_callback(method_callback_ud, obj, op.name, nullptr, 0); + } else { + const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * binds.size()); + for (int i = 0; i < binds.size(); i++) { + args[i] = (const Variant *)&binds[i]; + } + + method_callback(method_callback_ud, obj, op.name, args, binds.size()); + } } } break; case Operation::TYPE_PROPERTY: { - obj->set(op.name, op.args[0]); + obj->set(op.name, op.value); #ifdef TOOLS_ENABLED Resource *res = Object::cast_to<Resource>(obj); if (res) { @@ -346,7 +356,7 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { } #endif if (property_callback) { - property_callback(prop_callback_ud, obj, op.name, op.args[0]); + property_callback(prop_callback_ud, obj, op.name, op.value); } } break; case Operation::TYPE_REFERENCE: { @@ -444,87 +454,13 @@ UndoRedo::~UndoRedo() { clear_history(); } -void UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 0; - return; - } - - if (p_args[0]->get_type() != Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - return; - } - - if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::STRING_NAME; - return; - } - - r_error.error = Callable::CallError::CALL_OK; - - Object *object = *p_args[0]; - StringName method = *p_args[1]; - - add_do_methodp(object, method, p_args + 2, p_argcount - 2); -} - -void UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - if (p_argcount < 2) { - r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 0; - return; - } - - if (p_args[0]->get_type() != Variant::OBJECT) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 0; - r_error.expected = Variant::OBJECT; - return; - } - - if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = Variant::STRING_NAME; - return; - } - - r_error.error = Callable::CallError::CALL_OK; - - Object *object = *p_args[0]; - StringName method = *p_args[1]; - - add_undo_methodp(object, method, p_args + 2, p_argcount - 2); -} - void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE)); ClassDB::bind_method(D_METHOD("commit_action", "execute"), &UndoRedo::commit_action, DEFVAL(true)); ClassDB::bind_method(D_METHOD("is_committing_action"), &UndoRedo::is_committing_action); - { - MethodInfo mi; - mi.name = "add_do_method"; - mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); - mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method")); - - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_do_method", &UndoRedo::_add_do_method, mi, varray(), false); - } - - { - MethodInfo mi; - mi.name = "add_undo_method"; - mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); - mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method")); - - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_undo_method", &UndoRedo::_add_undo_method, mi, varray(), false); - } - + ClassDB::bind_method(D_METHOD("add_do_method", "callable"), &UndoRedo::add_do_method); + ClassDB::bind_method(D_METHOD("add_undo_method", "callable"), &UndoRedo::add_undo_method); ClassDB::bind_method(D_METHOD("add_do_property", "object", "property", "value"), &UndoRedo::add_do_property); ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &UndoRedo::add_undo_property); ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &UndoRedo::add_do_reference); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index 63cf3e5cbe..c7c58697c3 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -46,8 +46,6 @@ public: }; typedef void (*CommitNotifyCallback)(void *p_ud, const String &p_name); - void _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); - void _add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); typedef void (*MethodNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount); typedef void (*PropertyNotifyCallback)(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value); @@ -58,14 +56,14 @@ private: TYPE_METHOD, TYPE_PROPERTY, TYPE_REFERENCE - }; + } type; - Type type; bool force_keep_in_merge_ends; Ref<RefCounted> ref; ObjectID object; StringName name; - Vector<Variant> args; + Callable callable; + Variant value; void delete_reference(); }; @@ -106,30 +104,8 @@ protected: public: void create_action(const String &p_name = "", MergeMode p_mode = MERGE_DISABLE); - void add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); - void add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); - - template <typename... VarArgs> - void add_do_method(Object *p_object, const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - - add_do_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); - } - template <typename... VarArgs> - void add_undo_method(Object *p_object, const StringName &p_method, VarArgs... p_args) { - Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. - const Variant *argptrs[sizeof...(p_args) + 1]; - for (uint32_t i = 0; i < sizeof...(p_args); i++) { - argptrs[i] = &args[i]; - } - - add_undo_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); - } - + void add_do_method(const Callable &p_callable); + void add_undo_method(const Callable &p_callable); void add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value); void add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value); void add_do_reference(Object *p_object); diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index a592791d06..f3be495c8e 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -61,12 +61,16 @@ static const _KeyCodeText _keycodes[] = { {Key::PAGEDOWN ,"PageDown"}, {Key::SHIFT ,"Shift"}, {Key::CTRL ,"Ctrl"}, -#ifdef MACOS_ENABLED +#if defined(MACOS_ENABLED) {Key::META ,"Command"}, + {Key::ALT ,"Option"}, +#elif defined(WINDOWS_ENABLED) + {Key::META ,"Windows"}, + {Key::ALT ,"Alt"}, #else {Key::META ,"Meta"}, -#endif {Key::ALT ,"Alt"}, +#endif {Key::CAPSLOCK ,"CapsLock"}, {Key::NUMLOCK ,"NumLock"}, {Key::SCROLLLOCK ,"ScrollLock"}, @@ -437,6 +441,14 @@ String keycode_get_string(Key p_code) { codestr += find_keycode_name(Key::ALT); codestr += "+"; } + if ((p_code & KeyModifierMask::CMD_OR_CTRL) != Key::NONE) { +#ifdef MACOS_ENABLED + codestr += find_keycode_name(Key::META); +#else + codestr += find_keycode_name(Key::CTRL); +#endif + codestr += "+"; + } if ((p_code & KeyModifierMask::CTRL) != Key::NONE) { codestr += find_keycode_name(Key::CTRL); codestr += "+"; diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 29418049cb..e5d9b24e85 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -36,11 +36,11 @@ enum class Key { NONE = 0, // Special key: The strategy here is similar to the one used by toolkits, - // which consists in leaving the 24 bits unicode range for printable - // characters, and use the upper 8 bits for special keys and modifiers. + // which consists in leaving the 21 bits unicode range for printable + // characters, and use the upper 11 bits for special keys and modifiers. // This way everything (char/keycode) can fit nicely in one 32-bit // integer (the enum's underlying type is `int` by default). - SPECIAL = (1 << 24), + SPECIAL = (1 << 22), /* CURSOR/FUNCTION/BROWSER/MULTIMEDIA/MISC KEYS */ ESCAPE = SPECIAL | 0x01, TAB = SPECIAL | 0x02, @@ -312,17 +312,14 @@ enum class Key { }; enum class KeyModifierMask { - CODE_MASK = ((1 << 25) - 1), ///< Apply this mask to any keycode to remove modifiers. - MODIFIER_MASK = (0x7F << 24), ///< Apply this mask to isolate modifiers. + CODE_MASK = ((1 << 23) - 1), ///< Apply this mask to any keycode to remove modifiers. + MODIFIER_MASK = (0x7F << 22), ///< Apply this mask to isolate modifiers. + //RESERVED = (1 << 23), + CMD_OR_CTRL = (1 << 24), SHIFT = (1 << 25), ALT = (1 << 26), META = (1 << 27), CTRL = (1 << 28), -#ifdef APPLE_STYLE_KEYS - CMD = META, -#else - CMD = CTRL, -#endif KPAD = (1 << 29), GROUP_SWITCH = (1 << 30) }; diff --git a/core/os/os.h b/core/os/os.h index 0e8a2d0398..0f79ff1a23 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -69,6 +69,7 @@ class OS { // so we can retrieve the rendering drivers available int _display_driver_id = -1; String _current_rendering_driver_name; + String _current_rendering_method; protected: void _set_logger(CompositeLogger *p_logger); @@ -98,6 +99,8 @@ protected: virtual void initialize_joypads() = 0; void set_current_rendering_driver_name(String p_driver_name) { _current_rendering_driver_name = p_driver_name; } + void set_current_rendering_method(String p_name) { _current_rendering_method = p_name; } + void set_display_driver_id(int p_display_driver_id) { _display_driver_id = p_display_driver_id; } virtual void set_main_loop(MainLoop *p_main_loop) = 0; @@ -116,6 +119,8 @@ public: static OS *get_singleton(); String get_current_rendering_driver_name() const { return _current_rendering_driver_name; } + String get_current_rendering_method() const { return _current_rendering_method; } + int get_display_driver_id() const { return _display_driver_id; } void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, Logger::ErrorType p_type = Logger::ERR_ERROR); @@ -161,6 +166,8 @@ public: virtual bool set_environment(const String &p_var, const String &p_value) const = 0; virtual String get_name() const = 0; + virtual String get_distribution_name() const = 0; + virtual String get_version() const = 0; virtual List<String> get_cmdline_args() const { return _cmdline; } virtual List<String> get_cmdline_user_args() const { return _user_args; } virtual List<String> get_cmdline_platform_args() const { return List<String>(); } @@ -202,18 +209,15 @@ public: MONTH_DECEMBER, }; - struct Date { + struct DateTime { int64_t year; Month month; uint8_t day; Weekday weekday; - bool dst; - }; - - struct Time { uint8_t hour; uint8_t minute; uint8_t second; + bool dst; }; struct TimeZoneInfo { @@ -221,8 +225,7 @@ public: String name; }; - virtual Date get_date(bool p_utc = false) const = 0; - virtual Time get_time(bool p_utc = false) const = 0; + virtual DateTime get_datetime(bool utc = false) const = 0; virtual TimeZoneInfo get_time_zone_info() const = 0; virtual double get_unix_time() const; diff --git a/core/os/time.cpp b/core/os/time.cpp index a30e2a906b..a3c2c99b4c 100644 --- a/core/os/time.cpp +++ b/core/os/time.cpp @@ -324,63 +324,60 @@ String Time::get_offset_string_from_offset_minutes(int64_t p_offset_minutes) con } Dictionary Time::get_datetime_dict_from_system(bool p_utc) const { - OS::Date date = OS::get_singleton()->get_date(p_utc); - OS::Time time = OS::get_singleton()->get_time(p_utc); + OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc); Dictionary datetime; - datetime[YEAR_KEY] = date.year; - datetime[MONTH_KEY] = (uint8_t)date.month; - datetime[DAY_KEY] = date.day; - datetime[WEEKDAY_KEY] = (uint8_t)date.weekday; - datetime[DST_KEY] = date.dst; - datetime[HOUR_KEY] = time.hour; - datetime[MINUTE_KEY] = time.minute; - datetime[SECOND_KEY] = time.second; + datetime[YEAR_KEY] = dt.year; + datetime[MONTH_KEY] = (uint8_t)dt.month; + datetime[DAY_KEY] = dt.day; + datetime[WEEKDAY_KEY] = (uint8_t)dt.weekday; + datetime[HOUR_KEY] = dt.hour; + datetime[MINUTE_KEY] = dt.minute; + datetime[SECOND_KEY] = dt.second; + datetime[DST_KEY] = dt.dst; return datetime; } Dictionary Time::get_date_dict_from_system(bool p_utc) const { - OS::Date date = OS::get_singleton()->get_date(p_utc); + OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc); Dictionary date_dictionary; - date_dictionary[YEAR_KEY] = date.year; - date_dictionary[MONTH_KEY] = (uint8_t)date.month; - date_dictionary[DAY_KEY] = date.day; - date_dictionary[WEEKDAY_KEY] = (uint8_t)date.weekday; - date_dictionary[DST_KEY] = date.dst; + date_dictionary[YEAR_KEY] = dt.year; + date_dictionary[MONTH_KEY] = (uint8_t)dt.month; + date_dictionary[DAY_KEY] = dt.day; + date_dictionary[WEEKDAY_KEY] = (uint8_t)dt.weekday; return date_dictionary; } Dictionary Time::get_time_dict_from_system(bool p_utc) const { - OS::Time time = OS::get_singleton()->get_time(p_utc); + OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc); Dictionary time_dictionary; - time_dictionary[HOUR_KEY] = time.hour; - time_dictionary[MINUTE_KEY] = time.minute; - time_dictionary[SECOND_KEY] = time.second; + time_dictionary[HOUR_KEY] = dt.hour; + time_dictionary[MINUTE_KEY] = dt.minute; + time_dictionary[SECOND_KEY] = dt.second; return time_dictionary; } String Time::get_datetime_string_from_system(bool p_utc, bool p_use_space) const { - OS::Date date = OS::get_singleton()->get_date(p_utc); - OS::Time time = OS::get_singleton()->get_time(p_utc); + OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc); // vformat only supports up to 6 arguments, so we need to split this up into 2 parts. - String timestamp = vformat("%04d-%02d-%02d", date.year, (uint8_t)date.month, date.day); + String timestamp = vformat("%04d-%02d-%02d", dt.year, (uint8_t)dt.month, dt.day); if (p_use_space) { - timestamp = vformat("%s %02d:%02d:%02d", timestamp, time.hour, time.minute, time.second); + timestamp = vformat("%s %02d:%02d:%02d", timestamp, dt.hour, dt.minute, dt.second); } else { - timestamp = vformat("%sT%02d:%02d:%02d", timestamp, time.hour, time.minute, time.second); + timestamp = vformat("%sT%02d:%02d:%02d", timestamp, dt.hour, dt.minute, dt.second); } return timestamp; } String Time::get_date_string_from_system(bool p_utc) const { - OS::Date date = OS::get_singleton()->get_date(p_utc); + OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc); // Android is picky about the types passed to make Variant, so we need a cast. - return vformat("%04d-%02d-%02d", date.year, (uint8_t)date.month, date.day); + return vformat("%04d-%02d-%02d", dt.year, (uint8_t)dt.month, dt.day); } String Time::get_time_string_from_system(bool p_utc) const { - OS::Time time = OS::get_singleton()->get_time(p_utc); - return vformat("%02d:%02d:%02d", time.hour, time.minute, time.second); + OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc); + return vformat("%02d:%02d:%02d", dt.hour, dt.minute, dt.second); } Dictionary Time::get_time_zone_from_system() const { diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 1b3f11ffab..bad53ab4e6 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -58,6 +58,7 @@ #include "core/io/resource_format_binary.h" #include "core/io/resource_importer.h" #include "core/io/resource_uid.h" +#include "core/io/stream_peer_gzip.h" #include "core/io/stream_peer_tls.h" #include "core/io/tcp_server.h" #include "core/io/translation_loader_po.h" @@ -87,6 +88,8 @@ static Ref<TranslationLoaderPO> resource_format_po; static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto; static Ref<ResourceFormatLoaderCrypto> resource_format_loader_crypto; static Ref<NativeExtensionResourceLoader> resource_loader_native_extension; +static Ref<ResourceFormatSaverJSON> resource_saver_json; +static Ref<ResourceFormatLoaderJSON> resource_loader_json; static core_bind::ResourceLoader *_resource_loader = nullptr; static core_bind::ResourceSaver *_resource_saver = nullptr; @@ -182,6 +185,7 @@ void register_core_types() { GDREGISTER_ABSTRACT_CLASS(StreamPeer); GDREGISTER_CLASS(StreamPeerExtension); GDREGISTER_CLASS(StreamPeerBuffer); + GDREGISTER_CLASS(StreamPeerGZIP); GDREGISTER_CLASS(StreamPeerTCP); GDREGISTER_CLASS(TCPServer); @@ -211,6 +215,12 @@ void register_core_types() { resource_format_loader_crypto.instantiate(); ResourceLoader::add_resource_format_loader(resource_format_loader_crypto); + resource_loader_json.instantiate(); + ResourceLoader::add_resource_format_loader(resource_loader_json); + + resource_saver_json.instantiate(); + ResourceSaver::add_resource_format_saver(resource_saver_json); + GDREGISTER_CLASS(MainLoop); GDREGISTER_CLASS(Translation); GDREGISTER_CLASS(OptimizedTranslation); @@ -220,8 +230,8 @@ void register_core_types() { GDREGISTER_CLASS(ResourceFormatLoader); GDREGISTER_CLASS(ResourceFormatSaver); - GDREGISTER_CLASS(core_bind::File); - GDREGISTER_CLASS(core_bind::Directory); + GDREGISTER_ABSTRACT_CLASS(FileAccess); + GDREGISTER_ABSTRACT_CLASS(DirAccess); GDREGISTER_CLASS(core_bind::Thread); GDREGISTER_CLASS(core_bind::Mutex); GDREGISTER_CLASS(core_bind::Semaphore); @@ -241,6 +251,8 @@ void register_core_types() { GDREGISTER_CLASS(EncodedObjectAsID); GDREGISTER_CLASS(RandomNumberGenerator); + GDREGISTER_ABSTRACT_CLASS(ImageFormatLoader); + GDREGISTER_CLASS(ImageFormatLoaderExtension); GDREGISTER_ABSTRACT_CLASS(ResourceImporter); GDREGISTER_CLASS(NativeExtension); @@ -284,8 +296,8 @@ void register_core_settings() { ProjectSettings::get_singleton()->set_custom_property_info("network/limits/tcp/connect_timeout_seconds", PropertyInfo(Variant::INT, "network/limits/tcp/connect_timeout_seconds", PROPERTY_HINT_RANGE, "1,1800,1")); GLOBAL_DEF_RST("network/limits/packet_peer_stream/max_buffer_po2", (16)); ProjectSettings::get_singleton()->set_custom_property_info("network/limits/packet_peer_stream/max_buffer_po2", PropertyInfo(Variant::INT, "network/limits/packet_peer_stream/max_buffer_po2", PROPERTY_HINT_RANGE, "0,64,1,or_greater")); - GLOBAL_DEF("network/ssl/certificate_bundle_override", ""); - ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificate_bundle_override", PropertyInfo(Variant::STRING, "network/ssl/certificate_bundle_override", PROPERTY_HINT_FILE, "*.crt")); + GLOBAL_DEF("network/tls/certificate_bundle_override", ""); + ProjectSettings::get_singleton()->set_custom_property_info("network/tls/certificate_bundle_override", PropertyInfo(Variant::STRING, "network/tls/certificate_bundle_override", PROPERTY_HINT_FILE, "*.crt")); int worker_threads = GLOBAL_DEF("threading/worker_pool/max_threads", -1); bool low_priority_use_system_threads = GLOBAL_DEF("threading/worker_pool/use_system_threads_for_low_priority_tasks", true); @@ -387,6 +399,12 @@ void unregister_core_types() { ResourceLoader::remove_resource_format_loader(resource_format_loader_crypto); resource_format_loader_crypto.unref(); + ResourceSaver::remove_resource_format_saver(resource_saver_json); + resource_saver_json.unref(); + + ResourceLoader::remove_resource_format_loader(resource_loader_json); + resource_loader_json.unref(); + if (ip) { memdelete(ip); } diff --git a/core/string/locales.h b/core/string/locales.h index 32d6608ec2..0ccb17a436 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -1072,6 +1072,7 @@ static const char *script_list[][2] = { { "Jurchen", "Jurc" }, { "Kayah Li", "Kali" }, { "Katakana", "Kana" }, + { "Kawi", "Kawi" }, { "Kharoshthi", "Khar" }, { "Khmer", "Khmr" }, { "Khojki", "Khoj" }, @@ -1110,6 +1111,7 @@ static const char *script_list[][2] = { { "Meitei Mayek", "Mtei" }, { "Multani", "Mult" }, { "Myanmar (Burmese)", "Mymr" }, + { "Nag Mundari", "Nagm" }, { "Nandinagari", "Nand" }, { "Old North Arabian", "Narb" }, { "Nabataean", "Nbat" }, diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index b280fc9fe3..f24ffeb1a9 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3727,36 +3727,6 @@ String Variant::get_callable_error_text(const Callable &p_callable, const Varian return get_call_error_text(p_callable.get_object(), p_callable.get_method(), p_argptrs, p_argcount, ce); } -String vformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) { - Array args; - if (p1.get_type() != Variant::NIL) { - args.push_back(p1); - - if (p2.get_type() != Variant::NIL) { - args.push_back(p2); - - if (p3.get_type() != Variant::NIL) { - args.push_back(p3); - - if (p4.get_type() != Variant::NIL) { - args.push_back(p4); - - if (p5.get_type() != Variant::NIL) { - args.push_back(p5); - } - } - } - } - } - - bool error = false; - String fmt = p_text.sprintf(args, &error); - - ERR_FAIL_COND_V_MSG(error, String(), fmt); - - return fmt; -} - void Variant::register_types() { _register_variant_operators(); _register_variant_methods(); diff --git a/core/variant/variant.h b/core/variant/variant.h index 212f94a9a8..b0738e7d44 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -552,9 +552,6 @@ public: void zero(); Variant duplicate(bool p_deep = false) const; Variant recursive_duplicate(bool p_deep, int recursion_count) const; - static void blend(const Variant &a, const Variant &b, float c, Variant &r_dst); - static void interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst); - static void sub(const Variant &a, const Variant &b, Variant &r_dst); /* Built-In Methods */ @@ -807,7 +804,22 @@ const Variant::ObjData &Variant::_get_obj() const { return *reinterpret_cast<const ObjData *>(&_data._mem[0]); } -String vformat(const String &p_text, const Variant &p1 = Variant(), const Variant &p2 = Variant(), const Variant &p3 = Variant(), const Variant &p4 = Variant(), const Variant &p5 = Variant()); +template <typename... VarArgs> +String vformat(const String &p_text, const VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + Array args_array; + args_array.resize(sizeof...(p_args)); + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + args_array[i] = args[i]; + } + + bool error = false; + String fmt = p_text.sprintf(args_array, &error); + + ERR_FAIL_COND_V_MSG(error, String(), fmt); + + return fmt; +} template <typename... VarArgs> Callable Callable::bind(VarArgs... p_args) { diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index 57b953f7f0..ff67b187ef 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -1911,572 +1911,6 @@ Variant Variant::recursive_duplicate(bool p_deep, int recursion_count) const { } } -void Variant::sub(const Variant &a, const Variant &b, Variant &r_dst) { - if (a.type != b.type) { - return; - } - - switch (a.type) { - case NIL: { - r_dst = Variant(); - } - return; - case INT: { - int64_t va = a._data._int; - int64_t vb = b._data._int; - r_dst = int(va - vb); - } - return; - case FLOAT: { - double ra = a._data._float; - double rb = b._data._float; - r_dst = ra - rb; - } - return; - case VECTOR2: { - r_dst = *reinterpret_cast<const Vector2 *>(a._data._mem) - *reinterpret_cast<const Vector2 *>(b._data._mem); - } - return; - case VECTOR2I: { - int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; - r_dst = Vector2i(int32_t(vax - vbx), int32_t(vay - vby)); - } - return; - case RECT2: { - const Rect2 *ra = reinterpret_cast<const Rect2 *>(a._data._mem); - const Rect2 *rb = reinterpret_cast<const Rect2 *>(b._data._mem); - r_dst = Rect2(ra->position - rb->position, ra->size - rb->size); - } - return; - case RECT2I: { - const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); - const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); - - int32_t vax = ra->position.x; - int32_t vay = ra->position.y; - int32_t vbx = ra->size.x; - int32_t vby = ra->size.y; - int32_t vcx = rb->position.x; - int32_t vcy = rb->position.y; - int32_t vdx = rb->size.x; - int32_t vdy = rb->size.y; - - r_dst = Rect2i(int32_t(vax - vbx), int32_t(vay - vby), int32_t(vcx - vdx), int32_t(vcy - vdy)); - } - return; - case VECTOR3: { - r_dst = *reinterpret_cast<const Vector3 *>(a._data._mem) - *reinterpret_cast<const Vector3 *>(b._data._mem); - } - return; - case VECTOR3I: { - int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; - int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; - int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; - r_dst = Vector3i(int32_t(vax - vbx), int32_t(vay - vby), int32_t(vaz - vbz)); - } - return; - case AABB: { - const ::AABB *ra = reinterpret_cast<const ::AABB *>(a._data._mem); - const ::AABB *rb = reinterpret_cast<const ::AABB *>(b._data._mem); - r_dst = ::AABB(ra->position - rb->position, ra->size - rb->size); - } - return; - case QUATERNION: { - Quaternion empty_rot; - const Quaternion *qa = reinterpret_cast<const Quaternion *>(a._data._mem); - const Quaternion *qb = reinterpret_cast<const Quaternion *>(b._data._mem); - r_dst = (*qb).inverse() * *qa; - } - return; - case COLOR: { - const Color *ca = reinterpret_cast<const Color *>(a._data._mem); - const Color *cb = reinterpret_cast<const Color *>(b._data._mem); - float new_r = ca->r - cb->r; - float new_g = ca->g - cb->g; - float new_b = ca->b - cb->b; - float new_a = ca->a - cb->a; - new_r = new_r > 1.0 ? 1.0 : new_r; - new_g = new_g > 1.0 ? 1.0 : new_g; - new_b = new_b > 1.0 ? 1.0 : new_b; - new_a = new_a > 1.0 ? 1.0 : new_a; - r_dst = Color(new_r, new_g, new_b, new_a); - } - return; - default: { - r_dst = a; - } - return; - } -} - -void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst) { - if (a.type != b.type) { - if (a.is_num() && b.is_num()) { - real_t va = a; - real_t vb = b; - r_dst = va + vb * c; - } else { - r_dst = a; - } - return; - } - - switch (a.type) { - case NIL: { - r_dst = Variant(); - } - return; - case INT: { - int64_t va = a._data._int; - int64_t vb = b._data._int; - r_dst = int(va + vb * c + 0.5); - } - return; - case FLOAT: { - double ra = a._data._float; - double rb = b._data._float; - r_dst = ra + rb * c; - } - return; - case VECTOR2: { - r_dst = *reinterpret_cast<const Vector2 *>(a._data._mem) + *reinterpret_cast<const Vector2 *>(b._data._mem) * c; - } - return; - case VECTOR2I: { - int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; - r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); - } - return; - case RECT2: { - const Rect2 *ra = reinterpret_cast<const Rect2 *>(a._data._mem); - const Rect2 *rb = reinterpret_cast<const Rect2 *>(b._data._mem); - r_dst = Rect2(ra->position + rb->position * c, ra->size + rb->size * c); - } - return; - case RECT2I: { - const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); - const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); - - int32_t vax = ra->position.x; - int32_t vay = ra->position.y; - int32_t vbx = ra->size.x; - int32_t vby = ra->size.y; - int32_t vcx = rb->position.x; - int32_t vcy = rb->position.y; - int32_t vdx = rb->size.x; - int32_t vdy = rb->size.y; - - r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); - } - return; - case VECTOR3: { - r_dst = *reinterpret_cast<const Vector3 *>(a._data._mem) + *reinterpret_cast<const Vector3 *>(b._data._mem) * c; - } - return; - case VECTOR3I: { - int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; - int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; - int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; - r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); - } - return; - case AABB: { - const ::AABB *ra = reinterpret_cast<const ::AABB *>(a._data._mem); - const ::AABB *rb = reinterpret_cast<const ::AABB *>(b._data._mem); - r_dst = ::AABB(ra->position + rb->position * c, ra->size + rb->size * c); - } - return; - case QUATERNION: { - Quaternion empty_rot; - const Quaternion *qa = reinterpret_cast<const Quaternion *>(a._data._mem); - const Quaternion *qb = reinterpret_cast<const Quaternion *>(b._data._mem); - r_dst = *qa * empty_rot.slerp(*qb, c); - } - return; - case COLOR: { - const Color *ca = reinterpret_cast<const Color *>(a._data._mem); - const Color *cb = reinterpret_cast<const Color *>(b._data._mem); - float new_r = ca->r + cb->r * c; - float new_g = ca->g + cb->g * c; - float new_b = ca->b + cb->b * c; - float new_a = ca->a + cb->a * c; - new_r = new_r > 1.0 ? 1.0 : new_r; - new_g = new_g > 1.0 ? 1.0 : new_g; - new_b = new_b > 1.0 ? 1.0 : new_b; - new_a = new_a > 1.0 ? 1.0 : new_a; - r_dst = Color(new_r, new_g, new_b, new_a); - } - return; - default: { - r_dst = c < 0.5 ? a : b; - } - return; - } -} - -void Variant::interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst) { - if (a.type != b.type) { - if (a.is_num() && b.is_num()) { - //not as efficient but.. - real_t va = a; - real_t vb = b; - r_dst = va + (vb - va) * c; - - } else { - r_dst = a; - } - return; - } - - switch (a.type) { - case NIL: { - r_dst = Variant(); - } - return; - case BOOL: { - r_dst = a; - } - return; - case INT: { - int64_t va = a._data._int; - int64_t vb = b._data._int; - r_dst = int(va + (vb - va) * c); - } - return; - case FLOAT: { - real_t va = a._data._float; - real_t vb = b._data._float; - r_dst = va + (vb - va) * c; - } - return; - case STRING: { - //this is pretty funny and bizarre, but artists like to use it for typewriter effects - String sa = *reinterpret_cast<const String *>(a._data._mem); - String sb = *reinterpret_cast<const String *>(b._data._mem); - String dst; - int sa_len = sa.length(); - int sb_len = sb.length(); - int csize = sa_len + (sb_len - sa_len) * c; - if (csize == 0) { - r_dst = ""; - return; - } - dst.resize(csize + 1); - dst[csize] = 0; - int split = csize / 2; - - for (int i = 0; i < csize; i++) { - char32_t chr = ' '; - - if (i < split) { - if (i < sa.length()) { - chr = sa[i]; - } else if (i < sb.length()) { - chr = sb[i]; - } - - } else { - if (i < sb.length()) { - chr = sb[i]; - } else if (i < sa.length()) { - chr = sa[i]; - } - } - - dst[i] = chr; - } - - r_dst = dst; - } - return; - case VECTOR2: { - r_dst = reinterpret_cast<const Vector2 *>(a._data._mem)->lerp(*reinterpret_cast<const Vector2 *>(b._data._mem), c); - } - return; - case VECTOR2I: { - int32_t vax = reinterpret_cast<const Vector2i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector2i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector2i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector2i *>(b._data._mem)->y; - r_dst = Vector2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5)); - } - return; - - case RECT2: { - r_dst = Rect2(reinterpret_cast<const Rect2 *>(a._data._mem)->position.lerp(reinterpret_cast<const Rect2 *>(b._data._mem)->position, c), reinterpret_cast<const Rect2 *>(a._data._mem)->size.lerp(reinterpret_cast<const Rect2 *>(b._data._mem)->size, c)); - } - return; - case RECT2I: { - const Rect2i *ra = reinterpret_cast<const Rect2i *>(a._data._mem); - const Rect2i *rb = reinterpret_cast<const Rect2i *>(b._data._mem); - - int32_t vax = ra->position.x; - int32_t vay = ra->position.y; - int32_t vbx = ra->size.x; - int32_t vby = ra->size.y; - int32_t vcx = rb->position.x; - int32_t vcy = rb->position.y; - int32_t vdx = rb->size.x; - int32_t vdy = rb->size.y; - - r_dst = Rect2i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vcx + vdx * c + 0.5), int32_t(vcy + vdy * c + 0.5)); - } - return; - - case VECTOR3: { - r_dst = reinterpret_cast<const Vector3 *>(a._data._mem)->lerp(*reinterpret_cast<const Vector3 *>(b._data._mem), c); - } - return; - case VECTOR3I: { - int32_t vax = reinterpret_cast<const Vector3i *>(a._data._mem)->x; - int32_t vbx = reinterpret_cast<const Vector3i *>(b._data._mem)->x; - int32_t vay = reinterpret_cast<const Vector3i *>(a._data._mem)->y; - int32_t vby = reinterpret_cast<const Vector3i *>(b._data._mem)->y; - int32_t vaz = reinterpret_cast<const Vector3i *>(a._data._mem)->z; - int32_t vbz = reinterpret_cast<const Vector3i *>(b._data._mem)->z; - r_dst = Vector3i(int32_t(vax + vbx * c + 0.5), int32_t(vay + vby * c + 0.5), int32_t(vaz + vbz * c + 0.5)); - } - return; - - case TRANSFORM2D: { - r_dst = a._data._transform2d->interpolate_with(*b._data._transform2d, c); - } - return; - case PLANE: { - r_dst = a; - } - return; - case QUATERNION: { - r_dst = reinterpret_cast<const Quaternion *>(a._data._mem)->slerp(*reinterpret_cast<const Quaternion *>(b._data._mem), c); - } - return; - case AABB: { - r_dst = ::AABB(a._data._aabb->position.lerp(b._data._aabb->position, c), a._data._aabb->size.lerp(b._data._aabb->size, c)); - } - return; - case BASIS: { - r_dst = a._data._basis->lerp(*b._data._basis, c); - } - return; - case TRANSFORM3D: { - r_dst = a._data._transform3d->interpolate_with(*b._data._transform3d, c); - } - return; - case COLOR: { - r_dst = reinterpret_cast<const Color *>(a._data._mem)->lerp(*reinterpret_cast<const Color *>(b._data._mem), c); - } - return; - case STRING_NAME: { - r_dst = a; - } - return; - case NODE_PATH: { - r_dst = a; - } - return; - case RID: { - r_dst = a; - } - return; - case OBJECT: { - r_dst = a; - } - return; - case DICTIONARY: { - } - return; - case ARRAY: { - r_dst = a; - } - return; - case PACKED_BYTE_ARRAY: { - r_dst = a; - } - return; - case PACKED_INT32_ARRAY: { - const Vector<int32_t> *arr_a = &PackedArrayRef<int32_t>::get_array(a._data.packed_array); - const Vector<int32_t> *arr_b = &PackedArrayRef<int32_t>::get_array(b._data.packed_array); - int32_t sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<int32_t> v; - v.resize(sz); - { - int32_t *vw = v.ptrw(); - const int32_t *ar = arr_a->ptr(); - const int32_t *br = arr_b->ptr(); - - Variant va; - for (int32_t i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_INT64_ARRAY: { - const Vector<int64_t> *arr_a = &PackedArrayRef<int64_t>::get_array(a._data.packed_array); - const Vector<int64_t> *arr_b = &PackedArrayRef<int64_t>::get_array(b._data.packed_array); - int64_t sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<int64_t> v; - v.resize(sz); - { - int64_t *vw = v.ptrw(); - const int64_t *ar = arr_a->ptr(); - const int64_t *br = arr_b->ptr(); - - Variant va; - for (int64_t i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_FLOAT32_ARRAY: { - const Vector<float> *arr_a = &PackedArrayRef<float>::get_array(a._data.packed_array); - const Vector<float> *arr_b = &PackedArrayRef<float>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<float> v; - v.resize(sz); - { - float *vw = v.ptrw(); - const float *ar = arr_a->ptr(); - const float *br = arr_b->ptr(); - - Variant va; - for (int i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_FLOAT64_ARRAY: { - const Vector<double> *arr_a = &PackedArrayRef<double>::get_array(a._data.packed_array); - const Vector<double> *arr_b = &PackedArrayRef<double>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<double> v; - v.resize(sz); - { - double *vw = v.ptrw(); - const double *ar = arr_a->ptr(); - const double *br = arr_b->ptr(); - - Variant va; - for (int i = 0; i < sz; i++) { - Variant::interpolate(ar[i], br[i], c, va); - vw[i] = va; - } - } - r_dst = v; - } - } - return; - case PACKED_STRING_ARRAY: { - r_dst = a; - } - return; - case PACKED_VECTOR2_ARRAY: { - const Vector<Vector2> *arr_a = &PackedArrayRef<Vector2>::get_array(a._data.packed_array); - const Vector<Vector2> *arr_b = &PackedArrayRef<Vector2>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<Vector2> v; - v.resize(sz); - { - Vector2 *vw = v.ptrw(); - const Vector2 *ar = arr_a->ptr(); - const Vector2 *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - case PACKED_VECTOR3_ARRAY: { - const Vector<Vector3> *arr_a = &PackedArrayRef<Vector3>::get_array(a._data.packed_array); - const Vector<Vector3> *arr_b = &PackedArrayRef<Vector3>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<Vector3> v; - v.resize(sz); - { - Vector3 *vw = v.ptrw(); - const Vector3 *ar = arr_a->ptr(); - const Vector3 *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - case PACKED_COLOR_ARRAY: { - const Vector<Color> *arr_a = &PackedArrayRef<Color>::get_array(a._data.packed_array); - const Vector<Color> *arr_b = &PackedArrayRef<Color>::get_array(b._data.packed_array); - int sz = arr_a->size(); - if (sz == 0 || arr_b->size() != sz) { - r_dst = a; - } else { - Vector<Color> v; - v.resize(sz); - { - Color *vw = v.ptrw(); - const Color *ar = arr_a->ptr(); - const Color *br = arr_b->ptr(); - - for (int i = 0; i < sz; i++) { - vw[i] = ar[i].lerp(br[i], c); - } - } - r_dst = v; - } - } - return; - default: { - r_dst = a; - } - } -} - void Variant::_register_variant_setters_getters() { register_named_setters_getters(); register_indexed_setters_getters(); |