/*************************************************************************/
/*  export.cpp                                                           */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2018 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 "export.h"
#include "editor/editor_import_export.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "globals.h"
#include "io/marshalls.h"
#include "io/xml_parser.h"
#include "io/zip_io.h"
#include "os/file_access.h"
#include "os/os.h"
#include "platform/bb10/logo.gen.h"
#include "version.h"

#define MAX_DEVICES 5

class EditorExportPlatformBB10 : public EditorExportPlatform {

	OBJ_TYPE(EditorExportPlatformBB10, EditorExportPlatform);

	String custom_package;

	int version_code;
	String version_name;
	String package;
	String name;
	String category;
	String description;
	String author_name;
	String author_id;
	String icon;

	struct Device {

		int index;
		String name;
		String description;
	};

	Vector<Device> devices;
	bool devices_changed;
	Mutex *device_lock;
	Thread *device_thread;
	Ref<ImageTexture> logo;

	volatile bool quit_request;

	static void _device_poll_thread(void *ud);

	void _fix_descriptor(Vector<uint8_t> &p_manifest);

protected:
	bool _set(const StringName &p_name, const Variant &p_value);
	bool _get(const StringName &p_name, Variant &r_ret) const;
	void _get_property_list(List<PropertyInfo> *p_list) const;

public:
	virtual String get_name() const { return "BlackBerry 10"; }
	virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_ETC1; }
	virtual Ref<Texture> get_logo() const { return logo; }

	virtual bool poll_devices();
	virtual int get_device_count() const;
	virtual String get_device_name(int p_device) const;
	virtual String get_device_info(int p_device) const;
	virtual Error run(int p_device, int p_flags = 0);

	virtual bool requires_password(bool p_debug) const { return !p_debug; }
	virtual String get_binary_extension() const { return "bar"; }
	virtual Error export_project(const String &p_path, bool p_debug, int p_flags = 0);

	virtual bool can_export(String *r_error = NULL) const;

	EditorExportPlatformBB10();
	~EditorExportPlatformBB10();
};

bool EditorExportPlatformBB10::_set(const StringName &p_name, const Variant &p_value) {

	String n = p_name;

	if (n == "version/code")
		version_code = p_value;
	else if (n == "version/name")
		version_name = p_value;
	else if (n == "package/unique_name")
		package = p_value;
	else if (n == "package/category")
		category = p_value;
	else if (n == "package/name")
		name = p_value;
	else if (n == "package/description")
		description = p_value;
	else if (n == "package/icon")
		icon = p_value;
	else if (n == "package/custom_template")
		custom_package = p_value;
	else if (n == "release/author")
		author_name = p_value;
	else if (n == "release/author_id")
		author_id = p_value;
	else
		return false;

	return true;
}

