Procházet zdrojové kódy

-Added new scene conversion to binary on export (disabled by default, please test)
-This method works by directly converting text to binary, so the scene does not need to be loaded and saved

Juan Linietsky před 7 roky
rodič
revize
251433847f

+ 45 - 23
core/io/resource_format_binary.cpp

@@ -104,7 +104,7 @@ StringName ResourceInteractiveLoaderBinary::_get_string() {
 
 	uint32_t id = f->get_32();
 	if (id & 0x80000000) {
-		int len = id & 0x7FFFFFFF;
+		uint len = id & 0x7FFFFFFF;
 		if (len > str_buf.size()) {
 			str_buf.resize(len);
 		}
@@ -734,6 +734,7 @@ Error ResourceInteractiveLoaderBinary::poll() {
 	for (int i = 0; i < pc; i++) {
 
 		StringName name = _get_string();
+
 		if (name == StringName()) {
 			error = ERR_FILE_CORRUPT;
 			ERR_FAIL_V(ERR_FILE_CORRUPT);
@@ -902,7 +903,9 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
 
 		ExtResource er;
 		er.type = get_unicode_string();
+
 		er.path = get_unicode_string();
+
 		external_resources.push_back(er);
 	}
 
@@ -1271,7 +1274,7 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const
 ///////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////
 
-void ResourceFormatSaverBinaryInstance::_pad_buffer(int p_bytes) {
+void ResourceFormatSaverBinaryInstance::_pad_buffer(FileAccess *f, int p_bytes) {
 
 	int extra = 4 - (p_bytes % 4);
 	if (extra < 4) {
@@ -1280,7 +1283,12 @@ void ResourceFormatSaverBinaryInstance::_pad_buffer(int p_bytes) {
 	}
 }
 
-void ResourceFormatSaverBinaryInstance::write_variant(const Variant &p_property, const PropertyInfo &p_hint) {
+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()) {
 
@@ -1327,7 +1335,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant &p_property,
 
 			f->store_32(VARIANT_STRING);
 			String val = p_property;
-			save_unicode_string(val);
+			save_unicode_string(f, val);
 
 		} break;
 		case Variant::VECTOR2: {
@@ -1453,10 +1461,20 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant &p_property,
 			if (np.is_absolute())
 				snc |= 0x8000;
 			f->store_16(snc);
-			for (int i = 0; i < np.get_name_count(); i++)
-				f->store_32(get_string_index(np.get_name(i)));
-			for (int i = 0; i < np.get_subname_count(); i++)
-				f->store_32(get_string_index(np.get_subname(i)));
+			for (int i = 0; i < np.get_name_count(); i++) {
+				if (string_map.has(np.get_name(i))) {
+					f->store_32(string_map[np.get_name(i)]);
+				} else {
+					save_unicode_string(f, np.get_name(i), true);
+				}
+			}
+			for (int i = 0; i < np.get_subname_count(); i++) {
+				if (string_map.has(np.get_subname(i))) {
+					f->store_32(string_map[np.get_subname(i)]);
+				} else {
+					save_unicode_string(f, np.get_subname(i), true);
+				}
+			}
 
 		} break;
 		case Variant::_RID: {
@@ -1508,8 +1526,8 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant &p_property,
 					continue;
 				*/
 
-				write_variant(E->get());
-				write_variant(d[E->get()]);
+				write_variant(f, E->get(), resource_set, external_resources, string_map);
+				write_variant(f, d[E->get()], resource_set, external_resources, string_map);
 			}
 
 		} break;
@@ -1520,7 +1538,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant &p_property,
 			f->store_32(uint32_t(a.size()));
 			for (int i = 0; i < a.size(); i++) {
 
-				write_variant(a[i]);
+				write_variant(f, a[i], resource_set, external_resources, string_map);
 			}
 
 		} break;
@@ -1532,7 +1550,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant &p_property,
 			f->store_32(len);
 			PoolVector<uint8_t>::Read r = arr.read();
 			f->store_buffer(r.ptr(), len);
-			_pad_buffer(len);
+			_pad_buffer(f, len);
 
 		} break;
 		case Variant::POOL_INT_ARRAY: {
@@ -1566,7 +1584,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(const Variant &p_property,
 			f->store_32(len);
 			PoolVector<String>::Read r = arr.read();
 			for (int i = 0; i < len; i++) {
-				save_unicode_string(r[i]);
+				save_unicode_string(f, r[i]);
 			}
 
 		} break;
@@ -1693,10 +1711,14 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
 	}
 }
 
-void ResourceFormatSaverBinaryInstance::save_unicode_string(const String &p_string) {
+void ResourceFormatSaverBinaryInstance::save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len) {
 
 	CharString utf8 = p_string.utf8();
-	f->store_32(utf8.length() + 1);
+	if (p_bit_on_len) {
+		f->store_32(utf8.length() + 1 | 0x80000000);
+	} else {
+		f->store_32(utf8.length() + 1);
+	}
 	f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
 }
 
@@ -1763,7 +1785,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
 		return ERR_CANT_CREATE;
 	}
 
-	save_unicode_string(p_resource->get_class());
+	save_unicode_string(f, p_resource->get_class());
 	f->store_64(0); //offset to import metadata
 	for (int i = 0; i < 14; i++)
 		f->store_32(0); // reserved
@@ -1800,7 +1822,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
 
 	f->store_32(strings.size()); //string table size
 	for (int i = 0; i < strings.size(); i++) {
-		save_unicode_string(strings[i]);
+		save_unicode_string(f, strings[i]);
 	}
 
 	// save external resource table
@@ -1814,10 +1836,10 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
 
 	for (int i = 0; i < save_order.size(); i++) {
 
-		save_unicode_string(save_order[i]->get_save_class());
+		save_unicode_string(f, save_order[i]->get_save_class());
 		String path = save_order[i]->get_path();
 		path = relative_paths ? local_path.path_to_file(path) : path;
-		save_unicode_string(path);
+		save_unicode_string(f, path);
 	}
 	// save internal resource table
 	f->store_32(saved_resources.size()); //amount of internal resources
@@ -1853,7 +1875,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
 				used_indices.insert(new_subindex);
 			}
 
