summaryrefslogtreecommitdiff
path: root/core/io
diff options
context:
space:
mode:
Diffstat (limited to 'core/io')
-rw-r--r--core/io/dir_access.cpp164
-rw-r--r--core/io/dir_access.h33
-rw-r--r--core/io/file_access.cpp215
-rw-r--r--core/io/file_access.h55
-rw-r--r--core/io/file_access_compressed.cpp2
-rw-r--r--core/io/file_access_compressed.h2
-rw-r--r--core/io/file_access_encrypted.cpp10
-rw-r--r--core/io/file_access_encrypted.h2
-rw-r--r--core/io/file_access_memory.cpp2
-rw-r--r--core/io/file_access_memory.h2
-rw-r--r--core/io/file_access_network.cpp10
-rw-r--r--core/io/file_access_network.h2
-rw-r--r--core/io/file_access_pack.cpp2
-rw-r--r--core/io/file_access_pack.h2
-rw-r--r--core/io/file_access_zip.cpp4
-rw-r--r--core/io/file_access_zip.h2
-rw-r--r--core/io/http_client.cpp4
-rw-r--r--core/io/http_client.h4
-rw-r--r--core/io/http_client_tcp.cpp72
-rw-r--r--core/io/http_client_tcp.h6
-rw-r--r--core/io/image.cpp287
-rw-r--r--core/io/image.h21
-rw-r--r--core/io/image_loader.cpp52
-rw-r--r--core/io/image_loader.h52
-rw-r--r--core/io/json.cpp109
-rw-r--r--core/io/json.h26
-rw-r--r--core/io/logger.cpp2
-rw-r--r--core/io/marshalls.cpp6
-rw-r--r--core/io/packet_peer.h4
-rw-r--r--core/io/pck_packer.cpp4
-rw-r--r--core/io/resource.cpp19
-rw-r--r--core/io/resource_format_binary.cpp103
-rw-r--r--core/io/resource_importer.cpp9
-rw-r--r--core/io/resource_loader.cpp78
-rw-r--r--core/io/resource_loader.h3
-rw-r--r--core/io/resource_saver.cpp51
-rw-r--r--core/io/resource_saver.h2
-rw-r--r--core/io/resource_uid.cpp7
-rw-r--r--core/io/stream_peer.h8
-rw-r--r--core/io/stream_peer_gzip.cpp209
-rw-r--r--core/io/stream_peer_gzip.h76
-rw-r--r--core/io/stream_peer_tcp.cpp4
-rw-r--r--core/io/stream_peer_tcp.h2
-rw-r--r--core/io/stream_peer_tls.cpp (renamed from core/io/stream_peer_ssl.cpp)34
-rw-r--r--core/io/stream_peer_tls.h (renamed from core/io/stream_peer_ssl.h)20
-rw-r--r--core/io/xml_parser.cpp71
-rw-r--r--core/io/xml_parser.h8
-rw-r--r--core/io/zip_io.cpp12
48 files changed, 1469 insertions, 405 deletions
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index bed41b8d89..7eb50d2261 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -36,6 +36,8 @@
#include "core/os/os.h"
#include "core/templates/local_vector.h"
+thread_local Error DirAccess::last_dir_open_error = OK;
+
String DirAccess::_get_root_path() const {
switch (_access_type) {
case ACCESS_RESOURCES:
@@ -249,6 +251,61 @@ Ref<DirAccess> DirAccess::open(const String &p_path, Error *r_error) {
return da;
}
+Ref<DirAccess> DirAccess::_open(const String &p_path) {
+ Error err = OK;
+ Ref<DirAccess> da = open(p_path, &err);
+ last_dir_open_error = err;
+ if (err) {
+ return Ref<DirAccess>();
+ }
+ return da;
+}
+
+int DirAccess::_get_drive_count() {
+ Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ return d->get_drive_count();
+}
+
+String DirAccess::get_drive_name(int p_idx) {
+ Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ return d->get_drive(p_idx);
+}
+
+Error DirAccess::make_dir_absolute(const String &p_dir) {
+ Ref<DirAccess> d = DirAccess::create_for_path(p_dir);
+ return d->make_dir(p_dir);
+}
+
+Error DirAccess::make_dir_recursive_absolute(const String &p_dir) {
+ Ref<DirAccess> d = DirAccess::create_for_path(p_dir);
+ return d->make_dir_recursive(p_dir);
+}
+
+bool DirAccess::dir_exists_absolute(const String &p_dir) {
+ Ref<DirAccess> d = DirAccess::create_for_path(p_dir);
+ return d->dir_exists(p_dir);
+}
+
+Error DirAccess::copy_absolute(const String &p_from, const String &p_to, int p_chmod_flags) {
+ Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ // Support copying from res:// to user:// etc.
+ String from = ProjectSettings::get_singleton()->globalize_path(p_from);
+ String to = ProjectSettings::get_singleton()->globalize_path(p_to);
+ return d->copy(from, to, p_chmod_flags);
+}
+
+Error DirAccess::rename_absolute(const String &p_from, const String &p_to) {
+ Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ String from = ProjectSettings::get_singleton()->globalize_path(p_from);
+ String to = ProjectSettings::get_singleton()->globalize_path(p_to);
+ return d->rename(from, to);
+}
+
+Error DirAccess::remove_absolute(const String &p_path) {
+ Ref<DirAccess> d = DirAccess::create_for_path(p_path);
+ return d->remove(p_path);
+}
+
Ref<DirAccess> DirAccess::create(AccessType p_access) {
Ref<DirAccess> da = create_func[p_access] ? create_func[p_access]() : nullptr;
if (da.is_valid()) {
@@ -266,6 +323,10 @@ Ref<DirAccess> DirAccess::create(AccessType p_access) {
return da;
}
+Error DirAccess::get_open_error() {
+ return last_dir_open_error;
+}
+
String DirAccess::get_full_path(const String &p_path, AccessType p_access) {
Ref<DirAccess> d = DirAccess::create(p_access);
if (d.is_null()) {
@@ -290,7 +351,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
const size_t copy_buffer_limit = 65536; // 64 KB
fsrc->seek_end(0);
- int size = fsrc->get_position();
+ uint64_t size = fsrc->get_position();
fsrc->seek(0);
err = OK;
size_t buffer_size = MIN(size * sizeof(uint8_t), copy_buffer_limit);
@@ -424,3 +485,104 @@ bool DirAccess::exists(String p_dir) {
Ref<DirAccess> da = DirAccess::create_for_path(p_dir);
return da->change_dir(p_dir) == OK;
}
+
+PackedStringArray DirAccess::get_files() {
+ return _get_contents(false);
+}
+
+PackedStringArray DirAccess::get_files_at(const String &p_path) {
+ Ref<DirAccess> da = DirAccess::open(p_path);
+ ERR_FAIL_COND_V_MSG(da.is_null(), PackedStringArray(), vformat("Couldn't open directory at path \"%s\".", p_path));
+ return da->get_files();
+}
+
+PackedStringArray DirAccess::get_directories() {
+ return _get_contents(true);
+}
+
+PackedStringArray DirAccess::get_directories_at(const String &p_path) {
+ Ref<DirAccess> da = DirAccess::open(p_path);
+ ERR_FAIL_COND_V_MSG(da.is_null(), PackedStringArray(), vformat("Couldn't open directory at path \"%s\".", p_path));
+ return da->get_directories();
+}
+
+PackedStringArray DirAccess::_get_contents(bool p_directories) {
+ PackedStringArray ret;
+
+ list_dir_begin();
+ String s = _get_next();
+ while (!s.is_empty()) {
+ if (current_is_dir() == p_directories) {
+ ret.append(s);
+ }
+ s = _get_next();
+ }
+
+ ret.sort();
+ return ret;
+}
+
+String DirAccess::_get_next() {
+ String next = get_next();
+ while (!next.is_empty() && ((!include_navigational && (next == "." || next == "..")) || (!include_hidden && current_is_hidden()))) {
+ next = get_next();
+ }
+ return next;
+}
+
+void DirAccess::set_include_navigational(bool p_enable) {
+ include_navigational = p_enable;
+}
+
+bool DirAccess::get_include_navigational() const {
+ return include_navigational;
+}
+
+void DirAccess::set_include_hidden(bool p_enable) {
+ include_hidden = p_enable;
+}
+
+bool DirAccess::get_include_hidden() const {
+ return include_hidden;
+}
+
+void DirAccess::_bind_methods() {
+ ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error);
+
+ ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_next"), &DirAccess::_get_next);
+ ClassDB::bind_method(D_METHOD("current_is_dir"), &DirAccess::current_is_dir);
+ ClassDB::bind_method(D_METHOD("list_dir_end"), &DirAccess::list_dir_end);
+ ClassDB::bind_method(D_METHOD("get_files"), &DirAccess::get_files);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_files_at", "path"), &DirAccess::get_files_at);
+ ClassDB::bind_method(D_METHOD("get_directories"), &DirAccess::get_directories);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_directories_at", "path"), &DirAccess::get_directories_at);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_drive_count"), &DirAccess::_get_drive_count);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("get_drive_name", "idx"), &DirAccess::get_drive_name);
+ ClassDB::bind_method(D_METHOD("get_current_drive"), &DirAccess::get_current_drive);
+ ClassDB::bind_method(D_METHOD("change_dir", "to_dir"), &DirAccess::change_dir);
+ ClassDB::bind_method(D_METHOD("get_current_dir", "include_drive"), &DirAccess::get_current_dir, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("make_dir", "path"), &DirAccess::make_dir);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("make_dir_absolute", "path"), &DirAccess::make_dir_absolute);
+ ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &DirAccess::make_dir_recursive);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("make_dir_recursive_absolute", "path"), &DirAccess::make_dir_recursive_absolute);
+ ClassDB::bind_method(D_METHOD("file_exists", "path"), &DirAccess::file_exists);
+ ClassDB::bind_method(D_METHOD("dir_exists", "path"), &DirAccess::dir_exists);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("dir_exists_absolute", "path"), &DirAccess::dir_exists_absolute);
+ ClassDB::bind_method(D_METHOD("get_space_left"), &DirAccess::get_space_left);
+ ClassDB::bind_method(D_METHOD("copy", "from", "to", "chmod_flags"), &DirAccess::copy, DEFVAL(-1));
+ ClassDB::bind_static_method("DirAccess", D_METHOD("copy_absolute", "from", "to", "chmod_flags"), &DirAccess::copy_absolute, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("rename", "from", "to"), &DirAccess::rename);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("rename_absolute", "from", "to"), &DirAccess::rename_absolute);
+ ClassDB::bind_method(D_METHOD("remove", "path"), &DirAccess::remove);
+ ClassDB::bind_static_method("DirAccess", D_METHOD("remove_absolute", "path"), &DirAccess::remove_absolute);
+
+ ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational);
+ ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational);
+ ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden);
+ ClassDB::bind_method(D_METHOD("get_include_hidden"), &DirAccess::get_include_hidden);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");
+}
diff --git a/core/io/dir_access.h b/core/io/dir_access.h
index 2469c2a080..bd24214e73 100644
--- a/core/io/dir_access.h
+++ b/core/io/dir_access.h
@@ -37,6 +37,8 @@
//@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies
class DirAccess : public RefCounted {
+ GDCLASS(DirAccess, RefCounted);
+
public:
enum AccessType {
ACCESS_RESOURCES,
@@ -50,10 +52,18 @@ public:
private:
AccessType _access_type = ACCESS_FILESYSTEM;
static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object
+ static Ref<DirAccess> _open(const String &p_path);
Error _copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod_flags, bool p_copy_links);
+ PackedStringArray _get_contents(bool p_directories);
+
+ thread_local static Error last_dir_open_error;
+ bool include_navigational = false;
+ bool include_hidden = false;
protected:
+ static void _bind_methods();
+
String _get_root_path() const;
virtual String _get_root_string() const;
@@ -118,6 +128,7 @@ public:
static Ref<DirAccess> create_for_path(const String &p_path);
static Ref<DirAccess> create(AccessType p_access);
+ static Error get_open_error();
template <class T>
static void make_default(AccessType p_access) {
@@ -126,6 +137,28 @@ public:
static Ref<DirAccess> open(const String &p_path, Error *r_error = nullptr);
+ static int _get_drive_count();
+ static String get_drive_name(int p_idx);
+
+ static Error make_dir_absolute(const String &p_dir);
+ static Error make_dir_recursive_absolute(const String &p_dir);
+ static bool dir_exists_absolute(const String &p_dir);
+
+ static Error copy_absolute(const String &p_from, const String &p_to, int p_chmod_flags = -1);
+ static Error rename_absolute(const String &p_from, const String &p_to);
+ static Error remove_absolute(const String &p_path);
+
+ PackedStringArray get_files();
+ static PackedStringArray get_files_at(const String &p_path);
+ PackedStringArray get_directories();
+ static PackedStringArray get_directories_at(const String &p_path);
+ String _get_next();
+
+ void set_include_navigational(bool p_enable);
+ bool get_include_navigational() const;
+ void set_include_hidden(bool p_enable);
+ bool get_include_hidden() const;
+
DirAccess() {}
virtual ~DirAccess() {}
};
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index 72c00bd678..0ceb300f97 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -32,6 +32,8 @@
#include "core/config/project_settings.h"
#include "core/crypto/crypto_core.h"
+#include "core/io/file_access_compressed.h"
+#include "core/io/file_access_encrypted.h"
#include "core/io/file_access_pack.h"
#include "core/io/marshalls.h"
#include "core/os/os.h"
@@ -41,6 +43,7 @@ FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { nullptr, nullptr
FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr;
bool FileAccess::backup_save = false;
+thread_local Error FileAccess::last_file_open_error = OK;
Ref<FileAccess> FileAccess::create(AccessType p_access) {
ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr);
@@ -81,7 +84,7 @@ Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
}
Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
- return _open(p_path, p_mode_flags);
+ return open_internal(p_path, p_mode_flags);
}
Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) {
@@ -99,7 +102,7 @@ Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error *
}
ret = create_for_path(p_path);
- Error err = ret->_open(p_path, p_mode_flags);
+ Error err = ret->open_internal(p_path, p_mode_flags);
if (r_error) {
*r_error = err;
@@ -111,6 +114,66 @@ Ref<FileAccess> FileAccess::open(const String &p_path, int p_mode_flags, Error *
return ret;
}
+Ref<FileAccess> FileAccess::_open(const String &p_path, ModeFlags p_mode_flags) {
+ Error err = OK;
+ Ref<FileAccess> fa = open(p_path, p_mode_flags, &err);
+ last_file_open_error = err;
+ if (err) {
+ return Ref<FileAccess>();
+ }
+ return fa;
+}
+
+Ref<FileAccess> FileAccess::open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key) {
+ Ref<FileAccess> fa = _open(p_path, p_mode_flags);
+ if (fa.is_null()) {
+ return fa;
+ }
+
+ Ref<FileAccessEncrypted> fae;
+ fae.instantiate();
+ Error err = fae->open_and_parse(fa, p_key, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ);
+ if (err) {
+ last_file_open_error = err;
+ return Ref<FileAccess>();
+ }
+ return fae;
+}
+
+Ref<FileAccess> FileAccess::open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass) {
+ Ref<FileAccess> fa = _open(p_path, p_mode_flags);
+ if (fa.is_null()) {
+ return fa;
+ }
+
+ Ref<FileAccessEncrypted> fae;
+ fae.instantiate();
+ Error err = fae->open_and_parse_password(fa, p_pass, (p_mode_flags == WRITE) ? FileAccessEncrypted::MODE_WRITE_AES256 : FileAccessEncrypted::MODE_READ);
+ if (err) {
+ last_file_open_error = err;
+ return Ref<FileAccess>();
+ }
+ return fae;
+}
+
+Ref<FileAccess> FileAccess::open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode) {
+ Ref<FileAccessCompressed> fac;
+ fac.instantiate();
+ fac->configure("GCPF", (Compression::Mode)p_compress_mode);
+ Error err = fac->open_internal(p_path, p_mode_flags);
+
+ if (err) {
+ last_file_open_error = err;
+ return Ref<FileAccess>();
+ }
+
+ return fac;
+}
+
+Error FileAccess::get_open_error() {
+ return last_file_open_error;
+}
+
FileAccess::CreateFunc FileAccess::get_create_func(AccessType p_access) {
return create_func[p_access];
}
@@ -227,6 +290,20 @@ real_t FileAccess::get_real() const {
}
}
+Variant FileAccess::get_var(bool p_allow_objects) const {
+ uint32_t len = get_32();
+ Vector<uint8_t> buff = _get_buffer(len);
+ ERR_FAIL_COND_V((uint32_t)buff.size() != len, Variant());
+
+ const uint8_t *r = buff.ptr();
+
+ Variant v;
+ Error err = decode_variant(v, &r[0], len, nullptr, p_allow_objects);
+ ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to encode Variant.");
+
+ return v;
+}
+
double FileAccess::get_double() const {
MarshallDouble m;
m.l = get_64();
@@ -370,6 +447,17 @@ Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
return strings;
}
+String FileAccess::get_as_text(bool p_skip_cr) const {
+ uint64_t original_pos = get_position();
+ const_cast<FileAccess *>(this)->seek(0);
+
+ String text = get_as_utf8_string(p_skip_cr);
+
+ const_cast<FileAccess *>(this)->seek(original_pos);
+
+ return text;
+}
+
uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
@@ -381,6 +469,27 @@ uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
return i;
}
+Vector<uint8_t> FileAccess::_get_buffer(int64_t p_length) const {
+ Vector<uint8_t> data;
+
+ ERR_FAIL_COND_V_MSG(p_length < 0, data, "Length of buffer cannot be smaller than 0.");
+ if (p_length == 0) {
+ return data;
+ }
+
+ Error err = data.resize(p_length);
+ ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements.");
+
+ uint8_t *w = data.ptrw();
+ int64_t len = get_buffer(&w[0], p_length);
+
+ if (len < p_length) {
+ data.resize(len);
+ }
+
+ return data;
+}
+
String FileAccess::get_as_utf8_string(bool p_skip_cr) const {
Vector<uint8_t> sourcef;
uint64_t len = get_length();
@@ -439,7 +548,7 @@ void FileAccess::store_64(uint64_t p_dest) {
}
void FileAccess::store_real(real_t p_real) {
- if (sizeof(real_t) == 4) {
+ if constexpr (sizeof(real_t) == 4) {
store_float(p_real);
} else {
store_double(p_real);
@@ -554,7 +663,34 @@ void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
}
}
-Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) {
+void FileAccess::_store_buffer(const Vector<uint8_t> &p_buffer) {
+ uint64_t len = p_buffer.size();
+ if (len == 0) {
+ return;
+ }
+
+ const uint8_t *r = p_buffer.ptr();
+
+ store_buffer(&r[0], len);
+}
+
+void FileAccess::store_var(const Variant &p_var, bool p_full_objects) {
+ int len;
+ Error err = encode_variant(p_var, nullptr, len, p_full_objects);
+ ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant.");
+
+ Vector<uint8_t> buff;
+ buff.resize(len);
+
+ uint8_t *w = buff.ptrw();
+ err = encode_variant(p_var, &w[0], len, p_full_objects);
+ ERR_FAIL_COND_MSG(err != OK, "Error when trying to encode Variant.");
+
+ store_32(len);
+ _store_buffer(buff);
+}
+
+Vector<uint8_t> FileAccess::get_file_as_bytes(const String &p_path, Error *r_error) {
Ref<FileAccess> f = FileAccess::open(p_path, READ, r_error);
if (f.is_null()) {
if (r_error) { // if error requested, do not throw error
@@ -570,7 +706,7 @@ Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_err
String FileAccess::get_file_as_string(const String &p_path, Error *r_error) {
Error err;
- Vector<uint8_t> array = get_file_as_array(p_path, &err);
+ Vector<uint8_t> array = get_file_as_bytes(p_path, &err);
if (r_error) {
*r_error = err;
}
@@ -666,3 +802,72 @@ String FileAccess::get_sha256(const String &p_file) {
return String::hex_encode_buffer(hash, 32);
}
+
+void FileAccess::_bind_methods() {
+ ClassDB::bind_static_method("FileAccess", D_METHOD("open", "path", "flags"), &FileAccess::_open);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted", "path", "mode_flags", "key"), &FileAccess::open_encrypted);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("open_encrypted_with_pass", "path", "mode_flags", "pass"), &FileAccess::open_encrypted_pass);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("open_compressed", "path", "mode_flags", "compression_mode"), &FileAccess::open_compressed, DEFVAL(0));
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_open_error"), &FileAccess::get_open_error);
+
+ ClassDB::bind_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);
+
+ ClassDB::bind_method(D_METHOD("flush"), &FileAccess::flush);
+ ClassDB::bind_method(D_METHOD("get_path"), &FileAccess::get_path);
+ ClassDB::bind_method(D_METHOD("get_path_absolute"), &FileAccess::get_path_absolute);
+ ClassDB::bind_method(D_METHOD("is_open"), &FileAccess::is_open);
+ ClassDB::bind_method(D_METHOD("seek", "position"), &FileAccess::seek);
+ ClassDB::bind_method(D_METHOD("seek_end", "position"), &FileAccess::seek_end, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("get_position"), &FileAccess::get_position);
+ ClassDB::bind_method(D_METHOD("get_length"), &FileAccess::get_length);
+ ClassDB::bind_method(D_METHOD("eof_reached"), &FileAccess::eof_reached);
+ ClassDB::bind_method(D_METHOD("get_8"), &FileAccess::get_8);
+ ClassDB::bind_method(D_METHOD("get_16"), &FileAccess::get_16);
+ ClassDB::bind_method(D_METHOD("get_32"), &FileAccess::get_32);
+ ClassDB::bind_method(D_METHOD("get_64"), &FileAccess::get_64);
+ ClassDB::bind_method(D_METHOD("get_float"), &FileAccess::get_float);
+ ClassDB::bind_method(D_METHOD("get_double"), &FileAccess::get_double);
+ ClassDB::bind_method(D_METHOD("get_real"), &FileAccess::get_real);
+ ClassDB::bind_method(D_METHOD("get_buffer", "length"), &FileAccess::_get_buffer);
+ ClassDB::bind_method(D_METHOD("get_line"), &FileAccess::get_line);
+ ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &FileAccess::get_csv_line, DEFVAL(","));
+ ClassDB::bind_method(D_METHOD("get_as_text", "skip_cr"), &FileAccess::get_as_text, DEFVAL(false));
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_md5", "path"), &FileAccess::get_md5);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_sha256", "path"), &FileAccess::get_sha256);
+ ClassDB::bind_method(D_METHOD("is_big_endian"), &FileAccess::is_big_endian);
+ ClassDB::bind_method(D_METHOD("set_big_endian", "big_endian"), &FileAccess::set_big_endian);
+ ClassDB::bind_method(D_METHOD("get_error"), &FileAccess::get_error);
+ ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &FileAccess::get_var, DEFVAL(false));
+
+ ClassDB::bind_method(D_METHOD("store_8", "value"), &FileAccess::store_8);
+ ClassDB::bind_method(D_METHOD("store_16", "value"), &FileAccess::store_16);
+ ClassDB::bind_method(D_METHOD("store_32", "value"), &FileAccess::store_32);
+ ClassDB::bind_method(D_METHOD("store_64", "value"), &FileAccess::store_64);
+ ClassDB::bind_method(D_METHOD("store_float", "value"), &FileAccess::store_float);
+ ClassDB::bind_method(D_METHOD("store_double", "value"), &FileAccess::store_double);
+ ClassDB::bind_method(D_METHOD("store_real", "value"), &FileAccess::store_real);
+ ClassDB::bind_method(D_METHOD("store_buffer", "buffer"), &FileAccess::_store_buffer);
+ ClassDB::bind_method(D_METHOD("store_line", "line"), &FileAccess::store_line);
+ ClassDB::bind_method(D_METHOD("store_csv_line", "values", "delim"), &FileAccess::store_csv_line, DEFVAL(","));
+ ClassDB::bind_method(D_METHOD("store_string", "string"), &FileAccess::store_string);
+ ClassDB::bind_method(D_METHOD("store_var", "value", "full_objects"), &FileAccess::store_var, DEFVAL(false));
+
+ ClassDB::bind_method(D_METHOD("store_pascal_string", "string"), &FileAccess::store_pascal_string);
+ ClassDB::bind_method(D_METHOD("get_pascal_string"), &FileAccess::get_pascal_string);
+
+ ClassDB::bind_static_method("FileAccess", D_METHOD("file_exists", "path"), &FileAccess::exists);
+ ClassDB::bind_static_method("FileAccess", D_METHOD("get_modified_time", "file"), &FileAccess::get_modified_time);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian");
+
+ BIND_ENUM_CONSTANT(READ);
+ BIND_ENUM_CONSTANT(WRITE);
+ BIND_ENUM_CONSTANT(READ_WRITE);
+ BIND_ENUM_CONSTANT(WRITE_READ);
+
+ BIND_ENUM_CONSTANT(COMPRESSION_FASTLZ);
+ BIND_ENUM_CONSTANT(COMPRESSION_DEFLATE);
+ BIND_ENUM_CONSTANT(COMPRESSION_ZSTD);
+ BIND_ENUM_CONSTANT(COMPRESSION_GZIP);
+}
diff --git a/core/io/file_access.h b/core/io/file_access.h
index fc0eb95d44..54a0235333 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -31,6 +31,7 @@
#ifndef FILE_ACCESS_H
#define FILE_ACCESS_H
+#include "core/io/compression.h"
#include "core/math/math_defs.h"
#include "core/object/ref_counted.h"
#include "core/os/memory.h"
@@ -42,6 +43,8 @@
*/
class FileAccess : public RefCounted {
+ GDCLASS(FileAccess, RefCounted);
+
public:
enum AccessType {
ACCESS_RESOURCES,
@@ -50,6 +53,20 @@ public:
ACCESS_MAX
};
+ enum ModeFlags {
+ READ = 1,
+ WRITE = 2,
+ READ_WRITE = 3,
+ WRITE_READ = 7,
+ };
+
+ enum CompressionMode {
+ COMPRESSION_FASTLZ = Compression::MODE_FASTLZ,
+ COMPRESSION_DEFLATE = Compression::MODE_DEFLATE,
+ COMPRESSION_ZSTD = Compression::MODE_ZSTD,
+ COMPRESSION_GZIP = Compression::MODE_GZIP
+ };
+
typedef void (*FileCloseFailNotify)(const String &);
typedef Ref<FileAccess> (*CreateFunc)();
@@ -60,15 +77,19 @@ public:
virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0;
protected:
+ static void _bind_methods();
+
AccessType get_access_type() const;
String fix_path(const String &p_path) const;
- virtual Error _open(const String &p_path, int p_mode_flags) = 0; ///< open a file
+ virtual Error open_internal(const String &p_path, int p_mode_flags) = 0; ///< open a file
virtual uint64_t _get_modified_time(const String &p_file) = 0;
+ virtual void _set_access_type(AccessType p_access);
static FileCloseFailNotify close_fail_notify;
private:
static bool backup_save;
+ thread_local static Error last_file_open_error;
AccessType _access_type = ACCESS_FILESYSTEM;
static CreateFunc create_func[ACCESS_MAX]; /** default file access creation function for a platform */
@@ -77,18 +98,11 @@ private:
return memnew(T);
}
+ static Ref<FileAccess> _open(const String &p_path, ModeFlags p_mode_flags);
+
public:
static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; }
- virtual void _set_access_type(AccessType p_access);
-
- enum ModeFlags {
- READ = 1,
- WRITE = 2,
- READ_WRITE = 3,
- WRITE_READ = 7,
- };
-
virtual bool is_open() const = 0; ///< true when file is open
virtual String get_path() const { return ""; } /// returns the path for the current open file
@@ -110,10 +124,14 @@ public:
virtual double get_double() const;
virtual real_t get_real() const;
+ Variant get_var(bool p_allow_objects = false) const;
+
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes
+ Vector<uint8_t> _get_buffer(int64_t p_length) const;
virtual String get_line() const;
virtual String get_token() const;
virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
+ String get_as_text(bool p_skip_cr = false) const;
virtual String get_as_utf8_string(bool p_skip_cr = false) const;
/**
@@ -144,6 +162,9 @@ public:
virtual String get_pascal_string();
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes
+ void _store_buffer(const Vector<uint8_t> &p_buffer);
+
+ void store_var(const Variant &p_var, bool p_full_objects = false);
virtual bool file_exists(const String &p_name) = 0; ///< return true if a file exists
@@ -152,6 +173,12 @@ public:
static Ref<FileAccess> create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files.
static Ref<FileAccess> create_for_path(const String &p_path);
static Ref<FileAccess> open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files.
+
+ static Ref<FileAccess> open_encrypted(const String &p_path, ModeFlags p_mode_flags, const Vector<uint8_t> &p_key);
+ static Ref<FileAccess> open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass);
+ static Ref<FileAccess> open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ);
+ static Error get_open_error();
+
static CreateFunc get_create_func(AccessType p_access);
static bool exists(const String &p_name); ///< return true if a file exists
static uint64_t get_modified_time(const String &p_file);
@@ -165,9 +192,12 @@ public:
static String get_sha256(const String &p_file);
static String get_multiple_md5(const Vector<String> &p_file);
- static Vector<uint8_t> get_file_as_array(const String &p_path, Error *r_error = nullptr);
+ static Vector<uint8_t> get_file_as_bytes(const String &p_path, Error *r_error = nullptr);
static String get_file_as_string(const String &p_path, Error *r_error = nullptr);
+ static PackedByteArray _get_file_as_bytes(const String &p_path) { return get_file_as_bytes(p_path); }
+ static String _get_file_as_string(const String &p_path) { return get_file_as_string(p_path); };
+
template <class T>
static void make_default(AccessType p_access) {
create_func[p_access] = _create_builtin<T>;
@@ -177,4 +207,7 @@ public:
virtual ~FileAccess() {}
};
+VARIANT_ENUM_CAST(FileAccess::CompressionMode);
+VARIANT_ENUM_CAST(FileAccess::ModeFlags);
+
#endif // FILE_ACCESS_H
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index 1d0a718166..d2c8a88269 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -95,7 +95,7 @@ Error FileAccessCompressed::open_after_magic(Ref<FileAccess> p_base) {
return ret == -1 ? ERR_FILE_CORRUPT : OK;
}
-Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) {
+Error FileAccessCompressed::open_internal(const String &p_path, int p_mode_flags) {
ERR_FAIL_COND_V(p_mode_flags == READ_WRITE, ERR_UNAVAILABLE);
_close();
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h
index e41491a92c..ee114c2c65 100644
--- a/core/io/file_access_compressed.h
+++ b/core/io/file_access_compressed.h
@@ -70,7 +70,7 @@ public:
Error open_after_magic(Ref<FileAccess> p_base);
- virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
virtual void seek(uint64_t p_position) override; ///< seek to a given position
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index d1b014a0be..e7b2a2dfee 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -102,16 +102,16 @@ Error FileAccessEncrypted::open_and_parse(Ref<FileAccess> p_base, const Vector<u
Error FileAccessEncrypted::open_and_parse_password(Ref<FileAccess> p_base, const String &p_key, Mode p_mode) {
String cs = p_key.md5_text();
ERR_FAIL_COND_V(cs.length() != 32, ERR_INVALID_PARAMETER);
- Vector<uint8_t> key;
- key.resize(32);
+ Vector<uint8_t> key_md5;
+ key_md5.resize(32);
for (int i = 0; i < 32; i++) {
- key.write[i] = cs[i];
+ key_md5.write[i] = cs[i];
}
- return open_and_parse(p_base, key, p_mode);
+ return open_and_parse(p_base, key_md5, p_mode);
}
-Error FileAccessEncrypted::_open(const String &p_path, int p_mode_flags) {
+Error FileAccessEncrypted::open_internal(const String &p_path, int p_mode_flags) {
return OK;
}
diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h
index 6200f87a7a..6b4588841d 100644
--- a/core/io/file_access_encrypted.h
+++ b/core/io/file_access_encrypted.h
@@ -60,7 +60,7 @@ public:
Error open_and_parse(Ref<FileAccess> p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true);
Error open_and_parse_password(Ref<FileAccess> p_base, const String &p_key, Mode p_mode);
- virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
virtual String get_path() const override; /// returns the path for the current open file
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp
index 499d001234..21ded4247f 100644
--- a/core/io/file_access_memory.cpp
+++ b/core/io/file_access_memory.cpp
@@ -78,7 +78,7 @@ Error FileAccessMemory::open_custom(const uint8_t *p_data, uint64_t p_len) {
return OK;
}
-Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) {
+Error FileAccessMemory::open_internal(const String &p_path, int p_mode_flags) {
ERR_FAIL_COND_V(!files, ERR_FILE_NOT_FOUND);
String name = fix_path(p_path);
diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h
index f2bd2aa832..b1f408eb98 100644
--- a/core/io/file_access_memory.h
+++ b/core/io/file_access_memory.h
@@ -45,7 +45,7 @@ public:
static void cleanup();
virtual Error open_custom(const uint8_t *p_data, uint64_t p_len); ///< open a file
- virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
virtual void seek(uint64_t p_position) override; ///< seek to a given position
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 1365b4b593..87f3f66597 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -137,12 +137,12 @@ void FileAccessNetworkClient::_thread_func() {
int64_t offset = get_64();
int32_t len = get_32();
- Vector<uint8_t> block;
- block.resize(len);
- client->get_data(block.ptrw(), len);
+ Vector<uint8_t> resp_block;
+ resp_block.resize(len);
+ client->get_data(resp_block.ptrw(), len);
if (fa) { //may have been queued
- fa->_set_block(offset, block);
+ fa->_set_block(offset, resp_block);
}
} break;
@@ -252,7 +252,7 @@ void FileAccessNetwork::_respond(uint64_t p_len, Error p_status) {
pages.resize(pc);
}
-Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) {
+Error FileAccessNetwork::open_internal(const String &p_path, int p_mode_flags) {
ERR_FAIL_COND_V(p_mode_flags != READ, ERR_UNAVAILABLE);
_close();
diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h
index ceadc715a1..ee92d3b9db 100644
--- a/core/io/file_access_network.h
+++ b/core/io/file_access_network.h
@@ -132,7 +132,7 @@ public:
RESPONSE_GET_MODTIME,
};
- virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
virtual void seek(uint64_t p_position) override; ///< seek to a given position
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index adae0db0f4..dfcce30ab5 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -259,7 +259,7 @@ Ref<FileAccess> PackedSourcePCK::get_file(const String &p_path, PackedData::Pack
//////////////////////////////////////////////////////////////////
-Error FileAccessPack::_open(const String &p_path, int p_mode_flags) {
+Error FileAccessPack::open_internal(const String &p_path, int p_mode_flags) {
ERR_FAIL_V(ERR_UNAVAILABLE);
return ERR_UNAVAILABLE;
}
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 023758ac0f..4b9b49a161 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -148,7 +148,7 @@ class FileAccessPack : public FileAccess {
uint64_t off;
Ref<FileAccess> f;
- virtual Error _open(const String &p_path, int p_mode_flags) override;
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override;
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
virtual uint32_t _get_unix_permissions(const String &p_file) override { return 0; }
virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) override { return FAILED; }
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index 17f2335a8e..72503851c1 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -234,7 +234,7 @@ ZipArchive::~ZipArchive() {
packages.clear();
}
-Error FileAccessZip::_open(const String &p_path, int p_mode_flags) {
+Error FileAccessZip::open_internal(const String &p_path, int p_mode_flags) {
_close();
ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, FAILED);
@@ -337,7 +337,7 @@ bool FileAccessZip::file_exists(const String &p_name) {
}
FileAccessZip::FileAccessZip(const String &p_path, const PackedData::PackedFile &p_file) {
- _open(p_path, FileAccess::READ);
+ open_internal(p_path, FileAccess::READ);
}
FileAccessZip::~FileAccessZip() {
diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h
index 74a48192f3..6d61b9a291 100644
--- a/core/io/file_access_zip.h
+++ b/core/io/file_access_zip.h
@@ -85,7 +85,7 @@ class FileAccessZip : public FileAccess {
void _close();
public:
- virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file
+ virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
virtual bool is_open() const override; ///< true when file is open
virtual void seek(uint64_t p_position) override; ///< seek to a given position
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 52b1120b2a..93a310e83b 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -138,7 +138,7 @@ PackedStringArray HTTPClient::_get_response_headers() {
}
void HTTPClient::_bind_methods() {
- ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_ssl", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_tls", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("set_connection", "connection"), &HTTPClient::set_connection);
ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection);
ClassDB::bind_method(D_METHOD("request_raw", "method", "url", "headers", "body"), &HTTPClient::_request_raw);
@@ -190,7 +190,7 @@ void HTTPClient::_bind_methods() {
BIND_ENUM_CONSTANT(STATUS_REQUESTING); // Request in progress
BIND_ENUM_CONSTANT(STATUS_BODY); // Request resulted in body which must be read
BIND_ENUM_CONSTANT(STATUS_CONNECTION_ERROR);
- BIND_ENUM_CONSTANT(STATUS_SSL_HANDSHAKE_ERROR);
+ BIND_ENUM_CONSTANT(STATUS_TLS_HANDSHAKE_ERROR);
BIND_ENUM_CONSTANT(RESPONSE_CONTINUE);
BIND_ENUM_CONSTANT(RESPONSE_SWITCHING_PROTOCOLS);
diff --git a/core/io/http_client.h b/core/io/http_client.h
index de6045f647..0524b010f4 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -138,7 +138,7 @@ public:
STATUS_REQUESTING, // Request in progress
STATUS_BODY, // Request resulted in body, which must be read
STATUS_CONNECTION_ERROR,
- STATUS_SSL_HANDSHAKE_ERROR,
+ STATUS_TLS_HANDSHAKE_ERROR,
};
@@ -168,7 +168,7 @@ public:
Error verify_headers(const Vector<String> &p_headers);
virtual Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) = 0;
- virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) = 0;
+ virtual Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) = 0;
virtual void set_connection(const Ref<StreamPeer> &p_connection) = 0;
virtual Ref<StreamPeer> get_connection() const = 0;
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 9499a6f8e3..aff79320ca 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -32,14 +32,14 @@
#include "http_client_tcp.h"
-#include "core/io/stream_peer_ssl.h"
+#include "core/io/stream_peer_tls.h"
#include "core/version.h"
HTTPClient *HTTPClientTCP::_create_func() {
return memnew(HTTPClientTCP);
}
-Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
+Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_tls, bool p_verify_host) {
close();
conn_port = p_port;
@@ -47,21 +47,21 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
ip_candidates.clear();
- ssl = p_ssl;
- ssl_verify_host = p_verify_host;
+ tls = p_tls;
+ tls_verify_host = p_verify_host;
String host_lower = conn_host.to_lower();
if (host_lower.begins_with("http://")) {
conn_host = conn_host.substr(7, conn_host.length() - 7);
} else if (host_lower.begins_with("https://")) {
- ssl = true;
+ tls = true;
conn_host = conn_host.substr(8, conn_host.length() - 8);
}
ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER);
if (conn_port < 0) {
- if (ssl) {
+ if (tls) {
conn_port = PORT_HTTPS;
} else {
conn_port = PORT_HTTP;
@@ -70,11 +70,11 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
connection = tcp_connection;
- if (ssl && https_proxy_port != -1) {
+ if (tls && https_proxy_port != -1) {
proxy_client.instantiate(); // Needs proxy negotiation.
server_host = https_proxy_host;
server_port = https_proxy_port;
- } else if (!ssl && http_proxy_port != -1) {
+ } else if (!tls && http_proxy_port != -1) {
server_host = http_proxy_host;
server_port = http_proxy_port;
} else {
@@ -94,6 +94,10 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
} else {
// Host contains hostname and needs to be resolved to IP.
resolving = IP::get_singleton()->resolve_hostname_queue_item(server_host);
+ if (resolving == IP::RESOLVER_INVALID_ID) {
+ status = STATUS_CANT_RESOLVE;
+ return ERR_CANT_RESOLVE;
+ }
status = STATUS_RESOLVING;
}
@@ -103,9 +107,9 @@ Error HTTPClientTCP::connect_to_host(const String &p_host, int p_port, bool p_ss
void HTTPClientTCP::set_connection(const Ref<StreamPeer> &p_connection) {
ERR_FAIL_COND_MSG(p_connection.is_null(), "Connection is not a reference to a valid StreamPeer object.");
- if (ssl) {
- ERR_FAIL_NULL_MSG(Object::cast_to<StreamPeerSSL>(p_connection.ptr()),
- "Connection is not a reference to a valid StreamPeerSSL object.");
+ if (tls) {
+ ERR_FAIL_NULL_MSG(Object::cast_to<StreamPeerTLS>(p_connection.ptr()),
+ "Connection is not a reference to a valid StreamPeerTLS object.");
}
if (connection == p_connection) {
@@ -152,7 +156,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<
}
String uri = p_url;
- if (!ssl && http_proxy_port != -1) {
+ if (!tls && http_proxy_port != -1) {
uri = vformat("http://%s:%d%s", conn_host, conn_port, p_url);
}
@@ -177,7 +181,7 @@ Error HTTPClientTCP::request(Method p_method, const String &p_url, const Vector<
}
}
if (add_host) {
- if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
+ if ((tls && conn_port == PORT_HTTPS) || (!tls && conn_port == PORT_HTTP)) {
// Don't append the standard ports.
request += "Host: " + conn_host + "\r\n";
} else {
@@ -312,7 +316,7 @@ Error HTTPClientTCP::poll() {
return OK;
} break;
case StreamPeerTCP::STATUS_CONNECTED: {
- if (ssl && proxy_client.is_valid()) {
+ if (tls && proxy_client.is_valid()) {
Error err = proxy_client->poll();
if (err == ERR_UNCONFIGURED) {
proxy_client->set_connection(tcp_connection);
@@ -353,42 +357,42 @@ Error HTTPClientTCP::poll() {
return ERR_CANT_CONNECT;
} break;
}
- } else if (ssl) {
- Ref<StreamPeerSSL> ssl;
+ } else if (tls) {
+ Ref<StreamPeerTLS> tls_conn;
if (!handshaking) {
- // Connect the StreamPeerSSL and start handshaking.
- ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
- ssl->set_blocking_handshake_enabled(false);
- Error err = ssl->connect_to_stream(tcp_connection, ssl_verify_host, conn_host);
+ // Connect the StreamPeerTLS and start handshaking.
+ tls_conn = Ref<StreamPeerTLS>(StreamPeerTLS::create());
+ tls_conn->set_blocking_handshake_enabled(false);
+ Error err = tls_conn->connect_to_stream(tcp_connection, tls_verify_host, conn_host);
if (err != OK) {
close();
- status = STATUS_SSL_HANDSHAKE_ERROR;
+ status = STATUS_TLS_HANDSHAKE_ERROR;
return ERR_CANT_CONNECT;
}
- connection = ssl;
+ connection = tls_conn;
handshaking = true;
} else {
- // We are already handshaking, which means we can use your already active SSL connection.
- ssl = static_cast<Ref<StreamPeerSSL>>(connection);
- if (ssl.is_null()) {
+ // We are already handshaking, which means we can use your already active TLS connection.
+ tls_conn = static_cast<Ref<StreamPeerTLS>>(connection);
+ if (tls_conn.is_null()) {
close();
- status = STATUS_SSL_HANDSHAKE_ERROR;
+ status = STATUS_TLS_HANDSHAKE_ERROR;
return ERR_CANT_CONNECT;
}
- ssl->poll(); // Try to finish the handshake.
+ tls_conn->poll(); // Try to finish the handshake.
}
- if (ssl->get_status() == StreamPeerSSL::STATUS_CONNECTED) {
+ if (tls_conn->get_status() == StreamPeerTLS::STATUS_CONNECTED) {
// Handshake has been successful.
handshaking = false;
ip_candidates.clear();
status = STATUS_CONNECTED;
return OK;
- } else if (ssl->get_status() != StreamPeerSSL::STATUS_HANDSHAKING) {
+ } else if (tls_conn->get_status() != StreamPeerTLS::STATUS_HANDSHAKING) {
// Handshake has failed.
close();
- status = STATUS_SSL_HANDSHAKE_ERROR;
+ status = STATUS_TLS_HANDSHAKE_ERROR;
return ERR_CANT_CONNECT;
}
// ... we will need to poll more for handshake to finish.
@@ -417,10 +421,10 @@ Error HTTPClientTCP::poll() {
case STATUS_BODY:
case STATUS_CONNECTED: {
// Check if we are still connected.
- if (ssl) {
- Ref<StreamPeerSSL> tmp = connection;
+ if (tls) {
+ Ref<StreamPeerTLS> tmp = connection;
tmp->poll();
- if (tmp->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
+ if (tmp->get_status() != StreamPeerTLS::STATUS_CONNECTED) {
status = STATUS_CONNECTION_ERROR;
return ERR_CONNECTION_ERROR;
}
@@ -544,7 +548,7 @@ Error HTTPClientTCP::poll() {
return ERR_UNCONFIGURED;
} break;
case STATUS_CONNECTION_ERROR:
- case STATUS_SSL_HANDSHAKE_ERROR: {
+ case STATUS_TLS_HANDSHAKE_ERROR: {
return ERR_CONNECTION_ERROR;
} break;
case STATUS_CANT_CONNECT: {
diff --git a/core/io/http_client_tcp.h b/core/io/http_client_tcp.h
index c10e0b1eca..744c15f7ab 100644
--- a/core/io/http_client_tcp.h
+++ b/core/io/http_client_tcp.h
@@ -46,8 +46,8 @@ private:
String http_proxy_host;
int https_proxy_port = -1; // Proxy server for https requests.
String https_proxy_host;
- bool ssl = false;
- bool ssl_verify_host = false;
+ bool tls = false;
+ bool tls_verify_host = false;
bool blocking = false;
bool handshaking = false;
bool head_request = false;
@@ -79,7 +79,7 @@ public:
Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const uint8_t *p_body, int p_body_size) override;
- Error connect_to_host(const String &p_host, int p_port = -1, bool p_ssl = false, bool p_verify_host = true) override;
+ Error connect_to_host(const String &p_host, int p_port = -1, bool p_tls = false, bool p_verify_host = true) override;
void set_connection(const Ref<StreamPeer> &p_connection) override;
Ref<StreamPeer> get_connection() const override;
void close() override;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 667bb893f5..33ccd032ad 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -473,7 +473,7 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p
uint8_t rgba[4] = { 0, 0, 0, 255 };
- if (read_gray) {
+ if constexpr (read_gray) {
rgba[0] = rofs[0];
rgba[1] = rofs[0];
rgba[2] = rofs[0];
@@ -483,11 +483,11 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p
}
}
- if (read_alpha || write_alpha) {
+ if constexpr (read_alpha || write_alpha) {
rgba[3] = read_alpha ? rofs[read_bytes] : 255;
}
- if (write_gray) {
+ if constexpr (write_gray) {
//TODO: not correct grayscale, should use fixed point version of actual weights
wofs[0] = uint8_t((uint16_t(rgba[0]) + uint16_t(rgba[1]) + uint16_t(rgba[2])) / 3);
} else {
@@ -496,7 +496,7 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p
}
}
- if (write_alpha) {
+ if constexpr (write_alpha) {
wofs[write_bytes] = rgba[3];
}
}
@@ -669,7 +669,7 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
double xfac = (double)width / p_dst_width;
double yfac = (double)height / p_dst_height;
// coordinates of source points and coefficients
- double ox, oy, dx, dy, k1, k2;
+ double ox, oy, dx, dy;
int ox1, oy1, ox2, oy2;
// destination pixel values
// width and height decreased by 1
@@ -700,7 +700,7 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
for (int n = -1; n < 3; n++) {
// get Y coefficient
- k1 = _bicubic_interp_kernel(dy - (double)n);
+ [[maybe_unused]] double k1 = _bicubic_interp_kernel(dy - (double)n);
oy2 = oy1 + n;
if (oy2 < 0) {
@@ -712,7 +712,7 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
for (int m = -1; m < 3; m++) {
// get X coefficient
- k2 = k1 * _bicubic_interp_kernel((double)m - dx);
+ [[maybe_unused]] double k2 = k1 * _bicubic_interp_kernel((double)m - dx);
ox2 = ox1 + m;
if (ox2 < 0) {
@@ -726,7 +726,7 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
const T *__restrict p = ((T *)p_src) + (oy2 * p_src_width + ox2) * CC;
for (int i = 0; i < CC; i++) {
- if (sizeof(T) == 2) { //half float
+ if constexpr (sizeof(T) == 2) { //half float
color[i] = Math::half_to_float(p[i]);
} else {
color[i] += p[i] * k2;
@@ -736,9 +736,9 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
}
for (int i = 0; i < CC; i++) {
- if (sizeof(T) == 1) { //byte
+ if constexpr (sizeof(T) == 1) { //byte
dst[i] = CLAMP(Math::fast_ftoi(color[i]), 0, 255);
- } else if (sizeof(T) == 2) { //half float
+ } else if constexpr (sizeof(T) == 2) { //half float
dst[i] = Math::make_half_float(color[i]);
} else {
dst[i] = color[i];
@@ -787,7 +787,7 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict
src_xofs_right *= CC;
for (uint32_t l = 0; l < CC; l++) {
- if (sizeof(T) == 1) { //uint8
+ if constexpr (sizeof(T) == 1) { //uint8
uint32_t p00 = p_src[y_ofs_up + src_xofs_left + l] << FRAC_BITS;
uint32_t p10 = p_src[y_ofs_up + src_xofs_right + l] << FRAC_BITS;
uint32_t p01 = p_src[y_ofs_down + src_xofs_left + l] << FRAC_BITS;
@@ -798,7 +798,7 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict
uint32_t interp = interp_up + (((interp_down - interp_up) * src_yofs_frac) >> FRAC_BITS);
interp >>= FRAC_BITS;
p_dst[i * p_dst_width * CC + j * CC + l] = uint8_t(interp);
- } else if (sizeof(T) == 2) { //half float
+ } else if constexpr (sizeof(T) == 2) { //half float
float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS);
float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS);
@@ -815,7 +815,7 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict
float interp = interp_up + ((interp_down - interp_up) * yofs_frac);
dst[i * p_dst_width * CC + j * CC + l] = Math::make_half_float(interp);
- } else if (sizeof(T) == 4) { //float
+ } else if constexpr (sizeof(T) == 4) { //float
float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS);
float yofs_frac = float(src_yofs_frac) / (1 << FRAC_BITS);
@@ -906,7 +906,7 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict
const T *__restrict src_data = ((const T *)p_src) + (buffer_y * src_width + target_x) * CC;
for (uint32_t i = 0; i < CC; i++) {
- if (sizeof(T) == 2) { //half float
+ if constexpr (sizeof(T) == 2) { //half float
pixel[i] += Math::half_to_float(src_data[i]) * lanczos_val;
} else {
pixel[i] += src_data[i] * lanczos_val;
@@ -963,9 +963,9 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict
for (uint32_t i = 0; i < CC; i++) {
pixel[i] /= weight;
- if (sizeof(T) == 1) { //byte
+ if constexpr (sizeof(T) == 1) { //byte
dst_data[i] = CLAMP(Math::fast_ftoi(pixel[i]), 0, 255);
- } else if (sizeof(T) == 2) { //half float
+ } else if constexpr (sizeof(T) == 2) { //half float
dst_data[i] = Math::make_half_float(pixel[i]);
} else { // float
dst_data[i] = pixel[i];
@@ -1011,7 +1011,7 @@ void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) {
}
void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
- ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use create() or create_from_data() first.");
+ ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use set_data() first.");
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats.");
bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */;
@@ -1046,7 +1046,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
}
bool interpolate_mipmaps = mipmap_aware && mip1 != mip2;
if (interpolate_mipmaps) {
- dst2.create(p_width, p_height, false, format);
+ dst2.initialize_data(p_width, p_height, false, format);
}
bool had_mipmaps = mipmaps;
@@ -1370,78 +1370,126 @@ void Image::crop(int p_width, int p_height) {
void Image::rotate_90(ClockDirection p_direction) {
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
- ERR_FAIL_COND_MSG(width <= 1, "The Image width specified (" + itos(width) + " pixels) must be greater than 1 pixels.");
- ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
-
- int saved_width = height;
- int saved_height = width;
-
- if (width != height) {
- int n = MAX(width, height);
- resize(n, n, INTERPOLATE_NEAREST);
- }
+ ERR_FAIL_COND_MSG(width <= 0, "The Image width specified (" + itos(width) + " pixels) must be greater than 0 pixels.");
+ ERR_FAIL_COND_MSG(height <= 0, "The Image height specified (" + itos(height) + " pixels) must be greater than 0 pixels.");
bool used_mipmaps = has_mipmaps();
if (used_mipmaps) {
clear_mipmaps();
}
+ // In-place 90 degrees rotation by following the permutation cycles.
{
- uint8_t *w = data.ptrw();
- uint8_t src[16];
- uint8_t dst[16];
+ // Explanation by example (clockwise):
+ //
+ // abc da
+ // def -> eb
+ // fc
+ //
+ // In memory:
+ // 012345 012345
+ // abcdef -> daebfc
+ //
+ // Permutation cycles:
+ // (0 --a--> 1 --b--> 3 --d--> 0)
+ // (2 --c--> 5 --f--> 4 --e--> 2)
+ //
+ // Applying cycles (backwards):
+ // 0->s s=a (store)
+ // 3->0 abcdef -> dbcdef
+ // 1->3 dbcdef -> dbcbef
+ // s->1 dbcbef -> dacbef
+ //
+ // 2->s s=c
+ // 4->2 dacbef -> daebef
+ // 5->4 daebef -> daebff
+ // s->5 daebff -> daebfc
+
+ const int w = width;
+ const int h = height;
+ const int size = w * h;
+
+ uint8_t *data_ptr = data.ptrw();
uint32_t pixel_size = get_format_pixel_size(format);
- // Flip.
+ uint8_t single_pixel_buffer[16];
- if (p_direction == CLOCKWISE) {
- for (int y = 0; y < height / 2; y++) {
- for (int x = 0; x < width; x++) {
- _get_pixelb(x, y, pixel_size, w, src);
- _get_pixelb(x, height - y - 1, pixel_size, w, dst);
+#define PREV_INDEX_IN_CYCLE(index) (p_direction == CLOCKWISE) ? ((h - 1 - (index % h)) * w + (index / h)) : ((index % h) * w + (w - 1 - (index / h)))
- _put_pixelb(x, height - y - 1, pixel_size, w, src);
- _put_pixelb(x, y, pixel_size, w, dst);
+ if (w == h) { // Square case, 4-length cycles only (plus irrelevant thus skipped 1-length cycle in the middle for odd-sized squares).
+ for (int y = 0; y < h / 2; y++) {
+ for (int x = 0; x < (w + 1) / 2; x++) {
+ int current = y * w + x;
+ memcpy(single_pixel_buffer, data_ptr + current * pixel_size, pixel_size);
+ for (int i = 0; i < 3; i++) {
+ int prev = PREV_INDEX_IN_CYCLE(current);
+ memcpy(data_ptr + current * pixel_size, data_ptr + prev * pixel_size, pixel_size);
+ current = prev;
+ }
+ memcpy(data_ptr + current * pixel_size, single_pixel_buffer, pixel_size);
}
}
- } else {
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width / 2; x++) {
- _get_pixelb(x, y, pixel_size, w, src);
- _get_pixelb(width - x - 1, y, pixel_size, w, dst);
+ } else { // Rectangular case (w != h), kinda unpredictable cycles.
+ int permuted_pixels_count = 0;
+
+ for (int i = 0; i < size; i++) {
+ int prev = PREV_INDEX_IN_CYCLE(i);
+ if (prev == i) {
+ // 1-length cycle, pixel remains at the same index.
+ permuted_pixels_count++;
+ continue;
+ }
- _put_pixelb(width - x - 1, y, pixel_size, w, src);
- _put_pixelb(x, y, pixel_size, w, dst);
+ // Check whether we already processed this cycle.
+ // We iterate over it and if we'll find an index smaller than `i` then we already
+ // processed this cycle because we always start at the smallest index in the cycle.
+ // TODO: Improve this naive approach, can be done better.
+ while (prev > i) {
+ prev = PREV_INDEX_IN_CYCLE(prev);
+ }
+ if (prev < i) {
+ continue;
}
- }
- }
- // Transpose.
+ // Save the in-cycle pixel with the smallest index (`i`).
+ memcpy(single_pixel_buffer, data_ptr + i * pixel_size, pixel_size);
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- if (x < y) {
- _get_pixelb(x, y, pixel_size, w, src);
- _get_pixelb(y, x, pixel_size, w, dst);
+ // Overwrite pixels one by one by the preceding pixel in the cycle.
+ int current = i;
+ prev = PREV_INDEX_IN_CYCLE(current);
+ while (prev != i) {
+ memcpy(data_ptr + current * pixel_size, data_ptr + prev * pixel_size, pixel_size);
+ permuted_pixels_count++;
- _put_pixelb(y, x, pixel_size, w, src);
- _put_pixelb(x, y, pixel_size, w, dst);
+ current = prev;
+ prev = PREV_INDEX_IN_CYCLE(current);
+ };
+
+ // Overwrite the remaining pixel in the cycle by the saved pixel with the smallest index.
+ memcpy(data_ptr + current * pixel_size, single_pixel_buffer, pixel_size);
+ permuted_pixels_count++;
+
+ if (permuted_pixels_count == size) {
+ break;
}
}
+
+ width = h;
+ height = w;
}
+
+#undef PREV_INDEX_IN_CYCLE
}
- if (saved_width != saved_height) {
- resize(saved_width, saved_height, INTERPOLATE_NEAREST);
- } else if (used_mipmaps) {
+ if (used_mipmaps) {
generate_mipmaps();
}
}
void Image::rotate_180() {
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot rotate in compressed or custom image formats.");
- ERR_FAIL_COND_MSG(width <= 1, "The Image width specified (" + itos(width) + " pixels) must be greater than 1 pixels.");
- ERR_FAIL_COND_MSG(height <= 1, "The Image height specified (" + itos(height) + " pixels) must be greater than 1 pixels.");
+ ERR_FAIL_COND_MSG(width <= 0, "The Image width specified (" + itos(width) + " pixels) must be greater than 0 pixels.");
+ ERR_FAIL_COND_MSG(height <= 0, "The Image height specified (" + itos(height) + " pixels) must be greater than 0 pixels.");
bool used_mipmaps = has_mipmaps();
if (used_mipmaps) {
@@ -1449,19 +1497,21 @@ void Image::rotate_180() {
}
{
- uint8_t *w = data.ptrw();
- uint8_t src[16];
- uint8_t dst[16];
+ uint8_t *data_ptr = data.ptrw();
uint32_t pixel_size = get_format_pixel_size(format);
- for (int y = 0; y < height / 2; y++) {
- for (int x = 0; x < width; x++) {
- _get_pixelb(x, y, pixel_size, w, src);
- _get_pixelb(width - x - 1, height - y - 1, pixel_size, w, dst);
+ uint8_t single_pixel_buffer[16];
- _put_pixelb(width - x - 1, height - y - 1, pixel_size, w, src);
- _put_pixelb(x, y, pixel_size, w, dst);
- }
+ uint8_t *from_begin_ptr = data_ptr;
+ uint8_t *from_end_ptr = data_ptr + (width * height - 1) * pixel_size;
+
+ while (from_begin_ptr < from_end_ptr) {
+ memcpy(single_pixel_buffer, from_begin_ptr, pixel_size);
+ memcpy(from_begin_ptr, from_end_ptr, pixel_size);
+ memcpy(from_end_ptr, single_pixel_buffer, pixel_size);
+
+ from_begin_ptr += pixel_size;
+ from_end_ptr -= pixel_size;
}
}
@@ -2045,9 +2095,7 @@ Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, con
uint8_t* wr = imgdata.ptrw();
memcpy(wr.ptr(), ptr, size);
wr = uint8_t*();
- Ref<Image> im;
- im.instantiate();
- im->create(w, h, false, format, imgdata);
+ Ref<Image> im = Image::create_from_data(w, h, false, format, imgdata);
im->save_png("res://mipmap_" + itos(i) + ".png");
}
#endif
@@ -2080,7 +2128,25 @@ Vector<uint8_t> Image::get_data() const {
return data;
}
-void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
+Ref<Image> Image::create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
+ Ref<Image> image;
+ image.instantiate();
+ image->initialize_data(p_width, p_height, p_use_mipmaps, p_format);
+ return image;
+}
+
+Ref<Image> Image::create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) {
+ Ref<Image> image;
+ image.instantiate();
+ image->initialize_data(p_width, p_height, p_use_mipmaps, p_format, p_data);
+ return image;
+}
+
+void Image::set_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) {
+ initialize_data(p_width, p_height, p_use_mipmaps, p_format, p_data);
+}
+
+void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
ERR_FAIL_COND_MSG(p_width <= 0, "The Image width specified (" + itos(p_width) + " pixels) must be greater than 0 pixels.");
ERR_FAIL_COND_MSG(p_height <= 0, "The Image height specified (" + itos(p_height) + " pixels) must be greater than 0 pixels.");
ERR_FAIL_COND_MSG(p_width > MAX_WIDTH,
@@ -2106,7 +2172,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
format = p_format;
}
-void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) {
+void Image::initialize_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) {
ERR_FAIL_COND_MSG(p_width <= 0, "The Image width specified (" + itos(p_width) + " pixels) must be greater than 0 pixels.");
ERR_FAIL_COND_MSG(p_height <= 0, "The Image height specified (" + itos(p_height) + " pixels) must be greater than 0 pixels.");
ERR_FAIL_COND_MSG(p_width > MAX_WIDTH,
@@ -2144,7 +2210,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
mipmaps = p_use_mipmaps;
}
-void Image::create(const char **p_xpm) {
+void Image::initialize_data(const char **p_xpm) {
int size_width = 0;
int size_height = 0;
int pixelchars = 0;
@@ -2259,7 +2325,7 @@ void Image::create(const char **p_xpm) {
}
if (line == colormap_size) {
status = READING_PIXELS;
- create(size_width, size_height, false, has_alpha ? FORMAT_RGBA8 : FORMAT_RGB8);
+ initialize_data(size_width, size_height, false, has_alpha ? FORMAT_RGBA8 : FORMAT_RGB8);
data_write = data.ptrw();
pixel_size = has_alpha ? 4 : 3;
}
@@ -2594,7 +2660,7 @@ Image::Image(const char **p_xpm) {
mipmaps = false;
format = FORMAT_L8;
- create(p_xpm);
+ initialize_data(p_xpm);
}
Image::Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
@@ -2603,7 +2669,7 @@ Image::Image(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
mipmaps = p_use_mipmaps;
format = FORMAT_L8;
- create(p_width, p_height, p_use_mipmaps, p_format);
+ initialize_data(p_width, p_height, p_use_mipmaps, p_format);
}
Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const Vector<uint8_t> &p_data) {
@@ -2612,7 +2678,7 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const V
mipmaps = p_mipmaps;
format = FORMAT_L8;
- create(p_width, p_height, p_mipmaps, p_format, p_data);
+ initialize_data(p_width, p_height, p_mipmaps, p_format, p_data);
}
Rect2i Image::get_used_rect() const {
@@ -2655,9 +2721,9 @@ Rect2i Image::get_used_rect() const {
}
}
-Ref<Image> Image::get_rect(const Rect2i &p_area) const {
- Ref<Image> img = memnew(Image(p_area.size.x, p_area.size.y, mipmaps, format));
- img->blit_rect(Ref<Image>((Image *)this), p_area, Point2i(0, 0));
+Ref<Image> Image::get_region(const Rect2i &p_region) const {
+ Ref<Image> img = memnew(Image(p_region.size.x, p_region.size.y, mipmaps, format));
+ img->blit_rect(Ref<Image>((Image *)this), p_region, Point2i(0, 0));
return img;
}
@@ -2706,7 +2772,7 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const P
Rect2i src_rect;
Rect2i dest_rect;
_get_clipped_src_and_dest_rects(p_src, p_src_rect, p_dest, src_rect, dest_rect);
- if (src_rect.has_no_area() || dest_rect.has_no_area()) {
+ if (!src_rect.has_area() || !dest_rect.has_area()) {
return;
}
@@ -2752,7 +2818,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co
Rect2i src_rect;
Rect2i dest_rect;
_get_clipped_src_and_dest_rects(p_src, p_src_rect, p_dest, src_rect, dest_rect);
- if (src_rect.has_no_area() || dest_rect.has_no_area()) {
+ if (!src_rect.has_area() || !dest_rect.has_area()) {
return;
}
@@ -2797,7 +2863,7 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const
Rect2i src_rect;
Rect2i dest_rect;
_get_clipped_src_and_dest_rects(p_src, p_src_rect, p_dest, src_rect, dest_rect);
- if (src_rect.has_no_area() || dest_rect.has_no_area()) {
+ if (!src_rect.has_area() || !dest_rect.has_area()) {
return;
}
@@ -2837,7 +2903,7 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c
Rect2i src_rect;
Rect2i dest_rect;
_get_clipped_src_and_dest_rects(p_src, p_src_rect, p_dest, src_rect, dest_rect);
- if (src_rect.has_no_area() || dest_rect.has_no_area()) {
+ if (!src_rect.has_area() || !dest_rect.has_area()) {
return;
}
@@ -2881,6 +2947,9 @@ void Image::_repeat_pixel_over_subsequent_memory(uint8_t *p_pixel, int p_pixel_s
}
void Image::fill(const Color &p_color) {
+ if (data.size() == 0) {
+ return;
+ }
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot fill in compressed or custom image formats.");
uint8_t *dst_data_ptr = data.ptrw();
@@ -2894,10 +2963,13 @@ void Image::fill(const Color &p_color) {
}
void Image::fill_rect(const Rect2i &p_rect, const Color &p_color) {
+ if (data.size() == 0) {
+ return;
+ }
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot fill rect in compressed or custom image formats.");
Rect2i r = Rect2i(0, 0, width, height).intersection(p_rect.abs());
- if (r.has_no_area()) {
+ if (!r.has_area()) {
return;
}
@@ -2968,7 +3040,7 @@ void Image::_set_data(const Dictionary &p_data) {
ERR_FAIL_COND(ddformat == FORMAT_MAX);
- create(dwidth, dheight, dmipmaps, ddformat, ddata);
+ initialize_data(dwidth, dheight, dmipmaps, ddformat, ddata);
}
Dictionary Image::_get_data() const {
@@ -3331,8 +3403,9 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_mipmaps", "renormalize"), &Image::generate_mipmaps, DEFVAL(false));
ClassDB::bind_method(D_METHOD("clear_mipmaps"), &Image::clear_mipmaps);
- ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::create_empty);
- ClassDB::bind_method(D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::create_from_data);
+ ClassDB::bind_static_method("Image", D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::create_empty);
+ ClassDB::bind_static_method("Image", D_METHOD("create_from_data", "width", "height", "use_mipmaps", "format", "data"), &Image::create_from_data);
+ ClassDB::bind_method(D_METHOD("set_data", "width", "height", "use_mipmaps", "format", "data"), &Image::set_data);
ClassDB::bind_method(D_METHOD("is_empty"), &Image::is_empty);
@@ -3376,7 +3449,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("fill_rect", "rect", "color"), &Image::fill_rect);
ClassDB::bind_method(D_METHOD("get_used_rect"), &Image::get_used_rect);
- ClassDB::bind_method(D_METHOD("get_rect", "rect"), &Image::get_rect);
+ ClassDB::bind_method(D_METHOD("get_region", "region"), &Image::get_region);
ClassDB::bind_method(D_METHOD("copy_from", "src"), &Image::copy_internals_from);
@@ -3504,9 +3577,7 @@ Ref<Image> Image::rgbe_to_srgb() {
ERR_FAIL_COND_V(format != FORMAT_RGBE9995, Ref<Image>());
- Ref<Image> new_image;
- new_image.instantiate();
- new_image->create(width, height, false, Image::FORMAT_RGB8);
+ Ref<Image> new_image = create_empty(width, height, false, Image::FORMAT_RGB8);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
@@ -3547,6 +3618,7 @@ Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const {
void Image::bump_map_to_normal_map(float bump_scale) {
ERR_FAIL_COND(!_can_modify(format));
+ clear_mipmaps();
convert(Image::FORMAT_RF);
Vector<uint8_t> result_image; //rgba output
@@ -3770,6 +3842,19 @@ void Image::convert_ra_rgba8_to_rg() {
}
}
+void Image::convert_rgba8_to_bgra8() {
+ ERR_FAIL_COND(format != FORMAT_RGBA8);
+ ERR_FAIL_COND(!data.size());
+
+ int s = data.size();
+ uint8_t *w = data.ptrw();
+ for (int i = 0; i < s; i += 4) {
+ uint8_t r = w[i];
+ w[i] = w[i + 2]; // Swap R to B
+ w[i + 2] = r; // Swap B to R
+ }
+}
+
Error Image::_load_from_buffer(const Vector<uint8_t> &p_array, ImageMemLoadFunc p_loader) {
int buffer_size = p_array.size();
@@ -3913,15 +3998,15 @@ Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool
double image_metric_max, image_metric_mean, image_metric_mean_squared, image_metric_root_mean_squared, image_metric_peak_snr = 0.0;
const bool average_component_error = true;
- const uint32_t width = MIN(compared_image->get_width(), source_image->get_width());
- const uint32_t height = MIN(compared_image->get_height(), source_image->get_height());
+ const uint32_t w = MIN(compared_image->get_width(), source_image->get_width());
+ const uint32_t h = MIN(compared_image->get_height(), source_image->get_height());
// Histogram approach originally due to Charles Bloom.
double hist[256];
memset(hist, 0, sizeof(hist));
- for (uint32_t y = 0; y < height; y++) {
- for (uint32_t x = 0; x < width; x++) {
+ for (uint32_t y = 0; y < h; y++) {
+ for (uint32_t x = 0; x < w; x++) {
const Color color_a = compared_image->get_pixel(x, y);
const Color color_b = source_image->get_pixel(x, y);
@@ -3966,7 +4051,7 @@ Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool
}
// See http://richg42.blogspot.com/2016/09/how-to-compute-psnr-from-old-berkeley.html
- double total_values = width * height;
+ double total_values = w * h;
if (average_component_error) {
total_values *= 4;
diff --git a/core/io/image.h b/core/io/image.h
index 0e37e78c76..8bcee4404b 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -290,12 +290,12 @@ public:
void normalize(); //for normal maps
/**
- * Create a new image 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 create(int p_width, int p_height, bool p_use_mipmaps, Format p_format);
- void create(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data);
+ 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);
- void create(const char **p_xpm);
/**
* returns true when the image is empty (0,0) in size
*/
@@ -314,13 +314,9 @@ public:
Error save_webp(const String &p_path, const bool p_lossy = false, const float p_quality = 0.75f) const;
Vector<uint8_t> save_webp_to_buffer(const bool p_lossy = false, const float p_quality = 0.75f) const;
- void create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
- create(p_width, p_height, p_use_mipmaps, p_format);
- }
-
- void create_from_data(int p_width, int p_height, bool p_use_mipmaps, Format p_format, const Vector<uint8_t> &p_data) {
- create(p_width, p_height, p_use_mipmaps, p_format, p_data);
- }
+ static Ref<Image> create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format);
+ 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
@@ -393,7 +389,7 @@ public:
void fill_rect(const Rect2i &p_rect, const Color &p_color);
Rect2i get_used_rect() const;
- Ref<Image> get_rect(const Rect2i &p_area) const;
+ Ref<Image> get_region(const Rect2i &p_area) const;
static void set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels));
static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels));
@@ -407,6 +403,7 @@ public:
void convert_rg_to_ra_rgba8();
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);
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp
index d09697b951..6c00ca4b67 100644
--- a/core/io/image_loader.cpp
+++ b/core/io/image_loader.cpp
@@ -32,6 +32,12 @@
#include "core/string/print_string.h"
+void ImageFormatLoader::_bind_methods() {
+ BIND_BITFIELD_FLAG(FLAG_NONE);
+ BIND_BITFIELD_FLAG(FLAG_FORCE_LINEAR);
+ BIND_BITFIELD_FLAG(FLAG_CONVERT_COLORS);
+}
+
bool ImageFormatLoader::recognize(const String &p_extension) const {
List<String> extensions;
get_recognized_extensions(&extensions);
@@ -44,7 +50,37 @@ bool ImageFormatLoader::recognize(const String &p_extension) const {
return false;
}
-Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, uint32_t p_flags, float p_scale) {
+Error ImageFormatLoaderExtension::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
+ Error err = ERR_UNAVAILABLE;
+ GDVIRTUAL_CALL(_load_image, p_image, p_fileaccess, p_flags, p_scale, err);
+ return err;
+}
+
+void ImageFormatLoaderExtension::get_recognized_extensions(List<String> *p_extension) const {
+ PackedStringArray ext;
+ if (GDVIRTUAL_CALL(_get_recognized_extensions, ext)) {
+ for (int i = 0; i < ext.size(); i++) {
+ p_extension->push_back(ext[i]);
+ }
+ }
+}
+
+void ImageFormatLoaderExtension::add_format_loader() {
+ ImageLoader::add_image_format_loader(this);
+}
+
+void ImageFormatLoaderExtension::remove_format_loader() {
+ ImageLoader::remove_image_format_loader(this);
+}
+
+void ImageFormatLoaderExtension::_bind_methods() {
+ GDVIRTUAL_BIND(_get_recognized_extensions);
+ GDVIRTUAL_BIND(_load_image, "image", "fileaccess", "flags", "scale");
+ ClassDB::bind_method(D_METHOD("add_format_loader"), &ImageFormatLoaderExtension::add_format_loader);
+ ClassDB::bind_method(D_METHOD("remove_format_loader"), &ImageFormatLoaderExtension::remove_format_loader);
+}
+
+Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "It's not a reference to a valid Image object.");
Ref<FileAccess> f = p_custom;
@@ -60,7 +96,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess>
if (!loader[i]->recognize(extension)) {
continue;
}
- Error err = loader[i]->load_image(p_image, f, p_flags, p_scale);
+ Error err = loader.write[i]->load_image(p_image, f, p_flags, p_scale);
if (err != OK) {
ERR_PRINT("Error loading image: " + p_file);
}
@@ -79,7 +115,7 @@ void ImageLoader::get_recognized_extensions(List<String> *p_extensions) {
}
}
-ImageFormatLoader *ImageLoader::recognize(const String &p_extension) {
+Ref<ImageFormatLoader> ImageLoader::recognize(const String &p_extension) {
for (int i = 0; i < loader.size(); i++) {
if (loader[i]->recognize(p_extension)) {
return loader[i];
@@ -89,17 +125,17 @@ ImageFormatLoader *ImageLoader::recognize(const String &p_extension) {
return nullptr;
}
-Vector<ImageFormatLoader *> ImageLoader::loader;
+Vector<Ref<ImageFormatLoader>> ImageLoader::loader;
-void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) {
+void ImageLoader::add_image_format_loader(Ref<ImageFormatLoader> p_loader) {
loader.push_back(p_loader);
}
-void ImageLoader::remove_image_format_loader(ImageFormatLoader *p_loader) {
+void ImageLoader::remove_image_format_loader(Ref<ImageFormatLoader> p_loader) {
loader.erase(p_loader);
}
-const Vector<ImageFormatLoader *> &ImageLoader::get_image_format_loaders() {
+const Vector<Ref<ImageFormatLoader>> &ImageLoader::get_image_format_loaders() {
return loader;
}
@@ -152,7 +188,7 @@ Ref<Resource> ResourceFormatLoaderImage::load(const String &p_path, const String
Ref<Image> image;
image.instantiate();
- Error err = ImageLoader::loader[idx]->load_image(image, f);
+ Error err = ImageLoader::loader.write[idx]->load_image(image, f);
if (err != OK) {
if (r_error) {
diff --git a/core/io/image_loader.h b/core/io/image_loader.h
index cb64d2310e..f70fdf22aa 100644
--- a/core/io/image_loader.h
+++ b/core/io/image_loader.h
@@ -31,46 +31,74 @@
#ifndef IMAGE_LOADER_H
#define IMAGE_LOADER_H
+#include "core/core_bind.h"
#include "core/io/file_access.h"
#include "core/io/image.h"
#include "core/io/resource_loader.h"
+#include "core/object/gdvirtual.gen.inc"
#include "core/string/ustring.h"
#include "core/templates/list.h"
+#include "core/variant/binder_common.h"
class ImageLoader;
-class ImageFormatLoader {
+class ImageFormatLoader : public RefCounted {
+ GDCLASS(ImageFormatLoader, RefCounted);
+
friend class ImageLoader;
friend class ResourceFormatLoaderImage;
-protected:
- virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, uint32_t p_flags = (uint32_t)FLAG_NONE, float p_scale = 1.0) = 0;
- virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
- bool recognize(const String &p_extension) const;
-
public:
enum LoaderFlags {
FLAG_NONE = 0,
FLAG_FORCE_LINEAR = 1,
+ FLAG_CONVERT_COLORS = 2,
};
+protected:
+ static void _bind_methods();
+
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags = FLAG_NONE, float p_scale = 1.0) = 0;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
+ bool recognize(const String &p_extension) const;
+
+public:
virtual ~ImageFormatLoader() {}
};
+VARIANT_BITFIELD_CAST(ImageFormatLoader::LoaderFlags);
+
+class ImageFormatLoaderExtension : public ImageFormatLoader {
+ GDCLASS(ImageFormatLoaderExtension, ImageFormatLoader);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags = FLAG_NONE, float p_scale = 1.0) override;
+ virtual void get_recognized_extensions(List<String> *p_extensions) const override;
+
+ void add_format_loader();
+ void remove_format_loader();
+
+ GDVIRTUAL0RC(PackedStringArray, _get_recognized_extensions);
+ GDVIRTUAL4R(Error, _load_image, Ref<Image>, Ref<FileAccess>, BitField<ImageFormatLoader::LoaderFlags>, float);
+};
+
class ImageLoader {
- static Vector<ImageFormatLoader *> loader;
+ static Vector<Ref<ImageFormatLoader>> loader;
friend class ResourceFormatLoaderImage;
protected:
public:
- static Error load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), uint32_t p_flags = (uint32_t)ImageFormatLoader::FLAG_NONE, float p_scale = 1.0);
+ static Error load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), BitField<ImageFormatLoader::LoaderFlags> p_flags = ImageFormatLoader::FLAG_NONE, float p_scale = 1.0);
static void get_recognized_extensions(List<String> *p_extensions);
- static ImageFormatLoader *recognize(const String &p_extension);
+ static Ref<ImageFormatLoader> recognize(const String &p_extension);
- static void add_image_format_loader(ImageFormatLoader *p_loader);
- static void remove_image_format_loader(ImageFormatLoader *p_loader);
+ static void add_image_format_loader(Ref<ImageFormatLoader> p_loader);
+ static void remove_image_format_loader(Ref<ImageFormatLoader> p_loader);
- static const Vector<ImageFormatLoader *> &get_image_format_loaders();
+ static const Vector<Ref<ImageFormatLoader>> &get_image_format_loaders();
static void cleanup();
};
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 91500ff3d5..7e267d35d4 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -56,6 +56,8 @@ String JSON::_make_indent(const String &p_indent, int p_size) {
}
String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision) {
+ ERR_FAIL_COND_V_MSG(p_cur_indent > Variant::MAX_RECURSION_DEPTH, "...", "JSON structure is too deep. Bailing.");
+
String colon = ":";
String end_statement = "";
@@ -357,17 +359,22 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to
return ERR_PARSE_ERROR;
}
-Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) {
+Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) {
+ if (p_depth > Variant::MAX_RECURSION_DEPTH) {
+ r_err_str = "JSON structure is too deep. Bailing.";
+ return ERR_OUT_OF_MEMORY;
+ }
+
if (token.type == TK_CURLY_BRACKET_OPEN) {
Dictionary d;
- Error err = _parse_object(d, p_str, index, p_len, line, r_err_str);
+ Error err = _parse_object(d, p_str, index, p_len, line, p_depth + 1, r_err_str);
if (err) {
return err;
}
value = d;
} else if (token.type == TK_BRACKET_OPEN) {
Array a;
- Error err = _parse_array(a, p_str, index, p_len, line, r_err_str);
+ Error err = _parse_array(a, p_str, index, p_len, line, p_depth + 1, r_err_str);
if (err) {
return err;
}
@@ -396,7 +403,7 @@ Error JSON::_parse_value(Variant &value, Token &token, const char32_t *p_str, in
return OK;
}
-Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) {
+Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) {
Token token;
bool need_comma = false;
@@ -421,7 +428,7 @@ Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_
}
Variant v;
- err = _parse_value(v, token, p_str, index, p_len, line, r_err_str);
+ err = _parse_value(v, token, p_str, index, p_len, line, p_depth, r_err_str);
if (err) {
return err;
}
@@ -434,7 +441,7 @@ Error JSON::_parse_array(Array &array, const char32_t *p_str, int &index, int p_
return ERR_PARSE_ERROR;
}
-Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str) {
+Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str) {
bool at_key = true;
String key;
Token token;
@@ -483,7 +490,7 @@ Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index,
}
Variant v;
- err = _parse_value(v, token, p_str, index, p_len, line, r_err_str);
+ err = _parse_value(v, token, p_str, index, p_len, line, p_depth, r_err_str);
if (err) {
return err;
}
@@ -497,6 +504,10 @@ Error JSON::_parse_object(Dictionary &object, const char32_t *p_str, int &index,
return ERR_PARSE_ERROR;
}
+void JSON::set_data(const Variant &p_data) {
+ data = p_data;
+}
+
Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) {
const char32_t *str = p_json.ptr();
int idx = 0;
@@ -510,7 +521,7 @@ Error JSON::_parse_string(const String &p_json, Variant &r_ret, String &r_err_st
return err;
}
- err = _parse_value(r_ret, token, str, idx, len, r_err_line, r_err_str);
+ err = _parse_value(r_ret, token, str, idx, len, r_err_line, 0, r_err_str);
// Check if EOF is reached
// or it's a type of the next token.
@@ -557,6 +568,88 @@ void JSON::_bind_methods() {
ClassDB::bind_method(D_METHOD("parse", "json_string"), &JSON::parse);
ClassDB::bind_method(D_METHOD("get_data"), &JSON::get_data);
+ ClassDB::bind_method(D_METHOD("set_data", "data"), &JSON::set_data);
ClassDB::bind_method(D_METHOD("get_error_line"), &JSON::get_error_line);
ClassDB::bind_method(D_METHOD("get_error_message"), &JSON::get_error_message);
+
+ ADD_PROPERTY(PropertyInfo(Variant::NIL, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), "set_data", "get_data"); // Ensures that it can be serialized as binary.
+}
+
+////
+
+////////////
+
+Ref<Resource> ResourceFormatLoaderJSON::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ if (r_error) {
+ *r_error = ERR_FILE_CANT_OPEN;
+ }
+
+ if (!FileAccess::exists(p_path)) {
+ *r_error = ERR_FILE_NOT_FOUND;
+ return Ref<Resource>();
+ }
+
+ Ref<JSON> json;
+ json.instantiate();
+
+ Error err = json->parse(FileAccess::get_file_as_string(p_path));
+ if (err != OK) {
+ if (r_error) {
+ *r_error = err;
+ }
+ ERR_PRINT("Error parsing JSON file at '" + p_path + "', on line " + itos(json->get_error_line()) + ": " + json->get_error_message());
+ return Ref<Resource>();
+ }
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return json;
+}
+
+void ResourceFormatLoaderJSON::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("json");
+}
+
+bool ResourceFormatLoaderJSON::handles_type(const String &p_type) const {
+ return (p_type == "JSON");
+}
+
+String ResourceFormatLoaderJSON::get_resource_type(const String &p_path) const {
+ String el = p_path.get_extension().to_lower();
+ if (el == "json") {
+ return "JSON";
+ }
+ return "";
+}
+
+Error ResourceFormatSaverJSON::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
+ Ref<JSON> json = p_resource;
+ ERR_FAIL_COND_V(json.is_null(), ERR_INVALID_PARAMETER);
+
+ String source = JSON::stringify(json->get_data(), "\t", false, true);
+
+ Error err;
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+
+ ERR_FAIL_COND_V_MSG(err, err, "Cannot save json '" + p_path + "'.");
+
+ file->store_string(source);
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ return ERR_CANT_CREATE;
+ }
+
+ return OK;
+}
+
+void ResourceFormatSaverJSON::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
+ Ref<JSON> json = p_resource;
+ if (json.is_valid()) {
+ p_extensions->push_back("json");
+ }
+}
+
+bool ResourceFormatSaverJSON::recognize(const Ref<Resource> &p_resource) const {
+ return p_resource->get_class_name() == "JSON"; //only json, not inherited
}
diff --git a/core/io/json.h b/core/io/json.h
index 840b1cc08a..829a5f922b 100644
--- a/core/io/json.h
+++ b/core/io/json.h
@@ -31,7 +31,9 @@
#ifndef JSON_H
#define JSON_H
-#include "core/object/ref_counted.h"
+#include "core/io/resource.h"
+#include "core/io/resource_loader.h"
+#include "core/io/resource_saver.h"
#include "core/variant/variant.h"
class JSON : public RefCounted {
@@ -72,9 +74,9 @@ class JSON : public RefCounted {
static String _make_indent(const String &p_indent, int p_size);
static String _stringify(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, HashSet<const void *> &p_markers, bool p_full_precision = false);
static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str);
- static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
- static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
- static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str);
+ static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
+ static Error _parse_array(Array &array, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
+ static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, int p_depth, String &r_err_str);
static Error _parse_string(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line);
protected:
@@ -86,8 +88,24 @@ public:
static Variant parse_string(const String &p_json_string);
inline Variant get_data() const { return data; }
+ void set_data(const Variant &p_data);
inline int get_error_line() const { return err_line; }
inline String get_error_message() const { return err_str; }
};
+class ResourceFormatLoaderJSON : public ResourceFormatLoader {
+public:
+ virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual bool handles_type(const String &p_type) const;
+ virtual String get_resource_type(const String &p_path) const;
+};
+
+class ResourceFormatSaverJSON : public ResourceFormatSaver {
+public:
+ virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
+ virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+ virtual bool recognize(const Ref<Resource> &p_resource) const;
+};
+
#endif // JSON_H
diff --git a/core/io/logger.cpp b/core/io/logger.cpp
index b0f74f8db5..288e53d075 100644
--- a/core/io/logger.cpp
+++ b/core/io/logger.cpp
@@ -87,7 +87,7 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c
} else {
logf_error("USER %s: %s\n", err_type, err_details);
}
- logf_error(" at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code);
+ logf_error(" at: %s (%s:%i)\n", p_function, p_file, p_line);
}
void Logger::logf(const char *p_format, ...) {
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index b24c49f58d..9ba653e1a9 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -503,7 +503,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 16, ERR_INVALID_DATA);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
- val.matrix[i][j] = decode_double(&buf[(i * 4 + j) * sizeof(double)]);
+ val.columns[i][j] = decode_double(&buf[(i * 4 + j) * sizeof(double)]);
}
}
if (r_len) {
@@ -513,7 +513,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_COND_V((size_t)len < sizeof(float) * 16, ERR_INVALID_DATA);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
- val.matrix[i][j] = decode_float(&buf[(i * 4 + j) * sizeof(float)]);
+ val.columns[i][j] = decode_float(&buf[(i * 4 + j) * sizeof(float)]);
}
}
@@ -1450,7 +1450,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
Projection val = p_variant;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
- memcpy(&buf[(i * 4 + j) * sizeof(real_t)], &val.matrix[i][j], sizeof(real_t));
+ memcpy(&buf[(i * 4 + j) * sizeof(real_t)], &val.columns[i][j], sizeof(real_t));
}
}
}
diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h
index 07045e62a6..4a5f244911 100644
--- a/core/io/packet_peer.h
+++ b/core/io/packet_peer.h
@@ -86,10 +86,10 @@ protected:
public:
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
- GDVIRTUAL2R(Error, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
+ GDVIRTUAL2R(Error, _get_packet, GDExtensionConstPtr<const uint8_t *>, GDExtensionPtr<int>);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- GDVIRTUAL2R(Error, _put_packet, GDNativeConstPtr<const uint8_t>, int);
+ GDVIRTUAL2R(Error, _put_packet, GDExtensionConstPtr<const uint8_t>, int);
EXBIND0RC(int, get_available_packet_count);
EXBIND0RC(int, get_max_packet_size);
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index aa1b323db2..0556f45b0c 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -107,6 +107,8 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &
}
Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) {
+ 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);
if (f.is_null()) {
return ERR_FILE_CANT_OPEN;
@@ -118,7 +120,7 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encr
pf.ofs = ofs;
pf.size = f->get_length();
- Vector<uint8_t> data = FileAccess::get_file_as_array(p_src);
+ Vector<uint8_t> data = FileAccess::get_file_as_bytes(p_src);
{
unsigned char hash[16];
CryptoCore::md5(data.ptr(), data.size(), hash);
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index d117f86f39..be75ad7e25 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -93,15 +93,14 @@ String Resource::get_path() const {
String Resource::generate_scene_unique_id() {
// Generate a unique enough hash, but still user-readable.
// If it's not unique it does not matter because the saver will try again.
- OS::Date date = OS::get_singleton()->get_date();
- OS::Time time = OS::get_singleton()->get_time();
+ OS::DateTime dt = OS::get_singleton()->get_datetime();
uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec());
- hash = hash_murmur3_one_32(date.year, hash);
- hash = hash_murmur3_one_32(date.month, hash);
- hash = hash_murmur3_one_32(date.day, hash);
- hash = hash_murmur3_one_32(time.hour, hash);
- hash = hash_murmur3_one_32(time.minute, hash);
- hash = hash_murmur3_one_32(time.second, hash);
+ hash = hash_murmur3_one_32(dt.year, hash);
+ hash = hash_murmur3_one_32(dt.month, hash);
+ hash = hash_murmur3_one_32(dt.day, hash);
+ hash = hash_murmur3_one_32(dt.hour, hash);
+ hash = hash_murmur3_one_32(dt.minute, hash);
+ hash = hash_murmur3_one_32(dt.second, hash);
hash = hash_murmur3_one_32(Math::rand(), hash);
static constexpr uint32_t characters = 5;
@@ -437,7 +436,7 @@ void Resource::_bind_methods() {
ADD_GROUP("Resource", "resource_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path");
- ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "resource_name"), "set_name", "get_name");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name");
MethodInfo get_rid_bind("_get_rid");
get_rid_bind.return_val.type = Variant::RID;
@@ -490,7 +489,7 @@ bool ResourceCache::has(const String &p_path) {
Resource **res = resources.getptr(p_path);
- if (res && (*res)->reference_get_count() == 0) {
+ 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);
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 4f1204fc48..4611528db7 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -107,7 +107,7 @@ void ResourceLoaderBinary::_advance_padding(uint32_t p_len) {
static Error read_reals(real_t *dst, Ref<FileAccess> &f, size_t count) {
if (f->real_is_double) {
- if (sizeof(real_t) == 8) {
+ if constexpr (sizeof(real_t) == 8) {
// Ideal case with double-precision
f->get_buffer((uint8_t *)dst, count * sizeof(double));
#ifdef BIG_ENDIAN_ENABLED
@@ -118,7 +118,7 @@ static Error read_reals(real_t *dst, Ref<FileAccess> &f, size_t count) {
}
}
#endif
- } else if (sizeof(real_t) == 4) {
+ } else if constexpr (sizeof(real_t) == 4) {
// May be slower, but this is for compatibility. Eventually the data should be converted.
for (size_t i = 0; i < count; ++i) {
dst[i] = f->get_double();
@@ -127,7 +127,7 @@ static Error read_reals(real_t *dst, Ref<FileAccess> &f, size_t count) {
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "real_t size is neither 4 nor 8!");
}
} else {
- if (sizeof(real_t) == 4) {
+ if constexpr (sizeof(real_t) == 4) {
// Ideal case with float-precision
f->get_buffer((uint8_t *)dst, count * sizeof(float));
#ifdef BIG_ENDIAN_ENABLED
@@ -138,7 +138,7 @@ static Error read_reals(real_t *dst, Ref<FileAccess> &f, size_t count) {
}
}
#endif
- } else if (sizeof(real_t) == 8) {
+ } else if constexpr (sizeof(real_t) == 8) {
for (size_t i = 0; i < count; ++i) {
dst[i] = f->get_float();
}
@@ -169,10 +169,10 @@ StringName ResourceLoaderBinary::_get_string() {
}
Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
- uint32_t type = f->get_32();
- print_bl("find property of type: " + itos(type));
+ uint32_t prop_type = f->get_32();
+ print_bl("find property of type: " + itos(prop_type));
- switch (type) {
+ switch (prop_type) {
case VARIANT_NIL: {
r_v = Variant();
} break;
@@ -327,22 +327,22 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) {
} break;
case VARIANT_PROJECTION: {
Projection v;
- v.matrix[0].x = f->get_real();
- v.matrix[0].y = f->get_real();
- v.matrix[0].z = f->get_real();
- v.matrix[0].w = f->get_real();
- v.matrix[1].x = f->get_real();
- v.matrix[1].y = f->get_real();
- v.matrix[1].z = f->get_real();
- v.matrix[1].w = f->get_real();
- v.matrix[2].x = f->get_real();
- v.matrix[2].y = f->get_real();
- v.matrix[2].z = f->get_real();
- v.matrix[2].w = f->get_real();
- v.matrix[3].x = f->get_real();
- v.matrix[3].y = f->get_real();
- v.matrix[3].z = f->get_real();
- v.matrix[3].w = f->get_real();
+ v.columns[0].x = f->get_real();
+ v.columns[0].y = f->get_real();
+ v.columns[0].z = f->get_real();
+ v.columns[0].w = f->get_real();
+ v.columns[1].x = f->get_real();
+ v.columns[1].y = f->get_real();
+ v.columns[1].z = f->get_real();
+ v.columns[1].w = f->get_real();
+ v.columns[2].x = f->get_real();
+ v.columns[2].y = f->get_real();
+ v.columns[2].z = f->get_real();
+ v.columns[2].w = f->get_real();
+ v.columns[3].x = f->get_real();
+ v.columns[3].y = f->get_real();
+ v.columns[3].z = f->get_real();
+ v.columns[3].w = f->get_real();
r_v = v;
} break;
case VARIANT_COLOR: {
@@ -1042,7 +1042,14 @@ void ResourceLoaderBinary::open(Ref<FileAccess> p_f, bool p_no_resources, bool p
// If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path.
er.path = ResourceUID::get_singleton()->get_id_path(er.uid);
} else {
+#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 UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
+ }
+#else
WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data());
+#endif
}
}
}
@@ -1100,16 +1107,14 @@ String ResourceLoaderBinary::recognize(Ref<FileAccess> p_f) {
uint32_t ver_major = f->get_32();
f->get_32(); // ver_minor
- uint32_t ver_format = f->get_32();
+ uint32_t ver_fmt = f->get_32();
- if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) {
+ if (ver_fmt > FORMAT_VERSION || ver_major > VERSION_MAJOR) {
f.unref();
return "";
}
- String type = get_unicode_string();
-
- return type;
+ return get_unicode_string();
}
Ref<Resource> ResourceFormatLoaderBinary::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
@@ -1206,7 +1211,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
Ref<FileAccessCompressed> facw;
facw.instantiate();
facw->configure("RSCC");
- err = facw->_open(p_path + ".depren", FileAccess::WRITE);
+ err = facw->open_internal(p_path + ".depren", FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(err, ERR_FILE_CORRUPT, "Cannot create file '" + p_path + ".depren'.");
fw = facw;
@@ -1630,22 +1635,22 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
case Variant::PROJECTION: {
f->store_32(VARIANT_PROJECTION);
Projection val = p_property;
- f->store_real(val.matrix[0].x);
- f->store_real(val.matrix[0].y);
- f->store_real(val.matrix[0].z);
- f->store_real(val.matrix[0].w);
- f->store_real(val.matrix[1].x);
- f->store_real(val.matrix[1].y);
- f->store_real(val.matrix[1].z);
- f->store_real(val.matrix[1].w);
- f->store_real(val.matrix[2].x);
- f->store_real(val.matrix[2].y);
- f->store_real(val.matrix[2].z);
- f->store_real(val.matrix[2].w);
- f->store_real(val.matrix[3].x);
- f->store_real(val.matrix[3].y);
- f->store_real(val.matrix[3].z);
- f->store_real(val.matrix[3].w);
+ f->store_real(val.columns[0].x);
+ f->store_real(val.columns[0].y);
+ f->store_real(val.columns[0].z);
+ f->store_real(val.columns[0].w);
+ f->store_real(val.columns[1].x);
+ f->store_real(val.columns[1].y);
+ f->store_real(val.columns[1].z);
+ f->store_real(val.columns[1].w);
+ f->store_real(val.columns[2].x);
+ f->store_real(val.columns[2].y);
+ f->store_real(val.columns[2].z);
+ f->store_real(val.columns[2].w);
+ f->store_real(val.columns[3].x);
+ f->store_real(val.columns[3].y);
+ f->store_real(val.columns[3].z);
+ f->store_real(val.columns[3].w);
} break;
case Variant::COLOR: {
@@ -1986,7 +1991,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
fac.instantiate();
fac->configure("RSCC");
f = fac;
- err = fac->_open(p_path, FileAccess::WRITE);
+ err = fac->open_internal(p_path, FileAccess::WRITE);
} else {
f = FileAccess::open(p_path, FileAccess::WRITE, &err);
}
@@ -2118,9 +2123,9 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Re
for (int i = 0; i < save_order.size(); i++) {
save_unicode_string(f, save_order[i]->get_save_class());
- String path = save_order[i]->get_path();
- path = relative_paths ? local_path.path_to_file(path) : path;
- save_unicode_string(f, path);
+ String res_path = save_order[i]->get_path();
+ 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);
}
diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp
index aa7f96a047..564b37e662 100644
--- a/core/io/resource_importer.cpp
+++ b/core/io/resource_importer.cpp
@@ -108,6 +108,15 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
}
}
+#ifdef TOOLS_ENABLED
+ if (r_path_and_type.metadata && !r_path_and_type.path.is_empty()) {
+ Dictionary meta = r_path_and_type.metadata;
+ if (meta.has("has_editor_variant")) {
+ r_path_and_type.path = r_path_and_type.path.get_basename() + ".editor." + r_path_and_type.path.get_extension();
+ }
+ }
+#endif
+
if (r_path_and_type.path.is_empty() || r_path_and_type.type.is_empty()) {
return ERR_FILE_CORRUPT;
}
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index eccb397e2e..20445a8b03 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -49,6 +49,11 @@ Ref<ResourceFormatLoader> ResourceLoader::loader[ResourceLoader::MAX_LOADERS];
int ResourceLoader::loader_count = 0;
bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_for_type) const {
+ bool ret = false;
+ if (GDVIRTUAL_CALL(_recognize_path, p_path, p_for_type, ret)) {
+ return ret;
+ }
+
String extension = p_path.get_extension();
List<String> extensions;
@@ -68,12 +73,9 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_
}
bool ResourceFormatLoader::handles_type(const String &p_type) const {
- bool success;
- if (GDVIRTUAL_CALL(_handles_type, p_type, success)) {
- return success;
- }
-
- return false;
+ bool success = false;
+ GDVIRTUAL_CALL(_handles_type, p_type, success);
+ return success;
}
void ResourceFormatLoader::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {
@@ -93,21 +95,14 @@ void ResourceFormatLoader::get_classes_used(const String &p_path, HashSet<String
String ResourceFormatLoader::get_resource_type(const String &p_path) const {
String ret;
-
- if (GDVIRTUAL_CALL(_get_resource_type, p_path, ret)) {
- return ret;
- }
-
- return "";
+ GDVIRTUAL_CALL(_get_resource_type, p_path, ret);
+ return ret;
}
ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) const {
- int64_t uid;
- if (GDVIRTUAL_CALL(_get_resource_uid, p_path, uid)) {
- return uid;
- }
-
- return ResourceUID::INVALID_ID;
+ int64_t uid = ResourceUID::INVALID_ID;
+ GDVIRTUAL_CALL(_get_resource_uid, p_path, uid);
+ return uid;
}
void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const {
@@ -123,11 +118,11 @@ void ResourceLoader::get_recognized_extensions_for_type(const String &p_type, Li
}
bool ResourceFormatLoader::exists(const String &p_path) const {
- bool success;
+ bool success = false;
if (GDVIRTUAL_CALL(_exists, p_path, success)) {
return success;
}
- return FileAccess::exists(p_path); //by default just check file
+ return FileAccess::exists(p_path); // By default just check file.
}
void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions) const {
@@ -175,12 +170,9 @@ Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Hash
deps_dict[E.key] = E.value;
}
- int64_t err;
- if (GDVIRTUAL_CALL(_rename_dependencies, p_path, deps_dict, err)) {
- return (Error)err;
- }
-
- return OK;
+ int64_t err = OK;
+ GDVIRTUAL_CALL(_rename_dependencies, p_path, deps_dict, err);
+ return (Error)err;
}
void ResourceFormatLoader::_bind_methods() {
@@ -189,6 +181,7 @@ void ResourceFormatLoader::_bind_methods() {
BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE);
GDVIRTUAL_BIND(_get_recognized_extensions);
+ GDVIRTUAL_BIND(_recognize_path, "path", "type");
GDVIRTUAL_BIND(_handles_type, "type");
GDVIRTUAL_BIND(_get_resource_type, "path");
GDVIRTUAL_BIND(_get_resource_uid, "path");
@@ -908,7 +901,7 @@ void ResourceLoader::load_translation_remaps() {
return;
}
- Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps");
+ Dictionary remaps = GLOBAL_GET("internationalization/locale/translation_remaps");
List<Variant> keys;
remaps.get_key_list(&keys);
for (const Variant &E : keys) {
@@ -930,12 +923,41 @@ void ResourceLoader::clear_translation_remaps() {
}
}
+void ResourceLoader::clear_thread_load_tasks() {
+ thread_load_mutex->lock();
+
+ for (KeyValue<String, ResourceLoader::ThreadLoadTask> &E : thread_load_tasks) {
+ switch (E.value.status) {
+ case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_LOADED: {
+ E.value.resource = Ref<Resource>();
+ } break;
+
+ case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_IN_PROGRESS: {
+ if (E.value.thread != nullptr) {
+ E.value.thread->wait_to_finish();
+ memdelete(E.value.thread);
+ E.value.thread = nullptr;
+ }
+ E.value.resource = Ref<Resource>();
+ } break;
+
+ case ResourceLoader::ThreadLoadStatus::THREAD_LOAD_FAILED:
+ default: {
+ // do nothing
+ }
+ }
+ }
+ thread_load_tasks.clear();
+
+ thread_load_mutex->unlock();
+}
+
void ResourceLoader::load_path_remaps() {
if (!ProjectSettings::get_singleton()->has_setting("path_remap/remapped_paths")) {
return;
}
- Vector<String> remaps = ProjectSettings::get_singleton()->get("path_remap/remapped_paths");
+ Vector<String> remaps = GLOBAL_GET("path_remap/remapped_paths");
int rc = remaps.size();
ERR_FAIL_COND(rc & 1); //must be even
const String *r = remaps.ptr();
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 91ba930176..af10098bd8 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -51,6 +51,7 @@ protected:
static void _bind_methods();
GDVIRTUAL0RC(Vector<String>, _get_recognized_extensions)
+ GDVIRTUAL2RC(bool, _recognize_path, String, StringName)
GDVIRTUAL1RC(bool, _handles_type, StringName)
GDVIRTUAL1RC(String, _get_resource_type, String)
GDVIRTUAL1RC(ResourceUID::ID, _get_resource_uid, String)
@@ -218,6 +219,8 @@ public:
static void load_translation_remaps();
static void clear_translation_remaps();
+ static void clear_thread_load_tasks();
+
static void set_load_callback(ResourceLoadedCallback p_callback);
static ResourceLoaderImport import;
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 386ccb78e9..710afc614f 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -42,21 +42,15 @@ ResourceSavedCallback ResourceSaver::save_callback = nullptr;
ResourceSaverGetResourceIDForPath ResourceSaver::save_get_id_for_path = nullptr;
Error ResourceFormatSaver::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
- int64_t res;
- if (GDVIRTUAL_CALL(_save, p_resource, p_path, p_flags, res)) {
- return (Error)res;
- }
-
- return ERR_METHOD_NOT_FOUND;
+ int64_t res = ERR_METHOD_NOT_FOUND;
+ GDVIRTUAL_CALL(_save, p_resource, p_path, p_flags, res);
+ return (Error)res;
}
bool ResourceFormatSaver::recognize(const Ref<Resource> &p_resource) const {
- bool success;
- if (GDVIRTUAL_CALL(_recognize, p_resource, success)) {
- return success;
- }
-
- return false;
+ bool success = false;
+ GDVIRTUAL_CALL(_recognize, p_resource, success);
+ return success;
}
void ResourceFormatSaver::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
@@ -69,10 +63,31 @@ void ResourceFormatSaver::get_recognized_extensions(const Ref<Resource> &p_resou
}
}
+bool ResourceFormatSaver::recognize_path(const Ref<Resource> &p_resource, const String &p_path) const {
+ bool ret = false;
+ if (GDVIRTUAL_CALL(_recognize_path, p_resource, p_path, ret)) {
+ return ret;
+ }
+
+ String extension = p_path.get_extension();
+
+ List<String> extensions;
+ get_recognized_extensions(p_resource, &extensions);
+
+ for (const String &E : extensions) {
+ if (E.nocasecmp_to(extension) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void ResourceFormatSaver::_bind_methods() {
GDVIRTUAL_BIND(_save, "resource", "path", "flags");
GDVIRTUAL_BIND(_recognize, "resource");
GDVIRTUAL_BIND(_get_recognized_extensions, "resource");
+ GDVIRTUAL_BIND(_recognize_path, "resource", "path");
}
Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
@@ -90,17 +105,7 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path,
continue;
}
- List<String> extensions;
- bool recognized = false;
- saver[i]->get_recognized_extensions(p_resource, &extensions);
-
- for (const String &E : extensions) {
- if (E.nocasecmp_to(extension) == 0) {
- recognized = true;
- }
- }
-
- if (!recognized) {
+ if (!saver[i]->recognize_path(p_resource, path)) {
continue;
}
diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h
index 4fee2bcfd1..5e48ce88c3 100644
--- a/core/io/resource_saver.h
+++ b/core/io/resource_saver.h
@@ -44,11 +44,13 @@ protected:
GDVIRTUAL3R(int64_t, _save, Ref<Resource>, String, uint32_t)
GDVIRTUAL1RC(bool, _recognize, Ref<Resource>)
GDVIRTUAL1RC(Vector<String>, _get_recognized_extensions, Ref<Resource>)
+ GDVIRTUAL2RC(bool, _recognize_path, Ref<Resource>, String)
public:
virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0);
virtual bool recognize(const Ref<Resource> &p_resource) const;
virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+ virtual bool recognize_path(const Ref<Resource> &p_resource, const String &p_path) const;
virtual ~ResourceFormatSaver() {}
};
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
index 5324c5dd84..ed5ce3b911 100644
--- a/core/io/resource_uid.cpp
+++ b/core/io/resource_uid.cpp
@@ -113,7 +113,12 @@ void ResourceUID::set_id(ID p_id, const String &p_path) {
MutexLock l(mutex);
ERR_FAIL_COND(!unique_ids.has(p_id));
CharString cs = p_path.utf8();
- if (strcmp(cs.ptr(), unique_ids[p_id].cs.ptr()) != 0) {
+ const char *update_ptr = cs.ptr();
+ const char *cached_ptr = unique_ids[p_id].cs.ptr();
+ if (update_ptr == nullptr && cached_ptr == nullptr) {
+ return; // Both are empty strings.
+ }
+ if ((update_ptr == nullptr) != (cached_ptr == nullptr) || strcmp(update_ptr, cached_ptr) != 0) {
unique_ids[p_id].cs = cs;
unique_ids[p_id].saved_to_cache = false; //changed
changed = true;
diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h
index 108a8ce9d9..95cc655b45 100644
--- a/core/io/stream_peer.h
+++ b/core/io/stream_peer.h
@@ -105,16 +105,16 @@ protected:
public:
virtual Error put_data(const uint8_t *p_data, int p_bytes) override;
- GDVIRTUAL3R(Error, _put_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(Error, _put_data, GDExtensionConstPtr<const uint8_t>, int, GDExtensionPtr<int>);
virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
- GDVIRTUAL3R(Error, _put_partial_data, GDNativeConstPtr<const uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(Error, _put_partial_data, GDExtensionConstPtr<const uint8_t>, int, GDExtensionPtr<int>);
virtual Error get_data(uint8_t *p_buffer, int p_bytes) override;
- GDVIRTUAL3R(Error, _get_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(Error, _get_data, GDExtensionPtr<uint8_t>, int, GDExtensionPtr<int>);
virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
- GDVIRTUAL3R(Error, _get_partial_data, GDNativePtr<uint8_t>, int, GDNativePtr<int>);
+ GDVIRTUAL3R(Error, _get_partial_data, GDExtensionPtr<uint8_t>, int, GDExtensionPtr<int>);
EXBIND0RC(int, get_available_bytes);
};
diff --git a/core/io/stream_peer_gzip.cpp b/core/io/stream_peer_gzip.cpp
new file mode 100644
index 0000000000..ca8be2d62e
--- /dev/null
+++ b/core/io/stream_peer_gzip.cpp
@@ -0,0 +1,209 @@
+/*************************************************************************/
+/* stream_peer_gzip.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "core/io/stream_peer_gzip.h"
+
+#include "core/io/zip_io.h"
+#include <zlib.h>
+
+void StreamPeerGZIP::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("start_compression", "use_deflate", "buffer_size"), &StreamPeerGZIP::start_compression, DEFVAL(false), DEFVAL(65535));
+ ClassDB::bind_method(D_METHOD("start_decompression", "use_deflate", "buffer_size"), &StreamPeerGZIP::start_decompression, DEFVAL(false), DEFVAL(65535));
+ ClassDB::bind_method(D_METHOD("finish"), &StreamPeerGZIP::finish);
+ ClassDB::bind_method(D_METHOD("clear"), &StreamPeerGZIP::clear);
+}
+
+StreamPeerGZIP::StreamPeerGZIP() {
+}
+
+StreamPeerGZIP::~StreamPeerGZIP() {
+ _close();
+}
+
+void StreamPeerGZIP::_close() {
+ if (ctx) {
+ z_stream *strm = (z_stream *)ctx;
+ if (compressing) {
+ deflateEnd(strm);
+ } else {
+ inflateEnd(strm);
+ }
+ memfree(strm);
+ ctx = nullptr;
+ }
+}
+
+void StreamPeerGZIP::clear() {
+ _close();
+ rb.clear();
+ buffer.clear();
+}
+
+Error StreamPeerGZIP::start_compression(bool p_is_deflate, int buffer_size) {
+ return _start(true, p_is_deflate, buffer_size);
+}
+
+Error StreamPeerGZIP::start_decompression(bool p_is_deflate, int buffer_size) {
+ return _start(false, p_is_deflate, buffer_size);
+}
+
+Error StreamPeerGZIP::_start(bool p_compress, bool p_is_deflate, int buffer_size) {
+ ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE);
+ clear();
+ compressing = p_compress;
+ rb.resize(nearest_shift(buffer_size - 1));
+ buffer.resize(1024);
+
+ // Create ctx.
+ ctx = memalloc(sizeof(z_stream));
+ z_stream &strm = *(z_stream *)ctx;
+ strm.next_in = Z_NULL;
+ strm.avail_in = 0;
+ strm.zalloc = zipio_alloc;
+ strm.zfree = zipio_free;
+ strm.opaque = Z_NULL;
+ int window_bits = p_is_deflate ? 15 : (15 + 16);
+ int err = Z_OK;
+ int level = Z_DEFAULT_COMPRESSION;
+ if (compressing) {
+ err = deflateInit2(&strm, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY);
+ } else {
+ err = inflateInit2(&strm, window_bits);
+ }
+ ERR_FAIL_COND_V(err != Z_OK, FAILED);
+ return OK;
+}
+
+Error StreamPeerGZIP::_process(uint8_t *p_dst, int p_dst_size, const uint8_t *p_src, int p_src_size, int &r_consumed, int &r_out, bool p_close) {
+ ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED);
+ z_stream &strm = *(z_stream *)ctx;
+ strm.avail_in = p_src_size;
+ strm.avail_out = p_dst_size;
+ strm.next_in = (Bytef *)p_src;
+ strm.next_out = (Bytef *)p_dst;
+ int flush = p_close ? Z_FINISH : Z_NO_FLUSH;
+ if (compressing) {
+ int err = deflate(&strm, flush);
+ ERR_FAIL_COND_V(err != (p_close ? Z_STREAM_END : Z_OK), FAILED);
+ } else {
+ int err = inflate(&strm, flush);
+ ERR_FAIL_COND_V(err != Z_OK && err != Z_STREAM_END, FAILED);
+ }
+ r_out = p_dst_size - strm.avail_out;
+ r_consumed = p_src_size - strm.avail_in;
+ return OK;
+}
+
+Error StreamPeerGZIP::put_data(const uint8_t *p_data, int p_bytes) {
+ int wrote = 0;
+ Error err = put_partial_data(p_data, p_bytes, wrote);
+ if (err != OK) {
+ return err;
+ }
+ ERR_FAIL_COND_V(p_bytes != wrote, ERR_OUT_OF_MEMORY);
+ return OK;
+}
+
+Error StreamPeerGZIP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
+ ERR_FAIL_COND_V(!ctx, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(p_bytes < 0, ERR_INVALID_PARAMETER);
+
+ // Ensure we have enough space in temporary buffer.
+ if (buffer.size() < p_bytes) {
+ buffer.resize(p_bytes);
+ }
+
+ r_sent = 0;
+ while (r_sent < p_bytes && rb.space_left() > 1024) { // Keep the ring buffer size meaningful.
+ int sent = 0;
+ int to_write = 0;
+ // Compress or decompress
+ Error err = _process(buffer.ptrw(), MIN(buffer.size(), rb.space_left()), p_data + r_sent, p_bytes - r_sent, sent, to_write);
+ if (err != OK) {
+ return err;
+ }
+ // When decompressing, we might need to do another round.
+ r_sent += sent;
+
+ // We can't write more than this buffer is full.
+ if (sent == 0 && to_write == 0) {
+ return OK;
+ }
+ if (to_write) {
+ // Copy to ring buffer.
+ int wrote = rb.write(buffer.ptr(), to_write);
+ ERR_FAIL_COND_V(wrote != to_write, ERR_BUG);
+ }
+ }
+ return OK;
+}
+
+Error StreamPeerGZIP::get_data(uint8_t *p_buffer, int p_bytes) {
+ int received = 0;
+ Error err = get_partial_data(p_buffer, p_bytes, received);
+ if (err != OK) {
+ return err;
+ }
+ ERR_FAIL_COND_V(p_bytes != received, ERR_UNAVAILABLE);
+ return OK;
+}
+
+Error StreamPeerGZIP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
+ ERR_FAIL_COND_V(p_bytes < 0, ERR_INVALID_PARAMETER);
+
+ r_received = MIN(p_bytes, rb.data_left());
+ if (r_received == 0) {
+ return OK;
+ }
+ int received = rb.read(p_buffer, r_received);
+ ERR_FAIL_COND_V(received != r_received, ERR_BUG);
+ return OK;
+}
+
+int StreamPeerGZIP::get_available_bytes() const {
+ return rb.data_left();
+}
+
+Error StreamPeerGZIP::finish() {
+ ERR_FAIL_COND_V(!ctx || !compressing, ERR_UNAVAILABLE);
+ // Ensure we have enough space in temporary buffer.
+ if (buffer.size() < 1024) {
+ buffer.resize(1024); // 1024 should be more than enough.
+ }
+ int consumed = 0;
+ int to_write = 0;
+ Error err = _process(buffer.ptrw(), 1024, nullptr, 0, consumed, to_write, true); // compress
+ if (err != OK) {
+ return err;
+ }
+ int wrote = rb.write(buffer.ptr(), to_write);
+ ERR_FAIL_COND_V(wrote != to_write, ERR_OUT_OF_MEMORY);
+ return OK;
+}
diff --git a/core/io/stream_peer_gzip.h b/core/io/stream_peer_gzip.h
new file mode 100644
index 0000000000..5bafdbca9b
--- /dev/null
+++ b/core/io/stream_peer_gzip.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* stream_peer_gzip.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef STREAM_PEER_GZIP_H
+#define STREAM_PEER_GZIP_H
+
+#include "core/io/stream_peer.h"
+
+#include "core/core_bind.h"
+#include "core/io/compression.h"
+#include "core/templates/ring_buffer.h"
+
+class StreamPeerGZIP : public StreamPeer {
+ GDCLASS(StreamPeerGZIP, StreamPeer);
+
+private:
+ void *ctx = nullptr; // Will hold our z_stream instance.
+ bool compressing = true;
+
+ RingBuffer<uint8_t> rb;
+ Vector<uint8_t> buffer;
+
+ Error _process(uint8_t *p_dst, int p_dst_size, const uint8_t *p_src, int p_src_size, int &r_consumed, int &r_out, bool p_close = false);
+ void _close();
+ Error _start(bool p_compress, bool p_is_deflate, int buffer_size = 65535);
+
+protected:
+ static void _bind_methods();
+
+public:
+ Error start_compression(bool p_is_deflate, int buffer_size = 65535);
+ Error start_decompression(bool p_is_deflate, int buffer_size = 65535);
+
+ Error finish();
+ void clear();
+
+ virtual Error put_data(const uint8_t *p_data, int p_bytes) override;
+ virtual Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
+
+ virtual Error get_data(uint8_t *p_buffer, int p_bytes) override;
+ virtual Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
+
+ virtual int get_available_bytes() const override;
+
+ StreamPeerGZIP();
+ ~StreamPeerGZIP();
+};
+
+#endif // STREAM_PEER_GZIP_H
diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp
index ba79590c19..e035e1b613 100644
--- a/core/io/stream_peer_tcp.cpp
+++ b/core/io/stream_peer_tcp.cpp
@@ -256,9 +256,9 @@ void StreamPeerTCP::disconnect_from_host() {
peer_port = 0;
}
-Error StreamPeerTCP::wait(NetSocket::PollType p_type, int timeout) {
+Error StreamPeerTCP::wait(NetSocket::PollType p_type, int p_timeout) {
ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE);
- return _sock->poll(p_type, timeout);
+ return _sock->poll(p_type, p_timeout);
}
Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) {
diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h
index 39c2e84346..778fb83374 100644
--- a/core/io/stream_peer_tcp.h
+++ b/core/io/stream_peer_tcp.h
@@ -79,7 +79,7 @@ public:
Error poll();
// Wait or check for writable, readable.
- Error wait(NetSocket::PollType p_type, int timeout = 0);
+ Error wait(NetSocket::PollType p_type, int p_timeout = 0);
// Read/Write from StreamPeer
Error put_data(const uint8_t *p_data, int p_bytes) override;
diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_tls.cpp
index 5b90fb52a6..b1adde018a 100644
--- a/core/io/stream_peer_ssl.cpp
+++ b/core/io/stream_peer_tls.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* stream_peer_ssl.cpp */
+/* stream_peer_tls.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,42 +28,42 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "stream_peer_ssl.h"
+#include "stream_peer_tls.h"
#include "core/config/engine.h"
-StreamPeerSSL *(*StreamPeerSSL::_create)() = nullptr;
+StreamPeerTLS *(*StreamPeerTLS::_create)() = nullptr;
-StreamPeerSSL *StreamPeerSSL::create() {
+StreamPeerTLS *StreamPeerTLS::create() {
if (_create) {
return _create();
}
return nullptr;
}
-bool StreamPeerSSL::available = false;
+bool StreamPeerTLS::available = false;
-bool StreamPeerSSL::is_available() {
+bool StreamPeerTLS::is_available() {
return available;
}
-void StreamPeerSSL::set_blocking_handshake_enabled(bool p_enabled) {
+void StreamPeerTLS::set_blocking_handshake_enabled(bool p_enabled) {
blocking_handshake = p_enabled;
}
-bool StreamPeerSSL::is_blocking_handshake_enabled() const {
+bool StreamPeerTLS::is_blocking_handshake_enabled() const {
return blocking_handshake;
}
-void StreamPeerSSL::_bind_methods() {
- ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll);
- ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerSSL::accept_stream, DEFVAL(Ref<X509Certificate>()));
- ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>()));
- ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status);
- ClassDB::bind_method(D_METHOD("get_stream"), &StreamPeerSSL::get_stream);
- ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream);
- ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled);
- ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerSSL::is_blocking_handshake_enabled);
+void StreamPeerTLS::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("poll"), &StreamPeerTLS::poll);
+ ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerTLS::accept_stream, DEFVAL(Ref<X509Certificate>()));
+ ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerTLS::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>()));
+ ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTLS::get_status);
+ ClassDB::bind_method(D_METHOD("get_stream"), &StreamPeerTLS::get_stream);
+ ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerTLS::disconnect_from_stream);
+ ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerTLS::set_blocking_handshake_enabled);
+ ClassDB::bind_method(D_METHOD("is_blocking_handshake_enabled"), &StreamPeerTLS::is_blocking_handshake_enabled);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking_handshake"), "set_blocking_handshake_enabled", "is_blocking_handshake_enabled");
diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_tls.h
index fe68667adc..ed7334fab3 100644
--- a/core/io/stream_peer_ssl.h
+++ b/core/io/stream_peer_tls.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* stream_peer_ssl.h */
+/* stream_peer_tls.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,17 +28,17 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef STREAM_PEER_SSL_H
-#define STREAM_PEER_SSL_H
+#ifndef STREAM_PEER_TLS_H
+#define STREAM_PEER_TLS_H
#include "core/crypto/crypto.h"
#include "core/io/stream_peer.h"
-class StreamPeerSSL : public StreamPeer {
- GDCLASS(StreamPeerSSL, StreamPeer);
+class StreamPeerTLS : public StreamPeer {
+ GDCLASS(StreamPeerTLS, StreamPeer);
protected:
- static StreamPeerSSL *(*_create)();
+ static StreamPeerTLS *(*_create)();
static void _bind_methods();
static bool available;
@@ -65,13 +65,13 @@ public:
virtual void disconnect_from_stream() = 0;
- static StreamPeerSSL *create();
+ static StreamPeerTLS *create();
static bool is_available();
- StreamPeerSSL() {}
+ StreamPeerTLS() {}
};
-VARIANT_ENUM_CAST(StreamPeerSSL::Status);
+VARIANT_ENUM_CAST(StreamPeerTLS::Status);
-#endif // STREAM_PEER_SSL_H
+#endif // STREAM_PEER_TLS_H
diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp
index 154b55f5e7..16b7f3a1f6 100644
--- a/core/io/xml_parser.cpp
+++ b/core/io/xml_parser.cpp
@@ -41,11 +41,11 @@ static inline bool _is_white_space(char c) {
}
//! sets the state that text was found. Returns true if set should be set
-bool XMLParser::_set_text(char *start, char *end) {
+bool XMLParser::_set_text(const char *start, const char *end) {
// check if text is more than 2 characters, and if not, check if there is
// only white space, so that this text won't be reported
if (end - start < 3) {
- char *p = start;
+ const char *p = start;
for (; p != end; ++p) {
if (!_is_white_space(*p)) {
break;
@@ -92,7 +92,7 @@ void XMLParser::_parse_closing_xml_element() {
void XMLParser::_ignore_definition() {
node_type = NODE_UNKNOWN;
- char *F = P;
+ const char *F = P;
// move until end marked with '>' reached
while (*P && *P != '>') {
next_char();
@@ -123,8 +123,8 @@ bool XMLParser::_parse_cdata() {
return true;
}
- char *cDataBegin = P;
- char *cDataEnd = nullptr;
+ const char *cDataBegin = P;
+ const char *cDataEnd = nullptr;
// find end of CDATA
while (*P && !cDataEnd) {
@@ -137,7 +137,7 @@ bool XMLParser::_parse_cdata() {
next_char();
}
- if (cDataEnd) {
+ if (!cDataEnd) {
cDataEnd = P;
}
node_name = String::utf8(cDataBegin, (int)(cDataEnd - cDataBegin));
@@ -152,9 +152,9 @@ void XMLParser::_parse_comment() {
node_type = NODE_COMMENT;
P += 1;
- char *pEndOfInput = data + length;
- char *pCommentBegin;
- char *pCommentEnd;
+ const char *pEndOfInput = data + length;
+ const char *pCommentBegin;
+ const char *pCommentEnd;
if (P + 1 < pEndOfInput && P[0] == '-' && P[1] == '-') {
// Comment, use '-->' as end.
@@ -293,7 +293,7 @@ void XMLParser::_parse_opening_xml_element() {
}
void XMLParser::_parse_current_node() {
- char *start = P;
+ const char *start = P;
node_offset = P - data;
// more forward until '<' found
@@ -458,15 +458,36 @@ bool XMLParser::is_empty() const {
Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) {
ERR_FAIL_COND_V(p_buffer.size() == 0, ERR_INVALID_DATA);
- if (data) {
- memdelete_arr(data);
+ if (data_copy) {
+ memdelete_arr(data_copy);
+ data_copy = nullptr;
}
length = p_buffer.size();
- data = memnew_arr(char, length + 1);
- memcpy(data, p_buffer.ptr(), length);
- data[length] = 0;
+ data_copy = memnew_arr(char, length + 1);
+ memcpy(data_copy, p_buffer.ptr(), length);
+ data_copy[length] = 0;
+ data = data_copy;
+ P = data;
+ current_line = 0;
+
+ return OK;
+}
+
+Error XMLParser::_open_buffer(const uint8_t *p_buffer, size_t p_size) {
+ ERR_FAIL_COND_V(p_size == 0, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(!p_buffer, ERR_INVALID_DATA);
+
+ if (data_copy) {
+ memdelete_arr(data_copy);
+ data_copy = nullptr;
+ }
+
+ length = p_size;
+ data = (const char *)p_buffer;
P = data;
+ current_line = 0;
+
return OK;
}
@@ -479,13 +500,15 @@ Error XMLParser::open(const String &p_path) {
length = file->get_length();
ERR_FAIL_COND_V(length < 1, ERR_FILE_CORRUPT);
- if (data) {
- memdelete_arr(data);
+ if (data_copy) {
+ memdelete_arr(data_copy);
+ data_copy = nullptr;
}
- data = memnew_arr(char, length + 1);
- file->get_buffer((uint8_t *)data, length);
- data[length] = 0;
+ data_copy = memnew_arr(char, length + 1);
+ file->get_buffer((uint8_t *)data_copy, length);
+ data_copy[length] = 0;
+ data = data_copy;
P = data;
current_line = 0;
@@ -512,8 +535,9 @@ void XMLParser::skip_section() {
}
void XMLParser::close() {
- if (data) {
+ if (data_copy) {
memdelete_arr(data);
+ data_copy = nullptr;
}
data = nullptr;
length = 0;
@@ -528,7 +552,8 @@ int XMLParser::get_current_line() const {
}
XMLParser::~XMLParser() {
- if (data) {
- memdelete_arr(data);
+ if (data_copy) {
+ memdelete_arr(data_copy);
+ data_copy = nullptr;
}
}
diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h
index aea252ddc7..be44f771dc 100644
--- a/core/io/xml_parser.h
+++ b/core/io/xml_parser.h
@@ -65,8 +65,9 @@ public:
};
private:
- char *data = nullptr;
- char *P = nullptr;
+ char *data_copy = nullptr;
+ const char *data = nullptr;
+ const char *P = nullptr;
uint64_t length = 0;
uint64_t current_line = 0;
String node_name;
@@ -81,7 +82,7 @@ private:
Vector<Attribute> attributes;
- bool _set_text(char *start, char *end);
+ bool _set_text(const char *start, const char *end);
void _parse_closing_xml_element();
void _ignore_definition();
bool _parse_cdata();
@@ -118,6 +119,7 @@ public:
Error open(const String &p_path);
Error open_buffer(const Vector<uint8_t> &p_buffer);
+ Error _open_buffer(const uint8_t *p_buffer, size_t p_size);
void close();
diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp
index e573e8de19..200e5f5e83 100644
--- a/core/io/zip_io.cpp
+++ b/core/io/zip_io.cpp
@@ -37,11 +37,17 @@ void *zipio_open(voidpf opaque, const char *p_fname, int mode) {
String fname;
fname.parse_utf8(p_fname);
+ int file_access_mode = 0;
if (mode & ZLIB_FILEFUNC_MODE_WRITE) {
- (*fa) = FileAccess::open(fname, FileAccess::WRITE);
- } else {
- (*fa) = FileAccess::open(fname, FileAccess::READ);
+ file_access_mode |= FileAccess::WRITE;
}
+ if (mode & ZLIB_FILEFUNC_MODE_READ) {
+ file_access_mode |= FileAccess::READ;
+ }
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
+ file_access_mode |= FileAccess::WRITE_READ;
+ }
+ (*fa) = FileAccess::open(fname, file_access_mode);
if (fa->is_null()) {
return nullptr;