bool EditorExportPlatformBB10::_get(const StringName &p_name, Variant &r_ret) const {

	String n = p_name;

	if (n == "version/code")
		r_ret = version_code;
	else if (n == "version/name")
		r_ret = version_name;
	else if (n == "package/unique_name")
		r_ret = package;
	else if (n == "package/category")
		r_ret = category;
	else if (n == "package/name")
		r_ret = name;
	else if (n == "package/description")
		r_ret = description;
	else if (n == "package/icon")
		r_ret = icon;
	else if (n == "package/custom_template")
		r_ret = custom_package;
	else if (n == "release/author")
		r_ret = author_name;
	else if (n == "release/author_id")
		r_ret = author_id;
	else
		return false;

	return true;
}
void EditorExportPlatformBB10::_get_property_list(List<PropertyInfo> *p_list) const {

	p_list->push_back(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,65535,1"));
	p_list->push_back(PropertyInfo(Variant::STRING, "version/name"));
	p_list->push_back(PropertyInfo(Variant::STRING, "package/unique_name"));
	p_list->push_back(PropertyInfo(Variant::STRING, "package/category"));
	p_list->push_back(PropertyInfo(Variant::STRING, "package/name"));
	p_list->push_back(PropertyInfo(Variant::STRING, "package/description", PROPERTY_HINT_MULTILINE_TEXT));
	p_list->push_back(PropertyInfo(Variant::STRING, "package/icon", PROPERTY_HINT_FILE, "png"));
	p_list->push_back(PropertyInfo(Variant::STRING, "package/custom_template", PROPERTY_HINT_GLOBAL_FILE, "zip"));
	p_list->push_back(PropertyInfo(Variant::STRING, "release/author"));
	p_list->push_back(PropertyInfo(Variant::STRING, "release/author_id"));

	//p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)"));
}

void EditorExportPlatformBB10::_fix_descriptor(Vector<uint8_t> &p_descriptor) {

	String fpath = EditorSettings::get_singleton()->get_settings_path().plus_file("tmp_bar-settings.xml");
	{
		FileAccessRef f = FileAccess::open(fpath, FileAccess::WRITE);
		f->store_buffer(p_descriptor.ptr(), p_descriptor.size());
	}

	Ref<XMLParser> parser = memnew(XMLParser);
	Error err = parser->open(fpath);
	ERR_FAIL_COND(err != OK);

	String txt;
	err = parser->read();
	Vector<String> depth;

	while (err != ERR_FILE_EOF) {

		ERR_FAIL_COND(err != OK);

		switch (parser->get_node_type()) {

			case XMLParser::NODE_NONE: {
				print_line("???");
			} break;
			case XMLParser::NODE_ELEMENT: {
				String e = "<";
				e += parser->get_node_name();
				for (int i = 0; i < parser->get_attribute_count(); i++) {
					e += " ";
					e += parser->get_attribute_name(i) + "=\"";
					e += parser->get_attribute_value(i) + "\" ";
				}

				if (parser->is_empty()) {
					e += "/";
				} else {
					depth.push_back(parser->get_node_name());
				}

				e += ">";
				txt += e;

			} break;
			case XMLParser::NODE_ELEMENT_END: {

				txt += "</" + parser->get_node_name() + ">";
				if (depth.size() && depth[depth.size() - 1] == parser->get_node_name()) {
					depth.resize(depth.size() - 1);
				}

			} break;
			case XMLParser::NODE_TEXT: {
				if (depth.size() == 2 && depth[0] == "qnx" && depth[1] == "id") {

					txt += package;
				} else if (depth.size() == 2 && depth[0] == "qnx" && depth[1] == "name") {

					String aname;
					if (this->name != "") {
						aname = this->name;
					} else {
						aname = Globals::get_singleton()->get("application/name");
					}

					if (aname == "") {
						aname = _MKSTR(VERSION_NAME);
					}

					txt += aname;

				} else if (depth.size() == 2 && depth[0] == "qnx" && depth[1] == "versionNumber") {
					txt += itos(version_code);
				} else if (depth.size() == 2 && depth[0] == "qnx" && depth[1] == "description") {
					txt += description;
				} else if (depth.size() == 2 && depth[0] == "qnx" && depth[1] == "author") {
					txt += author_name;
				} else if (depth.size() == 2 && depth[0] == "qnx" && depth[1] == "authorId") {
					txt += author_id;
				} else if (depth.size() == 2 && depth[0] == "qnx" && depth[1] == "category") {
					txt += category;
				} else {
					txt += parser->get_node_data();
				}
			} break;
			case XMLParser::NODE_COMMENT: {
				txt += "<!--" + parser->get_node_name() + "-->";
			} break;
			case XMLParser::NODE_CDATA: {
				//ignore
				//print_line("cdata");
			} break;
			case XMLParser::NODE_UNKNOWN: {
				//ignore
				txt += "<" + parser->get_node_name() + ">";
			} break;
		}

		err = parser->read();
	}

	CharString cs = txt.utf8();
	p_descriptor.resize(cs.length());
	for (int i = 0; i < cs.length(); i++)
		p_descriptor[i] = cs[i];
}

Error EditorExportPlatformBB10::export_project(const String &p_path, bool p_debug, int p_flags) {

	EditorProgress ep("export", "Exporting for BlackBerry 10", 104);

	String src_template = custom_package;

	if (src_template == "") {
		String err;
		src_template = find_export_template("bb10.zip", &err);
		if (src_template == "") {
			EditorNode::add_io_error(err);
			return ERR_FILE_NOT_FOUND;
		}
	}

	FileAccess *src_f = NULL;
	zlib_filefunc_def io = zipio_create_io_from_file(&src_f);

	ep.step("Creating FileSystem for BAR", 0);

	unzFile pkg = unzOpen2(src_template.utf8().get_data(), &io);
	if (!pkg) {

		EditorNode::add_io_error("Could not find template zip to export:\n" + src_template);
		return ERR_FILE_NOT_FOUND;
	}

	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
	da->change_dir(EditorSettings::get_singleton()->get_settings_path());

	if (da->change_dir("tmp") != OK) {
		da->make_dir("tmp");
		if (da->change_dir("tmp") != OK)
			return ERR_CANT_CREATE;
	}

	if (da->change_dir("bb10_export") != OK) {
		da->make_dir("bb10_export");
		if (da->change_dir("bb10_export") != OK) {
			return ERR_CANT_CREATE;
		}
	}

	String bar_dir = da->get_current_dir();
	if (bar_dir.ends_with("/")) {
		bar_dir = bar_dir.substr(0, bar_dir.length() - 1);
	}

	//THIS IS SUPER, SUPER DANGEROUS!!!!
	//CAREFUL WITH THIS CODE, MIGHT DELETE USERS HARD DRIVE OR HOME DIR
	//EXTRA CHECKS ARE IN PLACE EVERYWERE TO MAKE SURE NOTHING BAD HAPPENS BUT STILL....
	//BE SUPER CAREFUL WITH THIS PLEASE!!!
	//BLACKBERRY THIS IS YOUR FAULT FOR NOT MAKING A BETTER WAY!!

	bool berr = bar_dir.ends_with("bb10_export");
	if (berr) {
		if (da->list_dir_begin()) {
			EditorNode::add_io_error("Can't ensure that dir is empty:\n" + bar_dir);
			ERR_FAIL_COND_V(berr, FAILED);
		};

		String f = da->get_next();
		while (f != "") {

			if (f == "." || f == "..") {
				f = da->get_next();
				continue;
			};
			Error err = da->remove(bar_dir + "/" + f);
			if (err != OK) {
				EditorNode::add_io_error("Can't ensure that dir is empty:\n" + bar_dir);
				ERR_FAIL_COND_V(err != OK, err);
			};
			f = da->get_next();
		};

		da->list_dir_end();

	} else {
		print_line("ARE YOU CRAZY??? THIS IS A SERIOUS BUG HERE!!!");
		ERR_FAIL_V(ERR_OMFG_THIS_IS_VERY_VERY_BAD);
	}

	ERR_FAIL_COND_V(!pkg, ERR_CANT_OPEN);
	int ret = unzGoToFirstFile(pkg);

	while (ret == UNZ_OK) {

		//get filename
		unz_file_info info;
		char fname[16384];
		ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, NULL, 0, NULL, 0);

		String file = fname;

		Vector<uint8_t> data;
		data.resize(info.uncompressed_size);

		//read
		unzOpenCurrentFile(pkg);
		unzReadCurrentFile(pkg, data.ptr(), data.size());
		unzCloseCurrentFile(pkg);

		//write

		if (file == "bar-descriptor.xml") {

			_fix_descriptor(data);
		}

		if (file == "icon.png") {
			bool found = false;

			if (this->icon != "" && this->icon.ends_with(".png")) {

				FileAccess *f = FileAccess::open(this->icon, FileAccess::READ);
				if (f) {

					data.resize(f->get_len());
					f->get_buffer(data.ptr(), data.size());
					memdelete(f);
					found = true;
				}
			}

			if (!found) {

				String appicon = Globals::get_singleton()->get("application/icon");
				if (appicon != "" && appicon.ends_with(".png")) {
					FileAccess *f = FileAccess::open(appicon, FileAccess::READ);
					if (f) {
						data.resize(f->get_len());
						f->get_buffer(data.ptr(), data.size());
						memdelete(f);
					}
				}
			}
		}

		if (file.find("/")) {

			da->make_dir_recursive(file.get_base_dir());
		}

		FileAccessRef wf = FileAccess::open(bar_dir.plus_file(file), FileAccess::WRITE);
		wf->store_buffer(data.ptr(), data.size());

		ret = unzGoToNextFile(pkg);
	}

	ep.step("Adding Files..", 2);

	FileAccess *dst = FileAccess::open(bar_dir + "/data.pck", FileAccess::WRITE);
	if (!dst) {
		EditorNode::add_io_error("Can't copy executable file to:\n " + p_path);
		return ERR_FILE_CANT_WRITE;
	}
	save_pack(dst, false, 1024);
	dst->close();
	memdelete(dst);

	ep.step("Creating BAR Package..", 104);

	String bb_packager = EditorSettings::get_singleton()->get("blackberry/host_tools");
	bb_packager = bb_packager.plus_file("blackberry-nativepackager");
	if (OS::get_singleton()->get_name() == "Windows")
		bb_packager += ".bat";

	if (!FileAccess::exists(bb_packager)) {
		EditorNode::add_io_error("Can't find packager:\n" + bb_packager);
		return ERR_CANT_OPEN;
	}

	List<String> args;
	args.push_back("-package");
	args.push_back(p_path);
	if (p_debug) {

		String debug_token = EditorSettings::get_singleton()->get("blackberry/debug_token");
		if (!FileAccess::exists(debug_token)) {
			EditorNode::add_io_error("Debug token not found!");
		} else {
			args.push_back("-debugToken");
			args.push_back(debug_token);
		}
		args.push_back("-devMode");
		args.push_back("-configuration");
		args.push_back("Device-Debug");
	} else {

		args.push_back("-configuration");
		args.push_back("Device-Release");
	}
	args.push_back(bar_dir.plus_file("bar-descriptor.xml"));

	int ec;

	Error err = OS::get_singleton()->execute(bb_packager, args, true, NULL, NULL, &ec);

	if (err != OK)
		return err;
	if (ec != 0)
		return ERR_CANT_CREATE;

	return OK;
}

bool EditorExportPlatformBB10::poll_devices() {

	bool dc = devices_changed;
	devices_changed = false;
	return dc;
}

int EditorExportPlatformBB10::get_device_count() const {

	device_lock->lock();
	int dc = devices.size();
	device_lock->unlock();

	return dc;
}
String EditorExportPlatformBB10::get_device_name(int p_device) const {

	ERR_FAIL_INDEX_V(p_device, devices.size(), "");
	device_lock->lock();
	String s = devices[p_device].name;
	device_lock->unlock();
	return s;
}
String EditorExportPlatformBB10::get_device_info(int p_device) const {

	ERR_FAIL_INDEX_V(p_device, devices.size(), "");
	device_lock->lock();
	String s = devices[p_device].description;
	device_lock->unlock();
	return s;
}

void EditorExportPlatformBB10::_device_poll_thread(void *ud) {

	EditorExportPlatformBB10 *ea = (EditorExportPlatformBB10 *)ud;

	while (!ea->quit_request) {

		String bb_deploy = EditorSettings::get_singleton()->get("blackberry/host_tools");
		bb_deploy = bb_deploy.plus_file("blackberry-deploy");
		bool windows = OS::get_singleton()->get_name() == "Windows";
		if (windows)
			bb_deploy += ".bat";

		if (FileAccess::exists(bb_deploy)) {

			Vector<Device> devices;

			for (int i = 0; i < MAX_DEVICES; i++) {

				String host = EditorSettings::get_singleton()->get("blackberry/device_" + itos(i + 1) + "/host");
				if (host == String())
					continue;
				String pass = EditorSettings::get_singleton()->get("blackberry/device_" + itos(i + 1) + "/password");
				if (pass == String())
					continue;

				List<String> args;
				args.push_back("-listDeviceInfo");
				args.push_back(host);
				args.push_back("-password");
				args.push_back(pass);

				int ec;
				String dp;

				Error err = OS::get_singleton()->execute(bb_deploy, args, true, NULL, &dp, &ec);

				if (err == OK && ec == 0) {

					Device dev;
					dev.index = i;
					String descr;
					Vector<String> ls = dp.split("\n");

					for (int i = 0; i < ls.size(); i++) {

						String l = ls[i].strip_edges();
						if (l.begins_with("modelfullname::")) {
							dev.name = l.get_slice("::", 1);
							descr += "Model: " + dev.name + "\n";
						}
						if (l.begins_with("modelnumber::")) {
							String s = l.get_slice("::", 1);
							dev.name += " (" + s + ")";
							descr += "Model Number: " + s + "\n";
						}
						if (l.begins_with("scmbundle::"))
							descr += "OS Version: " + l.get_slice("::", 1) + "\n";
						if (l.begins_with("[n]debug_token_expiration::"))
							descr += "Debug Token Expires:: " + l.get_slice("::", 1) + "\n";
					}

					dev.description = descr;
					devices.push_back(dev);
				}
			}

			bool changed = false;

			ea->device_lock->lock();

			if (ea->devices.size() != devices.size()) {
				changed = true;
			} else {

				for (int i = 0; i < ea->devices.size(); i++) {

					if (ea->devices[i].index != devices[i].index) {
						changed = true;
						break;
					}
				}
			}

			if (changed) {

				ea->devices = devices;
				ea->devices_changed = true;
			}

			ea->device_lock->unlock();
		}

		uint64_t wait = 3000000;
		uint64_t time = OS::get_singleton()->get_ticks_usec();
		while (OS::get_singleton()->get_ticks_usec() - time < wait) {
			OS::get_singleton()->delay_usec(1000);
			if (ea->quit_request)
				break;
		}
	}
}

Error EditorExportPlatformBB10::run(int p_device, int p_flags) {

	ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER);

	String bb_deploy = EditorSettings::get_singleton()->get("blackberry/host_tools");
	bb_deploy = bb_deploy.plus_file("blackberry-deploy");
	if (OS::get_singleton()->get_name() == "Windows")
		bb_deploy += ".bat";

	if (!FileAccess::exists(bb_deploy)) {
		EditorNode::add_io_error("Blackberry Deploy not found:\n" + bb_deploy);
		return ERR_FILE_NOT_FOUND;
	}

	device_lock->lock();

	EditorProgress ep("run", "Running on " + devices[p_device].name, 3);

	//export_temp
	ep.step("Exporting APK", 0);

	String export_to = EditorSettings::get_singleton()->get_settings_path().plus_file("/tmp/tmpexport.bar");
	Error err = export_project(export_to, true, p_flags);
	if (err) {
		device_lock->unlock();
		return err;
	}
#if 0
	ep.step("Uninstalling..",1);

	print_line("Uninstalling previous version: "+devices[p_device].name);
	List<String> args;
	args.push_back("-s");
	args.push_back(devices[p_device].id);
	args.push_back("uninstall");
	args.push_back(package);
	int rv;
	err = OS::get_singleton()->execute(adb,args,true,NULL,NULL,&rv);

	if (err || rv!=0) {
		EditorNode::add_io_error("Could not install to device.");
		device_lock->unlock();
		return ERR_CANT_CREATE;
	}

	print_line("Installing into device (please wait..): "+devices[p_device].name);

