feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
static int zstd_window_log_size;
|
||||
static int gzip_chunk;
|
||||
|
||||
enum Mode {
|
||||
enum Mode : int32_t {
|
||||
MODE_FASTLZ,
|
||||
MODE_DEFLATE,
|
||||
MODE_ZSTD,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#include "config_file.h"
|
||||
|
||||
#include "core/io/file_access_encrypted.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/string_builder.h"
|
||||
#include "core/variant/variant_parser.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/memory.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/os/time.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
|
||||
thread_local Error DirAccess::last_dir_open_error = OK;
|
||||
|
|
@ -155,9 +155,9 @@ Error DirAccess::make_dir_recursive(const String &p_dir) {
|
|||
} else if (full_dir.begins_with("user://")) {
|
||||
base = "user://";
|
||||
} else if (full_dir.is_network_share_path()) {
|
||||
int pos = full_dir.find("/", 2);
|
||||
int pos = full_dir.find_char('/', 2);
|
||||
ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
|
||||
pos = full_dir.find("/", pos + 1);
|
||||
pos = full_dir.find_char('/', pos + 1);
|
||||
ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
|
||||
base = full_dir.substr(0, pos + 1);
|
||||
} else if (full_dir.begins_with("/")) {
|
||||
|
|
@ -177,7 +177,7 @@ Error DirAccess::make_dir_recursive(const String &p_dir) {
|
|||
curpath = curpath.path_join(subdirs[i]);
|
||||
Error err = make_dir(curpath);
|
||||
if (err != OK && err != ERR_ALREADY_EXISTS) {
|
||||
ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath);
|
||||
ERR_FAIL_V_MSG(err, vformat("Could not create directory: '%s'.", curpath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +239,7 @@ Ref<DirAccess> DirAccess::create_for_path(const String &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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, vformat("Cannot create DirAccess for path '%s'.", p_path));
|
||||
Error err = da->change_dir(p_path);
|
||||
if (r_error) {
|
||||
*r_error = err;
|
||||
|
|
@ -323,6 +323,80 @@ Ref<DirAccess> DirAccess::create(AccessType p_access) {
|
|||
return da;
|
||||
}
|
||||
|
||||
Ref<DirAccess> DirAccess::create_temp(const String &p_prefix, bool p_keep, Error *r_error) {
|
||||
const String ERROR_COMMON_PREFIX = "Error while creating temporary directory";
|
||||
|
||||
if (!p_prefix.is_valid_filename()) {
|
||||
*r_error = ERR_FILE_BAD_PATH;
|
||||
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix));
|
||||
}
|
||||
|
||||
Ref<DirAccess> dir_access = DirAccess::open(OS::get_singleton()->get_temp_path());
|
||||
|
||||
uint32_t suffix_i = 0;
|
||||
String path;
|
||||
while (true) {
|
||||
String datetime = Time::get_singleton()->get_datetime_string_from_system().replace("-", "").replace("T", "").replace(":", "");
|
||||
datetime += itos(Time::get_singleton()->get_ticks_usec());
|
||||
String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : "");
|
||||
path = (p_prefix.is_empty() ? "" : p_prefix + "-") + suffix;
|
||||
if (!path.is_valid_filename()) {
|
||||
*r_error = ERR_FILE_BAD_PATH;
|
||||
return Ref<DirAccess>();
|
||||
}
|
||||
if (!DirAccess::exists(path)) {
|
||||
break;
|
||||
}
|
||||
suffix_i += 1;
|
||||
}
|
||||
|
||||
Error err = dir_access->make_dir(path);
|
||||
if (err != OK) {
|
||||
*r_error = err;
|
||||
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" couldn't create directory "%s".)", ERROR_COMMON_PREFIX, path));
|
||||
}
|
||||
err = dir_access->change_dir(path);
|
||||
if (err != OK) {
|
||||
*r_error = err;
|
||||
return Ref<DirAccess>();
|
||||
}
|
||||
|
||||
dir_access->_is_temp = true;
|
||||
dir_access->_temp_keep_after_free = p_keep;
|
||||
dir_access->_temp_path = dir_access->get_current_dir();
|
||||
|
||||
*r_error = OK;
|
||||
return dir_access;
|
||||
}
|
||||
|
||||
Ref<DirAccess> DirAccess::_create_temp(const String &p_prefix, bool p_keep) {
|
||||
return create_temp(p_prefix, p_keep, &last_dir_open_error);
|
||||
}
|
||||
|
||||
void DirAccess::_delete_temp() {
|
||||
if (!_is_temp || _temp_keep_after_free) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DirAccess::exists(_temp_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Error err;
|
||||
{
|
||||
Ref<DirAccess> dir_access = DirAccess::open(_temp_path, &err);
|
||||
if (err != OK) {
|
||||
return;
|
||||
}
|
||||
err = dir_access->erase_contents_recursive();
|
||||
if (err != OK) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DirAccess::remove_absolute(_temp_path);
|
||||
}
|
||||
|
||||
Error DirAccess::get_open_error() {
|
||||
return last_dir_open_error;
|
||||
}
|
||||
|
|
@ -345,10 +419,10 @@ Error DirAccess::copy(const String &p_from, const String &p_to, int p_chmod_flag
|
|||
Error err;
|
||||
{
|
||||
Ref<FileAccess> fsrc = FileAccess::open(p_from, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_from);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to open '%s'.", 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);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to open '%s'.", p_to));
|
||||
|
||||
const size_t copy_buffer_limit = 65536; // 64 KB
|
||||
|
||||
|
|
@ -444,11 +518,11 @@ Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, const String &p_to, int
|
|||
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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot create directory '%s'.", target_dir));
|
||||
}
|
||||
|
||||
Error err = change_dir(rel_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot change current directory to '%s'.", rel_path));
|
||||
|
||||
err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links);
|
||||
if (err) {
|
||||
|
|
@ -466,11 +540,11 @@ Error DirAccess::copy_dir(const String &p_from, String p_to, int p_chmod_flags,
|
|||
ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist.");
|
||||
|
||||
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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, vformat("Cannot create DirAccess for path '%s'.", p_to));
|
||||
|
||||
if (!target_da->dir_exists(p_to)) {
|
||||
Error err = target_da->make_dir_recursive(p_to);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot create directory '%s'.", p_to));
|
||||
}
|
||||
|
||||
if (!p_to.ends_with("/")) {
|
||||
|
|
@ -555,8 +629,9 @@ bool DirAccess::is_case_sensitive(const String &p_path) const {
|
|||
void DirAccess::_bind_methods() {
|
||||
ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open);
|
||||
ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error);
|
||||
ClassDB::bind_static_method("DirAccess", D_METHOD("create_temp", "prefix", "keep"), &DirAccess::_create_temp, DEFVAL(""), DEFVAL(false));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin, DEFVAL(false), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin);
|
||||
ClassDB::bind_method(D_METHOD("get_next"), &DirAccess::_get_next);
|
||||
ClassDB::bind_method(D_METHOD("current_is_dir"), &DirAccess::current_is_dir);
|
||||
ClassDB::bind_method(D_METHOD("list_dir_end"), &DirAccess::list_dir_end);
|
||||
|
|
@ -588,6 +663,8 @@ void DirAccess::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("read_link", "path"), &DirAccess::read_link);
|
||||
ClassDB::bind_method(D_METHOD("create_link", "source", "target"), &DirAccess::create_link);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_bundle", "path"), &DirAccess::is_bundle);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational);
|
||||
ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational);
|
||||
ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden);
|
||||
|
|
@ -598,3 +675,7 @@ void DirAccess::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");
|
||||
}
|
||||
|
||||
DirAccess::~DirAccess() {
|
||||
_delete_temp();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class DirAccess : public RefCounted {
|
|||
GDCLASS(DirAccess, RefCounted);
|
||||
|
||||
public:
|
||||
enum AccessType {
|
||||
enum AccessType : int32_t {
|
||||
ACCESS_RESOURCES,
|
||||
ACCESS_USERDATA,
|
||||
ACCESS_FILESYSTEM,
|
||||
|
|
@ -61,6 +61,13 @@ private:
|
|||
bool include_navigational = false;
|
||||
bool include_hidden = false;
|
||||
|
||||
bool _is_temp = false;
|
||||
bool _temp_keep_after_free = false;
|
||||
String _temp_path;
|
||||
void _delete_temp();
|
||||
|
||||
static Ref<DirAccess> _create_temp(const String &p_prefix = "", bool p_keep = false);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
|
@ -96,8 +103,8 @@ public:
|
|||
|
||||
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; };
|
||||
virtual bool is_readable(String p_dir) { return true; }
|
||||
virtual bool is_writable(String p_dir) { return true; }
|
||||
static bool exists(const String &p_dir);
|
||||
virtual uint64_t get_space_left() = 0;
|
||||
|
||||
|
|
@ -116,10 +123,10 @@ public:
|
|||
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);
|
||||
ERR_FAIL_MSG(vformat("Cannot remove file or directory: '%s'.", p_path));
|
||||
}
|
||||
} else {
|
||||
ERR_FAIL_MSG("Cannot remove non-existent file or directory: " + p_path);
|
||||
ERR_FAIL_MSG(vformat("Cannot remove non-existent file or directory: '%s'.", p_path));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,6 +143,7 @@ public:
|
|||
}
|
||||
|
||||
static Ref<DirAccess> open(const String &p_path, Error *r_error = nullptr);
|
||||
static Ref<DirAccess> create_temp(const String &p_prefix = "", bool p_keep = false, Error *r_error = nullptr);
|
||||
|
||||
static int _get_drive_count();
|
||||
static String get_drive_name(int p_idx);
|
||||
|
|
@ -160,9 +168,11 @@ public:
|
|||
bool get_include_hidden() const;
|
||||
|
||||
virtual bool is_case_sensitive(const String &p_path) const;
|
||||
virtual bool is_bundle(const String &p_file) const { return false; }
|
||||
|
||||
public:
|
||||
DirAccess() {}
|
||||
virtual ~DirAccess() {}
|
||||
virtual ~DirAccess();
|
||||
};
|
||||
|
||||
#endif // DIR_ACCESS_H
|
||||
|
|
|
|||
|
|
@ -30,15 +30,12 @@
|
|||
|
||||
#include "dtls_server.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
DTLSServer *(*DTLSServer::_create)() = nullptr;
|
||||
DTLSServer *(*DTLSServer::_create)(bool p_notify_postinitialize) = nullptr;
|
||||
bool DTLSServer::available = false;
|
||||
|
||||
DTLSServer *DTLSServer::create() {
|
||||
DTLSServer *DTLSServer::create(bool p_notify_postinitialize) {
|
||||
if (_create) {
|
||||
return _create();
|
||||
return _create(p_notify_postinitialize);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,14 +38,14 @@ class DTLSServer : public RefCounted {
|
|||
GDCLASS(DTLSServer, RefCounted);
|
||||
|
||||
protected:
|
||||
static DTLSServer *(*_create)();
|
||||
static DTLSServer *(*_create)(bool p_notify_postinitialize);
|
||||
static void _bind_methods();
|
||||
|
||||
static bool available;
|
||||
|
||||
public:
|
||||
static bool is_available();
|
||||
static DTLSServer *create();
|
||||
static DTLSServer *create(bool p_notify_postinitialize = true);
|
||||
|
||||
virtual Error setup(Ref<TLSOptions> p_options) = 0;
|
||||
virtual void stop() = 0;
|
||||
|
|
|
|||
112
engine/core/io/file_access.compat.inc
Normal file
112
engine/core/io/file_access.compat.inc
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/**************************************************************************/
|
||||
/* file_access.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 DISABLE_DEPRECATED
|
||||
|
||||
Ref<FileAccess> FileAccess::_open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
|
||||
return open_encrypted(p_path, p_mode_flags, p_key, Vector<uint8_t>());
|
||||
}
|
||||
|
||||
void FileAccess::store_8_bind_compat_78289(uint8_t p_dest) {
|
||||
store_8(p_dest);
|
||||
}
|
||||
|
||||
void FileAccess::store_16_bind_compat_78289(uint16_t p_dest) {
|
||||
store_16(p_dest);
|
||||
}
|
||||
|
||||
void FileAccess::store_32_bind_compat_78289(uint32_t p_dest) {
|
||||
store_32(p_dest);
|
||||
}
|
||||
|
||||
void FileAccess::store_64_bind_compat_78289(uint64_t p_dest) {
|
||||
store_64(p_dest);
|
||||
}
|
||||
|
||||
void FileAccess::store_buffer_bind_compat_78289(const Vector<uint8_t> &p_buffer) {
|
||||
store_buffer(p_buffer);
|
||||
}
|
||||
|
||||
void FileAccess::store_var_bind_compat_78289(const Variant &p_var, bool p_full_objects) {
|
||||
store_var(p_var, p_full_objects);
|
||||
}
|
||||
|
||||
void FileAccess::store_half_bind_compat_78289(float p_dest) {
|
||||
store_half(p_dest);
|
||||
}
|
||||
|
||||
void FileAccess::store_float_bind_compat_78289(float p_dest) {
|
||||
store_float(p_dest);
|
||||
}
|
||||
|
||||
void FileAccess::store_double_bind_compat_78289(double p_dest) {
|
||||
store_double(p_dest);
|
||||
}
|
||||
|
||||
void FileAccess::store_real_bind_compat_78289(real_t p_real) {
|
||||
store_real(p_real);
|
||||
}
|
||||
|
||||
void FileAccess::store_string_bind_compat_78289(const String &p_string) {
|
||||
store_string(p_string);
|
||||
}
|
||||
|
||||
void FileAccess::store_line_bind_compat_78289(const String &p_line) {
|
||||
store_line(p_line);
|
||||
}
|
||||
|
||||
void FileAccess::store_csv_line_bind_compat_78289(const Vector<String> &p_values, const String &p_delim) {
|
||||
store_csv_line(p_values, p_delim);
|
||||
}
|
||||
|
||||
void FileAccess::store_pascal_string_bind_compat_78289(const String &p_string) {
|
||||
store_pascal_string(p_string);
|
||||
}
|
||||
|
||||
void FileAccess::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::_open_encrypted_bind_compat_98918);
|
||||
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_8", "value"), &FileAccess::store_8_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_16", "value"), &FileAccess::store_16_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_32", "value"), &FileAccess::store_32_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_64", "value"), &FileAccess::store_64_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_half", "value"), &FileAccess::store_half_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_float", "value"), &FileAccess::store_float_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_double", "value"), &FileAccess::store_double_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_real", "value"), &FileAccess::store_real_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_buffer", "buffer"), &FileAccess::store_buffer_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_line", "line"), &FileAccess::store_line_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line_bind_compat_78289, DEFVAL(","));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_string", "string"), &FileAccess::store_string_bind_compat_78289);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_var", "value", "full_objects"), &FileAccess::store_var_bind_compat_78289, DEFVAL(false));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("store_pascal_string", "string"), &FileAccess::store_pascal_string_bind_compat_78289);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -29,6 +29,7 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "file_access.h"
|
||||
#include "file_access.compat.inc"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/crypto/crypto_core.h"
|
||||
|
|
@ -37,6 +38,7 @@
|
|||
#include "core/io/file_access_pack.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/os/time.h"
|
||||
|
||||
FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = {};
|
||||
|
||||
|
|
@ -59,11 +61,9 @@ bool FileAccess::exists(const String &p_name) {
|
|||
return true;
|
||||
}
|
||||
|
||||
Ref<FileAccess> f = open(p_name, READ);
|
||||
if (f.is_null()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// Using file_exists because it's faster than trying to open the file.
|
||||
Ref<FileAccess> ret = create_for_path(p_name);
|
||||
return ret->file_exists(p_name);
|
||||
}
|
||||
|
||||
void FileAccess::_set_access_type(AccessType p_access) {
|
||||
|
|
@ -72,7 +72,7 @@ void FileAccess::_set_access_type(AccessType p_access) {
|
|||
|
||||
Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
|
||||
Ref<FileAccess> ret;
|
||||
if (p_path.begins_with("res://")) {
|
||||
if (p_path.begins_with("res://") || p_path.begins_with("uid://")) {
|
||||
ret = create(ACCESS_RESOURCES);
|
||||
} else if (p_path.begins_with("user://")) {
|
||||
ret = create(ACCESS_USERDATA);
|
||||
|
|
@ -85,6 +85,79 @@ Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
Ref<FileAccess> FileAccess::create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep, Error *r_error) {
|
||||
const String ERROR_COMMON_PREFIX = "Error while creating temporary file";
|
||||
|
||||
if (!p_prefix.is_valid_filename()) {
|
||||
*r_error = ERR_FILE_BAD_PATH;
|
||||
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid prefix.)", ERROR_COMMON_PREFIX, p_prefix));
|
||||
}
|
||||
|
||||
if (!p_extension.is_valid_filename()) {
|
||||
*r_error = ERR_FILE_BAD_PATH;
|
||||
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: "%s" is not a valid extension.)", ERROR_COMMON_PREFIX, p_extension));
|
||||
}
|
||||
|
||||
const String TEMP_DIR = OS::get_singleton()->get_temp_path();
|
||||
String extension = p_extension.trim_prefix(".");
|
||||
|
||||
uint32_t suffix_i = 0;
|
||||
String path;
|
||||
while (true) {
|
||||
String datetime = Time::get_singleton()->get_datetime_string_from_system().replace("-", "").replace("T", "").replace(":", "");
|
||||
datetime += itos(Time::get_singleton()->get_ticks_usec());
|
||||
String suffix = datetime + (suffix_i > 0 ? itos(suffix_i) : "");
|
||||
path = TEMP_DIR.path_join((p_prefix.is_empty() ? "" : p_prefix + "-") + suffix + (extension.is_empty() ? "" : "." + extension));
|
||||
if (!DirAccess::exists(path)) {
|
||||
break;
|
||||
}
|
||||
suffix_i += 1;
|
||||
}
|
||||
|
||||
Error err;
|
||||
{
|
||||
// Create file first with WRITE mode.
|
||||
// Otherwise, it would fail to open with a READ mode.
|
||||
Ref<FileAccess> ret = FileAccess::open(path, FileAccess::ModeFlags::WRITE, &err);
|
||||
if (err != OK) {
|
||||
*r_error = err;
|
||||
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: could not create "%s".)", ERROR_COMMON_PREFIX, path));
|
||||
}
|
||||
ret->flush();
|
||||
}
|
||||
|
||||
// Open then the temp file with the correct mode flag.
|
||||
Ref<FileAccess> ret = FileAccess::open(path, p_mode_flags, &err);
|
||||
if (err != OK) {
|
||||
*r_error = err;
|
||||
ERR_FAIL_V_MSG(Ref<FileAccess>(), vformat(R"(%s: could not open "%s".)", ERROR_COMMON_PREFIX, path));
|
||||
}
|
||||
if (ret.is_valid()) {
|
||||
ret->_is_temp_file = true;
|
||||
ret->_temp_keep_after_use = p_keep;
|
||||
ret->_temp_path = ret->get_path_absolute();
|
||||
}
|
||||
|
||||
*r_error = OK;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Ref<FileAccess> FileAccess::_create_temp(int p_mode_flags, const String &p_prefix, const String &p_extension, bool p_keep) {
|
||||
return create_temp(p_mode_flags, p_prefix, p_extension, p_keep, &last_file_open_error);
|
||||
}
|
||||
|
||||
void FileAccess::_delete_temp() {
|
||||
if (!_is_temp_file || _temp_keep_after_use) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FileAccess::exists(_temp_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DirAccess::remove_absolute(_temp_path);
|
||||
}
|
||||
|
||||
Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
|
||||
return open_internal(p_path, p_mode_flags);
|
||||
}
|
||||
|
|
@ -126,7 +199,7 @@ Ref<FileAccess> FileAccess::_open(const String &p_path, ModeFlags p_mode_flags)
|
|||
return fa;
|
||||
}
|
||||
|
||||
Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
|
||||
Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv) {
|
||||
Ref<FileAccess> fa = _open(p_path, p_mode_flags);
|
||||
if (fa.is_null()) {
|
||||
return fa;
|
||||
|
|
@ -134,7 +207,7 @@ Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mod
|
|||
|
||||
Ref<FileAccessEncrypted> fae;
|
||||
fae.instantiate();
|
||||
Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ);
|
||||
Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ, true, p_iv);
|
||||
last_file_open_error = err;
|
||||
if (err) {
|
||||
return Ref<FileAccess>();
|
||||
|
|
@ -184,13 +257,17 @@ FileAccess::AccessType FileAccess::get_access_type() const {
|
|||
}
|
||||
|
||||
String FileAccess::fix_path(const String &p_path) const {
|
||||
//helper used by file accesses that use a single filesystem
|
||||
// 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("uid://")) {
|
||||
r_path = ResourceUID::uid_to_path(r_path);
|
||||
}
|
||||
|
||||
if (r_path.begins_with("res://")) {
|
||||
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
|
||||
if (!resource_path.is_empty()) {
|
||||
|
|
@ -225,59 +302,48 @@ String FileAccess::fix_path(const String &p_path) const {
|
|||
}
|
||||
|
||||
/* these are all implemented for ease of porting, then can later be optimized */
|
||||
uint8_t FileAccess::get_8() const {
|
||||
uint8_t data = 0;
|
||||
get_buffer(&data, sizeof(uint8_t));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
uint16_t FileAccess::get_16() const {
|
||||
uint16_t res;
|
||||
uint8_t a, b;
|
||||
|
||||
a = get_8();
|
||||
b = get_8();
|
||||
uint16_t data = 0;
|
||||
get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint16_t));
|
||||
|
||||
if (big_endian) {
|
||||
SWAP(a, b);
|
||||
data = BSWAP16(data);
|
||||
}
|
||||
|
||||
res = b;
|
||||
res <<= 8;
|
||||
res |= a;
|
||||
|
||||
return res;
|
||||
return data;
|
||||
}
|
||||
|
||||
uint32_t FileAccess::get_32() const {
|
||||
uint32_t res;
|
||||
uint16_t a, b;
|
||||
|
||||
a = get_16();
|
||||
b = get_16();
|
||||
uint32_t data = 0;
|
||||
get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint32_t));
|
||||
|
||||
if (big_endian) {
|
||||
SWAP(a, b);
|
||||
data = BSWAP32(data);
|
||||
}
|
||||
|
||||
res = b;
|
||||
res <<= 16;
|
||||
res |= a;
|
||||
|
||||
return res;
|
||||
return data;
|
||||
}
|
||||
|
||||
uint64_t FileAccess::get_64() const {
|
||||
uint64_t res;
|
||||
uint32_t a, b;
|
||||
|
||||
a = get_32();
|
||||
b = get_32();
|
||||
uint64_t data = 0;
|
||||
get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint64_t));
|
||||
|
||||
if (big_endian) {
|
||||
SWAP(a, b);
|
||||
data = BSWAP64(data);
|
||||
}
|
||||
|
||||
res = b;
|
||||
res <<= 32;
|
||||
res |= a;
|
||||
return data;
|
||||
}
|
||||
|
||||
return res;
|
||||
float FileAccess::get_half() const {
|
||||
return Math::half_to_float(get_16());
|
||||
}
|
||||
|
||||
float FileAccess::get_float() const {
|
||||
|
|
@ -317,7 +383,7 @@ double FileAccess::get_double() const {
|
|||
String FileAccess::get_token() const {
|
||||
CharString token;
|
||||
|
||||
char32_t c = get_8();
|
||||
uint8_t c = get_8();
|
||||
|
||||
while (!eof_reached()) {
|
||||
if (c <= ' ') {
|
||||
|
|
@ -325,7 +391,7 @@ String FileAccess::get_token() const {
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
token += c;
|
||||
token += char(c);
|
||||
}
|
||||
c = get_8();
|
||||
}
|
||||
|
|
@ -382,14 +448,14 @@ public:
|
|||
String FileAccess::get_line() const {
|
||||
CharBuffer line;
|
||||
|
||||
char32_t c = get_8();
|
||||
uint8_t c = get_8();
|
||||
|
||||
while (!eof_reached()) {
|
||||
if (c == '\n' || c == '\0') {
|
||||
if (c == '\n' || c == '\0' || get_error() != OK) {
|
||||
line.push_back(0);
|
||||
return String::utf8(line.get_data());
|
||||
} else if (c != '\r') {
|
||||
line.push_back(c);
|
||||
line.push_back(char(c));
|
||||
}
|
||||
|
||||
c = get_8();
|
||||
|
|
@ -467,17 +533,6 @@ String FileAccess::get_as_text(bool p_skip_cr) const {
|
|||
return text;
|
||||
}
|
||||
|
||||
uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||
|
||||
uint64_t i = 0;
|
||||
for (i = 0; i < p_length && !eof_reached(); i++) {
|
||||
p_dst[i] = get_8();
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
|
||||
Vector<uint8_t> data;
|
||||
|
||||
|
|
@ -487,10 +542,10 @@ Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const {
|
|||
}
|
||||
|
||||
Error err = data.resize(p_length);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, data, vformat("Can't resize data to %d elements.", p_length));
|
||||
|
||||
uint8_t *w = data.ptrw();
|
||||
int64_t len = get_buffer(&w[0], p_length);
|
||||
int64_t len = get_buffer(w, p_length);
|
||||
|
||||
if (len < p_length) {
|
||||
data.resize(len);
|
||||
|
|
@ -510,70 +565,60 @@ String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
|
|||
w[len] = 0;
|
||||
|
||||
String s;
|
||||
s.parse_utf8((const char *)w, -1, p_skip_cr);
|
||||
s.parse_utf8((const char *)w, len, p_skip_cr);
|
||||
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);
|
||||
bool FileAccess::store_8(uint8_t p_dest) {
|
||||
return store_buffer(&p_dest, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
void FileAccess::store_32(uint32_t p_dest) {
|
||||
uint16_t a, b;
|
||||
|
||||
a = p_dest & 0xFFFF;
|
||||
b = p_dest >> 16;
|
||||
|
||||
bool FileAccess::store_16(uint16_t p_dest) {
|
||||
if (big_endian) {
|
||||
SWAP(a, b);
|
||||
p_dest = BSWAP16(p_dest);
|
||||
}
|
||||
|
||||
store_16(a);
|
||||
store_16(b);
|
||||
return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint16_t));
|
||||
}
|
||||
|
||||
void FileAccess::store_64(uint64_t p_dest) {
|
||||
uint32_t a, b;
|
||||
|
||||
a = p_dest & 0xFFFFFFFF;
|
||||
b = p_dest >> 32;
|
||||
|
||||
bool FileAccess::store_32(uint32_t p_dest) {
|
||||
if (big_endian) {
|
||||
SWAP(a, b);
|
||||
p_dest = BSWAP32(p_dest);
|
||||
}
|
||||
|
||||
store_32(a);
|
||||
store_32(b);
|
||||
return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void FileAccess::store_real(real_t p_real) {
|
||||
bool FileAccess::store_64(uint64_t p_dest) {
|
||||
if (big_endian) {
|
||||
p_dest = BSWAP64(p_dest);
|
||||
}
|
||||
|
||||
return store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint64_t));
|
||||
}
|
||||
|
||||
bool FileAccess::store_real(real_t p_real) {
|
||||
if constexpr (sizeof(real_t) == 4) {
|
||||
store_float(p_real);
|
||||
return store_float(p_real);
|
||||
} else {
|
||||
store_double(p_real);
|
||||
return store_double(p_real);
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccess::store_float(float p_dest) {
|
||||
bool FileAccess::store_half(float p_dest) {
|
||||
return store_16(Math::make_half_float(p_dest));
|
||||
}
|
||||
|
||||
bool FileAccess::store_float(float p_dest) {
|
||||
MarshallFloat m;
|
||||
m.f = p_dest;
|
||||
store_32(m.i);
|
||||
return store_32(m.i);
|
||||
}
|
||||
|
||||
void FileAccess::store_double(double p_dest) {
|
||||
bool FileAccess::store_double(double p_dest) {
|
||||
MarshallDouble m;
|
||||
m.d = p_dest;
|
||||
store_64(m.l);
|
||||
return store_64(m.l);
|
||||
}
|
||||
|
||||
uint64_t FileAccess::get_modified_time(const String &p_file) {
|
||||
|
|
@ -582,7 +627,7 @@ uint64_t FileAccess::get_modified_time(const String &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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file));
|
||||
|
||||
uint64_t mt = fa->_get_modified_time(p_file);
|
||||
return mt;
|
||||
|
|
@ -594,7 +639,7 @@ BitField<FileAccess::UnixPermissionFlags> FileAccess::get_unix_permissions(const
|
|||
}
|
||||
|
||||
Ref<FileAccess> fa = create_for_path(p_file);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, "Cannot create FileAccess for path '" + p_file + "'.");
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), 0, vformat("Cannot create FileAccess for path '%s'.", p_file));
|
||||
|
||||
return fa->_get_unix_permissions(p_file);
|
||||
}
|
||||
|
|
@ -605,7 +650,7 @@ Error FileAccess::set_unix_permissions(const String &p_file, BitField<FileAccess
|
|||
}
|
||||
|
||||
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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
|
||||
|
||||
Error err = fa->_set_unix_permissions(p_file, p_permissions);
|
||||
return err;
|
||||
|
|
@ -617,7 +662,7 @@ bool FileAccess::get_hidden_attribute(const String &p_file) {
|
|||
}
|
||||
|
||||
Ref<FileAccess> fa = create_for_path(p_file);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'.");
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file));
|
||||
|
||||
return fa->_get_hidden_attribute(p_file);
|
||||
}
|
||||
|
|
@ -628,7 +673,7 @@ Error FileAccess::set_hidden_attribute(const String &p_file, bool p_hidden) {
|
|||
}
|
||||
|
||||
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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
|
||||
|
||||
Error err = fa->_set_hidden_attribute(p_file, p_hidden);
|
||||
return err;
|
||||
|
|
@ -640,7 +685,7 @@ bool FileAccess::get_read_only_attribute(const String &p_file) {
|
|||
}
|
||||
|
||||
Ref<FileAccess> fa = create_for_path(p_file);
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, "Cannot create FileAccess for path '" + p_file + "'.");
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), false, vformat("Cannot create FileAccess for path '%s'.", p_file));
|
||||
|
||||
return fa->_get_read_only_attribute(p_file);
|
||||
}
|
||||
|
|
@ -651,25 +696,24 @@ Error FileAccess::set_read_only_attribute(const String &p_file, bool p_ro) {
|
|||
}
|
||||
|
||||
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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(fa.is_null(), ERR_CANT_CREATE, vformat("Cannot create FileAccess for path '%s'.", p_file));
|
||||
|
||||
Error err = fa->_set_read_only_attribute(p_file, p_ro);
|
||||
return err;
|
||||
}
|
||||
|
||||
void FileAccess::store_string(const String &p_string) {
|
||||
bool FileAccess::store_string(const String &p_string) {
|
||||
if (p_string.length() == 0) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
CharString cs = p_string.utf8();
|
||||
store_buffer((uint8_t *)&cs[0], cs.length());
|
||||
return store_buffer((uint8_t *)&cs[0], cs.length());
|
||||
}
|
||||
|
||||
void FileAccess::store_pascal_string(const String &p_string) {
|
||||
bool 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());
|
||||
return store_32(cs.length()) && store_buffer((uint8_t *)&cs[0], cs.length());
|
||||
}
|
||||
|
||||
String FileAccess::get_pascal_string() {
|
||||
|
|
@ -680,24 +724,23 @@ String FileAccess::get_pascal_string() {
|
|||
cs[sl] = 0;
|
||||
|
||||
String ret;
|
||||
ret.parse_utf8(cs.ptr());
|
||||
ret.parse_utf8(cs.ptr(), sl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FileAccess::store_line(const String &p_line) {
|
||||
store_string(p_line);
|
||||
store_8('\n');
|
||||
bool FileAccess::store_line(const String &p_line) {
|
||||
return 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);
|
||||
bool FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
|
||||
ERR_FAIL_COND_V(p_delim.length() != 1, false);
|
||||
|
||||
String line = "";
|
||||
int size = p_values.size();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
String value = p_values[i];
|
||||
|
||||
if (value.contains("\"") || value.contains(p_delim) || value.contains("\n")) {
|
||||
if (value.contains_char('"') || value.contains(p_delim) || value.contains_char('\n')) {
|
||||
value = "\"" + value.replace("\"", "\"\"") + "\"";
|
||||
}
|
||||
if (i < size - 1) {
|
||||
|
|
@ -707,41 +750,43 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_
|
|||
line += value;
|
||||
}
|
||||
|
||||
store_line(line);
|
||||
return 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]);
|
||||
}
|
||||
}
|
||||
|
||||
void FileAccess::store_buffer(const Vector<uint8_t> &p_buffer) {
|
||||
bool FileAccess::store_buffer(const Vector<uint8_t> &p_buffer) {
|
||||
uint64_t len = p_buffer.size();
|
||||
if (len == 0) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t *r = p_buffer.ptr();
|
||||
|
||||
store_buffer(&r[0], len);
|
||||
return store_buffer(r, len);
|
||||
}
|
||||
|
||||
void FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
|
||||
bool FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_COND_V(!p_src && p_length > 0, false);
|
||||
for (uint64_t i = 0; i < p_length; i++) {
|
||||
if (unlikely(!store_8(p_src[i]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
|
||||
int len;
|
||||
Error err = encode_variant(p_var, nullptr, len, p_full_objects);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant.");
|
||||
|
||||
Vector<uint8_t> buff;
|
||||
buff.resize(len);
|
||||
|
||||
uint8_t *w = buff.ptrw();
|
||||
err = encode_variant(p_var, &w[0], len, p_full_objects);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, false, "Error when trying to encode Variant.");
|
||||
|
||||
store_32(len);
|
||||
store_buffer(buff);
|
||||
return store_32(uint32_t(len)) && store_buffer(buff);
|
||||
}
|
||||
|
||||
Vector<uint8_t> FileAccess::get_file_as_bytes(const String &p_path, Error *r_error) {
|
||||
|
|
@ -750,7 +795,7 @@ Vector<uint8_t> FileAccess::get_file_as_bytes(const String &p_path, Error *r_err
|
|||
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) + "'.");
|
||||
ERR_FAIL_V_MSG(Vector<uint8_t>(), vformat("Can't open file from path '%s'.", String(p_path)));
|
||||
}
|
||||
Vector<uint8_t> data;
|
||||
data.resize(f->get_length());
|
||||
|
|
@ -768,7 +813,7 @@ String FileAccess::get_file_as_string(const String &p_path, Error *r_error) {
|
|||
if (r_error) {
|
||||
return String();
|
||||
}
|
||||
ERR_FAIL_V_MSG(String(), "Can't get file as string from path '" + String(p_path) + "'.");
|
||||
ERR_FAIL_V_MSG(String(), vformat("Can't get file as string from path '%s'.", String(p_path)));
|
||||
}
|
||||
|
||||
String ret;
|
||||
|
|
@ -859,10 +904,11 @@ String FileAccess::get_sha256(const String &p_file) {
|
|||
|
||||
void FileAccess::_bind_methods() {
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("open", "path", "flags"), &FileAccess::_open);
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::open_encrypted);
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key", "iv"), &FileAccess::open_encrypted, DEFVAL(Vector<uint8_t>()));
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass);
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0));
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error);
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("create_temp", "mode_flags", "prefix", "extension", "keep"), &FileAccess::_create_temp, DEFVAL(""), DEFVAL(""), DEFVAL(false));
|
||||
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_bytes", "path"), &FileAccess::_get_file_as_bytes);
|
||||
ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_string", "path"), &FileAccess::_get_file_as_string);
|
||||
|
|
@ -881,6 +927,7 @@ void FileAccess::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_16"), &FileAccess::get_16);
|
||||
ClassDB::bind_method(D_METHOD("get_32"), &FileAccess::get_32);
|
||||
ClassDB::bind_method(D_METHOD("get_64"), &FileAccess::get_64);
|
||||
ClassDB::bind_method(D_METHOD("get_half"), &FileAccess::get_half);
|
||||
ClassDB::bind_method(D_METHOD("get_float"), &FileAccess::get_float);
|
||||
ClassDB::bind_method(D_METHOD("get_double"), &FileAccess::get_double);
|
||||
ClassDB::bind_method(D_METHOD("get_real"), &FileAccess::get_real);
|
||||
|
|
@ -899,10 +946,11 @@ void FileAccess::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("store_16", "value"), &FileAccess::store_16);
|
||||
ClassDB::bind_method(D_METHOD("store_32", "value"), &FileAccess::store_32);
|
||||
ClassDB::bind_method(D_METHOD("store_64", "value"), &FileAccess::store_64);
|
||||
ClassDB::bind_method(D_METHOD("store_half", "value"), &FileAccess::store_half);
|
||||
ClassDB::bind_method(D_METHOD("store_float", "value"), &FileAccess::store_float);
|
||||
ClassDB::bind_method(D_METHOD("store_double", "value"), &FileAccess::store_double);
|
||||
ClassDB::bind_method(D_METHOD("store_real", "value"), &FileAccess::store_real);
|
||||
ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), (void(FileAccess::*)(const Vector<uint8_t> &)) & FileAccess::store_buffer);
|
||||
ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), (bool(FileAccess::*)(const Vector<uint8_t> &)) & FileAccess::store_buffer);
|
||||
ClassDB::bind_method(D_METHOD("store_line", "line"), &FileAccess::store_line);
|
||||
ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line, DEFVAL(","));
|
||||
ClassDB::bind_method(D_METHOD("store_string", "string"), &FileAccess::store_string);
|
||||
|
|
@ -950,3 +998,7 @@ void FileAccess::_bind_methods() {
|
|||
BIND_BITFIELD_FLAG(UNIX_SET_GROUP_ID);
|
||||
BIND_BITFIELD_FLAG(UNIX_RESTRICTED_DELETE);
|
||||
}
|
||||
|
||||
FileAccess::~FileAccess() {
|
||||
_delete_temp();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class FileAccess : public RefCounted {
|
|||
GDCLASS(FileAccess, RefCounted);
|
||||
|
||||
public:
|
||||
enum AccessType {
|
||||
enum AccessType : int32_t {
|
||||
ACCESS_RESOURCES,
|
||||
ACCESS_USERDATA,
|
||||
ACCESS_FILESYSTEM,
|
||||
|
|
@ -54,14 +54,14 @@ public:
|
|||
ACCESS_MAX
|
||||
};
|
||||
|
||||
enum ModeFlags {
|
||||
enum ModeFlags : int32_t {
|
||||
READ = 1,
|
||||
WRITE = 2,
|
||||
READ_WRITE = 3,
|
||||
WRITE_READ = 7,
|
||||
};
|
||||
|
||||
enum UnixPermissionFlags {
|
||||
enum UnixPermissionFlags : int32_t {
|
||||
UNIX_EXECUTE_OTHER = 0x001,
|
||||
UNIX_WRITE_OTHER = 0x002,
|
||||
UNIX_READ_OTHER = 0x004,
|
||||
|
|
@ -76,7 +76,7 @@ public:
|
|||
UNIX_SET_USER_ID = 0x800,
|
||||
};
|
||||
|
||||
enum CompressionMode {
|
||||
enum CompressionMode : int32_t {
|
||||
COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
|
||||
COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
|
||||
COMPRESSION_ZSTD = Compression::MODE_ZSTD,
|
||||
|
|
@ -109,6 +109,27 @@ protected:
|
|||
|
||||
static FileCloseFailNotify close_fail_notify;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
static Ref<FileAccess> _open_encrypted_bind_compat_98918(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key);
|
||||
|
||||
void store_8_bind_compat_78289(uint8_t p_dest);
|
||||
void store_16_bind_compat_78289(uint16_t p_dest);
|
||||
void store_32_bind_compat_78289(uint32_t p_dest);
|
||||
void store_64_bind_compat_78289(uint64_t p_dest);
|
||||
void store_buffer_bind_compat_78289(const Vector<uint8_t> &p_buffer);
|
||||
void store_var_bind_compat_78289(const Variant &p_var, bool p_full_objects = false);
|
||||
void store_half_bind_compat_78289(float p_dest);
|
||||
void store_float_bind_compat_78289(float p_dest);
|
||||
void store_double_bind_compat_78289(double p_dest);
|
||||
void store_real_bind_compat_78289(real_t p_real);
|
||||
void store_string_bind_compat_78289(const String &p_string);
|
||||
void store_line_bind_compat_78289(const String &p_line);
|
||||
void store_csv_line_bind_compat_78289(const Vector<String> &p_values, const String &p_delim = ",");
|
||||
void store_pascal_string_bind_compat_78289(const String &p_string);
|
||||
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
private:
|
||||
static bool backup_save;
|
||||
thread_local static Error last_file_open_error;
|
||||
|
|
@ -122,6 +143,13 @@ private:
|
|||
|
||||
static Ref<FileAccess> _open(const String &p_path, ModeFlags p_mode_flags);
|
||||
|
||||
bool _is_temp_file = false;
|
||||
bool _temp_keep_after_use = false;
|
||||
String _temp_path;
|
||||
void _delete_temp();
|
||||
|
||||
static Ref<FileAccess> _create_temp(int p_mode_flags, const String &p_prefix = "", const String &p_extension = "", bool p_keep = false);
|
||||
|
||||
public:
|
||||
static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; }
|
||||
|
||||
|
|
@ -137,18 +165,19 @@ public:
|
|||
|
||||
virtual bool eof_reached() const = 0; ///< reading passed EOF
|
||||
|
||||
virtual uint8_t get_8() const = 0; ///< get a byte
|
||||
virtual uint8_t get_8() const; ///< 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_half() const;
|
||||
virtual float get_float() const;
|
||||
virtual double get_double() const;
|
||||
virtual real_t get_real() const;
|
||||
|
||||
Variant get_var(bool p_allow_objects = false) const;
|
||||
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const = 0; ///< get an array of bytes, needs to be overwritten by children.
|
||||
Vector<uint8_t> get_buffer(int64_t p_length) const;
|
||||
virtual String get_line() const;
|
||||
virtual String get_token() const;
|
||||
|
|
@ -157,6 +186,7 @@ public:
|
|||
virtual String get_as_utf8_string(bool p_skip_cr = false) 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.
|
||||
|
|
@ -168,26 +198,27 @@ public:
|
|||
|
||||
virtual Error resize(int64_t p_length) = 0;
|
||||
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 bool store_8(uint8_t p_dest); ///< store a byte
|
||||
virtual bool store_16(uint16_t p_dest); ///< store 16 bits uint
|
||||
virtual bool store_32(uint32_t p_dest); ///< store 32 bits uint
|
||||
virtual bool 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 bool store_half(float p_dest);
|
||||
virtual bool store_float(float p_dest);
|
||||
virtual bool store_double(double p_dest);
|
||||
virtual bool 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 bool store_string(const String &p_string);
|
||||
virtual bool store_line(const String &p_line);
|
||||
virtual bool store_csv_line(const Vector<String> &p_values, const String &p_delim = ",");
|
||||
|
||||
virtual void store_pascal_string(const String &p_string);
|
||||
virtual bool 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
|
||||
void store_buffer(const Vector<uint8_t> &p_buffer);
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) = 0; ///< store an array of bytes, needs to be overwritten by children.
|
||||
bool store_buffer(const Vector<uint8_t> &p_buffer);
|
||||
|
||||
void store_var(const Variant &p_var, bool p_full_objects = false);
|
||||
bool store_var(const Variant &p_var, bool p_full_objects = false);
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
|
|
@ -198,8 +229,9 @@ public:
|
|||
static Ref<FileAccess> create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files.
|
||||
static Ref<FileAccess> create_for_path(const String &p_path);
|
||||
static Ref<FileAccess> open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files.
|
||||
static Ref<FileAccess> create_temp(int p_mode_flags, const String &p_prefix = "", const String &p_extension = "", bool p_keep = false, Error *r_error = nullptr);
|
||||
|
||||
static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key);
|
||||
static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key, const Vector<uint8_t> &p_iv = Vector<uint8_t>());
|
||||
static Ref<FileAccess> open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass);
|
||||
static Ref<FileAccess> open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ);
|
||||
static Error get_open_error();
|
||||
|
|
@ -215,8 +247,8 @@ public:
|
|||
static bool get_read_only_attribute(const String &p_file);
|
||||
static Error set_read_only_attribute(const String &p_file, bool p_ro);
|
||||
|
||||
static void set_backup_save(bool p_enable) { backup_save = p_enable; };
|
||||
static bool is_backup_save_enabled() { return backup_save; };
|
||||
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);
|
||||
|
|
@ -233,8 +265,9 @@ public:
|
|||
create_func[p_access] = _create_builtin<T>;
|
||||
}
|
||||
|
||||
public:
|
||||
FileAccess() {}
|
||||
virtual ~FileAccess() {}
|
||||
virtual ~FileAccess();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(FileAccess::CompressionMode);
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include "file_access_compressed.h"
|
||||
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, uint32_t p_block_size) {
|
||||
magic = p_magic.ascii().get_data();
|
||||
magic = (magic + " ").substr(0, 4);
|
||||
|
|
@ -40,25 +38,13 @@ void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_
|
|||
block_size = p_block_size;
|
||||
}
|
||||
|
||||
#define WRITE_FIT(m_bytes) \
|
||||
{ \
|
||||
if (write_pos + (m_bytes) > write_max) { \
|
||||
write_max = write_pos + (m_bytes); \
|
||||
} \
|
||||
if (write_max > write_buffer_size) { \
|
||||
write_buffer_size = next_power_of_2(write_max); \
|
||||
buffer.resize(write_buffer_size); \
|
||||
write_ptr = buffer.ptrw(); \
|
||||
} \
|
||||
}
|
||||
|
||||
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.unref();
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Can't open compressed file '" + p_base->get_path() + "' with block size 0, it is corrupted.");
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, vformat("Can't open compressed file '%s' with block size 0, it is corrupted.", p_base->get_path()));
|
||||
}
|
||||
read_total = f->get_32();
|
||||
uint32_t bc = (read_total / block_size) + 1;
|
||||
|
|
@ -137,7 +123,7 @@ void FileAccessCompressed::_close() {
|
|||
f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //write header 4
|
||||
f->store_32(cmode); //write compression mode 4
|
||||
f->store_32(block_size); //write block size 4
|
||||
f->store_32(write_max); //max amount of data written 4
|
||||
f->store_32(uint32_t(write_max)); //max amount of data written 4
|
||||
uint32_t bc = (write_max / block_size) + 1;
|
||||
|
||||
for (uint32_t i = 0; i < bc; i++) {
|
||||
|
|
@ -159,7 +145,7 @@ void FileAccessCompressed::_close() {
|
|||
|
||||
f->seek(16); //ok write block sizes
|
||||
for (uint32_t i = 0; i < bc; i++) {
|
||||
f->store_32(block_sizes[i]);
|
||||
f->store_32(uint32_t(block_sizes[i]));
|
||||
}
|
||||
f->seek_end();
|
||||
f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //magic at the end too
|
||||
|
|
@ -260,38 +246,6 @@ bool FileAccessCompressed::eof_reached() const {
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t FileAccessCompressed::get_8() const {
|
||||
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) {
|
||||
read_eof = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ret = read_ptr[read_pos];
|
||||
|
||||
read_pos++;
|
||||
if (read_pos >= read_block_size) {
|
||||
read_block++;
|
||||
|
||||
if (read_block < read_block_count) {
|
||||
//read another block of compressed data
|
||||
f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize);
|
||||
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;
|
||||
|
||||
} else {
|
||||
read_block--;
|
||||
at_end = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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.is_null(), -1, "File must be opened before use.");
|
||||
|
|
@ -341,12 +295,25 @@ void FileAccessCompressed::flush() {
|
|||
// compressed files keep data in memory till close()
|
||||
}
|
||||
|
||||
void FileAccessCompressed::store_8(uint8_t p_dest) {
|
||||
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.");
|
||||
bool FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), false, "File must be opened before use.");
|
||||
ERR_FAIL_COND_V_MSG(!writing, false, "File has not been opened in write mode.");
|
||||
|
||||
WRITE_FIT(1);
|
||||
write_ptr[write_pos++] = p_dest;
|
||||
if (write_pos + (p_length) > write_max) {
|
||||
write_max = write_pos + (p_length);
|
||||
}
|
||||
if (write_max > write_buffer_size) {
|
||||
write_buffer_size = next_power_of_2(write_max);
|
||||
ERR_FAIL_COND_V(buffer.resize(write_buffer_size) != OK, false);
|
||||
write_ptr = buffer.ptrw();
|
||||
}
|
||||
|
||||
if (p_length) {
|
||||
memcpy(write_ptr + write_pos, p_src, p_length);
|
||||
}
|
||||
|
||||
write_pos += p_length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileAccessCompressed::file_exists(const String &p_name) {
|
||||
|
|
|
|||
|
|
@ -83,14 +83,13 @@ public:
|
|||
|
||||
virtual bool eof_reached() const override; ///< reading passed EOF
|
||||
|
||||
virtual uint8_t get_8() const override; ///< get a byte
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||
|
||||
virtual Error get_error() const override; ///< get last error
|
||||
|
||||
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
|
||||
virtual void flush() override;
|
||||
virtual void store_8(uint8_t p_dest) override; ///< store a byte
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
|
||||
|
||||
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
|
||||
|
||||
|
|
|
|||
|
|
@ -31,13 +31,10 @@
|
|||
#include "file_access_encrypted.h"
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
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.");
|
||||
Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic, const Vector<uint8_t> &p_iv) {
|
||||
ERR_FAIL_COND_V_MSG(file.is_valid(), ERR_ALREADY_IN_USE, vformat("Can't open file while another file from path '%s' is open.", file->get_path_absolute()));
|
||||
ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER);
|
||||
|
||||
pos = 0;
|
||||
|
|
@ -49,6 +46,16 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
|
|||
writing = true;
|
||||
file = p_base;
|
||||
key = p_key;
|
||||
if (p_iv.is_empty()) {
|
||||
iv.resize(16);
|
||||
CryptoCore::RandomGenerator rng;
|
||||
ERR_FAIL_COND_V_MSG(rng.init(), FAILED, "Failed to initialize random number generator.");
|
||||
Error err = rng.get_random_bytes(iv.ptrw(), 16);
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
} else {
|
||||
ERR_FAIL_COND_V(p_iv.size() != 16, ERR_INVALID_PARAMETER);
|
||||
iv = p_iv;
|
||||
}
|
||||
|
||||
} else if (p_mode == MODE_READ) {
|
||||
writing = false;
|
||||
|
|
@ -63,10 +70,8 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
|
|||
p_base->get_buffer(md5d, 16);
|
||||
length = p_base->get_64();
|
||||
|
||||
unsigned char iv[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
iv[i] = p_base->get_8();
|
||||
}
|
||||
iv.resize(16);
|
||||
p_base->get_buffer(iv.ptrw(), 16);
|
||||
|
||||
base = p_base->get_position();
|
||||
ERR_FAIL_COND_V(p_base->get_length() < base + length, ERR_FILE_CORRUPT);
|
||||
|
|
@ -83,7 +88,7 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
|
|||
CryptoCore::AESContext ctx;
|
||||
|
||||
ctx.set_encode_key(key.ptrw(), 256); // Due to the nature of CFB, same key schedule is used for both encryption and decryption!
|
||||
ctx.decrypt_cfb(ds, iv, data.ptrw(), data.ptrw());
|
||||
ctx.decrypt_cfb(ds, iv.ptrw(), data.ptrw(), data.ptrw());
|
||||
}
|
||||
|
||||
data.resize(length);
|
||||
|
|
@ -145,14 +150,9 @@ void FileAccessEncrypted::_close() {
|
|||
|
||||
file->store_buffer(hash, 16);
|
||||
file->store_64(data.size());
|
||||
file->store_buffer(iv.ptr(), 16);
|
||||
|
||||
unsigned char iv[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
iv[i] = Math::rand() % 256;
|
||||
file->store_8(iv[i]);
|
||||
}
|
||||
|
||||
ctx.encrypt_cfb(len, iv, compressed.ptrw(), compressed.ptrw());
|
||||
ctx.encrypt_cfb(len, iv.ptrw(), compressed.ptrw(), compressed.ptrw());
|
||||
|
||||
file->store_buffer(compressed.ptr(), compressed.size());
|
||||
data.clear();
|
||||
|
|
@ -162,7 +162,7 @@ void FileAccessEncrypted::_close() {
|
|||
}
|
||||
|
||||
bool FileAccessEncrypted::is_open() const {
|
||||
return file != nullptr;
|
||||
return file.is_valid();
|
||||
}
|
||||
|
||||
String FileAccessEncrypted::get_path() const {
|
||||
|
|
@ -206,26 +206,19 @@ bool FileAccessEncrypted::eof_reached() const {
|
|||
return eofed;
|
||||
}
|
||||
|
||||
uint8_t FileAccessEncrypted::get_8() const {
|
||||
ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode.");
|
||||
if (pos >= get_length()) {
|
||||
eofed = true;
|
||||
uint64_t FileAccessEncrypted::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode.");
|
||||
|
||||
if (!p_length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t b = data[pos];
|
||||
pos++;
|
||||
return b;
|
||||
}
|
||||
|
||||
uint64_t FileAccessEncrypted::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(writing, -1, "File has not been opened in read mode.");
|
||||
ERR_FAIL_NULL_V(p_dst, -1);
|
||||
|
||||
uint64_t to_copy = MIN(p_length, get_length() - pos);
|
||||
for (uint64_t i = 0; i < to_copy; i++) {
|
||||
p_dst[i] = data[pos++];
|
||||
}
|
||||
|
||||
memcpy(p_dst, data.ptr() + pos, to_copy);
|
||||
pos += to_copy;
|
||||
|
||||
if (to_copy < p_length) {
|
||||
eofed = true;
|
||||
|
|
@ -238,21 +231,23 @@ Error FileAccessEncrypted::get_error() const {
|
|||
return eofed ? ERR_FILE_EOF : OK;
|
||||
}
|
||||
|
||||
void FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
|
||||
ERR_FAIL_COND(!p_src && p_length > 0);
|
||||
bool FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_COND_V_MSG(!writing, false, "File has not been opened in write mode.");
|
||||
|
||||
if (pos < get_length()) {
|
||||
for (uint64_t i = 0; i < p_length; i++) {
|
||||
store_8(p_src[i]);
|
||||
}
|
||||
} else if (pos == get_length()) {
|
||||
data.resize(pos + p_length);
|
||||
for (uint64_t i = 0; i < p_length; i++) {
|
||||
data.write[pos + i] = p_src[i];
|
||||
}
|
||||
pos += p_length;
|
||||
if (!p_length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL_V(p_src, false);
|
||||
|
||||
if (pos + p_length >= get_length()) {
|
||||
ERR_FAIL_COND_V(data.resize(pos + p_length) != OK, false);
|
||||
}
|
||||
|
||||
memcpy(data.ptrw() + pos, p_src, p_length);
|
||||
pos += p_length;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileAccessEncrypted::flush() {
|
||||
|
|
@ -261,18 +256,6 @@ void FileAccessEncrypted::flush() {
|
|||
// encrypted files keep data in memory till close()
|
||||
}
|
||||
|
||||
void FileAccessEncrypted::store_8(uint8_t p_dest) {
|
||||
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
|
||||
|
||||
if (pos < get_length()) {
|
||||
data.write[pos] = p_dest;
|
||||
pos++;
|
||||
} else if (pos == get_length()) {
|
||||
data.push_back(p_dest);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileAccessEncrypted::file_exists(const String &p_name) {
|
||||
Ref<FileAccess> fa = FileAccess::open(p_name, FileAccess::READ);
|
||||
if (fa.is_null()) {
|
||||
|
|
|
|||
|
|
@ -37,13 +37,14 @@
|
|||
|
||||
class FileAccessEncrypted : public FileAccess {
|
||||
public:
|
||||
enum Mode {
|
||||
enum Mode : int32_t {
|
||||
MODE_READ,
|
||||
MODE_WRITE_AES256,
|
||||
MODE_MAX
|
||||
};
|
||||
|
||||
private:
|
||||
Vector<uint8_t> iv;
|
||||
Vector<uint8_t> key;
|
||||
bool writing = false;
|
||||
Ref<FileAccess> file;
|
||||
|
|
@ -57,9 +58,11 @@ private:
|
|||
void _close();
|
||||
|
||||
public:
|
||||
Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true);
|
||||
Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true, const Vector<uint8_t> &p_iv = Vector<uint8_t>());
|
||||
Error open_and_parse_password(Ref<FileAccess> p_base, const String &p_key, Mode p_mode);
|
||||
|
||||
Vector<uint8_t> get_iv() const { return iv; }
|
||||
|
||||
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
|
||||
virtual bool is_open() const override; ///< true when file is open
|
||||
|
||||
|
|
@ -73,15 +76,13 @@ public:
|
|||
|
||||
virtual bool eof_reached() const override; ///< reading passed EOF
|
||||
|
||||
virtual uint8_t get_8() const override; ///< get a byte
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||
|
||||
virtual Error get_error() const override; ///< get last error
|
||||
|
||||
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
|
||||
virtual void flush() override;
|
||||
virtual void store_8(uint8_t p_dest) override; ///< store a byte
|
||||
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||
|
||||
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
|
||||
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@
|
|||
#include "file_access_memory.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/templates/rb_map.h"
|
||||
|
||||
static HashMap<String, Vector<uint8_t>> *files = nullptr;
|
||||
|
||||
|
|
@ -85,7 +83,7 @@ Error FileAccessMemory::open_internal(const String &p_path, int p_mode_flags) {
|
|||
//name = DirAccess::normalize_path(name);
|
||||
|
||||
HashMap<String, Vector<uint8_t>>::Iterator E = files->find(name);
|
||||
ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, "Can't find file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, vformat("Can't find file '%s'.", p_path));
|
||||
|
||||
data = E->value.ptrw();
|
||||
length = E->value.size();
|
||||
|
|
@ -122,18 +120,12 @@ bool FileAccessMemory::eof_reached() const {
|
|||
return pos >= length;
|
||||
}
|
||||
|
||||
uint8_t FileAccessMemory::get_8() const {
|
||||
uint8_t ret = 0;
|
||||
if (pos < length) {
|
||||
ret = data[pos];
|
||||
}
|
||||
++pos;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||
if (!p_length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL_V(p_dst, -1);
|
||||
ERR_FAIL_NULL_V(data, -1);
|
||||
|
||||
uint64_t left = length - pos;
|
||||
|
|
@ -157,20 +149,20 @@ void FileAccessMemory::flush() {
|
|||
ERR_FAIL_NULL(data);
|
||||
}
|
||||
|
||||
void FileAccessMemory::store_8(uint8_t p_byte) {
|
||||
ERR_FAIL_NULL(data);
|
||||
ERR_FAIL_COND(pos >= length);
|
||||
data[pos++] = p_byte;
|
||||
}
|
||||
bool FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
if (!p_length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL_V(p_src, false);
|
||||
|
||||
void FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_COND(!p_src && p_length > 0);
|
||||
uint64_t left = length - pos;
|
||||
uint64_t write = MIN(p_length, left);
|
||||
if (write < p_length) {
|
||||
WARN_PRINT("Writing less data than requested");
|
||||
}
|
||||
|
||||
memcpy(&data[pos], p_src, write);
|
||||
pos += write;
|
||||
|
||||
ERR_FAIL_COND_V_MSG(write < p_length, false, "Writing less data than requested.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,16 +55,13 @@ public:
|
|||
|
||||
virtual bool eof_reached() const override; ///< reading passed EOF
|
||||
|
||||
virtual uint8_t get_8() const override; ///< get a byte
|
||||
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; ///< get an array of bytes
|
||||
|
||||
virtual Error get_error() const override; ///< get last error
|
||||
|
||||
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
|
||||
virtual void flush() override;
|
||||
virtual void store_8(uint8_t p_byte) override; ///< store a byte
|
||||
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||
|
||||
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,6 @@
|
|||
#include "core/os/os.h"
|
||||
#include "core/version.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) {
|
||||
for (int i = 0; i < sources.size(); i++) {
|
||||
if (sources[i]->try_open_pack(p_path, p_replace_files, p_offset)) {
|
||||
|
|
@ -48,7 +46,7 @@ Error PackedData::add_pack(const String &p_path, bool p_replace_files, uint64_t
|
|||
}
|
||||
|
||||
void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) {
|
||||
String simplified_path = p_path.simplify_path();
|
||||
String simplified_path = p_path.simplify_path().trim_prefix("res://");
|
||||
PathMD5 pmd5(simplified_path.md5_buffer());
|
||||
|
||||
bool exists = files.has(pmd5);
|
||||
|
|
@ -68,13 +66,11 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
|
|||
}
|
||||
|
||||
if (!exists) {
|
||||
//search for dir
|
||||
String p = simplified_path.replace_first("res://", "");
|
||||
// Search for directory.
|
||||
PackedDir *cd = root;
|
||||
|
||||
if (p.contains("/")) { //in a subdir
|
||||
|
||||
Vector<String> ds = p.get_base_dir().split("/");
|
||||
if (simplified_path.contains_char('/')) { // In a subdirectory.
|
||||
Vector<String> ds = simplified_path.get_base_dir().split("/");
|
||||
|
||||
for (int j = 0; j < ds.size(); j++) {
|
||||
if (!cd->subdirs.has(ds[j])) {
|
||||
|
|
@ -89,19 +85,79 @@ void PackedData::add_path(const String &p_pkg_path, const String &p_path, uint64
|
|||
}
|
||||
}
|
||||
String filename = simplified_path.get_file();
|
||||
// Don't add as a file if the path points to a directory
|
||||
// Don't add as a file if the path points to a directory.
|
||||
if (!filename.is_empty()) {
|
||||
cd->files.insert(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PackedData::remove_path(const String &p_path) {
|
||||
String simplified_path = p_path.simplify_path().trim_prefix("res://");
|
||||
PathMD5 pmd5(simplified_path.md5_buffer());
|
||||
if (!files.has(pmd5)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Search for directory.
|
||||
PackedDir *cd = root;
|
||||
|
||||
if (simplified_path.contains_char('/')) { // In a subdirectory.
|
||||
Vector<String> ds = simplified_path.get_base_dir().split("/");
|
||||
|
||||
for (int j = 0; j < ds.size(); j++) {
|
||||
if (!cd->subdirs.has(ds[j])) {
|
||||
return; // Subdirectory does not exist, do not bother creating.
|
||||
} else {
|
||||
cd = cd->subdirs[ds[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cd->files.erase(simplified_path.get_file());
|
||||
|
||||
files.erase(pmd5);
|
||||
}
|
||||
|
||||
void PackedData::add_pack_source(PackSource *p_source) {
|
||||
if (p_source != nullptr) {
|
||||
sources.push_back(p_source);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *PackedData::get_file_hash(const String &p_path) {
|
||||
String simplified_path = p_path.simplify_path().trim_prefix("res://");
|
||||
PathMD5 pmd5(simplified_path.md5_buffer());
|
||||
HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
|
||||
if (!E) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return E->value.md5;
|
||||
}
|
||||
|
||||
HashSet<String> PackedData::get_file_paths() const {
|
||||
HashSet<String> file_paths;
|
||||
_get_file_paths(root, root->name, file_paths);
|
||||
return file_paths;
|
||||
}
|
||||
|
||||
void PackedData::_get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const {
|
||||
for (const String &E : p_dir->files) {
|
||||
r_paths.insert(p_parent_dir.path_join(E));
|
||||
}
|
||||
|
||||
for (const KeyValue<String, PackedDir *> &E : p_dir->subdirs) {
|
||||
_get_file_paths(E.value, p_parent_dir.path_join(E.key), r_paths);
|
||||
}
|
||||
}
|
||||
|
||||
void PackedData::clear() {
|
||||
files.clear();
|
||||
_free_packed_dirs(root);
|
||||
root = memnew(PackedDir);
|
||||
}
|
||||
|
||||
PackedData *PackedData::singleton = nullptr;
|
||||
|
||||
PackedData::PackedData() {
|
||||
|
|
@ -207,8 +263,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.
|
||||
|
||||
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) + ".");
|
||||
ERR_FAIL_COND_V_MSG(version != PACK_FORMAT_VERSION, false, vformat("Pack version unsupported: %d.", version));
|
||||
ERR_FAIL_COND_V_MSG(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false, vformat("Pack created with a newer version of the engine: %d.%d.", ver_major, ver_minor));
|
||||
|
||||
uint32_t pack_flags = f->get_32();
|
||||
uint64_t file_base = f->get_64();
|
||||
|
|
@ -251,15 +307,19 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files,
|
|||
cs[sl] = 0;
|
||||
|
||||
String path;
|
||||
path.parse_utf8(cs.ptr());
|
||||
path.parse_utf8(cs.ptr(), sl);
|
||||
|
||||
uint64_t ofs = file_base + f->get_64();
|
||||
uint64_t ofs = f->get_64();
|
||||
uint64_t size = f->get_64();
|
||||
uint8_t md5[16];
|
||||
f->get_buffer(md5, 16);
|
||||
uint32_t flags = f->get_32();
|
||||
|
||||
PackedData::get_singleton()->add_path(p_path, path, ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
|
||||
if (flags & PACK_FILE_REMOVAL) { // The file was removed.
|
||||
PackedData::get_singleton()->remove_path(path);
|
||||
} else {
|
||||
PackedData::get_singleton()->add_path(p_path, path, file_base + ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -271,6 +331,44 @@ Ref<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::Pack
|
|||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
bool PackedSourceDirectory::try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) {
|
||||
// Load with offset feature only supported for PCK files.
|
||||
ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with directories.");
|
||||
|
||||
if (p_path != "res://") {
|
||||
return false;
|
||||
}
|
||||
add_directory(p_path, p_replace_files);
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<FileAccess> PackedSourceDirectory::get_file(const String &p_path, PackedData::PackedFile *p_file) {
|
||||
Ref<FileAccess> ret = FileAccess::create_for_path(p_path);
|
||||
ret->reopen(p_path, FileAccess::READ);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PackedSourceDirectory::add_directory(const String &p_path, bool p_replace_files) {
|
||||
Ref<DirAccess> da = DirAccess::open(p_path);
|
||||
if (da.is_null()) {
|
||||
return;
|
||||
}
|
||||
da->set_include_hidden(true);
|
||||
|
||||
for (const String &file_name : da->get_files()) {
|
||||
String file_path = p_path.path_join(file_name);
|
||||
uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
PackedData::get_singleton()->add_path(p_path, file_path, 0, 0, md5, this, p_replace_files, false);
|
||||
}
|
||||
|
||||
for (const String &sub_dir_name : da->get_directories()) {
|
||||
String sub_dir_path = p_path.path_join(sub_dir_name);
|
||||
add_directory(sub_dir_path, p_replace_files);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
Error FileAccessPack::open_internal(const String &p_path, int p_mode_flags) {
|
||||
ERR_PRINT("Can't open pack-referenced file.");
|
||||
return ERR_UNAVAILABLE;
|
||||
|
|
@ -313,17 +411,6 @@ bool FileAccessPack::eof_reached() const {
|
|||
return eof;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pos++;
|
||||
return f->get_8();
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -366,12 +453,8 @@ void FileAccessPack::flush() {
|
|||
ERR_FAIL();
|
||||
}
|
||||
|
||||
void FileAccessPack::store_8(uint8_t p_dest) {
|
||||
ERR_FAIL();
|
||||
}
|
||||
|
||||
void FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL();
|
||||
bool FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_V(false);
|
||||
}
|
||||
|
||||
bool FileAccessPack::file_exists(const String &p_name) {
|
||||
|
|
@ -385,7 +468,7 @@ void FileAccessPack::close() {
|
|||
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.is_null(), "Can't open pack-referenced file '" + String(pf.pack) + "'.");
|
||||
ERR_FAIL_COND_MSG(f.is_null(), vformat("Can't open pack-referenced file '%s'.", String(pf.pack)));
|
||||
|
||||
f->seek(pf.offset);
|
||||
off = pf.offset;
|
||||
|
|
@ -393,7 +476,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil
|
|||
if (pf.encrypted) {
|
||||
Ref<FileAccessEncrypted> fae;
|
||||
fae.instantiate();
|
||||
ERR_FAIL_COND_MSG(fae.is_null(), "Can't open encrypted pack-referenced file '" + String(pf.pack) + "'.");
|
||||
ERR_FAIL_COND_MSG(fae.is_null(), vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack)));
|
||||
|
||||
Vector<uint8_t> key;
|
||||
key.resize(32);
|
||||
|
|
@ -402,7 +485,7 @@ FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFil
|
|||
}
|
||||
|
||||
Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false);
|
||||
ERR_FAIL_COND_MSG(err, "Can't open encrypted pack-referenced file '" + String(pf.pack) + "'.");
|
||||
ERR_FAIL_COND_MSG(err, vformat("Can't open encrypted pack-referenced file '%s'.", String(pf.pack)));
|
||||
f = fae;
|
||||
off = 0;
|
||||
}
|
||||
|
|
@ -543,8 +626,6 @@ String DirAccessPack::get_current_dir(bool p_include_drive) const {
|
|||
}
|
||||
|
||||
bool DirAccessPack::file_exists(String p_file) {
|
||||
p_file = fix_path(p_file);
|
||||
|
||||
PackedData::PackedDir *pd = _find_dir(p_file.get_base_dir());
|
||||
if (!pd) {
|
||||
return false;
|
||||
|
|
@ -553,8 +634,6 @@ bool DirAccessPack::file_exists(String p_file) {
|
|||
}
|
||||
|
||||
bool DirAccessPack::dir_exists(String p_dir) {
|
||||
p_dir = fix_path(p_dir);
|
||||
|
||||
return _find_dir(p_dir) != nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
#include "core/string/print_string.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/templates/rb_map.h"
|
||||
|
||||
// Godot's packed file magic header ("GDPC" in ASCII).
|
||||
#define PACK_HEADER_MAGIC 0x43504447
|
||||
|
|
@ -49,7 +48,8 @@ enum PackFlags {
|
|||
};
|
||||
|
||||
enum PackFileFlags {
|
||||
PACK_FILE_ENCRYPTED = 1 << 0
|
||||
PACK_FILE_ENCRYPTED = 1 << 0,
|
||||
PACK_FILE_REMOVAL = 1 << 1,
|
||||
};
|
||||
|
||||
class PackSource;
|
||||
|
|
@ -107,10 +107,14 @@ private:
|
|||
bool disabled = false;
|
||||
|
||||
void _free_packed_dirs(PackedDir *p_dir);
|
||||
void _get_file_paths(PackedDir *p_dir, const String &p_parent_dir, HashSet<String> &r_paths) const;
|
||||
|
||||
public:
|
||||
void add_pack_source(PackSource *p_source);
|
||||
void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource
|
||||
void remove_path(const String &p_path);
|
||||
uint8_t *get_file_hash(const String &p_path);
|
||||
HashSet<String> get_file_paths() const;
|
||||
|
||||
void set_disabled(bool p_disabled) { disabled = p_disabled; }
|
||||
_FORCE_INLINE_ bool is_disabled() const { return disabled; }
|
||||
|
|
@ -118,6 +122,8 @@ public:
|
|||
static PackedData *get_singleton() { return singleton; }
|
||||
Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset);
|
||||
|
||||
void clear();
|
||||
|
||||
_FORCE_INLINE_ Ref<FileAccess> try_open_path(const String &p_path);
|
||||
_FORCE_INLINE_ bool has_path(const String &p_path);
|
||||
|
||||
|
|
@ -141,6 +147,14 @@ public:
|
|||
virtual Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file) override;
|
||||
};
|
||||
|
||||
class PackedSourceDirectory : public PackSource {
|
||||
void add_directory(const String &p_path, bool p_replace_files);
|
||||
|
||||
public:
|
||||
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 {
|
||||
PackedData::PackedFile pf;
|
||||
|
||||
|
|
@ -169,8 +183,6 @@ public:
|
|||
|
||||
virtual bool eof_reached() const override;
|
||||
|
||||
virtual uint8_t get_8() const override;
|
||||
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||
|
||||
virtual void set_big_endian(bool p_big_endian) override;
|
||||
|
|
@ -179,9 +191,7 @@ public:
|
|||
|
||||
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
|
||||
virtual void flush() override;
|
||||
virtual void store_8(uint8_t p_dest) override;
|
||||
|
||||
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override;
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
|
||||
|
||||
virtual bool file_exists(const String &p_name) override;
|
||||
|
||||
|
|
@ -191,21 +201,18 @@ public:
|
|||
};
|
||||
|
||||
Ref<FileAccess> PackedData::try_open_path(const String &p_path) {
|
||||
String simplified_path = p_path.simplify_path();
|
||||
String simplified_path = p_path.simplify_path().trim_prefix("res://");
|
||||
PathMD5 pmd5(simplified_path.md5_buffer());
|
||||
HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5);
|
||||
if (!E) {
|
||||
return nullptr; //not found
|
||||
}
|
||||
if (E->value.offset == 0) {
|
||||
return nullptr; //was erased
|
||||
return nullptr; // Not found.
|
||||
}
|
||||
|
||||
return E->value.src->get_file(p_path, &E->value);
|
||||
}
|
||||
|
||||
bool PackedData::has_path(const String &p_path) {
|
||||
return files.has(PathMD5(p_path.simplify_path().md5_buffer()));
|
||||
return files.has(PathMD5(p_path.simplify_path().trim_prefix("res://").md5_buffer()));
|
||||
}
|
||||
|
||||
bool PackedData::has_directory(const String &p_path) {
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ void ZipArchive::close_handle(unzFile p_file) const {
|
|||
}
|
||||
|
||||
unzFile ZipArchive::get_file_handle(const String &p_file) const {
|
||||
ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, "File '" + p_file + " doesn't exist.");
|
||||
ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, vformat("File '%s' doesn't exist.", p_file));
|
||||
File file = files[p_file];
|
||||
|
||||
zlib_filefunc_def io;
|
||||
|
|
@ -136,7 +136,7 @@ unzFile ZipArchive::get_file_handle(const String &p_file) const {
|
|||
io.free_mem = godot_free;
|
||||
|
||||
unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io);
|
||||
ERR_FAIL_NULL_V_MSG(pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'.");
|
||||
ERR_FAIL_NULL_V_MSG(pkg, nullptr, vformat("Cannot open file '%s'.", packages[file.package].filename));
|
||||
int unz_err = unzGoToFilePos(pkg, &file.file_pos);
|
||||
if (unz_err != UNZ_OK || unzOpenCurrentFile(pkg) != UNZ_OK) {
|
||||
unzClose(pkg);
|
||||
|
|
@ -291,12 +291,6 @@ bool FileAccessZip::eof_reached() const {
|
|||
return at_eof;
|
||||
}
|
||||
|
||||
uint8_t FileAccessZip::get_8() const {
|
||||
uint8_t ret = 0;
|
||||
get_buffer(&ret, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t FileAccessZip::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||
ERR_FAIL_NULL_V(zfile, -1);
|
||||
|
|
@ -328,8 +322,8 @@ void FileAccessZip::flush() {
|
|||
ERR_FAIL();
|
||||
}
|
||||
|
||||
void FileAccessZip::store_8(uint8_t p_dest) {
|
||||
ERR_FAIL();
|
||||
bool FileAccessZip::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||
ERR_FAIL_V(false);
|
||||
}
|
||||
|
||||
bool FileAccessZip::file_exists(const String &p_name) {
|
||||
|
|
|
|||
|
|
@ -34,12 +34,9 @@
|
|||
#ifdef MINIZIP_ENABLED
|
||||
|
||||
#include "core/io/file_access_pack.h"
|
||||
#include "core/templates/rb_map.h"
|
||||
|
||||
#include "thirdparty/minizip/unzip.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
class ZipArchive : public PackSource {
|
||||
public:
|
||||
struct File {
|
||||
|
|
@ -95,14 +92,13 @@ public:
|
|||
|
||||
virtual bool eof_reached() const override; ///< reading passed EOF
|
||||
|
||||
virtual uint8_t get_8() const override; ///< get a byte
|
||||
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||
|
||||
virtual Error get_error() const override; ///< get last error
|
||||
|
||||
virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; }
|
||||
virtual void flush() override;
|
||||
virtual void store_8(uint8_t p_dest) override; ///< store a byte
|
||||
virtual bool store_buffer(const uint8_t *p_src, uint64_t p_length) override;
|
||||
|
||||
virtual bool file_exists(const String &p_name) override; ///< return true if a file exists
|
||||
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ const char *HTTPClient::_methods[METHOD_MAX] = {
|
|||
"PATCH"
|
||||
};
|
||||
|
||||
HTTPClient *HTTPClient::create() {
|
||||
HTTPClient *HTTPClient::create(bool p_notify_postinitialize) {
|
||||
if (_create) {
|
||||
return _create();
|
||||
return _create(p_notify_postinitialize);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -100,9 +100,9 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
|
|||
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]);
|
||||
ERR_FAIL_COND_V_MSG(sanitized.is_empty(), ERR_INVALID_PARAMETER, vformat("Invalid HTTP header at index %d: empty.", i));
|
||||
ERR_FAIL_COND_V_MSG(sanitized.find_char(':') < 1, ERR_INVALID_PARAMETER,
|
||||
vformat("Invalid HTTP header at index %d: String must contain header-value pair, delimited by ':', but was: '%s'.", i, p_headers[i]));
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
|
@ -113,7 +113,7 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() {
|
|||
get_response_headers(&rh);
|
||||
Dictionary ret;
|
||||
for (const String &s : rh) {
|
||||
int sp = s.find(":");
|
||||
int sp = s.find_char(':');
|
||||
if (sp == -1) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,12 +158,12 @@ protected:
|
|||
Error _request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body);
|
||||
Error _request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body = String());
|
||||
|
||||
static HTTPClient *(*_create)();
|
||||
static HTTPClient *(*_create)(bool p_notify_postinitialize);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static HTTPClient *create();
|
||||
static HTTPClient *create(bool p_notify_postinitialize = true);
|
||||
|
||||
String query_string_from_dict(const Dictionary &p_dict);
|
||||
Error verify_headers(const Vector<String> &p_headers);
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@
|
|||
#include "core/io/stream_peer_tls.h"
|
||||
#include "core/version.h"
|
||||
|
||||
HTTPClient *HTTPClientTCP::_create_func() {
|
||||
return memnew(HTTPClientTCP);
|
||||
HTTPClient *HTTPClientTCP::_create_func(bool p_notify_postinitialize) {
|
||||
return static_cast<HTTPClient *>(ClassDB::creator<HTTPClientTCP>(p_notify_postinitialize));
|
||||
}
|
||||
|
||||
Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, Ref<TLSOptions> p_options) {
|
||||
|
|
@ -484,7 +484,7 @@ Error HTTPClientTCP::poll() {
|
|||
// End of response, parse.
|
||||
response_str.push_back(0);
|
||||
String response;
|
||||
response.parse_utf8((const char *)response_str.ptr());
|
||||
response.parse_utf8((const char *)response_str.ptr(), response_str.size());
|
||||
Vector<String> responses = response.split("\n");
|
||||
body_size = -1;
|
||||
chunked = false;
|
||||
|
|
@ -508,11 +508,11 @@ Error HTTPClientTCP::poll() {
|
|||
continue;
|
||||
}
|
||||
if (s.begins_with("content-length:")) {
|
||||
body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int();
|
||||
body_size = s.substr(s.find_char(':') + 1, s.length()).strip_edges().to_int();
|
||||
body_left = body_size;
|
||||
|
||||
} else if (s.begins_with("transfer-encoding:")) {
|
||||
String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
|
||||
String encoding = header.substr(header.find_char(':') + 1, header.length()).strip_edges();
|
||||
if (encoding == "chunked") {
|
||||
chunked = true;
|
||||
}
|
||||
|
|
@ -662,15 +662,16 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
|
|||
chunk_left -= rec;
|
||||
|
||||
if (chunk_left == 0) {
|
||||
if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
|
||||
const int chunk_size = chunk.size();
|
||||
if (chunk[chunk_size - 2] != '\r' || chunk[chunk_size - 1] != '\n') {
|
||||
ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
|
||||
status = STATUS_CONNECTION_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
ret.resize(chunk.size() - 2);
|
||||
ret.resize(chunk_size - 2);
|
||||
uint8_t *w = ret.ptrw();
|
||||
memcpy(w, chunk.ptr(), chunk.size() - 2);
|
||||
memcpy(w, chunk.ptr(), chunk_size - 2);
|
||||
chunk.clear();
|
||||
}
|
||||
|
||||
|
|
@ -792,6 +793,6 @@ HTTPClientTCP::HTTPClientTCP() {
|
|||
request_buffer.instantiate();
|
||||
}
|
||||
|
||||
HTTPClient *(*HTTPClient::_create)() = HTTPClientTCP::_create_func;
|
||||
HTTPClient *(*HTTPClient::_create)(bool p_notify_postinitialize) = HTTPClientTCP::_create_func;
|
||||
|
||||
#endif // WEB_ENABLED
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ private:
|
|||
Error _get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received);
|
||||
|
||||
public:
|
||||
static HTTPClient *_create_func();
|
||||
static HTTPClient *_create_func(bool p_notify_postinitialize);
|
||||
|
||||
Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
#include "core/io/resource.h"
|
||||
#include "core/math/color.h"
|
||||
#include "core/math/rect2.h"
|
||||
|
||||
/**
|
||||
* Image storage class. This is used to store an image in user memory, as well as
|
||||
|
|
@ -43,12 +42,17 @@
|
|||
|
||||
class Image;
|
||||
|
||||
// Function pointer prototypes.
|
||||
|
||||
typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
|
||||
typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
|
||||
|
||||
typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality);
|
||||
typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality);
|
||||
|
||||
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
|
||||
typedef Ref<Image> (*ScalableImageMemLoadFunc)(const uint8_t *p_data, int p_size, float p_scale);
|
||||
|
||||
typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
|
||||
typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
|
||||
|
||||
|
|
@ -59,57 +63,48 @@ class Image : public Resource {
|
|||
GDCLASS(Image, Resource);
|
||||
|
||||
public:
|
||||
static SavePNGFunc save_png_func;
|
||||
static SaveJPGFunc save_jpg_func;
|
||||
static SaveEXRFunc save_exr_func;
|
||||
static SavePNGBufferFunc save_png_buffer_func;
|
||||
static SaveEXRBufferFunc save_exr_buffer_func;
|
||||
static SaveJPGBufferFunc save_jpg_buffer_func;
|
||||
static SaveWebPFunc save_webp_func;
|
||||
static SaveWebPBufferFunc save_webp_buffer_func;
|
||||
|
||||
enum {
|
||||
MAX_WIDTH = (1 << 24), // force a limit somehow
|
||||
MAX_HEIGHT = (1 << 24), // force a limit somehow
|
||||
MAX_PIXELS = 268435456
|
||||
MAX_WIDTH = (1 << 24), // Force a limit somehow.
|
||||
MAX_HEIGHT = (1 << 24), // Force a limit somehow.
|
||||
MAX_PIXELS = 268435456 // 16384 ^ 2
|
||||
};
|
||||
|
||||
enum Format {
|
||||
FORMAT_L8, //luminance
|
||||
FORMAT_LA8, //luminance-alpha
|
||||
enum Format : int32_t {
|
||||
FORMAT_L8, // Luminance
|
||||
FORMAT_LA8, // Luminance-Alpha
|
||||
FORMAT_R8,
|
||||
FORMAT_RG8,
|
||||
FORMAT_RGB8,
|
||||
FORMAT_RGBA8,
|
||||
FORMAT_RGBA4444,
|
||||
FORMAT_RGB565,
|
||||
FORMAT_RF, //float
|
||||
FORMAT_RF, // Float
|
||||
FORMAT_RGF,
|
||||
FORMAT_RGBF,
|
||||
FORMAT_RGBAF,
|
||||
FORMAT_RH, //half float
|
||||
FORMAT_RH, // Half
|
||||
FORMAT_RGH,
|
||||
FORMAT_RGBH,
|
||||
FORMAT_RGBAH,
|
||||
FORMAT_RGBE9995,
|
||||
FORMAT_DXT1, //s3tc bc1
|
||||
FORMAT_DXT3, //bc2
|
||||
FORMAT_DXT5, //bc3
|
||||
FORMAT_RGTC_R,
|
||||
FORMAT_RGTC_RG,
|
||||
FORMAT_BPTC_RGBA, //btpc bc7
|
||||
FORMAT_BPTC_RGBF, //float bc6h
|
||||
FORMAT_BPTC_RGBFU, //unsigned float bc6hu
|
||||
FORMAT_ETC, //etc1
|
||||
FORMAT_ETC2_R11, //etc2
|
||||
FORMAT_ETC2_R11S, //signed, NOT srgb.
|
||||
FORMAT_DXT1, // BC1
|
||||
FORMAT_DXT3, // BC2
|
||||
FORMAT_DXT5, // BC3
|
||||
FORMAT_RGTC_R, // BC4
|
||||
FORMAT_RGTC_RG, // BC5
|
||||
FORMAT_BPTC_RGBA, // BC7
|
||||
FORMAT_BPTC_RGBF, // BC6 Signed
|
||||
FORMAT_BPTC_RGBFU, // BC6 Unsigned
|
||||
FORMAT_ETC, // ETC1
|
||||
FORMAT_ETC2_R11,
|
||||
FORMAT_ETC2_R11S, // Signed, NOT srgb.
|
||||
FORMAT_ETC2_RG11,
|
||||
FORMAT_ETC2_RG11S,
|
||||
FORMAT_ETC2_RG11S, // Signed, NOT srgb.
|
||||
FORMAT_ETC2_RGB8,
|
||||
FORMAT_ETC2_RGBA8,
|
||||
FORMAT_ETC2_RGB8A1,
|
||||
FORMAT_ETC2_RA_AS_RG, //used to make basis universal happy
|
||||
FORMAT_DXT5_RA_AS_RG, //used to make basis universal happy
|
||||
FORMAT_ETC2_RA_AS_RG, // ETC2 RGBA with a RA-RG swizzle for normal maps.
|
||||
FORMAT_DXT5_RA_AS_RG, // BC3 with a RA-RG swizzle for normal maps.
|
||||
FORMAT_ASTC_4x4,
|
||||
FORMAT_ASTC_4x4_HDR,
|
||||
FORMAT_ASTC_8x8,
|
||||
|
|
@ -118,17 +113,18 @@ public:
|
|||
};
|
||||
|
||||
static const char *format_names[FORMAT_MAX];
|
||||
|
||||
enum Interpolation {
|
||||
INTERPOLATE_NEAREST,
|
||||
INTERPOLATE_BILINEAR,
|
||||
INTERPOLATE_CUBIC,
|
||||
INTERPOLATE_TRILINEAR,
|
||||
INTERPOLATE_LANCZOS,
|
||||
/* INTERPOLATE_TRICUBIC, */
|
||||
/* INTERPOLATE GAUSS */
|
||||
// INTERPOLATE_TRICUBIC,
|
||||
// INTERPOLATE_GAUSS
|
||||
};
|
||||
|
||||
//this is used for compression
|
||||
// Used for obtaining optimal compression quality.
|
||||
enum UsedChannels {
|
||||
USED_CHANNELS_L,
|
||||
USED_CHANNELS_LA,
|
||||
|
|
@ -137,13 +133,66 @@ public:
|
|||
USED_CHANNELS_RGB,
|
||||
USED_CHANNELS_RGBA,
|
||||
};
|
||||
//some functions provided by something else
|
||||
|
||||
// ASTC supports block formats other than 4x4.
|
||||
enum ASTCFormat {
|
||||
ASTC_FORMAT_4x4,
|
||||
ASTC_FORMAT_8x8,
|
||||
};
|
||||
|
||||
enum RoughnessChannel {
|
||||
ROUGHNESS_CHANNEL_R,
|
||||
ROUGHNESS_CHANNEL_G,
|
||||
ROUGHNESS_CHANNEL_B,
|
||||
ROUGHNESS_CHANNEL_A,
|
||||
ROUGHNESS_CHANNEL_L,
|
||||
};
|
||||
|
||||
enum Image3DValidateError {
|
||||
VALIDATE_3D_OK,
|
||||
VALIDATE_3D_ERR_IMAGE_EMPTY,
|
||||
VALIDATE_3D_ERR_MISSING_IMAGES,
|
||||
VALIDATE_3D_ERR_EXTRA_IMAGES,
|
||||
VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH,
|
||||
VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH,
|
||||
VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS,
|
||||
};
|
||||
|
||||
enum CompressMode {
|
||||
COMPRESS_S3TC,
|
||||
COMPRESS_ETC,
|
||||
COMPRESS_ETC2,
|
||||
COMPRESS_BPTC,
|
||||
COMPRESS_ASTC,
|
||||
COMPRESS_MAX,
|
||||
};
|
||||
|
||||
enum CompressSource {
|
||||
COMPRESS_SOURCE_GENERIC,
|
||||
COMPRESS_SOURCE_SRGB,
|
||||
COMPRESS_SOURCE_NORMAL,
|
||||
COMPRESS_SOURCE_MAX,
|
||||
};
|
||||
|
||||
enum AlphaMode {
|
||||
ALPHA_NONE,
|
||||
ALPHA_BIT,
|
||||
ALPHA_BLEND
|
||||
};
|
||||
|
||||
// External saver function pointers.
|
||||
|
||||
static SavePNGFunc save_png_func;
|
||||
static SaveJPGFunc save_jpg_func;
|
||||
static SaveEXRFunc save_exr_func;
|
||||
static SaveWebPFunc save_webp_func;
|
||||
static SavePNGBufferFunc save_png_buffer_func;
|
||||
static SaveEXRBufferFunc save_exr_buffer_func;
|
||||
static SaveJPGBufferFunc save_jpg_buffer_func;
|
||||
static SaveWebPBufferFunc save_webp_buffer_func;
|
||||
|
||||
// External loader function pointers.
|
||||
|
||||
static ImageMemLoadFunc _png_mem_loader_func;
|
||||
static ImageMemLoadFunc _png_mem_unpacker_func;
|
||||
static ImageMemLoadFunc _jpg_mem_loader_func;
|
||||
|
|
@ -153,30 +202,37 @@ public:
|
|||
static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
|
||||
static ImageMemLoadFunc _ktx_mem_loader_func;
|
||||
|
||||
// External VRAM compression function pointers.
|
||||
|
||||
static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
|
||||
static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
|
||||
static void (*_image_compress_etc1_func)(Image *);
|
||||
static void (*_image_compress_etc2_func)(Image *, UsedChannels p_channels);
|
||||
static void (*_image_compress_astc_func)(Image *, ASTCFormat p_format);
|
||||
|
||||
static Error (*_image_compress_bptc_rd_func)(Image *, UsedChannels p_channels);
|
||||
static Error (*_image_compress_bc_rd_func)(Image *, UsedChannels p_channels);
|
||||
|
||||
// External VRAM decompression function pointers.
|
||||
|
||||
static void (*_image_decompress_bc)(Image *);
|
||||
static void (*_image_decompress_bptc)(Image *);
|
||||
static void (*_image_decompress_etc1)(Image *);
|
||||
static void (*_image_decompress_etc2)(Image *);
|
||||
static void (*_image_decompress_astc)(Image *);
|
||||
|
||||
// External packer function pointers.
|
||||
|
||||
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> (*webp_unpacker)(const Vector<uint8_t> &p_buffer);
|
||||
static Ref<Image> (*png_unpacker)(const Vector<uint8_t> &p_buffer);
|
||||
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);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
|
@ -187,15 +243,12 @@ private:
|
|||
int height = 0;
|
||||
bool mipmaps = false;
|
||||
|
||||
void _copy_internals_from(const Image &p_image) {
|
||||
format = p_image.format;
|
||||
width = p_image.width;
|
||||
height = p_image.height;
|
||||
mipmaps = p_image.mipmaps;
|
||||
data = p_image.data;
|
||||
}
|
||||
void _copy_internals_from(const Image &p_image);
|
||||
|
||||
_FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int64_t &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data
|
||||
_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);
|
||||
|
||||
_FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int64_t &r_offset, int &r_width, int &r_height) const; // Get where the mipmap begins in data.
|
||||
|
||||
static int64_t _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = nullptr, int *r_mm_height = nullptr);
|
||||
bool _can_modify(Format p_format) const;
|
||||
|
|
@ -212,6 +265,8 @@ private:
|
|||
|
||||
Error _load_from_buffer(const Vector<uint8_t> &p_array, ImageMemLoadFunc p_loader);
|
||||
|
||||
_FORCE_INLINE_ void _generate_mipmap_from_format(Image::Format p_format, const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height, bool p_renormalize = false);
|
||||
|
||||
static void average_4_uint8(uint8_t &p_out, const uint8_t &p_a, const uint8_t &p_b, const uint8_t &p_c, const uint8_t &p_d);
|
||||
static void average_4_float(float &p_out, const float &p_a, const float &p_b, const float &p_c, const float &p_d);
|
||||
static void average_4_half(uint16_t &p_out, const uint16_t &p_a, const uint16_t &p_b, const uint16_t &p_c, const uint16_t &p_d);
|
||||
|
|
@ -222,52 +277,32 @@ private:
|
|||
static void renormalize_rgbe9995(uint32_t *p_rgb);
|
||||
|
||||
public:
|
||||
int get_width() const; ///< Get image width
|
||||
int get_height() const; ///< Get image height
|
||||
int get_width() const;
|
||||
int get_height() const;
|
||||
Size2i get_size() const;
|
||||
bool has_mipmaps() const;
|
||||
int get_mipmap_count() const;
|
||||
|
||||
/**
|
||||
* Convert the image to another format, conversion only to raw byte format
|
||||
*/
|
||||
// Convert the image to another format, conversion only to raw byte format.
|
||||
void convert(Format p_new_format);
|
||||
|
||||
/**
|
||||
* Get the current image format.
|
||||
*/
|
||||
Format get_format() const;
|
||||
|
||||
/**
|
||||
* Get where the mipmap begins in data.
|
||||
*/
|
||||
// Get where the mipmap begins in data.
|
||||
int64_t get_mipmap_offset(int p_mipmap) const;
|
||||
void get_mipmap_offset_and_size(int p_mipmap, int64_t &r_ofs, int64_t &r_size) const;
|
||||
void get_mipmap_offset_size_and_dimensions(int p_mipmap, int64_t &r_ofs, int64_t &r_size, int &w, int &h) const;
|
||||
|
||||
enum Image3DValidateError {
|
||||
VALIDATE_3D_OK,
|
||||
VALIDATE_3D_ERR_IMAGE_EMPTY,
|
||||
VALIDATE_3D_ERR_MISSING_IMAGES,
|
||||
VALIDATE_3D_ERR_EXTRA_IMAGES,
|
||||
VALIDATE_3D_ERR_IMAGE_SIZE_MISMATCH,
|
||||
VALIDATE_3D_ERR_IMAGE_FORMAT_MISMATCH,
|
||||
VALIDATE_3D_ERR_IMAGE_HAS_MIPMAPS,
|
||||
};
|
||||
|
||||
static Image3DValidateError validate_3d_image(Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_images);
|
||||
static String get_3d_image_validation_error_text(Image3DValidateError p_error);
|
||||
|
||||
/**
|
||||
* Resize the image, using the preferred interpolation method.
|
||||
*/
|
||||
// Resize the image, using the preferred interpolation method.
|
||||
void resize_to_po2(bool p_square = false, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
|
||||
void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
|
||||
void shrink_x2();
|
||||
bool is_size_po2() const;
|
||||
/**
|
||||
* Crop the image to a specific size, if larger, then the image is filled by black
|
||||
*/
|
||||
|
||||
// Crop the image to a specific size, if larger, then the image is filled by black.
|
||||
void crop_from_point(int p_x, int p_y, int p_width, int p_height);
|
||||
void crop(int p_width, int p_height);
|
||||
|
||||
|
|
@ -277,34 +312,20 @@ public:
|
|||
void flip_x();
|
||||
void flip_y();
|
||||
|
||||
/**
|
||||
* Generate a mipmap to an image (creates an image 1/4 the size, with averaging of 4->1)
|
||||
*/
|
||||
// Generate a mipmap chain of an image (creates an image 1/4 the size, with averaging of 4->1).
|
||||
Error generate_mipmaps(bool p_renormalize = false);
|
||||
|
||||
enum RoughnessChannel {
|
||||
ROUGHNESS_CHANNEL_R,
|
||||
ROUGHNESS_CHANNEL_G,
|
||||
ROUGHNESS_CHANNEL_B,
|
||||
ROUGHNESS_CHANNEL_A,
|
||||
ROUGHNESS_CHANNEL_L,
|
||||
};
|
||||
|
||||
Error generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map);
|
||||
|
||||
void clear_mipmaps();
|
||||
void normalize(); //for normal maps
|
||||
void normalize();
|
||||
|
||||
/**
|
||||
* Creates new internal image data of a given size and format. Current image will be lost.
|
||||
*/
|
||||
// Creates new internal image data of a given size and format. Current image will be lost.
|
||||
void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format);
|
||||
void initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
|
||||
void initialize_data(const char **p_xpm);
|
||||
|
||||
/**
|
||||
* returns true when the image is empty (0,0) in size
|
||||
*/
|
||||
// Returns true when the image is empty (0,0) in size.
|
||||
bool is_empty() const;
|
||||
|
||||
Vector<uint8_t> get_data() const;
|
||||
|
|
@ -324,27 +345,14 @@ public:
|
|||
static Ref<Image> create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
|
||||
void set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
|
||||
|
||||
/**
|
||||
* create an empty image
|
||||
*/
|
||||
Image() {}
|
||||
/**
|
||||
* create an empty image of a specific size and format
|
||||
*/
|
||||
Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format);
|
||||
/**
|
||||
* import an image of a specific size and format from a pointer
|
||||
*/
|
||||
Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
|
||||
Image() = default; // Create an empty image.
|
||||
Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format); // Create an empty image of a specific size and format.
|
||||
Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data); // Import an image of a specific size and format from a byte vector.
|
||||
Image(const uint8_t *p_mem_png_jpg, int p_len = -1); // Import either a png or jpg from a pointer.
|
||||
Image(const char **p_xpm); // Import an XPM image.
|
||||
|
||||
~Image() {}
|
||||
|
||||
enum AlphaMode {
|
||||
ALPHA_NONE,
|
||||
ALPHA_BIT,
|
||||
ALPHA_BLEND
|
||||
};
|
||||
|
||||
AlphaMode detect_alpha() const;
|
||||
bool is_invisible() const;
|
||||
|
||||
|
|
@ -359,21 +367,6 @@ public:
|
|||
static int64_t get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap);
|
||||
static int64_t get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h);
|
||||
|
||||
enum CompressMode {
|
||||
COMPRESS_S3TC,
|
||||
COMPRESS_ETC,
|
||||
COMPRESS_ETC2,
|
||||
COMPRESS_BPTC,
|
||||
COMPRESS_ASTC,
|
||||
COMPRESS_MAX,
|
||||
};
|
||||
enum CompressSource {
|
||||
COMPRESS_SOURCE_GENERIC,
|
||||
COMPRESS_SOURCE_SRGB,
|
||||
COMPRESS_SOURCE_NORMAL,
|
||||
COMPRESS_SOURCE_MAX,
|
||||
};
|
||||
|
||||
Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
|
||||
Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, ASTCFormat p_astc_format = ASTC_FORMAT_4x4);
|
||||
Error decompress();
|
||||
|
|
@ -383,11 +376,14 @@ public:
|
|||
void fix_alpha_edges();
|
||||
void premultiply_alpha();
|
||||
void srgb_to_linear();
|
||||
void linear_to_srgb();
|
||||
void normal_map_to_xy();
|
||||
Ref<Image> rgbe_to_srgb();
|
||||
Ref<Image> get_image_from_mipmap(int p_mipmap) const;
|
||||
void bump_map_to_normal_map(float bump_scale = 1.0);
|
||||
|
||||
bool detect_signed(bool p_include_mips = true) const;
|
||||
|
||||
void blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);
|
||||
void blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest);
|
||||
void blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest);
|
||||
|
|
@ -398,9 +394,8 @@ public:
|
|||
Rect2i get_used_rect() const;
|
||||
Ref<Image> get_region(const Rect2i &p_area) const;
|
||||
|
||||
static void set_compress_bc_func(void (*p_compress_func)(Image *, UsedChannels));
|
||||
static void set_compress_bptc_func(void (*p_compress_func)(Image *, UsedChannels));
|
||||
static String get_format_name(Format p_format);
|
||||
static uint32_t get_format_component_mask(Format p_format);
|
||||
|
||||
Error load_png_from_buffer(const Vector<uint8_t> &p_array);
|
||||
Error load_jpg_from_buffer(const Vector<uint8_t> &p_array);
|
||||
|
|
@ -416,9 +411,6 @@ public:
|
|||
void convert_ra_rgba8_to_rg();
|
||||
void convert_rgba8_to_bgra8();
|
||||
|
||||
Image(const uint8_t *p_mem_png_jpg, int p_len = -1);
|
||||
Image(const char **p_xpm);
|
||||
|
||||
virtual Ref<Resource> duplicate(bool p_subresources = false) const override;
|
||||
|
||||
UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC) const;
|
||||
|
|
@ -437,14 +429,7 @@ public:
|
|||
|
||||
void set_as_black();
|
||||
|
||||
void copy_internals_from(const Ref<Image> &p_image) {
|
||||
ERR_FAIL_COND_MSG(p_image.is_null(), "Cannot copy image internals: invalid Image object.");
|
||||
format = p_image->format;
|
||||
width = p_image->width;
|
||||
height = p_image->height;
|
||||
mipmaps = p_image->mipmaps;
|
||||
data = p_image->data;
|
||||
}
|
||||
void copy_internals_from(const Ref<Image> &p_image);
|
||||
|
||||
Dictionary compute_image_metrics(const Ref<Image> p_compared_image, bool p_luma_metric = true);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include "image_loader.h"
|
||||
|
||||
#include "core/string/print_string.h"
|
||||
|
||||
void ImageFormatLoader::_bind_methods() {
|
||||
BIND_BITFIELD_FLAG(FLAG_NONE);
|
||||
BIND_BITFIELD_FLAG(FLAG_FORCE_LINEAR);
|
||||
|
|
@ -82,15 +80,16 @@ void ImageFormatLoaderExtension::_bind_methods() {
|
|||
|
||||
Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
|
||||
ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "Can't load an image: invalid Image object.");
|
||||
const String file = ResourceUID::ensure_path(p_file);
|
||||
|
||||
Ref<FileAccess> f = p_custom;
|
||||
if (f.is_null()) {
|
||||
Error err;
|
||||
f = FileAccess::open(p_file, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), err, "Error opening file '" + p_file + "'.");
|
||||
f = FileAccess::open(file, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), err, vformat("Error opening file '%s'.", file));
|
||||
}
|
||||
|
||||
String extension = p_file.get_extension();
|
||||
String extension = file.get_extension();
|
||||
|
||||
for (int i = 0; i < loader.size(); i++) {
|
||||
if (!loader[i]->recognize(extension)) {
|
||||
|
|
@ -98,7 +97,7 @@ Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<File
|
|||
}
|
||||
Error err = loader.write[i]->load_image(p_image, f, p_flags, p_scale);
|
||||
if (err != OK) {
|
||||
ERR_PRINT("Error loading image: " + p_file);
|
||||
ERR_PRINT(vformat("Error loading image: '%s'.", file));
|
||||
}
|
||||
|
||||
if (err != ERR_FILE_UNRECOGNIZED) {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ struct _IP_ResolverPrivate {
|
|||
response.clear();
|
||||
type = IP::TYPE_NONE;
|
||||
hostname = "";
|
||||
};
|
||||
}
|
||||
|
||||
QueueItem() {
|
||||
clear();
|
||||
|
|
@ -81,17 +81,17 @@ struct _IP_ResolverPrivate {
|
|||
continue;
|
||||
}
|
||||
|
||||
mutex.lock();
|
||||
MutexLock lock(mutex);
|
||||
List<IPAddress> response;
|
||||
String hostname = queue[i].hostname;
|
||||
IP::Type type = queue[i].type;
|
||||
mutex.unlock();
|
||||
lock.temp_unlock();
|
||||
|
||||
// We should not lock while resolving the hostname,
|
||||
// only when modifying the queue.
|
||||
IP::get_singleton()->_resolve_hostname(response, hostname, type);
|
||||
|
||||
MutexLock lock(mutex);
|
||||
lock.temp_relock();
|
||||
// Could have been completed by another function, or deleted.
|
||||
if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) {
|
||||
continue;
|
||||
|
|
@ -131,21 +131,22 @@ PackedStringArray IP::resolve_hostname_addresses(const String &p_hostname, 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 as long as the result is valid.
|
||||
if (res.size()) {
|
||||
resolver->cache[key] = res;
|
||||
{
|
||||
MutexLock lock(resolver->mutex);
|
||||
if (resolver->cache.has(key)) {
|
||||
res = resolver->cache[key];
|
||||
} else {
|
||||
// This should be run unlocked so the resolver thread can keep resolving
|
||||
// other requests.
|
||||
lock.temp_unlock();
|
||||
_resolve_hostname(res, p_hostname, p_type);
|
||||
lock.temp_relock();
|
||||
// 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();
|
||||
|
||||
PackedStringArray result;
|
||||
for (const IPAddress &E : res) {
|
||||
|
|
@ -200,7 +201,7 @@ IPAddress IP::get_resolve_item_address(ResolverID p_id) const {
|
|||
MutexLock lock(resolver->mutex);
|
||||
|
||||
if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) {
|
||||
ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet.");
|
||||
ERR_PRINT(vformat("Resolve of '%s' didn't complete yet.", resolver->queue[p_id].hostname));
|
||||
return IPAddress();
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +220,7 @@ Array IP::get_resolve_item_addresses(ResolverID p_id) const {
|
|||
MutexLock lock(resolver->mutex);
|
||||
|
||||
if (resolver->queue[p_id].status.get() != IP::RESOLVER_STATUS_DONE) {
|
||||
ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet.");
|
||||
ERR_PRINT(vformat("Resolve of '%s' didn't complete yet.", resolver->queue[p_id].hostname));
|
||||
return Array();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ IPAddress::operator Variant() const {
|
|||
return operator String();
|
||||
}*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
IPAddress::operator String() const {
|
||||
|
|
@ -202,7 +201,7 @@ IPAddress::IPAddress(const String &p_string) {
|
|||
// Wildcard (not a valid IP)
|
||||
wildcard = true;
|
||||
|
||||
} else if (p_string.contains(":")) {
|
||||
} else if (p_string.contains_char(':')) {
|
||||
// IPv6
|
||||
_parse_ipv6(p_string);
|
||||
valid = true;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -80,6 +80,9 @@ class JSON : public Resource {
|
|||
static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
|
||||
static Error _parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line);
|
||||
|
||||
static Variant _from_native(const Variant &p_variant, bool p_full_objects, int p_depth);
|
||||
static Variant _to_native(const Variant &p_json, bool p_allow_objects, int p_depth);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
|
|
@ -90,10 +93,18 @@ public:
|
|||
static String stringify(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true, bool p_full_precision = false);
|
||||
static Variant parse_string(const String &p_json_string);
|
||||
|
||||
inline Variant get_data() const { return data; }
|
||||
_FORCE_INLINE_ static Variant from_native(const Variant &p_variant, bool p_full_objects = false) {
|
||||
return _from_native(p_variant, p_full_objects, 0);
|
||||
}
|
||||
_FORCE_INLINE_ static Variant to_native(const Variant &p_json, bool p_allow_objects = false) {
|
||||
return _to_native(p_json, p_allow_objects, 0);
|
||||
}
|
||||
|
||||
void set_data(const Variant &p_data);
|
||||
inline int get_error_line() const { return err_line; }
|
||||
inline String get_error_message() const { return err_str; }
|
||||
_FORCE_INLINE_ Variant get_data() const { return data; }
|
||||
|
||||
_FORCE_INLINE_ int get_error_line() const { return err_line; }
|
||||
_FORCE_INLINE_ String get_error_message() const { return err_str; }
|
||||
};
|
||||
|
||||
class ResourceFormatLoaderJSON : public ResourceFormatLoader {
|
||||
|
|
@ -102,6 +113,10 @@ public:
|
|||
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
|
||||
virtual bool handles_type(const String &p_type) const override;
|
||||
virtual String get_resource_type(const String &p_path) const override;
|
||||
|
||||
// Treat JSON as a text file, do not generate a `*.json.uid` file.
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; }
|
||||
virtual bool has_custom_uid_support() const override { return true; }
|
||||
};
|
||||
|
||||
class ResourceFormatSaverJSON : public ResourceFormatSaver {
|
||||
|
|
|
|||
|
|
@ -30,14 +30,17 @@
|
|||
|
||||
#include "logger.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/core_globals.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/os/time.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/templates/rb_set.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
#include "modules/regex/regex.h"
|
||||
#else
|
||||
class RegEx : public RefCounted {};
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
|
||||
#if defined(MINGW_ENABLED) || defined(_MSC_VER)
|
||||
#define sprintf sprintf_s
|
||||
|
|
@ -84,11 +87,7 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
|
|||
err_details = p_code;
|
||||
}
|
||||
|
||||
if (p_editor_notify) {
|
||||
logf_error("%s: %s\n", err_type, err_details);
|
||||
} else {
|
||||
logf_error("USER %s: %s\n", err_type, err_details);
|
||||
}
|
||||
logf_error("%s: %s\n", err_type, err_details);
|
||||
logf_error(" at: %s (%s:%i)\n", p_function, p_file, p_line);
|
||||
}
|
||||
|
||||
|
|
@ -131,7 +130,9 @@ void RotatedFileLogger::clear_old_backups() {
|
|||
|
||||
da->list_dir_begin();
|
||||
String f = da->get_next();
|
||||
HashSet<String> backups;
|
||||
// backups is a RBSet because it guarantees that iterating on it is done in sorted order.
|
||||
// RotatedFileLogger depends on this behavior to delete the oldest log file first.
|
||||
RBSet<String> backups;
|
||||
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);
|
||||
|
|
@ -140,12 +141,12 @@ void RotatedFileLogger::clear_old_backups() {
|
|||
}
|
||||
da->list_dir_end();
|
||||
|
||||
if (backups.size() > (uint32_t)max_backups) {
|
||||
if (backups.size() > max_backups) {
|
||||
// since backups are appended with timestamp and Set iterates them in sorted order,
|
||||
// first backups are the oldest
|
||||
int to_delete = backups.size() - max_backups;
|
||||
for (HashSet<String>::Iterator E = backups.begin(); E && to_delete > 0; ++E, --to_delete) {
|
||||
da->remove(*E);
|
||||
for (RBSet<String>::Element *E = backups.front(); E && to_delete > 0; E = E->next(), --to_delete) {
|
||||
da->remove(E->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,13 +34,11 @@
|
|||
#include "core/io/file_access.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/vector.h"
|
||||
#include "modules/modules_enabled.gen.h" // For regex.
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
#include "modules/regex/regex.h"
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
class RegEx;
|
||||
|
||||
class Logger {
|
||||
protected:
|
||||
bool should_log(bool p_err);
|
||||
|
|
@ -90,9 +88,7 @@ class RotatedFileLogger : public Logger {
|
|||
void clear_old_backups();
|
||||
void rotate_file();
|
||||
|
||||
#ifdef MODULE_REGEX_ENABLED
|
||||
Ref<RegEx> strip_ansi_regex;
|
||||
#endif // MODULE_REGEX_ENABLED
|
||||
|
||||
public:
|
||||
explicit RotatedFileLogger(const String &p_base_path, int p_max_files = 10);
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@
|
|||
#include "core/io/resource_loader.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/variant/container_type_validate.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -69,10 +68,25 @@ ObjectID EncodedObjectAsID::get_object_id() const {
|
|||
// For `Variant::ARRAY`.
|
||||
// Occupies bits 16 and 17.
|
||||
#define HEADER_DATA_FIELD_TYPED_ARRAY_MASK (0b11 << 16)
|
||||
#define HEADER_DATA_FIELD_TYPED_ARRAY_NONE (0b00 << 16)
|
||||
#define HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN (0b01 << 16)
|
||||
#define HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME (0b10 << 16)
|
||||
#define HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT (0b11 << 16)
|
||||
#define HEADER_DATA_FIELD_TYPED_ARRAY_SHIFT 16
|
||||
|
||||
// For `Variant::DICTIONARY`.
|
||||
// Occupies bits 16 and 17.
|
||||
#define HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_MASK (0b11 << 16)
|
||||
#define HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_SHIFT 16
|
||||
// Occupies bits 18 and 19.
|
||||
#define HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_MASK (0b11 << 18)
|
||||
#define HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_SHIFT 18
|
||||
|
||||
enum ContainerTypeKind {
|
||||
CONTAINER_TYPE_KIND_NONE = 0b00,
|
||||
CONTAINER_TYPE_KIND_BUILTIN = 0b01,
|
||||
CONTAINER_TYPE_KIND_CLASS_NAME = 0b10,
|
||||
CONTAINER_TYPE_KIND_SCRIPT = 0b11,
|
||||
};
|
||||
|
||||
#define GET_CONTAINER_TYPE_KIND(m_header, m_field) \
|
||||
((ContainerTypeKind)(((m_header) & HEADER_DATA_FIELD_##m_field##_MASK) >> HEADER_DATA_FIELD_##m_field##_SHIFT))
|
||||
|
||||
static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) {
|
||||
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
||||
|
|
@ -80,7 +94,7 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
|
|||
int32_t strlen = decode_uint32(buf);
|
||||
int32_t pad = 0;
|
||||
|
||||
// Handle padding
|
||||
// Handle padding.
|
||||
if (strlen % 4) {
|
||||
pad = 4 - strlen % 4;
|
||||
}
|
||||
|
|
@ -88,7 +102,7 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
|
|||
buf += 4;
|
||||
len -= 4;
|
||||
|
||||
// Ensure buffer is big enough
|
||||
// Ensure buffer is big enough.
|
||||
ERR_FAIL_ADD_OF(strlen, pad, ERR_FILE_EOF);
|
||||
ERR_FAIL_COND_V(strlen < 0 || strlen + pad > len, ERR_FILE_EOF);
|
||||
|
||||
|
|
@ -96,10 +110,10 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
|
|||
ERR_FAIL_COND_V(str.parse_utf8((const char *)buf, strlen) != OK, ERR_INVALID_DATA);
|
||||
r_string = str;
|
||||
|
||||
// Add padding
|
||||
// Add padding.
|
||||
strlen += pad;
|
||||
|
||||
// Update buffer pos, left data count, and return size
|
||||
// Update buffer pos, left data count, and return size.
|
||||
buf += strlen;
|
||||
len -= strlen;
|
||||
if (r_len) {
|
||||
|
|
@ -109,6 +123,65 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r
|
|||
return OK;
|
||||
}
|
||||
|
||||
static Error _decode_container_type(const uint8_t *&buf, int &len, int *r_len, bool p_allow_objects, ContainerTypeKind p_type_kind, ContainerType &r_type) {
|
||||
switch (p_type_kind) {
|
||||
case CONTAINER_TYPE_KIND_NONE: {
|
||||
return OK;
|
||||
} break;
|
||||
case CONTAINER_TYPE_KIND_BUILTIN: {
|
||||
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
||||
|
||||
int32_t bt = decode_uint32(buf);
|
||||
buf += 4;
|
||||
len -= 4;
|
||||
if (r_len) {
|
||||
(*r_len) += 4;
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
|
||||
r_type.builtin_type = (Variant::Type)bt;
|
||||
if (!p_allow_objects && r_type.builtin_type == Variant::OBJECT) {
|
||||
r_type.class_name = EncodedObjectAsID::get_class_static();
|
||||
}
|
||||
return OK;
|
||||
} break;
|
||||
case CONTAINER_TYPE_KIND_CLASS_NAME: {
|
||||
String str;
|
||||
Error err = _decode_string(buf, len, r_len, str);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
r_type.builtin_type = Variant::OBJECT;
|
||||
if (p_allow_objects) {
|
||||
r_type.class_name = str;
|
||||
} else {
|
||||
r_type.class_name = EncodedObjectAsID::get_class_static();
|
||||
}
|
||||
return OK;
|
||||
} break;
|
||||
case CONTAINER_TYPE_KIND_SCRIPT: {
|
||||
String path;
|
||||
Error err = _decode_string(buf, len, r_len, path);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
r_type.builtin_type = Variant::OBJECT;
|
||||
if (p_allow_objects) {
|
||||
ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path \"%s\".", path));
|
||||
r_type.script = ResourceLoader::load(path, "Script");
|
||||
ERR_FAIL_COND_V_MSG(r_type.script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path \"%s\".", path));
|
||||
r_type.class_name = r_type.script->get_instance_base_type();
|
||||
} else {
|
||||
r_type.class_name = EncodedObjectAsID::get_class_static();
|
||||
}
|
||||
return OK;
|
||||
} break;
|
||||
}
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Invalid container type kind."); // Future proofing.
|
||||
}
|
||||
|
||||
Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_objects, int p_depth) {
|
||||
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;
|
||||
|
|
@ -126,7 +199,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
*r_len = 4;
|
||||
}
|
||||
|
||||
// Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded.
|
||||
// NOTE: We cannot use `sizeof(real_t)` for decoding, in case a different size is encoded.
|
||||
// Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
|
||||
// This does lead to some code duplication for decoding, but compatibility is the priority.
|
||||
switch (header & HEADER_TYPE_MASK) {
|
||||
|
|
@ -144,7 +217,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
case Variant::INT: {
|
||||
if (header & HEADER_DATA_FLAG_64) {
|
||||
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
|
||||
int64_t val = decode_uint64(buf);
|
||||
int64_t val = int64_t(decode_uint64(buf));
|
||||
r_variant = val;
|
||||
if (r_len) {
|
||||
(*r_len) += 8;
|
||||
|
|
@ -152,7 +225,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
|
||||
} else {
|
||||
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
||||
int32_t val = decode_uint32(buf);
|
||||
int32_t val = int32_t(decode_uint32(buf));
|
||||
r_variant = val;
|
||||
if (r_len) {
|
||||
(*r_len) += 4;
|
||||
|
|
@ -188,7 +261,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
|
||||
} break;
|
||||
|
||||
// math types
|
||||
// Math types.
|
||||
case Variant::VECTOR2: {
|
||||
Vector2 val;
|
||||
if (header & HEADER_DATA_FLAG_64) {
|
||||
|
|
@ -539,7 +612,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
r_variant = val;
|
||||
|
||||
} break;
|
||||
// misc types
|
||||
|
||||
// Misc types.
|
||||
case Variant::COLOR: {
|
||||
ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA);
|
||||
Color val;
|
||||
|
|
@ -568,7 +642,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
int32_t strlen = decode_uint32(buf);
|
||||
|
||||
if (strlen & 0x80000000) {
|
||||
//new format
|
||||
// New format.
|
||||
ERR_FAIL_COND_V(len < 12, ERR_INVALID_DATA);
|
||||
Vector<StringName> names;
|
||||
Vector<StringName> subnames;
|
||||
|
|
@ -607,8 +681,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
r_variant = NodePath(names, subnames, np_flags & 1);
|
||||
|
||||
} else {
|
||||
//old format, just a string
|
||||
|
||||
// Old format, just a string.
|
||||
ERR_FAIL_V(ERR_INVALID_DATA);
|
||||
}
|
||||
|
||||
|
|
@ -698,9 +771,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
if (str == "script" && value.get_type() != Variant::NIL) {
|
||||
ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String.");
|
||||
String path = value;
|
||||
ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, vformat("Invalid script path \"%s\".", path));
|
||||
Ref<Script> script = ResourceLoader::load(path, "Script");
|
||||
ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, vformat("Can't load script at path \"%s\".", path));
|
||||
obj->set_script(script);
|
||||
} else {
|
||||
obj->set(str, value);
|
||||
|
|
@ -731,9 +804,30 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
r_variant = Signal(id, StringName(name));
|
||||
} break;
|
||||
case Variant::DICTIONARY: {
|
||||
ContainerType key_type;
|
||||
|
||||
{
|
||||
ContainerTypeKind key_type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_DICTIONARY_KEY);
|
||||
Error err = _decode_container_type(buf, len, r_len, p_allow_objects, key_type_kind, key_type);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
ContainerType value_type;
|
||||
|
||||
{
|
||||
ContainerTypeKind value_type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_DICTIONARY_VALUE);
|
||||
Error err = _decode_container_type(buf, len, r_len, p_allow_objects, value_type_kind, value_type);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
||||
|
||||
int32_t count = decode_uint32(buf);
|
||||
// bool shared = count&0x80000000;
|
||||
//bool shared = count & 0x80000000;
|
||||
count &= 0x7FFFFFFF;
|
||||
|
||||
buf += 4;
|
||||
|
|
@ -743,7 +837,10 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
(*r_len) += 4; // Size of count number.
|
||||
}
|
||||
|
||||
Dictionary d;
|
||||
Dictionary dict;
|
||||
if (key_type.builtin_type != Variant::NIL || value_type.builtin_type != Variant::NIL) {
|
||||
dict.set_typed(key_type, value_type);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Variant key, value;
|
||||
|
|
@ -767,75 +864,27 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
(*r_len) += used;
|
||||
}
|
||||
|
||||
d[key] = value;
|
||||
dict[key] = value;
|
||||
}
|
||||
|
||||
r_variant = d;
|
||||
r_variant = dict;
|
||||
|
||||
} break;
|
||||
case Variant::ARRAY: {
|
||||
Variant::Type builtin_type = Variant::VARIANT_MAX;
|
||||
StringName class_name;
|
||||
Ref<Script> script;
|
||||
ContainerType type;
|
||||
|
||||
switch (header & HEADER_DATA_FIELD_TYPED_ARRAY_MASK) {
|
||||
case HEADER_DATA_FIELD_TYPED_ARRAY_NONE:
|
||||
break; // Untyped array.
|
||||
case HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN: {
|
||||
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
||||
|
||||
int32_t bt = decode_uint32(buf);
|
||||
buf += 4;
|
||||
len -= 4;
|
||||
if (r_len) {
|
||||
(*r_len) += 4;
|
||||
}
|
||||
|
||||
ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
|
||||
builtin_type = (Variant::Type)bt;
|
||||
if (!p_allow_objects && builtin_type == Variant::OBJECT) {
|
||||
class_name = EncodedObjectAsID::get_class_static();
|
||||
}
|
||||
} break;
|
||||
case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: {
|
||||
String str;
|
||||
Error err = _decode_string(buf, len, r_len, str);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
builtin_type = Variant::OBJECT;
|
||||
if (p_allow_objects) {
|
||||
class_name = str;
|
||||
} else {
|
||||
class_name = EncodedObjectAsID::get_class_static();
|
||||
}
|
||||
} break;
|
||||
case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: {
|
||||
String path;
|
||||
Error err = _decode_string(buf, len, r_len, path);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
builtin_type = Variant::OBJECT;
|
||||
if (p_allow_objects) {
|
||||
ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
|
||||
script = ResourceLoader::load(path, "Script");
|
||||
ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
|
||||
class_name = script->get_instance_base_type();
|
||||
} else {
|
||||
class_name = EncodedObjectAsID::get_class_static();
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing.
|
||||
{
|
||||
ContainerTypeKind type_kind = GET_CONTAINER_TYPE_KIND(header, TYPED_ARRAY);
|
||||
Error err = _decode_container_type(buf, len, r_len, p_allow_objects, type_kind, type);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
||||
|
||||
int32_t count = decode_uint32(buf);
|
||||
// bool shared = count&0x80000000;
|
||||
//bool shared = count & 0x80000000;
|
||||
count &= 0x7FFFFFFF;
|
||||
|
||||
buf += 4;
|
||||
|
|
@ -845,29 +894,29 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
(*r_len) += 4; // Size of count number.
|
||||
}
|
||||
|
||||
Array varr;
|
||||
if (builtin_type != Variant::VARIANT_MAX) {
|
||||
varr.set_typed(builtin_type, class_name, script);
|
||||
Array array;
|
||||
if (type.builtin_type != Variant::NIL) {
|
||||
array.set_typed(type);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
int used = 0;
|
||||
Variant v;
|
||||
Error err = decode_variant(v, buf, len, &used, p_allow_objects, p_depth + 1);
|
||||
Variant elem;
|
||||
Error err = decode_variant(elem, buf, len, &used, p_allow_objects, p_depth + 1);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to decode Variant.");
|
||||
buf += used;
|
||||
len -= used;
|
||||
varr.push_back(v);
|
||||
array.push_back(elem);
|
||||
if (r_len) {
|
||||
(*r_len) += used;
|
||||
}
|
||||
}
|
||||
|
||||
r_variant = varr;
|
||||
r_variant = array;
|
||||
|
||||
} break;
|
||||
|
||||
// arrays
|
||||
// Packed arrays.
|
||||
case Variant::PACKED_BYTE_ARRAY: {
|
||||
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
||||
int32_t count = decode_uint32(buf);
|
||||
|
|
@ -906,7 +955,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
Vector<int32_t> data;
|
||||
|
||||
if (count) {
|
||||
//const int*rbuf=(const int*)buf;
|
||||
//const int *rbuf = (const int *)buf;
|
||||
data.resize(count);
|
||||
int32_t *w = data.ptrw();
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
|
|
@ -930,7 +979,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
Vector<int64_t> data;
|
||||
|
||||
if (count) {
|
||||
//const int*rbuf=(const int*)buf;
|
||||
//const int *rbuf = (const int *)buf;
|
||||
data.resize(count);
|
||||
int64_t *w = data.ptrw();
|
||||
for (int64_t i = 0; i < count; i++) {
|
||||
|
|
@ -954,7 +1003,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
Vector<float> data;
|
||||
|
||||
if (count) {
|
||||
//const float*rbuf=(const float*)buf;
|
||||
//const float *rbuf = (const float *)buf;
|
||||
data.resize(count);
|
||||
float *w = data.ptrw();
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
|
|
@ -1265,13 +1314,50 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) {
|
|||
|
||||
r_len += 4 + utf8.length();
|
||||
while (r_len % 4) {
|
||||
r_len++; //pad
|
||||
r_len++; // Pad.
|
||||
if (buf) {
|
||||
*(buf++) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _encode_container_type_header(const ContainerType &p_type, uint32_t &header, uint32_t p_shift, bool p_full_objects) {
|
||||
if (p_type.builtin_type != Variant::NIL) {
|
||||
if (p_type.script.is_valid()) {
|
||||
header |= (p_full_objects ? CONTAINER_TYPE_KIND_SCRIPT : CONTAINER_TYPE_KIND_CLASS_NAME) << p_shift;
|
||||
} else if (p_type.class_name != StringName()) {
|
||||
header |= CONTAINER_TYPE_KIND_CLASS_NAME << p_shift;
|
||||
} else {
|
||||
// No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`.
|
||||
header |= CONTAINER_TYPE_KIND_BUILTIN << p_shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Error _encode_container_type(const ContainerType &p_type, uint8_t *&buf, int &r_len, bool p_full_objects) {
|
||||
if (p_type.builtin_type != Variant::NIL) {
|
||||
if (p_type.script.is_valid()) {
|
||||
if (p_full_objects) {
|
||||
String path = p_type.script->get_path();
|
||||
ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for a container type.");
|
||||
_encode_string(path, buf, r_len);
|
||||
} else {
|
||||
_encode_string(EncodedObjectAsID::get_class_static(), buf, r_len);
|
||||
}
|
||||
} else if (p_type.class_name != StringName()) {
|
||||
_encode_string(p_full_objects ? p_type.class_name.operator String() : EncodedObjectAsID::get_class_static(), buf, r_len);
|
||||
} else {
|
||||
// No need to check `p_full_objects` since `class_name` should be non-empty for `builtin_type == Variant::OBJECT`.
|
||||
if (buf) {
|
||||
encode_uint32(p_type.builtin_type, buf);
|
||||
buf += 4;
|
||||
}
|
||||
r_len += 4;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) {
|
||||
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;
|
||||
|
|
@ -1310,20 +1396,14 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
header |= HEADER_DATA_FLAG_OBJECT_AS_ID;
|
||||
}
|
||||
} break;
|
||||
case Variant::DICTIONARY: {
|
||||
const Dictionary dict = p_variant;
|
||||
_encode_container_type_header(dict.get_key_type(), header, HEADER_DATA_FIELD_TYPED_DICTIONARY_KEY_SHIFT, p_full_objects);
|
||||
_encode_container_type_header(dict.get_value_type(), header, HEADER_DATA_FIELD_TYPED_DICTIONARY_VALUE_SHIFT, p_full_objects);
|
||||
} break;
|
||||
case Variant::ARRAY: {
|
||||
Array array = p_variant;
|
||||
if (array.is_typed()) {
|
||||
Ref<Script> script = array.get_typed_script();
|
||||
if (script.is_valid()) {
|
||||
header |= p_full_objects ? HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT : HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
|
||||
} else if (array.get_typed_class_name() != StringName()) {
|
||||
header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
|
||||
} else {
|
||||
// No need to check `p_full_objects` since for `Variant::OBJECT`
|
||||
// `array.get_typed_class_name()` should be non-empty.
|
||||
header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
|
||||
}
|
||||
}
|
||||
const Array array = p_variant;
|
||||
_encode_container_type_header(array.get_element_type(), header, HEADER_DATA_FIELD_TYPED_ARRAY_SHIFT, p_full_objects);
|
||||
} break;
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
case Variant::VECTOR2:
|
||||
|
|
@ -1344,7 +1424,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
} break;
|
||||
#endif // REAL_T_IS_DOUBLE
|
||||
default: {
|
||||
} // nothing to do at this stage
|
||||
// Nothing to do at this stage.
|
||||
} break;
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
|
|
@ -1355,7 +1436,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
|
||||
switch (p_variant.get_type()) {
|
||||
case Variant::NIL: {
|
||||
//nothing to do
|
||||
// Nothing to do.
|
||||
} break;
|
||||
case Variant::BOOL: {
|
||||
if (buf) {
|
||||
|
|
@ -1367,15 +1448,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
} break;
|
||||
case Variant::INT: {
|
||||
if (header & HEADER_DATA_FLAG_64) {
|
||||
//64 bits
|
||||
// 64 bits.
|
||||
if (buf) {
|
||||
encode_uint64(p_variant.operator int64_t(), buf);
|
||||
encode_uint64(p_variant.operator uint64_t(), buf);
|
||||
}
|
||||
|
||||
r_len += 8;
|
||||
} else {
|
||||
if (buf) {
|
||||
encode_uint32(p_variant.operator int32_t(), buf);
|
||||
encode_uint32(p_variant.operator uint32_t(), buf);
|
||||
}
|
||||
|
||||
r_len += 4;
|
||||
|
|
@ -1401,7 +1482,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
case Variant::NODE_PATH: {
|
||||
NodePath np = p_variant;
|
||||
if (buf) {
|
||||
encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); //for compatibility with the old format
|
||||
encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); // For compatibility with the old format.
|
||||
encode_uint32(np.get_subname_count(), buf + 4);
|
||||
uint32_t np_flags = 0;
|
||||
if (np.is_absolute()) {
|
||||
|
|
@ -1451,7 +1532,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
|
||||
} break;
|
||||
|
||||
// math types
|
||||
// Math types.
|
||||
case Variant::VECTOR2: {
|
||||
if (buf) {
|
||||
Vector2 v2 = p_variant;
|
||||
|
|
@ -1635,7 +1716,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
|
||||
} break;
|
||||
|
||||
// misc types
|
||||
// Misc types.
|
||||
case Variant::COLOR: {
|
||||
if (buf) {
|
||||
Color c = p_variant;
|
||||
|
|
@ -1746,29 +1827,43 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
r_len += 8;
|
||||
} break;
|
||||
case Variant::DICTIONARY: {
|
||||
Dictionary d = p_variant;
|
||||
const Dictionary dict = p_variant;
|
||||
|
||||
{
|
||||
Error err = _encode_container_type(dict.get_key_type(), buf, r_len, p_full_objects);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Error err = _encode_container_type(dict.get_value_type(), buf, r_len, p_full_objects);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
encode_uint32(uint32_t(d.size()), buf);
|
||||
encode_uint32(uint32_t(dict.size()), buf);
|
||||
buf += 4;
|
||||
}
|
||||
r_len += 4;
|
||||
|
||||
List<Variant> keys;
|
||||
d.get_key_list(&keys);
|
||||
dict.get_key_list(&keys);
|
||||
|
||||
for (const Variant &E : keys) {
|
||||
for (const Variant &key : keys) {
|
||||
int len;
|
||||
Error err = encode_variant(E, buf, len, p_full_objects, p_depth + 1);
|
||||
Error err = encode_variant(key, buf, len, p_full_objects, p_depth + 1);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
||||
r_len += len;
|
||||
if (buf) {
|
||||
buf += len;
|
||||
}
|
||||
Variant *v = d.getptr(E);
|
||||
ERR_FAIL_NULL_V(v, ERR_BUG);
|
||||
err = encode_variant(*v, buf, len, p_full_objects, p_depth + 1);
|
||||
const Variant *value = dict.getptr(key);
|
||||
ERR_FAIL_NULL_V(value, ERR_BUG);
|
||||
err = encode_variant(*value, buf, len, p_full_objects, p_depth + 1);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
||||
r_len += len;
|
||||
|
|
@ -1779,29 +1874,12 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
|
||||
} break;
|
||||
case Variant::ARRAY: {
|
||||
Array array = p_variant;
|
||||
const Array array = p_variant;
|
||||
|
||||
if (array.is_typed()) {
|
||||
Variant variant = array.get_typed_script();
|
||||
Ref<Script> script = variant;
|
||||
if (script.is_valid()) {
|
||||
if (p_full_objects) {
|
||||
String path = script->get_path();
|
||||
ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type.");
|
||||
_encode_string(path, buf, r_len);
|
||||
} else {
|
||||
_encode_string(EncodedObjectAsID::get_class_static(), buf, r_len);
|
||||
}
|
||||
} else if (array.get_typed_class_name() != StringName()) {
|
||||
_encode_string(p_full_objects ? array.get_typed_class_name().operator String() : EncodedObjectAsID::get_class_static(), buf, r_len);
|
||||
} else {
|
||||
// No need to check `p_full_objects` since for `Variant::OBJECT`
|
||||
// `array.get_typed_class_name()` should be non-empty.
|
||||
if (buf) {
|
||||
encode_uint32(array.get_typed_builtin(), buf);
|
||||
buf += 4;
|
||||
}
|
||||
r_len += 4;
|
||||
{
|
||||
Error err = _encode_container_type(array.get_element_type(), buf, r_len, p_full_objects);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1811,9 +1889,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
}
|
||||
r_len += 4;
|
||||
|
||||
for (const Variant &var : array) {
|
||||
for (const Variant &elem : array) {
|
||||
int len;
|
||||
Error err = encode_variant(var, buf, len, p_full_objects, p_depth + 1);
|
||||
Error err = encode_variant(elem, buf, len, p_full_objects, p_depth + 1);
|
||||
ERR_FAIL_COND_V(err, err);
|
||||
ERR_FAIL_COND_V(len % 4, ERR_BUG);
|
||||
if (buf) {
|
||||
|
|
@ -1823,7 +1901,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
}
|
||||
|
||||
} break;
|
||||
// arrays
|
||||
|
||||
// Packed arrays.
|
||||
case Variant::PACKED_BYTE_ARRAY: {
|
||||
Vector<uint8_t> data = p_variant;
|
||||
int datalen = data.size();
|
||||
|
|
@ -1939,7 +2018,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
|
||||
r_len += 4 + utf8.length() + 1;
|
||||
while (r_len % 4) {
|
||||
r_len++; //pad
|
||||
r_len++; // Pad.
|
||||
if (buf) {
|
||||
*(buf++) = 0;
|
||||
}
|
||||
|
|
@ -2057,9 +2136,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
}
|
||||
|
||||
Vector<float> vector3_to_float32_array(const Vector3 *vecs, size_t count) {
|
||||
// We always allocate a new array, and we don't memcpy.
|
||||
// We also don't consider returning a pointer to the passed vectors when sizeof(real_t) == 4.
|
||||
// One reason is that we could decide to put a 4th component in Vector3 for SIMD/mobile performance,
|
||||
// We always allocate a new array, and we don't `memcpy()`.
|
||||
// We also don't consider returning a pointer to the passed vectors when `sizeof(real_t) == 4`.
|
||||
// One reason is that we could decide to put a 4th component in `Vector3` for SIMD/mobile performance,
|
||||
// which would cause trouble with these optimizations.
|
||||
Vector<float> floats;
|
||||
if (count == 0) {
|
||||
|
|
|
|||
|
|
@ -84,6 +84,12 @@ static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) {
|
|||
return sizeof(uint32_t);
|
||||
}
|
||||
|
||||
static inline unsigned int encode_half(float p_float, uint8_t *p_arr) {
|
||||
encode_uint16(Math::make_half_float(p_float), p_arr);
|
||||
|
||||
return sizeof(uint16_t);
|
||||
}
|
||||
|
||||
static inline unsigned int encode_float(float p_float, uint8_t *p_arr) {
|
||||
MarshallFloat mf;
|
||||
mf.f = p_float;
|
||||
|
|
@ -172,6 +178,10 @@ static inline uint32_t decode_uint32(const uint8_t *p_arr) {
|
|||
return u;
|
||||
}
|
||||
|
||||
static inline float decode_half(const uint8_t *p_arr) {
|
||||
return Math::half_to_float(decode_uint16(p_arr));
|
||||
}
|
||||
|
||||
static inline float decode_float(const uint8_t *p_arr) {
|
||||
MarshallFloat mf;
|
||||
mf.i = decode_uint32(p_arr);
|
||||
|
|
|
|||
|
|
@ -74,6 +74,10 @@ bool MissingResource::is_recording_properties() const {
|
|||
return recording_properties;
|
||||
}
|
||||
|
||||
String MissingResource::get_save_class() const {
|
||||
return original_class;
|
||||
}
|
||||
|
||||
void MissingResource::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingResource::set_original_class);
|
||||
ClassDB::bind_method(D_METHOD("get_original_class"), &MissingResource::get_original_class);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ public:
|
|||
void set_recording_properties(bool p_enable);
|
||||
bool is_recording_properties() const;
|
||||
|
||||
virtual String get_save_class() const override;
|
||||
|
||||
MissingResource();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -41,13 +41,13 @@ protected:
|
|||
public:
|
||||
static NetSocket *create();
|
||||
|
||||
enum PollType {
|
||||
enum PollType : int32_t {
|
||||
POLL_TYPE_IN,
|
||||
POLL_TYPE_OUT,
|
||||
POLL_TYPE_IN_OUT
|
||||
};
|
||||
|
||||
enum Type {
|
||||
enum Type : int32_t {
|
||||
TYPE_NONE,
|
||||
TYPE_TCP,
|
||||
TYPE_UDP,
|
||||
|
|
@ -76,6 +76,8 @@ public:
|
|||
virtual void set_reuse_address_enabled(bool p_enabled) = 0;
|
||||
virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0;
|
||||
virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0;
|
||||
|
||||
virtual ~NetSocket() {}
|
||||
};
|
||||
|
||||
#endif // NET_SOCKET_H
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
class PackedDataContainer : public Resource {
|
||||
GDCLASS(PackedDataContainer, Resource);
|
||||
|
||||
enum {
|
||||
enum : uint32_t {
|
||||
TYPE_DICT = 0xFFFFFFFF,
|
||||
TYPE_ARRAY = 0xFFFFFFFE,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ Ref<StreamPeer> PacketPeerStream::get_stream_peer() const {
|
|||
|
||||
void PacketPeerStream::set_input_buffer_max_size(int p_max_size) {
|
||||
ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0.");
|
||||
//warning may lose packets
|
||||
// WARNING: May lose packets.
|
||||
ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data.");
|
||||
ring_buffer.resize(nearest_shift(next_power_of_2(p_max_size + 4)) - 1);
|
||||
input_buffer.resize(next_power_of_2(p_max_size + 4));
|
||||
|
|
|
|||
|
|
@ -29,15 +29,13 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "packet_peer_dtls.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
PacketPeerDTLS *(*PacketPeerDTLS::_create)() = nullptr;
|
||||
PacketPeerDTLS *(*PacketPeerDTLS::_create)(bool p_notify_postinitialize) = nullptr;
|
||||
bool PacketPeerDTLS::available = false;
|
||||
|
||||
PacketPeerDTLS *PacketPeerDTLS::create() {
|
||||
PacketPeerDTLS *PacketPeerDTLS::create(bool p_notify_postinitialize) {
|
||||
if (_create) {
|
||||
return _create();
|
||||
return _create(p_notify_postinitialize);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class PacketPeerDTLS : public PacketPeer {
|
|||
GDCLASS(PacketPeerDTLS, PacketPeer);
|
||||
|
||||
protected:
|
||||
static PacketPeerDTLS *(*_create)();
|
||||
static PacketPeerDTLS *(*_create)(bool p_notify_postinitialize);
|
||||
static void _bind_methods();
|
||||
|
||||
static bool available;
|
||||
|
|
@ -57,7 +57,7 @@ public:
|
|||
virtual void disconnect_from_peer() = 0;
|
||||
virtual Status get_status() const = 0;
|
||||
|
||||
static PacketPeerDTLS *create();
|
||||
static PacketPeerDTLS *create(bool p_notify_postinitialize = true);
|
||||
static bool is_available();
|
||||
|
||||
PacketPeerDTLS() {}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) {
|
|||
|
||||
Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, const String &p_if_name) {
|
||||
ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER);
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
|
|
@ -62,7 +62,7 @@ Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, const Strin
|
|||
|
||||
Error PacketPeerUDP::leave_multicast_group(IPAddress p_multi_address, const String &p_if_name) {
|
||||
ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED);
|
||||
return _sock->leave_multicast_group(p_multi_address, p_if_name);
|
||||
}
|
||||
|
|
@ -105,6 +105,19 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
|
|||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
/* Bogus GCC warning here:
|
||||
* In member function 'int RingBuffer<T>::read(T*, int, bool) [with T = unsigned char]',
|
||||
* inlined from 'virtual Error PacketPeerUDP::get_packet(const uint8_t**, int&)' at core/io/packet_peer_udp.cpp:112:9,
|
||||
* inlined from 'virtual Error PacketPeerUDP::get_packet(const uint8_t**, int&)' at core/io/packet_peer_udp.cpp:99:7:
|
||||
* Error: ./core/ring_buffer.h:68:46: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=]
|
||||
* 68 | p_buf[dst++] = read[pos + i];
|
||||
* | ~~~~~~~~~~~~~^~~~~~~
|
||||
*/
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic warning "-Wstringop-overflow=0"
|
||||
#endif
|
||||
|
||||
uint32_t size = 0;
|
||||
uint8_t ipv6[16] = {};
|
||||
rb.read(ipv6, 16, true);
|
||||
|
|
@ -115,11 +128,16 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
|
|||
--queue_count;
|
||||
*r_buffer = packet_buffer;
|
||||
r_buffer_size = size;
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED);
|
||||
|
||||
Error err;
|
||||
|
|
@ -160,7 +178,7 @@ int PacketPeerUDP::get_max_packet_size() const {
|
|||
}
|
||||
|
||||
Error PacketPeerUDP::bind(int p_port, const IPAddress &p_bind_address, int p_recv_buffer_size) {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
|
||||
|
|
@ -209,7 +227,7 @@ void PacketPeerUDP::disconnect_shared_socket() {
|
|||
|
||||
Error PacketPeerUDP::connect_to_host(const IPAddress &p_host, int p_port) {
|
||||
ERR_FAIL_COND_V(udp_server, ERR_LOCKED);
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive).");
|
||||
|
||||
|
|
@ -260,12 +278,12 @@ void PacketPeerUDP::close() {
|
|||
}
|
||||
|
||||
Error PacketPeerUDP::wait() {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
return _sock->poll(NetSocket::POLL_TYPE_IN, -1);
|
||||
}
|
||||
|
||||
Error PacketPeerUDP::_poll() {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
return FAILED;
|
||||
|
|
|
|||
|
|
@ -47,12 +47,13 @@ static int _get_pad(int p_alignment, int p_n) {
|
|||
}
|
||||
|
||||
void PCKPacker::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("pck_start", "pck_path", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(32), DEFVAL("0000000000000000000000000000000000000000000000000000000000000000"), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("add_file", "target_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("add_file_removal", "target_path"), &PCKPacker::add_file_removal);
|
||||
ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false));
|
||||
}
|
||||
|
||||
Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &p_key, bool p_encrypt_directory) {
|
||||
Error PCKPacker::pck_start(const String &p_pck_path, int p_alignment, const String &p_key, bool p_encrypt_directory) {
|
||||
ERR_FAIL_COND_V_MSG((p_key.is_empty() || !p_key.is_valid_hex_number(false) || p_key.length() != 64), ERR_CANT_CREATE, "Invalid Encryption Key (must be 64 characters long).");
|
||||
ERR_FAIL_COND_V_MSG(p_alignment <= 0, ERR_CANT_CREATE, "Invalid alignment, must be greater then 0.");
|
||||
|
||||
|
|
@ -83,8 +84,8 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
|
|||
}
|
||||
enc_dir = p_encrypt_directory;
|
||||
|
||||
file = FileAccess::open(p_file, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, "Can't open file to write: " + String(p_file) + ".");
|
||||
file = FileAccess::open(p_pck_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_CANT_CREATE, vformat("Can't open file to write: '%s'.", String(p_pck_path)));
|
||||
|
||||
alignment = p_alignment;
|
||||
|
||||
|
|
@ -106,23 +107,42 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) {
|
||||
Error PCKPacker::add_file_removal(const String &p_target_path) {
|
||||
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(p_src, FileAccess::READ);
|
||||
File pf;
|
||||
// Simplify path here and on every 'files' access so that paths that have extra '/'
|
||||
// symbols or 'res://' in them still match the MD5 hash for the saved path.
|
||||
pf.path = p_target_path.simplify_path().trim_prefix("res://");
|
||||
pf.ofs = ofs;
|
||||
pf.size = 0;
|
||||
pf.removal = true;
|
||||
|
||||
pf.md5.resize(16);
|
||||
pf.md5.fill(0);
|
||||
|
||||
files.push_back(pf);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error PCKPacker::add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt) {
|
||||
ERR_FAIL_COND_V_MSG(file.is_null(), ERR_INVALID_PARAMETER, "File must be opened before use.");
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(p_source_path, FileAccess::READ);
|
||||
if (f.is_null()) {
|
||||
return ERR_FILE_CANT_OPEN;
|
||||
}
|
||||
|
||||
File pf;
|
||||
// Simplify path here and on every 'files' access so that paths that have extra '/'
|
||||
// symbols in them still match to the MD5 hash for the saved path.
|
||||
pf.path = p_file.simplify_path();
|
||||
pf.src_path = p_src;
|
||||
// symbols or 'res://' in them still match the MD5 hash for the saved path.
|
||||
pf.path = p_target_path.simplify_path().trim_prefix("res://");
|
||||
pf.src_path = p_source_path;
|
||||
pf.ofs = ofs;
|
||||
pf.size = f->get_length();
|
||||
|
||||
Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_src);
|
||||
Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_source_path);
|
||||
{
|
||||
unsigned char hash[16];
|
||||
CryptoCore::md5(data.ptr(), data.size(), hash);
|
||||
|
|
@ -162,7 +182,7 @@ Error PCKPacker::flush(bool p_verbose) {
|
|||
}
|
||||
|
||||
// write the index
|
||||
file->store_32(files.size());
|
||||
file->store_32(uint32_t(files.size()));
|
||||
|
||||
Ref<FileAccessEncrypted> fae;
|
||||
Ref<FileAccess> fhead = file;
|
||||
|
|
@ -178,11 +198,12 @@ Error PCKPacker::flush(bool p_verbose) {
|
|||
}
|
||||
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
int string_len = files[i].path.utf8().length();
|
||||
CharString utf8_string = files[i].path.utf8();
|
||||
int string_len = utf8_string.length();
|
||||
int pad = _get_pad(4, string_len);
|
||||
|
||||
fhead->store_32(string_len + pad);
|
||||
fhead->store_buffer((const uint8_t *)files[i].path.utf8().get_data(), string_len);
|
||||
fhead->store_32(uint32_t(string_len + pad));
|
||||
fhead->store_buffer((const uint8_t *)utf8_string.get_data(), string_len);
|
||||
for (int j = 0; j < pad; j++) {
|
||||
fhead->store_8(0);
|
||||
}
|
||||
|
|
@ -195,6 +216,9 @@ Error PCKPacker::flush(bool p_verbose) {
|
|||
if (files[i].encrypted) {
|
||||
flags |= PACK_FILE_ENCRYPTED;
|
||||
}
|
||||
if (files[i].removal) {
|
||||
flags |= PACK_FILE_REMOVAL;
|
||||
}
|
||||
fhead->store_32(flags);
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +232,7 @@ Error PCKPacker::flush(bool p_verbose) {
|
|||
file->store_8(0);
|
||||
}
|
||||
|
||||
int64_t file_base = file->get_position();
|
||||
uint64_t file_base = file->get_position();
|
||||
file->seek(file_base_ofs);
|
||||
file->store_64(file_base); // update files base
|
||||
file->seek(file_base);
|
||||
|
|
@ -218,6 +242,10 @@ Error PCKPacker::flush(bool p_verbose) {
|
|||
|
||||
int count = 0;
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
if (files[i].removal) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Ref<FileAccess> src = FileAccess::open(files[i].src_path, FileAccess::READ);
|
||||
uint64_t to_write = files[i].size;
|
||||
|
||||
|
|
|
|||
|
|
@ -53,13 +53,15 @@ class PCKPacker : public RefCounted {
|
|||
uint64_t ofs = 0;
|
||||
uint64_t size = 0;
|
||||
bool encrypted = false;
|
||||
bool removal = false;
|
||||
Vector<uint8_t> md5;
|
||||
};
|
||||
Vector<File> files;
|
||||
|
||||
public:
|
||||
Error pck_start(const String &p_file, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false);
|
||||
Error add_file(const String &p_file, const String &p_src, bool p_encrypt = false);
|
||||
Error pck_start(const String &p_pck_path, int p_alignment = 32, const String &p_key = "0000000000000000000000000000000000000000000000000000000000000000", bool p_encrypt_directory = false);
|
||||
Error add_file(const String &p_target_path, const String &p_source_path, bool p_encrypt = false);
|
||||
Error add_file_removal(const String &p_target_path);
|
||||
Error flush(bool p_verbose = false);
|
||||
|
||||
PCKPacker() {}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@
|
|||
|
||||
#include "plist.h"
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/os/time.h"
|
||||
|
||||
PList::PLNodeType PListNode::get_type() const {
|
||||
return data_type;
|
||||
}
|
||||
|
|
@ -450,7 +453,7 @@ PList::PList() {
|
|||
PList::PList(const String &p_string) {
|
||||
String err_str;
|
||||
bool ok = load_string(p_string, err_str);
|
||||
ERR_FAIL_COND_MSG(!ok, "PList: " + err_str);
|
||||
ERR_FAIL_COND_MSG(!ok, vformat("PList: %s.", err_str));
|
||||
}
|
||||
|
||||
uint64_t PList::read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size) {
|
||||
|
|
@ -661,12 +664,12 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
|||
List<Ref<PListNode>> stack;
|
||||
String key;
|
||||
while (pos >= 0) {
|
||||
int open_token_s = p_string.find("<", pos);
|
||||
int open_token_s = p_string.find_char('<', pos);
|
||||
if (open_token_s == -1) {
|
||||
r_err_out = "Unexpected end of data. No tags found.";
|
||||
return false;
|
||||
}
|
||||
int open_token_e = p_string.find(">", open_token_s);
|
||||
int open_token_e = p_string.find_char('>', open_token_s);
|
||||
pos = open_token_e;
|
||||
|
||||
String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1);
|
||||
|
|
@ -676,7 +679,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
|||
}
|
||||
String value;
|
||||
if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... >
|
||||
int end_token_e = p_string.find(">", open_token_s);
|
||||
int end_token_e = p_string.find_char('>', open_token_s);
|
||||
pos = end_token_e;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -708,7 +711,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
|||
stack.push_back(dict);
|
||||
} else {
|
||||
// Add root node.
|
||||
if (!root.is_null()) {
|
||||
if (root.is_valid()) {
|
||||
r_err_out = "Root node already set.";
|
||||
return false;
|
||||
}
|
||||
|
|
@ -740,7 +743,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
|||
stack.push_back(arr);
|
||||
} else {
|
||||
// Add root node.
|
||||
if (!root.is_null()) {
|
||||
if (root.is_valid()) {
|
||||
r_err_out = "Root node already set.";
|
||||
return false;
|
||||
}
|
||||
|
|
@ -769,7 +772,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
|||
r_err_out = vformat("Mismatched <%s> tag.", token);
|
||||
return false;
|
||||
}
|
||||
int end_token_e = p_string.find(">", end_token_s);
|
||||
int end_token_e = p_string.find_char('>', end_token_s);
|
||||
pos = end_token_e;
|
||||
String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2);
|
||||
if (end_token != token) {
|
||||
|
|
@ -814,7 +817,7 @@ bool PList::load_string(const String &p_string, String &r_err_out) {
|
|||
}
|
||||
|
||||
PackedByteArray PList::save_asn1() const {
|
||||
if (root == nullptr) {
|
||||
if (root.is_null()) {
|
||||
ERR_FAIL_V_MSG(PackedByteArray(), "PList: Invalid PList, no root node.");
|
||||
}
|
||||
size_t size = root->get_asn1_size(1);
|
||||
|
|
@ -848,7 +851,7 @@ PackedByteArray PList::save_asn1() const {
|
|||
}
|
||||
|
||||
String PList::save_text() const {
|
||||
if (root == nullptr) {
|
||||
if (root.is_null()) {
|
||||
ERR_FAIL_V_MSG(String(), "PList: Invalid PList, no root node.");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,9 +33,7 @@
|
|||
|
||||
// Property list file format (application/x-plist) parser, property list ASN-1 serialization.
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/time.h"
|
||||
|
||||
class PListNode;
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
Vector<RemoteFilesystemClient::FileCache> RemoteFilesystemClient::_load_cache_file() {
|
||||
Ref<FileAccess> fa = FileAccess::open(cache_path.path_join(FILES_CACHE_FILE), FileAccess::READ);
|
||||
if (!fa.is_valid()) {
|
||||
if (fa.is_null()) {
|
||||
return Vector<FileCache>(); // No cache, return empty
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ Error RemoteFilesystemClient::_store_file(const String &p_path, const LocalVecto
|
|||
}
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(full_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Unable to open file for writing to remote filesystem cache: " + p_path);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, vformat("Unable to open file for writing to remote filesystem cache: '%s'.", p_path));
|
||||
f->store_buffer(p_file.ptr(), p_file.size());
|
||||
Error err = f->get_error();
|
||||
if (err) {
|
||||
|
|
@ -115,10 +115,10 @@ Error RemoteFilesystemClient::_store_cache_file(const Vector<FileCache> &p_cache
|
|||
String full_path = cache_path.path_join(FILES_CACHE_FILE);
|
||||
String base_file_dir = full_path.get_base_dir();
|
||||
Error err = DirAccess::make_dir_recursive_absolute(base_file_dir);
|
||||
ERR_FAIL_COND_V_MSG(err != OK && err != ERR_ALREADY_EXISTS, err, "Unable to create base directory to store cache file: " + base_file_dir);
|
||||
ERR_FAIL_COND_V_MSG(err != OK && err != ERR_ALREADY_EXISTS, err, vformat("Unable to create base directory to store cache file: '%s'.", base_file_dir));
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(full_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, "Unable to open the remote cache file for writing: " + full_path);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_FILE_CANT_OPEN, vformat("Unable to open the remote cache file for writing: '%s'.", full_path));
|
||||
f->store_line(itos(FILESYSTEM_CACHE_VERSION));
|
||||
for (int i = 0; i < p_cache.size(); i++) {
|
||||
String l = p_cache[i].path + "::" + itos(p_cache[i].server_modified_time) + "::" + itos(p_cache[i].modified_time);
|
||||
|
|
@ -151,10 +151,10 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int
|
|||
tcp_client.instantiate();
|
||||
|
||||
IPAddress ip = p_host.is_valid_ip_address() ? IPAddress(p_host) : IP::get_singleton()->resolve_hostname(p_host);
|
||||
ERR_FAIL_COND_V_MSG(!ip.is_valid(), ERR_INVALID_PARAMETER, "Unable to resolve remote filesystem server hostname: " + p_host);
|
||||
ERR_FAIL_COND_V_MSG(!ip.is_valid(), ERR_INVALID_PARAMETER, vformat("Unable to resolve remote filesystem server hostname: '%s'.", p_host));
|
||||
print_verbose(vformat("Remote Filesystem: Connecting to host %s, port %d.", ip, p_port));
|
||||
Error err = tcp_client->connect_to_host(ip, p_port);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Unable to open connection to remote file server (" + String(p_host) + ", port " + itos(p_port) + ") failed.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Unable to open connection to remote file server (%s, port %d) failed.", String(p_host), p_port));
|
||||
|
||||
while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTING) {
|
||||
tcp_client->poll();
|
||||
|
|
@ -162,7 +162,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int
|
|||
}
|
||||
|
||||
if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
|
||||
ERR_FAIL_V_MSG(ERR_CANT_CONNECT, "Connection to remote file server (" + String(p_host) + ", port " + itos(p_port) + ") failed.");
|
||||
ERR_FAIL_V_MSG(ERR_CANT_CONNECT, vformat("Connection to remote file server (%s, port %d) failed.", String(p_host), p_port));
|
||||
}
|
||||
|
||||
// Connection OK, now send the current file state.
|
||||
|
|
@ -237,7 +237,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int
|
|||
tcp_client->poll();
|
||||
ERR_FAIL_COND_V_MSG(tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED, ERR_CONNECTION_ERROR, "Remote filesystem server disconnected after sending header.");
|
||||
|
||||
uint32_t file_count = tcp_client->get_32();
|
||||
uint32_t file_count = tcp_client->get_u32();
|
||||
|
||||
ERR_FAIL_COND_V_MSG(tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED, ERR_CONNECTION_ERROR, "Remote filesystem server disconnected while waiting for file list");
|
||||
|
||||
|
|
@ -280,7 +280,7 @@ Error RemoteFilesystemClient::_synchronize_with_server(const String &p_host, int
|
|||
|
||||
err = tcp_client->get_data(file_buffer.ptr(), file_size);
|
||||
if (err != OK) {
|
||||
ERR_PRINT("Error retrieving file from remote filesystem: " + file);
|
||||
ERR_PRINT(vformat("Error retrieving file from remote filesystem: '%s'.", file));
|
||||
server_disconnected = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#ifndef REMOTE_FILESYSTEM_CLIENT_H
|
||||
#define REMOTE_FILESYSTEM_CLIENT_H
|
||||
|
||||
#include "core/io/ip_address.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
|
|
|
|||
|
|
@ -30,22 +30,18 @@
|
|||
|
||||
#include "resource.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/os.h"
|
||||
#include "scene/main/node.h" //only so casting works
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void Resource::emit_changed() {
|
||||
if (ResourceLoader::is_within_load() && MessageQueue::get_main_singleton() != MessageQueue::get_singleton() && !MessageQueue::get_singleton()->is_flushing()) {
|
||||
// Let the connection happen on the call queue, later, since signals are not thread-safe.
|
||||
call_deferred("emit_signal", CoreStringName(changed));
|
||||
} else {
|
||||
emit_signal(CoreStringName(changed));
|
||||
if (ResourceLoader::is_within_load() && !Thread::is_main_thread()) {
|
||||
ResourceLoader::resource_changed_emit(this);
|
||||
return;
|
||||
}
|
||||
|
||||
emit_signal(CoreStringName(changed));
|
||||
}
|
||||
|
||||
void Resource::_resource_path_changed() {
|
||||
|
|
@ -60,33 +56,33 @@ void Resource::set_path(const String &p_path, bool p_take_over) {
|
|||
p_take_over = false; // Can't take over an empty path
|
||||
}
|
||||
|
||||
ResourceCache::lock.lock();
|
||||
{
|
||||
MutexLock lock(ResourceCache::lock);
|
||||
|
||||
if (!path_cache.is_empty()) {
|
||||
ResourceCache::resources.erase(path_cache);
|
||||
}
|
||||
if (!path_cache.is_empty()) {
|
||||
ResourceCache::resources.erase(path_cache);
|
||||
}
|
||||
|
||||
path_cache = "";
|
||||
path_cache = "";
|
||||
|
||||
Ref<Resource> existing = ResourceCache::get_ref(p_path);
|
||||
Ref<Resource> existing = ResourceCache::get_ref(p_path);
|
||||
|
||||
if (existing.is_valid()) {
|
||||
if (p_take_over) {
|
||||
existing->path_cache = String();
|
||||
ResourceCache::resources.erase(p_path);
|
||||
} else {
|
||||
ResourceCache::lock.unlock();
|
||||
ERR_FAIL_MSG("Another resource is loaded from path '" + p_path + "' (possible cyclic resource inclusion).");
|
||||
if (existing.is_valid()) {
|
||||
if (p_take_over) {
|
||||
existing->path_cache = String();
|
||||
ResourceCache::resources.erase(p_path);
|
||||
} else {
|
||||
ERR_FAIL_MSG(vformat("Another resource is loaded from path '%s' (possible cyclic resource inclusion).", p_path));
|
||||
}
|
||||
}
|
||||
|
||||
path_cache = p_path;
|
||||
|
||||
if (!path_cache.is_empty()) {
|
||||
ResourceCache::resources[path_cache] = this;
|
||||
}
|
||||
}
|
||||
|
||||
path_cache = p_path;
|
||||
|
||||
if (!path_cache.is_empty()) {
|
||||
ResourceCache::resources[path_cache] = this;
|
||||
}
|
||||
ResourceCache::lock.unlock();
|
||||
|
||||
_resource_path_changed();
|
||||
}
|
||||
|
||||
|
|
@ -96,33 +92,45 @@ String Resource::get_path() const {
|
|||
|
||||
void Resource::set_path_cache(const String &p_path) {
|
||||
path_cache = p_path;
|
||||
GDVIRTUAL_CALL(_set_path_cache, p_path);
|
||||
}
|
||||
|
||||
static thread_local RandomPCG unique_id_gen(0, RandomPCG::DEFAULT_INC);
|
||||
|
||||
void Resource::seed_scene_unique_id(uint32_t p_seed) {
|
||||
unique_id_gen.seed(p_seed);
|
||||
}
|
||||
|
||||
String Resource::generate_scene_unique_id() {
|
||||
// Generate a unique enough hash, but still user-readable.
|
||||
// If it's not unique it does not matter because the saver will try again.
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime();
|
||||
uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec());
|
||||
hash = hash_murmur3_one_32(dt.year, hash);
|
||||
hash = hash_murmur3_one_32(dt.month, hash);
|
||||
hash = hash_murmur3_one_32(dt.day, hash);
|
||||
hash = hash_murmur3_one_32(dt.hour, hash);
|
||||
hash = hash_murmur3_one_32(dt.minute, hash);
|
||||
hash = hash_murmur3_one_32(dt.second, hash);
|
||||
hash = hash_murmur3_one_32(Math::rand(), hash);
|
||||
if (unique_id_gen.get_seed() == 0) {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime();
|
||||
uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec());
|
||||
hash = hash_murmur3_one_32(dt.year, hash);
|
||||
hash = hash_murmur3_one_32(dt.month, hash);
|
||||
hash = hash_murmur3_one_32(dt.day, hash);
|
||||
hash = hash_murmur3_one_32(dt.hour, hash);
|
||||
hash = hash_murmur3_one_32(dt.minute, hash);
|
||||
hash = hash_murmur3_one_32(dt.second, hash);
|
||||
hash = hash_murmur3_one_32(Math::rand(), hash);
|
||||
unique_id_gen.seed(hash);
|
||||
}
|
||||
|
||||
uint32_t random_num = unique_id_gen.rand();
|
||||
|
||||
static constexpr uint32_t characters = 5;
|
||||
static constexpr uint32_t char_count = ('z' - 'a');
|
||||
static constexpr uint32_t base = char_count + ('9' - '0');
|
||||
String id;
|
||||
for (uint32_t i = 0; i < characters; i++) {
|
||||
uint32_t c = hash % base;
|
||||
uint32_t c = random_num % base;
|
||||
if (c < char_count) {
|
||||
id += String::chr('a' + c);
|
||||
} else {
|
||||
id += String::chr('0' + (c - char_count));
|
||||
}
|
||||
hash /= base;
|
||||
random_num /= base;
|
||||
}
|
||||
|
||||
return id;
|
||||
|
|
@ -166,28 +174,29 @@ bool Resource::editor_can_reload_from_file() {
|
|||
}
|
||||
|
||||
void Resource::connect_changed(const Callable &p_callable, uint32_t p_flags) {
|
||||
if (ResourceLoader::is_within_load() && MessageQueue::get_main_singleton() != MessageQueue::get_singleton() && !MessageQueue::get_singleton()->is_flushing()) {
|
||||
// Let the check and connection happen on the call queue, later, since signals are not thread-safe.
|
||||
callable_mp(this, &Resource::connect_changed).call_deferred(p_callable, p_flags);
|
||||
if (ResourceLoader::is_within_load() && !Thread::is_main_thread()) {
|
||||
ResourceLoader::resource_changed_connect(this, p_callable, p_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_connected(CoreStringName(changed), p_callable) || p_flags & CONNECT_REFERENCE_COUNTED) {
|
||||
connect(CoreStringName(changed), p_callable, p_flags);
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::disconnect_changed(const Callable &p_callable) {
|
||||
if (ResourceLoader::is_within_load() && MessageQueue::get_main_singleton() != MessageQueue::get_singleton() && !MessageQueue::get_singleton()->is_flushing()) {
|
||||
// Let the check and disconnection happen on the call queue, later, since signals are not thread-safe.
|
||||
callable_mp(this, &Resource::disconnect_changed).call_deferred(p_callable);
|
||||
if (ResourceLoader::is_within_load() && !Thread::is_main_thread()) {
|
||||
ResourceLoader::resource_changed_disconnect(this, p_callable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_connected(CoreStringName(changed), p_callable)) {
|
||||
disconnect(CoreStringName(changed), p_callable);
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::reset_state() {
|
||||
GDVIRTUAL_CALL(_reset_state);
|
||||
}
|
||||
|
||||
Error Resource::copy_from(const Ref<Resource> &p_resource) {
|
||||
|
|
@ -222,7 +231,7 @@ void Resource::reload_from_file() {
|
|||
|
||||
Ref<Resource> s = ResourceLoader::load(ResourceLoader::path_remap(path), get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
|
||||
|
||||
if (!s.is_valid()) {
|
||||
if (s.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -416,21 +425,15 @@ 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;
|
||||
RID ret;
|
||||
if (!GDVIRTUAL_CALL(_get_rid, ret)) {
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if (_get_extension() && _get_extension()->get_rid) {
|
||||
ret = RID::from_uint64(_get_extension()->get_rid(_get_extension_instance()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (_get_extension() && _get_extension()->get_rid) {
|
||||
RID ret = RID::from_uint64(_get_extension()->get_rid(_get_extension_instance()));
|
||||
if (ret.is_valid()) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return RID();
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
@ -492,20 +495,18 @@ void Resource::set_as_translation_remapped(bool p_remapped) {
|
|||
return;
|
||||
}
|
||||
|
||||
ResourceCache::lock.lock();
|
||||
MutexLock lock(ResourceCache::lock);
|
||||
|
||||
if (p_remapped) {
|
||||
ResourceLoader::remapped_list.add(&remapped_list);
|
||||
} else {
|
||||
ResourceLoader::remapped_list.remove(&remapped_list);
|
||||
}
|
||||
|
||||
ResourceCache::lock.unlock();
|
||||
}
|
||||
|
||||
#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) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (p_id.is_empty()) {
|
||||
ResourceCache::path_cache_lock.write_lock();
|
||||
ResourceCache::resource_path_cache[p_path].erase(get_path());
|
||||
|
|
@ -515,9 +516,11 @@ void Resource::set_id_for_path(const String &p_path, const String &p_id) {
|
|||
ResourceCache::resource_path_cache[p_path][get_path()] = p_id;
|
||||
ResourceCache::path_cache_lock.write_unlock();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
String Resource::get_id_for_path(const String &p_path) const {
|
||||
#ifdef TOOLS_ENABLED
|
||||
ResourceCache::path_cache_lock.read_lock();
|
||||
if (ResourceCache::resource_path_cache[p_path].has(get_path())) {
|
||||
String result = ResourceCache::resource_path_cache[p_path][get_path()];
|
||||
|
|
@ -527,13 +530,16 @@ String Resource::get_id_for_path(const String &p_path) const {
|
|||
ResourceCache::path_cache_lock.read_unlock();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
void Resource::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_path", "path"), &Resource::_set_path);
|
||||
ClassDB::bind_method(D_METHOD("take_over_path", "path"), &Resource::_take_over_path);
|
||||
ClassDB::bind_method(D_METHOD("get_path"), &Resource::get_path);
|
||||
ClassDB::bind_method(D_METHOD("set_path_cache", "path"), &Resource::set_path_cache);
|
||||
ClassDB::bind_method(D_METHOD("set_name", "name"), &Resource::set_name);
|
||||
ClassDB::bind_method(D_METHOD("get_name"), &Resource::get_name);
|
||||
ClassDB::bind_method(D_METHOD("get_rid"), &Resource::get_rid);
|
||||
|
|
@ -541,6 +547,12 @@ void Resource::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("is_local_to_scene"), &Resource::is_local_to_scene);
|
||||
ClassDB::bind_method(D_METHOD("get_local_scene"), &Resource::get_local_scene);
|
||||
ClassDB::bind_method(D_METHOD("setup_local_to_scene"), &Resource::setup_local_to_scene);
|
||||
ClassDB::bind_method(D_METHOD("reset_state"), &Resource::reset_state);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_id_for_path", "path", "id"), &Resource::set_id_for_path);
|
||||
ClassDB::bind_method(D_METHOD("get_id_for_path", "path"), &Resource::get_id_for_path);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_built_in"), &Resource::is_built_in);
|
||||
|
||||
ClassDB::bind_static_method("Resource", D_METHOD("generate_scene_unique_id"), &Resource::generate_scene_unique_id);
|
||||
ClassDB::bind_method(D_METHOD("set_scene_unique_id", "id"), &Resource::set_scene_unique_id);
|
||||
|
|
@ -558,11 +570,10 @@ void Resource::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_scene_unique_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_unique_id", "get_scene_unique_id");
|
||||
|
||||
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);
|
||||
GDVIRTUAL_BIND(_setup_local_to_scene);
|
||||
GDVIRTUAL_BIND(_get_rid);
|
||||
GDVIRTUAL_BIND(_reset_state);
|
||||
GDVIRTUAL_BIND(_set_path_cache, "path");
|
||||
}
|
||||
|
||||
Resource::Resource() :
|
||||
|
|
@ -573,14 +584,13 @@ Resource::~Resource() {
|
|||
return;
|
||||
}
|
||||
|
||||
ResourceCache::lock.lock();
|
||||
MutexLock lock(ResourceCache::lock);
|
||||
// Only unregister from the cache if this is the actual resource listed there.
|
||||
// (Other resources can have the same value in `path_cache` if loaded with `CACHE_IGNORE`.)
|
||||
HashMap<String, Resource *>::Iterator E = ResourceCache::resources.find(path_cache);
|
||||
if (likely(E && E->value == this)) {
|
||||
ResourceCache::resources.remove(E);
|
||||
}
|
||||
ResourceCache::lock.unlock();
|
||||
}
|
||||
|
||||
HashMap<String, Resource *> ResourceCache::resources;
|
||||
|
|
@ -609,19 +619,21 @@ void ResourceCache::clear() {
|
|||
}
|
||||
|
||||
bool ResourceCache::has(const String &p_path) {
|
||||
lock.lock();
|
||||
Resource **res = nullptr;
|
||||
|
||||
Resource **res = resources.getptr(p_path);
|
||||
{
|
||||
MutexLock mutex_lock(lock);
|
||||
|
||||
if (res && (*res)->get_reference_count() == 0) {
|
||||
// This resource is in the process of being deleted, ignore its existence.
|
||||
(*res)->path_cache = String();
|
||||
resources.erase(p_path);
|
||||
res = nullptr;
|
||||
res = resources.getptr(p_path);
|
||||
|
||||
if (res && (*res)->get_reference_count() == 0) {
|
||||
// This resource is in the process of being deleted, ignore its existence.
|
||||
(*res)->path_cache = String();
|
||||
resources.erase(p_path);
|
||||
res = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (!res) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -631,35 +643,34 @@ bool ResourceCache::has(const String &p_path) {
|
|||
|
||||
Ref<Resource> ResourceCache::get_ref(const String &p_path) {
|
||||
Ref<Resource> ref;
|
||||
lock.lock();
|
||||
{
|
||||
MutexLock mutex_lock(lock);
|
||||
Resource **res = resources.getptr(p_path);
|
||||
|
||||
Resource **res = resources.getptr(p_path);
|
||||
if (res) {
|
||||
ref = Ref<Resource>(*res);
|
||||
}
|
||||
|
||||
if (res) {
|
||||
ref = Ref<Resource>(*res);
|
||||
if (res && ref.is_null()) {
|
||||
// This resource is in the process of being deleted, ignore its existence
|
||||
(*res)->path_cache = String();
|
||||
resources.erase(p_path);
|
||||
res = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (res && !ref.is_valid()) {
|
||||
// This resource is in the process of being deleted, ignore its existence
|
||||
(*res)->path_cache = String();
|
||||
resources.erase(p_path);
|
||||
res = nullptr;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) {
|
||||
lock.lock();
|
||||
MutexLock mutex_lock(lock);
|
||||
|
||||
LocalVector<String> to_remove;
|
||||
|
||||
for (KeyValue<String, Resource *> &E : resources) {
|
||||
Ref<Resource> ref = Ref<Resource>(E.value);
|
||||
|
||||
if (!ref.is_valid()) {
|
||||
if (ref.is_null()) {
|
||||
// This resource is in the process of being deleted, ignore its existence
|
||||
E.value->path_cache = String();
|
||||
to_remove.push_back(E.key);
|
||||
|
|
@ -672,14 +683,9 @@ void ResourceCache::get_cached_resources(List<Ref<Resource>> *p_resources) {
|
|||
for (const String &E : to_remove) {
|
||||
resources.erase(E);
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
int ResourceCache::get_cached_resource_count() {
|
||||
lock.lock();
|
||||
int rc = resources.size();
|
||||
lock.unlock();
|
||||
|
||||
return rc;
|
||||
MutexLock mutex_lock(lock);
|
||||
return resources.size();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,11 +40,15 @@
|
|||
|
||||
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()); } \
|
||||
virtual String get_base_extension() const override { return m_ext; } \
|
||||
\
|
||||
#define RES_BASE_EXTENSION(m_ext) \
|
||||
public: \
|
||||
static void register_custom_data_to_otdb() { \
|
||||
ClassDB::add_resource_base_extension(m_ext, get_class_static()); \
|
||||
} \
|
||||
virtual String get_base_extension() const override { \
|
||||
return m_ext; \
|
||||
} \
|
||||
\
|
||||
private:
|
||||
|
||||
class Resource : public RefCounted {
|
||||
|
|
@ -87,6 +91,11 @@ protected:
|
|||
virtual void reset_local_to_scene();
|
||||
GDVIRTUAL0(_setup_local_to_scene);
|
||||
|
||||
GDVIRTUAL0RC(RID, _get_rid);
|
||||
|
||||
GDVIRTUAL1C(_set_path_cache, String);
|
||||
GDVIRTUAL0(_reset_state);
|
||||
|
||||
public:
|
||||
static Node *(*_get_local_scene_func)(); //used by editor
|
||||
static void (*_update_configuration_warning)(); //used by editor
|
||||
|
|
@ -109,6 +118,7 @@ public:
|
|||
virtual void set_path_cache(const String &p_path); // Set raw path without involving resource cache.
|
||||
_FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); }
|
||||
|
||||
static void seed_scene_unique_id(uint32_t p_seed);
|
||||
static String generate_scene_unique_id();
|
||||
void set_scene_unique_id(const String &p_id);
|
||||
String get_scene_unique_id() const;
|
||||
|
|
@ -142,11 +152,9 @@ public:
|
|||
|
||||
virtual RID get_rid() const; // some resources may offer conversion to RID
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
//helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored
|
||||
void set_id_for_path(const String &p_path, const String &p_id);
|
||||
String get_id_for_path(const String &p_path) const;
|
||||
#endif
|
||||
|
||||
Resource();
|
||||
~Resource();
|
||||
|
|
|
|||
|
|
@ -33,8 +33,6 @@
|
|||
#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/io/missing_resource.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/version.h"
|
||||
|
|
@ -165,7 +163,7 @@ StringName ResourceLoaderBinary::_get_string() {
|
|||
}
|
||||
f->get_buffer((uint8_t *)&str_buf[0], len);
|
||||
String s;
|
||||
s.parse_utf8(&str_buf[0]);
|
||||
s.parse_utf8(&str_buf[0], len);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
@ -411,7 +409,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
|
|||
|
||||
//always use internal cache for loading internal resources
|
||||
if (!internal_index_cache.has(path)) {
|
||||
WARN_PRINT(String("Couldn't load resource (no cache): " + path).utf8().get_data());
|
||||
WARN_PRINT(vformat("Couldn't load resource (no cache): %s.", path));
|
||||
r_v = Variant();
|
||||
} else {
|
||||
r_v = internal_index_cache[path];
|
||||
|
|
@ -435,7 +433,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
|
|||
Ref<Resource> res = ResourceLoader::load(path, exttype, cache_mode_for_external);
|
||||
|
||||
if (res.is_null()) {
|
||||
WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
|
||||
WARN_PRINT(vformat("Couldn't load resource: %s.", path));
|
||||
}
|
||||
r_v = res;
|
||||
|
||||
|
|
@ -458,7 +456,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
|
|||
ResourceLoader::notify_dependency_error(local_path, external_resources[erindex].path, external_resources[erindex].type);
|
||||
} else {
|
||||
error = ERR_FILE_MISSING_DEPENDENCIES;
|
||||
ERR_FAIL_V_MSG(error, "Can't load dependency: " + external_resources[erindex].path + ".");
|
||||
ERR_FAIL_V_MSG(error, vformat("Can't load dependency: '%s'.", external_resources[erindex].path));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -699,12 +697,12 @@ Error ResourceLoaderBinary::load() {
|
|||
|
||||
external_resources.write[i].path = path; //remap happens here, not on load because on load it can actually be used for filesystem dock resource remap
|
||||
external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external);
|
||||
if (!external_resources[i].load_token.is_valid()) {
|
||||
if (external_resources[i].load_token.is_null()) {
|
||||
if (!ResourceLoader::get_abort_on_missing_resources()) {
|
||||
ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type);
|
||||
} else {
|
||||
error = ERR_FILE_MISSING_DEPENDENCIES;
|
||||
ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + ".");
|
||||
ERR_FAIL_V_MSG(error, vformat("Can't load dependency: '%s'.", path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -780,7 +778,7 @@ Error ResourceLoaderBinary::load() {
|
|||
obj = missing_resource;
|
||||
} else {
|
||||
error = ERR_FILE_CORRUPT;
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + ".");
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, vformat("'%s': Resource of unrecognized type in file: '%s'.", local_path, t));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -789,7 +787,7 @@ Error ResourceLoaderBinary::load() {
|
|||
String obj_class = obj->get_class();
|
||||
error = ERR_FILE_CORRUPT;
|
||||
memdelete(obj); //bye
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + ".");
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, vformat("'%s': Resource type in resource field not a resource, type is: %s.", local_path, obj_class));
|
||||
}
|
||||
|
||||
res = Ref<Resource>(r);
|
||||
|
|
@ -833,7 +831,7 @@ Error ResourceLoaderBinary::load() {
|
|||
}
|
||||
|
||||
bool set_valid = true;
|
||||
if (value.get_type() == Variant::OBJECT && missing_resource != nullptr) {
|
||||
if (value.get_type() == Variant::OBJECT && missing_resource == nullptr && ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) {
|
||||
// If the property being set is a missing resource (and the parent is not),
|
||||
// then setting it will most likely not work.
|
||||
// Instead, save it as metadata.
|
||||
|
|
@ -857,6 +855,19 @@ Error ResourceLoaderBinary::load() {
|
|||
}
|
||||
}
|
||||
|
||||
if (value.get_type() == Variant::DICTIONARY) {
|
||||
Dictionary set_dict = value;
|
||||
bool is_get_valid = false;
|
||||
Variant get_value = res->get(name, &is_get_valid);
|
||||
if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
|
||||
Dictionary get_dict = get_value;
|
||||
if (!set_dict.is_same_typed(get_dict)) {
|
||||
value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
|
||||
get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (set_valid) {
|
||||
res->set(name, value);
|
||||
}
|
||||
|
|
@ -898,7 +909,7 @@ void ResourceLoaderBinary::set_translation_remapped(bool p_remapped) {
|
|||
|
||||
static void save_ustring(Ref<FileAccess> f, const String &p_string) {
|
||||
CharString utf8 = p_string.utf8();
|
||||
f->store_32(utf8.length() + 1);
|
||||
f->store_32(uint32_t(utf8.length() + 1));
|
||||
f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
|
||||
}
|
||||
|
||||
|
|
@ -908,7 +919,7 @@ static String get_ustring(Ref<FileAccess> f) {
|
|||
str_buf.resize(len);
|
||||
f->get_buffer((uint8_t *)&str_buf[0], len);
|
||||
String s;
|
||||
s.parse_utf8(&str_buf[0]);
|
||||
s.parse_utf8(&str_buf[0], len);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
@ -922,7 +933,7 @@ String ResourceLoaderBinary::get_unicode_string() {
|
|||
}
|
||||
f->get_buffer((uint8_t *)&str_buf[0], len);
|
||||
String s;
|
||||
s.parse_utf8(&str_buf[0]);
|
||||
s.parse_utf8(&str_buf[0], len);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
@ -986,7 +997,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
|
|||
error = fac->open_after_magic(f);
|
||||
if (error != OK) {
|
||||
f.unref();
|
||||
ERR_FAIL_MSG("Failed to open binary resource file: " + local_path + ".");
|
||||
ERR_FAIL_MSG(vformat("Failed to open binary resource file: '%s'.", local_path));
|
||||
}
|
||||
f = fac;
|
||||
|
||||
|
|
@ -994,7 +1005,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
|
|||
// Not normal.
|
||||
error = ERR_FILE_UNRECOGNIZED;
|
||||
f.unref();
|
||||
ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + ".");
|
||||
ERR_FAIL_MSG(vformat("Unrecognized binary resource file: '%s'.", local_path));
|
||||
}
|
||||
|
||||
bool big_endian = f->get_32();
|
||||
|
|
@ -1038,7 +1049,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
|
|||
f->real_is_double = (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_REAL_T_IS_DOUBLE) != 0;
|
||||
|
||||
if (using_uids) {
|
||||
uid = f->get_64();
|
||||
uid = ResourceUID::ID(f->get_64());
|
||||
} else {
|
||||
f->get_64(); // skip over uid field
|
||||
uid = ResourceUID::INVALID_ID;
|
||||
|
|
@ -1071,7 +1082,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
|
|||
er.type = get_unicode_string();
|
||||
er.path = get_unicode_string();
|
||||
if (using_uids) {
|
||||
er.uid = f->get_64();
|
||||
er.uid = ResourceUID::ID(f->get_64());
|
||||
if (!p_keep_uuid_paths && er.uid != ResourceUID::INVALID_ID) {
|
||||
if (ResourceUID::get_singleton()->has_id(er.uid)) {
|
||||
// If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
|
||||
|
|
@ -1080,10 +1091,10 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
|
|||
#ifdef TOOLS_ENABLED
|
||||
// Silence a warning that can happen during the initial filesystem scan due to cache being regenerated.
|
||||
if (ResourceLoader::get_resource_uid(res_path) != er.uid) {
|
||||
WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
|
||||
WARN_PRINT(vformat("'%s': In external resource #%d, invalid UID: '%s' - using text path instead: '%s'.", res_path, i, ResourceUID::get_singleton()->id_to_text(er.uid), er.path));
|
||||
}
|
||||
#else
|
||||
WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
|
||||
WARN_PRINT(vformat("'%s': In external resource #%d, invalid UID: '%s' - using text path instead: '%s'.", res_path, i, ResourceUID::get_singleton()->id_to_text(er.uid), er.path));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -1107,7 +1118,7 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
|
|||
if (f->eof_reached()) {
|
||||
error = ERR_FILE_CORRUPT;
|
||||
f.unref();
|
||||
ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + ".");
|
||||
ERR_FAIL_MSG(vformat("Premature end of file (EOF): '%s'.", local_path));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1211,7 +1222,7 @@ Ref<Resource> ResourceFormatLoaderBinary::load(const String &p_path, const Strin
|
|||
Error err;
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot open file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), vformat("Cannot open file '%s'.", p_path));
|
||||
|
||||
ResourceLoaderBinary loader;
|
||||
switch (p_cache_mode) {
|
||||
|
|
@ -1255,6 +1266,11 @@ void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String
|
|||
return;
|
||||
}
|
||||
|
||||
// res files not supported for GDExtension.
|
||||
if (p_type == "GDExtension") {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> extensions;
|
||||
ClassDB::get_extensions_for_type(p_type, &extensions);
|
||||
|
||||
|
|
@ -1283,7 +1299,7 @@ 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) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_MSG(f.is_null(), vformat("Cannot open file '%s'.", p_path));
|
||||
|
||||
ResourceLoaderBinary loader;
|
||||
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
|
||||
|
|
@ -1293,7 +1309,7 @@ void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<Str
|
|||
|
||||
Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) {
|
||||
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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Cannot open file '%s'.", p_path));
|
||||
|
||||
Ref<FileAccess> fw;
|
||||
|
||||
|
|
@ -1306,23 +1322,23 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
|||
Ref<FileAccessCompressed> fac;
|
||||
fac.instantiate();
|
||||
Error err = fac->open_after_magic(f);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot open file '%s'.", p_path));
|
||||
f = fac;
|
||||
|
||||
Ref<FileAccessCompressed> facw;
|
||||
facw.instantiate();
|
||||
facw->configure("RSCC");
|
||||
err = facw->open_internal(p_path + ".depren", FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".depren'.");
|
||||
ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, vformat("Cannot create file '%s.depren'.", p_path));
|
||||
|
||||
fw = facw;
|
||||
|
||||
} else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') {
|
||||
// Not normal.
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file '" + local_path + "'.");
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Unrecognized binary resource file '%s'.", local_path));
|
||||
} else {
|
||||
fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + ".depren'.");
|
||||
ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, vformat("Cannot create file '%s.depren'.", p_path));
|
||||
|
||||
uint8_t magic[4] = { 'R', 'S', 'R', 'C' };
|
||||
fw->store_buffer(magic, 4);
|
||||
|
|
@ -1354,12 +1370,12 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
|||
|
||||
// Use the old approach.
|
||||
|
||||
WARN_PRINT("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'.");
|
||||
WARN_PRINT(vformat("This file is old, so it can't refactor dependencies, opening and resaving '%s'.", p_path));
|
||||
|
||||
Error err;
|
||||
f = FileAccess::open(p_path, FileAccess::READ, &err);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_OPEN, "Cannot open file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_OPEN, vformat("Cannot open file '%s'.", p_path));
|
||||
|
||||
ResourceLoaderBinary loader;
|
||||
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
|
||||
|
|
@ -1371,7 +1387,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
|||
|
||||
ERR_FAIL_COND_V(err != ERR_FILE_EOF, ERR_FILE_CORRUPT);
|
||||
Ref<Resource> res = loader.get_resource();
|
||||
ERR_FAIL_COND_V(!res.is_valid(), ERR_FILE_CORRUPT);
|
||||
ERR_FAIL_COND_V(res.is_null(), ERR_FILE_CORRUPT);
|
||||
|
||||
return ResourceFormatSaverBinary::singleton->save(res, p_path);
|
||||
}
|
||||
|
|
@ -1459,7 +1475,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
|||
|
||||
if (using_uids) {
|
||||
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(full_path);
|
||||
fw->store_64(uid);
|
||||
fw->store_64(uint64_t(uid));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1505,7 +1521,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
|||
|
||||
void ResourceFormatLoaderBinary::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_MSG(f.is_null(), "Cannot open file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_MSG(f.is_null(), vformat("Cannot open file '%s'.", p_path));
|
||||
|
||||
ResourceLoaderBinary loader;
|
||||
loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path);
|
||||
|
|
@ -1559,6 +1575,10 @@ ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_pat
|
|||
return loader.uid;
|
||||
}
|
||||
|
||||
bool ResourceFormatLoaderBinary::has_custom_uid_support() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////
|
||||
|
|
@ -1587,11 +1607,11 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
int64_t val = p_property;
|
||||
if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) {
|
||||
f->store_32(VARIANT_INT64);
|
||||
f->store_64(val);
|
||||
f->store_64(uint64_t(val));
|
||||
|
||||
} else {
|
||||
f->store_32(VARIANT_INT);
|
||||
f->store_32(int32_t(p_property));
|
||||
f->store_32(uint32_t(p_property));
|
||||
}
|
||||
|
||||
} break;
|
||||
|
|
@ -1623,8 +1643,8 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
case Variant::VECTOR2I: {
|
||||
f->store_32(VARIANT_VECTOR2I);
|
||||
Vector2i val = p_property;
|
||||
f->store_32(val.x);
|
||||
f->store_32(val.y);
|
||||
f->store_32(uint32_t(val.x));
|
||||
f->store_32(uint32_t(val.y));
|
||||
|
||||
} break;
|
||||
case Variant::RECT2: {
|
||||
|
|
@ -1639,10 +1659,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
case Variant::RECT2I: {
|
||||
f->store_32(VARIANT_RECT2I);
|
||||
Rect2i val = p_property;
|
||||
f->store_32(val.position.x);
|
||||
f->store_32(val.position.y);
|
||||
f->store_32(val.size.x);
|
||||
f->store_32(val.size.y);
|
||||
f->store_32(uint32_t(val.position.x));
|
||||
f->store_32(uint32_t(val.position.y));
|
||||
f->store_32(uint32_t(val.size.x));
|
||||
f->store_32(uint32_t(val.size.y));
|
||||
|
||||
} break;
|
||||
case Variant::VECTOR3: {
|
||||
|
|
@ -1656,9 +1676,9 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
case Variant::VECTOR3I: {
|
||||
f->store_32(VARIANT_VECTOR3I);
|
||||
Vector3i val = p_property;
|
||||
f->store_32(val.x);
|
||||
f->store_32(val.y);
|
||||
f->store_32(val.z);
|
||||
f->store_32(uint32_t(val.x));
|
||||
f->store_32(uint32_t(val.y));
|
||||
f->store_32(uint32_t(val.z));
|
||||
|
||||
} break;
|
||||
case Variant::VECTOR4: {
|
||||
|
|
@ -1673,10 +1693,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
case Variant::VECTOR4I: {
|
||||
f->store_32(VARIANT_VECTOR4I);
|
||||
Vector4i val = p_property;
|
||||
f->store_32(val.x);
|
||||
f->store_32(val.y);
|
||||
f->store_32(val.z);
|
||||
f->store_32(val.w);
|
||||
f->store_32(uint32_t(val.x));
|
||||
f->store_32(uint32_t(val.y));
|
||||
f->store_32(uint32_t(val.z));
|
||||
f->store_32(uint32_t(val.w));
|
||||
|
||||
} break;
|
||||
case Variant::PLANE: {
|
||||
|
|
@ -1799,14 +1819,14 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_16(snc);
|
||||
for (int i = 0; i < np.get_name_count(); i++) {
|
||||
if (string_map.has(np.get_name(i))) {
|
||||
f->store_32(string_map[np.get_name(i)]);
|
||||
f->store_32(uint32_t(string_map[np.get_name(i)]));
|
||||
} else {
|
||||
save_unicode_string(f, np.get_name(i), true);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < np.get_subname_count(); i++) {
|
||||
if (string_map.has(np.get_subname(i))) {
|
||||
f->store_32(string_map[np.get_subname(i)]);
|
||||
f->store_32(uint32_t(string_map[np.get_subname(i)]));
|
||||
} else {
|
||||
save_unicode_string(f, np.get_subname(i), true);
|
||||
}
|
||||
|
|
@ -1817,7 +1837,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_RID);
|
||||
WARN_PRINT("Can't save RIDs.");
|
||||
RID val = p_property;
|
||||
f->store_32(val.get_id());
|
||||
f->store_32(uint32_t(val.get_id()));
|
||||
} break;
|
||||
case Variant::OBJECT: {
|
||||
f->store_32(VARIANT_OBJECT);
|
||||
|
|
@ -1829,7 +1849,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
|
||||
if (!res->is_built_in()) {
|
||||
f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX);
|
||||
f->store_32(external_resources[res]);
|
||||
f->store_32(uint32_t(external_resources[res]));
|
||||
} else {
|
||||
if (!resource_map.has(res)) {
|
||||
f->store_32(OBJECT_EMPTY);
|
||||
|
|
@ -1837,7 +1857,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
}
|
||||
|
||||
f->store_32(OBJECT_INTERNAL_RESOURCE);
|
||||
f->store_32(resource_map[res]);
|
||||
f->store_32(uint32_t(resource_map[res]));
|
||||
//internal resource
|
||||
}
|
||||
|
||||
|
|
@ -1878,7 +1898,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_BYTE_ARRAY);
|
||||
Vector<uint8_t> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const uint8_t *r = arr.ptr();
|
||||
f->store_buffer(r, len);
|
||||
_pad_buffer(f, len);
|
||||
|
|
@ -1888,10 +1908,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_INT32_ARRAY);
|
||||
Vector<int32_t> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const int32_t *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
f->store_32(r[i]);
|
||||
f->store_32(uint32_t(r[i]));
|
||||
}
|
||||
|
||||
} break;
|
||||
|
|
@ -1899,10 +1919,10 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_INT64_ARRAY);
|
||||
Vector<int64_t> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const int64_t *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
f->store_64(r[i]);
|
||||
f->store_64(uint64_t(r[i]));
|
||||
}
|
||||
|
||||
} break;
|
||||
|
|
@ -1910,7 +1930,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_FLOAT32_ARRAY);
|
||||
Vector<float> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const float *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
f->store_float(r[i]);
|
||||
|
|
@ -1921,7 +1941,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_FLOAT64_ARRAY);
|
||||
Vector<double> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const double *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
f->store_double(r[i]);
|
||||
|
|
@ -1932,7 +1952,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_STRING_ARRAY);
|
||||
Vector<String> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const String *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
save_unicode_string(f, r[i]);
|
||||
|
|
@ -1943,7 +1963,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_VECTOR2_ARRAY);
|
||||
Vector<Vector2> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const Vector2 *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
f->store_real(r[i].x);
|
||||
|
|
@ -1955,7 +1975,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_VECTOR3_ARRAY);
|
||||
Vector<Vector3> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const Vector3 *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
f->store_real(r[i].x);
|
||||
|
|
@ -1968,7 +1988,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_COLOR_ARRAY);
|
||||
Vector<Color> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const Color *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
f->store_float(r[i].r);
|
||||
|
|
@ -1982,7 +2002,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
|
|||
f->store_32(VARIANT_PACKED_VECTOR4_ARRAY);
|
||||
Vector<Vector4> arr = p_property;
|
||||
int len = arr.size();
|
||||
f->store_32(len);
|
||||
f->store_32(uint32_t(len));
|
||||
const Vector4 *r = arr.ptr();
|
||||
for (int i = 0; i < len; i++) {
|
||||
f->store_real(r[i].x);
|
||||
|
|
@ -2009,7 +2029,7 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
|
|||
|
||||
if (!p_main && (!bundle_resources) && !res->is_built_in()) {
|
||||
if (res->get_path() == path) {
|
||||
ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded.");
|
||||
ERR_PRINT(vformat("Circular reference to resource being saved found: '%s' will be null next time it's loaded.", local_path));
|
||||
return;
|
||||
}
|
||||
int idx = external_resources.size();
|
||||
|
|
@ -2064,6 +2084,8 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
|
|||
|
||||
case Variant::DICTIONARY: {
|
||||
Dictionary d = p_variant;
|
||||
_find_resources(d.get_typed_key_script());
|
||||
_find_resources(d.get_typed_value_script());
|
||||
List<Variant> keys;
|
||||
d.get_key_list(&keys);
|
||||
for (const Variant &E : keys) {
|
||||
|
|
@ -2091,9 +2113,9 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
|
|||
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) {
|
||||
p_f->store_32((utf8.length() + 1) | 0x80000000);
|
||||
p_f->store_32(uint32_t((utf8.length() + 1) | 0x80000000));
|
||||
} else {
|
||||
p_f->store_32(utf8.length() + 1);
|
||||
p_f->store_32(uint32_t(utf8.length() + 1));
|
||||
}
|
||||
p_f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
|
||||
}
|
||||
|
|
@ -2119,6 +2141,8 @@ static String _resource_get_class(Ref<Resource> p_resource) {
|
|||
}
|
||||
|
||||
Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
|
||||
Resource::seed_scene_unique_id(p_path.hash());
|
||||
|
||||
Error err;
|
||||
Ref<FileAccess> f;
|
||||
if (p_flags & ResourceSaver::FLAG_COMPRESS) {
|
||||
|
|
@ -2131,7 +2155,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
f = FileAccess::open(p_path, FileAccess::WRITE, &err);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot create file '%s'.", p_path));
|
||||
|
||||
relative_paths = p_flags & ResourceSaver::FLAG_RELATIVE_PATHS;
|
||||
skip_editor = p_flags & ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES;
|
||||
|
|
@ -2192,7 +2216,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
f->store_32(format_flags);
|
||||
}
|
||||
ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true);
|
||||
f->store_64(uid);
|
||||
f->store_64(uint64_t(uid));
|
||||
if (!script_class.is_empty()) {
|
||||
save_unicode_string(f, script_class);
|
||||
}
|
||||
|
|
@ -2203,10 +2227,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
|
||||
List<ResourceData> resources;
|
||||
|
||||
Dictionary missing_resource_properties = p_resource->get_meta(META_MISSING_RESOURCES, Dictionary());
|
||||
|
||||
{
|
||||
for (const Ref<Resource> &E : saved_resources) {
|
||||
Dictionary missing_resource_properties = E->get_meta(META_MISSING_RESOURCES, Dictionary());
|
||||
|
||||
ResourceData &rd = resources.push_back(ResourceData())->get();
|
||||
rd.type = _resource_get_class(E);
|
||||
|
||||
|
|
@ -2221,7 +2245,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
continue;
|
||||
}
|
||||
|
||||
if ((F.usage & PROPERTY_USAGE_STORAGE)) {
|
||||
if ((F.usage & PROPERTY_USAGE_STORAGE) || missing_resource_properties.has(F.name)) {
|
||||
Property p;
|
||||
p.name_idx = get_string_index(F.name);
|
||||
|
||||
|
|
@ -2236,7 +2260,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
p.value = E->get(F.name);
|
||||
}
|
||||
|
||||
if (p.pi.type == Variant::OBJECT && missing_resource_properties.has(F.name)) {
|
||||
if (F.type == Variant::OBJECT && missing_resource_properties.has(F.name)) {
|
||||
// Was this missing resource overridden? If so do not save the old value.
|
||||
Ref<Resource> res = p.value;
|
||||
if (res.is_null()) {
|
||||
|
|
@ -2258,7 +2282,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
}
|
||||
}
|
||||
|
||||
f->store_32(strings.size()); //string table size
|
||||
f->store_32(uint32_t(strings.size())); //string table size
|
||||
for (int i = 0; i < strings.size(); i++) {
|
||||
save_unicode_string(f, strings[i]);
|
||||
}
|
||||
|
|
@ -2278,10 +2302,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
res_path = relative_paths ? local_path.path_to_file(res_path) : res_path;
|
||||
save_unicode_string(f, res_path);
|
||||
ResourceUID::ID ruid = ResourceSaver::get_resource_id_for_path(save_order[i]->get_path(), false);
|
||||
f->store_64(ruid);
|
||||
f->store_64(uint64_t(ruid));
|
||||
}
|
||||
// save internal resource table
|
||||
f->store_32(saved_resources.size()); //amount of internal resources
|
||||
f->store_32(uint32_t(saved_resources.size())); //amount of internal resources
|
||||
Vector<uint64_t> ofs_pos;
|
||||
HashSet<String> used_unique_ids;
|
||||
|
||||
|
|
@ -2336,10 +2360,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
for (const ResourceData &rd : resources) {
|
||||
ofs_table.push_back(f->get_position());
|
||||
save_unicode_string(f, rd.type);
|
||||
f->store_32(rd.properties.size());
|
||||
f->store_32(uint32_t(rd.properties.size()));
|
||||
|
||||
for (const Property &p : rd.properties) {
|
||||
f->store_32(p.name_idx);
|
||||
f->store_32(uint32_t(p.name_idx));
|
||||
write_variant(f, p.value, resource_map, external_resources, string_map, p.pi);
|
||||
}
|
||||
}
|
||||
|
|
@ -2362,7 +2386,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
|
|||
|
||||
Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceUID::ID p_uid) {
|
||||
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 + "'.");
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Cannot open file '%s'.", p_path));
|
||||
|
||||
Ref<FileAccess> fw;
|
||||
|
||||
|
|
@ -2375,14 +2399,14 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU
|
|||
Ref<FileAccessCompressed> fac;
|
||||
fac.instantiate();
|
||||
Error err = fac->open_after_magic(f);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot open file '%s'.", p_path));
|
||||
f = fac;
|
||||
|
||||
Ref<FileAccessCompressed> facw;
|
||||
facw.instantiate();
|
||||
facw->configure("RSCC");
|
||||
err = facw->open_internal(p_path + ".uidren", FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".uidren'.");
|
||||
ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, vformat("Cannot create file '%s.uidren'.", p_path));
|
||||
|
||||
fw = facw;
|
||||
|
||||
|
|
@ -2391,7 +2415,7 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU
|
|||
return ERR_FILE_UNRECOGNIZED;
|
||||
} else {
|
||||
fw = FileAccess::open(p_path + ".uidren", FileAccess::WRITE);
|
||||
ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, "Cannot create file '" + p_path + ".uidren'.");
|
||||
ERR_FAIL_COND_V_MSG(fw.is_null(), ERR_CANT_CREATE, vformat("Cannot create file '%s.uidren'.", p_path));
|
||||
|
||||
uint8_t magich[4] = { 'R', 'S', 'R', 'C' };
|
||||
fw->store_buffer(magich, 4);
|
||||
|
|
@ -2422,7 +2446,7 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU
|
|||
|
||||
// Use the old approach.
|
||||
|
||||
WARN_PRINT("This file is old, so it does not support UIDs, opening and resaving '" + p_path + "'.");
|
||||
WARN_PRINT(vformat("This file is old, so it does not support UIDs, opening and resaving '%s'.", p_path));
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
|
|
@ -2447,7 +2471,7 @@ Error ResourceFormatSaverBinaryInstance::set_uid(const String &p_path, ResourceU
|
|||
f->get_64(); // Skip previous UID
|
||||
|
||||
fw->store_32(flags);
|
||||
fw->store_64(p_uid);
|
||||
fw->store_64(uint64_t(p_uid));
|
||||
|
||||
if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_HAS_SCRIPT_CLASS) {
|
||||
save_ustring(fw, get_ustring(f));
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ public:
|
|||
virtual String get_resource_script_class(const String &p_path) const override;
|
||||
virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override;
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const override;
|
||||
virtual bool has_custom_uid_support() const override;
|
||||
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
|
||||
virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
#include "core/os/os.h"
|
||||
#include "core/variant/variant_parser.h"
|
||||
|
||||
ResourceFormatImporterLoadOnStartup ResourceImporter::load_on_startup = nullptr;
|
||||
|
||||
bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const {
|
||||
return p_a->get_importer_name() < p_b->get_importer_name();
|
||||
}
|
||||
|
|
@ -73,7 +75,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
|
|||
if (err == ERR_FILE_EOF) {
|
||||
return OK;
|
||||
} else if (err != OK) {
|
||||
ERR_PRINT("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text);
|
||||
ERR_PRINT(vformat("ResourceFormatImporter::load - %s.import:%d error: %s.", p_path, lines, error_text));
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -137,6 +139,20 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
|
|||
}
|
||||
|
||||
Ref<Resource> ResourceFormatImporter::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) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
// When loading a resource on startup, we use the load_on_startup callback,
|
||||
// which executes the loading in the EditorFileSystem. It can reimport
|
||||
// the resource and retry the load, allowing the resource to be loaded
|
||||
// even if it is not yet imported.
|
||||
if (ResourceImporter::load_on_startup != nullptr) {
|
||||
return ResourceImporter::load_on_startup(this, p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode);
|
||||
}
|
||||
#endif
|
||||
|
||||
return load_internal(p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode, false);
|
||||
}
|
||||
|
||||
Ref<Resource> ResourceFormatImporter::load_internal(const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode, bool p_silence_errors) {
|
||||
PathAndType pat;
|
||||
Error err = _get_path_and_type(p_path, pat);
|
||||
|
||||
|
|
@ -148,6 +164,13 @@ Ref<Resource> ResourceFormatImporter::load(const String &p_path, const String &p
|
|||
return Ref<Resource>();
|
||||
}
|
||||
|
||||
if (p_silence_errors) {
|
||||
// Note: Some importers do not create files in the .godot folder, so we need to check if the path is empty.
|
||||
if (!pat.path.is_empty() && !FileAccess::exists(pat.path)) {
|
||||
return Ref<Resource>();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Resource> res = ResourceLoader::_load(pat.path, p_path, pat.type, p_cache_mode, r_error, p_use_sub_threads, r_progress);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
@ -312,7 +335,7 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat
|
|||
if (err == ERR_FILE_EOF) {
|
||||
return;
|
||||
} else if (err != OK) {
|
||||
ERR_PRINT("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text);
|
||||
ERR_PRINT(vformat("ResourceFormatImporter::get_internal_resource_path_list - %s.import:%d error: %s.", p_path, lines, error_text));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -364,6 +387,27 @@ ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) c
|
|||
return pat.uid;
|
||||
}
|
||||
|
||||
bool ResourceFormatImporter::has_custom_uid_support() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
Error ResourceFormatImporter::get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const {
|
||||
PathAndType pat;
|
||||
Error err = _get_path_and_type(p_path, pat);
|
||||
|
||||
if (err == OK) {
|
||||
r_type = pat.type;
|
||||
r_uid = pat.uid;
|
||||
r_import_group_file = pat.group_file;
|
||||
} else {
|
||||
r_type = "";
|
||||
r_uid = ResourceUID::INVALID_ID;
|
||||
r_import_group_file = "";
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const {
|
||||
PathAndType pat;
|
||||
Error err = _get_path_and_type(p_path, pat);
|
||||
|
|
@ -467,7 +511,7 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con
|
|||
|
||||
for (int i = 0; i < importers.size(); i++) {
|
||||
if (importers[i]->get_importer_name() == pat.importer) {
|
||||
if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid
|
||||
if (!importers[i]->are_import_settings_valid(p_path, pat.metadata)) { //importer thinks this is not valid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@
|
|||
#include "core/io/resource_saver.h"
|
||||
|
||||
class ResourceImporter;
|
||||
class ResourceFormatImporter;
|
||||
|
||||
typedef Ref<Resource> (*ResourceFormatImporterLoadOnStartup)(ResourceFormatImporter *p_importer, const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, ResourceFormatLoader::CacheMode p_cache_mode);
|
||||
|
||||
class ResourceFormatImporter : public ResourceFormatLoader {
|
||||
struct PathAndType {
|
||||
|
|
@ -43,7 +46,7 @@ class ResourceFormatImporter : public ResourceFormatLoader {
|
|||
String importer;
|
||||
String group_file;
|
||||
Variant metadata;
|
||||
uint64_t uid = ResourceUID::INVALID_ID;
|
||||
ResourceUID::ID uid = ResourceUID::INVALID_ID;
|
||||
};
|
||||
|
||||
Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = nullptr) const;
|
||||
|
|
@ -60,12 +63,14 @@ class ResourceFormatImporter : public ResourceFormatLoader {
|
|||
public:
|
||||
static ResourceFormatImporter *get_singleton() { return singleton; }
|
||||
virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
|
||||
Ref<Resource> load_internal(const String &p_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode, bool p_silence_errors);
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
|
||||
virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const override;
|
||||
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const override;
|
||||
virtual bool handles_type(const String &p_type) const override;
|
||||
virtual String get_resource_type(const String &p_path) const override;
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const override;
|
||||
virtual bool has_custom_uid_support() const override;
|
||||
virtual Variant get_resource_metadata(const String &p_path) const;
|
||||
virtual bool is_import_valid(const String &p_path) const override;
|
||||
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override;
|
||||
|
|
@ -93,6 +98,8 @@ public:
|
|||
String get_import_settings_hash() const;
|
||||
|
||||
String get_import_base_path(const String &p_for_file) const;
|
||||
Error get_resource_import_info(const String &p_path, StringName &r_type, ResourceUID::ID &r_uid, String &r_import_group_file) const;
|
||||
|
||||
ResourceFormatImporter();
|
||||
};
|
||||
|
||||
|
|
@ -103,6 +110,8 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static ResourceFormatImporterLoadOnStartup load_on_startup;
|
||||
|
||||
virtual String get_importer_name() const = 0;
|
||||
virtual String get_visible_name() const = 0;
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
|
||||
|
|
@ -139,13 +148,13 @@ public:
|
|||
virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {}
|
||||
virtual String get_option_group_file() const { return String(); }
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0;
|
||||
virtual bool can_import_threaded() const { return true; }
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0;
|
||||
virtual bool can_import_threaded() const { return false; }
|
||||
virtual void import_threaded_begin() {}
|
||||
virtual void import_threaded_end() {}
|
||||
|
||||
virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; }
|
||||
virtual bool are_import_settings_valid(const String &p_path) const { return true; }
|
||||
virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; }
|
||||
virtual String get_import_settings_string() const { return String(); }
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -34,9 +34,12 @@
|
|||
#include "core/io/resource.h"
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
#include "core/object/worker_thread_pool.h"
|
||||
#include "core/os/semaphore.h"
|
||||
#include "core/os/thread.h"
|
||||
|
||||
namespace core_bind {
|
||||
class ResourceLoader;
|
||||
}
|
||||
|
||||
class ConditionVariable;
|
||||
|
||||
template <int Tag>
|
||||
|
|
@ -81,6 +84,7 @@ public:
|
|||
virtual String get_resource_type(const String &p_path) const;
|
||||
virtual String get_resource_script_class(const String &p_path) const;
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const;
|
||||
virtual bool has_custom_uid_support() const;
|
||||
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
|
||||
virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
|
||||
virtual bool is_import_valid(const String &p_path) const { return true; }
|
||||
|
|
@ -100,10 +104,15 @@ typedef Error (*ResourceLoaderImport)(const String &p_path);
|
|||
typedef void (*ResourceLoadedCallback)(Ref<Resource> p_resource, const String &p_path);
|
||||
|
||||
class ResourceLoader {
|
||||
friend class LoadToken;
|
||||
friend class core_bind::ResourceLoader;
|
||||
|
||||
enum {
|
||||
MAX_LOADERS = 64
|
||||
};
|
||||
|
||||
struct ThreadLoadTask;
|
||||
|
||||
public:
|
||||
enum ThreadLoadStatus {
|
||||
THREAD_LOAD_INVALID_RESOURCE,
|
||||
|
|
@ -121,7 +130,8 @@ public:
|
|||
struct LoadToken : public RefCounted {
|
||||
String local_path;
|
||||
String user_path;
|
||||
Ref<Resource> res_if_unregistered;
|
||||
uint32_t user_rc = 0; // Having user RC implies regular RC incremented in one, until the user RC reaches zero.
|
||||
ThreadLoadTask *task_if_unregistered = nullptr;
|
||||
|
||||
void clear();
|
||||
|
||||
|
|
@ -130,10 +140,13 @@ public:
|
|||
|
||||
static const int BINARY_MUTEX_TAG = 1;
|
||||
|
||||
static Ref<LoadToken> _load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode);
|
||||
static Ref<LoadToken> _load_start(const String &p_path, const String &p_type_hint, LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode, bool p_for_user = false);
|
||||
static Ref<Resource> _load_complete(LoadToken &p_load_token, Error *r_error);
|
||||
|
||||
private:
|
||||
static LoadToken *_load_threaded_request_reuse_user_token(const String &p_path);
|
||||
static void _load_threaded_request_setup_user_token(LoadToken *p_token, const String &p_path);
|
||||
|
||||
static Ref<Resource> _load_complete_inner(LoadToken &p_load_token, Error *r_error, MutexLock<SafeBinaryMutex<BINARY_MUTEX_TAG>> &p_thread_load_lock);
|
||||
|
||||
static Ref<ResourceFormatLoader> loader[MAX_LOADERS];
|
||||
|
|
@ -167,9 +180,10 @@ private:
|
|||
Thread::ID thread_id = 0; // Used if running on an user thread (e.g., simple non-threaded load).
|
||||
bool awaited = false; // If it's in the pool, this helps not awaiting from more than one dependent thread.
|
||||
ConditionVariable *cond_var = nullptr; // In not in the worker pool or already awaiting, this is used as a secondary awaiting mechanism.
|
||||
uint32_t awaiters_count = 0;
|
||||
bool need_wait = true;
|
||||
LoadToken *load_token = nullptr;
|
||||
String local_path;
|
||||
String remapped_path;
|
||||
String type_hint;
|
||||
float progress = 0.0f;
|
||||
float max_reported_progress = 0.0f;
|
||||
|
|
@ -178,18 +192,27 @@ private:
|
|||
ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
|
||||
Error error = OK;
|
||||
Ref<Resource> resource;
|
||||
bool xl_remapped = false;
|
||||
bool use_sub_threads = false;
|
||||
HashSet<String> sub_tasks;
|
||||
|
||||
struct ResourceChangedConnection {
|
||||
Resource *source = nullptr;
|
||||
Callable callable;
|
||||
uint32_t flags = 0;
|
||||
};
|
||||
LocalVector<ResourceChangedConnection> resource_changed_connections;
|
||||
};
|
||||
|
||||
static void _thread_load_function(void *p_userdata);
|
||||
static void _run_load_task(void *p_userdata);
|
||||
|
||||
static thread_local int load_nesting;
|
||||
static thread_local WorkerThreadPool::TaskID caller_task_id;
|
||||
static thread_local HashMap<int, HashMap<String, Ref<Resource>>> res_ref_overrides; // Outermost key is nesting level.
|
||||
static thread_local Vector<String> *load_paths_stack; // A pointer to avoid broken TLS implementations from double-running the destructor.
|
||||
static thread_local Vector<String> load_paths_stack;
|
||||
static thread_local ThreadLoadTask *curr_load_task;
|
||||
|
||||
static SafeBinaryMutex<BINARY_MUTEX_TAG> thread_load_mutex;
|
||||
friend SafeBinaryMutex<BINARY_MUTEX_TAG> &_get_res_loader_mutex();
|
||||
|
||||
static HashMap<String, ThreadLoadTask> thread_load_tasks;
|
||||
static bool cleaning_tasks;
|
||||
|
||||
|
|
@ -199,12 +222,18 @@ private:
|
|||
|
||||
static bool _ensure_load_progress();
|
||||
|
||||
static String _validate_local_path(const String &p_path);
|
||||
|
||||
public:
|
||||
static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE);
|
||||
static ThreadLoadStatus load_threaded_get_status(const String &p_path, float *r_progress = nullptr);
|
||||
static Ref<Resource> load_threaded_get(const String &p_path, Error *r_error = nullptr);
|
||||
|
||||
static bool is_within_load() { return load_nesting > 0; };
|
||||
static bool is_within_load() { return load_nesting > 0; }
|
||||
|
||||
static void resource_changed_connect(Resource *p_source, const Callable &p_callable, uint32_t p_flags);
|
||||
static void resource_changed_disconnect(Resource *p_source, const Callable &p_callable);
|
||||
static void resource_changed_emit(Resource *p_source);
|
||||
|
||||
static Ref<Resource> load(const String &p_path, const String &p_type_hint = "", ResourceFormatLoader::CacheMode p_cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE, Error *r_error = nullptr);
|
||||
static bool exists(const String &p_path, const String &p_type_hint = "");
|
||||
|
|
@ -216,6 +245,8 @@ public:
|
|||
static String get_resource_type(const String &p_path);
|
||||
static String get_resource_script_class(const String &p_path);
|
||||
static ResourceUID::ID get_resource_uid(const String &p_path);
|
||||
static bool has_custom_uid_support(const String &p_path);
|
||||
static bool should_create_uid_file(const String &p_path);
|
||||
static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
|
||||
static Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map);
|
||||
static bool is_import_valid(const String &p_path);
|
||||
|
|
@ -280,6 +311,8 @@ public:
|
|||
|
||||
static bool is_cleaning_tasks();
|
||||
|
||||
static Vector<String> list_directory(const String &p_directory);
|
||||
|
||||
static void initialize();
|
||||
static void finalize();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ void ResourceFormatSaver::_bind_methods() {
|
|||
}
|
||||
|
||||
Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
|
||||
ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, vformat("Can't save empty resource to path '%s'.", p_path));
|
||||
String path = p_path;
|
||||
if (path.is_empty()) {
|
||||
path = p_resource->get_path();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
|
||||
// These constants are off by 1, causing the 'z' and '9' characters never to be used.
|
||||
// This cannot be fixed without breaking compatibility; see GH-83843.
|
||||
|
|
@ -44,23 +45,45 @@ String ResourceUID::get_cache_file() {
|
|||
return ProjectSettings::get_singleton()->get_project_data_path().path_join("uid_cache.bin");
|
||||
}
|
||||
|
||||
static constexpr uint8_t uuid_characters[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', '0', '1', '2', '3', '4', '5', '6', '7', '8' };
|
||||
static constexpr uint32_t uuid_characters_element_count = (sizeof(uuid_characters) / sizeof(*uuid_characters));
|
||||
static constexpr uint8_t max_uuid_number_length = 13; // Max 0x7FFFFFFFFFFFFFFF (uid://d4n4ub6itg400) size is 13 characters.
|
||||
|
||||
String ResourceUID::id_to_text(ID p_id) const {
|
||||
if (p_id < 0) {
|
||||
return "uid://<invalid>";
|
||||
}
|
||||
String txt;
|
||||
|
||||
while (p_id) {
|
||||
uint32_t c = p_id % base;
|
||||
if (c < char_count) {
|
||||
txt = String::chr('a' + c) + txt;
|
||||
} else {
|
||||
txt = String::chr('0' + (c - char_count)) + txt;
|
||||
}
|
||||
p_id /= base;
|
||||
char32_t tmp[max_uuid_number_length];
|
||||
uint32_t tmp_size = 0;
|
||||
do {
|
||||
uint32_t c = p_id % uuid_characters_element_count;
|
||||
tmp[tmp_size] = uuid_characters[c];
|
||||
p_id /= uuid_characters_element_count;
|
||||
++tmp_size;
|
||||
} while (p_id);
|
||||
|
||||
// tmp_size + uid:// (6) + 1 for null.
|
||||
String txt;
|
||||
txt.resize(tmp_size + 7);
|
||||
|
||||
char32_t *p = txt.ptrw();
|
||||
p[0] = 'u';
|
||||
p[1] = 'i';
|
||||
p[2] = 'd';
|
||||
p[3] = ':';
|
||||
p[4] = '/';
|
||||
p[5] = '/';
|
||||
uint32_t size = 6;
|
||||
|
||||
// The above loop give the number backward, recopy it in the string in the correct order.
|
||||
for (uint32_t i = 0; i < tmp_size; ++i) {
|
||||
p[size++] = tmp[tmp_size - i - 1];
|
||||
}
|
||||
|
||||
return "uid://" + txt;
|
||||
p[size] = 0;
|
||||
|
||||
return txt;
|
||||
}
|
||||
|
||||
ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const {
|
||||
|
|
@ -128,17 +151,53 @@ void ResourceUID::set_id(ID p_id, const String &p_path) {
|
|||
}
|
||||
|
||||
String ResourceUID::get_id_path(ID p_id) const {
|
||||
ERR_FAIL_COND_V_MSG(p_id == INVALID_ID, String(), "Invalid UID.");
|
||||
MutexLock l(mutex);
|
||||
ERR_FAIL_COND_V(!unique_ids.has(p_id), String());
|
||||
const CharString &cs = unique_ids[p_id].cs;
|
||||
const ResourceUID::Cache *cache = unique_ids.getptr(p_id);
|
||||
|
||||
#if TOOLS_ENABLED
|
||||
// On startup, the scan_for_uid_on_startup callback should be set and will
|
||||
// execute EditorFileSystem::scan_for_uid, which scans all project files
|
||||
// to reload the UID cache before the first scan.
|
||||
// Note: EditorFileSystem::scan_for_uid sets scan_for_uid_on_startup to nullptr
|
||||
// once the first scan_for_uid is complete.
|
||||
if (!cache && scan_for_uid_on_startup) {
|
||||
scan_for_uid_on_startup();
|
||||
cache = unique_ids.getptr(p_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!cache, String(), vformat("Unrecognized UID: \"%s\".", id_to_text(p_id)));
|
||||
const CharString &cs = cache->cs;
|
||||
return String::utf8(cs.ptr());
|
||||
}
|
||||
|
||||
void ResourceUID::remove_id(ID p_id) {
|
||||
MutexLock l(mutex);
|
||||
ERR_FAIL_COND(!unique_ids.has(p_id));
|
||||
unique_ids.erase(p_id);
|
||||
}
|
||||
|
||||
String ResourceUID::uid_to_path(const String &p_uid) {
|
||||
return singleton->get_id_path(singleton->text_to_id(p_uid));
|
||||
}
|
||||
|
||||
String ResourceUID::path_to_uid(const String &p_path) {
|
||||
const ID id = ResourceLoader::get_resource_uid(p_path);
|
||||
if (id == INVALID_ID) {
|
||||
return p_path;
|
||||
} else {
|
||||
return singleton->id_to_text(id);
|
||||
}
|
||||
}
|
||||
|
||||
String ResourceUID::ensure_path(const String &p_uid_or_path) {
|
||||
if (p_uid_or_path.begins_with("uid://")) {
|
||||
return uid_to_path(p_uid_or_path);
|
||||
}
|
||||
return p_uid_or_path;
|
||||
}
|
||||
|
||||
Error ResourceUID::save_to_cache() {
|
||||
String cache_file = get_cache_file();
|
||||
if (!FileAccess::exists(cache_file)) {
|
||||
|
|
@ -157,7 +216,7 @@ Error ResourceUID::save_to_cache() {
|
|||
cache_entries = 0;
|
||||
|
||||
for (KeyValue<ID, Cache> &E : unique_ids) {
|
||||
f->store_64(E.key);
|
||||
f->store_64(uint64_t(E.key));
|
||||
uint32_t s = E.value.cs.length();
|
||||
f->store_32(s);
|
||||
f->store_buffer((const uint8_t *)E.value.cs.ptr(), s);
|
||||
|
|
@ -186,7 +245,7 @@ Error ResourceUID::load_from_cache(bool p_reset) {
|
|||
int32_t len = f->get_32();
|
||||
Cache c;
|
||||
c.cs.resize(len + 1);
|
||||
ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // out of memory
|
||||
ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // Out of memory.
|
||||
c.cs[len] = 0;
|
||||
int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len);
|
||||
ERR_FAIL_COND_V(rl != len, ERR_FILE_CORRUPT);
|
||||
|
|
@ -214,13 +273,13 @@ Error ResourceUID::update_cache() {
|
|||
for (KeyValue<ID, Cache> &E : unique_ids) {
|
||||
if (!E.value.saved_to_cache) {
|
||||
if (f.is_null()) {
|
||||
f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); //append
|
||||
f = FileAccess::open(get_cache_file(), FileAccess::READ_WRITE); // Append.
|
||||
if (f.is_null()) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
f->seek_end();
|
||||
}
|
||||
f->store_64(E.key);
|
||||
f->store_64(uint64_t(E.key));
|
||||
uint32_t s = E.value.cs.length();
|
||||
f->store_32(s);
|
||||
f->store_buffer((const uint8_t *)E.value.cs.ptr(), s);
|
||||
|
|
@ -239,6 +298,25 @@ Error ResourceUID::update_cache() {
|
|||
return OK;
|
||||
}
|
||||
|
||||
String ResourceUID::get_path_from_cache(Ref<FileAccess> &p_cache_file, const String &p_uid_string) {
|
||||
const uint32_t entry_count = p_cache_file->get_32();
|
||||
CharString cs;
|
||||
for (uint32_t i = 0; i < entry_count; i++) {
|
||||
int64_t id = p_cache_file->get_64();
|
||||
int32_t len = p_cache_file->get_32();
|
||||
cs.resize(len + 1);
|
||||
ERR_FAIL_COND_V(cs.size() != len + 1, String());
|
||||
cs[len] = 0;
|
||||
int32_t rl = p_cache_file->get_buffer((uint8_t *)cs.ptrw(), len);
|
||||
ERR_FAIL_COND_V(rl != len, String());
|
||||
|
||||
if (singleton->id_to_text(id) == p_uid_string) {
|
||||
return String(cs);
|
||||
}
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void ResourceUID::clear() {
|
||||
cache_entries = 0;
|
||||
unique_ids.clear();
|
||||
|
|
|
|||
|
|
@ -35,13 +35,15 @@
|
|||
#include "core/string/string_name.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
|
||||
class FileAccess;
|
||||
|
||||
typedef void (*ResourceUIDScanForUIDOnStartup)();
|
||||
|
||||
class ResourceUID : public Object {
|
||||
GDCLASS(ResourceUID, Object)
|
||||
public:
|
||||
typedef int64_t ID;
|
||||
enum {
|
||||
INVALID_ID = -1
|
||||
};
|
||||
constexpr const static ID INVALID_ID = -1;
|
||||
|
||||
static String get_cache_file();
|
||||
|
||||
|
|
@ -63,6 +65,8 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
inline static ResourceUIDScanForUIDOnStartup scan_for_uid_on_startup = nullptr;
|
||||
|
||||
String id_to_text(ID p_id) const;
|
||||
ID text_to_id(const String &p_text) const;
|
||||
|
||||
|
|
@ -73,9 +77,14 @@ public:
|
|||
String get_id_path(ID p_id) const;
|
||||
void remove_id(ID p_id);
|
||||
|
||||
static String uid_to_path(const String &p_uid);
|
||||
static String path_to_uid(const String &p_path);
|
||||
static String ensure_path(const String &p_uid_or_path);
|
||||
|
||||
Error load_from_cache(bool p_reset);
|
||||
Error save_to_cache();
|
||||
Error update_cache();
|
||||
static String get_path_from_cache(Ref<FileAccess> &p_cache_file, const String &p_uid_string);
|
||||
|
||||
void clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,18 @@ void StreamPeer::put_64(int64_t p_val) {
|
|||
put_data(buf, 8);
|
||||
}
|
||||
|
||||
void StreamPeer::put_half(float p_val) {
|
||||
uint8_t buf[2];
|
||||
|
||||
encode_half(p_val, buf);
|
||||
uint16_t *p16 = (uint16_t *)buf;
|
||||
if (big_endian) {
|
||||
*p16 = BSWAP16(*p16);
|
||||
}
|
||||
|
||||
put_data(buf, 2);
|
||||
}
|
||||
|
||||
void StreamPeer::put_float(float p_val) {
|
||||
uint8_t buf[4];
|
||||
|
||||
|
|
@ -192,11 +204,13 @@ void StreamPeer::put_float(float p_val) {
|
|||
|
||||
void StreamPeer::put_double(double p_val) {
|
||||
uint8_t buf[8];
|
||||
|
||||
encode_double(p_val, buf);
|
||||
if (big_endian) {
|
||||
uint64_t *p64 = (uint64_t *)buf;
|
||||
*p64 = BSWAP64(*p64);
|
||||
}
|
||||
|
||||
put_data(buf, 8);
|
||||
}
|
||||
|
||||
|
|
@ -223,75 +237,99 @@ void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) {
|
|||
}
|
||||
|
||||
uint8_t StreamPeer::get_u8() {
|
||||
uint8_t buf[1];
|
||||
uint8_t buf[1] = {};
|
||||
get_data(buf, 1);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
int8_t StreamPeer::get_8() {
|
||||
uint8_t buf[1];
|
||||
uint8_t buf[1] = {};
|
||||
get_data(buf, 1);
|
||||
return buf[0];
|
||||
return int8_t(buf[0]);
|
||||
}
|
||||
|
||||
uint16_t StreamPeer::get_u16() {
|
||||
uint8_t buf[2];
|
||||
get_data(buf, 2);
|
||||
|
||||
uint16_t r = decode_uint16(buf);
|
||||
if (big_endian) {
|
||||
r = BSWAP16(r);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int16_t StreamPeer::get_16() {
|
||||
uint8_t buf[2];
|
||||
get_data(buf, 2);
|
||||
|
||||
uint16_t r = decode_uint16(buf);
|
||||
if (big_endian) {
|
||||
r = BSWAP16(r);
|
||||
}
|
||||
return r;
|
||||
|
||||
return int16_t(r);
|
||||
}
|
||||
|
||||
uint32_t StreamPeer::get_u32() {
|
||||
uint8_t buf[4];
|
||||
get_data(buf, 4);
|
||||
|
||||
uint32_t r = decode_uint32(buf);
|
||||
if (big_endian) {
|
||||
r = BSWAP32(r);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int32_t StreamPeer::get_32() {
|
||||
uint8_t buf[4];
|
||||
get_data(buf, 4);
|
||||
|
||||
uint32_t r = decode_uint32(buf);
|
||||
if (big_endian) {
|
||||
r = BSWAP32(r);
|
||||
}
|
||||
return r;
|
||||
|
||||
return int32_t(r);
|
||||
}
|
||||
|
||||
uint64_t StreamPeer::get_u64() {
|
||||
uint8_t buf[8];
|
||||
get_data(buf, 8);
|
||||
|
||||
uint64_t r = decode_uint64(buf);
|
||||
if (big_endian) {
|
||||
r = BSWAP64(r);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int64_t StreamPeer::get_64() {
|
||||
uint8_t buf[8];
|
||||
get_data(buf, 8);
|
||||
|
||||
uint64_t r = decode_uint64(buf);
|
||||
if (big_endian) {
|
||||
r = BSWAP64(r);
|
||||
}
|
||||
return r;
|
||||
|
||||
return int64_t(r);
|
||||
}
|
||||
|
||||
float StreamPeer::get_half() {
|
||||
uint8_t buf[2];
|
||||
get_data(buf, 2);
|
||||
|
||||
if (big_endian) {
|
||||
uint16_t *p16 = (uint16_t *)buf;
|
||||
*p16 = BSWAP16(*p16);
|
||||
}
|
||||
|
||||
return decode_half(buf);
|
||||
}
|
||||
|
||||
float StreamPeer::get_float() {
|
||||
|
|
@ -320,7 +358,7 @@ double StreamPeer::get_double() {
|
|||
|
||||
String StreamPeer::get_string(int p_bytes) {
|
||||
if (p_bytes < 0) {
|
||||
p_bytes = get_u32();
|
||||
p_bytes = get_32();
|
||||
}
|
||||
ERR_FAIL_COND_V(p_bytes < 0, String());
|
||||
|
||||
|
|
@ -335,7 +373,7 @@ String StreamPeer::get_string(int p_bytes) {
|
|||
|
||||
String StreamPeer::get_utf8_string(int p_bytes) {
|
||||
if (p_bytes < 0) {
|
||||
p_bytes = get_u32();
|
||||
p_bytes = get_32();
|
||||
}
|
||||
ERR_FAIL_COND_V(p_bytes < 0, String());
|
||||
|
||||
|
|
@ -385,6 +423,7 @@ void StreamPeer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("put_u32", "value"), &StreamPeer::put_u32);
|
||||
ClassDB::bind_method(D_METHOD("put_64", "value"), &StreamPeer::put_64);
|
||||
ClassDB::bind_method(D_METHOD("put_u64", "value"), &StreamPeer::put_u64);
|
||||
ClassDB::bind_method(D_METHOD("put_half", "value"), &StreamPeer::put_half);
|
||||
ClassDB::bind_method(D_METHOD("put_float", "value"), &StreamPeer::put_float);
|
||||
ClassDB::bind_method(D_METHOD("put_double", "value"), &StreamPeer::put_double);
|
||||
ClassDB::bind_method(D_METHOD("put_string", "value"), &StreamPeer::put_string);
|
||||
|
|
@ -399,6 +438,7 @@ void StreamPeer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_u32"), &StreamPeer::get_u32);
|
||||
ClassDB::bind_method(D_METHOD("get_64"), &StreamPeer::get_64);
|
||||
ClassDB::bind_method(D_METHOD("get_u64"), &StreamPeer::get_u64);
|
||||
ClassDB::bind_method(D_METHOD("get_half"), &StreamPeer::get_half);
|
||||
ClassDB::bind_method(D_METHOD("get_float"), &StreamPeer::get_float);
|
||||
ClassDB::bind_method(D_METHOD("get_double"), &StreamPeer::get_double);
|
||||
ClassDB::bind_method(D_METHOD("get_string", "bytes"), &StreamPeer::get_string, DEFVAL(-1));
|
||||
|
|
@ -441,7 +481,7 @@ Error StreamPeerExtension::put_data(const uint8_t *p_data, int p_bytes) {
|
|||
|
||||
Error StreamPeerExtension::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
|
||||
Error err;
|
||||
if (GDVIRTUAL_CALL(_put_data, p_data, p_bytes, &r_sent, err)) {
|
||||
if (GDVIRTUAL_CALL(_put_partial_data, p_data, p_bytes, &r_sent, err)) {
|
||||
return err;
|
||||
}
|
||||
WARN_PRINT_ONCE("StreamPeerExtension::_put_partial_data is unimplemented!");
|
||||
|
|
@ -472,7 +512,7 @@ void StreamPeerBuffer::_bind_methods() {
|
|||
}
|
||||
|
||||
Error StreamPeerBuffer::put_data(const uint8_t *p_data, int p_bytes) {
|
||||
if (p_bytes <= 0) {
|
||||
if (p_bytes <= 0 || !p_data) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
|
@ -503,6 +543,11 @@ Error StreamPeerBuffer::get_data(uint8_t *p_buffer, int p_bytes) {
|
|||
}
|
||||
|
||||
Error StreamPeerBuffer::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
|
||||
if (!p_bytes) {
|
||||
r_received = 0;
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (pointer + p_bytes > data.size()) {
|
||||
r_received = data.size() - pointer;
|
||||
if (r_received <= 0) {
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ public:
|
|||
void put_u32(uint32_t p_val);
|
||||
void put_64(int64_t p_val);
|
||||
void put_u64(uint64_t p_val);
|
||||
void put_half(float p_val);
|
||||
void put_float(float p_val);
|
||||
void put_double(double p_val);
|
||||
void put_string(const String &p_string);
|
||||
|
|
@ -87,6 +88,7 @@ public:
|
|||
int32_t get_32();
|
||||
uint64_t get_u64();
|
||||
int64_t get_64();
|
||||
float get_half();
|
||||
float get_float();
|
||||
double get_double();
|
||||
String get_string(int p_bytes = -1);
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IPAddress p_host, uint1
|
|||
}
|
||||
|
||||
Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ Error StreamPeerTCP::bind(int p_port, const IPAddress &p_host) {
|
|||
}
|
||||
|
||||
Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(status != STATUS_NONE, ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER);
|
||||
ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, ERR_INVALID_PARAMETER, "The remote port number must be between 1 and 65535 (inclusive).");
|
||||
|
|
@ -140,7 +140,7 @@ Error StreamPeerTCP::connect_to_host(const IPAddress &p_host, int p_port) {
|
|||
}
|
||||
|
||||
Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
|
||||
if (status != STATUS_CONNECTED) {
|
||||
return FAILED;
|
||||
|
|
@ -238,7 +238,7 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool
|
|||
}
|
||||
|
||||
void StreamPeerTCP::set_no_delay(bool p_enabled) {
|
||||
ERR_FAIL_COND(!_sock.is_valid() || !_sock->is_open());
|
||||
ERR_FAIL_COND(_sock.is_null() || !_sock->is_open());
|
||||
_sock->set_tcp_no_delay_enabled(p_enabled);
|
||||
}
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_rec
|
|||
}
|
||||
|
||||
int StreamPeerTCP::get_available_bytes() const {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), -1);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), -1);
|
||||
return _sock->get_available_bytes();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@
|
|||
|
||||
#include "core/config/engine.h"
|
||||
|
||||
StreamPeerTLS *(*StreamPeerTLS::_create)() = nullptr;
|
||||
StreamPeerTLS *(*StreamPeerTLS::_create)(bool p_notify_postinitialize) = nullptr;
|
||||
|
||||
StreamPeerTLS *StreamPeerTLS::create() {
|
||||
StreamPeerTLS *StreamPeerTLS::create(bool p_notify_postinitialize) {
|
||||
if (_create) {
|
||||
return _create();
|
||||
return _create(p_notify_postinitialize);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class StreamPeerTLS : public StreamPeer {
|
|||
GDCLASS(StreamPeerTLS, StreamPeer);
|
||||
|
||||
protected:
|
||||
static StreamPeerTLS *(*_create)();
|
||||
static StreamPeerTLS *(*_create)(bool p_notify_postinitialize);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
|
|
@ -58,7 +58,7 @@ public:
|
|||
|
||||
virtual void disconnect_from_stream() = 0;
|
||||
|
||||
static StreamPeerTLS *create();
|
||||
static StreamPeerTLS *create(bool p_notify_postinitialize = true);
|
||||
|
||||
static bool is_available();
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ void TCPServer::_bind_methods() {
|
|||
}
|
||||
|
||||
Error TCPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
|
||||
|
||||
|
|
@ -82,13 +82,13 @@ int TCPServer::get_local_port() const {
|
|||
}
|
||||
|
||||
bool TCPServer::is_listening() const {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), false);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), false);
|
||||
|
||||
return _sock->is_open();
|
||||
}
|
||||
|
||||
bool TCPServer::is_connection_available() const {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), false);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), false);
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
return false;
|
||||
|
|
@ -108,11 +108,11 @@ Ref<StreamPeerTCP> TCPServer::take_connection() {
|
|||
IPAddress ip;
|
||||
uint16_t port = 0;
|
||||
ns = _sock->accept(ip, port);
|
||||
if (!ns.is_valid()) {
|
||||
if (ns.is_null()) {
|
||||
return conn;
|
||||
}
|
||||
|
||||
conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
|
||||
conn.instantiate();
|
||||
conn->accept_socket(ns, ip, port);
|
||||
return conn;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#include "translation_loader_po.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "core/string/translation_po.h"
|
||||
|
||||
Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_error) {
|
||||
|
|
@ -109,7 +108,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
// Record plural rule.
|
||||
int p_start = config.find("Plural-Forms");
|
||||
if (p_start != -1) {
|
||||
int p_end = config.find("\n", p_start);
|
||||
int p_end = config.find_char('\n', p_start);
|
||||
translation->set_plural_rule(config.substr(p_start, p_end - p_start));
|
||||
}
|
||||
} else {
|
||||
|
|
@ -170,14 +169,14 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
// 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(Ref<Resource>(), "Unexpected EOF while reading PO file at: " + path + ":" + itos(line));
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Unexpected EOF while reading PO file at: %s:%d.", path, line));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (l.begins_with("msgctxt")) {
|
||||
ERR_FAIL_COND_V_MSG(status != STATUS_READING_STRING && status != STATUS_READING_PLURAL, Ref<Resource>(), "Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(status != STATUS_READING_STRING && status != STATUS_READING_PLURAL, Ref<Resource>(), vformat("Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: %s:%d.", path, 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.
|
||||
|
|
@ -185,7 +184,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
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, Ref<Resource>(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), vformat("Number of 'msgstr[]' doesn't match with number of plural forms: %s:%d.", path, line));
|
||||
translation->add_plural_message(msg_id, msgs_plural, msg_context);
|
||||
}
|
||||
}
|
||||
|
|
@ -197,9 +196,9 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
|
||||
if (l.begins_with("msgid_plural")) {
|
||||
if (plural_forms == 0) {
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), "PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: " + path + ":" + itos(line));
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: %s:%d.", path, line));
|
||||
} else if (status != STATUS_READING_ID) {
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), "Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: " + path + ":" + itos(line));
|
||||
ERR_FAIL_V_MSG(Ref<Resource>(), vformat("Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: %s:%d.", path, 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.
|
||||
|
|
@ -209,14 +208,14 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
msgs_plural.resize(plural_forms);
|
||||
status = STATUS_READING_PLURAL;
|
||||
} else if (l.begins_with("msgid")) {
|
||||
ERR_FAIL_COND_V_MSG(status == STATUS_READING_ID, Ref<Resource>(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(status == STATUS_READING_ID, Ref<Resource>(), vformat("Unexpected 'msgid', was expecting 'msgstr' while parsing: %s:%d.", path, 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, Ref<Resource>(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), vformat("Number of 'msgstr[]' doesn't match with number of plural forms: %s:%d.", path, line));
|
||||
translation->add_plural_message(msg_id, msgs_plural, msg_context);
|
||||
}
|
||||
}
|
||||
|
|
@ -225,7 +224,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
// Record plural rule.
|
||||
int p_start = config.find("Plural-Forms");
|
||||
if (p_start != -1) {
|
||||
int p_end = config.find("\n", p_start);
|
||||
int p_end = config.find_char('\n', p_start);
|
||||
translation->set_plural_rule(config.substr(p_start, p_end - p_start));
|
||||
plural_forms = translation->get_plural_forms();
|
||||
}
|
||||
|
|
@ -245,11 +244,11 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
}
|
||||
|
||||
if (l.begins_with("msgstr[")) {
|
||||
ERR_FAIL_COND_V_MSG(status != STATUS_READING_PLURAL, Ref<Resource>(), "Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(status != STATUS_READING_PLURAL, Ref<Resource>(), vformat("Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: %s:%d.", path, 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, Ref<Resource>(), "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(status != STATUS_READING_ID, Ref<Resource>(), vformat("Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: %s:%d.", path, line));
|
||||
l = l.substr(6, l.length()).strip_edges();
|
||||
status = STATUS_READING_STRING;
|
||||
}
|
||||
|
|
@ -262,7 +261,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
continue; // Nothing to read or comment.
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, Ref<Resource>(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, Ref<Resource>(), vformat("Invalid line '%s' while parsing: %s:%d.", l, path, line));
|
||||
|
||||
l = l.substr(1, l.length());
|
||||
// Find final quote, ignoring escaped ones (\").
|
||||
|
|
@ -284,7 +283,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
escape_next = false;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(end_pos == -1, Ref<Resource>(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(end_pos == -1, Ref<Resource>(), vformat("Expected '\"' at end of message while parsing: %s:%d.", path, line));
|
||||
|
||||
l = l.substr(0, end_pos);
|
||||
l = l.c_unescape();
|
||||
|
|
@ -296,7 +295,7 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
} 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, Ref<Resource>(), "Unexpected plural form while parsing: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(plural_index >= plural_forms, Ref<Resource>(), vformat("Unexpected plural form while parsing: %s:%d.", path, line));
|
||||
msgs_plural.write[plural_index] = msgs_plural[plural_index] + l;
|
||||
}
|
||||
|
||||
|
|
@ -314,18 +313,18 @@ Ref<Resource> TranslationLoaderPO::load_translation(Ref<FileAccess> f, Error *r_
|
|||
}
|
||||
} else if (status == STATUS_READING_PLURAL) {
|
||||
if (!skip_this && !msg_id.is_empty()) {
|
||||
ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line));
|
||||
ERR_FAIL_COND_V_MSG(plural_index != plural_forms - 1, Ref<Resource>(), vformat("Number of 'msgstr[]' doesn't match with number of plural forms: %s:%d.", path, line));
|
||||
translation->add_plural_message(msg_id, msgs_plural, msg_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(config.is_empty(), Ref<Resource>(), "No config found in file: " + path + ".");
|
||||
ERR_FAIL_COND_V_MSG(config.is_empty(), Ref<Resource>(), vformat("No config found in file: '%s'.", path));
|
||||
|
||||
Vector<String> configs = config.split("\n");
|
||||
for (int i = 0; i < configs.size(); i++) {
|
||||
String c = configs[i].strip_edges();
|
||||
int p = c.find(":");
|
||||
int p = c.find_char(':');
|
||||
if (p == -1) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -350,7 +349,7 @@ Ref<Resource> TranslationLoaderPO::load(const String &p_path, const String &p_or
|
|||
}
|
||||
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), Ref<Resource>(), "Cannot open file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), Ref<Resource>(), vformat("Cannot open file '%s'.", p_path));
|
||||
|
||||
return load_translation(f, r_error);
|
||||
}
|
||||
|
|
@ -361,7 +360,7 @@ void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions)
|
|||
}
|
||||
|
||||
bool TranslationLoaderPO::handles_type(const String &p_type) const {
|
||||
return (p_type == "Translation");
|
||||
return (p_type == "Translation") || (p_type == "TranslationPO");
|
||||
}
|
||||
|
||||
String TranslationLoaderPO::get_resource_type(const String &p_path) const {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ public:
|
|||
virtual bool handles_type(const String &p_type) const override;
|
||||
virtual String get_resource_type(const String &p_path) const override;
|
||||
|
||||
// Treat translations as text/binary files, do not generate a `*.{po,mo}.uid` file.
|
||||
virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; }
|
||||
virtual bool has_custom_uid_support() const override { return true; }
|
||||
|
||||
TranslationLoaderPO() {}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ void UDPServer::_bind_methods() {
|
|||
}
|
||||
|
||||
Error UDPServer::poll() {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
if (!_sock->is_open()) {
|
||||
return ERR_UNCONFIGURED;
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ Error UDPServer::poll() {
|
|||
}
|
||||
|
||||
Error UDPServer::listen(uint16_t p_port, const IPAddress &p_bind_address) {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), ERR_UNAVAILABLE);
|
||||
ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE);
|
||||
ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER);
|
||||
|
||||
|
|
@ -123,13 +123,13 @@ int UDPServer::get_local_port() const {
|
|||
}
|
||||
|
||||
bool UDPServer::is_listening() const {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), false);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), false);
|
||||
|
||||
return _sock->is_open();
|
||||
}
|
||||
|
||||
bool UDPServer::is_connection_available() const {
|
||||
ERR_FAIL_COND_V(!_sock.is_valid(), false);
|
||||
ERR_FAIL_COND_V(_sock.is_null(), false);
|
||||
|
||||
if (!_sock->is_open()) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "xml_parser.h"
|
||||
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
//#define DEBUG_XML
|
||||
|
||||
|
|
@ -429,7 +429,7 @@ String XMLParser::get_named_attribute_value(const String &p_name) const {
|
|||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(idx < 0, "", "Attribute not found: " + p_name + ".");
|
||||
ERR_FAIL_COND_V_MSG(idx < 0, "", vformat("Attribute not found: '%s'.", p_name));
|
||||
|
||||
return attributes[idx].value;
|
||||
}
|
||||
|
|
@ -493,7 +493,7 @@ Error XMLParser::open(const String &p_path) {
|
|||
Error err;
|
||||
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot open file '%s'.", p_path));
|
||||
|
||||
length = file->get_length();
|
||||
ERR_FAIL_COND_V(length < 1, ERR_FILE_CORRUPT);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#ifndef XML_PARSER_H
|
||||
#define XML_PARSER_H
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/vector.h"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue