summaryrefslogtreecommitdiff
path: root/core/io
diff options
context:
space:
mode:
Diffstat (limited to 'core/io')
-rw-r--r--core/io/file_access_buffered.h2
-rw-r--r--core/io/file_access_buffered_fa.h2
-rw-r--r--core/io/file_access_pack.h1
-rw-r--r--core/io/file_access_zip.cpp6
-rw-r--r--core/io/http_client.cpp98
-rw-r--r--core/io/http_client.h1
-rw-r--r--core/io/logger.h12
-rw-r--r--core/io/marshalls.cpp6
-rw-r--r--core/io/packet_peer.cpp2
-rw-r--r--core/io/resource_format_binary.cpp53
-rw-r--r--core/io/resource_format_binary.h9
-rw-r--r--core/io/resource_importer.cpp (renamed from core/io/resource_import.cpp)61
-rw-r--r--core/io/resource_importer.h (renamed from core/io/resource_import.h)28
-rw-r--r--core/io/resource_loader.cpp187
-rw-r--r--core/io/resource_loader.h32
-rw-r--r--core/io/resource_saver.cpp8
16 files changed, 403 insertions, 105 deletions
diff --git a/core/io/file_access_buffered.h b/core/io/file_access_buffered.h
index 756045a674..4065d77c58 100644
--- a/core/io/file_access_buffered.h
+++ b/core/io/file_access_buffered.h
@@ -31,8 +31,8 @@
#ifndef FILE_ACCESS_BUFFERED_H
#define FILE_ACCESS_BUFFERED_H
-#include "core/dvector.h"
#include "core/os/file_access.h"
+#include "core/pool_vector.h"
#include "core/ustring.h"
class FileAccessBuffered : public FileAccess {
diff --git a/core/io/file_access_buffered_fa.h b/core/io/file_access_buffered_fa.h
index 5180f2344f..be960fbc25 100644
--- a/core/io/file_access_buffered_fa.h
+++ b/core/io/file_access_buffered_fa.h
@@ -54,7 +54,7 @@ class FileAccessBufferedFA : public FileAccessBuffered {
cache.offset = p_offset;
cache.buffer.resize(p_size);
- // on dvector
+ // on PoolVector
//PoolVector<uint8_t>::Write write = cache.buffer.write();
//f.get_buffer(write.ptrw(), p_size);
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index ae5e83d405..a90672ce26 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -223,7 +223,6 @@ public:
virtual String get_filesystem_type() const;
-
DirAccessPack();
~DirAccessPack();
};
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index 66b911e83c..be28c9a877 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -168,10 +168,10 @@ bool ZipArchive::try_open_pack(const String &p_path) {
zlib_filefunc_def io;
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- if (!f)
+ FileAccess *fa = FileAccess::open(p_path, FileAccess::READ);
+ if (!fa)
return false;
- io.opaque = f;
+ io.opaque = fa;
io.zopen_file = godot_open;
io.zread_file = godot_read;
io.zwrite_file = godot_write;
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index b3fd814870..e5c6d2a4f2 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -278,6 +278,7 @@ void HTTPClient::close() {
body_size = -1;
body_left = 0;
chunk_left = 0;
+ chunk_trailer_part = 0;
read_until_eof = false;
response_num = 0;
handshaking = false;
@@ -421,16 +422,16 @@ Error HTTPClient::poll() {
chunked = false;
body_left = 0;
chunk_left = 0;
+ chunk_trailer_part = false;
read_until_eof = false;
response_str.clear();
response_headers.clear();
response_num = RESPONSE_OK;
- // Per the HTTP 1.1 spec, keep-alive is the default, but in practice
- // it's safe to assume it only if the explicit header is found, allowing
- // to handle body-up-to-EOF responses on naive servers; that's what Curl
- // and browsers do
- bool keep_alive = false;
+ // Per the HTTP 1.1 spec, keep-alive is the default.
+ // Not following that specification breaks standard implemetations.
+ // Broken web servers should be fixed.
+ bool keep_alive = true;
for (int i = 0; i < responses.size(); i++) {
@@ -447,8 +448,8 @@ Error HTTPClient::poll() {
if (encoding == "chunked") {
chunked = true;
}
- } else if (s.begins_with("connection: keep-alive")) {
- keep_alive = true;
+ } else if (s.begins_with("connection: close")) {
+ keep_alive = false;
}
if (i == 0 && responses[i].begins_with("HTTP")) {
@@ -505,13 +506,37 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
ERR_FAIL_COND_V(status != STATUS_BODY, PoolByteArray());
+ PoolByteArray ret;
Error err = OK;
if (chunked) {
while (true) {
- if (chunk_left == 0) {
+ 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)
+ break;
+
+ chunk.push_back(b);
+ int cs = chunk.size();
+ if ((cs >= 2 && chunk[cs - 2] == '\r' && chunk[cs - 1] == '\n')) {
+ if (cs == 2) {
+ // Finally over
+ chunk_trailer_part = false;
+ status = STATUS_CONNECTED;
+ chunk.clear();
+ break;
+ } else {
+ // We do not process nor return the trailer data
+ chunk.clear();
+ }
+ }
+ } else if (chunk_left == 0) {
// Reading length
uint8_t b;
int rec = 0;
@@ -525,7 +550,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
if (chunk.size() > 32) {
ERR_PRINT("HTTP Invalid chunk hex len");
status = STATUS_CONNECTION_ERROR;
- return PoolByteArray();
+ break;
}
if (chunk.size() > 2 && chunk[chunk.size() - 2] == '\r' && chunk[chunk.size() - 1] == '\n') {
@@ -543,22 +568,22 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
else {
ERR_PRINT("HTTP Chunk len not in hex!!");
status = STATUS_CONNECTION_ERROR;
- return PoolByteArray();
+ break;
}
len <<= 4;
len |= v;
if (len > (1 << 24)) {
ERR_PRINT("HTTP Chunk too big!! >16mb");
status = STATUS_CONNECTION_ERROR;
- return PoolByteArray();
+ break;
}
}
if (len == 0) {
// End reached!
- status = STATUS_CONNECTED;
+ chunk_trailer_part = true;
chunk.clear();
- return PoolByteArray();
+ break;
}
chunk_left = len + 2;
@@ -578,18 +603,13 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
status = STATUS_CONNECTION_ERROR;
- return PoolByteArray();
+ break;
}
- PoolByteArray ret;
ret.resize(chunk.size() - 2);
- {
- PoolByteArray::Write w = ret.write();
- copymem(w.ptr(), chunk.ptr(), chunk.size() - 2);
- }
+ PoolByteArray::Write w = ret.write();
+ copymem(w.ptr(), chunk.ptr(), chunk.size() - 2);
chunk.clear();
-
- return ret;
}
break;
@@ -599,45 +619,26 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
} else {
int to_read = !read_until_eof ? MIN(body_left, read_chunk_size) : read_chunk_size;
- PoolByteArray ret;
ret.resize(to_read);
int _offset = 0;
- while (read_until_eof || to_read > 0) {
+ while (to_read > 0) {
int rec = 0;
{
PoolByteArray::Write w = ret.write();
err = _get_http_data(w.ptr() + _offset, to_read, rec);
}
- if (rec < 0) {
- if (to_read > 0) // Ended up reading less
- ret.resize(_offset);
+ if (rec <= 0) { // Ended up reading less
+ ret.resize(_offset);
break;
} else {
_offset += rec;
+ to_read -= rec;
if (!read_until_eof) {
body_left -= rec;
- to_read -= rec;
- } else {
- if (rec < to_read) {
- ret.resize(_offset);
- err = ERR_FILE_EOF;
- break;
- }
- ret.resize(_offset + to_read);
}
}
- }
- if (!read_until_eof) {
- if (body_left == 0) {
- status = STATUS_CONNECTED;
- }
- return ret;
- } else {
- if (err == ERR_FILE_EOF) {
- err = OK; // EOF is expected here
- close();
- return ret;
- }
+ if (err != OK)
+ break;
}
}
@@ -652,12 +653,12 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
status = STATUS_CONNECTION_ERROR;
}
- } else if (body_left == 0 && !chunked) {
+ } else if (body_left == 0 && !chunked && !read_until_eof) {
status = STATUS_CONNECTED;
}
- return PoolByteArray();
+ return ret;
}
HTTPClient::Status HTTPClient::get_status() const {
@@ -719,6 +720,7 @@ HTTPClient::HTTPClient() {
body_left = 0;
read_until_eof = false;
chunk_left = 0;
+ chunk_trailer_part = false;
response_num = 0;
ssl = false;
blocking = false;
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 1ad44d5f01..85ee1959a2 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -172,6 +172,7 @@ private:
bool chunked;
Vector<uint8_t> chunk;
int chunk_left;
+ bool chunk_trailer_part;
int body_size;
int body_left;
bool read_until_eof;
diff --git a/core/io/logger.h b/core/io/logger.h
index 0b871a13de..ff5b8ce489 100644
--- a/core/io/logger.h
+++ b/core/io/logger.h
@@ -49,11 +49,11 @@ public:
ERR_SHADER
};
- virtual void logv(const char *p_format, va_list p_list, bool p_err) = 0;
+ virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0 = 0;
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
- void logf(const char *p_format, ...);
- void logf_error(const char *p_format, ...);
+ 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();
};
@@ -64,7 +64,7 @@ public:
class StdLogger : public Logger {
public:
- virtual void logv(const char *p_format, va_list p_list, bool p_err);
+ virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0;
virtual ~StdLogger();
};
@@ -88,7 +88,7 @@ class RotatedFileLogger : public Logger {
public:
RotatedFileLogger(const String &p_base_path, int p_max_files = 10);
- virtual void logv(const char *p_format, va_list p_list, bool p_err);
+ virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0;
virtual ~RotatedFileLogger();
};
@@ -99,7 +99,7 @@ class CompositeLogger : public Logger {
public:
CompositeLogger(Vector<Logger *> p_loggers);
- virtual void logv(const char *p_format, va_list p_list, bool p_err);
+ virtual void logv(const char *p_format, va_list p_list, bool p_err) _PRINTF_FORMAT_ATTRIBUTE_2_0;
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR);
void add_logger(Logger *p_logger);
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index eec1c55744..5087a63b68 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -889,11 +889,11 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
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 flags = 0;
+ uint32_t np_flags = 0;
if (np.is_absolute())
- flags |= 1;
+ np_flags |= 1;
- encode_uint32(flags, buf + 8);
+ encode_uint32(np_flags, buf + 8);
buf += 12;
}
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index 3aa1fcfd8f..d7bfdbbb37 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -224,7 +224,7 @@ Error PacketPeerStream::get_packet(const uint8_t **r_buffer, int &r_buffer_size)
uint32_t len = decode_uint32(lbuf);
ERR_FAIL_COND_V(remaining < (int)len, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(input_buffer.size() < len, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(input_buffer.size() < (int)len, ERR_UNAVAILABLE);
ring_buffer.read(lbuf, 4); //get rid of first 4 bytes
ring_buffer.read(input_buffer.ptrw(), len); // read packet
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index e27dea37d6..42070cd132 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -106,7 +106,7 @@ StringName ResourceInteractiveLoaderBinary::_get_string() {
uint32_t id = f->get_32();
if (id & 0x80000000) {
uint32_t len = id & 0x7FFFFFFF;
- if (len > str_buf.size()) {
+ if ((int)len > str_buf.size()) {
str_buf.resize(len);
}
if (len == 0)
@@ -296,9 +296,9 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
} break;
case VARIANT_OBJECT: {
- uint32_t type = f->get_32();
+ uint32_t objtype = f->get_32();
- switch (type) {
+ switch (objtype) {
case OBJECT_EMPTY: {
//do none
@@ -317,7 +317,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
case OBJECT_EXTERNAL_RESOURCE: {
//old file format, still around for compatibility
- String type = get_unicode_string();
+ String exttype = get_unicode_string();
String path = get_unicode_string();
if (path.find("://") == -1 && path.is_rel_path()) {
@@ -329,7 +329,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
path = remaps[path];
}
- RES res = ResourceLoader::load(path, type);
+ RES res = ResourceLoader::load(path, exttype);
if (res.is_null()) {
WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
@@ -342,11 +342,11 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
int erindex = f->get_32();
if (erindex < 0 || erindex >= external_resources.size()) {
- WARN_PRINT("Broken external resource! (index out of size");
+ WARN_PRINT("Broken external resource! (index out of size)");
r_v = Variant();
} else {
- String type = external_resources[erindex].type;
+ String exttype = external_resources[erindex].type;
String path = external_resources[erindex].path;
if (path.find("://") == -1 && path.is_rel_path()) {
@@ -354,7 +354,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
path = ProjectSettings::get_singleton()->localize_path(res_path.get_base_dir().plus_file(path));
}
- RES res = ResourceLoader::load(path, type);
+ RES res = ResourceLoader::load(path, exttype);
if (res.is_null()) {
WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data());
@@ -1314,8 +1314,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
} else {
f->store_32(VARIANT_INT);
- int val = p_property;
- f->store_32(int32_t(val));
+ f->store_32(int32_t(p_property));
}
} break;
@@ -1502,7 +1501,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
if (!resource_set.has(res)) {
f->store_32(OBJECT_EMPTY);
- ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?");
+ ERR_EXPLAIN("Resource was not pre cached for the resource section, most likely due to circular refedence.");
ERR_FAIL();
}
@@ -1651,6 +1650,10 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
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.");
+ return;
+ }
int idx = external_resources.size();
external_resources[res] = idx;
return;
@@ -1667,7 +1670,20 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
if (E->get().usage & PROPERTY_USAGE_STORAGE) {
- _find_resources(res->get(E->get().name));
+ Variant value = res->get(E->get().name);
+ if (E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ RES sres = value;
+ if (sres.is_valid()) {
+ NonPersistentKey npk;
+ npk.base = res;
+ npk.property = E->get().name;
+ non_persistent_map[npk] = sres;
+ resource_set.insert(sres);
+ saved_resources.push_back(sres);
+ }
+ } else {
+ _find_resources(value);
+ }
}
}
@@ -1762,6 +1778,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
takeover_paths = false;
local_path = p_path.get_base_dir();
+ path = ProjectSettings::get_singleton()->localize_path(p_path);
_find_resources(p_resource, true);
@@ -1811,7 +1828,17 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
if ((F->get().usage & PROPERTY_USAGE_STORAGE)) {
Property p;
p.name_idx = get_string_index(F->get().name);
- p.value = E->get()->get(F->get().name);
+
+ if (F->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) {
+ NonPersistentKey npk;
+ npk.base = E->get();
+ npk.property = F->get().name;
+ if (non_persistent_map.has(npk)) {
+ p.value = non_persistent_map[npk];
+ }
+ } else {
+ p.value = E->get()->get(F->get().name);
+ }
Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name);
diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h
index 8f2c48e745..a4894e4033 100644
--- a/core/io/resource_format_binary.h
+++ b/core/io/resource_format_binary.h
@@ -114,6 +114,7 @@ public:
class ResourceFormatSaverBinaryInstance {
String local_path;
+ String path;
bool relative_paths;
bool bundle_resources;
@@ -123,6 +124,14 @@ class ResourceFormatSaverBinaryInstance {
FileAccess *f;
String magic;
Set<RES> resource_set;
+
+ struct NonPersistentKey { //for resource properties generated on the fly
+ RES base;
+ StringName property;
+ bool operator<(const NonPersistentKey &p_key) const { return base == p_key.base ? property < p_key.property : base < p_key.base; }
+ };
+
+ Map<NonPersistentKey, RES> non_persistent_map;
Map<StringName, int> string_map;
Vector<StringName> strings;
diff --git a/core/io/resource_import.cpp b/core/io/resource_importer.cpp
index 63dc0b6a26..b5fa412576 100644
--- a/core/io/resource_import.cpp
+++ b/core/io/resource_importer.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_import.cpp */
+/* resource_importer.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,11 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "resource_import.h"
+#include "resource_importer.h"
#include "core/os/os.h"
#include "core/variant_parser.h"
+bool ResourceFormatImporter::SortImporterByName::operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const {
+ return p_a->get_importer_name() < p_b->get_importer_name();
+}
+
Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid) const {
Error err;
@@ -90,6 +94,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy
r_path_and_type.type = value;
} else if (assign == "importer") {
r_path_and_type.importer = value;
+ } else if (assign == "metadata") {
+ r_path_and_type.metadata = value;
} else if (assign == "valid") {
if (r_valid) {
*r_valid = value;
@@ -177,6 +183,11 @@ 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");
@@ -304,6 +315,18 @@ String ResourceFormatImporter::get_resource_type(const String &p_path) const {
return pat.type;
}
+Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const {
+ PathAndType pat;
+ Error err = _get_path_and_type(p_path, pat);
+
+ if (err != OK) {
+
+ return Variant();
+ }
+
+ return pat.metadata;
+}
+
void ResourceFormatImporter::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
PathAndType pat;
@@ -366,6 +389,40 @@ String ResourceFormatImporter::get_import_base_path(const String &p_for_file) co
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);
+
+ if (!valid) {
+ return false;
+ }
+
+ for (int i = 0; i < importers.size(); i++) {
+ if (importers[i]->get_importer_name() == pat.importer) {
+ if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+String ResourceFormatImporter::get_import_settings_hash() const {
+
+ Vector<Ref<ResourceImporter> > sorted_importers = importers;
+
+ sorted_importers.sort_custom<SortImporterByName>();
+
+ String hash;
+ for (int i = 0; i < sorted_importers.size(); i++) {
+ hash += ":" + sorted_importers[i]->get_importer_name() + ":" + sorted_importers[i]->get_import_settings_string();
+ }
+ return hash.md5_text();
+}
+
ResourceFormatImporter *ResourceFormatImporter::singleton = NULL;
ResourceFormatImporter::ResourceFormatImporter() {
diff --git a/core/io/resource_import.h b/core/io/resource_importer.h
index 96dd7983e6..1c146c33d7 100644
--- a/core/io/resource_import.h
+++ b/core/io/resource_importer.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* resource_import.h */
+/* resource_importer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCE_IMPORT_H
-#define RESOURCE_IMPORT_H
+#ifndef RESOURCE_IMPORTER_H
+#define RESOURCE_IMPORTER_H
#include "core/io/resource_loader.h"
@@ -43,12 +43,18 @@ class ResourceFormatImporter : public ResourceFormatLoader {
String path;
String type;
String importer;
+ Variant metadata;
};
Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = NULL) const;
static ResourceFormatImporter *singleton;
+ //need them to stay in order to compute the settings hash
+ struct SortImporterByName {
+ bool operator()(const Ref<ResourceImporter> &p_a, const Ref<ResourceImporter> &p_b) const;
+ };
+
Vector<Ref<ResourceImporter> > importers;
public:
@@ -59,8 +65,11 @@ public:
virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const;
virtual bool handles_type(const String &p_type) const;
virtual String get_resource_type(const String &p_path) const;
+ virtual Variant get_resource_metadata(const String &p_path) const;
virtual bool is_import_valid(const String &p_path) const;
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
+ virtual bool is_imported(const String &p_path) const { return recognize_path(p_path); }
+ virtual bool exists(const String &p_path) const;
virtual bool can_be_imported(const String &p_path) const;
virtual int get_import_order(const String &p_path) const;
@@ -68,12 +77,17 @@ public:
String get_internal_resource_path(const String &p_path) const;
void get_internal_resource_path_list(const String &p_path, List<String> *r_paths);
- void add_importer(const Ref<ResourceImporter> &p_importer) { importers.push_back(p_importer); }
+ void add_importer(const Ref<ResourceImporter> &p_importer) {
+ importers.push_back(p_importer);
+ }
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);
+ bool are_import_settings_valid(const String &p_path) const;
+ String get_import_settings_hash() const;
+
String get_import_base_path(const String &p_for_file) const;
ResourceFormatImporter();
};
@@ -107,7 +121,9 @@ public:
virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const = 0;
virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const = 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 = 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 = NULL, Variant *r_metadata = NULL) = 0;
+ virtual bool are_import_settings_valid(const String &p_path) const { return true; }
+ virtual String get_import_settings_string() const { return String(); }
};
-#endif // RESOURCE_IMPORT_H
+#endif // RESOURCE_IMPORTER_H
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index b8bc24c1de..e4b694b64f 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -30,7 +30,7 @@
#include "resource_loader.h"
-#include "core/io/resource_import.h"
+#include "core/io/resource_importer.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/path_remap.h"
@@ -53,6 +53,12 @@ Error ResourceInteractiveLoader::wait() {
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();
@@ -280,6 +286,63 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c
return RES();
}
+bool ResourceLoader::_add_to_loading_map(const String &p_path) {
+
+ bool success;
+ if (loading_map_mutex) {
+ loading_map_mutex->lock();
+ }
+
+ LoadingMapKey key;
+ key.path = p_path;
+ key.thread = Thread::get_caller_id();
+
+ if (loading_map.has(key)) {
+ success = false;
+ } else {
+ loading_map[key] = true;
+ success = true;
+ }
+
+ if (loading_map_mutex) {
+ loading_map_mutex->unlock();
+ }
+
+ return success;
+}
+
+void ResourceLoader::_remove_from_loading_map(const String &p_path) {
+ if (loading_map_mutex) {
+ loading_map_mutex->lock();
+ }
+
+ LoadingMapKey key;
+ key.path = p_path;
+ key.thread = Thread::get_caller_id();
+
+ loading_map.erase(key);
+
+ if (loading_map_mutex) {
+ loading_map_mutex->unlock();
+ }
+}
+
+void ResourceLoader::_remove_from_loading_map_and_thread(const String &p_path, Thread::ID p_thread) {
+ if (loading_map_mutex) {
+ loading_map_mutex->lock();
+ }
+
+ LoadingMapKey key;
+ key.path = p_path;
+ key.thread = p_thread;
+
+ loading_map.erase(key);
+
+ if (loading_map_mutex) {
+ loading_map_mutex->unlock();
+ }
+}
+
RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p_no_cache, Error *r_error) {
if (r_error)
@@ -292,6 +355,15 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
if (!p_no_cache) {
+
+ {
+ bool success = _add_to_loading_map(local_path);
+ if (!success) {
+ ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?");
+ ERR_FAIL_V(RES());
+ }
+ }
+
//lock first if possible
if (ResourceCache::lock) {
ResourceCache::lock->read_lock();
@@ -310,7 +382,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
if (ResourceCache::lock) {
ResourceCache::lock->read_unlock();
}
- print_verbose("Loading resource: " + local_path + " (cached)");
+ _remove_from_loading_map(local_path);
return res;
}
}
@@ -322,12 +394,21 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
bool xl_remapped = false;
String path = _path_remap(local_path, &xl_remapped);
- ERR_FAIL_COND_V(path == "", RES());
+ if (path == "") {
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
+ ERR_EXPLAIN("Remapping '" + local_path + "'failed.");
+ ERR_FAIL_V(RES());
+ }
print_verbose("Loading resource: " + path);
RES res = _load(path, local_path, p_type_hint, p_no_cache, r_error);
if (res.is_null()) {
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
return RES();
}
if (!p_no_cache)
@@ -346,6 +427,10 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
}
#endif
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
+
if (_loaded_callback) {
_loaded_callback(res, p_path);
}
@@ -394,19 +479,37 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
else
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
- if (!p_no_cache && ResourceCache::has(local_path)) {
+ if (!p_no_cache) {
- print_verbose("Loading resource: " + local_path + " (cached)");
- Ref<Resource> res_cached = ResourceCache::get(local_path);
- Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>(memnew(ResourceInteractiveLoaderDefault));
+ bool success = _add_to_loading_map(local_path);
+ if (!success) {
+ ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?");
+ ERR_FAIL_V(RES());
+ }
- ril->resource = res_cached;
- return ril;
+ 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);
- ERR_FAIL_COND_V(path == "", Ref<ResourceInteractiveLoader>());
+ if (path == "") {
+ if (!p_no_cache) {
+ _remove_from_loading_map(local_path);
+ }
+ ERR_EXPLAIN("Remapping '" + local_path + "'failed.");
+ ERR_FAIL_V(RES());
+ }
+
print_verbose("Loading resource: " + path);
bool found = false;
@@ -418,14 +521,22 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
Ref<ResourceInteractiveLoader> ril = loader[i]->load_interactive(path, local_path, r_error);
if (ril.is_null())
continue;
- if (!p_no_cache)
+ 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);
+ }
+
if (found) {
ERR_EXPLAIN("Failed loading resource: " + path);
} else {
@@ -522,6 +633,31 @@ bool ResourceLoader::is_import_valid(const String &p_path) {
return false; //not found
}
+bool ResourceLoader::is_imported(const String &p_path) {
+
+ String path = _path_remap(p_path);
+
+ String local_path;
+ if (path.is_rel_path())
+ local_path = "res://" + path;
+ else
+ local_path = ProjectSettings::get_singleton()->localize_path(path);
+
+ for (int i = 0; i < loader_count; i++) {
+
+ if (!loader[i]->recognize_path(local_path))
+ continue;
+ /*
+ if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint))
+ continue;
+ */
+
+ return loader[i]->is_imported(p_path);
+ }
+
+ return false; //not found
+}
+
void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {
String path = _path_remap(p_path);
@@ -794,9 +930,9 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
void ResourceLoader::remove_custom_resource_format_loader(String script_path) {
- Ref<ResourceFormatLoader> loader = _find_custom_resource_format_loader(script_path);
- if (loader.is_valid())
- remove_resource_format_loader(loader);
+ Ref<ResourceFormatLoader> custom_loader = _find_custom_resource_format_loader(script_path);
+ if (custom_loader.is_valid())
+ remove_resource_format_loader(custom_loader);
}
void ResourceLoader::add_custom_loaders() {
@@ -810,7 +946,7 @@ void ResourceLoader::add_custom_loaders() {
for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
StringName class_name = E->get();
- StringName base_class = ScriptServer::get_global_class_base(class_name);
+ StringName base_class = ScriptServer::get_global_class_native_base(class_name);
if (base_class == custom_loader_base_class) {
String path = ScriptServer::get_global_class_path(class_name);
@@ -833,6 +969,27 @@ 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
+}
+
+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
+}
+
ResourceLoadErrorNotify ResourceLoader::err_notify = NULL;
void *ResourceLoader::err_notify_ud = NULL;
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 6c0e9d5554..ca7610a0d2 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -31,8 +31,8 @@
#ifndef RESOURCE_LOADER_H
#define RESOURCE_LOADER_H
+#include "core/os/thread.h"
#include "core/resource.h"
-
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -40,6 +40,9 @@
class ResourceInteractiveLoader : public Reference {
GDCLASS(ResourceInteractiveLoader, Reference);
+ friend class ResourceLoader;
+ String path_loading;
+ Thread::ID path_loading_thread;
protected:
static void _bind_methods();
@@ -54,6 +57,7 @@ public:
virtual Error wait();
ResourceInteractiveLoader() {}
+ ~ResourceInteractiveLoader();
};
class ResourceFormatLoader : public Reference {
@@ -75,6 +79,7 @@ public:
virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
virtual bool is_import_valid(const String &p_path) const { return true; }
+ virtual bool is_imported(const String &p_path) const { return false; }
virtual int get_import_order(const String &p_path) const { return 0; }
virtual ~ResourceFormatLoader() {}
@@ -110,12 +115,33 @@ class ResourceLoader {
static SelfList<Resource>::List remapped_list;
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 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); }
+ };
+
+ static HashMap<LoadingMapKey, int, LoadingMapKeyHasher> loading_map;
+
+ 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);
public:
static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path, const String &p_type_hint = "", bool p_no_cache = false, Error *r_error = NULL);
@@ -129,6 +155,7 @@ public:
static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
static Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
static bool is_import_valid(const String &p_path);
+ static bool is_imported(const String &p_path);
static int get_import_order(const String &p_path);
static void set_timestamp_on_load(bool p_timestamp) { timestamp_on_load = p_timestamp; }
@@ -170,6 +197,9 @@ public:
static void remove_custom_resource_format_loader(String script_path);
static void add_custom_loaders();
static void remove_custom_loaders();
+
+ static void initialize();
+ static void finalize();
};
#endif
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 279788796c..0cecca904d 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -233,9 +233,9 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
void ResourceSaver::remove_custom_resource_format_saver(String script_path) {
- Ref<ResourceFormatSaver> saver = _find_custom_resource_format_saver(script_path);
- if (saver.is_valid())
- remove_resource_format_saver(saver);
+ Ref<ResourceFormatSaver> custom_saver = _find_custom_resource_format_saver(script_path);
+ if (custom_saver.is_valid())
+ remove_resource_format_saver(custom_saver);
}
void ResourceSaver::add_custom_savers() {
@@ -249,7 +249,7 @@ void ResourceSaver::add_custom_savers() {
for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
StringName class_name = E->get();
- StringName base_class = ScriptServer::get_global_class_base(class_name);
+ StringName base_class = ScriptServer::get_global_class_native_base(class_name);
if (base_class == custom_saver_base_class) {
String path = ScriptServer::get_global_class_path(class_name);