#endif
	ep.step("Installing to Device (please wait..)..", 2);

	List<String> args;
	args.clear();
	args.push_back("-installApp");
	args.push_back("-launchApp");
	args.push_back("-device");
	String host = EditorSettings::get_singleton()->get("blackberry/device_" + itos(p_device + 1) + "/host");
	String pass = EditorSettings::get_singleton()->get("blackberry/device_" + itos(p_device + 1) + "/password");
	args.push_back(host);
	args.push_back("-password");
	args.push_back(pass);
	args.push_back(export_to);

	int rv;
	err = OS::get_singleton()->execute(bb_deploy, args, true, NULL, NULL, &rv);
	if (err || rv != 0) {
		EditorNode::add_io_error("Could not install to device.");
		device_lock->unlock();
		return ERR_CANT_CREATE;
	}

	device_lock->unlock();
	return OK;
}

EditorExportPlatformBB10::EditorExportPlatformBB10() {

	version_code = 1;
	version_name = "1.0";
	package = "com.godot.noname";
	category = "core.games";
	name = "";
	author_name = "Cert. Name";
	author_id = "Cert. ID";
	description = "Game made with Godot Engine";

	device_lock = Mutex::create();
	quit_request = false;

	// Don't start polling devices if we can't export anyway. Means that an engine restart is needed to enable it though,
	// but it feels better than querying the editor settings all the time in the thread.
	if (can_export()) {
		device_thread = Thread::create(_device_poll_thread, this);
		devices_changed = true;
	} else {
		device_thread = NULL;
	}

	Image img(_bb10_logo);
	logo = Ref<ImageTexture>(memnew(ImageTexture));
	logo->create_from_image(img);
}

