diff options
Diffstat (limited to 'core/io')
72 files changed, 4076 insertions, 3231 deletions
diff --git a/core/io/SCsub b/core/io/SCsub index 1c5f954470..19a6549225 100644 --- a/core/io/SCsub +++ b/core/io/SCsub @@ -1,5 +1,5 @@ #!/usr/bin/env python -Import('env') +Import("env") env.add_source_files(env.core_sources, "*.cpp") diff --git a/core/io/compression.cpp b/core/io/compression.cpp index 20c9fdca6f..7480262835 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -40,10 +40,8 @@ #include <zstd.h> int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode) { - switch (p_mode) { case MODE_FASTLZ: { - if (p_src_size < 16) { uint8_t src[16]; zeromem(&src[p_src_size], 16 - p_src_size); @@ -56,7 +54,6 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, } break; case MODE_DEFLATE: case MODE_GZIP: { - int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; z_stream strm; @@ -65,8 +62,9 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, strm.opaque = Z_NULL; int level = p_mode == MODE_DEFLATE ? zlib_level : gzip_level; int err = deflateInit2(&strm, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY); - if (err != Z_OK) + if (err != Z_OK) { return -1; + } strm.avail_in = p_src_size; int aout = deflateBound(&strm, p_src_size); @@ -97,19 +95,17 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, } int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { - switch (p_mode) { case MODE_FASTLZ: { - int ss = p_src_size + p_src_size * 6 / 100; - if (ss < 66) + if (ss < 66) { ss = 66; + } return ss; } break; case MODE_DEFLATE: case MODE_GZIP: { - int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; z_stream strm; @@ -117,14 +113,14 @@ int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { strm.zfree = zipio_free; strm.opaque = Z_NULL; int err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY); - if (err != Z_OK) + if (err != Z_OK) { return -1; + } int aout = deflateBound(&strm, p_src_size); deflateEnd(&strm); return aout; } break; case MODE_ZSTD: { - return ZSTD_compressBound(p_src_size); } break; } @@ -133,10 +129,8 @@ int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { } int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { - switch (p_mode) { case MODE_FASTLZ: { - int ret_size = 0; if (p_dst_max_size < 16) { @@ -150,7 +144,6 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p } break; case MODE_DEFLATE: case MODE_GZIP: { - int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; z_stream strm; @@ -187,8 +180,95 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p ERR_FAIL_V(-1); } +/** + This will handle both Gzip and Deflat streams. It will automatically allocate the output buffer into the provided p_dst_vect Vector. + This is required for compressed data who's final uncompressed size is unknown, as is the case for HTTP response bodies. + This is much slower however than using Compression::decompress because it may result in multiple full copies of the output buffer. +*/ +int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { + int ret; + uint8_t *dst = nullptr; + int out_mark = 0; + z_stream strm; + + ERR_FAIL_COND_V(p_src_size <= 0, Z_DATA_ERROR); + + // This function only supports GZip and Deflate + int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; + ERR_FAIL_COND_V(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO); + + // Initialize the stream + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + + int err = inflateInit2(&strm, window_bits); + ERR_FAIL_COND_V(err != Z_OK, -1); + + // Setup the stream inputs + strm.next_in = (Bytef *)p_src; + strm.avail_in = p_src_size; + + // Ensure the destination buffer is empty + p_dst_vect->resize(0); + + // decompress until deflate stream ends or end of file + do { + // Add another chunk size to the output buffer + // This forces a copy of the whole buffer + p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); + // Get pointer to the actual output buffer + dst = p_dst_vect->ptrw(); + + // Set the stream to the new output stream + // Since it was copied, we need to reset the stream to the new buffer + strm.next_out = &(dst[out_mark]); + strm.avail_out = gzip_chunk; + + // run inflate() on input until output buffer is full and needs to be resized + // or input runs out + do { + ret = inflate(&strm, Z_SYNC_FLUSH); + + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + [[fallthrough]]; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + case Z_STREAM_ERROR: + WARN_PRINT(strm.msg); + (void)inflateEnd(&strm); + p_dst_vect->resize(0); + return ret; + } + } while (strm.avail_out > 0 && strm.avail_in > 0); + + out_mark += gzip_chunk; + + // Encorce max output size + if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { + (void)inflateEnd(&strm); + p_dst_vect->resize(0); + return Z_BUF_ERROR; + } + } while (ret != Z_STREAM_END); + + // If all done successfully, resize the output if it's larger than the actual output + if (ret == Z_STREAM_END && (unsigned long)p_dst_vect->size() > strm.total_out) { + p_dst_vect->resize(strm.total_out); + } + + // clean up and return + (void)inflateEnd(&strm); + return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR; +} + int Compression::zlib_level = Z_DEFAULT_COMPRESSION; int Compression::gzip_level = Z_DEFAULT_COMPRESSION; int Compression::zstd_level = 3; bool Compression::zstd_long_distance_matching = false; int Compression::zstd_window_log_size = 27; // ZSTD_WINDOWLOG_LIMIT_DEFAULT +int Compression::gzip_chunk = 16384; diff --git a/core/io/compression.h b/core/io/compression.h index 8354b581fa..c103fa8eae 100644 --- a/core/io/compression.h +++ b/core/io/compression.h @@ -32,15 +32,16 @@ #define COMPRESSION_H #include "core/typedefs.h" +#include "core/vector.h" class Compression { - public: static int zlib_level; static int gzip_level; static int zstd_level; static bool zstd_long_distance_matching; static int zstd_window_log_size; + static int gzip_chunk; enum Mode { MODE_FASTLZ, @@ -52,8 +53,9 @@ public: static int compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD); static int get_max_compressed_buffer_size(int p_src_size, Mode p_mode = MODE_ZSTD); static int decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD); + static int decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode); - Compression(); + Compression() {} }; #endif // COMPRESSION_H diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 5c25cad770..1af9142317 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -34,30 +34,26 @@ #include "core/os/keyboard.h" #include "core/variant_parser.h" -PoolStringArray ConfigFile::_get_sections() const { - +PackedStringArray ConfigFile::_get_sections() const { List<String> s; get_sections(&s); - PoolStringArray arr; + PackedStringArray arr; arr.resize(s.size()); int idx = 0; for (const List<String>::Element *E = s.front(); E; E = E->next()) { - arr.set(idx++, E->get()); } return arr; } -PoolStringArray ConfigFile::_get_section_keys(const String &p_section) const { - +PackedStringArray ConfigFile::_get_section_keys(const String &p_section) const { List<String> s; get_section_keys(p_section, &s); - PoolStringArray arr; + PackedStringArray arr; arr.resize(s.size()); int idx = 0; for (const List<String>::Element *E = s.front(); E; E = E->next()) { - arr.set(idx++, E->get()); } @@ -65,11 +61,11 @@ PoolStringArray ConfigFile::_get_section_keys(const String &p_section) const { } void ConfigFile::set_value(const String &p_section, const String &p_key, const Variant &p_value) { - if (p_value.get_type() == Variant::NIL) { //erase - if (!values.has(p_section)) + if (!values.has(p_section)) { return; // ? + } values[p_section].erase(p_key); if (values[p_section].empty()) { values.erase(p_section); @@ -83,35 +79,36 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V values[p_section][p_key] = p_value; } } -Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const { +Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const { if (!values.has(p_section) || !values[p_section].has(p_key)) { - ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, p_default, "Couldn't find the given section/key and no default was given."); + ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(), + vformat("Couldn't find the given section \"%s\" and key \"%s\", and no default was given.", p_section, p_key)); return p_default; } + return values[p_section][p_key]; } bool ConfigFile::has_section(const String &p_section) const { - return values.has(p_section); } -bool ConfigFile::has_section_key(const String &p_section, const String &p_key) const { - if (!values.has(p_section)) +bool ConfigFile::has_section_key(const String &p_section, const String &p_key) const { + if (!values.has(p_section)) { return false; + } return values[p_section].has(p_key); } void ConfigFile::get_sections(List<String> *r_sections) const { - - for (OrderedHashMap<String, OrderedHashMap<String, Variant> >::ConstElement E = values.front(); E; E = E.next()) { + for (OrderedHashMap<String, OrderedHashMap<String, Variant>>::ConstElement E = values.front(); E; E = E.next()) { r_sections->push_back(E.key()); } } -void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const { - ERR_FAIL_COND_MSG(!values.has(p_section), "Cannont get keys from nonexistent section '" + p_section + "'."); +void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const { + ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot get keys from nonexistent section \"%s\".", p_section)); for (OrderedHashMap<String, Variant>::ConstElement E = values[p_section].front(); E; E = E.next()) { r_keys->push_back(E.key()); @@ -119,25 +116,25 @@ void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) } void ConfigFile::erase_section(const String &p_section) { - + ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase nonexistent section \"%s\".", p_section)); values.erase(p_section); } void ConfigFile::erase_section_key(const String &p_section, const String &p_key) { - - ERR_FAIL_COND_MSG(!values.has(p_section), "Cannot erase key from nonexistent section '" + p_section + "'."); + ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase key \"%s\" from nonexistent section \"%s\".", p_key, p_section)); + ERR_FAIL_COND_MSG(!values[p_section].has(p_key), vformat("Cannot erase nonexistent key \"%s\" from section \"%s\".", p_key, p_section)); values[p_section].erase(p_key); } Error ConfigFile::save(const String &p_path) { - Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err); if (err) { - if (file) + if (file) { memdelete(file); + } return err; } @@ -145,12 +142,12 @@ Error ConfigFile::save(const String &p_path) { } Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_WRITE_AES256); @@ -163,12 +160,12 @@ Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_ } Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE, &err); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_WRITE_AES256); @@ -182,15 +179,13 @@ Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass } Error ConfigFile::_internal_save(FileAccess *file) { - - for (OrderedHashMap<String, OrderedHashMap<String, Variant> >::Element E = values.front(); E; E = E.next()) { - - if (E != values.front()) + for (OrderedHashMap<String, OrderedHashMap<String, Variant>>::Element E = values.front(); E; E = E.next()) { + if (E != values.front()) { file->store_string("\n"); + } file->store_string("[" + E.key() + "]\n\n"); for (OrderedHashMap<String, Variant>::Element F = E.get().front(); F; F = F.next()) { - String vstr; VariantWriter::write_to_string(F.get(), vstr); file->store_string(F.key() + "=" + vstr + "\n"); @@ -203,23 +198,23 @@ Error ConfigFile::_internal_save(FileAccess *file) { } Error ConfigFile::load(const String &p_path) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - if (!f) + if (!f) { return err; + } return _internal_load(p_path, f); } Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_READ); @@ -232,12 +227,12 @@ Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_ } Error ConfigFile::load_encrypted_pass(const String &p_path, const String &p_pass) { - Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - if (err) + if (err) { return err; + } FileAccessEncrypted *fae = memnew(FileAccessEncrypted); err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_READ); @@ -251,10 +246,23 @@ Error ConfigFile::load_encrypted_pass(const String &p_path, const String &p_pass } Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { - VariantParser::StreamFile stream; stream.f = f; + Error err = _parse(p_path, &stream); + + memdelete(f); + + return err; +} + +Error ConfigFile::parse(const String &p_data) { + VariantParser::StreamString stream; + stream.s = p_data; + return _parse("<string>", &stream); +} + +Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) { String assign; Variant value; VariantParser::Tag next_tag; @@ -265,18 +273,15 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { String section; while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); - Error err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true); + Error err = VariantParser::parse_tag_assign_eof(p_stream, lines, error_text, next_tag, assign, value, nullptr, true); if (err == ERR_FILE_EOF) { - memdelete(f); return OK; } else if (err != OK) { - ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text + "."); - memdelete(f); + ERR_PRINT(vformat("ConfigFile parse error at %s:%d: %s.", p_path, lines, error_text)); return err; } @@ -286,10 +291,11 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) { section = next_tag.name; } } + + return OK; } void ConfigFile::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_value", "section", "key", "value"), &ConfigFile::set_value); ClassDB::bind_method(D_METHOD("get_value", "section", "key", "default"), &ConfigFile::get_value, DEFVAL(Variant())); @@ -303,14 +309,12 @@ void ConfigFile::_bind_methods() { ClassDB::bind_method(D_METHOD("erase_section_key", "section", "key"), &ConfigFile::erase_section_key); ClassDB::bind_method(D_METHOD("load", "path"), &ConfigFile::load); + ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse); ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save); ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted); - ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "pass"), &ConfigFile::load_encrypted_pass); + ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "password"), &ConfigFile::load_encrypted_pass); ClassDB::bind_method(D_METHOD("save_encrypted", "path", "key"), &ConfigFile::save_encrypted); - ClassDB::bind_method(D_METHOD("save_encrypted_pass", "path", "pass"), &ConfigFile::save_encrypted_pass); -} - -ConfigFile::ConfigFile() { + ClassDB::bind_method(D_METHOD("save_encrypted_pass", "path", "password"), &ConfigFile::save_encrypted_pass); } diff --git a/core/io/config_file.h b/core/io/config_file.h index 95a581d156..ae06960f02 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -34,18 +34,20 @@ #include "core/ordered_hash_map.h" #include "core/os/file_access.h" #include "core/reference.h" +#include "core/variant_parser.h" class ConfigFile : public Reference { - GDCLASS(ConfigFile, Reference); - OrderedHashMap<String, OrderedHashMap<String, Variant> > values; + OrderedHashMap<String, OrderedHashMap<String, Variant>> values; - PoolStringArray _get_sections() const; - PoolStringArray _get_section_keys(const String &p_section) const; + PackedStringArray _get_sections() const; + PackedStringArray _get_section_keys(const String &p_section) const; Error _internal_load(const String &p_path, FileAccess *f); Error _internal_save(FileAccess *file); + Error _parse(const String &p_path, VariantParser::Stream *p_stream); + protected: static void _bind_methods(); @@ -64,14 +66,13 @@ public: Error save(const String &p_path); Error load(const String &p_path); + Error parse(const String &p_data); Error load_encrypted(const String &p_path, const Vector<uint8_t> &p_key); Error load_encrypted_pass(const String &p_path, const String &p_pass); Error save_encrypted(const String &p_path, const Vector<uint8_t> &p_key); Error save_encrypted_pass(const String &p_path, const String &p_pass); - - ConfigFile(); }; #endif // CONFIG_FILE_H diff --git a/core/io/dtls_server.cpp b/core/io/dtls_server.cpp new file mode 100644 index 0000000000..e43b1f5385 --- /dev/null +++ b/core/io/dtls_server.cpp @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* dtls_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 "dtls_server.h" + +#include "core/os/file_access.h" +#include "core/project_settings.h" + +DTLSServer *(*DTLSServer::_create)() = nullptr; +bool DTLSServer::available = false; + +DTLSServer *DTLSServer::create() { + if (_create) { + return _create(); + } + return nullptr; +} + +bool DTLSServer::is_available() { + return available; +} + +void DTLSServer::_bind_methods() { + ClassDB::bind_method(D_METHOD("setup", "key", "certificate", "chain"), &DTLSServer::setup, DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("take_connection", "udp_peer"), &DTLSServer::take_connection); +} diff --git a/core/io/dtls_server.h b/core/io/dtls_server.h new file mode 100644 index 0000000000..ae1d3bcd98 --- /dev/null +++ b/core/io/dtls_server.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* dtls_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 DTLS_SERVER_H +#define DTLS_SERVER_H + +#include "core/io/net_socket.h" +#include "core/io/packet_peer_dtls.h" + +class DTLSServer : public Reference { + GDCLASS(DTLSServer, Reference); + +protected: + static DTLSServer *(*_create)(); + static void _bind_methods(); + + static bool available; + +public: + static bool is_available(); + static DTLSServer *create(); + + virtual Error setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0; + virtual void stop() = 0; + virtual Ref<PacketPeerDTLS> take_connection(Ref<PacketPeerUDP> p_peer) = 0; + + DTLSServer() {} +}; + +#endif // DTLS_SERVER_H diff --git a/core/io/file_access_buffered.cpp b/core/io/file_access_buffered.cpp index 6ac065a19b..6208f3a4d1 100644 --- a/core/io/file_access_buffered.cpp +++ b/core/io/file_access_buffered.cpp @@ -33,28 +33,23 @@ #include "core/error_macros.h" Error FileAccessBuffered::set_error(Error p_error) const { - return (last_error = p_error); } void FileAccessBuffered::set_cache_size(int p_size) { - cache_size = p_size; } int FileAccessBuffered::get_cache_size() { - return cache_size; } int FileAccessBuffered::cache_data_left() const { - if (file.offset >= file.size) { return 0; } if (cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size()) { - return read_data_block(file.offset, cache_size); } @@ -62,37 +57,30 @@ int FileAccessBuffered::cache_data_left() const { } void FileAccessBuffered::seek(size_t p_position) { - file.offset = p_position; } void FileAccessBuffered::seek_end(int64_t p_position) { - file.offset = file.size + p_position; } size_t FileAccessBuffered::get_position() const { - return file.offset; } size_t FileAccessBuffered::get_len() const { - return file.size; } bool FileAccessBuffered::eof_reached() const { - return file.offset > file.size; } uint8_t FileAccessBuffered::get_8() const { - ERR_FAIL_COND_V_MSG(!file.open, 0, "Can't get data, when file is not opened."); uint8_t byte = 0; if (cache_data_left() >= 1) { - byte = cache.buffer[file.offset - cache.offset]; } @@ -102,18 +90,15 @@ uint8_t FileAccessBuffered::get_8() const { } int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { - ERR_FAIL_COND_V_MSG(!file.open, -1, "Can't get buffer, when file is not opened."); if (p_length > cache_size) { - int total_read = 0; if (!(cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size())) { - int size = (cache.buffer.size() - (file.offset - cache.offset)); size = size - (size % 4); - //PoolVector<uint8_t>::Read read = cache.buffer.read(); + //const uint8_t* read = cache.buffer.ptr(); //memcpy(p_dest, read.ptr() + (file.offset - cache.offset), size); memcpy(p_dest, cache.buffer.ptr() + (file.offset - cache.offset), size); p_dest += size; @@ -134,7 +119,6 @@ int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { int to_read = p_length; int total_read = 0; while (to_read > 0) { - int left = cache_data_left(); if (left == 0) { file.offset += to_read; @@ -145,7 +129,7 @@ int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { } int r = MIN(left, to_read); - //PoolVector<uint8_t>::Read read = cache.buffer.read(); + //const uint8_t* read = cache.buffer.ptr(); //memcpy(p_dest+total_read, &read.ptr()[file.offset - cache.offset], r); memcpy(p_dest + total_read, cache.buffer.ptr() + (file.offset - cache.offset), r); @@ -158,19 +142,9 @@ int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const { } bool FileAccessBuffered::is_open() const { - return file.open; } Error FileAccessBuffered::get_error() const { - return last_error; } - -FileAccessBuffered::FileAccessBuffered() { - - cache_size = DEFAULT_CACHE_SIZE; -} - -FileAccessBuffered::~FileAccessBuffered() { -} diff --git a/core/io/file_access_buffered.h b/core/io/file_access_buffered.h index 5e2939c1a2..99d5ce903d 100644 --- a/core/io/file_access_buffered.h +++ b/core/io/file_access_buffered.h @@ -32,18 +32,17 @@ #define FILE_ACCESS_BUFFERED_H #include "core/os/file_access.h" -#include "core/pool_vector.h" + #include "core/ustring.h" class FileAccessBuffered : public FileAccess { - public: enum { DEFAULT_CACHE_SIZE = 128 * 1024, }; private: - int cache_size; + int cache_size = DEFAULT_CACHE_SIZE; int cache_data_left() const; mutable Error last_error; @@ -52,21 +51,19 @@ protected: Error set_error(Error p_error) const; mutable struct File { - - bool open; - int size; - int offset; + bool open = false; + int size = 0; + int offset = 0; String name; - int access_flags; + int access_flags = 0; } file; mutable struct Cache { - Vector<uint8_t> buffer; - int offset; + int offset = 0; } cache; - virtual int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const = 0; + virtual int read_data_block(int p_offset, int p_size, uint8_t *p_dest = nullptr) const = 0; void set_cache_size(int p_size); int get_cache_size(); @@ -87,8 +84,8 @@ public: virtual Error get_error() const; - FileAccessBuffered(); - virtual ~FileAccessBuffered(); + FileAccessBuffered() {} + virtual ~FileAccessBuffered() {} }; #endif diff --git a/core/io/file_access_buffered_fa.h b/core/io/file_access_buffered_fa.h index c17652080c..f22e54e154 100644 --- a/core/io/file_access_buffered_fa.h +++ b/core/io/file_access_buffered_fa.h @@ -35,70 +35,60 @@ template <class T> class FileAccessBufferedFA : public FileAccessBuffered { - T f; int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const { - ERR_FAIL_COND_V_MSG(!f.is_open(), -1, "Can't read data block when file is not opened."); ((T *)&f)->seek(p_offset); if (p_dest) { - f.get_buffer(p_dest, p_size); return p_size; } else { - cache.offset = p_offset; cache.buffer.resize(p_size); - // on PoolVector - //PoolVector<uint8_t>::Write write = cache.buffer.write(); + // on Vector + //uint8_t* write = cache.buffer.ptrw(); //f.get_buffer(write.ptrw(), p_size); // on vector f.get_buffer(cache.buffer.ptrw(), p_size); return p_size; - }; - }; + } + } static FileAccess *create() { - return memnew(FileAccessBufferedFA<T>()); - }; + } protected: virtual void _set_access_type(AccessType p_access) { f._set_access_type(p_access); FileAccessBuffered::_set_access_type(p_access); - }; + } public: void flush() { - f.flush(); - }; + } void store_8(uint8_t p_dest) { - f.store_8(p_dest); - }; + } void store_buffer(const uint8_t *p_src, int p_length) { - f.store_buffer(p_src, p_length); - }; + } bool file_exists(const String &p_name) { - return f.file_exists(p_name); - }; + } Error _open(const String &p_path, int p_mode_flags) { - close(); Error ret = f._open(p_path, p_mode_flags); @@ -116,10 +106,9 @@ public: cache.offset = 0; return set_error(OK); - }; + } void close() { - f.close(); file.offset = 0; @@ -130,16 +119,9 @@ public: cache.buffer.resize(0); cache.offset = 0; set_error(OK); - }; - - /* - static void make_default() { - FileAccess::create_func = FileAccessBufferedFA<T>::create; - }; - */ + } virtual uint64_t _get_modified_time(const String &p_file) { - return f._get_modified_time(p_file); } @@ -151,9 +133,7 @@ public: return f._set_unix_permissions(p_file, p_permissions); } - FileAccessBufferedFA(){ - - }; + FileAccessBufferedFA() {} }; #endif // FILE_ACCESS_BUFFERED_FA_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 87ead37b91..7817ccb773 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -33,13 +33,13 @@ #include "core/print_string.h" void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_mode, int p_block_size) { - magic = p_magic.ascii().get_data(); - if (magic.length() > 4) + if (magic.length() > 4) { magic = magic.substr(0, 4); - else { - while (magic.length() < 4) + } else { + while (magic.length() < 4) { magic += " "; + } } cmode = p_mode; @@ -59,16 +59,18 @@ void FileAccessCompressed::configure(const String &p_magic, Compression::Mode p_ } Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { - f = p_base; cmode = (Compression::Mode)f->get_32(); block_size = f->get_32(); + if (block_size == 0) { + f = nullptr; // Let the caller to handle the FileAccess object if failed to open as compressed file. + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Can't open compressed file '" + p_base->get_path() + "' with block size 0, it is corrupted."); + } read_total = f->get_32(); int bc = (read_total / block_size) + 1; int acc_ofs = f->get_position() + bc * 4; int max_bs = 0; for (int i = 0; i < bc; i++) { - ReadBlock rb; rb.offset = acc_ofs; rb.csize = f->get_32(); @@ -94,23 +96,22 @@ Error FileAccessCompressed::open_after_magic(FileAccess *p_base) { } Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { - ERR_FAIL_COND_V(p_mode_flags == READ_WRITE, ERR_UNAVAILABLE); - if (f) + if (f) { close(); + } Error err; f = FileAccess::open(p_path, p_mode_flags, &err); if (err != OK) { //not openable - f = NULL; + f = nullptr; return err; } if (p_mode_flags & WRITE) { - buffer.clear(); writing = true; write_pos = 0; @@ -121,25 +122,23 @@ Error FileAccessCompressed::_open(const String &p_path, int p_mode_flags) { //don't store anything else unless it's done saving! } else { - char rmagic[5]; f->get_buffer((uint8_t *)rmagic, 4); rmagic[4] = 0; - if (magic != rmagic) { + if (magic != rmagic || open_after_magic(f) != OK) { memdelete(f); - f = NULL; + f = nullptr; return ERR_FILE_UNRECOGNIZED; } - - open_after_magic(f); } return OK; } -void FileAccessCompressed::close() { - if (!f) +void FileAccessCompressed::close() { + if (!f) { return; + } if (writing) { //save block table and all compressed blocks @@ -157,7 +156,6 @@ void FileAccessCompressed::close() { Vector<int> block_sizes; for (int i = 0; i < bc; i++) { - int bl = i == (bc - 1) ? write_max % block_size : block_size; uint8_t *bp = &write_ptr[i * block_size]; @@ -170,40 +168,36 @@ void FileAccessCompressed::close() { } f->seek(16); //ok write block sizes - for (int i = 0; i < bc; i++) + for (int i = 0; i < bc; i++) { f->store_32(block_sizes[i]); + } f->seek_end(); f->store_buffer((const uint8_t *)mgc.get_data(), mgc.length()); //magic at the end too buffer.clear(); } else { - comp_buffer.clear(); buffer.clear(); read_blocks.clear(); } memdelete(f); - f = NULL; + f = nullptr; } bool FileAccessCompressed::is_open() const { - - return f != NULL; + return f != nullptr; } void FileAccessCompressed::seek(size_t p_position) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); if (writing) { - ERR_FAIL_COND(p_position > write_max); write_pos = p_position; } else { - ERR_FAIL_COND(p_position > read_total); if (p_position == read_total) { at_end = true; @@ -212,7 +206,6 @@ void FileAccessCompressed::seek(size_t p_position) { read_eof = false; int block_idx = p_position / block_size; if (block_idx != read_block) { - read_block = block_idx; f->seek(read_blocks[read_block].offset); f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize); @@ -226,32 +219,26 @@ void FileAccessCompressed::seek(size_t p_position) { } void FileAccessCompressed::seek_end(int64_t p_position) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); if (writing) { - seek(write_max + p_position); } else { - seek(read_total + p_position); } } -size_t FileAccessCompressed::get_position() const { +size_t FileAccessCompressed::get_position() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); if (writing) { - return write_pos; } else { - return read_block * block_size + read_pos; } } -size_t FileAccessCompressed::get_len() const { +size_t FileAccessCompressed::get_len() const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); if (writing) { - return write_max; } else { return read_total; @@ -259,7 +246,6 @@ size_t FileAccessCompressed::get_len() const { } bool FileAccessCompressed::eof_reached() const { - ERR_FAIL_COND_V_MSG(!f, false, "File must be opened before use."); if (writing) { return false; @@ -269,7 +255,6 @@ bool FileAccessCompressed::eof_reached() const { } uint8_t FileAccessCompressed::get_8() const { - ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); @@ -299,8 +284,8 @@ uint8_t FileAccessCompressed::get_8() const { return ret; } -int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { +int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use."); ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); @@ -310,7 +295,6 @@ int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { } for (int i = 0; i < p_length; i++) { - p_dst[i] = read_ptr[read_pos]; read_pos++; if (read_pos >= read_block_size) { @@ -326,8 +310,9 @@ int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { } else { read_block--; at_end = true; - if (i < p_length - 1) + if (i < p_length - 1) { read_eof = true; + } return i; } } @@ -337,7 +322,6 @@ int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessCompressed::get_error() const { - return read_eof ? ERR_FILE_EOF : OK; } @@ -349,7 +333,6 @@ void FileAccessCompressed::flush() { } void FileAccessCompressed::store_8(uint8_t p_dest) { - ERR_FAIL_COND_MSG(!f, "File must be opened before use."); ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); @@ -358,25 +341,26 @@ void FileAccessCompressed::store_8(uint8_t p_dest) { } bool FileAccessCompressed::file_exists(const String &p_name) { - FileAccess *fa = FileAccess::open(p_name, FileAccess::READ); - if (!fa) + if (!fa) { return false; + } memdelete(fa); return true; } uint64_t FileAccessCompressed::_get_modified_time(const String &p_file) { - - if (f) + if (f) { return f->get_modified_time(p_file); - else + } else { return 0; + } } uint32_t FileAccessCompressed::_get_unix_permissions(const String &p_file) { - if (f) + if (f) { return f->_get_unix_permissions(p_file); + } return 0; } @@ -387,27 +371,8 @@ Error FileAccessCompressed::_set_unix_permissions(const String &p_file, uint32_t return FAILED; } -FileAccessCompressed::FileAccessCompressed() : - cmode(Compression::MODE_ZSTD), - writing(false), - write_ptr(0), - write_buffer_size(0), - write_max(0), - block_size(0), - read_eof(false), - at_end(false), - read_ptr(NULL), - read_block(0), - read_block_count(0), - read_block_size(0), - read_pos(0), - read_total(0), - magic("GCMP"), - f(NULL) { -} - FileAccessCompressed::~FileAccessCompressed() { - - if (f) + if (f) { close(); + } } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index 0bb311faa8..52284b347e 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -35,16 +35,15 @@ #include "core/os/file_access.h" class FileAccessCompressed : public FileAccess { - - Compression::Mode cmode; - bool writing; - uint32_t write_pos; - uint8_t *write_ptr; - uint32_t write_buffer_size; - uint32_t write_max; - uint32_t block_size; - mutable bool read_eof; - mutable bool at_end; + Compression::Mode cmode = Compression::MODE_ZSTD; + bool writing = false; + uint32_t write_pos = 0; + uint8_t *write_ptr = nullptr; + uint32_t write_buffer_size = 0; + uint32_t write_max = 0; + uint32_t block_size = 0; + mutable bool read_eof = false; + mutable bool at_end = false; struct ReadBlock { int csize; @@ -52,17 +51,17 @@ class FileAccessCompressed : public FileAccess { }; mutable Vector<uint8_t> comp_buffer; - uint8_t *read_ptr; - mutable int read_block; - int read_block_count; - mutable int read_block_size; - mutable int read_pos; + uint8_t *read_ptr = nullptr; + mutable int read_block = 0; + int read_block_count = 0; + mutable int read_block_size = 0; + mutable int read_pos = 0; Vector<ReadBlock> read_blocks; - uint32_t read_total; + uint32_t read_total = 0; - String magic; + String magic = "GCMP"; mutable Vector<uint8_t> buffer; - FileAccess *f; + FileAccess *f = nullptr; public: void configure(const String &p_magic, Compression::Mode p_mode = Compression::MODE_ZSTD, int p_block_size = 4096); @@ -94,7 +93,7 @@ public: virtual uint32_t _get_unix_permissions(const String &p_file); virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); - FileAccessCompressed(); + FileAccessCompressed() {} virtual ~FileAccessCompressed(); }; diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index 20b6fc81dc..eb684f457e 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -37,56 +37,54 @@ #include <stdio.h> -#define COMP_MAGIC 0x43454447 - -Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode) { - - ERR_FAIL_COND_V_MSG(file != NULL, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open."); +Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic) { + ERR_FAIL_COND_V_MSG(file != nullptr, ERR_ALREADY_IN_USE, "Can't open file while another file from path '" + file->get_path_absolute() + "' is open."); ERR_FAIL_COND_V(p_key.size() != 32, ERR_INVALID_PARAMETER); pos = 0; eofed = false; + use_magic = p_with_magic; if (p_mode == MODE_WRITE_AES256) { - data.clear(); writing = true; file = p_base; - mode = p_mode; key = p_key; } else if (p_mode == MODE_READ) { - writing = false; key = p_key; - uint32_t magic = p_base->get_32(); - ERR_FAIL_COND_V(magic != COMP_MAGIC, ERR_FILE_UNRECOGNIZED); - mode = Mode(p_base->get_32()); - ERR_FAIL_INDEX_V(mode, MODE_MAX, ERR_FILE_CORRUPT); - ERR_FAIL_COND_V(mode == 0, ERR_FILE_CORRUPT); + if (use_magic) { + uint32_t magic = p_base->get_32(); + ERR_FAIL_COND_V(magic != ENCRYPTED_HEADER_MAGIC, ERR_FILE_UNRECOGNIZED); + } unsigned char md5d[16]; p_base->get_buffer(md5d, 16); length = p_base->get_64(); + + unsigned char iv[16]; + for (int i = 0; i < 16; i++) { + iv[i] = p_base->get_8(); + } + base = p_base->get_position(); ERR_FAIL_COND_V(p_base->get_len() < base + length, ERR_FILE_CORRUPT); uint32_t ds = length; if (ds % 16) { ds += 16 - (ds % 16); } - data.resize(ds); uint32_t blen = p_base->get_buffer(data.ptrw(), ds); ERR_FAIL_COND_V(blen != ds, ERR_FILE_CORRUPT); - CryptoCore::AESContext ctx; - ctx.set_decode_key(key.ptrw(), 256); - - for (size_t i = 0; i < ds; i += 16) { + { + CryptoCore::AESContext ctx; - ctx.decrypt_ecb(&data.write[i], &data.write[i]); + ctx.set_encode_key(key.ptrw(), 256); // Due to the nature of CFB, same key schedule is used for both encryption and decryption! + ctx.decrypt_cfb(ds, iv, data.ptrw(), data.ptrw()); } data.resize(length); @@ -103,13 +101,11 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8 } Error FileAccessEncrypted::open_and_parse_password(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); for (int i = 0; i < 32; i++) { - key.write[i] = cs[i]; } @@ -117,16 +113,34 @@ Error FileAccessEncrypted::open_and_parse_password(FileAccess *p_base, const Str } Error FileAccessEncrypted::_open(const String &p_path, int p_mode_flags) { - return OK; } + void FileAccessEncrypted::close() { + if (!file) { + return; + } + + _release(); - if (!file) + file->close(); + memdelete(file); + + file = nullptr; +} + +void FileAccessEncrypted::release() { + if (!file) { return; + } - if (writing) { + _release(); + file = nullptr; +} + +void FileAccessEncrypted::_release() { + if (writing) { Vector<uint8_t> compressed; size_t len = data.size(); if (len % 16) { @@ -145,82 +159,72 @@ void FileAccessEncrypted::close() { CryptoCore::AESContext ctx; ctx.set_encode_key(key.ptrw(), 256); - for (size_t i = 0; i < len; i += 16) { - - ctx.encrypt_ecb(&compressed.write[i], &compressed.write[i]); + if (use_magic) { + file->store_32(ENCRYPTED_HEADER_MAGIC); } - file->store_32(COMP_MAGIC); - file->store_32(mode); - file->store_buffer(hash, 16); file->store_64(data.size()); - file->store_buffer(compressed.ptr(), compressed.size()); - file->close(); - memdelete(file); - file = NULL; - data.clear(); + unsigned char iv[16]; + for (int i = 0; i < 16; i++) { + iv[i] = Math::rand() % 256; + file->store_8(iv[i]); + } - } else { + ctx.encrypt_cfb(len, iv, compressed.ptrw(), compressed.ptrw()); - file->close(); - memdelete(file); + file->store_buffer(compressed.ptr(), compressed.size()); data.clear(); - file = NULL; } } bool FileAccessEncrypted::is_open() const { - - return file != NULL; + return file != nullptr; } String FileAccessEncrypted::get_path() const { - - if (file) + if (file) { return file->get_path(); - else + } else { return ""; + } } String FileAccessEncrypted::get_path_absolute() const { - - if (file) + if (file) { return file->get_path_absolute(); - else + } else { return ""; + } } void FileAccessEncrypted::seek(size_t p_position) { - - if (p_position > (size_t)data.size()) + if (p_position > (size_t)data.size()) { p_position = data.size(); + } pos = p_position; eofed = false; } void FileAccessEncrypted::seek_end(int64_t p_position) { - seek(data.size() + p_position); } -size_t FileAccessEncrypted::get_position() const { +size_t FileAccessEncrypted::get_position() const { return pos; } -size_t FileAccessEncrypted::get_len() const { +size_t FileAccessEncrypted::get_len() const { return data.size(); } bool FileAccessEncrypted::eof_reached() const { - return eofed; } uint8_t FileAccessEncrypted::get_8() const { - ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); if (pos >= data.size()) { eofed = true; @@ -231,13 +235,12 @@ uint8_t FileAccessEncrypted::get_8() const { pos++; return b; } -int FileAccessEncrypted::get_buffer(uint8_t *p_dst, int p_length) const { +int FileAccessEncrypted::get_buffer(uint8_t *p_dst, int p_length) const { ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); int to_copy = MIN(p_length, data.size() - pos); for (int i = 0; i < to_copy; i++) { - p_dst[i] = data[pos++]; } @@ -249,25 +252,19 @@ int FileAccessEncrypted::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessEncrypted::get_error() const { - return eofed ? ERR_FILE_EOF : OK; } void FileAccessEncrypted::store_buffer(const uint8_t *p_src, int p_length) { - ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); if (pos < data.size()) { - for (int i = 0; i < p_length; i++) { - store_8(p_src[i]); } } else if (pos == data.size()) { - data.resize(pos + p_length); for (int i = 0; i < p_length; i++) { - data.write[pos + i] = p_src[i]; } pos += p_length; @@ -281,7 +278,6 @@ void FileAccessEncrypted::flush() { } void FileAccessEncrypted::store_8(uint8_t p_dest) { - ERR_FAIL_COND_MSG(!writing, "File has not been opened in read mode."); if (pos < data.size()) { @@ -294,21 +290,19 @@ void FileAccessEncrypted::store_8(uint8_t p_dest) { } bool FileAccessEncrypted::file_exists(const String &p_name) { - FileAccess *fa = FileAccess::open(p_name, FileAccess::READ); - if (!fa) + if (!fa) { return false; + } memdelete(fa); return true; } uint64_t FileAccessEncrypted::_get_modified_time(const String &p_file) { - return 0; } uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) { - return 0; } @@ -317,17 +311,8 @@ Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t return ERR_UNAVAILABLE; } -FileAccessEncrypted::FileAccessEncrypted() { - - file = NULL; - pos = 0; - eofed = false; - mode = MODE_MAX; - writing = false; -} - FileAccessEncrypted::~FileAccessEncrypted() { - - if (file) + if (file) { close(); + } } diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 7a9f4ecdd8..fddc6842f3 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -33,6 +33,8 @@ #include "core/os/file_access.h" +#define ENCRYPTED_HEADER_MAGIC 0x43454447 + class FileAccessEncrypted : public FileAccess { public: enum Mode { @@ -42,22 +44,25 @@ public: }; private: - Mode mode; Vector<uint8_t> key; - bool writing; - FileAccess *file; + bool writing = false; + FileAccess *file = nullptr; size_t base; size_t length; Vector<uint8_t> data; - mutable int pos; - mutable bool eofed; + mutable int pos = 0; + mutable bool eofed = false; + bool use_magic = true; + + void _release(); public: - Error open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode); + Error open_and_parse(FileAccess *p_base, const Vector<uint8_t> &p_key, Mode p_mode, bool p_with_magic = true); Error open_and_parse_password(FileAccess *p_base, const String &p_key, Mode p_mode); virtual Error _open(const String &p_path, int p_mode_flags); ///< open a file virtual void close(); ///< close a file + virtual void release(); ///< finish and keep base file open virtual bool is_open() const; ///< true when file is open virtual String get_path() const; /// returns the path for the current open file @@ -85,7 +90,7 @@ public: virtual uint32_t _get_unix_permissions(const String &p_file); virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions); - FileAccessEncrypted(); + FileAccessEncrypted() {} ~FileAccessEncrypted(); }; diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index fc318b3dd2..a65ff92a89 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -35,47 +35,44 @@ #include "core/os/dir_access.h" #include "core/project_settings.h" -static Map<String, Vector<uint8_t> > *files = NULL; +static Map<String, Vector<uint8_t>> *files = nullptr; void FileAccessMemory::register_file(String p_name, Vector<uint8_t> p_data) { - if (!files) { - files = memnew((Map<String, Vector<uint8_t> >)); + files = memnew((Map<String, Vector<uint8_t>>)); } String name; - if (ProjectSettings::get_singleton()) + if (ProjectSettings::get_singleton()) { name = ProjectSettings::get_singleton()->globalize_path(p_name); - else + } else { name = p_name; + } //name = DirAccess::normalize_path(name); (*files)[name] = p_data; } void FileAccessMemory::cleanup() { - - if (!files) + if (!files) { return; + } memdelete(files); } FileAccess *FileAccessMemory::create() { - return memnew(FileAccessMemory); } bool FileAccessMemory::file_exists(const String &p_name) { - String name = fix_path(p_name); //name = DirAccess::normalize_path(name); - return files && (files->find(name) != NULL); + return files && (files->find(name) != nullptr); } Error FileAccessMemory::open_custom(const uint8_t *p_data, int p_len) { - data = (uint8_t *)p_data; length = p_len; pos = 0; @@ -83,13 +80,12 @@ Error FileAccessMemory::open_custom(const uint8_t *p_data, int p_len) { } Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { - ERR_FAIL_COND_V(!files, ERR_FILE_NOT_FOUND); String name = fix_path(p_path); //name = DirAccess::normalize_path(name); - Map<String, Vector<uint8_t> >::Element *E = files->find(name); + Map<String, Vector<uint8_t>>::Element *E = files->find(name); ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, "Can't find file '" + p_path + "'."); data = E->get().ptrw(); @@ -100,46 +96,38 @@ Error FileAccessMemory::_open(const String &p_path, int p_mode_flags) { } void FileAccessMemory::close() { - - data = NULL; + data = nullptr; } bool FileAccessMemory::is_open() const { - - return data != NULL; + return data != nullptr; } void FileAccessMemory::seek(size_t p_position) { - ERR_FAIL_COND(!data); pos = p_position; } void FileAccessMemory::seek_end(int64_t p_position) { - ERR_FAIL_COND(!data); pos = length + p_position; } size_t FileAccessMemory::get_position() const { - ERR_FAIL_COND_V(!data, 0); return pos; } size_t FileAccessMemory::get_len() const { - ERR_FAIL_COND_V(!data, 0); return length; } bool FileAccessMemory::eof_reached() const { - return pos > length; } uint8_t FileAccessMemory::get_8() const { - uint8_t ret = 0; if (pos < length) { ret = data[pos]; @@ -150,7 +138,6 @@ uint8_t FileAccessMemory::get_8() const { } int FileAccessMemory::get_buffer(uint8_t *p_dst, int p_length) const { - ERR_FAIL_COND_V(!data, -1); int left = length - pos; @@ -158,7 +145,7 @@ int FileAccessMemory::get_buffer(uint8_t *p_dst, int p_length) const { if (read < p_length) { WARN_PRINT("Reading less data than requested"); - }; + } copymem(p_dst, &data[pos], read); pos += p_length; @@ -167,7 +154,6 @@ int FileAccessMemory::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessMemory::get_error() const { - return pos >= length ? ERR_FILE_EOF : OK; } @@ -176,14 +162,12 @@ void FileAccessMemory::flush() { } void FileAccessMemory::store_8(uint8_t p_byte) { - ERR_FAIL_COND(!data); ERR_FAIL_COND(pos >= length); data[pos++] = p_byte; } void FileAccessMemory::store_buffer(const uint8_t *p_src, int p_length) { - int left = length - pos; int write = MIN(p_length, left); if (write < p_length) { @@ -193,8 +177,3 @@ void FileAccessMemory::store_buffer(const uint8_t *p_src, int p_length) { copymem(&data[pos], p_src, write); pos += p_length; } - -FileAccessMemory::FileAccessMemory() { - - data = NULL; -} diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 2db14db265..1a9bd3fbbb 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -34,8 +34,7 @@ #include "core/os/file_access.h" class FileAccessMemory : public FileAccess { - - uint8_t *data; + uint8_t *data = nullptr; int length; mutable int pos; @@ -73,7 +72,7 @@ public: virtual uint32_t _get_unix_permissions(const String &p_file) { return 0; } virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) { return FAILED; } - FileAccessMemory(); + FileAccessMemory() {} }; #endif // FILE_ACCESS_MEMORY_H diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 1b09ac7208..6890740d90 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -41,19 +41,16 @@ #define DEBUG_TIME(m_what) void FileAccessNetworkClient::lock_mutex() { - - mutex->lock(); + mutex.lock(); lockcount++; } void FileAccessNetworkClient::unlock_mutex() { - lockcount--; - mutex->unlock(); + mutex.unlock(); } void FileAccessNetworkClient::put_32(int p_32) { - uint8_t buf[4]; encode_uint32(p_32, buf); client->put_data(buf, 4); @@ -61,7 +58,6 @@ void FileAccessNetworkClient::put_32(int p_32) { } void FileAccessNetworkClient::put_64(int64_t p_64) { - uint8_t buf[8]; encode_uint64(p_64, buf); client->put_data(buf, 8); @@ -69,43 +65,38 @@ void FileAccessNetworkClient::put_64(int64_t p_64) { } int FileAccessNetworkClient::get_32() { - uint8_t buf[4]; client->get_data(buf, 4); return decode_uint32(buf); } int64_t FileAccessNetworkClient::get_64() { - uint8_t buf[8]; client->get_data(buf, 8); return decode_uint64(buf); } void FileAccessNetworkClient::_thread_func() { - client->set_no_delay(true); while (!quit) { - DEBUG_PRINT("SEM WAIT - " + itos(sem->get())); - Error err = sem->wait(); - if (err != OK) - ERR_PRINT("sem->wait() failed"); + sem.wait(); DEBUG_TIME("sem_unlock"); //DEBUG_PRINT("semwait returned "+itos(werr)); DEBUG_PRINT("MUTEX LOCK " + itos(lockcount)); lock_mutex(); DEBUG_PRINT("MUTEX PASS"); - blockrequest_mutex->lock(); - while (block_requests.size()) { - put_32(block_requests.front()->get().id); - put_32(FileAccessNetwork::COMMAND_READ_BLOCK); - put_64(block_requests.front()->get().offset); - put_32(block_requests.front()->get().size); - block_requests.pop_front(); + { + MutexLock lock(blockrequest_mutex); + while (block_requests.size()) { + put_32(block_requests.front()->get().id); + put_32(FileAccessNetwork::COMMAND_READ_BLOCK); + put_64(block_requests.front()->get().offset); + put_32(block_requests.front()->get().size); + block_requests.pop_front(); + } } - blockrequest_mutex->unlock(); DEBUG_PRINT("THREAD ITER"); @@ -115,7 +106,7 @@ void FileAccessNetworkClient::_thread_func() { int response = get_32(); DEBUG_PRINT("GET RESPONSE: " + itos(response)); - FileAccessNetwork *fa = NULL; + FileAccessNetwork *fa = nullptr; if (response != FileAccessNetwork::RESPONSE_DATA) { if (!accesses.has(id)) { @@ -124,13 +115,12 @@ void FileAccessNetworkClient::_thread_func() { } } - if (accesses.has(id)) + if (accesses.has(id)) { fa = accesses[id]; + } switch (response) { - case FileAccessNetwork::RESPONSE_OPEN: { - DEBUG_TIME("sem_open"); int status = get_32(); if (status != OK) { @@ -140,11 +130,10 @@ void FileAccessNetworkClient::_thread_func() { fa->_respond(len, Error(status)); } - fa->sem->post(); + fa->sem.post(); } break; case FileAccessNetwork::RESPONSE_DATA: { - int64_t offset = get_64(); uint32_t len = get_32(); @@ -152,22 +141,21 @@ void FileAccessNetworkClient::_thread_func() { block.resize(len); client->get_data(block.ptrw(), len); - if (fa) //may have been queued + if (fa) { //may have been queued fa->_set_block(offset, block); + } } break; case FileAccessNetwork::RESPONSE_FILE_EXISTS: { - int status = get_32(); fa->exists_modtime = status != 0; - fa->sem->post(); + fa->sem.post(); } break; case FileAccessNetwork::RESPONSE_GET_MODTIME: { - uint64_t status = get_64(); fa->exists_modtime = status; - fa->sem->post(); + fa->sem.post(); } break; } @@ -177,14 +165,12 @@ void FileAccessNetworkClient::_thread_func() { } void FileAccessNetworkClient::_thread_func(void *s) { - FileAccessNetworkClient *self = (FileAccessNetworkClient *)s; self->_thread_func(); } Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const String &p_password) { - IP_Address ip; if (p_host.is_valid_ip_address()) { @@ -220,37 +206,23 @@ Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const S return OK; } -FileAccessNetworkClient *FileAccessNetworkClient::singleton = NULL; +FileAccessNetworkClient *FileAccessNetworkClient::singleton = nullptr; FileAccessNetworkClient::FileAccessNetworkClient() { - - thread = NULL; - mutex = Mutex::create(); - blockrequest_mutex = Mutex::create(); - quit = false; singleton = this; - last_id = 0; client.instance(); - sem = Semaphore::create(); - lockcount = 0; } FileAccessNetworkClient::~FileAccessNetworkClient() { - if (thread) { quit = true; - sem->post(); + sem.post(); Thread::wait_to_finish(thread); memdelete(thread); } - - memdelete(blockrequest_mutex); - memdelete(mutex); - memdelete(sem); } void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) { - int page = p_offset / page_size; ERR_FAIL_INDEX(page, pages.size()); if (page < pages.size() - 1) { @@ -259,23 +231,24 @@ void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) ERR_FAIL_COND((p_block.size() != (int)(total_size % page_size))); } - buffer_mutex->lock(); - pages.write[page].buffer = p_block; - pages.write[page].queued = false; - buffer_mutex->unlock(); + { + MutexLock lock(buffer_mutex); + pages.write[page].buffer = p_block; + pages.write[page].queued = false; + } if (waiting_on_page == page) { waiting_on_page = -1; - page_sem->post(); + page_sem.post(); } } void FileAccessNetwork::_respond(size_t p_len, Error p_status) { - DEBUG_PRINT("GOT RESPONSE - len: " + itos(p_len) + " status: " + itos(p_status)); response = p_status; - if (response != OK) + if (response != OK) { return; + } opened = true; total_size = p_len; int pc = ((total_size - 1) / page_size) + 1; @@ -283,10 +256,10 @@ void FileAccessNetwork::_respond(size_t p_len, Error p_status) { } Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) { - ERR_FAIL_COND_V(p_mode_flags != READ, ERR_UNAVAILABLE); - if (opened) + if (opened) { close(); + } FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; DEBUG_PRINT("open: " + p_path); @@ -302,15 +275,15 @@ Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) { pos = 0; eof_flag = false; last_page = -1; - last_page_buff = NULL; + last_page_buff = nullptr; //buffers.clear(); nc->unlock_mutex(); DEBUG_PRINT("OPEN POST"); DEBUG_TIME("open_post"); - nc->sem->post(); //awaiting answer + nc->sem.post(); //awaiting answer DEBUG_PRINT("WAIT..."); - sem->wait(); + sem.wait(); DEBUG_TIME("open_end"); DEBUG_PRINT("WAIT ENDED..."); @@ -318,9 +291,9 @@ Error FileAccessNetwork::_open(const String &p_path, int p_mode_flags) { } void FileAccessNetwork::close() { - - if (!opened) + if (!opened) { return; + } FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; @@ -332,13 +305,12 @@ void FileAccessNetwork::close() { opened = false; nc->unlock_mutex(); } -bool FileAccessNetwork::is_open() const { +bool FileAccessNetwork::is_open() const { return opened; } void FileAccessNetwork::seek(size_t p_position) { - ERR_FAIL_COND_MSG(!opened, "File must be opened before use."); eof_flag = p_position > total_size; @@ -350,57 +322,53 @@ void FileAccessNetwork::seek(size_t p_position) { } void FileAccessNetwork::seek_end(int64_t p_position) { - seek(total_size + p_position); } -size_t FileAccessNetwork::get_position() const { +size_t FileAccessNetwork::get_position() const { ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use."); return pos; } -size_t FileAccessNetwork::get_len() const { +size_t FileAccessNetwork::get_len() const { ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use."); return total_size; } bool FileAccessNetwork::eof_reached() const { - ERR_FAIL_COND_V_MSG(!opened, false, "File must be opened before use."); return eof_flag; } uint8_t FileAccessNetwork::get_8() const { - uint8_t v; get_buffer(&v, 1); return v; } void FileAccessNetwork::_queue_page(int p_page) const { - - if (p_page >= pages.size()) + if (p_page >= pages.size()) { return; + } if (pages[p_page].buffer.empty() && !pages[p_page].queued) { - FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; - - nc->blockrequest_mutex->lock(); - FileAccessNetworkClient::BlockRequest br; - br.id = id; - br.offset = size_t(p_page) * page_size; - br.size = page_size; - nc->block_requests.push_back(br); - pages.write[p_page].queued = true; - nc->blockrequest_mutex->unlock(); + { + MutexLock lock(nc->blockrequest_mutex); + + FileAccessNetworkClient::BlockRequest br; + br.id = id; + br.offset = size_t(p_page) * page_size; + br.size = page_size; + nc->block_requests.push_back(br); + pages.write[p_page].queued = true; + } DEBUG_PRINT("QUEUE PAGE POST"); - nc->sem->post(); + nc->sem.post(); DEBUG_PRINT("queued " + itos(p_page)); } } int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { - //bool eof=false; if (pos + p_length > total_size) { eof_flag = true; @@ -414,29 +382,25 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { uint8_t *buff = last_page_buff; for (int i = 0; i < p_length; i++) { - int page = pos / page_size; if (page != last_page) { - buffer_mutex->lock(); + buffer_mutex.lock(); if (pages[page].buffer.empty()) { waiting_on_page = page; for (int j = 0; j < read_ahead; j++) { - _queue_page(page + j); } - buffer_mutex->unlock(); + buffer_mutex.unlock(); DEBUG_PRINT("wait"); - page_sem->wait(); + page_sem.wait(); DEBUG_PRINT("done"); } else { - for (int j = 0; j < read_ahead; j++) { - _queue_page(page + j); } //queue pages - buffer_mutex->unlock(); + buffer_mutex.unlock(); } buff = pages.write[page].buffer.ptrw(); @@ -452,7 +416,6 @@ int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const { } Error FileAccessNetwork::get_error() const { - return pos == total_size ? ERR_FILE_EOF : OK; } @@ -461,12 +424,10 @@ void FileAccessNetwork::flush() { } void FileAccessNetwork::store_8(uint8_t p_dest) { - ERR_FAIL(); } bool FileAccessNetwork::file_exists(const String &p_path) { - FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); nc->put_32(id); @@ -476,14 +437,13 @@ bool FileAccessNetwork::file_exists(const String &p_path) { nc->client->put_data((const uint8_t *)cs.ptr(), cs.length()); nc->unlock_mutex(); DEBUG_PRINT("FILE EXISTS POST"); - nc->sem->post(); - sem->wait(); + nc->sem.post(); + sem.wait(); return exists_modtime != 0; } uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) { - FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); nc->put_32(id); @@ -493,8 +453,8 @@ uint64_t FileAccessNetwork::_get_modified_time(const String &p_file) { nc->client->put_data((const uint8_t *)cs.ptr(), cs.length()); nc->unlock_mutex(); DEBUG_PRINT("MODTIME POST"); - nc->sem->post(); - sem->wait(); + nc->sem.post(); + sem.wait(); return exists_modtime; } @@ -510,7 +470,6 @@ Error FileAccessNetwork::_set_unix_permissions(const String &p_file, uint32_t p_ } void FileAccessNetwork::configure() { - GLOBAL_DEF("network/remote_fs/page_size", 65536); ProjectSettings::get_singleton()->set_custom_property_info("network/remote_fs/page_size", PropertyInfo(Variant::INT, "network/remote_fs/page_size", PROPERTY_HINT_RANGE, "1,65536,1,or_greater")); //is used as denominator and can't be zero GLOBAL_DEF("network/remote_fs/page_read_ahead", 4); @@ -518,13 +477,6 @@ void FileAccessNetwork::configure() { } FileAccessNetwork::FileAccessNetwork() { - - eof_flag = false; - opened = false; - pos = 0; - sem = Semaphore::create(); - page_sem = Semaphore::create(); - buffer_mutex = Mutex::create(); FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); id = nc->last_id++; @@ -532,17 +484,10 @@ FileAccessNetwork::FileAccessNetwork() { nc->unlock_mutex(); page_size = GLOBAL_GET("network/remote_fs/page_size"); read_ahead = GLOBAL_GET("network/remote_fs/page_read_ahead"); - last_activity_val = 0; - waiting_on_page = -1; - last_page = -1; } FileAccessNetwork::~FileAccessNetwork() { - close(); - memdelete(sem); - memdelete(page_sem); - memdelete(buffer_mutex); FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; nc->lock_mutex(); diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h index e2da1d0893..dc5ce1e883 100644 --- a/core/io/file_access_network.h +++ b/core/io/file_access_network.h @@ -39,9 +39,7 @@ class FileAccessNetwork; class FileAccessNetworkClient { - struct BlockRequest { - int id; uint64_t offset; int size; @@ -49,14 +47,15 @@ class FileAccessNetworkClient { List<BlockRequest> block_requests; - Semaphore *sem; - Thread *thread; - bool quit; - Mutex *mutex; - Mutex *blockrequest_mutex; + Semaphore sem; + Thread *thread = nullptr; + bool quit = false; + Mutex mutex; + Mutex blockrequest_mutex; Map<int, FileAccessNetwork *> accesses; Ref<StreamPeerTCP> client; - int last_id; + int last_id = 0; + int lockcount = 0; Vector<uint8_t> block; @@ -67,7 +66,6 @@ class FileAccessNetworkClient { void put_64(int64_t p_64); int get_32(); int64_t get_64(); - int lockcount; void lock_mutex(); void unlock_mutex(); @@ -84,31 +82,26 @@ public: }; class FileAccessNetwork : public FileAccess { - - Semaphore *sem; - Semaphore *page_sem; - Mutex *buffer_mutex; - bool opened; + Semaphore sem; + Semaphore page_sem; + Mutex buffer_mutex; + bool opened = false; size_t total_size; - mutable size_t pos; + mutable size_t pos = 0; int id; - mutable bool eof_flag; - mutable int last_page; - mutable uint8_t *last_page_buff; + mutable bool eof_flag = false; + mutable int last_page = -1; + mutable uint8_t *last_page_buff = nullptr; int page_size; int read_ahead; - mutable int waiting_on_page; - mutable int last_activity_val; + mutable int waiting_on_page = -1; + struct Page { - int activity; - bool queued; + int activity = 0; + bool queued = false; Vector<uint8_t> buffer; - Page() { - activity = 0; - queued = false; - } }; mutable Vector<Page> pages; diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 83ce03418a..8fdbb650d4 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -30,40 +30,41 @@ #include "file_access_pack.h" +#include "core/io/file_access_encrypted.h" +#include "core/script_language.h" #include "core/version.h" #include <stdio.h> -Error PackedData::add_pack(const String &p_path, bool p_replace_files) { - +Error PackedData::add_pack(const String &p_path, bool p_replace_files, size_t p_offset) { for (int i = 0; i < sources.size(); i++) { - - if (sources[i]->try_open_pack(p_path, p_replace_files)) { - + if (sources[i]->try_open_pack(p_path, p_replace_files, p_offset)) { return OK; - }; - }; + } + } return ERR_FILE_UNRECOGNIZED; -}; - -void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files) { +} +void PackedData::add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted) { PathMD5 pmd5(path.md5_buffer()); - //printf("adding path %ls, %lli, %lli\n", path.c_str(), pmd5.a, pmd5.b); + //printf("adding path %s, %lli, %lli\n", path.utf8().get_data(), pmd5.a, pmd5.b); bool exists = files.has(pmd5); PackedFile pf; + pf.encrypted = p_encrypted; pf.pack = pkg_path; pf.offset = ofs; pf.size = size; - for (int i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { pf.md5[i] = p_md5[i]; + } pf.src = p_src; - if (!exists || p_replace_files) + if (!exists || p_replace_files) { files[pmd5] = pf; + } if (!exists) { //search for dir @@ -75,9 +76,7 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o Vector<String> ds = p.get_base_dir().split("/"); for (int j = 0; j < ds.size(); j++) { - if (!cd->subdirs.has(ds[j])) { - PackedDir *pd = memnew(PackedDir); pd->name = ds[j]; pd->parent = cd; @@ -97,33 +96,28 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o } void PackedData::add_pack_source(PackSource *p_source) { - - if (p_source != NULL) { + if (p_source != nullptr) { sources.push_back(p_source); } -}; +} -PackedData *PackedData::singleton = NULL; +PackedData *PackedData::singleton = nullptr; PackedData::PackedData() { - singleton = this; root = memnew(PackedDir); - root->parent = NULL; - disabled = false; add_pack_source(memnew(PackedSourcePCK)); } void PackedData::_free_packed_dirs(PackedDir *p_dir) { - - for (Map<String, PackedDir *>::Element *E = p_dir->subdirs.front(); E; E = E->next()) + for (Map<String, PackedDir *>::Element *E = p_dir->subdirs.front(); E; E = E->next()) { _free_packed_dirs(E->get()); + } memdelete(p_dir); } PackedData::~PackedData() { - for (int i = 0; i < sources.size(); i++) { memdelete(sources[i]); } @@ -132,21 +126,29 @@ PackedData::~PackedData() { ////////////////////////////////////////////////////////////////// -bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) { - +bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ); - if (!f) + if (!f) { return false; + } + + f->seek(p_offset); uint32_t magic = f->get_32(); if (magic != PACK_HEADER_MAGIC) { + // loading with offset feature not supported for self contained exe files + if (p_offset != 0) { + f->close(); + memdelete(f); + ERR_FAIL_V_MSG(false, "Loading self-contained executable with offset not supported."); + } + //maybe at the end.... self contained exe f->seek_end(); f->seek(f->get_position() - 4); magic = f->get_32(); if (magic != PACK_HEADER_MAGIC) { - f->close(); memdelete(f); return false; @@ -158,7 +160,6 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) magic = f->get_32(); if (magic != PACK_HEADER_MAGIC) { - f->close(); memdelete(f); return false; @@ -181,6 +182,11 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) ERR_FAIL_V_MSG(false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + "."); } + uint32_t pack_flags = f->get_32(); + uint64_t file_base = f->get_64(); + + bool enc_directory = (pack_flags & PACK_DIR_ENCRYPTED); + for (int i = 0; i < 16; i++) { //reserved f->get_32(); @@ -188,8 +194,31 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) int file_count = f->get_32(); - for (int i = 0; i < file_count; i++) { + if (enc_directory) { + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + if (!fae) { + f->close(); + memdelete(f); + ERR_FAIL_V_MSG(false, "Can't open encrypted pack directory."); + } + + Vector<uint8_t> key; + key.resize(32); + for (int i = 0; i < key.size(); i++) { + key.write[i] = script_encryption_key[i]; + } + Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); + if (err) { + f->close(); + memdelete(f); + memdelete(fae); + ERR_FAIL_V_MSG(false, "Can't open encrypted pack directory."); + } + f = fae; + } + + for (int i = 0; i < file_count; i++) { uint32_t sl = f->get_32(); CharString cs; cs.resize(sl + 1); @@ -199,72 +228,67 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files) String path; path.parse_utf8(cs.ptr()); - uint64_t ofs = f->get_64(); + uint64_t ofs = file_base + f->get_64(); uint64_t size = f->get_64(); uint8_t md5[16]; f->get_buffer(md5, 16); - PackedData::get_singleton()->add_path(p_path, path, ofs, size, md5, this, p_replace_files); - }; + uint32_t flags = f->get_32(); + + PackedData::get_singleton()->add_path(p_path, path, ofs + p_offset, size, md5, this, p_replace_files, (flags & PACK_FILE_ENCRYPTED)); + } f->close(); memdelete(f); return true; -}; +} FileAccess *PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile *p_file) { - return memnew(FileAccessPack(p_path, *p_file)); -}; +} ////////////////////////////////////////////////////////////////// Error FileAccessPack::_open(const String &p_path, int p_mode_flags) { - ERR_FAIL_V(ERR_UNAVAILABLE); return ERR_UNAVAILABLE; } void FileAccessPack::close() { - f->close(); } bool FileAccessPack::is_open() const { - return f->is_open(); } void FileAccessPack::seek(size_t p_position) { - if (p_position > pf.size) { eof = true; } else { eof = false; } - f->seek(pf.offset + p_position); + f->seek(off + p_position); pos = p_position; } -void FileAccessPack::seek_end(int64_t p_position) { +void FileAccessPack::seek_end(int64_t p_position) { seek(pf.size + p_position); } -size_t FileAccessPack::get_position() const { +size_t FileAccessPack::get_position() const { return pos; } -size_t FileAccessPack::get_len() const { +size_t FileAccessPack::get_len() const { return pf.size; } bool FileAccessPack::eof_reached() const { - return eof; } uint8_t FileAccessPack::get_8() const { - if (pos >= pf.size) { eof = true; return 0; @@ -275,9 +299,9 @@ uint8_t FileAccessPack::get_8() const { } int FileAccessPack::get_buffer(uint8_t *p_dst, int p_length) const { - - if (eof) + if (eof) { return 0; + } uint64_t to_read = p_length; if (to_read + pos > pf.size) { @@ -287,8 +311,9 @@ int FileAccessPack::get_buffer(uint8_t *p_dst, int p_length) const { pos += p_length; - if (to_read <= 0) + if (to_read <= 0) { return 0; + } f->get_buffer(p_dst, to_read); return to_read; @@ -300,46 +325,65 @@ void FileAccessPack::set_endian_swap(bool p_swap) { } Error FileAccessPack::get_error() const { - - if (eof) + if (eof) { return ERR_FILE_EOF; + } return OK; } void FileAccessPack::flush() { - ERR_FAIL(); } void FileAccessPack::store_8(uint8_t p_dest) { - ERR_FAIL(); } void FileAccessPack::store_buffer(const uint8_t *p_src, int p_length) { - ERR_FAIL(); } bool FileAccessPack::file_exists(const String &p_name) { - return false; } FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) : pf(p_file), f(FileAccess::open(pf.pack, FileAccess::READ)) { - ERR_FAIL_COND_MSG(!f, "Can't open pack-referenced file '" + String(pf.pack) + "'."); f->seek(pf.offset); + off = pf.offset; + + if (pf.encrypted) { + FileAccessEncrypted *fae = memnew(FileAccessEncrypted); + if (!fae) { + ERR_FAIL_MSG("Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); + } + + Vector<uint8_t> key; + key.resize(32); + for (int i = 0; i < key.size(); i++) { + key.write[i] = script_encryption_key[i]; + } + + Error err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_READ, false); + if (err) { + memdelete(fae); + ERR_FAIL_MSG("Can't open encrypted pack-referenced file '" + String(pf.pack) + "'."); + } + f = fae; + off = 0; + } pos = 0; eof = false; } FileAccessPack::~FileAccessPack() { - if (f) + if (f) { + f->close(); memdelete(f); + } } ////////////////////////////////////////////////////////////////////////////////// @@ -347,17 +391,14 @@ FileAccessPack::~FileAccessPack() { ////////////////////////////////////////////////////////////////////////////////// Error DirAccessPack::list_dir_begin() { - list_dirs.clear(); list_files.clear(); for (Map<String, PackedData::PackedDir *>::Element *E = current->subdirs.front(); E; E = E->next()) { - list_dirs.push_back(E->key()); } for (Set<String>::Element *E = current->files.front(); E; E = E->next()) { - list_files.push_back(E->get()); } @@ -365,7 +406,6 @@ Error DirAccessPack::list_dir_begin() { } String DirAccessPack::get_next() { - if (list_dirs.size()) { cdir = true; String d = list_dirs.front()->get(); @@ -380,31 +420,29 @@ String DirAccessPack::get_next() { return String(); } } -bool DirAccessPack::current_is_dir() const { +bool DirAccessPack::current_is_dir() const { return cdir; } -bool DirAccessPack::current_is_hidden() const { +bool DirAccessPack::current_is_hidden() const { return false; } -void DirAccessPack::list_dir_end() { +void DirAccessPack::list_dir_end() { list_dirs.clear(); list_files.clear(); } int DirAccessPack::get_drive_count() { - return 0; } -String DirAccessPack::get_drive(int p_drive) { +String DirAccessPack::get_drive(int p_drive) { return ""; } Error DirAccessPack::change_dir(String p_dir) { - String nd = p_dir.replace("\\", "/"); bool absolute = false; if (nd.begins_with("res://")) { @@ -414,7 +452,9 @@ Error DirAccessPack::change_dir(String p_dir) { nd = nd.simplify_path(); - if (nd == "") nd = "."; + if (nd == "") { + nd = "."; + } if (nd.begins_with("/")) { nd = nd.replace_first("/", ""); @@ -425,13 +465,13 @@ Error DirAccessPack::change_dir(String p_dir) { PackedData::PackedDir *pd; - if (absolute) + if (absolute) { pd = PackedData::get_singleton()->root; - else + } else { pd = current; + } for (int i = 0; i < paths.size(); i++) { - String p = paths[i]; if (p == ".") { continue; @@ -440,11 +480,9 @@ Error DirAccessPack::change_dir(String p_dir) { pd = pd->parent; } } else if (pd->subdirs.has(p)) { - pd = pd->subdirs[p]; } else { - return ERR_INVALID_PARAMETER; } } @@ -454,8 +492,7 @@ Error DirAccessPack::change_dir(String p_dir) { return OK; } -String DirAccessPack::get_current_dir() { - +String DirAccessPack::get_current_dir(bool p_include_drive) { PackedData::PackedDir *pd = current; String p = current->name; @@ -468,35 +505,30 @@ String DirAccessPack::get_current_dir() { } bool DirAccessPack::file_exists(String p_file) { - p_file = fix_path(p_file); return current->files.has(p_file); } bool DirAccessPack::dir_exists(String p_dir) { - p_dir = fix_path(p_dir); return current->subdirs.has(p_dir); } Error DirAccessPack::make_dir(String p_dir) { - return ERR_UNAVAILABLE; } Error DirAccessPack::rename(String p_from, String p_to) { - return ERR_UNAVAILABLE; } -Error DirAccessPack::remove(String p_name) { +Error DirAccessPack::remove(String p_name) { return ERR_UNAVAILABLE; } size_t DirAccessPack::get_space_left() { - return 0; } @@ -505,10 +537,5 @@ String DirAccessPack::get_filesystem_type() const { } DirAccessPack::DirAccessPack() { - current = PackedData::get_singleton()->root; - cdir = false; -} - -DirAccessPack::~DirAccessPack() { } diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index b6ea9c158f..d934b0deb5 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -40,7 +40,15 @@ // Godot's packed file magic header ("GDPC" in ASCII). #define PACK_HEADER_MAGIC 0x43504447 // The current packed file format version number. -#define PACK_FORMAT_VERSION 1 +#define PACK_FORMAT_VERSION 2 + +enum PackFlags { + PACK_DIR_ENCRYPTED = 1 << 0 +}; + +enum PackFileFlags { + PACK_FILE_ENCRYPTED = 1 << 0 +}; class PackSource; @@ -51,27 +59,26 @@ class PackedData { public: struct PackedFile { - String pack; uint64_t offset; //if offset is ZERO, the file was ERASED uint64_t size; uint8_t md5[16]; PackSource *src; + bool encrypted; }; private: struct PackedDir { - PackedDir *parent; + PackedDir *parent = nullptr; String name; Map<String, PackedDir *> subdirs; Set<String> files; }; struct PathMD5 { - uint64_t a; - uint64_t b; + uint64_t a = 0; + uint64_t b = 0; bool operator<(const PathMD5 &p_md5) const { - if (p_md5.a == a) { return b < p_md5.b; } else { @@ -81,16 +88,14 @@ private: bool operator==(const PathMD5 &p_md5) const { return a == p_md5.a && b == p_md5.b; - }; + } - PathMD5() { - a = b = 0; - }; + PathMD5() {} PathMD5(const Vector<uint8_t> p_buf) { a = *((uint64_t *)&p_buf[0]); b = *((uint64_t *)&p_buf[8]); - }; + } }; Map<PathMD5, PackedFile> files; @@ -98,22 +103,21 @@ private: Vector<PackSource *> sources; PackedDir *root; - //Map<String,PackedDir*> dirs; static PackedData *singleton; - bool disabled; + bool disabled = false; void _free_packed_dirs(PackedDir *p_dir); public: void add_pack_source(PackSource *p_source); - void add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files); // for PackSource + void add_path(const String &pkg_path, const String &path, uint64_t ofs, uint64_t size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource void set_disabled(bool p_disabled) { disabled = p_disabled; } _FORCE_INLINE_ bool is_disabled() const { return disabled; } static PackedData *get_singleton() { return singleton; } - Error add_pack(const String &p_path, bool p_replace_files); + Error add_pack(const String &p_path, bool p_replace_files, size_t p_offset); _FORCE_INLINE_ FileAccess *try_open_path(const String &p_path); _FORCE_INLINE_ bool has_path(const String &p_path); @@ -123,26 +127,24 @@ public: }; class PackSource { - public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files) = 0; + virtual bool try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset) = 0; virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file) = 0; virtual ~PackSource() {} }; class PackedSourcePCK : public PackSource { - public: - virtual bool try_open_pack(const String &p_path, bool p_replace_files); + virtual bool try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset); virtual FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); }; class FileAccessPack : public FileAccess { - PackedData::PackedFile pf; mutable size_t pos; mutable bool eof; + uint64_t off; FileAccess *f; virtual Error _open(const String &p_path, int p_mode_flags); @@ -181,29 +183,28 @@ public: }; FileAccess *PackedData::try_open_path(const String &p_path) { - PathMD5 pmd5(p_path.md5_buffer()); Map<PathMD5, PackedFile>::Element *E = files.find(pmd5); - if (!E) - return NULL; //not found - if (E->get().offset == 0) - return NULL; //was erased + if (!E) { + return nullptr; //not found + } + if (E->get().offset == 0) { + return nullptr; //was erased + } return E->get().src->get_file(p_path, &E->get()); } bool PackedData::has_path(const String &p_path) { - return files.has(PathMD5(p_path.md5_buffer())); } class DirAccessPack : public DirAccess { - PackedData::PackedDir *current; List<String> list_dirs; List<String> list_files; - bool cdir; + bool cdir = false; public: virtual Error list_dir_begin(); @@ -216,7 +217,7 @@ public: virtual String get_drive(int p_drive); virtual Error change_dir(String p_dir); - virtual String get_current_dir(); + virtual String get_current_dir(bool p_include_drive = true); virtual bool file_exists(String p_file); virtual bool dir_exists(String p_dir); @@ -231,7 +232,7 @@ public: virtual String get_filesystem_type() const; DirAccessPack(); - ~DirAccessPack(); + ~DirAccessPack() {} }; #endif // FILE_ACCESS_PACK_H diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index 680450ba43..ce402fe8ed 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -35,47 +35,41 @@ #include "core/os/copymem.h" #include "core/os/file_access.h" -ZipArchive *ZipArchive::instance = NULL; +ZipArchive *ZipArchive::instance = nullptr; extern "C" { static void *godot_open(void *data, const char *p_fname, int mode) { - if (mode & ZLIB_FILEFUNC_MODE_WRITE) { - return NULL; + return nullptr; } FileAccess *f = (FileAccess *)data; f->open(p_fname, FileAccess::READ); - return f->is_open() ? data : NULL; + return f->is_open() ? data : nullptr; } static uLong godot_read(void *data, void *fdata, void *buf, uLong size) { - FileAccess *f = (FileAccess *)data; f->get_buffer((uint8_t *)buf, size); return size; } static uLong godot_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { - return 0; } static long godot_tell(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)opaque; return f->get_position(); } static long godot_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - FileAccess *f = (FileAccess *)opaque; int pos = offset; switch (origin) { - case ZLIB_FILEFUNC_SEEK_CUR: pos = f->get_position() + offset; break; @@ -91,32 +85,27 @@ static long godot_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { } static int godot_close(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)opaque; f->close(); return 0; } static int godot_testerror(voidpf opaque, voidpf stream) { - FileAccess *f = (FileAccess *)opaque; return f->get_error() != OK ? 1 : 0; } static voidpf godot_alloc(voidpf opaque, uInt items, uInt size) { - return memalloc(items * size); } static void godot_free(voidpf opaque, voidpf address) { - memfree(address); } } // extern "C" void ZipArchive::close_handle(unzFile p_file) const { - ERR_FAIL_COND_MSG(!p_file, "Cannot close a file if none is open."); FileAccess *f = (FileAccess *)unzGetOpaque(p_file); unzCloseCurrentFile(p_file); @@ -125,12 +114,11 @@ void ZipArchive::close_handle(unzFile p_file) const { } unzFile ZipArchive::get_file_handle(String p_file) const { - - ERR_FAIL_COND_V_MSG(!file_exists(p_file), NULL, "File '" + p_file + " doesn't exist."); + ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, "File '" + p_file + " doesn't exist."); File file = files[p_file]; FileAccess *f = FileAccess::open(packages[file.package].filename, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!f, NULL, "Cannot open file '" + packages[file.package].filename + "'."); + ERR_FAIL_COND_V_MSG(!f, nullptr, "Cannot open file '" + packages[file.package].filename + "'."); zlib_filefunc_def io; zeromem(&io, sizeof(io)); @@ -149,28 +137,31 @@ unzFile ZipArchive::get_file_handle(String p_file) const { io.free_mem = godot_free; unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io); - ERR_FAIL_COND_V(!pkg, NULL); + ERR_FAIL_COND_V(!pkg, nullptr); int unz_err = unzGoToFilePos(pkg, &file.file_pos); if (unz_err != UNZ_OK || unzOpenCurrentFile(pkg) != UNZ_OK) { - unzClose(pkg); - ERR_FAIL_V(NULL); + ERR_FAIL_V(nullptr); } return pkg; } -bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { +bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset = 0) { + //printf("opening zip pack %s, %i, %i\n", p_name.utf8().get_data(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); + // load with offset feature only supported for PCK files + ERR_FAIL_COND_V_MSG(p_offset != 0, false, "Invalid PCK data. Note that loading files with a non-zero offset isn't supported with ZIP archives."); - //printf("opening zip pack %ls, %i, %i\n", p_name.c_str(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); - if (p_path.get_extension().nocasecmp_to("zip") != 0 && p_path.get_extension().nocasecmp_to("pcz") != 0) + if (p_path.get_extension().nocasecmp_to("zip") != 0 && p_path.get_extension().nocasecmp_to("pcz") != 0) { return false; + } zlib_filefunc_def io; FileAccess *fa = FileAccess::open(p_path, FileAccess::READ); - if (!fa) + if (!fa) { return false; + } io.opaque = fa; io.zopen_file = godot_open; io.zread_file = godot_read; @@ -195,11 +186,10 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { int pkg_num = packages.size() - 1; for (uint64_t i = 0; i < gi.number_entry; i++) { - char filename_inzip[256]; unz_file_info64 file_info; - err = unzGetCurrentFileInfo64(zfile, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0); + err = unzGetCurrentFileInfo64(zfile, &file_info, filename_inzip, sizeof(filename_inzip), nullptr, 0, nullptr, 0); ERR_CONTINUE(err != UNZ_OK); File f; @@ -210,8 +200,8 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { files[fname] = f; uint8_t md5[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this, p_replace_files); - //printf("packed data add path %ls, %ls\n", p_name.c_str(), fname.c_str()); + PackedData::get_singleton()->add_path(p_path, fname, 1, 0, md5, this, p_replace_files, false); + //printf("packed data add path %s, %s\n", p_name.utf8().get_data(), fname.utf8().get_data()); if ((i + 1) < gi.number_entry) { unzGoToNextFile(zfile); @@ -222,18 +212,15 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files) { } bool ZipArchive::file_exists(String p_name) const { - return files.has(p_name); } FileAccess *ZipArchive::get_file(const String &p_path, PackedData::PackedFile *p_file) { - return memnew(FileAccessZip(p_path, *p_file)); } ZipArchive *ZipArchive::get_singleton() { - - if (instance == NULL) { + if (instance == nullptr) { instance = memnew(ZipArchive); } @@ -241,15 +228,11 @@ ZipArchive *ZipArchive::get_singleton() { } ZipArchive::ZipArchive() { - instance = this; - //fa_create_func = FileAccess::get_create_func(); } ZipArchive::~ZipArchive() { - for (int i = 0; i < packages.size(); i++) { - FileAccess *f = (FileAccess *)unzGetOpaque(packages[i].zfile); unzClose(packages[i].zfile); memdelete(f); @@ -259,7 +242,6 @@ ZipArchive::~ZipArchive() { } Error FileAccessZip::_open(const String &p_path, int p_mode_flags) { - close(); ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, FAILED); @@ -268,83 +250,75 @@ Error FileAccessZip::_open(const String &p_path, int p_mode_flags) { zfile = arch->get_file_handle(p_path); ERR_FAIL_COND_V(!zfile, FAILED); - int err = unzGetCurrentFileInfo64(zfile, &file_info, NULL, 0, NULL, 0, NULL, 0); + int err = unzGetCurrentFileInfo64(zfile, &file_info, nullptr, 0, nullptr, 0, nullptr, 0); ERR_FAIL_COND_V(err != UNZ_OK, FAILED); return OK; } void FileAccessZip::close() { - - if (!zfile) + if (!zfile) { return; + } ZipArchive *arch = ZipArchive::get_singleton(); ERR_FAIL_COND(!arch); arch->close_handle(zfile); - zfile = NULL; + zfile = nullptr; } bool FileAccessZip::is_open() const { - - return zfile != NULL; + return zfile != nullptr; } void FileAccessZip::seek(size_t p_position) { - ERR_FAIL_COND(!zfile); unzSeekCurrentFile(zfile, p_position); } void FileAccessZip::seek_end(int64_t p_position) { - ERR_FAIL_COND(!zfile); unzSeekCurrentFile(zfile, get_len() + p_position); } size_t FileAccessZip::get_position() const { - ERR_FAIL_COND_V(!zfile, 0); return unztell(zfile); } size_t FileAccessZip::get_len() const { - ERR_FAIL_COND_V(!zfile, 0); return file_info.uncompressed_size; } bool FileAccessZip::eof_reached() const { - ERR_FAIL_COND_V(!zfile, true); return at_eof; } uint8_t FileAccessZip::get_8() const { - uint8_t ret = 0; get_buffer(&ret, 1); return ret; } int FileAccessZip::get_buffer(uint8_t *p_dst, int p_length) const { - ERR_FAIL_COND_V(!zfile, -1); at_eof = unzeof(zfile); - if (at_eof) + if (at_eof) { return 0; + } int read = unzReadCurrentFile(zfile, p_dst, p_length); ERR_FAIL_COND_V(read < 0, read); - if (read < p_length) + if (read < p_length) { at_eof = true; + } return read; } Error FileAccessZip::get_error() const { - if (!zfile) { - return ERR_UNCONFIGURED; } if (eof_reached()) { @@ -355,28 +329,23 @@ Error FileAccessZip::get_error() const { } void FileAccessZip::flush() { - ERR_FAIL(); } void FileAccessZip::store_8(uint8_t p_dest) { - ERR_FAIL(); } bool FileAccessZip::file_exists(const String &p_name) { - return false; } -FileAccessZip::FileAccessZip(const String &p_path, const PackedData::PackedFile &p_file) : - zfile(NULL) { +FileAccessZip::FileAccessZip(const String &p_path, const PackedData::PackedFile &p_file) { _open(p_path, FileAccess::READ); } FileAccessZip::~FileAccessZip() { - close(); } -#endif +#endif // MINIZIP_ENABLED diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index d5ce7d7a8d..c251b3c424 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifdef MINIZIP_ENABLED - #ifndef FILE_ACCESS_ZIP_H #define FILE_ACCESS_ZIP_H +#ifdef MINIZIP_ENABLED + #include "core/io/file_access_pack.h" #include "core/map.h" @@ -41,22 +41,17 @@ #include <stdlib.h> class ZipArchive : public PackSource { - public: struct File { - - int package; + int package = -1; unz_file_pos file_pos; - File() { - - package = -1; - }; + File() {} }; private: struct Package { String filename; - unzFile zfile; + unzFile zfile = nullptr; }; Vector<Package> packages; @@ -74,7 +69,7 @@ public: bool file_exists(String p_name) const; - virtual bool try_open_pack(const String &p_path, bool p_replace_files); + virtual bool try_open_pack(const String &p_path, bool p_replace_files, size_t p_offset); FileAccess *get_file(const String &p_path, PackedData::PackedFile *p_file); static ZipArchive *get_singleton(); @@ -84,8 +79,7 @@ public: }; class FileAccessZip : public FileAccess { - - unzFile zfile; + unzFile zfile = nullptr; unz_file_info64 file_info; mutable bool at_eof; @@ -119,6 +113,6 @@ public: ~FileAccessZip(); }; -#endif // FILE_ACCESS_ZIP_H - #endif // MINIZIP_ENABLED + +#endif // FILE_ACCESS_ZIP_H diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 541bd3a7c8..46e45500bf 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -47,7 +47,6 @@ const char *HTTPClient::_methods[METHOD_MAX] = { #ifndef JAVASCRIPT_ENABLED Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) { - close(); conn_port = p_port; @@ -58,10 +57,8 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, 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; conn_host = conn_host.substr(8, conn_host.length() - 8); } @@ -97,7 +94,6 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, } void HTTPClient::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."); close(); @@ -106,12 +102,10 @@ void HTTPClient::set_connection(const Ref<StreamPeer> &p_connection) { } Ref<StreamPeer> HTTPClient::get_connection() const { - return connection; } -Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const PoolVector<uint8_t> &p_body) { - +Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body) { ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); @@ -152,10 +146,10 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector request += "\r\n"; CharString cs = request.utf8(); - PoolVector<uint8_t> data; + Vector<uint8_t> data; data.resize(cs.length()); { - PoolVector<uint8_t>::Write data_write = data.write(); + uint8_t *data_write = data.ptrw(); for (int i = 0; i < cs.length(); i++) { data_write[i] = cs[i]; } @@ -163,7 +157,7 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector data.append_array(p_body); - PoolVector<uint8_t>::Read r = data.read(); + const uint8_t *r = data.ptr(); Error err = connection->put_data(&r[0], data.size()); if (err) { @@ -179,7 +173,6 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector } Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) { - ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER); @@ -235,27 +228,23 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str } bool HTTPClient::has_response() const { - return response_headers.size() != 0; } bool HTTPClient::is_response_chunked() const { - return chunked; } int HTTPClient::get_response_code() const { - return response_num; } Error HTTPClient::get_response_headers(List<String> *r_response) { - - if (!response_headers.size()) + if (!response_headers.size()) { return ERR_INVALID_PARAMETER; + } for (int i = 0; i < response_headers.size(); i++) { - r_response->push_back(response_headers[i]); } @@ -265,15 +254,14 @@ Error HTTPClient::get_response_headers(List<String> *r_response) { } void HTTPClient::close() { - - if (tcp_connection->get_status() != StreamPeerTCP::STATUS_NONE) + if (tcp_connection->get_status() != StreamPeerTCP::STATUS_NONE) { tcp_connection->disconnect_from_host(); + } connection.unref(); status = STATUS_DISCONNECTED; head_request = false; if (resolving != IP::RESOLVER_INVALID_ID) { - IP::get_singleton()->erase_resolve_item(resolving); resolving = IP::RESOLVER_INVALID_ID; } @@ -283,16 +271,14 @@ void HTTPClient::close() { body_size = -1; body_left = 0; chunk_left = 0; - chunk_trailer_part = 0; + chunk_trailer_part = false; read_until_eof = false; response_num = 0; handshaking = false; } Error HTTPClient::poll() { - switch (status) { - case STATUS_RESOLVING: { ERR_FAIL_COND_V(resolving == IP::RESOLVER_INVALID_ID, ERR_BUG); @@ -302,7 +288,6 @@ Error HTTPClient::poll() { return OK; // Still resolving case IP::RESOLVER_STATUS_DONE: { - IP_Address host = IP::get_singleton()->get_resolve_item_address(resolving); Error err = tcp_connection->connect_to_host(host, conn_port); IP::get_singleton()->erase_resolve_item(resolving); @@ -316,7 +301,6 @@ Error HTTPClient::poll() { } break; case IP::RESOLVER_STATUS_NONE: case IP::RESOLVER_STATUS_ERROR: { - IP::get_singleton()->erase_resolve_item(resolving); resolving = IP::RESOLVER_INVALID_ID; close(); @@ -326,10 +310,8 @@ Error HTTPClient::poll() { } } break; case STATUS_CONNECTING: { - StreamPeerTCP::Status s = tcp_connection->get_status(); switch (s) { - case StreamPeerTCP::STATUS_CONNECTING: { return OK; } break; @@ -350,7 +332,7 @@ Error HTTPClient::poll() { handshaking = true; } else { // We are already handshaking, which means we can use your already active SSL connection - ssl = static_cast<Ref<StreamPeerSSL> >(connection); + ssl = static_cast<Ref<StreamPeerSSL>>(connection); if (ssl.is_null()) { close(); status = STATUS_SSL_HANDSHAKE_ERROR; @@ -379,7 +361,6 @@ Error HTTPClient::poll() { } break; case StreamPeerTCP::STATUS_ERROR: case StreamPeerTCP::STATUS_NONE: { - close(); status = STATUS_CANT_CONNECT; return ERR_CANT_CONNECT; @@ -404,7 +385,6 @@ Error HTTPClient::poll() { return OK; } break; case STATUS_REQUESTING: { - while (true) { uint8_t byte; int rec = 0; @@ -415,15 +395,15 @@ Error HTTPClient::poll() { return ERR_CONNECTION_ERROR; } - if (rec == 0) + if (rec == 0) { return OK; // Still requesting, keep trying! + } response_str.push_back(byte); int rs = response_str.size(); if ( (rs >= 2 && response_str[rs - 2] == '\n' && response_str[rs - 1] == '\n') || (rs >= 4 && response_str[rs - 4] == '\r' && response_str[rs - 3] == '\n' && response_str[rs - 2] == '\r' && response_str[rs - 1] == '\n')) { - // End of response, parse. response_str.push_back(0); String response; @@ -445,11 +425,11 @@ Error HTTPClient::poll() { bool keep_alive = true; for (int i = 0; i < responses.size(); i++) { - String header = responses[i].strip_edges(); String s = header.to_lower(); - if (s.length() == 0) + if (s.length() == 0) { continue; + } if (s.begins_with("content-length:")) { body_size = s.substr(s.find(":") + 1, s.length()).strip_edges().to_int(); body_left = body_size; @@ -464,30 +444,25 @@ Error HTTPClient::poll() { } if (i == 0 && responses[i].begins_with("HTTP")) { - String num = responses[i].get_slicec(' ', 1); response_num = num.to_int(); } else { - response_headers.push_back(header); } } - // This is a HEAD request, we wont receive anything. + // This is a HEAD request, we won't receive anything. if (head_request) { body_size = 0; body_left = 0; } if (body_size != -1 || chunked) { - status = STATUS_BODY; } else if (!keep_alive) { - read_until_eof = true; status = STATUS_BODY; } else { - status = STATUS_CONNECTED; } return OK; @@ -513,29 +488,26 @@ Error HTTPClient::poll() { } int HTTPClient::get_response_body_length() const { - return body_size; } -PoolByteArray HTTPClient::read_response_body_chunk() { - - ERR_FAIL_COND_V(status != STATUS_BODY, PoolByteArray()); +PackedByteArray HTTPClient::read_response_body_chunk() { + ERR_FAIL_COND_V(status != STATUS_BODY, PackedByteArray()); - PoolByteArray ret; + PackedByteArray ret; Error err = OK; if (chunked) { - while (true) { - if (chunk_trailer_part) { // We need to consume the trailer part too or keep-alive will break uint8_t b; int rec = 0; err = _get_http_data(&b, 1, rec); - if (rec == 0) + if (rec == 0) { break; + } chunk.push_back(b); int cs = chunk.size(); @@ -557,8 +529,9 @@ PoolByteArray HTTPClient::read_response_body_chunk() { int rec = 0; err = _get_http_data(&b, 1, rec); - if (rec == 0) + if (rec == 0) { break; + } chunk.push_back(b); @@ -569,18 +542,17 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') { - int len = 0; for (int i = 0; i < chunk.size() - 2; i++) { char c = chunk[i]; int v = 0; - if (c >= '0' && c <= '9') + if (c >= '0' && c <= '9') { v = c - '0'; - else if (c >= 'a' && c <= 'f') + } else if (c >= 'a' && c <= 'f') { v = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') + } else if (c >= 'A' && c <= 'F') { v = c - 'A' + 10; - else { + } else { ERR_PRINT("HTTP Chunk len not in hex!!"); status = STATUS_CONNECTION_ERROR; break; @@ -605,7 +577,6 @@ PoolByteArray HTTPClient::read_response_body_chunk() { chunk.resize(chunk_left); } } else { - int rec = 0; err = _get_http_data(&chunk.write[chunk.size() - chunk_left], chunk_left, rec); if (rec == 0) { @@ -614,7 +585,6 @@ PoolByteArray HTTPClient::read_response_body_chunk() { chunk_left -= rec; if (chunk_left == 0) { - if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') { ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)"); status = STATUS_CONNECTION_ERROR; @@ -622,8 +592,8 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } ret.resize(chunk.size() - 2); - PoolByteArray::Write w = ret.write(); - copymem(w.ptr(), chunk.ptr(), chunk.size() - 2); + uint8_t *w = ret.ptrw(); + copymem(w, chunk.ptr(), chunk.size() - 2); chunk.clear(); } @@ -632,15 +602,14 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } } else { - int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size; ret.resize(to_read); int _offset = 0; while (to_read > 0) { int rec = 0; { - PoolByteArray::Write w = ret.write(); - err = _get_http_data(w.ptr() + _offset, to_read, rec); + uint8_t *w = ret.ptrw(); + err = _get_http_data(w + _offset, to_read, rec); } if (rec <= 0) { // Ended up reading less ret.resize(_offset); @@ -652,24 +621,21 @@ PoolByteArray HTTPClient::read_response_body_chunk() { body_left -= rec; } } - if (err != OK) + if (err != OK) { break; + } } } if (err != OK) { - close(); if (err == ERR_FILE_EOF) { - status = STATUS_DISCONNECTED; // Server disconnected } else { - status = STATUS_CONNECTION_ERROR; } } else if (body_left == 0 && !chunked && !read_until_eof) { - status = STATUS_CONNECTED; } @@ -677,24 +643,19 @@ PoolByteArray HTTPClient::read_response_body_chunk() { } HTTPClient::Status HTTPClient::get_status() const { - return status; } void HTTPClient::set_blocking_mode(bool p_enable) { - blocking = p_enable; } bool HTTPClient::is_blocking_mode_enabled() const { - return blocking; } Error HTTPClient::_get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - if (blocking) { - // We can't use StreamPeer.get_data, since when reaching EOF we will get an // error without knowing how many bytes we received. Error err = ERR_FILE_EOF; @@ -729,27 +690,10 @@ int HTTPClient::get_read_chunk_size() const { } HTTPClient::HTTPClient() { - tcp_connection.instance(); - resolving = IP::RESOLVER_INVALID_ID; - status = STATUS_DISCONNECTED; - head_request = false; - conn_port = -1; - body_size = -1; - chunked = false; - body_left = 0; - read_until_eof = false; - chunk_left = 0; - chunk_trailer_part = false; - response_num = 0; - ssl = false; - blocking = false; - handshaking = false; - read_chunk_size = 4096; } -HTTPClient::~HTTPClient() { -} +HTTPClient::~HTTPClient() {} #endif // #ifndef JAVASCRIPT_ENABLED @@ -784,15 +728,15 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { } Dictionary HTTPClient::_get_response_headers_as_dictionary() { - List<String> rh; get_response_headers(&rh); Dictionary ret; for (const List<String>::Element *E = rh.front(); E; E = E->next()) { const String &s = E->get(); int sp = s.find(":"); - if (sp == -1) + if (sp == -1) { continue; + } String key = s.substr(0, sp).strip_edges(); String value = s.substr(sp + 1, s.length()).strip_edges(); ret[key] = value; @@ -801,11 +745,10 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() { return ret; } -PoolStringArray HTTPClient::_get_response_headers() { - +PackedStringArray HTTPClient::_get_response_headers() { List<String> rh; get_response_headers(&rh); - PoolStringArray ret; + PackedStringArray ret; ret.resize(rh.size()); int idx = 0; for (const List<String>::Element *E = rh.front(); E; E = E->next()) { @@ -816,7 +759,6 @@ PoolStringArray 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("set_connection", "connection"), &HTTPClient::set_connection); ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection); diff --git a/core/io/http_client.h b/core/io/http_client.h index 3796eb992c..1dc1f3d76a 100644 --- a/core/io/http_client.h +++ b/core/io/http_client.h @@ -37,7 +37,6 @@ #include "core/reference.h" class HTTPClient : public Reference { - GDCLASS(HTTPClient, Reference); public: @@ -158,32 +157,32 @@ private: }; #ifndef JAVASCRIPT_ENABLED - Status status; - IP::ResolverID resolving; - int conn_port; + Status status = STATUS_DISCONNECTED; + IP::ResolverID resolving = IP::RESOLVER_INVALID_ID; + int conn_port = -1; String conn_host; - bool ssl; - bool ssl_verify_host; - bool blocking; - bool handshaking; - bool head_request; + bool ssl = false; + bool ssl_verify_host = false; + bool blocking = false; + bool handshaking = false; + bool head_request = false; Vector<uint8_t> response_str; - bool chunked; + bool chunked = false; Vector<uint8_t> chunk; - int chunk_left; - bool chunk_trailer_part; - int body_size; - int body_left; - bool read_until_eof; + int chunk_left = 0; + bool chunk_trailer_part = false; + int body_size = -1; + int body_left = 0; + bool read_until_eof = false; Ref<StreamPeerTCP> tcp_connection; Ref<StreamPeer> connection; - int response_num; + int response_num = 0; Vector<String> response_headers; - int read_chunk_size; + int read_chunk_size = 4096; Error _get_http_data(uint8_t *p_buffer, int p_bytes, int &r_received); @@ -191,7 +190,7 @@ private: #include "platform/javascript/http_client.h.inc" #endif - PoolStringArray _get_response_headers(); + PackedStringArray _get_response_headers(); Dictionary _get_response_headers_as_dictionary(); static void _bind_methods(); @@ -202,7 +201,7 @@ public: void set_connection(const Ref<StreamPeer> &p_connection); Ref<StreamPeer> get_connection() const; - Error request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const PoolVector<uint8_t> &p_body); + Error request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const Vector<uint8_t> &p_body); Error request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body = String()); void close(); @@ -215,7 +214,7 @@ public: Error get_response_headers(List<String> *r_response); int get_response_body_length() const; - PoolByteArray read_response_body_chunk(); // Can't get body as partial text because of most encodings UTF8, gzip, etc. + PackedByteArray read_response_body_chunk(); // Can't get body as partial text because of most encodings UTF8, gzip, etc. void set_blocking_mode(bool p_enable); // Useful mostly if running in a thread bool is_blocking_mode_enabled() const; diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 05929ee8ed..b1e92eb87f 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -33,13 +33,12 @@ #include "core/print_string.h" bool ImageFormatLoader::recognize(const String &p_extension) const { - List<String> extensions; get_recognized_extensions(&extensions); for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - - if (E->get().nocasecmp_to(p_extension) == 0) + if (E->get().nocasecmp_to(p_extension) == 0) { return true; + } } return false; @@ -53,7 +52,7 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c Error err; f = FileAccess::open(p_file, FileAccess::READ, &err); if (!f) { - ERR_PRINTS("Error opening file '" + p_file + "'."); + ERR_PRINT("Error opening file '" + p_file + "'."); return err; } } @@ -61,67 +60,61 @@ Error ImageLoader::load_image(String p_file, Ref<Image> p_image, FileAccess *p_c String extension = p_file.get_extension(); for (int i = 0; i < loader.size(); i++) { - - if (!loader[i]->recognize(extension)) + if (!loader[i]->recognize(extension)) { continue; + } Error err = loader[i]->load_image(p_image, f, p_force_linear, p_scale); if (err != OK) { - ERR_PRINTS("Error loading image: " + p_file); + ERR_PRINT("Error loading image: " + p_file); } if (err != ERR_FILE_UNRECOGNIZED) { - - if (!p_custom) + if (!p_custom) { memdelete(f); + } return err; } } - if (!p_custom) + if (!p_custom) { memdelete(f); + } return ERR_FILE_UNRECOGNIZED; } void ImageLoader::get_recognized_extensions(List<String> *p_extensions) { - for (int i = 0; i < loader.size(); i++) { - loader[i]->get_recognized_extensions(p_extensions); } } ImageFormatLoader *ImageLoader::recognize(const String &p_extension) { - for (int i = 0; i < loader.size(); i++) { - - if (loader[i]->recognize(p_extension)) + if (loader[i]->recognize(p_extension)) { return loader[i]; + } } - return NULL; + return nullptr; } Vector<ImageFormatLoader *> ImageLoader::loader; void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { - loader.push_back(p_loader); } void ImageLoader::remove_image_format_loader(ImageFormatLoader *p_loader) { - loader.erase(p_loader); } const Vector<ImageFormatLoader *> &ImageLoader::get_image_format_loaders() { - return loader; } void ImageLoader::cleanup() { - while (loader.size()) { remove_image_format_loader(loader[0]); } @@ -129,8 +122,7 @@ void ImageLoader::cleanup() { ///////////////// -RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error) { - +RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { FileAccess *f = FileAccess::open(p_path, FileAccess::READ); if (!f) { if (r_error) { @@ -192,16 +184,13 @@ RES ResourceFormatLoaderImage::load(const String &p_path, const String &p_origin } void ResourceFormatLoaderImage::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("image"); } bool ResourceFormatLoaderImage::handles_type(const String &p_type) const { - return p_type == "Image"; } String ResourceFormatLoaderImage::get_resource_type(const String &p_path) const { - return p_path.get_extension().to_lower() == "image" ? "Image" : String(); } diff --git a/core/io/image_loader.h b/core/io/image_loader.h index d6dfd261ca..9682f144c7 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -53,13 +53,12 @@ public: }; class ImageLoader { - static Vector<ImageFormatLoader *> loader; friend class ResourceFormatLoaderImage; protected: public: - static Error load_image(String p_file, Ref<Image> p_image, FileAccess *p_custom = NULL, bool p_force_linear = false, float p_scale = 1.0); + static Error load_image(String p_file, Ref<Image> p_image, FileAccess *p_custom = nullptr, bool p_force_linear = false, float p_scale = 1.0); static void get_recognized_extensions(List<String> *p_extensions); static ImageFormatLoader *recognize(const String &p_extension); @@ -73,10 +72,10 @@ public: class ResourceFormatLoaderImage : public ResourceFormatLoader { public: - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); 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; }; -#endif +#endif // IMAGE_LOADER_H diff --git a/core/io/ip.cpp b/core/io/ip.cpp index ea791fb327..24b8ec7cc1 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -39,9 +39,7 @@ VARIANT_ENUM_CAST(IP::ResolverStatus); /************* RESOLVER ******************/ struct _IP_ResolverPrivate { - struct QueueItem { - volatile IP::ResolverStatus status; IP_Address response; String hostname; @@ -56,53 +54,50 @@ struct _IP_ResolverPrivate { QueueItem() { clear(); - }; + } }; QueueItem queue[IP::RESOLVER_MAX_QUERIES]; IP::ResolverID find_empty_id() const { - for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) { - if (queue[i].status == IP::RESOLVER_STATUS_NONE) + if (queue[i].status == IP::RESOLVER_STATUS_NONE) { return i; + } } return IP::RESOLVER_INVALID_ID; } - Mutex *mutex; - Semaphore *sem; + Mutex mutex; + Semaphore sem; Thread *thread; //Semaphore* semaphore; bool thread_abort; void resolve_queues() { - for (int i = 0; i < IP::RESOLVER_MAX_QUERIES; i++) { - - if (queue[i].status != IP::RESOLVER_STATUS_WAITING) + if (queue[i].status != IP::RESOLVER_STATUS_WAITING) { continue; + } queue[i].response = IP::get_singleton()->resolve_hostname(queue[i].hostname, queue[i].type); - if (!queue[i].response.is_valid()) + if (!queue[i].response.is_valid()) { queue[i].status = IP::RESOLVER_STATUS_ERROR; - else + } else { queue[i].status = IP::RESOLVER_STATUS_DONE; + } } } static void _thread_function(void *self) { - _IP_ResolverPrivate *ipr = (_IP_ResolverPrivate *)self; while (!ipr->thread_abort) { + ipr->sem.wait(); - ipr->sem->wait(); - - ipr->mutex->lock(); + MutexLock lock(ipr->mutex); ipr->resolve_queues(); - ipr->mutex->unlock(); } } @@ -114,31 +109,26 @@ struct _IP_ResolverPrivate { }; IP_Address IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { - - resolver->mutex->lock(); + MutexLock lock(resolver->mutex); String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); if (resolver->cache.has(key) && resolver->cache[key].is_valid()) { IP_Address res = resolver->cache[key]; - resolver->mutex->unlock(); return res; } IP_Address res = _resolve_hostname(p_hostname, p_type); resolver->cache[key] = res; - resolver->mutex->unlock(); return res; } IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Type p_type) { - - resolver->mutex->lock(); + MutexLock lock(resolver->mutex); ResolverID id = resolver->find_empty_id(); if (id == RESOLVER_INVALID_ID) { WARN_PRINT("Out of resolver queries"); - resolver->mutex->unlock(); return id; } @@ -151,64 +141,53 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ } else { resolver->queue[id].response = IP_Address(); resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING; - if (resolver->thread) - resolver->sem->post(); - else + if (resolver->thread) { + resolver->sem.post(); + } else { resolver->resolve_queues(); + } } - resolver->mutex->unlock(); return id; } IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { - ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP::RESOLVER_STATUS_NONE); - resolver->mutex->lock(); + MutexLock lock(resolver->mutex); + if (resolver->queue[p_id].status == IP::RESOLVER_STATUS_NONE) { ERR_PRINT("Condition status == IP::RESOLVER_STATUS_NONE"); - resolver->mutex->unlock(); + resolver->mutex.unlock(); return IP::RESOLVER_STATUS_NONE; } - IP::ResolverStatus res = resolver->queue[p_id].status; - - resolver->mutex->unlock(); - return res; + return resolver->queue[p_id].status; } IP_Address IP::get_resolve_item_address(ResolverID p_id) const { - ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP_Address()); - resolver->mutex->lock(); + MutexLock lock(resolver->mutex); if (resolver->queue[p_id].status != IP::RESOLVER_STATUS_DONE) { - ERR_PRINTS("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet."); - resolver->mutex->unlock(); + ERR_PRINT("Resolve of '" + resolver->queue[p_id].hostname + "'' didn't complete yet."); + resolver->mutex.unlock(); return IP_Address(); } - IP_Address res = resolver->queue[p_id].response; - - resolver->mutex->unlock(); - return res; + return resolver->queue[p_id].response; } void IP::erase_resolve_item(ResolverID p_id) { - ERR_FAIL_INDEX(p_id, IP::RESOLVER_MAX_QUERIES); - resolver->mutex->lock(); + MutexLock lock(resolver->mutex); resolver->queue[p_id].status = IP::RESOLVER_STATUS_NONE; - - resolver->mutex->unlock(); } void IP::clear_cache(const String &p_hostname) { - - resolver->mutex->lock(); + MutexLock lock(resolver->mutex); if (p_hostname.empty()) { resolver->cache.clear(); @@ -218,12 +197,9 @@ void IP::clear_cache(const String &p_hostname) { resolver->cache.erase(_IP_ResolverPrivate::get_cache_key(p_hostname, IP::TYPE_IPV6)); resolver->cache.erase(_IP_ResolverPrivate::get_cache_key(p_hostname, IP::TYPE_ANY)); } - - resolver->mutex->unlock(); } Array IP::_get_local_addresses() const { - Array addresses; List<IP_Address> ip_addresses; get_local_addresses(&ip_addresses); @@ -235,7 +211,6 @@ Array IP::_get_local_addresses() const { } Array IP::_get_local_interfaces() const { - Array results; Map<String, Interface_Info> interfaces; get_local_interfaces(&interfaces); @@ -259,7 +234,6 @@ Array IP::_get_local_interfaces() const { } void IP::get_local_addresses(List<IP_Address> *r_addresses) const { - Map<String, Interface_Info> interfaces; get_local_interfaces(&interfaces); for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { @@ -270,7 +244,6 @@ void IP::get_local_addresses(List<IP_Address> *r_addresses) const { } void IP::_bind_methods() { - ClassDB::bind_method(D_METHOD("resolve_hostname", "host", "ip_type"), &IP::resolve_hostname, DEFVAL(IP::TYPE_ANY)); ClassDB::bind_method(D_METHOD("resolve_hostname_queue_item", "host", "ip_type"), &IP::resolve_hostname_queue_item, DEFVAL(IP::TYPE_ANY)); ClassDB::bind_method(D_METHOD("get_resolve_item_status", "id"), &IP::get_resolve_item_status); @@ -294,61 +267,44 @@ void IP::_bind_methods() { BIND_ENUM_CONSTANT(TYPE_ANY); } -IP *IP::singleton = NULL; +IP *IP::singleton = nullptr; IP *IP::get_singleton() { - return singleton; } -IP *(*IP::_create)() = NULL; +IP *(*IP::_create)() = nullptr; IP *IP::create() { - - ERR_FAIL_COND_V_MSG(singleton, NULL, "IP singleton already exist."); - ERR_FAIL_COND_V(!_create, NULL); + ERR_FAIL_COND_V_MSG(singleton, nullptr, "IP singleton already exist."); + ERR_FAIL_COND_V(!_create, nullptr); return _create(); } IP::IP() { - singleton = this; resolver = memnew(_IP_ResolverPrivate); - resolver->sem = NULL; - resolver->mutex = Mutex::create(); #ifndef NO_THREADS - resolver->sem = Semaphore::create(); - if (resolver->sem) { - resolver->thread_abort = false; + resolver->thread_abort = false; - resolver->thread = Thread::create(_IP_ResolverPrivate::_thread_function, resolver); - - if (!resolver->thread) - memdelete(resolver->sem); //wtf - } else { - resolver->thread = NULL; - } + resolver->thread = Thread::create(_IP_ResolverPrivate::_thread_function, resolver); #else - resolver->sem = NULL; - resolver->thread = NULL; + resolver->thread = nullptr; #endif } IP::~IP() { - #ifndef NO_THREADS if (resolver->thread) { resolver->thread_abort = true; - resolver->sem->post(); + resolver->sem.post(); Thread::wait_to_finish(resolver->thread); memdelete(resolver->thread); - memdelete(resolver->sem); } #endif - memdelete(resolver->mutex); memdelete(resolver); } diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp index f5fd8ae205..d0fb63b958 100644 --- a/core/io/ip_address.cpp +++ b/core/io/ip_address.cpp @@ -39,40 +39,40 @@ IP_Address::operator Variant() const { #include <string.h> IP_Address::operator String() const { - - if (wildcard) + if (wildcard) { return "*"; + } - if (!valid) + if (!valid) { return ""; + } - if (is_ipv4()) + if (is_ipv4()) { // IPv4 address mapped to IPv6 return itos(field8[12]) + "." + itos(field8[13]) + "." + itos(field8[14]) + "." + itos(field8[15]); + } String ret; for (int i = 0; i < 8; i++) { - if (i > 0) + if (i > 0) { ret = ret + ":"; + } uint16_t num = (field8[i * 2] << 8) + field8[i * 2 + 1]; ret = ret + String::num_int64(num, 16); - }; + } return ret; } static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { - uint16_t ret = 0; for (int i = p_start; i < p_start + 4; i++) { - if (i >= p_string.length()) { break; - }; + } int n = 0; - CharType c = p_string[i]; + char32_t c = p_string[i]; if (c >= '0' && c <= '9') { - n = c - '0'; } else if (c >= 'a' && c <= 'f') { n = 10 + (c - 'a'); @@ -82,17 +82,16 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) { break; } else { ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + "."); - }; + } ret = ret << 4; ret += n; - }; + } p_dst[0] = ret >> 8; p_dst[1] = ret & 0xff; -}; +} void IP_Address::_parse_ipv6(const String &p_string) { - static const int parts_total = 8; int parts[parts_total] = { 0 }; int parts_count = 0; @@ -102,20 +101,17 @@ void IP_Address::_parse_ipv6(const String &p_string) { int parts_idx = 0; for (int i = 0; i < p_string.length(); i++) { - - CharType c = p_string[i]; + char32_t c = p_string[i]; if (c == ':') { - if (i == 0) { continue; // next must be a ":" - }; + } if (!part_found) { part_skip = true; parts[parts_idx++] = -1; - }; + } part_found = false; } else if (c == '.') { - part_ipv4 = true; } else if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { @@ -123,58 +119,54 @@ void IP_Address::_parse_ipv6(const String &p_string) { parts[parts_idx++] = i; part_found = true; ++parts_count; - }; + } } else { ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + "."); - }; - }; + } + } int parts_extra = 0; if (part_skip) { parts_extra = parts_total - parts_count; - }; + } int idx = 0; for (int i = 0; i < parts_idx; i++) { - if (parts[i] == -1) { - for (int j = 0; j < parts_extra; j++) { field16[idx++] = 0; - }; + } continue; - }; + } if (part_ipv4 && i == parts_idx - 1) { _parse_ipv4(p_string, parts[i], (uint8_t *)&field16[idx]); // should be the last one } else { _parse_hex(p_string, parts[i], (uint8_t *)&(field16[idx++])); - }; - }; -}; + } + } +} void IP_Address::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret) { - String ip; if (p_start != 0) { ip = p_string.substr(p_start, p_string.length() - p_start); } else { ip = p_string; - }; + } int slices = ip.get_slice_count("."); ERR_FAIL_COND_MSG(slices != 4, "Invalid IP address string: " + ip + "."); for (int i = 0; i < 4; i++) { p_ret[i] = ip.get_slicec('.', i).to_int(); } -}; +} void IP_Address::clear() { - memset(&field8[0], 0, sizeof(field8)); valid = false; wildcard = false; -}; +} bool IP_Address::is_ipv4() const { return (field32[0] == 0 && field32[1] == 0 && field16[4] == 0 && field16[5] == 0xffff); @@ -199,12 +191,12 @@ const uint8_t *IP_Address::get_ipv6() const { void IP_Address::set_ipv6(const uint8_t *p_buf) { clear(); valid = true; - for (int i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) { field8[i] = p_buf[i]; + } } IP_Address::IP_Address(const String &p_string) { - clear(); if (p_string == "*") { @@ -228,15 +220,13 @@ IP_Address::IP_Address(const String &p_string) { } _FORCE_INLINE_ static void _32_to_buf(uint8_t *p_dst, uint32_t p_n) { - p_dst[0] = (p_n >> 24) & 0xff; p_dst[1] = (p_n >> 16) & 0xff; p_dst[2] = (p_n >> 8) & 0xff; p_dst[3] = (p_n >> 0) & 0xff; -}; +} IP_Address::IP_Address(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, bool is_v6) { - clear(); valid = true; if (!is_v6) { @@ -247,7 +237,6 @@ IP_Address::IP_Address(uint32_t p_a, uint32_t p_b, uint32_t p_c, uint32_t p_d, b field8[14] = p_c; field8[15] = p_d; } else { - _32_to_buf(&field8[0], p_a); _32_to_buf(&field8[4], p_b); _32_to_buf(&field8[8], p_c); diff --git a/core/io/ip_address.h b/core/io/ip_address.h index 89cf37ff8f..2f8f83503e 100644 --- a/core/io/ip_address.h +++ b/core/io/ip_address.h @@ -34,7 +34,6 @@ #include "core/ustring.h" struct IP_Address { - private: union { uint8_t field8[16]; @@ -52,19 +51,32 @@ protected: public: //operator Variant() const; bool operator==(const IP_Address &p_ip) const { - if (p_ip.valid != valid) return false; - if (!valid) return false; - for (int i = 0; i < 4; i++) - if (field32[i] != p_ip.field32[i]) + if (p_ip.valid != valid) { + return false; + } + if (!valid) { + return false; + } + for (int i = 0; i < 4; i++) { + if (field32[i] != p_ip.field32[i]) { return false; + } + } return true; } + bool operator!=(const IP_Address &p_ip) const { - if (p_ip.valid != valid) return true; - if (!valid) return true; - for (int i = 0; i < 4; i++) - if (field32[i] != p_ip.field32[i]) + if (p_ip.valid != valid) { + return true; + } + if (!valid) { + return true; + } + for (int i = 0; i < 4; i++) { + if (field32[i] != p_ip.field32[i]) { return true; + } + } return false; } diff --git a/core/io/json.cpp b/core/io/json.cpp index dbf1676e62..1b89d966fd 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -46,17 +46,16 @@ const char *JSON::tk_name[TK_MAX] = { }; static String _make_indent(const String &p_indent, int p_size) { - String indent_text = ""; if (!p_indent.empty()) { - for (int i = 0; i < p_size; i++) + for (int i = 0; i < p_size; i++) { indent_text += p_indent; + } } return indent_text; } String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys) { - String colon = ":"; String end_statement = ""; @@ -66,16 +65,20 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ } switch (p_var.get_type()) { - - case Variant::NIL: return "null"; - case Variant::BOOL: return p_var.operator bool() ? "true" : "false"; - case Variant::INT: return itos(p_var); - case Variant::REAL: return rtos(p_var); - case Variant::POOL_INT_ARRAY: - case Variant::POOL_REAL_ARRAY: - case Variant::POOL_STRING_ARRAY: + case Variant::NIL: + return "null"; + case Variant::BOOL: + return p_var.operator bool() ? "true" : "false"; + case Variant::INT: + return itos(p_var); + case Variant::FLOAT: + return rtos(p_var); + case Variant::PACKED_INT32_ARRAY: + case Variant::PACKED_INT64_ARRAY: + case Variant::PACKED_FLOAT32_ARRAY: + case Variant::PACKED_FLOAT64_ARRAY: + case Variant::PACKED_STRING_ARRAY: case Variant::ARRAY: { - String s = "["; s += end_statement; Array a = p_var; @@ -88,20 +91,19 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ } s += end_statement + _make_indent(p_indent, p_cur_indent) + "]"; return s; - }; + } case Variant::DICTIONARY: { - String s = "{"; s += end_statement; Dictionary d = p_var; List<Variant> keys; d.get_key_list(&keys); - if (p_sort_keys) + if (p_sort_keys) { keys.sort(); + } for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - if (E != keys.front()) { s += ","; s += end_statement; @@ -113,69 +115,59 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ s += end_statement + _make_indent(p_indent, p_cur_indent) + "}"; return s; - }; - default: return "\"" + String(p_var).json_escape() + "\""; + } + default: + return "\"" + String(p_var).json_escape() + "\""; } } String JSON::print(const Variant &p_var, const String &p_indent, bool p_sort_keys) { - return _print_var(p_var, p_indent, 0, p_sort_keys); } -Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) { - +Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) { while (p_len > 0) { switch (p_str[index]) { - case '\n': { - line++; index++; break; - }; + } case 0: { r_token.type = TK_EOF; return OK; } break; case '{': { - r_token.type = TK_CURLY_BRACKET_OPEN; index++; return OK; - }; + } case '}': { - r_token.type = TK_CURLY_BRACKET_CLOSE; index++; return OK; - }; + } case '[': { - r_token.type = TK_BRACKET_OPEN; index++; return OK; - }; + } case ']': { - r_token.type = TK_BRACKET_CLOSE; index++; return OK; - }; + } case ':': { - r_token.type = TK_COLON; index++; return OK; - }; + } case ',': { - r_token.type = TK_COMMA; index++; return OK; - }; + } case '"': { - index++; String str; while (true) { @@ -188,35 +180,42 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to } else if (p_str[index] == '\\') { //escaped characters... index++; - CharType next = p_str[index]; + char32_t next = p_str[index]; if (next == 0) { r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } - CharType res = 0; + char32_t res = 0; switch (next) { - - case 'b': res = 8; break; - case 't': res = 9; break; - case 'n': res = 10; break; - case 'f': res = 12; break; - case 'r': res = 13; break; + case 'b': + res = 8; + break; + case 't': + res = 9; + break; + case 'n': + res = 10; + break; + case 'f': + res = 12; + break; + case 'r': + res = 13; + break; case 'u': { - //hexnumbarh - oct is deprecated - + // hex number for (int j = 0; j < 4; j++) { - CharType c = p_str[index + j + 1]; + char32_t c = p_str[index + j + 1]; if (c == 0) { r_err_str = "Unterminated String"; return ERR_PARSE_ERROR; } if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { - r_err_str = "Malformed hex constant in string"; return ERR_PARSE_ERROR; } - CharType v; + char32_t v; if (c >= '0' && c <= '9') { v = c - '0'; } else if (c >= 'a' && c <= 'f') { @@ -226,7 +225,7 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to v = c - 'A'; v += 10; } else { - ERR_PRINT("BUG"); + ERR_PRINT("Bug parsing hex constant."); v = 0; } @@ -236,21 +235,17 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to index += 4; //will add at the end anyway } break; - //case '\"': res='\"'; break; - //case '\\': res='\\'; break; - //case '/': res='/'; break; default: { res = next; - //r_err_str="Invalid escape sequence"; - //return ERR_PARSE_ERROR; } break; } str += res; } else { - if (p_str[index] == '\n') + if (p_str[index] == '\n') { line++; + } str += p_str[index]; } index++; @@ -262,7 +257,6 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to } break; default: { - if (p_str[index] <= 32) { index++; break; @@ -270,19 +264,17 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to if (p_str[index] == '-' || (p_str[index] >= '0' && p_str[index] <= '9')) { //a number - const CharType *rptr; - double number = String::to_double(&p_str[index], &rptr); + const char32_t *rptr; + double number = String::to_float(&p_str[index], &rptr); index += (rptr - &p_str[index]); r_token.type = TK_NUMBER; r_token.value = number; return OK; } else if ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { - String id; while ((p_str[index] >= 'A' && p_str[index] <= 'Z') || (p_str[index] >= 'a' && p_str[index] <= 'z')) { - id += p_str[index]; index++; } @@ -301,46 +293,42 @@ Error JSON::_get_token(const CharType *p_str, int &index, int p_len, Token &r_to return ERR_PARSE_ERROR; } -Error JSON::_parse_value(Variant &value, Token &token, const CharType *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, String &r_err_str) { if (token.type == TK_CURLY_BRACKET_OPEN) { - Dictionary d; Error err = _parse_object(d, p_str, index, p_len, line, r_err_str); - if (err) + if (err) { return err; + } value = d; return OK; } else if (token.type == TK_BRACKET_OPEN) { - Array a; Error err = _parse_array(a, p_str, index, p_len, line, r_err_str); - if (err) + if (err) { return err; + } value = a; return OK; } else if (token.type == TK_IDENTIFIER) { - String id = token.value; - if (id == "true") + if (id == "true") { value = true; - else if (id == "false") + } else if (id == "false") { value = false; - else if (id == "null") + } else if (id == "null") { value = Variant(); - else { + } else { r_err_str = "Expected 'true','false' or 'null', got '" + id + "'."; return ERR_PARSE_ERROR; } return OK; } else if (token.type == TK_NUMBER) { - value = token.value; return OK; } else if (token.type == TK_STRING) { - value = token.value; return OK; } else { @@ -349,26 +337,22 @@ Error JSON::_parse_value(Variant &value, Token &token, const CharType *p_str, in } } -Error JSON::_parse_array(Array &array, const CharType *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, String &r_err_str) { Token token; bool need_comma = false; while (index < p_len) { - Error err = _get_token(p_str, index, p_len, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type == TK_BRACKET_CLOSE) { - return OK; } if (need_comma) { - if (token.type != TK_COMMA) { - r_err_str = "Expected ','"; return ERR_PARSE_ERROR; } else { @@ -379,40 +363,37 @@ Error JSON::_parse_array(Array &array, const CharType *p_str, int &index, int p_ Variant v; err = _parse_value(v, token, p_str, index, p_len, line, r_err_str); - if (err) + if (err) { return err; + } array.push_back(v); need_comma = true; } + r_err_str = "Expected ']'"; return ERR_PARSE_ERROR; } -Error JSON::_parse_object(Dictionary &object, const CharType *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, String &r_err_str) { bool at_key = true; String key; Token token; bool need_comma = false; while (index < p_len) { - if (at_key) { - Error err = _get_token(p_str, index, p_len, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type == TK_CURLY_BRACKET_CLOSE) { - return OK; } if (need_comma) { - if (token.type != TK_COMMA) { - r_err_str = "Expected '}' or ','"; return ERR_PARSE_ERROR; } else { @@ -422,43 +403,43 @@ Error JSON::_parse_object(Dictionary &object, const CharType *p_str, int &index, } if (token.type != TK_STRING) { - r_err_str = "Expected key"; return ERR_PARSE_ERROR; } key = token.value; err = _get_token(p_str, index, p_len, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } if (token.type != TK_COLON) { - r_err_str = "Expected ':'"; return ERR_PARSE_ERROR; } at_key = false; } else { - Error err = _get_token(p_str, index, p_len, token, line, r_err_str); - if (err != OK) + if (err != OK) { return err; + } Variant v; err = _parse_value(v, token, p_str, index, p_len, line, r_err_str); - if (err) + if (err) { return err; + } object[key] = v; need_comma = true; at_key = true; } } + r_err_str = "Expected '}'"; return ERR_PARSE_ERROR; } Error JSON::parse(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line) { - - const CharType *str = p_json.ptr(); + const char32_t *str = p_json.ptr(); int idx = 0; int len = p_json.length(); Token token; @@ -466,8 +447,9 @@ Error JSON::parse(const String &p_json, Variant &r_ret, String &r_err_str, int & String aux_key; Error err = _get_token(str, idx, len, token, r_err_line, r_err_str); - if (err) + if (err) { return err; + } err = _parse_value(r_ret, token, str, idx, len, r_err_line, r_err_str); diff --git a/core/io/json.h b/core/io/json.h index 2e851afcf4..9122228163 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -34,7 +34,6 @@ #include "core/variant.h" class JSON { - enum TokenType { TK_CURLY_BRACKET_OPEN, TK_CURLY_BRACKET_CLOSE, @@ -58,7 +57,6 @@ class JSON { }; struct Token { - TokenType type; Variant value; }; @@ -67,10 +65,10 @@ class JSON { static String _print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys); - static Error _get_token(const CharType *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 CharType *p_str, int &index, int p_len, int &line, String &r_err_str); - static Error _parse_array(Array &array, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str); - static Error _parse_object(Dictionary &object, const CharType *p_str, int &index, int p_len, int &line, String &r_err_str); + 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); public: static String print(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true); diff --git a/core/io/logger.cpp b/core/io/logger.cpp index c3ea0d024e..886e5695b1 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -34,17 +34,6 @@ #include "core/os/os.h" #include "core/print_string.h" -// va_copy was defined in the C99, but not in C++ standards before C++11. -// When you compile C++ without --std=c++<XX> option, compilers still define -// va_copy, otherwise you have to use the internal version (__va_copy). -#if !defined(va_copy) -#if defined(__GNUC__) -#define va_copy(d, s) __va_copy((d), (s)) -#else -#define va_copy(d, s) ((d) = (s)) -#endif -#endif - #if defined(MINGW_ENABLED) || defined(_MSC_VER) #define sprintf sprintf_s #endif @@ -58,23 +47,34 @@ void Logger::log_error(const char *p_function, const char *p_file, int p_line, c return; } - const char *err_type = "**ERROR**"; + const char *err_type = "ERROR"; switch (p_type) { - case ERR_ERROR: err_type = "**ERROR**"; break; - case ERR_WARNING: err_type = "**WARNING**"; break; - case ERR_SCRIPT: err_type = "**SCRIPT ERROR**"; break; - case ERR_SHADER: err_type = "**SHADER ERROR**"; break; - default: ERR_PRINT("Unknown error type"); break; + case ERR_ERROR: + err_type = "ERROR"; + break; + case ERR_WARNING: + err_type = "WARNING"; + break; + case ERR_SCRIPT: + err_type = "SCRIPT ERROR"; + break; + case ERR_SHADER: + err_type = "SHADER ERROR"; + break; + default: + ERR_PRINT("Unknown error type"); + break; } const char *err_details; - if (p_rationale && *p_rationale) + if (p_rationale && *p_rationale) { err_details = p_rationale; - else + } else { err_details = p_code; + } logf_error("%s: %s\n", err_type, err_details); - logf_error(" At: %s:%i:%s() - %s\n", p_file, p_line, p_function, p_code); + logf_error(" at: %s (%s:%i) - %s\n", p_function, p_file, p_line, p_code); } void Logger::logf(const char *p_format, ...) { @@ -103,12 +103,10 @@ void Logger::logf_error(const char *p_format, ...) { va_end(argp); } -Logger::~Logger() {} - void RotatedFileLogger::close_file() { if (file) { memdelete(file); - file = NULL; + file = nullptr; } } @@ -154,7 +152,7 @@ void RotatedFileLogger::rotate_file() { char timestamp[21]; OS::Date date = OS::get_singleton()->get_date(); OS::Time time = OS::get_singleton()->get_time(); - sprintf(timestamp, "-%04d-%02d-%02d-%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); + sprintf(timestamp, "_%04d-%02d-%02d_%02d-%02d-%02d", date.year, date.month, date.day, time.hour, time.min, time.sec); String backup_name = base_path.get_basename() + timestamp; if (base_path.get_extension() != String()) { @@ -181,8 +179,7 @@ void RotatedFileLogger::rotate_file() { RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) : base_path(p_base_path.simplify_path()), - max_files(p_max_files > 0 ? p_max_files : 1), - file(NULL) { + max_files(p_max_files > 0 ? p_max_files : 1) { rotate_file(); } @@ -237,8 +234,6 @@ void StdLogger::logv(const char *p_format, va_list p_list, bool p_err) { } } -StdLogger::~StdLogger() {} - CompositeLogger::CompositeLogger(Vector<Logger *> p_loggers) : loggers(p_loggers) { } diff --git a/core/io/logger.h b/core/io/logger.h index ab2f9d8bc7..277be9ed35 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -55,17 +55,16 @@ public: void logf(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; void logf_error(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3; - virtual ~Logger(); + virtual ~Logger() {} }; /** * Writes messages to stdout/stderr. */ class StdLogger : public Logger { - public: virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0; - virtual ~StdLogger(); + virtual ~StdLogger() {} }; /** @@ -78,7 +77,7 @@ class RotatedFileLogger : public Logger { String base_path; int max_files; - FileAccess *file; + FileAccess *file = nullptr; void rotate_file_without_closing(); void close_file(); @@ -107,4 +106,4 @@ public: virtual ~CompositeLogger(); }; -#endif +#endif // LOGGER_H diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index e847a9cf0c..eb39b1433f 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -49,14 +49,9 @@ void EncodedObjectAsID::set_object_id(ObjectID p_id) { } ObjectID EncodedObjectAsID::get_object_id() const { - return id; } -EncodedObjectAsID::EncodedObjectAsID() : - id(0) { -} - #define _S(a) ((int32_t)a) #define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(_S(b) < 0 || _S(a) < 0 || _S(a) > INT_MAX - _S(b), err) #define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(_S(a) < 0 || _S(b) <= 0 || _S(a) > INT_MAX / _S(b), err) @@ -101,7 +96,6 @@ static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r } Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_objects) { - const uint8_t *buf = p_buffer; int len = p_len; @@ -113,83 +107,95 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int buf += 4; len -= 4; - if (r_len) + if (r_len) { *r_len = 4; + } switch (type & ENCODE_MASK) { - case Variant::NIL: { - r_variant = Variant(); } break; case Variant::BOOL: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); bool val = decode_uint32(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4; + } } break; case Variant::INT: { - if (type & ENCODE_FLAG_64) { ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); int64_t val = decode_uint64(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 8; + } } else { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t val = decode_uint32(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4; + } } } break; - case Variant::REAL: { - + case Variant::FLOAT: { if (type & ENCODE_FLAG_64) { ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); double val = decode_double(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 8; + } } else { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); float val = decode_float(buf); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4; + } } } break; case Variant::STRING: { - String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } r_variant = str; } break; // math types case Variant::VECTOR2: { - ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA); Vector2 val; val.x = decode_float(&buf[0]); val.y = decode_float(&buf[4]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 2; + } - } break; // 5 - case Variant::RECT2: { + } break; + case Variant::VECTOR2I: { + ERR_FAIL_COND_V(len < 4 * 2, ERR_INVALID_DATA); + Vector2i val; + val.x = decode_uint32(&buf[0]); + val.y = decode_uint32(&buf[4]); + r_variant = val; + + if (r_len) { + (*r_len) += 4 * 2; + } + } break; + case Variant::RECT2: { ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Rect2 val; val.position.x = decode_float(&buf[0]); @@ -198,12 +204,26 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.size.y = decode_float(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } } break; - case Variant::VECTOR3: { + case Variant::RECT2I: { + ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); + Rect2i val; + val.position.x = decode_uint32(&buf[0]); + val.position.y = decode_uint32(&buf[4]); + val.size.x = decode_uint32(&buf[8]); + val.size.y = decode_uint32(&buf[12]); + r_variant = val; + if (r_len) { + (*r_len) += 4 * 4; + } + + } break; + case Variant::VECTOR3: { ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA); Vector3 val; val.x = decode_float(&buf[0]); @@ -211,29 +231,41 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.z = decode_float(&buf[8]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 3; + } } break; - case Variant::TRANSFORM2D: { + case Variant::VECTOR3I: { + ERR_FAIL_COND_V(len < 4 * 3, ERR_INVALID_DATA); + Vector3i val; + val.x = decode_uint32(&buf[0]); + val.y = decode_uint32(&buf[4]); + val.z = decode_uint32(&buf[8]); + r_variant = val; + + if (r_len) { + (*r_len) += 4 * 3; + } + } break; + case Variant::TRANSFORM2D: { ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); Transform2D val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - val.elements[i][j] = decode_float(&buf[(i * 2 + j) * 4]); } } r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 6; + } } break; case Variant::PLANE: { - ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Plane val; val.normal.x = decode_float(&buf[0]); @@ -242,12 +274,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.d = decode_float(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } } break; case Variant::QUAT: { - ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Quat val; val.x = decode_float(&buf[0]); @@ -256,12 +288,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.w = decode_float(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } } break; case Variant::AABB: { - ERR_FAIL_COND_V(len < 4 * 6, ERR_INVALID_DATA); AABB val; val.position.x = decode_float(&buf[0]); @@ -272,34 +304,32 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.size.z = decode_float(&buf[20]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 6; + } } break; case Variant::BASIS: { - ERR_FAIL_COND_V(len < 4 * 9, ERR_INVALID_DATA); Basis val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - val.elements[i][j] = decode_float(&buf[(i * 3 + j) * 4]); } } r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 9; + } } break; case Variant::TRANSFORM: { - ERR_FAIL_COND_V(len < 4 * 12, ERR_INVALID_DATA); Transform val; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - val.basis.elements[i][j] = decode_float(&buf[(i * 3 + j) * 4]); } } @@ -309,14 +339,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 12; + } } break; // misc types case Variant::COLOR: { - ERR_FAIL_COND_V(len < 4 * 4, ERR_INVALID_DATA); Color val; val.r = decode_float(&buf[0]); @@ -325,12 +355,22 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int val.a = decode_float(&buf[12]); r_variant = val; - if (r_len) + if (r_len) { (*r_len) += 4 * 4; + } + + } break; + case Variant::STRING_NAME: { + String str; + Error err = _decode_string(buf, len, r_len, str); + if (err) { + return err; + } + r_variant = StringName(str); } break; - case Variant::NODE_PATH: { + case Variant::NODE_PATH: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t strlen = decode_uint32(buf); @@ -347,25 +387,28 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int len -= 12; buf += 12; - if (flags & 2) // Obsolete format with property separate from subpath + if (flags & 2) { // Obsolete format with property separate from subpath subnamecount++; + } uint32_t total = namecount + subnamecount; - if (r_len) + if (r_len) { (*r_len) += 12; + } for (uint32_t i = 0; i < total; i++) { - String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } - if (i < namecount) + if (i < namecount) { names.push_back(str); - else + } else { subnames.push_back(str); + } } r_variant = NodePath(names, subnames, flags & 1); @@ -378,20 +421,19 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::_RID: { - r_variant = RID(); } break; case Variant::OBJECT: { - if (type & ENCODE_FLAG_OBJECT_AS_ID) { //this _is_ allowed ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); - ObjectID val = decode_uint64(buf); - if (r_len) + ObjectID val = ObjectID(decode_uint64(buf)); + if (r_len) { (*r_len) += 8; + } - if (val == 0) { - r_variant = (Object *)NULL; + if (val.is_null()) { + r_variant = (Object *)nullptr; } else { Ref<EncodedObjectAsID> obj_as_id; obj_as_id.instance(); @@ -405,13 +447,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } if (str == String()) { - r_variant = (Object *)NULL; + r_variant = (Object *)nullptr; } else { - Object *obj = ClassDB::instance(str); ERR_FAIL_COND_V(!obj, ERR_UNAVAILABLE); @@ -425,17 +467,18 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } for (int i = 0; i < count; i++) { - str = String(); err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } Variant value; int used; err = decode_variant(value, buf, len, &used, p_allow_objects); - if (err) + if (err) { return err; + } buf += used; len -= used; @@ -456,8 +499,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } } break; - case Variant::DICTIONARY: { + case Variant::CALLABLE: { + r_variant = Callable(); + } break; + case Variant::SIGNAL: { + r_variant = Signal(); + } break; + case Variant::DICTIONARY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; @@ -473,7 +522,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Dictionary d; for (int i = 0; i < count; i++) { - Variant key, value; int used; @@ -502,7 +550,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::ARRAY: { - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; @@ -518,7 +565,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Array varr; for (int i = 0; i < count; i++) { - int used = 0; Variant v; Error err = decode_variant(v, buf, len, &used, p_allow_objects); @@ -536,21 +582,19 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; // arrays - case Variant::POOL_BYTE_ARRAY: { - + case Variant::PACKED_BYTE_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; len -= 4; ERR_FAIL_COND_V(count < 0 || count > len, ERR_INVALID_DATA); - PoolVector<uint8_t> data; + Vector<uint8_t> data; if (count) { data.resize(count); - PoolVector<uint8_t>::Write w = data.write(); + uint8_t *w = data.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i] = buf[i]; } } @@ -558,14 +602,14 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = data; if (r_len) { - if (count % 4) + if (count % 4) { (*r_len) += 4 - count % 4; + } (*r_len) += 4 + count; } } break; - case Variant::POOL_INT_ARRAY: { - + case Variant::PACKED_INT32_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -573,25 +617,47 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); - PoolVector<int> data; + Vector<int32_t> data; if (count) { //const int*rbuf=(const int*)buf; data.resize(count); - PoolVector<int>::Write w = data.write(); + int32_t *w = data.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i] = decode_uint32(&buf[i * 4]); } } r_variant = Variant(data); if (r_len) { - (*r_len) += 4 + count * sizeof(int); + (*r_len) += 4 + count * sizeof(int32_t); } } break; - case Variant::POOL_REAL_ARRAY: { + case Variant::PACKED_INT64_ARRAY: { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + int64_t count = decode_uint64(buf); + buf += 4; + len -= 4; + ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 8 > len, ERR_INVALID_DATA); + + Vector<int64_t> data; + if (count) { + //const int*rbuf=(const int*)buf; + data.resize(count); + int64_t *w = data.ptrw(); + for (int64_t i = 0; i < count; i++) { + w[i] = decode_uint64(&buf[i * 8]); + } + } + r_variant = Variant(data); + if (r_len) { + (*r_len) += 4 + count * sizeof(int64_t); + } + + } break; + case Variant::PACKED_FLOAT32_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -599,14 +665,13 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int ERR_FAIL_MUL_OF(count, 4, ERR_INVALID_DATA); ERR_FAIL_COND_V(count < 0 || count * 4 > len, ERR_INVALID_DATA); - PoolVector<float> data; + Vector<float> data; if (count) { //const float*rbuf=(const float*)buf; data.resize(count); - PoolVector<float>::Write w = data.write(); + float *w = data.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i] = decode_float(&buf[i * 4]); } } @@ -617,25 +682,50 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } } break; - case Variant::POOL_STRING_ARRAY: { + case Variant::PACKED_FLOAT64_ARRAY: { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + int64_t count = decode_uint64(buf); + buf += 4; + len -= 4; + ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA); + ERR_FAIL_COND_V(count < 0 || count * 8 > len, ERR_INVALID_DATA); + + Vector<double> data; + if (count) { + //const double*rbuf=(const double*)buf; + data.resize(count); + double *w = data.ptrw(); + for (int64_t i = 0; i < count; i++) { + w[i] = decode_double(&buf[i * 8]); + } + } + r_variant = data; + + if (r_len) { + (*r_len) += 4 + count * sizeof(double); + } + + } break; + case Variant::PACKED_STRING_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); - PoolVector<String> strings; + Vector<String> strings; buf += 4; len -= 4; - if (r_len) + if (r_len) { (*r_len) += 4; + } //printf("string count: %i\n",count); for (int32_t i = 0; i < count; i++) { - String str; Error err = _decode_string(buf, len, r_len, str); - if (err) + if (err) { return err; + } strings.push_back(str); } @@ -643,8 +733,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = strings; } break; - case Variant::POOL_VECTOR2_ARRAY: { - + case Variant::PACKED_VECTOR2_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -652,7 +741,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int ERR_FAIL_MUL_OF(count, 4 * 2, ERR_INVALID_DATA); ERR_FAIL_COND_V(count < 0 || count * 4 * 2 > len, ERR_INVALID_DATA); - PoolVector<Vector2> varray; + Vector<Vector2> varray; if (r_len) { (*r_len) += 4; @@ -660,25 +749,24 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (count) { varray.resize(count); - PoolVector<Vector2>::Write w = varray.write(); + Vector2 *w = varray.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i].x = decode_float(buf + i * 4 * 2 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 2 + 4 * 1); } int adv = 4 * 2 * count; - if (r_len) + if (r_len) { (*r_len) += adv; + } } r_variant = varray; } break; - case Variant::POOL_VECTOR3_ARRAY: { - + case Variant::PACKED_VECTOR3_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -687,7 +775,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int ERR_FAIL_MUL_OF(count, 4 * 3, ERR_INVALID_DATA); ERR_FAIL_COND_V(count < 0 || count * 4 * 3 > len, ERR_INVALID_DATA); - PoolVector<Vector3> varray; + Vector<Vector3> varray; if (r_len) { (*r_len) += 4; @@ -695,10 +783,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (count) { varray.resize(count); - PoolVector<Vector3>::Write w = varray.write(); + Vector3 *w = varray.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i].x = decode_float(buf + i * 4 * 3 + 4 * 0); w[i].y = decode_float(buf + i * 4 * 3 + 4 * 1); w[i].z = decode_float(buf + i * 4 * 3 + 4 * 2); @@ -706,15 +793,15 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int int adv = 4 * 3 * count; - if (r_len) + if (r_len) { (*r_len) += adv; + } } r_variant = varray; } break; - case Variant::POOL_COLOR_ARRAY: { - + case Variant::PACKED_COLOR_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; @@ -723,7 +810,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int ERR_FAIL_MUL_OF(count, 4 * 4, ERR_INVALID_DATA); ERR_FAIL_COND_V(count < 0 || count * 4 * 4 > len, ERR_INVALID_DATA); - PoolVector<Color> carray; + Vector<Color> carray; if (r_len) { (*r_len) += 4; @@ -731,10 +818,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (count) { carray.resize(count); - PoolVector<Color>::Write w = carray.write(); + Color *w = carray.ptrw(); for (int32_t i = 0; i < count; i++) { - w[i].r = decode_float(buf + i * 4 * 4 + 4 * 0); w[i].g = decode_float(buf + i * 4 * 4 + 4 * 1); w[i].b = decode_float(buf + i * 4 * 4 + 4 * 2); @@ -743,8 +829,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int int adv = 4 * 4 * count; - if (r_len) + if (r_len) { (*r_len) += adv; + } } r_variant = carray; @@ -759,7 +846,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { - CharString utf8 = p_string.utf8(); if (buf) { @@ -779,7 +865,6 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { } Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects) { - uint8_t *buf = r_buffer; r_len = 0; @@ -787,15 +872,13 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo uint32_t flags = 0; switch (p_variant.get_type()) { - case Variant::INT: { int64_t val = p_variant; if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) { flags |= ENCODE_FLAG_64; } } break; - case Variant::REAL: { - + case Variant::FLOAT: { double d = p_variant; float f = d; if (double(f) != d) { @@ -803,18 +886,17 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } } break; case Variant::OBJECT: { -#ifdef DEBUG_ENABLED // Test for potential wrong values sent by the debugger when it breaks. - Object *obj = p_variant; - if (!obj || !ObjectDB::instance_validate(obj)) { - // Object is invalid, send a NULL instead. + Object *obj = p_variant.get_validated_object(); + if (!obj) { + // Object is invalid, send a nullptr instead. if (buf) { encode_uint32(Variant::NIL, buf); } r_len += 4; return OK; } -#endif // DEBUG_ENABLED + if (!p_full_objects) { flags |= ENCODE_FLAG_OBJECT_AS_ID; } @@ -830,13 +912,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; switch (p_variant.get_type()) { - case Variant::NIL: { - //nothing to do } break; case Variant::BOOL: { - if (buf) { encode_uint32(p_variant.operator bool(), buf); } @@ -845,7 +924,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::INT: { - if (flags & ENCODE_FLAG_64) { //64 bits if (buf) { @@ -861,8 +939,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; } } break; - case Variant::REAL: { - + case Variant::FLOAT: { if (flags & ENCODE_FLAG_64) { if (buf) { encode_double(p_variant.operator double(), buf); @@ -871,7 +948,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 8; } else { - if (buf) { encode_float(p_variant.operator float(), buf); } @@ -881,14 +957,14 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::NODE_PATH: { - NodePath np = p_variant; if (buf) { encode_uint32(uint32_t(np.get_name_count()) | 0x80000000, buf); //for compatibility with the old format encode_uint32(np.get_subname_count(), buf + 4); uint32_t np_flags = 0; - if (np.is_absolute()) + if (np.is_absolute()) { np_flags |= 1; + } encode_uint32(np_flags, buf + 8); @@ -900,20 +976,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo int total = np.get_name_count() + np.get_subname_count(); for (int i = 0; i < total; i++) { - String str; - if (i < np.get_name_count()) + if (i < np.get_name_count()) { str = np.get_name(i); - else + } else { str = np.get_subname(i - np.get_name_count()); + } CharString utf8 = str.utf8(); int pad = 0; - if (utf8.length() % 4) + if (utf8.length() % 4) { pad = 4 - utf8.length() % 4; + } if (buf) { encode_uint32(utf8.length(), buf); @@ -927,14 +1004,16 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::STRING: { + _encode_string(p_variant, buf, r_len); + } break; + case Variant::STRING_NAME: { _encode_string(p_variant, buf, r_len); } break; // math types case Variant::VECTOR2: { - if (buf) { Vector2 v2 = p_variant; encode_float(v2.x, &buf[0]); @@ -943,9 +1022,18 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 2 * 4; - } break; // 5 - case Variant::RECT2: { + } break; + case Variant::VECTOR2I: { + if (buf) { + Vector2i v2 = p_variant; + encode_uint32(v2.x, &buf[0]); + encode_uint32(v2.y, &buf[4]); + } + + r_len += 2 * 4; + } break; + case Variant::RECT2: { if (buf) { Rect2 r2 = p_variant; encode_float(r2.position.x, &buf[0]); @@ -956,8 +1044,18 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 * 4; } break; - case Variant::VECTOR3: { + case Variant::RECT2I: { + if (buf) { + Rect2i r2 = p_variant; + encode_uint32(r2.position.x, &buf[0]); + encode_uint32(r2.position.y, &buf[4]); + encode_uint32(r2.size.x, &buf[8]); + encode_uint32(r2.size.y, &buf[12]); + } + r_len += 4 * 4; + } break; + case Variant::VECTOR3: { if (buf) { Vector3 v3 = p_variant; encode_float(v3.x, &buf[0]); @@ -968,13 +1066,22 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 3 * 4; } break; - case Variant::TRANSFORM2D: { + case Variant::VECTOR3I: { + if (buf) { + Vector3i v3 = p_variant; + encode_uint32(v3.x, &buf[0]); + encode_uint32(v3.y, &buf[4]); + encode_uint32(v3.z, &buf[8]); + } + + r_len += 3 * 4; + } break; + case Variant::TRANSFORM2D: { if (buf) { Transform2D val = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { - copymem(&buf[(i * 2 + j) * 4], &val.elements[i][j], sizeof(float)); } } @@ -984,7 +1091,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::PLANE: { - if (buf) { Plane p = p_variant; encode_float(p.normal.x, &buf[0]); @@ -997,7 +1103,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::QUAT: { - if (buf) { Quat q = p_variant; encode_float(q.x, &buf[0]); @@ -1010,7 +1115,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::AABB: { - if (buf) { AABB aabb = p_variant; encode_float(aabb.position.x, &buf[0]); @@ -1025,12 +1129,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::BASIS: { - if (buf) { Basis val = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - copymem(&buf[(i * 3 + j) * 4], &val.elements[i][j], sizeof(float)); } } @@ -1040,12 +1142,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::TRANSFORM: { - if (buf) { Transform val = p_variant; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - copymem(&buf[(i * 3 + j) * 4], &val.basis.elements[i][j], sizeof(float)); } } @@ -1061,7 +1161,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo // misc types case Variant::COLOR: { - if (buf) { Color c = p_variant; encode_float(c.r, &buf[0]); @@ -1074,12 +1173,13 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::_RID: { - + } break; + case Variant::CALLABLE: { + } break; + case Variant::SIGNAL: { } break; case Variant::OBJECT: { - if (p_full_objects) { - Object *obj = p_variant; if (!obj) { if (buf) { @@ -1095,9 +1195,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo int pc = 0; for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; + } pc++; } @@ -1109,28 +1209,29 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) + if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { continue; + } _encode_string(E->get().name, buf, r_len); int len; Error err = encode_variant(obj->get(E->get().name), buf, len, p_full_objects); - if (err) + if (err) { return err; + } ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } } } } else { if (buf) { - - Object *obj = p_variant; - ObjectID id = 0; - if (obj && ObjectDB::instance_validate(obj)) { + Object *obj = p_variant.get_validated_object(); + ObjectID id; + if (obj) { id = obj->get_instance_id(); } @@ -1142,7 +1243,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::DICTIONARY: { - Dictionary d = p_variant; if (buf) { @@ -1155,7 +1255,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - /* CharString utf8 = E->->utf8(); @@ -1173,20 +1272,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_variant(E->get(), buf, len, p_full_objects); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } Variant *v = d.getptr(E->get()); ERR_FAIL_COND_V(!v, ERR_BUG); encode_variant(*v, buf, len, p_full_objects); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } } } break; case Variant::ARRAY: { - Array v = p_variant; if (buf) { @@ -1197,27 +1297,26 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; for (int i = 0; i < v.size(); i++) { - int len; encode_variant(v.get(i), buf, len, p_full_objects); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; - if (buf) + if (buf) { buf += len; + } } } break; // arrays - case Variant::POOL_BYTE_ARRAY: { - - PoolVector<uint8_t> data = p_variant; + case Variant::PACKED_BYTE_ARRAY: { + Vector<uint8_t> data = p_variant; int datalen = data.size(); int datasize = sizeof(uint8_t); if (buf) { encode_uint32(datalen, buf); buf += 4; - PoolVector<uint8_t>::Read r = data.read(); + const uint8_t *r = data.ptr(); copymem(buf, &r[0], datalen * datasize); buf += datalen * datasize; } @@ -1225,48 +1324,82 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 + datalen * datasize; while (r_len % 4) { r_len++; - if (buf) + if (buf) { *(buf++) = 0; + } } } break; - case Variant::POOL_INT_ARRAY: { - - PoolVector<int> data = p_variant; + case Variant::PACKED_INT32_ARRAY: { + Vector<int32_t> data = p_variant; int datalen = data.size(); int datasize = sizeof(int32_t); if (buf) { encode_uint32(datalen, buf); buf += 4; - PoolVector<int>::Read r = data.read(); - for (int i = 0; i < datalen; i++) + const int32_t *r = data.ptr(); + for (int32_t i = 0; i < datalen; i++) { encode_uint32(r[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; } break; - case Variant::POOL_REAL_ARRAY: { + case Variant::PACKED_INT64_ARRAY: { + Vector<int64_t> data = p_variant; + int datalen = data.size(); + int datasize = sizeof(int64_t); + + if (buf) { + encode_uint64(datalen, buf); + buf += 4; + const int64_t *r = data.ptr(); + for (int64_t i = 0; i < datalen; i++) { + encode_uint64(r[i], &buf[i * datasize]); + } + } + + r_len += 4 + datalen * datasize; - PoolVector<real_t> data = p_variant; + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + Vector<float> data = p_variant; int datalen = data.size(); - int datasize = sizeof(real_t); + int datasize = sizeof(float); if (buf) { encode_uint32(datalen, buf); buf += 4; - PoolVector<real_t>::Read r = data.read(); - for (int i = 0; i < datalen; i++) + const float *r = data.ptr(); + for (int i = 0; i < datalen; i++) { encode_float(r[i], &buf[i * datasize]); + } } r_len += 4 + datalen * datasize; } break; - case Variant::POOL_STRING_ARRAY: { + case Variant::PACKED_FLOAT64_ARRAY: { + Vector<double> data = p_variant; + int datalen = data.size(); + int datasize = sizeof(double); - PoolVector<String> data = p_variant; + if (buf) { + encode_uint32(datalen, buf); + buf += 4; + const double *r = data.ptr(); + for (int i = 0; i < datalen; i++) { + encode_double(r[i], &buf[i * datasize]); + } + } + + r_len += 4 + datalen * datasize; + + } break; + case Variant::PACKED_STRING_ARRAY: { + Vector<String> data = p_variant; int len = data.size(); if (buf) { @@ -1277,7 +1410,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; for (int i = 0; i < len; i++) { - CharString utf8 = data.get(i).utf8(); if (buf) { @@ -1290,15 +1422,15 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 + utf8.length() + 1; while (r_len % 4) { r_len++; //pad - if (buf) + if (buf) { *(buf++) = 0; + } } } } break; - case Variant::POOL_VECTOR2_ARRAY: { - - PoolVector<Vector2> data = p_variant; + case Variant::PACKED_VECTOR2_ARRAY: { + Vector<Vector2> data = p_variant; int len = data.size(); if (buf) { @@ -1309,9 +1441,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; if (buf) { - for (int i = 0; i < len; i++) { - Vector2 v = data.get(i); encode_float(v.x, &buf[0]); @@ -1323,9 +1453,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 * 2 * len; } break; - case Variant::POOL_VECTOR3_ARRAY: { - - PoolVector<Vector3> data = p_variant; + case Variant::PACKED_VECTOR3_ARRAY: { + Vector<Vector3> data = p_variant; int len = data.size(); if (buf) { @@ -1336,9 +1465,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; if (buf) { - for (int i = 0; i < len; i++) { - Vector3 v = data.get(i); encode_float(v.x, &buf[0]); @@ -1351,9 +1478,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4 * 3 * len; } break; - case Variant::POOL_COLOR_ARRAY: { - - PoolVector<Color> data = p_variant; + case Variant::PACKED_COLOR_ARRAY: { + Vector<Color> data = p_variant; int len = data.size(); if (buf) { @@ -1364,9 +1490,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; if (buf) { - for (int i = 0; i < len; i++) { - Color c = data.get(i); encode_float(c.r, &buf[0]); diff --git a/core/io/marshalls.h b/core/io/marshalls.h index 484f0755de..c8ed497528 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -41,21 +41,17 @@ */ union MarshallFloat { - uint32_t i; ///< int float f; ///< float }; union MarshallDouble { - uint64_t l; ///< long long double d; ///< double }; static inline unsigned int encode_uint16(uint16_t p_uint, uint8_t *p_arr) { - for (int i = 0; i < 2; i++) { - *p_arr = p_uint & 0xFF; p_arr++; p_uint >>= 8; @@ -65,9 +61,7 @@ static inline unsigned int encode_uint16(uint16_t p_uint, uint8_t *p_arr) { } static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) { - for (int i = 0; i < 4; i++) { - *p_arr = p_uint & 0xFF; p_arr++; p_uint >>= 8; @@ -77,7 +71,6 @@ static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) { } static inline unsigned int encode_float(float p_float, uint8_t *p_arr) { - MarshallFloat mf; mf.f = p_float; encode_uint32(mf.i, p_arr); @@ -86,9 +79,7 @@ static inline unsigned int encode_float(float p_float, uint8_t *p_arr) { } static inline unsigned int encode_uint64(uint64_t p_uint, uint8_t *p_arr) { - for (int i = 0; i < 8; i++) { - *p_arr = p_uint & 0xFF; p_arr++; p_uint >>= 8; @@ -98,7 +89,6 @@ static inline unsigned int encode_uint64(uint64_t p_uint, uint8_t *p_arr) { } static inline unsigned int encode_double(double p_double, uint8_t *p_arr) { - MarshallDouble md; md.d = p_double; encode_uint64(md.l, p_arr); @@ -107,30 +97,27 @@ static inline unsigned int encode_double(double p_double, uint8_t *p_arr) { } static inline int encode_cstring(const char *p_string, uint8_t *p_data) { - int len = 0; while (*p_string) { - if (p_data) { - *p_data = (uint8_t)*p_string; p_data++; } p_string++; len++; - }; + } - if (p_data) *p_data = 0; + if (p_data) { + *p_data = 0; + } return len + 1; } static inline uint16_t decode_uint16(const uint8_t *p_arr) { - uint16_t u = 0; for (int i = 0; i < 2; i++) { - uint16_t b = *p_arr; b <<= (i * 8); u |= b; @@ -141,11 +128,9 @@ static inline uint16_t decode_uint16(const uint8_t *p_arr) { } static inline uint32_t decode_uint32(const uint8_t *p_arr) { - uint32_t u = 0; for (int i = 0; i < 4; i++) { - uint32_t b = *p_arr; b <<= (i * 8); u |= b; @@ -156,18 +141,15 @@ static inline uint32_t decode_uint32(const uint8_t *p_arr) { } static inline float decode_float(const uint8_t *p_arr) { - MarshallFloat mf; mf.i = decode_uint32(p_arr); return mf.f; } static inline uint64_t decode_uint64(const uint8_t *p_arr) { - uint64_t u = 0; for (int i = 0; i < 8; i++) { - uint64_t b = (*p_arr) & 0xFF; b <<= (i * 8); u |= b; @@ -178,7 +160,6 @@ static inline uint64_t decode_uint64(const uint8_t *p_arr) { } static inline double decode_double(const uint8_t *p_arr) { - MarshallDouble md; md.l = decode_uint64(p_arr); return md.d; @@ -196,10 +177,10 @@ public: void set_object_id(ObjectID p_id); ObjectID get_object_id() const; - EncodedObjectAsID(); + EncodedObjectAsID() {} }; -Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = NULL, bool p_allow_objects = false); +Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false); Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false); -#endif +#endif // MARSHALLS_H diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 381ac4c0bb..2f17d9e746 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -30,17 +30,22 @@ #include "multiplayer_api.h" +#include "core/debugger/engine_debugger.h" #include "core/io/marshalls.h" #include "scene/main/node.h" +#include <stdint.h> + +#define NODE_ID_COMPRESSION_SHIFT 3 +#define NAME_ID_COMPRESSION_SHIFT 5 +#define BYTE_ONLY_OR_NO_ARGS_SHIFT 6 + #ifdef DEBUG_ENABLED #include "core/os/os.h" #endif _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { - switch (mode) { - case MultiplayerAPI::RPC_MODE_DISABLED: { // Do nothing. } break; @@ -48,9 +53,10 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas // Do nothing also. Remote cannot produce a local call. } break; case MultiplayerAPI::RPC_MODE_MASTERSYNC: { - if (is_master) + if (is_master) { r_skip_rpc = true; // I am the master, so skip remote call. - FALLTHROUGH; + } + [[fallthrough]]; } case MultiplayerAPI::RPC_MODE_REMOTESYNC: case MultiplayerAPI::RPC_MODE_PUPPETSYNC: { @@ -58,8 +64,9 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas return true; } break; case MultiplayerAPI::RPC_MODE_MASTER: { - if (is_master) + if (is_master) { r_skip_rpc = true; // I am the master, so skip remote call. + } return is_master; } break; case MultiplayerAPI::RPC_MODE_PUPPET: { @@ -71,7 +78,6 @@ _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_mas _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { switch (mode) { - case MultiplayerAPI::RPC_MODE_DISABLED: { return false; } break; @@ -93,17 +99,17 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i } void MultiplayerAPI::poll() { - - if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) + if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) { return; + } network_peer->poll(); - if (!network_peer.is_valid()) // It's possible that polling might have resulted in a disconnection, so check here. + if (!network_peer.is_valid()) { // It's possible that polling might have resulted in a disconnection, so check here. return; + } while (network_peer->get_available_packet_count()) { - int sender = network_peer->get_packet_peer(); const uint8_t *packet; int len; @@ -137,29 +143,30 @@ void MultiplayerAPI::set_root_node(Node *p_node) { } void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) { - - if (p_peer == network_peer) return; // Nothing to do + if (p_peer == network_peer) { + return; // Nothing to do + } ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Supplied NetworkedMultiplayerPeer must be connecting or connected."); if (network_peer.is_valid()) { - network_peer->disconnect("peer_connected", this, "_add_peer"); - network_peer->disconnect("peer_disconnected", this, "_del_peer"); - network_peer->disconnect("connection_succeeded", this, "_connected_to_server"); - network_peer->disconnect("connection_failed", this, "_connection_failed"); - network_peer->disconnect("server_disconnected", this, "_server_disconnected"); + network_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); + network_peer->disconnect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); + network_peer->disconnect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); + network_peer->disconnect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); + network_peer->disconnect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); clear(); } network_peer = p_peer; if (network_peer.is_valid()) { - network_peer->connect("peer_connected", this, "_add_peer"); - network_peer->connect("peer_disconnected", this, "_del_peer"); - network_peer->connect("connection_succeeded", this, "_connected_to_server"); - network_peer->connect("connection_failed", this, "_connection_failed"); - network_peer->connect("server_disconnected", this, "_server_disconnected"); + network_peer->connect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); + network_peer->connect("peer_disconnected", callable_mp(this, &MultiplayerAPI::_del_peer)); + network_peer->connect("connection_succeeded", callable_mp(this, &MultiplayerAPI::_connected_to_server)); + network_peer->connect("connection_failed", callable_mp(this, &MultiplayerAPI::_connection_failed)); + network_peer->connect("server_disconnected", callable_mp(this, &MultiplayerAPI::_server_disconnected)); } } @@ -167,83 +174,154 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const { return network_peer; } -void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { +#ifdef DEBUG_ENABLED +void _profile_node_data(const String &p_what, ObjectID p_id) { + if (EngineDebugger::is_profiling("multiplayer")) { + Array values; + values.push_back("node"); + values.push_back(p_id); + values.push_back(p_what); + EngineDebugger::profiler_add_frame_data("multiplayer", values); + } +} - ERR_FAIL_COND_MSG(root_node == NULL, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); +void _profile_bandwidth_data(const String &p_inout, int p_size) { + if (EngineDebugger::is_profiling("multiplayer")) { + Array values; + values.push_back("bandwidth"); + values.push_back(p_inout); + values.push_back(OS::get_singleton()->get_ticks_msec()); + values.push_back(p_size); + EngineDebugger::profiler_add_frame_data("multiplayer", values); + } +} +#endif + +// Returns the packet size stripping the node path added when the node is not yet cached. +int get_packet_len(uint32_t p_node_target, int p_packet_len) { + if (p_node_target & 0x80000000) { + int ofs = p_node_target & 0x7FFFFFFF; + return p_packet_len - (p_packet_len - ofs); + } else { + return p_packet_len; + } +} + +void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_FAIL_COND_MSG(root_node == nullptr, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it."); ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small."); #ifdef DEBUG_ENABLED - if (profiling) { - bandwidth_incoming_data.write[bandwidth_incoming_pointer].timestamp = OS::get_singleton()->get_ticks_msec(); - bandwidth_incoming_data.write[bandwidth_incoming_pointer].packet_size = p_packet_len; - bandwidth_incoming_pointer = (bandwidth_incoming_pointer + 1) % bandwidth_incoming_data.size(); - } + _profile_bandwidth_data("in", p_packet_len); #endif - uint8_t packet_type = p_packet[0]; + // Extract the `packet_type` from the LSB three bits: + uint8_t packet_type = p_packet[0] & 7; switch (packet_type) { - case NETWORK_COMMAND_SIMPLIFY_PATH: { - _process_simplify_path(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_CONFIRM_PATH: { - _process_confirm_path(p_from, p_packet, p_packet_len); } break; case NETWORK_COMMAND_REMOTE_CALL: case NETWORK_COMMAND_REMOTE_SET: { + // Extract packet meta + int packet_min_size = 1; + int name_id_offset = 1; + ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); + // Compute the meta size, which depends on the compression level. + int node_id_compression = (p_packet[0] & 24) >> NODE_ID_COMPRESSION_SHIFT; + int name_id_compression = (p_packet[0] & 32) >> NAME_ID_COMPRESSION_SHIFT; + + switch (node_id_compression) { + case NETWORK_NODE_ID_COMPRESSION_8: + packet_min_size += 1; + name_id_offset += 1; + break; + case NETWORK_NODE_ID_COMPRESSION_16: + packet_min_size += 2; + name_id_offset += 2; + break; + case NETWORK_NODE_ID_COMPRESSION_32: + packet_min_size += 4; + name_id_offset += 4; + break; + default: + ERR_FAIL_MSG("Was not possible to extract the node id compression mode."); + } + switch (name_id_compression) { + case NETWORK_NAME_ID_COMPRESSION_8: + packet_min_size += 1; + break; + case NETWORK_NAME_ID_COMPRESSION_16: + packet_min_size += 2; + break; + default: + ERR_FAIL_MSG("Was not possible to extract the name id compression mode."); + } + ERR_FAIL_COND_MSG(p_packet_len < packet_min_size, "Invalid packet received. Size too small."); - ERR_FAIL_COND_MSG(p_packet_len < 6, "Invalid packet received. Size too small."); - - Node *node = _process_get_node(p_from, p_packet, p_packet_len); - - ERR_FAIL_COND_MSG(node == NULL, "Invalid packet received. Requested node was not found."); - - // Detect cstring end. - int len_end = 5; - for (; len_end < p_packet_len; len_end++) { - if (p_packet[len_end] == 0) { + uint32_t node_target = 0; + switch (node_id_compression) { + case NETWORK_NODE_ID_COMPRESSION_8: + node_target = p_packet[1]; break; - } + case NETWORK_NODE_ID_COMPRESSION_16: + node_target = decode_uint16(p_packet + 1); + break; + case NETWORK_NODE_ID_COMPRESSION_32: + node_target = decode_uint32(p_packet + 1); + break; + default: + // Unreachable, checked before. + CRASH_NOW(); } - ERR_FAIL_COND_MSG(len_end >= p_packet_len, "Invalid packet received. Size too small."); + Node *node = _process_get_node(p_from, p_packet, node_target, p_packet_len); + ERR_FAIL_COND_MSG(node == nullptr, "Invalid packet received. Requested node was not found."); - StringName name = String::utf8((const char *)&p_packet[5]); + uint16_t name_id = 0; + switch (name_id_compression) { + case NETWORK_NAME_ID_COMPRESSION_8: + name_id = p_packet[name_id_offset]; + break; + case NETWORK_NAME_ID_COMPRESSION_16: + name_id = decode_uint16(p_packet + name_id_offset); + break; + default: + // Unreachable, checked before. + CRASH_NOW(); + } + const int packet_len = get_packet_len(node_target, p_packet_len); if (packet_type == NETWORK_COMMAND_REMOTE_CALL) { - - _process_rpc(node, name, p_from, p_packet, p_packet_len, len_end + 1); + _process_rpc(node, name_id, p_from, p_packet, packet_len, packet_min_size); } else { - - _process_rset(node, name, p_from, p_packet, p_packet_len, len_end + 1); + _process_rset(node, name_id, p_from, p_packet, packet_len, packet_min_size); } } break; case NETWORK_COMMAND_RAW: { - _process_raw(p_from, p_packet, p_packet_len); } break; } } -Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len) { - - uint32_t target = decode_uint32(&p_packet[1]); - Node *node = NULL; +Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len) { + Node *node = nullptr; - if (target & 0x80000000) { + if (p_node_target & 0x80000000) { // Use full path (not cached yet). - int ofs = target & 0x7FFFFFFF; + int ofs = p_node_target & 0x7FFFFFFF; - ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, NULL, "Invalid packet received. Size smaller than declared."); + ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, nullptr, "Invalid packet received. Size smaller than declared."); String paths; paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs); @@ -252,127 +330,150 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int node = root_node->get_node(np); - if (!node) - ERR_PRINTS("Failed to get path from RPC: " + String(np) + "."); + if (!node) { + ERR_PRINT("Failed to get path from RPC: " + String(np) + "."); + } } else { // Use cached path. - int id = target; + int id = p_node_target; Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from); - ERR_FAIL_COND_V_MSG(!E, NULL, "Invalid packet received. Requests invalid peer cache."); + ERR_FAIL_COND_V_MSG(!E, nullptr, "Invalid packet received. Requests invalid peer cache."); Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id); - ERR_FAIL_COND_V_MSG(!F, NULL, "Invalid packet received. Unabled to find requested cached node."); + ERR_FAIL_COND_V_MSG(!F, nullptr, "Invalid packet received. Unabled to find requested cached node."); PathGetCache::NodeInfo *ni = &F->get(); // Do proper caching later. node = root_node->get_node(ni->path); - if (!node) - ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path) + "."); + if (!node) { + ERR_PRINT("Failed to get cached path from RPC: " + String(ni->path) + "."); + } } return node; } -void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); +void MultiplayerAPI::_process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { + ERR_FAIL_COND_MSG(p_offset > p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RPC on this node. - RPCMode rpc_mode = RPC_MODE_DISABLED; - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_name); - if (E) { - rpc_mode = E->get(); - } else if (p_node->get_script_instance()) { - rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_name); + StringName name = p_node->get_node_rpc_method(p_rpc_method_id); + RPCMode rpc_mode = p_node->get_node_rpc_mode_by_id(p_rpc_method_id); + if (name == StringName() && p_node->get_script_instance()) { + name = p_node->get_script_instance()->get_rpc_method(p_rpc_method_id); + rpc_mode = p_node->get_script_instance()->get_rpc_mode_by_id(p_rpc_method_id); } + ERR_FAIL_COND(name == StringName()); bool can_call = _can_call_mode(p_node, rpc_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + + int argc = 0; + bool byte_only = false; + + const bool byte_only_or_no_args = ((p_packet[0] & 64) >> BYTE_ONLY_OR_NO_ARGS_SHIFT) == 1; + if (byte_only_or_no_args) { + if (p_offset < p_packet_len) { + // This packet contains only bytes. + argc = 1; + byte_only = true; + } else { + // This rpc calls a method without parameters. + } + } else { + // Normal variant, takes the argument count from the packet. + ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); + argc = p_packet[p_offset]; + p_offset += 1; + } - int argc = p_packet[p_offset]; Vector<Variant> args; Vector<const Variant *> argp; args.resize(argc); argp.resize(argc); - p_offset++; - #ifdef DEBUG_ENABLED - if (profiling) { - ObjectID id = p_node->get_instance_id(); - _init_node_profile(id); - profiler_frame_data[id].incoming_rpc += 1; - } + _profile_node_data("in_rpc", p_node->get_instance_id()); #endif - for (int i = 0; i < argc; i++) { - - ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); + if (byte_only) { + Vector<uint8_t> pure_data; + const int len = p_packet_len - p_offset; + pure_data.resize(len); + memcpy(pure_data.ptrw(), &p_packet[p_offset], len); + args.write[0] = pure_data; + argp.write[0] = &args[0]; + p_offset += len; + } else { + for (int i = 0; i < argc; i++) { + ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); - int vlen; - Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen, allow_object_decoding || network_peer->is_object_decoding_allowed()); - ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); + int vlen; + Error err = _decode_and_decompress_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen); + ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument."); - argp.write[i] = &args[i]; - p_offset += vlen; + argp.write[i] = &args[i]; + p_offset += vlen; + } } - Variant::CallError ce; + Callable::CallError ce; - p_node->call(p_name, (const Variant **)argp.ptr(), argc, ce); - if (ce.error != Variant::CallError::CALL_OK) { - String error = Variant::get_call_error_text(p_node, p_name, (const Variant **)argp.ptr(), argc, ce); + p_node->call(name, (const Variant **)argp.ptr(), argc, ce); + if (ce.error != Callable::CallError::CALL_OK) { + String error = Variant::get_call_error_text(p_node, name, (const Variant **)argp.ptr(), argc, ce); error = "RPC - " + error; - ERR_PRINTS(error); + ERR_PRINT(error); } } -void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { - +void MultiplayerAPI::_process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) { ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small."); // Check that remote can call the RSET on this node. - RPCMode rset_mode = RPC_MODE_DISABLED; - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_name); - if (E) { - rset_mode = E->get(); - } else if (p_node->get_script_instance()) { - rset_mode = p_node->get_script_instance()->get_rset_mode(p_name); + StringName name = p_node->get_node_rset_property(p_rpc_property_id); + RPCMode rset_mode = p_node->get_node_rset_mode_by_id(p_rpc_property_id); + if (name == StringName() && p_node->get_script_instance()) { + name = p_node->get_script_instance()->get_rset_property(p_rpc_property_id); + rset_mode = p_node->get_script_instance()->get_rset_mode_by_id(p_rpc_property_id); } + ERR_FAIL_COND(name == StringName()); bool can_call = _can_call_mode(p_node, rset_mode, p_from); - ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); + ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + "."); #ifdef DEBUG_ENABLED - if (profiling) { - ObjectID id = p_node->get_instance_id(); - _init_node_profile(id); - profiler_frame_data[id].incoming_rset += 1; - } + _profile_node_data("in_rset", p_node->get_instance_id()); #endif Variant value; - Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed()); + Error err = _decode_and_decompress_variant(value, &p_packet[p_offset], p_packet_len - p_offset, nullptr); ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value."); bool valid; - p_node->set(p_name, value, &valid); + p_node->set(name, value, &valid); if (!valid) { - String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class() + "."; - ERR_PRINTS(error); + String error = "Error setting remote property '" + String(name) + "', not found in object of type " + p_node->get_class() + "."; + ERR_PRINT(error); } } void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_FAIL_COND_MSG(p_packet_len < 38, "Invalid packet received. Size too small."); + int ofs = 1; - ERR_FAIL_COND_MSG(p_packet_len < 5, "Invalid packet received. Size too small."); - int id = decode_uint32(&p_packet[1]); + String methods_md5; + methods_md5.parse_utf8((const char *)(p_packet + ofs), 32); + ofs += 33; + + int id = decode_uint32(&p_packet[ofs]); + ofs += 4; String paths; - paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5); + paths.parse_utf8((const char *)(p_packet + ofs), p_packet_len - ofs); NodePath path = paths; @@ -380,21 +481,28 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, path_get_cache[p_from] = PathGetCache(); } + Node *node = root_node->get_node(path); + ERR_FAIL_COND(node == nullptr); + const bool valid_rpc_checksum = node->get_rpc_md5() == methods_md5; + if (valid_rpc_checksum == false) { + ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); + } + PathGetCache::NodeInfo ni; ni.path = path; - ni.instance = 0; path_get_cache[p_from].nodes[id] = ni; // Encode path to send ack. CharString pname = String(path).utf8(); - int len = encode_cstring(pname.get_data(), NULL); + int len = encode_cstring(pname.get_data(), nullptr); Vector<uint8_t> packet; - packet.resize(1 + len); + packet.resize(1 + 1 + len); packet.write[0] = NETWORK_COMMAND_CONFIRM_PATH; - encode_cstring(pname.get_data(), &packet.write[1]); + packet.write[1] = valid_rpc_checksum; + encode_cstring(pname.get_data(), &packet.write[2]); network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_target_peer(p_from); @@ -402,14 +510,19 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, } void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) { + ERR_FAIL_COND_MSG(p_packet_len < 3, "Invalid packet received. Size too small."); - ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); + const bool valid_rpc_checksum = p_packet[1]; String paths; - paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1); + paths.parse_utf8((const char *)&p_packet[2], p_packet_len - 2); NodePath path = paths; + if (valid_rpc_checksum == false) { + ERR_PRINT("The rpc node checksum failed. Make sure to have the same methods on both nodes. Node path: " + path); + } + PathSentCache *psc = path_send_cache.getptr(path); ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache."); @@ -418,17 +531,18 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, E->get() = true; } -bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) { +bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { bool has_all_peers = true; List<int> peers_to_add; // If one is missing, take note to add it. for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - - if (p_target < 0 && E->get() == -p_target) + if (p_target < 0 && E->get() == -p_target) { continue; // Continue, excluded. + } - if (p_target > 0 && E->get() != p_target) + if (p_target > 0 && E->get() != p_target) { continue; // Continue, not for this peer. + } Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); @@ -443,33 +557,198 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int } } - // Those that need to be added, send a message for this. - - for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { + if (peers_to_add.size() > 0) { + // Those that need to be added, send a message for this. // Encode function name. - CharString pname = String(p_path).utf8(); - int len = encode_cstring(pname.get_data(), NULL); + const CharString path = String(p_path).utf8(); + const int path_len = encode_cstring(path.get_data(), nullptr); + + // Extract MD5 from rpc methods list. + const String methods_md5 = p_node->get_rpc_md5(); + const int methods_md5_len = 33; // 32 + 1 for the `0` that is added by the encoder. Vector<uint8_t> packet; + packet.resize(1 + 4 + path_len + methods_md5_len); + int ofs = 0; + + packet.write[ofs] = NETWORK_COMMAND_SIMPLIFY_PATH; + ofs += 1; + + ofs += encode_cstring(methods_md5.utf8().get_data(), &packet.write[ofs]); - packet.resize(1 + 4 + len); - packet.write[0] = NETWORK_COMMAND_SIMPLIFY_PATH; - encode_uint32(psc->id, &packet.write[1]); - encode_cstring(pname.get_data(), &packet.write[5]); + ofs += encode_uint32(psc->id, &packet.write[ofs]); - network_peer->set_target_peer(E->get()); // To all of you. - network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); - network_peer->put_packet(packet.ptr(), packet.size()); + ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. + for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { + network_peer->set_target_peer(E->get()); // To all of you. + network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + network_peer->put_packet(packet.ptr(), packet.size()); + + psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. + } } return has_all_peers; } -void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) { +// The variant is compressed and encoded; The first byte contains all the meta +// information and the format is: +// - The first LSB 5 bits are used for the variant type. +// - The next two bits are used to store the encoding mode. +// - The most significant is used to store the boolean value. +#define VARIANT_META_TYPE_MASK 0x1F +#define VARIANT_META_EMODE_MASK 0x60 +#define VARIANT_META_BOOL_MASK 0x80 +#define ENCODE_8 0 << 5 +#define ENCODE_16 1 << 5 +#define ENCODE_32 2 << 5 +#define ENCODE_64 3 << 5 +Error MultiplayerAPI::_encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len) { + // Unreachable because `VARIANT_MAX` == 27 and `ENCODE_VARIANT_MASK` == 31 + CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK); + + uint8_t *buf = r_buffer; + r_len = 0; + uint8_t encode_mode = 0; + + switch (p_variant.get_type()) { + case Variant::BOOL: { + if (buf) { + // We still have 1 free bit in the meta, so let's use it. + buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0; + buf[0] |= encode_mode | p_variant.get_type(); + } + r_len += 1; + } break; + case Variant::INT: { + if (buf) { + // Reserve the first byte for the meta. + buf += 1; + } + r_len += 1; + int64_t val = p_variant; + if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) { + // Use 8 bit + encode_mode = ENCODE_8; + if (buf) { + buf[0] = val; + } + r_len += 1; + } else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) { + // Use 16 bit + encode_mode = ENCODE_16; + if (buf) { + encode_uint16(val, buf); + } + r_len += 2; + } else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) { + // Use 32 bit + encode_mode = ENCODE_32; + if (buf) { + encode_uint32(val, buf); + } + r_len += 4; + } else { + // Use 64 bit + encode_mode = ENCODE_64; + if (buf) { + encode_uint64(val, buf); + } + r_len += 8; + } + // Store the meta + if (buf) { + buf -= 1; + buf[0] = encode_mode | p_variant.get_type(); + } + } break; + default: + // Any other case is not yet compressed. + Error err = encode_variant(p_variant, r_buffer, r_len, allow_object_decoding); + if (err != OK) { + return err; + } + if (r_buffer) { + // The first byte is not used by the marshaling, so store the type + // so we know how to decompress and decode this variant. + r_buffer[0] = p_variant.get_type(); + } + } + + return OK; +} + +Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len) { + const uint8_t *buf = p_buffer; + int len = p_len; + + ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); + uint8_t type = buf[0] & VARIANT_META_TYPE_MASK; + uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK; + ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA); + + switch (type) { + case Variant::BOOL: { + bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0; + r_variant = val; + if (r_len) { + *r_len = 1; + } + } break; + case Variant::INT: { + buf += 1; + len -= 1; + if (r_len) { + *r_len = 1; + } + if (encode_mode == ENCODE_8) { + // 8 bits. + ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA); + int8_t val = buf[0]; + r_variant = val; + if (r_len) { + (*r_len) += 1; + } + } else if (encode_mode == ENCODE_16) { + // 16 bits. + ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA); + int16_t val = decode_uint16(buf); + r_variant = val; + if (r_len) { + (*r_len) += 2; + } + } else if (encode_mode == ENCODE_32) { + // 32 bits. + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + int32_t val = decode_uint32(buf); + r_variant = val; + if (r_len) { + (*r_len) += 4; + } + } else { + // 64 bits. + ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); + int64_t val = decode_uint64(buf); + r_variant = val; + if (r_len) { + (*r_len) += 8; + } + } + } break; + default: + Error err = decode_variant(r_variant, p_buffer, p_len, r_len, allow_object_decoding); + if (err != OK) { + return err; + } + } + + return OK; +} + +void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); @@ -496,87 +775,180 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p psc->id = last_send_cache_id++; } + // See if all peers have cached path (if so, call can be fast). + const bool has_all_peers = _send_confirm_path(p_from, from_path, psc, p_to); + // Create base packet, lots of hardcode because it must be tight. int ofs = 0; -#define MAKE_ROOM(m_amount) \ - if (packet_cache.size() < m_amount) packet_cache.resize(m_amount); +#define MAKE_ROOM(m_amount) \ + if (packet_cache.size() < m_amount) \ + packet_cache.resize(m_amount); + + // Encode meta. + // The meta is composed by a single byte that contains (starting from the least segnificant bit): + // - `NetworkCommands` in the first three bits. + // - `NetworkNodeIdCompression` in the next 2 bits. + // - `NetworkNameIdCompression` in the next 1 bit. + // - `byte_only_or_no_args` in the next 1 bit. + // - So we still have the last bit free! + uint8_t command_type = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + uint8_t node_id_compression = UINT8_MAX; + uint8_t name_id_compression = UINT8_MAX; + bool byte_only_or_no_args = false; - // Encode type. MAKE_ROOM(1); - packet_cache.write[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL; + // The meta is composed along the way, so just set 0 for now. + packet_cache.write[0] = 0; ofs += 1; - // Encode ID. - MAKE_ROOM(ofs + 4); - encode_uint32(psc->id, &(packet_cache.write[ofs])); - ofs += 4; - - // Encode function name. - CharString name = String(p_name).utf8(); - int len = encode_cstring(name.get_data(), NULL); - MAKE_ROOM(ofs + len); - encode_cstring(name.get_data(), &(packet_cache.write[ofs])); - ofs += len; + // Encode Node ID. + if (has_all_peers) { + // Compress the node ID only if all the target peers already know it. + if (psc->id >= 0 && psc->id <= 255) { + // We can encode the id in 1 byte + node_id_compression = NETWORK_NODE_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(psc->id); + ofs += 1; + } else if (psc->id >= 0 && psc->id <= 65535) { + // We can encode the id in 2 bytes + node_id_compression = NETWORK_NODE_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(static_cast<uint16_t>(psc->id), &(packet_cache.write[ofs])); + ofs += 2; + } else { + // Too big, let's use 4 bytes. + node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; + MAKE_ROOM(ofs + 4); + encode_uint32(psc->id, &(packet_cache.write[ofs])); + ofs += 4; + } + } else { + // The targets doesn't know the node yet, so we need to use 32 bits int. + node_id_compression = NETWORK_NODE_ID_COMPRESSION_32; + MAKE_ROOM(ofs + 4); + encode_uint32(psc->id, &(packet_cache.write[ofs])); + ofs += 4; + } if (p_set) { + // Take the rpc property ID + uint16_t property_id = p_from->get_node_rset_property_id(p_name); + if (property_id == UINT16_MAX && p_from->get_script_instance()) { + property_id = p_from->get_script_instance()->get_rset_property_id(p_name); + } + ERR_FAIL_COND_MSG(property_id == UINT16_MAX, "Unable to take the `property_id` for the property:" + p_name + ". this can happen only if this property is not marked as `remote`."); + + if (property_id <= UINT8_MAX) { + // The ID fits in 1 byte + name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(property_id); + ofs += 1; + } else { + // The ID is larger, let's use 2 bytes + name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(property_id, &(packet_cache.write[ofs])); + ofs += 2; + } + // Set argument. - Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); + int len(0); + Error err = _encode_and_compress_variant(*p_arg[0], nullptr, len); ERR_FAIL_COND_MSG(err != OK, "Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!"); MAKE_ROOM(ofs + len); - encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed()); + _encode_and_compress_variant(*p_arg[0], &(packet_cache.write[ofs]), len); ofs += len; } else { - // Call arguments. - MAKE_ROOM(ofs + 1); - packet_cache.write[ofs] = p_argcount; - ofs += 1; - for (int i = 0; i < p_argcount; i++) { - Error err = encode_variant(*p_arg[i], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed()); - ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); - MAKE_ROOM(ofs + len); - encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed()); - ofs += len; + // Take the rpc method ID + uint16_t method_id = p_from->get_node_rpc_method_id(p_name); + if (method_id == UINT16_MAX && p_from->get_script_instance()) { + method_id = p_from->get_script_instance()->get_rpc_method_id(p_name); + } + ERR_FAIL_COND_MSG(method_id == UINT16_MAX, + vformat("Unable to take the `method_id` for the function \"%s\" at path: \"%s\". This happens when the method is not marked as `remote`.", p_name, p_from->get_path())); + + if (method_id <= UINT8_MAX) { + // The ID fits in 1 byte + name_id_compression = NETWORK_NAME_ID_COMPRESSION_8; + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = static_cast<uint8_t>(method_id); + ofs += 1; + } else { + // The ID is larger, let's use 2 bytes + name_id_compression = NETWORK_NAME_ID_COMPRESSION_16; + MAKE_ROOM(ofs + 2); + encode_uint16(method_id, &(packet_cache.write[ofs])); + ofs += 2; + } + + if (p_argcount == 0) { + byte_only_or_no_args = true; + } else if (p_argcount == 1 && p_arg[0]->get_type() == Variant::PACKED_BYTE_ARRAY) { + byte_only_or_no_args = true; + // Special optimization when only the byte vector is sent. + const Vector<uint8_t> data = *p_arg[0]; + MAKE_ROOM(ofs + data.size()); + copymem(&(packet_cache.write[ofs]), data.ptr(), sizeof(uint8_t) * data.size()); + ofs += data.size(); + } else { + // Arguments + MAKE_ROOM(ofs + 1); + packet_cache.write[ofs] = p_argcount; + ofs += 1; + for (int i = 0; i < p_argcount; i++) { + int len(0); + Error err = _encode_and_compress_variant(*p_arg[i], nullptr, len); + ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!"); + MAKE_ROOM(ofs + len); + _encode_and_compress_variant(*p_arg[i], &(packet_cache.write[ofs]), len); + ofs += len; + } } } + ERR_FAIL_COND(command_type > 7); + ERR_FAIL_COND(node_id_compression > 3); + ERR_FAIL_COND(name_id_compression > 1); + + // We can now set the meta + packet_cache.write[0] = command_type + (node_id_compression << NODE_ID_COMPRESSION_SHIFT) + (name_id_compression << NAME_ID_COMPRESSION_SHIFT) + ((byte_only_or_no_args ? 1 : 0) << BYTE_ONLY_OR_NO_ARGS_SHIFT); + #ifdef DEBUG_ENABLED - if (profiling) { - bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec(); - bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].packet_size = ofs; - bandwidth_outgoing_pointer = (bandwidth_outgoing_pointer + 1) % bandwidth_outgoing_data.size(); - } + _profile_bandwidth_data("out", ofs); #endif - // See if all peers have cached path (is so, call can be fast). - bool has_all_peers = _send_confirm_path(from_path, psc, p_to); - // Take chance and set transfer mode, since all send methods will use it. network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); if (has_all_peers) { - // They all have verified paths, so send fast. network_peer->set_target_peer(p_to); // To all of you. network_peer->put_packet(packet_cache.ptr(), ofs); // A message with love. } else { + // Unreachable because the node ID is never compressed if the peers doesn't know it. + CRASH_COND(node_id_compression != NETWORK_NODE_ID_COMPRESSION_32); + // Not all verified path, so send one by one. // Append path at the end, since we will need it for some packets. CharString pname = String(from_path).utf8(); - int path_len = encode_cstring(pname.get_data(), NULL); + int path_len = encode_cstring(pname.get_data(), nullptr); MAKE_ROOM(ofs + path_len); encode_cstring(pname.get_data(), &(packet_cache.write[ofs])); for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) { - - if (p_to < 0 && E->get() == -p_to) + if (p_to < 0 && E->get() == -p_to) { continue; // Continue, excluded. + } - if (p_to > 0 && E->get() != p_to) + if (p_to > 0 && E->get() != p_to) { continue; // Continue, not for this peer. + } Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get()); ERR_CONTINUE(!F); // Should never happen. @@ -618,22 +990,18 @@ void MultiplayerAPI::_del_peer(int p_id) { } void MultiplayerAPI::_connected_to_server() { - emit_signal("connected_to_server"); } void MultiplayerAPI::_connection_failed() { - emit_signal("connection_failed"); } void MultiplayerAPI::_server_disconnected() { - emit_signal("server_disconnected"); } void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); @@ -647,28 +1015,21 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { // Check that send mode can use local call. - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method); - if (E) { - call_local_native = _should_call_local(E->get(), is_master, skip_rpc); - } + RPCMode rpc_mode = p_node->get_node_rpc_mode(p_method); + call_local_native = _should_call_local(rpc_mode, is_master, skip_rpc); if (call_local_native) { // Done below. } else if (p_node->get_script_instance()) { // Attempt with script. - RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); + rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method); call_local_script = _should_call_local(rpc_mode, is_master, skip_rpc); } } if (!skip_rpc) { - #ifdef DEBUG_ENABLED - if (profiling) { - ObjectID id = p_node->get_instance_id(); - _init_node_profile(id); - profiler_frame_data[id].outgoing_rpc += 1; - } + _profile_node_data("out_rpc", p_node->get_instance_id()); #endif _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); @@ -677,13 +1038,13 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const if (call_local_native) { int temp_id = rpc_sender_id; rpc_sender_id = get_network_unique_id(); - Variant::CallError ce; + Callable::CallError ce; p_node->call(p_method, p_arg, p_argcount, ce); rpc_sender_id = temp_id; - if (ce.error != Variant::CallError::CALL_OK) { + if (ce.error != Callable::CallError::CALL_OK) { String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); error = "rpc() aborted in local call: - " + error + "."; - ERR_PRINTS(error); + ERR_PRINT(error); return; } } @@ -691,14 +1052,14 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const if (call_local_script) { int temp_id = rpc_sender_id; rpc_sender_id = get_network_unique_id(); - Variant::CallError ce; - ce.error = Variant::CallError::CALL_OK; + Callable::CallError ce; + ce.error = Callable::CallError::CALL_OK; p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce); rpc_sender_id = temp_id; - if (ce.error != Variant::CallError::CALL_OK) { + if (ce.error != Callable::CallError::CALL_OK) { String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce); error = "rpc() aborted in script local call: - " + error + "."; - ERR_PRINTS(error); + ERR_PRINT(error); return; } } @@ -707,7 +1068,6 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to RSET while no network peer is active."); ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to RSET on a node which is not inside SceneTree."); ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to send an RSET via a network peer which is not connected."); @@ -719,11 +1079,8 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { // Check that send mode can use local call. - const Map<StringName, RPCMode>::Element *E = p_node->get_node_rset_mode(p_property); - if (E) { - - set_local = _should_call_local(E->get(), is_master, skip_rset); - } + RPCMode rpc_mode = p_node->get_node_rset_mode(p_property); + set_local = _should_call_local(rpc_mode, is_master, skip_rset); if (set_local) { bool valid; @@ -735,12 +1092,12 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const if (!valid) { String error = "rset() aborted in local set, property not found: - " + String(p_property) + "."; - ERR_PRINTS(error); + ERR_PRINT(error); return; } } else if (p_node->get_script_instance()) { // Attempt with script. - RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); + rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property); set_local = _should_call_local(rpc_mode, is_master, skip_rset); @@ -753,7 +1110,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const if (!valid) { String error = "rset() aborted in local script set, property not found: - " + String(p_property) + "."; - ERR_PRINTS(error); + ERR_PRINT(error); return; } } @@ -766,11 +1123,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const } #ifdef DEBUG_ENABLED - if (profiling) { - ObjectID id = p_node->get_instance_id(); - _init_node_profile(id); - profiler_frame_data[id].outgoing_rset += 1; - } + _profile_node_data("out_rset", p_node->get_instance_id()); #endif const Variant *vptr = &p_value; @@ -778,14 +1131,13 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1); } -Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { - +Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); MAKE_ROOM(p_data.size() + 1); - PoolVector<uint8_t>::Read r = p_data.read(); + const uint8_t *r = p_data.ptr(); packet_cache.write[0] = NETWORK_COMMAND_RAW; memcpy(&packet_cache.write[1], &r[0], p_data.size()); @@ -796,46 +1148,40 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, Networked } void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) { - ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small."); - PoolVector<uint8_t> out; + Vector<uint8_t> out; int len = p_packet_len - 1; out.resize(len); { - PoolVector<uint8_t>::Write w = out.write(); + uint8_t *w = out.ptrw(); memcpy(&w[0], &p_packet[1], len); } emit_signal("network_peer_packet", p_from, out); } int MultiplayerAPI::get_network_unique_id() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID."); return network_peer->get_unique_id(); } bool MultiplayerAPI::is_network_server() const { - // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server."); return network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { - ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'."); network_peer->set_refuse_new_connections(p_refuse); } bool MultiplayerAPI::is_refusing_new_network_connections() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'."); return network_peer->is_refusing_new_connections(); } Vector<int> MultiplayerAPI::get_network_connected_peers() const { - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected."); Vector<int> ret; @@ -847,104 +1193,13 @@ Vector<int> MultiplayerAPI::get_network_connected_peers() const { } void MultiplayerAPI::set_allow_object_decoding(bool p_enable) { - allow_object_decoding = p_enable; } bool MultiplayerAPI::is_object_decoding_allowed() const { - return allow_object_decoding; } -void MultiplayerAPI::profiling_start() { -#ifdef DEBUG_ENABLED - profiling = true; - profiler_frame_data.clear(); - - bandwidth_incoming_pointer = 0; - bandwidth_incoming_data.resize(16384); // ~128kB - for (int i = 0; i < bandwidth_incoming_data.size(); ++i) { - bandwidth_incoming_data.write[i].packet_size = -1; - } - - bandwidth_outgoing_pointer = 0; - bandwidth_outgoing_data.resize(16384); // ~128kB - for (int i = 0; i < bandwidth_outgoing_data.size(); ++i) { - bandwidth_outgoing_data.write[i].packet_size = -1; - } -#endif -} - -void MultiplayerAPI::profiling_end() { -#ifdef DEBUG_ENABLED - profiling = false; - bandwidth_incoming_data.clear(); - bandwidth_outgoing_data.clear(); -#endif -} - -int MultiplayerAPI::get_profiling_frame(ProfilingInfo *r_info) { - int i = 0; -#ifdef DEBUG_ENABLED - for (Map<ObjectID, ProfilingInfo>::Element *E = profiler_frame_data.front(); E; E = E->next()) { - r_info[i] = E->get(); - ++i; - } - profiler_frame_data.clear(); -#endif - return i; -} - -int MultiplayerAPI::get_incoming_bandwidth_usage() { -#ifdef DEBUG_ENABLED - return _get_bandwidth_usage(bandwidth_incoming_data, bandwidth_incoming_pointer); -#else - return 0; -#endif -} - -int MultiplayerAPI::get_outgoing_bandwidth_usage() { -#ifdef DEBUG_ENABLED - return _get_bandwidth_usage(bandwidth_outgoing_data, bandwidth_outgoing_pointer); -#else - return 0; -#endif -} - -#ifdef DEBUG_ENABLED -int MultiplayerAPI::_get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer) { - int total_bandwidth = 0; - - uint32_t timestamp = OS::get_singleton()->get_ticks_msec(); - uint32_t final_timestamp = timestamp - 1000; - - int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size(); - - while (i != p_pointer && p_buffer[i].packet_size > 0) { - if (p_buffer[i].timestamp < final_timestamp) { - return total_bandwidth; - } - total_bandwidth += p_buffer[i].packet_size; - i = (i + p_buffer.size() - 1) % p_buffer.size(); - } - - ERR_FAIL_COND_V_MSG(i == p_pointer, total_bandwidth, "Reached the end of the bandwidth profiler buffer, values might be inaccurate."); - return total_bandwidth; -} - -void MultiplayerAPI::_init_node_profile(ObjectID p_node) { - if (profiler_frame_data.has(p_node)) - return; - profiler_frame_data.insert(p_node, ProfilingInfo()); - profiler_frame_data[p_node].node = p_node; - profiler_frame_data[p_node].node_path = Object::cast_to<Node>(ObjectDB::get_instance(p_node))->get_path(); - profiler_frame_data[p_node].incoming_rpc = 0; - profiler_frame_data[p_node].incoming_rset = 0; - profiler_frame_data[p_node].outgoing_rpc = 0; - profiler_frame_data[p_node].outgoing_rset = 0; -} -#endif - void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); @@ -953,15 +1208,10 @@ void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server); ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id); - ClassDB::bind_method(D_METHOD("_add_peer", "id"), &MultiplayerAPI::_add_peer); - ClassDB::bind_method(D_METHOD("_del_peer", "id"), &MultiplayerAPI::_del_peer); ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer); ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll); ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear); - ClassDB::bind_method(D_METHOD("_connected_to_server"), &MultiplayerAPI::_connected_to_server); - ClassDB::bind_method(D_METHOD("_connection_failed"), &MultiplayerAPI::_connection_failed); - ClassDB::bind_method(D_METHOD("_server_disconnected"), &MultiplayerAPI::_server_disconnected); ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers); ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections); ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); @@ -975,7 +1225,7 @@ void MultiplayerAPI::_bind_methods() { ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::POOL_BYTE_ARRAY, "packet"))); + ADD_SIGNAL(MethodInfo("network_peer_packet", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "packet"))); ADD_SIGNAL(MethodInfo("connected_to_server")); ADD_SIGNAL(MethodInfo("connection_failed")); ADD_SIGNAL(MethodInfo("server_disconnected")); @@ -984,20 +1234,12 @@ void MultiplayerAPI::_bind_methods() { BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); BIND_ENUM_CONSTANT(RPC_MODE_MASTER); BIND_ENUM_CONSTANT(RPC_MODE_PUPPET); - BIND_ENUM_CONSTANT(RPC_MODE_SLAVE); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC); - BIND_ENUM_CONSTANT(RPC_MODE_SYNC); // Deprecated. BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC); BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); } -MultiplayerAPI::MultiplayerAPI() : - allow_object_decoding(false) { - rpc_sender_id = 0; - root_node = NULL; -#ifdef DEBUG_ENABLED - profiling = false; -#endif +MultiplayerAPI::MultiplayerAPI() { clear(); } diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index c9f127b6b2..06eab7796c 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -28,26 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef MULTIPLAYER_PROTOCOL_H -#define MULTIPLAYER_PROTOCOL_H +#ifndef MULTIPLAYER_API_H +#define MULTIPLAYER_API_H #include "core/io/networked_multiplayer_peer.h" #include "core/reference.h" class MultiplayerAPI : public Reference { - GDCLASS(MultiplayerAPI, Reference); -public: - struct ProfilingInfo { - ObjectID node; - String node_path; - int incoming_rpc; - int incoming_rset; - int outgoing_rpc; - int outgoing_rset; - }; - private: //path sent caches struct PathSentCache { @@ -65,32 +54,15 @@ private: Map<int, NodeInfo> nodes; }; -#ifdef DEBUG_ENABLED - struct BandwidthFrame { - uint32_t timestamp; - int packet_size; - }; - - int bandwidth_incoming_pointer; - Vector<BandwidthFrame> bandwidth_incoming_data; - int bandwidth_outgoing_pointer; - Vector<BandwidthFrame> bandwidth_outgoing_data; - Map<ObjectID, ProfilingInfo> profiler_frame_data; - bool profiling; - - void _init_node_profile(ObjectID p_node); - int _get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer); -#endif - Ref<NetworkedMultiplayerPeer> network_peer; - int rpc_sender_id; + int rpc_sender_id = 0; Set<int> connected_peers; HashMap<NodePath, PathSentCache> path_send_cache; Map<int, PathGetCache> path_get_cache; int last_send_cache_id; Vector<uint8_t> packet_cache; - Node *root_node; - bool allow_object_decoding; + Node *root_node = nullptr; + bool allow_object_decoding = false; protected: static void _bind_methods(); @@ -98,32 +70,44 @@ protected: void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len); - Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len); - void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); - void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); + void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); + void _process_rset(Node *p_node, const uint16_t p_rpc_property_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount); - bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target); + bool _send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target); + + Error _encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len); + Error _decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len); public: enum NetworkCommands { - NETWORK_COMMAND_REMOTE_CALL, + NETWORK_COMMAND_REMOTE_CALL = 0, NETWORK_COMMAND_REMOTE_SET, NETWORK_COMMAND_SIMPLIFY_PATH, NETWORK_COMMAND_CONFIRM_PATH, NETWORK_COMMAND_RAW, }; + enum NetworkNodeIdCompression { + NETWORK_NODE_ID_COMPRESSION_8 = 0, + NETWORK_NODE_ID_COMPRESSION_16, + NETWORK_NODE_ID_COMPRESSION_32, + }; + + enum NetworkNameIdCompression { + NETWORK_NAME_ID_COMPRESSION_8 = 0, + NETWORK_NAME_ID_COMPRESSION_16, + }; + enum RPCMode { RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default) RPC_MODE_REMOTE, // Using rpc() on it will call method / set property in all remote peers RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets - RPC_MODE_SLAVE = RPC_MODE_PUPPET, // Deprecated, same as puppet RPC_MODE_REMOTESYNC, // Using rpc() on it will call method / set property in all remote peers and locally - RPC_MODE_SYNC = RPC_MODE_REMOTESYNC, // Deprecated. Same as RPC_MODE_REMOTESYNC RPC_MODE_MASTERSYNC, // Using rpc() on it will call method / set property in the master peer and locally RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method / set property in all puppets peers and locally }; @@ -133,7 +117,7 @@ public: void set_root_node(Node *p_node); void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer); Ref<NetworkedMultiplayerPeer> get_network_peer() const; - Error send_bytes(PoolVector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer::TransferMode p_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + Error send_bytes(Vector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer::TransferMode p_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); // Called by Node.rpc void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); @@ -157,17 +141,10 @@ public: void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; - void profiling_start(); - void profiling_end(); - - int get_profiling_frame(ProfilingInfo *r_info); - int get_incoming_bandwidth_usage(); - int get_outgoing_bandwidth_usage(); - MultiplayerAPI(); ~MultiplayerAPI(); }; VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode); -#endif // MULTIPLAYER_PROTOCOL_H +#endif // MULTIPLAYER_API_H diff --git a/core/io/net_socket.cpp b/core/io/net_socket.cpp index 23edbc7d64..130a2e245e 100644 --- a/core/io/net_socket.cpp +++ b/core/io/net_socket.cpp @@ -30,13 +30,13 @@ #include "net_socket.h" -NetSocket *(*NetSocket::_create)() = NULL; +NetSocket *(*NetSocket::_create)() = nullptr; NetSocket *NetSocket::create() { - - if (_create) + if (_create) { return _create(); + } ERR_PRINT("Unable to create network socket, platform not supported"); - return NULL; + return nullptr; } diff --git a/core/io/net_socket.h b/core/io/net_socket.h index 710df2dd78..746945eced 100644 --- a/core/io/net_socket.h +++ b/core/io/net_socket.h @@ -35,7 +35,6 @@ #include "core/reference.h" class NetSocket : public Reference { - protected: static NetSocket *(*_create)(); @@ -61,7 +60,7 @@ public: virtual Error connect_to_host(IP_Address p_addr, uint16_t p_port) = 0; virtual Error poll(PollType p_type, int timeout) const = 0; virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) = 0; - virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port) = 0; + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IP_Address &r_ip, uint16_t &r_port, bool p_peek = false) = 0; virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) = 0; virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP_Address p_ip, uint16_t p_port) = 0; virtual Ref<NetSocket> accept(IP_Address &r_ip, uint16_t &r_port) = 0; diff --git a/core/io/networked_multiplayer_peer.cpp b/core/io/networked_multiplayer_peer.cpp index b2f810d212..f521f2bb79 100644 --- a/core/io/networked_multiplayer_peer.cpp +++ b/core/io/networked_multiplayer_peer.cpp @@ -31,7 +31,6 @@ #include "networked_multiplayer_peer.h" void NetworkedMultiplayerPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &NetworkedMultiplayerPeer::set_transfer_mode); ClassDB::bind_method(D_METHOD("get_transfer_mode"), &NetworkedMultiplayerPeer::get_transfer_mode); ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &NetworkedMultiplayerPeer::set_target_peer); @@ -66,6 +65,3 @@ void NetworkedMultiplayerPeer::_bind_methods() { ADD_SIGNAL(MethodInfo("connection_succeeded")); ADD_SIGNAL(MethodInfo("connection_failed")); } - -NetworkedMultiplayerPeer::NetworkedMultiplayerPeer() { -} diff --git a/core/io/networked_multiplayer_peer.h b/core/io/networked_multiplayer_peer.h index bffd544589..dc76237f45 100644 --- a/core/io/networked_multiplayer_peer.h +++ b/core/io/networked_multiplayer_peer.h @@ -34,7 +34,6 @@ #include "core/io/packet_peer.h" class NetworkedMultiplayerPeer : public PacketPeer { - GDCLASS(NetworkedMultiplayerPeer, PacketPeer); protected: @@ -74,10 +73,10 @@ public: virtual ConnectionStatus get_connection_status() const = 0; - NetworkedMultiplayerPeer(); + NetworkedMultiplayerPeer() {} }; VARIANT_ENUM_CAST(NetworkedMultiplayerPeer::TransferMode) VARIANT_ENUM_CAST(NetworkedMultiplayerPeer::ConnectionStatus) -#endif // NetworkedMultiplayerPeer_H +#endif // NETWORKED_MULTIPLAYER_PEER_H diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 9e53d773ba..dacd548a3e 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -35,24 +35,7 @@ /* helpers / binders */ -PacketPeer::PacketPeer() : - last_get_error(OK), - allow_object_decoding(false), - encode_buffer_max_size(8 * 1024 * 1024) { -} - -void PacketPeer::set_allow_object_decoding(bool p_enable) { - - allow_object_decoding = p_enable; -} - -bool PacketPeer::is_object_decoding_allowed() const { - - return allow_object_decoding; -} - void PacketPeer::set_encode_buffer_max_size(int p_max_size) { - ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes"); ERR_FAIL_COND_MSG(p_max_size > 256 * 1024 * 1024, "Max encode buffer cannot exceed 256 MiB"); encode_buffer_max_size = next_power_of_2(p_max_size); @@ -60,59 +43,61 @@ void PacketPeer::set_encode_buffer_max_size(int p_max_size) { } int PacketPeer::get_encode_buffer_max_size() const { - return encode_buffer_max_size; } -Error PacketPeer::get_packet_buffer(PoolVector<uint8_t> &r_buffer) { - +Error PacketPeer::get_packet_buffer(Vector<uint8_t> &r_buffer) { const uint8_t *buffer; int buffer_size; Error err = get_packet(&buffer, buffer_size); - if (err) + if (err) { return err; + } r_buffer.resize(buffer_size); - if (buffer_size == 0) + if (buffer_size == 0) { return OK; + } - PoolVector<uint8_t>::Write w = r_buffer.write(); - for (int i = 0; i < buffer_size; i++) + uint8_t *w = r_buffer.ptrw(); + for (int i = 0; i < buffer_size; i++) { w[i] = buffer[i]; + } return OK; } -Error PacketPeer::put_packet_buffer(const PoolVector<uint8_t> &p_buffer) { - +Error PacketPeer::put_packet_buffer(const Vector<uint8_t> &p_buffer) { int len = p_buffer.size(); - if (len == 0) + if (len == 0) { return OK; + } - PoolVector<uint8_t>::Read r = p_buffer.read(); + const uint8_t *r = p_buffer.ptr(); return put_packet(&r[0], len); } Error PacketPeer::get_var(Variant &r_variant, bool p_allow_objects) { - const uint8_t *buffer; int buffer_size; Error err = get_packet(&buffer, buffer_size); - if (err) + if (err) { return err; + } - return decode_variant(r_variant, buffer, buffer_size, NULL, p_allow_objects || allow_object_decoding); + return decode_variant(r_variant, buffer, buffer_size, nullptr, p_allow_objects); } Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { - int len; - Error err = encode_variant(p_packet, NULL, len, p_full_objects || allow_object_decoding); // compute len first - if (err) + Error err = encode_variant(p_packet, nullptr, len, p_full_objects); // compute len first + if (err) { return err; + } - if (len == 0) + if (len == 0) { return OK; + } ERR_FAIL_COND_V_MSG(len > encode_buffer_max_size, ERR_OUT_OF_MEMORY, "Failed to encode variant, encode size is bigger then encode_buffer_max_size. Consider raising it via 'set_encode_buffer_max_size'."); @@ -121,11 +106,11 @@ Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) { encode_buffer.resize(next_power_of_2(len)); } - PoolVector<uint8_t>::Write w = encode_buffer.write(); - err = encode_variant(p_packet, w.ptr(), len, p_full_objects || allow_object_decoding); + uint8_t *w = encode_buffer.ptrw(); + err = encode_variant(p_packet, w, len, p_full_objects); ERR_FAIL_COND_V_MSG(err != OK, err, "Error when trying to encode Variant."); - return put_packet(w.ptr(), len); + return put_packet(w, len); } Variant PacketPeer::_bnd_get_var(bool p_allow_objects) { @@ -136,23 +121,21 @@ Variant PacketPeer::_bnd_get_var(bool p_allow_objects) { return var; } -Error PacketPeer::_put_packet(const PoolVector<uint8_t> &p_buffer) { +Error PacketPeer::_put_packet(const Vector<uint8_t> &p_buffer) { return put_packet_buffer(p_buffer); } -PoolVector<uint8_t> PacketPeer::_get_packet() { - PoolVector<uint8_t> raw; +Vector<uint8_t> PacketPeer::_get_packet() { + Vector<uint8_t> raw; last_get_error = get_packet_buffer(raw); return raw; } Error PacketPeer::_get_packet_error() const { - return last_get_error; } void PacketPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_var", "allow_objects"), &PacketPeer::_bnd_get_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("put_var", "var", "full_objects"), &PacketPeer::put_var, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_packet"), &PacketPeer::_get_packet); @@ -160,25 +143,20 @@ void PacketPeer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_packet_error"), &PacketPeer::_get_packet_error); ClassDB::bind_method(D_METHOD("get_available_packet_count"), &PacketPeer::get_available_packet_count); - ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &PacketPeer::set_allow_object_decoding); - ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &PacketPeer::is_object_decoding_allowed); ClassDB::bind_method(D_METHOD("get_encode_buffer_max_size"), &PacketPeer::get_encode_buffer_max_size); ClassDB::bind_method(D_METHOD("set_encode_buffer_max_size", "max_size"), &PacketPeer::set_encode_buffer_max_size); ADD_PROPERTY(PropertyInfo(Variant::INT, "encode_buffer_max_size"), "set_encode_buffer_max_size", "get_encode_buffer_max_size"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); -}; +} /***************/ void PacketPeerStream::_set_stream_peer(REF p_peer) { - ERR_FAIL_COND_MSG(p_peer.is_null(), "It's not a reference to a valid Resource object."); set_stream_peer(p_peer); } void PacketPeerStream::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_stream_peer", "peer"), &PacketPeerStream::set_stream_peer); ClassDB::bind_method(D_METHOD("get_stream_peer"), &PacketPeerStream::get_stream_peer); ClassDB::bind_method(D_METHOD("set_input_buffer_max_size", "max_size_bytes"), &PacketPeerStream::set_input_buffer_max_size); @@ -192,16 +170,17 @@ void PacketPeerStream::_bind_methods() { } Error PacketPeerStream::_poll_buffer() const { - ERR_FAIL_COND_V(peer.is_null(), ERR_UNCONFIGURED); int read = 0; ERR_FAIL_COND_V(input_buffer.size() < ring_buffer.space_left(), ERR_UNAVAILABLE); Error err = peer->get_partial_data(input_buffer.ptrw(), ring_buffer.space_left(), read); - if (err) + if (err) { return err; - if (read == 0) + } + if (read == 0) { return OK; + } int w = ring_buffer.write(&input_buffer[0], read); ERR_FAIL_COND_V(w != read, ERR_BUG); @@ -210,7 +189,6 @@ Error PacketPeerStream::_poll_buffer() const { } int PacketPeerStream::get_available_packet_count() const { - _poll_buffer(); uint32_t remaining = ring_buffer.data_left(); @@ -219,14 +197,14 @@ int PacketPeerStream::get_available_packet_count() const { int count = 0; while (remaining >= 4) { - uint8_t lbuf[4]; ring_buffer.copy(lbuf, ofs, 4); uint32_t len = decode_uint32(lbuf); remaining -= 4; ofs += 4; - if (len > remaining) + if (len > remaining) { break; + } remaining -= len; ofs += len; count++; @@ -236,7 +214,6 @@ int PacketPeerStream::get_available_packet_count() const { } Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - ERR_FAIL_COND_V(peer.is_null(), ERR_UNCONFIGURED); _poll_buffer(); @@ -258,50 +235,48 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size) } Error PacketPeerStream::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(peer.is_null(), ERR_UNCONFIGURED); Error err = _poll_buffer(); //won't hurt to poll here too - if (err) + if (err) { return err; + } - if (p_buffer_size == 0) + if (p_buffer_size == 0) { return OK; + } ERR_FAIL_COND_V(p_buffer_size < 0, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_buffer_size + 4 > output_buffer.size(), ERR_INVALID_PARAMETER); encode_uint32(p_buffer_size, output_buffer.ptrw()); uint8_t *dst = &output_buffer.write[4]; - for (int i = 0; i < p_buffer_size; i++) + for (int i = 0; i < p_buffer_size; i++) { dst[i] = p_buffer[i]; + } return peer->put_data(&output_buffer[0], p_buffer_size + 4); } int PacketPeerStream::get_max_packet_size() const { - return output_buffer.size(); } void PacketPeerStream::set_stream_peer(const Ref<StreamPeer> &p_peer) { - //ERR_FAIL_COND(p_peer.is_null()); if (p_peer.ptr() != peer.ptr()) { ring_buffer.advance_read(ring_buffer.data_left()); // reset the ring buffer - }; + } peer = p_peer; } Ref<StreamPeer> PacketPeerStream::get_stream_peer() const { - return peer; } void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { - ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0."); //warning may lose packets ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data."); @@ -310,22 +285,18 @@ void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { } int PacketPeerStream::get_input_buffer_max_size() const { - return input_buffer.size() - 4; } void PacketPeerStream::set_output_buffer_max_size(int p_max_size) { - output_buffer.resize(next_power_of_2(p_max_size + 4)); } int PacketPeerStream::get_output_buffer_max_size() const { - return output_buffer.size() - 4; } PacketPeerStream::PacketPeerStream() { - int rbsize = GLOBAL_GET("network/limits/packet_peer_stream/max_buffer_po2"); ring_buffer.resize(rbsize); diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h index 2b13f2e952..92cdbc4151 100644 --- a/core/io/packet_peer.h +++ b/core/io/packet_peer.h @@ -36,23 +36,20 @@ #include "core/ring_buffer.h" class PacketPeer : public Reference { - GDCLASS(PacketPeer, Reference); Variant _bnd_get_var(bool p_allow_objects = false); static void _bind_methods(); - Error _put_packet(const PoolVector<uint8_t> &p_buffer); - PoolVector<uint8_t> _get_packet(); + Error _put_packet(const Vector<uint8_t> &p_buffer); + Vector<uint8_t> _get_packet(); Error _get_packet_error() const; - mutable Error last_get_error; - - bool allow_object_decoding; + mutable Error last_get_error = OK; - int encode_buffer_max_size; - PoolVector<uint8_t> encode_buffer; + int encode_buffer_max_size = 8 * 1024 * 1024; + Vector<uint8_t> encode_buffer; public: virtual int get_available_packet_count() const = 0; @@ -63,24 +60,20 @@ public: /* helpers / binders */ - virtual Error get_packet_buffer(PoolVector<uint8_t> &r_buffer); - virtual Error put_packet_buffer(const PoolVector<uint8_t> &p_buffer); + virtual Error get_packet_buffer(Vector<uint8_t> &r_buffer); + virtual Error put_packet_buffer(const Vector<uint8_t> &p_buffer); virtual Error get_var(Variant &r_variant, bool p_allow_objects = false); virtual Error put_var(const Variant &p_packet, bool p_full_objects = false); - void set_allow_object_decoding(bool p_enable); - bool is_object_decoding_allowed() const; - void set_encode_buffer_max_size(int p_max_size); int get_encode_buffer_max_size() const; - PacketPeer(); + PacketPeer() {} ~PacketPeer() {} }; class PacketPeerStream : public PacketPeer { - GDCLASS(PacketPeerStream, PacketPeer); //the way the buffers work sucks, will change later @@ -97,11 +90,11 @@ protected: static void _bind_methods(); public: - virtual int get_available_packet_count() const; - virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); - virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size); + virtual int get_available_packet_count() const override; + virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; + virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; - virtual int get_max_packet_size() const; + virtual int get_max_packet_size() const override; void set_stream_peer(const Ref<StreamPeer> &p_peer); Ref<StreamPeer> get_stream_peer() const; diff --git a/core/io/packet_peer_dtls.cpp b/core/io/packet_peer_dtls.cpp new file mode 100644 index 0000000000..632f86a9f6 --- /dev/null +++ b/core/io/packet_peer_dtls.cpp @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* packet_peer_dtls.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 "packet_peer_dtls.h" +#include "core/os/file_access.h" +#include "core/project_settings.h" + +PacketPeerDTLS *(*PacketPeerDTLS::_create)() = nullptr; +bool PacketPeerDTLS::available = false; + +PacketPeerDTLS *PacketPeerDTLS::create() { + if (_create) { + return _create(); + } + return nullptr; +} + +bool PacketPeerDTLS::is_available() { + return available; +} + +void PacketPeerDTLS::_bind_methods() { + ClassDB::bind_method(D_METHOD("poll"), &PacketPeerDTLS::poll); + ClassDB::bind_method(D_METHOD("connect_to_peer", "packet_peer", "validate_certs", "for_hostname", "valid_certificate"), &PacketPeerDTLS::connect_to_peer, DEFVAL(true), DEFVAL(String()), DEFVAL(Ref<X509Certificate>())); + ClassDB::bind_method(D_METHOD("get_status"), &PacketPeerDTLS::get_status); + ClassDB::bind_method(D_METHOD("disconnect_from_peer"), &PacketPeerDTLS::disconnect_from_peer); + + BIND_ENUM_CONSTANT(STATUS_DISCONNECTED); + BIND_ENUM_CONSTANT(STATUS_HANDSHAKING); + BIND_ENUM_CONSTANT(STATUS_CONNECTED); + BIND_ENUM_CONSTANT(STATUS_ERROR); + BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); +} diff --git a/core/io/packet_peer_dtls.h b/core/io/packet_peer_dtls.h new file mode 100644 index 0000000000..c2ff4e1a7f --- /dev/null +++ b/core/io/packet_peer_dtls.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* packet_peer_dtls.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 PACKET_PEER_DTLS_H +#define PACKET_PEER_DTLS_H + +#include "core/crypto/crypto.h" +#include "core/io/packet_peer_udp.h" + +class PacketPeerDTLS : public PacketPeer { + GDCLASS(PacketPeerDTLS, PacketPeer); + +protected: + static PacketPeerDTLS *(*_create)(); + static void _bind_methods(); + + static bool available; + +public: + enum Status { + STATUS_DISCONNECTED, + STATUS_HANDSHAKING, + STATUS_CONNECTED, + STATUS_ERROR, + STATUS_ERROR_HOSTNAME_MISMATCH + }; + + virtual void poll() = 0; + virtual Error connect_to_peer(Ref<PacketPeerUDP> p_base, bool p_validate_certs = true, const String &p_for_hostname = String(), Ref<X509Certificate> p_ca_certs = Ref<X509Certificate>()) = 0; + virtual void disconnect_from_peer() = 0; + virtual Status get_status() const = 0; + + static PacketPeerDTLS *create(); + static bool is_available(); + + PacketPeerDTLS() {} +}; + +VARIANT_ENUM_CAST(PacketPeerDTLS::Status); + +#endif // PACKET_PEER_DTLS_H diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index 5c98c4fcab..e633a56d54 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -31,20 +31,22 @@ #include "packet_peer_udp.h" #include "core/io/ip.h" +#include "core/io/udp_server.h" void PacketPeerUDP::set_blocking_mode(bool p_enable) { - blocking = p_enable; } void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) { + ERR_FAIL_COND(udp_server); broadcast = p_enabled; - if (_sock.is_valid() && _sock->is_open()) + if (_sock.is_valid() && _sock->is_open()) { _sock->set_broadcasting_enabled(p_enabled); + } } Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_if_name) { - + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER); @@ -59,26 +61,25 @@ Error PacketPeerUDP::join_multicast_group(IP_Address p_multi_address, String p_i } Error PacketPeerUDP::leave_multicast_group(IP_Address p_multi_address, String p_if_name) { - + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED); return _sock->leave_multicast_group(p_multi_address, p_if_name); } String PacketPeerUDP::_get_packet_ip() const { - return get_packet_address(); } Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { - IP_Address ip; if (p_address.is_valid_ip_address()) { ip = p_address; } else { ip = IP::get_singleton()->resolve_hostname(p_address); - if (!ip.is_valid()) + if (!ip.is_valid()) { return ERR_CANT_RESOLVE; + } } set_dest_address(ip, p_port); @@ -86,22 +87,23 @@ Error PacketPeerUDP::_set_dest_address(const String &p_address, int p_port) { } int PacketPeerUDP::get_available_packet_count() const { - // TODO we should deprecate this, and expose poll instead! Error err = const_cast<PacketPeerUDP *>(this)->_poll(); - if (err != OK) + if (err != OK) { return -1; + } return queue_count; } Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { - Error err = _poll(); - if (err != OK) + if (err != OK) { return err; - if (queue_count == 0) + } + if (queue_count == 0) { return ERR_UNAVAILABLE; + } uint32_t size = 0; uint8_t ipv6[16]; @@ -117,7 +119,6 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { } Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!peer_addr.is_valid(), ERR_UNCONFIGURED); @@ -133,12 +134,17 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { } do { - err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); + if (connected && !udp_server) { + err = _sock->send(p_buffer, p_buffer_size, sent); + } else { + err = _sock->sendto(p_buffer, p_buffer_size, sent, peer_addr, peer_port); + } if (err != OK) { - if (err != ERR_BUSY) + if (err != ERR_BUSY) { return FAILED; - else if (!blocking) + } else if (!blocking) { return ERR_BUSY; + } // Keep trying to send full packet continue; } @@ -150,12 +156,10 @@ Error PacketPeerUDP::put_packet(const uint8_t *p_buffer, int p_buffer_size) { } int PacketPeerUDP::get_max_packet_size() const { - return 512; // uhm maybe not } Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_recv_buffer_size) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); @@ -163,13 +167,15 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_ Error err; IP::Type ip_type = IP::TYPE_ANY; - if (p_bind_address.is_valid()) + if (p_bind_address.is_valid()) { ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + } err = _sock->open(NetSocket::TYPE_UDP, ip_type); - if (err != OK) + if (err != OK) { return ERR_CANT_CREATE; + } _sock->set_blocking_enabled(false); _sock->set_reuse_address_enabled(true); @@ -184,27 +190,88 @@ Error PacketPeerUDP::listen(int p_port, const IP_Address &p_bind_address, int p_ return OK; } -void PacketPeerUDP::close() { +Error PacketPeerUDP::connect_shared_socket(Ref<NetSocket> p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *p_server) { + udp_server = p_server; + connected = true; + _sock = p_sock; + peer_addr = p_ip; + peer_port = p_port; + packet_ip = peer_addr; + packet_port = peer_port; + return OK; +} + +void PacketPeerUDP::disconnect_shared_socket() { + udp_server = nullptr; + _sock = Ref<NetSocket>(NetSocket::create()); + close(); +} + +Error PacketPeerUDP::connect_to_host(const IP_Address &p_host, int p_port) { + ERR_FAIL_COND_V(udp_server, ERR_LOCKED); + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); + + Error err; - if (_sock.is_valid()) + if (!_sock->is_open()) { + IP::Type ip_type = p_host.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + err = _sock->open(NetSocket::TYPE_UDP, ip_type); + ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN); + _sock->set_blocking_enabled(false); + } + + err = _sock->connect_to_host(p_host, p_port); + + // I see no reason why we should get ERR_BUSY (wouldblock/eagain) here. + // This is UDP, so connect is only used to tell the OS to which socket + // it shuold deliver packets when multiple are bound on the same address/port. + if (err != OK) { + close(); + ERR_FAIL_V_MSG(FAILED, "Unable to connect"); + } + + connected = true; + + peer_addr = p_host; + peer_port = p_port; + + // Flush any packet we might still have in queue. + rb.clear(); + return OK; +} + +bool PacketPeerUDP::is_connected_to_host() const { + return connected; +} + +void PacketPeerUDP::close() { + if (udp_server) { + udp_server->remove_peer(peer_addr, peer_port); + udp_server = nullptr; + _sock = Ref<NetSocket>(NetSocket::create()); + } else if (_sock.is_valid()) { _sock->close(); + } rb.resize(16); queue_count = 0; + connected = false; } Error PacketPeerUDP::wait() { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); return _sock->poll(NetSocket::POLL_TYPE_IN, -1); } Error PacketPeerUDP::_poll() { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); if (!_sock->is_open()) { return FAILED; } + if (udp_server) { + return OK; // Handled by UDPServer. + } Error err; int read; @@ -212,58 +279,69 @@ Error PacketPeerUDP::_poll() { uint16_t port; while (true) { - err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + if (connected) { + err = _sock->recv(recv_buffer, sizeof(recv_buffer), read); + ip = peer_addr; + port = peer_port; + } else { + err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + } if (err != OK) { - if (err == ERR_BUSY) + if (err == ERR_BUSY) { break; + } return FAILED; } - if (rb.space_left() < read + 24) { + err = store_packet(ip, port, recv_buffer, read); #ifdef TOOLS_ENABLED - WARN_PRINTS("Buffer full, dropping packets!"); -#endif - continue; + if (err != OK) { + WARN_PRINT("Buffer full, dropping packets!"); } - - uint32_t port32 = port; - rb.write(ip.get_ipv6(), 16); - rb.write((uint8_t *)&port32, 4); - rb.write((uint8_t *)&read, 4); - rb.write(recv_buffer, read); - ++queue_count; +#endif } return OK; } -bool PacketPeerUDP::is_listening() const { +Error PacketPeerUDP::store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size) { + if (rb.space_left() < p_buf_size + 24) { + return ERR_OUT_OF_MEMORY; + } + rb.write(p_ip.get_ipv6(), 16); + rb.write((uint8_t *)&p_port, 4); + rb.write((uint8_t *)&p_buf_size, 4); + rb.write(p_buf, p_buf_size); + ++queue_count; + return OK; +} + +bool PacketPeerUDP::is_listening() const { return _sock.is_valid() && _sock->is_open(); } IP_Address PacketPeerUDP::get_packet_address() const { - return packet_ip; } int PacketPeerUDP::get_packet_port() const { - return packet_port; } void PacketPeerUDP::set_dest_address(const IP_Address &p_address, int p_port) { - + ERR_FAIL_COND_MSG(connected, "Destination address cannot be set for connected sockets"); peer_addr = p_address; peer_port = p_port; } void PacketPeerUDP::_bind_methods() { - ClassDB::bind_method(D_METHOD("listen", "port", "bind_address", "recv_buf_size"), &PacketPeerUDP::listen, DEFVAL("*"), DEFVAL(65536)); ClassDB::bind_method(D_METHOD("close"), &PacketPeerUDP::close); ClassDB::bind_method(D_METHOD("wait"), &PacketPeerUDP::wait); ClassDB::bind_method(D_METHOD("is_listening"), &PacketPeerUDP::is_listening); + ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port"), &PacketPeerUDP::connect_to_host); + ClassDB::bind_method(D_METHOD("is_connected_to_host"), &PacketPeerUDP::is_connected_to_host); ClassDB::bind_method(D_METHOD("get_packet_ip"), &PacketPeerUDP::_get_packet_ip); ClassDB::bind_method(D_METHOD("get_packet_port"), &PacketPeerUDP::get_packet_port); ClassDB::bind_method(D_METHOD("set_dest_address", "host", "port"), &PacketPeerUDP::_set_dest_address); @@ -273,16 +351,10 @@ void PacketPeerUDP::_bind_methods() { } PacketPeerUDP::PacketPeerUDP() : - packet_port(0), - queue_count(0), - peer_port(0), - blocking(true), - broadcast(false), _sock(Ref<NetSocket>(NetSocket::create())) { rb.resize(16); } PacketPeerUDP::~PacketPeerUDP() { - close(); } diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index 15b4d00c37..9a44a1ebea 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -35,6 +35,8 @@ #include "core/io/net_socket.h" #include "core/io/packet_peer.h" +class UDPServer; + class PacketPeerUDP : public PacketPeer { GDCLASS(PacketPeerUDP, PacketPeer); @@ -47,13 +49,15 @@ protected: uint8_t recv_buffer[PACKET_BUFFER_SIZE]; uint8_t packet_buffer[PACKET_BUFFER_SIZE]; IP_Address packet_ip; - int packet_port; - int queue_count; + int packet_port = 0; + int queue_count = 0; IP_Address peer_addr; - int peer_port; - bool blocking; - bool broadcast; + int peer_port = 0; + bool connected = false; + bool blocking = true; + bool broadcast = false; + UDPServer *udp_server = nullptr; Ref<NetSocket> _sock; static void _bind_methods(); @@ -70,14 +74,21 @@ public: void close(); Error wait(); bool is_listening() const; + + Error connect_shared_socket(Ref<NetSocket> p_sock, IP_Address p_ip, uint16_t p_port, UDPServer *ref); // Used by UDPServer + void disconnect_shared_socket(); // Used by UDPServer + Error store_packet(IP_Address p_ip, uint32_t p_port, uint8_t *p_buf, int p_buf_size); // Used internally and by UDPServer + Error connect_to_host(const IP_Address &p_host, int p_port); + bool is_connected_to_host() const; + IP_Address get_packet_address() const; int get_packet_port() const; void set_dest_address(const IP_Address &p_address, int p_port); - Error put_packet(const uint8_t *p_buffer, int p_buffer_size); - Error get_packet(const uint8_t **r_buffer, int &r_buffer_size); - int get_available_packet_count() const; - int get_max_packet_size() const; + Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override; + Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; + int get_available_packet_count() const override; + int get_max_packet_size() const override; void set_broadcast_enabled(bool p_enabled); Error join_multicast_group(IP_Address p_multi_address, String p_if_name); Error leave_multicast_group(IP_Address p_multi_address, String p_if_name); diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index fb83f0ac90..5480d3c535 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -30,40 +30,59 @@ #include "pck_packer.h" +#include "core/crypto/crypto_core.h" +#include "core/io/file_access_encrypted.h" #include "core/io/file_access_pack.h" // PACK_HEADER_MAGIC, PACK_FORMAT_VERSION #include "core/os/file_access.h" #include "core/version.h" -static uint64_t _align(uint64_t p_n, int p_alignment) { - - if (p_alignment == 0) - return p_n; - - uint64_t rest = p_n % p_alignment; - if (rest == 0) - return p_n; - else - return p_n + (p_alignment - rest); -}; - -static void _pad(FileAccess *p_file, int p_bytes) { - - for (int i = 0; i < p_bytes; i++) { +static int _get_pad(int p_alignment, int p_n) { + int rest = p_n % p_alignment; + int pad = 0; + if (rest > 0) { + pad = p_alignment - rest; + } - p_file->store_8(0); - }; -}; + return pad; +} void PCKPacker::_bind_methods() { - - ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment"), &PCKPacker::pck_start, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path"), &PCKPacker::add_file); + ClassDB::bind_method(D_METHOD("pck_start", "pck_name", "alignment", "key", "encrypt_directory"), &PCKPacker::pck_start, DEFVAL(0), DEFVAL(String()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("add_file", "pck_path", "source_path", "encrypt"), &PCKPacker::add_file, DEFVAL(false)); ClassDB::bind_method(D_METHOD("flush", "verbose"), &PCKPacker::flush, DEFVAL(false)); -}; - -Error PCKPacker::pck_start(const String &p_file, int p_alignment) { +} + +Error PCKPacker::pck_start(const String &p_file, int p_alignment, const String &p_key, bool p_encrypt_directory) { + ERR_FAIL_COND_V_MSG((p_key.empty() || !p_key.is_valid_hex_number(false) || p_key.length() != 64), ERR_CANT_CREATE, "Invalid Encryption Key (must be 64 characters long)."); + + String _key = p_key.to_lower(); + key.resize(32); + for (int i = 0; i < 32; i++) { + int v = 0; + if (i * 2 < _key.length()) { + char32_t ct = _key[i * 2]; + if (ct >= '0' && ct <= '9') { + ct = ct - '0'; + } else if (ct >= 'a' && ct <= 'f') { + ct = 10 + ct - 'a'; + } + v |= ct << 4; + } + + if (i * 2 + 1 < _key.length()) { + char32_t ct = _key[i * 2 + 1]; + if (ct >= '0' && ct <= '9') { + ct = ct - '0'; + } else if (ct >= 'a' && ct <= 'f') { + ct = 10 + ct - 'a'; + } + v |= ct; + } + key.write[i] = v; + } + enc_dir = p_encrypt_directory; - if (file != NULL) { + if (file != nullptr) { memdelete(file); } @@ -79,28 +98,53 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment) { file->store_32(VERSION_MINOR); file->store_32(VERSION_PATCH); - for (int i = 0; i < 16; i++) { - - file->store_32(0); // reserved - }; + uint32_t pack_flags = 0; + if (enc_dir) { + pack_flags |= PACK_DIR_ENCRYPTED; + } + file->store_32(pack_flags); // flags files.clear(); + ofs = 0; return OK; -}; - -Error PCKPacker::add_file(const String &p_file, const String &p_src) { +} +Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encrypt) { FileAccess *f = FileAccess::open(p_src, FileAccess::READ); if (!f) { return ERR_FILE_CANT_OPEN; - }; + } File pf; pf.path = p_file; pf.src_path = p_src; + pf.ofs = ofs; pf.size = f->get_len(); - pf.offset_offset = 0; + + Vector<uint8_t> data = FileAccess::get_file_as_array(p_src); + { + unsigned char hash[16]; + CryptoCore::md5(data.ptr(), data.size(), hash); + pf.md5.resize(16); + for (int i = 0; i < 16; i++) { + pf.md5.write[i] = hash[i]; + } + } + pf.encrypted = p_encrypt; + + uint64_t _size = pf.size; + if (p_encrypt) { // Add encryption overhead. + if (_size % 16) { // Pad to encryption block size. + _size += 16 - (_size % 16); + } + _size += 16; // hash + _size += 8; // data size + _size += 16; // iv + } + + int pad = _get_pad(alignment, ofs + _size); + ofs = ofs + _size + pad; files.push_back(pf); @@ -108,86 +152,130 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src) { memdelete(f); return OK; -}; +} Error PCKPacker::flush(bool p_verbose) { - ERR_FAIL_COND_V_MSG(!file, ERR_INVALID_PARAMETER, "File must be opened before use."); - // write the index + int64_t file_base_ofs = file->get_position(); + file->store_64(0); // files base + + for (int i = 0; i < 16; i++) { + file->store_32(0); // reserved + } + // write the index file->store_32(files.size()); - for (int i = 0; i < files.size(); i++) { + FileAccessEncrypted *fae = nullptr; + FileAccess *fhead = file; + + if (enc_dir) { + fae = memnew(FileAccessEncrypted); + ERR_FAIL_COND_V(!fae, ERR_CANT_CREATE); - file->store_pascal_string(files[i].path); - files.write[i].offset_offset = file->get_position(); - file->store_64(0); // offset - file->store_64(files[i].size); // size + Error err = fae->open_and_parse(file, key, FileAccessEncrypted::MODE_WRITE_AES256, false); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - // # empty md5 - file->store_32(0); - file->store_32(0); - file->store_32(0); - file->store_32(0); - }; + fhead = fae; + } + + for (int i = 0; i < files.size(); i++) { + int string_len = files[i].path.utf8().length(); + int pad = _get_pad(4, string_len); + + fhead->store_32(string_len + pad); + fhead->store_buffer((const uint8_t *)files[i].path.utf8().get_data(), string_len); + for (int j = 0; j < pad; j++) { + fhead->store_8(0); + } + + fhead->store_64(files[i].ofs); + fhead->store_64(files[i].size); // pay attention here, this is where file is + fhead->store_buffer(files[i].md5.ptr(), 16); //also save md5 for file + + uint32_t flags = 0; + if (files[i].encrypted) { + flags |= PACK_FILE_ENCRYPTED; + } + fhead->store_32(flags); + } + + if (fae) { + fae->release(); + memdelete(fae); + } - uint64_t ofs = file->get_position(); - ofs = _align(ofs, alignment); + int header_padding = _get_pad(alignment, file->get_position()); + for (int i = 0; i < header_padding; i++) { + file->store_8(Math::rand() % 256); + } - _pad(file, ofs - file->get_position()); + int64_t file_base = file->get_position(); + file->seek(file_base_ofs); + file->store_64(file_base); // update files base + file->seek(file_base); const uint32_t buf_max = 65536; uint8_t *buf = memnew_arr(uint8_t, buf_max); int count = 0; for (int i = 0; i < files.size(); i++) { - FileAccess *src = FileAccess::open(files[i].src_path, FileAccess::READ); uint64_t to_write = files[i].size; - while (to_write > 0) { + fae = nullptr; + FileAccess *ftmp = file; + if (files[i].encrypted) { + fae = memnew(FileAccessEncrypted); + ERR_FAIL_COND_V(!fae, ERR_CANT_CREATE); + + Error err = fae->open_and_parse(file, key, FileAccessEncrypted::MODE_WRITE_AES256, false); + ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); + ftmp = fae; + } + + while (to_write > 0) { int read = src->get_buffer(buf, MIN(to_write, buf_max)); - file->store_buffer(buf, read); + ftmp->store_buffer(buf, read); to_write -= read; - }; + } - uint64_t pos = file->get_position(); - file->seek(files[i].offset_offset); // go back to store the file's offset - file->store_64(ofs); - file->seek(pos); + if (fae) { + fae->release(); + memdelete(fae); + } - ofs = _align(ofs + files[i].size, alignment); - _pad(file, ofs - pos); + int pad = _get_pad(alignment, file->get_position()); + for (int j = 0; j < pad; j++) { + file->store_8(Math::rand() % 256); + } src->close(); memdelete(src); count += 1; - if (p_verbose) { + const int file_num = files.size(); + if (p_verbose && (file_num > 0)) { if (count % 100 == 0) { - printf("%i/%i (%.2f)\r", count, files.size(), float(count) / files.size() * 100); + printf("%i/%i (%.2f)\r", count, file_num, float(count) / file_num * 100); fflush(stdout); - }; - }; - }; + } + } + } - if (p_verbose) + if (p_verbose) { printf("\n"); + } file->close(); memdelete_arr(buf); return OK; -}; - -PCKPacker::PCKPacker() { - - file = NULL; -}; +} PCKPacker::~PCKPacker() { - if (file != NULL) { + if (file != nullptr) { memdelete(file); - }; - file = NULL; -}; + } + file = nullptr; +} diff --git a/core/io/pck_packer.h b/core/io/pck_packer.h index 6058de8345..a6054dff2c 100644 --- a/core/io/pck_packer.h +++ b/core/io/pck_packer.h @@ -36,29 +36,33 @@ class FileAccess; class PCKPacker : public Reference { - GDCLASS(PCKPacker, Reference); - FileAccess *file; + FileAccess *file = nullptr; int alignment; + uint64_t ofs = 0; + + Vector<uint8_t> key; + bool enc_dir = false; static void _bind_methods(); struct File { - String path; String src_path; - int size; - uint64_t offset_offset; + uint64_t ofs; + uint64_t size; + bool encrypted; + Vector<uint8_t> md5; }; Vector<File> files; public: - Error pck_start(const String &p_file, int p_alignment = 0); - Error add_file(const String &p_file, const String &p_src); + Error pck_start(const String &p_file, int p_alignment = 0, const String &p_key = String(), bool p_encrypt_directory = false); + Error add_file(const String &p_file, const String &p_src, bool p_encrypt = false); Error flush(bool p_verbose = false); - PCKPacker(); + PCKPacker() {} ~PCKPacker(); }; diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 272d5c1116..5097f6d98b 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -46,7 +46,7 @@ enum { VARIANT_NIL = 1, VARIANT_BOOL = 2, VARIANT_INT = 3, - VARIANT_REAL = 4, + VARIANT_FLOAT = 4, VARIANT_STRING = 5, VARIANT_VECTOR2 = 10, VARIANT_RECT2 = 11, @@ -65,21 +65,22 @@ enum { VARIANT_DICTIONARY = 26, VARIANT_ARRAY = 30, VARIANT_RAW_ARRAY = 31, - VARIANT_INT_ARRAY = 32, - VARIANT_REAL_ARRAY = 33, + VARIANT_INT32_ARRAY = 32, + VARIANT_FLOAT32_ARRAY = 33, VARIANT_STRING_ARRAY = 34, VARIANT_VECTOR3_ARRAY = 35, VARIANT_COLOR_ARRAY = 36, VARIANT_VECTOR2_ARRAY = 37, VARIANT_INT64 = 40, VARIANT_DOUBLE = 41, -#ifndef DISABLE_DEPRECATED - VARIANT_IMAGE = 21, // - no longer variant type - IMAGE_ENCODING_EMPTY = 0, - IMAGE_ENCODING_RAW = 1, - IMAGE_ENCODING_LOSSLESS = 2, - IMAGE_ENCODING_LOSSY = 3, -#endif + VARIANT_CALLABLE = 42, + VARIANT_SIGNAL = 43, + VARIANT_STRING_NAME = 44, + VARIANT_VECTOR2I = 45, + VARIANT_RECT2I = 46, + VARIANT_VECTOR3I = 47, + VARIANT_INT64_ARRAY = 48, + VARIANT_FLOAT64_ARRAY = 49, OBJECT_EMPTY = 0, OBJECT_EXTERNAL_RESOURCE = 1, OBJECT_INTERNAL_RESOURCE = 2, @@ -92,25 +93,25 @@ enum { }; -void ResourceInteractiveLoaderBinary::_advance_padding(uint32_t p_len) { - +void ResourceLoaderBinary::_advance_padding(uint32_t p_len) { uint32_t extra = 4 - (p_len % 4); if (extra < 4) { - for (uint32_t i = 0; i < extra; i++) + for (uint32_t i = 0; i < extra; i++) { f->get_8(); //pad to 32 + } } } -StringName ResourceInteractiveLoaderBinary::_get_string() { - +StringName ResourceLoaderBinary::_get_string() { uint32_t id = f->get_32(); if (id & 0x80000000) { uint32_t len = id & 0x7FFFFFFF; if ((int)len > str_buf.size()) { str_buf.resize(len); } - if (len == 0) + if (len == 0) { return StringName(); + } f->get_buffer((uint8_t *)&str_buf[0], len); String s; s.parse_utf8(&str_buf[0]); @@ -120,51 +121,47 @@ StringName ResourceInteractiveLoaderBinary::_get_string() { return string_map[id]; } -Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { - +Error ResourceLoaderBinary::parse_variant(Variant &r_v) { uint32_t type = f->get_32(); print_bl("find property of type: " + itos(type)); switch (type) { - case VARIANT_NIL: { - r_v = Variant(); } break; case VARIANT_BOOL: { - r_v = bool(f->get_32()); } break; case VARIANT_INT: { - r_v = int(f->get_32()); } break; case VARIANT_INT64: { - r_v = int64_t(f->get_64()); } break; - case VARIANT_REAL: { - + case VARIANT_FLOAT: { r_v = f->get_real(); } break; case VARIANT_DOUBLE: { - r_v = f->get_double(); } break; case VARIANT_STRING: { - r_v = get_unicode_string(); } break; case VARIANT_VECTOR2: { - Vector2 v; v.x = f->get_real(); v.y = f->get_real(); r_v = v; } break; - case VARIANT_RECT2: { + case VARIANT_VECTOR2I: { + Vector2i v; + v.x = f->get_32(); + v.y = f->get_32(); + r_v = v; + } break; + case VARIANT_RECT2: { Rect2 v; v.position.x = f->get_real(); v.position.y = f->get_real(); @@ -173,16 +170,30 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; - case VARIANT_VECTOR3: { + case VARIANT_RECT2I: { + Rect2i v; + v.position.x = f->get_32(); + v.position.y = f->get_32(); + v.size.x = f->get_32(); + v.size.y = f->get_32(); + r_v = v; + } break; + case VARIANT_VECTOR3: { Vector3 v; v.x = f->get_real(); v.y = f->get_real(); v.z = f->get_real(); r_v = v; } break; + case VARIANT_VECTOR3I: { + Vector3i v; + v.x = f->get_32(); + v.y = f->get_32(); + v.z = f->get_32(); + r_v = v; + } break; case VARIANT_PLANE: { - Plane v; v.normal.x = f->get_real(); v.normal.y = f->get_real(); @@ -200,7 +211,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_AABB: { - AABB v; v.position.x = f->get_real(); v.position.y = f->get_real(); @@ -212,7 +222,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_MATRIX32: { - Transform2D v; v.elements[0].x = f->get_real(); v.elements[0].y = f->get_real(); @@ -224,7 +233,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_MATRIX3: { - Basis v; v.elements[0].x = f->get_real(); v.elements[0].y = f->get_real(); @@ -239,7 +247,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_TRANSFORM: { - Transform v; v.basis.elements[0].x = f->get_real(); v.basis.elements[0].y = f->get_real(); @@ -256,7 +263,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; case VARIANT_COLOR: { - Color v; v.r = f->get_real(); v.g = f->get_real(); @@ -265,9 +271,11 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { r_v = v; } break; + case VARIANT_STRING_NAME: { + r_v = StringName(get_unicode_string()); + } break; case VARIANT_NODE_PATH: { - Vector<StringName> names; Vector<StringName> subnames; bool absolute; @@ -280,10 +288,12 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { subname_count += 1; // has a property field, so we should count it as well } - for (int i = 0; i < name_count; i++) + for (int i = 0; i < name_count; i++) { names.push_back(_get_string()); - for (uint32_t i = 0; i < subname_count; i++) + } + for (uint32_t i = 0; i < subname_count; i++) { subnames.push_back(_get_string()); + } NodePath np = NodePath(names, subnames, absolute); @@ -291,15 +301,12 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_RID: { - r_v = f->get_32(); } break; case VARIANT_OBJECT: { - uint32_t objtype = f->get_32(); switch (objtype) { - case OBJECT_EMPTY: { //do none @@ -307,11 +314,19 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { case OBJECT_INTERNAL_RESOURCE: { uint32_t index = f->get_32(); String path = res_path + "::" + itos(index); - RES res = ResourceLoader::load(path); - if (res.is_null()) { - WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data()); + + if (use_nocache) { + if (!internal_index_cache.has(path)) { + WARN_PRINT(String("Couldn't load resource (no cache): " + path).utf8().get_data()); + } + r_v = internal_index_cache[path]; + } else { + RES res = ResourceLoader::load(path); + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data()); + } + r_v = res; } - r_v = res; } break; case OBJECT_EXTERNAL_RESOURCE: { @@ -345,33 +360,41 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { WARN_PRINT("Broken external resource! (index out of size)"); r_v = Variant(); } else { - - String exttype = external_resources[erindex].type; - String path = external_resources[erindex].path; - - if (path.find("://") == -1 && path.is_rel_path()) { - // path is relative to file being loaded, so convert to a resource path - path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path)); + if (external_resources[erindex].cache.is_null()) { + //cache not here yet, wait for it? + if (use_sub_threads) { + Error err; + external_resources.write[erindex].cache = ResourceLoader::load_threaded_get(external_resources[erindex].path, &err); + + if (err != OK || external_resources[erindex].cache.is_null()) { + if (!ResourceLoader::get_abort_on_missing_resources()) { + ResourceLoader::notify_dependency_error(local_path, external_resources[erindex].path, external_resources[erindex].type); + } else { + error = ERR_FILE_MISSING_DEPENDENCIES; + ERR_FAIL_V_MSG(error, "Can't load dependency: " + external_resources[erindex].path + "."); + } + } + } } - RES res = ResourceLoader::load(path, exttype); - - if (res.is_null()) { - WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data()); - } - r_v = res; + r_v = external_resources[erindex].cache; } } break; default: { - ERR_FAIL_V(ERR_FILE_CORRUPT); } break; } } break; - case VARIANT_DICTIONARY: { + case VARIANT_CALLABLE: { + r_v = Callable(); + } break; + case VARIANT_SIGNAL: { + r_v = Signal(); + } break; + case VARIANT_DICTIONARY: { uint32_t len = f->get_32(); Dictionary d; //last bit means shared len &= 0x7FFFFFFF; @@ -387,7 +410,6 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { r_v = d; } break; case VARIANT_ARRAY: { - uint32_t len = f->get_32(); Array a; //last bit means shared len &= 0x7FFFFFFF; @@ -402,87 +424,117 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } break; case VARIANT_RAW_ARRAY: { - uint32_t len = f->get_32(); - PoolVector<uint8_t> array; + Vector<uint8_t> array; array.resize(len); - PoolVector<uint8_t>::Write w = array.write(); - f->get_buffer(w.ptr(), len); + uint8_t *w = array.ptrw(); + f->get_buffer(w, len); _advance_padding(len); - w.release(); + r_v = array; } break; - case VARIANT_INT_ARRAY: { - + case VARIANT_INT32_ARRAY: { uint32_t len = f->get_32(); - PoolVector<int> array; + Vector<int32_t> array; array.resize(len); - PoolVector<int>::Write w = array.write(); - f->get_buffer((uint8_t *)w.ptr(), len * 4); + int32_t *w = array.ptrw(); + f->get_buffer((uint8_t *)w, len * sizeof(int32_t)); #ifdef BIG_ENDIAN_ENABLED { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len; i++) { - ptr[i] = BSWAP32(ptr[i]); } } #endif - w.release(); + r_v = array; } break; - case VARIANT_REAL_ARRAY: { + case VARIANT_INT64_ARRAY: { + uint32_t len = f->get_32(); + + Vector<int64_t> array; + array.resize(len); + int64_t *w = array.ptrw(); + f->get_buffer((uint8_t *)w, len * sizeof(int64_t)); +#ifdef BIG_ENDIAN_ENABLED + { + uint64_t *ptr = (uint64_t *)w.ptr(); + for (int i = 0; i < len; i++) { + ptr[i] = BSWAP64(ptr[i]); + } + } +#endif + + r_v = array; + } break; + case VARIANT_FLOAT32_ARRAY: { uint32_t len = f->get_32(); - PoolVector<real_t> array; + Vector<float> array; array.resize(len); - PoolVector<real_t>::Write w = array.write(); - f->get_buffer((uint8_t *)w.ptr(), len * sizeof(real_t)); + float *w = array.ptrw(); + f->get_buffer((uint8_t *)w, len * sizeof(float)); #ifdef BIG_ENDIAN_ENABLED { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len; i++) { - ptr[i] = BSWAP32(ptr[i]); } } #endif - w.release(); r_v = array; } break; - case VARIANT_STRING_ARRAY: { + case VARIANT_FLOAT64_ARRAY: { + uint32_t len = f->get_32(); + Vector<double> array; + array.resize(len); + double *w = array.ptrw(); + f->get_buffer((uint8_t *)w, len * sizeof(double)); +#ifdef BIG_ENDIAN_ENABLED + { + uint64_t *ptr = (uint64_t *)w.ptr(); + for (int i = 0; i < len; i++) { + ptr[i] = BSWAP64(ptr[i]); + } + } + +#endif + + r_v = array; + } break; + case VARIANT_STRING_ARRAY: { uint32_t len = f->get_32(); - PoolVector<String> array; + Vector<String> array; array.resize(len); - PoolVector<String>::Write w = array.write(); - for (uint32_t i = 0; i < len; i++) + String *w = array.ptrw(); + for (uint32_t i = 0; i < len; i++) { w[i] = get_unicode_string(); - w.release(); + } + r_v = array; } break; case VARIANT_VECTOR2_ARRAY: { - uint32_t len = f->get_32(); - PoolVector<Vector2> array; + Vector<Vector2> array; array.resize(len); - PoolVector<Vector2>::Write w = array.write(); + Vector2 *w = array.ptrw(); if (sizeof(Vector2) == 8) { - f->get_buffer((uint8_t *)w.ptr(), len * sizeof(real_t) * 2); + f->get_buffer((uint8_t *)w, len * sizeof(real_t) * 2); #ifdef BIG_ENDIAN_ENABLED { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len * 2; i++) { - ptr[i] = BSWAP32(ptr[i]); } } @@ -492,24 +544,22 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } else { ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector2 size is NOT 8!"); } - w.release(); + r_v = array; } break; case VARIANT_VECTOR3_ARRAY: { - uint32_t len = f->get_32(); - PoolVector<Vector3> array; + Vector<Vector3> array; array.resize(len); - PoolVector<Vector3>::Write w = array.write(); + Vector3 *w = array.ptrw(); if (sizeof(Vector3) == 12) { - f->get_buffer((uint8_t *)w.ptr(), len * sizeof(real_t) * 3); + f->get_buffer((uint8_t *)w, len * sizeof(real_t) * 3); #ifdef BIG_ENDIAN_ENABLED { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len * 3; i++) { - ptr[i] = BSWAP32(ptr[i]); } } @@ -519,24 +569,22 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } else { ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector3 size is NOT 12!"); } - w.release(); + r_v = array; } break; case VARIANT_COLOR_ARRAY: { - uint32_t len = f->get_32(); - PoolVector<Color> array; + Vector<Color> array; array.resize(len); - PoolVector<Color>::Write w = array.write(); + Color *w = array.ptrw(); if (sizeof(Color) == 16) { - f->get_buffer((uint8_t *)w.ptr(), len * sizeof(real_t) * 4); + f->get_buffer((uint8_t *)w, len * sizeof(real_t) * 4); #ifdef BIG_ENDIAN_ENABLED { uint32_t *ptr = (uint32_t *)w.ptr(); for (int i = 0; i < len * 4; i++) { - ptr[i] = BSWAP32(ptr[i]); } } @@ -546,72 +594,9 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { } else { ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Color size is NOT 16!"); } - w.release(); - r_v = array; - } break; -#ifndef DISABLE_DEPRECATED - case VARIANT_IMAGE: { - uint32_t encoding = f->get_32(); - if (encoding == IMAGE_ENCODING_EMPTY) { - r_v = Ref<Image>(); - break; - } else if (encoding == IMAGE_ENCODING_RAW) { - uint32_t width = f->get_32(); - uint32_t height = f->get_32(); - uint32_t mipmaps = f->get_32(); - uint32_t format = f->get_32(); - const uint32_t format_version_shift = 24; - const uint32_t format_version_mask = format_version_shift - 1; - - uint32_t format_version = format >> format_version_shift; - - const uint32_t current_version = 0; - if (format_version > current_version) { - - ERR_PRINT("Format version for encoded binary image is too new."); - return ERR_PARSE_ERROR; - } - - Image::Format fmt = Image::Format(format & format_version_mask); //if format changes, we can add a compatibility bit on top - - uint32_t datalen = f->get_32(); - - PoolVector<uint8_t> imgdata; - imgdata.resize(datalen); - PoolVector<uint8_t>::Write w = imgdata.write(); - f->get_buffer(w.ptr(), datalen); - _advance_padding(datalen); - w.release(); - - Ref<Image> image; - image.instance(); - image->create(width, height, mipmaps, fmt, imgdata); - r_v = image; - - } else { - //compressed - PoolVector<uint8_t> data; - data.resize(f->get_32()); - PoolVector<uint8_t>::Write w = data.write(); - f->get_buffer(w.ptr(), data.size()); - w.release(); - - Ref<Image> image; - - if (encoding == IMAGE_ENCODING_LOSSY && Image::lossy_unpacker) { - - image = Image::lossy_unpacker(data); - } else if (encoding == IMAGE_ENCODING_LOSSLESS && Image::lossless_unpacker) { - - image = Image::lossless_unpacker(data); - } - _advance_padding(data.size()); - - r_v = image; - } + r_v = array; } break; -#endif default: { ERR_FAIL_V(ERR_FILE_CORRUPT); } break; @@ -620,173 +605,178 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) { return OK; //never reach anyway } -void ResourceInteractiveLoaderBinary::set_local_path(const String &p_local_path) { - +void ResourceLoaderBinary::set_local_path(const String &p_local_path) { res_path = p_local_path; } -Ref<Resource> ResourceInteractiveLoaderBinary::get_resource() { - +Ref<Resource> ResourceLoaderBinary::get_resource() { return resource; } -Error ResourceInteractiveLoaderBinary::poll() { - if (error != OK) +Error ResourceLoaderBinary::load() { + if (error != OK) { return error; + } - int s = stage; - - if (s < external_resources.size()) { + int stage = 0; - String path = external_resources[s].path; + for (int i = 0; i < external_resources.size(); i++) { + String path = external_resources[i].path; if (remaps.has(path)) { path = remaps[path]; } - RES res = ResourceLoader::load(path, external_resources[s].type); - if (res.is_null()) { - if (!ResourceLoader::get_abort_on_missing_resources()) { + if (path.find("://") == -1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path = ProjectSettings::get_singleton()->localize_path(path.get_base_dir().plus_file(external_resources[i].path)); + } - ResourceLoader::notify_dependency_error(local_path, path, external_resources[s].type); - } else { + external_resources.write[i].path = path; //remap happens here, not on load because on load it can actually be used for filesystem dock resource remap - error = ERR_FILE_MISSING_DEPENDENCIES; - ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + "."); + if (!use_sub_threads) { + external_resources.write[i].cache = ResourceLoader::load(path, external_resources[i].type); + + if (external_resources[i].cache.is_null()) { + if (!ResourceLoader::get_abort_on_missing_resources()) { + ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type); + } else { + error = ERR_FILE_MISSING_DEPENDENCIES; + ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + "."); + } } } else { - resource_cache.push_back(res); + Error err = ResourceLoader::load_threaded_request(path, external_resources[i].type, use_sub_threads, local_path); + if (err != OK) { + if (!ResourceLoader::get_abort_on_missing_resources()) { + ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type); + } else { + error = ERR_FILE_MISSING_DEPENDENCIES; + ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + "."); + } + } } stage++; - return error; } - s -= external_resources.size(); + for (int i = 0; i < internal_resources.size(); i++) { + bool main = i == (internal_resources.size() - 1); - if (s >= internal_resources.size()) { + //maybe it is loaded already + String path; + int subindex = 0; - error = ERR_BUG; - ERR_FAIL_COND_V(s >= internal_resources.size(), error); - } + if (!main) { + path = internal_resources[i].path; - bool main = s == (internal_resources.size() - 1); - - //maybe it is loaded already - String path; - int subindex = 0; - - if (!main) { - - path = internal_resources[s].path; - if (path.begins_with("local://")) { - path = path.replace_first("local://", ""); - subindex = path.to_int(); - path = res_path + "::" + path; - } + if (path.begins_with("local://")) { + path = path.replace_first("local://", ""); + subindex = path.to_int(); + path = res_path + "::" + path; + } - if (ResourceCache::has(path)) { - //already loaded, don't do anything - stage++; - error = OK; - return error; + if (!use_nocache) { + if (ResourceCache::has(path)) { + //already loaded, don't do anything + stage++; + error = OK; + continue; + } + } + } else { + if (!use_nocache && !ResourceCache::has(res_path)) { + path = res_path; + } } - } else { - if (!ResourceCache::has(res_path)) - path = res_path; - } + uint64_t offset = internal_resources[i].offset; - uint64_t offset = internal_resources[s].offset; + f->seek(offset); - f->seek(offset); + String t = get_unicode_string(); - String t = get_unicode_string(); - - Object *obj = ClassDB::instance(t); - if (!obj) { - error = ERR_FILE_CORRUPT; - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + "."); - } + Object *obj = ClassDB::instance(t); + if (!obj) { + error = ERR_FILE_CORRUPT; + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + "."); + } - Resource *r = Object::cast_to<Resource>(obj); - if (!r) { - String obj_class = obj->get_class(); - error = ERR_FILE_CORRUPT; - memdelete(obj); //bye - ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + "."); - } + Resource *r = Object::cast_to<Resource>(obj); + if (!r) { + String obj_class = obj->get_class(); + error = ERR_FILE_CORRUPT; + memdelete(obj); //bye + ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + "."); + } - RES res = RES(r); + RES res = RES(r); - r->set_path(path); - r->set_subindex(subindex); + if (path != String()) { + r->set_path(path); + } + r->set_subindex(subindex); - int pc = f->get_32(); + if (!main) { + internal_index_cache[path] = res; + } - //set properties + int pc = f->get_32(); - for (int i = 0; i < pc; i++) { + //set properties - StringName name = _get_string(); + for (int j = 0; j < pc; j++) { + StringName name = _get_string(); - if (name == StringName()) { - error = ERR_FILE_CORRUPT; - ERR_FAIL_V(ERR_FILE_CORRUPT); - } + if (name == StringName()) { + error = ERR_FILE_CORRUPT; + ERR_FAIL_V(ERR_FILE_CORRUPT); + } - Variant value; + Variant value; - error = parse_variant(value); - if (error) - return error; + error = parse_variant(value); + if (error) { + return error; + } - res->set(name, value); - } + res->set(name, value); + } #ifdef TOOLS_ENABLED - res->set_edited(false); + res->set_edited(false); #endif - stage++; - - resource_cache.push_back(res); + stage++; - if (main) { + if (progress) { + *progress = (i + 1) / float(internal_resources.size()); + } - f->close(); - resource = res; - resource->set_as_translation_remapped(translation_remapped); - error = ERR_FILE_EOF; + resource_cache.push_back(res); - } else { - error = OK; + if (main) { + f->close(); + resource = res; + resource->set_as_translation_remapped(translation_remapped); + error = OK; + return OK; + } } - return OK; -} -int ResourceInteractiveLoaderBinary::get_stage() const { - - return stage; + return ERR_FILE_EOF; } -int ResourceInteractiveLoaderBinary::get_stage_count() const { - - return external_resources.size() + internal_resources.size(); -} - -void ResourceInteractiveLoaderBinary::set_translation_remapped(bool p_remapped) { +void ResourceLoaderBinary::set_translation_remapped(bool p_remapped) { translation_remapped = p_remapped; } static void save_ustring(FileAccess *f, const String &p_string) { - CharString utf8 = p_string.utf8(); f->store_32(utf8.length() + 1); f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1); } static String get_ustring(FileAccess *f) { - int len = f->get_32(); Vector<char> str_buf; str_buf.resize(len); @@ -796,28 +786,27 @@ static String get_ustring(FileAccess *f) { return s; } -String ResourceInteractiveLoaderBinary::get_unicode_string() { - +String ResourceLoaderBinary::get_unicode_string() { int len = f->get_32(); if (len > str_buf.size()) { str_buf.resize(len); } - if (len == 0) + if (len == 0) { return String(); + } f->get_buffer((uint8_t *)&str_buf[0], len); String s; s.parse_utf8(&str_buf[0]); return s; } -void ResourceInteractiveLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) { - +void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) { open(p_f); - if (error) + if (error) { return; + } for (int i = 0; i < external_resources.size(); i++) { - String dep = external_resources[i].path; if (p_add_types && external_resources[i].type != String()) { @@ -828,23 +817,27 @@ void ResourceInteractiveLoaderBinary::get_dependencies(FileAccess *p_f, List<Str } } -void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { - +void ResourceLoaderBinary::open(FileAccess *p_f) { error = OK; f = p_f; uint8_t header[4]; f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { - //compressed + // Compressed. FileAccessCompressed *fac = memnew(FileAccessCompressed); - fac->open_after_magic(f); + error = fac->open_after_magic(f); + if (error != OK) { + memdelete(fac); + f->close(); + ERR_FAIL_MSG("Failed to open binary resource file: " + local_path + "."); + } f = fac; } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { - //not normal - + // Not normal. error = ERR_FILE_UNRECOGNIZED; + f->close(); ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + "."); } @@ -869,7 +862,6 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { print_bl("format: " + itos(ver_format)); if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - f->close(); ERR_FAIL_MSG("File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + "."); } @@ -879,13 +871,13 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { print_bl("type: " + type); importmd_ofs = f->get_64(); - for (int i = 0; i < 14; i++) + for (int i = 0; i < 14; i++) { f->get_32(); //skip a few reserved fields + } uint32_t string_table_size = f->get_32(); string_map.resize(string_table_size); for (uint32_t i = 0; i < string_table_size; i++) { - StringName s = get_unicode_string(); string_map.write[i] = s; } @@ -894,7 +886,6 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { uint32_t ext_resources_size = f->get_32(); for (uint32_t i = 0; i < ext_resources_size; i++) { - ExtResource er; er.type = get_unicode_string(); @@ -907,7 +898,6 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { uint32_t int_resources_size = f->get_32(); for (uint32_t i = 0; i < int_resources_size; i++) { - IntResource ir; ir.path = get_unicode_string(); ir.offset = f->get_64(); @@ -917,28 +907,33 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { print_bl("int resources: " + itos(int_resources_size)); if (f->eof_reached()) { - error = ERR_FILE_CORRUPT; + f->close(); ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + "."); } } -String ResourceInteractiveLoaderBinary::recognize(FileAccess *p_f) { - +String ResourceLoaderBinary::recognize(FileAccess *p_f) { error = OK; f = p_f; uint8_t header[4]; f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { - //compressed + // Compressed. FileAccessCompressed *fac = memnew(FileAccessCompressed); - fac->open_after_magic(f); + error = fac->open_after_magic(f); + if (error != OK) { + memdelete(fac); + f->close(); + return ""; + } f = fac; } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { - //not normal + // Not normal. error = ERR_FILE_UNRECOGNIZED; + f->close(); return ""; } @@ -952,7 +947,6 @@ String ResourceInteractiveLoaderBinary::recognize(FileAccess *p_f) { uint32_t ver_format = f->get_32(); if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - f->close(); return ""; } @@ -962,41 +956,45 @@ String ResourceInteractiveLoaderBinary::recognize(FileAccess *p_f) { return type; } -ResourceInteractiveLoaderBinary::ResourceInteractiveLoaderBinary() : - translation_remapped(false), - f(NULL), - error(OK), - stage(0) { -} - -ResourceInteractiveLoaderBinary::~ResourceInteractiveLoaderBinary() { - - if (f) +ResourceLoaderBinary::~ResourceLoaderBinary() { + if (f) { memdelete(f); + } } -Ref<ResourceInteractiveLoader> ResourceFormatLoaderBinary::load_interactive(const String &p_path, const String &p_original_path, Error *r_error) { - - if (r_error) +RES ResourceFormatLoaderBinary::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { + if (r_error) { *r_error = ERR_FILE_CANT_OPEN; + } Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); - ERR_FAIL_COND_V_MSG(err != OK, Ref<ResourceInteractiveLoader>(), "Cannot open file '" + p_path + "'."); + ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot open file '" + p_path + "'."); - Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); + ResourceLoaderBinary loader; + loader.use_nocache = p_no_cache; + loader.use_sub_threads = p_use_sub_threads; + loader.progress = r_progress; String path = p_original_path != "" ? p_original_path : p_path; - ria->local_path = ProjectSettings::get_singleton()->localize_path(path); - ria->res_path = ria->local_path; - //ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); - ria->open(f); + loader.local_path = ProjectSettings::get_singleton()->localize_path(path); + loader.res_path = loader.local_path; + //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); + loader.open(f); + + err = loader.load(); + + if (r_error) { + *r_error = err; + } - return ria; + if (err) { + return RES(); + } + return loader.resource; } void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - if (p_type == "") { get_recognized_extensions(p_extensions); return; @@ -1012,8 +1010,8 @@ void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String p_extensions->push_back(ext); } } -void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_extensions) const { +void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_extensions) const { List<String> extensions; ClassDB::get_resource_base_extensions(&extensions); extensions.sort(); @@ -1025,44 +1023,46 @@ void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_exten } bool ResourceFormatLoaderBinary::handles_type(const String &p_type) const { - return true; //handles all } void ResourceFormatLoaderBinary::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_MSG(!f, "Cannot open file '" + p_path + "'."); - Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); - ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); - ria->res_path = ria->local_path; - //ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); - ria->get_dependencies(f, p_dependencies, p_add_types); + ResourceLoaderBinary loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); + loader.get_dependencies(f, p_dependencies, p_add_types); } Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - //Error error=OK; FileAccess *f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_path + "'."); - FileAccess *fw = NULL; //=FileAccess::open(p_path+".depren"); + FileAccess *fw = nullptr; //=FileAccess::open(p_path+".depren"); String local_path = p_path.get_base_dir(); uint8_t header[4]; f->get_buffer(header, 4); if (header[0] == 'R' && header[1] == 'S' && header[2] == 'C' && header[3] == 'C') { - //compressed + // Compressed. FileAccessCompressed *fac = memnew(FileAccessCompressed); - fac->open_after_magic(f); + Error err = fac->open_after_magic(f); + if (err != OK) { + memdelete(fac); + memdelete(f); + ERR_FAIL_V_MSG(err, "Cannot open file '" + p_path + "'."); + } f = fac; FileAccessCompressed *facw = memnew(FileAccessCompressed); facw->configure("RSCC"); - Error err = facw->_open(p_path + ".depren", FileAccess::WRITE); + err = facw->_open(p_path + ".depren", FileAccess::WRITE); if (err) { memdelete(fac); memdelete(facw); @@ -1072,9 +1072,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons fw = facw; } else if (header[0] != 'R' || header[1] != 'S' || header[2] != 'R' || header[3] != 'C') { - //not normal - - //error=ERR_FILE_UNRECOGNIZED; + // Not normal. memdelete(f); ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file '" + local_path + "'."); } else { @@ -1105,7 +1103,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons uint32_t ver_format = f->get_32(); if (ver_format < FORMAT_VERSION_CAN_RENAME_DEPS) { - memdelete(f); memdelete(fw); DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); @@ -1113,35 +1110,30 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons memdelete(da); //use the old approach - WARN_PRINTS("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'."); + WARN_PRINT("This file is old, so it can't refactor dependencies, opening and resaving '" + p_path + "'."); Error err; f = FileAccess::open(p_path, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_OPEN, "Cannot open file '" + p_path + "'."); - Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); - ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); - ria->res_path = ria->local_path; - ria->remaps = p_map; - //ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); - ria->open(f); + ResourceLoaderBinary loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + loader.remaps = p_map; + //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); + loader.open(f); - err = ria->poll(); - - while (err == OK) { - err = ria->poll(); - } + err = loader.load(); ERR_FAIL_COND_V(err != ERR_FILE_EOF, ERR_FILE_CORRUPT); - RES res = ria->get_resource(); + RES res = loader.get_resource(); ERR_FAIL_COND_V(!res.is_valid(), ERR_FILE_CORRUPT); return ResourceFormatSaverBinary::singleton->save(p_path, res); } if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) { - memdelete(f); memdelete(fw); ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + "."); @@ -1170,7 +1162,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons fw->store_32(string_table_size); for (uint32_t i = 0; i < string_table_size; i++) { - String s = get_ustring(f); save_ustring(fw, s); } @@ -1179,7 +1170,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons uint32_t ext_resources_size = f->get_32(); fw->store_32(ext_resources_size); for (uint32_t i = 0; i < ext_resources_size; i++) { - String type = get_ustring(f); String path = get_ustring(f); @@ -1210,7 +1200,6 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons fw->store_32(int_resources_size); for (uint32_t i = 0; i < int_resources_size; i++) { - String path = get_ustring(f); uint64_t offset = f->get_64(); save_ustring(fw, path); @@ -1244,18 +1233,17 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons } String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const { - FileAccess *f = FileAccess::open(p_path, FileAccess::READ); if (!f) { return ""; //could not rwead } - Ref<ResourceInteractiveLoaderBinary> ria = memnew(ResourceInteractiveLoaderBinary); - ria->local_path = ProjectSettings::get_singleton()->localize_path(p_path); - ria->res_path = ria->local_path; - //ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); - String r = ria->recognize(f); - return r; + ResourceLoaderBinary loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); + String r = loader.recognize(f); + return ClassDB::get_compatibility_remapped_class(r); } /////////////////////////////////////////////////////////// @@ -1263,36 +1251,30 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const /////////////////////////////////////////////////////////// void ResourceFormatSaverBinaryInstance::_pad_buffer(FileAccess *f, int p_bytes) { - int extra = 4 - (p_bytes % 4); if (extra < 4) { - for (int i = 0; i < extra; i++) + for (int i = 0; i < extra; i++) { f->store_8(0); //pad to 32 + } } } void ResourceFormatSaverBinaryInstance::_write_variant(const Variant &p_property, const PropertyInfo &p_hint) { - write_variant(f, p_property, resource_set, external_resources, string_map, p_hint); } void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Set<RES> &resource_set, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) { - switch (p_property.get_type()) { - case Variant::NIL: { - f->store_32(VARIANT_NIL); // don't store anything } break; case Variant::BOOL: { - f->store_32(VARIANT_BOOL); bool val = p_property; f->store_32(val); } break; case Variant::INT: { - int64_t val = p_property; if (val > 0x7FFFFFFF || val < -(int64_t)0x80000000) { f->store_32(VARIANT_INT64); @@ -1304,37 +1286,39 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } } break; - case Variant::REAL: { - + case Variant::FLOAT: { double d = p_property; float fl = d; if (double(fl) != d) { f->store_32(VARIANT_DOUBLE); f->store_double(d); } else { - - f->store_32(VARIANT_REAL); + f->store_32(VARIANT_FLOAT); f->store_real(fl); } } break; case Variant::STRING: { - f->store_32(VARIANT_STRING); String val = p_property; save_unicode_string(f, val); } break; case Variant::VECTOR2: { - f->store_32(VARIANT_VECTOR2); Vector2 val = p_property; f->store_real(val.x); f->store_real(val.y); } break; - case Variant::RECT2: { + case Variant::VECTOR2I: { + f->store_32(VARIANT_VECTOR2I); + Vector2i val = p_property; + f->store_32(val.x); + f->store_32(val.y); + } break; + case Variant::RECT2: { f->store_32(VARIANT_RECT2); Rect2 val = p_property; f->store_real(val.position.x); @@ -1343,8 +1327,16 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia f->store_real(val.size.y); } break; - case Variant::VECTOR3: { + case Variant::RECT2I: { + f->store_32(VARIANT_RECT2I); + Rect2i val = p_property; + f->store_32(val.position.x); + f->store_32(val.position.y); + f->store_32(val.size.x); + f->store_32(val.size.y); + } break; + case Variant::VECTOR3: { f->store_32(VARIANT_VECTOR3); Vector3 val = p_property; f->store_real(val.x); @@ -1352,8 +1344,15 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia f->store_real(val.z); } break; - case Variant::PLANE: { + case Variant::VECTOR3I: { + f->store_32(VARIANT_VECTOR3I); + Vector3i val = p_property; + f->store_32(val.x); + f->store_32(val.y); + f->store_32(val.z); + } break; + case Variant::PLANE: { f->store_32(VARIANT_PLANE); Plane val = p_property; f->store_real(val.normal.x); @@ -1363,7 +1362,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::QUAT: { - f->store_32(VARIANT_QUAT); Quat val = p_property; f->store_real(val.x); @@ -1373,7 +1371,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::AABB: { - f->store_32(VARIANT_AABB); AABB val = p_property; f->store_real(val.position.x); @@ -1385,7 +1382,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::TRANSFORM2D: { - f->store_32(VARIANT_MATRIX32); Transform2D val = p_property; f->store_real(val.elements[0].x); @@ -1397,7 +1393,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::BASIS: { - f->store_32(VARIANT_MATRIX3); Basis val = p_property; f->store_real(val.elements[0].x); @@ -1412,7 +1407,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::TRANSFORM: { - f->store_32(VARIANT_TRANSFORM); Transform val = p_property; f->store_real(val.basis.elements[0].x); @@ -1430,7 +1424,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::COLOR: { - f->store_32(VARIANT_COLOR); Color val = p_property; f->store_real(val.r); @@ -1439,14 +1432,21 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia f->store_real(val.a); } break; + case Variant::STRING_NAME: { + f->store_32(VARIANT_STRING_NAME); + String val = p_property; + save_unicode_string(f, val); + + } break; case Variant::NODE_PATH: { f->store_32(VARIANT_NODE_PATH); NodePath np = p_property; f->store_16(np.get_name_count()); uint16_t snc = np.get_subname_count(); - if (np.is_absolute()) + if (np.is_absolute()) { snc |= 0x8000; + } f->store_16(snc); for (int i = 0; i < np.get_name_count(); i++) { if (string_map.has(np.get_name(i))) { @@ -1465,14 +1465,12 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::_RID: { - f->store_32(VARIANT_RID); WARN_PRINT("Can't save RIDs."); RID val = p_property; f->store_32(val.get_id()); } break; case Variant::OBJECT: { - f->store_32(VARIANT_OBJECT); RES res = p_property; if (res.is_null()) { @@ -1484,7 +1482,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX); f->store_32(external_resources[res]); } else { - if (!resource_set.has(res)) { f->store_32(OBJECT_EMPTY); ERR_FAIL_MSG("Resource was not pre cached for the resource section, most likely due to circular reference."); @@ -1496,8 +1493,16 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } } break; - case Variant::DICTIONARY: { + case Variant::CALLABLE: { + f->store_32(VARIANT_CALLABLE); + WARN_PRINT("Can't save Callables."); + } break; + case Variant::SIGNAL: { + f->store_32(VARIANT_SIGNAL); + WARN_PRINT("Can't save Signals."); + } break; + case Variant::DICTIONARY: { f->store_32(VARIANT_DICTIONARY); Dictionary d = p_property; f->store_32(uint32_t(d.size())); @@ -1506,7 +1511,6 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - /* if (!_check_type(dict[E->get()])) continue; @@ -1518,69 +1522,85 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; case Variant::ARRAY: { - f->store_32(VARIANT_ARRAY); Array a = p_property; f->store_32(uint32_t(a.size())); for (int i = 0; i < a.size(); i++) { - write_variant(f, a[i], resource_set, external_resources, string_map); } } break; - case Variant::POOL_BYTE_ARRAY: { - + case Variant::PACKED_BYTE_ARRAY: { f->store_32(VARIANT_RAW_ARRAY); - PoolVector<uint8_t> arr = p_property; + Vector<uint8_t> arr = p_property; int len = arr.size(); f->store_32(len); - PoolVector<uint8_t>::Read r = arr.read(); - f->store_buffer(r.ptr(), len); + const uint8_t *r = arr.ptr(); + f->store_buffer(r, len); _pad_buffer(f, len); } break; - case Variant::POOL_INT_ARRAY: { - - f->store_32(VARIANT_INT_ARRAY); - PoolVector<int> arr = p_property; + case Variant::PACKED_INT32_ARRAY: { + f->store_32(VARIANT_INT32_ARRAY); + Vector<int32_t> arr = p_property; int len = arr.size(); f->store_32(len); - PoolVector<int>::Read r = arr.read(); - for (int i = 0; i < len; i++) + const int32_t *r = arr.ptr(); + for (int i = 0; i < len; i++) { f->store_32(r[i]); + } } break; - case Variant::POOL_REAL_ARRAY: { + case Variant::PACKED_INT64_ARRAY: { + f->store_32(VARIANT_INT64_ARRAY); + Vector<int64_t> arr = p_property; + int len = arr.size(); + f->store_32(len); + const int64_t *r = arr.ptr(); + for (int i = 0; i < len; i++) { + f->store_64(r[i]); + } - f->store_32(VARIANT_REAL_ARRAY); - PoolVector<real_t> arr = p_property; + } break; + case Variant::PACKED_FLOAT32_ARRAY: { + f->store_32(VARIANT_FLOAT32_ARRAY); + Vector<float> arr = p_property; int len = arr.size(); f->store_32(len); - PoolVector<real_t>::Read r = arr.read(); + const float *r = arr.ptr(); for (int i = 0; i < len; i++) { f->store_real(r[i]); } } break; - case Variant::POOL_STRING_ARRAY: { + case Variant::PACKED_FLOAT64_ARRAY: { + f->store_32(VARIANT_FLOAT64_ARRAY); + Vector<double> arr = p_property; + int len = arr.size(); + f->store_32(len); + const double *r = arr.ptr(); + for (int i = 0; i < len; i++) { + f->store_double(r[i]); + } + } break; + case Variant::PACKED_STRING_ARRAY: { f->store_32(VARIANT_STRING_ARRAY); - PoolVector<String> arr = p_property; + Vector<String> arr = p_property; int len = arr.size(); f->store_32(len); - PoolVector<String>::Read r = arr.read(); + const String *r = arr.ptr(); for (int i = 0; i < len; i++) { save_unicode_string(f, r[i]); } } break; - case Variant::POOL_VECTOR3_ARRAY: { - + case Variant::PACKED_VECTOR3_ARRAY: { f->store_32(VARIANT_VECTOR3_ARRAY); - PoolVector<Vector3> arr = p_property; + Vector<Vector3> arr = p_property; int len = arr.size(); f->store_32(len); - PoolVector<Vector3>::Read r = arr.read(); + const Vector3 *r = arr.ptr(); for (int i = 0; i < len; i++) { f->store_real(r[i].x); f->store_real(r[i].y); @@ -1588,26 +1608,24 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } } break; - case Variant::POOL_VECTOR2_ARRAY: { - + case Variant::PACKED_VECTOR2_ARRAY: { f->store_32(VARIANT_VECTOR2_ARRAY); - PoolVector<Vector2> arr = p_property; + Vector<Vector2> arr = p_property; int len = arr.size(); f->store_32(len); - PoolVector<Vector2>::Read r = arr.read(); + const Vector2 *r = arr.ptr(); for (int i = 0; i < len; i++) { f->store_real(r[i].x); f->store_real(r[i].y); } } break; - case Variant::POOL_COLOR_ARRAY: { - + case Variant::PACKED_COLOR_ARRAY: { f->store_32(VARIANT_COLOR_ARRAY); - PoolVector<Color> arr = p_property; + Vector<Color> arr = p_property; int len = arr.size(); f->store_32(len); - PoolVector<Color>::Read r = arr.read(); + const Color *r = arr.ptr(); for (int i = 0; i < len; i++) { f->store_real(r[i].r); f->store_real(r[i].g); @@ -1617,25 +1635,23 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia } break; default: { - ERR_FAIL_MSG("Invalid variant."); } } } void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant, bool p_main) { - switch (p_variant.get_type()) { case Variant::OBJECT: { + RES res = p_variant; - RES res = p_variant.operator RefPtr(); - - if (res.is_null() || external_resources.has(res)) + if (res.is_null() || external_resources.has(res)) { return; + } if (!p_main && (!bundle_resources) && res->get_path().length() && res->get_path().find("::") == -1) { if (res->get_path() == path) { - ERR_PRINTS("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded."); + ERR_PRINT("Circular reference to resource being saved found: '" + local_path + "' will be null next time it's loaded."); return; } int idx = external_resources.size(); @@ -1643,17 +1659,16 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant return; } - if (resource_set.has(res)) + if (resource_set.has(res)) { return; + } List<PropertyInfo> property_list; res->get_property_list(&property_list); for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE) { - Variant value = res->get(E->get().name); if (E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { RES sres = value; @@ -1677,11 +1692,9 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant } break; case Variant::ARRAY: { - Array varray = p_variant; int len = varray.size(); for (int i = 0; i < len; i++) { - const Variant &v = varray.get(i); _find_resources(v); } @@ -1689,12 +1702,10 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant } break; case Variant::DICTIONARY: { - Dictionary d = p_variant; List<Variant> keys; d.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _find_resources(E->get()); Variant v = d[E->get()]; _find_resources(v); @@ -1703,10 +1714,12 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant case Variant::NODE_PATH: { //take the chance and save node path strings NodePath np = p_variant; - for (int i = 0; i < np.get_name_count(); i++) + for (int i = 0; i < np.get_name_count(); i++) { get_string_index(np.get_name(i)); - for (int i = 0; i < np.get_subname_count(); i++) + } + for (int i = 0; i < np.get_subname_count(); i++) { get_string_index(np.get_subname(i)); + } } break; default: { @@ -1715,7 +1728,6 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant } void ResourceFormatSaverBinaryInstance::save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len) { - CharString utf8 = p_string.utf8(); if (p_bit_on_len) { f->store_32((utf8.length() + 1) | 0x80000000); @@ -1726,10 +1738,10 @@ void ResourceFormatSaverBinaryInstance::save_unicode_string(FileAccess *f, const } int ResourceFormatSaverBinaryInstance::get_string_index(const String &p_string) { - StringName s = p_string; - if (string_map.has(s)) + if (string_map.has(s)) { return string_map[s]; + } string_map[s] = strings.size(); strings.push_back(s); @@ -1737,15 +1749,15 @@ int ResourceFormatSaverBinaryInstance::get_string_index(const String &p_string) } Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - Error err; if (p_flags & ResourceSaver::FLAG_COMPRESS) { FileAccessCompressed *fac = memnew(FileAccessCompressed); fac->configure("RSCC"); f = fac; err = fac->_open(p_path, FileAccess::WRITE); - if (err) + if (err) { memdelete(f); + } } else { f = FileAccess::open(p_path, FileAccess::WRITE, &err); @@ -1759,8 +1771,9 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p big_endian = p_flags & ResourceSaver::FLAG_SAVE_BIG_ENDIAN; takeover_paths = p_flags & ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS; - if (!p_path.begins_with("res://")) + if (!p_path.begins_with("res://")) { takeover_paths = false; + } local_path = p_path.get_base_dir(); path = ProjectSettings::get_singleton()->localize_path(p_path); @@ -1776,8 +1789,9 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p if (big_endian) { f->store_32(1); f->set_endian_swap(true); - } else + } else { f->store_32(0); + } f->store_32(0); //64 bits file, false for now f->store_32(VERSION_MAJOR); @@ -1792,15 +1806,14 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p save_unicode_string(f, p_resource->get_class()); f->store_64(0); //offset to import metadata - for (int i = 0; i < 14; i++) + for (int i = 0; i < 14; i++) { f->store_32(0); // reserved + } List<ResourceData> resources; { - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - ResourceData &rd = resources.push_back(ResourceData())->get(); rd.type = E->get()->get_class(); @@ -1808,9 +1821,9 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p E->get()->get_property_list(&property_list); for (List<PropertyInfo>::Element *F = property_list.front(); F; F = F->next()) { - - if (skip_editor && F->get().name.begins_with("__editor")) + if (skip_editor && F->get().name.begins_with("__editor")) { continue; + } if ((F->get().usage & PROPERTY_USAGE_STORAGE)) { Property p; p.name_idx = get_string_index(F->get().name); @@ -1855,7 +1868,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p } 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; @@ -1867,10 +1879,8 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p Set<int> used_indices; for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES r = E->get(); if (r->get_path() == "" || r->get_path().find("::") != -1) { - if (r->get_subindex() != 0) { if (used_indices.has(r->get_subindex())) { r->set_subindex(0); //repeated @@ -1882,7 +1892,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p } for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES r = E->get(); if (r->get_path() == "" || r->get_path().find("::") != -1) { if (r->get_subindex() == 0) { @@ -1913,7 +1922,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p //now actually save the resources for (List<ResourceData>::Element *E = resources.front(); E; E = E->next()) { - ResourceData &rd = E->get(); ofs_table.push_back(f->get_position()); @@ -1921,7 +1929,6 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p f->store_32(rd.properties.size()); for (List<Property>::Element *F = rd.properties.front(); F; F = F->next()) { - Property &p = F->get(); f->store_32(p.name_idx); _write_variant(p.value, F->get().pi); @@ -1950,28 +1957,25 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p } Error ResourceFormatSaverBinary::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - String local_path = ProjectSettings::get_singleton()->localize_path(p_path); ResourceFormatSaverBinaryInstance saver; return saver.save(local_path, p_resource, p_flags); } bool ResourceFormatSaverBinary::recognize(const RES &p_resource) const { - return true; //all recognized } void ResourceFormatSaverBinary::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { - String base = p_resource->get_base_extension().to_lower(); p_extensions->push_back(base); - if (base != "res") + if (base != "res") { p_extensions->push_back("res"); + } } -ResourceFormatSaverBinary *ResourceFormatSaverBinary::singleton = NULL; +ResourceFormatSaverBinary *ResourceFormatSaverBinary::singleton = nullptr; ResourceFormatSaverBinary::ResourceFormatSaverBinary() { - singleton = this; } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index 7737006d10..54cddca49e 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -35,23 +35,21 @@ #include "core/io/resource_saver.h" #include "core/os/file_access.h" -class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader { - - bool translation_remapped; +class ResourceLoaderBinary { + bool translation_remapped = false; String local_path; String res_path; String type; Ref<Resource> resource; - uint32_t ver_format; + uint32_t ver_format = 0; - FileAccess *f; + FileAccess *f = nullptr; - uint64_t importmd_ofs; + uint64_t importmd_ofs = 0; Vector<char> str_buf; List<RES> resource_cache; - //Map<int,StringName> string_map; Vector<StringName> string_map; StringName _get_string(); @@ -59,8 +57,11 @@ class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader { struct ExtResource { String path; String type; + RES cache; }; + bool use_sub_threads = false; + float *progress = nullptr; Vector<ExtResource> external_resources; struct IntResource { @@ -69,39 +70,40 @@ class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader { }; Vector<IntResource> internal_resources; + Map<String, RES> internal_index_cache; String get_unicode_string(); void _advance_padding(uint32_t p_len); Map<String, String> remaps; - Error error; + Error error = OK; - int stage; + bool use_nocache = false; friend class ResourceFormatLoaderBinary; Error parse_variant(Variant &r_v); + Map<String, RES> dependency_cache; + public: - virtual void set_local_path(const String &p_local_path); - virtual Ref<Resource> get_resource(); - virtual Error poll(); - virtual int get_stage() const; - virtual int get_stage_count() const; - virtual void set_translation_remapped(bool p_remapped); + void set_local_path(const String &p_local_path); + Ref<Resource> get_resource(); + Error load(); + void set_translation_remapped(bool p_remapped); void set_remaps(const Map<String, String> &p_remaps) { remaps = p_remaps; } void open(FileAccess *p_f); String recognize(FileAccess *p_f); void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types); - ResourceInteractiveLoaderBinary(); - ~ResourceInteractiveLoaderBinary(); + ResourceLoaderBinary() {} + ~ResourceLoaderBinary(); }; class ResourceFormatLoaderBinary : public ResourceFormatLoader { public: - virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual bool handles_type(const String &p_type) const; @@ -111,7 +113,6 @@ public: }; class ResourceFormatSaverBinaryInstance { - String local_path; String path; @@ -144,7 +145,6 @@ class ResourceFormatSaverBinaryInstance { }; struct ResourceData { - String type; List<Property> properties; }; diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index c2d90ed204..9ed159bd20 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -38,7 +38,6 @@ bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceIm } Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid) const { - Error err; FileAccess *f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); @@ -64,17 +63,16 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy String error_text; bool path_found = false; //first match must have priority while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); - err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true); + err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); if (err == ERR_FILE_EOF) { memdelete(f); return OK; } else if (err != OK) { - ERR_PRINTS("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text); + ERR_PRINT("ResourceFormatImporter::load - " + p_path + ".import:" + itos(lines) + " error: " + error_text); memdelete(f); return err; } @@ -91,7 +89,7 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy r_path_and_type.path = value; path_found = true; //first match must have priority } else if (assign == "type") { - r_path_and_type.type = value; + r_path_and_type.type = ClassDB::get_compatibility_remapped_class(value); } else if (assign == "importer") { r_path_and_type.importer = value; } else if (assign == "group_file") { @@ -117,20 +115,19 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy return OK; } -RES ResourceFormatImporter::load(const String &p_path, const String &p_original_path, Error *r_error) { - +RES ResourceFormatImporter::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { PathAndType pat; Error err = _get_path_and_type(p_path, pat); if (err != OK) { - - if (r_error) + if (r_error) { *r_error = err; + } return RES(); } - RES res = ResourceLoader::_load(pat.path, p_path, pat.type, false, r_error); + RES res = ResourceLoader::_load(pat.path, p_path, pat.type, p_no_cache, r_error, p_use_sub_threads, r_progress); #ifdef TOOLS_ENABLED if (res.is_valid()) { @@ -143,7 +140,6 @@ RES ResourceFormatImporter::load(const String &p_path, const String &p_original_ } void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extensions) const { - Set<String> found; for (int i = 0; i < importers.size(); i++) { @@ -159,7 +155,6 @@ void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extension } void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - if (p_type == "") { get_recognized_extensions(p_extensions); return; @@ -169,11 +164,13 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_ for (int i = 0; i < importers.size(); i++) { String res_type = importers[i]->get_resource_type(); - if (res_type == String()) + if (res_type == String()) { continue; + } - if (!ClassDB::is_parent_class(res_type, p_type)) + if (!ClassDB::is_parent_class(res_type, p_type)) { continue; + } List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); @@ -187,26 +184,21 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_ } bool ResourceFormatImporter::exists(const String &p_path) const { - return FileAccess::exists(p_path + ".import"); } bool ResourceFormatImporter::recognize_path(const String &p_path, const String &p_for_type) const { - return FileAccess::exists(p_path + ".import"); } bool ResourceFormatImporter::can_be_imported(const String &p_path) const { - return ResourceFormatLoader::recognize_path(p_path); } int ResourceFormatImporter::get_import_order(const String &p_path) const { - Ref<ResourceImporter> importer; if (FileAccess::exists(p_path + ".import")) { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); @@ -214,37 +206,35 @@ int ResourceFormatImporter::get_import_order(const String &p_path) const { importer = get_importer_by_name(pat.importer); } } else { - importer = get_importer_by_extension(p_path.get_extension().to_lower()); } - if (importer.is_valid()) + if (importer.is_valid()) { return importer->get_import_order(); + } return 0; } bool ResourceFormatImporter::handles_type(const String &p_type) const { - for (int i = 0; i < importers.size(); i++) { - String res_type = importers[i]->get_resource_type(); - if (res_type == String()) + if (res_type == String()) { continue; - if (ClassDB::is_parent_class(res_type, p_type)) + } + if (ClassDB::is_parent_class(res_type, p_type)) { return true; + } } return true; } String ResourceFormatImporter::get_internal_resource_path(const String &p_path) const { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); if (err != OK) { - return String(); } @@ -252,12 +242,12 @@ String ResourceFormatImporter::get_internal_resource_path(const String &p_path) } void ResourceFormatImporter::get_internal_resource_path_list(const String &p_path, List<String> *r_paths) { - Error err; FileAccess *f = FileAccess::open(p_path + ".import", FileAccess::READ, &err); - if (!f) + if (!f) { return; + } VariantParser::StreamFile stream; stream.f = f; @@ -269,17 +259,16 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat int lines = 0; String error_text; while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); - err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true); + err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); if (err == ERR_FILE_EOF) { memdelete(f); return; } else if (err != OK) { - ERR_PRINTS("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text); + ERR_PRINT("ResourceFormatImporter::get_internal_resource_path_list - " + p_path + ".import:" + itos(lines) + " error: " + error_text); memdelete(f); return; } @@ -298,7 +287,6 @@ void ResourceFormatImporter::get_internal_resource_path_list(const String &p_pat } String ResourceFormatImporter::get_import_group_file(const String &p_path) const { - bool valid = true; PathAndType pat; _get_path_and_type(p_path, pat, &valid); @@ -306,7 +294,6 @@ String ResourceFormatImporter::get_import_group_file(const String &p_path) const } bool ResourceFormatImporter::is_import_valid(const String &p_path) const { - bool valid = true; PathAndType pat; _get_path_and_type(p_path, pat, &valid); @@ -314,12 +301,10 @@ bool ResourceFormatImporter::is_import_valid(const String &p_path) const { } String ResourceFormatImporter::get_resource_type(const String &p_path) const { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); if (err != OK) { - return ""; } @@ -331,7 +316,6 @@ Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) cons Error err = _get_path_and_type(p_path, pat); if (err != OK) { - return Variant(); } @@ -339,12 +323,10 @@ Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) cons } void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - PathAndType pat; Error err = _get_path_and_type(p_path, pat); if (err != OK) { - return; } @@ -352,7 +334,6 @@ void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> } Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String &p_name) const { - for (int i = 0; i < importers.size(); i++) { if (importers[i]->get_importer_name() == p_name) { return importers[i]; @@ -362,8 +343,7 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_name(const String return Ref<ResourceImporter>(); } -void ResourceFormatImporter::get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter> > *r_importers) { - +void ResourceFormatImporter::get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter>> *r_importers) { for (int i = 0; i < importers.size(); i++) { List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); @@ -376,12 +356,10 @@ void ResourceFormatImporter::get_importers_for_extension(const String &p_extensi } Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const String &p_extension) const { - Ref<ResourceImporter> importer; float priority = 0; for (int i = 0; i < importers.size(); i++) { - List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); for (List<String>::Element *F = local_exts.front(); F; F = F->next()) { @@ -396,12 +374,10 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St } String ResourceFormatImporter::get_import_base_path(const String &p_for_file) const { - return "res://.import/" + p_for_file.get_file() + "-" + p_for_file.md5_text(); } bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) const { - bool valid = true; PathAndType pat; _get_path_and_type(p_path, pat, &valid); @@ -422,8 +398,7 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con } String ResourceFormatImporter::get_import_settings_hash() const { - - Vector<Ref<ResourceImporter> > sorted_importers = importers; + Vector<Ref<ResourceImporter>> sorted_importers = importers; sorted_importers.sort_custom<SortImporterByName>(); @@ -434,7 +409,7 @@ String ResourceFormatImporter::get_import_settings_hash() const { return hash.md5_text(); } -ResourceFormatImporter *ResourceFormatImporter::singleton = NULL; +ResourceFormatImporter *ResourceFormatImporter::singleton = nullptr; ResourceFormatImporter::ResourceFormatImporter() { singleton = this; diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 4eb04586e6..d31a9a0194 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -36,7 +36,6 @@ class ResourceImporter; class ResourceFormatImporter : public ResourceFormatLoader { - struct PathAndType { String path; String type; @@ -45,7 +44,7 @@ class ResourceFormatImporter : public ResourceFormatLoader { Variant metadata; }; - Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = NULL) const; + Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = nullptr) const; static ResourceFormatImporter *singleton; @@ -54,11 +53,11 @@ class ResourceFormatImporter : public ResourceFormatLoader { bool operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const; }; - Vector<Ref<ResourceImporter> > importers; + Vector<Ref<ResourceImporter>> importers; public: static ResourceFormatImporter *get_singleton() { return singleton; } - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const; @@ -83,7 +82,7 @@ public: void remove_importer(const Ref<ResourceImporter> &p_importer) { importers.erase(p_importer); } Ref<ResourceImporter> get_importer_by_name(const String &p_name) const; Ref<ResourceImporter> get_importer_by_extension(const String &p_extension) const; - void get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter> > *r_importers); + void get_importers_for_extension(const String &p_extension, List<Ref<ResourceImporter>> *r_importers); bool are_import_settings_valid(const String &p_path) const; String get_import_settings_hash() const; @@ -93,7 +92,6 @@ public: }; class ResourceImporter : public Reference { - GDCLASS(ResourceImporter, Reference); public: @@ -123,9 +121,9 @@ public: virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const = 0; virtual String get_option_group_file() const { return String(); } - virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL) = 0; + virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0; - virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths) { return ERR_UNAVAILABLE; } + virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant>> &p_source_file_options, const Map<String, String> &p_base_paths) { return ERR_UNAVAILABLE; } virtual bool are_import_settings_valid(const String &p_path) const { return true; } virtual String get_import_settings_string() const { return String(); } }; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 7471ab4241..b5c598e860 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -33,34 +33,22 @@ #include "core/io/resource_importer.h" #include "core/os/file_access.h" #include "core/os/os.h" -#include "core/path_remap.h" #include "core/print_string.h" #include "core/project_settings.h" #include "core/translation.h" #include "core/variant_parser.h" +#ifdef DEBUG_LOAD_THREADED +#define print_lt(m_text) print_line(m_text) +#else +#define print_lt(m_text) +#endif + Ref<ResourceFormatLoader> ResourceLoader::loader[ResourceLoader::MAX_LOADERS]; int ResourceLoader::loader_count = 0; -Error ResourceInteractiveLoader::wait() { - - Error err = poll(); - while (err == OK) { - err = poll(); - } - - return err; -} - -ResourceInteractiveLoader::~ResourceInteractiveLoader() { - if (path_loading != String()) { - ResourceLoader::_remove_from_loading_map_and_thread(path_loading, path_loading_thread); - } -} - bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_for_type) const { - String extension = p_path.get_extension(); List<String> extensions; @@ -71,16 +59,15 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_ } for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - - if (E->get().nocasecmp_to(extension) == 0) + if (E->get().nocasecmp_to(extension) == 0) { return true; + } } return false; } bool ResourceFormatLoader::handles_type(const String &p_type) const { - if (get_script_instance() && get_script_instance()->has_method("handles_type")) { // I guess custom loaders for custom resources should use "Resource" return get_script_instance()->call("handles_type", p_type); @@ -90,7 +77,6 @@ bool ResourceFormatLoader::handles_type(const String &p_type) const { } String ResourceFormatLoader::get_resource_type(const String &p_path) const { - if (get_script_instance() && get_script_instance()->has_method("get_resource_type")) { return get_script_instance()->call("get_resource_type", p_path); } @@ -99,68 +85,27 @@ String ResourceFormatLoader::get_resource_type(const String &p_path) const { } void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { - - if (p_type == "" || handles_type(p_type)) + if (p_type == "" || handles_type(p_type)) { get_recognized_extensions(p_extensions); + } } void ResourceLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) { - for (int i = 0; i < loader_count; i++) { loader[i]->get_recognized_extensions_for_type(p_type, p_extensions); } } -void ResourceInteractiveLoader::_bind_methods() { - - ClassDB::bind_method(D_METHOD("get_resource"), &ResourceInteractiveLoader::get_resource); - ClassDB::bind_method(D_METHOD("poll"), &ResourceInteractiveLoader::poll); - ClassDB::bind_method(D_METHOD("wait"), &ResourceInteractiveLoader::wait); - ClassDB::bind_method(D_METHOD("get_stage"), &ResourceInteractiveLoader::get_stage); - ClassDB::bind_method(D_METHOD("get_stage_count"), &ResourceInteractiveLoader::get_stage_count); -} - -class ResourceInteractiveLoaderDefault : public ResourceInteractiveLoader { - - GDCLASS(ResourceInteractiveLoaderDefault, ResourceInteractiveLoader); - -public: - Ref<Resource> resource; - - virtual void set_local_path(const String &p_local_path) { /*scene->set_filename(p_local_path);*/ - } - virtual Ref<Resource> get_resource() { return resource; } - virtual Error poll() { return ERR_FILE_EOF; } - virtual int get_stage() const { return 1; } - virtual int get_stage_count() const { return 1; } - virtual void set_translation_remapped(bool p_remapped) { resource->set_as_translation_remapped(p_remapped); } - - ResourceInteractiveLoaderDefault() {} -}; - -Ref<ResourceInteractiveLoader> ResourceFormatLoader::load_interactive(const String &p_path, const String &p_original_path, Error *r_error) { - - //either this - Ref<Resource> res = load(p_path, p_original_path, r_error); - if (res.is_null()) - return Ref<ResourceInteractiveLoader>(); - - Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>(memnew(ResourceInteractiveLoaderDefault)); - ril->resource = res; - return ril; -} - bool ResourceFormatLoader::exists(const String &p_path) const { return FileAccess::exists(p_path); //by default just check file } void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions) const { - if (get_script_instance() && get_script_instance()->has_method("get_recognized_extensions")) { - PoolStringArray exts = get_script_instance()->call("get_recognized_extensions"); + PackedStringArray exts = get_script_instance()->call("get_recognized_extensions"); { - PoolStringArray::Read r = exts.read(); + const String *r = exts.ptr(); for (int i = 0; i < exts.size(); ++i) { p_extensions->push_back(r[i]); } @@ -168,54 +113,34 @@ void ResourceFormatLoader::get_recognized_extensions(List<String> *p_extensions) } } -RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error) { - +RES ResourceFormatLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { if (get_script_instance() && get_script_instance()->has_method("load")) { - Variant res = get_script_instance()->call("load", p_path, p_original_path); + Variant res = get_script_instance()->call("load", p_path, p_original_path, p_use_sub_threads); if (res.get_type() == Variant::INT) { - - if (r_error) + if (r_error) { *r_error = (Error)res.operator int64_t(); + } } else { - - if (r_error) + if (r_error) { *r_error = OK; + } return res; } - } - - //or this must be implemented - Ref<ResourceInteractiveLoader> ril = load_interactive(p_path, p_original_path, r_error); - if (!ril.is_valid()) - return RES(); - ril->set_local_path(p_original_path); - - while (true) { - - Error err = ril->poll(); - if (err == ERR_FILE_EOF) { - if (r_error) - *r_error = OK; - return ril->get_resource(); - } - - if (r_error) - *r_error = err; - - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Failed to load resource '" + p_path + "'."); + return res; } + + ERR_FAIL_V_MSG(RES(), "Failed to load resource '" + p_path + "', ResourceFormatLoader::load was not implemented for this resource type."); } void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - if (get_script_instance() && get_script_instance()->has_method("get_dependencies")) { - PoolStringArray deps = get_script_instance()->call("get_dependencies", p_path, p_add_types); + PackedStringArray deps = get_script_instance()->call("get_dependencies", p_path, p_add_types); { - PoolStringArray::Read r = deps.read(); + const String *r = deps.ptr(); for (int i = 0; i < deps.size(); ++i) { p_dependencies->push_back(r[i]); } @@ -224,9 +149,7 @@ void ResourceFormatLoader::get_dependencies(const String &p_path, List<String> * } Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - if (get_script_instance() && get_script_instance()->has_method("rename_dependencies")) { - Dictionary deps_dict; for (Map<String, String>::Element *E = p_map.front(); E; E = E->next()) { deps_dict[E->key()] = E->value(); @@ -240,15 +163,14 @@ Error ResourceFormatLoader::rename_dependencies(const String &p_path, const Map< } void ResourceFormatLoader::_bind_methods() { - { MethodInfo info = MethodInfo(Variant::NIL, "load", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "original_path")); info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; ClassDB::add_virtual_method(get_class_static(), info); } - ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::POOL_STRING_ARRAY, "get_recognized_extensions")); - ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "handles_type", PropertyInfo(Variant::STRING, "typename"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::PACKED_STRING_ARRAY, "get_recognized_extensions")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "handles_type", PropertyInfo(Variant::STRING_NAME, "typename"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_resource_type", PropertyInfo(Variant::STRING, "path"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo("get_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "add_types"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "rename_dependencies", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::STRING, "renames"))); @@ -256,18 +178,16 @@ void ResourceFormatLoader::_bind_methods() { /////////////////////////////////// -RES ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { - +RES ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error, bool p_use_sub_threads, float *r_progress) { bool found = false; // Try all loaders and pick the first match for the type hint for (int i = 0; i < loader_count; i++) { - if (!loader[i]->recognize_path(p_path, p_type_hint)) { continue; } found = true; - RES res = loader[i]->load(p_path, p_original_path != String() ? p_original_path : p_path, r_error); + RES res = loader[i]->load(p_path, p_original_path != String() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_no_cache); if (res.is_null()) { continue; } @@ -275,7 +195,8 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c return res; } - ERR_FAIL_COND_V_MSG(found, RES(), "Failed loading resource: " + p_path + "."); + ERR_FAIL_COND_V_MSG(found, RES(), + vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path)); #ifdef TOOLS_ENABLED FileAccessRef file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES); @@ -285,164 +206,424 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c ERR_FAIL_V_MSG(RES(), "No loader found for resource: " + p_path + "."); } -bool ResourceLoader::_add_to_loading_map(const String &p_path) { +void ResourceLoader::_thread_load_function(void *p_userdata) { + ThreadLoadTask &load_task = *(ThreadLoadTask *)p_userdata; + load_task.loader_id = Thread::get_caller_id(); - bool success; - if (loading_map_mutex) { - loading_map_mutex->lock(); + if (load_task.semaphore) { + //this is an actual thread, so wait for Ok fom semaphore + thread_load_semaphore->wait(); //wait until its ok to start loading } + load_task.resource = _load(load_task.remapped_path, load_task.remapped_path != load_task.local_path ? load_task.local_path : String(), load_task.type_hint, false, &load_task.error, load_task.use_sub_threads, &load_task.progress); - LoadingMapKey key; - key.path = p_path; - key.thread = Thread::get_caller_id(); + load_task.progress = 1.0; //it was fully loaded at this point, so force progress to 1.0 - if (loading_map.has(key)) { - success = false; + thread_load_mutex->lock(); + if (load_task.error != OK) { + load_task.status = THREAD_LOAD_FAILED; } else { - loading_map[key] = true; - success = true; + load_task.status = THREAD_LOAD_LOADED; } + if (load_task.semaphore) { + if (load_task.start_next && thread_waiting_count > 0) { + thread_waiting_count--; + //thread loading count remains constant, this ends but another one begins + thread_load_semaphore->post(); + } else { + thread_loading_count--; //no threads waiting, just reduce loading count + } - if (loading_map_mutex) { - loading_map_mutex->unlock(); + print_lt("END: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count)); + + for (int i = 0; i < load_task.poll_requests; i++) { + load_task.semaphore->post(); + } + memdelete(load_task.semaphore); + load_task.semaphore = nullptr; + } + + if (load_task.resource.is_valid()) { + load_task.resource->set_path(load_task.local_path); + + if (load_task.xl_remapped) { + load_task.resource->set_as_translation_remapped(true); + } + +#ifdef TOOLS_ENABLED + + load_task.resource->set_edited(false); + if (timestamp_on_load) { + uint64_t mt = FileAccess::get_modified_time(load_task.remapped_path); + //printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt); + load_task.resource->set_last_modified_time(mt); + } +#endif + + if (_loaded_callback) { + _loaded_callback(load_task.resource, load_task.local_path); + } } - return success; + thread_load_mutex->unlock(); } -void ResourceLoader::_remove_from_loading_map(const String &p_path) { - if (loading_map_mutex) { - loading_map_mutex->lock(); +Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, const String &p_source_resource) { + String local_path; + if (p_path.is_rel_path()) { + local_path = "res://" + p_path; + } else { + local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } + + thread_load_mutex->lock(); + + if (p_source_resource != String()) { + //must be loading from this resource + if (!thread_load_tasks.has(p_source_resource)) { + thread_load_mutex->unlock(); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "There is no thread loading source resource '" + p_source_resource + "'."); + } + //must be loading from this thread + if (thread_load_tasks[p_source_resource].loader_id != Thread::get_caller_id()) { + thread_load_mutex->unlock(); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Threading loading resource'" + local_path + " failed: Source specified: '" + p_source_resource + "' but was not called by it."); + } + + //must not be already added as s sub tasks + if (thread_load_tasks[p_source_resource].sub_tasks.has(local_path)) { + thread_load_mutex->unlock(); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Thread loading source resource '" + p_source_resource + "' already is loading '" + local_path + "'."); + } } - LoadingMapKey key; - key.path = p_path; - key.thread = Thread::get_caller_id(); + if (thread_load_tasks.has(local_path)) { + thread_load_tasks[local_path].requests++; + if (p_source_resource != String()) { + thread_load_tasks[p_source_resource].sub_tasks.insert(local_path); + } + thread_load_mutex->unlock(); + return OK; + } + + { + //create load task + + ThreadLoadTask load_task; + + load_task.requests = 1; + load_task.remapped_path = _path_remap(local_path, &load_task.xl_remapped); + load_task.local_path = local_path; + load_task.type_hint = p_type_hint; + load_task.use_sub_threads = p_use_sub_threads; + + { //must check if resource is already loaded before attempting to load it in a thread + + if (load_task.loader_id == Thread::get_caller_id()) { + thread_load_mutex->unlock(); + ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Attempted to load a resource already being loaded from this thread, cyclic reference?"); + } + //lock first if possible + if (ResourceCache::lock) { + ResourceCache::lock->read_lock(); + } + + //get ptr + Resource **rptr = ResourceCache::resources.getptr(local_path); + + if (rptr) { + RES res(*rptr); + //it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached + if (res.is_valid()) { + //referencing is fine + load_task.resource = res; + load_task.status = THREAD_LOAD_LOADED; + load_task.progress = 1.0; + } + } + if (ResourceCache::lock) { + ResourceCache::lock->read_unlock(); + } + } + + if (p_source_resource != String()) { + thread_load_tasks[p_source_resource].sub_tasks.insert(local_path); + } + + thread_load_tasks[local_path] = load_task; + } + + ThreadLoadTask &load_task = thread_load_tasks[local_path]; + + if (load_task.resource.is_null()) { //needs to be loaded in thread + + load_task.semaphore = memnew(Semaphore); + if (thread_loading_count < thread_load_max) { + thread_loading_count++; + thread_load_semaphore->post(); //we have free threads, so allow one + } else { + thread_waiting_count++; + } + + print_lt("REQUEST: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count)); + + load_task.thread = Thread::create(_thread_load_function, &thread_load_tasks[local_path]); + load_task.loader_id = load_task.thread->get_id(); + } + + thread_load_mutex->unlock(); + + return OK; +} + +float ResourceLoader::_dependency_get_progress(const String &p_path) { + if (thread_load_tasks.has(p_path)) { + ThreadLoadTask &load_task = thread_load_tasks[p_path]; + int dep_count = load_task.sub_tasks.size(); + if (dep_count > 0) { + float dep_progress = 0; + for (Set<String>::Element *E = load_task.sub_tasks.front(); E; E = E->next()) { + dep_progress += _dependency_get_progress(E->get()); + } + dep_progress /= float(dep_count); + dep_progress *= 0.5; + dep_progress += load_task.progress * 0.5; + return dep_progress; + } else { + return load_task.progress; + } - loading_map.erase(key); + } else { + return 1.0; //assume finished loading it so it no longer exists + } +} - if (loading_map_mutex) { - loading_map_mutex->unlock(); +ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, float *r_progress) { + String local_path; + if (p_path.is_rel_path()) { + local_path = "res://" + p_path; + } else { + local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } + + thread_load_mutex->lock(); + if (!thread_load_tasks.has(local_path)) { + thread_load_mutex->unlock(); + return THREAD_LOAD_INVALID_RESOURCE; } + ThreadLoadTask &load_task = thread_load_tasks[local_path]; + ThreadLoadStatus status; + status = load_task.status; + if (r_progress) { + *r_progress = _dependency_get_progress(local_path); + } + + thread_load_mutex->unlock(); + + return status; } -void ResourceLoader::_remove_from_loading_map_and_thread(const String &p_path, Thread::ID p_thread) { - if (loading_map_mutex) { - loading_map_mutex->lock(); +RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) { + String local_path; + if (p_path.is_rel_path()) { + local_path = "res://" + p_path; + } else { + local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } + + thread_load_mutex->lock(); + if (!thread_load_tasks.has(local_path)) { + thread_load_mutex->unlock(); + if (r_error) { + *r_error = ERR_INVALID_PARAMETER; + } + return RES(); } - LoadingMapKey key; - key.path = p_path; - key.thread = p_thread; + ThreadLoadTask &load_task = thread_load_tasks[local_path]; + + //semaphore still exists, meaning its still loading, request poll + Semaphore *semaphore = load_task.semaphore; + if (semaphore) { + load_task.poll_requests++; + + { + // As we got a semaphore, this means we are going to have to wait + // until the sub-resource is done loading + // + // As this thread will become 'blocked' we should "echange" its + // active status with a waiting one, to ensure load continues. + // + // This ensures loading is never blocked and that is also within + // the maximum number of active threads. + + if (thread_waiting_count > 0) { + thread_waiting_count--; + thread_loading_count++; + thread_load_semaphore->post(); + + load_task.start_next = false; //do not start next since we are doing it here + } + + thread_suspended_count++; + + print_lt("GET: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count)); + } - loading_map.erase(key); + thread_load_mutex->unlock(); + semaphore->wait(); + thread_load_mutex->lock(); - if (loading_map_mutex) { - loading_map_mutex->unlock(); + thread_suspended_count--; + + if (!thread_load_tasks.has(local_path)) { //may have been erased during unlock and this was always an invalid call + thread_load_mutex->unlock(); + if (r_error) { + *r_error = ERR_INVALID_PARAMETER; + } + return RES(); + } + } + + RES resource = load_task.resource; + if (r_error) { + *r_error = load_task.error; } + + load_task.requests--; + + if (load_task.requests == 0) { + if (load_task.thread) { //thread may not have been used + Thread::wait_to_finish(load_task.thread); + memdelete(load_task.thread); + } + thread_load_tasks.erase(local_path); + } + + thread_load_mutex->unlock(); + + return resource; } RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { - - if (r_error) + if (r_error) { *r_error = ERR_CANT_OPEN; + } String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } if (!p_no_cache) { + thread_load_mutex->lock(); + + //Is it already being loaded? poll until done + if (thread_load_tasks.has(local_path)) { + Error err = load_threaded_request(p_path, p_type_hint); + if (err != OK) { + if (r_error) { + *r_error = err; + } + thread_load_mutex->unlock(); + return RES(); + } + thread_load_mutex->unlock(); - { - bool success = _add_to_loading_map(local_path); - ERR_FAIL_COND_V_MSG(!success, RES(), "Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); + return load_threaded_get(p_path, r_error); } - //lock first if possible + //Is it cached? if (ResourceCache::lock) { ResourceCache::lock->read_lock(); } - //get ptr Resource **rptr = ResourceCache::resources.getptr(local_path); if (rptr) { RES res(*rptr); + //it is possible this resource was just freed in a thread. If so, this referencing will not work and resource is considered not cached if (res.is_valid()) { - //referencing is fine - if (r_error) - *r_error = OK; if (ResourceCache::lock) { ResourceCache::lock->read_unlock(); } - _remove_from_loading_map(local_path); - return res; + thread_load_mutex->unlock(); + + if (r_error) { + *r_error = OK; + } + + return res; //use cached } } + if (ResourceCache::lock) { ResourceCache::lock->read_unlock(); } - } - bool xl_remapped = false; - String path = _path_remap(local_path, &xl_remapped); + //load using task (but this thread) + ThreadLoadTask load_task; + + load_task.requests = 1; + load_task.local_path = local_path; + load_task.remapped_path = _path_remap(local_path, &load_task.xl_remapped); + load_task.type_hint = p_type_hint; + load_task.loader_id = Thread::get_caller_id(); + + thread_load_tasks[local_path] = load_task; + + thread_load_mutex->unlock(); + + _thread_load_function(&thread_load_tasks[local_path]); + + return load_threaded_get(p_path, r_error); + + } else { + bool xl_remapped = false; + String path = _path_remap(local_path, &xl_remapped); - if (path == "") { - if (!p_no_cache) { - _remove_from_loading_map(local_path); + if (path == "") { + ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed."); } - ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed."); - } - print_verbose("Loading resource: " + path); - RES res = _load(path, local_path, p_type_hint, p_no_cache, r_error); + print_verbose("Loading resource: " + path); + float p; + RES res = _load(path, local_path, p_type_hint, p_no_cache, r_error, false, &p); - if (res.is_null()) { - if (!p_no_cache) { - _remove_from_loading_map(local_path); + if (res.is_null()) { + print_verbose("Failed loading resource: " + path); + return RES(); } - return RES(); - } - if (!p_no_cache) - res->set_path(local_path); - if (xl_remapped) - res->set_as_translation_remapped(true); + if (xl_remapped) { + res->set_as_translation_remapped(true); + } #ifdef TOOLS_ENABLED - res->set_edited(false); - if (timestamp_on_load) { - uint64_t mt = FileAccess::get_modified_time(path); - //printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt); - res->set_last_modified_time(mt); - } + res->set_edited(false); + if (timestamp_on_load) { + uint64_t mt = FileAccess::get_modified_time(path); + //printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt); + res->set_last_modified_time(mt); + } #endif - if (!p_no_cache) { - _remove_from_loading_map(local_path); - } - - if (_loaded_callback) { - _loaded_callback(res, p_path); + return res; } - - return res; } bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { - String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } if (ResourceCache::has(local_path)) { - return true; // If cached, it probably exists } @@ -451,90 +632,19 @@ bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { // Try all loaders and pick the first match for the type hint for (int i = 0; i < loader_count; i++) { - if (!loader[i]->recognize_path(path, p_type_hint)) { continue; } - if (loader[i]->exists(path)) + if (loader[i]->exists(path)) { return true; - } - - return false; -} - -Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) { - - if (r_error) - *r_error = ERR_CANT_OPEN; - - String local_path; - if (p_path.is_rel_path()) - local_path = "res://" + p_path; - else - local_path = ProjectSettings::get_singleton()->localize_path(p_path); - - if (!p_no_cache) { - - bool success = _add_to_loading_map(local_path); - ERR_FAIL_COND_V_MSG(!success, RES(), "Resource: '" + local_path + "' is already being loaded. Cyclic reference?"); - - if (ResourceCache::has(local_path)) { - - print_verbose("Loading resource: " + local_path + " (cached)"); - Ref<Resource> res_cached = ResourceCache::get(local_path); - Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>(memnew(ResourceInteractiveLoaderDefault)); - - ril->resource = res_cached; - ril->path_loading = local_path; - ril->path_loading_thread = Thread::get_caller_id(); - return ril; - } - } - - bool xl_remapped = false; - String path = _path_remap(local_path, &xl_remapped); - if (path == "") { - if (!p_no_cache) { - _remove_from_loading_map(local_path); } - ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed."); } - print_verbose("Loading resource: " + path); - - bool found = false; - for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(path, p_type_hint)) - continue; - found = true; - Ref<ResourceInteractiveLoader> ril = loader[i]->load_interactive(path, local_path, r_error); - if (ril.is_null()) - continue; - if (!p_no_cache) { - ril->set_local_path(local_path); - ril->path_loading = local_path; - ril->path_loading_thread = Thread::get_caller_id(); - } - - if (xl_remapped) - ril->set_translation_remapped(true); - - return ril; - } - - if (!p_no_cache) { - _remove_from_loading_map(local_path); - } - - ERR_FAIL_COND_V_MSG(found, Ref<ResourceInteractiveLoader>(), "Failed loading resource: " + path + "."); - - ERR_FAIL_V_MSG(Ref<ResourceInteractiveLoader>(), "No loader found for resource: " + path + "."); + return false; } void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front) { - ERR_FAIL_COND(p_format_loader.is_null()); ERR_FAIL_COND(loader_count >= MAX_LOADERS); @@ -550,14 +660,14 @@ void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> p_form } void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader) { - ERR_FAIL_COND(p_format_loader.is_null()); // Find loader int i = 0; for (; i < loader_count; ++i) { - if (loader[i] == p_format_loader) + if (loader[i] == p_format_loader) { break; + } } ERR_FAIL_COND(i >= loader_count); // Not found @@ -571,19 +681,19 @@ void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_f } int ResourceLoader::get_import_order(const String &p_path) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -599,15 +709,16 @@ String ResourceLoader::get_import_group_file(const String &p_path) { String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -620,19 +731,19 @@ String ResourceLoader::get_import_group_file(const String &p_path) { } bool ResourceLoader::is_import_valid(const String &p_path) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -645,19 +756,19 @@ bool ResourceLoader::is_import_valid(const String &p_path) { } bool ResourceLoader::is_imported(const String &p_path) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -670,19 +781,19 @@ bool ResourceLoader::is_imported(const String &p_path) { } void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -693,19 +804,19 @@ void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_depe } Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - String path = _path_remap(p_path); String local_path; - if (path.is_rel_path()) + if (path.is_rel_path()) { local_path = "res://" + path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(path); + } for (int i = 0; i < loader_count; i++) { - - if (!loader[i]->recognize_path(local_path)) + if (!loader[i]->recognize_path(local_path)) { continue; + } /* if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) continue; @@ -718,30 +829,29 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String } String ResourceLoader::get_resource_type(const String &p_path) { - String local_path; - if (p_path.is_rel_path()) + if (p_path.is_rel_path()) { local_path = "res://" + p_path; - else + } else { local_path = ProjectSettings::get_singleton()->localize_path(p_path); + } for (int i = 0; i < loader_count; i++) { - String result = loader[i]->get_resource_type(local_path); - if (result != "") + if (result != "") { return result; + } } return ""; } String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_remapped) { - String new_path = p_path; if (translation_remaps.has(p_path)) { // translation_remaps has the following format: - // { "res://path.png": PoolStringArray( "res://path-ru.png:ru", "res://path-de.png:de" ) } + // { "res://path.png": PackedStringArray( "res://path-ru.png:ru", "res://path-de.png:de" ) } // To find the path of the remapped resource, we extract the locale name after // the last ':' to match the project locale. @@ -756,7 +866,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem bool near_match = false; for (int i = 0; i < res_remaps.size(); i++) { - int split = res_remaps[i].find_last(":"); + int split = res_remaps[i].rfind(":"); if (split == -1) { continue; } @@ -795,7 +905,6 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem FileAccess *f = FileAccess::open(p_path + ".remap", FileAccess::READ, &err); if (f) { - VariantParser::StreamFile stream; stream.f = f; @@ -806,16 +915,15 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem int lines = 0; String error_text; while (true) { - assign = Variant(); next_tag.fields.clear(); next_tag.name = String(); - err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true); + err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, nullptr, true); if (err == ERR_FILE_EOF) { break; } else if (err != OK) { - ERR_PRINTS("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); + ERR_PRINT("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + "."); break; } @@ -835,9 +943,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem } String ResourceLoader::import_remap(const String &p_path) { - if (ResourceFormatImporter::get_singleton()->recognize_path(p_path)) { - return ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path); } @@ -849,7 +955,6 @@ String ResourceLoader::path_remap(const String &p_path) { } void ResourceLoader::reload_translation_remaps() { - if (ResourceCache::lock) { ResourceCache::lock->read_lock(); } @@ -874,15 +979,14 @@ void ResourceLoader::reload_translation_remaps() { } void ResourceLoader::load_translation_remaps() { - - if (!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) + if (!ProjectSettings::get_singleton()->has_setting("locale/translation_remaps")) { return; + } Dictionary remaps = ProjectSettings::get_singleton()->get("locale/translation_remaps"); List<Variant> keys; remaps.get_key_list(&keys); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - Array langs = remaps[E->get()]; Vector<String> lang_remaps; lang_remaps.resize(langs.size()); @@ -896,26 +1000,27 @@ void ResourceLoader::load_translation_remaps() { void ResourceLoader::clear_translation_remaps() { translation_remaps.clear(); + while (remapped_list.first() != nullptr) { + remapped_list.remove(remapped_list.first()); + } } void ResourceLoader::load_path_remaps() { - - if (!ProjectSettings::get_singleton()->has_setting("path_remap/remapped_paths")) + if (!ProjectSettings::get_singleton()->has_setting("path_remap/remapped_paths")) { return; + } - PoolVector<String> remaps = ProjectSettings::get_singleton()->get("path_remap/remapped_paths"); + Vector<String> remaps = ProjectSettings::get_singleton()->get("path_remap/remapped_paths"); int rc = remaps.size(); ERR_FAIL_COND(rc & 1); //must be even - PoolVector<String>::Read r = remaps.read(); + const String *r = remaps.ptr(); for (int i = 0; i < rc; i += 2) { - path_remaps[r[i]] = r[i + 1]; } } void ResourceLoader::clear_path_remaps() { - path_remaps.clear(); } @@ -923,7 +1028,7 @@ void ResourceLoader::set_load_callback(ResourceLoadedCallback p_callback) { _loaded_callback = p_callback; } -ResourceLoadedCallback ResourceLoader::_loaded_callback = NULL; +ResourceLoadedCallback ResourceLoader::_loaded_callback = nullptr; Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(String path) { for (int i = 0; i < loader_count; ++i) { @@ -935,9 +1040,9 @@ Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(St } bool ResourceLoader::add_custom_resource_format_loader(String script_path) { - - if (_find_custom_resource_format_loader(script_path).is_valid()) + if (_find_custom_resource_format_loader(script_path).is_valid()) { return false; + } Ref<Resource> res = ResourceLoader::load(script_path); ERR_FAIL_COND_V(res.is_null(), false); @@ -950,20 +1055,20 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { Object *obj = ClassDB::instance(ibt); - ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); + ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); ResourceFormatLoader *crl = Object::cast_to<ResourceFormatLoader>(obj); - crl->set_script(s.get_ref_ptr()); + crl->set_script(s); ResourceLoader::add_resource_format_loader(crl); return true; } void ResourceLoader::remove_custom_resource_format_loader(String script_path) { - Ref<ResourceFormatLoader> custom_loader = _find_custom_resource_format_loader(script_path); - if (custom_loader.is_valid()) + if (custom_loader.is_valid()) { remove_resource_format_loader(custom_loader); + } } void ResourceLoader::add_custom_loaders() { @@ -975,7 +1080,6 @@ void ResourceLoader::add_custom_loaders() { ScriptServer::get_global_class_list(&global_classes); for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { - StringName class_name = E->get(); StringName base_class = ScriptServer::get_global_class_native_base(class_name); @@ -987,8 +1091,7 @@ void ResourceLoader::add_custom_loaders() { } void ResourceLoader::remove_custom_loaders() { - - Vector<Ref<ResourceFormatLoader> > custom_loaders; + Vector<Ref<ResourceFormatLoader>> custom_loaders; for (int i = 0; i < loader_count; ++i) { if (loader[i]->get_script_instance()) { custom_loaders.push_back(loader[i]); @@ -1000,38 +1103,40 @@ void ResourceLoader::remove_custom_loaders() { } } -Mutex *ResourceLoader::loading_map_mutex = NULL; -HashMap<ResourceLoader::LoadingMapKey, int, ResourceLoader::LoadingMapKeyHasher> ResourceLoader::loading_map; - void ResourceLoader::initialize() { -#ifndef NO_THREADS - loading_map_mutex = Mutex::create(); -#endif + thread_load_mutex = memnew(Mutex); + thread_load_max = OS::get_singleton()->get_processor_count(); + thread_loading_count = 0; + thread_waiting_count = 0; + thread_suspended_count = 0; + thread_load_semaphore = memnew(Semaphore); } void ResourceLoader::finalize() { -#ifndef NO_THREADS - const LoadingMapKey *K = NULL; - while ((K = loading_map.next(K))) { - ERR_PRINTS("Exited while resource is being loaded: " + K->path); - } - loading_map.clear(); - memdelete(loading_map_mutex); - loading_map_mutex = NULL; -#endif + memdelete(thread_load_mutex); + memdelete(thread_load_semaphore); } -ResourceLoadErrorNotify ResourceLoader::err_notify = NULL; -void *ResourceLoader::err_notify_ud = NULL; +ResourceLoadErrorNotify ResourceLoader::err_notify = nullptr; +void *ResourceLoader::err_notify_ud = nullptr; -DependencyErrorNotify ResourceLoader::dep_err_notify = NULL; -void *ResourceLoader::dep_err_notify_ud = NULL; +DependencyErrorNotify ResourceLoader::dep_err_notify = nullptr; +void *ResourceLoader::dep_err_notify_ud = nullptr; bool ResourceLoader::abort_on_missing_resource = true; bool ResourceLoader::timestamp_on_load = false; +Mutex *ResourceLoader::thread_load_mutex = nullptr; +HashMap<String, ResourceLoader::ThreadLoadTask> ResourceLoader::thread_load_tasks; +Semaphore *ResourceLoader::thread_load_semaphore = nullptr; + +int ResourceLoader::thread_loading_count = 0; +int ResourceLoader::thread_waiting_count = 0; +int ResourceLoader::thread_suspended_count = 0; +int ResourceLoader::thread_load_max = 0; + SelfList<Resource>::List ResourceLoader::remapped_list; -HashMap<String, Vector<String> > ResourceLoader::translation_remaps; +HashMap<String, Vector<String>> ResourceLoader::translation_remaps; HashMap<String, String> ResourceLoader::path_remaps; -ResourceLoaderImport ResourceLoader::import = NULL; +ResourceLoaderImport ResourceLoader::import = nullptr; diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 4e83427fae..9322b5273a 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -31,42 +31,18 @@ #ifndef RESOURCE_LOADER_H #define RESOURCE_LOADER_H +#include "core/os/semaphore.h" #include "core/os/thread.h" #include "core/resource.h" -class ResourceInteractiveLoader : public Reference { - - GDCLASS(ResourceInteractiveLoader, Reference); - friend class ResourceLoader; - String path_loading; - Thread::ID path_loading_thread; - -protected: - static void _bind_methods(); - -public: - virtual void set_local_path(const String &p_local_path) = 0; - virtual Ref<Resource> get_resource() = 0; - virtual Error poll() = 0; - virtual int get_stage() const = 0; - virtual int get_stage_count() const = 0; - virtual void set_translation_remapped(bool p_remapped) = 0; - virtual Error wait(); - - ResourceInteractiveLoader() {} - ~ResourceInteractiveLoader(); -}; - class ResourceFormatLoader : public Reference { - GDCLASS(ResourceFormatLoader, Reference); protected: static void _bind_methods(); public: - virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); virtual bool exists(const String &p_path) const; virtual void get_recognized_extensions(List<String> *p_extensions) const; virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; @@ -90,11 +66,19 @@ typedef Error (*ResourceLoaderImport)(const String &p_path); typedef void (*ResourceLoadedCallback)(RES p_resource, const String &p_path); class ResourceLoader { - enum { MAX_LOADERS = 64 }; +public: + enum ThreadLoadStatus { + THREAD_LOAD_INVALID_RESOURCE, + THREAD_LOAD_IN_PROGRESS, + THREAD_LOAD_FAILED, + THREAD_LOAD_LOADED + }; + +private: static Ref<ResourceFormatLoader> loader[MAX_LOADERS]; static int loader_count; static bool timestamp_on_load; @@ -104,10 +88,10 @@ class ResourceLoader { static void *dep_err_notify_ud; static DependencyErrorNotify dep_err_notify; static bool abort_on_missing_resource; - static HashMap<String, Vector<String> > translation_remaps; + static HashMap<String, Vector<String>> translation_remaps; static HashMap<String, String> path_remaps; - static String _path_remap(const String &p_path, bool *r_translation_remapped = NULL); + static String _path_remap(const String &p_path, bool *r_translation_remapped = nullptr); friend class Resource; static SelfList<Resource>::List remapped_list; @@ -115,35 +99,48 @@ class ResourceLoader { friend class ResourceFormatImporter; friend class ResourceInteractiveLoader; //internal load function - static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error); + static RES _load(const String &p_path, const String &p_original_path, const String &p_type_hint, bool p_no_cache, Error *r_error, bool p_use_sub_threads, float *r_progress); static ResourceLoadedCallback _loaded_callback; static Ref<ResourceFormatLoader> _find_custom_resource_format_loader(String path); - static Mutex *loading_map_mutex; - - //used to track paths being loaded in a thread, avoids cyclic recursion - struct LoadingMapKey { - String path; - Thread::ID thread; - bool operator==(const LoadingMapKey &p_key) const { - return (thread == p_key.thread && path == p_key.path); - } - }; - struct LoadingMapKeyHasher { - static _FORCE_INLINE_ uint32_t hash(const LoadingMapKey &p_key) { return p_key.path.hash() + HashMapHasherDefault::hash(p_key.thread); } + struct ThreadLoadTask { + Thread *thread = nullptr; + Thread::ID loader_id = 0; + Semaphore *semaphore = nullptr; + String local_path; + String remapped_path; + String type_hint; + float progress = 0.0; + ThreadLoadStatus status = THREAD_LOAD_IN_PROGRESS; + Error error = OK; + RES resource; + bool xl_remapped = false; + bool use_sub_threads = false; + bool start_next = true; + int requests = 0; + int poll_requests = 0; + Set<String> sub_tasks; }; - static HashMap<LoadingMapKey, int, LoadingMapKeyHasher> loading_map; + static void _thread_load_function(void *p_userdata); + static Mutex *thread_load_mutex; + static HashMap<String, ThreadLoadTask> thread_load_tasks; + static Semaphore *thread_load_semaphore; + static int thread_waiting_count; + static int thread_loading_count; + static int thread_suspended_count; + static int thread_load_max; - static bool _add_to_loading_map(const String &p_path); - static void _remove_from_loading_map(const String &p_path); - static void _remove_from_loading_map_and_thread(const String &p_path, Thread::ID p_thread); + static float _dependency_get_progress(const String &p_path); public: - static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); - static RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL); + static Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, const String &p_source_resource = String()); + static ThreadLoadStatus load_threaded_get_status(const String &p_path, float *r_progress = nullptr); + static RES load_threaded_get(const String &p_path, Error *r_error = nullptr); + + static RES load(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = nullptr); static bool exists(const String &p_path, const String &p_type_hint = ""); static void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions); @@ -161,7 +158,9 @@ public: static bool get_timestamp_on_load() { return timestamp_on_load; } static void notify_load_error(const String &p_err) { - if (err_notify) err_notify(err_notify_ud, p_err); + if (err_notify) { + err_notify(err_notify_ud, p_err); + } } static void set_error_notify_func(void *p_ud, ResourceLoadErrorNotify p_err_notify) { err_notify = p_err_notify; @@ -169,7 +168,9 @@ public: } static void notify_dependency_error(const String &p_path, const String &p_dependency, const String &p_type) { - if (dep_err_notify) dep_err_notify(dep_err_notify_ud, p_path, p_dependency, p_type); + if (dep_err_notify) { + dep_err_notify(dep_err_notify_ud, p_path, p_dependency, p_type); + } } static void set_dependency_error_notify_func(void *p_ud, DependencyErrorNotify p_err_notify) { dep_err_notify = p_err_notify; @@ -201,4 +202,4 @@ public: static void finalize(); }; -#endif +#endif // RESOURCE_LOADER_H diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index b468685e4d..a8da215b61 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -38,10 +38,9 @@ Ref<ResourceFormatSaver> ResourceSaver::saver[MAX_SAVERS]; int ResourceSaver::saver_count = 0; bool ResourceSaver::timestamp_on_save = false; -ResourceSavedCallback ResourceSaver::save_callback = 0; +ResourceSavedCallback ResourceSaver::save_callback = nullptr; Error ResourceFormatSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - if (get_script_instance() && get_script_instance()->has_method("save")) { return (Error)get_script_instance()->call("save", p_path, p_resource, p_flags).operator int64_t(); } @@ -50,7 +49,6 @@ Error ResourceFormatSaver::save(const String &p_path, const RES &p_resource, uin } bool ResourceFormatSaver::recognize(const RES &p_resource) const { - if (get_script_instance() && get_script_instance()->has_method("recognize")) { return get_script_instance()->call("recognize", p_resource); } @@ -59,12 +57,11 @@ bool ResourceFormatSaver::recognize(const RES &p_resource) const { } void ResourceFormatSaver::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { - if (get_script_instance() && get_script_instance()->has_method("get_recognized_extensions")) { - PoolStringArray exts = get_script_instance()->call("get_recognized_extensions", p_resource); + PackedStringArray exts = get_script_instance()->call("get_recognized_extensions", p_resource); { - PoolStringArray::Read r = exts.read(); + const String *r = exts.ptr(); for (int i = 0; i < exts.size(); ++i) { p_extensions->push_back(r[i]); } @@ -73,7 +70,6 @@ void ResourceFormatSaver::get_recognized_extensions(const RES &p_resource, List< } void ResourceFormatSaver::_bind_methods() { - { PropertyInfo arg0 = PropertyInfo(Variant::STRING, "path"); PropertyInfo arg1 = PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"); @@ -81,45 +77,45 @@ void ResourceFormatSaver::_bind_methods() { ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "save", arg0, arg1, arg2)); } - ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::POOL_STRING_ARRAY, "get_recognized_extensions", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::PACKED_STRING_ARRAY, "get_recognized_extensions", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "recognize", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); } Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - String extension = p_path.get_extension(); Error err = ERR_FILE_UNRECOGNIZED; for (int i = 0; i < saver_count; i++) { - - if (!saver[i]->recognize(p_resource)) + if (!saver[i]->recognize(p_resource)) { continue; + } List<String> extensions; bool recognized = false; saver[i]->get_recognized_extensions(p_resource, &extensions); for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - - if (E->get().nocasecmp_to(extension) == 0) + if (E->get().nocasecmp_to(extension) == 0) { recognized = true; + } } - if (!recognized) + if (!recognized) { continue; + } String old_path = p_resource->get_path(); String local_path = ProjectSettings::get_singleton()->localize_path(p_path); RES rwcopy = p_resource; - if (p_flags & FLAG_CHANGE_PATH) + if (p_flags & FLAG_CHANGE_PATH) { rwcopy->set_path(local_path); + } err = saver[i]->save(p_path, p_resource, p_flags); if (err == OK) { - #ifdef TOOLS_ENABLED ((Resource *)p_resource.ptr())->set_edited(false); @@ -130,11 +126,13 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t } #endif - if (p_flags & FLAG_CHANGE_PATH) + if (p_flags & FLAG_CHANGE_PATH) { rwcopy->set_path(old_path); + } - if (save_callback && p_path.begins_with("res://")) + if (save_callback && p_path.begins_with("res://")) { save_callback(p_resource, p_path); + } return OK; } @@ -144,20 +142,16 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t } void ResourceSaver::set_save_callback(ResourceSavedCallback p_callback) { - save_callback = p_callback; } void ResourceSaver::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) { - for (int i = 0; i < saver_count; i++) { - saver[i]->get_recognized_extensions(p_resource, p_extensions); } } void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front) { - ERR_FAIL_COND_MSG(p_format_saver.is_null(), "It's not a reference to a valid ResourceFormatSaver object."); ERR_FAIL_COND(saver_count >= MAX_SAVERS); @@ -173,14 +167,14 @@ void ResourceSaver::add_resource_format_saver(Ref<ResourceFormatSaver> p_format_ } void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver) { - ERR_FAIL_COND_MSG(p_format_saver.is_null(), "It's not a reference to a valid ResourceFormatSaver object."); // Find saver int i = 0; for (; i < saver_count; ++i) { - if (saver[i] == p_format_saver) + if (saver[i] == p_format_saver) { break; + } } ERR_FAIL_COND(i >= saver_count); // Not found @@ -203,9 +197,9 @@ Ref<ResourceFormatSaver> ResourceSaver::_find_custom_resource_format_saver(Strin } bool ResourceSaver::add_custom_resource_format_saver(String script_path) { - - if (_find_custom_resource_format_saver(script_path).is_valid()) + if (_find_custom_resource_format_saver(script_path).is_valid()) { return false; + } Ref<Resource> res = ResourceLoader::load(script_path); ERR_FAIL_COND_V(res.is_null(), false); @@ -218,20 +212,20 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { Object *obj = ClassDB::instance(ibt); - ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); + ERR_FAIL_COND_V_MSG(obj == nullptr, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); ResourceFormatSaver *crl = Object::cast_to<ResourceFormatSaver>(obj); - crl->set_script(s.get_ref_ptr()); + crl->set_script(s); ResourceSaver::add_resource_format_saver(crl); return true; } void ResourceSaver::remove_custom_resource_format_saver(String script_path) { - Ref<ResourceFormatSaver> custom_saver = _find_custom_resource_format_saver(script_path); - if (custom_saver.is_valid()) + if (custom_saver.is_valid()) { remove_resource_format_saver(custom_saver); + } } void ResourceSaver::add_custom_savers() { @@ -243,7 +237,6 @@ void ResourceSaver::add_custom_savers() { ScriptServer::get_global_class_list(&global_classes); for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { - StringName class_name = E->get(); StringName base_class = ScriptServer::get_global_class_native_base(class_name); @@ -255,8 +248,7 @@ void ResourceSaver::add_custom_savers() { } void ResourceSaver::remove_custom_savers() { - - Vector<Ref<ResourceFormatSaver> > custom_savers; + Vector<Ref<ResourceFormatSaver>> custom_savers; for (int i = 0; i < saver_count; ++i) { if (saver[i]->get_script_instance()) { custom_savers.push_back(saver[i]); diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index e749f54cfa..8b4cdd86f8 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -50,7 +50,6 @@ public: typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path); class ResourceSaver { - enum { MAX_SAVERS = 64 }; @@ -90,4 +89,4 @@ public: static void remove_custom_savers(); }; -#endif +#endif // RESOURCE_SAVER_H diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index dee82de691..403f61bb24 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -32,17 +32,16 @@ #include "core/io/marshalls.h" -Error StreamPeer::_put_data(const PoolVector<uint8_t> &p_data) { - +Error StreamPeer::_put_data(const Vector<uint8_t> &p_data) { int len = p_data.size(); - if (len == 0) + if (len == 0) { return OK; - PoolVector<uint8_t>::Read r = p_data.read(); + } + const uint8_t *r = p_data.ptr(); return put_data(&r[0], len); } -Array StreamPeer::_put_partial_data(const PoolVector<uint8_t> &p_data) { - +Array StreamPeer::_put_partial_data(const Vector<uint8_t> &p_data) { Array ret; int len = p_data.size(); @@ -52,7 +51,7 @@ Array StreamPeer::_put_partial_data(const PoolVector<uint8_t> &p_data) { return ret; } - PoolVector<uint8_t>::Read r = p_data.read(); + const uint8_t *r = p_data.ptr(); int sent; Error err = put_partial_data(&r[0], len, sent); @@ -65,48 +64,42 @@ Array StreamPeer::_put_partial_data(const PoolVector<uint8_t> &p_data) { } Array StreamPeer::_get_data(int p_bytes) { - Array ret; - PoolVector<uint8_t> data; + Vector<uint8_t> data; data.resize(p_bytes); if (data.size() != p_bytes) { - ret.push_back(ERR_OUT_OF_MEMORY); - ret.push_back(PoolVector<uint8_t>()); + ret.push_back(Vector<uint8_t>()); return ret; } - PoolVector<uint8_t>::Write w = data.write(); + uint8_t *w = data.ptrw(); Error err = get_data(&w[0], p_bytes); - w.release(); + ret.push_back(err); ret.push_back(data); return ret; } Array StreamPeer::_get_partial_data(int p_bytes) { - Array ret; - PoolVector<uint8_t> data; + Vector<uint8_t> data; data.resize(p_bytes); if (data.size() != p_bytes) { - ret.push_back(ERR_OUT_OF_MEMORY); - ret.push_back(PoolVector<uint8_t>()); + ret.push_back(Vector<uint8_t>()); return ret; } - PoolVector<uint8_t>::Write w = data.write(); + uint8_t *w = data.ptrw(); int received; Error err = get_partial_data(&w[0], p_bytes, received); - w.release(); if (err != OK) { data.resize(0); } else if (received != data.size()) { - data.resize(received); } @@ -116,12 +109,10 @@ Array StreamPeer::_get_partial_data(int p_bytes) { } void StreamPeer::set_big_endian(bool p_enable) { - big_endian = p_enable; } bool StreamPeer::is_big_endian_enabled() const { - return big_endian; } @@ -130,11 +121,10 @@ void StreamPeer::put_u8(uint8_t p_val) { } void StreamPeer::put_8(int8_t p_val) { - put_data((const uint8_t *)&p_val, 1); } -void StreamPeer::put_u16(uint16_t p_val) { +void StreamPeer::put_u16(uint16_t p_val) { if (big_endian) { p_val = BSWAP16(p_val); } @@ -142,8 +132,8 @@ void StreamPeer::put_u16(uint16_t p_val) { encode_uint16(p_val, buf); put_data(buf, 2); } -void StreamPeer::put_16(int16_t p_val) { +void StreamPeer::put_16(int16_t p_val) { if (big_endian) { p_val = BSWAP16(p_val); } @@ -151,8 +141,8 @@ void StreamPeer::put_16(int16_t p_val) { encode_uint16(p_val, buf); put_data(buf, 2); } -void StreamPeer::put_u32(uint32_t p_val) { +void StreamPeer::put_u32(uint32_t p_val) { if (big_endian) { p_val = BSWAP32(p_val); } @@ -160,8 +150,8 @@ void StreamPeer::put_u32(uint32_t p_val) { encode_uint32(p_val, buf); put_data(buf, 4); } -void StreamPeer::put_32(int32_t p_val) { +void StreamPeer::put_32(int32_t p_val) { if (big_endian) { p_val = BSWAP32(p_val); } @@ -169,8 +159,8 @@ void StreamPeer::put_32(int32_t p_val) { encode_uint32(p_val, buf); put_data(buf, 4); } -void StreamPeer::put_u64(uint64_t p_val) { +void StreamPeer::put_u64(uint64_t p_val) { if (big_endian) { p_val = BSWAP64(p_val); } @@ -178,8 +168,8 @@ void StreamPeer::put_u64(uint64_t p_val) { encode_uint64(p_val, buf); put_data(buf, 8); } -void StreamPeer::put_64(int64_t p_val) { +void StreamPeer::put_64(int64_t p_val) { if (big_endian) { p_val = BSWAP64(p_val); } @@ -187,8 +177,8 @@ void StreamPeer::put_64(int64_t p_val) { encode_uint64(p_val, buf); put_data(buf, 8); } -void StreamPeer::put_float(float p_val) { +void StreamPeer::put_float(float p_val) { uint8_t buf[4]; encode_float(p_val, buf); @@ -199,8 +189,8 @@ void StreamPeer::put_float(float p_val) { put_data(buf, 4); } -void StreamPeer::put_double(double p_val) { +void StreamPeer::put_double(double p_val) { uint8_t buf[8]; encode_double(p_val, buf); if (big_endian) { @@ -209,23 +199,23 @@ void StreamPeer::put_double(double p_val) { } put_data(buf, 8); } -void StreamPeer::put_string(const String &p_string) { +void StreamPeer::put_string(const String &p_string) { CharString cs = p_string.ascii(); put_u32(cs.length()); put_data((const uint8_t *)cs.get_data(), cs.length()); } -void StreamPeer::put_utf8_string(const String &p_string) { +void StreamPeer::put_utf8_string(const String &p_string) { CharString cs = p_string.utf8(); put_u32(cs.length()); put_data((const uint8_t *)cs.get_data(), cs.length()); } -void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) { +void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) { int len = 0; Vector<uint8_t> buf; - encode_variant(p_variant, NULL, len, p_full_objects); + encode_variant(p_variant, nullptr, len, p_full_objects); buf.resize(len); put_32(len); encode_variant(p_variant, buf.ptrw(), len, p_full_objects); @@ -233,19 +223,18 @@ void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) { } uint8_t StreamPeer::get_u8() { - uint8_t buf[1]; get_data(buf, 1); return buf[0]; } -int8_t StreamPeer::get_8() { +int8_t StreamPeer::get_8() { uint8_t buf[1]; get_data(buf, 1); return buf[0]; } -uint16_t StreamPeer::get_u16() { +uint16_t StreamPeer::get_u16() { uint8_t buf[2]; get_data(buf, 2); uint16_t r = decode_uint16(buf); @@ -254,8 +243,8 @@ uint16_t StreamPeer::get_u16() { } return r; } -int16_t StreamPeer::get_16() { +int16_t StreamPeer::get_16() { uint8_t buf[2]; get_data(buf, 2); uint16_t r = decode_uint16(buf); @@ -264,8 +253,8 @@ int16_t StreamPeer::get_16() { } return r; } -uint32_t StreamPeer::get_u32() { +uint32_t StreamPeer::get_u32() { uint8_t buf[4]; get_data(buf, 4); uint32_t r = decode_uint32(buf); @@ -274,8 +263,8 @@ uint32_t StreamPeer::get_u32() { } return r; } -int32_t StreamPeer::get_32() { +int32_t StreamPeer::get_32() { uint8_t buf[4]; get_data(buf, 4); uint32_t r = decode_uint32(buf); @@ -284,8 +273,8 @@ int32_t StreamPeer::get_32() { } return r; } -uint64_t StreamPeer::get_u64() { +uint64_t StreamPeer::get_u64() { uint8_t buf[8]; get_data(buf, 8); uint64_t r = decode_uint64(buf); @@ -294,8 +283,8 @@ uint64_t StreamPeer::get_u64() { } return r; } -int64_t StreamPeer::get_64() { +int64_t StreamPeer::get_64() { uint8_t buf[8]; get_data(buf, 8); uint64_t r = decode_uint64(buf); @@ -304,8 +293,8 @@ int64_t StreamPeer::get_64() { } return r; } -float StreamPeer::get_float() { +float StreamPeer::get_float() { uint8_t buf[4]; get_data(buf, 4); @@ -318,7 +307,6 @@ float StreamPeer::get_float() { } double StreamPeer::get_double() { - uint8_t buf[8]; get_data(buf, 8); @@ -329,10 +317,11 @@ double StreamPeer::get_double() { return decode_double(buf); } -String StreamPeer::get_string(int p_bytes) { - if (p_bytes < 0) +String StreamPeer::get_string(int p_bytes) { + if (p_bytes < 0) { p_bytes = get_u32(); + } ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<char> buf; @@ -343,10 +332,11 @@ String StreamPeer::get_string(int p_bytes) { buf.write[p_bytes] = 0; return buf.ptr(); } -String StreamPeer::get_utf8_string(int p_bytes) { - if (p_bytes < 0) +String StreamPeer::get_utf8_string(int p_bytes) { + if (p_bytes < 0) { p_bytes = get_u32(); + } ERR_FAIL_COND_V(p_bytes < 0, String()); Vector<uint8_t> buf; @@ -359,8 +349,8 @@ String StreamPeer::get_utf8_string(int p_bytes) { ret.parse_utf8((const char *)buf.ptr(), buf.size()); return ret; } -Variant StreamPeer::get_var(bool p_allow_objects) { +Variant StreamPeer::get_var(bool p_allow_objects) { int len = get_32(); Vector<uint8_t> var; Error err = var.resize(len); @@ -369,14 +359,13 @@ Variant StreamPeer::get_var(bool p_allow_objects) { ERR_FAIL_COND_V(err != OK, Variant()); Variant ret; - err = decode_variant(ret, var.ptr(), len, NULL, p_allow_objects); + err = decode_variant(ret, var.ptr(), len, nullptr, p_allow_objects); ERR_FAIL_COND_V_MSG(err != OK, Variant(), "Error when trying to decode Variant."); return ret; } void StreamPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("put_data", "data"), &StreamPeer::_put_data); ClassDB::bind_method(D_METHOD("put_partial_data", "data"), &StreamPeer::_put_partial_data); @@ -418,10 +407,10 @@ void StreamPeer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "big_endian"), "set_big_endian", "is_big_endian_enabled"); } + //////////////////////////////// void StreamPeerBuffer::_bind_methods() { - ClassDB::bind_method(D_METHOD("seek", "position"), &StreamPeerBuffer::seek); ClassDB::bind_method(D_METHOD("get_size"), &StreamPeerBuffer::get_size); ClassDB::bind_method(D_METHOD("get_position"), &StreamPeerBuffer::get_position); @@ -431,19 +420,19 @@ void StreamPeerBuffer::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &StreamPeerBuffer::clear); ClassDB::bind_method(D_METHOD("duplicate"), &StreamPeerBuffer::duplicate); - ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data_array"), "set_data_array", "get_data_array"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data_array"), "set_data_array", "get_data_array"); } Error StreamPeerBuffer::put_data(const uint8_t *p_data, int p_bytes) { - - if (p_bytes <= 0) + if (p_bytes <= 0) { return OK; + } if (pointer + p_bytes > data.size()) { data.resize(pointer + p_bytes); } - PoolVector<uint8_t>::Write w = data.write(); + uint8_t *w = data.ptrw(); copymem(&w[pointer], p_data, p_bytes); pointer += p_bytes; @@ -451,23 +440,21 @@ Error StreamPeerBuffer::put_data(const uint8_t *p_data, int p_bytes) { } Error StreamPeerBuffer::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - r_sent = p_bytes; return put_data(p_data, p_bytes); } Error StreamPeerBuffer::get_data(uint8_t *p_buffer, int p_bytes) { - int recv; get_partial_data(p_buffer, p_bytes, recv); - if (recv != p_bytes) + if (recv != p_bytes) { return ERR_INVALID_PARAMETER; + } return OK; } Error StreamPeerBuffer::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - if (pointer + p_bytes > data.size()) { r_received = data.size() - pointer; if (r_received <= 0) { @@ -478,8 +465,8 @@ Error StreamPeerBuffer::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_ r_received = p_bytes; } - PoolVector<uint8_t>::Read r = data.read(); - copymem(p_buffer, r.ptr() + pointer, r_received); + const uint8_t *r = data.ptr(); + copymem(p_buffer, r + pointer, r_received); pointer += r_received; // FIXME: return what? OK or ERR_* @@ -488,57 +475,44 @@ Error StreamPeerBuffer::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_ } int StreamPeerBuffer::get_available_bytes() const { - return data.size() - pointer; } void StreamPeerBuffer::seek(int p_pos) { - ERR_FAIL_COND(p_pos < 0); ERR_FAIL_COND(p_pos > data.size()); pointer = p_pos; } -int StreamPeerBuffer::get_size() const { +int StreamPeerBuffer::get_size() const { return data.size(); } int StreamPeerBuffer::get_position() const { - return pointer; } void StreamPeerBuffer::resize(int p_size) { - data.resize(p_size); } -void StreamPeerBuffer::set_data_array(const PoolVector<uint8_t> &p_data) { - +void StreamPeerBuffer::set_data_array(const Vector<uint8_t> &p_data) { data = p_data; pointer = 0; } -PoolVector<uint8_t> StreamPeerBuffer::get_data_array() const { - +Vector<uint8_t> StreamPeerBuffer::get_data_array() const { return data; } void StreamPeerBuffer::clear() { - data.resize(0); pointer = 0; } Ref<StreamPeerBuffer> StreamPeerBuffer::duplicate() const { - Ref<StreamPeerBuffer> spb; spb.instance(); spb->data = data; return spb; } - -StreamPeerBuffer::StreamPeerBuffer() { - - pointer = 0; -} diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h index d6ce7ddc16..39097a57f2 100644 --- a/core/io/stream_peer.h +++ b/core/io/stream_peer.h @@ -41,13 +41,13 @@ protected: static void _bind_methods(); //bind helpers - Error _put_data(const PoolVector<uint8_t> &p_data); - Array _put_partial_data(const PoolVector<uint8_t> &p_data); + Error _put_data(const Vector<uint8_t> &p_data); + Array _put_partial_data(const Vector<uint8_t> &p_data); Array _get_data(int p_bytes); Array _get_partial_data(int p_bytes); - bool big_endian; + bool big_endian = false; public: virtual Error put_data(const uint8_t *p_data, int p_bytes) = 0; ///< put a whole chunk of data, blocking until it sent @@ -89,41 +89,40 @@ public: String get_utf8_string(int p_bytes = -1); Variant get_var(bool p_allow_objects = false); - StreamPeer() { big_endian = false; } + StreamPeer() {} }; class StreamPeerBuffer : public StreamPeer { - GDCLASS(StreamPeerBuffer, StreamPeer); - PoolVector<uint8_t> data; - int pointer; + Vector<uint8_t> data; + int pointer = 0; protected: static void _bind_methods(); public: - Error put_data(const uint8_t *p_data, int p_bytes); - Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); + Error put_data(const uint8_t *p_data, int p_bytes) override; + Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; - Error get_data(uint8_t *p_buffer, int p_bytes); - Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); + Error get_data(uint8_t *p_buffer, int p_bytes) override; + Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; - virtual int get_available_bytes() const; + virtual int get_available_bytes() const override; void seek(int p_pos); int get_size() const; int get_position() const; void resize(int p_size); - void set_data_array(const PoolVector<uint8_t> &p_data); - PoolVector<uint8_t> get_data_array() const; + void set_data_array(const Vector<uint8_t> &p_data); + Vector<uint8_t> get_data_array() const; void clear(); Ref<StreamPeerBuffer> duplicate() const; - StreamPeerBuffer(); + StreamPeerBuffer() {} }; #endif // STREAM_PEER_H diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp index 03ca726619..3dc31c6769 100644 --- a/core/io/stream_peer_ssl.cpp +++ b/core/io/stream_peer_ssl.cpp @@ -32,13 +32,13 @@ #include "core/engine.h" -StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL; +StreamPeerSSL *(*StreamPeerSSL::_create)() = nullptr; StreamPeerSSL *StreamPeerSSL::create() { - - if (_create) + if (_create) { return _create(); - return NULL; + } + return nullptr; } bool StreamPeerSSL::available = false; @@ -56,7 +56,6 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const { } 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>())); @@ -73,7 +72,3 @@ void StreamPeerSSL::_bind_methods() { BIND_ENUM_CONSTANT(STATUS_ERROR); BIND_ENUM_CONSTANT(STATUS_ERROR_HOSTNAME_MISMATCH); } - -StreamPeerSSL::StreamPeerSSL() { - blocking_handshake = true; -} diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h index de3cb09c60..81b95b856d 100644 --- a/core/io/stream_peer_ssl.h +++ b/core/io/stream_peer_ssl.h @@ -43,7 +43,7 @@ protected: static bool available; - bool blocking_handshake; + bool blocking_handshake = true; public: enum Status { @@ -68,7 +68,7 @@ public: static bool is_available(); - StreamPeerSSL(); + StreamPeerSSL() {} }; VARIANT_ENUM_CAST(StreamPeerSSL::Status); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 044431743a..cce728c30a 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -33,7 +33,6 @@ #include "core/project_settings.h" Error StreamPeerTCP::_poll_connection() { - ERR_FAIL_COND_V(status != STATUS_CONNECTING || !_sock.is_valid() || !_sock->is_open(), FAILED); Error err = _sock->connect_to_host(peer_host, peer_port); @@ -58,7 +57,6 @@ Error StreamPeerTCP::_poll_connection() { } void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint16_t p_port) { - _sock = p_sock; _sock->set_blocking_enabled(false); @@ -70,7 +68,6 @@ void StreamPeerTCP::accept_socket(Ref<NetSocket> p_sock, IP_Address p_host, uint } Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(!p_host.is_valid(), ERR_INVALID_PARAMETER); @@ -103,18 +100,14 @@ Error StreamPeerTCP::connect_to_host(const IP_Address &p_host, uint16_t p_port) } Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool p_block) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); if (status == STATUS_NONE || status == STATUS_ERROR) { - return FAILED; } if (status != STATUS_CONNECTED) { - if (_poll_connection() != OK) { - return FAILED; } @@ -124,8 +117,9 @@ Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool } } - if (!_sock->is_open()) + if (!_sock->is_open()) { return FAILED; + } Error err; int data_to_send = p_bytes; @@ -133,12 +127,10 @@ Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool int total_sent = 0; while (data_to_send) { - int sent_amount = 0; err = _sock->send(offset, data_to_send, sent_amount); if (err != OK) { - if (err != ERR_BUSY) { disconnect_from_host(); return FAILED; @@ -156,7 +148,6 @@ Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool return FAILED; } } else { - data_to_send -= sent_amount; offset += sent_amount; total_sent += sent_amount; @@ -169,16 +160,12 @@ Error StreamPeerTCP::write(const uint8_t *p_data, int p_bytes, int &r_sent, bool } Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool p_block) { - if (!is_connected_to_host()) { - return FAILED; } if (status == STATUS_CONNECTING) { - if (_poll_connection() != OK) { - return FAILED; } @@ -194,12 +181,10 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool r_received = 0; while (to_read) { - int read = 0; err = _sock->recv(p_buffer + total_read, to_read, read); if (err != OK) { - if (err != ERR_BUSY) { disconnect_from_host(); return FAILED; @@ -218,13 +203,11 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool } } else if (read == 0) { - disconnect_from_host(); r_received = total_read; return ERR_FILE_EOF; } else { - to_read -= read; total_read += read; @@ -241,18 +224,15 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool } void StreamPeerTCP::set_no_delay(bool p_enabled) { - ERR_FAIL_COND(!is_connected_to_host()); _sock->set_tcp_no_delay_enabled(p_enabled); } bool StreamPeerTCP::is_connected_to_host() const { - return _sock.is_valid() && _sock->is_open() && (status == STATUS_CONNECTED || status == STATUS_CONNECTING); } StreamPeerTCP::Status StreamPeerTCP::get_status() { - if (status == STATUS_CONNECTING) { _poll_connection(); } else if (status == STATUS_CONNECTED) { @@ -278,9 +258,9 @@ StreamPeerTCP::Status StreamPeerTCP::get_status() { } void StreamPeerTCP::disconnect_from_host() { - - if (_sock.is_valid() && _sock->is_open()) + if (_sock.is_valid() && _sock->is_open()) { _sock->close(); + } timeout = 0; status = STATUS_NONE; @@ -288,60 +268,57 @@ void StreamPeerTCP::disconnect_from_host() { peer_port = 0; } -Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) { +Error StreamPeerTCP::poll(NetSocket::PollType p_type, int timeout) { + ERR_FAIL_COND_V(_sock.is_null() || !_sock->is_open(), ERR_UNAVAILABLE); + return _sock->poll(p_type, timeout); +} +Error StreamPeerTCP::put_data(const uint8_t *p_data, int p_bytes) { int total; return write(p_data, p_bytes, total, true); } Error StreamPeerTCP::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) { - return write(p_data, p_bytes, r_sent, false); } Error StreamPeerTCP::get_data(uint8_t *p_buffer, int p_bytes) { - int total; return read(p_buffer, p_bytes, total, true); } Error StreamPeerTCP::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) { - return read(p_buffer, p_bytes, r_received, false); } int StreamPeerTCP::get_available_bytes() const { - ERR_FAIL_COND_V(!_sock.is_valid(), -1); return _sock->get_available_bytes(); } IP_Address StreamPeerTCP::get_connected_host() const { - return peer_host; } uint16_t StreamPeerTCP::get_connected_port() const { - return peer_port; } Error StreamPeerTCP::_connect(const String &p_address, int p_port) { - IP_Address ip; if (p_address.is_valid_ip_address()) { ip = p_address; } else { ip = IP::get_singleton()->resolve_hostname(p_address); - if (!ip.is_valid()) + if (!ip.is_valid()) { return ERR_CANT_RESOLVE; + } } return connect_to_host(ip, p_port); } void StreamPeerTCP::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port"), &StreamPeerTCP::_connect); ClassDB::bind_method(D_METHOD("is_connected_to_host"), &StreamPeerTCP::is_connected_to_host); ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerTCP::get_status); @@ -357,13 +334,9 @@ void StreamPeerTCP::_bind_methods() { } StreamPeerTCP::StreamPeerTCP() : - _sock(Ref<NetSocket>(NetSocket::create())), - timeout(0), - status(STATUS_NONE), - peer_port(0) { + _sock(Ref<NetSocket>(NetSocket::create())) { } StreamPeerTCP::~StreamPeerTCP() { - disconnect_from_host(); } diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h index f16d4a2bd4..45205866b4 100644 --- a/core/io/stream_peer_tcp.h +++ b/core/io/stream_peer_tcp.h @@ -37,7 +37,6 @@ #include "core/io/stream_peer.h" class StreamPeerTCP : public StreamPeer { - GDCLASS(StreamPeerTCP, StreamPeer); OBJ_CATEGORY("Networking"); @@ -52,10 +51,10 @@ public: protected: Ref<NetSocket> _sock; - uint64_t timeout; - Status status; + uint64_t timeout = 0; + Status status = STATUS_NONE; IP_Address peer_host; - uint16_t peer_port; + uint16_t peer_port = 0; Error _connect(const String &p_address, int p_port); Error _poll_connection(); @@ -73,16 +72,19 @@ public: uint16_t get_connected_port() const; void disconnect_from_host(); - int get_available_bytes() const; + int get_available_bytes() const override; Status get_status(); void set_no_delay(bool p_enabled); + // Poll functions (wait or check for writable, readable) + Error poll(NetSocket::PollType p_type, int timeout = 0); + // Read/Write from StreamPeer - Error put_data(const uint8_t *p_data, int p_bytes); - Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent); - Error get_data(uint8_t *p_buffer, int p_bytes); - Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received); + Error put_data(const uint8_t *p_data, int p_bytes) override; + Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override; + Error get_data(uint8_t *p_buffer, int p_bytes) override; + Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override; StreamPeerTCP(); ~StreamPeerTCP(); @@ -90,4 +92,4 @@ public: VARIANT_ENUM_CAST(StreamPeerTCP::Status); -#endif +#endif // STREAM_PEER_TCP_H diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp index 69c2ba7943..d7061b6bf4 100644 --- a/core/io/tcp_server.cpp +++ b/core/io/tcp_server.cpp @@ -31,7 +31,6 @@ #include "tcp_server.h" void TCP_Server::_bind_methods() { - ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &TCP_Server::listen, DEFVAL("*")); ClassDB::bind_method(D_METHOD("is_connection_available"), &TCP_Server::is_connection_available); ClassDB::bind_method(D_METHOD("is_listening"), &TCP_Server::is_listening); @@ -40,7 +39,6 @@ void TCP_Server::_bind_methods() { } Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { - ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); @@ -49,8 +47,9 @@ Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { IP::Type ip_type = IP::TYPE_ANY; // If the bind address is valid use its type as the socket type - if (p_bind_address.is_valid()) + if (p_bind_address.is_valid()) { ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + } err = _sock->open(NetSocket::TYPE_TCP, ip_type); @@ -62,7 +61,6 @@ Error TCP_Server::listen(uint16_t p_port, const IP_Address &p_bind_address) { err = _sock->bind(p_bind_address, p_port); if (err != OK) { - _sock->close(); return ERR_ALREADY_IN_USE; } @@ -83,18 +81,17 @@ bool TCP_Server::is_listening() const { } bool TCP_Server::is_connection_available() const { - ERR_FAIL_COND_V(!_sock.is_valid(), false); - if (!_sock->is_open()) + if (!_sock->is_open()) { return false; + } Error err = _sock->poll(NetSocket::POLL_TYPE_IN, 0); return (err == OK); } Ref<StreamPeerTCP> TCP_Server::take_connection() { - Ref<StreamPeerTCP> conn; if (!is_connection_available()) { return conn; @@ -104,8 +101,9 @@ Ref<StreamPeerTCP> TCP_Server::take_connection() { IP_Address ip; uint16_t port = 0; ns = _sock->accept(ip, port); - if (!ns.is_valid()) + if (!ns.is_valid()) { return conn; + } conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP)); conn->accept_socket(ns, ip, port); @@ -113,7 +111,6 @@ Ref<StreamPeerTCP> TCP_Server::take_connection() { } void TCP_Server::stop() { - if (_sock.is_valid()) { _sock->close(); } @@ -124,6 +121,5 @@ TCP_Server::TCP_Server() : } TCP_Server::~TCP_Server() { - stop(); } diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h index ca52b13ba1..eb715a745c 100644 --- a/core/io/tcp_server.h +++ b/core/io/tcp_server.h @@ -37,7 +37,6 @@ #include "core/io/stream_peer_tcp.h" class TCP_Server : public Reference { - GDCLASS(TCP_Server, Reference); protected: diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp index 4f7eeddc43..d8ddb213c3 100644 --- a/core/io/translation_loader_po.cpp +++ b/core/io/translation_loader_po.cpp @@ -32,75 +32,146 @@ #include "core/os/file_access.h" #include "core/translation.h" +#include "core/translation_po.h" -RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const String &p_path) { - +RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) { enum Status { - STATUS_NONE, STATUS_READING_ID, STATUS_READING_STRING, + STATUS_READING_CONTEXT, + STATUS_READING_PLURAL, }; Status status = STATUS_NONE; String msg_id; String msg_str; + String msg_context; + Vector<String> msgs_plural; String config; - if (r_error) + if (r_error) { *r_error = ERR_FILE_CORRUPT; + } - Ref<Translation> translation = Ref<Translation>(memnew(Translation)); + Ref<TranslationPO> translation = Ref<TranslationPO>(memnew(TranslationPO)); int line = 1; + int plural_forms = 0; + int plural_index = -1; + bool entered_context = false; bool skip_this = false; bool skip_next = false; bool is_eof = false; + const String path = f->get_path(); while (!is_eof) { - String l = f->get_line().strip_edges(); is_eof = f->eof_reached(); // If we reached last line and it's not a content line, break, otherwise let processing that last loop if (is_eof && l.empty()) { - - if (status == STATUS_READING_ID) { + if (status == STATUS_READING_ID || status == STATUS_READING_CONTEXT || (status == STATUS_READING_PLURAL && plural_index != plural_forms - 1)) { memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading PO file at: " + path + ":" + itos(line)); } else { break; } } - if (l.begins_with("msgid")) { + if (l.begins_with("msgctxt")) { + if (status != STATUS_READING_STRING && status != STATUS_READING_PLURAL) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line)); + } - if (status == STATUS_READING_ID) { + // In PO file, "msgctxt" appears before "msgid". If we encounter a "msgctxt", we add what we have read + // and set "entered_context" to true to prevent adding twice. + if (!skip_this && msg_id != "") { + if (status == STATUS_READING_STRING) { + translation->add_message(msg_id, msg_str, msg_context); + } else if (status == STATUS_READING_PLURAL) { + if (plural_index != plural_forms - 1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + } + translation->add_plural_message(msg_id, msgs_plural, msg_context); + } + } + msg_context = ""; + l = l.substr(7, l.length()).strip_edges(); + status = STATUS_READING_CONTEXT; + entered_context = true; + } + if (l.begins_with("msgid_plural")) { + if (plural_forms == 0) { memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: "); + ERR_FAIL_V_MSG(RES(), "PO file uses 'msgid_plural' but 'Plural-Forms' is invalid or missing in header: " + path + ":" + itos(line)); + } else if (status != STATUS_READING_ID) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: " + path + ":" + itos(line)); + } + // We don't record the message in "msgid_plural" itself as tr_n(), TTRN(), RTRN() interfaces provide the plural string already. + // We just have to reset variables related to plurals for "msgstr[]" later on. + l = l.substr(12, l.length()).strip_edges(); + plural_index = -1; + msgs_plural.clear(); + msgs_plural.resize(plural_forms); + status = STATUS_READING_PLURAL; + } else if (l.begins_with("msgid")) { + if (status == STATUS_READING_ID) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line)); } if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") + if (!skip_this && !entered_context) { + if (status == STATUS_READING_STRING) { + translation->add_message(msg_id, msg_str, msg_context); + } else if (status == STATUS_READING_PLURAL) { + if (plural_index != plural_forms - 1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + } + translation->add_plural_message(msg_id, msgs_plural, msg_context); + } + } + } else if (config == "") { config = msg_str; + // Record plural rule. + int p_start = config.find("Plural-Forms"); + if (p_start != -1) { + int p_end = config.find("\n", p_start); + translation->set_plural_rule(config.substr(p_start, p_end - p_start)); + plural_forms = translation->get_plural_forms(); + } + } l = l.substr(5, l.length()).strip_edges(); status = STATUS_READING_ID; + // If we did not encounter msgctxt, we reset context to empty to reset it. + if (!entered_context) { + msg_context = ""; + } msg_id = ""; msg_str = ""; skip_this = skip_next; skip_next = false; + entered_context = false; } - if (l.begins_with("msgstr")) { - + if (l.begins_with("msgstr[")) { + if (status != STATUS_READING_PLURAL) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: " + path + ":" + itos(line)); + } + plural_index++; // Increment to add to the next slot in vector msgs_plural. + l = l.substr(9, l.length()).strip_edges(); + } else if (l.begins_with("msgstr")) { if (status != STATUS_READING_ID) { - memdelete(f); - ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: "); + ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line)); } l = l.substr(6, l.length()).strip_edges(); @@ -112,56 +183,85 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S skip_next = true; } line++; - continue; //nothing to read or comment + continue; // Nothing to read or comment. } - ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), p_path + ":" + itos(line) + " Invalid line '" + l + "' while parsing: "); + if (!l.begins_with("\"") || status == STATUS_NONE) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line)); + } l = l.substr(1, l.length()); - //find final quote + // Find final quote, ignoring escaped ones (\"). + // The escape_next logic is necessary to properly parse things like \\" + // where the blackslash is the one being escaped, not the quote. int end_pos = -1; + bool escape_next = false; for (int i = 0; i < l.length(); i++) { + if (l[i] == '\\' && !escape_next) { + escape_next = true; + continue; + } - if (l[i] == '"' && (i == 0 || l[i - 1] != '\\')) { + if (l[i] == '"' && !escape_next) { end_pos = i; break; } + + escape_next = false; } - ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), p_path + ":" + itos(line) + " Expected '\"' at end of message while parsing file: "); + if (end_pos == -1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line)); + } l = l.substr(0, end_pos); l = l.c_unescape(); - if (status == STATUS_READING_ID) + if (status == STATUS_READING_ID) { msg_id += l; - else + } else if (status == STATUS_READING_STRING) { msg_str += l; + } else if (status == STATUS_READING_CONTEXT) { + msg_context += l; + } else if (status == STATUS_READING_PLURAL && plural_index >= 0) { + msgs_plural.write[plural_index] = msgs_plural[plural_index] + l; + } line++; } - f->close(); memdelete(f); + // Add the last set of data from last iteration. if (status == STATUS_READING_STRING) { - if (msg_id != "") { - if (!skip_this) - translation->add_message(msg_id, msg_str); - } else if (config == "") + if (!skip_this) { + translation->add_message(msg_id, msg_str, msg_context); + } + } else if (config == "") { config = msg_str; + } + } else if (status == STATUS_READING_PLURAL) { + if (!skip_this && msg_id != "") { + if (plural_index != plural_forms - 1) { + memdelete(f); + ERR_FAIL_V_MSG(RES(), "Number of 'msgstr[]' doesn't match with number of plural forms: " + path + ":" + itos(line)); + } + translation->add_plural_message(msg_id, msgs_plural, msg_context); + } } - ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + p_path + "."); + ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + path + "."); Vector<String> configs = config.split("\n"); for (int i = 0; i < configs.size(); i++) { - String c = configs[i].strip_edges(); int p = c.find(":"); - if (p == -1) + if (p == -1) { continue; + } String prop = c.substr(0, p).strip_edges(); String value = c.substr(p + 1, c.length()).strip_edges(); @@ -170,16 +270,17 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S } } - if (r_error) + if (r_error) { *r_error = OK; + } return translation; } -RES TranslationLoaderPO::load(const String &p_path, const String &p_original_path, Error *r_error) { - - if (r_error) +RES TranslationLoaderPO::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { + if (r_error) { *r_error = ERR_CANT_OPEN; + } FileAccess *f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot open file '" + p_path + "'."); @@ -188,21 +289,16 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat } void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("po"); - //p_extensions->push_back("mo"); //mo in the future... } -bool TranslationLoaderPO::handles_type(const String &p_type) const { +bool TranslationLoaderPO::handles_type(const String &p_type) const { return (p_type == "Translation"); } String TranslationLoaderPO::get_resource_type(const String &p_path) const { - - if (p_path.get_extension().to_lower() == "po") + if (p_path.get_extension().to_lower() == "po") { return "Translation"; + } return ""; } - -TranslationLoaderPO::TranslationLoaderPO() { -} diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 47e64276ca..a196a37dc0 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -37,13 +37,13 @@ class TranslationLoaderPO : public ResourceFormatLoader { public: - static RES load_translation(FileAccess *f, Error *r_error, const String &p_path = String()); - virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL); + static RES load_translation(FileAccess *f, Error *r_error = nullptr); + virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false); 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; - TranslationLoaderPO(); + TranslationLoaderPO() {} }; #endif // TRANSLATION_LOADER_PO_H diff --git a/core/io/udp_server.cpp b/core/io/udp_server.cpp new file mode 100644 index 0000000000..acd15aadc6 --- /dev/null +++ b/core/io/udp_server.cpp @@ -0,0 +1,202 @@ +/*************************************************************************/ +/* udp_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 "udp_server.h" + +void UDPServer::_bind_methods() { + ClassDB::bind_method(D_METHOD("listen", "port", "bind_address"), &UDPServer::listen, DEFVAL("*")); + ClassDB::bind_method(D_METHOD("poll"), &UDPServer::poll); + ClassDB::bind_method(D_METHOD("is_connection_available"), &UDPServer::is_connection_available); + ClassDB::bind_method(D_METHOD("is_listening"), &UDPServer::is_listening); + ClassDB::bind_method(D_METHOD("take_connection"), &UDPServer::take_connection); + ClassDB::bind_method(D_METHOD("stop"), &UDPServer::stop); + ClassDB::bind_method(D_METHOD("set_max_pending_connections", "max_pending_connections"), &UDPServer::set_max_pending_connections); + ClassDB::bind_method(D_METHOD("get_max_pending_connections"), &UDPServer::get_max_pending_connections); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_pending_connections", PROPERTY_HINT_RANGE, "0,256,1"), "set_max_pending_connections", "get_max_pending_connections"); +} + +Error UDPServer::poll() { + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + if (!_sock->is_open()) { + return ERR_UNCONFIGURED; + } + Error err; + int read; + IP_Address ip; + uint16_t port; + while (true) { + err = _sock->recvfrom(recv_buffer, sizeof(recv_buffer), read, ip, port); + if (err != OK) { + if (err == ERR_BUSY) { + break; + } + return FAILED; + } + Peer p; + p.ip = ip; + p.port = port; + List<Peer>::Element *E = peers.find(p); + if (!E) { + E = pending.find(p); + } + if (E) { + E->get().peer->store_packet(ip, port, recv_buffer, read); + } else { + if (pending.size() >= max_pending_connections) { + // Drop connection. + continue; + } + // It's a new peer, add it to the pending list. + Peer peer; + peer.ip = ip; + peer.port = port; + peer.peer = memnew(PacketPeerUDP); + peer.peer->connect_shared_socket(_sock, ip, port, this); + peer.peer->store_packet(ip, port, recv_buffer, read); + pending.push_back(peer); + } + } + return OK; +} + +Error UDPServer::listen(uint16_t p_port, const IP_Address &p_bind_address) { + ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); + ERR_FAIL_COND_V(_sock->is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER); + + Error err; + IP::Type ip_type = IP::TYPE_ANY; + + if (p_bind_address.is_valid()) { + ip_type = p_bind_address.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + } + + err = _sock->open(NetSocket::TYPE_UDP, ip_type); + + if (err != OK) { + return ERR_CANT_CREATE; + } + + _sock->set_blocking_enabled(false); + _sock->set_reuse_address_enabled(true); + err = _sock->bind(p_bind_address, p_port); + + if (err != OK) { + stop(); + return err; + } + bind_address = p_bind_address; + bind_port = p_port; + return OK; +} + +bool UDPServer::is_listening() const { + ERR_FAIL_COND_V(!_sock.is_valid(), false); + + return _sock->is_open(); +} + +bool UDPServer::is_connection_available() const { + ERR_FAIL_COND_V(!_sock.is_valid(), false); + + if (!_sock->is_open()) { + return false; + } + + return pending.size() > 0; +} + +void UDPServer::set_max_pending_connections(int p_max) { + ERR_FAIL_COND_MSG(p_max < 0, "Max pending connections value must be a positive number (0 means refuse new connections)."); + max_pending_connections = p_max; + while (p_max > pending.size()) { + List<Peer>::Element *E = pending.back(); + if (!E) { + break; + } + memdelete(E->get().peer); + pending.erase(E); + } +} + +int UDPServer::get_max_pending_connections() const { + return max_pending_connections; +} + +Ref<PacketPeerUDP> UDPServer::take_connection() { + Ref<PacketPeerUDP> conn; + if (!is_connection_available()) { + return conn; + } + + Peer peer = pending[0]; + pending.pop_front(); + peers.push_back(peer); + return peer.peer; +} + +void UDPServer::remove_peer(IP_Address p_ip, int p_port) { + Peer peer; + peer.ip = p_ip; + peer.port = p_port; + List<Peer>::Element *E = peers.find(peer); + if (E) { + peers.erase(E); + } +} + +void UDPServer::stop() { + if (_sock.is_valid()) { + _sock->close(); + } + bind_port = 0; + bind_address = IP_Address(); + List<Peer>::Element *E = peers.front(); + while (E) { + E->get().peer->disconnect_shared_socket(); + E = E->next(); + } + E = pending.front(); + while (E) { + E->get().peer->disconnect_shared_socket(); + memdelete(E->get().peer); + E = E->next(); + } + peers.clear(); + pending.clear(); +} + +UDPServer::UDPServer() : + _sock(Ref<NetSocket>(NetSocket::create())) { +} + +UDPServer::~UDPServer() { + stop(); +} diff --git a/core/io/udp_server.h b/core/io/udp_server.h new file mode 100644 index 0000000000..3175b09b19 --- /dev/null +++ b/core/io/udp_server.h @@ -0,0 +1,83 @@ +/*************************************************************************/ +/* udp_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 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 UDP_SERVER_H +#define UDP_SERVER_H + +#include "core/io/net_socket.h" +#include "core/io/packet_peer_udp.h" + +class UDPServer : public Reference { + GDCLASS(UDPServer, Reference); + +protected: + enum { + PACKET_BUFFER_SIZE = 65536 + }; + + struct Peer { + PacketPeerUDP *peer; + IP_Address ip; + uint16_t port = 0; + + bool operator==(const Peer &p_other) const { + return (ip == p_other.ip && port == p_other.port); + } + }; + uint8_t recv_buffer[PACKET_BUFFER_SIZE]; + + int bind_port = 0; + IP_Address bind_address; + + List<Peer> peers; + List<Peer> pending; + int max_pending_connections = 16; + + Ref<NetSocket> _sock; + + static void _bind_methods(); + +public: + void remove_peer(IP_Address p_ip, int p_port); + Error listen(uint16_t p_port, const IP_Address &p_bind_address = IP_Address("*")); + Error poll(); + bool is_listening() const; + bool is_connection_available() const; + void set_max_pending_connections(int p_max); + int get_max_pending_connections() const; + Ref<PacketPeerUDP> take_connection(); + + void stop(); + + UDPServer(); + ~UDPServer(); +}; + +#endif // UDP_SERVER_H diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index bd450dd84f..fc75ac7d1e 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -36,11 +36,13 @@ VARIANT_ENUM_CAST(XMLParser::NodeType); -static bool _equalsn(const CharType *str1, const CharType *str2, int len) { +static bool _equalsn(const char32_t *str1, const char32_t *str2, int len) { int i; - for (i = 0; i < len && str1[i] && str2[i]; ++i) - if (str1[i] != str2[i]) + for (i = 0; i < len && str1[i] && str2[i]; ++i) { + if (str1[i] != str2[i]) { return false; + } + } // if one (or both) of the strings was smaller then they // are only equal if they have the same length @@ -48,12 +50,12 @@ static bool _equalsn(const CharType *str1, const CharType *str2, int len) { } String XMLParser::_replace_special_characters(const String &origstr) { - int pos = origstr.find("&"); int oldPos = 0; - if (pos == -1) + if (pos == -1) { return origstr; + } String newstr; @@ -62,7 +64,7 @@ String XMLParser::_replace_special_characters(const String &origstr) { int specialChar = -1; for (int i = 0; i < (int)special_characters.size(); ++i) { - const CharType *p = &origstr[pos] + 1; + const char32_t *p = &origstr[pos] + 1; if (_equalsn(&special_characters[i][1], p, special_characters[i].length() - 1)) { specialChar = i; @@ -84,8 +86,9 @@ String XMLParser::_replace_special_characters(const String &origstr) { pos = origstr.find("&", pos); } - if (oldPos < origstr.length() - 1) + if (oldPos < origstr.length() - 1) { newstr += (origstr.substr(oldPos, origstr.length() - oldPos)); + } return newstr; } @@ -100,12 +103,15 @@ bool XMLParser::_set_text(char *start, char *end) { // only white space, so that this text won't be reported if (end - start < 3) { char *p = start; - for (; p != end; ++p) - if (!_is_white_space(*p)) + for (; p != end; ++p) { + if (!_is_white_space(*p)) { break; + } + } - if (p == end) + if (p == end) { return false; + } } // set current text to the parsed text, and replace xml special characters @@ -126,8 +132,9 @@ void XMLParser::_parse_closing_xml_element() { ++P; const char *pBeginClose = P; - while (*P != '>') + while (*P != '>') { ++P; + } node_name = String::utf8(pBeginClose, (int)(P - pBeginClose)); #ifdef DEBUG_XML @@ -141,16 +148,17 @@ void XMLParser::_ignore_definition() { char *F = P; // move until end marked with '>' reached - while (*P != '>') + while (*P != '>') { ++P; + } node_name.parse_utf8(F, P - F); ++P; } bool XMLParser::_parse_cdata() { - - if (*(P + 1) != '[') + if (*(P + 1) != '[') { return false; + } node_type = NODE_CDATA; @@ -161,11 +169,12 @@ bool XMLParser::_parse_cdata() { ++count; } - if (!*P) + if (!*P) { return true; + } char *cDataBegin = P; - char *cDataEnd = 0; + char *cDataEnd = nullptr; // find end of CDATA while (*P && !cDataEnd) { @@ -178,10 +187,11 @@ bool XMLParser::_parse_cdata() { ++P; } - if (cDataEnd) + if (cDataEnd) { node_name = String::utf8(cDataBegin, (int)(cDataEnd - cDataBegin)); - else + } else { node_name = ""; + } #ifdef DEBUG_XML print_line("XML CDATA: " + node_name); #endif @@ -190,7 +200,6 @@ bool XMLParser::_parse_cdata() { } void XMLParser::_parse_comment() { - node_type = NODE_COMMENT; P += 1; @@ -200,10 +209,11 @@ void XMLParser::_parse_comment() { // move until end of comment reached while (count) { - if (*P == '>') + if (*P == '>') { --count; - else if (*P == '<') + } else if (*P == '<') { ++count; + } ++P; } @@ -217,7 +227,6 @@ void XMLParser::_parse_comment() { } void XMLParser::_parse_opening_xml_element() { - node_type = NODE_ELEMENT; node_empty = false; attributes.clear(); @@ -226,46 +235,52 @@ void XMLParser::_parse_opening_xml_element() { const char *startName = P; // find end of element - while (*P != '>' && !_is_white_space(*P)) + while (*P != '>' && !_is_white_space(*P)) { ++P; + } const char *endName = P; // find attributes while (*P != '>') { - if (_is_white_space(*P)) + if (_is_white_space(*P)) { ++P; - else { + } else { if (*P != '/') { // we've got an attribute // read the attribute names const char *attributeNameBegin = P; - while (!_is_white_space(*P) && *P != '=') + while (!_is_white_space(*P) && *P != '=') { ++P; + } const char *attributeNameEnd = P; ++P; // read the attribute value // check for quotes and single quotes, thx to murphy - while ((*P != '\"') && (*P != '\'') && *P) + while ((*P != '\"') && (*P != '\'') && *P) { ++P; + } - if (!*P) // malformatted xml file + if (!*P) { // malformatted xml file return; + } const char attributeQuoteChar = *P; ++P; const char *attributeValueBegin = P; - while (*P != attributeQuoteChar && *P) + while (*P != attributeQuoteChar && *P) { ++P; + } - if (!*P) // malformatted xml file + if (!*P) { // malformatted xml file return; + } const char *attributeValueEnd = P; ++P; @@ -304,21 +319,23 @@ void XMLParser::_parse_opening_xml_element() { } void XMLParser::_parse_current_node() { - char *start = P; node_offset = P - data; // more forward until '<' found - while (*P != '<' && *P) + while (*P != '<' && *P) { ++P; + } - if (!*P) + if (!*P) { return; + } if (P - start > 0) { // we found some text, store it - if (_set_text(start, P)) + if (_set_text(start, P)) { return; + } } ++P; @@ -332,8 +349,9 @@ void XMLParser::_parse_current_node() { _ignore_definition(); break; case '!': - if (!_parse_cdata()) + if (!_parse_cdata()) { _parse_comment(); + } break; default: _parse_opening_xml_element(); @@ -342,22 +360,19 @@ void XMLParser::_parse_current_node() { } uint64_t XMLParser::get_node_offset() const { - return node_offset; -}; +} Error XMLParser::seek(uint64_t p_pos) { - ERR_FAIL_COND_V(!data, ERR_FILE_EOF); ERR_FAIL_COND_V(p_pos >= length, ERR_FILE_EOF); P = data + p_pos; return read(); -}; +} void XMLParser::_bind_methods() { - ClassDB::bind_method(D_METHOD("read"), &XMLParser::read); ClassDB::bind_method(D_METHOD("get_node_type"), &XMLParser::get_node_type); ClassDB::bind_method(D_METHOD("get_node_name"), &XMLParser::get_node_name); @@ -383,10 +398,9 @@ void XMLParser::_bind_methods() { BIND_ENUM_CONSTANT(NODE_COMMENT); BIND_ENUM_CONSTANT(NODE_CDATA); BIND_ENUM_CONSTANT(NODE_UNKNOWN); -}; +} Error XMLParser::read() { - // if not end reached, parse the node if (P && (P - data) < (int64_t)length - 1 && *P != 0) { _parse_current_node(); @@ -397,11 +411,10 @@ Error XMLParser::read() { } XMLParser::NodeType XMLParser::get_node_type() { - return node_type; } -String XMLParser::get_node_data() const { +String XMLParser::get_node_data() const { ERR_FAIL_COND_V(node_type != NODE_TEXT, ""); return node_name; } @@ -410,31 +423,32 @@ String XMLParser::get_node_name() const { ERR_FAIL_COND_V(node_type == NODE_TEXT, ""); return node_name; } -int XMLParser::get_attribute_count() const { +int XMLParser::get_attribute_count() const { return attributes.size(); } -String XMLParser::get_attribute_name(int p_idx) const { +String XMLParser::get_attribute_name(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, attributes.size(), ""); return attributes[p_idx].name; } -String XMLParser::get_attribute_value(int p_idx) const { +String XMLParser::get_attribute_value(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, attributes.size(), ""); return attributes[p_idx].value; } -bool XMLParser::has_attribute(const String &p_name) const { +bool XMLParser::has_attribute(const String &p_name) const { for (int i = 0; i < attributes.size(); i++) { - if (attributes[i].name == p_name) + if (attributes[i].name == p_name) { return true; + } } return false; } -String XMLParser::get_attribute_value(const String &p_name) const { +String XMLParser::get_attribute_value(const String &p_name) const { int idx = -1; for (int i = 0; i < attributes.size(); i++) { if (attributes[i].name == p_name) { @@ -449,7 +463,6 @@ String XMLParser::get_attribute_value(const String &p_name) const { } String XMLParser::get_attribute_value_safe(const String &p_name) const { - int idx = -1; for (int i = 0; i < attributes.size(); i++) { if (attributes[i].name == p_name) { @@ -458,17 +471,17 @@ String XMLParser::get_attribute_value_safe(const String &p_name) const { } } - if (idx < 0) + if (idx < 0) { return ""; + } return attributes[idx].value; } -bool XMLParser::is_empty() const { +bool XMLParser::is_empty() const { return node_empty; } Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) { - ERR_FAIL_COND_V(p_buffer.size() == 0, ERR_INVALID_DATA); if (data) { @@ -484,7 +497,6 @@ Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) { } Error XMLParser::open(const String &p_path) { - Error err; FileAccess *file = FileAccess::open(p_path, FileAccess::READ, &err); @@ -508,10 +520,10 @@ Error XMLParser::open(const String &p_path) { } void XMLParser::skip_section() { - // skip if this element is empty anyway. - if (is_empty()) + if (is_empty()) { return; + } // read until we've reached the last element in this section int tagcount = 1; @@ -520,40 +532,38 @@ void XMLParser::skip_section() { if (get_node_type() == XMLParser::NODE_ELEMENT && !is_empty()) { ++tagcount; - } else if (get_node_type() == XMLParser::NODE_ELEMENT_END) + } else if (get_node_type() == XMLParser::NODE_ELEMENT_END) { --tagcount; + } } } void XMLParser::close() { - - if (data) + if (data) { memdelete_arr(data); - data = NULL; + } + data = nullptr; length = 0; - P = NULL; + P = nullptr; node_empty = false; node_type = NODE_NONE; node_offset = 0; } int XMLParser::get_current_line() const { - return 0; } XMLParser::XMLParser() { - - data = NULL; - close(); special_characters.push_back("&"); special_characters.push_back("<lt;"); special_characters.push_back(">gt;"); special_characters.push_back("\"quot;"); special_characters.push_back("'apos;"); } -XMLParser::~XMLParser() { - if (data) +XMLParser::~XMLParser() { + if (data) { memdelete_arr(data); + } } diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h index 47e276da28..ee2174d52c 100644 --- a/core/io/xml_parser.h +++ b/core/io/xml_parser.h @@ -41,7 +41,6 @@ */ class XMLParser : public Reference { - GDCLASS(XMLParser, Reference); public: @@ -66,15 +65,15 @@ public: }; private: - char *data; - char *P; - uint64_t length; + char *data = nullptr; + char *P = nullptr; + uint64_t length = 0; void unescape(String &p_str); Vector<String> special_characters; String node_name; - bool node_empty; - NodeType node_type; - uint64_t node_offset; + bool node_empty = false; + NodeType node_type = NODE_NONE; + uint64_t node_offset = 0; struct Attribute { String name; @@ -121,4 +120,4 @@ public: ~XMLParser(); }; -#endif +#endif // XML_PARSER_H diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index 40e902d874..b8e7fd34d0 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -33,7 +33,6 @@ #include "core/os/copymem.h" void *zipio_open(void *data, const char *p_fname, int mode) { - FileAccess *&f = *(FileAccess **)data; String fname; @@ -42,42 +41,37 @@ void *zipio_open(void *data, const char *p_fname, int mode) { if (mode & ZLIB_FILEFUNC_MODE_WRITE) { f = FileAccess::open(fname, FileAccess::WRITE); } else { - f = FileAccess::open(fname, FileAccess::READ); } - if (!f) - return NULL; + if (!f) { + return nullptr; + } return data; } uLong zipio_read(void *data, void *fdata, void *buf, uLong size) { - FileAccess *f = *(FileAccess **)data; return f->get_buffer((uint8_t *)buf, size); } uLong zipio_write(voidpf opaque, voidpf stream, const void *buf, uLong size) { - FileAccess *f = *(FileAccess **)opaque; f->store_buffer((uint8_t *)buf, size); return size; } long zipio_tell(voidpf opaque, voidpf stream) { - FileAccess *f = *(FileAccess **)opaque; return f->get_position(); } long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { - FileAccess *f = *(FileAccess **)opaque; int pos = offset; switch (origin) { - case ZLIB_FILEFUNC_SEEK_CUR: pos = f->get_position() + offset; break; @@ -86,43 +80,38 @@ long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { break; default: break; - }; + } f->seek(pos); return 0; } int zipio_close(voidpf opaque, voidpf stream) { - FileAccess *&f = *(FileAccess **)opaque; if (f) { f->close(); memdelete(f); - f = NULL; + f = nullptr; } return 0; } int zipio_testerror(voidpf opaque, voidpf stream) { - FileAccess *f = *(FileAccess **)opaque; return (f && f->get_error() != OK) ? 1 : 0; } voidpf zipio_alloc(voidpf opaque, uInt items, uInt size) { - voidpf ptr = memalloc(items * size); zeromem(ptr, items * size); return ptr; } void zipio_free(voidpf opaque, voidpf address) { - memfree(address); } zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { - zlib_filefunc_def io; io.opaque = p_file; io.zopen_file = zipio_open; |