-			save_unicode_string("local://" + itos(r->get_subindex()));
+			save_unicode_string(f, "local://" + itos(r->get_subindex()));
 			if (takeover_paths) {
 				r->set_path(p_path + "::" + itos(r->get_subindex()), true);
 			}
@@ -1861,7 +1883,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
 			r->set_edited(false);
 #endif
 		} else {
-			save_unicode_string(r->get_path()); //actual external
+			save_unicode_string(f, r->get_path()); //actual external
 		}
 		ofs_pos.push_back(f->get_position());
 		f->store_64(0); //offset in 64 bits
@@ -1875,14 +1897,14 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
 		ResourceData &rd = E->get();
 
 		ofs_table.push_back(f->get_position());
-		save_unicode_string(rd.type);
+		save_unicode_string(f, rd.type);
 		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);
+			_write_variant(p.value, F->get().pi);
 		}
 	}
 

+ 4 - 3
core/io/resource_format_binary.h

@@ -140,14 +140,15 @@ class ResourceFormatSaverBinaryInstance {
 		List<Property> properties;
 	};
 
-	void _pad_buffer(int p_bytes);
-	void write_variant(const Variant &p_property, const PropertyInfo &p_hint = PropertyInfo());
+	static void _pad_buffer(FileAccess *f, int p_bytes);
+	void _write_variant(const Variant &p_property, const PropertyInfo &p_hint = PropertyInfo());
 	void _find_resources(const Variant &p_variant, bool p_main = false);
-	void save_unicode_string(const String &p_string);
+	static void save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false);
 	int get_string_index(const String &p_string);
 
 public:
 	Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
+	static void 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 = PropertyInfo());
 };
 
 class ResourceFormatSaverBinary : public ResourceFormatSaver {

+ 29 - 2
editor/editor_export.cpp

@@ -39,10 +39,10 @@
 #include "io/zip_io.h"
 #include "os/file_access.h"
 #include "project_settings.h"
+#include "scene/resources/scene_format_text.h"
 #include "script_language.h"
-#include "version.h"
-
 #include "thirdparty/misc/md5.h"
+#include "version.h"
 
 static int _get_pad(int p_alignment, int p_n) {
 
@@ -1399,3 +1399,30 @@ EditorExportPlatformPC::EditorExportPlatformPC() {
 
 	chmod_flags = -1;
 }
+
+///////////////////////
+
+void EditorExportTextSceneToBinaryPlugin::_export_file(const String &p_path, const String &p_type, const Set<String> &p_features) {
+
+	String extension = p_path.get_extension().to_lower();
+	if (extension != "tres" && extension != "tscn") {
+		return;
+	}
+
+	print_line("exporting " + p_path);
+
+	bool convert = GLOBAL_GET("editor/convert_text_resources_to_binary_on_export");
+	if (!convert)
+		return;
+	String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("file.res");
+	Error err = ResourceFormatLoaderText::convert_file_to_binary(p_path, tmp_path);
+	ERR_FAIL_COND(err != OK);
+	Vector<uint8_t> data = FileAccess::get_file_as_array(tmp_path);
+	ERR_FAIL_COND(data.size() == 0);
+	add_file(p_path + ".converted.res", data, true);
+}
+
+EditorExportTextSceneToBinaryPlugin::EditorExportTextSceneToBinaryPlugin() {
+
+	GLOBAL_DEF("editor/convert_text_resources_to_binary_on_export", false);
+}

+ 9 - 0
editor/editor_export.h

@@ -408,4 +408,13 @@ public:
 	EditorExportPlatformPC();
 };
 
+class EditorExportTextSceneToBinaryPlugin : public EditorExportPlugin {
+
+	GDCLASS(EditorExportTextSceneToBinaryPlugin, EditorExportPlugin)
+
+public:
+	virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features);
+	EditorExportTextSceneToBinaryPlugin();
+};
+
 #endif // EDITOR_IMPORT_EXPORT_H

