feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python
from misc.utility.scons_hints import *
Import("env")

View file

@ -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,

View file

@ -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"

View file

@ -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();
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View 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

View file

@ -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();
}

View file

@ -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);

View file

@ -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) {

View file

@ -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

View file

@ -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()) {

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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);
};

View file

@ -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) {

View file

@ -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();
}

View file

@ -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

View file

@ -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 {

View file

@ -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());
}
}
}

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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);

View file

@ -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();
};

View file

@ -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

View file

@ -36,7 +36,7 @@
class PackedDataContainer : public Resource {
GDCLASS(PackedDataContainer, Resource);
enum {
enum : uint32_t {
TYPE_DICT = 0xFFFFFFFF,
TYPE_ARRAY = 0xFFFFFFFE,
};

View file

@ -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));

View file

@ -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;
}

View file

@ -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() {}

View file

@ -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;

View file

@ -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;

View file

@ -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() {}

View file

@ -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.");
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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"

View file

@ -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();
}

View file

@ -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();

View file

@ -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));

View file

@ -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;
};

View file

@ -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;
}
}

View file

@ -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

View file

@ -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();
};

View file

@ -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();

View file

@ -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();

View file

@ -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();

View file

@ -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) {

View file

@ -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);

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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;
}

View file

@ -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 {

View file

@ -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() {}
};

View file

@ -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;

View file

@ -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);

View file

@ -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"