diff options
Diffstat (limited to 'core/io')
47 files changed, 1560 insertions, 99 deletions
diff --git a/core/io/config_file.h b/core/io/config_file.h index 1b28257c60..dbba43ace5 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -31,13 +31,13 @@ #ifndef CONFIG_FILE_H #define CONFIG_FILE_H -#include "core/object/reference.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" +#include "core/object/ref_counted.h" #include "core/templates/ordered_hash_map.h" #include "core/variant/variant_parser.h" -class ConfigFile : public Reference { - GDCLASS(ConfigFile, Reference); +class ConfigFile : public RefCounted { + GDCLASS(ConfigFile, RefCounted); OrderedHashMap<String, OrderedHashMap<String, Variant>> values; diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp new file mode 100644 index 0000000000..dfba00067f --- /dev/null +++ b/core/io/dir_access.cpp @@ -0,0 +1,418 @@ +/*************************************************************************/ +/* dir_access.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "dir_access.h" + +#include "core/config/project_settings.h" +#include "core/io/file_access.h" +#include "core/os/memory.h" +#include "core/os/os.h" + +String DirAccess::_get_root_path() const { + switch (_access_type) { + case ACCESS_RESOURCES: + return ProjectSettings::get_singleton()->get_resource_path(); + case ACCESS_USERDATA: + return OS::get_singleton()->get_user_data_dir(); + default: + return ""; + } +} + +String DirAccess::_get_root_string() const { + switch (_access_type) { + case ACCESS_RESOURCES: + return "res://"; + case ACCESS_USERDATA: + return "user://"; + default: + return ""; + } +} + +int DirAccess::get_current_drive() { + String path = get_current_dir().to_lower(); + for (int i = 0; i < get_drive_count(); i++) { + String d = get_drive(i).to_lower(); + if (path.begins_with(d)) { + return i; + } + } + + return 0; +} + +bool DirAccess::drives_are_shortcuts() { + return false; +} + +static Error _erase_recursive(DirAccess *da) { + List<String> dirs; + List<String> files; + + da->list_dir_begin(); + String n = da->get_next(); + while (n != String()) { + if (n != "." && n != "..") { + if (da->current_is_dir()) { + dirs.push_back(n); + } else { + files.push_back(n); + } + } + + n = da->get_next(); + } + + da->list_dir_end(); + + for (List<String>::Element *E = dirs.front(); E; E = E->next()) { + Error err = da->change_dir(E->get()); + if (err == OK) { + err = _erase_recursive(da); + if (err) { + da->change_dir(".."); + return err; + } + err = da->change_dir(".."); + if (err) { + return err; + } + err = da->remove(da->get_current_dir().plus_file(E->get())); + if (err) { + return err; + } + } else { + return err; + } + } + + for (List<String>::Element *E = files.front(); E; E = E->next()) { + Error err = da->remove(da->get_current_dir().plus_file(E->get())); + if (err) { + return err; + } + } + + return OK; +} + +Error DirAccess::erase_contents_recursive() { + return _erase_recursive(this); +} + +Error DirAccess::make_dir_recursive(String p_dir) { + if (p_dir.length() < 1) { + return OK; + } + + String full_dir; + + if (p_dir.is_rel_path()) { + //append current + full_dir = get_current_dir().plus_file(p_dir); + + } else { + full_dir = 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.begins_with("/")) { + base = "/"; + } else if (full_dir.find(":/") != -1) { + base = full_dir.substr(0, full_dir.find(":/") + 2); + } else { + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + + full_dir = full_dir.replace_first(base, "").simplify_path(); + + Vector<String> subdirs = full_dir.split("/"); + + String curpath = base; + for (int i = 0; i < subdirs.size(); i++) { + curpath = curpath.plus_file(subdirs[i]); + Error err = make_dir(curpath); + if (err != OK && err != ERR_ALREADY_EXISTS) { + ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath); + } + } + + return OK; +} + +String DirAccess::fix_path(String p_path) const { + switch (_access_type) { + case ACCESS_RESOURCES: { + if (ProjectSettings::get_singleton()) { + if (p_path.begins_with("res://")) { + String resource_path = ProjectSettings::get_singleton()->get_resource_path(); + if (resource_path != "") { + return p_path.replace_first("res:/", resource_path); + } + return p_path.replace_first("res://", ""); + } + } + + } break; + case ACCESS_USERDATA: { + if (p_path.begins_with("user://")) { + String data_dir = OS::get_singleton()->get_user_data_dir(); + if (data_dir != "") { + return p_path.replace_first("user:/", data_dir); + } + return p_path.replace_first("user://", ""); + } + + } break; + case ACCESS_FILESYSTEM: { + return p_path; + } break; + case ACCESS_MAX: + break; // Can't happen, but silences warning + } + + return p_path; +} + +DirAccess::CreateFunc DirAccess::create_func[ACCESS_MAX] = { nullptr, nullptr, nullptr }; + +DirAccess *DirAccess::create_for_path(const String &p_path) { + DirAccess *da = nullptr; + if (p_path.begins_with("res://")) { + da = create(ACCESS_RESOURCES); + } else if (p_path.begins_with("user://")) { + da = create(ACCESS_USERDATA); + } else { + da = create(ACCESS_FILESYSTEM); + } + + 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 + "'."); + 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) { + da->_access_type = p_access; + } + + return da; +} + +String DirAccess::get_full_path(const String &p_path, AccessType p_access) { + DirAccess *d = DirAccess::create(p_access); + if (!d) { + 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; + } + + 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; + } + + 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) { + err = OK; + } + } + + memdelete(fsrc); + memdelete(fdst); + + return err; +} + +// Changes dir for the current scope, returning back to the original dir +// when scope exits +class DirChanger { + DirAccess *da; + String original_dir; + +public: + DirChanger(DirAccess *p_da, String p_dir) : + da(p_da), + original_dir(p_da->get_current_dir()) { + p_da->change_dir(p_dir); + } + + ~DirChanger() { + da->change_dir(original_dir); + } +}; + +Error DirAccess::_copy_dir(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()) { + 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); + } else if (current_is_dir()) { + dirs.push_back(n); + } else { + const String &rel_path = n; + if (!n.is_rel_path()) { + list_dir_end(); + return ERR_BUG; + } + Error err = copy(get_current_dir().plus_file(n), p_to + rel_path, p_chmod_flags); + if (err) { + list_dir_end(); + return err; + } + } + } + + n = get_next(); + } + + list_dir_end(); + + for (List<String>::Element *E = dirs.front(); E; E = E->next()) { + String rel_path = E->get(); + String target_dir = p_to + rel_path; + if (!p_target_da->dir_exists(target_dir)) { + Error err = p_target_da->make_dir(target_dir); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'."); + } + + Error err = change_dir(E->get()); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'."); + + err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links); + if (err) { + change_dir(".."); + ERR_FAIL_V_MSG(err, "Failed to copy recursively."); + } + err = change_dir(".."); + ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back."); + } + + return OK; +} + +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 + "'."); + + 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 + "'."); + } + + if (!p_to.ends_with("/")) { + p_to = p_to + "/"; + } + + 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; +} diff --git a/core/io/dir_access.h b/core/io/dir_access.h new file mode 100644 index 0000000000..16154a4850 --- /dev/null +++ b/core/io/dir_access.h @@ -0,0 +1,147 @@ +/*************************************************************************/ +/* dir_access.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef DIR_ACCESS_H +#define DIR_ACCESS_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 { +public: + enum AccessType { + ACCESS_RESOURCES, + ACCESS_USERDATA, + ACCESS_FILESYSTEM, + ACCESS_MAX + }; + + typedef 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); + +protected: + String _get_root_path() const; + String _get_root_string() const; + + String fix_path(String p_path) const; + + template <class T> + static DirAccess *_create_builtin() { + return memnew(T); + } + +public: + virtual Error list_dir_begin() = 0; ///< This starts dir listing + virtual String get_next() = 0; + virtual bool current_is_dir() const = 0; + virtual bool current_is_hidden() const = 0; + + virtual void list_dir_end() = 0; ///< + + virtual int get_drive_count() = 0; + virtual String get_drive(int p_drive) = 0; + virtual int get_current_drive(); + 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 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! + + virtual bool file_exists(String p_file) = 0; + virtual bool dir_exists(String p_dir) = 0; + virtual bool is_readable(String p_dir) { return true; }; + virtual bool is_writable(String p_dir) { return true; }; + static bool exists(String p_dir); + virtual uint64_t get_space_left() = 0; + + Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1, bool p_copy_links = false); + virtual Error copy(String p_from, String p_to, int p_chmod_flags = -1); + virtual Error rename(String p_from, String p_to) = 0; + virtual Error remove(String p_name) = 0; + + virtual bool is_link(String p_file) = 0; + virtual String read_link(String p_file) = 0; + virtual Error create_link(String p_source, String p_target) = 0; + + // 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); + 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 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); + + 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 288b2efe0e..655fb18535 100644 --- a/core/io/dtls_server.cpp +++ b/core/io/dtls_server.cpp @@ -31,7 +31,7 @@ #include "dtls_server.h" #include "core/config/project_settings.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" DTLSServer *(*DTLSServer::_create)() = nullptr; bool DTLSServer::available = false; diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h index 92b6caf508..02a32533e1 100644 --- a/core/io/dtls_server.h +++ b/core/io/dtls_server.h @@ -34,8 +34,8 @@ #include "core/io/net_socket.h" #include "core/io/packet_peer_dtls.h" -class DTLSServer : public Reference { - GDCLASS(DTLSServer, Reference); +class DTLSServer : public RefCounted { + GDCLASS(DTLSServer, RefCounted); protected: static DTLSServer *(*_create)(); diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp new file mode 100644 index 0000000000..d21c0bd9a2 --- /dev/null +++ b/core/io/file_access.cpp @@ -0,0 +1,676 @@ +/*************************************************************************/ +/* file_access.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "file_access.h" + +#include "core/config/project_settings.h" +#include "core/crypto/crypto_core.h" +#include "core/io/file_access_pack.h" +#include "core/io/marshalls.h" +#include "core/os/os.h" + +FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { nullptr, nullptr }; + +FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr; + +bool FileAccess::backup_save = false; + +FileAccess *FileAccess::create(AccessType p_access) { + ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr); + + FileAccess *ret = create_func[p_access](); + ret->_set_access_type(p_access); + return ret; +} + +bool FileAccess::exists(const String &p_name) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_name)) { + return true; + } + + FileAccess *f = open(p_name, READ); + if (!f) { + return false; + } + memdelete(f); + return true; +} + +void FileAccess::_set_access_type(AccessType p_access) { + _access_type = p_access; +} + +FileAccess *FileAccess::create_for_path(const String &p_path) { + FileAccess *ret = nullptr; + if (p_path.begins_with("res://")) { + ret = create(ACCESS_RESOURCES); + } else if (p_path.begins_with("user://")) { + ret = create(ACCESS_USERDATA); + + } else { + ret = create(ACCESS_FILESYSTEM); + } + + return ret; +} + +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) { + //try packed data first + + FileAccess *ret = nullptr; + 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 (r_error) { + *r_error = OK; + } + return ret; + } + } + + ret = create_for_path(p_path); + Error err = ret->_open(p_path, p_mode_flags); + + if (r_error) { + *r_error = err; + } + if (err != OK) { + memdelete(ret); + ret = nullptr; + } + + return ret; +} + +FileAccess::CreateFunc FileAccess::get_create_func(AccessType p_access) { + return create_func[p_access]; +} + +String FileAccess::fix_path(const String &p_path) const { + //helper used by file accesses that use a single filesystem + + String r_path = p_path.replace("\\", "/"); + + switch (_access_type) { + case ACCESS_RESOURCES: { + if (ProjectSettings::get_singleton()) { + if (r_path.begins_with("res://")) { + String resource_path = ProjectSettings::get_singleton()->get_resource_path(); + if (resource_path != "") { + return r_path.replace("res:/", resource_path); + } + return r_path.replace("res://", ""); + } + } + + } break; + case ACCESS_USERDATA: { + if (r_path.begins_with("user://")) { + String data_dir = OS::get_singleton()->get_user_data_dir(); + if (data_dir != "") { + return r_path.replace("user:/", data_dir); + } + return r_path.replace("user://", ""); + } + + } break; + case ACCESS_FILESYSTEM: { + return r_path; + } break; + case ACCESS_MAX: + break; // Can't happen, but silences warning + } + + return r_path; +} + +/* these are all implemented for ease of porting, then can later be optimized */ + +uint16_t FileAccess::get_16() const { + uint16_t res; + uint8_t a, b; + + a = get_8(); + b = get_8(); + + if (big_endian) { + SWAP(a, b); + } + + res = b; + res <<= 8; + res |= a; + + return res; +} + +uint32_t FileAccess::get_32() const { + uint32_t res; + uint16_t a, b; + + a = get_16(); + b = get_16(); + + if (big_endian) { + SWAP(a, b); + } + + res = b; + res <<= 16; + res |= a; + + return res; +} + +uint64_t FileAccess::get_64() const { + uint64_t res; + uint32_t a, b; + + a = get_32(); + b = get_32(); + + if (big_endian) { + SWAP(a, b); + } + + res = b; + res <<= 32; + res |= a; + + return res; +} + +float FileAccess::get_float() const { + MarshallFloat m; + m.i = get_32(); + return m.f; +} + +real_t FileAccess::get_real() const { + if (real_is_double) { + return get_double(); + } else { + return get_float(); + } +} + +double FileAccess::get_double() const { + MarshallDouble m; + m.l = get_64(); + return m.d; +} + +String FileAccess::get_token() const { + CharString token; + + char32_t c = get_8(); + + while (!eof_reached()) { + if (c <= ' ') { + if (token.length()) { + break; + } + } else { + token += c; + } + c = get_8(); + } + + return String::utf8(token.get_data()); +} + +class CharBuffer { + Vector<char> vector; + char stack_buffer[256]; + + char *buffer = nullptr; + int capacity = 0; + int written = 0; + + bool grow() { + if (vector.resize(next_power_of_2(1 + written)) != OK) { + return false; + } + + if (buffer == stack_buffer) { // first chunk? + + for (int i = 0; i < written; i++) { + vector.write[i] = stack_buffer[i]; + } + } + + buffer = vector.ptrw(); + capacity = vector.size(); + ERR_FAIL_COND_V(written >= capacity, false); + + return true; + } + +public: + _FORCE_INLINE_ CharBuffer() : + buffer(stack_buffer), + capacity(sizeof(stack_buffer) / sizeof(char)) { + } + + _FORCE_INLINE_ void push_back(char c) { + if (written >= capacity) { + ERR_FAIL_COND(!grow()); + } + + buffer[written++] = c; + } + + _FORCE_INLINE_ const char *get_data() const { + return buffer; + } +}; + +String FileAccess::get_line() const { + CharBuffer line; + + char32_t c = get_8(); + + while (!eof_reached()) { + if (c == '\n' || c == '\0') { + line.push_back(0); + return String::utf8(line.get_data()); + } else if (c != '\r') { + line.push_back(c); + } + + c = get_8(); + } + line.push_back(0); + return String::utf8(line.get_data()); +} + +Vector<String> FileAccess::get_csv_line(const String &p_delim) const { + ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>()); + + String l; + int qc = 0; + do { + if (eof_reached()) { + break; + } + + l += get_line() + "\n"; + qc = 0; + for (int i = 0; i < l.length(); i++) { + if (l[i] == '"') { + qc++; + } + } + + } while (qc % 2); + + l = l.substr(0, l.length() - 1); + + Vector<String> strings; + + bool in_quote = false; + String current; + for (int i = 0; i < l.length(); i++) { + char32_t c = l[i]; + char32_t s[2] = { 0, 0 }; + + if (!in_quote && c == p_delim[0]) { + strings.push_back(current); + current = String(); + } else if (c == '"') { + if (l[i + 1] == '"' && in_quote) { + s[0] = '"'; + current += s; + i++; + } else { + in_quote = !in_quote; + } + } else { + s[0] = c; + current += s; + } + } + + strings.push_back(current); + + return strings; +} + +uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const { + ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); + + uint64_t i = 0; + for (i = 0; i < p_length && !eof_reached(); i++) { + p_dst[i] = get_8(); + } + + return i; +} + +String FileAccess::get_as_utf8_string() const { + Vector<uint8_t> sourcef; + uint64_t len = get_length(); + sourcef.resize(len + 1); + + uint8_t *w = sourcef.ptrw(); + uint64_t r = get_buffer(w, len); + ERR_FAIL_COND_V(r != len, String()); + w[len] = 0; + + String s; + if (s.parse_utf8((const char *)w)) { + return String(); + } + return s; +} + +void FileAccess::store_16(uint16_t p_dest) { + uint8_t a, b; + + a = p_dest & 0xFF; + b = p_dest >> 8; + + if (big_endian) { + SWAP(a, b); + } + + store_8(a); + store_8(b); +} + +void FileAccess::store_32(uint32_t p_dest) { + uint16_t a, b; + + a = p_dest & 0xFFFF; + b = p_dest >> 16; + + if (big_endian) { + SWAP(a, b); + } + + store_16(a); + store_16(b); +} + +void FileAccess::store_64(uint64_t p_dest) { + uint32_t a, b; + + a = p_dest & 0xFFFFFFFF; + b = p_dest >> 32; + + if (big_endian) { + SWAP(a, b); + } + + store_32(a); + store_32(b); +} + +void FileAccess::store_real(real_t p_real) { + if (sizeof(real_t) == 4) { + store_float(p_real); + } else { + store_double(p_real); + } +} + +void FileAccess::store_float(float p_dest) { + MarshallFloat m; + m.f = p_dest; + store_32(m.i); +} + +void FileAccess::store_double(double p_dest) { + MarshallDouble m; + m.d = p_dest; + store_64(m.l); +} + +uint64_t FileAccess::get_modified_time(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(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 + "'."); + + uint64_t mt = fa->_get_modified_time(p_file); + memdelete(fa); + return mt; +} + +uint32_t FileAccess::get_unix_permissions(const String &p_file) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(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 + "'."); + + uint32_t mt = fa->_get_unix_permissions(p_file); + memdelete(fa); + return mt; +} + +Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) { + if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && (PackedData::get_singleton()->has_path(p_file) || PackedData::get_singleton()->has_directory(p_file))) { + 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 + "'."); + + Error err = fa->_set_unix_permissions(p_file, p_permissions); + memdelete(fa); + return err; +} + +void FileAccess::store_string(const String &p_string) { + if (p_string.length() == 0) { + return; + } + + CharString cs = p_string.utf8(); + store_buffer((uint8_t *)&cs[0], cs.length()); +} + +void FileAccess::store_pascal_string(const String &p_string) { + CharString cs = p_string.utf8(); + store_32(cs.length()); + store_buffer((uint8_t *)&cs[0], cs.length()); +} + +String FileAccess::get_pascal_string() { + uint32_t sl = get_32(); + CharString cs; + cs.resize(sl + 1); + get_buffer((uint8_t *)cs.ptr(), sl); + cs[sl] = 0; + + String ret; + ret.parse_utf8(cs.ptr()); + + return ret; +} + +void FileAccess::store_line(const String &p_line) { + store_string(p_line); + store_8('\n'); +} + +void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) { + ERR_FAIL_COND(p_delim.length() != 1); + + String line = ""; + int size = p_values.size(); + 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) { + value = "\"" + value.replace("\"", "\"\"") + "\""; + } + if (i < size - 1) { + value += p_delim; + } + + line += value; + } + + store_line(line); +} + +void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) { + ERR_FAIL_COND(!p_src && p_length > 0); + for (uint64_t i = 0; i < p_length; i++) { + store_8(p_src[i]); + } +} + +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) { + if (r_error) { // if error requested, do not throw error + return Vector<uint8_t>(); + } + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_path) + "'."); + } + Vector<uint8_t> data; + data.resize(f->get_length()); + f->get_buffer(data.ptrw(), data.size()); + memdelete(f); + return data; +} + +String FileAccess::get_file_as_string(const String &p_path, Error *r_error) { + Error err; + Vector<uint8_t> array = get_file_as_array(p_path, &err); + if (r_error) { + *r_error = err; + } + if (err != OK) { + if (r_error) { + return String(); + } + ERR_FAIL_V_MSG(String(), "Can't get file as string from path '" + String(p_path) + "'."); + } + + String ret; + ret.parse_utf8((const char *)array.ptr(), array.size()); + return ret; +} + +String FileAccess::get_md5(const String &p_file) { + FileAccess *f = FileAccess::open(p_file, READ); + if (!f) { + return String(); + } + + CryptoCore::MD5Context ctx; + ctx.start(); + + unsigned char step[32768]; + + while (true) { + uint64_t br = f->get_buffer(step, 32768); + if (br > 0) { + ctx.update(step, br); + } + if (br < 4096) { + break; + } + } + + unsigned char hash[16]; + ctx.finish(hash); + + memdelete(f); + + return String::md5(hash); +} + +String FileAccess::get_multiple_md5(const Vector<String> &p_file) { + CryptoCore::MD5Context ctx; + ctx.start(); + + for (int i = 0; i < p_file.size(); i++) { + FileAccess *f = FileAccess::open(p_file[i], READ); + ERR_CONTINUE(!f); + + unsigned char step[32768]; + + while (true) { + uint64_t br = f->get_buffer(step, 32768); + if (br > 0) { + ctx.update(step, br); + } + if (br < 4096) { + break; + } + } + memdelete(f); + } + + unsigned char hash[16]; + ctx.finish(hash); + + return String::md5(hash); +} + +String FileAccess::get_sha256(const String &p_file) { + FileAccess *f = FileAccess::open(p_file, READ); + if (!f) { + return String(); + } + + CryptoCore::SHA256Context ctx; + ctx.start(); + + unsigned char step[32768]; + + while (true) { + uint64_t br = f->get_buffer(step, 32768); + if (br > 0) { + ctx.update(step, br); + } + if (br < 4096) { + break; + } + } + + 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 new file mode 100644 index 0000000000..5804aa2c47 --- /dev/null +++ b/core/io/file_access.h @@ -0,0 +1,198 @@ +/*************************************************************************/ +/* file_access.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FILE_ACCESS_H +#define FILE_ACCESS_H + +#include "core/math/math_defs.h" +#include "core/os/memory.h" +#include "core/string/ustring.h" +#include "core/typedefs.h" + +/** + * Multi-Platform abstraction for accessing to files. + */ + +class FileAccess { +public: + enum AccessType { + ACCESS_RESOURCES, + ACCESS_USERDATA, + ACCESS_FILESYSTEM, + ACCESS_MAX + }; + + typedef void (*FileCloseFailNotify)(const String &); + + typedef FileAccess *(*CreateFunc)(); + bool big_endian = false; + bool real_is_double = false; + + virtual uint32_t _get_unix_permissions(const String &p_file) = 0; + virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0; + +protected: + String fix_path(const String &p_path) const; + virtual Error _open(const String &p_path, int p_mode_flags) = 0; ///< open a file + virtual uint64_t _get_modified_time(const String &p_file) = 0; + + static FileCloseFailNotify close_fail_notify; + +private: + static bool backup_save; + + 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() { + return memnew(T); + } + +public: + static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; } + + virtual void _set_access_type(AccessType p_access); + + enum ModeFlags { + READ = 1, + WRITE = 2, + READ_WRITE = 3, + 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 + virtual String get_path_absolute() const { return ""; } /// returns the absolute path for the current open file + + virtual void seek(uint64_t p_position) = 0; ///< seek to a given position + virtual void seek_end(int64_t p_position = 0) = 0; ///< seek from the end of file with negative offset + virtual uint64_t get_position() const = 0; ///< get position in the file + virtual uint64_t get_length() const = 0; ///< get size of the file + + virtual bool eof_reached() const = 0; ///< reading passed EOF + + virtual uint8_t get_8() const = 0; ///< get a byte + virtual uint16_t get_16() const; ///< get 16 bits uint + virtual uint32_t get_32() const; ///< get 32 bits uint + virtual uint64_t get_64() const; ///< get 64 bits uint + + virtual float get_float() const; + virtual double get_double() const; + virtual real_t get_real() const; + + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes + virtual String get_line() const; + virtual String get_token() const; + virtual Vector<String> get_csv_line(const String &p_delim = ",") const; + virtual String get_as_utf8_string() const; + + /** + * Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac) + * It's not about the current CPU type but file formats. + * This flag gets reset to `false` (little endian) on each open. + */ + virtual void set_big_endian(bool p_big_endian) { big_endian = p_big_endian; } + inline bool is_big_endian() const { return big_endian; } + + virtual Error get_error() const = 0; ///< get last error + + virtual void flush() = 0; + virtual void store_8(uint8_t p_dest) = 0; ///< store a byte + virtual void store_16(uint16_t p_dest); ///< store 16 bits uint + virtual void store_32(uint32_t p_dest); ///< store 32 bits uint + virtual void store_64(uint64_t p_dest); ///< store 64 bits uint + + virtual void store_float(float p_dest); + virtual void store_double(double p_dest); + virtual void store_real(real_t p_real); + + virtual void store_string(const String &p_string); + virtual void store_line(const String &p_line); + virtual void store_csv_line(const Vector<String> &p_values, const String &p_delim = ","); + + virtual void store_pascal_string(const String &p_string); + virtual String get_pascal_string(); + + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes + + virtual bool file_exists(const String &p_name) = 0; ///< return true if a file exists + + 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 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); + static uint32_t get_unix_permissions(const String &p_file); + static Error set_unix_permissions(const String &p_file, uint32_t p_permissions); + + static void set_backup_save(bool p_enable) { backup_save = p_enable; }; + static bool is_backup_save_enabled() { return backup_save; }; + + static String get_md5(const String &p_file); + static String get_sha256(const String &p_file); + static String get_multiple_md5(const Vector<String> &p_file); + + static Vector<uint8_t> get_file_as_array(const String &p_path, Error *r_error = nullptr); + static String get_file_as_string(const String &p_path, Error *r_error = nullptr); + + template <class T> + static void make_default(AccessType p_access) { + create_func[p_access] = _create_builtin<T>; + } + + FileAccess() {} + 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.h b/core/io/file_access_compressed.h index 19e4f241dd..3389e020e3 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -32,7 +32,7 @@ #define FILE_ACCESS_COMPRESSED_H #include "core/io/compression.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" class FileAccessCompressed : public FileAccess { Compression::Mode cmode = Compression::MODE_ZSTD; diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 00f14099f9..decffae696 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -31,7 +31,7 @@ #ifndef FILE_ACCESS_ENCRYPTED_H #define FILE_ACCESS_ENCRYPTED_H -#include "core/os/file_access.h" +#include "core/io/file_access.h" #define ENCRYPTED_HEADER_MAGIC 0x43454447 diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index d9be2a4a75..627fd2bf9c 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -31,7 +31,7 @@ #include "file_access_memory.h" #include "core/config/project_settings.h" -#include "core/os/dir_access.h" +#include "core/io/dir_access.h" #include "core/templates/map.h" static Map<String, Vector<uint8_t>> *files = nullptr; diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 4157531d01..14135bd68c 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -31,7 +31,7 @@ #ifndef FILE_ACCESS_MEMORY_H #define FILE_ACCESS_MEMORY_H -#include "core/os/file_access.h" +#include "core/io/file_access.h" class FileAccessMemory : public FileAccess { uint8_t *data = nullptr; diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index 94b66c2480..1d9d761fbb 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -31,8 +31,8 @@ #ifndef FILE_ACCESS_NETWORK_H #define FILE_ACCESS_NETWORK_H +#include "core/io/file_access.h" #include "core/io/stream_peer_tcp.h" -#include "core/os/file_access.h" #include "core/os/semaphore.h" #include "core/os/thread.h" diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 7a83fc938f..2f0ee62723 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -31,8 +31,8 @@ #ifndef FILE_ACCESS_PACK_H #define FILE_ACCESS_PACK_H -#include "core/os/dir_access.h" -#include "core/os/file_access.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" #include "core/string/print_string.h" #include "core/templates/list.h" #include "core/templates/map.h" diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index b8383fd865..b5c882e9ce 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -32,7 +32,7 @@ #include "file_access_zip.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" ZipArchive *ZipArchive::instance = nullptr; diff --git a/core/io/http_client.h b/core/io/http_client.h index ec4b86b26f..f70999836f 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -34,10 +34,10 @@ #include "core/io/ip.h" #include "core/io/stream_peer.h" #include "core/io/stream_peer_tcp.h" -#include "core/object/reference.h" +#include "core/object/ref_counted.h" -class HTTPClient : public Reference { - GDCLASS(HTTPClient, Reference); +class HTTPClient : public RefCounted { + GDCLASS(HTTPClient, RefCounted); public: enum ResponseCode { diff --git a/core/io/image.cpp b/core/io/image.cpp index c36fa6e45f..9cd0ea7b5d 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -1428,16 +1428,23 @@ void Image::flip_x() { } } +/// Get mipmap size and offset. int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) { + // Data offset in mipmaps (including the original texture). int size = 0; + int w = p_width; int h = p_height; + + // Current mipmap index in the loop below. p_mipmaps is the target mipmap index. + // In this function, mipmap 0 represents the first mipmap instead of the original texture. int mm = 0; int pixsize = get_format_pixel_size(p_format); int pixshift = get_format_pixel_rshift(p_format); int block = get_format_block_size(p_format); - //technically, you can still compress up to 1 px no matter the format, so commenting this + + // Technically, you can still compress up to 1 px no matter the format, so commenting this. //int minw, minh; //get_format_min_pixel_size(p_format, minw, minh); int minw = 1, minh = 1; @@ -1453,17 +1460,6 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & size += s; - if (r_mm_width) { - *r_mm_width = bw; - } - if (r_mm_height) { - *r_mm_height = bh; - } - - if (p_mipmaps >= 0 && mm == p_mipmaps) { - break; - } - if (p_mipmaps >= 0) { w = MAX(minw, w >> 1); h = MAX(minh, h >> 1); @@ -1474,6 +1470,21 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int & w = MAX(minw, w >> 1); h = MAX(minh, h >> 1); } + + // 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; + } + if (r_mm_height) { + *r_mm_height = bh >> 1; + } + + // Reach target mipmap. + if (p_mipmaps >= 0 && mm == p_mipmaps) { + break; + } + mm++; } @@ -2718,10 +2729,11 @@ void (*Image::_image_decompress_bptc)(Image *) = nullptr; void (*Image::_image_decompress_etc1)(Image *) = nullptr; void (*Image::_image_decompress_etc2)(Image *) = nullptr; -Vector<uint8_t> (*Image::lossy_packer)(const Ref<Image> &, float) = nullptr; -Ref<Image> (*Image::lossy_unpacker)(const Vector<uint8_t> &) = nullptr; -Vector<uint8_t> (*Image::lossless_packer)(const Ref<Image> &) = nullptr; -Ref<Image> (*Image::lossless_unpacker)(const Vector<uint8_t> &) = nullptr; +Vector<uint8_t> (*Image::webp_lossy_packer)(const Ref<Image> &, float) = nullptr; +Vector<uint8_t> (*Image::webp_lossless_packer)(const Ref<Image> &) = nullptr; +Ref<Image> (*Image::webp_unpacker)(const Vector<uint8_t> &) = nullptr; +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; diff --git a/core/io/image.h b/core/io/image.h index df8f9b35a1..060e54a308 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -148,10 +148,11 @@ public: static void (*_image_decompress_etc1)(Image *); static void (*_image_decompress_etc2)(Image *); - static Vector<uint8_t> (*lossy_packer)(const Ref<Image> &p_image, float p_quality); - static Ref<Image> (*lossy_unpacker)(const Vector<uint8_t> &p_buffer); - static Vector<uint8_t> (*lossless_packer)(const Ref<Image> &p_image); - static Ref<Image> (*lossless_unpacker)(const Vector<uint8_t> &p_buffer); + static Vector<uint8_t> (*webp_lossy_packer)(const Ref<Image> &p_image, float p_quality); + static Vector<uint8_t> (*webp_lossless_packer)(const Ref<Image> &p_image); + static Ref<Image> (*webp_unpacker)(const Vector<uint8_t> &p_buffer); + static Vector<uint8_t> (*png_packer)(const Ref<Image> &p_image); + 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); diff --git a/core/io/image_loader.h b/core/io/image_loader.h index a5d588e0b5..6d1b1e3646 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -31,9 +31,9 @@ #ifndef IMAGE_LOADER_H #define IMAGE_LOADER_H +#include "core/io/file_access.h" #include "core/io/image.h" #include "core/io/resource_loader.h" -#include "core/os/file_access.h" #include "core/string/ustring.h" #include "core/templates/list.h" diff --git a/core/io/json.cpp b/core/io/json.cpp index 394cf216e8..e3e9d6158b 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -55,7 +55,7 @@ static String _make_indent(const String &p_indent, int p_size) { return indent_text; } -String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys) { +String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, bool p_full_precision) { String colon = ":"; String end_statement = ""; @@ -71,8 +71,17 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ return p_var.operator bool() ? "true" : "false"; case Variant::INT: return itos(p_var); - case Variant::FLOAT: - return rtos(p_var); + case Variant::FLOAT: { + double num = p_var; + if (p_full_precision) { + // Store unreliable digits (17) instead of just reliable + // digits (14) so that the value can be decoded exactly. + return String::num(num, 17 - (int)floor(log10(num))); + } else { + // Store only reliable digits (14) by default. + return String::num(num, 14 - (int)floor(log10(num))); + } + } case Variant::PACKED_INT32_ARRAY: case Variant::PACKED_INT64_ARRAY: case Variant::PACKED_FLOAT32_ARRAY: @@ -121,8 +130,8 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ } } -String JSON::print(const Variant &p_var, const String &p_indent, bool p_sort_keys) { - return _print_var(p_var, p_indent, 0, p_sort_keys); +String JSON::print(const Variant &p_var, const String &p_indent, bool p_sort_keys, bool p_full_precision) { + return _print_var(p_var, p_indent, 0, p_sort_keys, p_full_precision); } Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) { diff --git a/core/io/json.h b/core/io/json.h index 537477666e..bfd2347725 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -31,7 +31,7 @@ #ifndef JSON_H #define JSON_H -#include "core/object/reference.h" +#include "core/object/ref_counted.h" #include "core/variant/variant.h" class JSON { enum TokenType { @@ -62,7 +62,7 @@ class JSON { static const char *tk_name[TK_MAX]; - static String _print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys); + static String _print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, bool p_full_precision = false); static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str); static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); @@ -70,12 +70,12 @@ class JSON { static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); public: - static String print(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true); + static String print(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true, bool p_full_precision = false); static Error parse(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line); }; -class JSONParser : public Reference { - GDCLASS(JSONParser, Reference); +class JSONParser : public RefCounted { + GDCLASS(JSONParser, RefCounted); Variant data; String string; diff --git a/core/io/logger.cpp b/core/io/logger.cpp index 7ffa5b50ea..09539f716c 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -31,7 +31,7 @@ #include "logger.h" #include "core/config/project_settings.h" -#include "core/os/dir_access.h" +#include "core/io/dir_access.h" #include "core/os/os.h" #include "core/os/time.h" #include "core/string/print_string.h" diff --git a/core/io/logger.h b/core/io/logger.h index a12945911c..ccf68562d6 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -31,7 +31,7 @@ #ifndef LOGGER_H #define LOGGER_H -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "core/string/ustring.h" #include "core/templates/vector.h" diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 18e1092c26..4c58c84c14 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -30,7 +30,7 @@ #include "marshalls.h" -#include "core/object/reference.h" +#include "core/object/ref_counted.h" #include "core/os/keyboard.h" #include "core/string/print_string.h" @@ -489,8 +489,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int obj->set(str, value); } - if (Object::cast_to<Reference>(obj)) { - REF ref = REF(Object::cast_to<Reference>(obj)); + if (Object::cast_to<RefCounted>(obj)) { + REF ref = REF(Object::cast_to<RefCounted>(obj)); r_variant = ref; } else { r_variant = obj; diff --git a/core/io/marshalls.h b/core/io/marshalls.h index cc0e9ba301..7fac708f97 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -31,7 +31,7 @@ #ifndef MARSHALLS_H #define MARSHALLS_H -#include "core/object/reference.h" +#include "core/object/ref_counted.h" #include "core/typedefs.h" #include "core/variant/variant.h" @@ -165,8 +165,8 @@ static inline double decode_double(const uint8_t *p_arr) { return md.d; } -class EncodedObjectAsID : public Reference { - GDCLASS(EncodedObjectAsID, Reference); +class EncodedObjectAsID : public RefCounted { + GDCLASS(EncodedObjectAsID, RefCounted); ObjectID id; diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index 6a251cf77b..43804a20ec 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -32,10 +32,10 @@ #define MULTIPLAYER_API_H #include "core/io/networked_multiplayer_peer.h" -#include "core/object/reference.h" +#include "core/object/ref_counted.h" -class MultiplayerAPI : public Reference { - GDCLASS(MultiplayerAPI, Reference); +class MultiplayerAPI : public RefCounted { + GDCLASS(MultiplayerAPI, RefCounted); public: enum RPCMode { diff --git a/core/io/net_socket.h b/core/io/net_socket.h index 98ff9562d9..fd7d50c704 100644 --- a/core/io/net_socket.h +++ b/core/io/net_socket.h @@ -32,9 +32,9 @@ #define NET_SOCKET_H #include "core/io/ip.h" -#include "core/object/reference.h" +#include "core/object/ref_counted.h" -class NetSocket : public Reference { +class NetSocket : public RefCounted { protected: static NetSocket *(*_create)(); diff --git a/core/io/packed_data_container.h b/core/io/packed_data_container.h index 7791e21bb3..40772bb2bf 100644 --- a/core/io/packed_data_container.h +++ b/core/io/packed_data_container.h @@ -80,8 +80,8 @@ public: PackedDataContainer() {} }; -class PackedDataContainerRef : public Reference { - GDCLASS(PackedDataContainerRef, Reference); +class PackedDataContainerRef : public RefCounted { + GDCLASS(PackedDataContainerRef, RefCounted); friend class PackedDataContainer; uint32_t offset = 0; diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 9e03c44750..9a345af3d0 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -35,8 +35,8 @@ #include "core/object/class_db.h" #include "core/templates/ring_buffer.h" -class PacketPeer : public Reference { - GDCLASS(PacketPeer, Reference); +class PacketPeer : public RefCounted { + GDCLASS(PacketPeer, RefCounted); Variant _bnd_get_var(bool p_allow_objects = false); diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp index bac98e20e7..a6d220622b 100644 --- a/core/io/packet_peer_dtls.cpp +++ b/core/io/packet_peer_dtls.cpp @@ -30,7 +30,7 @@ #include "packet_peer_dtls.h" #include "core/config/project_settings.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" PacketPeerDTLS *(*PacketPeerDTLS::_create)() = nullptr; bool PacketPeerDTLS::available = false; diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index cadb02b5dd..806a95398f 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -31,9 +31,9 @@ #include "pck_packer.h" #include "core/crypto/crypto_core.h" +#include "core/io/file_access.h" #include "core/io/file_access_encrypted.h" #include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION -#include "core/os/file_access.h" #include "core/version.h" static int _get_pad(int p_alignment, int p_n) { diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h index dec8f8748d..3d2ce8f240 100644 --- a/core/io/pck_packer.h +++ b/core/io/pck_packer.h @@ -31,12 +31,12 @@ #ifndef PCK_PACKER_H #define PCK_PACKER_H -#include "core/object/reference.h" +#include "core/object/ref_counted.h" class FileAccess; -class PCKPacker : public Reference { - GDCLASS(PCKPacker, Reference); +class PCKPacker : public RefCounted { + GDCLASS(PCKPacker, RefCounted); FileAccess *file = nullptr; int alignment = 0; diff --git a/core/io/resource.cpp b/core/io/resource.cpp index d46e9edafa..b970e85c99 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -31,9 +31,9 @@ #include "resource.h" #include "core/core_string_names.h" +#include "core/io/file_access.h" #include "core/io/resource_loader.h" #include "core/object/script_language.h" -#include "core/os/file_access.h" #include "core/os/os.h" #include "scene/main/node.h" //only so casting works diff --git a/core/io/resource.h b/core/io/resource.h index 75a9f928f8..028fed1c6e 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -32,7 +32,7 @@ #define RESOURCE_H #include "core/object/class_db.h" -#include "core/object/reference.h" +#include "core/object/ref_counted.h" #include "core/templates/safe_refcount.h" #include "core/templates/self_list.h" @@ -43,8 +43,8 @@ public: \ private: -class Resource : public Reference { - GDCLASS(Resource, Reference); +class Resource : public RefCounted { + GDCLASS(Resource, RefCounted); OBJ_CATEGORY("Resources"); public: diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 385f15c0cf..f83ba30514 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -31,10 +31,10 @@ #include "resource_format_binary.h" #include "core/config/project_settings.h" +#include "core/io/dir_access.h" #include "core/io/file_access_compressed.h" #include "core/io/image.h" #include "core/io/marshalls.h" -#include "core/os/dir_access.h" #include "core/version.h" //#define print_bl(m_what) print_line(m_what) diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 3592bbdbc4..abc7403935 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -31,9 +31,9 @@ #ifndef RESOURCE_FORMAT_BINARY_H #define RESOURCE_FORMAT_BINARY_H +#include "core/io/file_access.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" -#include "core/os/file_access.h" class ResourceLoaderBinary { bool translation_remapped = false; diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index a14d6ba52c..2ceeb176e5 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -93,8 +93,8 @@ public: ResourceFormatImporter(); }; -class ResourceImporter : public Reference { - GDCLASS(ResourceImporter, Reference); +class ResourceImporter : public RefCounted { + GDCLASS(ResourceImporter, RefCounted); public: virtual String get_importer_name() const = 0; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index b48c48b1bc..9839a563fd 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -31,8 +31,8 @@ #include "resource_loader.h" #include "core/config/project_settings.h" +#include "core/io/file_access.h" #include "core/io/resource_importer.h" -#include "core/os/file_access.h" #include "core/os/os.h" #include "core/string/print_string.h" #include "core/string/translation.h" diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 914d988caa..c656b9a69c 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -35,8 +35,8 @@ #include "core/os/semaphore.h" #include "core/os/thread.h" -class ResourceFormatLoader : public Reference { - GDCLASS(ResourceFormatLoader, Reference); +class ResourceFormatLoader : public RefCounted { + GDCLASS(ResourceFormatLoader, RefCounted); public: enum CacheMode { diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 7ebc7f34b3..f7ddea7266 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -30,9 +30,9 @@ #include "resource_saver.h" #include "core/config/project_settings.h" +#include "core/io/file_access.h" #include "core/io/resource_loader.h" #include "core/object/script_language.h" -#include "core/os/file_access.h" Ref<ResourceFormatSaver> ResourceSaver::saver[MAX_SAVERS]; diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 2c9e8f1aa3..07154aac4d 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -33,8 +33,8 @@ #include "core/io/resource.h" -class ResourceFormatSaver : public Reference { - GDCLASS(ResourceFormatSaver, Reference); +class ResourceFormatSaver : public RefCounted { + GDCLASS(ResourceFormatSaver, RefCounted); protected: static void _bind_methods(); diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index 1e1a3e890c..effc3850af 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -31,10 +31,10 @@ #ifndef STREAM_PEER_H #define STREAM_PEER_H -#include "core/object/reference.h" +#include "core/object/ref_counted.h" -class StreamPeer : public Reference { - GDCLASS(StreamPeer, Reference); +class StreamPeer : public RefCounted { + GDCLASS(StreamPeer, RefCounted); OBJ_CATEGORY("Networking"); protected: diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index abefa53c6f..10985a04d5 100644 --- a/core/io/tcp_server.h +++ b/core/io/tcp_server.h @@ -36,8 +36,8 @@ #include "core/io/stream_peer.h" #include "core/io/stream_peer_tcp.h" -class TCPServer : public Reference { - GDCLASS(TCPServer, Reference); +class TCPServer : public RefCounted { + GDCLASS(TCPServer, RefCounted); protected: enum { diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 9adf912224..83d575cee8 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -30,7 +30,7 @@ #include "translation_loader_po.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "core/string/translation.h" #include "core/string/translation_po.h" diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 36d33fcac3..c52820e60d 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -31,8 +31,8 @@ #ifndef TRANSLATION_LOADER_PO_H #define TRANSLATION_LOADER_PO_H +#include "core/io/file_access.h" #include "core/io/resource_loader.h" -#include "core/os/file_access.h" #include "core/string/translation.h" class TranslationLoaderPO : public ResourceFormatLoader { diff --git a/core/io/udp_server.h b/core/io/udp_server.h index 60d03f37f0..e49a559c51 100644 --- a/core/io/udp_server.h +++ b/core/io/udp_server.h @@ -34,8 +34,8 @@ #include "core/io/net_socket.h" #include "core/io/packet_peer_udp.h" -class UDPServer : public Reference { - GDCLASS(UDPServer, Reference); +class UDPServer : public RefCounted { + GDCLASS(UDPServer, RefCounted); protected: enum { diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h index c323301eac..1113cce715 100644 --- a/core/io/xml_parser.h +++ b/core/io/xml_parser.h @@ -31,8 +31,8 @@ #ifndef XML_PARSER_H #define XML_PARSER_H -#include "core/object/reference.h" -#include "core/os/file_access.h" +#include "core/io/file_access.h" +#include "core/object/ref_counted.h" #include "core/string/ustring.h" #include "core/templates/vector.h" @@ -40,8 +40,8 @@ Based on irrXML (see their zlib license). Added mainly for compatibility with their Collada loader. */ -class XMLParser : public Reference { - GDCLASS(XMLParser, Reference); +class XMLParser : public RefCounted { + GDCLASS(XMLParser, RefCounted); public: //! Enumeration of all supported source text file formats diff --git a/core/io/zip_io.h b/core/io/zip_io.h index 52691c65e9..776473bfa1 100644 --- a/core/io/zip_io.h +++ b/core/io/zip_io.h @@ -31,7 +31,7 @@ #ifndef ZIP_IO_H #define ZIP_IO_H -#include "core/os/file_access.h" +#include "core/io/file_access.h" // Not directly used in this header, but assumed available in downstream users // like platform/*/export/export.cpp. Could be fixed, but probably better to have |