+ 5 - 0
editor/editor_node.cpp

@@ -5718,6 +5718,11 @@ EditorNode::EditorNode() {
 	editor_plugins_force_over = memnew(EditorPluginList);
 	editor_plugins_force_input_forwarding = memnew(EditorPluginList);
 
+	Ref<EditorExportTextSceneToBinaryPlugin> export_text_to_binary_plugin;
+	export_text_to_binary_plugin.instance();
+
+	EditorExport::get_singleton()->add_export_plugin(export_text_to_binary_plugin);
+
 	_edit_current();
 	current = NULL;
 

+ 554 - 220
scene/resources/scene_format_text.cpp

@@ -28,7 +28,7 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 #include "scene_format_text.h"
-
+#include "core/io/resource_format_binary.h"
 #include "os/dir_access.h"
 #include "project_settings.h"
 #include "version.h"
@@ -53,6 +53,60 @@ Ref<Resource> ResourceInteractiveLoaderText::get_resource() {
 	return resource;
 }
 
+Error ResourceInteractiveLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
+
+	VariantParser::Token token;
+	VariantParser::get_token(p_stream, token, line, r_err_str);
+	if (token.type != VariantParser::TK_NUMBER) {
+		r_err_str = "Expected number (sub-resource index)";
+		return ERR_PARSE_ERROR;
+	}
+
+	int index = token.value;
+
+	if (!p_data->resource_map.has(index)) {
+		Ref<DummyResource> dr;
+		dr.instance();
+		dr->set_subindex(index);
+		p_data->resource_map[index] = dr;
+		p_data->resource_set.insert(dr);
+	}
+
+	r_res = p_data->resource_map[index];
+
+	VariantParser::get_token(p_stream, token, line, r_err_str);
+	if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
+		r_err_str = "Expected ')'";
+		return ERR_PARSE_ERROR;
+	}
+
+	return OK;
+}
+
+Error ResourceInteractiveLoaderText::_parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
+
+	VariantParser::Token token;
+	VariantParser::get_token(p_stream, token, line, r_err_str);
+	if (token.type != VariantParser::TK_NUMBER) {
+		r_err_str = "Expected number (sub-resource index)";
+		return ERR_PARSE_ERROR;
+	}
+
+	int id = token.value;
+
+	ERR_FAIL_COND_V(!p_data->rev_external_resources.has(id), ERR_PARSE_ERROR);
+
+	r_res = p_data->rev_external_resources[id];
+
+	VariantParser::get_token(p_stream, token, line, r_err_str);
+	if (token.type != VariantParser::TK_PARENTHESIS_CLOSE) {
+		r_err_str = "Expected ')'";
+		return ERR_PARSE_ERROR;
+	}
+
+	return OK;
+}
+
 Error ResourceInteractiveLoaderText::_parse_sub_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) {
 
 	VariantParser::Token token;
@@ -131,6 +185,203 @@ Error ResourceInteractiveLoaderText::_parse_ext_resource(VariantParser::Stream *
 	return OK;
 }
 
+Ref<PackedScene> ResourceInteractiveLoaderText::_parse_node_tag(VariantParser::ResourceParser &parser) {
+	Ref<PackedScene> packed_scene;
+	packed_scene.instance();
+
+	while (true) {
+
+		if (next_tag.name == "node") {
+
+			int parent = -1;
+			int owner = -1;
+			int type = -1;
+			int name = -1;
+			int instance = -1;
+			//int base_scene=-1;
+
+			if (next_tag.fields.has("name")) {
+				name = packed_scene->get_state()->add_name(next_tag.fields["name"]);
+			}
+
+			if (next_tag.fields.has("parent")) {
+				NodePath np = next_tag.fields["parent"];
+				np.prepend_period(); //compatible to how it manages paths internally
+				parent = packed_scene->get_state()->add_node_path(np);
+			}
+
+			if (next_tag.fields.has("type")) {
+				type = packed_scene->get_state()->add_name(next_tag.fields["type"]);
+			} else {
+				type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced
+			}
+
+			if (next_tag.fields.has("instance")) {
+
+				instance = packed_scene->get_state()->add_value(next_tag.fields["instance"]);
+
+				if (packed_scene->get_state()->get_node_count() == 0 && parent == -1) {
+					packed_scene->get_state()->set_base_scene(instance);
+					instance = -1;
+				}
+			}
+
+			if (next_tag.fields.has("instance_placeholder")) {
+
+				String path = next_tag.fields["instance_placeholder"];
+
+				int path_v = packed_scene->get_state()->add_value(path);
+
+				if (packed_scene->get_state()->get_node_count() == 0) {
+					error = ERR_FILE_CORRUPT;
+					error_text = "Instance Placeholder can't be used for inheritance.";
+					_printerr();
+					return Ref<PackedScene>();
+				}
+
+				instance = path_v | SceneState::FLAG_INSTANCE_IS_PLACEHOLDER;
+			}
+
+			if (next_tag.fields.has("owner")) {
+				owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]);
+			} else {
+				if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1))
+					owner = 0; //if no owner, owner is root
+			}
+
+			int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance);
+
+			if (next_tag.fields.has("groups")) {
+
+				Array groups = next_tag.fields["groups"];
+				for (int i = 0; i < groups.size(); i++) {
+					packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(groups[i]));
+				}
+			}
+
+			while (true) {
+
+				String assign;
+				Variant value;
+
+				error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &parser);
+
+				if (error) {
+					if (error != ERR_FILE_EOF) {
+						_printerr();
+						return Ref<PackedScene>();
+					} else {
+						return packed_scene;
+					}
+				}
+
+				if (assign != String()) {
+					int nameidx = packed_scene->get_state()->add_name(assign);
+					int valueidx = packed_scene->get_state()->add_value(value);
+					packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx);
+					//it's assignment
+				} else if (next_tag.name != String()) {
+					break;
+				}
+			}
+		} else if (next_tag.name == "connection") {
+
+			if (!next_tag.fields.has("from")) {
+				error = ERR_FILE_CORRUPT;
+				error_text = "missing 'from' field fron connection tag";
+				return Ref<PackedScene>();
+			}
+
+			if (!next_tag.fields.has("to")) {
+				error = ERR_FILE_CORRUPT;
+				error_text = "missing 'to' field fron connection tag";
+				return Ref<PackedScene>();
+			}
+
+			if (!next_tag.fields.has("signal")) {
+				error = ERR_FILE_CORRUPT;
+				error_text = "missing 'signal' field fron connection tag";
+				return Ref<PackedScene>();
+			}
+
+			if (!next_tag.fields.has("method")) {
+				error = ERR_FILE_CORRUPT;
+				error_text = "missing 'method' field fron connection tag";
+				return Ref<PackedScene>();
+			}
+
+			NodePath from = next_tag.fields["from"];
+			NodePath to = next_tag.fields["to"];
+			StringName method = next_tag.fields["method"];
+			StringName signal = next_tag.fields["signal"];
+			int flags = CONNECT_PERSIST;
+			Array binds;
+
+			if (next_tag.fields.has("flags")) {
+				flags = next_tag.fields["flags"];
+			}
+
+			if (next_tag.fields.has("binds")) {
+				binds = next_tag.fields["binds"];
+			}
+
+			Vector<int> bind_ints;
+			for (int i = 0; i < binds.size(); i++) {
+				bind_ints.push_back(packed_scene->get_state()->add_value(binds[i]));
+			}
+
+			packed_scene->get_state()->add_connection(
+					packed_scene->get_state()->add_node_path(from.simplified()),
+					packed_scene->get_state()->add_node_path(to.simplified()),
+					packed_scene->get_state()->add_name(signal),
+					packed_scene->get_state()->add_name(method),
+					flags,
+					bind_ints);
+
+			error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser);
+
+			if (error) {
+				if (error != ERR_FILE_EOF) {
+					_printerr();
+					return Ref<PackedScene>();
+				} else {
+					return packed_scene;
+				}
+			}
+		} else if (next_tag.name == "editable") {
+
+			if (!next_tag.fields.has("path")) {
+				error = ERR_FILE_CORRUPT;
+				error_text = "missing 'path' field fron connection tag";
+				_printerr();
+				return Ref<PackedScene>();
+			}
+
+			NodePath path = next_tag.fields["path"];
+
+			packed_scene->get_state()->add_editable_instance(path.simplified());
+
+			error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &parser);
+
+			if (error) {
+				if (error != ERR_FILE_EOF) {
+					_printerr();
+					return Ref<PackedScene>();
+				} else {
+					return packed_scene;
+				}
+			}
+		} else {
+
+			error = ERR_FILE_CORRUPT;
+			_printerr();
+			return Ref<PackedScene>();
+		}
+	}
+
+	return packed_scene;
+}
+
 Error ResourceInteractiveLoaderText::poll() {
 
 	if (error != OK)
@@ -364,231 +615,21 @@ Error ResourceInteractiveLoaderText::poll() {
 			return error;
 		}
 
-		/*
-		int add_name(const StringName& p_name);
-		int add_value(const Variant& p_value);
-		int add_node_path(const NodePath& p_path);
-		int add_node(int p_parent,int p_owner,int p_type,int p_name, int p_instance);
-		void add_node_property(int p_node,int p_name,int p_value);
-		void add_node_group(int p_node,int p_group);
-		void set_base_scene(int p_idx);
-		void add_connection(int p_from,int p_to, int p_signal, int p_method, int p_flags,const Vector<int>& p_binds);
-		void add_editable_instance(const NodePath& p_path);
-
-		*/
-
-		int parent = -1;
-		int owner = -1;
-		int type = -1;
-		int name = -1;
-		int instance = -1;
-		//int base_scene=-1;
-
-		if (next_tag.fields.has("name")) {
-			name = packed_scene->get_state()->add_name(next_tag.fields["name"]);
-		}
-
-		if (next_tag.fields.has("parent")) {
-			NodePath np = next_tag.fields["parent"];
-			np.prepend_period(); //compatible to how it manages paths internally
-			parent = packed_scene->get_state()->add_node_path(np);
-		}
-
-		if (next_tag.fields.has("type")) {
-			type = packed_scene->get_state()->add_name(next_tag.fields["type"]);
-		} else {
-			type = SceneState::TYPE_INSTANCED; //no type? assume this was instanced
-		}
-
-		if (next_tag.fields.has("instance")) {
-
-			instance = packed_scene->get_state()->add_value(next_tag.fields["instance"]);
-
-			if (packed_scene->get_state()->get_node_count() == 0 && parent == -1) {
-				packed_scene->get_state()->set_base_scene(instance);
-				instance = -1;
-			}
-		}
-
-		if (next_tag.fields.has("instance_placeholder")) {
-
-			String path = next_tag.fields["instance_placeholder"];
-
-			int path_v = packed_scene->get_state()->add_value(path);
-
-			if (packed_scene->get_state()->get_node_count() == 0) {
-				error = ERR_FILE_CORRUPT;
-				error_text = "Instance Placeholder can't be used for inheritance.";
-				_printerr();
-				return error;
-			}
-
-			instance = path_v | SceneState::FLAG_INSTANCE_IS_PLACEHOLDER;
-		}
-
-		if (next_tag.fields.has("owner")) {
-			owner = packed_scene->get_state()->add_node_path(next_tag.fields["owner"]);
-		} else {
-			if (parent != -1 && !(type == SceneState::TYPE_INSTANCED && instance == -1))
-				owner = 0; //if no owner, owner is root
-		}
-
-		int node_id = packed_scene->get_state()->add_node(parent, owner, type, name, instance);
-
-		if (next_tag.fields.has("groups")) {
-
-			Array groups = next_tag.fields["groups"];
-			for (int i = 0; i < groups.size(); i++) {
-				packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(groups[i]));
-			}
-		}
-
-		while (true) {
-
-			String assign;
-			Variant value;
-
-			error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
-
-			if (error) {
-				if (error != ERR_FILE_EOF) {
-					_printerr();
-				} else {
-					resource = packed_scene;
-					if (!ResourceCache::has(res_path)) {
-						packed_scene->set_path(res_path);
-					}
-				}
-				return error;
-			}
-
-			if (assign != String()) {
-				int nameidx = packed_scene->get_state()->add_name(assign);
-				int valueidx = packed_scene->get_state()->add_value(value);
-				packed_scene->get_state()->add_node_property(node_id, nameidx, valueidx);
-				//it's assignment
-			} else if (next_tag.name != String()) {
-
-				error = OK;
-				return error;
-			} else {
-
-				resource = packed_scene;
-				error = ERR_FILE_EOF;
-				return error;
-			}
-		}
-
-		return OK;
-
-	} else if (next_tag.name == "connection") {
-
-		if (!is_scene) {
-
-			error_text += "found the 'connection' tag on a resource file!";
-			_printerr();
-			error = ERR_FILE_CORRUPT;
-			return error;
-		}
-
-		if (!next_tag.fields.has("from")) {
-			error = ERR_FILE_CORRUPT;
-			error_text = "missing 'from' field fron connection tag";
-			return error;
-		}
-
-		if (!next_tag.fields.has("to")) {
-			error = ERR_FILE_CORRUPT;
-			error_text = "missing 'to' field fron connection tag";
-			return error;
-		}
-
-		if (!next_tag.fields.has("signal")) {
-			error = ERR_FILE_CORRUPT;
-			error_text = "missing 'signal' field fron connection tag";
-			return error;
-		}
-
-		if (!next_tag.fields.has("method")) {
-			error = ERR_FILE_CORRUPT;
-			error_text = "missing 'method' field fron connection tag";
-			return error;
-		}
-
-		NodePath from = next_tag.fields["from"];
-		NodePath to = next_tag.fields["to"];
-		StringName method = next_tag.fields["method"];
-		StringName signal = next_tag.fields["signal"];
-		int flags = CONNECT_PERSIST;
-		Array binds;
-
-		if (next_tag.fields.has("flags")) {
-			flags = next_tag.fields["flags"];
-		}
-
-		if (next_tag.fields.has("binds")) {
-			binds = next_tag.fields["binds"];
-		}
-
-		Vector<int> bind_ints;
-		for (int i = 0; i < binds.size(); i++) {
-			bind_ints.push_back(packed_scene->get_state()->add_value(binds[i]));
-		}
-
-		packed_scene->get_state()->add_connection(
-				packed_scene->get_state()->add_node_path(from.simplified()),
-				packed_scene->get_state()->add_node_path(to.simplified()),
-				packed_scene->get_state()->add_name(signal),
-				packed_scene->get_state()->add_name(method),
-				flags,
-				bind_ints);
-
-		error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
-
-		if (error) {
-			if (error != ERR_FILE_EOF) {
-				_printerr();
-			} else {
-				resource = packed_scene;
-			}
-		}
-
-		return error;
-	} else if (next_tag.name == "editable") {
+		Ref<PackedScene> packed_scene = _parse_node_tag(rp);
 
-		if (!is_scene) {
-
-			error_text += "found the 'editable' tag on a resource file!";
-			_printerr();
-			error = ERR_FILE_CORRUPT;
+		if (!packed_scene.is_valid())
 			return error;
-		}
 
-		if (!next_tag.fields.has("path")) {
-			error = ERR_FILE_CORRUPT;
-			error_text = "missing 'path' field fron connection tag";
-			_printerr();
-			return error;
+		error = OK;
+		//get it here
+		resource = packed_scene;
+		if (!ResourceCache::has(res_path)) {
+			packed_scene->set_path(res_path);
 		}
 
-		NodePath path = next_tag.fields["path"];
-
-		packed_scene->get_state()->add_editable_instance(path.simplified());
-
-		error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
-
-		if (error) {
-			if (error != ERR_FILE_EOF) {
-				_printerr();
-			} else {
-				resource = packed_scene;
-			}
-		}
-
-		return error;
+		return ERR_FILE_EOF;
 
 	} else {
-
 		error_text += "Unknown tag in file: " + next_tag.name;
 		_printerr();
 		error = ERR_FILE_CORRUPT;
@@ -804,7 +845,6 @@ void ResourceInteractiveLoaderText::open(FileAccess *p_f, bool p_skip_first_tag)
 
 	if (tag.name == "gd_scene") {
 		is_scene = true;
-		packed_scene.instance();
 
 	} else if (tag.name == "gd_resource") {
 		if (!tag.fields.has("type")) {
@@ -846,6 +886,281 @@ void ResourceInteractiveLoaderText::open(FileAccess *p_f, bool p_skip_first_tag)
 	rp.userdata = this;
 }
 
+static void bs_save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false) {
+
+	CharString utf8 = p_string.utf8();
+	if (p_bit_on_len) {
+		f->store_32(utf8.length() + 1 | 0x80000000);
+	} else {
+		f->store_32(utf8.length() + 1);
+	}
+	f->store_buffer((const uint8_t *)utf8.get_data(), utf8.length() + 1);
+}
+
+Error ResourceInteractiveLoaderText::save_as_binary(FileAccess *p_f, const String &p_path) {
+
+	if (error)
+		return error;
+
+	FileAccessRef wf = FileAccess::open(p_path, FileAccess::WRITE);
+	if (!wf) {
+		return ERR_CANT_OPEN;
+	}
+
+	//save header compressed
+	static const uint8_t header[4] = { 'R', 'S', 'R', 'C' };
+	wf->store_buffer(header, 4);
+
+	wf->store_32(0); //endianness, little endian
+	wf->store_32(0); //64 bits file, false for now
+	wf->store_32(VERSION_MAJOR);
+	wf->store_32(VERSION_MINOR);
+	static const int save_format_version = 3; //use format version 3 for saving
+	wf->store_32(save_format_version);
+
+	bs_save_unicode_string(wf.f, is_scene ? "PackedScene" : resource_type);
+	wf->store_64(0); //offset to import metadata, this is no longer used
+	for (int i = 0; i < 14; i++)
+		wf->store_32(0); // reserved
+
+	wf->store_32(0); //string table size, will not be in use
+	size_t ext_res_count_pos = wf->get_position();
+
+	wf->store_32(0); //zero ext resources, still parsing them
+
+	//go with external resources
+
+	DummyReadData dummy_read;
+	VariantParser::ResourceParser rp;
+	rp.ext_func = _parse_ext_resource_dummys;
+	rp.sub_func = _parse_sub_resource_dummys;
+	rp.userdata = &dummy_read;
+
+	while (next_tag.name == "ext_resource") {
+
+		if (!next_tag.fields.has("path")) {
+			error = ERR_FILE_CORRUPT;
+			error_text = "Missing 'path' in external resource tag";
+			_printerr();
+			return error;
+		}
+
+		if (!next_tag.fields.has("type")) {
+			error = ERR_FILE_CORRUPT;
+			error_text = "Missing 'type' in external resource tag";
+			_printerr();
+			return error;
+		}
+
+		if (!next_tag.fields.has("id")) {
+			error = ERR_FILE_CORRUPT;
+			error_text = "Missing 'id' in external resource tag";
+			_printerr();
+			return error;
+		}
+
+		String path = next_tag.fields["path"];
+		String type = next_tag.fields["type"];
+		int index = next_tag.fields["id"];
+
+		bs_save_unicode_string(wf.f, type);
+		bs_save_unicode_string(wf.f, path);
+
+		int lindex = dummy_read.external_resources.size();
+		Ref<DummyResource> dr;
+		dr.instance();
+		dr->set_path("res://dummy" + itos(lindex)); //anything is good to detect it for saving as external
+		dummy_read.external_resources[dr] = lindex;
+		dummy_read.rev_external_resources[index] = dr;
+
+		error = VariantParser::parse_tag(&stream, lines, error_text, next_tag, &rp);
+
+		if (error) {
+			_printerr();
+			return error;
+		}
+	}
+
+	// save external resource table
+	wf->seek(ext_res_count_pos);
+	wf->store_32(dummy_read.external_resources.size());
+	wf->seek_end();
+
+	//now, save resources to a separate file, for now
+
+	size_t sub_res_count_pos = wf->get_position();
+	wf->store_32(0); //zero sub resources, still parsing them
+
+	String temp_file = p_path + ".temp";
+	FileAccessRef wf2 = FileAccess::open(temp_file, FileAccess::WRITE);
+	if (!wf2) {
+		return ERR_CANT_OPEN;
+	}
+
+	Vector<size_t> local_offsets;
+	Vector<size_t> local_pointers_pos;
+
+	while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
+
+		String type;
+		int id = -1;
+		bool main_res;
+
+		if (next_tag.name == "sub_resource") {
+			if (!next_tag.fields.has("type")) {
+				error = ERR_FILE_CORRUPT;
+				error_text = "Missing 'type' in external resource tag";
+				_printerr();
+				return error;
+			}
+
+			if (!next_tag.fields.has("id")) {
+				error = ERR_FILE_CORRUPT;
+				error_text = "Missing 'index' in external resource tag";
+				_printerr();
+				return error;
+			}
+
+			type = next_tag.fields["type"];
+			id = next_tag.fields["id"];
+			main_res = false;
+		} else {
+			type = res_type;
+			id = 0; //used for last anyway
+			main_res = true;
+		}
+
+		local_offsets.push_back(wf2->get_position());
+
+		bs_save_unicode_string(wf, "local://" + itos(id));
+		local_pointers_pos.push_back(wf->get_position());
+		wf->store_64(0); //temp local offset
+
+		bs_save_unicode_string(wf2, type);
+		size_t propcount_ofs = wf2->get_position();
+		wf2->store_32(0);
+
+		int prop_count = 0;
+
+		while (true) {
+
+			String assign;
+			Variant value;
+
+			error = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, &rp);
+
+			if (error) {
+				if (main_res && error == ERR_FILE_EOF) {
+					next_tag.name = ""; //exit
+					break;
+				}
+
+				_printerr();
+				return error;
+			}
+
+			if (assign != String()) {
+
+				Map<StringName, int> empty_string_map; //unused
+				bs_save_unicode_string(wf2, assign, true);
+				ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map);
+				prop_count++;
+
+			} else if (next_tag.name != String()) {
+
+				error = OK;
+				break;
+			} else {
+				error = ERR_FILE_CORRUPT;
+				error_text = "Premature end of file while parsing [sub_resource]";
+				_printerr();
+				return error;
+			}
+		}
+
+		wf2->seek(propcount_ofs);
+		wf2->store_32(prop_count);
+		wf2->seek_end();
+	}
+
+	if (next_tag.name == "node") {
+		//this is a node, must save one more!
+
+		if (!is_scene) {
+
+			error_text += "found the 'node' tag on a resource file!";
+			_printerr();
+			error = ERR_FILE_CORRUPT;
+			return error;
+		}
+
+		Ref<PackedScene> packed_scene = _parse_node_tag(rp);
+
+		if (!packed_scene.is_valid())
+			return error;
+
+		error = OK;
+		//get it here
+		List<PropertyInfo> props;
+		packed_scene->get_property_list(&props);
+
+		bs_save_unicode_string(wf, "local://0");
+		local_pointers_pos.push_back(wf->get_position());
+		wf->store_64(0); //temp local offset
+
+		local_offsets.push_back(wf2->get_position());
+		bs_save_unicode_string(wf2, "PackedScene");
+		size_t propcount_ofs = wf2->get_position();
+		wf2->store_32(0);
+
+		int prop_count = 0;
+
+		for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+
+			if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
+				continue;
+
+			String name = E->get().name;
+			Variant value = packed_scene->get(name);
+
+			Map<StringName, int> empty_string_map; //unused
+			bs_save_unicode_string(wf2, name, true);
+			ResourceFormatSaverBinaryInstance::write_variant(wf2, value, dummy_read.resource_set, dummy_read.external_resources, empty_string_map);
+			prop_count++;
+		}
+
+		wf2->seek(propcount_ofs);
+		wf2->store_32(prop_count);
+		wf2->seek_end();
+	}
+
+	wf2->close();
+
+	size_t offset_from = wf->get_position();
+	wf->seek(sub_res_count_pos); //plus one because the saved one
+	wf->store_32(local_offsets.size());
+
+	for (int i = 0; i < local_offsets.size(); i++) {
+		wf->seek(local_pointers_pos[i]);
+		wf->store_64(local_offsets[i] + offset_from);
+	}
+
+	wf->seek_end();
+
+	Vector<uint8_t> data = FileAccess::get_file_as_array(temp_file);
+	wf->store_buffer(data.ptr(), data.size());
+	{
+		DirAccessRef dar = DirAccess::open(temp_file.get_base_dir());
+		dar->remove(temp_file);
+	}
+
+	wf->store_buffer((const uint8_t *)"RSRC", 4); //magic at end
+
+	wf->close();
+
+	return OK;
+}
+
 String ResourceInteractiveLoaderText::recognize(FileAccess *p_f) {
 
 	error = OK;
@@ -991,6 +1306,25 @@ Error ResourceFormatLoaderText::rename_dependencies(const String &p_path, const
 	return ria->rename_dependencies(f, p_path, p_map);
 }
 
+Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path, const String &p_dst_path) {
+
+	Error err;
+	FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err);
+
+	if (err != OK) {
+
+		ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN);
+	}
+
+	Ref<ResourceInteractiveLoaderText> ria = memnew(ResourceInteractiveLoaderText);
+	String path = p_src_path;
+	ria->local_path = ProjectSettings::get_singleton()->localize_path(path);
+	ria->res_path = ria->local_path;
+	//ria->set_local_path( ProjectSettings::get_singleton()->localize_path(p_path) );
+	ria->open(f);
+	return ria->save_as_binary(f, p_dst_path);
+}
+
 /*****************************************************************************************************/
 /*****************************************************************************************************/
 /*****************************************************************************************************/

