diff options
Diffstat (limited to 'core/io')
80 files changed, 1426 insertions, 1391 deletions
diff --git a/core/io/compression.cpp b/core/io/compression.cpp index 790b6febc0..ae5ccf8354 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -212,7 +212,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s strm.avail_in = p_src_size; // Ensure the destination buffer is empty - p_dst_vect->resize(0); + p_dst_vect->clear(); // decompress until deflate stream ends or end of file do { @@ -244,7 +244,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s WARN_PRINT(strm.msg); } (void)inflateEnd(&strm); - p_dst_vect->resize(0); + p_dst_vect->clear(); return ret; } } while (strm.avail_out > 0 && strm.avail_in > 0); @@ -254,7 +254,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s // Enforce max output size if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { (void)inflateEnd(&strm); - p_dst_vect->resize(0); + p_dst_vect->clear(); return Z_BUF_ERROR; } } while (ret != Z_STREAM_END); diff --git a/core/io/compression.h b/core/io/compression.h index 06f26876e5..2e66970a6c 100644 --- a/core/io/compression.h +++ b/core/io/compression.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 33f992e153..bc24cac955 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -129,12 +129,9 @@ void ConfigFile::erase_section_key(const String &p_section, const String &p_key) Error ConfigFile::save(const String &p_path) { Error err; - FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); if (err) { - if (file) { - memdelete(file); - } return err; } @@ -143,17 +140,16 @@ Error ConfigFile::save(const String &p_path) { Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err); if (err) { return err; } - FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + Ref<FileAccessEncrypted> fae; + fae.instantiate(); err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_WRITE_AES256); if (err) { - memdelete(fae); - memdelete(f); return err; } return _internal_save(fae); @@ -161,29 +157,28 @@ Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_ Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass) { Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err); if (err) { return err; } - FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + Ref<FileAccessEncrypted> fae; + fae.instantiate(); err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_WRITE_AES256); if (err) { - memdelete(fae); - memdelete(f); return err; } return _internal_save(fae); } -Error ConfigFile::_internal_save(FileAccess *file) { +Error ConfigFile::_internal_save(Ref<FileAccess> file) { for (OrderedHashMap<String, OrderedHashMap<String, Variant>>::Element E = values.front(); E; E = E.next()) { if (E != values.front()) { file->store_string("\n"); } - if (E.key() != "") { + if (!E.key().is_empty()) { file->store_string("[" + E.key() + "]\n\n"); } @@ -194,16 +189,14 @@ Error ConfigFile::_internal_save(FileAccess *file) { } } - memdelete(file); - return OK; } Error ConfigFile::load(const String &p_path) { Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); - if (!f) { + if (f.is_null()) { return err; } @@ -212,17 +205,16 @@ Error ConfigFile::load(const String &p_path) { Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { return err; } - FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + Ref<FileAccessEncrypted> fae; + fae.instantiate(); err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_READ); if (err) { - memdelete(fae); - memdelete(f); return err; } return _internal_load(p_path, fae); @@ -230,31 +222,28 @@ Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_ Error ConfigFile::load_encrypted_pass(const String &p_path, const String &p_pass) { Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { return err; } - FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + Ref<FileAccessEncrypted> fae; + fae.instantiate(); err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_READ); if (err) { - memdelete(fae); - memdelete(f); return err; } return _internal_load(p_path, fae); } -Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { +Error ConfigFile::_internal_load(const String &p_path, Ref<FileAccess> f) { VariantParser::StreamFile stream; stream.f = f; Error err = _parse(p_path, &stream); - memdelete(f); - return err; } @@ -287,9 +276,9 @@ Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) return err; } - if (assign != String()) { + if (!assign.is_empty()) { set_value(section, assign, value); - } else if (next_tag.name != String()) { + } else if (!next_tag.name.is_empty()) { section = next_tag.name; } } diff --git a/core/io/config_file.h b/core/io/config_file.h index dbba43ace5..7a52b0e16a 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -43,8 +43,8 @@ class ConfigFile : public RefCounted { PackedStringArray _get_sections() const; PackedStringArray _get_section_keys(const String &p_section) const; - Error _internal_load(const String &p_path, FileAccess *f); - Error _internal_save(FileAccess *file); + Error _internal_load(const String &p_path, Ref<FileAccess> f); + Error _internal_save(Ref<FileAccess> file); Error _parse(const String &p_path, VariantParser::Stream *p_stream); diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 3bff0a3fd5..433a7efb21 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -79,7 +79,7 @@ static Error _erase_recursive(DirAccess *da) { da->list_dir_begin(); String n = da->get_next(); - while (n != String()) { + while (!n.is_empty()) { if (n != "." && n != "..") { if (da->current_is_dir()) { dirs.push_back(n); @@ -145,17 +145,21 @@ Error DirAccess::make_dir_recursive(String p_dir) { full_dir = full_dir.replace("\\", "/"); - //int slices = full_dir.get_slice_count("/"); - String base; if (full_dir.begins_with("res://")) { base = "res://"; } else if (full_dir.begins_with("user://")) { base = "user://"; + } else if (full_dir.is_network_share_path()) { + int pos = full_dir.find("/", 2); + ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER); + pos = full_dir.find("/", pos + 1); + ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER); + base = full_dir.substr(0, pos + 1); } else if (full_dir.begins_with("/")) { base = "/"; - } else if (full_dir.find(":/") != -1) { + } else if (full_dir.contains(":/")) { base = full_dir.substr(0, full_dir.find(":/") + 2); } else { ERR_FAIL_V(ERR_INVALID_PARAMETER); @@ -183,7 +187,7 @@ String DirAccess::fix_path(String p_path) const { if (ProjectSettings::get_singleton()) { if (p_path.begins_with("res://")) { String resource_path = ProjectSettings::get_singleton()->get_resource_path(); - if (resource_path != "") { + if (!resource_path.is_empty()) { return p_path.replace_first("res:/", resource_path); } return p_path.replace_first("res://", ""); @@ -194,7 +198,7 @@ String DirAccess::fix_path(String p_path) const { case ACCESS_USERDATA: { if (p_path.begins_with("user://")) { String data_dir = OS::get_singleton()->get_user_data_dir(); - if (data_dir != "") { + if (!data_dir.is_empty()) { return p_path.replace_first("user:/", data_dir); } return p_path.replace_first("user://", ""); @@ -213,8 +217,8 @@ String DirAccess::fix_path(String p_path) const { DirAccess::CreateFunc DirAccess::create_func[ACCESS_MAX] = { nullptr, nullptr, nullptr }; -DirAccess *DirAccess::create_for_path(const String &p_path) { - DirAccess *da = nullptr; +Ref<DirAccess> DirAccess::create_for_path(const String &p_path) { + Ref<DirAccess> da; if (p_path.begins_with("res://")) { da = create(ACCESS_RESOURCES); } else if (p_path.begins_with("user://")) { @@ -226,80 +230,77 @@ DirAccess *DirAccess::create_for_path(const String &p_path) { return da; } -DirAccess *DirAccess::open(const String &p_path, Error *r_error) { - DirAccess *da = create_for_path(p_path); - - ERR_FAIL_COND_V_MSG(!da, nullptr, "Cannot create DirAccess for path '" + p_path + "'."); +Ref<DirAccess> DirAccess::open(const String &p_path, Error *r_error) { + Ref<DirAccess> da = create_for_path(p_path); + ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, "Cannot create DirAccess for path '" + p_path + "'."); Error err = da->change_dir(p_path); if (r_error) { *r_error = err; } if (err != OK) { - memdelete(da); return nullptr; } return da; } -DirAccess *DirAccess::create(AccessType p_access) { - DirAccess *da = create_func[p_access] ? create_func[p_access]() : nullptr; - if (da) { +Ref<DirAccess> DirAccess::create(AccessType p_access) { + Ref<DirAccess> da = create_func[p_access] ? create_func[p_access]() : nullptr; + if (da.is_valid()) { da->_access_type = p_access; + + // for ACCESS_RESOURCES and ACCESS_FILESYSTEM, current_dir already defaults to where game was started + // in case current directory is force changed elsewhere for ACCESS_RESOURCES + if (p_access == ACCESS_RESOURCES) { + da->change_dir("res://"); + } else if (p_access == ACCESS_USERDATA) { + da->change_dir("user://"); + } } return da; } String DirAccess::get_full_path(const String &p_path, AccessType p_access) { - DirAccess *d = DirAccess::create(p_access); - if (!d) { + Ref<DirAccess> d = DirAccess::create(p_access); + if (d.is_null()) { return p_path; } d->change_dir(p_path); String full = d->get_current_dir(); - memdelete(d); return full; } Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { //printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data()); Error err; - FileAccess *fsrc = FileAccess::open(p_from, FileAccess::READ, &err); - - if (err) { - ERR_PRINT("Failed to open " + p_from); - return err; - } - - FileAccess *fdst = FileAccess::open(p_to, FileAccess::WRITE, &err); - if (err) { - fsrc->close(); - memdelete(fsrc); - ERR_PRINT("Failed to open " + p_to); - return err; - } + { + Ref<FileAccess> fsrc = FileAccess::open(p_from, FileAccess::READ, &err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_from); + + Ref<FileAccess> fdst = FileAccess::open(p_to, FileAccess::WRITE, &err); + ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_to); + + fsrc->seek_end(0); + int size = fsrc->get_position(); + fsrc->seek(0); + err = OK; + while (size--) { + if (fsrc->get_error() != OK) { + err = fsrc->get_error(); + break; + } + if (fdst->get_error() != OK) { + err = fdst->get_error(); + break; + } - fsrc->seek_end(0); - int size = fsrc->get_position(); - fsrc->seek(0); - err = OK; - while (size--) { - if (fsrc->get_error() != OK) { - err = fsrc->get_error(); - break; + fdst->store_8(fsrc->get_8()); } - if (fdst->get_error() != OK) { - err = fdst->get_error(); - break; - } - - fdst->store_8(fsrc->get_8()); } if (err == OK && p_chmod_flags != -1) { - fdst->close(); err = FileAccess::set_unix_permissions(p_to, p_chmod_flags); // If running on a platform with no chmod support (i.e., Windows), don't fail if (err == ERR_UNAVAILABLE) { @@ -307,9 +308,6 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { } } - memdelete(fsrc); - memdelete(fdst); - return err; } @@ -331,13 +329,13 @@ public: } }; -Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags, bool p_copy_links) { +Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod_flags, bool p_copy_links) { List<String> dirs; String curdir = get_current_dir(); list_dir_begin(); String n = get_next(); - while (n != String()) { + while (!n.is_empty()) { if (n != "." && n != "..") { if (p_copy_links && is_link(get_current_dir().plus_file(n))) { create_link(read_link(get_current_dir().plus_file(n)), p_to + n); @@ -387,14 +385,11 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_copy_links) { ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist."); - DirAccess *target_da = DirAccess::create_for_path(p_to); - ERR_FAIL_COND_V_MSG(!target_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'."); + Ref<DirAccess> target_da = DirAccess::create_for_path(p_to); + ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'."); if (!target_da->dir_exists(p_to)) { Error err = target_da->make_dir_recursive(p_to); - if (err) { - memdelete(target_da); - } ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'."); } @@ -404,14 +399,11 @@ Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_ DirChanger dir_changer(this, p_from); Error err = _copy_dir(target_da, p_to, p_chmod_flags, p_copy_links); - memdelete(target_da); return err; } bool DirAccess::exists(String p_dir) { - DirAccess *da = DirAccess::create_for_path(p_dir); - bool valid = da->change_dir(p_dir) == OK; - memdelete(da); - return valid; + Ref<DirAccess> da = DirAccess::create_for_path(p_dir); + return da->change_dir(p_dir) == OK; } diff --git a/core/io/dir_access.h b/core/io/dir_access.h index 16154a4850..0125f011b5 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -31,11 +31,12 @@ #ifndef DIR_ACCESS_H #define DIR_ACCESS_H +#include "core/object/ref_counted.h" #include "core/string/ustring.h" #include "core/typedefs.h" //@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies -class DirAccess { +class DirAccess : public RefCounted { public: enum AccessType { ACCESS_RESOURCES, @@ -44,13 +45,13 @@ public: ACCESS_MAX }; - typedef DirAccess *(*CreateFunc)(); + typedef Ref<DirAccess> (*CreateFunc)(); private: AccessType _access_type = ACCESS_FILESYSTEM; static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object - Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags, bool p_copy_links); + Error _copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod_flags, bool p_copy_links); protected: String _get_root_path() const; @@ -59,7 +60,7 @@ protected: String fix_path(String p_path) const; template <class T> - static DirAccess *_create_builtin() { + static Ref<DirAccess> _create_builtin() { return memnew(T); } @@ -77,7 +78,7 @@ public: virtual bool drives_are_shortcuts(); virtual Error change_dir(String p_dir) = 0; ///< can be relative or absolute, return false on success - virtual String get_current_dir(bool p_include_drive = true) = 0; ///< return current dir location + virtual String get_current_dir(bool p_include_drive = true) const = 0; ///< return current dir location virtual Error make_dir(String p_dir) = 0; virtual Error make_dir_recursive(String p_dir); virtual Error erase_contents_recursive(); //super dangerous, use with care! @@ -101,47 +102,29 @@ public: // Meant for editor code when we want to quickly remove a file without custom // handling (e.g. removing a cache file). static void remove_file_or_error(String p_path) { - DirAccess *da = create(ACCESS_FILESYSTEM); + Ref<DirAccess> da = create(ACCESS_FILESYSTEM); if (da->file_exists(p_path)) { if (da->remove(p_path) != OK) { ERR_FAIL_MSG("Cannot remove file or directory: " + p_path); } } - memdelete(da); } virtual String get_filesystem_type() const = 0; static String get_full_path(const String &p_path, AccessType p_access); - static DirAccess *create_for_path(const String &p_path); + static Ref<DirAccess> create_for_path(const String &p_path); - static DirAccess *create(AccessType p_access); + static Ref<DirAccess> create(AccessType p_access); template <class T> static void make_default(AccessType p_access) { create_func[p_access] = _create_builtin<T>; } - static DirAccess *open(const String &p_path, Error *r_error = nullptr); + static Ref<DirAccess> open(const String &p_path, Error *r_error = nullptr); DirAccess() {} virtual ~DirAccess() {} }; -struct DirAccessRef { - _FORCE_INLINE_ DirAccess *operator->() { - return f; - } - - operator bool() const { return f != nullptr; } - - DirAccess *f; - - DirAccessRef(DirAccess *fa) { f = fa; } - ~DirAccessRef() { - if (f) { - memdelete(f); - } - } -}; - #endif // DIR_ACCESS_H diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp index 655fb18535..b5c44e1b64 100644 --- a/core/io/dtls_server.cpp +++ b/core/io/dtls_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h index 02a32533e1..0c9b0149b0 100644 --- a/core/io/dtls_server.h +++ b/core/io/dtls_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index e6e79dff8a..7d8da1b11c 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -42,10 +42,10 @@ FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr; bool FileAccess::backup_save = false; -FileAccess *FileAccess::create(AccessType p_access) { +Ref<FileAccess> FileAccess::create(AccessType p_access) { ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr); - FileAccess *ret = create_func[p_access](); + Ref<FileAccess> ret = create_func[p_access](); ret->_set_access_type(p_access); return ret; } @@ -55,11 +55,10 @@ bool FileAccess::exists(const String &p_name) { return true; } - FileAccess *f = open(p_name, READ); - if (!f) { + Ref<FileAccess> f = open(p_name, READ); + if (f.is_null()) { return false; } - memdelete(f); return true; } @@ -67,8 +66,8 @@ void FileAccess::_set_access_type(AccessType p_access) { _access_type = p_access; } -FileAccess *FileAccess::create_for_path(const String &p_path) { - FileAccess *ret = nullptr; +Ref<FileAccess> FileAccess::create_for_path(const String &p_path) { + Ref<FileAccess> ret; if (p_path.begins_with("res://")) { ret = create(ACCESS_RESOURCES); } else if (p_path.begins_with("user://")) { @@ -85,13 +84,13 @@ Error FileAccess::reopen(const String &p_path, int p_mode_flags) { return _open(p_path, p_mode_flags); } -FileAccess *FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) { +Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) { //try packed data first - FileAccess *ret = nullptr; + Ref<FileAccess> ret; if (!(p_mode_flags & WRITE) && PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled()) { ret = PackedData::get_singleton()->try_open_path(p_path); - if (ret) { + if (ret.is_valid()) { if (r_error) { *r_error = OK; } @@ -106,8 +105,7 @@ FileAccess *FileAccess::open(const String &p_path, int p_mode_flags, Error *r_er *r_error = err; } if (err != OK) { - memdelete(ret); - ret = nullptr; + ret.unref(); } return ret; @@ -127,7 +125,7 @@ String FileAccess::fix_path(const String &p_path) const { if (ProjectSettings::get_singleton()) { if (r_path.begins_with("res://")) { String resource_path = ProjectSettings::get_singleton()->get_resource_path(); - if (resource_path != "") { + if (!resource_path.is_empty()) { return r_path.replace("res:/", resource_path); } return r_path.replace("res://", ""); @@ -138,7 +136,7 @@ String FileAccess::fix_path(const String &p_path) const { case ACCESS_USERDATA: { if (r_path.begins_with("user://")) { String data_dir = OS::get_singleton()->get_user_data_dir(); - if (data_dir != "") { + if (!data_dir.is_empty()) { return r_path.replace("user:/", data_dir); } return r_path.replace("user://", ""); @@ -463,11 +461,10 @@ uint64_t FileAccess::get_modified_time(const String &p_file) { return 0; } - FileAccess *fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'."); + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'."); uint64_t mt = fa->_get_modified_time(p_file); - memdelete(fa); return mt; } @@ -476,11 +473,10 @@ uint32_t FileAccess::get_unix_permissions(const String &p_file) { return 0; } - FileAccess *fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'."); + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'."); uint32_t mt = fa->_get_unix_permissions(p_file); - memdelete(fa); return mt; } @@ -489,11 +485,10 @@ Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissi return ERR_UNAVAILABLE; } - FileAccess *fa = create_for_path(p_file); - ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); + Ref<FileAccess> fa = create_for_path(p_file); + ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'."); Error err = fa->_set_unix_permissions(p_file, p_permissions); - memdelete(fa); return err; } @@ -538,7 +533,7 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_ for (int i = 0; i < size; ++i) { String value = p_values[i]; - if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) { + if (value.contains("\"") || value.contains(p_delim) || value.contains("\n")) { value = "\"" + value.replace("\"", "\"\"") + "\""; } if (i < size - 1) { @@ -559,8 +554,8 @@ void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) { } Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) { - FileAccess *f = FileAccess::open(p_path, READ, r_error); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, READ, r_error); + if (f.is_null()) { if (r_error) { // if error requested, do not throw error return Vector<uint8_t>(); } @@ -569,7 +564,6 @@ Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_err Vector<uint8_t> data; data.resize(f->get_length()); f->get_buffer(data.ptrw(), data.size()); - memdelete(f); return data; } @@ -592,8 +586,8 @@ String FileAccess::get_file_as_string(const String &p_path, Error *r_error) { } String FileAccess::get_md5(const String &p_file) { - FileAccess *f = FileAccess::open(p_file, READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_file, READ); + if (f.is_null()) { return String(); } @@ -615,8 +609,6 @@ String FileAccess::get_md5(const String &p_file) { unsigned char hash[16]; ctx.finish(hash); - memdelete(f); - return String::md5(hash); } @@ -625,8 +617,8 @@ String FileAccess::get_multiple_md5(const Vector<String> &p_file) { ctx.start(); for (int i = 0; i < p_file.size(); i++) { - FileAccess *f = FileAccess::open(p_file[i], READ); - ERR_CONTINUE(!f); + Ref<FileAccess> f = FileAccess::open(p_file[i], READ); + ERR_CONTINUE(f.is_null()); unsigned char step[32768]; @@ -639,7 +631,6 @@ String FileAccess::get_multiple_md5(const Vector<String> &p_file) { break; } } - memdelete(f); } unsigned char hash[16]; @@ -649,8 +640,8 @@ String FileAccess::get_multiple_md5(const Vector<String> &p_file) { } String FileAccess::get_sha256(const String &p_file) { - FileAccess *f = FileAccess::open(p_file, READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_file, READ); + if (f.is_null()) { return String(); } @@ -672,6 +663,5 @@ String FileAccess::get_sha256(const String &p_file) { unsigned char hash[32]; ctx.finish(hash); - memdelete(f); return String::hex_encode_buffer(hash, 32); } diff --git a/core/io/file_access.h b/core/io/file_access.h index 5804aa2c47..e2c11142d7 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -32,6 +32,7 @@ #define FILE_ACCESS_H #include "core/math/math_defs.h" +#include "core/object/ref_counted.h" #include "core/os/memory.h" #include "core/string/ustring.h" #include "core/typedefs.h" @@ -40,7 +41,7 @@ * Multi-Platform abstraction for accessing to files. */ -class FileAccess { +class FileAccess : public RefCounted { public: enum AccessType { ACCESS_RESOURCES, @@ -51,7 +52,7 @@ public: typedef void (*FileCloseFailNotify)(const String &); - typedef FileAccess *(*CreateFunc)(); + typedef Ref<FileAccess> (*CreateFunc)(); bool big_endian = false; bool real_is_double = false; @@ -71,7 +72,7 @@ private: AccessType _access_type = ACCESS_FILESYSTEM; static CreateFunc create_func[ACCESS_MAX]; /** default file access creation function for a platform */ template <class T> - static FileAccess *_create_builtin() { + static Ref<FileAccess> _create_builtin() { return memnew(T); } @@ -87,7 +88,6 @@ public: WRITE_READ = 7, }; - virtual void close() = 0; ///< close a file 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 @@ -148,9 +148,9 @@ public: virtual Error reopen(const String &p_path, int p_mode_flags); ///< does not change the AccessType - static FileAccess *create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files. - static FileAccess *create_for_path(const String &p_path); - static 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> 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 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); @@ -176,23 +176,4 @@ public: virtual ~FileAccess() {} }; -struct FileAccessRef { - _FORCE_INLINE_ FileAccess *operator->() { - return f; - } - - operator bool() const { return f != nullptr; } - - FileAccess *f; - - operator FileAccess *() { return f; } - - FileAccessRef(FileAccess *fa) { f = fa; } - ~FileAccessRef() { - if (f) { - memdelete(f); - } - } -}; - #endif // FILE_ACCESS_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index df631053b8..1d0a718166 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -58,12 +58,12 @@ void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_ } \ } -Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { +Error FileAccessCompressed::open_after_magic(Ref<FileAccess> p_base) { f = p_base; cmode = (Compression::Mode)f->get_32(); block_size = f->get_32(); if (block_size == 0) { - f = nullptr; // Let the caller to handle the FileAccess object if failed to open as compressed file. + f.unref(); ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Can't open compressed file '" + p_base->get_path() + "' with block size 0, it is corrupted."); } read_total = f->get_32(); @@ -88,26 +88,22 @@ Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { read_block_count = bc; read_block_size = read_blocks.size() == 1 ? read_total : block_size; - Compression::decompress(buffer.ptrw(), read_block_size, comp_buffer.ptr(), read_blocks[0].csize, cmode); + int ret = Compression::decompress(buffer.ptrw(), read_block_size, comp_buffer.ptr(), read_blocks[0].csize, cmode); read_block = 0; read_pos = 0; - return OK; + return ret == -1 ? ERR_FILE_CORRUPT : OK; } Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { ERR_FAIL_COND_V(p_mode_flags == READ_WRITE, ERR_UNAVAILABLE); - - if (f) { - close(); - } + _close(); Error err; f = FileAccess::open(p_path, p_mode_flags, &err); if (err != OK) { //not openable - - f = nullptr; + f.unref(); return err; } @@ -125,18 +121,18 @@ Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { char rmagic[5]; f->get_buffer((uint8_t *)rmagic, 4); rmagic[4] = 0; - if (magic != rmagic || open_after_magic(f) != OK) { - memdelete(f); - f = nullptr; - return ERR_FILE_UNRECOGNIZED; + err = ERR_FILE_UNRECOGNIZED; + if (magic != rmagic || (err = open_after_magic(f)) != OK) { + f.unref(); + return err; } } return OK; } -void FileAccessCompressed::close() { - if (!f) { +void FileAccessCompressed::_close() { + if (f.is_null()) { return; } @@ -181,17 +177,15 @@ void FileAccessCompressed::close() { buffer.clear(); read_blocks.clear(); } - - memdelete(f); - f = nullptr; + f.unref(); } bool FileAccessCompressed::is_open() const { - return f != nullptr; + return f.is_valid(); } void FileAccessCompressed::seek(uint64_t p_position) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); if (writing) { ERR_FAIL_COND(p_position > write_max); @@ -210,7 +204,8 @@ void FileAccessCompressed::seek(uint64_t p_position) { read_block = block_idx; f->seek(read_blocks[read_block].offset); f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize); - Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode); + int ret = Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode); + ERR_FAIL_COND_MSG(ret == -1, "Compressed file is corrupt."); read_block_size = read_block == read_block_count - 1 ? read_total % block_size : block_size; } @@ -220,7 +215,7 @@ void FileAccessCompressed::seek(uint64_t p_position) { } void FileAccessCompressed::seek_end(int64_t p_position) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); if (writing) { seek(write_max + p_position); } else { @@ -229,7 +224,7 @@ void FileAccessCompressed::seek_end(int64_t p_position) { } uint64_t FileAccessCompressed::get_position() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use."); if (writing) { return write_pos; } else { @@ -238,7 +233,7 @@ uint64_t FileAccessCompressed::get_position() const { } uint64_t FileAccessCompressed::get_length() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use."); if (writing) { return write_max; } else { @@ -247,7 +242,7 @@ uint64_t FileAccessCompressed::get_length() const { } bool FileAccessCompressed::eof_reached() const { - ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(f.is_null(), false, "File must be opened before use."); if (writing) { return false; } else { @@ -256,7 +251,7 @@ bool FileAccessCompressed::eof_reached() const { } uint8_t FileAccessCompressed::get_8() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use."); ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (at_end) { @@ -273,7 +268,8 @@ uint8_t FileAccessCompressed::get_8() const { if (read_block < read_block_count) { //read another block of compressed data f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize); - Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode); + int total = Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode); + ERR_FAIL_COND_V_MSG(total == -1, 0, "Compressed file is corrupt."); read_block_size = read_block == read_block_count - 1 ? read_total % block_size : block_size; read_pos = 0; @@ -288,7 +284,7 @@ uint8_t FileAccessCompressed::get_8() const { uint64_t FileAccessCompressed::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); - ERR_FAIL_COND_V_MSG(!f, -1, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use."); ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode."); if (at_end) { @@ -305,17 +301,18 @@ uint64_t FileAccessCompressed::get_buffer(uint8_t *p_dst, uint64_t p_length) con if (read_block < read_block_count) { //read another block of compressed data f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize); - Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode); + int ret = Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode); + ERR_FAIL_COND_V_MSG(ret == -1, -1, "Compressed file is corrupt."); read_block_size = read_block == read_block_count - 1 ? read_total % block_size : block_size; read_pos = 0; } else { read_block--; at_end = true; - if (i < p_length - 1) { + if (i + 1 < p_length) { read_eof = true; } - return i; + return i + 1; } } } @@ -328,14 +325,14 @@ Error FileAccessCompressed::get_error() const { } void FileAccessCompressed::flush() { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); // compressed files keep data in memory till close() } void FileAccessCompressed::store_8(uint8_t p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); + ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); WRITE_FIT(1); @@ -343,16 +340,15 @@ void FileAccessCompressed::store_8(uint8_t p_dest) { } bool FileAccessCompressed::file_exists(const String &p_name) { - FileAccess *fa = FileAccess::open(p_name, FileAccess::READ); - if (!fa) { + Ref<FileAccess> fa = FileAccess::open(p_name, FileAccess::READ); + if (fa.is_null()) { return false; } - memdelete(fa); return true; } uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { - if (f) { + if (f.is_valid()) { return f->get_modified_time(p_file); } else { return 0; @@ -360,21 +356,19 @@ uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { } uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) { - if (f) { + if (f.is_valid()) { return f->_get_unix_permissions(p_file); } return 0; } Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t p_permissions) { - if (f) { + if (f.is_valid()) { return f->_set_unix_permissions(p_file, p_permissions); } return FAILED; } FileAccessCompressed::~FileAccessCompressed() { - if (f) { - close(); - } + _close(); } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 3389e020e3..b8382e61d9 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -61,15 +61,16 @@ class FileAccessCompressed : public FileAccess { String magic = "GCMP"; mutable Vector<uint8_t> buffer; - FileAccess *f = nullptr; + Ref<FileAccess> f; + + void _close(); public: void configure(const String &p_magic, Compression::Mode p_mode = Compression::MODE_ZSTD, uint32_t p_block_size = 4096); - Error open_after_magic(FileAccess *p_base); + Error open_after_magic(Ref<FileAccess> p_base); virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file - virtual void close(); ///< close a file virtual bool is_open() const; ///< true when file is open virtual void seek(uint64_t p_position); ///< seek to a given position diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 9e316291e8..d1b014a0be 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -36,7 +36,7 @@ #include <stdio.h> -Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) { +Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) { ERR_FAIL_COND_V_MSG(file != nullptr, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open."); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); @@ -99,7 +99,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8 return OK; } -Error FileAccessEncrypted::open_and_parse_password(FileAccess *p_base, const String &p_key, Mode p_mode) { +Error FileAccessEncrypted::open_and_parse_password(Ref<FileAccess> p_base, const String &p_key, Mode p_mode) { String cs = p_key.md5_text(); ERR_FAIL_COND_V(cs.length() != 32, ERR_INVALID_PARAMETER); Vector<uint8_t> key; @@ -115,30 +115,11 @@ Error FileAccessEncrypted::_open(const String &p_path, int p_mode_flags) { return OK; } -void FileAccessEncrypted::close() { - if (!file) { +void FileAccessEncrypted::_close() { + if (file.is_null()) { return; } - _release(); - - file->close(); - memdelete(file); - - file = nullptr; -} - -void FileAccessEncrypted::release() { - if (!file) { - return; - } - - _release(); - - file = nullptr; -} - -void FileAccessEncrypted::_release() { if (writing) { Vector<uint8_t> compressed; uint64_t len = data.size(); @@ -176,6 +157,8 @@ void FileAccessEncrypted::_release() { file->store_buffer(compressed.ptr(), compressed.size()); data.clear(); } + + file.unref(); } bool FileAccessEncrypted::is_open() const { @@ -183,7 +166,7 @@ bool FileAccessEncrypted::is_open() const { } String FileAccessEncrypted::get_path() const { - if (file) { + if (file.is_valid()) { return file->get_path(); } else { return ""; @@ -191,7 +174,7 @@ String FileAccessEncrypted::get_path() const { } String FileAccessEncrypted::get_path_absolute() const { - if (file) { + if (file.is_valid()) { return file->get_path_absolute(); } else { return ""; @@ -291,11 +274,10 @@ void FileAccessEncrypted::store_8(uint8_t p_dest) { } bool FileAccessEncrypted::file_exists(const String &p_name) { - FileAccess *fa = FileAccess::open(p_name, FileAccess::READ); - if (!fa) { + Ref<FileAccess> fa = FileAccess::open(p_name, FileAccess::READ); + if (fa.is_null()) { return false; } - memdelete(fa); return true; } @@ -313,7 +295,5 @@ Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t } FileAccessEncrypted::~FileAccessEncrypted() { - if (file) { - close(); - } + _close(); } diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index decffae696..0d1ee6a4d8 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -46,7 +46,7 @@ public: private: Vector<uint8_t> key; bool writing = false; - FileAccess *file = nullptr; + Ref<FileAccess> file; uint64_t base = 0; uint64_t length = 0; Vector<uint8_t> data; @@ -54,15 +54,13 @@ private: mutable bool eofed = false; bool use_magic = true; - void _release(); + void _close(); public: - Error open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true); - Error open_and_parse_password(FileAccess *p_base, const String &p_key, Mode p_mode); + 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); ///< open a file - virtual void close(); ///< close a file - virtual void release(); ///< finish and keep base file open virtual bool is_open() const; ///< true when file is open virtual String get_path() const; /// 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 627fd2bf9c..943dc72307 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -60,7 +60,7 @@ void FileAccessMemory::cleanup() { memdelete(files); } -FileAccess *FileAccessMemory::create() { +Ref<FileAccess> FileAccessMemory::create() { return memnew(FileAccessMemory); } @@ -94,10 +94,6 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { return OK; } -void FileAccessMemory::close() { - data = nullptr; -} - bool FileAccessMemory::is_open() const { return data != nullptr; } diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 14135bd68c..868b8ed481 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -38,7 +38,7 @@ class FileAccessMemory : public FileAccess { uint64_t length = 0; mutable uint64_t pos = 0; - static FileAccess *create(); + static Ref<FileAccess> create(); public: static void register_file(String p_name, Vector<uint8_t> p_data); @@ -46,7 +46,6 @@ public: 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); ///< open a file - virtual void close(); ///< close a file virtual bool is_open() const; ///< true when file is open virtual void seek(uint64_t p_position); ///< seek to a given position diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 9ee3876c2f..1365b4b593 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -165,7 +165,7 @@ void FileAccessNetworkClient::_thread_func() { } void FileAccessNetworkClient::_thread_func(void *s) { - FileAccessNetworkClient *self = (FileAccessNetworkClient *)s; + FileAccessNetworkClient *self = static_cast<FileAccessNetworkClient *>(s); self->_thread_func(); } @@ -254,9 +254,8 @@ void FileAccessNetwork::_respond(uint64_t p_len, Error p_status) { Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) { ERR_FAIL_COND_V(p_mode_flags != READ, ERR_UNAVAILABLE); - if (opened) { - close(); - } + _close(); + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; DEBUG_PRINT("open: " + p_path); @@ -287,7 +286,7 @@ Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) { return response; } -void FileAccessNetwork::close() { +void FileAccessNetwork::_close() { if (!opened) { return; } @@ -483,11 +482,10 @@ FileAccessNetwork::FileAccessNetwork() { } FileAccessNetwork::~FileAccessNetwork() { - close(); + _close(); FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); - id = nc->last_id++; nc->accesses.erase(id); nc->unlock_mutex(); } diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index 1d9d761fbb..6afbf6adf5 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -113,6 +113,7 @@ class FileAccessNetwork : public FileAccess { void _queue_page(int32_t p_page) const; void _respond(uint64_t p_len, Error p_status); void _set_block(uint64_t p_offset, const Vector<uint8_t> &p_block); + void _close(); public: enum Command { @@ -131,7 +132,6 @@ public: }; virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file - virtual void close(); ///< close a file virtual bool is_open() const; ///< true when file is open virtual void seek(uint64_t p_position); ///< seek to a given position diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index b2832b2a75..c6e14ffee7 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -70,7 +70,7 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64 String p = p_path.replace_first("res://", ""); PackedDir *cd = root; - if (p.find("/") != -1) { //in a subdir + if (p.contains("/")) { //in a subdir Vector<String> ds = p.get_base_dir().split("/"); @@ -126,8 +126,8 @@ PackedData::~PackedData() { ////////////////////////////////////////////////////////////////// bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { return false; } @@ -137,19 +137,13 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, if (magic != PACK_HEADER_MAGIC) { // loading with offset feature not supported for self contained exe files - if (p_offset != 0) { - f->close(); - memdelete(f); - ERR_FAIL_V_MSG(false, "Loading self-contained executable with offset not supported."); - } + ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Loading self-contained executable with offset not supported."); //maybe at the end.... self contained exe f->seek_end(); f->seek(f->get_position() - 4); magic = f->get_32(); if (magic != PACK_HEADER_MAGIC) { - f->close(); - memdelete(f); return false; } f->seek(f->get_position() - 12); @@ -159,8 +153,6 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, magic = f->get_32(); if (magic != PACK_HEADER_MAGIC) { - f->close(); - memdelete(f); return false; } } @@ -170,16 +162,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint32_t ver_minor = f->get_32(); f->get_32(); // patch number, not used for validation. - if (version != PACK_FORMAT_VERSION) { - f->close(); - memdelete(f); - ERR_FAIL_V_MSG(false, "Pack version unsupported: " + itos(version) + "."); - } - if (ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR)) { - f->close(); - memdelete(f); - ERR_FAIL_V_MSG(false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "."); - } + ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION, false, "Pack version unsupported: " + itos(version) + "."); + ERR_FAIL_COND_V_MSG(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "."); uint32_t pack_flags = f->get_32(); uint64_t file_base = f->get_64(); @@ -194,12 +178,9 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, int file_count = f->get_32(); if (enc_directory) { - FileAccessEncrypted *fae = memnew(FileAccessEncrypted); - if (!fae) { - f->close(); - memdelete(f); - ERR_FAIL_V_MSG(false, "Can't open encrypted pack directory."); - } + Ref<FileAccessEncrypted> fae; + fae.instantiate(); + ERR_FAIL_COND_V_MSG(fae.is_null(), false, "Can't open encrypted pack directory."); Vector<uint8_t> key; key.resize(32); @@ -208,12 +189,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, } Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); - if (err) { - f->close(); - memdelete(f); - memdelete(fae); - ERR_FAIL_V_MSG(false, "Can't open encrypted pack directory."); - } + ERR_FAIL_COND_V_MSG(err, false, "Can't open encrypted pack directory."); f = fae; } @@ -236,12 +212,10 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, PackedData::get_singleton()->add_path(p_path, path, ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED)); } - f->close(); - memdelete(f); return true; } -FileAccess *PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) { +Ref<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) { return memnew(FileAccessPack(p_path, *p_file)); } @@ -252,15 +226,17 @@ Error FileAccessPack::_open(const String &p_path, int p_mode_flags) { return ERR_UNAVAILABLE; } -void FileAccessPack::close() { - f->close(); -} - bool FileAccessPack::is_open() const { - return f->is_open(); + if (f.is_valid()) { + return f->is_open(); + } else { + return false; + } } void FileAccessPack::seek(uint64_t p_position) { + ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); + if (p_position > pf.size) { eof = true; } else { @@ -288,6 +264,7 @@ bool FileAccessPack::eof_reached() const { } uint8_t FileAccessPack::get_8() const { + ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use."); if (pos >= pf.size) { eof = true; return 0; @@ -298,6 +275,7 @@ uint8_t FileAccessPack::get_8() const { } uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const { + ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use."); ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); if (eof) { @@ -321,6 +299,8 @@ uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const { } void FileAccessPack::set_big_endian(bool p_big_endian) { + ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); + FileAccess::set_big_endian(p_big_endian); f->set_big_endian(p_big_endian); } @@ -351,16 +331,15 @@ bool FileAccessPack::file_exists(const String &p_name) { FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) : pf(p_file), f(FileAccess::open(pf.pack, FileAccess::READ)) { - ERR_FAIL_COND_MSG(!f, "Can't open pack-referenced file '" + String(pf.pack) + "'."); + ERR_FAIL_COND_MSG(f.is_null(), "Can't open pack-referenced file '" + String(pf.pack) + "'."); f->seek(pf.offset); off = pf.offset; if (pf.encrypted) { - FileAccessEncrypted *fae = memnew(FileAccessEncrypted); - if (!fae) { - ERR_FAIL_MSG("Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); - } + Ref<FileAccessEncrypted> fae; + fae.instantiate(); + ERR_FAIL_COND_MSG(fae.is_null(), "Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); Vector<uint8_t> key; key.resize(32); @@ -369,10 +348,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil } Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); - if (err) { - memdelete(fae); - ERR_FAIL_MSG("Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); - } + ERR_FAIL_COND_MSG(err, "Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); f = fae; off = 0; } @@ -380,13 +356,6 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil eof = false; } -FileAccessPack::~FileAccessPack() { - if (f) { - f->close(); - memdelete(f); - } -} - ////////////////////////////////////////////////////////////////////////////////// // DIR ACCESS ////////////////////////////////////////////////////////////////////////////////// @@ -459,7 +428,7 @@ PackedData::PackedDir *DirAccessPack::_find_dir(String p_dir) { nd = nd.simplify_path(); - if (nd == "") { + if (nd.is_empty()) { nd = "."; } @@ -507,7 +476,7 @@ Error DirAccessPack::change_dir(String p_dir) { } } -String DirAccessPack::get_current_dir(bool p_include_drive) { +String DirAccessPack::get_current_dir(bool p_include_drive) const { PackedData::PackedDir *pd = current; String p = current->name; diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 2f0ee62723..44df2029bd 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -64,7 +64,7 @@ public: uint64_t offset; //if offset is ZERO, the file was ERASED uint64_t size; uint8_t md5[16]; - PackSource *src; + PackSource *src = nullptr; bool encrypted; }; @@ -103,7 +103,7 @@ private: Vector<PackSource *> sources; - PackedDir *root; + PackedDir *root = nullptr; static PackedData *singleton; bool disabled = false; @@ -120,10 +120,10 @@ public: static PackedData *get_singleton() { return singleton; } Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset); - _FORCE_INLINE_ FileAccess *try_open_path(const String &p_path); + _FORCE_INLINE_ Ref<FileAccess> try_open_path(const String &p_path); _FORCE_INLINE_ bool has_path(const String &p_path); - _FORCE_INLINE_ DirAccess *try_open_directory(const String &p_path); + _FORCE_INLINE_ Ref<DirAccess> try_open_directory(const String &p_path); _FORCE_INLINE_ bool has_directory(const String &p_path); PackedData(); @@ -133,14 +133,14 @@ public: class PackSource { public: virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) = 0; - virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file) = 0; + virtual Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file) = 0; virtual ~PackSource() {} }; class PackedSourcePCK : public PackSource { public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset); - virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; + virtual Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file) override; }; class FileAccessPack : public FileAccess { @@ -150,14 +150,13 @@ class FileAccessPack : public FileAccess { mutable bool eof; uint64_t off; - FileAccess *f; + Ref<FileAccess> f; virtual Error _open(const String &p_path, int p_mode_flags); virtual uint64_t _get_modified_time(const String &p_file) { return 0; } virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; } virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; } public: - virtual void close(); virtual bool is_open() const; virtual void seek(uint64_t p_position); @@ -183,10 +182,9 @@ public: virtual bool file_exists(const String &p_name); FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file); - ~FileAccessPack(); }; -FileAccess *PackedData::try_open_path(const String &p_path) { +Ref<FileAccess> PackedData::try_open_path(const String &p_path) { PathMD5 pmd5(p_path.md5_buffer()); Map<PathMD5, PackedFile>::Element *E = files.find(pmd5); if (!E) { @@ -204,9 +202,8 @@ bool PackedData::has_path(const String &p_path) { } bool PackedData::has_directory(const String &p_path) { - DirAccess *da = try_open_directory(p_path); - if (da) { - memdelete(da); + Ref<DirAccess> da = try_open_directory(p_path); + if (da.is_valid()) { return true; } else { return false; @@ -233,7 +230,7 @@ public: virtual String get_drive(int p_drive); virtual Error change_dir(String p_dir); - virtual String get_current_dir(bool p_include_drive = true); + virtual String get_current_dir(bool p_include_drive = true) const; virtual bool file_exists(String p_file); virtual bool dir_exists(String p_dir); @@ -252,14 +249,12 @@ public: virtual String get_filesystem_type() const; DirAccessPack(); - ~DirAccessPack() {} }; -DirAccess *PackedData::try_open_directory(const String &p_path) { - DirAccess *da = memnew(DirAccessPack()); +Ref<DirAccess> PackedData::try_open_directory(const String &p_path) { + Ref<DirAccess> da = memnew(DirAccessPack()); if (da->change_dir(p_path) != OK) { - memdelete(da); - da = nullptr; + da = Ref<DirAccess>(); } return da; } diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index 53bf7456e6..17f2335a8e 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -38,20 +38,26 @@ ZipArchive *ZipArchive::instance = nullptr; extern "C" { -static void *godot_open(void *data, const char *p_fname, int mode) { +struct ZipData { + Ref<FileAccess> f; +}; + +static void *godot_open(voidpf opaque, const char *p_fname, int mode) { if (mode & ZLIB_FILEFUNC_MODE_WRITE) { return nullptr; } - FileAccess *f = FileAccess::open(p_fname, FileAccess::READ); - ERR_FAIL_COND_V(!f, nullptr); + Ref<FileAccess> f = FileAccess::open(p_fname, FileAccess::READ); + ERR_FAIL_COND_V(f.is_null(), nullptr); - return f; + ZipData *zd = memnew(ZipData); + zd->f = f; + return zd; } -static uLong godot_read(void *data, void *fdata, void *buf, uLong size) { - FileAccess *f = (FileAccess *)fdata; - f->get_buffer((uint8_t *)buf, size); +static uLong godot_read(voidpf opaque, voidpf stream, void *buf, uLong size) { + ZipData *zd = (ZipData *)stream; + zd->f->get_buffer((uint8_t *)buf, size); return size; } @@ -60,42 +66,38 @@ static uLong godot_write(voidpf opaque, voidpf stream, const void *buf, uLong si } static long godot_tell(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)stream; - return f->get_position(); + ZipData *zd = (ZipData *)stream; + return zd->f->get_position(); } static long godot_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - FileAccess *f = (FileAccess *)stream; + ZipData *zd = (ZipData *)stream; uint64_t pos = offset; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR: - pos = f->get_position() + offset; + pos = zd->f->get_position() + offset; break; case ZLIB_FILEFUNC_SEEK_END: - pos = f->get_length() + offset; + pos = zd->f->get_length() + offset; break; default: break; } - f->seek(pos); + zd->f->seek(pos); return 0; } static int godot_close(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)stream; - if (f) { - f->close(); - memdelete(f); - f = nullptr; - } + ZipData *zd = (ZipData *)stream; + memdelete(zd); return 0; } static int godot_testerror(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)stream; - return f->get_error() != OK ? 1 : 0; + ZipData *zd = (ZipData *)stream; + return zd->f->get_error() != OK ? 1 : 0; } static voidpf godot_alloc(voidpf opaque, uInt items, uInt size) { @@ -189,7 +191,7 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint6 f.package = pkg_num; unzGetFilePos(zfile, &f.file_pos); - String fname = String("res://") + filename_inzip; + String fname = String("res://") + String::utf8(filename_inzip); files[fname] = f; uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -208,7 +210,7 @@ bool ZipArchive::file_exists(String p_name) const { return files.has(p_name); } -FileAccess *ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file) { +Ref<FileAccess> ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file) { return memnew(FileAccessZip(p_path, *p_file)); } @@ -233,7 +235,7 @@ ZipArchive::~ZipArchive() { } Error FileAccessZip::_open(const String &p_path, int p_mode_flags) { - close(); + _close(); ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, FAILED); ZipArchive *arch = ZipArchive::get_singleton(); @@ -247,7 +249,7 @@ Error FileAccessZip::_open(const String &p_path, int p_mode_flags) { return OK; } -void FileAccessZip::close() { +void FileAccessZip::_close() { if (!zfile) { return; } @@ -339,7 +341,7 @@ FileAccessZip::FileAccessZip(const String &p_path, const PackedData::PackedFile } FileAccessZip::~FileAccessZip() { - close(); + _close(); } #endif // MINIZIP_ENABLED diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index cca14ded62..2504aeedc4 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -67,8 +67,8 @@ public: bool file_exists(String p_name) const; - virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset); - FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); + virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; + Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file) override; static ZipArchive *get_singleton(); @@ -82,9 +82,10 @@ class FileAccessZip : public FileAccess { mutable bool at_eof; + void _close(); + public: virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file - virtual void close(); ///< close a file virtual bool is_open() const; ///< true when file is open virtual void seek(uint64_t p_position); ///< seek to a given position diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 800ac779e5..52b1120b2a 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -49,6 +49,14 @@ HTTPClient *HTTPClient::create() { return nullptr; } +void HTTPClient::set_http_proxy(const String &p_host, int p_port) { + WARN_PRINT("HTTP proxy feature is not available"); +} + +void HTTPClient::set_https_proxy(const String &p_host, int p_port) { + WARN_PRINT("HTTPS proxy feature is not available"); +} + Error HTTPClient::_request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) { int size = p_body.size(); return request(p_method, p_url, p_headers, size > 0 ? p_body.ptr() : nullptr, size); @@ -88,6 +96,17 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { return query.substr(1); } +Error HTTPClient::verify_headers(const Vector<String> &p_headers) { + for (int i = 0; i < p_headers.size(); i++) { + String sanitized = p_headers[i].strip_edges(); + ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, "Invalid HTTP header at index " + itos(i) + ": empty."); + ERR_FAIL_COND_V_MSG(sanitized.find(":") < 1, ERR_INVALID_PARAMETER, + "Invalid HTTP header at index " + itos(i) + ": String must contain header-value pair, delimited by ':', but was: " + p_headers[i]); + } + + return OK; +} + Dictionary HTTPClient::_get_response_headers_as_dictionary() { List<String> rh; get_response_headers(&rh); @@ -142,6 +161,9 @@ void HTTPClient::_bind_methods() { ClassDB::bind_method(D_METHOD("get_status"), &HTTPClient::get_status); ClassDB::bind_method(D_METHOD("poll"), &HTTPClient::poll); + ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPClient::set_http_proxy); + ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPClient::set_https_proxy); + ClassDB::bind_method(D_METHOD("query_string_from_dict", "fields"), &HTTPClient::query_string_from_dict); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_mode_enabled"), "set_blocking_mode", "is_blocking_mode_enabled"); diff --git a/core/io/http_client.h b/core/io/http_client.h index 718c3a905e..de6045f647 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -165,6 +165,7 @@ public: static HTTPClient *create(); String query_string_from_dict(const Dictionary &p_dict); + 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; @@ -180,7 +181,7 @@ public: virtual bool is_response_chunked() const = 0; virtual int get_response_code() const = 0; virtual Error get_response_headers(List<String> *r_response) = 0; - virtual int get_response_body_length() const = 0; + virtual int64_t get_response_body_length() const = 0; virtual PackedByteArray read_response_body_chunk() = 0; // Can't get body as partial text because of most encodings UTF8, gzip, etc. @@ -192,6 +193,10 @@ public: virtual Error poll() = 0; + // Use empty string or -1 to unset + virtual void set_http_proxy(const String &p_host, int p_port); + virtual void set_https_proxy(const String &p_host, int p_port); + HTTPClient() {} virtual ~HTTPClient() {} }; diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index b3d35b3603..f920799677 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -70,9 +70,21 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss connection = tcp_connection; - if (conn_host.is_valid_ip_address()) { - // Host contains valid IP - Error err = tcp_connection->connect_to_host(IPAddress(conn_host), p_port); + if (ssl && 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) { + server_host = http_proxy_host; + server_port = http_proxy_port; + } else { + server_host = conn_host; + server_port = conn_port; + } + + if (server_host.is_valid_ip_address()) { + // Host contains valid IP. + Error err = tcp_connection->connect_to_host(IPAddress(server_host), server_port); if (err) { status = STATUS_CANT_CONNECT; return err; @@ -80,8 +92,8 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss status = STATUS_CONNECTING; } else { - // Host contains hostname and needs to be resolved to IP - resolving = IP::get_singleton()->resolve_hostname_queue_item(conn_host); + // Host contains hostname and needs to be resolved to IP. + resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host); status = STATUS_RESOLVING; } @@ -112,7 +124,7 @@ Ref<StreamPeer> HTTPClientTCP::get_connection() const { static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_url) { switch (p_method) { case HTTPClientTCP::METHOD_CONNECT: { - // Authority in host:port format, as in RFC7231 + // Authority in host:port format, as in RFC7231. int pos = p_url.find_char(':'); return 0 < pos && pos < p_url.length() - 1; } @@ -123,7 +135,7 @@ static bool _check_request_url(HTTPClientTCP::Method p_method, const String &p_u [[fallthrough]]; } default: - // Absolute path or absolute URL + // Absolute path or absolute URL. return p_url.begins_with("/") || p_url.begins_with("http://") || p_url.begins_with("https://"); } } @@ -134,7 +146,17 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA); - String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n"; + Error err = verify_headers(p_headers); + if (err) { + return err; + } + + String uri = p_url; + if (!ssl && http_proxy_port != -1) { + uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url); + } + + String request = String(_methods[p_method]) + " " + uri + " HTTP/1.1\r\n"; bool add_host = true; bool add_clen = p_body_size > 0; bool add_uagent = true; @@ -156,7 +178,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)) { - // Don't append the standard ports + // Don't append the standard ports. request += "Host: " + conn_host + "\r\n"; } else { request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n"; @@ -175,21 +197,12 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector< request += "\r\n"; CharString cs = request.utf8(); - Vector<uint8_t> data; - data.resize(cs.length() + p_body_size); - memcpy(data.ptrw(), cs.get_data(), cs.length()); + request_buffer->clear(); + request_buffer->put_data((const uint8_t *)cs.get_data(), cs.length()); if (p_body_size > 0) { - memcpy(data.ptrw() + cs.length(), p_body, p_body_size); - } - - // TODO Implement non-blocking requests. - Error err = connection->put_data(data.ptr(), data.size()); - - if (err) { - close(); - status = STATUS_CONNECTION_ERROR; - return err; + request_buffer->put_data(p_body, p_body_size); } + request_buffer->seek(0); status = STATUS_REQUESTING; head_request = p_method == METHOD_HEAD; @@ -229,6 +242,7 @@ void HTTPClientTCP::close() { } connection.unref(); + proxy_client.unref(); status = STATUS_DISCONNECTED; head_request = false; if (resolving != IP::RESOLVER_INVALID_ID) { @@ -239,6 +253,7 @@ void HTTPClientTCP::close() { ip_candidates.clear(); response_headers.clear(); response_str.clear(); + request_buffer->clear(); body_size = -1; body_left = 0; chunk_left = 0; @@ -256,7 +271,7 @@ Error HTTPClientTCP::poll() { IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving); switch (rstatus) { case IP::RESOLVER_STATUS_WAITING: - return OK; // Still resolving + return OK; // Still resolving. case IP::RESOLVER_STATUS_DONE: { ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolving); @@ -265,7 +280,7 @@ Error HTTPClientTCP::poll() { Error err = ERR_BUG; // Should be at least one entry. while (ip_candidates.size() > 0) { - err = tcp_connection->connect_to_host(ip_candidates.front(), conn_port); + err = tcp_connection->connect_to_host(ip_candidates.pop_front(), server_port); if (err == OK) { break; } @@ -294,10 +309,51 @@ Error HTTPClientTCP::poll() { return OK; } break; case StreamPeerTCP::STATUS_CONNECTED: { - if (ssl) { + if (ssl && proxy_client.is_valid()) { + Error err = proxy_client->poll(); + if (err == ERR_UNCONFIGURED) { + proxy_client->set_connection(tcp_connection); + const Vector<String> headers; + err = proxy_client->request(METHOD_CONNECT, vformat("%s:%d", conn_host, conn_port), headers, nullptr, 0); + if (err != OK) { + status = STATUS_CANT_CONNECT; + return err; + } + } else if (err != OK) { + status = STATUS_CANT_CONNECT; + return err; + } + switch (proxy_client->get_status()) { + case STATUS_REQUESTING: { + return OK; + } break; + case STATUS_BODY: { + proxy_client->read_response_body_chunk(); + return OK; + } break; + case STATUS_CONNECTED: { + if (proxy_client->get_response_code() != RESPONSE_OK) { + status = STATUS_CANT_CONNECT; + return ERR_CANT_CONNECT; + } + proxy_client.unref(); + return OK; + } + case STATUS_DISCONNECTED: + case STATUS_RESOLVING: + case STATUS_CONNECTING: { + status = STATUS_CANT_CONNECT; + ERR_FAIL_V(ERR_BUG); + } break; + default: { + status = STATUS_CANT_CONNECT; + return ERR_CANT_CONNECT; + } break; + } + } else if (ssl) { Ref<StreamPeerSSL> ssl; if (!handshaking) { - // Connect the StreamPeerSSL and start handshaking + // Connect the StreamPeerSSL and start handshaking. ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); ssl->set_blocking_handshake_enabled(false); Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host); @@ -309,7 +365,7 @@ Error HTTPClientTCP::poll() { connection = ssl; handshaking = true; } else { - // We are already handshaking, which means we can use your already active SSL connection + // We are already handshaking, which means we can use your already active SSL connection. ssl = static_cast<Ref<StreamPeerSSL>>(connection); if (ssl.is_null()) { close(); @@ -317,22 +373,22 @@ Error HTTPClientTCP::poll() { return ERR_CANT_CONNECT; } - ssl->poll(); // Try to finish the handshake + ssl->poll(); // Try to finish the handshake. } if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) { - // Handshake has been successful + // Handshake has been successful. handshaking = false; ip_candidates.clear(); status = STATUS_CONNECTED; return OK; } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) { - // Handshake has failed + // Handshake has failed. close(); status = STATUS_SSL_HANDSHAKE_ERROR; return ERR_CANT_CONNECT; } - // ... we will need to poll more for handshake to finish + // ... we will need to poll more for handshake to finish. } else { ip_candidates.clear(); status = STATUS_CONNECTED; @@ -344,7 +400,7 @@ Error HTTPClientTCP::poll() { Error err = ERR_CANT_CONNECT; while (ip_candidates.size() > 0) { tcp_connection->disconnect_from_host(); - err = tcp_connection->connect_to_host(ip_candidates.pop_front(), conn_port); + err = tcp_connection->connect_to_host(ip_candidates.pop_front(), server_port); if (err == OK) { return OK; } @@ -357,7 +413,7 @@ Error HTTPClientTCP::poll() { } break; case STATUS_BODY: case STATUS_CONNECTED: { - // Check if we are still connected + // Check if we are still connected. if (ssl) { Ref<StreamPeerSSL> tmp = connection; tmp->poll(); @@ -369,10 +425,34 @@ Error HTTPClientTCP::poll() { status = STATUS_CONNECTION_ERROR; return ERR_CONNECTION_ERROR; } - // Connection established, requests can now be made + // Connection established, requests can now be made. return OK; } break; case STATUS_REQUESTING: { + if (request_buffer->get_available_bytes()) { + int avail = request_buffer->get_available_bytes(); + int pos = request_buffer->get_position(); + const Vector<uint8_t> data = request_buffer->get_data_array(); + int wrote = 0; + Error err; + if (blocking) { + err = connection->put_data(data.ptr() + pos, avail); + wrote += avail; + } else { + err = connection->put_partial_data(data.ptr() + pos, avail, wrote); + } + if (err != OK) { + close(); + status = STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + pos += wrote; + request_buffer->seek(pos); + if (avail - wrote > 0) { + return OK; + } + request_buffer->clear(); + } while (true) { uint8_t byte; int rec = 0; @@ -475,7 +555,7 @@ Error HTTPClientTCP::poll() { return OK; } -int HTTPClientTCP::get_response_body_length() const { +int64_t HTTPClientTCP::get_response_body_length() const { return body_size; } @@ -488,7 +568,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { if (chunked) { while (true) { if (chunk_trailer_part) { - // We need to consume the trailer part too or keep-alive will break + // We need to consume the trailer part too or keep-alive will break. uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); @@ -501,18 +581,18 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { int cs = chunk.size(); if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) { if (cs == 2) { - // Finally over + // Finally over. chunk_trailer_part = false; status = STATUS_CONNECTED; chunk.clear(); break; } else { - // We do not process nor return the trailer data + // We do not process nor return the trailer data. chunk.clear(); } } } else if (chunk_left == 0) { - // Reading length + // Reading length. uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); @@ -534,7 +614,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { for (int i = 0; i < chunk.size() - 2; i++) { char c = chunk[i]; int v = 0; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a' + 10; @@ -599,7 +679,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { uint8_t *w = ret.ptrw(); err = _get_http_data(w + _offset, to_read, rec); } - if (rec <= 0) { // Ended up reading less + if (rec <= 0) { // Ended up reading less. ret.resize(_offset); break; } else { @@ -620,7 +700,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { close(); if (err == ERR_FILE_EOF) { - status = STATUS_DISCONNECTED; // Server disconnected + status = STATUS_DISCONNECTED; // Server disconnected. } else { status = STATUS_CONNECTION_ERROR; } @@ -678,8 +758,29 @@ int HTTPClientTCP::get_read_chunk_size() const { return read_chunk_size; } +void HTTPClientTCP::set_http_proxy(const String &p_host, int p_port) { + if (p_host.is_empty() || p_port == -1) { + http_proxy_host = ""; + http_proxy_port = -1; + } else { + http_proxy_host = p_host; + http_proxy_port = p_port; + } +} + +void HTTPClientTCP::set_https_proxy(const String &p_host, int p_port) { + if (p_host.is_empty() || p_port == -1) { + https_proxy_host = ""; + https_proxy_port = -1; + } else { + https_proxy_host = p_host; + https_proxy_port = p_port; + } +} + HTTPClientTCP::HTTPClientTCP() { tcp_connection.instantiate(); + request_buffer.instantiate(); } HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func; diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h index 170afb551c..c10e0b1eca 100644 --- a/core/io/http_client_tcp.h +++ b/core/io/http_client_tcp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -38,8 +38,14 @@ private: Status status = STATUS_DISCONNECTED; IP::ResolverID resolving = IP::RESOLVER_INVALID_ID; Array ip_candidates; - int conn_port = -1; + int conn_port = -1; // Server to make requests to. String conn_host; + int server_port = -1; // Server to connect to (might be a proxy server). + String server_host; + int http_proxy_port = -1; // Proxy server for http requests. + 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 blocking = false; @@ -52,12 +58,14 @@ private: Vector<uint8_t> chunk; int chunk_left = 0; bool chunk_trailer_part = false; - int body_size = -1; - int body_left = 0; + int64_t body_size = -1; + int64_t body_left = 0; bool read_until_eof = false; + Ref<StreamPeerBuffer> request_buffer; Ref<StreamPeerTCP> tcp_connection; Ref<StreamPeer> connection; + Ref<HTTPClientTCP> proxy_client; // Negotiate with proxy server. int response_num = 0; Vector<String> response_headers; @@ -80,13 +88,15 @@ public: bool is_response_chunked() const override; int get_response_code() const override; Error get_response_headers(List<String> *r_response) override; - int get_response_body_length() const override; + int64_t get_response_body_length() const override; PackedByteArray read_response_body_chunk() override; void set_blocking_mode(bool p_enable) override; bool is_blocking_mode_enabled() const override; void set_read_chunk_size(int p_size) override; int get_read_chunk_size() const override; Error poll() override; + void set_http_proxy(const String &p_host, int p_port) override; + void set_https_proxy(const String &p_host, int p_port) override; HTTPClientTCP(); }; diff --git a/core/io/image.cpp b/core/io/image.cpp index e69b584932..661a9f7177 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -30,14 +30,17 @@ #include "image.h" +#include "core/error/error_list.h" #include "core/error/error_macros.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" #include "core/math/math_funcs.h" #include "core/string/print_string.h" #include "core/templates/hash_map.h" +#include "core/variant/dictionary.h" #include <stdio.h> +#include <cmath> const char *Image::format_names[Image::FORMAT_MAX] = { "Lum8", //luminance @@ -65,10 +68,6 @@ const char *Image::format_names[Image::FORMAT_MAX] = { "BPTC_RGBA", "BPTC_RGBF", "BPTC_RGBFU", - "PVRTC1_2", //pvrtc - "PVRTC1_2A", - "PVRTC1_4", - "PVRTC1_4A", "ETC", //etc1 "ETC2_R11", //etc2 "ETC2_R11S", //signed", NOT srgb. @@ -148,14 +147,6 @@ int Image::get_format_pixel_size(Format p_format) { return 1; //float / case FORMAT_BPTC_RGBFU: return 1; //unsigned float - case FORMAT_PVRTC1_2: - return 1; //pvrtc - case FORMAT_PVRTC1_2A: - return 1; - case FORMAT_PVRTC1_4: - return 1; - case FORMAT_PVRTC1_4A: - return 1; case FORMAT_ETC: return 1; //etc1 case FORMAT_ETC2_R11: @@ -193,16 +184,6 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { r_w = 4; r_h = 4; } break; - case FORMAT_PVRTC1_2: - case FORMAT_PVRTC1_2A: { - r_w = 16; - r_h = 8; - } break; - case FORMAT_PVRTC1_4A: - case FORMAT_PVRTC1_4: { - r_w = 8; - r_h = 8; - } break; case FORMAT_ETC: { r_w = 4; r_h = 4; @@ -235,10 +216,8 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) { } int Image::get_format_pixel_rshift(Format p_format) { - if (p_format == FORMAT_DXT1 || p_format == FORMAT_RGTC_R || p_format == FORMAT_PVRTC1_4 || p_format == FORMAT_PVRTC1_4A || p_format == FORMAT_ETC || p_format == FORMAT_ETC2_R11 || p_format == FORMAT_ETC2_R11S || p_format == FORMAT_ETC2_RGB8 || p_format == FORMAT_ETC2_RGB8A1) { + if (p_format == FORMAT_DXT1 || p_format == FORMAT_RGTC_R || p_format == FORMAT_ETC || p_format == FORMAT_ETC2_R11 || p_format == FORMAT_ETC2_R11S || p_format == FORMAT_ETC2_RGB8 || p_format == FORMAT_ETC2_RGB8A1) { return 1; - } else if (p_format == FORMAT_PVRTC1_2 || p_format == FORMAT_PVRTC1_2A) { - return 2; } else { return 0; } @@ -254,14 +233,6 @@ int Image::get_format_block_size(Format p_format) { return 4; } - case FORMAT_PVRTC1_2: - case FORMAT_PVRTC1_2A: { - return 4; - } - case FORMAT_PVRTC1_4A: - case FORMAT_PVRTC1_4: { - return 4; - } case FORMAT_ETC: { return 4; } @@ -407,25 +378,25 @@ Image::Image3DValidateError Image::validate_3d_image(Image::Format p_format, int String Image::get_3d_image_validation_error_text(Image3DValidateError p_error) { switch (p_error) { case VALIDATE_3D_OK: { - return TTR("Ok"); + return "Ok"; } break; case VALIDATE_3D_ERR_IMAGE_EMPTY: { - return TTR("Empty Image found"); + return "Empty Image found"; } break; case VALIDATE_3D_ERR_MISSING_IMAGES: { - return TTR("Missing Images"); + return "Missing Images"; } break; case VALIDATE_3D_ERR_EXTRA_IMAGES: { - return TTR("Too many Images"); + return "Too many Images"; } break; case VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH: { - return TTR("Image size mismatch"); + return "Image size mismatch"; } break; case VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH: { - return TTR("Image format mismatch"); + return "Image format mismatch"; } break; case VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS: { - return TTR("Image has included mipmaps"); + return "Image has included mipmaps"; } break; } return String(); @@ -1466,12 +1437,11 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & } // Set mipmap size. - // It might be necessary to put this after the minimum mipmap size check because of the possible occurrence of "1 >> 1". if (r_mm_width) { - *r_mm_width = bw >> 1; + *r_mm_width = w; } if (r_mm_height) { - *r_mm_height = bh >> 1; + *r_mm_height = h; } // Reach target mipmap. @@ -1495,8 +1465,8 @@ template <class Component, int CC, bool renormalize, void (*renormalize_func)(Component *)> static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint32_t p_width, uint32_t p_height) { //fast power of 2 mipmap generation - uint32_t dst_w = MAX(p_width >> 1, 1); - uint32_t dst_h = MAX(p_height >> 1, 1); + uint32_t dst_w = MAX(p_width >> 1, 1u); + uint32_t dst_h = MAX(p_height >> 1, 1u); int right_step = (p_width == 1) ? 0 : CC; int down_step = (p_height == 1) ? 0 : (p_width * CC); @@ -2089,7 +2059,7 @@ void Image::create(const char **p_xpm) { for (int i = 0; i < 6; i++) { char v = line_ptr[i]; - if (v >= '0' && v <= '9') { + if (is_digit(v)) { v -= '0'; } else if (v >= 'A' && v <= 'F') { v = (v - 'A') + 10; @@ -2222,8 +2192,6 @@ bool Image::is_invisible() const { } break; - case FORMAT_PVRTC1_2A: - case FORMAT_PVRTC1_4A: case FORMAT_DXT3: case FORMAT_DXT5: { detected = true; @@ -2264,8 +2232,6 @@ Image::AlphaMode Image::detect_alpha() const { } } break; - case FORMAT_PVRTC1_2A: - case FORMAT_PVRTC1_4A: case FORMAT_DXT3: case FORMAT_DXT5: { detected = true; @@ -2361,8 +2327,6 @@ Error Image::decompress() { _image_decompress_bc(this); } else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc) { _image_decompress_bptc(this); - } else if (format >= FORMAT_PVRTC1_2 && format <= FORMAT_PVRTC1_4A && _image_decompress_pvrtc) { - _image_decompress_pvrtc(this); } else if (format == FORMAT_ETC && _image_decompress_etc1) { _image_decompress_etc1(this); } else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RA_AS_RG && _image_decompress_etc2) { @@ -2385,10 +2349,6 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE); _image_compress_bc_func(this, p_lossy_quality, p_channels); } break; - case COMPRESS_PVRTC1_4: { - ERR_FAIL_COND_V(!_image_compress_pvrtc1_4bpp_func, ERR_UNAVAILABLE); - _image_compress_pvrtc1_4bpp_func(this); - } break; case COMPRESS_ETC: { ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE); _image_compress_etc1_func(this, p_lossy_quality); @@ -2749,10 +2709,8 @@ ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = nullptr; -void (*Image::_image_compress_pvrtc1_4bpp_func)(Image *) = nullptr; void (*Image::_image_compress_etc1_func)(Image *, float) = nullptr; void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = nullptr; -void (*Image::_image_decompress_pvrtc)(Image *) = nullptr; void (*Image::_image_decompress_bc)(Image *) = nullptr; void (*Image::_image_decompress_bptc)(Image *) = nullptr; void (*Image::_image_decompress_etc1)(Image *) = nullptr; @@ -2765,6 +2723,7 @@ Vector<uint8_t> (*Image::png_packer)(const Ref<Image> &) = nullptr; Ref<Image> (*Image::png_unpacker)(const Vector<uint8_t> &) = nullptr; Vector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::UsedChannels) = nullptr; Ref<Image> (*Image::basis_universal_unpacker)(const Vector<uint8_t> &) = nullptr; +Ref<Image> (*Image::basis_universal_unpacker_ptr)(const uint8_t *, int) = nullptr; void Image::_set_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("width")); @@ -3047,7 +3006,7 @@ void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation) } } -Image::UsedChannels Image::detect_used_channels(CompressSource p_source) { +Image::UsedChannels Image::detect_used_channels(CompressSource p_source) const { ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA); ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA); bool r = false, g = false, b = false, a = false, c = false; @@ -3151,8 +3110,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_mipmaps", "renormalize"), &Image::generate_mipmaps, DEFVAL(false)); ClassDB::bind_method(D_METHOD("clear_mipmaps"), &Image::clear_mipmaps); - ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::_create_empty); - ClassDB::bind_method(D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::_create_from_data); + ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::create_empty); + ClassDB::bind_method(D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::create_from_data); ClassDB::bind_method(D_METHOD("is_empty"), &Image::is_empty); @@ -3177,6 +3136,8 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb); ClassDB::bind_method(D_METHOD("bump_map_to_normal_map", "bump_scale"), &Image::bump_map_to_normal_map, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("compute_image_metrics", "compared_image", "use_luma"), &Image::compute_image_metrics); + ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect); ClassDB::bind_method(D_METHOD("blit_rect_mask", "src", "mask", "src_rect", "dst"), &Image::blit_rect_mask); ClassDB::bind_method(D_METHOD("blend_rect", "src", "src_rect", "dst"), &Image::blend_rect); @@ -3235,10 +3196,6 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBA); //btpc bc6h BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBF); //float / BIND_ENUM_CONSTANT(FORMAT_BPTC_RGBFU); //unsigned float - BIND_ENUM_CONSTANT(FORMAT_PVRTC1_2); //pvrtc - BIND_ENUM_CONSTANT(FORMAT_PVRTC1_2A); - BIND_ENUM_CONSTANT(FORMAT_PVRTC1_4); - BIND_ENUM_CONSTANT(FORMAT_PVRTC1_4A); BIND_ENUM_CONSTANT(FORMAT_ETC); //etc1 BIND_ENUM_CONSTANT(FORMAT_ETC2_R11); //etc2 BIND_ENUM_CONSTANT(FORMAT_ETC2_R11S); //signed ); NOT srgb. @@ -3262,7 +3219,6 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(ALPHA_BLEND); BIND_ENUM_CONSTANT(COMPRESS_S3TC); - BIND_ENUM_CONSTANT(COMPRESS_PVRTC1_4); BIND_ENUM_CONSTANT(COMPRESS_ETC); BIND_ENUM_CONSTANT(COMPRESS_ETC2); BIND_ENUM_CONSTANT(COMPRESS_BPTC); @@ -3317,7 +3273,7 @@ Ref<Image> Image::rgbe_to_srgb() { for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { - new_image->set_pixel(col, row, get_pixel(col, row).to_srgb()); + new_image->set_pixel(col, row, get_pixel(col, row).linear_to_srgb()); } } @@ -3655,6 +3611,10 @@ Image::Image(const uint8_t *p_mem_png_jpg, int p_len) { if (is_empty() && _jpg_mem_loader_func) { copy_internals_from(_jpg_mem_loader_func(p_mem_png_jpg, p_len)); } + + if (is_empty() && _webp_mem_loader_func) { + copy_internals_from(_webp_mem_loader_func(p_mem_png_jpg, p_len)); + } } Ref<Resource> Image::duplicate(bool p_subresources) const { @@ -3667,3 +3627,128 @@ Ref<Resource> Image::duplicate(bool p_subresources) const { void Image::set_as_black() { memset(data.ptrw(), 0, data.size()); } + +Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric) { + // https://github.com/richgel999/bc7enc_rdo/blob/master/LICENSE + // + // This is free and unencumbered software released into the public domain. + // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this + // software, either in source code form or as a compiled binary, for any purpose, + // commercial or non - commercial, and by any means. + // In jurisdictions that recognize copyright laws, the author or authors of this + // software dedicate any and all copyright interest in the software to the public + // domain. We make this dedication for the benefit of the public at large and to + // the detriment of our heirs and successors. We intend this dedication to be an + // overt act of relinquishment in perpetuity of all present and future rights to + // this software under copyright law. + // 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 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. + + Dictionary result; + result["max"] = INFINITY; + result["mean"] = INFINITY; + result["mean_squared"] = INFINITY; + result["root_mean_squared"] = INFINITY; + result["peak_snr"] = 0.0f; + + ERR_FAIL_NULL_V(p_compared_image, result); + Error err = OK; + Ref<Image> compared_image = duplicate(true); + if (compared_image->is_compressed()) { + err = compared_image->decompress(); + } + ERR_FAIL_COND_V(err != OK, result); + Ref<Image> source_image = p_compared_image->duplicate(true); + if (source_image->is_compressed()) { + err = source_image->decompress(); + } + ERR_FAIL_COND_V(err != OK, result); + + ERR_FAIL_COND_V(err != OK, result); + + ERR_FAIL_COND_V_MSG((compared_image->get_format() >= Image::FORMAT_RH) && (compared_image->get_format() <= Image::FORMAT_RGBE9995), result, "Metrics on HDR images are not supported."); + ERR_FAIL_COND_V_MSG((source_image->get_format() >= Image::FORMAT_RH) && (source_image->get_format() <= Image::FORMAT_RGBE9995), result, "Metrics on HDR images are not supported."); + + double image_metric_max, image_metric_mean, image_metric_mean_squared, image_metric_root_mean_squared, image_metric_peak_snr = 0.0; + const bool average_component_error = true; + + const uint32_t width = MIN(compared_image->get_width(), source_image->get_width()); + const uint32_t height = MIN(compared_image->get_height(), source_image->get_height()); + + // Histogram approach originally due to Charles Bloom. + double hist[256]; + memset(hist, 0, sizeof(hist)); + + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + const Color color_a = compared_image->get_pixel(x, y); + + const Color color_b = source_image->get_pixel(x, y); + + if (!p_luma_metric) { + ERR_FAIL_COND_V_MSG(color_a.r > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.r > 1.0f, Dictionary(), "Can't compare HDR colors."); + hist[Math::abs(color_a.get_r8() - color_b.get_r8())]++; + ERR_FAIL_COND_V_MSG(color_a.g > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.g > 1.0f, Dictionary(), "Can't compare HDR colors."); + hist[Math::abs(color_a.get_g8() - color_b.get_g8())]++; + ERR_FAIL_COND_V_MSG(color_a.b > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.b > 1.0f, Dictionary(), "Can't compare HDR colors."); + hist[Math::abs(color_a.get_b8() - color_b.get_b8())]++; + ERR_FAIL_COND_V_MSG(color_a.a > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.a > 1.0f, Dictionary(), "Can't compare HDR colors."); + hist[Math::abs(color_a.get_a8() - color_b.get_a8())]++; + } else { + ERR_FAIL_COND_V_MSG(color_a.r > 1.0f, Dictionary(), "Can't compare HDR colors."); + ERR_FAIL_COND_V_MSG(color_b.r > 1.0f, Dictionary(), "Can't compare HDR colors."); + // REC709 weightings + int luma_a = (13938U * color_a.get_r8() + 46869U * color_a.get_g8() + 4729U * color_a.get_b8() + 32768U) >> 16U; + int luma_b = (13938U * color_b.get_r8() + 46869U * color_b.get_g8() + 4729U * color_b.get_b8() + 32768U) >> 16U; + hist[Math::abs(luma_a - luma_b)]++; + } + } + } + + image_metric_max = 0; + double sum = 0.0f, sum2 = 0.0f; + for (uint32_t i = 0; i < 256; i++) { + if (!hist[i]) { + continue; + } + + image_metric_max = MAX(image_metric_max, i); + + double x = i * hist[i]; + + sum += x; + sum2 += i * x; + } + + // See http://richg42.blogspot.com/2016/09/how-to-compute-psnr-from-old-berkeley.html + double total_values = width * height; + + if (average_component_error) { + total_values *= 4; + } + + image_metric_mean = CLAMP(sum / total_values, 0.0f, 255.0f); + image_metric_mean_squared = CLAMP(sum2 / total_values, 0.0f, 255.0f * 255.0f); + + image_metric_root_mean_squared = sqrt(image_metric_mean_squared); + + if (!image_metric_root_mean_squared) { + image_metric_peak_snr = 1e+10f; + } else { + image_metric_peak_snr = CLAMP(log10(255.0f / image_metric_root_mean_squared) * 20.0f, 0.0f, 500.0f); + } + result["max"] = image_metric_max; + result["mean"] = image_metric_mean; + result["mean_squared"] = image_metric_mean_squared; + result["root_mean_squared"] = image_metric_root_mean_squared; + result["peak_snr"] = image_metric_peak_snr; + return result; +} diff --git a/core/io/image.h b/core/io/image.h index cc626ec52c..1025554d51 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -36,8 +36,6 @@ #include "core/math/rect2.h" /** - * @author Juan Linietsky <reduzio@gmail.com> - * * Image storage class. This is used to store an image in user memory, as well as * providing some basic methods for image manipulation. * Images can be loaded from a file, or registered into the Render object as textures. @@ -91,10 +89,6 @@ public: FORMAT_BPTC_RGBA, //btpc bc7 FORMAT_BPTC_RGBF, //float bc6h FORMAT_BPTC_RGBFU, //unsigned float bc6hu - FORMAT_PVRTC1_2, //pvrtc1 - FORMAT_PVRTC1_2A, - FORMAT_PVRTC1_4, - FORMAT_PVRTC1_4A, FORMAT_ETC, //etc1 FORMAT_ETC2_R11, //etc2 FORMAT_ETC2_R11S, //signed, NOT srgb. @@ -138,11 +132,9 @@ public: static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels); - static void (*_image_compress_pvrtc1_4bpp_func)(Image *); static void (*_image_compress_etc1_func)(Image *, float); static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels); - static void (*_image_decompress_pvrtc)(Image *); static void (*_image_decompress_bc)(Image *); static void (*_image_decompress_bptc)(Image *); static void (*_image_decompress_etc1)(Image *); @@ -155,6 +147,7 @@ public: static Ref<Image> (*png_unpacker)(const Vector<uint8_t> &p_buffer); static Vector<uint8_t> (*basis_universal_packer)(const Ref<Image> &p_image, UsedChannels p_channels); static Ref<Image> (*basis_universal_unpacker)(const Vector<uint8_t> &p_buffer); + static Ref<Image> (*basis_universal_unpacker_ptr)(const uint8_t *p_data, int p_size); _FORCE_INLINE_ Color _get_color_at_ofs(const uint8_t *ptr, uint32_t ofs) const; _FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color); @@ -163,14 +156,6 @@ protected: static void _bind_methods(); private: - void _create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { - create(p_width, p_height, p_use_mipmaps, p_format); - } - - void _create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { - create(p_width, p_height, p_use_mipmaps, p_format, p_data); - } - Format format = FORMAT_L8; Vector<uint8_t> data; int width = 0; @@ -299,6 +284,14 @@ public: Vector<uint8_t> save_png_to_buffer() const; Error save_exr(const String &p_path, bool p_grayscale) const; + void create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) { + create(p_width, p_height, p_use_mipmaps, p_format); + } + + void create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) { + create(p_width, p_height, p_use_mipmaps, p_format, p_data); + } + /** * create an empty image */ @@ -336,7 +329,6 @@ public: enum CompressMode { COMPRESS_S3TC, - COMPRESS_PVRTC1_4, COMPRESS_ETC, COMPRESS_ETC2, COMPRESS_BPTC, @@ -390,7 +382,7 @@ public: virtual Ref<Resource> duplicate(bool p_subresources = false) const override; - UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC); + UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC) const; void optimize_channels(); Color get_pixelv(const Point2i &p_point) const; @@ -410,6 +402,8 @@ public: mipmaps = p_image->mipmaps; data = p_image->data; } + + Dictionary compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric = true); }; VARIANT_ENUM_CAST(Image::Format) diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index b9fc416f65..2ccc95f0de 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -44,17 +44,14 @@ bool ImageFormatLoader::recognize(const String &p_extension) const { return false; } -Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_custom, bool p_force_linear, float p_scale) { +Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, bool p_force_linear, 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."); - FileAccess *f = p_custom; - if (!f) { + Ref<FileAccess> f = p_custom; + if (f.is_null()) { Error err; f = FileAccess::open(p_file, FileAccess::READ, &err); - if (!f) { - ERR_PRINT("Error opening file '" + p_file + "'."); - return err; - } + ERR_FAIL_COND_V_MSG(f.is_null(), err, "Error opening file '" + p_file + "'."); } String extension = p_file.get_extension(); @@ -69,18 +66,10 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c } if (err != ERR_FILE_UNRECOGNIZED) { - if (!p_custom) { - memdelete(f); - } - return err; } } - if (!p_custom) { - memdelete(f); - } - return ERR_FILE_UNRECOGNIZED; } @@ -123,8 +112,8 @@ void ImageLoader::cleanup() { ///////////////// RES ResourceFormatLoaderImage::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) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { if (r_error) { *r_error = ERR_CANT_OPEN; } @@ -136,7 +125,6 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin bool unrecognized = header[0] != 'G' || header[1] != 'D' || header[2] != 'I' || header[3] != 'M'; if (unrecognized) { - memdelete(f); if (r_error) { *r_error = ERR_FILE_UNRECOGNIZED; } @@ -155,7 +143,6 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin } if (idx == -1) { - memdelete(f); if (r_error) { *r_error = ERR_FILE_UNRECOGNIZED; } @@ -167,8 +154,6 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin Error err = ImageLoader::loader[idx]->load_image(image, f, false, 1.0); - memdelete(f); - if (err != OK) { if (r_error) { *r_error = err; diff --git a/core/io/image_loader.h b/core/io/image_loader.h index 6d1b1e3646..9409617268 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -44,7 +44,7 @@ class ImageFormatLoader { friend class ResourceFormatLoaderImage; protected: - virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) = 0; + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, bool p_force_linear, float p_scale) = 0; virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; bool recognize(const String &p_extension) const; @@ -58,7 +58,7 @@ class ImageLoader { protected: public: - static Error load_image(String p_file, Ref<Image> p_image, FileAccess *p_custom = nullptr, bool p_force_linear = false, float p_scale = 1.0); + static Error load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), bool p_force_linear = false, float p_scale = 1.0); static void get_recognized_extensions(List<String> *p_extensions); static ImageFormatLoader *recognize(const String &p_extension); diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 68b4e4b354..52674150bb 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -98,13 +98,18 @@ struct _IP_ResolverPrivate { if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) { continue; } + // We might be overriding another result, but we don't care as long as the result is valid. + if (response.size()) { + String key = get_cache_key(hostname, type); + cache[key] = response; + } queue[i].response = response; queue[i].status.set(response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE); } } static void _thread_function(void *self) { - _IP_ResolverPrivate *ipr = (_IP_ResolverPrivate *)self; + _IP_ResolverPrivate *ipr = static_cast<_IP_ResolverPrivate *>(self); while (!ipr->thread_abort) { ipr->sem.wait(); @@ -120,30 +125,8 @@ struct _IP_ResolverPrivate { }; IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { - List<IPAddress> res; - String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); - - resolver->mutex.lock(); - if (resolver->cache.has(key)) { - res = resolver->cache[key]; - } else { - // This should be run unlocked so the resolver thread can keep - // resolving other requests. - resolver->mutex.unlock(); - _resolve_hostname(res, p_hostname, p_type); - resolver->mutex.lock(); - // We might be overriding another result, but we don't care (they are the - // same hostname). - resolver->cache[key] = res; - } - resolver->mutex.unlock(); - - for (int i = 0; i < res.size(); ++i) { - if (res[i].is_valid()) { - return res[i]; - } - } - return IPAddress(); + const Array addresses = resolve_hostname_addresses(p_hostname, p_type); + return addresses.size() ? addresses[0].operator IPAddress() : IPAddress(); } Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) { @@ -159,17 +142,16 @@ Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) { resolver->mutex.unlock(); _resolve_hostname(res, p_hostname, p_type); resolver->mutex.lock(); - // We might be overriding another result, but we don't care (they are the - // same hostname). - resolver->cache[key] = res; + // We might be overriding another result, but we don't care as long as the result is valid. + if (res.size()) { + resolver->cache[key] = res; + } } resolver->mutex.unlock(); Array result; for (int i = 0; i < res.size(); ++i) { - if (res[i].is_valid()) { - result.push_back(String(res[i])); - } + result.push_back(String(res[i])); } return result; } @@ -204,7 +186,7 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ } IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { - ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP::RESOLVER_STATUS_NONE); + ERR_FAIL_INDEX_V_MSG(p_id, IP::RESOLVER_MAX_QUERIES, IP::RESOLVER_STATUS_NONE, vformat("Too many concurrent DNS resolver queries (%d, but should be %d at most). Try performing less network requests at once.", p_id, IP::RESOLVER_MAX_QUERIES)); IP::ResolverStatus res = resolver->queue[p_id].status.get(); if (res == IP::RESOLVER_STATUS_NONE) { @@ -215,7 +197,7 @@ IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { } IPAddress IP::get_resolve_item_address(ResolverID p_id) const { - ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IPAddress()); + ERR_FAIL_INDEX_V_MSG(p_id, IP::RESOLVER_MAX_QUERIES, IPAddress(), vformat("Too many concurrent DNS resolver queries (%d, but should be %d at most). Try performing less network requests at once.", p_id, IP::RESOLVER_MAX_QUERIES)); MutexLock lock(resolver->mutex); @@ -235,7 +217,7 @@ IPAddress IP::get_resolve_item_address(ResolverID p_id) const { } Array IP::get_resolve_item_addresses(ResolverID p_id) const { - ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, Array()); + ERR_FAIL_INDEX_V_MSG(p_id, IP::RESOLVER_MAX_QUERIES, Array(), vformat("Too many concurrent DNS resolver queries (%d, but should be %d at most). Try performing less network requests at once.", p_id, IP::RESOLVER_MAX_QUERIES)); MutexLock lock(resolver->mutex); if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) { @@ -255,7 +237,7 @@ Array IP::get_resolve_item_addresses(ResolverID p_id) const { } void IP::erase_resolve_item(ResolverID p_id) { - ERR_FAIL_INDEX(p_id, IP::RESOLVER_MAX_QUERIES); + ERR_FAIL_INDEX_MSG(p_id, IP::RESOLVER_MAX_QUERIES, vformat("Too many concurrent DNS resolver queries (%d, but should be %d at most). Try performing less network requests at once.", p_id, IP::RESOLVER_MAX_QUERIES)); resolver->queue[p_id].status.set(IP::RESOLVER_STATUS_NONE); } diff --git a/core/io/ip.h b/core/io/ip.h index 3c6040a1f0..06ff8a4d70 100644 --- a/core/io/ip.h +++ b/core/io/ip.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -38,7 +38,6 @@ struct _IP_ResolverPrivate; class IP : public Object { GDCLASS(IP, Object); - OBJ_CATEGORY("Networking"); public: enum ResolverStatus { @@ -56,14 +55,14 @@ public: }; enum { - RESOLVER_MAX_QUERIES = 32, + RESOLVER_MAX_QUERIES = 256, RESOLVER_INVALID_ID = -1 }; typedef int ResolverID; private: - _IP_ResolverPrivate *resolver; + _IP_ResolverPrivate *resolver = nullptr; protected: static IP *singleton; diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index 1c1ac8a88f..d183c60798 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -71,7 +71,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { int n = 0; char32_t c = p_string[i]; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { n = c - '0'; } else if (c >= 'a' && c <= 'f') { n = 10 + (c - 'a'); @@ -113,7 +113,7 @@ void IPAddress::_parse_ipv6(const String &p_string) { } else if (c == '.') { part_ipv4 = true; - } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + } else if (is_hex_digit(c)) { if (!part_found) { parts[parts_idx++] = i; part_found = true; diff --git a/core/io/ip_address.h b/core/io/ip_address.h index 05da675704..dc0c19bf48 100644 --- a/core/io/ip_address.h +++ b/core/io/ip_address.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/json.cpp b/core/io/json.cpp index 5823afbdcd..4b745dff44 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -229,12 +229,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } char32_t v; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -265,12 +265,12 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { + if (!is_hex_digit(c)) { r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } char32_t v; - if (c >= '0' && c <= '9') { + if (is_digit(c)) { v = c - '0'; } else if (c >= 'a' && c <= 'f') { v = c - 'a'; @@ -326,7 +326,7 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to break; } - if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) { + if (p_str[index] == '-' || is_digit(p_str[index])) { //a number const char32_t *rptr; double number = String::to_float(&p_str[index], &rptr); @@ -335,10 +335,10 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_token.value = number; return OK; - } else if ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { + } else if (is_ascii_char(p_str[index])) { String id; - while ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { + while (is_ascii_char(p_str[index])) { id += p_str[index]; index++; } diff --git a/core/io/json.h b/core/io/json.h index f20c97f540..ed251938ec 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 8a8bdf07d3..c19fc2820b 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -115,28 +115,21 @@ void Logger::logf_error(const char *p_format, ...) { va_end(argp); } -void RotatedFileLogger::close_file() { - if (file) { - memdelete(file); - file = nullptr; - } -} - void RotatedFileLogger::clear_old_backups() { int max_backups = max_files - 1; // -1 for the current file String basename = base_path.get_file().get_basename(); String extension = base_path.get_extension(); - DirAccess *da = DirAccess::open(base_path.get_base_dir()); - if (!da) { + Ref<DirAccess> da = DirAccess::open(base_path.get_base_dir()); + if (da.is_null()) { return; } da->list_dir_begin(); String f = da->get_next(); Set<String> backups; - while (f != String()) { + while (!f.is_empty()) { if (!da->current_is_dir() && f.begins_with(basename) && f.get_extension() == extension && f != base_path.get_file()) { backups.insert(f); } @@ -152,37 +145,34 @@ void RotatedFileLogger::clear_old_backups() { da->remove(E->get()); } } - - memdelete(da); } void RotatedFileLogger::rotate_file() { - close_file(); + file.unref(); if (FileAccess::exists(base_path)) { if (max_files > 1) { String timestamp = Time::get_singleton()->get_datetime_string_from_system().replace(":", "."); String backup_name = base_path.get_basename() + timestamp; - if (base_path.get_extension() != String()) { + if (!base_path.get_extension().is_empty()) { backup_name += "." + base_path.get_extension(); } - DirAccess *da = DirAccess::open(base_path.get_base_dir()); - if (da) { + Ref<DirAccess> da = DirAccess::open(base_path.get_base_dir()); + if (da.is_valid()) { da->copy(base_path, backup_name); - memdelete(da); } clear_old_backups(); } } else { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_USERDATA); - if (da) { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_USERDATA); + if (da.is_valid()) { da->make_dir_recursive(base_path.get_base_dir()); - memdelete(da); } } file = FileAccess::open(base_path, FileAccess::WRITE); + file->detach_from_objectdb(); // Note: This FileAccess instance will exist longer than ObjectDB, therefor can't be registered in ObjectDB. } RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) : @@ -196,7 +186,7 @@ void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { return; } - if (file) { + if (file.is_valid()) { const int static_buf_size = 512; char static_buf[static_buf_size]; char *buf = static_buf; @@ -222,10 +212,6 @@ void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { } } -RotatedFileLogger::~RotatedFileLogger() { - close_file(); -} - void StdLogger::logv(const char *p_format, va_list p_list, bool p_err) { if (!should_log(p_err)) { return; diff --git a/core/io/logger.h b/core/io/logger.h index 48b073aa45..e3ac00f11c 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -81,9 +81,8 @@ class RotatedFileLogger : public Logger { String base_path; int max_files; - FileAccess *file = nullptr; + Ref<FileAccess> file; - void close_file(); void clear_old_backups(); void rotate_file(); @@ -91,8 +90,6 @@ public: RotatedFileLogger(const String &p_base_path, int p_max_files = 10); virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0; - - virtual ~RotatedFileLogger(); }; class CompositeLogger : public Logger { diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index e7d5b78d14..d0bc05566e 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -52,9 +52,8 @@ ObjectID EncodedObjectAsID::get_object_id() const { return id; } -#define _S(a) ((int32_t)a) -#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) -#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) +#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err) +#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err) #define ENCODE_MASK 0xFF #define ENCODE_FLAG_64 1 << 16 @@ -95,7 +94,8 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r return OK; } -Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_objects) { +Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_objects, int p_depth) { + ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Variant is too deep. Bailing."); const uint8_t *buf = p_buffer; int len = p_len; @@ -562,7 +562,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int return err; } - if (str == String()) { + if (str.is_empty()) { r_variant = (Object *)nullptr; } else { Object *obj = ClassDB::instantiate(str); @@ -586,7 +586,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Variant value; int used; - err = decode_variant(value, buf, len, &used, p_allow_objects); + err = decode_variant(value, buf, len, &used, p_allow_objects, p_depth + 1); if (err) { return err; } @@ -636,7 +636,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Variant key, value; int used; - Error err = decode_variant(key, buf, len, &used, p_allow_objects); + Error err = decode_variant(key, buf, len, &used, p_allow_objects, p_depth + 1); ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; @@ -645,7 +645,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += used; } - err = decode_variant(value, buf, len, &used, p_allow_objects); + err = decode_variant(value, buf, len, &used, p_allow_objects, p_depth + 1); ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; @@ -678,7 +678,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int for (int i = 0; i < count; i++) { int used = 0; Variant v; - Error err = decode_variant(v, buf, len, &used, p_allow_objects); + Error err = decode_variant(v, buf, len, &used, p_allow_objects, p_depth + 1); ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant."); buf += used; len -= used; @@ -1031,7 +1031,7 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { } Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) { - ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential inifite recursion detected. Bailing."); + ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential infinite recursion detected. Bailing."); uint8_t *buf = r_buffer; r_len = 0; @@ -1068,6 +1068,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo flags |= ENCODE_FLAG_OBJECT_AS_ID; } } break; +#ifdef REAL_T_IS_DOUBLE + case Variant::VECTOR2: + case Variant::VECTOR3: + case Variant::PACKED_VECTOR2_ARRAY: + case Variant::PACKED_VECTOR3_ARRAY: + case Variant::TRANSFORM2D: + case Variant::TRANSFORM3D: + case Variant::QUATERNION: + case Variant::PLANE: + case Variant::BASIS: + case Variant::RECT2: + case Variant::AABB: { + flags |= ENCODE_FLAG_64; + } break; +#endif // REAL_T_IS_DOUBLE default: { } // nothing to do at this stage } @@ -1417,19 +1432,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo d.get_key_list(&keys); for (const Variant &E : keys) { - /* - CharString utf8 = E->->utf8(); - - if (buf) { - encode_uint32(utf8.length()+1,buf); - buf+=4; - memcpy(buf,utf8.get_data(),utf8.length()+1); - } - - r_len+=4+utf8.length()+1; - while (r_len%4) - r_len++; //pad - */ int len; Error err = encode_variant(E, buf, len, p_full_objects, p_depth + 1); ERR_FAIL_COND_V(err, err); diff --git a/core/io/marshalls.h b/core/io/marshalls.h index 9ea060e48c..fef3a1c2c1 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -212,7 +212,7 @@ public: EncodedObjectAsID() {} }; -Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false); +Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false, int p_depth = 0); Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false, int p_depth = 0); #endif // MARSHALLS_H diff --git a/core/io/net_socket.cpp b/core/io/net_socket.cpp index b51d26ba83..3e5246a134 100644 --- a/core/io/net_socket.cpp +++ b/core/io/net_socket.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/net_socket.h b/core/io/net_socket.h index fd7d50c704..23eff17a61 100644 --- a/core/io/net_socket.h +++ b/core/io/net_socket.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp index d34b5b6fe3..027fdd51aa 100644 --- a/core/io/packed_data_container.cpp +++ b/core/io/packed_data_container.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -105,7 +105,7 @@ Variant PackedDataContainer::_get_at_ofs(uint32_t p_ofs, const uint8_t *p_buf, b if (type == TYPE_ARRAY || type == TYPE_DICT) { Ref<PackedDataContainerRef> pdcr = memnew(PackedDataContainerRef); - Ref<PackedDataContainer> pdc = Ref<PackedDataContainer>((PackedDataContainer *)this); + Ref<PackedDataContainer> pdc = Ref<PackedDataContainer>(const_cast<PackedDataContainer *>(this)); pdcr->from = pdc; pdcr->offset = p_ofs; diff --git a/core/io/packed_data_container.h b/core/io/packed_data_container.h index 40772bb2bf..f042b364ee 100644 --- a/core/io/packed_data_container.h +++ b/core/io/packed_data_container.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 87d2b66e5b..0af236f766 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -39,7 +39,7 @@ void PacketPeer::set_encode_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes"); ERR_FAIL_COND_MSG(p_max_size > 256 * 1024 * 1024, "Max encode buffer cannot exceed 256 MiB"); encode_buffer_max_size = next_power_of_2(p_max_size); - encode_buffer.resize(0); + encode_buffer.clear(); } int PacketPeer::get_encode_buffer_max_size() const { diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index bc1f4aaabf..0b12640627 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp index a6d220622b..3c7ab28e12 100644 --- a/core/io/packet_peer_dtls.cpp +++ b/core/io/packet_peer_dtls.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/packet_peer_dtls.h b/core/io/packet_peer_dtls.h index 31c52f539f..c5443d043e 100644 --- a/core/io/packet_peer_dtls.h +++ b/core/io/packet_peer_dtls.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index f951a5158c..503eb17cae 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index 40d3c44e40..444a6dd5ba 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 8d75581342..aa1b323db2 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -62,7 +62,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & int v = 0; if (i * 2 < _key.length()) { char32_t ct = _key[i * 2]; - if (ct >= '0' && ct <= '9') { + if (is_digit(ct)) { ct = ct - '0'; } else if (ct >= 'a' && ct <= 'f') { ct = 10 + ct - 'a'; @@ -72,7 +72,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & if (i * 2 + 1 < _key.length()) { char32_t ct = _key[i * 2 + 1]; - if (ct >= '0' && ct <= '9') { + if (is_digit(ct)) { ct = ct - '0'; } else if (ct >= 'a' && ct <= 'f') { ct = 10 + ct - 'a'; @@ -83,13 +83,8 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & } enc_dir = p_encrypt_directory; - if (file != nullptr) { - memdelete(file); - } - file = FileAccess::open(p_file, FileAccess::WRITE); - - ERR_FAIL_COND_V_MSG(!file, ERR_CANT_CREATE, "Can't open file to write: " + String(p_file) + "."); + ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, "Can't open file to write: " + String(p_file) + "."); alignment = p_alignment; @@ -112,8 +107,8 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String & } Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) { - FileAccess *f = FileAccess::open(p_src, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ); + if (f.is_null()) { return ERR_FILE_CANT_OPEN; } @@ -149,14 +144,11 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encr files.push_back(pf); - f->close(); - memdelete(f); - return OK; } Error PCKPacker::flush(bool p_verbose) { - ERR_FAIL_COND_V_MSG(!file, ERR_INVALID_PARAMETER, "File must be opened before use."); + ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use."); int64_t file_base_ofs = file->get_position(); file->store_64(0); // files base @@ -168,12 +160,12 @@ Error PCKPacker::flush(bool p_verbose) { // write the index file->store_32(files.size()); - FileAccessEncrypted *fae = nullptr; - FileAccess *fhead = file; + Ref<FileAccessEncrypted> fae; + Ref<FileAccess> fhead = file; if (enc_dir) { - fae = memnew(FileAccessEncrypted); - ERR_FAIL_COND_V(!fae, ERR_CANT_CREATE); + fae.instantiate(); + ERR_FAIL_COND_V(fae.is_null(), ERR_CANT_CREATE); Error err = fae->open_and_parse(file, key, FileAccessEncrypted::MODE_WRITE_AES256, false); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); @@ -202,9 +194,9 @@ Error PCKPacker::flush(bool p_verbose) { fhead->store_32(flags); } - if (fae) { - fae->release(); - memdelete(fae); + if (fae.is_valid()) { + fhead.unref(); + fae.unref(); } int header_padding = _get_pad(alignment, file->get_position()); @@ -222,14 +214,13 @@ Error PCKPacker::flush(bool p_verbose) { int count = 0; for (int i = 0; i < files.size(); i++) { - FileAccess *src = FileAccess::open(files[i].src_path, FileAccess::READ); + Ref<FileAccess> src = FileAccess::open(files[i].src_path, FileAccess::READ); uint64_t to_write = files[i].size; - fae = nullptr; - FileAccess *ftmp = file; + Ref<FileAccess> ftmp = file; if (files[i].encrypted) { - fae = memnew(FileAccessEncrypted); - ERR_FAIL_COND_V(!fae, ERR_CANT_CREATE); + fae.instantiate(); + ERR_FAIL_COND_V(fae.is_null(), ERR_CANT_CREATE); Error err = fae->open_and_parse(file, key, FileAccessEncrypted::MODE_WRITE_AES256, false); ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); @@ -242,9 +233,9 @@ Error PCKPacker::flush(bool p_verbose) { to_write -= read; } - if (fae) { - fae->release(); - memdelete(fae); + if (fae.is_valid()) { + ftmp.unref(); + fae.unref(); } int pad = _get_pad(alignment, file->get_position()); @@ -252,15 +243,10 @@ Error PCKPacker::flush(bool p_verbose) { file->store_8(Math::rand() % 256); } - src->close(); - memdelete(src); count += 1; const int file_num = files.size(); if (p_verbose && (file_num > 0)) { - if (count % 100 == 0) { - printf("%i/%i (%.2f)\r", count, file_num, float(count) / file_num * 100); - fflush(stdout); - } + print_line(vformat("[%d/%d - %d%%] PCKPacker flush: %s -> %s", count, file_num, float(count) / file_num * 100, files[i].src_path, files[i].path)); } } @@ -268,15 +254,8 @@ Error PCKPacker::flush(bool p_verbose) { printf("\n"); } - file->close(); + file.unref(); memdelete_arr(buf); return OK; } - -PCKPacker::~PCKPacker() { - if (file != nullptr) { - memdelete(file); - } - file = nullptr; -} diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h index bd8902a01d..77ec293fb2 100644 --- a/core/io/pck_packer.h +++ b/core/io/pck_packer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -38,7 +38,7 @@ class FileAccess; class PCKPacker : public RefCounted { GDCLASS(PCKPacker, RefCounted); - FileAccess *file = nullptr; + Ref<FileAccess> file; int alignment = 0; uint64_t ofs = 0; @@ -63,7 +63,6 @@ public: Error flush(bool p_verbose = false); PCKPacker() {} - ~PCKPacker(); }; #endif // PCK_PACKER_H diff --git a/core/io/resource.cpp b/core/io/resource.cpp index 972076e397..bf91438810 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -52,7 +52,7 @@ void Resource::set_path(const String &p_path, bool p_take_over) { return; } - if (path_cache != "") { + if (!path_cache.is_empty()) { ResourceCache::lock.write_lock(); ResourceCache::resources.erase(path_cache); ResourceCache::lock.write_unlock(); @@ -82,7 +82,7 @@ void Resource::set_path(const String &p_path, bool p_take_over) { } path_cache = p_path; - if (path_cache != "") { + if (!path_cache.is_empty()) { ResourceCache::lock.write_lock(); ResourceCache::resources[path_cache] = this; ResourceCache::lock.write_unlock(); @@ -290,6 +290,21 @@ void Resource::_take_over_path(const String &p_path) { } RID Resource::get_rid() const { + if (get_script_instance()) { + Callable::CallError ce; + RID ret = get_script_instance()->callp(SNAME("_get_rid"), nullptr, 0, ce); + if (ce.error == Callable::CallError::CALL_OK && ret.is_valid()) { + return ret; + } + } + if (_get_extension() && _get_extension()->get_rid) { + RID ret; + ret.from_uint64(_get_extension()->get_rid(_get_extension_instance())); + if (ret.is_valid()) { + return ret; + } + } + return RID(); } @@ -383,7 +398,7 @@ bool Resource::is_translation_remapped() const { #ifdef TOOLS_ENABLED //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored void Resource::set_id_for_path(const String &p_path, const String &p_id) { - if (p_id == "") { + if (p_id.is_empty()) { ResourceCache::path_cache_lock.write_lock(); ResourceCache::resource_path_cache[p_path].erase(get_path()); ResourceCache::path_cache_lock.write_unlock(); @@ -428,13 +443,18 @@ void Resource::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "resource_name"), "set_name", "get_name"); + + MethodInfo get_rid_bind("_get_rid"); + get_rid_bind.return_val.type = Variant::RID; + + ::ClassDB::add_virtual_method(get_class_static(), get_rid_bind, true, Vector<String>(), true); } Resource::Resource() : remapped_list(this) {} Resource::~Resource() { - if (path_cache != "") { + if (!path_cache.is_empty()) { ResourceCache::lock.write_lock(); ResourceCache::resources.erase(path_cache); ResourceCache::lock.write_unlock(); @@ -518,10 +538,10 @@ void ResourceCache::dump(const char *p_file, bool p_short) { Map<String, int> type_count; - FileAccess *f = nullptr; + Ref<FileAccess> f; if (p_file) { - f = FileAccess::open(p_file, FileAccess::WRITE); - ERR_FAIL_COND_MSG(!f, "Cannot create file at path '" + String(p_file) + "'."); + f = FileAccess::open(String::utf8(p_file), FileAccess::WRITE); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot create file at path '" + String::utf8(p_file) + "'."); } const String *K = nullptr; @@ -535,21 +555,17 @@ void ResourceCache::dump(const char *p_file, bool p_short) { type_count[r->get_class()]++; if (!p_short) { - if (f) { + if (f.is_valid()) { f->store_line(r->get_class() + ": " + r->get_path()); } } } for (const KeyValue<String, int> &E : type_count) { - if (f) { + if (f.is_valid()) { f->store_line(E.key + " count: " + itos(E.value)); } } - if (f) { - f->close(); - memdelete(f); - } lock.read_unlock(); #else diff --git a/core/io/resource.h b/core/io/resource.h index 109c0f6611..8068000f32 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -37,6 +37,8 @@ #include "core/templates/safe_refcount.h" #include "core/templates/self_list.h" +class Node; + #define RES_BASE_EXTENSION(m_ext) \ public: \ static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension(m_ext, get_class_static()); } \ @@ -46,7 +48,6 @@ private: class Resource : public RefCounted { GDCLASS(Resource, RefCounted); - OBJ_CATEGORY("Resources"); public: static void register_custom_data_to_otdb() { ClassDB::add_resource_base_extension("res", get_class_static()); } @@ -103,7 +104,7 @@ public: virtual void set_path(const String &p_path, bool p_take_over = false); String get_path() const; - _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.find("::") != -1 || path_cache.begins_with("local://"); } + _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); } static String generate_scene_unique_id(); void set_scene_unique_id(const String &p_id); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index a5a195f859..8d4dbc3f73 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -53,9 +53,9 @@ enum { VARIANT_PLANE = 13, VARIANT_QUATERNION = 14, VARIANT_AABB = 15, - VARIANT_MATRIX3 = 16, - VARIANT_TRANSFORM = 17, - VARIANT_MATRIX32 = 18, + VARIANT_BASIS = 16, + VARIANT_TRANSFORM3D = 17, + VARIANT_TRANSFORM2D = 18, VARIANT_COLOR = 20, VARIANT_NODE_PATH = 22, VARIANT_RID = 23, @@ -63,13 +63,13 @@ enum { VARIANT_INPUT_EVENT = 25, VARIANT_DICTIONARY = 26, VARIANT_ARRAY = 30, - VARIANT_RAW_ARRAY = 31, - VARIANT_INT32_ARRAY = 32, - VARIANT_FLOAT32_ARRAY = 33, - VARIANT_STRING_ARRAY = 34, - VARIANT_VECTOR3_ARRAY = 35, - VARIANT_COLOR_ARRAY = 36, - VARIANT_VECTOR2_ARRAY = 37, + VARIANT_PACKED_BYTE_ARRAY = 31, + VARIANT_PACKED_INT32_ARRAY = 32, + VARIANT_PACKED_FLOAT32_ARRAY = 33, + VARIANT_PACKED_STRING_ARRAY = 34, + VARIANT_PACKED_VECTOR3_ARRAY = 35, + VARIANT_PACKED_COLOR_ARRAY = 36, + VARIANT_PACKED_VECTOR2_ARRAY = 37, VARIANT_INT64 = 40, VARIANT_DOUBLE = 41, VARIANT_CALLABLE = 42, @@ -78,8 +78,8 @@ enum { VARIANT_VECTOR2I = 45, VARIANT_RECT2I = 46, VARIANT_VECTOR3I = 47, - VARIANT_INT64_ARRAY = 48, - VARIANT_FLOAT64_ARRAY = 49, + VARIANT_PACKED_INT64_ARRAY = 48, + VARIANT_PACKED_FLOAT64_ARRAY = 49, OBJECT_EMPTY = 0, OBJECT_EXTERNAL_RESOURCE = 1, OBJECT_INTERNAL_RESOURCE = 2, @@ -101,6 +101,50 @@ void ResourceLoaderBinary::_advance_padding(uint32_t p_len) { } } +static Error read_reals(real_t *dst, Ref<FileAccess> &f, size_t count) { + if (f->real_is_double) { + if (sizeof(real_t) == 8) { + // Ideal case with double-precision + f->get_buffer((uint8_t *)dst, count * sizeof(double)); +#ifdef BIG_ENDIAN_ENABLED + { + uint64_t *dst = (uint64_t *)dst; + for (size_t i = 0; i < count; i++) { + dst[i] = BSWAP64(dst[i]); + } + } +#endif + } else if (sizeof(real_t) == 4) { + // May be slower, but this is for compatibility. Eventually the data should be converted. + for (size_t i = 0; i < count; ++i) { + dst[i] = f->get_double(); + } + } else { + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "real_t size is neither 4 nor 8!"); + } + } else { + if (sizeof(real_t) == 4) { + // Ideal case with float-precision + f->get_buffer((uint8_t *)dst, count * sizeof(float)); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *dst = (uint32_t *)dst; + for (size_t i = 0; i < count; i++) { + dst[i] = BSWAP32(dst[i]); + } + } +#endif + } else if (sizeof(real_t) == 8) { + for (size_t i = 0; i < count; ++i) { + dst[i] = f->get_float(); + } + } else { + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "real_t size is neither 4 nor 8!"); + } + } + return OK; +} + StringName ResourceLoaderBinary::_get_string() { uint32_t id = f->get_32(); if (id & 0x80000000) { @@ -220,7 +264,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; - case VARIANT_MATRIX32: { + case VARIANT_TRANSFORM2D: { Transform2D v; v.elements[0].x = f->get_real(); v.elements[0].y = f->get_real(); @@ -231,7 +275,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; - case VARIANT_MATRIX3: { + case VARIANT_BASIS: { Basis v; v.elements[0].x = f->get_real(); v.elements[0].y = f->get_real(); @@ -245,7 +289,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; - case VARIANT_TRANSFORM: { + case VARIANT_TRANSFORM3D: { Transform3D v; v.basis.elements[0].x = f->get_real(); v.basis.elements[0].y = f->get_real(); @@ -335,7 +379,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { String exttype = get_unicode_string(); String path = get_unicode_string(); - if (path.find("://") == -1 && path.is_relative_path()) { + if (!path.contains("://") && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path)); } @@ -422,7 +466,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = a; } break; - case VARIANT_RAW_ARRAY: { + case VARIANT_PACKED_BYTE_ARRAY: { uint32_t len = f->get_32(); Vector<uint8_t> array; @@ -434,7 +478,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; - case VARIANT_INT32_ARRAY: { + case VARIANT_PACKED_INT32_ARRAY: { uint32_t len = f->get_32(); Vector<int32_t> array; @@ -453,7 +497,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; - case VARIANT_INT64_ARRAY: { + case VARIANT_PACKED_INT64_ARRAY: { uint32_t len = f->get_32(); Vector<int64_t> array; @@ -472,7 +516,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; - case VARIANT_FLOAT32_ARRAY: { + case VARIANT_PACKED_FLOAT32_ARRAY: { uint32_t len = f->get_32(); Vector<float> array; @@ -491,7 +535,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; - case VARIANT_FLOAT64_ARRAY: { + case VARIANT_PACKED_FLOAT64_ARRAY: { uint32_t len = f->get_32(); Vector<double> array; @@ -510,7 +554,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; - case VARIANT_STRING_ARRAY: { + case VARIANT_PACKED_STRING_ARRAY: { uint32_t len = f->get_32(); Vector<String> array; array.resize(len); @@ -522,78 +566,51 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { r_v = array; } break; - case VARIANT_VECTOR2_ARRAY: { + case VARIANT_PACKED_VECTOR2_ARRAY: { uint32_t len = f->get_32(); Vector<Vector2> array; array.resize(len); Vector2 *w = array.ptrw(); - if (sizeof(Vector2) == 8) { - f->get_buffer((uint8_t *)w, len * sizeof(real_t) * 2); -#ifdef BIG_ENDIAN_ENABLED - { - uint32_t *ptr = (uint32_t *)w.ptr(); - for (int i = 0; i < len * 2; i++) { - ptr[i] = BSWAP32(ptr[i]); - } - } - -#endif - - } else { - ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector2 size is NOT 8!"); - } + static_assert(sizeof(Vector2) == 2 * sizeof(real_t)); + const Error err = read_reals(reinterpret_cast<real_t *>(w), f, len * 2); + ERR_FAIL_COND_V(err != OK, err); r_v = array; } break; - case VARIANT_VECTOR3_ARRAY: { + case VARIANT_PACKED_VECTOR3_ARRAY: { uint32_t len = f->get_32(); Vector<Vector3> array; array.resize(len); Vector3 *w = array.ptrw(); - if (sizeof(Vector3) == 12) { - f->get_buffer((uint8_t *)w, len * sizeof(real_t) * 3); -#ifdef BIG_ENDIAN_ENABLED - { - uint32_t *ptr = (uint32_t *)w.ptr(); - for (int i = 0; i < len * 3; i++) { - ptr[i] = BSWAP32(ptr[i]); - } - } - -#endif - - } else { - ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector3 size is NOT 12!"); - } + static_assert(sizeof(Vector3) == 3 * sizeof(real_t)); + const Error err = read_reals(reinterpret_cast<real_t *>(w), f, len * 3); + ERR_FAIL_COND_V(err != OK, err); r_v = array; } break; - case VARIANT_COLOR_ARRAY: { + case VARIANT_PACKED_COLOR_ARRAY: { uint32_t len = f->get_32(); Vector<Color> array; array.resize(len); Color *w = array.ptrw(); - if (sizeof(Color) == 16) { - f->get_buffer((uint8_t *)w, len * sizeof(real_t) * 4); + // Colors always use `float` even with double-precision support enabled + static_assert(sizeof(Color) == 4 * sizeof(float)); + f->get_buffer((uint8_t *)w, len * sizeof(float) * 4); #ifdef BIG_ENDIAN_ENABLED - { - uint32_t *ptr = (uint32_t *)w.ptr(); - for (int i = 0; i < len * 4; i++) { - ptr[i] = BSWAP32(ptr[i]); - } + { + uint32_t *ptr = (uint32_t *)w.ptr(); + for (int i = 0; i < len * 4; i++) { + ptr[i] = BSWAP32(ptr[i]); } + } #endif - } else { - ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Color size is NOT 16!"); - } - r_v = array; } break; default: { @@ -626,7 +643,7 @@ Error ResourceLoaderBinary::load() { path = remaps[path]; } - if (path.find("://") == -1 && path.is_relative_path()) { + if (!path.contains("://") && path.is_relative_path()) { // path is relative to file being loaded, so convert to a resource path path = ProjectSettings::get_singleton()->localize_path(path.get_base_dir().plus_file(external_resources[i].path)); } @@ -678,11 +695,13 @@ Error ResourceLoaderBinary::load() { internal_resources.write[i].path = path; // Update path. } - if (cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) { - if (ResourceCache::has(path)) { + if (cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE && ResourceCache::has(path)) { + RES cached = ResourceCache::get(path); + if (cached.is_valid()) { //already loaded, don't do anything stage++; error = OK; + internal_index_cache[path] = cached; continue; } } @@ -727,7 +746,7 @@ Error ResourceLoaderBinary::load() { } res = RES(r); - if (path != String() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { + if (!path.is_empty() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it } r->set_scene_unique_id(id); @@ -770,7 +789,7 @@ Error ResourceLoaderBinary::load() { resource_cache.push_back(res); if (main) { - f->close(); + f.unref(); resource = res; resource->set_as_translation_remapped(translation_remapped); error = OK; @@ -785,13 +804,13 @@ void ResourceLoaderBinary::set_translation_remapped(bool p_remapped) { translation_remapped = p_remapped; } -static void save_ustring(FileAccess *f, const String &p_string) { +static void save_ustring(Ref<FileAccess> f, const String &p_string) { CharString utf8 = p_string.utf8(); f->store_32(utf8.length() + 1); f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); } -static String get_ustring(FileAccess *f) { +static String get_ustring(Ref<FileAccess> f) { int len = f->get_32(); Vector<char> str_buf; str_buf.resize(len); @@ -815,7 +834,7 @@ String ResourceLoaderBinary::get_unicode_string() { return s; } -void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) { +void ResourceLoaderBinary::get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types) { open(p_f, false, true); if (error) { return; @@ -829,7 +848,7 @@ void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dep dep = external_resources[i].path; } - if (p_add_types && external_resources[i].type != String()) { + if (p_add_types && !external_resources[i].type.is_empty()) { dep += "::" + external_resources[i].type; } @@ -837,7 +856,7 @@ void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dep } } -void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_keep_uuid_paths) { +void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p_keep_uuid_paths) { error = OK; f = p_f; @@ -845,11 +864,11 @@ void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_kee f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { // Compressed. - FileAccessCompressed *fac = memnew(FileAccessCompressed); + Ref<FileAccessCompressed> fac; + fac.instantiate(); error = fac->open_after_magic(f); if (error != OK) { - memdelete(fac); - f->close(); + f.unref(); ERR_FAIL_MSG("Failed to open binary resource file: " + local_path + "."); } f = fac; @@ -857,7 +876,7 @@ void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_kee } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { // Not normal. error = ERR_FILE_UNRECOGNIZED; - f->close(); + f.unref(); ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + "."); } @@ -882,7 +901,7 @@ void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_kee print_bl("format: " + itos(ver_format)); if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - f->close(); + f.unref(); ERR_FAIL_MSG(vformat("File '%s' can't be loaded, as it uses a format version (%d) or engine version (%d.%d) which are not supported by your engine version (%s).", local_path, ver_format, ver_major, ver_minor, VERSION_BRANCH)); } @@ -899,6 +918,7 @@ void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_kee if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS) { using_uids = true; } + f->real_is_double = (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_REAL_T_IS_DOUBLE) != 0; if (using_uids) { uid = f->get_64(); @@ -958,12 +978,12 @@ void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_kee if (f->eof_reached()) { error = ERR_FILE_CORRUPT; - f->close(); + f.unref(); ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + "."); } } -String ResourceLoaderBinary::recognize(FileAccess *p_f) { +String ResourceLoaderBinary::recognize(Ref<FileAccess> p_f) { error = OK; f = p_f; @@ -971,11 +991,11 @@ String ResourceLoaderBinary::recognize(FileAccess *p_f) { f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { // Compressed. - FileAccessCompressed *fac = memnew(FileAccessCompressed); + Ref<FileAccessCompressed> fac; + fac.instantiate(); error = fac->open_after_magic(f); if (error != OK) { - memdelete(fac); - f->close(); + f.unref(); return ""; } f = fac; @@ -983,7 +1003,7 @@ String ResourceLoaderBinary::recognize(FileAccess *p_f) { } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { // Not normal. error = ERR_FILE_UNRECOGNIZED; - f->close(); + f.unref(); return ""; } @@ -997,7 +1017,7 @@ String ResourceLoaderBinary::recognize(FileAccess *p_f) { uint32_t ver_format = f->get_32(); if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - f->close(); + f.unref(); return ""; } @@ -1006,19 +1026,13 @@ String ResourceLoaderBinary::recognize(FileAccess *p_f) { return type; } -ResourceLoaderBinary::~ResourceLoaderBinary() { - if (f) { - memdelete(f); - } -} - RES ResourceFormatLoaderBinary::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; } Error err; - FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot open file '" + p_path + "'."); @@ -1026,10 +1040,9 @@ RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_origi loader.cache_mode = p_cache_mode; loader.use_sub_threads = p_use_sub_threads; loader.progress = r_progress; - String path = p_original_path != "" ? p_original_path : p_path; + String path = !p_original_path.is_empty() ? p_original_path : p_path; loader.local_path = ProjectSettings::get_singleton()->localize_path(path); loader.res_path = loader.local_path; - //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); loader.open(f); err = loader.load(); @@ -1045,7 +1058,7 @@ RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_origi } void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - if (p_type == "") { + if (p_type.is_empty()) { get_recognized_extensions(p_extensions); return; } @@ -1077,23 +1090,20 @@ bool ResourceFormatLoaderBinary::handles_type(const String &p_type) const { } void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_MSG(!f, "Cannot open file '" + p_path + "'."); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file '" + p_path + "'."); ResourceLoaderBinary loader; loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); loader.res_path = loader.local_path; - //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); loader.get_dependencies(f, p_dependencies, p_add_types); } Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - //Error error=OK; + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file '" + p_path + "'."); - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_path + "'."); - - FileAccess *fw = nullptr; //=FileAccess::open(p_path+".depren"); + Ref<FileAccess> fw; String local_path = p_path.get_base_dir(); @@ -1101,36 +1111,26 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { // Compressed. - FileAccessCompressed *fac = memnew(FileAccessCompressed); + Ref<FileAccessCompressed> fac; + fac.instantiate(); Error err = fac->open_after_magic(f); - if (err != OK) { - memdelete(fac); - memdelete(f); - ERR_FAIL_V_MSG(err, "Cannot open file '" + p_path + "'."); - } + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); f = fac; - FileAccessCompressed *facw = memnew(FileAccessCompressed); + Ref<FileAccessCompressed> facw; + facw.instantiate(); facw->configure("RSCC"); err = facw->_open(p_path + ".depren", FileAccess::WRITE); - if (err) { - memdelete(fac); - memdelete(facw); - ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".depren'."); - } + ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".depren'."); fw = facw; } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { // Not normal. - memdelete(f); ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file '" + local_path + "'."); } else { fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE); - if (!fw) { - memdelete(f); - } - ERR_FAIL_COND_V_MSG(!fw, ERR_CANT_CREATE, "Cannot create file '" + p_path + ".depren'."); + ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + ".depren'."); uint8_t magic[4] = { 'R', 'S', 'R', 'C' }; fw->store_buffer(magic, 4); @@ -1153,12 +1153,12 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons uint32_t ver_format = f->get_32(); if (ver_format < FORMAT_VERSION_CAN_RENAME_DEPS) { - memdelete(f); - memdelete(fw); - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - da->remove(p_path + ".depren"); - memdelete(da); - //use the old approach + { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + da->remove(p_path + ".depren"); + } + + // Use the old approach. WARN_PRINT("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'."); @@ -1171,7 +1171,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); loader.res_path = loader.local_path; loader.remaps = p_map; - //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); loader.open(f); err = loader.load(); @@ -1184,8 +1183,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons } if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - memdelete(f); - memdelete(fw); ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("File '%s' can't be loaded, as it uses a format version (%d) or engine version (%d.%d) which are not supported by your engine version (%s).", local_path, ver_format, ver_major, ver_minor, VERSION_BRANCH)); @@ -1294,30 +1291,25 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons fw->seek(md_ofs); fw->store_64(importmd_ofs + size_diff); - memdelete(f); - memdelete(fw); - if (!all_ok) { return ERR_CANT_CREATE; } - DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); da->remove(p_path); da->rename(p_path + ".depren", p_path); - memdelete(da); return OK; } String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { return ""; //could not read } ResourceLoaderBinary loader; loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); loader.res_path = loader.local_path; - //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); String r = loader.recognize(f); return ClassDB::get_compatibility_remapped_class(r); } @@ -1328,15 +1320,14 @@ ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_pat return ResourceUID::INVALID_ID; } - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + if (f.is_null()) { return ResourceUID::INVALID_ID; //could not read } ResourceLoaderBinary loader; loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); loader.res_path = loader.local_path; - //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); loader.open(f, true); if (loader.error != OK) { return ResourceUID::INVALID_ID; //could not read @@ -1348,7 +1339,7 @@ ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_pat /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// -void ResourceFormatSaverBinaryInstance::_pad_buffer(FileAccess *f, int p_bytes) { +void ResourceFormatSaverBinaryInstance::_pad_buffer(Ref<FileAccess> f, int p_bytes) { int extra = 4 - (p_bytes % 4); if (extra < 4) { for (int i = 0; i < extra; i++) { @@ -1357,7 +1348,7 @@ void ResourceFormatSaverBinaryInstance::_pad_buffer(FileAccess *f, int p_bytes) } } -void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) { +void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) { switch (p_property.get_type()) { case Variant::NIL: { f->store_32(VARIANT_NIL); @@ -1476,7 +1467,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::TRANSFORM2D: { - f->store_32(VARIANT_MATRIX32); + f->store_32(VARIANT_TRANSFORM2D); Transform2D val = p_property; f->store_real(val.elements[0].x); f->store_real(val.elements[0].y); @@ -1487,7 +1478,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::BASIS: { - f->store_32(VARIANT_MATRIX3); + f->store_32(VARIANT_BASIS); Basis val = p_property; f->store_real(val.elements[0].x); f->store_real(val.elements[0].y); @@ -1501,7 +1492,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::TRANSFORM3D: { - f->store_32(VARIANT_TRANSFORM); + f->store_32(VARIANT_TRANSFORM3D); Transform3D val = p_property; f->store_real(val.basis.elements[0].x); f->store_real(val.basis.elements[0].y); @@ -1605,11 +1596,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia d.get_key_list(&keys); for (const Variant &E : keys) { - /* - if (!_check_type(dict[E])) - continue; - */ - write_variant(f, E, resource_map, external_resources, string_map); write_variant(f, d[E], resource_map, external_resources, string_map); } @@ -1625,7 +1611,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_BYTE_ARRAY: { - f->store_32(VARIANT_RAW_ARRAY); + f->store_32(VARIANT_PACKED_BYTE_ARRAY); Vector<uint8_t> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1635,7 +1621,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_INT32_ARRAY: { - f->store_32(VARIANT_INT32_ARRAY); + f->store_32(VARIANT_PACKED_INT32_ARRAY); Vector<int32_t> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1646,7 +1632,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_INT64_ARRAY: { - f->store_32(VARIANT_INT64_ARRAY); + f->store_32(VARIANT_PACKED_INT64_ARRAY); Vector<int64_t> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1657,7 +1643,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_FLOAT32_ARRAY: { - f->store_32(VARIANT_FLOAT32_ARRAY); + f->store_32(VARIANT_PACKED_FLOAT32_ARRAY); Vector<float> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1668,7 +1654,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_FLOAT64_ARRAY: { - f->store_32(VARIANT_FLOAT64_ARRAY); + f->store_32(VARIANT_PACKED_FLOAT64_ARRAY); Vector<double> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1679,7 +1665,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_STRING_ARRAY: { - f->store_32(VARIANT_STRING_ARRAY); + f->store_32(VARIANT_PACKED_STRING_ARRAY); Vector<String> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1690,7 +1676,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_VECTOR3_ARRAY: { - f->store_32(VARIANT_VECTOR3_ARRAY); + f->store_32(VARIANT_PACKED_VECTOR3_ARRAY); Vector<Vector3> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1703,7 +1689,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_VECTOR2_ARRAY: { - f->store_32(VARIANT_VECTOR2_ARRAY); + f->store_32(VARIANT_PACKED_VECTOR2_ARRAY); Vector<Vector2> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1715,7 +1701,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::PACKED_COLOR_ARRAY: { - f->store_32(VARIANT_COLOR_ARRAY); + f->store_32(VARIANT_PACKED_COLOR_ARRAY); Vector<Color> arr = p_property; int len = arr.size(); f->store_32(len); @@ -1821,14 +1807,14 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant } } -void ResourceFormatSaverBinaryInstance::save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len) { +void ResourceFormatSaverBinaryInstance::save_unicode_string(Ref<FileAccess> p_f, const String &p_string, bool p_bit_on_len) { CharString utf8 = p_string.utf8(); if (p_bit_on_len) { - f->store_32((utf8.length() + 1) | 0x80000000); + p_f->store_32((utf8.length() + 1) | 0x80000000); } else { - f->store_32(utf8.length() + 1); + p_f->store_32(utf8.length() + 1); } - f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); + p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); } int ResourceFormatSaverBinaryInstance::get_string_index(const String &p_string) { @@ -1844,15 +1830,13 @@ int ResourceFormatSaverBinaryInstance::get_string_index(const String &p_string) Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { Error err; + Ref<FileAccess> f; if (p_flags & ResourceSaver::FLAG_COMPRESS) { - FileAccessCompressed *fac = memnew(FileAccessCompressed); + Ref<FileAccessCompressed> fac; + fac.instantiate(); fac->configure("RSCC"); f = fac; err = fac->_open(p_path, FileAccess::WRITE); - if (err) { - memdelete(f); - } - } else { f = FileAccess::open(p_path, FileAccess::WRITE, &err); } @@ -1893,14 +1877,18 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p f->store_32(FORMAT_VERSION); if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { - f->close(); - memdelete(f); return ERR_CANT_CREATE; } save_unicode_string(f, p_resource->get_class()); f->store_64(0); //offset to import metadata - f->store_32(FORMAT_FLAG_NAMED_SCENE_IDS | FORMAT_FLAG_UIDS); + { + uint32_t format_flags = FORMAT_FLAG_NAMED_SCENE_IDS | FORMAT_FLAG_UIDS; +#ifdef REAL_T_IS_DOUBLE + format_flags |= FORMAT_FLAG_REAL_T_IS_DOUBLE; +#endif + f->store_32(format_flags); + } ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true); f->store_64(uid); for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) { @@ -1979,7 +1967,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p for (RES &r : saved_resources) { if (r->is_built_in()) { - if (r->get_scene_unique_id() != "") { + if (!r->get_scene_unique_id().is_empty()) { if (used_unique_ids.has(r->get_scene_unique_id())) { r->set_scene_unique_id(""); } else { @@ -1993,7 +1981,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p int res_index = 0; for (RES &r : saved_resources) { if (r->is_built_in()) { - if (r->get_scene_unique_id() == "") { + if (r->get_scene_unique_id().is_empty()) { String new_id; while (true) { @@ -2046,14 +2034,9 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p f->store_buffer((const uint8_t *)"RSRC", 4); //magic at end if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) { - f->close(); - memdelete(f); return ERR_CANT_CREATE; } - f->close(); - memdelete(f); - return OK; } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index a6e6d1848e..72a3c6751d 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -43,7 +43,7 @@ class ResourceLoaderBinary { Ref<Resource> resource; uint32_t ver_format = 0; - FileAccess *f = nullptr; + Ref<FileAccess> f; uint64_t importmd_ofs = 0; @@ -98,12 +98,11 @@ public: void set_translation_remapped(bool p_remapped); void set_remaps(const Map<String, String> &p_remaps) { remaps = p_remaps; } - void open(FileAccess *p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false); - String recognize(FileAccess *p_f); - void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types); + void open(Ref<FileAccess> p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false); + String recognize(Ref<FileAccess> p_f); + void get_dependencies(Ref<FileAccess> p_f, List<String> *p_dependencies, bool p_add_types); ResourceLoaderBinary() {} - ~ResourceLoaderBinary(); }; class ResourceFormatLoaderBinary : public ResourceFormatLoader { @@ -127,7 +126,6 @@ class ResourceFormatSaverBinaryInstance { bool skip_editor; bool big_endian; bool takeover_paths; - FileAccess *f; String magic; Set<RES> resource_set; @@ -155,20 +153,22 @@ class ResourceFormatSaverBinaryInstance { List<Property> properties; }; - static void _pad_buffer(FileAccess *f, int p_bytes); + static void _pad_buffer(Ref<FileAccess> f, int p_bytes); void _find_resources(const Variant &p_variant, bool p_main = false); - static void save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false); + static void save_unicode_string(Ref<FileAccess> f, const String &p_string, bool p_bit_on_len = false); int get_string_index(const String &p_string); public: enum { FORMAT_FLAG_NAMED_SCENE_IDS = 1, FORMAT_FLAG_UIDS = 2, + FORMAT_FLAG_REAL_T_IS_DOUBLE = 4, + // Amount of reserved 32-bit fields in resource header RESERVED_FIELDS = 11 }; Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - static void write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo()); + static void write_variant(Ref<FileAccess> f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo()); }; class ResourceFormatSaverBinary : public ResourceFormatSaver { diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index cd44c537a8..b4f73b3b25 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -40,9 +40,9 @@ bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceIm Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid) const { Error err; - FileAccess *f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); - if (!f) { + if (f.is_null()) { if (r_valid) { *r_valid = false; } @@ -70,16 +70,14 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); if (err == ERR_FILE_EOF) { - memdelete(f); return OK; } else if (err != OK) { ERR_PRINT("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text); - memdelete(f); return err; } - if (assign != String()) { - if (!path_found && assign.begins_with("path.") && r_path_and_type.path == String()) { + if (!assign.is_empty()) { + if (!path_found && assign.begins_with("path.") && r_path_and_type.path.is_empty()) { String feature = assign.get_slicec('.', 1); if (OS::get_singleton()->has_feature(feature)) { r_path_and_type.path = value; @@ -110,9 +108,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy } } - memdelete(f); - - if (r_path_and_type.path == String() || r_path_and_type.type == String()) { + if (r_path_and_type.path.is_empty() || r_path_and_type.type.is_empty()) { return ERR_FILE_CORRUPT; } return OK; @@ -158,7 +154,7 @@ void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extension } void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - if (p_type == "") { + if (p_type.is_empty()) { get_recognized_extensions(p_extensions); return; } @@ -167,7 +163,7 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_ for (int i = 0; i < importers.size(); i++) { String res_type = importers[i]->get_resource_type(); - if (res_type == String()) { + if (res_type.is_empty()) { continue; } @@ -246,7 +242,7 @@ int ResourceFormatImporter::get_import_order(const String &p_path) const { bool ResourceFormatImporter::handles_type(const String &p_type) const { for (int i = 0; i < importers.size(); i++) { String res_type = importers[i]->get_resource_type(); - if (res_type == String()) { + if (res_type.is_empty()) { continue; } if (ClassDB::is_parent_class(res_type, p_type)) { @@ -270,9 +266,9 @@ String ResourceFormatImporter::get_internal_resource_path(const String &p_path) void ResourceFormatImporter::get_internal_resource_path_list(const String &p_path, List<String> *r_paths) { Error err; - FileAccess *f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); + Ref<FileAccess> f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); - if (!f) { + if (f.is_null()) { return; } @@ -292,15 +288,13 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); if (err == ERR_FILE_EOF) { - memdelete(f); return; } else if (err != OK) { ERR_PRINT("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text); - memdelete(f); return; } - if (assign != String()) { + if (!assign.is_empty()) { if (assign.begins_with("path.")) { r_paths->push_back(value); } else if (assign == "path") { @@ -310,7 +304,6 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat break; } } - memdelete(f); } String ResourceFormatImporter::get_import_group_file(const String &p_path) const { @@ -463,3 +456,12 @@ void ResourceImporter::_bind_methods() { BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT); BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE); } + +void ResourceFormatImporter::add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority) { + ERR_FAIL_COND(p_importer.is_null()); + if (p_first_priority) { + importers.insert(0, p_importer); + } else { + importers.push_back(p_importer); + } +} diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index cd583e2533..2fffc16ad8 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -80,9 +80,8 @@ public: String get_internal_resource_path(const String &p_path) const; void get_internal_resource_path_list(const String &p_path, List<String> *r_paths); - void add_importer(const Ref<ResourceImporter> &p_importer) { - importers.push_back(p_importer); - } + void add_importer(const Ref<ResourceImporter> &p_importer, bool p_first_priority = false); + void remove_importer(const Ref<ResourceImporter> &p_importer) { importers.erase(p_importer); } Ref<ResourceImporter> get_importer_by_name(const String &p_name) const; Ref<ResourceImporter> get_importer_by_extension(const String &p_extension) const; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 2198761c2a..fe9693aa20 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -52,7 +52,7 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_ String extension = p_path.get_extension(); List<String> extensions; - if (p_for_type == String()) { + if (p_for_type.is_empty()) { get_recognized_extensions(&extensions); } else { get_recognized_extensions_for_type(p_for_type, &extensions); @@ -96,7 +96,7 @@ ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) con } void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - if (p_type == "" || handles_type(p_type)) { + if (p_type.is_empty() || handles_type(p_type)) { get_recognized_extensions(p_extensions); } } @@ -194,7 +194,7 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c continue; } found = true; - RES res = loader[i]->load(p_path, p_original_path != String() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); + RES res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); if (res.is_null()) { continue; } @@ -206,7 +206,7 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path)); #ifdef TOOLS_ENABLED - FileAccessRef file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); + Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); ERR_FAIL_COND_V_MSG(!file_check->file_exists(p_path), RES(), "Resource file not found: " + p_path + "."); #endif @@ -289,7 +289,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String & thread_load_mutex->lock(); - if (p_source_resource != String()) { + if (!p_source_resource.is_empty()) { //must be loading from this resource if (!thread_load_tasks.has(p_source_resource)) { thread_load_mutex->unlock(); @@ -310,7 +310,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String & if (thread_load_tasks.has(local_path)) { thread_load_tasks[local_path].requests++; - if (p_source_resource != String()) { + if (!p_source_resource.is_empty()) { thread_load_tasks[p_source_resource].sub_tasks.insert(local_path); } thread_load_mutex->unlock(); @@ -354,7 +354,7 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String & ResourceCache::lock.read_unlock(); } - if (p_source_resource != String()) { + if (!p_source_resource.is_empty()) { thread_load_tasks[p_source_resource].sub_tasks.insert(local_path); } @@ -574,7 +574,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour bool xl_remapped = false; String path = _path_remap(local_path, &xl_remapped); - if (path == "") { + if (path.is_empty()) { ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed."); } @@ -672,10 +672,6 @@ int ResourceLoader::get_import_order(const String &p_path) { if (!loader[i]->recognize_path(local_path)) { continue; } - /* - if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) - continue; - */ return loader[i]->get_import_order(p_path); } @@ -690,10 +686,6 @@ String ResourceLoader::get_import_group_file(const String &p_path) { if (!loader[i]->recognize_path(local_path)) { continue; } - /* - if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) - continue; - */ return loader[i]->get_import_group_file(p_path); } @@ -708,10 +700,6 @@ bool ResourceLoader::is_import_valid(const String &p_path) { if (!loader[i]->recognize_path(local_path)) { continue; } - /* - if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) - continue; - */ return loader[i]->is_import_valid(p_path); } @@ -726,10 +714,6 @@ bool ResourceLoader::is_imported(const String &p_path) { if (!loader[i]->recognize_path(local_path)) { continue; } - /* - if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) - continue; - */ return loader[i]->is_imported(p_path); } @@ -744,10 +728,6 @@ void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_depe if (!loader[i]->recognize_path(local_path)) { continue; } - /* - if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) - continue; - */ loader[i]->get_dependencies(local_path, p_dependencies, p_add_types); } @@ -760,10 +740,6 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String if (!loader[i]->recognize_path(local_path)) { continue; } - /* - if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) - continue; - */ return loader[i]->rename_dependencies(local_path, p_map); } @@ -776,7 +752,7 @@ String ResourceLoader::get_resource_type(const String &p_path) { for (int i = 0; i < loader_count; i++) { String result = loader[i]->get_resource_type(local_path); - if (result != "") { + if (!result.is_empty()) { return result; } } @@ -806,38 +782,26 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem // To find the path of the remapped resource, we extract the locale name after // the last ':' to match the project locale. - // We also fall back in case of regional locales as done in TranslationServer::translate - // (e.g. 'ru_RU' -> 'ru' if the former has no specific mapping). String locale = TranslationServer::get_singleton()->get_locale(); ERR_FAIL_COND_V_MSG(locale.length() < 2, p_path, "Could not remap path '" + p_path + "' for translation as configured locale '" + locale + "' is invalid."); - String lang = TranslationServer::get_language_code(locale); Vector<String> &res_remaps = *translation_remaps.getptr(new_path); - bool near_match = false; + int best_score = 0; for (int i = 0; i < res_remaps.size(); i++) { int split = res_remaps[i].rfind(":"); if (split == -1) { continue; } - String l = res_remaps[i].substr(split + 1).strip_edges(); - if (l == locale) { // Exact match. + int score = TranslationServer::get_singleton()->compare_locales(locale, l); + if (score > 0 && score >= best_score) { new_path = res_remaps[i].left(split); - break; - } else if (near_match) { - continue; // Already found near match, keep going for potential exact match. - } - - // No exact match (e.g. locale 'ru_RU' but remap is 'ru'), let's look further - // for a near match (same language code, i.e. first 2 or 3 letters before - // regional code, if included). - if (TranslationServer::get_language_code(l) == lang) { - // Language code matches, that's a near match. Keep looking for exact match. - near_match = true; - new_path = res_remaps[i].left(split); - continue; + best_score = score; + if (score == 10) { + break; // Exact match, skip the rest. + } } } @@ -853,9 +817,8 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem if (new_path == p_path) { // Did not remap. // Try file remap. Error err; - FileAccess *f = FileAccess::open(p_path + ".remap", FileAccess::READ, &err); - - if (f) { + Ref<FileAccess> f = FileAccess::open(p_path + ".remap", FileAccess::READ, &err); + if (f.is_valid()) { VariantParser::StreamFile stream; stream.f = f; @@ -885,8 +848,6 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem break; } } - - memdelete(f); } } diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index f1d9815635..a3fdefa0f1 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 823b5f75b1..c883e8502f 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index fcde835dab..ebc3be91a1 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -71,6 +71,7 @@ class ResourceSaver { public: enum SaverFlags { + FLAG_NONE = 0, FLAG_RELATIVE_PATHS = 1, FLAG_BUNDLE_RESOURCES = 2, FLAG_CHANGE_PATH = 4, @@ -80,7 +81,7 @@ public: FLAG_REPLACE_SUBRESOURCE_PATHS = 64, }; - static Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); + static Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = (uint32_t)FLAG_NONE); static void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions); static void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front = false); static void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver); diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index b7d01712ff..515b7c710e 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -31,7 +31,7 @@ #include "resource_uid.h" #include "core/config/project_settings.h" -#include "core/crypto/crypto.h" +#include "core/crypto/crypto_core.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" @@ -71,9 +71,9 @@ ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const { for (uint32_t i = 6; i < l; i++) { uid *= base; uint32_t c = p_text[i]; - if (c >= 'a' && c <= 'z') { + if (is_ascii_lower_case(c)) { uid += c - 'a'; - } else if (c >= '0' && c <= '9') { + } else if (is_digit(c)) { uid += c - '0' + char_count; } else { return INVALID_ID; @@ -82,20 +82,14 @@ ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const { return ID(uid & 0x7FFFFFFFFFFFFFFF); } -ResourceUID::ID ResourceUID::create_id() const { - mutex.lock(); - if (crypto.is_null()) { - crypto = Ref<Crypto>(Crypto::create()); - } - mutex.unlock(); +ResourceUID::ID ResourceUID::create_id() { while (true) { - PackedByteArray bytes = crypto->generate_random_bytes(8); - ERR_FAIL_COND_V(bytes.size() != 8, INVALID_ID); - const uint64_t *ptr64 = (const uint64_t *)bytes.ptr(); - ID id = int64_t((*ptr64) & 0x7FFFFFFFFFFFFFFF); - mutex.lock(); + ID id = INVALID_ID; + MutexLock lock(mutex); + Error err = ((CryptoCore::RandomGenerator *)crypto)->get_random_bytes((uint8_t *)&id, sizeof(id)); + ERR_FAIL_COND_V(err != OK, INVALID_ID); + id &= 0x7FFFFFFFFFFFFFFF; bool exists = unique_ids.has(id); - mutex.unlock(); if (!exists) { return id; } @@ -141,12 +135,12 @@ void ResourceUID::remove_id(ID p_id) { Error ResourceUID::save_to_cache() { String cache_file = get_cache_file(); if (!FileAccess::exists(cache_file)) { - DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES); d->make_dir_recursive(String(cache_file).get_base_dir()); //ensure base dir exists } - FileAccessRef f = FileAccess::open(cache_file, FileAccess::WRITE); - if (!f) { + Ref<FileAccess> f = FileAccess::open(cache_file, FileAccess::WRITE); + if (f.is_null()) { return ERR_CANT_OPEN; } @@ -169,8 +163,8 @@ Error ResourceUID::save_to_cache() { } Error ResourceUID::load_from_cache() { - FileAccessRef f = FileAccess::open(get_cache_file(), FileAccess::READ); - if (!f) { + Ref<FileAccess> f = FileAccess::open(get_cache_file(), FileAccess::READ); + if (f.is_null()) { return ERR_CANT_OPEN; } @@ -207,12 +201,12 @@ Error ResourceUID::update_cache() { } MutexLock l(mutex); - FileAccess *f = nullptr; + Ref<FileAccess> f; for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) { if (!E.get().saved_to_cache) { - if (f == nullptr) { + if (f.is_null()) { f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append - if (!f) { + if (f.is_null()) { return ERR_CANT_OPEN; } f->seek_end(); @@ -226,11 +220,9 @@ Error ResourceUID::update_cache() { } } - if (f != nullptr) { + if (f.is_valid()) { f->seek(0); f->store_32(cache_entries); //update amount of entries - f->close(); - memdelete(f); } changed = false; @@ -261,6 +253,9 @@ ResourceUID *ResourceUID::singleton = nullptr; ResourceUID::ResourceUID() { ERR_FAIL_COND(singleton != nullptr); singleton = this; + crypto = memnew(CryptoCore::RandomGenerator); + ((CryptoCore::RandomGenerator *)crypto)->init(); } ResourceUID::~ResourceUID() { + memdelete((CryptoCore::RandomGenerator *)crypto); } diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h index 2f1bfdf243..0b7ffdf6d0 100644 --- a/core/io/resource_uid.h +++ b/core/io/resource_uid.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -35,7 +35,6 @@ #include "core/string/string_name.h" #include "core/templates/ordered_hash_map.h" -class Crypto; class ResourceUID : public Object { GDCLASS(ResourceUID, Object) public: @@ -47,7 +46,7 @@ public: static String get_cache_file(); private: - mutable Ref<Crypto> crypto; + void *crypto = nullptr; // CryptoCore::RandomGenerator (avoid including crypto_core.h) Mutex mutex; struct Cache { CharString cs; @@ -67,7 +66,7 @@ public: String id_to_text(ID p_id) const; ID text_to_id(const String &p_text) const; - ID create_id() const; + ID create_id(); bool has_id(ID p_id) const; void add_id(ID p_id, const String &p_path); void set_id(ID p_id, const String &p_path); diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 8ab025dda1..c65968ef03 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -98,7 +98,7 @@ Array StreamPeer::_get_partial_data(int p_bytes) { Error err = get_partial_data(&w[0], p_bytes, received); if (err != OK) { - data.resize(0); + data.clear(); } else if (received != data.size()) { data.resize(received); } @@ -563,7 +563,7 @@ Vector<uint8_t> StreamPeerBuffer::get_data_array() const { } void StreamPeerBuffer::clear() { - data.resize(0); + data.clear(); pointer = 0; } diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 89432951c5..4609e52aa2 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -39,7 +39,6 @@ class StreamPeer : public RefCounted { GDCLASS(StreamPeer, RefCounted); - OBJ_CATEGORY("Networking"); protected: static void _bind_methods(); diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 6f5a06474c..ea8435e587 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index 1df9ced08c..15f646d897 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 5b794274ca..6d5784a8ab 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index a2a7f447d8..bf49cc8a5f 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -38,7 +38,6 @@ class StreamPeerTCP : public StreamPeer { GDCLASS(StreamPeerTCP, StreamPeer); - OBJ_CATEGORY("Networking"); public: enum Status { diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp index 5e0c0390f9..f1e745cf36 100644 --- a/core/io/tcp_server.cpp +++ b/core/io/tcp_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index 10985a04d5..2d427818a0 100644 --- a/core/io/tcp_server.h +++ b/core/io/tcp_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 83d575cee8..ae1ad304d7 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -34,226 +34,293 @@ #include "core/string/translation.h" #include "core/string/translation_po.h" -RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { - enum Status { - STATUS_NONE, - STATUS_READING_ID, - STATUS_READING_STRING, - STATUS_READING_CONTEXT, - STATUS_READING_PLURAL, - }; - - Status status = STATUS_NONE; - - String msg_id; - String msg_str; - String msg_context; - Vector<String> msgs_plural; - String config; - +RES TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_error) { if (r_error) { *r_error = ERR_FILE_CORRUPT; } - Ref<TranslationPO> translation = Ref<TranslationPO>(memnew(TranslationPO)); - int line = 1; - int plural_forms = 0; - int plural_index = -1; - bool entered_context = false; - bool skip_this = false; - bool skip_next = false; - bool is_eof = false; const String path = f->get_path(); + Ref<TranslationPO> translation = Ref<TranslationPO>(memnew(TranslationPO)); + String config; - while (!is_eof) { - String l = f->get_line().strip_edges(); - is_eof = f->eof_reached(); - - // If we reached last line and it's not a content line, break, otherwise let processing that last loop - if (is_eof && l.is_empty()) { - if (status == STATUS_READING_ID || status == STATUS_READING_CONTEXT || (status == STATUS_READING_PLURAL && plural_index != plural_forms - 1)) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading PO file at: " + path + ":" + itos(line)); - } else { - break; - } - } - - if (l.begins_with("msgctxt")) { - if (status != STATUS_READING_STRING && status != STATUS_READING_PLURAL) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line)); + uint32_t magic = f->get_32(); + if (magic == 0x950412de) { + // Load binary MO file. + + uint16_t version_maj = f->get_16(); + uint16_t version_min = f->get_16(); + ERR_FAIL_COND_V_MSG(version_maj > 1, RES(), vformat("Unsupported MO file %s, version %d.%d.", path, version_maj, version_min)); + + uint32_t num_strings = f->get_32(); + uint32_t id_table_offset = f->get_32(); + uint32_t trans_table_offset = f->get_32(); + + // Read string tables. + for (uint32_t i = 0; i < num_strings; i++) { + String msg_id; + String msg_id_plural; + String msg_context; + + // Read id strings and context. + { + Vector<uint8_t> data; + f->seek(id_table_offset + i * 8); + uint32_t str_start = 0; + uint32_t str_len = f->get_32(); + uint32_t str_offset = f->get_32(); + + data.resize(str_len + 1); + f->seek(str_offset); + f->get_buffer(data.ptrw(), str_len); + data.write[str_len] = 0; + + bool is_plural = false; + for (uint32_t j = 0; j < str_len + 1; j++) { + if (data[j] == 0x04) { + msg_context.parse_utf8((const char *)data.ptr(), j); + str_start = j + 1; + } + if (data[j] == 0x00) { + if (is_plural) { + msg_id_plural.parse_utf8((const char *)(data.ptr() + str_start), j - str_start); + } else { + msg_id.parse_utf8((const char *)(data.ptr() + str_start), j - str_start); + is_plural = true; + } + str_start = j + 1; + } + } } - // In PO file, "msgctxt" appears before "msgid". If we encounter a "msgctxt", we add what we have read - // and set "entered_context" to true to prevent adding twice. - if (!skip_this && msg_id != "") { - if (status == STATUS_READING_STRING) { - translation->add_message(msg_id, msg_str, msg_context); - } else if (status == STATUS_READING_PLURAL) { - if (plural_index != plural_forms - 1) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + // Read translated strings. + { + Vector<uint8_t> data; + f->seek(trans_table_offset + i * 8); + uint32_t str_len = f->get_32(); + uint32_t str_offset = f->get_32(); + + data.resize(str_len + 1); + f->seek(str_offset); + f->get_buffer(data.ptrw(), str_len); + data.write[str_len] = 0; + + if (msg_id.is_empty()) { + config = String::utf8((const char *)data.ptr(), str_len); + // Record plural rule. + int p_start = config.find("Plural-Forms"); + if (p_start != -1) { + int p_end = config.find("\n", p_start); + translation->set_plural_rule(config.substr(p_start, p_end - p_start)); + } + } else { + uint32_t str_start = 0; + Vector<String> plural_msg; + for (uint32_t j = 0; j < str_len + 1; j++) { + if (data[j] == 0x00) { + if (msg_id_plural.is_empty()) { + translation->add_message(msg_id, String::utf8((const char *)(data.ptr() + str_start), j - str_start), msg_context); + } else { + plural_msg.push_back(String::utf8((const char *)(data.ptr() + str_start), j - str_start)); + } + str_start = j + 1; + } + } + if (!plural_msg.is_empty()) { + translation->add_plural_message(msg_id, plural_msg, msg_context); } - translation->add_plural_message(msg_id, msgs_plural, msg_context); } } - msg_context = ""; - l = l.substr(7, l.length()).strip_edges(); - status = STATUS_READING_CONTEXT; - entered_context = true; } - if (l.begins_with("msgid_plural")) { - if (plural_forms == 0) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: " + path + ":" + itos(line)); - } else if (status != STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: " + path + ":" + itos(line)); - } - // We don't record the message in "msgid_plural" itself as tr_n(), TTRN(), RTRN() interfaces provide the plural string already. - // We just have to reset variables related to plurals for "msgstr[]" later on. - l = l.substr(12, l.length()).strip_edges(); - plural_index = -1; - msgs_plural.clear(); - msgs_plural.resize(plural_forms); - status = STATUS_READING_PLURAL; - } else if (l.begins_with("msgid")) { - if (status == STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line)); + } else { + // Try to load as text PO file. + f->seek(0); + + enum Status { + STATUS_NONE, + STATUS_READING_ID, + STATUS_READING_STRING, + STATUS_READING_CONTEXT, + STATUS_READING_PLURAL, + }; + + Status status = STATUS_NONE; + + String msg_id; + String msg_str; + String msg_context; + Vector<String> msgs_plural; + + if (r_error) { + *r_error = ERR_FILE_CORRUPT; + } + + int line = 1; + int plural_forms = 0; + int plural_index = -1; + bool entered_context = false; + bool skip_this = false; + bool skip_next = false; + bool is_eof = false; + + while (!is_eof) { + String l = f->get_line().strip_edges(); + is_eof = f->eof_reached(); + + // If we reached last line and it's not a content line, break, otherwise let processing that last loop + if (is_eof && l.is_empty()) { + if (status == STATUS_READING_ID || status == STATUS_READING_CONTEXT || (status == STATUS_READING_PLURAL && plural_index != plural_forms - 1)) { + ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading PO file at: " + path + ":" + itos(line)); + } else { + break; + } } - if (msg_id != "") { - if (!skip_this && !entered_context) { + if (l.begins_with("msgctxt")) { + ERR_FAIL_COND_V_MSG(status != STATUS_READING_STRING && status != STATUS_READING_PLURAL, RES(), "Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line)); + + // In PO file, "msgctxt" appears before "msgid". If we encounter a "msgctxt", we add what we have read + // and set "entered_context" to true to prevent adding twice. + if (!skip_this && !msg_id.is_empty()) { if (status == STATUS_READING_STRING) { translation->add_message(msg_id, msg_str, msg_context); } else if (status == STATUS_READING_PLURAL) { - if (plural_index != plural_forms - 1) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); - } + ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); translation->add_plural_message(msg_id, msgs_plural, msg_context); } } - } else if (config == "") { - config = msg_str; - // Record plural rule. - int p_start = config.find("Plural-Forms"); - if (p_start != -1) { - int p_end = config.find("\n", p_start); - translation->set_plural_rule(config.substr(p_start, p_end - p_start)); - plural_forms = translation->get_plural_forms(); - } - } - - l = l.substr(5, l.length()).strip_edges(); - status = STATUS_READING_ID; - // If we did not encounter msgctxt, we reset context to empty to reset it. - if (!entered_context) { msg_context = ""; - } - msg_id = ""; - msg_str = ""; - skip_this = skip_next; - skip_next = false; - entered_context = false; - } - - if (l.begins_with("msgstr[")) { - if (status != STATUS_READING_PLURAL) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: " + path + ":" + itos(line)); - } - plural_index++; // Increment to add to the next slot in vector msgs_plural. - l = l.substr(9, l.length()).strip_edges(); - } else if (l.begins_with("msgstr")) { - if (status != STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line)); + l = l.substr(7, l.length()).strip_edges(); + status = STATUS_READING_CONTEXT; + entered_context = true; } - l = l.substr(6, l.length()).strip_edges(); - status = STATUS_READING_STRING; - } + if (l.begins_with("msgid_plural")) { + if (plural_forms == 0) { + ERR_FAIL_V_MSG(RES(), "PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: " + path + ":" + itos(line)); + } else if (status != STATUS_READING_ID) { + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: " + path + ":" + itos(line)); + } + // We don't record the message in "msgid_plural" itself as tr_n(), TTRN(), RTRN() interfaces provide the plural string already. + // We just have to reset variables related to plurals for "msgstr[]" later on. + l = l.substr(12, l.length()).strip_edges(); + plural_index = -1; + msgs_plural.clear(); + msgs_plural.resize(plural_forms); + status = STATUS_READING_PLURAL; + } else if (l.begins_with("msgid")) { + ERR_FAIL_COND_V_MSG(status == STATUS_READING_ID, RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line)); + + if (!msg_id.is_empty()) { + if (!skip_this && !entered_context) { + if (status == STATUS_READING_STRING) { + translation->add_message(msg_id, msg_str, msg_context); + } else if (status == STATUS_READING_PLURAL) { + ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + translation->add_plural_message(msg_id, msgs_plural, msg_context); + } + } + } else if (config.is_empty()) { + config = msg_str; + // Record plural rule. + int p_start = config.find("Plural-Forms"); + if (p_start != -1) { + int p_end = config.find("\n", p_start); + translation->set_plural_rule(config.substr(p_start, p_end - p_start)); + plural_forms = translation->get_plural_forms(); + } + } - if (l == "" || l.begins_with("#")) { - if (l.find("fuzzy") != -1) { - skip_next = true; + l = l.substr(5, l.length()).strip_edges(); + status = STATUS_READING_ID; + // If we did not encounter msgctxt, we reset context to empty to reset it. + if (!entered_context) { + msg_context = ""; + } + msg_id = ""; + msg_str = ""; + skip_this = skip_next; + skip_next = false; + entered_context = false; } - line++; - continue; // Nothing to read or comment. - } - - if (!l.begins_with("\"") || status == STATUS_NONE) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line)); - } - l = l.substr(1, l.length()); - // Find final quote, ignoring escaped ones (\"). - // The escape_next logic is necessary to properly parse things like \\" - // where the backslash is the one being escaped, not the quote. - int end_pos = -1; - bool escape_next = false; - for (int i = 0; i < l.length(); i++) { - if (l[i] == '\\' && !escape_next) { - escape_next = true; - continue; + if (l.begins_with("msgstr[")) { + ERR_FAIL_COND_V_MSG(status != STATUS_READING_PLURAL, RES(), "Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: " + path + ":" + itos(line)); + plural_index++; // Increment to add to the next slot in vector msgs_plural. + l = l.substr(9, l.length()).strip_edges(); + } else if (l.begins_with("msgstr")) { + ERR_FAIL_COND_V_MSG(status != STATUS_READING_ID, RES(), "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line)); + l = l.substr(6, l.length()).strip_edges(); + status = STATUS_READING_STRING; } - if (l[i] == '"' && !escape_next) { - end_pos = i; - break; + if (l.is_empty() || l.begins_with("#")) { + if (l.contains("fuzzy")) { + skip_next = true; + } + line++; + continue; // Nothing to read or comment. } - escape_next = false; - } + ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line)); + + l = l.substr(1, l.length()); + // Find final quote, ignoring escaped ones (\"). + // The escape_next logic is necessary to properly parse things like \\" + // where the backslash is the one being escaped, not the quote. + int end_pos = -1; + bool escape_next = false; + for (int i = 0; i < l.length(); i++) { + if (l[i] == '\\' && !escape_next) { + escape_next = true; + continue; + } - if (end_pos == -1) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line)); - } + if (l[i] == '"' && !escape_next) { + end_pos = i; + break; + } - l = l.substr(0, end_pos); - l = l.c_unescape(); - - if (status == STATUS_READING_ID) { - msg_id += l; - } else if (status == STATUS_READING_STRING) { - msg_str += l; - } else if (status == STATUS_READING_CONTEXT) { - msg_context += l; - } else if (status == STATUS_READING_PLURAL && plural_index >= 0) { - msgs_plural.write[plural_index] = msgs_plural[plural_index] + l; - } + escape_next = false; + } - line++; - } + ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line)); - memdelete(f); + l = l.substr(0, end_pos); + l = l.c_unescape(); - // Add the last set of data from last iteration. - if (status == STATUS_READING_STRING) { - if (msg_id != "") { - if (!skip_this) { - translation->add_message(msg_id, msg_str, msg_context); + if (status == STATUS_READING_ID) { + msg_id += l; + } else if (status == STATUS_READING_STRING) { + msg_str += l; + } else if (status == STATUS_READING_CONTEXT) { + msg_context += l; + } else if (status == STATUS_READING_PLURAL && plural_index >= 0) { + ERR_FAIL_COND_V_MSG(plural_index >= plural_forms, RES(), "Unexpected plural form while parsing: " + path + ":" + itos(line)); + msgs_plural.write[plural_index] = msgs_plural[plural_index] + l; } - } else if (config == "") { - config = msg_str; + + line++; } - } else if (status == STATUS_READING_PLURAL) { - if (!skip_this && msg_id != "") { - if (plural_index != plural_forms - 1) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + + // Add the last set of data from last iteration. + if (status == STATUS_READING_STRING) { + if (!msg_id.is_empty()) { + if (!skip_this) { + translation->add_message(msg_id, msg_str, msg_context); + } + } else if (config.is_empty()) { + config = msg_str; + } + } else if (status == STATUS_READING_PLURAL) { + if (!skip_this && !msg_id.is_empty()) { + ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + translation->add_plural_message(msg_id, msgs_plural, msg_context); } - translation->add_plural_message(msg_id, msgs_plural, msg_context); } } - ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + path + "."); + ERR_FAIL_COND_V_MSG(config.is_empty(), RES(), "No config found in file: " + path + "."); Vector<String> configs = config.split("\n"); for (int i = 0; i < configs.size(); i++) { @@ -282,14 +349,15 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat *r_error = ERR_CANT_OPEN; } - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot open file '" + p_path + "'."); + Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); + ERR_FAIL_COND_V_MSG(f.is_null(), RES(), "Cannot open file '" + p_path + "'."); return load_translation(f, r_error); } void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("po"); + p_extensions->push_back("mo"); } bool TranslationLoaderPO::handles_type(const String &p_type) const { @@ -297,7 +365,7 @@ bool TranslationLoaderPO::handles_type(const String &p_type) const { } String TranslationLoaderPO::get_resource_type(const String &p_path) const { - if (p_path.get_extension().to_lower() == "po") { + if (p_path.get_extension().to_lower() == "po" || p_path.get_extension().to_lower() == "mo") { return "Translation"; } return ""; diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index c52820e60d..7da361cf24 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -37,7 +37,7 @@ class TranslationLoaderPO : public ResourceFormatLoader { public: - static RES load_translation(FileAccess *f, Error *r_error = nullptr); + static RES load_translation(Ref<FileAccess> f, Error *r_error = nullptr); virtual RES 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; diff --git a/core/io/udp_server.cpp b/core/io/udp_server.cpp index 27a1cab721..4934e3a7b6 100644 --- a/core/io/udp_server.cpp +++ b/core/io/udp_server.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/udp_server.h b/core/io/udp_server.h index e49a559c51..47f06b2490 100644 --- a/core/io/udp_server.h +++ b/core/io/udp_server.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -43,7 +43,7 @@ protected: }; struct Peer { - PacketPeerUDP *peer; + PacketPeerUDP *peer = nullptr; IPAddress ip; uint16_t port = 0; diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index 938d93a01b..7b43193f47 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -472,7 +472,7 @@ Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) { Error XMLParser::open(const String &p_path) { Error err; - FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &err); + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'."); @@ -488,8 +488,6 @@ Error XMLParser::open(const String &p_path) { data[length] = 0; P = data; - memdelete(file); - return OK; } diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h index 1113cce715..da14ee8eae 100644 --- a/core/io/xml_parser.h +++ b/core/io/xml_parser.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index 24808cc8d6..2cc844b628 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -30,73 +30,69 @@ #include "zip_io.h" -void *zipio_open(void *data, const char *p_fname, int mode) { - FileAccess *&f = *(FileAccess **)data; +void *zipio_open(voidpf opaque, const char *p_fname, int mode) { + ZipIOData *zd = (ZipIOData *)opaque; String fname; fname.parse_utf8(p_fname); if (mode & ZLIB_FILEFUNC_MODE_WRITE) { - f = FileAccess::open(fname, FileAccess::WRITE); + zd->f = FileAccess::open(fname, FileAccess::WRITE); } else { - f = FileAccess::open(fname, FileAccess::READ); + zd->f = FileAccess::open(fname, FileAccess::READ); } - if (!f) { + if (zd->f.is_null()) { return nullptr; } - return data; + return opaque; } -uLong zipio_read(void *data, void *fdata, void *buf, uLong size) { - FileAccess *f = *(FileAccess **)data; - return f->get_buffer((uint8_t *)buf, size); +uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size) { + ZipIOData *zd = (ZipIOData *)opaque; + return zd->f->get_buffer((uint8_t *)buf, size); } uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { - FileAccess *f = *(FileAccess **)opaque; - f->store_buffer((uint8_t *)buf, size); + ZipIOData *zd = (ZipIOData *)opaque; + zd->f->store_buffer((uint8_t *)buf, size); return size; } long zipio_tell(voidpf opaque, voidpf stream) { - FileAccess *f = *(FileAccess **)opaque; - return f->get_position(); + ZipIOData *zd = (ZipIOData *)opaque; + return zd->f->get_position(); } long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - FileAccess *f = *(FileAccess **)opaque; + ZipIOData *zd = (ZipIOData *)opaque; uint64_t pos = offset; switch (origin) { case ZLIB_FILEFUNC_SEEK_CUR: - pos = f->get_position() + offset; + pos = zd->f->get_position() + offset; break; case ZLIB_FILEFUNC_SEEK_END: - pos = f->get_length() + offset; + pos = zd->f->get_length() + offset; break; default: break; } - f->seek(pos); + zd->f->seek(pos); return 0; } int zipio_close(voidpf opaque, voidpf stream) { - FileAccess *&f = *(FileAccess **)opaque; - if (f) { - f->close(); - memdelete(f); - f = nullptr; - } + ZipIOData *zd = (ZipIOData *)opaque; + memdelete(zd); return 0; } int zipio_testerror(voidpf opaque, voidpf stream) { - FileAccess *f = *(FileAccess **)opaque; - return (f && f->get_error() != OK) ? 1 : 0; + ZipIOData *zd = (ZipIOData *)opaque; + return (zd->f.is_valid() && zd->f->get_error() != OK) ? 1 : 0; } voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { @@ -109,9 +105,9 @@ void zipio_free(voidpf opaque, voidpf address) { memfree(address); } -zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { +zlib_filefunc_def zipio_create_io() { zlib_filefunc_def io; - io.opaque = p_file; + io.opaque = (void *)memnew(ZipIOData); io.zopen_file = zipio_open; io.zread_file = zipio_read; io.zwrite_file = zipio_write; diff --git a/core/io/zip_io.h b/core/io/zip_io.h index 776473bfa1..3bcd1f830d 100644 --- a/core/io/zip_io.h +++ b/core/io/zip_io.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* 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 */ @@ -39,8 +39,12 @@ #include "thirdparty/minizip/unzip.h" #include "thirdparty/minizip/zip.h" -void *zipio_open(void *data, const char *p_fname, int mode); -uLong zipio_read(void *data, void *fdata, void *buf, uLong size); +struct ZipIOData { + Ref<FileAccess> f; +}; + +void *zipio_open(voidpf opaque, const char *p_fname, int mode); +uLong zipio_read(voidpf opaque, voidpf stream, void *buf, uLong size); uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size); long zipio_tell(voidpf opaque, voidpf stream); @@ -53,6 +57,6 @@ int zipio_testerror(voidpf opaque, voidpf stream); voidpf zipio_alloc(voidpf opaque, uInt items, uInt size); void zipio_free(voidpf opaque, voidpf address); -zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file); +zlib_filefunc_def zipio_create_io(); #endif // ZIP_IO_H |