bool EditorExportPlatformBB10::can_export(String *r_error) const {

	bool valid = true;
	String bb_deploy = EditorSettings::get_singleton()->get("blackberry/host_tools");
	String err;

	if (!FileAccess::exists(bb_deploy.plus_file("blackberry-deploy"))) {

		valid = false;
		err += "Blackberry host tools not configured in editor settings.\n";
	}

	if (!exists_export_template("bb10.zip")) {
		valid = false;
		err += "No export template found.\nDownload and install export templates.\n";
	}

	String debug_token = EditorSettings::get_singleton()->get("blackberry/debug_token");

	if (!FileAccess::exists(debug_token)) {
		valid = false;
		err += "No debug token set, will not be able to test on device.\n";
	}

	if (custom_package != "" && !FileAccess::exists(custom_package)) {
		valid = false;
		err += "Custom release package not found.\n";
	}

	if (r_error)
		*r_error = err;

	return valid;
}

EditorExportPlatformBB10::~EditorExportPlatformBB10() {

	quit_request = true;
	if (device_thread) {
		Thread::wait_to_finish(device_thread);
		memdelete(device_thread);
		device_thread = NULL;
	}

	if (device_lock) {
		memdelete(device_lock);
		device_lock = NULL;
	}
}

void register_bb10_exporter() {

	EDITOR_DEF("blackberry/host_tools", "");
	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "blackberry/host_tools", PROPERTY_HINT_GLOBAL_DIR));
	EDITOR_DEF("blackberry/debug_token", "");
	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "blackberry/debug_token", PROPERTY_HINT_GLOBAL_FILE, "bar"));
	EDITOR_DEF("blackberry/device_1/host", "");
	EDITOR_DEF("blackberry/device_1/password", "");
	EDITOR_DEF("blackberry/device_2/host", "");
	EDITOR_DEF("blackberry/device_2/password", "");
	EDITOR_DEF("blackberry/device_3/host", "");
	EDITOR_DEF("blackberry/device_3/password", "");
	EDITOR_DEF("blackberry/device_4/host", "");
	EDITOR_DEF("blackberry/device_4/password", "");
	EDITOR_DEF("blackberry/device_5/host", "");
	EDITOR_DEF("blackberry/device_5/password", "");

	Ref<EditorExportPlatformBB10> exporter = Ref<EditorExportPlatformBB10>(memnew(EditorExportPlatformBB10));
	EditorImportExport::get_singleton()->add_export_platform(exporter);
}