+ 24 - 2
scene/resources/scene_format_text.h

@@ -78,9 +78,26 @@ class ResourceInteractiveLoaderText : public ResourceInteractiveLoader {
 	Error _parse_sub_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
 	Error _parse_ext_resource(VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
 
-	VariantParser::ResourceParser rp;
+	// for converter
+	class DummyResource : public Resource {
+	public:
+	};
 
-	Ref<PackedScene> packed_scene;
+	struct DummyReadData {
+
+		Map<RES, int> external_resources;
+		Map<int, RES> rev_external_resources;
+		Set<RES> resource_set;
+		Map<int, RES> resource_map;
+	};
+
+	static Error _parse_sub_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_sub_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); }
+	static Error _parse_ext_resource_dummys(void *p_self, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str) { return _parse_ext_resource_dummy((DummyReadData *)(p_self), p_stream, r_res, line, r_err_str); }
+
+	static Error _parse_sub_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
+	static Error _parse_ext_resource_dummy(DummyReadData *p_data, VariantParser::Stream *p_stream, Ref<Resource> &r_res, int &line, String &r_err_str);
+
+	VariantParser::ResourceParser rp;
 
 	friend class ResourceFormatLoaderText;
 
@@ -89,6 +106,8 @@ class ResourceInteractiveLoaderText : public ResourceInteractiveLoader {
 
 	RES resource;
 
+	Ref<PackedScene> _parse_node_tag(VariantParser::ResourceParser &parser);
+
 public:
 	virtual void set_local_path(const String &p_local_path);
 	virtual Ref<Resource> get_resource();
@@ -102,6 +121,7 @@ public:
 	void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types);
 	Error rename_dependencies(FileAccess *p_f, const String &p_path, const Map<String, String> &p_map);
 
+	Error save_as_binary(FileAccess *p_f, const String &p_path);
 	ResourceInteractiveLoaderText();
 	~ResourceInteractiveLoaderText();
 };
@@ -115,6 +135,8 @@ public:
 	virtual String get_resource_type(const String &p_path) const;
 	virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false);
 	virtual Error rename_dependencies(const String &p_path, const Map<String, String> &p_map);
+
+	static Error convert_file_to_binary(const String &p_src_path, const String &p_dst_path);
 };
 
 class ResourceFormatSaverTextInstance {