diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-02-09 22:10:30 -0300 |
commit | 0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch) | |
tree | 276c4d099e178eb67fbd14f61d77b05e3808e9e3 /platform/javascript/export | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'platform/javascript/export')
-rw-r--r-- | platform/javascript/export/export.cpp | 452 | ||||
-rw-r--r-- | platform/javascript/export/export.h | 29 |
2 files changed, 481 insertions, 0 deletions
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp new file mode 100644 index 0000000000..a81fabe085 --- /dev/null +++ b/platform/javascript/export/export.cpp @@ -0,0 +1,452 @@ +/*************************************************************************/ +/* export.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "version.h" +#include "export.h" +#include "tools/editor/editor_settings.h" +#include "tools/editor/editor_import_export.h" +#include "tools/editor/editor_node.h" +#include "io/zip_io.h" +#include "io/marshalls.h" +#include "globals.h" +#include "os/file_access.h" +#include "os/os.h" +#include "platform/javascript/logo.h" +#include "string.h" +class EditorExportPlatformJavaScript : public EditorExportPlatform { + + OBJ_TYPE( EditorExportPlatformJavaScript,EditorExportPlatform ); + + String custom_release_package; + String custom_debug_package; + + enum PackMode { + PACK_SINGLE_FILE, + PACK_MULTIPLE_FILES + }; + + + PackMode pack_mode; + + bool show_run; + + int max_memory; + int version_code; + + Ref<ImageTexture> logo; + + static Error save_pack_file_js(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total); + +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 "HTML5"; } + virtual ImageCompression get_image_compression() const { return IMAGE_COMPRESSION_BC; } + virtual Ref<Texture> get_logo() const { return logo; } + + + virtual bool poll_devices() { return show_run?true:false;} + virtual int get_device_count() const { return show_run?1:0; }; + virtual String get_device_name(int p_device) const { return "Run in Browser"; } + virtual String get_device_info(int p_device) const { return "Run exported HTML in the system's default browser."; } + virtual Error run(int p_device); + + virtual bool requieres_password(bool p_debug) const { return false; } + virtual String get_binary_extension() const { return "html"; } + virtual Error export_project(const String& p_path,bool p_debug,const String& p_password=""); + + virtual bool can_export(String *r_error=NULL) const; + + EditorExportPlatformJavaScript(); + ~EditorExportPlatformJavaScript(); +}; + +bool EditorExportPlatformJavaScript::_set(const StringName& p_name, const Variant& p_value) { + + String n=p_name; + + if (n=="custom_package/debug") + custom_debug_package=p_value; + else if (n=="custom_package/release") + custom_release_package=p_value; + else if (n=="browser/enable_run") + show_run=p_value; + else if (n=="options/memory_size") + max_memory=p_value; + else if (n=="options/pack_mode") + pack_mode=PackMode(int(p_value)); + else + return false; + + return true; +} + +bool EditorExportPlatformJavaScript::_get(const StringName& p_name,Variant &r_ret) const{ + + String n=p_name; + + if (n=="custom_package/debug") + r_ret=custom_debug_package; + else if (n=="custom_package/release") + r_ret=custom_release_package; + else if (n=="browser/enable_run") + r_ret=show_run; + else if (n=="options/memory_size") + r_ret=max_memory; + else if (n=="options/pack_mode") + r_ret=pack_mode; + else + return false; + + return true; +} +void EditorExportPlatformJavaScript::_get_property_list( List<PropertyInfo> *p_list) const{ + + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/debug", PROPERTY_HINT_FILE,"zip")); + p_list->push_back( PropertyInfo( Variant::STRING, "custom_package/release", PROPERTY_HINT_FILE,"zip")); + p_list->push_back( PropertyInfo( Variant::INT, "options/pack_mode",PROPERTY_HINT_ENUM,"Single File, Multiple Files")); + p_list->push_back( PropertyInfo( Variant::INT, "options/memory_size",PROPERTY_HINT_ENUM,"32mb,64mb,128mb,256mb,512mb,1024mb")); + p_list->push_back( PropertyInfo( Variant::BOOL, "browser/enable_run")); + + //p_list->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); + +} + + +static const char* files_pre=""\ +"var Module;\n"\ +"if (typeof Module === 'undefined') Module = eval('(function() { try { return Module || {} } catch(e) { return {} } })()');\n"\ +"if (!Module.expectedDataFileDownloads) {\n"\ +" Module.expectedDataFileDownloads = 0;\n"\ +" Module.finishedDataFileDownloads = 0;\n"\ +"}\n"\ +"Module.expectedDataFileDownloads++;\n"\ +"(function() {\n"\ +"\n"\ +" function runWithFS() {\n"\ +"function assert(check, msg) {\n"\ +" if (!check) throw msg + new Error().stack;\n"\ +"} \n"; + +static const char* files_post=""\ +"}\n"\ +"if (Module['calledRun']) {\n"\ +" runWithFS();\n"\ +"} else {\n"\ +" if (!Module['preRun']) Module['preRun'] = [];\n"\ +" Module[\"preRun\"].push(runWithFS); // FS is not initialized yet, wait for it\n"\ +"}\n"\ +"})();"; + + +static void _fix_html(Vector<uint8_t>& html,const String& name,int max_memory) { + + + String str; + String strnew; + str.parse_utf8((const char*)html.ptr(),html.size()); + Vector<String> lines=str.split("\n"); + for(int i=0;i<lines.size();i++) { + if (lines[i].find("godot.js")!=-1) { + strnew+="<script type=\"text/javascript\" src=\""+name+"_files.js\"></script>\n"; + strnew+="<script async type=\"text/javascript\" src=\""+name+".js\"></script>\n"; + } else if (lines[i].find("var Module")!=-1) { + strnew+=lines[i]; + strnew+="TOTAL_MEMORY:"+itos(max_memory*1024*1024)+","; + } else { + strnew+=lines[i]+"\n"; + } + } + + CharString cs = strnew.utf8(); + html.resize(cs.size()); + for(int i=9;i<cs.size();i++) { + html[i]=cs[i]; + } +} + + +struct JSExportData { + + EditorProgress *ep; + FileAccess *f; + +}; + + +static void store_file_buffer(FileAccess*f,const String& p_path,const Vector<uint8_t>& p_data) { + + + String pre = "Module['FS_createDataFile']('/', '"+p_path.replace("res://","")+"',["; + CharString cs = pre.utf8(); + f->store_buffer((const uint8_t*)cs.ptr(),cs.length()); + for(int i=0;i<p_data.size();i++) { + + + uint8_t c=','; + if (i>0) + f->store_buffer(&c,1); + + uint8_t str[4]; + uint8_t d = p_data[i]; + if (d<10) { + str[0]='0'+d; + str[1]=0; + f->store_buffer(str,1); + } else if (d<100) { + + str[0]='0'+d/10; + str[1]='0'+d%10; + str[2]=0; + f->store_buffer(str,2); + + } else { + str[0]='0'+d/100; + str[1]='0'+(d/10)%10; + str[2]='0'+d%10; + str[3]=0; + f->store_buffer(str,3); + } + } + String post = "],true,true);\n"; + cs = post.utf8(); + f->store_buffer((const uint8_t*)cs.ptr(),cs.length()); +} + + +Error EditorExportPlatformJavaScript::save_pack_file_js(void *p_userdata,const String& p_path, const Vector<uint8_t>& p_data,int p_file,int p_total) { + + JSExportData *ed=(JSExportData*)p_userdata; + + FileAccess *f=(FileAccess *)p_userdata; + store_file_buffer(ed->f,p_path,p_data); + ed->ep->step("File: "+p_path,3+p_file*100/p_total); + return OK; + +} + +Error EditorExportPlatformJavaScript::export_project(const String& p_path,bool p_debug,const String& p_password) { + + + String src_template; + + EditorProgress ep("export","Exporting for javascript",104); + + String template_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (p_debug) { + + src_template=custom_debug_package!=""?custom_debug_package:template_path+"javascript_debug.zip"; + } else { + + src_template=custom_release_package!=""?custom_release_package:template_path+"javascript_release.zip"; + + } + + + FileAccess *src_f=NULL; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + ep.step("Exporting to HTML5",0); + + unzFile pkg = unzOpen2(src_template.utf8().get_data(), &io); + if (!pkg) { + + EditorNode::add_io_error("Could not find template HTML5 to export:\n"+src_template); + return ERR_FILE_NOT_FOUND; + } + + 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=="godot.html") { + + _fix_html(data,p_path.get_file().basename(),1<<(max_memory+5)); + file=p_path.get_file(); + } + if (file=="godot.js") { + + //_fix_godot(data); + file=p_path.get_file().basename()+".js"; + } + + String dst = p_path.get_base_dir().plus_file(file); + FileAccess *f=FileAccess::open(dst,FileAccess::WRITE); + if (!f) { + EditorNode::add_io_error("Could not create file for writing:\n"+dst); + unzClose(pkg); + return ERR_FILE_CANT_WRITE; + } + f->store_buffer(data.ptr(),data.size()); + memdelete(f); + + + ret = unzGoToNextFile(pkg); + } + + + ep.step("Finding Files..",1); + + Vector<String> remaps; + + FileAccess *f=FileAccess::open(p_path.basename()+"_files.js",FileAccess::WRITE); + if (!f) { + EditorNode::add_io_error("Could not create file for writing:\n"+p_path.basename()+"_files.js"); + return ERR_FILE_CANT_WRITE; + } + + f->store_buffer((const uint8_t*)files_pre,strlen(files_pre)); + + if (pack_mode==PACK_SINGLE_FILE) { + + String ftmp = EditorSettings::get_singleton()->get_settings_path()+"/tmp/webpack.pck"; + FileAccess *f2 = FileAccess::open(ftmp,FileAccess::WRITE); + if (!f2) { + memdelete(f); + return ERR_CANT_CREATE; + } + Error err = save_pack(f2,false); + memdelete(f2); + if (err) { + memdelete(f); + return ERR_CANT_CREATE; + } + + Vector<uint8_t> data = FileAccess::get_file_as_array(ftmp); + store_file_buffer(f,"data.pck",data); + + + } else { + JSExportData ed; + ed.ep=&ep; + ed.f=f; + + Error err =export_project_files(save_pack_file_js,&ed,false); + if (err) + return err; + } + f->store_buffer((const uint8_t*)files_post,strlen(files_post)); + memdelete(f); + + + return OK; + +} + + +Error EditorExportPlatformJavaScript::run(int p_device) { + + String path = EditorSettings::get_singleton()->get_settings_path()+"/tmp/tmp_export.html"; + Error err = export_project(path,true,""); + if (err) + return err; + + OS::get_singleton()->shell_open(path); + + return OK; +} + + +EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { + + show_run=false; + Image img( _javascript_logo ); + logo = Ref<ImageTexture>( memnew( ImageTexture )); + logo->create_from_image(img); + max_memory=3; + pack_mode=PACK_SINGLE_FILE; +} + +bool EditorExportPlatformJavaScript::can_export(String *r_error) const { + + + bool valid=true; + String err; + String exe_path = EditorSettings::get_singleton()->get_settings_path()+"/templates/"; + + if (!FileAccess::exists(exe_path+"javascript_debug.zip") || !FileAccess::exists(exe_path+"javascript_release.zip")) { + valid=false; + err+="No export templates found.\nDownload and install export templates.\n"; + } + + if (custom_debug_package!="" && !FileAccess::exists(custom_debug_package)) { + valid=false; + err+="Custom debug package not found.\n"; + } + + if (custom_release_package!="" && !FileAccess::exists(custom_release_package)) { + valid=false; + err+="Custom release package not found.\n"; + } + + if (r_error) + *r_error=err; + + return valid; +} + + +EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { + +} + + +void register_javascript_exporter() { + + + Ref<EditorExportPlatformJavaScript> exporter = Ref<EditorExportPlatformJavaScript>( memnew(EditorExportPlatformJavaScript) ); + EditorImportExport::get_singleton()->add_export_platform(exporter); + + +} + diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h new file mode 100644 index 0000000000..2b575c9149 --- /dev/null +++ b/platform/javascript/export/export.h @@ -0,0 +1,29 @@ +/*************************************************************************/ +/* export.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/*************************************************************************/ +void register_javascript_exporter(); |