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 /core/io | |
parent | 0e49da1687bc8192ed210947da52c9e5c5f301bb (diff) |
GODOT IS OPEN SOURCE
Diffstat (limited to 'core/io')
74 files changed, 27307 insertions, 0 deletions
diff --git a/core/io/SCsub b/core/io/SCsub new file mode 100644 index 0000000000..5aecb4b915 --- /dev/null +++ b/core/io/SCsub @@ -0,0 +1,9 @@ +Import('env') + +env.add_source_files(env.core_sources,"*.cpp") +env.add_source_files(env.core_sources,"*.c") +#env.core_sources.append("io/fastlz.c") + +Export('env') + + diff --git a/core/io/base64.c b/core/io/base64.c new file mode 100644 index 0000000000..0c799e9f07 --- /dev/null +++ b/core/io/base64.c @@ -0,0 +1,110 @@ +#include <string.h> + +char b64string[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +long base64_encode (to, from, len) + char *to, *from; + unsigned int len; +{ + char *fromp = from; + char *top = to; + unsigned char cbyte; + unsigned char obyte; + char end[3]; + + for (; len >= 3; len -= 3) { + cbyte = *fromp++; + *top++ = b64string[(int)(cbyte >> 2)]; + obyte = (cbyte << 4) & 0x30; /* 0011 0000 */ + + cbyte = *fromp++; + obyte |= (cbyte >> 4); /* 0000 1111 */ + *top++ = b64string[(int)obyte]; + obyte = (cbyte << 2) & 0x3C; /* 0011 1100 */ + + cbyte = *fromp++; + obyte |= (cbyte >> 6); /* 0000 0011 */ + *top++ = b64string[(int)obyte]; + *top++ = b64string[(int)(cbyte & 0x3F)];/* 0011 1111 */ + } + + if (len) { + end[0] = *fromp++; + if (--len) end[1] = *fromp++; else end[1] = 0; + end[2] = 0; + + cbyte = end[0]; + *top++ = b64string[(int)(cbyte >> 2)]; + obyte = (cbyte << 4) & 0x30; /* 0011 0000 */ + + cbyte = end[1]; + obyte |= (cbyte >> 4); + *top++ = b64string[(int)obyte]; + obyte = (cbyte << 2) & 0x3C; /* 0011 1100 */ + + if (len) *top++ = b64string[(int)obyte]; + else *top++ = '='; + *top++ = '='; + } + *top = 0; + return top - to; +} + +/* badchar(): check if c is decent; puts either the */ +/* location of c or null into p. */ +#define badchar(c,p) (!(p = memchr(b64string, c, 64))) + +long base64_decode (to, from, len) + char *to, *from; + unsigned int len; +{ + char *fromp = from; + char *top = to; + char *p; + unsigned char cbyte; + unsigned char obyte; + int padding = 0; + + for (; len >= 4; len -= 4) { + if ((cbyte = *fromp++) == '=') cbyte = 0; + else { + if (badchar(cbyte, p)) return -1; + cbyte = (p - b64string); + } + obyte = cbyte << 2; /* 1111 1100 */ + + if ((cbyte = *fromp++) == '=') cbyte = 0; + else { + if (badchar(cbyte, p)) return -1; + cbyte = p - b64string; + } + obyte |= cbyte >> 4; /* 0000 0011 */ + *top++ = obyte; + + obyte = cbyte << 4; /* 1111 0000 */ + if ((cbyte = *fromp++) == '=') { cbyte = 0; padding++; } + else { + padding = 0; + if (badchar (cbyte, p)) return -1; + cbyte = p - b64string; + } + obyte |= cbyte >> 2; /* 0000 1111 */ + *top++ = obyte; + + obyte = cbyte << 6; /* 1100 0000 */ + if ((cbyte = *fromp++) == '=') { cbyte = 0; padding++; } + else { + padding = 0; + if (badchar (cbyte, p)) return -1; + cbyte = p - b64string; + } + obyte |= cbyte; /* 0011 1111 */ + *top++ = obyte; + } + + *top = 0; + if (len) return -1; + return (top - to) - padding; +} + diff --git a/core/io/base64.h b/core/io/base64.h new file mode 100644 index 0000000000..b70b387983 --- /dev/null +++ b/core/io/base64.h @@ -0,0 +1,11 @@ +#ifndef BASE64_H +#define BASE64_H + +extern "C" { + +uint32_t base64_encode (char* to, char* from, uint32_t len); +uint32_t base64_decode (char* to, char* from, uint32_t len); + +}; + +#endif /* BASE64_H */ diff --git a/core/io/compression.cpp b/core/io/compression.cpp new file mode 100644 index 0000000000..156767d241 --- /dev/null +++ b/core/io/compression.cpp @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* compression.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 "compression.h"
+
+#include "fastlz.h"
+#include "os/copymem.h"
+
+int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size,Mode p_mode) {
+
+ switch(p_mode) {
+ case MODE_FASTLZ: {
+
+ if (p_src_size<16) {
+ uint8_t src[16];
+ zeromem(&src[p_src_size],16-p_src_size);
+ copymem(src,p_src,p_src_size);
+ return fastlz_compress(src,16,p_dst);
+ } else {
+ return fastlz_compress(p_src,p_src_size,p_dst);
+ }
+
+ } break;
+ }
+
+ ERR_FAIL_V(-1);
+}
+
+int Compression::get_max_compressed_buffer_size(int p_src_size,Mode p_mode){
+
+ switch(p_mode) {
+ case MODE_FASTLZ: {
+
+
+ int ss = p_src_size+p_src_size*6/100;
+ if (ss<66)
+ ss=66;
+ return ss;
+
+ } break;
+ }
+
+ ERR_FAIL_V(-1);
+
+}
+
+
+
+void Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size,Mode p_mode){
+
+ switch(p_mode) {
+ case MODE_FASTLZ: {
+
+ if (p_dst_max_size<16) {
+ uint8_t dst[16];
+ fastlz_decompress(p_src,p_src_size,dst,16);
+ copymem(p_dst,dst,p_dst_max_size);
+ } else {
+ fastlz_decompress(p_src,p_src_size,p_dst,p_dst_max_size);
+ }
+ return;
+ } break;
+ }
+
+ ERR_FAIL();
+}
diff --git a/core/io/compression.h b/core/io/compression.h new file mode 100644 index 0000000000..70742d42d6 --- /dev/null +++ b/core/io/compression.h @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* compression.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. */ +/*************************************************************************/ +#ifndef COMPRESSION_H
+#define COMPRESSION_H
+
+#include "typedefs.h"
+
+class Compression
+{
+public:
+
+ enum Mode {
+ MODE_FASTLZ,
+ MODE_DEFLATE
+ };
+
+
+ static int compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size,Mode p_mode=MODE_FASTLZ);
+ static int get_max_compressed_buffer_size(int p_src_size,Mode p_mode=MODE_FASTLZ);
+ static void decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size,Mode p_mode=MODE_FASTLZ);
+
+ Compression();
+};
+
+
+
+#endif // COMPRESSION_H
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp new file mode 100644 index 0000000000..45e8cf69ab --- /dev/null +++ b/core/io/config_file.cpp @@ -0,0 +1,744 @@ +/*************************************************************************/ +/* config_file.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 "config_file.h" +#include "os/keyboard.h" +#include "os/file_access.h" + +StringArray ConfigFile::_get_sections() const { + + List<String> s; + get_sections(&s); + StringArray arr; + arr.resize(s.size()); + int idx=0; + for(const List<String>::Element *E=s.front();E;E=E->next()) { + + arr.set(idx++,E->get()); + } + + return arr; +} + +StringArray ConfigFile::_get_section_keys(const String& p_section) const{ + + List<String> s; + get_section_keys(p_section,&s); + StringArray arr; + arr.resize(s.size()); + int idx=0; + for(const List<String>::Element *E=s.front();E;E=E->next()) { + + arr.set(idx++,E->get()); + } + + return arr; + +} + + +void ConfigFile::set_value(const String& p_section, const String& p_key, const Variant& p_value){ + + if (p_value.get_type()==Variant::NIL) { + //erase + if (!values.has(p_section)) + return; // ? + values[p_section].erase(p_key); + if (values[p_section].empty()) { + values.erase(p_section); + } + + } else { + if (!values.has(p_section)) { + values[p_section]=Map<String, Variant>(); + } + + values[p_section][p_key]=p_value; + + } + +} +Variant ConfigFile::get_value(const String& p_section, const String& p_key) const{ + + ERR_FAIL_COND_V(!values.has(p_section),Variant()); + ERR_FAIL_COND_V(!values[p_section].has(p_key),Variant()); + return values[p_section][p_key]; + +} + +bool ConfigFile::has_section(const String& p_section) const { + + return values.has(p_section); +} +bool ConfigFile::has_section_key(const String& p_section,const String& p_key) const { + + if (!values.has(p_section)) + return false; + return values[p_section].has(p_key); +} + +void ConfigFile::get_sections(List<String> *r_sections) const{ + + for(const Map< String, Map<String, Variant> >::Element *E=values.front();E;E=E->next()) { + r_sections->push_back(E->key()); + } +} +void ConfigFile::get_section_keys(const String& p_section,List<String> *r_keys) const{ + + ERR_FAIL_COND(!values.has(p_section)); + + for(const Map<String, Variant> ::Element *E=values[p_section].front();E;E=E->next()) { + r_keys->push_back(E->key()); + } + +} + +static String _encode_variant(const Variant& p_variant) { + + switch(p_variant.get_type()) { + + case Variant::BOOL: { + bool val = p_variant; + return (val?"true":"false"); + } break; + case Variant::INT: { + int val = p_variant; + return itos(val); + } break; + case Variant::REAL: { + float val = p_variant; + return rtos(val)+(val==int(val)?".0":""); + } break; + case Variant::STRING: { + String val = p_variant; + return "\""+val.xml_escape()+"\""; + } break; + case Variant::COLOR: { + + Color val = p_variant; + return "#"+val.to_html(); + } break; + case Variant::STRING_ARRAY: + case Variant::INT_ARRAY: + case Variant::REAL_ARRAY: + case Variant::ARRAY: { + Array arr = p_variant; + String str="["; + for(int i=0;i<arr.size();i++) { + + if (i>0) + str+=", "; + str+=_encode_variant(arr[i]); + } + str+="]"; + return str; + } break; + case Variant::DICTIONARY: { + Dictionary d = p_variant; + String str="{"; + List<Variant> keys; + d.get_key_list(&keys); + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + if (E!=keys.front()) + str+=", "; + str+=_encode_variant(E->get()); + str+=":"; + str+=_encode_variant(d[E->get()]); + + } + str+="}"; + return str; + } break; + case Variant::IMAGE: { + String str="img("; + + Image img=p_variant; + if (!img.empty()) { + + String format; + switch(img.get_format()) { + + case Image::FORMAT_GRAYSCALE: format="grayscale"; break; + case Image::FORMAT_INTENSITY: format="intensity"; break; + case Image::FORMAT_GRAYSCALE_ALPHA: format="grayscale_alpha"; break; + case Image::FORMAT_RGB: format="rgb"; break; + case Image::FORMAT_RGBA: format="rgba"; break; + case Image::FORMAT_INDEXED : format="indexed"; break; + case Image::FORMAT_INDEXED_ALPHA: format="indexed_alpha"; break; + case Image::FORMAT_BC1: format="bc1"; break; + case Image::FORMAT_BC2: format="bc2"; break; + case Image::FORMAT_BC3: format="bc3"; break; + case Image::FORMAT_BC4: format="bc4"; break; + case Image::FORMAT_BC5: format="bc5"; break; + case Image::FORMAT_CUSTOM: format="custom custom_size="+itos(img.get_data().size())+""; break; + default: {} + } + + str+=format+", "; + str+=itos(img.get_mipmaps())+", "; + str+=itos(img.get_width())+", "; + str+=itos(img.get_height())+", "; + DVector<uint8_t> data = img.get_data(); + int ds=data.size(); + DVector<uint8_t>::Read r = data.read(); + for(int i=0;i<ds;i++) { + uint8_t byte = r[i]; + const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + char bstr[3]={ hex[byte>>4], hex[byte&0xF], 0}; + str+=bstr; + } + } + str+=")"; + return str; + } break; + case Variant::INPUT_EVENT: { + + InputEvent ev = p_variant; + + switch(ev.type) { + + case InputEvent::KEY: { + + String mods; + if (ev.key.mod.control) + mods+="C"; + if (ev.key.mod.shift) + mods+="S"; + if (ev.key.mod.alt) + mods+="A"; + if (ev.key.mod.meta) + mods+="M"; + if (mods!="") + mods=", "+mods; + + return "key("+keycode_get_string(ev.key.scancode)+mods+")"; + } break; + case InputEvent::MOUSE_BUTTON: { + + return "mbutton("+itos(ev.device)+", "+itos(ev.mouse_button.button_index)+")"; + } break; + case InputEvent::JOYSTICK_BUTTON: { + + return "jbutton("+itos(ev.device)+", "+itos(ev.joy_button.button_index)+")"; + } break; + case InputEvent::JOYSTICK_MOTION: { + + return "jaxis("+itos(ev.device)+", "+itos(ev.joy_motion.axis)+")"; + } break; + default: { + + return "nil"; + } break; + + } + } break; + default: {} + } + + return "nil"; //don't know wha to do with this +} + + +Error ConfigFile::save(const String& p_path){ + + Error err; + FileAccess *file = FileAccess::open(p_path,FileAccess::WRITE,&err); + + if (err) { + return err; + } + + + for(Map< String, Map<String, Variant> >::Element *E=values.front();E;E=E->next()) { + + if (E!=values.front()) + file->store_string("\n"); + file->store_string("["+E->key()+"]\n\n"); + + for(Map<String, Variant>::Element *F=E->get().front();F;F=F->next()) { + + file->store_string(F->key()+"="+_encode_variant(F->get())+"\n"); + } + } + + memdelete(file); + + return OK; +} + +static Vector<String> _decode_params(const String& p_string) { + + int begin=p_string.find("("); + ERR_FAIL_COND_V(begin==-1,Vector<String>()); + begin++; + int end=p_string.find(")"); + ERR_FAIL_COND_V(end<begin,Vector<String>()); + return p_string.substr(begin,end-begin).split(","); +} + +static String _get_chunk(const String& str,int &pos, int close_pos) { + + + enum { + MIN_COMMA, + MIN_COLON, + MIN_CLOSE, + MIN_QUOTE, + MIN_PARENTHESIS, + MIN_CURLY_OPEN, + MIN_OPEN + }; + + int min_pos=close_pos; + int min_what=MIN_CLOSE; + +#define TEST_MIN(m_how,m_what) \ +{\ +int res = str.find(m_how,pos);\ +if (res!=-1 && res < min_pos) {\ + min_pos=res;\ + min_what=m_what;\ +}\ +}\ + + + TEST_MIN(",",MIN_COMMA); + TEST_MIN("[",MIN_OPEN); + TEST_MIN("{",MIN_CURLY_OPEN); + TEST_MIN("(",MIN_PARENTHESIS); + TEST_MIN("\"",MIN_QUOTE); + + int end=min_pos; + + + switch(min_what) { + + case MIN_COMMA: { + } break; + case MIN_CLOSE: { + //end because it's done + } break; + case MIN_QUOTE: { + end=str.find("\"",min_pos+1)+1; + ERR_FAIL_COND_V(end==-1,Variant()); + + } break; + case MIN_PARENTHESIS: { + + end=str.find(")",min_pos+1)+1; + ERR_FAIL_COND_V(end==-1,Variant()); + + } break; + case MIN_OPEN: { + int level=1; + while(end<close_pos) { + + if (str[end]=='[') + level++; + if (str[end]==']') { + level--; + if (level==0) + break; + } + end++; + } + ERR_FAIL_COND_V(level!=0,Variant()); + end++; + } break; + case MIN_CURLY_OPEN: { + int level=1; + while(end<close_pos) { + + if (str[end]=='{') + level++; + if (str[end]=='}') { + level--; + if (level==0) + break; + } + end++; + } + ERR_FAIL_COND_V(level!=0,Variant()); + end++; + } break; + + } + + String ret = str.substr(pos,end-pos); + + pos=end; + while(pos<close_pos) { + if (str[pos]!=',' && str[pos]!=' ' && str[pos]!=':') + break; + pos++; + } + + return ret; + +} + + +static Variant _decode_variant(const String& p_string) { + + + String str = p_string.strip_edges(); + + if (str.nocasecmp_to("true")==0) + return Variant(true); + if (str.nocasecmp_to("false")==0) + return Variant(false); + if (str.nocasecmp_to("nil")==0) + return Variant(); + if (str.is_valid_float()) { + if (str.find(".")==-1) + return str.to_int(); + else + return str.to_double(); + + } + if (str.begins_with("#")) { //string + return Color::html(str); + } + if (str.begins_with("\"")) { //string + int end = str.find_last("\""); + ERR_FAIL_COND_V(end==0,Variant()); + return str.substr(1,end-1).xml_unescape(); + + } + + if (str.begins_with("[")) { //array + + int close_pos = str.find_last("]"); + ERR_FAIL_COND_V(close_pos==-1,Variant()); + Array array; + + int pos=1; + + while(pos<close_pos) { + + String s = _get_chunk(str,pos,close_pos); + array.push_back(_decode_variant(s)); + } + return array; + + } + + if (str.begins_with("{")) { //array + + int close_pos = str.find_last("}"); + ERR_FAIL_COND_V(close_pos==-1,Variant()); + Dictionary d; + + int pos=1; + + while(pos<close_pos) { + + String key = _get_chunk(str,pos,close_pos); + String data = _get_chunk(str,pos,close_pos); + d[_decode_variant(key)]=_decode_variant(data); + } + return d; + + } + if (str.begins_with("key")) { + Vector<String> params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=1 && params.size()!=2,Variant()); + int scode=0; + + if (params[0].is_numeric()) + scode=params[0].to_int(); + else + scode=find_keycode(params[0]); + + InputEvent ie; + ie.type=InputEvent::KEY; + ie.key.scancode=scode; + + if (params.size()==2) { + String mods=params[1]; + if (mods.findn("C")!=-1) + ie.key.mod.control=true; + if (mods.findn("A")!=-1) + ie.key.mod.alt=true; + if (mods.findn("S")!=-1) + ie.key.mod.shift=true; + if (mods.findn("M")!=-1) + ie.key.mod.meta=true; + } + return ie; + + } + + if (str.begins_with("mbutton")) { + Vector<String> params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::MOUSE_BUTTON; + ie.device=params[0].to_int(); + ie.mouse_button.button_index=params[1].to_int(); + + return ie; + } + + if (str.begins_with("jbutton")) { + Vector<String> params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::JOYSTICK_BUTTON; + ie.device=params[0].to_int(); + ie.joy_button.button_index=params[1].to_int(); + + return ie; + } + + if (str.begins_with("jaxis")) { + Vector<String> params = _decode_params(p_string); + ERR_FAIL_COND_V(params.size()!=2,Variant()); + + InputEvent ie; + ie.type=InputEvent::JOYSTICK_MOTION; + ie.device=params[0].to_int(); + ie.joy_motion.axis=params[1].to_int(); + + return ie; + } + if (str.begins_with("img")) { + Vector<String> params = _decode_params(p_string); + if (params.size()==0) { + return Image(); + } + + ERR_FAIL_COND_V(params.size()!=5,Image()); + + String format=params[0].strip_edges(); + + Image::Format imgformat; + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + } else if (format=="bc1") { + imgformat=Image::FORMAT_BC1; + } else if (format=="bc2") { + imgformat=Image::FORMAT_BC2; + } else if (format=="bc3") { + imgformat=Image::FORMAT_BC3; + } else if (format=="bc4") { + imgformat=Image::FORMAT_BC4; + } else if (format=="bc5") { + imgformat=Image::FORMAT_BC5; + } else if (format=="custom") { + imgformat=Image::FORMAT_CUSTOM; + } else { + + ERR_FAIL_V( Image() ); + } + + int mipmaps=params[1].to_int(); + int w=params[2].to_int(); + int h=params[3].to_int(); + + if (w == 0 && w == 0) { + //r_v = Image(w, h, imgformat); + return Image(); + }; + + + String data=params[4]; + int datasize=data.length()/2; + DVector<uint8_t> pixels; + pixels.resize(datasize); + DVector<uint8_t>::Write wb = pixels.write(); + const CharType *cptr=data.c_str(); + + int idx=0; + uint8_t byte; + while( idx<datasize*2) { + + CharType c=*(cptr++); + + ERR_FAIL_COND_V(c=='<',ERR_FILE_CORRUPT); + + if ( (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (idx&1) { + + byte|=HEX2CHR(c); + wb[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + } + + wb = DVector<uint8_t>::Write(); + + return Image(w,h,mipmaps,imgformat,pixels); + } + + if (str.find(",")!=-1) { //vector2 or vector3 + Vector<float> farr = str.split_floats(",",true); + if (farr.size()==2) { + return Point2(farr[0],farr[1]); + } + if (farr.size()==3) { + return Vector3(farr[0],farr[1],farr[2]); + } + ERR_FAIL_V(Variant()); + } + + + return Variant(); +} + +Error ConfigFile::load(const String& p_path) { + + Error err; + FileAccess *f= FileAccess::open(p_path,FileAccess::READ,&err); + + if (err!=OK) { + + return err; + } + + + String line; + String section; + String subpath; + + int line_count = 0; + + while(!f->eof_reached()) { + + String line = f->get_line().strip_edges(); + line_count++; + + if (line=="") + continue; + + // find comments + + { + + int pos=0; + while (true) { + int ret = line.find(";",pos); + if (ret==-1) + break; + + int qc=0; + for(int i=0;i<ret;i++) { + + if (line[i]=='"') + qc++; + } + + if ( !(qc&1) ) { + //not inside string, real comment + line=line.substr(0,ret); + break; + + } + + pos=ret+1; + + + } + } + + if (line.begins_with("[")) { + + int end = line.find_last("]"); + ERR_CONTINUE(end!=line.length()-1); + + section=line.substr(1,line.length()-2); + + } else if (line.find("=")!=-1) { + + + int eqpos = line.find("="); + String var=line.substr(0,eqpos).strip_edges(); + String value=line.substr(eqpos+1,line.length()).strip_edges(); + + Variant val = _decode_variant(value); + + set_value(section,var,val); + + } else { + + if (line.length() > 0) { + ERR_PRINT(String("Syntax error on line "+itos(line_count)+" of file "+p_path).ascii().get_data()); + }; + }; + } + + memdelete(f); + + return OK; +} + + + +void ConfigFile::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("set_value","section","key","value"),&ConfigFile::set_value); + ObjectTypeDB::bind_method(_MD("get_value","section","key"),&ConfigFile::get_value); + + ObjectTypeDB::bind_method(_MD("has_section","section"),&ConfigFile::has_section); + ObjectTypeDB::bind_method(_MD("has_section_key","section","key"),&ConfigFile::has_section_key); + + ObjectTypeDB::bind_method(_MD("get_sections"),&ConfigFile::_get_sections); + ObjectTypeDB::bind_method(_MD("get_section_keys"),&ConfigFile::_get_section_keys); + + ObjectTypeDB::bind_method(_MD("load:Error","path"),&ConfigFile::load); + ObjectTypeDB::bind_method(_MD("save:Error","path"),&ConfigFile::save); + +} + + +ConfigFile::ConfigFile() +{ +} diff --git a/core/io/config_file.h b/core/io/config_file.h new file mode 100644 index 0000000000..e132e46fea --- /dev/null +++ b/core/io/config_file.h @@ -0,0 +1,63 @@ +/*************************************************************************/ +/* config_file.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. */ +/*************************************************************************/ +#ifndef CONFIG_FILE_H +#define CONFIG_FILE_H + +#include "reference.h" + + +class ConfigFile : public Reference { + + OBJ_TYPE(ConfigFile,Reference); + + Map< String, Map<String, Variant> > values; + + StringArray _get_sections() const; + StringArray _get_section_keys(const String& p_section) const; +protected: + + static void _bind_methods(); +public: + + void set_value(const String& p_section, const String& p_key, const Variant& p_value); + Variant get_value(const String& p_section, const String& p_key) const; + + bool has_section(const String& p_section) const; + bool has_section_key(const String& p_section,const String& p_key) const; + + void get_sections(List<String> *r_sections) const; + void get_section_keys(const String& p_section,List<String> *r_keys) const; + + Error save(const String& p_path); + Error load(const String& p_path); + + ConfigFile(); +}; + +#endif // CONFIG_FILE_H diff --git a/core/io/crypt.h b/core/io/crypt.h new file mode 100644 index 0000000000..a01d08d932 --- /dev/null +++ b/core/io/crypt.h @@ -0,0 +1,131 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const unsigned long* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize<RAND_HEAD_LEN) + return 0; + + /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the + * output of rand() to get less predictability, since rand() is + * often poorly implemented. + */ + if (++calls == 1) + { + srand((unsigned)(time(NULL) ^ ZCR_SEED2)); + } + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + c = (rand() >> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/core/io/fastlz.c b/core/io/fastlz.c new file mode 100644 index 0000000000..508f6ea2ae --- /dev/null +++ b/core/io/fastlz.c @@ -0,0 +1,551 @@ + /* + FastLZ - lightning-fast lossless compression library + + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) + + 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. +*/ + +#if !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) + +/* + * Always check for bound when decompressing. + * Generally it is best to leave it defined. + */ +#define FASTLZ_SAFE + +/* + * Give hints to the compiler for branch prediction optimization. + */ +#if defined(__GNUC__) && (__GNUC__ > 2) +#define FASTLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) +#define FASTLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) +#else +#define FASTLZ_EXPECT_CONDITIONAL(c) (c) +#define FASTLZ_UNEXPECT_CONDITIONAL(c) (c) +#endif + +/* + * Use inlined functions for supported systems. + */ +#if defined(__GNUC__) || defined(__DMC__) || defined(__POCC__) || defined(__WATCOMC__) || defined(__SUNPRO_C) +#define FASTLZ_INLINE inline +#elif defined(__BORLANDC__) || defined(_MSC_VER) || defined(__LCC__) +#define FASTLZ_INLINE __inline +#else +#define FASTLZ_INLINE +#endif + +/* + * Prevent accessing more than 8-bit at once, except on x86 architectures. + */ +#if !defined(FASTLZ_STRICT_ALIGN) +#define FASTLZ_STRICT_ALIGN +#if defined(__i386__) || defined(__386) /* GNU C, Sun Studio */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(_M_IX86) /* Intel, MSVC */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__386) +#undef FASTLZ_STRICT_ALIGN +#elif defined(_X86_) /* MinGW */ +#undef FASTLZ_STRICT_ALIGN +#elif defined(__I86__) /* Digital Mars */ +#undef FASTLZ_STRICT_ALIGN +#endif +#endif + +/* + * FIXME: use preprocessor magic to set this on different platforms! + */ +typedef unsigned char flzuint8; +typedef unsigned short flzuint16; +typedef unsigned int flzuint32; + +/* prototypes */ +int fastlz_compress(const void* input, int length, void* output); +int fastlz_compress_level(int level, const void* input, int length, void* output); +int fastlz_decompress(const void* input, int length, void* output, int maxout); + +#define MAX_COPY 32 +#define MAX_LEN 264 /* 256 + 8 */ +#define MAX_DISTANCE 8192 + +#if !defined(FASTLZ_STRICT_ALIGN) +#define FASTLZ_READU16(p) *((const flzuint16*)(p)) +#else +#define FASTLZ_READU16(p) ((p)[0] | (p)[1]<<8) +#endif + +#define HASH_LOG 13 +#define HASH_SIZE (1<< HASH_LOG) +#define HASH_MASK (HASH_SIZE-1) +#define HASH_FUNCTION(v,p) { v = FASTLZ_READU16(p); v ^= FASTLZ_READU16(p+1)^(v>>(16-HASH_LOG));v &= HASH_MASK; } + +#undef FASTLZ_LEVEL +#define FASTLZ_LEVEL 1 + +#undef FASTLZ_COMPRESSOR +#undef FASTLZ_DECOMPRESSOR +#define FASTLZ_COMPRESSOR fastlz1_compress +#define FASTLZ_DECOMPRESSOR fastlz1_decompress +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); +#include "fastlz.c" + +#undef FASTLZ_LEVEL +#define FASTLZ_LEVEL 2 + +#undef MAX_DISTANCE +#define MAX_DISTANCE 8191 +#define MAX_FARDISTANCE (65535+MAX_DISTANCE-1) + +#undef FASTLZ_COMPRESSOR +#undef FASTLZ_DECOMPRESSOR +#define FASTLZ_COMPRESSOR fastlz2_compress +#define FASTLZ_DECOMPRESSOR fastlz2_decompress +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output); +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout); +#include "fastlz.c" + +int fastlz_compress(const void* input, int length, void* output) +{ + /* for short block, choose fastlz1 */ + if(length < 65536) + return fastlz1_compress(input, length, output); + + /* else... */ + return fastlz2_compress(input, length, output); +} + +int fastlz_decompress(const void* input, int length, void* output, int maxout) +{ + /* magic identifier for compression level */ + int level = ((*(const flzuint8*)input) >> 5) + 1; + + if(level == 1) + return fastlz1_decompress(input, length, output, maxout); + if(level == 2) + return fastlz2_decompress(input, length, output, maxout); + + /* unknown level, trigger error */ + return 0; +} + +int fastlz_compress_level(int level, const void* input, int length, void* output) +{ + if(level == 1) + return fastlz1_compress(input, length, output); + if(level == 2) + return fastlz2_compress(input, length, output); + + return 0; +} + +#else /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ + +static FASTLZ_INLINE int FASTLZ_COMPRESSOR(const void* input, int length, void* output) +{ + const flzuint8* ip = (const flzuint8*) input; + const flzuint8* ip_bound = ip + length - 2; + const flzuint8* ip_limit = ip + length - 12; + flzuint8* op = (flzuint8*) output; + + const flzuint8* htab[HASH_SIZE]; + const flzuint8** hslot; + flzuint32 hval; + + flzuint32 copy; + + /* sanity check */ + if(FASTLZ_UNEXPECT_CONDITIONAL(length < 4)) + { + if(length) + { + /* create literal copy only */ + *op++ = length-1; + ip_bound++; + while(ip <= ip_bound) + *op++ = *ip++; + return length+1; + } + else + return 0; + } + + /* initializes hash table */ + for (hslot = htab; hslot < htab + HASH_SIZE; hslot++) + *hslot = ip; + + /* we start with literal copy */ + copy = 2; + *op++ = MAX_COPY-1; + *op++ = *ip++; + *op++ = *ip++; + + /* main loop */ + while(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) + { + const flzuint8* ref; + flzuint32 distance; + + /* minimum match length */ + flzuint32 len = 3; + + /* comparison starting-point */ + const flzuint8* anchor = ip; + + /* check for a run */ +#if FASTLZ_LEVEL==2 + if(ip[0] == ip[-1] && FASTLZ_READU16(ip-1)==FASTLZ_READU16(ip+1)) + { + distance = 1; + ip += 3; + ref = anchor - 1 + 3; + goto match; + } +#endif + + /* find potential match */ + HASH_FUNCTION(hval,ip); + hslot = htab + hval; + ref = htab[hval]; + + /* calculate distance to the match */ + distance = anchor - ref; + + /* update hash table */ + *hslot = anchor; + + /* is this a match? check the first 3 bytes */ + if(distance==0 || +#if FASTLZ_LEVEL==1 + (distance >= MAX_DISTANCE) || +#else + (distance >= MAX_FARDISTANCE) || +#endif + *ref++ != *ip++ || *ref++!=*ip++ || *ref++!=*ip++) + goto literal; + +#if FASTLZ_LEVEL==2 + /* far, needs at least 5-byte match */ + if(distance >= MAX_DISTANCE) + { + if(*ip++ != *ref++ || *ip++!= *ref++) + goto literal; + len += 2; + } + + match: +#endif + + /* last matched byte */ + ip = anchor + len; + + /* distance is biased */ + distance--; + + if(!distance) + { + /* zero distance means a run */ + flzuint8 x = ip[-1]; + while(ip < ip_bound) + if(*ref++ != x) break; else ip++; + } + else + for(;;) + { + /* safe because the outer check against ip limit */ + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + if(*ref++ != *ip++) break; + while(ip < ip_bound) + if(*ref++ != *ip++) break; + break; + } + + /* if we have copied something, adjust the copy count */ + if(copy) + /* copy is biased, '0' means 1 byte copy */ + *(op-copy-1) = copy-1; + else + /* back, to overwrite the copy count */ + op--; + + /* reset literal counter */ + copy = 0; + + /* length is biased, '1' means a match of 3 bytes */ + ip -= 3; + len = ip - anchor; + + /* encode the match */ +#if FASTLZ_LEVEL==2 + if(distance < MAX_DISTANCE) + { + if(len < 7) + { + *op++ = (len << 5) + (distance >> 8); + *op++ = (distance & 255); + } + else + { + *op++ = (7 << 5) + (distance >> 8); + for(len-=7; len >= 255; len-= 255) + *op++ = 255; + *op++ = len; + *op++ = (distance & 255); + } + } + else + { + /* far away, but not yet in the another galaxy... */ + if(len < 7) + { + distance -= MAX_DISTANCE; + *op++ = (len << 5) + 31; + *op++ = 255; + *op++ = distance >> 8; + *op++ = distance & 255; + } + else + { + distance -= MAX_DISTANCE; + *op++ = (7 << 5) + 31; + for(len-=7; len >= 255; len-= 255) + *op++ = 255; + *op++ = len; + *op++ = 255; + *op++ = distance >> 8; + *op++ = distance & 255; + } + } +#else + + if(FASTLZ_UNEXPECT_CONDITIONAL(len > MAX_LEN-2)) + while(len > MAX_LEN-2) + { + *op++ = (7 << 5) + (distance >> 8); + *op++ = MAX_LEN - 2 - 7 -2; + *op++ = (distance & 255); + len -= MAX_LEN-2; + } + + if(len < 7) + { + *op++ = (len << 5) + (distance >> 8); + *op++ = (distance & 255); + } + else + { + *op++ = (7 << 5) + (distance >> 8); + *op++ = len - 7; + *op++ = (distance & 255); + } +#endif + + /* update the hash at match boundary */ + HASH_FUNCTION(hval,ip); + htab[hval] = ip++; + HASH_FUNCTION(hval,ip); + htab[hval] = ip++; + + /* assuming literal copy */ + *op++ = MAX_COPY-1; + + continue; + + literal: + *op++ = *anchor++; + ip = anchor; + copy++; + if(FASTLZ_UNEXPECT_CONDITIONAL(copy == MAX_COPY)) + { + copy = 0; + *op++ = MAX_COPY-1; + } + } + + /* left-over as literal copy */ + ip_bound++; + while(ip <= ip_bound) + { + *op++ = *ip++; + copy++; + if(copy == MAX_COPY) + { + copy = 0; + *op++ = MAX_COPY-1; + } + } + + /* if we have copied something, adjust the copy length */ + if(copy) + *(op-copy-1) = copy-1; + else + op--; + +#if FASTLZ_LEVEL==2 + /* marker for fastlz2 */ + *(flzuint8*)output |= (1 << 5); +#endif + + return op - (flzuint8*)output; +} + +static FASTLZ_INLINE int FASTLZ_DECOMPRESSOR(const void* input, int length, void* output, int maxout) +{ + const flzuint8* ip = (const flzuint8*) input; + const flzuint8* ip_limit = ip + length; + flzuint8* op = (flzuint8*) output; + flzuint8* op_limit = op + maxout; + flzuint32 ctrl = (*ip++) & 31; + int loop = 1; + + do + { + const flzuint8* ref = op; + flzuint32 len = ctrl >> 5; + flzuint32 ofs = (ctrl & 31) << 8; + + if(ctrl >= 32) + { +#if FASTLZ_LEVEL==2 + flzuint8 code; +#endif + len--; + ref -= ofs; + if (len == 7-1) +#if FASTLZ_LEVEL==1 + len += *ip++; + ref -= *ip++; +#else + do + { + code = *ip++; + len += code; + } while (code==255); + code = *ip++; + ref -= code; + + /* match from 16-bit distance */ + if(FASTLZ_UNEXPECT_CONDITIONAL(code==255)) + if(FASTLZ_EXPECT_CONDITIONAL(ofs==(31 << 8))) + { + ofs = (*ip++) << 8; + ofs += *ip++; + ref = op - ofs - MAX_DISTANCE; + } +#endif + +#ifdef FASTLZ_SAFE + if (FASTLZ_UNEXPECT_CONDITIONAL(op + len + 3 > op_limit)) + return 0; + + if (FASTLZ_UNEXPECT_CONDITIONAL(ref-1 < (flzuint8 *)output)) + return 0; +#endif + + if(FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit)) + ctrl = *ip++; + else + loop = 0; + + if(ref == op) + { + /* optimize copy for a run */ + flzuint8 b = ref[-1]; + *op++ = b; + *op++ = b; + *op++ = b; + for(; len; --len) + *op++ = b; + } + else + { +#if !defined(FASTLZ_STRICT_ALIGN) + const flzuint16* p; + flzuint16* q; +#endif + /* copy from reference */ + ref--; + *op++ = *ref++; + *op++ = *ref++; + *op++ = *ref++; + +#if !defined(FASTLZ_STRICT_ALIGN) + /* copy a byte, so that now it's word aligned */ + if(len & 1) + { + *op++ = *ref++; + len--; + } + + /* copy 16-bit at once */ + q = (flzuint16*) op; + op += len; + p = (const flzuint16*) ref; + for(len>>=1; len > 4; len-=4) + { + *q++ = *p++; + *q++ = *p++; + *q++ = *p++; + *q++ = *p++; + } + for(; len; --len) + *q++ = *p++; +#else + for(; len; --len) + *op++ = *ref++; +#endif + } + } + else + { + ctrl++; +#ifdef FASTLZ_SAFE + if (FASTLZ_UNEXPECT_CONDITIONAL(op + ctrl > op_limit)) + return 0; + if (FASTLZ_UNEXPECT_CONDITIONAL(ip + ctrl > ip_limit)) + return 0; +#endif + + *op++ = *ip++; + for(--ctrl; ctrl; ctrl--) + *op++ = *ip++; + + loop = FASTLZ_EXPECT_CONDITIONAL(ip < ip_limit); + if(loop) + ctrl = *ip++; + } + } + while(FASTLZ_EXPECT_CONDITIONAL(loop)); + + return op - (flzuint8*)output; +} + +#endif /* !defined(FASTLZ_COMPRESSOR) && !defined(FASTLZ_DECOMPRESSOR) */ diff --git a/core/io/fastlz.h b/core/io/fastlz.h new file mode 100644 index 0000000000..f87bc7be31 --- /dev/null +++ b/core/io/fastlz.h @@ -0,0 +1,100 @@ +/* + FastLZ - lightning-fast lossless compression library + + Copyright (C) 2007 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2006 Ariya Hidayat (ariya@kde.org) + Copyright (C) 2005 Ariya Hidayat (ariya@kde.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef FASTLZ_H +#define FASTLZ_H + +#define FASTLZ_VERSION 0x000100 + +#define FASTLZ_VERSION_MAJOR 0 +#define FASTLZ_VERSION_MINOR 0 +#define FASTLZ_VERSION_REVISION 0 + +#define FASTLZ_VERSION_STRING "0.1.0" + +#if defined (__cplusplus) +extern "C" { +#endif + +/** + Compress a block of data in the input buffer and returns the size of + compressed block. The size of input buffer is specified by length. The + minimum input buffer size is 16. + + The output buffer must be at least 5% larger than the input buffer + and can not be smaller than 66 bytes. + + If the input is not compressible, the return value might be larger than + length (input buffer size). + + The input buffer and the output buffer can not overlap. +*/ + +int fastlz_compress(const void* input, int length, void* output); + +/** + Decompress a block of compressed data and returns the size of the + decompressed block. If error occurs, e.g. the compressed data is + corrupted or the output buffer is not large enough, then 0 (zero) + will be returned instead. + + The input buffer and the output buffer can not overlap. + + Decompression is memory safe and guaranteed not to write the output buffer + more than what is specified in maxout. + */ + +int fastlz_decompress(const void* input, int length, void* output, int maxout); + +/** + Compress a block of data in the input buffer and returns the size of + compressed block. The size of input buffer is specified by length. The + minimum input buffer size is 16. + + The output buffer must be at least 5% larger than the input buffer + and can not be smaller than 66 bytes. + + If the input is not compressible, the return value might be larger than + length (input buffer size). + + The input buffer and the output buffer can not overlap. + + Compression level can be specified in parameter level. At the moment, + only level 1 and level 2 are supported. + Level 1 is the fastest compression and generally useful for short data. + Level 2 is slightly slower but it gives better compression ratio. + + Note that the compressed data, regardless of the level, can always be + decompressed using the function fastlz_decompress above. +*/ + +int fastlz_compress_level(int level, const void* input, int length, void* output); + +#if defined (__cplusplus) +} +#endif + +#endif /* FASTLZ_H */ diff --git a/core/io/file_access_buffered.cpp b/core/io/file_access_buffered.cpp new file mode 100644 index 0000000000..6927b3772f --- /dev/null +++ b/core/io/file_access_buffered.cpp @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* file_access_buffered.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 "file_access_buffered.h" + +#include <string.h> + +#include "error_macros.h" + +Error FileAccessBuffered::set_error(Error p_error) const { + + return (last_error = p_error); +}; + +void FileAccessBuffered::set_cache_size(int p_size) { + + cache_size = p_size; +}; + +int FileAccessBuffered::get_cache_size() { + + return cache_size; +}; + +int FileAccessBuffered::cache_data_left() const { + + if (file.offset >= file.size) { + return 0; + }; + + if (cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size()) { + + return read_data_block(file.offset, cache_size); + + } else { + + return cache.buffer.size() - (file.offset - cache.offset); + }; + + return 0; +}; + +void FileAccessBuffered::seek(size_t p_position) { + + file.offset = p_position; +}; + +void FileAccessBuffered::seek_end(int64_t p_position) { + + file.offset = file.size + p_position; +}; + +size_t FileAccessBuffered::get_pos() const { + + return file.offset; +}; + +size_t FileAccessBuffered::get_len() const { + + return file.size; +}; + +bool FileAccessBuffered::eof_reached() const { + + return file.offset > file.size; +}; + +uint8_t FileAccessBuffered::get_8() const { + + ERR_FAIL_COND_V(!file.open,0); + + uint8_t byte = 0; + if (cache_data_left() >= 1) { + + byte = cache.buffer[file.offset - cache.offset]; + }; + + ++file.offset; + + return byte; +}; + +int FileAccessBuffered::get_buffer(uint8_t *p_dest,int p_elements) const { + + ERR_FAIL_COND_V(!file.open, -1); + + if (p_elements > cache_size) { + + int total_read = 0; + + if (!(cache.offset == -1 || file.offset < cache.offset || file.offset >= cache.offset + cache.buffer.size())) { + + int size = (cache.buffer.size() - (file.offset - cache.offset)); + size = size - (size % 4); + //DVector<uint8_t>::Read read = cache.buffer.read(); + //memcpy(p_dest, read.ptr() + (file.offset - cache.offset), size); + memcpy(p_dest, cache.buffer.ptr() + (file.offset - cache.offset), size); + p_dest += size; + p_elements -= size; + file.offset += size; + total_read += size; + }; + + int err = read_data_block(file.offset, p_elements, p_dest); + if (err >= 0) { + total_read += err; + file.offset += err; + }; + + return total_read; + }; + + + int to_read = p_elements; + int total_read = 0; + while (to_read > 0) { + + int left = cache_data_left(); + if (left == 0) { + if (to_read > 0) { + file.offset += to_read; + }; + return total_read; + }; + if (left < 0) { + return left; + }; + + int r = MIN(left, to_read); + //DVector<uint8_t>::Read read = cache.buffer.read(); + //memcpy(p_dest+total_read, &read.ptr()[file.offset - cache.offset], r); + memcpy(p_dest+total_read, cache.buffer.ptr() + (file.offset - cache.offset), r); + + file.offset += r; + total_read += r; + to_read -= r; + }; + + return p_elements; +}; + +bool FileAccessBuffered::is_open() const { + + return file.open; +}; + +Error FileAccessBuffered::get_error() const { + + return last_error; +}; + +FileAccessBuffered::FileAccessBuffered() { + + cache_size = DEFAULT_CACHE_SIZE; +}; + +FileAccessBuffered::~FileAccessBuffered(){ + +} diff --git a/core/io/file_access_buffered.h b/core/io/file_access_buffered.h new file mode 100644 index 0000000000..533cc6c7dd --- /dev/null +++ b/core/io/file_access_buffered.h @@ -0,0 +1,97 @@ +/*************************************************************************/ +/* file_access_buffered.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. */ +/*************************************************************************/ +#ifndef FILE_ACCESS_BUFFERED_H +#define FILE_ACCESS_BUFFERED_H + +#include "os/file_access.h" + +#include "dvector.h" +#include "ustring.h" + +class FileAccessBuffered : public FileAccess { + +public: + enum { + DEFAULT_CACHE_SIZE = 128 * 1024, + }; + +private: + + int cache_size; + + int cache_data_left() const; + mutable Error last_error; + +protected: + + Error set_error(Error p_error) const; + + mutable struct File { + + bool open; + int size; + int offset; + String name; + int access_flags; + } file; + + mutable struct Cache { + + Vector<uint8_t> buffer; + int offset; + } cache; + + virtual int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const =0; + + void set_cache_size(int p_size); + int get_cache_size(); + +public: + + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + + virtual bool eof_reached() const; + + virtual uint8_t get_8() const; + virtual int get_buffer(uint8_t *p_dst,int p_length) const; ///< get an array of bytes + + virtual bool is_open() const; + + virtual Error get_error() const; + + FileAccessBuffered(); + virtual ~FileAccessBuffered(); +}; + +#endif + diff --git a/core/io/file_access_buffered_fa.h b/core/io/file_access_buffered_fa.h new file mode 100644 index 0000000000..5de2f66ace --- /dev/null +++ b/core/io/file_access_buffered_fa.h @@ -0,0 +1,147 @@ +/*************************************************************************/ +/* file_access_buffered_fa.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. */ +/*************************************************************************/ +#ifndef FILE_ACCESS_BUFFERED_FA_H +#define FILE_ACCESS_BUFFERED_FA_H + +#include "core/io/file_access_buffered.h" + +template<class T> +class FileAccessBufferedFA : public FileAccessBuffered { + + T f; + + int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const { + + ERR_FAIL_COND_V( !f.is_open(), -1 ); + + ((T*)&f)->seek(p_offset); + + if (p_dest) { + + f.get_buffer(p_dest, p_size); + return p_size; + + } else { + + cache.offset = p_offset; + cache.buffer.resize(p_size); + + // on dvector + //DVector<uint8_t>::Write write = cache.buffer.write(); + //f.get_buffer(write.ptr(), p_size); + + // on vector + f.get_buffer(cache.buffer.ptr(), p_size); + + return p_size; + }; + }; + + static FileAccess* create() { + + return memnew( FileAccessBufferedFA<T>() ); + }; + +protected: + virtual void _set_access_type(AccessType p_access) { + f._set_access_type(p_access); + FileAccessBuffered::_set_access_type(p_access); + }; + +public: + + + void store_8(uint8_t p_dest) { + + f.store_8(p_dest); + }; + + void store_buffer(const uint8_t *p_src,int p_length) { + + f.store_buffer(p_src, p_length); + }; + + bool file_exists(const String& p_name) { + + return f.file_exists(p_name); + }; + + Error _open(const String& p_path, int p_mode_flags) { + + close(); + + Error ret = f._open(p_path, p_mode_flags); + if (ret !=OK) + return ret; + //ERR_FAIL_COND_V( ret != OK, ret ); + + file.size = f.get_len(); + file.offset = 0; + file.open = true; + file.name = p_path; + file.access_flags = p_mode_flags; + + cache.buffer.resize(0); + cache.offset = 0; + + return set_error(OK); + }; + + void close() { + + f.close(); + + file.offset = 0; + file.size = 0; + file.open = false; + file.name = ""; + + cache.buffer.resize(0); + cache.offset = 0; + set_error(OK); + }; + +// static void make_default() { + + //FileAccess::create_func = FileAccessBufferedFA<T>::create; +// }; + + virtual uint64_t _get_modified_time(const String& p_file) { + + return f._get_modified_time(p_file); + } + + FileAccessBufferedFA() { + + + }; +}; + + +#endif // FILE_ACCESS_BUFFERED_FA_H diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp new file mode 100644 index 0000000000..ec9dce28e2 --- /dev/null +++ b/core/io/file_access_compressed.cpp @@ -0,0 +1,421 @@ +/*************************************************************************/ +/* file_access_compressed.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 "file_access_compressed.h"
+#include "print_string.h"
+void FileAccessCompressed::configure(const String& p_magic, Compression::Mode p_mode, int p_block_size) {
+
+ magic=p_magic.ascii().get_data();
+ if (magic.length()>4)
+ magic=magic.substr(0,4);
+ else {
+ while(magic.length()<4)
+ magic+=" ";
+ }
+
+ cmode=p_mode;
+ block_size=p_block_size;
+
+}
+
+
+#define WRITE_FIT(m_bytes) \
+{\
+if (write_pos+(m_bytes) > write_max) {\
+ write_max=write_pos+(m_bytes);\
+}\
+if (write_max > write_buffer_size) {\
+ write_buffer_size = nearest_power_of_2( write_max );\
+ buffer.resize(write_buffer_size);\
+ write_ptr=buffer.ptr();\
+}\
+}
+
+
+Error FileAccessCompressed::open_after_magic(FileAccess *p_base) {
+
+
+ f=p_base;
+ cmode=(Compression::Mode)f->get_32();
+ block_size=f->get_32();
+ read_total=f->get_32();
+ int bc = (read_total/block_size)+1;
+ int acc_ofs=f->get_pos()+bc*4;
+ int max_bs=0;
+ for(int i=0;i<bc;i++) {
+
+ ReadBlock rb;
+ rb.offset=acc_ofs;
+ rb.csize=f->get_32();
+ acc_ofs+=rb.csize;
+ max_bs=MAX(max_bs,rb.csize);
+ read_blocks.push_back(rb);
+
+
+ }
+
+ comp_buffer.resize(max_bs);
+ buffer.resize(block_size);
+ read_ptr=buffer.ptr();
+ f->get_buffer(comp_buffer.ptr(),read_blocks[0].csize);
+ at_end=false;
+ read_eof=false;
+ read_block_count=bc;
+ read_block_size=read_blocks.size()==1?read_total:block_size;
+
+ Compression::decompress(buffer.ptr(),read_block_size,comp_buffer.ptr(),read_blocks[0].csize,cmode);
+ read_block=0;
+ read_pos=0;
+
+ return OK;
+
+}
+
+Error FileAccessCompressed::_open(const String& p_path, int p_mode_flags){
+
+ ERR_FAIL_COND_V(p_mode_flags==READ_WRITE,ERR_UNAVAILABLE);
+
+ if (f)
+ close();
+
+
+ Error err;
+ f = FileAccess::open(p_path,p_mode_flags,&err);
+ if (err!=OK) {
+ //not openable
+
+ f=NULL;
+ return err;
+ }
+
+ if (p_mode_flags&WRITE) {
+
+ buffer.clear();
+ writing=true;
+ write_pos=0;
+ write_buffer_size=256;
+ buffer.resize(256);
+ write_max=0;
+ write_ptr=buffer.ptr();
+
+
+
+ //don't store anything else unless it's done saving!
+ } else {
+
+ char rmagic[5];
+ f->get_buffer((uint8_t*)rmagic,4);
+ rmagic[4]=0;
+ if (magic!=rmagic) {
+ memdelete(f);
+ f=NULL;
+ return ERR_FILE_UNRECOGNIZED;
+ }
+
+ open_after_magic(f);
+
+ }
+
+ return OK;
+
+}
+void FileAccessCompressed::close(){
+
+ if (!f)
+ return;
+
+
+ if (writing) {
+ //save block table and all compressed blocks
+
+ CharString mgc = magic.utf8();
+ f->store_buffer((const uint8_t*)mgc.get_data(),mgc.length()); //write header 4
+ f->store_32(cmode); //write compression mode 4
+ f->store_32(block_size); //write block size 4
+ f->store_32(write_max); //max amount of data written 4
+ int bc=(write_max/block_size)+1;
+
+ for(int i=0;i<bc;i++) {
+ f->store_32(0); //compressed sizes, will update later
+ }
+
+
+ Vector<int> block_sizes;
+ for(int i=0;i<bc;i++) {
+
+ int bl = i==(bc-1) ? write_max % block_size : block_size;
+ uint8_t *bp = &write_ptr[i*block_size];
+
+ Vector<uint8_t> cblock;
+ cblock.resize(Compression::get_max_compressed_buffer_size(bl,cmode));
+ int s = Compression::compress(cblock.ptr(),bp,bl,cmode);
+
+ f->store_buffer(cblock.ptr(),s);
+ block_sizes.push_back(s);
+ }
+
+ f->seek(16); //ok write block sizes
+ for(int i=0;i<bc;i++)
+ f->store_32(block_sizes[i]);
+ f->seek_end();
+ f->store_buffer((const uint8_t*)mgc.get_data(),mgc.length()); //magic at the end too
+
+ buffer.clear();
+
+ } else {
+
+
+ comp_buffer.clear();
+ buffer.clear();
+ read_blocks.clear();
+ }
+
+ memdelete(f);
+ f=NULL;
+
+}
+
+bool FileAccessCompressed::is_open() const{
+
+ return f!=NULL;
+}
+
+void FileAccessCompressed::seek(size_t p_position){
+
+ ERR_FAIL_COND(!f);
+ if (writing) {
+
+ ERR_FAIL_COND(p_position>write_max);
+
+ write_pos=p_position;
+
+ } else {
+
+ ERR_FAIL_COND(p_position>read_total);
+ if (p_position==read_total) {
+ at_end=true;
+ } else {
+
+ int block_idx = p_position/block_size;
+ if (block_idx!=read_block) {
+
+ read_block=block_idx;
+ f->seek(read_blocks[read_block].offset);
+ f->get_buffer(comp_buffer.ptr(),read_blocks[read_block].csize);
+ Compression::decompress(buffer.ptr(),read_blocks.size()==1?read_total:block_size,comp_buffer.ptr(),read_blocks[read_block].csize,cmode);
+ read_block_size=read_block==read_block_count-1?read_total%block_size:block_size;
+ }
+
+ read_pos=p_position%block_size;
+ }
+ }
+}
+
+
+void FileAccessCompressed::seek_end(int64_t p_position){
+
+ ERR_FAIL_COND(!f);
+ if (writing) {
+
+ seek(write_max+p_position);
+ } else {
+
+ seek(read_total+p_position);
+
+ }
+
+
+}
+size_t FileAccessCompressed::get_pos() const{
+
+ ERR_FAIL_COND_V(!f,0);
+ if (writing) {
+
+ return write_pos;
+ } else {
+
+ return read_block*block_size+read_pos;
+ }
+
+}
+size_t FileAccessCompressed::get_len() const{
+
+ ERR_FAIL_COND_V(!f,0);
+ if (writing) {
+
+ return write_max;
+ } else {
+ return read_total;
+ }
+}
+
+bool FileAccessCompressed::eof_reached() const{
+
+ ERR_FAIL_COND_V(!f,false);
+ if (writing) {
+ return false;
+ } else {
+ return read_eof;
+ }
+}
+
+uint8_t FileAccessCompressed::get_8() const{
+
+ ERR_FAIL_COND_V(writing,0);
+ ERR_FAIL_COND_V(!f,0);
+
+ if (at_end) {
+ read_eof=true;
+ return 0;
+ }
+
+ uint8_t ret = read_ptr[read_pos];
+
+ read_pos++;
+ if (read_pos>=read_block_size) {
+ read_block++;
+
+ if (read_block<read_block_count) {
+ //read another block of compressed data
+ f->get_buffer(comp_buffer.ptr(),read_blocks[read_block].csize);
+ Compression::decompress(buffer.ptr(),read_blocks.size()==1?read_total:block_size,comp_buffer.ptr(),read_blocks[read_block].csize,cmode);
+ read_block_size=read_block==read_block_count-1?read_total%block_size:block_size;
+ read_pos=0;
+
+ } else {
+ read_block--;
+ at_end=true;
+ ret =0;
+ }
+ }
+
+ return ret;
+
+}
+int FileAccessCompressed::get_buffer(uint8_t *p_dst, int p_length) const{
+
+ ERR_FAIL_COND_V(writing,0);
+ ERR_FAIL_COND_V(!f,0);
+
+ if (at_end) {
+ read_eof=true;
+ return 0;
+ }
+
+
+ for(int i=0;i<p_length;i++) {
+
+
+ p_dst[i]=read_ptr[read_pos];
+ read_pos++;
+ if (read_pos>=read_block_size) {
+ read_block++;
+
+ if (read_block<read_block_count) {
+ //read another block of compressed data
+ f->get_buffer(comp_buffer.ptr(),read_blocks[read_block].csize);
+ Compression::decompress(buffer.ptr(),read_blocks.size()==1?read_total:block_size,comp_buffer.ptr(),read_blocks[read_block].csize,cmode);
+ read_block_size=read_block==read_block_count-1?read_total%block_size:block_size;
+ read_pos=0;
+
+ } else {
+ read_block--;
+ at_end=true;
+ if (i<p_length-1)
+ read_eof=true;
+ return i;
+
+ }
+ }
+
+ }
+
+ return p_length;
+
+}
+
+Error FileAccessCompressed::get_error() const{
+
+ return read_eof?ERR_FILE_EOF:OK;
+}
+
+void FileAccessCompressed::store_8(uint8_t p_dest){
+
+ ERR_FAIL_COND(!f);
+ ERR_FAIL_COND(!writing);
+
+ WRITE_FIT(1);
+ write_ptr[write_pos++]=p_dest;
+
+}
+
+bool FileAccessCompressed::file_exists(const String& p_name){
+
+ FileAccess *fa = FileAccess::open(p_name,FileAccess::READ);
+ if (!fa)
+ return false;
+ memdelete(fa);
+ return true;
+}
+
+uint64_t FileAccessCompressed::_get_modified_time(const String& p_file) {
+
+ if (f)
+ return f->get_modified_time(p_file);
+ else
+ return 0;
+}
+
+FileAccessCompressed::FileAccessCompressed() {
+
+ f=NULL;
+ magic="GCMP";
+ block_size=4096;
+ cmode=Compression::MODE_FASTLZ;
+ writing=false;
+ write_ptr=0;
+ write_buffer_size=0;
+ write_max=0;
+ block_size=0;
+ read_eof=false;
+ at_end=false;
+ read_total=0;
+ read_ptr=NULL;
+ read_block=0;
+ read_block_count=0;
+ read_block_size=0;
+ read_pos=0;
+
+}
+
+FileAccessCompressed::~FileAccessCompressed(){
+
+ if (f)
+ close();
+
+}
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h new file mode 100644 index 0000000000..3039b5c8be --- /dev/null +++ b/core/io/file_access_compressed.h @@ -0,0 +1,101 @@ +/*************************************************************************/ +/* file_access_compressed.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. */ +/*************************************************************************/ +#ifndef FILE_ACCESS_COMPRESSED_H
+#define FILE_ACCESS_COMPRESSED_H
+
+#include "io/compression.h"
+#include "os/file_access.h"
+
+class FileAccessCompressed : public FileAccess {
+
+ Compression::Mode cmode;
+ bool writing;
+ int write_pos;
+ uint8_t*write_ptr;
+ int write_buffer_size;
+ int write_max;
+ int block_size;
+ mutable bool read_eof;
+ mutable bool at_end;
+
+ struct ReadBlock {
+ int csize;
+ int offset;
+ };
+
+ mutable Vector<uint8_t> comp_buffer;
+ uint8_t *read_ptr;
+ mutable int read_block;
+ int read_block_count;
+ mutable int read_block_size;
+ mutable int read_pos;
+ Vector<ReadBlock> read_blocks;
+ int read_total;
+
+
+
+
+ String magic;
+ mutable Vector<uint8_t> buffer;
+ FileAccess *f;
+public:
+
+ void configure(const String& p_magic, Compression::Mode p_mode=Compression::MODE_FASTLZ, int p_block_size=4096);
+
+ Error open_after_magic(FileAccess *p_base);
+
+ virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file
+ virtual void close(); ///< close a file
+ virtual bool is_open() const; ///< true when file is open
+
+ virtual void seek(size_t p_position); ///< seek to a given position
+ virtual void seek_end(int64_t p_position=0); ///< seek from the end of file
+ virtual size_t get_pos() const; ///< get position in the file
+ virtual size_t get_len() const; ///< get size of the file
+
+ virtual bool eof_reached() const; ///< reading passed EOF
+
+ virtual uint8_t get_8() const; ///< get a byte
+ virtual int get_buffer(uint8_t *p_dst, int p_length) const;
+
+ virtual Error get_error() const; ///< get last error
+
+ virtual void store_8(uint8_t p_dest); ///< store a byte
+
+ virtual bool file_exists(const String& p_name); ///< return true if a file exists
+
+ virtual uint64_t _get_modified_time(const String& p_file);
+
+
+ FileAccessCompressed();
+ virtual ~FileAccessCompressed();
+
+};
+
+#endif // FILE_ACCESS_COMPRESSED_H
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp new file mode 100644 index 0000000000..a1dd2e48bb --- /dev/null +++ b/core/io/file_access_memory.cpp @@ -0,0 +1,188 @@ +/*************************************************************************/ +/* file_access_memory.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 "file_access_memory.h" + +#include "os/dir_access.h" +#include "os/copymem.h" +#include "globals.h" +#include "map.h" + +static Map<String, Vector<uint8_t> >* files = NULL; + +void FileAccessMemory::register_file(String p_name, Vector<uint8_t> p_data) { + + if (!files) { + files = memnew((Map<String, Vector<uint8_t> >)); + }; + + String name; + if (Globals::get_singleton()) + name = Globals::get_singleton()->globalize_path(p_name); + else + name = p_name; + name = DirAccess::normalize_path(name); + + (*files)[name] = p_data; +}; + +void FileAccessMemory::cleanup() { + + if (!files) + return; + + memdelete(files); +}; + + +FileAccess* FileAccessMemory::create() { + + return memnew(FileAccessMemory); +}; + +bool FileAccessMemory::file_exists(const String& p_name) { + + String name = fix_path(p_name); + name = DirAccess::normalize_path(name); + + return files && (files->find(name) != NULL); +}; + + +Error FileAccessMemory::_open(const String& p_path, int p_mode_flags) { + + ERR_FAIL_COND_V(!files, ERR_FILE_NOT_FOUND); + + String name = fix_path(p_path); + name = DirAccess::normalize_path(name); + + Map<String, Vector<uint8_t> >::Element* E = files->find(name); + ERR_FAIL_COND_V(!E, ERR_FILE_NOT_FOUND); + + data = &(E->get()[0]); + length = E->get().size(); + pos = 0; + + return OK; +}; + +void FileAccessMemory::close() { + + data = NULL; +}; + +bool FileAccessMemory::is_open() const { + + return data != NULL; +}; + +void FileAccessMemory::seek(size_t p_position) { + + ERR_FAIL_COND(!data); + pos = p_position; +}; + +void FileAccessMemory::seek_end(int64_t p_position) { + + ERR_FAIL_COND(!data); + pos = length + p_position; +}; + +size_t FileAccessMemory::get_pos() const { + + ERR_FAIL_COND_V(!data, 0); + return pos; +}; + +size_t FileAccessMemory::get_len() const { + + ERR_FAIL_COND_V(!data, 0); + return length; +}; + +bool FileAccessMemory::eof_reached() const { + + return pos >= length; +}; + +uint8_t FileAccessMemory::get_8() const { + + uint8_t ret; + if (pos < length) { + ret = data[pos]; + }; + ++pos; + + return ret; +}; + +int FileAccessMemory::get_buffer(uint8_t *p_dst,int p_length) const { + + ERR_FAIL_COND_V(!data, -1); + + int left = length - pos; + int read = MIN(p_length, left); + + if (read < p_length) { + WARN_PRINT("Reading less data than requested"); + }; + + copymem(p_dst, &data[pos], read); + pos += p_length; + + return read; +}; + +Error FileAccessMemory::get_error() const { + + return pos >= length ? ERR_FILE_EOF : OK; +}; + +void FileAccessMemory::store_8(uint8_t p_byte) { + + ERR_FAIL_COND(!data); + ERR_FAIL_COND(pos >= length); + data[pos++] = p_byte; +}; + +void FileAccessMemory::store_buffer(const uint8_t *p_src,int p_length) { + + int left = length - pos; + int write = MIN(p_length, left); + if (write < p_length) { + WARN_PRINT("Writing less data than requested"); + }; + + copymem(&data[pos], p_src, write); + pos += p_length; +}; + +FileAccessMemory::FileAccessMemory() { + + data = NULL; +} diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h new file mode 100644 index 0000000000..a02a022a4f --- /dev/null +++ b/core/io/file_access_memory.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* file_access_memory.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. */ +/*************************************************************************/ +#ifndef FILE_ACCESS_MEMORY_H +#define FILE_ACCESS_MEMORY_H + +#include "os/file_access.h" + +class FileAccessMemory : public FileAccess { + + uint8_t* data; + int length; + mutable int pos; + + static FileAccess* create(); + +public: + + static void register_file(String p_name, Vector<uint8_t> p_data); + static void cleanup(); + + virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + + virtual int get_buffer(uint8_t *p_dst,int p_length) const; ///< get an array of bytes + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + virtual void store_buffer(const uint8_t *p_src,int p_length); ///< store an array of bytes + + virtual bool file_exists(const String& p_name); ///< return true if a file exists + + virtual uint64_t _get_modified_time(const String& p_file) { return 0; } + + + + FileAccessMemory(); +}; + +#endif // FILE_ACCESS_MEMORY_H diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp new file mode 100644 index 0000000000..a5636b137d --- /dev/null +++ b/core/io/file_access_network.cpp @@ -0,0 +1,566 @@ +/*************************************************************************/ +/* file_access_network.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 "file_access_network.h" +#include "marshalls.h" +#include "globals.h" +#include "os/os.h" +#include "io/ip.h" + + + +#define DEBUG_PRINT(m_p) print_line(m_p) +//#define DEBUG_TIME(m_what) printf("MS: %s - %lli\n",m_what,OS::get_singleton()->get_ticks_usec()); +//#define DEBUG_PRINT(m_p) +#define DEBUG_TIME(m_what) + + +void FileAccessNetworkClient::lock_mutex() { + + mutex->lock(); + lockcount++; +} + +void FileAccessNetworkClient::unlock_mutex() { + + lockcount--; + mutex->unlock(); + +} + +void FileAccessNetworkClient::put_32(int p_32) { + + uint8_t buf[4]; + encode_uint32(p_32,buf); + client->put_data(buf,4); + DEBUG_PRINT("put32: "+itos(p_32)); +} + +void FileAccessNetworkClient::put_64(int64_t p_64) { + + uint8_t buf[8]; + encode_uint64(p_64,buf); + client->put_data(buf,8); + DEBUG_PRINT("put64: "+itos(p_64)); + +} + +int FileAccessNetworkClient::get_32() { + + uint8_t buf[4]; + client->get_data(buf,4); + return decode_uint32(buf); + +} + +int64_t FileAccessNetworkClient::get_64() { + + uint8_t buf[8]; + client->get_data(buf,8); + return decode_uint64(buf); + +} + +void FileAccessNetworkClient::_thread_func() { + + client->set_nodelay(true); + while(!quit) { + + DEBUG_PRINT("SEM WAIT - "+itos(sem->get())); + Error err = sem->wait(); + DEBUG_TIME("sem_unlock"); + //DEBUG_PRINT("semwait returned "+itos(werr)); + DEBUG_PRINT("MUTEX LOCK "+itos(lockcount)); + DEBUG_PRINT("POPO"); + DEBUG_PRINT("PEPE"); + lock_mutex(); + DEBUG_PRINT("MUTEX PASS"); + + blockrequest_mutex->lock(); + while(block_requests.size()) { + put_32(block_requests.front()->get().id); + put_32(FileAccessNetwork::COMMAND_READ_BLOCK); + put_64(block_requests.front()->get().offset); + put_32(block_requests.front()->get().size); + block_requests.pop_front(); + } + blockrequest_mutex->unlock(); + + DEBUG_PRINT("THREAD ITER"); + + DEBUG_TIME("sem_read"); + int id = get_32(); + + int response = get_32(); + DEBUG_PRINT("GET RESPONSE: "+itos(response)); + + FileAccessNetwork *fa=NULL; + + if (response!=FileAccessNetwork::RESPONSE_DATA) { + ERR_FAIL_COND(!accesses.has(id)); + } + + if (accesses.has(id)) + fa=accesses[id]; + + + switch(response) { + + case FileAccessNetwork::RESPONSE_OPEN: { + + + DEBUG_TIME("sem_open"); + int status = get_32(); + if (status!=OK) { + fa->_respond(0,Error(status)); + } else { + uint64_t len = get_64(); + fa->_respond(len,Error(status)); + } + + fa->sem->post(); + + + } break; + case FileAccessNetwork::RESPONSE_DATA: { + + int64_t offset = get_64(); + uint32_t len = get_32(); + + Vector<uint8_t> block; + block.resize(len); + client->get_data(block.ptr(),len); + + if (fa) //may have been queued + fa->_set_block(offset,block); + + } break; + case FileAccessNetwork::RESPONSE_FILE_EXISTS: { + + + int status = get_32(); + fa->exists_modtime=status!=0; + fa->sem->post(); + + + + } break; + case FileAccessNetwork::RESPONSE_GET_MODTIME: { + + + uint64_t status = get_64(); + fa->exists_modtime=status; + fa->sem->post(); + + } break; + + } + + + unlock_mutex(); + } + +} + +void FileAccessNetworkClient::_thread_func(void *s) { + + FileAccessNetworkClient *self =(FileAccessNetworkClient*)s; + + self->_thread_func(); + +} + +Error FileAccessNetworkClient::connect(const String& p_host,int p_port,const String& p_password) { + + IP_Address ip; + + if (p_host.is_valid_ip_address()) { + ip=p_host; + } else { + ip=IP::get_singleton()->resolve_hostname(p_host); + } + + DEBUG_PRINT("IP: "+String(ip)+" port "+itos(p_port)); + Error err = client->connect(ip,p_port); + ERR_FAIL_COND_V(err,err); + while(client->get_status()==StreamPeerTCP::STATUS_CONNECTING) { +//DEBUG_PRINT("trying to connect...."); + OS::get_singleton()->delay_usec(1000); + } + + if (client->get_status()!=StreamPeerTCP::STATUS_CONNECTED) { + return ERR_CANT_CONNECT; + } + + CharString cs = p_password.utf8(); + put_32(cs.length()); + client->put_data((const uint8_t*)cs.ptr(),cs.length()); + + int e = get_32(); + + if (e!=OK) { + return ERR_INVALID_PARAMETER; + } + + thread = Thread::create(_thread_func,this); + + return OK; +} + +FileAccessNetworkClient *FileAccessNetworkClient::singleton=NULL; + + +FileAccessNetworkClient::FileAccessNetworkClient() { + + thread=NULL; + mutex = Mutex::create(); + blockrequest_mutex = Mutex::create(); + quit=false; + singleton=this; + last_id=0; + client = Ref<StreamPeerTCP>( StreamPeerTCP::create() ); + sem=Semaphore::create(); + lockcount=0; +} + +FileAccessNetworkClient::~FileAccessNetworkClient() { + + if (thread) { + quit=true; + sem->post(); + Thread::wait_to_finish(thread); + } + + memdelete(blockrequest_mutex); + memdelete(mutex); + memdelete(sem); + + +} + +void FileAccessNetwork::_set_block(size_t p_offset,const Vector<uint8_t>& p_block) { + + + int page = p_offset/page_size; + ERR_FAIL_INDEX(page,pages.size()); + if (page<pages.size()-1) { + ERR_FAIL_COND(p_block.size()!=page_size); + } else { + ERR_FAIL_COND( (p_block.size() != (total_size%page_size))); + } + + buffer_mutex->lock(); + pages[page].buffer=p_block; + pages[page].queued=false; + buffer_mutex->unlock(); + + if (waiting_on_page==page) { + waiting_on_page=-1; + page_sem->post(); + } +} + + +void FileAccessNetwork::_respond(size_t p_len,Error p_status) { + + DEBUG_PRINT("GOT RESPONSE - len: "+itos(p_len)+" status: "+itos(p_status)); + response=p_status; + if (response!=OK) + return; + opened=true; + total_size=p_len; + int pc = ((total_size-1)/page_size)+1; + pages.resize(pc); + + + + +} + +Error FileAccessNetwork::_open(const String& p_path, int p_mode_flags) { + + ERR_FAIL_COND_V(p_mode_flags!=READ,ERR_UNAVAILABLE); + if (opened) + close(); + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + DEBUG_PRINT("open: "+p_path); + + DEBUG_TIME("open_begin"); + + nc->lock_mutex(); + nc->put_32(id); + nc->accesses[id]=this; + nc->put_32(COMMAND_OPEN_FILE); + CharString cs =p_path.utf8(); + nc->put_32(cs.length()); + nc->client->put_data((const uint8_t*)cs.ptr(),cs.length()); + pos=0; + eof_flag=false; + last_page=-1; + last_page_buff=NULL; + +// buffers.clear(); + nc->unlock_mutex(); + DEBUG_PRINT("OPEN POST"); + DEBUG_TIME("open_post"); + nc->sem->post(); //awaiting answer + DEBUG_PRINT("WAIT..."); + sem->wait(); + DEBUG_TIME("open_end"); + DEBUG_PRINT("WAIT ENDED..."); + + return response; +} + +void FileAccessNetwork::close(){ + + if (!opened) + return; + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + + DEBUG_PRINT("CLOSE"); + nc->lock_mutex(); + nc->put_32(id); + nc->put_32(COMMAND_CLOSE); + pages.clear(); + opened=false; + nc->unlock_mutex(); + + +} +bool FileAccessNetwork::is_open() const{ + + return opened; +} + +void FileAccessNetwork::seek(size_t p_position){ + + ERR_FAIL_COND(!opened); + eof_flag=p_position>total_size; + + if (p_position>=total_size) { + p_position=total_size; + } + + pos=p_position; +} + +void FileAccessNetwork::seek_end(int64_t p_position){ + + seek(total_size+p_position); + +} +size_t FileAccessNetwork::get_pos() const{ + + ERR_FAIL_COND_V(!opened,0); + return pos; +} +size_t FileAccessNetwork::get_len() const{ + + ERR_FAIL_COND_V(!opened,0); + return total_size; +} + +bool FileAccessNetwork::eof_reached() const{ + + ERR_FAIL_COND_V(!opened,false); + return eof_flag; +} + +uint8_t FileAccessNetwork::get_8() const{ + + uint8_t v; + get_buffer(&v,1); + return v; + +} + + +void FileAccessNetwork::_queue_page(int p_page) const { + + if (p_page>=pages.size()) + return; + if (pages[p_page].buffer.empty() && !pages[p_page].queued) { + + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + + nc->blockrequest_mutex->lock(); + FileAccessNetworkClient::BlockRequest br; + br.id=id; + br.offset=size_t(p_page)*page_size; + br.size=page_size; + nc->block_requests.push_back(br); + pages[p_page].queued=true; + nc->blockrequest_mutex->unlock(); + DEBUG_PRINT("QUEUE PAGE POST"); + nc->sem->post(); + DEBUG_PRINT("queued "+itos(p_page)); + } + +} + +int FileAccessNetwork::get_buffer(uint8_t *p_dst, int p_length) const{ + + //bool eof=false; + if (pos+p_length>total_size) { + eof_flag=true; + } + if (pos+p_length>=total_size) { + p_length=total_size-pos; + } + +// FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + + uint8_t *buff=last_page_buff; + + for(int i=0;i<p_length;i++) { + + int page=pos/page_size; + + if (page!=last_page) { + buffer_mutex->lock(); + if (pages[page].buffer.empty()) { + //fuck + + waiting_on_page=page; + for(int j=0;j<read_ahead;j++) { + + _queue_page(page+j); + } + buffer_mutex->unlock(); + DEBUG_PRINT("wait"); + page_sem->wait(); + DEBUG_PRINT("done"); + } else { + + for(int j=0;j<read_ahead;j++) { + + _queue_page(page+j); + } + buff=pages[page].buffer.ptr(); + //queue pages + buffer_mutex->unlock(); + } + + buff=pages[page].buffer.ptr(); + last_page_buff=buff; + last_page=page; + } + + p_dst[i]=buff[pos-uint64_t(page)*page_size]; + pos++; + } + + return p_length; +} + +Error FileAccessNetwork::get_error() const{ + + return pos==total_size?ERR_FILE_EOF:OK; +} + +void FileAccessNetwork::store_8(uint8_t p_dest) { + + ERR_FAIL(); +} + +bool FileAccessNetwork::file_exists(const String& p_path){ + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + nc->lock_mutex(); + nc->put_32(id); + nc->put_32(COMMAND_FILE_EXISTS); + CharString cs=p_path.utf8(); + nc->put_32(cs.length()); + nc->client->put_data((const uint8_t*)cs.ptr(),cs.length()); + nc->unlock_mutex(); + DEBUG_PRINT("FILE EXISTS POST"); + nc->sem->post(); + sem->wait(); + + return exists_modtime!=0; + +} + +uint64_t FileAccessNetwork::_get_modified_time(const String& p_file){ + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + nc->lock_mutex(); + nc->put_32(id); + nc->put_32(COMMAND_GET_MODTIME); + CharString cs=p_file.utf8(); + nc->put_32(cs.length()); + nc->client->put_data((const uint8_t*)cs.ptr(),cs.length()); + nc->unlock_mutex(); + DEBUG_PRINT("MODTIME POST"); + nc->sem->post(); + sem->wait(); + + return exists_modtime; + +} + +FileAccessNetwork::FileAccessNetwork() { + + eof_flag=false; + opened=false; + pos=0; + sem=Semaphore::create(); + page_sem=Semaphore::create(); + buffer_mutex=Mutex::create(); + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + nc->lock_mutex(); + id=nc->last_id++; + nc->accesses[id]=this; + nc->unlock_mutex(); + page_size = GLOBAL_DEF("remote_fs/page_size",65536); + read_ahead = GLOBAL_DEF("remote_fs/page_read_ahead",4); + max_pages = GLOBAL_DEF("remote_fs/max_pages",20); + last_activity_val=0; + waiting_on_page=-1; + last_page=-1; + + +} + +FileAccessNetwork::~FileAccessNetwork() { + + close(); + memdelete(sem); + memdelete(page_sem); + memdelete(buffer_mutex); + + FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton; + nc->lock_mutex(); + id=nc->last_id++; + nc->accesses.erase(id); + nc->unlock_mutex(); + +} diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h new file mode 100644 index 0000000000..f338bea43f --- /dev/null +++ b/core/io/file_access_network.h @@ -0,0 +1,169 @@ +/*************************************************************************/ +/* file_access_network.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. */ +/*************************************************************************/ +#ifndef FILE_ACCESS_NETWORK_H +#define FILE_ACCESS_NETWORK_H + +#include "os/file_access.h" +#include "os/semaphore.h" +#include "os/thread.h" +#include "io/stream_peer_tcp.h" + +class FileAccessNetwork; + +class FileAccessNetworkClient { + + + struct BlockRequest { + + int id; + uint64_t offset; + int size; + }; + + int ml; + + List<BlockRequest> block_requests; + + Semaphore *sem; + Thread *thread; + bool quit; + Mutex *mutex; + Mutex *blockrequest_mutex; + Map<int,FileAccessNetwork*> accesses; + Ref<StreamPeerTCP> client; + int last_id; + + Vector<uint8_t> block; + + void _thread_func(); + static void _thread_func(void *s); + + void put_32(int p_32); + void put_64(int64_t p_64); + int get_32(); + int64_t get_64(); + int lockcount; + void lock_mutex(); + void unlock_mutex(); + +friend class FileAccessNetwork; + static FileAccessNetworkClient *singleton; + +public: + + static FileAccessNetworkClient *get_singleton() { return singleton; } + + Error connect(const String& p_host,int p_port,const String& p_password=""); + + FileAccessNetworkClient(); + ~FileAccessNetworkClient(); + +}; + +class FileAccessNetwork : public FileAccess { + + Semaphore *sem; + Semaphore *page_sem; + Mutex *buffer_mutex; + bool opened; + size_t total_size; + mutable size_t pos; + int id; + mutable bool eof_flag; + mutable int last_page; + mutable uint8_t *last_page_buff; + + uint32_t page_size; + int read_ahead; + int max_pages; + + mutable int waiting_on_page; + mutable int last_activity_val; + struct Page { + int activity; + bool queued; + Vector<uint8_t> buffer; + Page() { activity=0; queued=false; } + }; + + mutable Vector< Page > pages; + + mutable Error response; + + uint64_t exists_modtime; +friend class FileAccessNetworkClient; + void _queue_page(int p_page) const; + void _respond(size_t p_len,Error p_status); + void _set_block(size_t p_offset,const Vector<uint8_t>& p_block); + +public: + + enum Command { + COMMAND_OPEN_FILE, + COMMAND_READ_BLOCK, + COMMAND_CLOSE, + COMMAND_FILE_EXISTS, + COMMAND_GET_MODTIME, + }; + + enum Response { + RESPONSE_OPEN, + RESPONSE_DATA, + RESPONSE_FILE_EXISTS, + RESPONSE_GET_MODTIME, + }; + + + virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + virtual int get_buffer(uint8_t *p_dst, int p_length) const; + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + + virtual bool file_exists(const String& p_path); ///< return true if a file exists + + virtual uint64_t _get_modified_time(const String& p_file); + + FileAccessNetwork(); + ~FileAccessNetwork(); +}; + +#endif // FILE_ACCESS_NETWORK_H diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp new file mode 100644 index 0000000000..4443dd3575 --- /dev/null +++ b/core/io/file_access_pack.cpp @@ -0,0 +1,469 @@ +/*************************************************************************/ +/* file_access_pack.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 "file_access_pack.h" +#include "version.h" + +#include <stdio.h> + +Error PackedData::add_pack(const String& p_path) { + + for (int i=0; i<sources.size(); i++) { + + if (sources[i]->try_open_pack(p_path)) { + + return OK; + }; + }; + + return ERR_FILE_UNRECOGNIZED; +}; + +void PackedData::add_path(const String& pkg_path, const String& path, uint64_t ofs, uint64_t size, PackSource* p_src) { + + bool exists = files.has(path); + + PackedFile pf; + pf.pack=pkg_path; + pf.offset=ofs; + pf.size=size; + pf.src = p_src; + + files[path]=pf; + + if (!exists) { + //search for dir + String p = path.replace_first("res://",""); + PackedDir *cd=root; + + if (p.find("/")!=-1) { //in a subdir + + Vector<String> ds=p.get_base_dir().split("/"); + + for(int j=0;j<ds.size();j++) { + + if (!cd->subdirs.has(ds[j])) { + + PackedDir *pd = memnew( PackedDir ); + pd->name=ds[j]; + pd->parent=cd; + cd->subdirs[pd->name]=pd; + cd=pd; + } else { + cd=cd->subdirs[ds[j]]; + } + } + } + cd->files.insert(path.get_file()); + } +} + +void PackedData::add_pack_source(PackSource *p_source) { + + sources.push_back(p_source); +}; + +PackedData *PackedData::singleton=NULL; + +PackedData::PackedData() { + + singleton=this; + root=memnew(PackedDir); + root->parent=NULL; + disabled=false; + + add_pack_source(memnew(PackedSourcePCK)); +} + + +////////////////////////////////////////////////////////////////// + +bool PackedSourcePCK::try_open_pack(const String& p_path) { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) + return false; + + uint32_t magic= f->get_32(); + + if (magic != 0x4b435047) { + //maybe at he end.... self contained exe + f->seek_end(); + f->seek( f->get_pos() -4 ); + magic = f->get_32(); + if (magic != 0x4b435047) { + + memdelete(f); + return false; + } + f->seek( f->get_pos() -12 ); + + + uint64_t ds = f->get_64(); + f->seek( f->get_pos() -ds-8 ); + + magic = f->get_32(); + if (magic != 0x4b435047) { + + memdelete(f); + return false; + } + + } + + uint32_t ver_major = f->get_32(); + uint32_t ver_minor = f->get_32(); + uint32_t ver_rev = f->get_32(); + + ERR_EXPLAIN("Pack created with a newer version of the engine: "+itos(ver_major)+"."+itos(ver_minor)+"."+itos(ver_rev)); + ERR_FAIL_COND_V( ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), ERR_INVALID_DATA); + + for(int i=0;i<16;i++) { + //reserved + f->get_32(); + } + + int file_count = f->get_32(); + + for(int i=0;i<file_count;i++) { + + uint32_t sl = f->get_32(); + CharString cs; + cs.resize(sl+1); + f->get_buffer((uint8_t*)cs.ptr(),sl); + cs[sl]=0; + + String path; + path.parse_utf8(cs.ptr()); + + uint64_t ofs = f->get_64(); + uint64_t size = f->get_64(); + + PackedData::get_singleton()->add_path(p_path, path, ofs, size, this); + }; + + return true; +}; + +FileAccess* PackedSourcePCK::get_file(const String &p_path, PackedData::PackedFile* p_file) { + + return memnew( FileAccessPack(p_path, *p_file)); +}; + +////////////////////////////////////////////////////////////////// + + +Error FileAccessPack::_open(const String& p_path, int p_mode_flags) { + + ERR_FAIL_V(ERR_UNAVAILABLE); + return ERR_UNAVAILABLE; +} + +void FileAccessPack::close() { + + f->close(); +} + +bool FileAccessPack::is_open() const{ + + return f->is_open(); +} + +void FileAccessPack::seek(size_t p_position){ + + if (p_position>pf.size) { + eof=true; + } else { + eof=false; + } + + f->seek(pf.offset+p_position); +} +void FileAccessPack::seek_end(int64_t p_position){ + + seek(pf.size+p_position); + +} +size_t FileAccessPack::get_pos() const { + + return pos; +} +size_t FileAccessPack::get_len() const{ + + return pf.size; +} + +bool FileAccessPack::eof_reached() const{ + + return eof; +} + +uint8_t FileAccessPack::get_8() const { + + if (pos>=pf.size) { + eof=true; + return 0; + } + + pos++; + return f->get_8(); +} + + +int FileAccessPack::get_buffer(uint8_t *p_dst,int p_length) const { + + if (eof) + return 0; + + int64_t to_read=p_length; + if (to_read+pos > pf.size) { + eof=true; + to_read=int64_t(pf.size)-int64_t(pos); + } + + pos+=p_length; + + if (to_read<=0) + return 0; + f->get_buffer(p_dst,to_read); + + return to_read; +} + +void FileAccessPack::set_endian_swap(bool p_swap) { + FileAccess::set_endian_swap(p_swap); + f->set_endian_swap(p_swap); +} + +Error FileAccessPack::get_error() const { + + if (eof) + return ERR_FILE_EOF; + return OK; +} + +void FileAccessPack::store_8(uint8_t p_dest) { + + ERR_FAIL(); + +} + +void FileAccessPack::store_buffer(const uint8_t *p_src,int p_length) { + + ERR_FAIL(); + +} + +bool FileAccessPack::file_exists(const String& p_name) { + + return false; +} + + +FileAccessPack::FileAccessPack(const String& p_path, const PackedData::PackedFile& p_file) { + + pf=p_file; + f=FileAccess::open(pf.pack,FileAccess::READ); + if (!f) { + ERR_EXPLAIN("Can't open pack-referenced file: "+String(pf.pack)); + ERR_FAIL_COND(!f); + } + f->seek(pf.offset); + pos=0; + eof=false; +} + +FileAccessPack::~FileAccessPack() { + if (f) + memdelete(f); +} + + +////////////////////////////////////////////////////////////////////////////////// +// DIR ACCESS +////////////////////////////////////////////////////////////////////////////////// + + +bool DirAccessPack::list_dir_begin() { + + + list_dirs.clear(); + list_files.clear(); + + for (Map<String,PackedData::PackedDir*>::Element *E=current->subdirs.front();E;E=E->next()) { + + list_dirs.push_back(E->key()); + } + + for (Set<String>::Element *E=current->files.front();E;E=E->next()) { + + list_files.push_back(E->get()); + } + + return true; +} + +String DirAccessPack::get_next(){ + + if (list_dirs.size()) { + cdir=true; + String d = list_dirs.front()->get(); + list_dirs.pop_front(); + return d; + } else if (list_files.size()) { + cdir=false; + String f = list_files.front()->get(); + list_files.pop_front(); + return f; + } else { + return String(); + } +} +bool DirAccessPack::current_is_dir() const{ + + return cdir; +} +void DirAccessPack::list_dir_end() { + + list_dirs.clear(); + list_files.clear(); +} + +int DirAccessPack::get_drive_count() { + + return 0; +} +String DirAccessPack::get_drive(int p_drive) { + + return ""; +} + +Error DirAccessPack::change_dir(String p_dir) { + + String nd = p_dir.replace("\\","/"); + bool absolute=false; + if (nd.begins_with("res://")) { + nd=nd.replace_first("res://",""); + absolute=true; + } + + nd=nd.simplify_path(); + + if (nd.begins_with("/")) { + nd=nd.replace_first("/","") ; + absolute=true; + } + + Vector<String> paths = nd.split("/"); + + PackedData::PackedDir *pd; + + if (absolute) + pd = PackedData::get_singleton()->root; + else + pd = current; + + for(int i=0;i<paths.size();i++) { + + String p = paths[i]; + if (p==".") { + continue; + } else if (p=="..") { + if (pd->parent) { + pd=pd->parent; + } + } else if (pd->subdirs.has(p)) { + + pd=pd->subdirs[p]; + + } else { + + return ERR_INVALID_PARAMETER; + } + } + + current=pd; + + return OK; + + +} + +String DirAccessPack::get_current_dir() { + + String p; + PackedData::PackedDir *pd = current; + while(pd->parent) { + + if (pd!=current) + p="/"+p; + p=p+pd->name; + } + + return "res://"+p; + +} + +bool DirAccessPack::file_exists(String p_file){ + + return current->files.has(p_file); +} + +Error DirAccessPack::make_dir(String p_dir){ + + return ERR_UNAVAILABLE; +} + +Error DirAccessPack::rename(String p_from, String p_to){ + + return ERR_UNAVAILABLE; + +} +Error DirAccessPack::remove(String p_name){ + + return ERR_UNAVAILABLE; + +} + +size_t DirAccessPack::get_space_left(){ + + return 0; +} + +DirAccessPack::DirAccessPack() { + + current=PackedData::get_singleton()->root; + cdir=false; +} + +DirAccessPack::~DirAccessPack() { + + +} + + diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h new file mode 100644 index 0000000000..97e236009e --- /dev/null +++ b/core/io/file_access_pack.h @@ -0,0 +1,204 @@ +/*************************************************************************/ +/* file_access_pack.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. */ +/*************************************************************************/ +#ifndef FILE_ACCESS_PACK_H +#define FILE_ACCESS_PACK_H + +#include "os/file_access.h" +#include "os/dir_access.h" +#include "map.h" +#include "list.h" +#include "print_string.h" + +class PackSource; + +class PackedData { +friend class FileAccessPack; +friend class DirAccessPack; +friend class PackSource; + +public: + struct PackedFile { + + String pack; + uint64_t offset; + uint64_t size; + PackSource* src; + }; + +private: + struct PackedDir { + PackedDir *parent; + String name; + Map<String,PackedDir*> subdirs; + Set<String> files; + }; + + + Map<String,PackedFile> files; + Vector<PackSource*> sources; + + PackedDir *root; + //Map<String,PackedDir*> dirs; + + static PackedData *singleton; + bool disabled; + +public: + + void add_pack_source(PackSource* p_source); + void add_path(const String& pkg_path, const String& path, uint64_t ofs, uint64_t size, PackSource* p_src); // for PackSource + + void set_disabled(bool p_disabled) { disabled=p_disabled; } + _FORCE_INLINE_ bool is_disabled() const { return disabled; } + + static PackedData *get_singleton() { return singleton; } + Error add_pack(const String& p_path); + + _FORCE_INLINE_ FileAccess *try_open_path(const String& p_path); + _FORCE_INLINE_ bool has_path(const String& p_path); + + PackedData(); +}; + +class PackSource { + +public: + + virtual bool try_open_pack(const String& p_path)=0; + virtual FileAccess* get_file(const String& p_path, PackedData::PackedFile* p_file)=0; +}; + +class PackedSourcePCK : public PackSource { + +public: + + virtual bool try_open_pack(const String &p_path); + virtual FileAccess* get_file(const String& p_path, PackedData::PackedFile* p_file); +}; + + +class FileAccessPack : public FileAccess { + + PackedData::PackedFile pf; + + mutable size_t pos; + mutable bool eof; + + FileAccess *f; + virtual Error _open(const String& p_path, int p_mode_flags); + virtual uint64_t _get_modified_time(const String& p_file) { return 0; } + +public: + + + virtual void close(); + virtual bool is_open() const; + + virtual void seek(size_t p_position); + virtual void seek_end(int64_t p_position=0); + virtual size_t get_pos() const; + virtual size_t get_len() const; + + virtual bool eof_reached() const; + + virtual uint8_t get_8() const; + + + virtual int get_buffer(uint8_t *p_dst,int p_length) const; + + virtual void set_endian_swap(bool p_swap); + + virtual Error get_error() const; + + virtual void store_8(uint8_t p_dest); + + virtual void store_buffer(const uint8_t *p_src,int p_length); + + virtual bool file_exists(const String& p_name); + + + FileAccessPack(const String& p_path, const PackedData::PackedFile& p_file); + ~FileAccessPack(); +}; + + +FileAccess *PackedData::try_open_path(const String& p_path) { + + if (files.has(p_path)) { + return files[p_path].src->get_file(p_path, &files[p_path]); + } + + return NULL; +} + +bool PackedData::has_path(const String& p_path) { + + return files.has(p_path); +} + + +class DirAccessPack : public DirAccess { + + + PackedData::PackedDir *current; + + List<String> list_dirs; + List<String> list_files; + bool cdir; + +public: + + virtual bool list_dir_begin(); + virtual String get_next(); + virtual bool current_is_dir() const; + virtual void list_dir_end(); + + virtual int get_drive_count(); + virtual String get_drive(int p_drive); + + virtual Error change_dir(String p_dir); + virtual String get_current_dir(); + + + virtual bool file_exists(String p_file); + + virtual Error make_dir(String p_dir); + + virtual Error rename(String p_from, String p_to); + virtual Error remove(String p_name); + + size_t get_space_left(); + + DirAccessPack(); + ~DirAccessPack(); + +}; + + +#endif // FILE_ACCESS_PACK_H diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp new file mode 100644 index 0000000000..72e929619f --- /dev/null +++ b/core/io/file_access_zip.cpp @@ -0,0 +1,367 @@ +/*************************************************************************/ +/* file_access_zip.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. */ +/*************************************************************************/ +#ifdef MINIZIP_ENABLED + +#include "file_access_zip.h" + +#include "core/os/file_access.h" + +ZipArchive* ZipArchive::instance = NULL; + +extern "C" { + +static void* godot_open(void* data, const char* p_fname, int mode) { + + if (mode & ZLIB_FILEFUNC_MODE_WRITE) { + return NULL; + }; + + FileAccess* f = (FileAccess*)data; + f->open(p_fname, FileAccess::READ); + + return f->is_open()?data:NULL; + +}; + +static uLong godot_read(void* data, void* fdata, void* buf, uLong size) { + + FileAccess* f = (FileAccess*)data; + f->get_buffer((uint8_t*)buf, size); + return size; +}; + +static uLong godot_write(voidpf opaque, voidpf stream, const void* buf, uLong size) { + + return 0; +}; + + +static long godot_tell (voidpf opaque, voidpf stream) { + + FileAccess* f = (FileAccess*)opaque; + return f->get_pos(); +}; + +static long godot_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { + + FileAccess* f = (FileAccess*)opaque; + + int pos = offset; + switch (origin) { + + case ZLIB_FILEFUNC_SEEK_CUR: + pos = f->get_pos() + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + pos = f->get_len() + offset; + break; + default: + break; + }; + + f->seek(pos); + return 0; +}; + + +static int godot_close(voidpf opaque, voidpf stream) { + + FileAccess* f = (FileAccess*)opaque; + f->close(); + return 0; +}; + +static int godot_testerror(voidpf opaque, voidpf stream) { + + FileAccess* f = (FileAccess*)opaque; + return f->get_error()!=OK?1:0; +}; + + +}; + + +void ZipArchive::close_handle(unzFile p_file) const { + + ERR_FAIL_COND(!p_file); + FileAccess* f = (FileAccess*)unzGetOpaque(p_file); + unzCloseCurrentFile(p_file); + unzClose(p_file); + memdelete(f); +}; + +unzFile ZipArchive::get_file_handle(String p_file) const { + + ERR_FAIL_COND_V(!file_exists(p_file), NULL); + File file = files[p_file]; + + FileAccess* f = FileAccess::open(packages[file.package].filename, FileAccess::READ); + ERR_FAIL_COND_V(!f, NULL); + + zlib_filefunc_def io; + + io.opaque = f; + io.zopen_file = godot_open; + io.zread_file = godot_read; + io.zwrite_file = godot_write; + + io.ztell_file = godot_tell; + io.zseek_file = godot_seek; + io.zclose_file = godot_close; + io.zerror_file = godot_testerror; + + unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io); + ERR_FAIL_COND_V(!pkg, NULL); + unzGoToFilePos(pkg, &file.file_pos); + if (unzOpenCurrentFile(pkg) != UNZ_OK) { + + unzClose(pkg); + ERR_FAIL_V(NULL); + }; + + return pkg; +}; + +bool ZipArchive::try_open_pack(const String& p_name) { + + printf("opening pack %ls, %i, %i\n", p_name.c_str(), p_name.extension().nocasecmp_to("zip"), p_name.extension().nocasecmp_to("pcz")); + if (p_name.extension().nocasecmp_to("zip") != 0 && p_name.extension().nocasecmp_to("pcz") != 0) + return false; + + zlib_filefunc_def io; + + FileAccess* f = FileAccess::open(p_name, FileAccess::READ); + if (!f) + return false; + io.opaque = f; + io.zopen_file = godot_open; + io.zread_file = godot_read; + io.zwrite_file = godot_write; + + io.ztell_file = godot_tell; + io.zseek_file = godot_seek; + io.zclose_file = godot_close; + io.zerror_file = godot_testerror; + + unzFile zfile = unzOpen2(p_name.utf8().get_data(), &io); + ERR_FAIL_COND_V(!zfile, false); + + unz_global_info64 gi; + int err = unzGetGlobalInfo64(zfile, &gi); + ERR_FAIL_COND_V(err!=UNZ_OK, false); + + Package pkg; + pkg.filename = p_name; + pkg.zfile = zfile; + packages.push_back(pkg); + int pkg_num = packages.size()-1; + + for (unsigned int i=0;i<gi.number_entry;i++) { + + char filename_inzip[256]; + + unz_file_info64 file_info; + err = unzGetCurrentFileInfo64(zfile,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0); + ERR_CONTINUE(err != UNZ_OK); + + File f; + f.package = pkg_num; + unzGetFilePos(zfile, &f.file_pos); + + String fname = String("res://") + filename_inzip; + files[fname] = f; + + PackedData::get_singleton()->add_path(p_name, fname, 0, 0, this); + + if ((i+1)<gi.number_entry) { + unzGoToNextFile(zfile); + }; + }; + + return true; +}; + +bool ZipArchive::file_exists(String p_name) const { + + return files.has(p_name); +}; + +FileAccess* ZipArchive::get_file(const String& p_path, PackedData::PackedFile* p_file) { + + return memnew(FileAccessZip(p_path, *p_file)); +}; + + +ZipArchive* ZipArchive::get_singleton() { + + if (instance == NULL) { + instance = memnew(ZipArchive); + }; + + return instance; +}; + +ZipArchive::ZipArchive() { + + instance = this; + //fa_create_func = FileAccess::get_create_func(); +}; + +ZipArchive::~ZipArchive() { + + for (int i=0; i<packages.size(); i++) { + + FileAccess* f = (FileAccess*)unzGetOpaque(packages[i].zfile); + unzClose(packages[i].zfile); + memdelete(f); + }; + + packages.clear(); +}; + + +Error FileAccessZip::_open(const String& p_path, int p_mode_flags) { + + close(); + + ERR_FAIL_COND_V(p_mode_flags & FileAccess::WRITE, FAILED); + ZipArchive* arch = ZipArchive::get_singleton(); + ERR_FAIL_COND_V(!arch, FAILED); + zfile = arch->get_file_handle(p_path); + ERR_FAIL_COND_V(!zfile, FAILED); + + int err = unzGetCurrentFileInfo64(zfile,&file_info,NULL,0,NULL,0,NULL,0); + ERR_FAIL_COND_V(err != UNZ_OK, FAILED); + + return OK; +}; + +void FileAccessZip::close() { + + if (!zfile) + return; + + ZipArchive* arch = ZipArchive::get_singleton(); + ERR_FAIL_COND(!arch); + arch->close_handle(zfile); + zfile = NULL; +}; + +bool FileAccessZip::is_open() const { + + return zfile != NULL; +}; + +void FileAccessZip::seek(size_t p_position) { + + ERR_FAIL_COND(!zfile); + unzSeekCurrentFile(zfile, p_position); +}; + +void FileAccessZip::seek_end(int64_t p_position) { + + ERR_FAIL_COND(!zfile); + unzSeekCurrentFile(zfile, get_len() + p_position); +}; + +size_t FileAccessZip::get_pos() const { + + ERR_FAIL_COND_V(!zfile, 0); + return unztell(zfile); +}; + +size_t FileAccessZip::get_len() const { + + ERR_FAIL_COND_V(!zfile, 0); + return file_info.uncompressed_size; +}; + +bool FileAccessZip::eof_reached() const { + + ERR_FAIL_COND_V(!zfile, true); + + return at_eof; +}; + +uint8_t FileAccessZip::get_8() const { + + uint8_t ret = 0; + get_buffer(&ret, 1); + return ret; +}; + +int FileAccessZip::get_buffer(uint8_t *p_dst,int p_length) const { + + ERR_FAIL_COND_V(!zfile, -1); + at_eof = unzeof(zfile); + if (at_eof) + return 0; + int read = unzReadCurrentFile(zfile, p_dst, p_length); + ERR_FAIL_COND_V(read < 0, read); + if (read < p_length) + at_eof = true; + return read; +}; + +Error FileAccessZip::get_error() const { + + if (!zfile) { + + return ERR_UNCONFIGURED; + }; + if (eof_reached()) { + return ERR_FILE_EOF; + }; + + return OK; +}; + +void FileAccessZip::store_8(uint8_t p_dest) { + + ERR_FAIL(); +}; + +bool FileAccessZip::file_exists(const String& p_name) { + + return false; +}; + + +FileAccessZip::FileAccessZip(const String& p_path, const PackedData::PackedFile& p_file) { + + zfile = NULL; + _open(p_path, FileAccess::READ); +}; + +FileAccessZip::~FileAccessZip() { + + close(); +}; + +#endif diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h new file mode 100644 index 0000000000..88272e6cfc --- /dev/null +++ b/core/io/file_access_zip.h @@ -0,0 +1,125 @@ +/*************************************************************************/ +/* file_access_zip.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. */ +/*************************************************************************/ +#ifdef MINIZIP_ENABLED + +#ifndef FILE_ACCESS_Zip_H +#define FILE_ACCESS_Zip_H + +#include <stdlib.h> +#include "core/io/file_access_pack.h" +#include "unzip.h" +#include "map.h" + +class ZipArchive : public PackSource { + +public: + + struct File { + + int package; + unz_file_pos file_pos; + File() { + + package = -1; + }; + }; + + +private: + + struct Package { + String filename; + unzFile zfile; + }; + Vector<Package> packages; + + Map<String,File> files; + + static ZipArchive* instance; + + FileAccess::CreateFunc fa_create_func; + +public: + + void close_handle(unzFile p_file) const; + unzFile get_file_handle(String p_file) const; + + Error add_package(String p_name); + + bool file_exists(String p_name) const; + + virtual bool try_open_pack(const String& p_path); + FileAccess* get_file(const String& p_path, PackedData::PackedFile* p_file); + + static ZipArchive* get_singleton(); + + ZipArchive(); + ~ZipArchive(); +}; + + +class FileAccessZip : public FileAccess { + + unzFile zfile; + unz_file_info64 file_info; + + mutable bool at_eof; + + ZipArchive* archive; + +public: + + virtual Error _open(const String& p_path, int p_mode_flags); ///< open a file + virtual void close(); ///< close a file + virtual bool is_open() const; ///< true when file is open + + virtual void seek(size_t p_position); ///< seek to a given position + virtual void seek_end(int64_t p_position=0); ///< seek from the end of file + virtual size_t get_pos() const; ///< get position in the file + virtual size_t get_len() const; ///< get size of the file + + virtual bool eof_reached() const; ///< reading passed EOF + + virtual uint8_t get_8() const; ///< get a byte + virtual int get_buffer(uint8_t *p_dst,int p_length) const; + + virtual Error get_error() const; ///< get last error + + virtual void store_8(uint8_t p_dest); ///< store a byte + virtual bool file_exists(const String& p_name); ///< return true if a file exists + + virtual uint64_t _get_modified_time(const String& p_file) { return 0; } // todo + + FileAccessZip(const String& p_path, const PackedData::PackedFile& p_file); + ~FileAccessZip(); +}; + +#endif // FILE_ACCESS_ZIP_H + +#endif diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp new file mode 100644 index 0000000000..33c5f23f6d --- /dev/null +++ b/core/io/http_client.cpp @@ -0,0 +1,641 @@ +/*************************************************************************/ +/* http_client.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 "http_client.h" + + +Error HTTPClient::connect_url(const String& p_url) { + + return OK; +} + +Error HTTPClient::connect(const String &p_host,int p_port){ + + close(); + conn_port=p_port; + conn_host=p_host; + + if (conn_host.begins_with("http://")) { + + conn_host=conn_host.replace_first("http://",""); + } else if (conn_host.begins_with("https://")) { + //use https + conn_host=conn_host.replace_first("https://",""); + } + + + connection=tcp_connection; + if (conn_host.is_valid_ip_address()) { + //is ip + Error err = tcp_connection->connect(IP_Address(conn_host),p_port); + if (err) { + status=STATUS_CANT_CONNECT; + return err; + } + + status=STATUS_CONNECTING; + } else { + //is hostname + resolving=IP::get_singleton()->resolve_hostname_queue_item(conn_host); + status=STATUS_RESOLVING; + + } + + return OK; +} + + +void HTTPClient::set_connection(const Ref<StreamPeer>& p_connection){ + + close(); + connection=p_connection; + +} + + +Error HTTPClient::request( Method p_method, const String& p_url, const Vector<String>& p_headers,const String& p_body) { + + ERR_FAIL_INDEX_V(p_method,METHOD_MAX,ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(status!=STATUS_CONNECTED,ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(connection.is_null(),ERR_INVALID_DATA); + + + static const char* _methods[METHOD_MAX]={ + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "OPTIONS", + "TRACE", + "CONNECT"}; + + String request=String(_methods[p_method])+" "+p_url+" HTTP/1.1\r\n"; + request+="Host: "+conn_host+":"+itos(conn_port)+"\r\n"; + for(int i=0;i<p_headers.size();i++) { + request+=p_headers[i]+"\r\n"; + } + request+="\r\n"; + request+=p_body; + + CharString cs=request.utf8(); + Error err = connection->put_data((const uint8_t*)cs.ptr(),cs.length()); + if (err) { + close(); + status=STATUS_CONNECTION_ERROR; + return err; + } + + status=STATUS_REQUESTING; + + return OK; +} + +Error HTTPClient::send_body_text(const String& p_body){ + + return OK; +} + +Error HTTPClient::send_body_data(const ByteArray& p_body){ + + return OK; +} + +bool HTTPClient::has_response() const { + + return response_headers.size()!=0; +} + +bool HTTPClient::is_response_chunked() const { + + return chunked; +} + +int HTTPClient::get_response_code() const { + + return response_num; +} +Error HTTPClient::get_response_headers(List<String> *r_response) { + + if (!response_headers.size()) + return ERR_INVALID_PARAMETER; + + for(int i=0;i<response_headers.size();i++) { + + r_response->push_back(response_headers[i]); + } + + response_headers.clear(); + + return OK; +} + + +void HTTPClient::close(){ + + if (tcp_connection->get_status()!=StreamPeerTCP::STATUS_NONE) + tcp_connection->disconnect(); + + connection.unref(); + status=STATUS_DISCONNECTED; + if (resolving!=IP::RESOLVER_INVALID_ID) { + + IP::get_singleton()->erase_resolve_item(resolving); + resolving=IP::RESOLVER_INVALID_ID; + + } + + response_headers.clear(); + response_str.clear(); + body_size=0; + body_left=0; + chunk_left=0; + response_num=0; +} + + +Error HTTPClient::poll(){ + + switch(status) { + + + case STATUS_RESOLVING: { + ERR_FAIL_COND_V(resolving==IP::RESOLVER_INVALID_ID,ERR_BUG); + + IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving); + switch(rstatus) { + case IP::RESOLVER_STATUS_WAITING: return OK; //still resolving + + case IP::RESOLVER_STATUS_DONE: { + + IP_Address host = IP::get_singleton()->get_resolve_item_address(resolving); + Error err = tcp_connection->connect(host,conn_port); + IP::get_singleton()->erase_resolve_item(resolving); + resolving=IP::RESOLVER_INVALID_ID; + if (err) { + status=STATUS_CANT_CONNECT; + return err; + } + + status=STATUS_CONNECTING; + } break; + case IP::RESOLVER_STATUS_NONE: + case IP::RESOLVER_STATUS_ERROR: { + + IP::get_singleton()->erase_resolve_item(resolving); + resolving=IP::RESOLVER_INVALID_ID; + close(); + status=STATUS_CANT_RESOLVE; + return ERR_CANT_RESOLVE; + } break; + + } + } break; + case STATUS_CONNECTING: { + + StreamPeerTCP::Status s = tcp_connection->get_status(); + switch(s) { + + case StreamPeerTCP::STATUS_CONNECTING: { + return OK; //do none + } break; + case StreamPeerTCP::STATUS_CONNECTED: { + status=STATUS_CONNECTED; + return OK; + } break; + case StreamPeerTCP::STATUS_ERROR: + case StreamPeerTCP::STATUS_NONE: { + + close(); + status=STATUS_CANT_CONNECT; + return ERR_CANT_CONNECT; + } break; + } + } break; + case STATUS_CONNECTED: { + //request something please + return OK; + } break; + case STATUS_REQUESTING: { + + + while(true) { + uint8_t byte; + int rec=0; + Error err = connection->get_partial_data(&byte,1,rec); + if (err!=OK) { + close(); + status=STATUS_CONNECTION_ERROR; + return ERR_CONNECTION_ERROR; + } + + if (rec==0) + return OK; //keep trying! + + response_str.push_back(byte); + int rs = response_str.size(); + if ( + (rs>=2 && response_str[rs-2]=='\n' && response_str[rs-1]=='\n') || + (rs>=4 && response_str[rs-4]=='\r' && response_str[rs-3]=='\n' && rs>=4 && response_str[rs-2]=='\r' && response_str[rs-1]=='\n') + ) { + + + //end of response, parse. + response_str.push_back(0); + String response; + response.parse_utf8((const char*)response_str.ptr()); + print_line("END OF RESPONSE? :\n"+response+"\n------"); + Vector<String> responses = response.split("\n"); + body_size=0; + chunked=false; + body_left=0; + chunk_left=0; + response_headers.clear(); + response_num = RESPONSE_OK; + + for(int i=0;i<responses.size();i++) { + + String s = responses[i].strip_edges(); + if (s.length()==0) + continue; + if (s.begins_with("Content-Length:")) { + body_size = s.substr(s.find(":")+1,s.length()).strip_edges().to_int(); + body_left=body_size; + } + + if (s.begins_with("Transfer-Encoding:")) { + String encoding = s.substr(s.find(":")+1,s.length()).strip_edges(); + print_line("TRANSFER ENCODING: "+encoding); + if (encoding=="chunked") { + chunked=true; + } + + } + + if (i==0 && responses[i].begins_with("HTTP")) { + + String num = responses[i].get_slice(" ",1); + response_num=num.to_int(); + } else { + + response_headers.push_back(s); + } + + } + + if (body_size==0 && !chunked) { + + status=STATUS_CONNECTED; //ask for something again? + } else { + status=STATUS_BODY; + } + return OK; + } + } + //wait for response + return OK; + } break; + case STATUS_DISCONNECTED: { + return ERR_UNCONFIGURED; + } break; + case STATUS_CONNECTION_ERROR: { + return ERR_CONNECTION_ERROR; + } break; + case STATUS_CANT_CONNECT: { + return ERR_CANT_CONNECT; + } break; + case STATUS_CANT_RESOLVE: { + return ERR_CANT_RESOLVE; + } break; + } + + + return OK; +} + + +Dictionary HTTPClient::_get_response_headers_as_dictionary() { + + List<String> rh; + get_response_headers(&rh); + Dictionary ret; + for(const List<String>::Element *E=rh.front();E;E=E->next()) { + String s = E->get(); + int sp = s.find(":"); + if (sp==-1) + continue; + String key = s.substr(0,sp).strip_edges(); + String value = s.substr(sp+1,s.length()).strip_edges(); + ret[key]=value; + + } + + return ret; +} + +StringArray HTTPClient::_get_response_headers() { + + List<String> rh; + get_response_headers(&rh); + StringArray ret; + ret.resize(rh.size()); + int idx=0; + for(const List<String>::Element *E=rh.front();E;E=E->next()) { + ret.set(idx++,E->get()); + } + + return ret; +} + +int HTTPClient::get_response_body_length() const { + + return body_size; +} + +ByteArray HTTPClient::read_response_body_chunk() { + + ERR_FAIL_COND_V( status !=STATUS_BODY, ByteArray() ); + + Error err=OK; + + if (chunked) { + + while(true) { + + if (chunk_left==0) { + //reading len + uint8_t b; + int rec=0; + err = connection->get_partial_data(&b,1,rec); + + if (rec==0) + break; + + chunk.push_back(b); + + if (chunk.size()>32) { + ERR_PRINT("HTTP Invalid chunk hex len"); + status=STATUS_CONNECTION_ERROR; + return ByteArray(); + } + + if (chunk.size()>2 && chunk[chunk.size()-2]=='\r' && chunk[chunk.size()-1]=='\n') { + + int len=0; + for(int i=0;i<chunk.size()-2;i++) { + char c = chunk[i]; + int v=0; + if (c>='0' && c<='9') + v=c-'0'; + else if (c>='a' && c<='f') + v=c-'a'+10; + else if (c>='A' && c<='F') + v=c-'A'+10; + else { + ERR_PRINT("HTTP Chunk len not in hex!!"); + status=STATUS_CONNECTION_ERROR; + return ByteArray(); + } + len<<=4; + len|=v; + if (len>(1<<24)) { + ERR_PRINT("HTTP Chunk too big!! >16mb"); + status=STATUS_CONNECTION_ERROR; + return ByteArray(); + } + + } + + if (len==0) { + //end! + status=STATUS_CONNECTED; + chunk.clear(); + return ByteArray(); + } + + chunk_left=len+2; + chunk.resize(chunk_left); + + } + } else { + + int rec=0; + err = connection->get_partial_data(&chunk[chunk.size()-chunk_left],chunk_left,rec); + if (rec==0) { + break; + } + chunk_left-=rec; + + if (chunk_left==0) { + + if (chunk[chunk.size()-2]!='\r' || chunk[chunk.size()-1]!='\n') { + ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)"); + status=STATUS_CONNECTION_ERROR; + return ByteArray(); + } + + ByteArray ret; + ret.resize(chunk.size()-2); + { + ByteArray::Write w = ret.write(); + copymem(w.ptr(),chunk.ptr(),chunk.size()-2); + } + chunk.clear(); + + return ret; + + } + + break; + } + } + + } else { + ByteArray::Write r = tmp_read.write(); + int rec=0; + err = connection->get_partial_data(r.ptr(),MIN(body_left,tmp_read.size()),rec); + if (rec>0) { + ByteArray ret; + ret.resize(rec); + ByteArray::Write w = ret.write(); + copymem(w.ptr(),r.ptr(),rec); + body_left-=rec; + if (body_left==0) { + status=STATUS_CONNECTED; + } + return ret; + } + + } + + + if (err!=OK) { + close(); + if (err==ERR_FILE_EOF) { + + status=STATUS_DISCONNECTED; //server disconnected + } else { + + status=STATUS_CONNECTION_ERROR; + } + } else if (body_left==0 && !chunked) { + + status=STATUS_CONNECTED; + } + + return ByteArray(); +} + +HTTPClient::Status HTTPClient::get_status() const { + + + return status; +} + +void HTTPClient::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("connect:Error","host","port"),&HTTPClient::connect); + ObjectTypeDB::bind_method(_MD("set_connection","connection:StreamPeer"),&HTTPClient::set_connection); + ObjectTypeDB::bind_method(_MD("request","method","url","headers","body"),&HTTPClient::request,DEFVAL(String())); + ObjectTypeDB::bind_method(_MD("send_body_text","body"),&HTTPClient::send_body_text); + ObjectTypeDB::bind_method(_MD("send_body_data","body"),&HTTPClient::send_body_data); + ObjectTypeDB::bind_method(_MD("close"),&HTTPClient::close); + + ObjectTypeDB::bind_method(_MD("has_response"),&HTTPClient::has_response); + ObjectTypeDB::bind_method(_MD("is_response_chunked"),&HTTPClient::is_response_chunked); + ObjectTypeDB::bind_method(_MD("get_response_code"),&HTTPClient::get_response_code); + ObjectTypeDB::bind_method(_MD("get_response_headers"),&HTTPClient::_get_response_headers); + ObjectTypeDB::bind_method(_MD("get_response_headers_as_dictionary"),&HTTPClient::_get_response_headers_as_dictionary); + ObjectTypeDB::bind_method(_MD("get_response_body_length"),&HTTPClient::get_response_body_length); + ObjectTypeDB::bind_method(_MD("read_response_body_chunk"),&HTTPClient::read_response_body_chunk); + + ObjectTypeDB::bind_method(_MD("get_status"),&HTTPClient::get_status); + ObjectTypeDB::bind_method(_MD("poll:Error"),&HTTPClient::poll); + + BIND_CONSTANT( METHOD_GET ); + BIND_CONSTANT( METHOD_HEAD ); + BIND_CONSTANT( METHOD_POST ); + BIND_CONSTANT( METHOD_PUT ); + BIND_CONSTANT( METHOD_DELETE ); + BIND_CONSTANT( METHOD_OPTIONS ); + BIND_CONSTANT( METHOD_TRACE ); + BIND_CONSTANT( METHOD_CONNECT ); + BIND_CONSTANT( METHOD_MAX ); + + BIND_CONSTANT( STATUS_DISCONNECTED ); + BIND_CONSTANT( STATUS_RESOLVING ); //resolving hostname (if passed a hostname) + BIND_CONSTANT( STATUS_CANT_RESOLVE ); + BIND_CONSTANT( STATUS_CONNECTING ); //connecting to ip + BIND_CONSTANT( STATUS_CANT_CONNECT ); + BIND_CONSTANT( STATUS_CONNECTED ); //connected ); requests only accepted here + BIND_CONSTANT( STATUS_REQUESTING ); // request in progress + BIND_CONSTANT( STATUS_BODY ); // request resulted in body ); which must be read + BIND_CONSTANT( STATUS_CONNECTION_ERROR ); + + + BIND_CONSTANT( RESPONSE_CONTINUE ); + BIND_CONSTANT( RESPONSE_SWITCHING_PROTOCOLS ); + BIND_CONSTANT( RESPONSE_PROCESSING ); + + // 2xx successful + BIND_CONSTANT( RESPONSE_OK ); + BIND_CONSTANT( RESPONSE_CREATED ); + BIND_CONSTANT( RESPONSE_ACCEPTED ); + BIND_CONSTANT( RESPONSE_NON_AUTHORITATIVE_INFORMATION ); + BIND_CONSTANT( RESPONSE_NO_CONTENT ); + BIND_CONSTANT( RESPONSE_RESET_CONTENT ); + BIND_CONSTANT( RESPONSE_PARTIAL_CONTENT ); + BIND_CONSTANT( RESPONSE_MULTI_STATUS ); + BIND_CONSTANT( RESPONSE_IM_USED ); + + // 3xx redirection + BIND_CONSTANT( RESPONSE_MULTIPLE_CHOICES ); + BIND_CONSTANT( RESPONSE_MOVED_PERMANENTLY ); + BIND_CONSTANT( RESPONSE_FOUND ); + BIND_CONSTANT( RESPONSE_SEE_OTHER ); + BIND_CONSTANT( RESPONSE_NOT_MODIFIED ); + BIND_CONSTANT( RESPONSE_USE_PROXY ); + BIND_CONSTANT( RESPONSE_TEMPORARY_REDIRECT ); + + // 4xx client error + BIND_CONSTANT( RESPONSE_BAD_REQUEST ); + BIND_CONSTANT( RESPONSE_UNAUTHORIZED ); + BIND_CONSTANT( RESPONSE_PAYMENT_REQUIRED ); + BIND_CONSTANT( RESPONSE_FORBIDDEN ); + BIND_CONSTANT( RESPONSE_NOT_FOUND ); + BIND_CONSTANT( RESPONSE_METHOD_NOT_ALLOWED ); + BIND_CONSTANT( RESPONSE_NOT_ACCEPTABLE ); + BIND_CONSTANT( RESPONSE_PROXY_AUTHENTICATION_REQUIRED ); + BIND_CONSTANT( RESPONSE_REQUEST_TIMEOUT ); + BIND_CONSTANT( RESPONSE_CONFLICT ); + BIND_CONSTANT( RESPONSE_GONE ); + BIND_CONSTANT( RESPONSE_LENGTH_REQUIRED ); + BIND_CONSTANT( RESPONSE_PRECONDITION_FAILED ); + BIND_CONSTANT( RESPONSE_REQUEST_ENTITY_TOO_LARGE ); + BIND_CONSTANT( RESPONSE_REQUEST_URI_TOO_LONG ); + BIND_CONSTANT( RESPONSE_UNSUPPORTED_MEDIA_TYPE ); + BIND_CONSTANT( RESPONSE_REQUESTED_RANGE_NOT_SATISFIABLE ); + BIND_CONSTANT( RESPONSE_EXPECTATION_FAILED ); + BIND_CONSTANT( RESPONSE_UNPROCESSABLE_ENTITY ); + BIND_CONSTANT( RESPONSE_LOCKED ); + BIND_CONSTANT( RESPONSE_FAILED_DEPENDENCY ); + BIND_CONSTANT( RESPONSE_UPGRADE_REQUIRED ); + + // 5xx server error + BIND_CONSTANT( RESPONSE_INTERNAL_SERVER_ERROR ); + BIND_CONSTANT( RESPONSE_NOT_IMPLEMENTED ); + BIND_CONSTANT( RESPONSE_BAD_GATEWAY ); + BIND_CONSTANT( RESPONSE_SERVICE_UNAVAILABLE ); + BIND_CONSTANT( RESPONSE_GATEWAY_TIMEOUT ); + BIND_CONSTANT( RESPONSE_HTTP_VERSION_NOT_SUPPORTED ); + BIND_CONSTANT( RESPONSE_INSUFFICIENT_STORAGE ); + BIND_CONSTANT( RESPONSE_NOT_EXTENDED ); + +} + +HTTPClient::HTTPClient(){ + + tcp_connection = StreamPeerTCP::create(); + resolving = IP::RESOLVER_INVALID_ID; + status=STATUS_DISCONNECTED; + conn_port=80; + body_size=0; + chunked=false; + body_left=0; + chunk_left=0; + response_num=0; + + tmp_read.resize(4096); +} + +HTTPClient::~HTTPClient(){ + + +} + + diff --git a/core/io/http_client.h b/core/io/http_client.h new file mode 100644 index 0000000000..2f22ba1fde --- /dev/null +++ b/core/io/http_client.h @@ -0,0 +1,188 @@ +/*************************************************************************/ +/* http_client.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. */ +/*************************************************************************/ +#ifndef HTTP_CLIENT_H +#define HTTP_CLIENT_H + +#include "io/stream_peer.h" +#include "io/stream_peer_tcp.h" +#include "io/ip.h" +#include "reference.h" + + +class HTTPClient : public Reference { + + OBJ_TYPE(HTTPClient,Reference); +public: + + enum RespondeCode { + + // 1xx informational + RESPONSE_CONTINUE = 100, + RESPONSE_SWITCHING_PROTOCOLS = 101, + RESPONSE_PROCESSING = 102, + + // 2xx successful + RESPONSE_OK = 200, + RESPONSE_CREATED = 201, + RESPONSE_ACCEPTED = 202, + RESPONSE_NON_AUTHORITATIVE_INFORMATION = 203, + RESPONSE_NO_CONTENT = 204, + RESPONSE_RESET_CONTENT = 205, + RESPONSE_PARTIAL_CONTENT = 206, + RESPONSE_MULTI_STATUS = 207, + RESPONSE_IM_USED = 226, + + // 3xx redirection + RESPONSE_MULTIPLE_CHOICES = 300, + RESPONSE_MOVED_PERMANENTLY = 301, + RESPONSE_FOUND = 302, + RESPONSE_SEE_OTHER = 303, + RESPONSE_NOT_MODIFIED = 304, + RESPONSE_USE_PROXY = 305, + RESPONSE_TEMPORARY_REDIRECT = 307, + + // 4xx client error + RESPONSE_BAD_REQUEST = 400, + RESPONSE_UNAUTHORIZED = 401, + RESPONSE_PAYMENT_REQUIRED = 402, + RESPONSE_FORBIDDEN = 403, + RESPONSE_NOT_FOUND = 404, + RESPONSE_METHOD_NOT_ALLOWED = 405, + RESPONSE_NOT_ACCEPTABLE = 406, + RESPONSE_PROXY_AUTHENTICATION_REQUIRED = 407, + RESPONSE_REQUEST_TIMEOUT = 408, + RESPONSE_CONFLICT = 409, + RESPONSE_GONE = 410, + RESPONSE_LENGTH_REQUIRED = 411, + RESPONSE_PRECONDITION_FAILED = 412, + RESPONSE_REQUEST_ENTITY_TOO_LARGE = 413, + RESPONSE_REQUEST_URI_TOO_LONG = 414, + RESPONSE_UNSUPPORTED_MEDIA_TYPE = 415, + RESPONSE_REQUESTED_RANGE_NOT_SATISFIABLE = 416, + RESPONSE_EXPECTATION_FAILED = 417, + RESPONSE_UNPROCESSABLE_ENTITY = 422, + RESPONSE_LOCKED = 423, + RESPONSE_FAILED_DEPENDENCY = 424, + RESPONSE_UPGRADE_REQUIRED = 426, + + // 5xx server error + RESPONSE_INTERNAL_SERVER_ERROR = 500, + RESPONSE_NOT_IMPLEMENTED = 501, + RESPONSE_BAD_GATEWAY = 502, + RESPONSE_SERVICE_UNAVAILABLE = 503, + RESPONSE_GATEWAY_TIMEOUT = 504, + RESPONSE_HTTP_VERSION_NOT_SUPPORTED = 505, + RESPONSE_INSUFFICIENT_STORAGE = 507, + RESPONSE_NOT_EXTENDED = 510, + + }; + + enum Method { + + METHOD_GET, + METHOD_HEAD, + METHOD_POST, + METHOD_PUT, + METHOD_DELETE, + METHOD_OPTIONS, + METHOD_TRACE, + METHOD_CONNECT, + METHOD_MAX + }; + + enum Status { + STATUS_DISCONNECTED, + STATUS_RESOLVING, //resolving hostname (if passed a hostname) + STATUS_CANT_RESOLVE, + STATUS_CONNECTING, //connecting to ip + STATUS_CANT_CONNECT, + STATUS_CONNECTED, //connected, requests only accepted here + STATUS_REQUESTING, // request in progress + STATUS_BODY, // request resulted in body, which must be read + STATUS_CONNECTION_ERROR, + }; + +private: + + Status status; + IP::ResolverID resolving; + int conn_port; + String conn_host; + + Vector<uint8_t> response_str; + + bool chunked; + Vector<uint8_t> chunk; + int chunk_left; + int body_size; + int body_left; + + Ref<StreamPeerTCP> tcp_connection; + Ref<StreamPeer> connection; + + int response_num; + Vector<String> response_headers; + + static void _bind_methods(); + StringArray _get_response_headers(); + Dictionary _get_response_headers_as_dictionary(); + ByteArray tmp_read; +public: + + + Error connect_url(const String& p_url); //connects to a full url and perform request + Error connect(const String &p_host,int p_port); + + void set_connection(const Ref<StreamPeer>& p_connection); + + Error request( Method p_method, const String& p_url, const Vector<String>& p_headers,const String& p_body=String()); + Error send_body_text(const String& p_body); + Error send_body_data(const ByteArray& p_body); + + void close(); + + Status get_status() const; + + bool has_response() const; + bool is_response_chunked() const; + int get_response_code() const; + Error get_response_headers(List<String> *r_response); + int get_response_body_length() const; + + ByteArray read_response_body_chunk(); // can't get body as partial text because of most encodings UTF8, gzip, etc. + + Error poll(); + + HTTPClient(); + ~HTTPClient(); +}; + +VARIANT_ENUM_CAST(HTTPClient::Method); + +#endif // HTTP_CLIENT_H diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp new file mode 100644 index 0000000000..180df8964b --- /dev/null +++ b/core/io/image_loader.cpp @@ -0,0 +1,112 @@ +/*************************************************************************/ +/* image_loader.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 "image_loader.h" + +#include "print_string.h" +bool ImageFormatLoader::recognize(const String& p_extension) const { + + + List<String> extensions; + get_recognized_extensions(&extensions); + for (List<String>::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(p_extension.extension())==0) + return true; + } + + return false; +} + +Error ImageLoader::load_image(String p_file,Image *p_image, FileAccess *p_custom) { + + + FileAccess *f=p_custom; + if (!f) { + Error err; + f=FileAccess::open(p_file,FileAccess::READ,&err); + if (!f) + return err; + } + + String extension = p_file.extension(); + + + for (int i=0;i<loader_count;i++) { + + if (!loader[i]->recognize(extension)) + continue; + Error err = loader[i]->load_image(p_image,f); + if (err!=ERR_FILE_UNRECOGNIZED) { + if (!p_custom) + memdelete(f); + return err; + } + + + } + + if (!p_custom) + memdelete(f); + + return ERR_FILE_UNRECOGNIZED; + +} + +void ImageLoader::get_recognized_extensions(List<String> *p_extensions) { + + for (int i=0;i<loader_count;i++) { + + loader[i]->get_recognized_extensions(p_extensions); + + } +} + +bool ImageLoader::recognize(const String& p_extension) { + + for (int i=0;i<loader_count;i++) { + + if (loader[i]->recognize(p_extension)) + return true; + + } + + return false; +} + +ImageFormatLoader *ImageLoader::loader[MAX_LOADERS]; +int ImageLoader::loader_count=0; + +void ImageLoader::add_image_format_loader(ImageFormatLoader *p_loader) { + + ERR_FAIL_COND(loader_count >=MAX_LOADERS ); + loader[loader_count++]=p_loader; +} + + + diff --git a/core/io/image_loader.h b/core/io/image_loader.h new file mode 100644 index 0000000000..665cc0b460 --- /dev/null +++ b/core/io/image_loader.h @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* image_loader.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. */ +/*************************************************************************/ +#ifndef IMAGE_LOADER_H +#define IMAGE_LOADER_H + +#include "image.h" +#include "ustring.h" +#include "os/file_access.h" +#include "list.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + + +/** + * @class ImageScanLineLoader + * @author Juan Linietsky <reduzio@gmail.com> + * + + */ +class ImageLoader; + + +/** + * @class ImageLoader + * Base Class and singleton for loading images from disk + * Can load images in one go, or by scanline + */ + + +class ImageFormatLoader { +friend class ImageLoader; +protected: + virtual Error load_image(Image *p_image,FileAccess *p_fileaccess)=0; + virtual void get_recognized_extensions(List<String> *p_extensions) const=0; + bool recognize(const String& p_extension) const; + + +public: + virtual ~ImageFormatLoader() {} +}; + +class ImageLoader { + + enum { + MAX_LOADERS=8 + }; + + static ImageFormatLoader *loader[MAX_LOADERS]; + static int loader_count; + +protected: + + +public: + + static Error load_image(String p_file,Image *p_image, FileAccess *p_custom=NULL); + static void get_recognized_extensions(List<String> *p_extensions) ; + static bool recognize(const String& p_extension) ; + + static void add_image_format_loader(ImageFormatLoader *p_loader); + +}; + +#endif diff --git a/core/io/ioapi.c b/core/io/ioapi.c new file mode 100644 index 0000000000..cc49d775b9 --- /dev/null +++ b/core/io/ioapi.c @@ -0,0 +1,235 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if (defined(_WIN32)) + #define _CRT_SECURE_NO_WARNINGS +#endif + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == ((uLong)-1)) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + +/* + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen64((const char*)filename, mode_fopen); + return file; +} + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = ftello64((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(fseeko64((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} +*/ diff --git a/core/io/ioapi.h b/core/io/ioapi.h new file mode 100644 index 0000000000..53d01d65c5 --- /dev/null +++ b/core/io/ioapi.h @@ -0,0 +1,199 @@ +/* this file is part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#include <stdio.h> +#include <stdlib.h> +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include <stdint.h> + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/core/io/ip.cpp b/core/io/ip.cpp new file mode 100644 index 0000000000..503a009444 --- /dev/null +++ b/core/io/ip.cpp @@ -0,0 +1,270 @@ +/*************************************************************************/ +/* ip.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 "ip.h" +#include "os/thread.h" +#include "os/semaphore.h" +#include "hash_map.h" + + + +/************* RESOLVER ******************/ + + +struct _IP_ResolverPrivate { + + struct QueueItem { + + volatile IP::ResolverStatus status; + IP_Address response; + String hostname; + + void clear() { + status = IP::RESOLVER_STATUS_NONE; + response = IP_Address(); + hostname=""; + }; + + QueueItem() { + clear(); + }; + }; + + QueueItem queue[IP::RESOLVER_MAX_QUERIES]; + + IP::ResolverID find_empty_id() const { + + for(int i=0;i<IP::RESOLVER_MAX_QUERIES;i++) { + if (queue[i].status==IP::RESOLVER_STATUS_NONE) + return i; + } + return IP::RESOLVER_INVALID_ID; + } + + Semaphore *sem; + + Thread* thread; + //Semaphore* semaphore; + bool thread_abort; + + void resolve_queues() { + + for(int i=0;i<IP::RESOLVER_MAX_QUERIES;i++) { + + if (queue[i].status!=IP::RESOLVER_STATUS_WAITING) + continue; + queue[i].response=IP::get_singleton()->resolve_hostname(queue[i].hostname); + + if (queue[i].response.host==0) + queue[i].status=IP::RESOLVER_STATUS_ERROR; + else + queue[i].status=IP::RESOLVER_STATUS_DONE; + + } + } + + + static void _thread_function(void *self) { + + _IP_ResolverPrivate *ipr=(_IP_ResolverPrivate*)self; + + while(!ipr->thread_abort) { + + ipr->sem->wait(); + GLOBAL_LOCK_FUNCTION; + ipr->resolve_queues(); + + } + + } + + HashMap<String, IP_Address> cache; + +}; + + + +IP_Address IP::resolve_hostname(const String& p_hostname) { + + GLOBAL_LOCK_FUNCTION + + if (resolver->cache.has(p_hostname)) + return resolver->cache[p_hostname]; + + IP_Address res = _resolve_hostname(p_hostname); + resolver->cache[p_hostname]=res; + return res; + +} +IP::ResolverID IP::resolve_hostname_queue_item(const String& p_hostname) { + + GLOBAL_LOCK_FUNCTION + + ResolverID id = resolver->find_empty_id(); + + if (id==RESOLVER_INVALID_ID) { + WARN_PRINT("Out of resolver queries"); + return id; + } + + resolver->queue[id].hostname=p_hostname; + if (resolver->cache.has(p_hostname)) { + resolver->queue[id].response=resolver->cache[p_hostname]; + resolver->queue[id].status=IP::RESOLVER_STATUS_DONE; + } else { + resolver->queue[id].response=IP_Address(); + resolver->queue[id].status=IP::RESOLVER_STATUS_WAITING; + if (resolver->thread) + resolver->sem->post(); + else + resolver->resolve_queues(); + } + + + + + + return id; +} + +IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { + + ERR_FAIL_INDEX_V(p_id,IP::RESOLVER_MAX_QUERIES,IP::RESOLVER_STATUS_NONE); + + GLOBAL_LOCK_FUNCTION; + ERR_FAIL_COND_V(resolver->queue[p_id].status==IP::RESOLVER_STATUS_NONE,IP::RESOLVER_STATUS_NONE); + + return resolver->queue[p_id].status; + +} +IP_Address IP::get_resolve_item_address(ResolverID p_id) const { + + ERR_FAIL_INDEX_V(p_id,IP::RESOLVER_MAX_QUERIES,IP_Address()); + + GLOBAL_LOCK_FUNCTION; + + if (resolver->queue[p_id].status!=IP::RESOLVER_STATUS_DONE) { + ERR_EXPLAIN("Resolve of '"+resolver->queue[p_id].hostname+"'' didn't complete yet."); + ERR_FAIL_COND_V(resolver->queue[p_id].status!=IP::RESOLVER_STATUS_DONE,IP_Address()); + } + + + return resolver->queue[p_id].response; + +} +void IP::erase_resolve_item(ResolverID p_id) { + + ERR_FAIL_INDEX(p_id,IP::RESOLVER_MAX_QUERIES); + + GLOBAL_LOCK_FUNCTION; + + resolver->queue[p_id].status=IP::RESOLVER_STATUS_DONE; + +} + + +void IP::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("resolve_hostname","host"),&IP::resolve_hostname); + ObjectTypeDB::bind_method(_MD("resolve_hostname_queue_item","host"),&IP::resolve_hostname_queue_item); + ObjectTypeDB::bind_method(_MD("get_resolve_item_status","id"),&IP::get_resolve_item_status); + ObjectTypeDB::bind_method(_MD("get_resolve_item_address","id"),&IP::get_resolve_item_address); + ObjectTypeDB::bind_method(_MD("erase_resolve_item","id"),&IP::erase_resolve_item); + + BIND_CONSTANT( RESOLVER_STATUS_NONE ); + BIND_CONSTANT( RESOLVER_STATUS_WAITING ); + BIND_CONSTANT( RESOLVER_STATUS_DONE ); + BIND_CONSTANT( RESOLVER_STATUS_ERROR ); + + BIND_CONSTANT( RESOLVER_MAX_QUERIES ); + BIND_CONSTANT( RESOLVER_INVALID_ID ); + +} + + +IP*IP::singleton=NULL; + +IP* IP::get_singleton() { + + return singleton; +} + + +IP* (*IP::_create)()=NULL; + +IP* IP::create() { + + ERR_FAIL_COND_V(singleton,NULL); + ERR_FAIL_COND_V(!_create,NULL); + return _create(); +} + +IP::IP() { + + singleton=this; + resolver = memnew( _IP_ResolverPrivate ); + resolver->sem=NULL; + +#ifndef NO_THREADS + + //resolver->sem = Semaphore::create(); + + resolver->sem=NULL; + if (resolver->sem) { + resolver->thread_abort=false; + + resolver->thread = Thread::create( _IP_ResolverPrivate::_thread_function,resolver ); + + if (!resolver->thread) + memdelete(resolver->sem); //wtf + } else { + resolver->thread=NULL; + } +#else + resolver->sem = NULL; + resolver->thread=NULL; +#endif + + +} + +IP::~IP() { + +#ifndef NO_THREADS + if (resolver->thread) { + resolver->thread_abort=true; + resolver->sem->post(); + Thread::wait_to_finish(resolver->thread); + memdelete( resolver->thread ); + memdelete( resolver->sem); + } + memdelete(resolver); + +#endif + +} diff --git a/core/io/ip.h b/core/io/ip.h new file mode 100644 index 0000000000..f1ef5fe794 --- /dev/null +++ b/core/io/ip.h @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* ip.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. */ +/*************************************************************************/ +#ifndef IP_H +#define IP_H + + +#include "os/os.h" +#include "io/ip_address.h" + +struct _IP_ResolverPrivate; + +class IP : public Object { + OBJ_TYPE( IP, Object ); + OBJ_CATEGORY("Networking"); +public: + + enum ResolverStatus { + + RESOLVER_STATUS_NONE, + RESOLVER_STATUS_WAITING, + RESOLVER_STATUS_DONE, + RESOLVER_STATUS_ERROR, + }; + + enum { + RESOLVER_MAX_QUERIES = 32, + RESOLVER_INVALID_ID=-1 + }; + + + typedef int ResolverID; + + +private: + + _IP_ResolverPrivate *resolver; +protected: + + static IP*singleton; + static void _bind_methods(); + + virtual IP_Address _resolve_hostname(const String& p_hostname)=0; + + static IP* (*_create)(); +public: + + + IP_Address resolve_hostname(const String& p_hostname); + // async resolver hostname + ResolverID resolve_hostname_queue_item(const String& p_hostname); + ResolverStatus get_resolve_item_status(ResolverID p_id) const; + IP_Address get_resolve_item_address(ResolverID p_id) const; + void erase_resolve_item(ResolverID p_id); + + static IP* get_singleton(); + + static IP* create(); + + IP(); + ~IP(); + + +}; + +#endif // IP_H diff --git a/core/io/ip_address.cpp b/core/io/ip_address.cpp new file mode 100644 index 0000000000..a1400adbb6 --- /dev/null +++ b/core/io/ip_address.cpp @@ -0,0 +1,60 @@ +/*************************************************************************/ +/* ip_address.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 "ip_address.h" +/* +IP_Address::operator Variant() const { + + return operator String(); +}*/ +IP_Address::operator String() const { + + return itos(field[0])+"."+itos(field[1])+"."+itos(field[2])+"."+itos(field[3]); +} + +IP_Address::IP_Address(const String& p_string) { + + host=0; + int slices = p_string.get_slice_count("."); + if (slices!=4) { + ERR_EXPLAIN("Invalid IP Address String: "+p_string); + ERR_FAIL(); + } + for(int i=0;i<4;i++) { + + field[i]=p_string.get_slice(".",i).to_int(); + } +} + +IP_Address::IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d) { + + field[0]=p_a; + field[1]=p_b; + field[2]=p_c; + field[3]=p_d; +} diff --git a/core/io/ip_address.h b/core/io/ip_address.h new file mode 100644 index 0000000000..cd39aa6c81 --- /dev/null +++ b/core/io/ip_address.h @@ -0,0 +1,50 @@ +/*************************************************************************/ +/* ip_address.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. */ +/*************************************************************************/ +#ifndef IP_ADDRESS_H +#define IP_ADDRESS_H + +#include "ustring.h" + +struct IP_Address { + + union { + uint8_t field[4]; + uint32_t host; + }; + + //operator Variant() const; + operator String() const; + IP_Address(const String& p_string); + IP_Address(uint8_t p_a,uint8_t p_b,uint8_t p_c,uint8_t p_d); + IP_Address() { host=0; } +}; + + + +#endif // IP_ADDRESS_H diff --git a/core/io/json.cpp b/core/io/json.cpp new file mode 100644 index 0000000000..a83d7e4d6e --- /dev/null +++ b/core/io/json.cpp @@ -0,0 +1,477 @@ +/*************************************************************************/ +/* json.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 "json.h" +#include "print_string.h" + +const char * JSON::tk_name[TK_MAX] = { + "'{'", + "'}'", + "'['", + "']'", + "identifier", + "string", + "number", + "':'", + "','", + "EOF", +}; + + + +String JSON::_print_var(const Variant& p_var) { + + switch(p_var.get_type()) { + + case Variant::NIL: return "null"; + case Variant::BOOL: return p_var.operator bool() ? "true": "false"; + case Variant::INT: return itos(p_var); + case Variant::REAL: return rtos(p_var); + case Variant::INT_ARRAY: + case Variant::REAL_ARRAY: + case Variant::STRING_ARRAY: + case Variant::ARRAY: { + + String s = "["; + Array a = p_var; + for(int i=0;i<a.size();i++) { + if (i>0) + s+=", "; + s+=_print_var(a[i]); + } + s+="]"; + return s; + }; + case Variant::DICTIONARY: { + + String s = "{"; + Dictionary d = p_var; + List<Variant> keys; + d.get_key_list(&keys); + + for (List<Variant>::Element *E=keys.front();E;E=E->next()) { + + if (E!=keys.front()) + s+=", "; + s+=_print_var(String(E->get())); + s+=":"; + s+=_print_var(d[E->get()]); + } + + s+="}"; + return s; + }; + default: return "\""+String(p_var).c_escape()+"\""; + + } + +} + +String JSON::print(const Dictionary& p_dict) { + + return _print_var(p_dict); +} + + +Error JSON::_get_token(const CharType *p_str, int &idx, int p_len, Token& r_token,int &line,String &r_err_str) { + + while (true) { + switch(p_str[idx]) { + + case '\n': { + + line++; + idx++; + break; + }; + case 0: { + r_token.type=TK_EOF; + return OK; + } break; + case '{': { + + r_token.type=TK_CURLY_BRACKET_OPEN; + idx++; + return OK; + }; + case '}': { + + r_token.type=TK_CURLY_BRACKET_CLOSE; + idx++; + return OK; + }; + case '[': { + + r_token.type=TK_BRACKET_OPEN; + idx++; + return OK; + }; + case ']': { + + r_token.type=TK_BRACKET_CLOSE; + idx++; + return OK; + }; + case ':': { + + r_token.type=TK_COLON; + idx++; + return OK; + }; + case ',': { + + r_token.type=TK_COMMA; + idx++; + return OK; + }; + case '"': { + + idx++; + String str; + while(true) { + if (p_str[idx]==0) { + r_err_str="Unterminated String"; + return ERR_PARSE_ERROR; + } else if (p_str[idx]=='"') { + idx++; + break; + } else if (p_str[idx]=='\\') { + //escaped characters... + idx++; + CharType next = p_str[idx]; + if (next==0) { + r_err_str="Unterminated String"; + return ERR_PARSE_ERROR; + } + CharType res=0; + + switch(next) { + + case 'b': res=8; break; + case 't': res=9; break; + case 'n': res=10; break; + case 'f': res=12; break; + case 'r': res=13; break; + case '\"': res='\"'; break; + case '\\': res='\\'; break; + case '/': res='/'; break; //wtf + case 'u': { + //hexnumbarh - oct is deprecated + + + for(int j=0;j<4;j++) { + CharType c = p_str[idx+j+1]; + if (c==0) { + r_err_str="Unterminated String"; + return ERR_PARSE_ERROR; + } + if (!((c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'))) { + + r_err_str="Malformed hex constant in string"; + return ERR_PARSE_ERROR; + } + CharType v; + if (c>='0' && c<='9') { + v=c-'0'; + } else if (c>='a' && c<='f') { + v=c-'a'; + v+=10; + } else if (c>='A' && c<='F') { + v=c-'A'; + v+=10; + } else { + ERR_PRINT("BUG"); + v=0; + } + + res<<=4; + res|=v; + + + } + idx+=4; //will add at the end anyway + + + } break; + default: { + + r_err_str="Invalid escape sequence"; + return ERR_PARSE_ERROR; + } break; + } + + str+=res; + + } else { + if (p_str[idx]=='\n') + line++; + str+=p_str[idx]; + } + idx++; + } + + r_token.type=TK_STRING; + r_token.value=str; + return OK; + + } break; + default: { + + if (p_str[idx]<=32) { + idx++; + break; + } + + if (p_str[idx]=='-' || (p_str[idx]>='0' && p_str[idx]<='9')) { + //a number + const CharType *rptr; + double number = String::to_double(&p_str[idx],-1,&rptr); + idx+=(rptr - &p_str[idx]); + r_token.type=TK_NUMBER; + r_token.value=number; + return OK; + + } else if ((p_str[idx]>='A' && p_str[idx]<='Z') || (p_str[idx]>='a' && p_str[idx]<='z')) { + + String id; + + while((p_str[idx]>='A' && p_str[idx]<='Z') || (p_str[idx]>='a' && p_str[idx]<='z')) { + + id+=p_str[idx]; + idx++; + } + + r_token.type=TK_IDENTIFIER; + r_token.value=id; + return OK; + } else { + r_err_str="Unexpected character."; + return ERR_PARSE_ERROR; + } + } + + } + } + + return ERR_PARSE_ERROR; +} + + + +Error JSON::_parse_value(Variant &value,Token& token,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str) { + + + if (token.type==TK_CURLY_BRACKET_OPEN) { + + Dictionary d; + Error err = _parse_object(d,p_str,index,p_len,line,r_err_str); + if (err) + return err; + value=d; + return OK; + } else if (token.type==TK_BRACKET_OPEN) { + + Array a; + Error err = _parse_array(a,p_str,index,p_len,line,r_err_str); + if (err) + return err; + value=a; + return OK; + + } else if (token.type==TK_IDENTIFIER) { + + String id = token.value; + if (id=="true") + value=true; + else if (id=="false") + value=false; + else if (id=="null") + value=Variant(); + else { + r_err_str="Expected 'true','false' or 'null', got '"+id+"'."; + return ERR_PARSE_ERROR; + } + return OK; + + } else if (token.type==TK_NUMBER) { + + value=token.value; + return OK; + } else if (token.type==TK_STRING) { + + value=token.value; + return OK; + } else { + r_err_str="Expected value, got "+String(tk_name[token.type])+"."; + return ERR_PARSE_ERROR; + } + + return ERR_PARSE_ERROR; +} + + +Error JSON::_parse_array(Array &array,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str) { + + Token token; + bool need_comma=false; + + + while(index<p_len) { + + Error err = _get_token(p_str,index,p_len,token,line,r_err_str); + if (err!=OK) + return err; + + if (token.type==TK_BRACKET_CLOSE) { + + return OK; + } + + if (need_comma) { + + if (token.type!=TK_COMMA) { + + r_err_str="Expected ','"; + return ERR_PARSE_ERROR; + } else { + need_comma=false; + continue; + } + } + + Variant v; + err = _parse_value(v,token,p_str,index,p_len,line,r_err_str); + if (err) + return err; + + array.push_back(v); + need_comma=true; + + } + + return OK; + +} + +Error JSON::_parse_object(Dictionary &object,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str) { + + bool at_key=true; + String key; + Token token; + bool need_comma=false; + + + while(index<p_len) { + + + if (at_key) { + + Error err = _get_token(p_str,index,p_len,token,line,r_err_str); + if (err!=OK) + return err; + + if (token.type==TK_CURLY_BRACKET_CLOSE) { + + return OK; + } + + if (need_comma) { + + if (token.type!=TK_COMMA) { + + r_err_str="Expected '}' or ','"; + return ERR_PARSE_ERROR; + } else { + need_comma=false; + continue; + } + } + + if (token.type!=TK_STRING) { + + + r_err_str="Expected key"; + return ERR_PARSE_ERROR; + } + + key=token.value; + err = _get_token(p_str,index,p_len,token,line,r_err_str); + if (err!=OK) + return err; + if (token.type!=TK_COLON) { + + r_err_str="Expected ':'"; + return ERR_PARSE_ERROR; + } + at_key=false; + } else { + + + Error err = _get_token(p_str,index,p_len,token,line,r_err_str); + if (err!=OK) + return err; + + Variant v; + err = _parse_value(v,token,p_str,index,p_len,line,r_err_str); + if (err) + return err; + object[key]=v; + need_comma=true; + at_key=true; + } + } + + return OK; +} + + +Error JSON::parse(const String& p_json,Dictionary& r_ret,String &r_err_str,int &r_err_line) { + + + const CharType *str = p_json.ptr(); + int idx = 0; + int len = p_json.length(); + Token token; + int line=0; + String aux_key; + + Error err = _get_token(str,idx,len,token,line,r_err_str); + if (err) + return err; + + if (token.type!=TK_CURLY_BRACKET_OPEN) { + + r_err_str="Expected '{'"; + return ERR_PARSE_ERROR; + } + + return _parse_object(r_ret,str,idx,len,r_err_line,r_err_str); + +} + + diff --git a/core/io/json.h b/core/io/json.h new file mode 100644 index 0000000000..d113d0e4ef --- /dev/null +++ b/core/io/json.h @@ -0,0 +1,81 @@ +/*************************************************************************/ +/* json.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. */ +/*************************************************************************/ +#ifndef JSON_H +#define JSON_H + + + +#include "variant.h" + + +class JSON { + + enum TokenType { + TK_CURLY_BRACKET_OPEN, + TK_CURLY_BRACKET_CLOSE, + TK_BRACKET_OPEN, + TK_BRACKET_CLOSE, + TK_IDENTIFIER, + TK_STRING, + TK_NUMBER, + TK_COLON, + TK_COMMA, + TK_EOF, + TK_MAX + }; + + enum Expecting { + + EXPECT_OBJECT, + EXPECT_OBJECT_KEY, + EXPECT_COLON, + EXPECT_OBJECT_VALUE, + }; + + struct Token { + + TokenType type; + Variant value; + }; + + static const char * tk_name[TK_MAX]; + + static String _print_var(const Variant& p_var); + + static Error _get_token(const CharType *p_str,int &index, int p_len,Token& r_token,int &line,String &r_err_str); + static Error _parse_value(Variant &value,Token& token,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str); + static Error _parse_array(Array &array,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str); + static Error _parse_object(Dictionary &object,const CharType *p_str,int &index, int p_len,int &line,String &r_err_str); + +public: + static String print(const Dictionary& p_dict); + static Error parse(const String& p_json,Dictionary& r_ret,String &r_err_str,int &r_err_line); +}; + +#endif // JSON_H diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp new file mode 100644 index 0000000000..f5f9d34439 --- /dev/null +++ b/core/io/marshalls.cpp @@ -0,0 +1,1195 @@ +/*************************************************************************/ +/* marshalls.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 "marshalls.h" +#include "print_string.h" +#include "os/keyboard.h" +#include <stdio.h> + +Error decode_variant(Variant& r_variant,const uint8_t *p_buffer, int p_len,int *r_len) { + + const uint8_t * buf=p_buffer; + int len=p_len; + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + + + uint32_t type=decode_uint32(buf); + + ERR_FAIL_COND_V(type>=Variant::VARIANT_MAX,ERR_INVALID_DATA); + + buf+=4; + len-=4; + if (r_len) + *r_len=4; + + switch(type) { + + case Variant::NIL: { + + r_variant=Variant(); + } break; + case Variant::BOOL: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + bool val = decode_uint32(buf); + r_variant=val; + if (r_len) + (*r_len)+=4; + } break; + case Variant::INT: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + int val = decode_uint32(buf); + r_variant=val; + if (r_len) + (*r_len)+=4; + + } break; + case Variant::REAL: { + + ERR_FAIL_COND_V(len<(int)4,ERR_INVALID_DATA); + float val = decode_float(buf); + r_variant=val; + if (r_len) + (*r_len)+=4; + + } break; + case Variant::STRING: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t strlen = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)strlen>len,ERR_INVALID_DATA); + + String str; + str.parse_utf8((const char*)buf,strlen); + r_variant=str; + + if (r_len) { + if (strlen%4) + (*r_len)+=4-strlen%4; + (*r_len)+=4+strlen; + + } + + } break; + // math types + + case Variant::VECTOR2: { + + ERR_FAIL_COND_V(len<(int)4*2,ERR_INVALID_DATA); + Vector2 val; + val.x=decode_float(&buf[0]); + val.y=decode_float(&buf[4]); + r_variant=val; + + if (r_len) + (*r_len)+=4*2; + + } break; // 5 + case Variant::RECT2: { + + ERR_FAIL_COND_V(len<(int)4*4,ERR_INVALID_DATA); + Rect2 val; + val.pos.x=decode_float(&buf[0]); + val.pos.y=decode_float(&buf[4]); + val.size.x=decode_float(&buf[8]); + val.size.y=decode_float(&buf[12]); + r_variant=val; + + if (r_len) + (*r_len)+=4*4; + + } break; + case Variant::VECTOR3: { + + ERR_FAIL_COND_V(len<(int)4*3,ERR_INVALID_DATA); + Vector3 val; + val.x=decode_float(&buf[0]); + val.y=decode_float(&buf[4]); + val.z=decode_float(&buf[8]); + r_variant=val; + + if (r_len) + (*r_len)+=4*3; + + } break; + case Variant::PLANE: { + + ERR_FAIL_COND_V(len<(int)4*4,ERR_INVALID_DATA); + Plane val; + val.normal.x=decode_float(&buf[0]); + val.normal.y=decode_float(&buf[4]); + val.normal.z=decode_float(&buf[8]); + val.d=decode_float(&buf[12]); + r_variant=val; + + if (r_len) + (*r_len)+=4*4; + + } break; + case Variant::QUAT: { + + ERR_FAIL_COND_V(len<(int)4*4,ERR_INVALID_DATA); + Quat val; + val.x=decode_float(&buf[0]); + val.y=decode_float(&buf[4]); + val.z=decode_float(&buf[8]); + val.w=decode_float(&buf[12]); + r_variant=val; + + if (r_len) + (*r_len)+=4*4; + + } break; + case Variant::_AABB: { + + ERR_FAIL_COND_V(len<(int)4*6,ERR_INVALID_DATA); + AABB val; + val.pos.x=decode_float(&buf[0]); + val.pos.y=decode_float(&buf[4]); + val.pos.z=decode_float(&buf[8]); + val.size.x=decode_float(&buf[12]); + val.size.y=decode_float(&buf[16]); + val.size.z=decode_float(&buf[20]); + r_variant=val; + + if (r_len) + (*r_len)+=4*6; + + } break; + case Variant::MATRIX3: { + + ERR_FAIL_COND_V(len<(int)4*9,ERR_INVALID_DATA); + Matrix3 val; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + val.elements[i][j]=decode_float(&buf[(i*3+j)*4]); + } + } + + r_variant=val; + + if (r_len) + (*r_len)+=4*9; + + } break; + case Variant::TRANSFORM: { + + ERR_FAIL_COND_V(len<(int)4*12,ERR_INVALID_DATA); + Transform val; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + val.basis.elements[i][j]=decode_float(&buf[(i*3+j)*4]); + } + } + val.origin[0]=decode_float(&buf[36]); + val.origin[1]=decode_float(&buf[40]); + val.origin[2]=decode_float(&buf[44]); + + r_variant=val; + + if (r_len) + (*r_len)+=4*12; + + } break; + + // misc types + case Variant::COLOR: { + + ERR_FAIL_COND_V(len<(int)4*4,ERR_INVALID_DATA); + Color val; + val.r=decode_float(&buf[0]); + val.g=decode_float(&buf[4]); + val.b=decode_float(&buf[8]); + val.a=decode_float(&buf[12]); + r_variant=val; + + if (r_len) + (*r_len)+=4*4; + + } break; + case Variant::IMAGE: { + + ERR_FAIL_COND_V(len<(int)5*4,ERR_INVALID_DATA); + Image::Format fmt = (Image::Format)decode_uint32(&buf[0]); + ERR_FAIL_INDEX_V( fmt, Image::FORMAT_MAX, ERR_INVALID_DATA); + uint32_t mipmaps = decode_uint32(&buf[4]); + uint32_t w = decode_uint32(&buf[8]); + uint32_t h = decode_uint32(&buf[12]); + uint32_t datalen = decode_uint32(&buf[16]); + + Image img; + if (datalen>0) { + len-=5*4; + ERR_FAIL_COND_V( len < datalen, ERR_INVALID_DATA ); + DVector<uint8_t> data; + data.resize(datalen); + DVector<uint8_t>::Write wr = data.write(); + copymem(&wr[0],&buf[20],datalen); + wr = DVector<uint8_t>::Write(); + + + + img=Image(w,h,mipmaps,fmt,data); + } + + r_variant=img; + if (r_len) + (*r_len)+=4*5+datalen; + + } break; + case Variant::NODE_PATH: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t strlen = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)strlen>len,ERR_INVALID_DATA); + + + String str; + str.parse_utf8((const char*)buf,strlen); + + r_variant=NodePath(str); + + if (r_len) + (*r_len)+=4+strlen; + + } break; + /*case Variant::RESOURCE: { + + ERR_EXPLAIN("Can't marshallize resources"); + ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go + } break;*/ + case Variant::_RID: { + + r_variant = RID(); + } break; + case Variant::OBJECT: { + + + r_variant = (Object*)NULL; + } break; + case Variant::INPUT_EVENT: { + + InputEvent ie; + + ie.type=decode_uint32(&buf[0]); + ie.device=decode_uint32(&buf[4]); + uint32_t len = decode_uint32(&buf[8])-12; + + if (r_len) + (*r_len)+=12; + + switch(ie.type) { + + case InputEvent::KEY: { + + uint32_t mods=decode_uint32(&buf[12]); + if (mods&KEY_MASK_SHIFT) + ie.key.mod.shift=true; + if (mods&KEY_MASK_CTRL) + ie.key.mod.control=true; + if (mods&KEY_MASK_ALT) + ie.key.mod.alt=true; + if (mods&KEY_MASK_META) + ie.key.mod.meta=true; + ie.key.scancode=decode_uint32(&buf[16]); + + if (r_len) + (*r_len)+=8; + + + } break; + case InputEvent::MOUSE_BUTTON: { + + ie.mouse_button.button_index=decode_uint32(&buf[12]); + if (r_len) + (*r_len)+=4; + + } break; + case InputEvent::JOYSTICK_BUTTON: { + + ie.joy_button.button_index=decode_uint32(&buf[12]); + if (r_len) + (*r_len)+=4; + } break; + case InputEvent::SCREEN_TOUCH: { + + ie.screen_touch.index=decode_uint32(&buf[12]); + if (r_len) + (*r_len)+=4; + } break; + case InputEvent::JOYSTICK_MOTION: { + + ie.joy_motion.axis=decode_uint32(&buf[12]); + if (r_len) + (*r_len)+=4; + } break; + } + + r_variant = ie; + + } break; + case Variant::DICTIONARY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + bool shared = count&0x80000000; + count&=0x7FFFFFFF; + + buf+=4; + len-=4; + + if (r_len) { + (*r_len)+=4; + } + + Dictionary d(shared); + + for(uint32_t i=0;i<count;i++) { + + Variant key,value; + + int used; + Error err = decode_variant(key,buf,len,&used); + ERR_FAIL_COND_V(err,err); + + buf+=used; + len-=used; + if (r_len) { + (*r_len)+=used; + } + + err = decode_variant(value,buf,len,&used); + ERR_FAIL_COND_V(err,err); + + buf+=used; + len-=used; + if (r_len) { + (*r_len)+=used; + } + + d[key]=value; + } + + r_variant=d; + + } break; + case Variant::ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + bool shared = count&0x80000000; + count&=0x7FFFFFFF; + + buf+=4; + len-=4; + + if (r_len) { + (*r_len)+=4; + } + + Array varr(shared); + + for(uint32_t i=0;i<count;i++) { + + int used=0; + Variant v; + Error err = decode_variant(v,buf,len,&used); + ERR_FAIL_COND_V(err,err); + buf+=used; + len-=used; + varr.push_back(v); + if (r_len) { + (*r_len)+=used; + } + } + + r_variant=varr; + + + } break; + + // arrays + case Variant::RAW_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)count>len,ERR_INVALID_DATA); + + + DVector<uint8_t> data; + + if (count) { + data.resize(count); + DVector<uint8_t>::Write w = data.write(); + for(int i=0;i<count;i++) { + + w[i]=buf[i]; + } + + w = DVector<uint8_t>::Write(); + } + + r_variant=data; + + if (r_len) { + if (count%4) + (*r_len)+=4-count%4; + (*r_len)+=4+count; + } + + + + } break; + case Variant::INT_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)count*4>len,ERR_INVALID_DATA); + + DVector<int> data; + + if (count) { + //const int*rbuf=(const int*)buf; + data.resize(count); + DVector<int>::Write w = data.write(); + for(int i=0;i<count;i++) { + + w[i]=decode_uint32(&buf[i*4]); + } + + w = DVector<int>::Write(); + } + r_variant=Variant(data); + if (r_len) { + (*r_len)+=4+count*sizeof(int); + } + + } break; + case Variant::REAL_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)count*4>len,ERR_INVALID_DATA); + + DVector<float> data; + + if (count) { + //const float*rbuf=(const float*)buf; + data.resize(count); + DVector<float>::Write w = data.write(); + for(int i=0;i<count;i++) { + + w[i]=decode_float(&buf[i*4]); + } + + w = DVector<float>::Write(); + } + r_variant=data; + + if (r_len) { + (*r_len)+=4+count*sizeof(float); + } + + + } break; + case Variant::STRING_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + ERR_FAIL_COND_V(count<0,ERR_INVALID_DATA); + + DVector<String> strings; + buf+=4; + len-=4; + + if (r_len) + (*r_len)+=4; + //printf("string count: %i\n",count); + + for(int i=0;i<(int)count;i++) { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t strlen = decode_uint32(buf); + + buf+=4; + len-=4; + ERR_FAIL_COND_V((int)strlen>len,ERR_INVALID_DATA); + + //printf("loaded string: %s\n",(const char*)buf); + String str; + str.parse_utf8((const char*)buf,strlen); + + strings.push_back(str); + + buf+=strlen; + len-=strlen; + + if (r_len) + (*r_len)+=4+strlen; + + if (strlen%4) { + int pad = 4-(strlen%4); + buf+=pad; + len-=pad; + if (r_len) { + (*r_len)+=pad; + } + } + + } + + r_variant=strings; + + + } break; + case Variant::VECTOR3_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + ERR_FAIL_COND_V(count<0,ERR_INVALID_DATA); + buf+=4; + len-=4; + + ERR_FAIL_COND_V((int)count*4*3>len,ERR_INVALID_DATA); + DVector<Vector3> varray; + + if (r_len) { + (*r_len)+=4; + } + + if (count) { + varray.resize(count); + DVector<Vector3>::Write w = varray.write(); + const float *r = (const float*)buf; + + for(int i=0;i<(int)count;i++) { + + w[i].x=decode_float(buf+i*4*3+4*0); + w[i].y=decode_float(buf+i*4*3+4*1); + w[i].z=decode_float(buf+i*4*3+4*2); + + } + + int adv = 4*3*count; + + if (r_len) + (*r_len)+=adv; + len-=adv; + buf+=adv; + + } + + r_variant=varray; + + } break; + case Variant::COLOR_ARRAY: { + + ERR_FAIL_COND_V(len<4,ERR_INVALID_DATA); + uint32_t count = decode_uint32(buf); + ERR_FAIL_COND_V(count<0,ERR_INVALID_DATA); + buf+=4; + len-=4; + + ERR_FAIL_COND_V((int)count*4*4>len,ERR_INVALID_DATA); + DVector<Color> carray; + + if (r_len) { + (*r_len)+=4; + } + + if (count) { + carray.resize(count); + DVector<Color>::Write w = carray.write(); + const float *r = (const float*)buf; + + for(int i=0;i<(int)count;i++) { + + w[i].r=decode_float(buf+i*4*4+4*0); + w[i].g=decode_float(buf+i*4*4+4*1); + w[i].b=decode_float(buf+i*4*4+4*2); + w[i].a=decode_float(buf+i*4*4+4*3); + + } + + int adv = 4*4*count; + + if (r_len) + (*r_len)+=adv; + len-=adv; + buf+=adv; + + } + + r_variant=carray; + + } break; + default: { ERR_FAIL_V(ERR_BUG); } + } + + return OK; +} + +Error encode_variant(const Variant& p_variant, uint8_t *r_buffer, int &r_len) { + + uint8_t * buf=r_buffer; + + r_len=0; + + if (buf) { + encode_uint32(p_variant.get_type(),buf); + buf+=4; + } + r_len+=4; + + switch(p_variant.get_type()) { + + case Variant::NIL: { + + //nothing to do + } break; + case Variant::BOOL: { + + if (buf) { + encode_uint32(p_variant.operator bool(),buf); + } + + r_len+=4; + + } break; + case Variant::INT: { + + if (buf) { + encode_uint32(p_variant.operator int(),buf); + } + + r_len+=4; + + } break; + case Variant::REAL: { + + if (buf) { + encode_float(p_variant.operator float(),buf); + } + + r_len+=4; + + } break; + case Variant::NODE_PATH: + case Variant::STRING: { + + + CharString utf8 = p_variant.operator String().utf8(); + + if (buf) { + encode_uint32(utf8.length(),buf); + buf+=4; + copymem(buf,utf8.get_data(),utf8.length()); + } + + r_len+=4+utf8.length(); + while (r_len%4) + r_len++; //pad + + } break; + // math types + + case Variant::VECTOR2: { + + if (buf) { + Vector2 v2=p_variant; + encode_float(v2.x,&buf[0]); + encode_float(v2.y,&buf[4]); + + } + + r_len+=2*4; + + } break; // 5 + case Variant::RECT2: { + + if (buf) { + Rect2 r2=p_variant; + encode_float(r2.pos.x,&buf[0]); + encode_float(r2.pos.y,&buf[4]); + encode_float(r2.size.x,&buf[8]); + encode_float(r2.size.y,&buf[12]); + } + r_len+=4*4; + + } break; + case Variant::VECTOR3: { + + if (buf) { + Vector3 v3=p_variant; + encode_float(v3.x,&buf[0]); + encode_float(v3.y,&buf[4]); + encode_float(v3.z,&buf[8]); + } + + r_len+=3*4; + + } break; + case Variant::PLANE: { + + if (buf) { + Plane p=p_variant; + encode_float(p.normal.x,&buf[0]); + encode_float(p.normal.y,&buf[4]); + encode_float(p.normal.z,&buf[8]); + encode_float(p.d,&buf[12]); + } + + r_len+=4*4; + + } break; + case Variant::QUAT: { + + if (buf) { + Quat q=p_variant; + encode_float(q.x,&buf[0]); + encode_float(q.y,&buf[4]); + encode_float(q.z,&buf[8]); + encode_float(q.w,&buf[12]); + } + + r_len+=4*4; + + } break; + case Variant::_AABB: { + + if (buf) { + AABB aabb=p_variant; + encode_float(aabb.pos.x,&buf[0]); + encode_float(aabb.pos.y,&buf[4]); + encode_float(aabb.pos.z,&buf[8]); + encode_float(aabb.size.x,&buf[12]); + encode_float(aabb.size.y,&buf[16]); + encode_float(aabb.size.z,&buf[20]); + } + + r_len+=6*4; + + + } break; + case Variant::MATRIX3: { + + if (buf) { + Matrix3 val=p_variant; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + copymem(&buf[(i*3+j)*4],&val.elements[i][j],sizeof(float)); + } + } + } + + + r_len+=9*4; + + } break; + case Variant::TRANSFORM: { + + if (buf) { + Transform val=p_variant; + for(int i=0;i<3;i++) { + for(int j=0;j<3;j++) { + + copymem(&buf[(i*3+j)*4],&val.basis.elements[i][j],sizeof(float)); + } + } + + encode_float(val.origin.x,&buf[36]); + encode_float(val.origin.y,&buf[40]); + encode_float(val.origin.z,&buf[44]); + + + } + + r_len+=12*4; + + } break; + + // misc types + case Variant::COLOR: { + + if (buf) { + Color c=p_variant; + encode_float(c.r,&buf[0]); + encode_float(c.g,&buf[4]); + encode_float(c.b,&buf[8]); + encode_float(c.a,&buf[12]); + } + + r_len+=4*4; + + } break; + case Variant::IMAGE: { + + Image image = p_variant; + DVector<uint8_t> data=image.get_data(); + + if (buf) { + + encode_uint32(image.get_format(),&buf[0]); + encode_uint32(image.get_mipmaps(),&buf[4]); + encode_uint32(image.get_width(),&buf[8]); + encode_uint32(image.get_height(),&buf[12]); + int ds=data.size(); + encode_uint32(ds,&buf[16]); + DVector<uint8_t>::Read r = data.read(); + copymem(&buf[20],&r[0],ds); + } + + r_len+=data.size()+5*4; + + } break; + /*case Variant::RESOURCE: { + + ERR_EXPLAIN("Can't marshallize resources"); + ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go + } break;*/ + case Variant::_RID: + case Variant::OBJECT: { + + + } break; + case Variant::INPUT_EVENT: { + + + InputEvent ie=p_variant; + + if (buf) { + + encode_uint32(ie.type,&buf[0]); + encode_uint32(ie.device,&buf[4]); + encode_uint32(0,&buf[8]); + } + + int llen=12; + + switch(ie.type) { + + case InputEvent::KEY: { + + if (buf) { + + uint32_t mods=0; + if (ie.key.mod.shift) + mods|=KEY_MASK_SHIFT; + if (ie.key.mod.control) + mods|=KEY_MASK_CTRL; + if (ie.key.mod.alt) + mods|=KEY_MASK_ALT; + if (ie.key.mod.meta) + mods|=KEY_MASK_META; + + encode_uint32(mods,&buf[llen]); + encode_uint32(ie.key.scancode,&buf[llen+4]); + } + llen+=8; + + } break; + case InputEvent::MOUSE_BUTTON: { + + if (buf) { + + encode_uint32(ie.mouse_button.button_index,&buf[llen]); + } + llen+=4; + } break; + case InputEvent::JOYSTICK_BUTTON: { + + if (buf) { + + encode_uint32(ie.joy_button.button_index,&buf[llen]); + } + llen+=4; + } break; + case InputEvent::SCREEN_TOUCH: { + + if (buf) { + + encode_uint32(ie.screen_touch.index,&buf[llen]); + } + llen+=4; + } break; + case InputEvent::JOYSTICK_MOTION: { + + if (buf) { + + int axis = ie.joy_motion.axis; + encode_uint32(axis,&buf[llen]); + } + llen+=4; + } break; + } + + if (buf) + encode_uint32(llen,&buf[8]); + r_len+=llen; + + + // not supported + } break; + case Variant::DICTIONARY: { + + Dictionary d = p_variant; + + if (buf) { + encode_uint32(uint32_t(d.size())|(d.is_shared()?0x80000000:0),buf); + buf+=4; + } + r_len+=4; + + List<Variant> keys; + d.get_key_list(&keys); + + + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + /* + CharString utf8 = E->->utf8(); + + if (buf) { + encode_uint32(utf8.length()+1,buf); + buf+=4; + copymem(buf,utf8.get_data(),utf8.length()+1); + } + + r_len+=4+utf8.length()+1; + while (r_len%4) + r_len++; //pad + */ + int len; + encode_variant(E->get(),buf,len); + ERR_FAIL_COND_V(len%4,ERR_BUG); + r_len+=len; + if (buf) + buf += len; + encode_variant(d[E->get()],buf,len); + ERR_FAIL_COND_V(len%4,ERR_BUG); + r_len+=len; + if (buf) + buf += len; + } + + } break; + case Variant::ARRAY: { + + Array v = p_variant; + + if (buf) { + encode_uint32(uint32_t(v.size())|(v.is_shared()?0x80000000:0),buf); + buf+=4; + } + + r_len+=4; + + for(int i=0;i<v.size();i++) { + + int len; + encode_variant(v.get(i),buf,len); + ERR_FAIL_COND_V(len%4,ERR_BUG); + r_len+=len; + if (buf) + buf+=len; + } + + + } break; + // arrays + case Variant::RAW_ARRAY: { + + DVector<uint8_t> data = p_variant; + int datalen=data.size(); + int datasize=sizeof(uint8_t); + + if (buf) { + encode_uint32(datalen,buf); + buf+=4; + DVector<uint8_t>::Read r = data.read(); + copymem(buf,&r[0],datalen*datasize); + + } + + r_len+=4+datalen*datasize; + while(r_len%4) + r_len++; + + } break; + case Variant::INT_ARRAY: { + + DVector<int> data = p_variant; + int datalen=data.size(); + int datasize=sizeof(int32_t); + + if (buf) { + encode_uint32(datalen,buf); + buf+=4; + DVector<int>::Read r = data.read(); + for(int i=0;i<datalen;i++) + encode_uint32(r[i],&buf[i*datasize]); + + } + + r_len+=4+datalen*datasize; + + } break; + case Variant::REAL_ARRAY: { + + DVector<real_t> data = p_variant; + int datalen=data.size(); + int datasize=sizeof(real_t); + + if (buf) { + encode_uint32(datalen,buf); + buf+=4; + DVector<real_t>::Read r = data.read(); + for(int i=0;i<datalen;i++) + encode_float(r[i],&buf[i*datasize]); + + } + + r_len+=4+datalen*datasize; + + } break; + case Variant::STRING_ARRAY: { + + + DVector<String> data = p_variant; + int len=data.size(); + + if (buf) { + encode_uint32(len,buf); + buf+=4; + } + + r_len+=4; + + for(int i=0;i<len;i++) { + + + CharString utf8 = data.get(i).utf8(); + + if (buf) { + encode_uint32(utf8.length()+1,buf); + buf+=4; + copymem(buf,utf8.get_data(),utf8.length()+1); + buf+=utf8.length()+1; + } + + r_len+=4+utf8.length()+1; + while (r_len%4) { + r_len++; //pad + if (buf) + buf++; + } + } + + } break; + case Variant::VECTOR3_ARRAY: { + + DVector<Vector3> data = p_variant; + int len=data.size(); + + if (buf) { + encode_uint32(len,buf); + buf+=4; + } + + r_len+=4; + + if (buf) { + + for(int i=0;i<len;i++) { + + Vector3 v = data.get(i); + + encode_float(v.x,&buf[0]); + encode_float(v.y,&buf[4]); + encode_float(v.z,&buf[8]); + buf+=4*3; + + } + } + + r_len+=4*3*len; + + } break; + case Variant::COLOR_ARRAY: { + + DVector<Color> data = p_variant; + int len=data.size(); + + if (buf) { + encode_uint32(len,buf); + buf+=4; + } + + r_len+=4; + + if (buf) { + + for(int i=0;i<len;i++) { + + Color c = data.get(i); + + + encode_float(c.r,&buf[0]); + encode_float(c.g,&buf[4]); + encode_float(c.b,&buf[8]); + encode_float(c.a,&buf[12]); + buf+=4*4; + } + } + + r_len+=4*4*len; + + } break; + default: { ERR_FAIL_V(ERR_BUG); } + } + + return OK; + +} + + diff --git a/core/io/marshalls.h b/core/io/marshalls.h new file mode 100644 index 0000000000..bb8d3b336a --- /dev/null +++ b/core/io/marshalls.h @@ -0,0 +1,190 @@ +/*************************************************************************/ +/* marshalls.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. */ +/*************************************************************************/ +#ifndef MARSHALLS_H +#define MARSHALLS_H + +#include "typedefs.h" + +#include "variant.h" + +/** + * Miscelaneous helpers for marshalling data types, and encoding + * in an endian independent way + */ + + +union MarshallFloat { + + uint32_t i; ///< int + float f; ///< float +}; + +union MarshallDouble { + + uint64_t l; ///< long long + double d; ///< double +}; + +static inline unsigned int encode_uint16(uint16_t p_uint, uint8_t *p_arr) { + + for (int i=0;i<2;i++) { + + *p_arr=p_uint&0xFF; + p_arr++; p_uint>>=8; + } + + return sizeof( uint16_t ); +} + +static inline unsigned int encode_uint32(uint32_t p_uint, uint8_t *p_arr) { + + for (int i=0;i<4;i++) { + + *p_arr=p_uint&0xFF; + p_arr++; p_uint>>=8; + } + + return sizeof( uint32_t ); +} + +static inline unsigned int encode_float(float p_float, uint8_t *p_arr) { + + MarshallFloat mf; + mf.f=p_float; + encode_uint32( mf.i, p_arr ); + + return sizeof( uint32_t ); +} + +static inline unsigned int encode_uint64(uint64_t p_uint, uint8_t *p_arr) { + + for (int i=0;i<8;i++) { + + *p_arr=p_uint&0xFF; + p_arr++; p_uint>>=8; + } + + return sizeof(uint64_t); +} + +static inline unsigned int encode_double(double p_double, uint8_t *p_arr) { + + MarshallDouble md; + md.d=p_double; + encode_uint64( md.l, p_arr ); + + return sizeof(uint64_t); + +} + + +static inline int encode_cstring(const char *p_string, uint8_t * p_data) { + + int len=0; + + while (*p_string) { + + if (p_data) { + + *p_data=(uint8_t)*p_string; + p_data++; + } + p_string++; + len++; + }; + + if (p_data) *p_data = 0; + return len+1; +} + +static inline uint16_t decode_uint16(const uint8_t *p_arr) { + + uint16_t u=0; + + for (int i=0;i<2;i++) { + + uint16_t b = *p_arr; + b<<=(i*8); + u|=b; + p_arr++; + } + + return u; +} + +static inline uint32_t decode_uint32(const uint8_t *p_arr) { + + uint32_t u=0; + + for (int i=0;i<4;i++) { + + uint32_t b = *p_arr; + b<<=(i*8); + u|=b; + p_arr++; + } + + return u; +} + +static inline float decode_float(const uint8_t *p_arr) { + + MarshallFloat mf; + mf.i = decode_uint32(p_arr); + return mf.f; +} + +static inline uint64_t decode_uint64(const uint8_t *p_arr) { + + uint64_t u=0; + + for (int i=0;i<8;i++) { + + uint64_t b = (*p_arr)&0xFF; + b<<=(i*8); + u|=b; + p_arr++; + } + + return u; +} + +static inline double decode_double(const uint8_t *p_arr) { + + MarshallDouble md; + md.l = decode_uint64( p_arr ); + return md.d; + +} + + +Error decode_variant(Variant& r_variant,const uint8_t *p_buffer, int p_len,int *r_len=NULL); +Error encode_variant(const Variant& p_variant, uint8_t *r_buffer, int &r_len); + +#endif diff --git a/core/io/md5.cpp b/core/io/md5.cpp new file mode 100644 index 0000000000..5a88328dd4 --- /dev/null +++ b/core/io/md5.cpp @@ -0,0 +1,269 @@ +#include "md5.h" + +/* + ********************************************************************** + ** md5.c ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* -- include the following line if the md5.h header file is separate -- */ +/* #include "md5.h" */ + +/* forward declaration */ +static void Transform (uint32_t *buf, uint32_t *in); + + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G and H are basic MD5 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +void MD5Init (MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (uint32_t)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (uint32_t)0x67452301; + mdContext->buf[1] = (uint32_t)0xefcdab89; + mdContext->buf[2] = (uint32_t)0x98badcfe; + mdContext->buf[3] = (uint32_t)0x10325476; +} + +void MD5Update (MD5_CTX *mdContext,unsigned char *inBuf,unsigned int inLen) { + uint32_t in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((uint32_t)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((uint32_t)inLen << 3); + mdContext->i[1] += ((uint32_t)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((uint32_t)mdContext->in[ii+3]) << 24) | + (((uint32_t)mdContext->in[ii+2]) << 16) | + (((uint32_t)mdContext->in[ii+1]) << 8) | + ((uint32_t)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +void MD5Final (MD5_CTX *mdContext) { + uint32_t in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((uint32_t)mdContext->in[ii+3]) << 24) | + (((uint32_t)mdContext->in[ii+2]) << 16) | + (((uint32_t)mdContext->in[ii+1]) << 8) | + ((uint32_t)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transform buf based on in. + */ +static void Transform (uint32_t *buf, uint32_t *in) { + uint32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + ********************************************************************** + ** End of md5.c ** + ******************************* (cut) ******************************** + */ diff --git a/core/io/md5.h b/core/io/md5.h new file mode 100644 index 0000000000..e99d58b443 --- /dev/null +++ b/core/io/md5.h @@ -0,0 +1,61 @@ +#ifndef MD5_H +#define MD5_H + +/* + ********************************************************************** + ** md5.h -- Header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* NOT typedef a 32 bit type */ + +#include "typedefs.h" + +/* Data structure for MD5 (Message Digest) computation */ +typedef struct { + uint32_t i[2]; /* number of _bits_ handled mod 2^64 */ + uint32_t buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (MD5_CTX *mdContext); +void MD5Update (MD5_CTX *mdContext,unsigned char *inBuf,unsigned int inLen); +void MD5Final (MD5_CTX *mdContext); + + + +#endif // MD5_H diff --git a/core/io/object_format_binary.cpp b/core/io/object_format_binary.cpp new file mode 100644 index 0000000000..c031f6e82b --- /dev/null +++ b/core/io/object_format_binary.cpp @@ -0,0 +1,1491 @@ +/*************************************************************************/ +/* object_format_binary.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 "object_format_binary.h" +#include "resource.h" +#include "io/resource_loader.h" +#include "print_string.h" +#include "object_type_db.h" +#include "globals.h" +#include "os/os.h" +#include "version.h" + + +#define print_bl(m_what) +#ifdef OLD_SCENE_FORMAT_ENABLED + + +enum { + + SECTION_RESOURCE=0, + SECTION_OBJECT=1, + SECTION_META_OBJECT=2, + SECTION_PROPERTY=3, + SECTION_END=4, + + //numbering must be different from variant, in case new variant types are added (variant must be always contiguous for jumptable optimization) + VARIANT_NIL=1, + VARIANT_BOOL=2, + VARIANT_INT=3, + VARIANT_REAL=4, + VARIANT_STRING=5, + VARIANT_VECTOR2=10, + VARIANT_RECT2=11, + VARIANT_VECTOR3=12, + VARIANT_PLANE=13, + VARIANT_QUAT=14, + VARIANT_AABB=15, + VARIANT_MATRIX3=16, + VARIANT_TRANSFORM=17, + VARIANT_MATRIX32=18, + VARIANT_COLOR=20, + VARIANT_IMAGE=21, + VARIANT_NODE_PATH=22, + VARIANT_RID=23, + VARIANT_OBJECT=24, + VARIANT_INPUT_EVENT=25, + VARIANT_DICTIONARY=26, + VARIANT_ARRAY=30, + VARIANT_RAW_ARRAY=31, + VARIANT_INT_ARRAY=32, + VARIANT_REAL_ARRAY=33, + VARIANT_STRING_ARRAY=34, + VARIANT_VECTOR3_ARRAY=35, + VARIANT_COLOR_ARRAY=36, + VARIANT_VECTOR2_ARRAY=37, + + IMAGE_ENCODING_EMPTY=0, + IMAGE_ENCODING_RAW=1, + IMAGE_ENCODING_PNG=2, //not yet + IMAGE_ENCODING_JPG=3, + + IMAGE_FORMAT_GRAYSCALE=0, + IMAGE_FORMAT_INTENSITY=1, + IMAGE_FORMAT_GRAYSCALE_ALPHA=2, + IMAGE_FORMAT_RGB=3, + IMAGE_FORMAT_RGBA=4, + IMAGE_FORMAT_INDEXED=5, + IMAGE_FORMAT_INDEXED_ALPHA=6, + IMAGE_FORMAT_BC1=7, + IMAGE_FORMAT_BC2=8, + IMAGE_FORMAT_BC3=9, + IMAGE_FORMAT_BC4=10, + IMAGE_FORMAT_BC5=11, + IMAGE_FORMAT_CUSTOM=12, + + OBJECT_EMPTY=0, + OBJECT_EXTERNAL_RESOURCE=1, + OBJECT_INTERNAL_RESOURCE=2, + + +}; + + +void ObjectFormatSaverBinary::_pad_buffer(int p_bytes) { + + int extra = 4-(p_bytes%4); + if (extra<4) { + for(int i=0;i<extra;i++) + f->store_8(0); //pad to 32 + } + +} + + +void ObjectFormatSaverBinary::write_property(int p_idx,const Variant& p_property) { + + f->store_32(SECTION_PROPERTY); + f->store_32(p_idx); + + switch(p_property.get_type()) { + + case Variant::NIL: { + + f->store_32(VARIANT_NIL); + // don't store anything + } break; + case Variant::BOOL: { + + f->store_32(VARIANT_BOOL); + bool val=p_property; + f->store_32(val); + } break; + case Variant::INT: { + + f->store_32(VARIANT_INT); + int val=p_property; + f->store_32(val); + } break; + case Variant::REAL: { + + f->store_32(VARIANT_REAL); + real_t val=p_property; + f->store_real(val); + + } break; + case Variant::STRING: { + + f->store_32(VARIANT_STRING); + String val=p_property; + save_unicode_string(val); + + } break; + case Variant::VECTOR2: { + + f->store_32(VARIANT_VECTOR2); + Vector2 val=p_property; + f->store_real(val.x); + f->store_real(val.y); + + } break; + case Variant::RECT2: { + + f->store_32(VARIANT_RECT2); + Rect2 val=p_property; + f->store_real(val.pos.x); + f->store_real(val.pos.y); + f->store_real(val.size.x); + f->store_real(val.size.y); + + } break; + case Variant::VECTOR3: { + + f->store_32(VARIANT_VECTOR3); + Vector3 val=p_property; + f->store_real(val.x); + f->store_real(val.y); + f->store_real(val.z); + + } break; + case Variant::PLANE: { + + f->store_32(VARIANT_PLANE); + Plane val=p_property; + f->store_real(val.normal.x); + f->store_real(val.normal.y); + f->store_real(val.normal.z); + f->store_real(val.d); + + } break; + case Variant::QUAT: { + + f->store_32(VARIANT_QUAT); + Quat val=p_property; + f->store_real(val.x); + f->store_real(val.y); + f->store_real(val.z); + f->store_real(val.w); + + } break; + case Variant::_AABB: { + + f->store_32(VARIANT_AABB); + AABB val=p_property; + f->store_real(val.pos.x); + f->store_real(val.pos.y); + f->store_real(val.pos.z); + f->store_real(val.size.x); + f->store_real(val.size.y); + f->store_real(val.size.z); + + } break; + case Variant::MATRIX32: { + + f->store_32(VARIANT_MATRIX32); + Matrix32 val=p_property; + f->store_real(val.elements[0].x); + f->store_real(val.elements[0].y); + f->store_real(val.elements[1].x); + f->store_real(val.elements[1].y); + f->store_real(val.elements[2].x); + f->store_real(val.elements[2].y); + + } break; + case Variant::MATRIX3: { + + f->store_32(VARIANT_MATRIX3); + Matrix3 val=p_property; + f->store_real(val.elements[0].x); + f->store_real(val.elements[0].y); + f->store_real(val.elements[0].z); + f->store_real(val.elements[1].x); + f->store_real(val.elements[1].y); + f->store_real(val.elements[1].z); + f->store_real(val.elements[2].x); + f->store_real(val.elements[2].y); + f->store_real(val.elements[2].z); + + } break; + case Variant::TRANSFORM: { + + f->store_32(VARIANT_TRANSFORM); + Transform val=p_property; + f->store_real(val.basis.elements[0].x); + f->store_real(val.basis.elements[0].y); + f->store_real(val.basis.elements[0].z); + f->store_real(val.basis.elements[1].x); + f->store_real(val.basis.elements[1].y); + f->store_real(val.basis.elements[1].z); + f->store_real(val.basis.elements[2].x); + f->store_real(val.basis.elements[2].y); + f->store_real(val.basis.elements[2].z); + f->store_real(val.origin.x); + f->store_real(val.origin.y); + f->store_real(val.origin.z); + + } break; + case Variant::COLOR: { + + f->store_32(VARIANT_COLOR); + Color val=p_property; + f->store_real(val.r); + f->store_real(val.g); + f->store_real(val.b); + f->store_real(val.a); + + } break; + case Variant::IMAGE: { + + f->store_32(VARIANT_IMAGE); + Image val =p_property; + if (val.empty()) { + f->store_32(IMAGE_ENCODING_EMPTY); + break; + } + f->store_32(IMAGE_ENCODING_RAW); //raw encoding + f->store_32(val.get_width()); + f->store_32(val.get_height()); + f->store_32(val.get_mipmaps()); + switch(val.get_format()) { + + case Image::FORMAT_GRAYSCALE: f->store_32(IMAGE_FORMAT_GRAYSCALE ); break; ///< one byte per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255 + case Image::FORMAT_INTENSITY: f->store_32(IMAGE_FORMAT_INTENSITY ); break; ///< one byte per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255 + case Image::FORMAT_GRAYSCALE_ALPHA: f->store_32(IMAGE_FORMAT_GRAYSCALE_ALPHA ); break; ///< two bytes per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255. alpha 0-255 + case Image::FORMAT_RGB: f->store_32(IMAGE_FORMAT_RGB ); break; ///< one byte R: f->store_32(IMAGE_FORMAT_ ); break; one byte G: f->store_32(IMAGE_FORMAT_ ); break; one byte B + case Image::FORMAT_RGBA: f->store_32(IMAGE_FORMAT_RGBA ); break; ///< one byte R: f->store_32(IMAGE_FORMAT_ ); break; one byte G: f->store_32(IMAGE_FORMAT_ ); break; one byte B: f->store_32(IMAGE_FORMAT_ ); break; one byte A + case Image::FORMAT_INDEXED: f->store_32(IMAGE_FORMAT_INDEXED ); break; ///< index byte 0-256: f->store_32(IMAGE_FORMAT_ ); break; and after image end: f->store_32(IMAGE_FORMAT_ ); break; 256*3 bytes of palette + case Image::FORMAT_INDEXED_ALPHA: f->store_32(IMAGE_FORMAT_INDEXED_ALPHA ); break; ///< index byte 0-256: f->store_32(IMAGE_FORMAT_ ); break; and after image end: f->store_32(IMAGE_FORMAT_ ); break; 256*4 bytes of palette (alpha) + case Image::FORMAT_BC1: f->store_32(IMAGE_FORMAT_BC1 ); break; // DXT1 + case Image::FORMAT_BC2: f->store_32(IMAGE_FORMAT_BC2 ); break; // DXT3 + case Image::FORMAT_BC3: f->store_32(IMAGE_FORMAT_BC3 ); break; // DXT5 + case Image::FORMAT_BC4: f->store_32(IMAGE_FORMAT_BC4 ); break; // ATI1 + case Image::FORMAT_BC5: f->store_32(IMAGE_FORMAT_BC5 ); break; // ATI2 + case Image::FORMAT_CUSTOM: f->store_32(IMAGE_FORMAT_CUSTOM ); break; + default: {} + + } + + int dlen = val.get_data().size(); + f->store_32(dlen); + DVector<uint8_t>::Read r = val.get_data().read(); + f->store_buffer(r.ptr(),dlen); + _pad_buffer(dlen); + + } break; + case Variant::NODE_PATH: { + f->store_32(VARIANT_NODE_PATH); + save_unicode_string(p_property); + } break; + case Variant::_RID: { + + f->store_32(VARIANT_RID); + WARN_PRINT("Can't save RIDs"); + RID val = p_property; + f->store_32(val.get_id()); + } break; + case Variant::OBJECT: { + + f->store_32(VARIANT_OBJECT); + RES res = p_property; + if (res.is_null()) { + f->store_32(OBJECT_EMPTY); + return; // don't save it + } + + if (res->get_path().length() && res->get_path().find("::")==-1) { + f->store_32(OBJECT_EXTERNAL_RESOURCE); + save_unicode_string(res->get_type()); + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + save_unicode_string(path); + } else { + + if (!resource_map.has(res)) { + f->store_32(OBJECT_EMPTY); + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL(); + } + + f->store_32(OBJECT_INTERNAL_RESOURCE); + f->store_32(resource_map[res]); + //internal resource + } + + + } break; + case Variant::INPUT_EVENT: { + + f->store_32(VARIANT_INPUT_EVENT); + WARN_PRINT("Can't save InputEvent (maybe it could..)"); + } break; + case Variant::DICTIONARY: { + + f->store_32(VARIANT_DICTIONARY); + Dictionary d = p_property; + f->store_32(d.size()); + + List<Variant> keys; + d.get_key_list(&keys); + + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + + write_property(0,E->get()); + write_property(0,d[E->get()]); + } + + + } break; + case Variant::ARRAY: { + + f->store_32(VARIANT_ARRAY); + Array a=p_property; + f->store_32(a.size()); + for(int i=0;i<a.size();i++) { + + write_property(i,a[i]); + } + + } break; + case Variant::RAW_ARRAY: { + + f->store_32(VARIANT_RAW_ARRAY); + DVector<uint8_t> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<uint8_t>::Read r = arr.read(); + f->store_buffer(r.ptr(),len); + _pad_buffer(len); + + } break; + case Variant::INT_ARRAY: { + + f->store_32(VARIANT_INT_ARRAY); + DVector<int> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<int>::Read r = arr.read(); + for(int i=0;i<len;i++) + f->store_32(r[i]); + + } break; + case Variant::REAL_ARRAY: { + + f->store_32(VARIANT_REAL_ARRAY); + DVector<real_t> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<real_t>::Read r = arr.read(); + for(int i=0;i<len;i++) { + f->store_real(r[i]); + } + + } break; + case Variant::STRING_ARRAY: { + + f->store_32(VARIANT_STRING_ARRAY); + DVector<String> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<String>::Read r = arr.read(); + for(int i=0;i<len;i++) { + save_unicode_string(r[i]); + } + + } break; + case Variant::VECTOR3_ARRAY: { + + f->store_32(VARIANT_VECTOR3_ARRAY); + DVector<Vector3> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<Vector3>::Read r = arr.read(); + for(int i=0;i<len;i++) { + f->store_real(r[i].x); + f->store_real(r[i].y); + f->store_real(r[i].z); + } + + } break; + case Variant::VECTOR2_ARRAY: { + + f->store_32(VARIANT_VECTOR2_ARRAY); + DVector<Vector2> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<Vector2>::Read r = arr.read(); + for(int i=0;i<len;i++) { + f->store_real(r[i].x); + f->store_real(r[i].y); + } + + } break; + case Variant::COLOR_ARRAY: { + + f->store_32(VARIANT_COLOR_ARRAY); + DVector<Color> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<Color>::Read r = arr.read(); + for(int i=0;i<len;i++) { + f->store_real(r[i].r); + f->store_real(r[i].g); + f->store_real(r[i].b); + f->store_real(r[i].a); + } + + } break; + default: { + + ERR_EXPLAIN("Invalid variant"); + ERR_FAIL(); + } + } +} + + +void ObjectFormatSaverBinary::_find_resources(const Variant& p_variant) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null()) + return; + + if (!bundle_resources && res->get_path().length() && res->get_path().find("::") == -1 ) + return; + + if (resource_map.has(res)) + return; + + List<PropertyInfo> property_list; + + res->get_property_list(&property_list); + + for(List<PropertyInfo>::Element *E=property_list.front();E;E=E->next()) { + + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + _find_resources(res->get(E->get().name)); + } + } + + SavedObject *so = memnew( SavedObject ); + _save_obj(res.ptr(),so); + so->meta=res.get_ref_ptr(); + + resource_map[ res ] = saved_resources.size(); + saved_resources.push_back(so); + + } break; + + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i<len;i++) { + + Variant v=varray.get(i); + _find_resources(v); + } + + } break; + + case Variant::DICTIONARY: { + + Dictionary d=p_variant; + List<Variant> keys; + d.get_key_list(&keys); + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} +Error ObjectFormatSaverBinary::_save_obj(const Object *p_object,SavedObject *so) { + + if (optimizer.is_valid()) { + //use optimizer + + List<OptimizedSaver::Property> props; + optimizer->get_property_list(p_object,&props); + + for(List<OptimizedSaver::Property>::Element *E=props.front();E;E=E->next()) { + + if (skip_editor && String(E->get().name).begins_with("__editor")) + continue; + _find_resources(E->get().value); + SavedObject::SavedProperty sp; + + sp.name_idx=get_string_index(E->get().name); + sp.value=E->get().value; + so->properties.push_back(sp); + } + + } else { + //use classic way + List<PropertyInfo> property_list; + p_object->get_property_list( &property_list ); + + for(List<PropertyInfo>::Element *E=property_list.front();E;E=E->next()) { + + if (skip_editor && E->get().name.begins_with("__editor")) + continue; + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + SavedObject::SavedProperty sp; + sp.name_idx=get_string_index(E->get().name); + sp.value = p_object->get(E->get().name); + _find_resources(sp.value); + so->properties.push_back(sp); + } + } + } + + return OK; + +} + +Error ObjectFormatSaverBinary::save(const Object *p_object,const Variant &p_meta) { + + ERR_FAIL_COND_V(!f,ERR_UNCONFIGURED); + ERR_EXPLAIN("write_object should supply either an object, a meta, or both"); + ERR_FAIL_COND_V(!p_object && p_meta.get_type()==Variant::NIL, ERR_INVALID_PARAMETER); + + SavedObject *so = memnew( SavedObject ); + + if (p_object) + so->type=p_object->get_type(); + + _find_resources(p_meta); + so->meta=p_meta; + Error err = _save_obj(p_object,so); + ERR_FAIL_COND_V( err, ERR_INVALID_DATA ); + + saved_objects.push_back(so); + + return OK; +} + +void ObjectFormatSaverBinary::save_unicode_string(const String& p_string) { + + + CharString utf8 = p_string.utf8(); + f->store_32(utf8.length()+1); + f->store_buffer((const uint8_t*)utf8.get_data(),utf8.length()+1); +} + +ObjectFormatSaverBinary::ObjectFormatSaverBinary(FileAccess *p_file,const String& p_magic,const String& p_local_path,uint32_t p_flags,const Ref<OptimizedSaver>& p_optimizer) { + + optimizer=p_optimizer; + relative_paths=p_flags&ObjectSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ObjectSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ObjectSaver::FLAG_BUNDLE_RESOURCES; + big_endian=p_flags&ObjectSaver::FLAG_SAVE_BIG_ENDIAN; + f=p_file; // should be already opened + local_path=p_local_path; + magic=p_magic; + + bin_meta_idx = get_string_index("__bin_meta__"); //is often used, so create +} + +int ObjectFormatSaverBinary::get_string_index(const String& p_string) { + + StringName s=p_string; + if (string_map.has(s)) + return string_map[s]; + + string_map[s]=strings.size(); + strings.push_back(s); + return strings.size()-1; +} + +ObjectFormatSaverBinary::~ObjectFormatSaverBinary() { + + + static const uint8_t header[4]={'O','B','D','B'}; + f->store_buffer(header,4); + if (big_endian) { + f->store_32(1); + f->set_endian_swap(true); + } else + f->store_32(0); + + f->store_32(0); //64 bits file, false for now + f->store_32(VERSION_MAJOR); + f->store_32(VERSION_MINOR); + save_unicode_string(magic); + for(int i=0;i<16;i++) + f->store_32(0); // reserved + + f->store_32(strings.size()); //string table size + for(int i=0;i<strings.size();i++) { + print_bl("saving string: "+strings[i]); + save_unicode_string(strings[i]); + } + + // save resources + + for(int i=0;i<saved_resources.size();i++) { + + SavedObject *so = saved_resources[i]; + RES res = so->meta; + ERR_CONTINUE(!resource_map.has(res)); + + f->store_32(SECTION_RESOURCE); + size_t skip_pos = f->get_pos(); + f->store_64(0); // resource skip seek pos + save_unicode_string(res->get_type()); + + if (res->get_path().length() && res->get_path().find("::") == -1 ) + save_unicode_string(res->get_path()); + else + save_unicode_string("local://"+itos(i)); + + + + List<SavedObject::SavedProperty>::Element *SE = so->properties.front(); + + while(SE) { + + write_property(SE->get().name_idx,SE->get().value); + SE=SE->next(); + } + + f->store_32(SECTION_END); + + size_t end=f->get_pos(); + f->seek(skip_pos); + f->store_64(end); + f->seek_end(); + + memdelete( so ); + } + + if (!saved_objects.empty()) { + + + for(List<SavedObject*>::Element *E=saved_objects.front();E;E=E->next()) { + + SavedObject *so = E->get(); + + + size_t section_end; + + if (so->type!="") { + f->store_32(SECTION_OBJECT); + section_end=f->get_pos(); + f->store_64(0); //section end + save_unicode_string(so->type); + } else { + f->store_32(SECTION_META_OBJECT); + section_end=f->get_pos(); + f->store_64(0); //section end + } + + + if (so->meta.get_type()!=Variant::NIL) + write_property(bin_meta_idx,so->meta); + + List<SavedObject::SavedProperty>::Element *SE = so->properties.front(); + + while(SE) { + + write_property(SE->get().name_idx,SE->get().value); + SE=SE->next(); + } + + f->store_32(SECTION_END); + + size_t end=f->get_pos(); + f->seek(section_end); + f->store_64(end); + f->seek_end(); + + memdelete(so); //no longer needed + } + + + } + + f->store_32(SECTION_END); + + f->close(); + memdelete(f); +} + + +ObjectFormatSaver* ObjectFormatSaverInstancerBinary::instance(const String& p_file,const String& p_magic,uint32_t p_flags,const Ref<OptimizedSaver>& p_optimizer) { + + FileAccess *f = FileAccess::open(p_file, FileAccess::WRITE); + + ERR_FAIL_COND_V( !f, NULL ); + String local_path = Globals::get_singleton()->localize_path(p_file); + + return memnew( ObjectFormatSaverBinary( f, p_magic,local_path,p_flags,p_optimizer ) ); +} + +void ObjectFormatSaverInstancerBinary::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("bin"); +} + + +ObjectFormatSaverInstancerBinary::~ObjectFormatSaverInstancerBinary() { + + +} + + + +/************************************************/ +/************************************************/ +/************************************************/ +/************************************************/ +/************************************************/ + + +void ObjectFormatLoaderBinary::_advance_padding(uint32_t p_len) { + + uint32_t extra = 4-(p_len%4); + if (extra<4) { + for(uint32_t i=0;i<extra;i++) + f->get_8(); //pad to 32 + } + +} + +Error ObjectFormatLoaderBinary::parse_property(Variant& r_v, int &r_index) { + + + uint32_t prop = f->get_32(); + if (prop==SECTION_END) + return ERR_FILE_EOF; + ERR_FAIL_COND_V(prop!=SECTION_PROPERTY,ERR_FILE_CORRUPT); + + r_index = f->get_32(); + + uint32_t type = f->get_32(); + print_bl("find property of type: "+itos(type)); + + + switch(type) { + + case VARIANT_NIL: { + + r_v=Variant(); + } break; + case VARIANT_BOOL: { + + r_v=bool(f->get_32()); + } break; + case VARIANT_INT: { + + r_v=int(f->get_32()); + } break; + case VARIANT_REAL: { + + r_v=f->get_real(); + } break; + case VARIANT_STRING: { + + r_v=get_unicode_string(); + } break; + case VARIANT_VECTOR2: { + + Vector2 v; + v.x=f->get_real(); + v.y=f->get_real(); + r_v=v; + + } break; + case VARIANT_RECT2: { + + Rect2 v; + v.pos.x=f->get_real(); + v.pos.y=f->get_real(); + v.size.x=f->get_real(); + v.size.y=f->get_real(); + r_v=v; + + } break; + case VARIANT_VECTOR3: { + + Vector3 v; + v.x=f->get_real(); + v.y=f->get_real(); + v.z=f->get_real(); + r_v=v; + } break; + case VARIANT_PLANE: { + + Plane v; + v.normal.x=f->get_real(); + v.normal.y=f->get_real(); + v.normal.z=f->get_real(); + v.d=f->get_real(); + r_v=v; + } break; + case VARIANT_QUAT: { + Quat v; + v.x=f->get_real(); + v.y=f->get_real(); + v.z=f->get_real(); + v.w=f->get_real(); + r_v=v; + + } break; + case VARIANT_AABB: { + + AABB v; + v.pos.x=f->get_real(); + v.pos.y=f->get_real(); + v.pos.z=f->get_real(); + v.size.x=f->get_real(); + v.size.y=f->get_real(); + v.size.z=f->get_real(); + r_v=v; + + } break; + case VARIANT_MATRIX32: { + + Matrix32 v; + v.elements[0].x=f->get_real(); + v.elements[0].y=f->get_real(); + v.elements[1].x=f->get_real(); + v.elements[1].y=f->get_real(); + v.elements[2].x=f->get_real(); + v.elements[2].y=f->get_real(); + r_v=v; + + } break; + case VARIANT_MATRIX3: { + + Matrix3 v; + v.elements[0].x=f->get_real(); + v.elements[0].y=f->get_real(); + v.elements[0].z=f->get_real(); + v.elements[1].x=f->get_real(); + v.elements[1].y=f->get_real(); + v.elements[1].z=f->get_real(); + v.elements[2].x=f->get_real(); + v.elements[2].y=f->get_real(); + v.elements[2].z=f->get_real(); + r_v=v; + + } break; + case VARIANT_TRANSFORM: { + + Transform v; + v.basis.elements[0].x=f->get_real(); + v.basis.elements[0].y=f->get_real(); + v.basis.elements[0].z=f->get_real(); + v.basis.elements[1].x=f->get_real(); + v.basis.elements[1].y=f->get_real(); + v.basis.elements[1].z=f->get_real(); + v.basis.elements[2].x=f->get_real(); + v.basis.elements[2].y=f->get_real(); + v.basis.elements[2].z=f->get_real(); + v.origin.x=f->get_real(); + v.origin.y=f->get_real(); + v.origin.z=f->get_real(); + r_v=v; + } break; + case VARIANT_COLOR: { + + Color v; + v.r=f->get_real(); + v.g=f->get_real(); + v.b=f->get_real(); + v.a=f->get_real(); + r_v=v; + + } break; + case VARIANT_IMAGE: { + + + uint32_t encoding = f->get_32(); + if (encoding==IMAGE_ENCODING_EMPTY) { + r_v=Variant(); + break; + } + + if (encoding==IMAGE_ENCODING_RAW) { + uint32_t width = f->get_32(); + uint32_t height = f->get_32(); + uint32_t mipmaps = f->get_32(); + uint32_t format = f->get_32(); + Image::Format fmt; + switch(format) { + + case IMAGE_FORMAT_GRAYSCALE: { fmt=Image::FORMAT_GRAYSCALE; } break; + case IMAGE_FORMAT_INTENSITY: { fmt=Image::FORMAT_INTENSITY; } break; + case IMAGE_FORMAT_GRAYSCALE_ALPHA: { fmt=Image::FORMAT_GRAYSCALE_ALPHA; } break; + case IMAGE_FORMAT_RGB: { fmt=Image::FORMAT_RGB; } break; + case IMAGE_FORMAT_RGBA: { fmt=Image::FORMAT_RGBA; } break; + case IMAGE_FORMAT_INDEXED: { fmt=Image::FORMAT_INDEXED; } break; + case IMAGE_FORMAT_INDEXED_ALPHA: { fmt=Image::FORMAT_INDEXED_ALPHA; } break; + case IMAGE_FORMAT_BC1: { fmt=Image::FORMAT_BC1; } break; + case IMAGE_FORMAT_BC2: { fmt=Image::FORMAT_BC2; } break; + case IMAGE_FORMAT_BC3: { fmt=Image::FORMAT_BC3; } break; + case IMAGE_FORMAT_BC4: { fmt=Image::FORMAT_BC4; } break; + case IMAGE_FORMAT_BC5: { fmt=Image::FORMAT_BC5; } break; + case IMAGE_FORMAT_CUSTOM: { fmt=Image::FORMAT_CUSTOM; } break; + default: { + + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + } + + + uint32_t datalen = f->get_32(); + + print_bl("width: "+itos(width)); + print_bl("height: "+itos(height)); + print_bl("mipmaps: "+itos(mipmaps)); + print_bl("format: "+itos(format)); + print_bl("datalen: "+itos(datalen)); + + DVector<uint8_t> imgdata; + imgdata.resize(datalen); + DVector<uint8_t>::Write w = imgdata.write(); + f->get_buffer(w.ptr(),datalen); + _advance_padding(datalen); + w=DVector<uint8_t>::Write(); + + r_v=Image(width,height,mipmaps,fmt,imgdata); + } + + + } break; + case VARIANT_NODE_PATH: { + + r_v=NodePath(get_unicode_string()); + } break; + case VARIANT_RID: { + + r_v=f->get_32(); + } break; + case VARIANT_OBJECT: { + + uint32_t type=f->get_32(); + + switch(type) { + + case OBJECT_EMPTY: { + //do none + + } break; + case OBJECT_INTERNAL_RESOURCE: { + uint32_t index=f->get_32(); + String path = local_path+"::"+itos(index); + RES res = ResourceLoader::load(path); + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: "+path).utf8().get_data()); + } + r_v=res; + + } break; + case OBJECT_EXTERNAL_RESOURCE: { + + String type = get_unicode_string(); + String path = get_unicode_string(); + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + + } + + RES res=ResourceLoader::load(path,type); + + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: "+path).utf8().get_data()); + } + r_v=res; + + } break; + default: { + + ERR_FAIL_V(ERR_FILE_CORRUPT); + } break; + } + + } break; + case VARIANT_INPUT_EVENT: { + + } break; + case VARIANT_DICTIONARY: { + + int len=f->get_32(); + Dictionary d; + for(int i=0;i<len;i++) { + int idx; + Variant key; + Error err = parse_property(key,idx); + ERR_FAIL_COND_V(err,ERR_FILE_CORRUPT); + Variant value; + err = parse_property(value,idx); + ERR_FAIL_COND_V(err,ERR_FILE_CORRUPT); + d[key]=value; + } + r_v=d; + } break; + case VARIANT_ARRAY: { + int len=f->get_32(); + Array a; + a.resize(len); + for(int i=0;i<len;i++) { + int idx; + Variant val; + Error err = parse_property(val,idx); + ERR_FAIL_COND_V(err,ERR_FILE_CORRUPT); + a[i]=val; + } + r_v=a; + + } break; + case VARIANT_RAW_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<uint8_t> array; + array.resize(len); + DVector<uint8_t>::Write w = array.write(); + f->get_buffer(w.ptr(),len); + _advance_padding(len); + w=DVector<uint8_t>::Write(); + r_v=array; + + } break; + case VARIANT_INT_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<int> array; + array.resize(len); + DVector<int>::Write w = array.write(); + f->get_buffer((uint8_t*)w.ptr(),len*4); + w=DVector<int>::Write(); + r_v=array; + } break; + case VARIANT_REAL_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<real_t> array; + array.resize(len); + DVector<real_t>::Write w = array.write(); + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)); + w=DVector<real_t>::Write(); + r_v=array; + } break; + case VARIANT_STRING_ARRAY: { + + uint32_t len = f->get_32(); + DVector<String> array; + array.resize(len); + DVector<String>::Write w = array.write(); + for(int i=0;i<len;i++) + w[i]=get_unicode_string(); + w=DVector<String>::Write(); + r_v=array; + + + } break; + case VARIANT_VECTOR2_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<Vector2> array; + array.resize(len); + DVector<Vector2>::Write w = array.write(); + if (sizeof(Vector2)==8) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*2); + } else { + ERR_EXPLAIN("Vector2 size is NOT 8!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector<Vector2>::Write(); + r_v=array; + + } break; + case VARIANT_VECTOR3_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<Vector3> array; + array.resize(len); + DVector<Vector3>::Write w = array.write(); + if (sizeof(Vector3)==12) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*3); + } else { + ERR_EXPLAIN("Vector3 size is NOT 12!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector<Vector3>::Write(); + r_v=array; + + } break; + case VARIANT_COLOR_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<Color> array; + array.resize(len); + DVector<Color>::Write w = array.write(); + if (sizeof(Color)==16) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*4); + } else { + ERR_EXPLAIN("Color size is NOT 16!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector<Color>::Write(); + r_v=array; + } break; + + default: { + ERR_FAIL_V(ERR_FILE_CORRUPT); + } break; + } + + + + return OK; //never reach anyway + +} + +Error ObjectFormatLoaderBinary::load(Object **p_object,Variant &p_meta) { + + + + while(true) { + + if (f->eof_reached()) { + ERR_EXPLAIN("Premature end of file at: "+local_path); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + RES resource; + Object *obj=NULL; + bool meta=false; + + uint32_t section = f->get_32(); + + switch(section) { + + + case SECTION_RESOURCE: { + + print_bl("resource found"); + + size_t section_end = f->get_64(); + print_bl("section end: "+itos(section_end)); + String type = get_unicode_string(); + String path = get_unicode_string(); + print_bl("path: "+path); + + if (path.begins_with("local://")) { + //built-in resource (but really external) + path=path.replace("local://",local_path+"::"); + } + + if (ResourceCache::has(path)) { + f->seek(section_end); + continue; + } + + //load properties + + + obj = ObjectTypeDB::instance(type); + if (!obj) { + ERR_EXPLAIN("Object of unrecognized type '"+type+"' in file: "+type); + } + + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + + Resource *r = obj->cast_to<Resource>(); + if (!r) { + memdelete(obj); //bye + ERR_EXPLAIN("Object type in resource field not a resource, type is: "+obj->get_type()); + ERR_FAIL_COND_V(!obj->cast_to<Resource>(),ERR_FILE_CORRUPT); + } + + resource = RES( r ); + r->set_path(path); + } break; + case SECTION_META_OBJECT: + meta=true; + print_bl("meta found"); + + case SECTION_OBJECT: { + + uint64_t section_end = f->get_64(); + + if (!meta) { + print_bl("object"); + + String type = get_unicode_string(); + if (ObjectTypeDB::can_instance(type)) { + obj = ObjectTypeDB::instance(type); + if (!obj) { + ERR_EXPLAIN("Object of unrecognized type in file: "+type); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + } else { + + f->seek(section_end); + return ERR_SKIP; + }; + + + } + + + } break; + case SECTION_END: { + + + return ERR_FILE_EOF; + } break; + + default: { + + ERR_EXPLAIN("Invalid Section ID '"+itos(section)+"' in file: "+local_path); + ERR_FAIL_V(ERR_FILE_CORRUPT); + + } + + } + + + //load properties + + while(true) { + + int name_idx; + Variant v; + Error err; + err = parse_property(v,name_idx); + + print_bl("prop idx "+itos(name_idx)+" value: "+String(v)); + + if (err==ERR_FILE_EOF) + break; + + if (err!=OK) { + ERR_EXPLAIN("File Corrupted"); + ERR_FAIL_COND_V(err!=OK,ERR_FILE_CORRUPT); + } + + + if (resource.is_null() && name_idx==0) { //0 is __bin_meta__ + + p_meta=v; + continue; + } else if (!obj) { + + ERR_EXPLAIN("Normal property found in meta object."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + Map<int,StringName>::Element *E=string_map.find(name_idx); + if (!E) { + ERR_EXPLAIN("Property ID has no matching name: "+itos(name_idx)); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + obj->set(E->get(),v); + } + + if (!obj) { + *p_object=NULL; + return OK; // it was a meta object + } + + if (resource.is_null()) { + + //regular object + *p_object=obj; + return OK; + } else { + + resource_cache.push_back(resource); //keep it in mem until finished loading + } + + } +} + + +ObjectFormatLoaderBinary::~ObjectFormatLoaderBinary() { + + if (f) { + if (f->is_open()) + f->close(); + memdelete(f); + } +} + + +String ObjectFormatLoaderBinary::get_unicode_string() { + + uint32_t len = f->get_32(); + if (len>str_buf.size()) { + str_buf.resize(len); + } + f->get_buffer((uint8_t*)&str_buf[0],len); + String s; + s.parse_utf8(&str_buf[0]); + return s; +} + +ObjectFormatLoaderBinary::ObjectFormatLoaderBinary(FileAccess *p_f,bool p_endian_swap,bool p_use64) { + + f=p_f; + endian_swap=p_endian_swap; + use_real64=p_use64; + + //load string table + uint32_t string_table_size = f->get_32(); + print_bl("string table size: "+itos(string_table_size)); + for(int i=0;i<string_table_size;i++) { + + String str = get_unicode_string(); + print_bl("string "+itos(i)+" is: "+str); + string_map[i]=str; + } + + +} + +ObjectFormatLoaderBinary* ObjectFormatLoaderInstancerBinary::instance(const String& p_file,const String& p_magic) { + + FileAccess *f=FileAccess::open(p_file,FileAccess::READ); + ERR_FAIL_COND_V(!f,NULL); + + uint8_t header[4]; + f->get_buffer(header,4); + if (header[0]!='O' || header[1]!='B' || header[2]!='D' || header[3]!='B') { + + ERR_EXPLAIN("File not in valid binary format: "+p_file); + ERR_FAIL_V(NULL); + } + + uint32_t big_endian = f->get_32(); +#ifdef BIG_ENDIAN_ENABLED + bool endian_swap = !big_endian; +#else + bool endian_swap = big_endian; +#endif + + bool use_real64 = f->get_32(); + + f->set_endian_swap(big_endian!=0); //read big endian if saved as big endian + + uint32_t ver_major=f->get_32(); + uint32_t ver_minor=f->get_32(); + + print_bl("big endian: "+itos(big_endian)); + print_bl("endian swap: "+itos(endian_swap)); + print_bl("real64: "+itos(use_real64)); + print_bl("major: "+itos(ver_major)); + print_bl("minor: "+itos(ver_minor)); + + if (ver_major>VERSION_MAJOR || (ver_major==VERSION_MAJOR && ver_minor>VERSION_MINOR)) { + + f->close(); + memdelete(f); + ERR_EXPLAIN("File Format '"+itos(ver_major)+"."+itos(ver_minor)+"' is too new! Please upgrade to a a new engine version: "+p_file); + ERR_FAIL_V(NULL); + + } + + uint32_t magic_len = f->get_32(); + Vector<char> magic; + magic.resize(magic_len); + f->get_buffer((uint8_t*)&magic[0],magic_len); + String magic_str; + magic_str.parse_utf8(&magic[0]); + + print_bl("magic: "+magic_str); + if (magic_str!=p_magic) { + + f->close(); + memdelete(f); + ERR_EXPLAIN("File magic mismatch, found '"+magic_str+"' in : "+p_file); + ERR_FAIL_V(NULL); + } + + print_bl("skipping 32"); + for(int i=0;i<16;i++) + f->get_32(); //skip a few reserved fields + + if (f->eof_reached()) { + + f->close(); + memdelete(f); + ERR_EXPLAIN("Premature End Of File: "+p_file); + ERR_FAIL_V(NULL); + + } + + print_bl("creating loader"); + ObjectFormatLoaderBinary *loader = memnew( ObjectFormatLoaderBinary(f,endian_swap,use_real64) ); + loader->local_path=p_file; + + return loader; +} + +void ObjectFormatLoaderInstancerBinary::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("bin"); +} + + +#endif diff --git a/core/io/object_format_binary.h b/core/io/object_format_binary.h new file mode 100644 index 0000000000..aaf6bf357a --- /dev/null +++ b/core/io/object_format_binary.h @@ -0,0 +1,158 @@ +/*************************************************************************/ +/* object_format_binary.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. */ +/*************************************************************************/ +#ifndef OBJECT_FORMAT_BINARY_H +#define OBJECT_FORMAT_BINARY_H + +#include "object_loader.h" +#include "object_saver_base.h" +#include "dvector.h" +#include "core/os/file_access.h" + +#ifdef OLD_SCENE_FORMAT_ENABLED +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + + +class ObjectFormatSaverBinary : public ObjectFormatSaver { + + String local_path; + + + Ref<OptimizedSaver> optimizer; + + bool relative_paths; + bool bundle_resources; + bool skip_editor; + bool big_endian; + int bin_meta_idx; + FileAccess *f; + String magic; + Map<RES,int> resource_map; + Map<StringName,int> string_map; + Vector<StringName> strings; + + struct SavedObject { + + Variant meta; + String type; + + + struct SavedProperty { + + int name_idx; + Variant value; + }; + + List<SavedProperty> properties; + }; + + + int get_string_index(const String& p_string); + void save_unicode_string(const String& p_string); + + List<SavedObject*> saved_objects; + List<SavedObject*> saved_resources; + + void _pad_buffer(int p_bytes); + Error _save_obj(const Object *p_object,SavedObject *so); + void _find_resources(const Variant& p_variant); + void write_property(int p_idx,const Variant& p_property); + + +public: + + virtual Error save(const Object *p_object,const Variant &p_meta); + + ObjectFormatSaverBinary(FileAccess *p_file,const String& p_magic,const String& p_local_path,uint32_t p_flags,const Ref<OptimizedSaver>& p_optimizer); + ~ObjectFormatSaverBinary(); +}; + +class ObjectFormatSaverInstancerBinary : public ObjectFormatSaverInstancer { +public: + + virtual ObjectFormatSaver* instance(const String& p_file,const String& p_magic,uint32_t p_flags=0,const Ref<OptimizedSaver>& p_optimizer=Ref<OptimizedSaver>()); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + + virtual ~ObjectFormatSaverInstancerBinary(); +}; + + + + +/***********************************/ +/***********************************/ +/***********************************/ +/***********************************/ + +class ObjectFormatLoaderBinary : public ObjectFormatLoader { + + String local_path; + + FileAccess *f; + + bool endian_swap; + bool use_real64; + + Vector<char> str_buf; + List<RES> resource_cache; + + Map<int,StringName> string_map; + + String get_unicode_string(); + void _advance_padding(uint32_t p_len); + +friend class ObjectFormatLoaderInstancerBinary; + + + Error parse_property(Variant& r_v, int& r_index); + +public: + + + virtual Error load(Object **p_object,Variant &p_meta); + + ObjectFormatLoaderBinary(FileAccess *f,bool p_endian_swap,bool p_use64); + virtual ~ObjectFormatLoaderBinary(); +}; + +class ObjectFormatLoaderInstancerBinary : public ObjectFormatLoaderInstancer { +public: + + virtual ObjectFormatLoaderBinary* instance(const String& p_file,const String& p_magic); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + + + +}; + + + +#endif // OBJECT_FORMAT_BINARY_H +#endif diff --git a/core/io/object_format_xml.cpp b/core/io/object_format_xml.cpp new file mode 100644 index 0000000000..0a8ce70d5e --- /dev/null +++ b/core/io/object_format_xml.cpp @@ -0,0 +1,3190 @@ +/*************************************************************************/ +/* object_format_xml.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. */ +/*************************************************************************/ +#ifdef XML_ENABLED +#ifdef OLD_SCENE_FORMAT_ENABLED +#include "object_format_xml.h" +#include "resource.h" +#include "io/resource_loader.h" +#include "print_string.h" +#include "object_type_db.h" +#include "globals.h" +#include "os/os.h" +#include "version.h" + +void ObjectFormatSaverXML::escape(String& p_str) { + + p_str=p_str.replace("&","&"); + p_str=p_str.replace("<",">"); + p_str=p_str.replace(">","<"); + p_str=p_str.replace("'","'"); + p_str=p_str.replace("\"","""); + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + p_str=p_str.replace(chr,"&#"+String::num(i)+";"); + } + + +} +void ObjectFormatSaverXML::write_tabs(int p_diff) { + + for (int i=0;i<depth+p_diff;i++) { + + f->store_8('\t'); + } +} + +void ObjectFormatSaverXML::write_string(String p_str,bool p_escape) { + + /* write an UTF8 string */ + if (p_escape) + escape(p_str); + + f->store_string(p_str);; + /* + CharString cs=p_str.utf8(); + const char *data=cs.get_data(); + + while (*data) { + f->store_8(*data); + data++; + }*/ + + +} + +void ObjectFormatSaverXML::enter_tag(const String& p_section,const String& p_args) { + + if (p_args.length()) + write_string("<"+p_section+" "+p_args+">",false); + else + write_string("<"+p_section+">",false); + depth++; +} +void ObjectFormatSaverXML::exit_tag(const String& p_section) { + + depth--; + write_string("</"+p_section+">",false); + +} + +/* +static bool _check_type(const Variant& p_property) { + + if (p_property.get_type()==Variant::_RID) + return false; + if (p_property.get_type()==Variant::OBJECT) { + RES res = p_property; + if (res.is_null()) + return false; + } + + return true; +}*/ + +void ObjectFormatSaverXML::write_property(const String& p_name,const Variant& p_property,bool *r_ok) { + + if (r_ok) + *r_ok=false; + + String type; + String params; + bool oneliner=true; + + switch( p_property.get_type() ) { + + case Variant::NIL: type="nil"; break; + case Variant::BOOL: type="bool"; break; + case Variant::INT: type="int"; break; + case Variant::REAL: type="real"; break; + case Variant::STRING: type="string"; break; + case Variant::VECTOR2: type="vector2"; break; + case Variant::RECT2: type="rect2"; break; + case Variant::VECTOR3: type="vector3"; break; + case Variant::PLANE: type="plane"; break; + case Variant::_AABB: type="aabb"; break; + case Variant::QUAT: type="quaternion"; break; + case Variant::MATRIX32: type="matrix32"; break; + case Variant::MATRIX3: type="matrix3"; break; + case Variant::TRANSFORM: type="transform"; break; + case Variant::COLOR: type="color"; break; + case Variant::IMAGE: { + type="image"; + Image img=p_property; + if (img.empty()) { + enter_tag(type,"name=\""+p_name+"\""); + exit_tag(type); + if (r_ok) + *r_ok=true; + return; + } + params+="encoding=\"raw\""; + params+=" width=\""+itos(img.get_width())+"\""; + params+=" height=\""+itos(img.get_height())+"\""; + params+=" mipmaps=\""+itos(img.get_mipmaps())+"\""; + + switch(img.get_format()) { + + case Image::FORMAT_GRAYSCALE: params+=" format=\"grayscale\""; break; + case Image::FORMAT_INTENSITY: params+=" format=\"intensity\""; break; + case Image::FORMAT_GRAYSCALE_ALPHA: params+=" format=\"grayscale_alpha\""; break; + case Image::FORMAT_RGB: params+=" format=\"rgb\""; break; + case Image::FORMAT_RGBA: params+=" format=\"rgba\""; break; + case Image::FORMAT_INDEXED : params+=" format=\"indexed\""; break; + case Image::FORMAT_INDEXED_ALPHA: params+=" format=\"indexed_alpha\""; break; + case Image::FORMAT_BC1: params+=" format=\"bc1\""; break; + case Image::FORMAT_BC2: params+=" format=\"bc2\""; break; + case Image::FORMAT_BC3: params+=" format=\"bc3\""; break; + case Image::FORMAT_BC4: params+=" format=\"bc4\""; break; + case Image::FORMAT_BC5: params+=" format=\"bc5\""; break; + case Image::FORMAT_CUSTOM: params+=" format=\"custom\" custom_size=\""+itos(img.get_data().size())+"\""; break; + default: {} + } + } break; + case Variant::NODE_PATH: type="node_path"; break; + case Variant::OBJECT: { + type="resource"; + RES res = p_property; + if (res.is_null()) { + enter_tag(type,"name=\""+p_name+"\""); + exit_tag(type); + if (r_ok) + *r_ok=true; + + return; // don't save it + } + + params="resource_type=\""+res->get_type()+"\""; + + if (res->get_path().length() && res->get_path().find("::")==-1) { + //external resource + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + escape(path); + params+=" path=\""+path+"\""; + } else { + + //internal resource + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL_COND(!resource_map.has(res)); + + params+=" path=\"local://"+itos(resource_map[res])+"\""; + } + + } break; + case Variant::INPUT_EVENT: type="input_event"; break; + case Variant::DICTIONARY: type="dictionary" ; oneliner=false; break; + case Variant::ARRAY: type="array"; params="len=\""+itos(p_property.operator Array().size())+"\""; oneliner=false; break; + + case Variant::RAW_ARRAY: type="raw_array"; params="len=\""+itos(p_property.operator DVector < uint8_t >().size())+"\""; break; + case Variant::INT_ARRAY: type="int_array"; params="len=\""+itos(p_property.operator DVector < int >().size())+"\""; break; + case Variant::REAL_ARRAY: type="real_array"; params="len=\""+itos(p_property.operator DVector < real_t >().size())+"\""; break; + case Variant::STRING_ARRAY: type="string_array"; params="len=\""+itos(p_property.operator DVector < String >().size())+"\""; break; + case Variant::VECTOR2_ARRAY: type="vector2_array"; params="len=\""+itos(p_property.operator DVector < Vector2 >().size())+"\""; break; + case Variant::VECTOR3_ARRAY: type="vector3_array"; params="len=\""+itos(p_property.operator DVector < Vector3 >().size())+"\""; break; + case Variant::COLOR_ARRAY: type="color_array"; params="len=\""+itos(p_property.operator DVector < Color >().size())+"\""; break; + default: { + + ERR_PRINT("Unknown Variant type."); + ERR_FAIL(); + } + + } + + write_tabs(); + + if (p_name!="") { + if (params.length()) + enter_tag(type,"name=\""+p_name+"\" "+params); + else + enter_tag(type,"name=\""+p_name+"\""); + } else { + if (params.length()) + enter_tag(type," "+params); + else + enter_tag(type,""); + } + + if (!oneliner) + write_string("\n",false); + else + write_string(" ",false); + + + switch( p_property.get_type() ) { + + case Variant::NIL: { + + } break; + case Variant::BOOL: { + + write_string( p_property.operator bool() ? "True":"False" ); + } break; + case Variant::INT: { + + write_string( itos(p_property.operator int()) ); + } break; + case Variant::REAL: { + + write_string( rtos(p_property.operator real_t()) ); + } break; + case Variant::STRING: { + + String str=p_property; + escape(str); + str="\""+str+"\""; + write_string( str,false ); + } break; + case Variant::VECTOR2: { + + Vector2 v = p_property; + write_string( rtoss(v.x) +", "+rtoss(v.y) ); + } break; + case Variant::RECT2: { + + Rect2 aabb = p_property; + write_string( rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) ); + + } break; + case Variant::VECTOR3: { + + Vector3 v = p_property; + write_string( rtoss(v.x) +", "+rtoss(v.y)+", "+rtoss(v.z) ); + } break; + case Variant::PLANE: { + + Plane p = p_property; + write_string( rtoss(p.normal.x) +", "+rtoss(p.normal.y)+", "+rtoss(p.normal.z)+", "+rtoss(p.d) ); + + } break; + case Variant::_AABB: { + + AABB aabb = p_property; + write_string( rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.pos.z) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) +", "+rtoss(aabb.size.z) ); + + } break; + case Variant::QUAT: { + + Quat quat = p_property; + write_string( rtoss(quat.x)+", "+rtoss(quat.y)+", "+rtoss(quat.z)+", "+rtoss(quat.w)+", "); + + } break; + case Variant::MATRIX32: { + + String s; + Matrix32 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + write_string(s); + + } break; + case Variant::MATRIX3: { + + String s; + Matrix3 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + write_string(s); + + } break; + case Variant::TRANSFORM: { + + String s; + Transform t = p_property; + Matrix3 &m3 = t.basis; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + s=s+", "+rtoss(t.origin.x) +", "+rtoss(t.origin.y)+", "+rtoss(t.origin.z); + + write_string(s); + } break; + + // misc types + case Variant::COLOR: { + + Color c = p_property; + write_string( rtoss(c.r) +", "+rtoss(c.g)+", "+rtoss(c.b)+", "+rtoss(c.a) ); + + } break; + case Variant::IMAGE: { + + String s; + Image img = p_property; + DVector<uint8_t> data = img.get_data(); + int len = data.size(); + DVector<uint8_t>::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i<len;i++) { + + uint8_t byte = ptr[i]; + const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + char str[3]={ hex[byte>>4], hex[byte&0xF], 0}; + s+=str; + } + + write_string(s); + } break; + case Variant::NODE_PATH: { + + String str=p_property; + escape(str); + str="\""+str+"\""; + write_string( str,false); + + } break; + + case Variant::OBJECT: { + /* this saver does not save resources in here + RES res = p_property; + + if (!res.is_null()) { + + String path=res->get_path(); + if (!res->is_shared() || !path.length()) { + // if no path, or path is from inside a scene + write_object( *res ); + } + + } + */ + + } break; + case Variant::INPUT_EVENT: { + + write_string( p_property.operator String() ); + } break; + case Variant::DICTIONARY: { + + Dictionary dict = p_property; + + + List<Variant> keys; + dict.get_key_list(&keys); + + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + + bool ok; + write_property("",E->get(),&ok); + ERR_CONTINUE(!ok); + + write_property("",dict[E->get()],&ok); + if (!ok) + write_property("",Variant()); //at least make the file consistent.. + } + + + + + } break; + case Variant::ARRAY: { + + Array array = p_property; + int len=array.size(); + for (int i=0;i<len;i++) { + + write_property("",array[i]); + + } + + } break; + + case Variant::RAW_ARRAY: { + + String s; + DVector<uint8_t> data = p_property; + int len = data.size(); + DVector<uint8_t>::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i<len;i++) { + + uint8_t byte = ptr[i]; + const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + char str[3]={ hex[byte>>4], hex[byte&0xF], 0}; + s+=str; + } + + write_string(s,false); + + } break; + case Variant::INT_ARRAY: { + + DVector<int> data = p_property; + int len = data.size(); + DVector<int>::Read r = data.read(); + const int *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + + write_string(itos(ptr[i]),false); + } + + + + } break; + case Variant::REAL_ARRAY: { + + DVector<real_t> data = p_property; + int len = data.size(); + DVector<real_t>::Read r = data.read(); + const real_t *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + write_string(rtoss(ptr[i]),false); + } + + + } break; + case Variant::STRING_ARRAY: { + + DVector<String> data = p_property; + int len = data.size(); + DVector<String>::Read r = data.read(); + const String *ptr=r.ptr();; + String s; + + for (int i=0;i<len;i++) { + + if (i>0) + s+=", "; + String str=ptr[i]; + escape(str); + s=s+"\""+str+"\""; + } + + write_string(s,false); + + } break; + case Variant::VECTOR2_ARRAY: { + + DVector<Vector2> data = p_property; + int len = data.size(); + DVector<Vector2>::Read r = data.read(); + const Vector2 *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + write_string(rtoss(ptr[i].x),false); + write_string(", "+rtoss(ptr[i].y),false); + + } + + + } break; + case Variant::VECTOR3_ARRAY: { + + DVector<Vector3> data = p_property; + int len = data.size(); + DVector<Vector3>::Read r = data.read(); + const Vector3 *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + write_string(rtoss(ptr[i].x),false); + write_string(", "+rtoss(ptr[i].y),false); + write_string(", "+rtoss(ptr[i].z),false); + + } + + + } break; + case Variant::COLOR_ARRAY: { + + DVector<Color> data = p_property; + int len = data.size(); + DVector<Color>::Read r = data.read(); + const Color *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + + write_string(rtoss(ptr[i].r),false); + write_string(", "+rtoss(ptr[i].g),false); + write_string(", "+rtoss(ptr[i].b),false); + write_string(", "+rtoss(ptr[i].a),false); + + } + + } break; + default: {} + + } + if (oneliner) + write_string(" "); + else + write_tabs(-1); + exit_tag(type); + + write_string("\n",false); + + if (r_ok) + *r_ok=true; + +} + + +void ObjectFormatSaverXML::_find_resources(const Variant& p_variant) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null()) + return; + + if (!bundle_resources && res->get_path().length() && res->get_path().find("::") == -1 ) + return; + + if (resource_map.has(res)) + return; + + List<PropertyInfo> property_list; + + res->get_property_list( &property_list ); + + List<PropertyInfo>::Element *I=property_list.front(); + + while(I) { + + PropertyInfo pi=I->get(); + + if (pi.usage&PROPERTY_USAGE_STORAGE || (bundle_resources && pi.usage&PROPERTY_USAGE_BUNDLE)) { + + Variant v=res->get(I->get().name); + _find_resources(v); + } + + I=I->next(); + } + + resource_map[ res ] = resource_map.size(); //saved after, so the childs it needs are available when loaded + saved_resources.push_back(res); + + } break; + + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i<len;i++) { + + Variant v=varray.get(i); + _find_resources(v); + } + + } break; + + case Variant::DICTIONARY: { + + Dictionary d=p_variant; + List<Variant> keys; + d.get_key_list(&keys); + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} + + + +Error ObjectFormatSaverXML::save(const Object *p_object,const Variant &p_meta) { + + ERR_FAIL_COND_V(!f,ERR_UNCONFIGURED); + ERR_EXPLAIN("write_object should supply either an object, a meta, or both"); + ERR_FAIL_COND_V(!p_object && p_meta.get_type()==Variant::NIL, ERR_INVALID_PARAMETER); + + SavedObject *so = memnew( SavedObject ); + + if (p_object) + so->type=p_object->get_type(); + + _find_resources(p_meta); + so->meta=p_meta; + + if (p_object) { + + + if (optimizer.is_valid()) { + //use optimizer + + List<OptimizedSaver::Property> props; + optimizer->get_property_list(p_object,&props); + + for(List<OptimizedSaver::Property>::Element *E=props.front();E;E=E->next()) { + + if (skip_editor && String(E->get().name).begins_with("__editor")) + continue; + _find_resources(E->get().value); + SavedObject::SavedProperty sp; + sp.name=E->get().name; + sp.value=E->get().value; + so->properties.push_back(sp); + } + + } else { + //use classic way + List<PropertyInfo> property_list; + p_object->get_property_list( &property_list ); + + for(List<PropertyInfo>::Element *E=property_list.front();E;E=E->next()) { + + if (skip_editor && E->get().name.begins_with("__editor")) + continue; + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + SavedObject::SavedProperty sp; + sp.name=E->get().name; + sp.value = p_object->get(E->get().name); + _find_resources(sp.value); + so->properties.push_back(sp); + } + } + } + + } + + saved_objects.push_back(so); + + return OK; +} + +ObjectFormatSaverXML::ObjectFormatSaverXML(FileAccess *p_file,const String& p_magic,const String& p_local_path,uint32_t p_flags,const Ref<OptimizedSaver>& p_optimizer) { + + optimizer=p_optimizer; + relative_paths=p_flags&ObjectSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ObjectSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ObjectSaver::FLAG_BUNDLE_RESOURCES; + f=p_file; // should be already opened + depth=0; + local_path=p_local_path; + magic=p_magic; +} +ObjectFormatSaverXML::~ObjectFormatSaverXML() { + + write_string("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>",false); //no escape + write_string("\n",false); + enter_tag("object_file","magic=\""+magic+"\" "+"version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\" version_name=\""+VERSION_FULL_NAME+"\""); + write_string("\n",false); + + // save resources + + for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) { + + RES res = E->get(); + ERR_CONTINUE(!resource_map.has(res)); + + write_tabs(); + if (res->get_path().length() && res->get_path().find("::") == -1 ) + enter_tag("resource","type=\""+res->get_type()+"\" path=\""+res->get_path()+"\""); //bundled + else + enter_tag("resource","type=\""+res->get_type()+"\" path=\"local://"+itos(resource_map[res])+"\""); + + if (optimizer.is_valid()) { + + List<OptimizedSaver::Property> props; + optimizer->get_property_list(res.ptr(),&props); + + for(List<OptimizedSaver::Property>::Element *E=props.front();E;E=E->next()) { + + if (skip_editor && String(E->get().name).begins_with("__editor")) + continue; + + write_property(E->get().name,E->get().value); + } + + + } else { + + List<PropertyInfo> property_list; + res->get_property_list(&property_list); + for(List<PropertyInfo>::Element *PE = property_list.front();PE;PE=PE->next()) { + + + if (skip_editor && PE->get().name.begins_with("__editor")) + continue; + + if (PE->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && PE->get().usage&PROPERTY_USAGE_BUNDLE)) { + + String name = PE->get().name; + Variant value = res->get(name); + write_property(name,value); + } + + + } + + } + write_tabs(-1); + exit_tag("resource"); + write_string("\n",false); + } + + if (!saved_objects.empty()) { + + + for(List<SavedObject*>::Element *E=saved_objects.front();E;E=E->next()) { + + SavedObject *so = E->get(); + + + + write_tabs(); + if (so->type!="") + enter_tag("object","type=\""+so->type+"\""); + else + enter_tag("object"); + write_string("\n",false); + + if (so->meta.get_type()!=Variant::NIL) { + + write_property("__xml_meta__",so->meta); + + } + + List<SavedObject::SavedProperty>::Element *SE = so->properties.front(); + + while(SE) { + + write_property(SE->get().name,SE->get().value); + SE=SE->next(); + } + + + write_tabs(-1); + exit_tag("object"); + write_string("\n",false); + memdelete(so); //no longer needed + } + + + } else { + + WARN_PRINT("File contains no saved objects."); + } + + exit_tag("object_file"); + f->close(); + memdelete(f); +} + + +ObjectFormatSaver* ObjectFormatSaverInstancerXML::instance(const String& p_file,const String& p_magic,uint32_t p_flags,const Ref<OptimizedSaver>& p_optimizer) { + + Error err; + FileAccess *f = FileAccess::open(p_file, FileAccess::WRITE,&err); + + ERR_FAIL_COND_V( err, NULL ); + String local_path = Globals::get_singleton()->localize_path(p_file); + + return memnew( ObjectFormatSaverXML( f, p_magic,local_path,p_flags,p_optimizer ) ); +} + +void ObjectFormatSaverInstancerXML::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("xml"); +} + + +ObjectFormatSaverInstancerXML::~ObjectFormatSaverInstancerXML() { + + +} + +/************************************************/ +/************************************************/ +/************************************************/ +/************************************************/ +/************************************************/ + + + +#ifdef OPTIMIZED_XML_LOADER + +#define IS_FLOAT_CHAR(m_c) \ + ((m_c>='0' && m_c<='9') || m_c=='e' || m_c=='-' || m_c=='+' || m_c=='.') + +#define XML_FAIL(m_cond,m_err) \ + if (m_cond) {\ + ERR_EXPLAIN(local_path+":"+itos(parser->get_current_line())+": "+String(m_err));\ + ERR_FAIL_COND_V( m_cond, ERR_FILE_CORRUPT );\ + } + + +Error ObjectFormatLoaderXML::_parse_property(Variant& r_v,String& r_name) { + + XML_FAIL( parser->is_empty(), "unexpected empty tag"); + + String type=parser->get_node_name(); + String name=parser->get_attribute_value_safe("name"); + + r_v=Variant(); + r_name=name; + + if (type=="dictionary") { + + Dictionary d; + int reading=0; + Variant key; + while(parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT) { + Error err; + String tagname; + + if (reading==0) { + + err=_parse_property(key,tagname); + XML_FAIL( err,"error parsing dictionary key: "+name); + reading++; + } else { + + reading=0; + Variant value; + err=_parse_property(value,tagname); + XML_FAIL( err,"error parsing dictionary value: "+name); + d[key]=value; + } + + } else if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()=="dictionary") { + r_v=d; + return OK; + } + } + + + XML_FAIL( true, "unexpected end of file while reading dictionary: "+name); + + } else if (type=="array") { + + XML_FAIL( !parser->has_attribute("len"), "array missing 'len' attribute"); + + int len=parser->get_attribute_value("len").to_int(); + + Array array; + array.resize(len); + + + Variant v; + String tagname; + int idx=0; + + + while(parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT) { + + XML_FAIL( idx >= len, "array size mismatch (too many elements)"); + Error err; + String tagname; + Variant key; + + err=_parse_property(key,tagname); + XML_FAIL( err,"error parsing element of array: "+name); + array[idx]=key; + idx++; + + + } else if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()=="array") { + + XML_FAIL( idx != len, "array size mismatch (not "+itos(len)+"):"+name); + r_v=array; + return OK; + } + } + + XML_FAIL( true, "unexpected end of file while reading dictionary: "+name); + + } else if (type=="resource") { + + + XML_FAIL(!parser->has_attribute("path"),"resource property has no 'path' set (embedding not supported).") + + String path=parser->get_attribute_value("path"); + String hint = parser->get_attribute_value_safe("resource_type"); + + if (path.begins_with("local://")) + path=path.replace("local://",local_path+"::"); + else if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + + } + + //take advantage of the resource loader cache. The resource is cached on it, even if + RES res=ResourceLoader::load(path,hint); + + + if (res.is_null()) { + + WARN_PRINT(String("Couldn't load resource: "+path).ascii().get_data()); + } + + r_v=res.get_ref_ptr(); + + } else if (type=="image") { + + if (parser->has_attribute("encoding")) { //there is image data + + String encoding=parser->get_attribute_value("encoding"); + + if (encoding=="raw") { + + //raw image (bytes) + + XML_FAIL( !parser->has_attribute("width"), "missing attribute in raw encoding: 'width'."); + XML_FAIL( !parser->has_attribute("height"), "missing attribute in raw encoding: 'height'."); + XML_FAIL( !parser->has_attribute("format"), "missing attribute in raw encoding: 'format'."); + + String format = parser->get_attribute_value("format"); + String width = parser->get_attribute_value("width"); + String height = parser->get_attribute_value("height"); + + Image::Format imgformat; + int chans=0; + int pal=0; + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + chans=1; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + chans=1; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + chans=2; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + chans=3; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + chans=4; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + chans=1; + pal=256*3; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + chans=1; + pal=256*4; + } else { + + XML_FAIL(true, "invalid format for image: "+format); + + } + + XML_FAIL( chans==0, "invalid number of color channels in image (0)."); + + int w=width.to_int(); + int h=height.to_int(); + + if (w == 0 && w == 0) { //epmty, don't even bother + //r_v = Image(w, h, imgformat); + r_v=Image(); + return OK; + } else { + + //decode hexa + + DVector<uint8_t> pixels; + pixels.resize(chans*w*h+pal); + int pixels_size=pixels.size(); + XML_FAIL( pixels_size==0, "corrupt"); + + ERR_FAIL_COND_V(pixels_size==0,ERR_FILE_CORRUPT); + DVector<uint8_t>::Write wr=pixels.write(); + uint8_t *bytes=wr.ptr(); + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + + String text = parser->get_node_data().strip_edges(); + XML_FAIL( text.length()/2 != pixels_size, "unexpected image data size" ); + + for(int i=0;i<pixels_size*2;i++) { + + uint8_t byte; + CharType c=text[i]; + + if ( (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (i&1) { + + byte|=HEX2CHR(c); + bytes[i>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + + } + } + + wr=DVector<uint8_t>::Write(); + r_v=Image(w,h,imgformat,pixels); + } + } + + } else { + r_v=Image(); // empty image, since no encoding defined + } + + } else if (type=="raw_array") { + + XML_FAIL( !parser->has_attribute("len"), "array missing 'len' attribute"); + + int len=parser->get_attribute_value("len").to_int(); + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + XML_FAIL( text.length() != len*2, "raw array length mismatch" ); + + DVector<uint8_t> bytes; + bytes.resize(len); + DVector<uint8_t>::Write w=bytes.write(); + uint8_t *bytesptr=w.ptr(); + + + for(int i=0;i<len*2;i++) { + + uint8_t byte; + CharType c=text[i]; + + if ( (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (i&1) { + + byte|=HEX2CHR(c); + bytesptr[i>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + } + } + + w=DVector<uint8_t>::Write(); + r_v=bytes; + } + + } else if (type=="int_array") { + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector<int> varray; + varray.resize(len); + DVector<int>::Write w = varray.write(); + + int idx=0; + const CharType *from=c-1; + + while(*c) { + + bool ischar = (*c >='0' && *c<='9') || *c=='+' || *c=='-'; + if (!ischar) { + + if (int64_t(c-from)>1) { + + int i = String::to_int(from+1,int64_t(c-from)); + w[idx++]=i; + } + + from=c; + } else { + + XML_FAIL( idx >= len, "array too big"); + } + + c++; + } + + XML_FAIL( idx != len, "array size mismatch"); + + w = varray.write(); + r_v=varray; + } + + + + } else if (type=="real_array") { + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector<real_t> varray; + varray.resize(len); + DVector<real_t>::Write w = varray.write(); + + int idx=0; + const CharType *from=c-1; + + while(*c) { + + bool ischar = IS_FLOAT_CHAR((*c)); + if (!ischar) { + + if (int64_t(c-from)>1) { + + real_t f = String::to_double(from+1,int64_t(c-from)); + w[idx++]=f; + } + + from=c; + } else { + + XML_FAIL( idx >= len, "array too big"); + } + + c++; + } + + XML_FAIL( idx != len, "array size mismatch"); + + w = varray.write(); + r_v=varray; + } + + } else if (type=="string_array") { + + + // this is invalid xml, and will have to be fixed at some point.. + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector<String> sarray; + sarray.resize(len); + DVector<String>::Write w = sarray.write(); + + + bool inside=false; + const CharType *from=c; + int idx=0; + + while(*c) { + + if (inside) { + + if (*c == '"') { + inside=false; + String s = String(from,int64_t(c-from)); + w[idx]=s; + idx++; + } + } else { + + if (*c == '"') { + inside=true; + from=c+1; + XML_FAIL( idx>=len, "string array is too big!!: "+name); + } + } + + c++; + } + + XML_FAIL( inside, "unterminated string array: "+name); + XML_FAIL( len != idx, "string array size mismatch: "+name); + + w = DVector<String>::Write(); + + r_v=sarray; + + } + } else if (type=="vector3_array") { + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector<Vector3> varray; + varray.resize(len); + DVector<Vector3>::Write w = varray.write(); + + int idx=0; + int sidx=0; + Vector3 v; + const CharType *from=c-1; + + while(*c) { + + bool ischar = IS_FLOAT_CHAR((*c)); + if (!ischar) { + + if (int64_t(c-from)>1) { + + real_t f = String::to_double(from+1,int64_t(c-from)); + v[sidx++]=f; + if (sidx==3) { + w[idx++]=v; + sidx=0; + + } + } + + from=c; + } else { + + XML_FAIL( idx >= len, "array too big"); + } + + c++; + } + + XML_FAIL( idx != len, "array size mismatch"); + + w = varray.write(); + r_v=varray; + } + + } else if (type=="color_array") { + + int len=parser->get_attribute_value("len").to_int(); + + if (len>0) { + + XML_FAIL( parser->read()!=OK, "error reading" ); + XML_FAIL( parser->get_node_type()!=XMLParser::NODE_TEXT, "expected text!"); + String text = parser->get_node_data(); + + const CharType *c=text.c_str(); + DVector<Color> carray; + carray.resize(len); + DVector<Color>::Write w = carray.write(); + + int idx=0; + int sidx=0; + Color v; + const CharType *from=c-1; + + while(*c) { + + bool ischar = IS_FLOAT_CHAR((*c)); + if (!ischar) { + + if (int64_t(c-from)>1) { + + real_t f = String::to_double(from+1,int64_t(c-from)); + v[sidx++]=f; + if (sidx==4) { + w[idx++]=v; + sidx=0; + + } + } + + from=c; + } else { + + XML_FAIL( idx >= len, "array too big"); + } + + c++; + } + + XML_FAIL( idx != len, "array size mismatch"); + + w = carray.write(); + r_v=carray; + } + } else { + // simple string parsing code + XML_FAIL( parser->read()!=OK, "can't read data" ); + + String data=parser->get_node_data(); + data=data.strip_edges(); + + if (type=="nil") { + // uh do nothing + + } else if (type=="bool") { + // uh do nothing + if (data.nocasecmp_to("true")==0 || data.to_int()!=0) + r_v=true; + else + r_v=false; + + } else if (type=="int") { + + r_v=data.to_int(); + } else if (type=="real") { + + r_v=data.to_double(); + } else if (type=="string") { + + String str=data; + str=str.substr(1,str.length()-2); + r_v=str; + } else if (type=="vector3") { + + r_v=Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ); + + } else if (type=="vector2") { + + + r_v=Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ); + + } else if (type=="plane") { + + r_v=Plane( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="quaternion") { + + r_v=Quat( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="rect2") { + + r_v=Rect2( + Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ), + Vector2( + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ) + ); + + + } else if (type=="aabb") { + + r_v=AABB( + Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ), + Vector3( + data.get_slice(",",3).to_double(), + data.get_slice(",",4).to_double(), + data.get_slice(",",5).to_double() + ) + ); + + + } else if (type=="matrix3") { + + Matrix3 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + m3.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + } + r_v=m3; + + } else if (type=="transform") { + + Transform tr; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + tr.basis.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + + } + tr.origin=Vector3( + data.get_slice(",",9).to_double(), + data.get_slice(",",10).to_double(), + data.get_slice(",",11).to_double() + ); + r_v=tr; + + } else if (type=="color") { + + r_v=Color( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="node_path") { + + String str=data; + str=str.substr(1,str.length()-2); + r_v=NodePath( str ); + + } else if (type=="input_event") { + + // ? + } else { + + XML_FAIL(true,"unrecognized property tag: "+type); + } + } + + _close_tag(type); + + return OK; +} + + + + +Error ObjectFormatLoaderXML::_close_tag(const String& p_tag) { + + int c=1; + + while(parser->read()==OK) { + + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT && parser->get_node_name()==p_tag) { + c++; + } else if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()==p_tag) { + c--; + + if (c==0) + return OK; + } + + } + + return ERR_FILE_CORRUPT; +} + +Error ObjectFormatLoaderXML::load(Object **p_object,Variant &p_meta) { + + *p_object=NULL; + p_meta=Variant(); + + while(parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT) { + + String name = parser->get_node_name(); + + + XML_FAIL( !parser->has_attribute("type"), "'type' attribute missing." ); + String type = parser->get_attribute_value("type"); + + + Object *obj=NULL; + Ref<Resource> resource; + if (name=="resource") { + + XML_FAIL( !parser->has_attribute("path"), "'path' attribute missing." ); + String path = parser->get_attribute_value("path"); + + XML_FAIL(!path.begins_with("local://"),"path does not begin with 'local://'"); + + + path=path.replace("local://",local_path+"::"); + + if (ResourceCache::has(path)) { + Error err = _close_tag(name); + XML_FAIL( err, "error skipping resource."); + continue; //it's a resource, and it's already loaded + + } + + obj = ObjectTypeDB::instance(type); + XML_FAIL(!obj,"couldn't instance object of type: '"+type+"'"); + + Resource *r = obj->cast_to<Resource>(); + XML_FAIL(!obj,"object isn't of type Resource: '"+type+"'"); + + resource = RES( r ); + r->set_path(path); + + + } else if (name=="object") { + + + if (ObjectTypeDB::can_instance(type)) { + obj = ObjectTypeDB::instance(type); + XML_FAIL(!obj,"couldn't instance object of type: '"+type+"'"); + } else { + + _close_tag(name); + return ERR_SKIP; + }; + } else { + XML_FAIL(true,"Unknown main tag: "+parser->get_node_name()); + } + + //load properties + + while (parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()==name) + break; + else if (parser->get_node_type()==XMLParser::NODE_ELEMENT) { + + String name; + Variant v; + Error err; + err = _parse_property(v,name); + XML_FAIL(err,"Error parsing property: "+name); + + if (resource.is_null() && name=="__xml_meta__") { + + p_meta=v; + continue; + } else { + + XML_FAIL( !obj, "Normal property found in meta object"); + + } + + obj->set(name,v); + + + } + } + + + if (!obj) { + *p_object=NULL; + return OK; // it was a meta object + } + + if (resource.is_null()) { + //regular object + *p_object=obj; + return OK; + } else { + + resource_cache.push_back(resource); //keep it in mem until finished loading and load next + } + + + } else if (parser->get_node_type()==XMLParser::NODE_ELEMENT_END && parser->get_node_name()=="object_file") + return ERR_FILE_EOF; + } + + return OK; //never reach anyway +} + +ObjectFormatLoaderXML* ObjectFormatLoaderInstancerXML::instance(const String& p_file,const String& p_magic) { + + Ref<XMLParser> parser = memnew( XMLParser ); + + Error err = parser->open(p_file); + ERR_FAIL_COND_V(err,NULL); + + ObjectFormatLoaderXML *loader = memnew( ObjectFormatLoaderXML ); + + loader->parser=parser; + loader->local_path = Globals::get_singleton()->localize_path(p_file); + + while(parser->read()==OK) { + + if (parser->get_node_type()==XMLParser::NODE_ELEMENT && parser->get_node_name()=="object_file") { + + ERR_FAIL_COND_V( parser->is_empty(), NULL ); + + String version = parser->get_attribute_value_safe("version"); + String magic = parser->get_attribute_value_safe("MAGIC"); + + if (version.get_slice_count(".")!=2) { + + ERR_EXPLAIN("Invalid Version String '"+version+"'' in file: "+p_file); + ERR_FAIL_V(NULL); + } + + int major = version.get_slice(".",0).to_int(); + int minor = version.get_slice(".",1).to_int(); + + if (major>VERSION_MAJOR || (major==VERSION_MAJOR && minor>VERSION_MINOR)) { + + ERR_EXPLAIN("File Format '"+version+"' is too new! Please upgrade to a a new engine version: "+p_file); + ERR_FAIL_V(NULL); + + } + + return loader; + } + + } + + ERR_EXPLAIN("No data found in file!"); + ERR_FAIL_V(NULL); +} + +void ObjectFormatLoaderInstancerXML::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("xml"); +} + + + +#else + +ObjectFormatLoaderXML::Tag* ObjectFormatLoaderXML::parse_tag(bool *r_exit) { + + + while(get_char()!='<' && !f->eof_reached()) {} + if (f->eof_reached()) + return NULL; + + Tag tag; + bool exit=false; + if (r_exit) + *r_exit=false; + + bool complete=false; + while(!f->eof_reached()) { + + CharType c=get_char(); + if (c<33 && tag.name.length() && !exit) { + break; + } else if (c=='>') { + complete=true; + break; + } else if (c=='/') { + exit=true; + } else { + tag.name+=c; + } + } + + if (f->eof_reached()) + return NULL; + + if (exit) { + if (!tag_stack.size()) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unmatched exit tag </"+tag.name+">"); + ERR_FAIL_COND_V(!tag_stack.size(),NULL); + } + + if (tag_stack.back()->get().name!=tag.name) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Mismatched exit tag. Got </"+tag.name+">, expected </"+tag_stack.back()->get().name+">"); + ERR_FAIL_COND_V(tag_stack.back()->get().name!=tag.name,NULL); + } + + if (!complete) { + while(get_char()!='>' && !f->eof_reached()) {} + if (f->eof_reached()) + return NULL; + } + + if (r_exit) + *r_exit=true; + + tag_stack.pop_back(); + return NULL; + + } + + if (!complete) { + String name; + String value; + bool reading_value=false; + + while(!f->eof_reached()) { + + CharType c=get_char(); + if (c=='>') { + if (value.length()) { + + tag.args[name]=value; + } + break; + + } else if ( ((!reading_value && (c<33)) || c=='=' || c=='"') && tag.name.length()) { + + if (!reading_value && name.length()) { + + reading_value=true; + } else if (reading_value && value.length()) { + + tag.args[name]=value; + name=""; + value=""; + reading_value=false; + } + + } else if (reading_value) { + + value+=c; + } else { + + name+=c; + } + } + + if (f->eof_reached()) + return NULL; + } + + tag_stack.push_back(tag); + + return &tag_stack.back()->get(); +} + + +Error ObjectFormatLoaderXML::close_tag(const String& p_name) { + + int level=0; + bool inside_tag=false; + + while(true) { + + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": EOF found while attempting to find </"+p_name+">"); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + uint8_t c = get_char(); + + if (c == '<') { + + if (inside_tag) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML. Already inside Tag."); + ERR_FAIL_COND_V(inside_tag,ERR_FILE_CORRUPT); + } + inside_tag=true; + c = get_char(); + if (c == '/') { + + --level; + } else { + + ++level; + }; + } else if (c == '>') { + + if (!inside_tag) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML. Already outside Tag"); + ERR_FAIL_COND_V(!inside_tag,ERR_FILE_CORRUPT); + } + inside_tag=false; + if (level == -1) { + tag_stack.pop_back(); + return OK; + }; + }; + } + + return OK; +} + +void ObjectFormatLoaderXML::unquote(String& p_str) { + + p_str=p_str.strip_edges(); + p_str=p_str.replace("\"",""); + p_str=p_str.replace(">","<"); + p_str=p_str.replace("<",">"); + p_str=p_str.replace("'","'"); + p_str=p_str.replace(""","\""); + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + p_str=p_str.replace("&#"+String::num(i)+";",chr); + } + p_str=p_str.replace("&","&"); + + //p_str.parse_utf8( p_str.ascii(true).get_data() ); + +} + +Error ObjectFormatLoaderXML::goto_end_of_tag() { + + uint8_t c; + while(true) { + + c=get_char(); + if (c=='>') //closetag + break; + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": EOF found while attempting to find close tag."); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + } + tag_stack.pop_back(); + + return OK; +} + + +Error ObjectFormatLoaderXML::parse_property_data(String &r_data) { + + r_data=""; + CharString cs; + while(true) { + + CharType c=get_char(); + if (c=='<') + break; + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + cs.push_back(c); + } + + cs.push_back(0); + + r_data.parse_utf8(cs.get_data()); + + while(get_char()!='>' && !f->eof_reached()) {} + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML."); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + r_data=r_data.strip_edges(); + tag_stack.pop_back(); + + return OK; +} + + +Error ObjectFormatLoaderXML::_parse_array_element(Vector<char> &buff,bool p_number_only,FileAccess *f,bool *end) { + + if (buff.empty()) + buff.resize(32); // optimize + + int buff_max=buff.size(); + int buff_size=0; + *end=false; + char *buffptr=&buff[0]; + bool found=false; + bool quoted=false; + + while(true) { + + char c=get_char(); + + if (c==0) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (zero found)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } else if (c=='"') { + quoted=!quoted; + } else if ((!quoted && ((p_number_only && c<33) || c==',')) || c=='<') { + + + if (c=='<') { + *end=true; + break; + } + if (c<32 && f->eof_reached()) { + *end=true; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (unexpected EOF)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + if (found) + break; + + } else { + + found=true; + if (buff_size>=buff_max) { + + buff_max++; + buff.resize(buff_max); + + } + + buffptr[buff_size]=c; + buff_size++; + } + } + + if (buff_size>=buff_max) { + + buff_max++; + buff.resize(buff_max); + + } + + buff[buff_size]=0; + buff_size++; + + return OK; +} + +Error ObjectFormatLoaderXML::parse_property(Variant& r_v, String &r_name) { + + bool exit; + Tag *tag = parse_tag(&exit); + + if (!tag) { + if (exit) // shouldn't have exited + return ERR_FILE_EOF; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (No Property Tag)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + r_v=Variant(); + r_name=""; + + + //ERR_FAIL_COND_V(tag->name!="property",ERR_FILE_CORRUPT); + //ERR_FAIL_COND_V(!tag->args.has("name"),ERR_FILE_CORRUPT); +// ERR_FAIL_COND_V(!tag->args.has("type"),ERR_FILE_CORRUPT); + + //String name=tag->args["name"]; + //ERR_FAIL_COND_V(name=="",ERR_FILE_CORRUPT); + String type=tag->name; + String name=tag->args["name"]; + + if (type=="") { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": 'type' field is empty."); + ERR_FAIL_COND_V(type=="",ERR_FILE_CORRUPT); + } + + if (type=="dictionary") { + + Dictionary d; + + while(true) { + + Error err; + String tagname; + Variant key; + + int dictline = get_current_line(); + + + err=parse_property(key,tagname); + + if (err && err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error parsing dictionary: "+name+" (from line "+itos(dictline)+")"); + ERR_FAIL_COND_V(err && err!=ERR_FILE_EOF,err); + } + //ERR_FAIL_COND_V(tagname!="key",ERR_FILE_CORRUPT); + if (err) + break; + Variant value; + err=parse_property(value,tagname); + if (err) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error parsing dictionary: "+name+" (from line "+itos(dictline)+")"); + } + + ERR_FAIL_COND_V(err,err); + //ERR_FAIL_COND_V(tagname!="value",ERR_FILE_CORRUPT); + + d[key]=value; + } + + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=d; + return OK; + + } else if (type=="array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + + + int len=tag->args["len"].to_int(); + + Array array; + array.resize(len); + + Error err; + Variant v; + String tagname; + int idx=0; + while( (err=parse_property(v,tagname))==OK ) { + + ERR_CONTINUE( idx <0 || idx >=len ); + + array.set(idx,v); + idx++; + } + + if (idx!=len) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array (size mismatch): "+name); + ERR_FAIL_COND_V(idx!=len,err); + } + + if (err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array: "+name); + ERR_FAIL_COND_V(err!=ERR_FILE_EOF,err); + } + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=array; + return OK; + + } else if (type=="resource") { + + if (tag->args.has("path")) { + + String path=tag->args["path"]; + String hint; + if (tag->args.has("resource_type")) + hint=tag->args["resource_type"]; + + if (path.begins_with("local://")) + path=path.replace("local://",local_path+"::"); + else if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + + } + + //take advantage of the resource loader cache. The resource is cached on it, even if + RES res=ResourceLoader::load(path,hint); + + + if (res.is_null()) { + + WARN_PRINT(String("Couldn't load resource: "+path).ascii().get_data()); + } + + r_v=res.get_ref_ptr(); + } + + + + Error err=goto_end_of_tag(); + if (err) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error closing <resource> tag."); + ERR_FAIL_COND_V(err,err); + } + + + r_name=name; + + return OK; + + } else if (type=="image") { + + if (!tag->args.has("encoding")) { + //empty image + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + } + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'encoding' field."); + ERR_FAIL_COND_V( !tag->args.has("encoding"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'width' field."); + ERR_FAIL_COND_V( !tag->args.has("width"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'height' field."); + ERR_FAIL_COND_V( !tag->args.has("height"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'format' field."); + ERR_FAIL_COND_V( !tag->args.has("format"), ERR_FILE_CORRUPT ); + + String encoding=tag->args["encoding"]; + + if (encoding=="raw") { + String width=tag->args["width"]; + String height=tag->args["height"]; + String format=tag->args["format"]; + int mipmaps=tag->args.has("mipmaps")?int(tag->args["mipmaps"].to_int()):int(0); + int custom_size = tag->args.has("custom_size")?int(tag->args["custom_size"].to_int()):int(0); + + r_name=name; + + Image::Format imgformat; + + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + } else if (format=="bc1") { + imgformat=Image::FORMAT_BC1; + } else if (format=="bc2") { + imgformat=Image::FORMAT_BC2; + } else if (format=="bc3") { + imgformat=Image::FORMAT_BC3; + } else if (format=="bc4") { + imgformat=Image::FORMAT_BC4; + } else if (format=="bc5") { + imgformat=Image::FORMAT_BC5; + } else if (format=="custom") { + imgformat=Image::FORMAT_CUSTOM; + } else { + + ERR_FAIL_V( ERR_FILE_CORRUPT ); + } + + + int datasize; + int w=width.to_int(); + int h=height.to_int(); + + if (w == 0 && w == 0) { + //r_v = Image(w, h, imgformat); + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + }; + + if (imgformat==Image::FORMAT_CUSTOM) { + + datasize=custom_size; + } else { + + datasize = Image::get_image_data_size(h,w,imgformat,mipmaps); + } + + if (datasize==0) { + //r_v = Image(w, h, imgformat); + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + }; + + DVector<uint8_t> pixels; + pixels.resize(datasize); + DVector<uint8_t>::Write wb = pixels.write(); + + int idx=0; + uint8_t byte; + while( idx<datasize*2) { + + CharType c=get_char(); + + ERR_FAIL_COND_V(c=='<',ERR_FILE_CORRUPT); + + if ( (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (idx&1) { + + byte|=HEX2CHR(c); + wb[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + } + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + wb=DVector<uint8_t>::Write(); + + r_v=Image(w,h,mipmaps,imgformat,pixels); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + + return OK; + } + + ERR_FAIL_V(ERR_FILE_CORRUPT); + + } else if (type=="raw_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": RawArray missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector<uint8_t> bytes; + bytes.resize(len); + DVector<uint8_t>::Write w=bytes.write(); + uint8_t *bytesptr=w.ptr(); + int idx=0; + uint8_t byte; + while( idx<len*2) { + + CharType c=get_char(); + + if (idx&1) { + + byte|=HEX2CHR(c); + bytesptr[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + w=DVector<uint8_t>::Write(); + r_v=bytes; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="int_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector<int> ints; + ints.resize(len); + DVector<int>::Write w=ints.write(); + int *intsptr=w.ptr(); + int idx=0; + String str; +#if 0 + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + intsptr[idx]=str.to_int(); + str=""; + idx++; + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + +#else + + Vector<char> tmpdata; + + while( idx<len ) { + + bool end=false; + Error err = _parse_array_element(tmpdata,true,f,&end); + ERR_FAIL_COND_V(err,err); + + intsptr[idx]=String::to_int(&tmpdata[0]); + idx++; + if (end) + break; + + } + +#endif + w=DVector<int>::Write(); + + r_v=ints; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } else if (type=="real_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector<real_t> reals; + reals.resize(len); + DVector<real_t>::Write w=reals.write(); + real_t *realsptr=w.ptr(); + int idx=0; + String str; + + +#if 0 + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + realsptr[idx]=str.to_double(); + str=""; + idx++; + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + +#else + + + + Vector<char> tmpdata; + + while( idx<len ) { + + bool end=false; + Error err = _parse_array_element(tmpdata,true,f,&end); + ERR_FAIL_COND_V(err,err); + + realsptr[idx]=String::to_double(&tmpdata[0]); + idx++; + + if (end) + break; + } + +#endif + + w=DVector<real_t>::Write(); + r_v=reals; + + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } else if (type=="string_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector<String> strings; + strings.resize(len); + DVector<String>::Write w=strings.write(); + String *stringsptr=w.ptr(); + int idx=0; + String str; + + bool inside_str=false; + CharString cs; + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c=='"') { + if (inside_str) { + + cs.push_back(0); + String str; + str.parse_utf8(cs.get_data()); + unquote(str); + stringsptr[idx]=str; + cs.clear(); + idx++; + inside_str=false; + } else { + inside_str=true; + } + } else if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + + + } else if (inside_str){ + + cs.push_back(c); + } + } + w=DVector<String>::Write(); + r_v=strings; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + + r_name=name; + + return OK; + } else if (type=="vector3_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector<Vector3> vectors; + vectors.resize(len); + DVector<Vector3>::Write w=vectors.write(); + Vector3 *vectorsptr=w.ptr(); + int idx=0; + int subidx=0; + Vector3 auxvec; + String str; + +// uint64_t tbegin = OS::get_singleton()->get_ticks_usec(); +#if 0 + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + auxvec[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==3) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } +#else + + Vector<char> tmpdata; + + while( idx<len ) { + + bool end=false; + Error err = _parse_array_element(tmpdata,true,f,&end); + ERR_FAIL_COND_V(err,err); + + + auxvec[subidx]=String::to_double(&tmpdata[0]); + subidx++; + if (subidx==3) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + + if (end) + break; + } + + + +#endif + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Premature end of vector3 array"); + ERR_FAIL_COND_V(idx<len,ERR_FILE_CORRUPT); +// double time_taken = (OS::get_singleton()->get_ticks_usec() - tbegin)/1000000.0; + + + w=DVector<Vector3>::Write(); + r_v=vectors; + String sdfsdfg; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="vector2_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector<Vector2> vectors; + vectors.resize(len); + DVector<Vector2>::Write w=vectors.write(); + Vector2 *vectorsptr=w.ptr(); + int idx=0; + int subidx=0; + Vector2 auxvec; + String str; + +// uint64_t tbegin = OS::get_singleton()->get_ticks_usec(); +#if 0 + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c<22 || c==',' || c=='<') { + + if (str.length()) { + + auxvec[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==2) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } +#else + + Vector<char> tmpdata; + + while( idx<len ) { + + bool end=false; + Error err = _parse_array_element(tmpdata,true,f,&end); + ERR_FAIL_COND_V(err,err); + + + auxvec[subidx]=String::to_double(&tmpdata[0]); + subidx++; + if (subidx==2) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + + if (end) + break; + } + + + +#endif + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Premature end of vector2 array"); + ERR_FAIL_COND_V(idx<len,ERR_FILE_CORRUPT); +// double time_taken = (OS::get_singleton()->get_ticks_usec() - tbegin)/1000000.0; + + + w=DVector<Vector2>::Write(); + r_v=vectors; + String sdfsdfg; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="color_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector<Color> colors; + colors.resize(len); + DVector<Color>::Write w=colors.write(); + Color *colorsptr=w.ptr(); + int idx=0; + int subidx=0; + Color auxcol; + String str; + + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + auxcol[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==4) { + colorsptr[idx]=auxcol; + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + w=DVector<Color>::Write(); + r_v=colors; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } + + + String data; + Error err = parse_property_data(data); + ERR_FAIL_COND_V(err!=OK,err); + + if (type=="nil") { + // uh do nothing + + } else if (type=="bool") { + // uh do nothing + if (data.nocasecmp_to("true")==0 || data.to_int()!=0) + r_v=true; + else + r_v=false; + } else if (type=="int") { + + r_v=data.to_int(); + } else if (type=="real") { + + r_v=data.to_double(); + } else if (type=="string") { + + String str=data; + unquote(str); + r_v=str; + } else if (type=="vector3") { + + + r_v=Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ); + + } else if (type=="vector2") { + + + r_v=Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ); + + } else if (type=="plane") { + + r_v=Plane( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="quaternion") { + + r_v=Quat( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="rect2") { + + r_v=Rect2( + Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ), + Vector2( + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ) + ); + + + } else if (type=="aabb") { + + r_v=AABB( + Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ), + Vector3( + data.get_slice(",",3).to_double(), + data.get_slice(",",4).to_double(), + data.get_slice(",",5).to_double() + ) + ); + + } else if (type=="matrix32") { + + Matrix32 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + m3.elements[i][j]=data.get_slice(",",i*2+j).to_double(); + } + } + r_v=m3; + + } else if (type=="matrix3") { + + Matrix3 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + m3.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + } + r_v=m3; + + } else if (type=="transform") { + + Transform tr; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + tr.basis.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + + } + tr.origin=Vector3( + data.get_slice(",",9).to_double(), + data.get_slice(",",10).to_double(), + data.get_slice(",",11).to_double() + ); + r_v=tr; + + } else if (type=="color") { + + r_v=Color( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="node_path") { + + String str=data; + unquote(str); + r_v=NodePath( str ); + } else if (type=="input_event") { + + // ? + } else { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unrecognized tag in file: "+type); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + r_name=name; + return OK; +} + + +Error ObjectFormatLoaderXML::load(Object **p_object,Variant &p_meta) { + + *p_object=NULL; + p_meta=Variant(); + + + + while(true) { + + + bool exit; + Tag *tag = parse_tag(&exit); + + + if (!tag) { + if (!exit) // shouldn't have exited + ERR_FAIL_V(ERR_FILE_CORRUPT); + *p_object=NULL; + return ERR_FILE_EOF; + } + + RES resource; + Object *obj=NULL; + + if (tag->name=="resource") { + //loading resource + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <resource> missing 'len' field."); + ERR_FAIL_COND_V(!tag->args.has("path"),ERR_FILE_CORRUPT); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <resource> missing 'type' field."); + ERR_FAIL_COND_V(!tag->args.has("type"),ERR_FILE_CORRUPT); + String path=tag->args["path"]; + + if (path.begins_with("local://")) { + //built-in resource (but really external) + path=path.replace("local://",local_path+"::"); + } + + + if (ResourceCache::has(path)) { + Error err = close_tag(tag->name); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unable to close <resource> tag."); + ERR_FAIL_COND_V( err, err ); + continue; //it's a resource, and it's already loaded + + } + + String type = tag->args["type"]; + + obj = ObjectTypeDB::instance(type); + if (!obj) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object of unrecognized type in file: "+type); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + + Resource *r = obj->cast_to<Resource>(); + if (!r) { + memdelete(obj); //bye + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object type in resource field not a resource, type is: "+obj->get_type()); + ERR_FAIL_COND_V(!obj->cast_to<Resource>(),ERR_FILE_CORRUPT); + } + + resource = RES( r ); + r->set_path(path); + + + + } else if (tag->name=="object") { + + if ( tag->args.has("type") ) { + + ERR_FAIL_COND_V(!ObjectTypeDB::type_exists(tag->args["type"]), ERR_FILE_CORRUPT); + + if (ObjectTypeDB::can_instance(tag->args["type"])) { + obj = ObjectTypeDB::instance(tag->args["type"]); + if (!obj) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object of unrecognized type in file: "+tag->args["type"]); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + } else { + + close_tag(tag->name); + return ERR_SKIP; + }; + } else { + //otherwise it's a meta object + } + + } else { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unknown main tag: "+tag->name); + ERR_FAIL_V( ERR_FILE_CORRUPT ); + } + + //load properties + + while(true) { + + String name; + Variant v; + Error err; + err = parse_property(v,name); + if (err==ERR_FILE_EOF) //tag closed + break; + if (err!=OK) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": XML Parsing aborted."); + ERR_FAIL_COND_V(err!=OK,ERR_FILE_CORRUPT); + } + if (resource.is_null() && name=="__xml_meta__") { + + p_meta=v; + continue; + } else if (!obj) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Normal property found in meta object."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + obj->set(name,v); + } + + if (!obj) { + *p_object=NULL; + return OK; // it was a meta object + } + + if (resource.is_null()) { + + //regular object + *p_object=obj; + return OK; + } else { + + + resource_cache.push_back(resource); //keep it in mem until finished loading + } + + // a resource.. continue! + + } + + + + return OK; //never reach anyway + +} + +int ObjectFormatLoaderXML::get_current_line() const { + + return lines; +} + + +uint8_t ObjectFormatLoaderXML::get_char() const { + + uint8_t c = f->get_8(); + if (c=='\n') + lines++; + return c; + +} + +ObjectFormatLoaderXML::~ObjectFormatLoaderXML() { + + if (f) { + if (f->is_open()) + f->close(); + memdelete(f); + } +} + + + +ObjectFormatLoaderXML* ObjectFormatLoaderInstancerXML::instance(const String& p_file,const String& p_magic) { + + Error err; + FileAccess *f=FileAccess::open(p_file,FileAccess::READ,&err); + if (err!=OK) { + + ERR_FAIL_COND_V(err!=OK,NULL); + } + + ObjectFormatLoaderXML *loader = memnew( ObjectFormatLoaderXML ); + + loader->lines=1; + loader->f=f; + loader->local_path = Globals::get_singleton()->localize_path(p_file); + + ObjectFormatLoaderXML::Tag *tag = loader->parse_tag(); + if (!tag || tag->name!="?xml" || !tag->args.has("version") || !tag->args.has("encoding") || tag->args["encoding"]!="UTF-8") { + + f->close(); + memdelete(loader); + ERR_EXPLAIN("Not a XML:UTF-8 File: "+p_file); + ERR_FAIL_V(NULL); + } + + loader->tag_stack.clear(); + + tag = loader->parse_tag(); + + if (!tag || tag->name!="object_file" || !tag->args.has("magic") || !tag->args.has("version") || tag->args["magic"]!=p_magic) { + + f->close(); + memdelete(loader); + ERR_EXPLAIN("Unrecognized XML File: "+p_file); + ERR_FAIL_V(NULL); + } + + String version = tag->args["version"]; + if (version.get_slice_count(".")!=2) { + + f->close(); + memdelete(loader); + ERR_EXPLAIN("Invalid Version String '"+version+"'' in file: "+p_file); + ERR_FAIL_V(NULL); + } + + int major = version.get_slice(".",0).to_int(); + int minor = version.get_slice(".",1).to_int(); + + if (major>VERSION_MAJOR || (major==VERSION_MAJOR && minor>VERSION_MINOR)) { + + f->close(); + memdelete(loader); + ERR_EXPLAIN("File Format '"+version+"' is too new! Please upgrade to a a new engine version: "+p_file); + ERR_FAIL_V(NULL); + + } + + return loader; +} + +void ObjectFormatLoaderInstancerXML::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("xml"); +} + + +#endif +#endif +#endif diff --git a/core/io/object_format_xml.h b/core/io/object_format_xml.h new file mode 100644 index 0000000000..1169a1de58 --- /dev/null +++ b/core/io/object_format_xml.h @@ -0,0 +1,196 @@ +/*************************************************************************/ +/* object_format_xml.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. */ +/*************************************************************************/ +#ifndef OBJECT_FORMAT_XML_H +#define OBJECT_FORMAT_XML_H + +#ifdef XML_ENABLED +#ifdef OLD_SCENE_FORMAT_ENABLED +#include "io/object_loader.h" +#include "io/object_saver.h" +#include "os/file_access.h" +#include "map.h" +#include "resource.h" +#include "xml_parser.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class ObjectFormatSaverXML : public ObjectFormatSaver { + + String local_path; + + + Ref<OptimizedSaver> optimizer; + + bool relative_paths; + bool bundle_resources; + bool skip_editor; + FileAccess *f; + String magic; + int depth; + Map<RES,int> resource_map; + + struct SavedObject { + + Variant meta; + String type; + + + struct SavedProperty { + + String name; + Variant value; + }; + + List<SavedProperty> properties; + }; + + List<RES> saved_resources; + + List<SavedObject*> saved_objects; + + void enter_tag(const String& p_section,const String& p_args=""); + void exit_tag(const String& p_section); + + void _find_resources(const Variant& p_variant); + void write_property(const String& p_name,const Variant& p_property,bool *r_ok=NULL); + + + void escape(String& p_str); + void write_tabs(int p_diff=0); + void write_string(String p_str,bool p_escape=true); + +public: + + virtual Error save(const Object *p_object,const Variant &p_meta); + + ObjectFormatSaverXML(FileAccess *p_file,const String& p_magic,const String& p_local_path,uint32_t p_flags,const Ref<OptimizedSaver>& p_optimizer); + ~ObjectFormatSaverXML(); +}; + +class ObjectFormatSaverInstancerXML : public ObjectFormatSaverInstancer { +public: + + virtual ObjectFormatSaver* instance(const String& p_file,const String& p_magic,uint32_t p_flags=0,const Ref<OptimizedSaver>& p_optimizer=Ref<OptimizedSaver>()); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + + virtual ~ObjectFormatSaverInstancerXML(); +}; + +/***********************************/ +/***********************************/ +/***********************************/ +/***********************************/ + +//#define OPTIMIZED_XML_LOADER + +#ifdef OPTIMIZED_XML_LOADER + +class ObjectFormatLoaderXML : public ObjectFormatLoader { + + Ref<XMLParser> parser; + String local_path; + + Error _close_tag(const String& p_tag); + Error _parse_property(Variant& r_property,String& r_name); + +friend class ObjectFormatLoaderInstancerXML; + + List<RES> resource_cache; +public: + + + virtual Error load(Object **p_object,Variant &p_meta); + + +}; + +class ObjectFormatLoaderInstancerXML : public ObjectFormatLoaderInstancer { +public: + + virtual ObjectFormatLoaderXML* instance(const String& p_file,const String& p_magic); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + +}; + +#else + + +class ObjectFormatLoaderXML : public ObjectFormatLoader { + + String local_path; + + FileAccess *f; + + struct Tag { + + String name; + HashMap<String,String> args; + }; + + _FORCE_INLINE_ Error _parse_array_element(Vector<char> &buff,bool p_number_only,FileAccess *f,bool *end); + + mutable int lines; + uint8_t get_char() const; + int get_current_line() const; + +friend class ObjectFormatLoaderInstancerXML; + List<Tag> tag_stack; + + List<RES> resource_cache; + Tag* parse_tag(bool* r_exit=NULL); + Error close_tag(const String& p_name); + void unquote(String& p_str); + Error goto_end_of_tag(); + Error parse_property_data(String &r_data); + Error parse_property(Variant& r_v, String &r_name); + +public: + + + virtual Error load(Object **p_object,Variant &p_meta); + + virtual ~ObjectFormatLoaderXML(); +}; + +class ObjectFormatLoaderInstancerXML : public ObjectFormatLoaderInstancer { +public: + + virtual ObjectFormatLoaderXML* instance(const String& p_file,const String& p_magic); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + + + +}; + +#endif +#endif +#endif +#endif diff --git a/core/io/object_loader.cpp b/core/io/object_loader.cpp new file mode 100644 index 0000000000..bb42cf7338 --- /dev/null +++ b/core/io/object_loader.cpp @@ -0,0 +1,84 @@ +/*************************************************************************/ +/* object_loader.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 "object_loader.h" + +#ifdef OLD_SCENE_FORMAT_ENABLED + +bool ObjectFormatLoaderInstancer::recognize(const String& p_extension) const { + + + List<String> extensions; + get_recognized_extensions(&extensions); + for (List<String>::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(p_extension)==0) + return true; + } + + return false; +} + +ObjectFormatLoaderInstancer *ObjectLoader::loader[MAX_LOADERS]; +int ObjectLoader::loader_count=0; + + +ObjectFormatLoader *ObjectLoader::instance_format_loader(const String& p_path,const String& p_magic,String p_force_extension) { + + String extension=p_force_extension.length()?p_force_extension:p_path.extension(); + + for (int i=0;i<loader_count;i++) { + + if (!loader[i]->recognize(extension)) + continue; + ObjectFormatLoader *format_loader = loader[i]->instance(p_path,p_magic); + if (format_loader) + return format_loader; + } + + return NULL; +} + +void ObjectLoader::get_recognized_extensions(List<String> *p_extensions) { + + for (int i=0;i<loader_count;i++) { + + loader[i]->get_recognized_extensions(p_extensions); + } +} + + + +void ObjectLoader::add_object_format_loader_instancer(ObjectFormatLoaderInstancer *p_format_loader_instancer) { + + ERR_FAIL_COND(loader_count>=MAX_LOADERS ); + loader[loader_count++]=p_format_loader_instancer; +} + + +#endif diff --git a/core/io/object_loader.h b/core/io/object_loader.h new file mode 100644 index 0000000000..9199313f04 --- /dev/null +++ b/core/io/object_loader.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* object_loader.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. */ +/*************************************************************************/ +#ifndef OBJECT_LOADER_H +#define OBJECT_LOADER_H + +#include "object.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +#ifdef OLD_SCENE_FORMAT_ENABLED +class ObjectFormatLoader { +public: + + virtual Error load(Object **p_object,Variant &p_meta)=0; + + virtual ~ObjectFormatLoader() {} +}; + +class ObjectFormatLoaderInstancer { +public: + + virtual ObjectFormatLoader* instance(const String& p_file,const String& p_magic)=0; + virtual void get_recognized_extensions(List<String> *p_extensions) const=0; + bool recognize(const String& p_extension) const; + + virtual ~ObjectFormatLoaderInstancer() {} +}; + +class ObjectLoader { + + enum { + MAX_LOADERS=64 + }; + + static ObjectFormatLoaderInstancer *loader[MAX_LOADERS]; + static int loader_count; + +public: + + static ObjectFormatLoader *instance_format_loader(const String& p_path,const String& p_magic,String p_force_extension=""); + static void add_object_format_loader_instancer(ObjectFormatLoaderInstancer *p_format_loader_instancer); + static void get_recognized_extensions(List<String> *p_extensions); + + + +}; + +#endif +#endif diff --git a/core/io/object_saver.cpp b/core/io/object_saver.cpp new file mode 100644 index 0000000000..cff2e836a7 --- /dev/null +++ b/core/io/object_saver.cpp @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* object_saver.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 "object_saver.h" +#ifdef OLD_SCENE_FORMAT_ENABLED + +void OptimizedSaver::add_property(const StringName& p_name, const Variant& p_value) { + + ERR_FAIL_COND(!_list); + Property p; + p.name=p_name; + p.value=p_value; + _list->push_back(p); +} + +bool OptimizedSaver::optimize_object(const Object *p_object) { + + return false; //not optimize +} + +void OptimizedSaver::get_property_list(const Object* p_object,List<Property> *p_properties) { + + + _list=p_properties; + + bool res = call("optimize_object",p_object); + + if (!res) { + + List<PropertyInfo> plist; + p_object->get_property_list(&plist); + for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) { + + PropertyInfo pinfo=E->get(); + if ((pinfo.usage&PROPERTY_USAGE_STORAGE) || (is_bundle_resources_enabled() && pinfo.usage&PROPERTY_USAGE_BUNDLE)) { + + add_property(pinfo.name,p_object->get(pinfo.name)); + } + } + + } + + _list=NULL; +} + +void OptimizedSaver::set_target_platform(const String& p_platform) { + + ERR_FAIL_COND(p_platform!="" && !p_platform.is_valid_identifier()); + platform=p_platform; +} + +String OptimizedSaver::get_target_platform() const { + + return platform; +} + +void OptimizedSaver::set_target_name(const String& p_name) { + + name=p_name; +} + +String OptimizedSaver::get_target_name() const { + + return name; +} + +void OptimizedSaver::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_target_platform","name"),&OptimizedSaver::set_target_platform); + ObjectTypeDB::bind_method(_MD("get_target_platform"),&OptimizedSaver::get_target_platform); + ObjectTypeDB::bind_method(_MD("set_target_name","name"),&OptimizedSaver::set_target_name); + ObjectTypeDB::bind_method(_MD("add_property","name","value"),&OptimizedSaver::add_property); + ObjectTypeDB::bind_method(_MD("optimize_object","obj"),&OptimizedSaver::optimize_object); +} + +OptimizedSaver::OptimizedSaver() { + + _list=NULL; +} + +ObjectFormatSaverInstancer *ObjectSaver::saver[MAX_LOADERS]; +int ObjectSaver::saver_count=0; + +bool ObjectFormatSaverInstancer::recognize(const String& p_extension) const { + + + List<String> extensions; + get_recognized_extensions(&extensions); + for (List<String>::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(p_extension.extension())==0) + return true; + } + + return false; +} + +ObjectFormatSaver *ObjectSaver::instance_format_saver(const String& p_path,const String& p_magic,String p_force_extension,uint32_t p_flags,const Ref<OptimizedSaver>& p_optimizer) { + + String extension=p_force_extension.length()?p_force_extension:p_path.extension(); + + for (int i=0;i<saver_count;i++) { + + if (!saver[i]->recognize(extension)) + continue; + ObjectFormatSaver *format_saver = saver[i]->instance(p_path,p_magic,p_flags,p_optimizer); + if (format_saver) + return format_saver; + } + + return NULL; +} + +void ObjectSaver::get_recognized_extensions(List<String> *p_extensions) { + + for (int i=0;i<saver_count;i++) { + + saver[i]->get_recognized_extensions(p_extensions); + } +} + + + +void ObjectSaver::add_object_format_saver_instancer(ObjectFormatSaverInstancer *p_format_saver_instancer) { + + ERR_FAIL_COND(saver_count>=MAX_LOADERS ); + saver[saver_count++]=p_format_saver_instancer; +} + + + +#endif diff --git a/core/io/object_saver.h b/core/io/object_saver.h new file mode 100644 index 0000000000..b22f7e05bb --- /dev/null +++ b/core/io/object_saver.h @@ -0,0 +1,128 @@ +/*************************************************************************/ +/* object_saver.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. */ +/*************************************************************************/ +#ifndef OBJECT_SAVER_H +#define OBJECT_SAVER_H + +#include "object.h" +#include "resource.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +#ifdef OLD_SCENE_FORMAT_ENABLED + +class OptimizedSaver : public Reference { + + OBJ_TYPE(OptimizedSaver,Reference); +public: + + struct Property { + + StringName name; + Variant value; + }; + +private: + + String name; + String platform; + List<Property> *_list; +protected: + + + void set_target_platform(const String& p_platform); + void set_target_name(const String& p_name); + void add_property(const StringName& p_name, const Variant& p_value); + static void _bind_methods(); + + virtual bool optimize_object(const Object *p_object); + +public: + + + virtual bool is_bundle_resources_enabled() const { return false; } + + String get_target_platform() const; + String get_target_name() const; + void get_property_list(const Object* p_object, List<Property> *p_properties); + + + OptimizedSaver(); + +}; + + +class ObjectFormatSaver { +public: + + virtual Error save(const Object *p_object,const Variant &p_meta=Variant())=0; + + virtual ~ObjectFormatSaver() {} +}; + +class ObjectFormatSaverInstancer { +public: + + virtual void get_recognized_extensions(List<String> *p_extensions) const=0; + virtual ObjectFormatSaver* instance(const String& p_file,const String& p_magic="",uint32_t p_flags=0,const Ref<OptimizedSaver>& p_optimizer=Ref<OptimizedSaver>())=0; + bool recognize(const String& p_extension) const; + + virtual ~ObjectFormatSaverInstancer() {} +}; + +class ObjectSaver { + + enum { + MAX_LOADERS=64 + }; + + static ObjectFormatSaverInstancer *saver[MAX_LOADERS]; + static int saver_count; + +public: + + enum SaverFlags { + + FLAG_RELATIVE_PATHS=1, + FLAG_BUNDLE_RESOURCES=2, + FLAG_OMIT_EDITOR_PROPERTIES=4, + FLAG_SAVE_BIG_ENDIAN=8 + }; + + + static ObjectFormatSaver *instance_format_saver(const String& p_path,const String& p_magic,String p_force_extension="",uint32_t p_flags=0,const Ref<OptimizedSaver>& p_optimizer=Ref<OptimizedSaver>()); + static void get_recognized_extensions(List<String> *p_extensions); + + static void add_object_format_saver_instancer(ObjectFormatSaverInstancer *p_format_saver_instancer); + + +}; + +#endif +#endif diff --git a/core/io/object_saver_base.cpp b/core/io/object_saver_base.cpp new file mode 100644 index 0000000000..94d715de28 --- /dev/null +++ b/core/io/object_saver_base.cpp @@ -0,0 +1,150 @@ +/*************************************************************************/ +/* object_saver_base.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 "object_saver_base.h" +#ifdef OLD_SCENE_FORMAT_ENABLED +void ObjectSaverBase::_find_resources(const Variant& p_variant) { + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null() || (res->get_path().length() && res->get_path().find("::") == -1 )) + return; + + if (resource_map.has(res)) + return; + + List<PropertyInfo> property_list; + + res->get_property_list( &property_list ); + + List<PropertyInfo>::Element *I=property_list.front(); + + while(I) { + + PropertyInfo pi=I->get(); + + if (pi.usage&PROPERTY_USAGE_STORAGE) { + + if (pi.type==Variant::OBJECT) { + + Variant v=res->get(I->get().name); + _find_resources(v); + } + } + + I=I->next(); + } + + resource_map[ res ] = resource_map.size(); //saved after, so the childs it needs are available when loaded + saved_resources.push_back(res); + + } break; + + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i<len;i++) { + + Variant v=varray.get(i); + _find_resources(v); + } + + } break; + + case Variant::DICTIONARY: { + + Dictionary d=p_variant; + List<Variant> keys; + d.get_key_list(&keys); + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} + + +Error ObjectSaverBase::save(const Object *p_object,const Variant &p_meta) { + + ERR_EXPLAIN("write_object should supply either an object, a meta, or both"); + ERR_FAIL_COND_V(!p_object && p_meta.get_type()==Variant::NIL, ERR_INVALID_PARAMETER); + + SavedObject *so = memnew( SavedObject ); + + if (p_object) { + so->type=p_object->get_type(); + }; + + _find_resources(p_meta); + so->meta=p_meta; + + if (p_object) { + + List<PropertyInfo> property_list; + p_object->get_property_list( &property_list ); + + List<PropertyInfo>::Element *I=property_list.front(); + + while(I) { + + if (I->get().usage&PROPERTY_USAGE_STORAGE) { + + SavedObject::SavedProperty sp; + sp.name=I->get().name; + sp.value = p_object->get(I->get().name); + _find_resources(sp.value); + so->properties.push_back(sp); + } + + I=I->next(); + } + + } + + saved_objects.push_back(so); + + return OK; +} + +ObjectSaverBase::ObjectSaverBase() { + +}; + +ObjectSaverBase::~ObjectSaverBase() { + +}; +#endif diff --git a/core/io/object_saver_base.h b/core/io/object_saver_base.h new file mode 100644 index 0000000000..d9ec4a3aba --- /dev/null +++ b/core/io/object_saver_base.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* object_saver_base.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. */ +/*************************************************************************/ +#ifndef OBJECT_SAVER_BASE_H +#define OBJECT_SAVER_BASE_H + + +#ifdef OLD_SCENE_FORMAT_ENABLED +#include "object_saver.h" + +#include "map.h" +#include "resource.h" + +class ObjectSaverBase : public ObjectFormatSaver { + +protected: + + Map<RES,int> resource_map; + + struct SavedObject { + + Variant meta; + String type; + + + struct SavedProperty { + + String name; + Variant value; + }; + + List<SavedProperty> properties; + }; + + List<RES> saved_resources; + + List<SavedObject*> saved_objects; + + void _find_resources(const Variant& p_variant); + + virtual Error write()=0; +public: + + virtual Error save(const Object *p_object,const Variant &p_meta); + + ObjectSaverBase(); + ~ObjectSaverBase(); +}; + +#endif +#endif // OBJECT_SAVER_BASE_H diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp new file mode 100644 index 0000000000..f67a10df2e --- /dev/null +++ b/core/io/packet_peer.cpp @@ -0,0 +1,255 @@ +/*************************************************************************/ +/* packet_peer.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 "packet_peer.h" + +#include "io/marshalls.h" +#include "globals.h" +/* helpers / binders */ + + + +PacketPeer::PacketPeer() { + + +} + +Error PacketPeer::get_packet_buffer(DVector<uint8_t> &r_buffer) const { + + const uint8_t *buffer; + int buffer_size; + Error err = get_packet(&buffer,buffer_size); + if (err) + return err; + + r_buffer.resize(buffer_size); + if (buffer_size==0) + return OK; + + DVector<uint8_t>::Write w = r_buffer.write(); + for(int i=0;i<buffer_size;i++) + w[i]=buffer[i]; + + return OK; + +} + +Error PacketPeer::put_packet_buffer(const DVector<uint8_t> &p_buffer) { + + int len = p_buffer.size(); + if (len==0) + return OK; + + DVector<uint8_t>::Read r = p_buffer.read(); + return put_packet(&r[0],len); + +} + +Error PacketPeer::get_var(Variant &r_variant) const { + + const uint8_t *buffer; + int buffer_size; + Error err = get_packet(&buffer,buffer_size); + if (err) + return err; + + return decode_variant(r_variant,buffer,buffer_size); + +} + +Error PacketPeer::put_var(const Variant& p_packet) { + + int len; + Error err = encode_variant(p_packet,NULL,len); // compute len first + if (err) + return err; + + if (len==0) + return OK; + + uint8_t *buf = (uint8_t*)alloca(len); + ERR_FAIL_COND_V(!buf,ERR_OUT_OF_MEMORY); + err = encode_variant(p_packet,buf,len); + ERR_FAIL_COND_V(err, err); + + return put_packet(buf, len); + +} + +Variant PacketPeer::_bnd_get_var() const { + Variant var; + get_var(var); + + return var; +}; + +void PacketPeer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("get_var"),&PacketPeer::_bnd_get_var); + ObjectTypeDB::bind_method(_MD("put_var", "var:Variant"),&PacketPeer::put_var); +}; + +/***************/ + + +void PacketPeerStream::_set_stream_peer(REF p_peer) { + + ERR_FAIL_COND(p_peer.is_null()); + set_stream_peer(p_peer); +} + +void PacketPeerStream::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_stream_peer","peer:StreamPeer"),&PacketPeerStream::_set_stream_peer); +} + +Error PacketPeerStream::_poll_buffer() const { + + ERR_FAIL_COND_V(peer.is_null(),ERR_UNCONFIGURED); + + int read = 0; + Error err = peer->get_partial_data(&temp_buffer[0], ring_buffer.space_left(), read); + if (err) + return err; + + if (read==0) + return OK; + + int w = ring_buffer.write(&temp_buffer[0],read); + ERR_FAIL_COND_V(w!=read,ERR_BUG); + + return OK; +} + +int PacketPeerStream::get_available_packet_count() const { + + _poll_buffer(); + + uint32_t remaining = ring_buffer.data_left(); + + int ofs=0; + int count=0; + + while(remaining>=4) { + + uint8_t lbuf[4]; + ring_buffer.copy(lbuf,ofs,4); + uint32_t len = decode_uint32(lbuf); + remaining-=4; + ofs+=4; + if (len>remaining) + break; + remaining-=len; + ofs+=len; + count++; + } + + return count; +} + +Error PacketPeerStream::get_packet(const uint8_t **r_buffer,int &r_buffer_size) const { + + ERR_FAIL_COND_V(peer.is_null(),ERR_UNCONFIGURED); + _poll_buffer(); + + int remaining = ring_buffer.data_left(); + ERR_FAIL_COND_V(remaining<4,ERR_UNAVAILABLE); + uint8_t lbuf[4]; + ring_buffer.copy(lbuf,0,4); + remaining-=4; + uint32_t len = decode_uint32(lbuf); + ERR_FAIL_COND_V(remaining<(int)len,ERR_UNAVAILABLE); + + ring_buffer.read(lbuf,4); //get rid of first 4 bytes + ring_buffer.read(&temp_buffer[0],len); // read packet + + *r_buffer=&temp_buffer[0]; + r_buffer_size=len; + return OK; + +} + +Error PacketPeerStream::put_packet(const uint8_t *p_buffer,int p_buffer_size) { + + ERR_FAIL_COND_V(peer.is_null(),ERR_UNCONFIGURED); + Error err = _poll_buffer(); //won't hurt to poll here too + + if (err) + return err; + + if (p_buffer_size==0) + return OK; + + ERR_FAIL_COND_V( p_buffer_size<0, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V( p_buffer_size+4 > temp_buffer.size(), ERR_INVALID_PARAMETER ); + + encode_uint32(p_buffer_size,&temp_buffer[0]); + uint8_t *dst=&temp_buffer[4]; + for(int i=0;i<p_buffer_size;i++) + dst[i]=p_buffer[i]; + + return peer->put_data(&temp_buffer[0],p_buffer_size+4); +} + +int PacketPeerStream::get_max_packet_size() const { + + + return temp_buffer.size(); +} + +void PacketPeerStream::set_stream_peer(const Ref<StreamPeer> &p_peer) { + + ERR_FAIL_COND(p_peer.is_null()); + + if (p_peer.ptr() != peer.ptr()) { + ring_buffer.advance_read(ring_buffer.data_left()); // reset the ring buffer + }; + + peer=p_peer; +} + +void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { + + //warning may lose packets + ERR_EXPLAIN("Buffer in use, resizing would cause loss of data"); + ERR_FAIL_COND(ring_buffer.data_left()); + ring_buffer.resize(nearest_shift(p_max_size+4)); + temp_buffer.resize(nearest_power_of_2(p_max_size+4)); + +} + +PacketPeerStream::PacketPeerStream() { + + + int rbsize=GLOBAL_DEF( "core/packet_stream_peer_max_buffer_po2",(16)); + + ring_buffer.resize(rbsize); + temp_buffer.resize(1<<rbsize); + + +} diff --git a/core/io/packet_peer.h b/core/io/packet_peer.h new file mode 100644 index 0000000000..e9d6be4fb9 --- /dev/null +++ b/core/io/packet_peer.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* packet_peer.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. */ +/*************************************************************************/ +#ifndef PACKET_PEER_H +#define PACKET_PEER_H + +#include "object.h" +#include "io/stream_peer.h" +#include "ring_buffer.h" +class PacketPeer : public Reference { + + OBJ_TYPE( PacketPeer, Reference ); + + Variant _bnd_get_var() const; + void _bnd_put_var(const Variant& p_var); + + static void _bind_methods(); + +public: + + virtual int get_available_packet_count() const=0; + virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const=0; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size)=0; + + virtual int get_max_packet_size() const=0; + + /* helpers / binders */ + + virtual Error get_packet_buffer(DVector<uint8_t> &r_buffer) const; + virtual Error put_packet_buffer(const DVector<uint8_t> &p_buffer); + + virtual Error get_var(Variant &r_variant) const; + virtual Error put_var(const Variant& p_packet); + + PacketPeer(); + ~PacketPeer(){} +}; + +class PacketPeerStream : public PacketPeer { + + OBJ_TYPE(PacketPeerStream,PacketPeer); + + //the way the buffers work sucks, will change later + + mutable Ref<StreamPeer> peer; + mutable RingBuffer<uint8_t> ring_buffer; + mutable Vector<uint8_t> temp_buffer; + + Error _poll_buffer() const; +protected: + + void _set_stream_peer(REF p_peer); + static void _bind_methods(); +public: + + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const; + virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size); + + virtual int get_max_packet_size() const; + + void set_stream_peer(const Ref<StreamPeer>& p_peer); + void set_input_buffer_max_size(int p_max_size); + PacketPeerStream(); + +}; + + +#endif // PACKET_STREAM_H diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp new file mode 100644 index 0000000000..d2461498a6 --- /dev/null +++ b/core/io/resource_format_binary.cpp @@ -0,0 +1,1918 @@ +/*************************************************************************/ +/* resource_format_binary.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 "resource_format_binary.h" +#include "globals.h" +#include "io/file_access_compressed.h" +#include "io/marshalls.h" +//#define print_bl(m_what) print_line(m_what) +#define print_bl(m_what) + + +enum { + + //numbering must be different from variant, in case new variant types are added (variant must be always contiguous for jumptable optimization) + VARIANT_NIL=1, + VARIANT_BOOL=2, + VARIANT_INT=3, + VARIANT_REAL=4, + VARIANT_STRING=5, + VARIANT_VECTOR2=10, + VARIANT_RECT2=11, + VARIANT_VECTOR3=12, + VARIANT_PLANE=13, + VARIANT_QUAT=14, + VARIANT_AABB=15, + VARIANT_MATRIX3=16, + VARIANT_TRANSFORM=17, + VARIANT_MATRIX32=18, + VARIANT_COLOR=20, + VARIANT_IMAGE=21, + VARIANT_NODE_PATH=22, + VARIANT_RID=23, + VARIANT_OBJECT=24, + VARIANT_INPUT_EVENT=25, + VARIANT_DICTIONARY=26, + VARIANT_ARRAY=30, + VARIANT_RAW_ARRAY=31, + VARIANT_INT_ARRAY=32, + VARIANT_REAL_ARRAY=33, + VARIANT_STRING_ARRAY=34, + VARIANT_VECTOR3_ARRAY=35, + VARIANT_COLOR_ARRAY=36, + VARIANT_VECTOR2_ARRAY=37, + + IMAGE_ENCODING_EMPTY=0, + IMAGE_ENCODING_RAW=1, + IMAGE_ENCODING_LOSSLESS=2, + IMAGE_ENCODING_LOSSY=3, + + IMAGE_FORMAT_GRAYSCALE=0, + IMAGE_FORMAT_INTENSITY=1, + IMAGE_FORMAT_GRAYSCALE_ALPHA=2, + IMAGE_FORMAT_RGB=3, + IMAGE_FORMAT_RGBA=4, + IMAGE_FORMAT_INDEXED=5, + IMAGE_FORMAT_INDEXED_ALPHA=6, + IMAGE_FORMAT_BC1=7, + IMAGE_FORMAT_BC2=8, + IMAGE_FORMAT_BC3=9, + IMAGE_FORMAT_BC4=10, + IMAGE_FORMAT_BC5=11, + IMAGE_FORMAT_PVRTC2=12, + IMAGE_FORMAT_PVRTC2_ALPHA=13, + IMAGE_FORMAT_PVRTC4=14, + IMAGE_FORMAT_PVRTC4_ALPHA=15, + IMAGE_FORMAT_ETC=16, + IMAGE_FORMAT_CUSTOM=30, + + + OBJECT_EMPTY=0, + OBJECT_EXTERNAL_RESOURCE=1, + OBJECT_INTERNAL_RESOURCE=2, + FORMAT_VERSION=0 + + +}; + + +void ResourceInteractiveLoaderBinary::_advance_padding(uint32_t p_len) { + + uint32_t extra = 4-(p_len%4); + if (extra<4) { + for(uint32_t i=0;i<extra;i++) + f->get_8(); //pad to 32 + } + +} + +Error ResourceInteractiveLoaderBinary::parse_variant(Variant& r_v) { + + + uint32_t type = f->get_32(); + print_bl("find property of type: "+itos(type)); + + + switch(type) { + + case VARIANT_NIL: { + + r_v=Variant(); + } break; + case VARIANT_BOOL: { + + r_v=bool(f->get_32()); + } break; + case VARIANT_INT: { + + r_v=int(f->get_32()); + } break; + case VARIANT_REAL: { + + r_v=f->get_real(); + } break; + case VARIANT_STRING: { + + r_v=get_unicode_string(); + } break; + case VARIANT_VECTOR2: { + + Vector2 v; + v.x=f->get_real(); + v.y=f->get_real(); + r_v=v; + + } break; + case VARIANT_RECT2: { + + Rect2 v; + v.pos.x=f->get_real(); + v.pos.y=f->get_real(); + v.size.x=f->get_real(); + v.size.y=f->get_real(); + r_v=v; + + } break; + case VARIANT_VECTOR3: { + + Vector3 v; + v.x=f->get_real(); + v.y=f->get_real(); + v.z=f->get_real(); + r_v=v; + } break; + case VARIANT_PLANE: { + + Plane v; + v.normal.x=f->get_real(); + v.normal.y=f->get_real(); + v.normal.z=f->get_real(); + v.d=f->get_real(); + r_v=v; + } break; + case VARIANT_QUAT: { + Quat v; + v.x=f->get_real(); + v.y=f->get_real(); + v.z=f->get_real(); + v.w=f->get_real(); + r_v=v; + + } break; + case VARIANT_AABB: { + + AABB v; + v.pos.x=f->get_real(); + v.pos.y=f->get_real(); + v.pos.z=f->get_real(); + v.size.x=f->get_real(); + v.size.y=f->get_real(); + v.size.z=f->get_real(); + r_v=v; + + } break; + case VARIANT_MATRIX32: { + + Matrix32 v; + v.elements[0].x=f->get_real(); + v.elements[0].y=f->get_real(); + v.elements[1].x=f->get_real(); + v.elements[1].y=f->get_real(); + v.elements[2].x=f->get_real(); + v.elements[2].y=f->get_real(); + r_v=v; + + } break; + case VARIANT_MATRIX3: { + + Matrix3 v; + v.elements[0].x=f->get_real(); + v.elements[0].y=f->get_real(); + v.elements[0].z=f->get_real(); + v.elements[1].x=f->get_real(); + v.elements[1].y=f->get_real(); + v.elements[1].z=f->get_real(); + v.elements[2].x=f->get_real(); + v.elements[2].y=f->get_real(); + v.elements[2].z=f->get_real(); + r_v=v; + + } break; + case VARIANT_TRANSFORM: { + + Transform v; + v.basis.elements[0].x=f->get_real(); + v.basis.elements[0].y=f->get_real(); + v.basis.elements[0].z=f->get_real(); + v.basis.elements[1].x=f->get_real(); + v.basis.elements[1].y=f->get_real(); + v.basis.elements[1].z=f->get_real(); + v.basis.elements[2].x=f->get_real(); + v.basis.elements[2].y=f->get_real(); + v.basis.elements[2].z=f->get_real(); + v.origin.x=f->get_real(); + v.origin.y=f->get_real(); + v.origin.z=f->get_real(); + r_v=v; + } break; + case VARIANT_COLOR: { + + Color v; + v.r=f->get_real(); + v.g=f->get_real(); + v.b=f->get_real(); + v.a=f->get_real(); + r_v=v; + + } break; + case VARIANT_IMAGE: { + + + uint32_t encoding = f->get_32(); + if (encoding==IMAGE_ENCODING_EMPTY) { + r_v=Variant(); + break; + } else if (encoding==IMAGE_ENCODING_RAW) { + uint32_t width = f->get_32(); + uint32_t height = f->get_32(); + uint32_t mipmaps = f->get_32(); + uint32_t format = f->get_32(); + Image::Format fmt; + switch(format) { + + case IMAGE_FORMAT_GRAYSCALE: { fmt=Image::FORMAT_GRAYSCALE; } break; + case IMAGE_FORMAT_INTENSITY: { fmt=Image::FORMAT_INTENSITY; } break; + case IMAGE_FORMAT_GRAYSCALE_ALPHA: { fmt=Image::FORMAT_GRAYSCALE_ALPHA; } break; + case IMAGE_FORMAT_RGB: { fmt=Image::FORMAT_RGB; } break; + case IMAGE_FORMAT_RGBA: { fmt=Image::FORMAT_RGBA; } break; + case IMAGE_FORMAT_INDEXED: { fmt=Image::FORMAT_INDEXED; } break; + case IMAGE_FORMAT_INDEXED_ALPHA: { fmt=Image::FORMAT_INDEXED_ALPHA; } break; + case IMAGE_FORMAT_BC1: { fmt=Image::FORMAT_BC1; } break; + case IMAGE_FORMAT_BC2: { fmt=Image::FORMAT_BC2; } break; + case IMAGE_FORMAT_BC3: { fmt=Image::FORMAT_BC3; } break; + case IMAGE_FORMAT_BC4: { fmt=Image::FORMAT_BC4; } break; + case IMAGE_FORMAT_BC5: { fmt=Image::FORMAT_BC5; } break; + case IMAGE_FORMAT_PVRTC2: { fmt=Image::FORMAT_PVRTC2; } break; + case IMAGE_FORMAT_PVRTC2_ALPHA: { fmt=Image::FORMAT_PVRTC2_ALPHA; } break; + case IMAGE_FORMAT_PVRTC4: { fmt=Image::FORMAT_PVRTC4; } break; + case IMAGE_FORMAT_PVRTC4_ALPHA: { fmt=Image::FORMAT_PVRTC4_ALPHA; } break; + case IMAGE_FORMAT_ETC: { fmt=Image::FORMAT_ETC; } break; + case IMAGE_FORMAT_CUSTOM: { fmt=Image::FORMAT_CUSTOM; } break; + default: { + + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + } + + + uint32_t datalen = f->get_32(); + + DVector<uint8_t> imgdata; + imgdata.resize(datalen); + DVector<uint8_t>::Write w = imgdata.write(); + f->get_buffer(w.ptr(),datalen); + _advance_padding(datalen); + w=DVector<uint8_t>::Write(); + + r_v=Image(width,height,mipmaps,fmt,imgdata); + + } else { + //compressed + DVector<uint8_t> data; + data.resize(f->get_32()); + DVector<uint8_t>::Write w = data.write(); + f->get_buffer(w.ptr(),data.size()); + w = DVector<uint8_t>::Write(); + + Image img; + + if (encoding==IMAGE_ENCODING_LOSSY && Image::lossy_unpacker) { + + img = Image::lossy_unpacker(data); + } else if (encoding==IMAGE_ENCODING_LOSSLESS && Image::lossless_unpacker) { + + img = Image::lossless_unpacker(data); + } + _advance_padding(data.size()); + + + r_v=img; + + } + + } break; + case VARIANT_NODE_PATH: { + + Vector<StringName> names; + Vector<StringName> subnames; + StringName property; + bool absolute; + + int name_count = f->get_16(); + uint32_t subname_count = f->get_16(); + absolute=subname_count&0x8000; + subname_count&=0x7FFF; + + + for(int i=0;i<name_count;i++) + names.push_back(string_map[f->get_32()]); + for(uint32_t i=0;i<subname_count;i++) + subnames.push_back(string_map[f->get_32()]); + property=string_map[f->get_32()]; + + NodePath np = NodePath(names,subnames,absolute,property); + //print_line("got path: "+String(np)); + + r_v=np; + + } break; + case VARIANT_RID: { + + r_v=f->get_32(); + } break; + case VARIANT_OBJECT: { + + uint32_t type=f->get_32(); + + switch(type) { + + case OBJECT_EMPTY: { + //do none + + } break; + case OBJECT_INTERNAL_RESOURCE: { + uint32_t index=f->get_32(); + String path = res_path+"::"+itos(index); + RES res = ResourceLoader::load(path); + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: "+path).utf8().get_data()); + } + r_v=res; + + } break; + case OBJECT_EXTERNAL_RESOURCE: { + + String type = get_unicode_string(); + String path = get_unicode_string(); + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(res_path.get_base_dir()+"/"+path); + + } + + RES res=ResourceLoader::load(path,type); + + if (res.is_null()) { + WARN_PRINT(String("Couldn't load resource: "+path).utf8().get_data()); + } + r_v=res; + + } break; + default: { + + ERR_FAIL_V(ERR_FILE_CORRUPT); + } break; + } + + } break; + case VARIANT_INPUT_EVENT: { + + } break; + case VARIANT_DICTIONARY: { + + uint32_t len=f->get_32(); + Dictionary d(len&0x80000000); //last bit means shared + len&=0x7FFFFFFF; + for(uint32_t i=0;i<len;i++) { + Variant key; + Error err = parse_variant(key); + ERR_FAIL_COND_V(err,ERR_FILE_CORRUPT); + Variant value; + err = parse_variant(value); + ERR_FAIL_COND_V(err,ERR_FILE_CORRUPT); + d[key]=value; + } + r_v=d; + } break; + case VARIANT_ARRAY: { + + uint32_t len=f->get_32(); + Array a(len&0x80000000); //last bit means shared + len&=0x7FFFFFFF; + a.resize(len); + for(uint32_t i=0;i<len;i++) { + Variant val; + Error err = parse_variant(val); + ERR_FAIL_COND_V(err,ERR_FILE_CORRUPT); + a[i]=val; + } + r_v=a; + + } break; + case VARIANT_RAW_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<uint8_t> array; + array.resize(len); + DVector<uint8_t>::Write w = array.write(); + f->get_buffer(w.ptr(),len); + _advance_padding(len); + w=DVector<uint8_t>::Write(); + r_v=array; + + } break; + case VARIANT_INT_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<int> array; + array.resize(len); + DVector<int>::Write w = array.write(); + f->get_buffer((uint8_t*)w.ptr(),len*4); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i<len;i++) { + + ptr[i]=BSWAP32(ptr[i]); + } + } + +#endif + w=DVector<int>::Write(); + r_v=array; + } break; + case VARIANT_REAL_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<real_t> array; + array.resize(len); + DVector<real_t>::Write w = array.write(); + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i<len;i++) { + + ptr[i]=BSWAP32(ptr[i]); + } + } + +#endif + + w=DVector<real_t>::Write(); + r_v=array; + } break; + case VARIANT_STRING_ARRAY: { + + uint32_t len = f->get_32(); + DVector<String> array; + array.resize(len); + DVector<String>::Write w = array.write(); + for(uint32_t i=0;i<len;i++) + w[i]=get_unicode_string(); + w=DVector<String>::Write(); + r_v=array; + + + } break; + case VARIANT_VECTOR2_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<Vector2> array; + array.resize(len); + DVector<Vector2>::Write w = array.write(); + if (sizeof(Vector2)==8) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*2); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i<len*2;i++) { + + ptr[i]=BSWAP32(ptr[i]); + } + } + +#endif + + } else { + ERR_EXPLAIN("Vector2 size is NOT 8!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector<Vector2>::Write(); + r_v=array; + + } break; + case VARIANT_VECTOR3_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<Vector3> array; + array.resize(len); + DVector<Vector3>::Write w = array.write(); + if (sizeof(Vector3)==12) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*3); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i<len*3;i++) { + + ptr[i]=BSWAP32(ptr[i]); + } + } + +#endif + + } else { + ERR_EXPLAIN("Vector3 size is NOT 12!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector<Vector3>::Write(); + r_v=array; + + } break; + case VARIANT_COLOR_ARRAY: { + + uint32_t len = f->get_32(); + + DVector<Color> array; + array.resize(len); + DVector<Color>::Write w = array.write(); + if (sizeof(Color)==16) { + f->get_buffer((uint8_t*)w.ptr(),len*sizeof(real_t)*4); +#ifdef BIG_ENDIAN_ENABLED + { + uint32_t *ptr=(uint32_t*)w.ptr(); + for(int i=0;i<len*4;i++) { + + ptr[i]=BSWAP32(ptr[i]); + } + } + +#endif + + } else { + ERR_EXPLAIN("Color size is NOT 16!"); + ERR_FAIL_V(ERR_UNAVAILABLE); + } + w=DVector<Color>::Write(); + r_v=array; + } break; + + default: { + ERR_FAIL_V(ERR_FILE_CORRUPT); + } break; + } + + + + return OK; //never reach anyway + +} + + +void ResourceInteractiveLoaderBinary::set_local_path(const String& p_local_path) { + + res_path=p_local_path; +} + +Ref<Resource> ResourceInteractiveLoaderBinary::get_resource(){ + + + return resource; +} +Error ResourceInteractiveLoaderBinary::poll(){ + + if (error!=OK) + return error; + + + int s = stage; + + if (s<external_resources.size()) { + + RES res = ResourceLoader::load(external_resources[s].path,external_resources[s].type); + if (res.is_null()) { + + if (!ResourceLoader::get_abort_on_missing_resources()) { + + ResourceLoader::notify_load_error("Resource Not Found: "+external_resources[s].path); + } else { + + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN("Can't load dependency: "+external_resources[s].path); + ERR_FAIL_V(error); + } + + } else { + resource_cache.push_back(res); + } + + stage++; + return OK; + } + + s-=external_resources.size(); + + + if (s>=internal_resources.size()) { + + error=ERR_BUG; + ERR_FAIL_COND_V(s>=internal_resources.size(),error); + } + + bool main = s==(internal_resources.size()-1); + + //maybe it is loaded already + String path; + + + + if (!main) { + + path=internal_resources[s].path; + if (path.begins_with("local://")) + path=path.replace("local://",res_path+"::"); + + + + if (ResourceCache::has(path)) { + //already loaded, don't do anything + stage++; + error=OK; + return error; + } + } else { + + path=res_path; + } + + uint64_t offset = internal_resources[s].offset; + + f->seek(offset); + + String t = get_unicode_string(); + + Object *obj = ObjectTypeDB::instance(t); + if (!obj) { + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":Resource of unrecognized type in file: "+t); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + + Resource *r = obj->cast_to<Resource>(); + if (!r) { + error=ERR_FILE_CORRUPT; + memdelete(obj); //bye + ERR_EXPLAIN(local_path+":Resoucre type in resource field not a resource, type is: "+obj->get_type()); + ERR_FAIL_COND_V(!r,ERR_FILE_CORRUPT); + } + + RES res = RES( r ); + + r->set_path(path); + + int pc = f->get_32(); + + //set properties + + for(int i=0;i<pc;i++) { + + uint32_t name_idx = f->get_32(); + if (name_idx>=(uint32_t)string_map.size()) { + error=ERR_FILE_CORRUPT; + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + Variant value; + + error = parse_variant(value); + if (error) + return error; + + res->set(string_map[name_idx],value); + } +#ifdef TOOLS_ENABLED + res->set_edited(false); +#endif + stage++; + + resource_cache.push_back(res); + + if (main) { + if (importmd_ofs) { + + f->seek(importmd_ofs); + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + imd->set_editor(get_unicode_string()); + int sc = f->get_32(); + for(int i=0;i<sc;i++) { + + String src = get_unicode_string(); + String md5 = get_unicode_string(); + imd->add_source(src,md5); + } + int pc = f->get_32(); + + for(int i=0;i<pc;i++) { + + String name = get_unicode_string(); + Variant val; + parse_variant(val); + imd->set_option(name,val); + } + res->set_import_metadata(imd); + + } + f->close(); + resource=res; + error=ERR_FILE_EOF; + + } else { + error=OK; + } + + return OK; + +} +int ResourceInteractiveLoaderBinary::get_stage() const{ + + return stage; +} +int ResourceInteractiveLoaderBinary::get_stage_count() const { + + return external_resources.size()+internal_resources.size(); +} + +String ResourceInteractiveLoaderBinary::get_unicode_string() { + + int len = f->get_32(); + if (len>str_buf.size()) { + str_buf.resize(len); + } + f->get_buffer((uint8_t*)&str_buf[0],len); + String s; + s.parse_utf8(&str_buf[0]); + return s; +} + + + +void ResourceInteractiveLoaderBinary::get_dependencies(FileAccess *p_f,List<String> *p_dependencies) { + + open(p_f); + if (error) + return; + + for(int i=0;i<external_resources.size();i++) { + + p_dependencies->push_back(external_resources[i].path); + } + +} + + + + +void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) { + + + error=OK; + + f=p_f; + uint8_t header[4]; + f->get_buffer(header,4); + if (header[0]=='R' && header[1]=='S' && header[2]=='C' && header[3]=='C') { + //compressed + FileAccessCompressed *fac = memnew( FileAccessCompressed ); + fac->open_after_magic(f); + f=fac; + + } else if (header[0]!='R' || header[1]!='S' || header[2]!='R' || header[3]!='C') { + //not normal + + error=ERR_FILE_UNRECOGNIZED; + ERR_EXPLAIN("Unrecognized binary resource file: "+local_path); + ERR_FAIL_V(); + } + + bool big_endian = f->get_32(); +#ifdef BIG_ENDIAN_ENABLED + endian_swap = !big_endian; +#else + bool endian_swap = big_endian; +#endif + + bool use_real64 = f->get_32(); + + f->set_endian_swap(big_endian!=0); //read big endian if saved as big endian + + uint32_t ver_major=f->get_32(); + uint32_t ver_minor=f->get_32(); + uint32_t ver_format=f->get_32(); + + print_bl("big endian: "+itos(big_endian)); + print_bl("endian swap: "+itos(endian_swap)); + print_bl("real64: "+itos(use_real64)); + print_bl("major: "+itos(ver_major)); + print_bl("minor: "+itos(ver_minor)); + print_bl("format: "+itos(ver_format)); + + if (ver_format<FORMAT_VERSION || ver_major>VERSION_MAJOR || (ver_major==VERSION_MAJOR && ver_minor>VERSION_MINOR)) { + + f->close(); + ERR_EXPLAIN("File Format '"+itos(FORMAT_VERSION)+"."+itos(ver_major)+"."+itos(ver_minor)+"' is too new! Please upgrade to a a new engine version: "+local_path); + ERR_FAIL(); + + } + + type=get_unicode_string(); + + print_bl("type: "+type); + + importmd_ofs = f->get_64(); + for(int i=0;i<14;i++) + f->get_32(); //skip a few reserved fields + + uint32_t string_table_size=f->get_32(); + string_map.resize(string_table_size); + for(uint32_t i=0;i<string_table_size;i++) { + + StringName s = get_unicode_string(); + string_map[i]=s; + } + + print_bl("strings: "+itos(string_table_size)); + + uint32_t ext_resources_size=f->get_32(); + for(uint32_t i=0;i<ext_resources_size;i++) { + + ExtResoucre er; + er.type=get_unicode_string(); + er.path=get_unicode_string(); + external_resources.push_back(er); + + } + + print_bl("ext resources: "+itos(ext_resources_size)); + uint32_t int_resources_size=f->get_32(); + + for(uint32_t i=0;i<int_resources_size;i++) { + + IntResoucre ir; + ir.path=get_unicode_string(); + ir.offset=f->get_64(); + internal_resources.push_back(ir); + } + + print_bl("int resources: "+itos(int_resources_size)); + + + if (f->eof_reached()) { + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN("Premature End Of File: "+local_path); + ERR_FAIL(); + } + +} + +String ResourceInteractiveLoaderBinary::recognize(FileAccess *p_f) { + + error=OK; + + + f=p_f; + uint8_t header[4]; + f->get_buffer(header,4); + if (header[0]=='R' && header[1]=='S' && header[2]=='C' && header[3]=='C') { + //compressed + FileAccessCompressed *fac = memnew( FileAccessCompressed ); + fac->open_after_magic(f); + f=fac; + + } else if (header[0]!='R' || header[1]!='S' || header[2]!='R' || header[3]!='C') { + //not normal + return ""; + } + + bool big_endian = f->get_32(); +#ifdef BIG_ENDIAN_ENABLED + endian_swap = !big_endian; +#else + bool endian_swap = big_endian; +#endif + + bool use_real64 = f->get_32(); + + f->set_endian_swap(big_endian!=0); //read big endian if saved as big endian + + uint32_t ver_major=f->get_32(); + uint32_t ver_minor=f->get_32(); + uint32_t ver_format=f->get_32(); + + if (ver_format<FORMAT_VERSION || ver_major>VERSION_MAJOR || (ver_major==VERSION_MAJOR && ver_minor>VERSION_MINOR)) { + + f->close(); + return ""; + } + + String type=get_unicode_string(); + + return type; +} + +ResourceInteractiveLoaderBinary::ResourceInteractiveLoaderBinary() { + + f=NULL; + stage=0; + endian_swap=false; + use_real64=false; + error=OK; +} + +ResourceInteractiveLoaderBinary::~ResourceInteractiveLoaderBinary() { + + if (f) + memdelete(f); +} + + +Ref<ResourceInteractiveLoader> ResourceFormatLoaderBinary::load_interactive(const String &p_path) { + + + Error err; + FileAccess *f = FileAccess::open(p_path,FileAccess::READ,&err); + + if (err!=OK) { + + ERR_FAIL_COND_V(err!=OK,Ref<ResourceInteractiveLoader>()); + } + + Ref<ResourceInteractiveLoaderBinary> ria = memnew( ResourceInteractiveLoaderBinary ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->open(f); + + + return ria; +} + +void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) const { + + if (p_type=="") { + get_recognized_extensions(p_extensions); + return; + } + + List<String> extensions; + ObjectTypeDB::get_extensions_for_type(p_type,&extensions); + + extensions.sort(); + + for(List<String>::Element *E=extensions.front();E;E=E->next()) { + String ext = E->get().to_lower(); + if (ext=="res") + continue; +// p_extensions->push_back("x"+ext); + p_extensions->push_back(ext); + } + + p_extensions->push_back("res"); + +} +void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_extensions) const{ + + List<String> extensions; + ObjectTypeDB::get_resource_base_extensions(&extensions); + extensions.sort(); + + for(List<String>::Element *E=extensions.front();E;E=E->next()) { + String ext = E->get().to_lower(); + if (ext=="res") + continue; + p_extensions->push_back(ext); + } + + p_extensions->push_back("res"); +} + +bool ResourceFormatLoaderBinary::handles_type(const String& p_type) const{ + + + return true; //handles all +} + +Error ResourceFormatLoaderBinary::load_import_metadata(const String &p_path, Ref<ResourceImportMetadata>& r_var) const { + + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + return ERR_FILE_CANT_OPEN; + } + + Ref<ResourceInteractiveLoaderBinary> ria = memnew( ResourceInteractiveLoaderBinary ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->recognize(f); + if(ria->error!=OK) + return ERR_FILE_UNRECOGNIZED; + f=ria->f; + uint64_t imp_ofs = f->get_64(); + + if (imp_ofs==0) + return ERR_UNAVAILABLE; + + f->seek(imp_ofs); + Ref<ResourceImportMetadata> imd = memnew( ResourceImportMetadata ); + imd->set_editor(ria->get_unicode_string()); + int sc = f->get_32(); + for(int i=0;i<sc;i++) { + + String src = ria->get_unicode_string(); + String md5 = ria->get_unicode_string(); + imd->add_source(src,md5); + } + int pc = f->get_32(); + + for(int i=0;i<pc;i++) { + + String name = ria->get_unicode_string(); + Variant val; + ria->parse_variant(val); + imd->set_option(name,val); + } + + r_var=imd; + + return OK; + +} + + +void ResourceFormatLoaderBinary::get_dependencies(const String& p_path,List<String> *p_dependencies) { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + ERR_FAIL_COND(!f); + + Ref<ResourceInteractiveLoaderBinary> ria = memnew( ResourceInteractiveLoaderBinary ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->get_dependencies(f,p_dependencies); +} + + +String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + return ""; //could not rwead + } + + Ref<ResourceInteractiveLoaderBinary> ria = memnew( ResourceInteractiveLoaderBinary ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + String r = ria->recognize(f); + return r; + + +} + + + +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////// + + +void ResourceFormatSaverBinaryInstance::_pad_buffer(int p_bytes) { + + int extra = 4-(p_bytes%4); + if (extra<4) { + for(int i=0;i<extra;i++) + f->store_8(0); //pad to 32 + } + +} + + +void ResourceFormatSaverBinaryInstance::write_variant(const Variant& p_property,const PropertyInfo& p_hint) { + + switch(p_property.get_type()) { + + case Variant::NIL: { + + f->store_32(VARIANT_NIL); + // don't store anything + } break; + case Variant::BOOL: { + + f->store_32(VARIANT_BOOL); + bool val=p_property; + f->store_32(val); + } break; + case Variant::INT: { + + f->store_32(VARIANT_INT); + int val=p_property; + f->store_32(val); + } break; + case Variant::REAL: { + + f->store_32(VARIANT_REAL); + real_t val=p_property; + f->store_real(val); + + } break; + case Variant::STRING: { + + f->store_32(VARIANT_STRING); + String val=p_property; + save_unicode_string(val); + + } break; + case Variant::VECTOR2: { + + f->store_32(VARIANT_VECTOR2); + Vector2 val=p_property; + f->store_real(val.x); + f->store_real(val.y); + + } break; + case Variant::RECT2: { + + f->store_32(VARIANT_RECT2); + Rect2 val=p_property; + f->store_real(val.pos.x); + f->store_real(val.pos.y); + f->store_real(val.size.x); + f->store_real(val.size.y); + + } break; + case Variant::VECTOR3: { + + f->store_32(VARIANT_VECTOR3); + Vector3 val=p_property; + f->store_real(val.x); + f->store_real(val.y); + f->store_real(val.z); + + } break; + case Variant::PLANE: { + + f->store_32(VARIANT_PLANE); + Plane val=p_property; + f->store_real(val.normal.x); + f->store_real(val.normal.y); + f->store_real(val.normal.z); + f->store_real(val.d); + + } break; + case Variant::QUAT: { + + f->store_32(VARIANT_QUAT); + Quat val=p_property; + f->store_real(val.x); + f->store_real(val.y); + f->store_real(val.z); + f->store_real(val.w); + + } break; + case Variant::_AABB: { + + f->store_32(VARIANT_AABB); + AABB val=p_property; + f->store_real(val.pos.x); + f->store_real(val.pos.y); + f->store_real(val.pos.z); + f->store_real(val.size.x); + f->store_real(val.size.y); + f->store_real(val.size.z); + + } break; + case Variant::MATRIX32: { + + f->store_32(VARIANT_MATRIX32); + Matrix32 val=p_property; + f->store_real(val.elements[0].x); + f->store_real(val.elements[0].y); + f->store_real(val.elements[1].x); + f->store_real(val.elements[1].y); + f->store_real(val.elements[2].x); + f->store_real(val.elements[2].y); + + } break; + case Variant::MATRIX3: { + + f->store_32(VARIANT_MATRIX3); + Matrix3 val=p_property; + f->store_real(val.elements[0].x); + f->store_real(val.elements[0].y); + f->store_real(val.elements[0].z); + f->store_real(val.elements[1].x); + f->store_real(val.elements[1].y); + f->store_real(val.elements[1].z); + f->store_real(val.elements[2].x); + f->store_real(val.elements[2].y); + f->store_real(val.elements[2].z); + + } break; + case Variant::TRANSFORM: { + + f->store_32(VARIANT_TRANSFORM); + Transform val=p_property; + f->store_real(val.basis.elements[0].x); + f->store_real(val.basis.elements[0].y); + f->store_real(val.basis.elements[0].z); + f->store_real(val.basis.elements[1].x); + f->store_real(val.basis.elements[1].y); + f->store_real(val.basis.elements[1].z); + f->store_real(val.basis.elements[2].x); + f->store_real(val.basis.elements[2].y); + f->store_real(val.basis.elements[2].z); + f->store_real(val.origin.x); + f->store_real(val.origin.y); + f->store_real(val.origin.z); + + } break; + case Variant::COLOR: { + + f->store_32(VARIANT_COLOR); + Color val=p_property; + f->store_real(val.r); + f->store_real(val.g); + f->store_real(val.b); + f->store_real(val.a); + + } break; + case Variant::IMAGE: { + + f->store_32(VARIANT_IMAGE); + Image val =p_property; + if (val.empty()) { + f->store_32(IMAGE_ENCODING_EMPTY); + break; + } + + int encoding=IMAGE_ENCODING_RAW; + float quality=0.7; + + if (val.get_format() <= Image::FORMAT_INDEXED_ALPHA) { + //can only compress uncompressed stuff + + if (p_hint.hint==PROPERTY_HINT_IMAGE_COMPRESS_LOSSY && Image::lossy_packer) { + encoding=IMAGE_ENCODING_LOSSY; + float qs=p_hint.hint_string.to_double(); + if (qs!=0.0) + quality=qs; + + } else if (p_hint.hint==PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS && Image::lossless_packer) { + encoding=IMAGE_ENCODING_LOSSLESS; + + } + } + + f->store_32(encoding); //raw encoding + + if (encoding==IMAGE_ENCODING_RAW) { + + + f->store_32(val.get_width()); + f->store_32(val.get_height()); + f->store_32(val.get_mipmaps()); + switch(val.get_format()) { + + case Image::FORMAT_GRAYSCALE: f->store_32(IMAGE_FORMAT_GRAYSCALE ); break; ///< one byte per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255 + case Image::FORMAT_INTENSITY: f->store_32(IMAGE_FORMAT_INTENSITY ); break; ///< one byte per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255 + case Image::FORMAT_GRAYSCALE_ALPHA: f->store_32(IMAGE_FORMAT_GRAYSCALE_ALPHA ); break; ///< two bytes per pixel: f->store_32(IMAGE_FORMAT_ ); break; 0-255. alpha 0-255 + case Image::FORMAT_RGB: f->store_32(IMAGE_FORMAT_RGB ); break; ///< one byte R: f->store_32(IMAGE_FORMAT_ ); break; one byte G: f->store_32(IMAGE_FORMAT_ ); break; one byte B + case Image::FORMAT_RGBA: f->store_32(IMAGE_FORMAT_RGBA ); break; ///< one byte R: f->store_32(IMAGE_FORMAT_ ); break; one byte G: f->store_32(IMAGE_FORMAT_ ); break; one byte B: f->store_32(IMAGE_FORMAT_ ); break; one byte A + case Image::FORMAT_INDEXED: f->store_32(IMAGE_FORMAT_INDEXED ); break; ///< index byte 0-256: f->store_32(IMAGE_FORMAT_ ); break; and after image end: f->store_32(IMAGE_FORMAT_ ); break; 256*3 bytes of palette + case Image::FORMAT_INDEXED_ALPHA: f->store_32(IMAGE_FORMAT_INDEXED_ALPHA ); break; ///< index byte 0-256: f->store_32(IMAGE_FORMAT_ ); break; and after image end: f->store_32(IMAGE_FORMAT_ ); break; 256*4 bytes of palette (alpha) + case Image::FORMAT_BC1: f->store_32(IMAGE_FORMAT_BC1 ); break; // DXT1 + case Image::FORMAT_BC2: f->store_32(IMAGE_FORMAT_BC2 ); break; // DXT3 + case Image::FORMAT_BC3: f->store_32(IMAGE_FORMAT_BC3 ); break; // DXT5 + case Image::FORMAT_BC4: f->store_32(IMAGE_FORMAT_BC4 ); break; // ATI1 + case Image::FORMAT_BC5: f->store_32(IMAGE_FORMAT_BC5 ); break; // ATI2 + case Image::FORMAT_PVRTC2: f->store_32(IMAGE_FORMAT_PVRTC2 ); break; + case Image::FORMAT_PVRTC2_ALPHA: f->store_32(IMAGE_FORMAT_PVRTC2_ALPHA ); break; + case Image::FORMAT_PVRTC4: f->store_32(IMAGE_FORMAT_PVRTC4 ); break; + case Image::FORMAT_PVRTC4_ALPHA: f->store_32(IMAGE_FORMAT_PVRTC4_ALPHA ); break; + case Image::FORMAT_ETC: f->store_32(IMAGE_FORMAT_ETC); break; + case Image::FORMAT_CUSTOM: f->store_32(IMAGE_FORMAT_CUSTOM ); break; + default: {} + + } + + int dlen = val.get_data().size(); + f->store_32(dlen); + DVector<uint8_t>::Read r = val.get_data().read(); + f->store_buffer(r.ptr(),dlen); + _pad_buffer(dlen); + } else { + + DVector<uint8_t> data; + if (encoding==IMAGE_ENCODING_LOSSY) { + data=Image::lossy_packer(val,quality); + + } else if (encoding==IMAGE_ENCODING_LOSSLESS) { + data=Image::lossless_packer(val); + + } + + int ds=data.size(); + f->store_32(ds); + if (ds>0) { + DVector<uint8_t>::Read r = data.read(); + f->store_buffer(r.ptr(),ds); + + _pad_buffer(ds); + + } + } + + } break; + case Variant::NODE_PATH: { + f->store_32(VARIANT_NODE_PATH); + NodePath np=p_property; + f->store_16(np.get_name_count()); + uint16_t snc = np.get_subname_count(); + if (np.is_absolute()) + 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))); + f->store_32(get_string_index(np.get_property())); + + } break; + case Variant::_RID: { + + f->store_32(VARIANT_RID); + WARN_PRINT("Can't save RIDs"); + RID val = p_property; + f->store_32(val.get_id()); + } break; + case Variant::OBJECT: { + + f->store_32(VARIANT_OBJECT); + RES res = p_property; + if (res.is_null()) { + f->store_32(OBJECT_EMPTY); + return; // don't save it + } + + if (res->get_path().length() && res->get_path().find("::")==-1) { + f->store_32(OBJECT_EXTERNAL_RESOURCE); + save_unicode_string(res->get_save_type()); + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + if (no_extensions) + path=path.basename()+".*"; + save_unicode_string(path); + } else { + + if (!resource_map.has(res)) { + f->store_32(OBJECT_EMPTY); + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL(); + } + + f->store_32(OBJECT_INTERNAL_RESOURCE); + f->store_32(resource_map[res]); + //internal resource + } + + + } break; + case Variant::INPUT_EVENT: { + + f->store_32(VARIANT_INPUT_EVENT); + WARN_PRINT("Can't save InputEvent (maybe it could..)"); + } break; + case Variant::DICTIONARY: { + + f->store_32(VARIANT_DICTIONARY); + Dictionary d = p_property; + f->store_32(uint32_t(d.size())|(d.is_shared()?0x80000000:0)); + + List<Variant> keys; + d.get_key_list(&keys); + + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + + write_variant(E->get()); + write_variant(d[E->get()]); + } + + + } break; + case Variant::ARRAY: { + + f->store_32(VARIANT_ARRAY); + Array a=p_property; + f->store_32(uint32_t(a.size())|(a.is_shared()?0x80000000:0)); + for(int i=0;i<a.size();i++) { + + write_variant(a[i]); + } + + } break; + case Variant::RAW_ARRAY: { + + f->store_32(VARIANT_RAW_ARRAY); + DVector<uint8_t> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<uint8_t>::Read r = arr.read(); + f->store_buffer(r.ptr(),len); + _pad_buffer(len); + + } break; + case Variant::INT_ARRAY: { + + f->store_32(VARIANT_INT_ARRAY); + DVector<int> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<int>::Read r = arr.read(); + for(int i=0;i<len;i++) + f->store_32(r[i]); + + } break; + case Variant::REAL_ARRAY: { + + f->store_32(VARIANT_REAL_ARRAY); + DVector<real_t> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<real_t>::Read r = arr.read(); + for(int i=0;i<len;i++) { + f->store_real(r[i]); + } + + } break; + case Variant::STRING_ARRAY: { + + f->store_32(VARIANT_STRING_ARRAY); + DVector<String> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<String>::Read r = arr.read(); + for(int i=0;i<len;i++) { + save_unicode_string(r[i]); + } + + } break; + case Variant::VECTOR3_ARRAY: { + + f->store_32(VARIANT_VECTOR3_ARRAY); + DVector<Vector3> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<Vector3>::Read r = arr.read(); + for(int i=0;i<len;i++) { + f->store_real(r[i].x); + f->store_real(r[i].y); + f->store_real(r[i].z); + } + + } break; + case Variant::VECTOR2_ARRAY: { + + f->store_32(VARIANT_VECTOR2_ARRAY); + DVector<Vector2> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<Vector2>::Read r = arr.read(); + for(int i=0;i<len;i++) { + f->store_real(r[i].x); + f->store_real(r[i].y); + } + + } break; + case Variant::COLOR_ARRAY: { + + f->store_32(VARIANT_COLOR_ARRAY); + DVector<Color> arr = p_property; + int len=arr.size(); + f->store_32(len); + DVector<Color>::Read r = arr.read(); + for(int i=0;i<len;i++) { + f->store_real(r[i].r); + f->store_real(r[i].g); + f->store_real(r[i].b); + f->store_real(r[i].a); + } + + } break; + default: { + + ERR_EXPLAIN("Invalid variant"); + ERR_FAIL(); + } + } +} + + +void ResourceFormatSaverBinaryInstance::_find_resources(const Variant& p_variant,bool p_main) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null()) + return; + + if (!p_main && (!bundle_resources ) && res->get_path().length() && res->get_path().find("::") == -1 ) { + external_resources.insert(res); + return; + } + + + if (resource_map.has(res)) + return; + + List<PropertyInfo> property_list; + + res->get_property_list(&property_list); + + for(List<PropertyInfo>::Element *E=property_list.front();E;E=E->next()) { + + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + _find_resources(res->get(E->get().name)); + } + } + + resource_map[ res ] = saved_resources.size(); + saved_resources.push_back(res); + + } break; + + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i<len;i++) { + + Variant v=varray.get(i); + _find_resources(v); + } + + } break; + + case Variant::DICTIONARY: { + + Dictionary d=p_variant; + List<Variant> keys; + d.get_key_list(&keys); + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + _find_resources(E->get()); + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + case Variant::NODE_PATH: { + //take the chance and save node path strings + NodePath np = p_variant; + for(int i=0;i<np.get_name_count();i++) + get_string_index(np.get_name(i)); + for(int i=0;i<np.get_subname_count();i++) + get_string_index(np.get_subname(i)); + get_string_index(np.get_property()); + + + } break; + + default: {} + } + +} +#if 0 +Error ResourceFormatSaverBinary::_save_obj(const Object *p_object,SavedObject *so) { + + //use classic way + List<PropertyInfo> property_list; + p_object->get_property_list( &property_list ); + + for(List<PropertyInfo>::Element *E=property_list.front();E;E=E->next()) { + + if (skip_editor && E->get().name.begins_with("__editor")) + continue; + if (E->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && E->get().usage&PROPERTY_USAGE_BUNDLE)) { + + SavedObject::SavedProperty sp; + sp.name_idx=get_string_index(E->get().name); + sp.value = p_object->get(E->get().name); + _find_resources(sp.value); + so->properties.push_back(sp); + } + } + + return OK; + +} + + + +Error ResourceFormatSaverBinary::save(const Object *p_object,const Variant &p_meta) { + + ERR_FAIL_COND_V(!f,ERR_UNCONFIGURED); + ERR_EXPLAIN("write_object should supply either an object, a meta, or both"); + ERR_FAIL_COND_V(!p_object && p_meta.get_type()==Variant::NIL, ERR_INVALID_PARAMETER); + + SavedObject *so = memnew( SavedObject ); + + if (p_object) + so->type=p_object->get_type(); + + _find_resources(p_meta); + so->meta=p_meta; + Error err = _save_obj(p_object,so); + ERR_FAIL_COND_V( err, ERR_INVALID_DATA ); + + saved_objects.push_back(so); + + return OK; +} +#endif + +void ResourceFormatSaverBinaryInstance::save_unicode_string(const String& p_string) { + + + CharString utf8 = p_string.utf8(); + f->store_32(utf8.length()+1); + f->store_buffer((const uint8_t*)utf8.get_data(),utf8.length()+1); +} + +int ResourceFormatSaverBinaryInstance::get_string_index(const String& p_string) { + + StringName s=p_string; + if (string_map.has(s)) + return string_map[s]; + + string_map[s]=strings.size(); + strings.push_back(s); + return strings.size()-1; +} + + +Error ResourceFormatSaverBinaryInstance::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + Error err; + if (p_flags&ResourceSaver::FLAG_COMPRESS) { + FileAccessCompressed *fac = memnew( FileAccessCompressed ); + fac->configure("RSCC"); + f=fac; + err = fac->_open(p_path,FileAccess::WRITE); + if (err) + memdelete(f); + + } else { + f=FileAccess::open(p_path,FileAccess::WRITE,&err); + } + + + ERR_FAIL_COND_V(err,err); + FileAccessRef _fref(f); + + + relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES; + big_endian=p_flags&ResourceSaver::FLAG_SAVE_BIG_ENDIAN; + no_extensions=p_flags&ResourceSaver::FLAG_NO_EXTENSION; + + local_path=p_path.get_base_dir(); + //bin_meta_idx = get_string_index("__bin_meta__"); //is often used, so create + + _find_resources(p_resource,true); + + if (!(p_flags&ResourceSaver::FLAG_COMPRESS)) { + //save header compressed + static const uint8_t header[4]={'R','S','R','C'}; + f->store_buffer(header,4); + } + + if (big_endian) { + f->store_32(1); + f->set_endian_swap(true); + } else + f->store_32(0); + + f->store_32(0); //64 bits file, false for now + f->store_32(VERSION_MAJOR); + f->store_32(VERSION_MINOR); + f->store_32(FORMAT_VERSION); + + //f->store_32(saved_resources.size()+external_resources.size()); // load steps -not needed + save_unicode_string(p_resource->get_type()); + uint64_t md_at = f->get_pos(); + f->store_64(0); //offset to impoty metadata + for(int i=0;i<14;i++) + f->store_32(0); // reserved + + + List<ResourceData> resources; + + + { + + + for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) { + + + ResourceData &rd = resources.push_back(ResourceData())->get(); + rd.type=E->get()->get_type(); + + List<PropertyInfo> property_list; + E->get()->get_property_list( &property_list ); + + for(List<PropertyInfo>::Element *F=property_list.front();F;F=F->next()) { + + if (skip_editor && F->get().name.begins_with("__editor")) + continue; + if (F->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && F->get().usage&PROPERTY_USAGE_BUNDLE)) { + 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_STORE_IF_NONZERO && p.value.is_zero()) + continue; + p.pi=F->get(); + + rd.properties.push_back(p); + + } + } + + + + } + } + + + f->store_32(strings.size()); //string table size + for(int i=0;i<strings.size();i++) { + //print_bl("saving string: "+strings[i]); + save_unicode_string(strings[i]); + } + + // save external resource table + f->store_32(external_resources.size()); //amount of external resources + for(Set<RES>::Element *E=external_resources.front();E;E=E->next()) { + + save_unicode_string(E->get()->get_save_type()); + String path = E->get()->get_path(); + if (no_extensions) + path=path.basename()+".*"; + save_unicode_string(path); + } + // save internal resource table + f->store_32(saved_resources.size()); //amount of internal resources + Vector<uint64_t> ofs_pos; + for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) { + + RES r = E->get(); + if (r->get_path()=="" || r->get_path().find("::")!=-1) + save_unicode_string("local://"+itos(ofs_pos.size())); + else + save_unicode_string(r->get_path()); //actual external + ofs_pos.push_back(f->get_pos()); + f->store_64(0); //offset in 64 bits + } + + Vector<uint64_t> ofs_table; +// int saved_idx=0; + //now actually save the resources + + for(List<ResourceData>::Element *E=resources.front();E;E=E->next()) { + + ResourceData & rd = E->get(); + + ofs_table.push_back(f->get_pos()); + save_unicode_string(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); + } + + } + + for(int i=0;i<ofs_table.size();i++) { + f->seek(ofs_pos[i]); + f->store_64(ofs_table[i]); + } + + f->seek_end(); + if (p_resource->get_import_metadata().is_valid()) { + uint64_t md_pos = f->get_pos(); + Ref<ResourceImportMetadata> imd=p_resource->get_import_metadata(); + save_unicode_string(imd->get_editor()); + f->store_32(imd->get_source_count()); + for(int i=0;i<imd->get_source_count();i++) { + save_unicode_string(imd->get_source_path(i)); + save_unicode_string(imd->get_source_md5(i)); + } + List<String> options; + imd->get_options(&options); + f->store_32(options.size()); + for(List<String>::Element *E=options.front();E;E=E->next()) { + save_unicode_string(E->get()); + write_variant(imd->get_option(E->get())); + } + + f->seek(md_at); + f->store_64(md_pos); + f->seek_end(); + } + + + f->store_buffer((const uint8_t*)"RSRC",4); //magic at end + + f->close(); + + + return OK; +} + + + +Error ResourceFormatSaverBinary::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + + String local_path = Globals::get_singleton()->localize_path(p_path); + ResourceFormatSaverBinaryInstance saver; + return saver.save(local_path,p_resource,p_flags); + +} + + +bool ResourceFormatSaverBinary::recognize(const RES& p_resource) const { + + return true; //all recognized + +} + +void ResourceFormatSaverBinary::get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const { + + + //here comes the sun, lalalala + String base = p_resource->get_base_extension().to_lower(); + if (base!="res") { + + p_extensions->push_back(base); + } + + p_extensions->push_back("res"); + + +} + diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h new file mode 100644 index 0000000000..006148f5a8 --- /dev/null +++ b/core/io/resource_format_binary.h @@ -0,0 +1,184 @@ +/*************************************************************************/ +/* resource_format_binary.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. */ +/*************************************************************************/ +#ifndef RESOURCE_FORMAT_BINARY_H +#define RESOURCE_FORMAT_BINARY_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" + + +class ResourceInteractiveLoaderBinary : public ResourceInteractiveLoader { + + String local_path; + String res_path; + String type; + Ref<Resource> resource; + + FileAccess *f; + + + bool endian_swap; + bool use_real64; + uint64_t importmd_ofs; + + Vector<char> str_buf; + List<RES> resource_cache; + + //Map<int,StringName> string_map; + Vector<StringName> string_map; + + struct ExtResoucre { + String path; + String type; + }; + + Vector<ExtResoucre> external_resources; + + struct IntResoucre { + String path; + uint64_t offset; + }; + + Vector<IntResoucre> internal_resources; + + String get_unicode_string(); + void _advance_padding(uint32_t p_len); + + Error error; + + int stage; + +friend class ResourceFormatLoaderBinary; + + + Error parse_variant(Variant& r_v); + +public: + + virtual void set_local_path(const String& p_local_path); + virtual Ref<Resource> get_resource(); + virtual Error poll(); + virtual int get_stage() const; + virtual int get_stage_count() const; + + void open(FileAccess *p_f); + String recognize(FileAccess *p_f); + void get_dependencies(FileAccess *p_f,List<String> *p_dependencies); + + + ResourceInteractiveLoaderBinary(); + ~ResourceInteractiveLoaderBinary(); + +}; + +class ResourceFormatLoaderBinary : public ResourceFormatLoader { +public: + + virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path); + virtual void get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String& p_type) const; + virtual String get_resource_type(const String &p_path) const; + virtual void get_dependencies(const String& p_path,List<String> *p_dependencies); + virtual Error load_import_metadata(const String &p_path, Ref<ResourceImportMetadata>& r_var) const; + + + +}; + + + + +class ResourceFormatSaverBinaryInstance { + + String local_path; + + bool no_extensions; + bool relative_paths; + bool bundle_resources; + bool skip_editor; + bool big_endian; + int bin_meta_idx; + FileAccess *f; + String magic; + Map<RES,int> resource_map; + Map<StringName,int> string_map; + Vector<StringName> strings; + + + Set<RES> external_resources; + List<RES> saved_resources; + + + struct Property { + int name_idx; + Variant value; + PropertyInfo pi; + + }; + + struct ResourceData { + + String type; + List<Property> properties; + }; + + + + + void _pad_buffer(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); + int get_string_index(const String& p_string); +public: + + + Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); +}; + + + +class ResourceFormatSaverBinary : public ResourceFormatSaver { + + + + +public: + + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + virtual bool recognize(const RES& p_resource) const; + virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const; + + +}; + + +#endif // RESOURCE_FORMAT_BINARY_H diff --git a/core/io/resource_format_xml.cpp b/core/io/resource_format_xml.cpp new file mode 100644 index 0000000000..f175c73e98 --- /dev/null +++ b/core/io/resource_format_xml.cpp @@ -0,0 +1,2557 @@ +/*************************************************************************/ +/* resource_format_xml.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 "resource_format_xml.h" +#include "globals.h" +#include "version.h" + + + +ResourceInteractiveLoaderXML::Tag* ResourceInteractiveLoaderXML::parse_tag(bool *r_exit,bool p_printerr) { + + + while(get_char()!='<' && !f->eof_reached()) {} + if (f->eof_reached()) { + return NULL; + } + + Tag tag; + bool exit=false; + if (r_exit) + *r_exit=false; + + bool complete=false; + while(!f->eof_reached()) { + + CharType c=get_char(); + if (c<33 && tag.name.length() && !exit) { + break; + } else if (c=='>') { + complete=true; + break; + } else if (c=='/') { + exit=true; + } else { + tag.name+=c; + } + } + + if (f->eof_reached()) { + + return NULL; + } + + if (exit) { + if (!tag_stack.size()) { + if (!p_printerr) + return NULL; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unmatched exit tag </"+tag.name+">"); + ERR_FAIL_COND_V(!tag_stack.size(),NULL); + } + + if (tag_stack.back()->get().name!=tag.name) { + if (!p_printerr) + return NULL; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Mismatched exit tag. Got </"+tag.name+">, expected </"+tag_stack.back()->get().name+">"); + ERR_FAIL_COND_V(tag_stack.back()->get().name!=tag.name,NULL); + } + + if (!complete) { + while(get_char()!='>' && !f->eof_reached()) {} + if (f->eof_reached()) + return NULL; + } + + if (r_exit) + *r_exit=true; + + tag_stack.pop_back(); + return NULL; + + } + + if (!complete) { + String name; + String value; + bool reading_value=false; + + while(!f->eof_reached()) { + + CharType c=get_char(); + if (c=='>') { + if (value.length()) { + + tag.args[name]=value; + } + break; + + } else if ( ((!reading_value && (c<33)) || c=='=' || c=='"') && tag.name.length()) { + + if (!reading_value && name.length()) { + + reading_value=true; + } else if (reading_value && value.length()) { + + tag.args[name]=value; + name=""; + value=""; + reading_value=false; + } + + } else if (reading_value) { + + value+=c; + } else { + + name+=c; + } + } + + if (f->eof_reached()) + return NULL; + } + + tag_stack.push_back(tag); + + return &tag_stack.back()->get(); +} + + +Error ResourceInteractiveLoaderXML::close_tag(const String& p_name) { + + int level=0; + bool inside_tag=false; + + while(true) { + + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": EOF found while attempting to find </"+p_name+">"); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + uint8_t c = get_char(); + + if (c == '<') { + + if (inside_tag) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML. Already inside Tag."); + ERR_FAIL_COND_V(inside_tag,ERR_FILE_CORRUPT); + } + inside_tag=true; + c = get_char(); + if (c == '/') { + + --level; + } else { + + ++level; + }; + } else if (c == '>') { + + if (!inside_tag) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML. Already outside Tag"); + ERR_FAIL_COND_V(!inside_tag,ERR_FILE_CORRUPT); + } + inside_tag=false; + if (level == -1) { + tag_stack.pop_back(); + return OK; + }; + }; + } + + return OK; +} + +void ResourceInteractiveLoaderXML::unquote(String& p_str) { + + p_str=p_str.strip_edges(); + p_str=p_str.replace("\"",""); + p_str=p_str.replace(">","<"); + p_str=p_str.replace("<",">"); + p_str=p_str.replace("'","'"); + p_str=p_str.replace(""","\""); + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + p_str=p_str.replace("&#"+String::num(i)+";",chr); + } + p_str=p_str.replace("&","&"); + + //p_str.parse_utf8( p_str.ascii(true).get_data() ); + +} + +Error ResourceInteractiveLoaderXML::goto_end_of_tag() { + + uint8_t c; + while(true) { + + c=get_char(); + if (c=='>') //closetag + break; + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": EOF found while attempting to find close tag."); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + } + tag_stack.pop_back(); + + return OK; +} + + +Error ResourceInteractiveLoaderXML::parse_property_data(String &r_data) { + + r_data=""; + CharString cs; + while(true) { + + CharType c=get_char(); + if (c=='<') + break; + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + cs.push_back(c); + } + + cs.push_back(0); + + r_data.parse_utf8(cs.get_data()); + + while(get_char()!='>' && !f->eof_reached()) {} + if (f->eof_reached()) { + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Malformed XML."); + ERR_FAIL_COND_V( f->eof_reached(), ERR_FILE_CORRUPT ); + } + + r_data=r_data.strip_edges(); + tag_stack.pop_back(); + + return OK; +} + + +Error ResourceInteractiveLoaderXML::_parse_array_element(Vector<char> &buff,bool p_number_only,FileAccess *f,bool *end) { + + if (buff.empty()) + buff.resize(32); // optimi + + int buff_max=buff.size(); + int buff_size=0; + *end=false; + char *buffptr=&buff[0]; + bool found=false; + bool quoted=false; + + while(true) { + + char c=get_char(); + + if (c==0) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (zero found)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } else if (c=='"') { + quoted=!quoted; + } else if ((!quoted && ((p_number_only && c<33) || c==',')) || c=='<') { + + + if (c=='<') { + *end=true; + break; + } + if (c<32 && f->eof_reached()) { + *end=true; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (unexpected EOF)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + if (found) + break; + + } else { + + found=true; + if (buff_size>=buff_max) { + + buff_max++; + buff.resize(buff_max); + + } + + buffptr[buff_size]=c; + buff_size++; + } + } + + if (buff_size>=buff_max) { + + buff_max++; + buff.resize(buff_max); + + } + + buff[buff_size]=0; + buff_size++; + + return OK; +} + +Error ResourceInteractiveLoaderXML::parse_property(Variant& r_v, String &r_name) { + + bool exit; + Tag *tag = parse_tag(&exit); + + if (!tag) { + if (exit) // shouldn't have exited + return ERR_FILE_EOF; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": File corrupt (No Property Tag)."); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + + r_v=Variant(); + r_name=""; + + + //ERR_FAIL_COND_V(tag->name!="property",ERR_FILE_CORRUPT); + //ERR_FAIL_COND_V(!tag->args.has("name"),ERR_FILE_CORRUPT); +// ERR_FAIL_COND_V(!tag->args.has("type"),ERR_FILE_CORRUPT); + + //String name=tag->args["name"]; + //ERR_FAIL_COND_V(name=="",ERR_FILE_CORRUPT); + String type=tag->name; + String name=tag->args["name"]; + + if (type=="") { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": 'type' field is empty."); + ERR_FAIL_COND_V(type=="",ERR_FILE_CORRUPT); + } + + if (type=="dictionary") { + + Dictionary d( tag->args.has("shared") && (String(tag->args["shared"])=="true" || String(tag->args["shared"])=="1")); + + while(true) { + + Error err; + String tagname; + Variant key; + + int dictline = get_current_line(); + + + err=parse_property(key,tagname); + + if (err && err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error parsing dictionary: "+name+" (from line "+itos(dictline)+")"); + ERR_FAIL_COND_V(err && err!=ERR_FILE_EOF,err); + } + //ERR_FAIL_COND_V(tagname!="key",ERR_FILE_CORRUPT); + if (err) + break; + Variant value; + err=parse_property(value,tagname); + if (err) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error parsing dictionary: "+name+" (from line "+itos(dictline)+")"); + } + + ERR_FAIL_COND_V(err,err); + //ERR_FAIL_COND_V(tagname!="value",ERR_FILE_CORRUPT); + + d[key]=value; + } + + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=d; + return OK; + + } else if (type=="array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + + + int len=tag->args["len"].to_int(); + bool shared = tag->args.has("shared") && (String(tag->args["shared"])=="true" || String(tag->args["shared"])=="1"); + + Array array(shared); + array.resize(len); + + Error err; + Variant v; + String tagname; + int idx=0; + while( (err=parse_property(v,tagname))==OK ) { + + ERR_CONTINUE( idx <0 || idx >=len ); + + array.set(idx,v); + idx++; + } + + if (idx!=len) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array (size mismatch): "+name); + ERR_FAIL_COND_V(idx!=len,err); + } + + if (err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array: "+name); + ERR_FAIL_COND_V(err!=ERR_FILE_EOF,err); + } + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=array; + return OK; + + } else if (type=="resource") { + + if (tag->args.has("path")) { + + String path=tag->args["path"]; + String hint; + if (tag->args.has("resource_type")) + hint=tag->args["resource_type"]; + + if (path.begins_with("local://")) + path=path.replace("local://",local_path+"::"); + else if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + + } + + //take advantage of the resource loader cache. The resource is cached on it, even if + RES res=ResourceLoader::load(path,hint); + + + if (res.is_null()) { + + WARN_PRINT(String("Couldn't load resource: "+path).ascii().get_data()); + } + + r_v=res.get_ref_ptr(); + } + + + + Error err=goto_end_of_tag(); + if (err) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error closing <resource> tag."); + ERR_FAIL_COND_V(err,err); + } + + + r_name=name; + + return OK; + + } else if (type=="image") { + + if (!tag->args.has("encoding")) { + //empty image + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + } + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'encoding' field."); + ERR_FAIL_COND_V( !tag->args.has("encoding"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'width' field."); + ERR_FAIL_COND_V( !tag->args.has("width"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'height' field."); + ERR_FAIL_COND_V( !tag->args.has("height"), ERR_FILE_CORRUPT ); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Image missing 'format' field."); + ERR_FAIL_COND_V( !tag->args.has("format"), ERR_FILE_CORRUPT ); + + String encoding=tag->args["encoding"]; + + if (encoding=="raw") { + String width=tag->args["width"]; + String height=tag->args["height"]; + String format=tag->args["format"]; + int mipmaps=tag->args.has("mipmaps")?int(tag->args["mipmaps"].to_int()):int(0); + int custom_size = tag->args.has("custom_size")?int(tag->args["custom_size"].to_int()):int(0); + + r_name=name; + + Image::Format imgformat; + + + if (format=="grayscale") { + imgformat=Image::FORMAT_GRAYSCALE; + } else if (format=="intensity") { + imgformat=Image::FORMAT_INTENSITY; + } else if (format=="grayscale_alpha") { + imgformat=Image::FORMAT_GRAYSCALE_ALPHA; + } else if (format=="rgb") { + imgformat=Image::FORMAT_RGB; + } else if (format=="rgba") { + imgformat=Image::FORMAT_RGBA; + } else if (format=="indexed") { + imgformat=Image::FORMAT_INDEXED; + } else if (format=="indexed_alpha") { + imgformat=Image::FORMAT_INDEXED_ALPHA; + } else if (format=="bc1") { + imgformat=Image::FORMAT_BC1; + } else if (format=="bc2") { + imgformat=Image::FORMAT_BC2; + } else if (format=="bc3") { + imgformat=Image::FORMAT_BC3; + } else if (format=="bc4") { + imgformat=Image::FORMAT_BC4; + } else if (format=="bc5") { + imgformat=Image::FORMAT_BC5; + } else if (format=="pvrtc2") { + imgformat=Image::FORMAT_PVRTC2; + } else if (format=="pvrtc2a") { + imgformat=Image::FORMAT_PVRTC2_ALPHA; + } else if (format=="pvrtc4") { + imgformat=Image::FORMAT_PVRTC4; + } else if (format=="pvrtc4a") { + imgformat=Image::FORMAT_PVRTC4_ALPHA; + } else if (format=="etc") { + imgformat=Image::FORMAT_ETC; + } else if (format=="custom") { + imgformat=Image::FORMAT_CUSTOM; + } else { + + ERR_FAIL_V( ERR_FILE_CORRUPT ); + } + + + int datasize; + int w=width.to_int(); + int h=height.to_int(); + + if (w == 0 && w == 0) { + //r_v = Image(w, h, imgformat); + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + }; + + if (imgformat==Image::FORMAT_CUSTOM) { + + datasize=custom_size; + } else { + + datasize = Image::get_image_data_size(h,w,imgformat,mipmaps); + } + + if (datasize==0) { + //r_v = Image(w, h, imgformat); + r_v=Image(); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + return OK; + }; + + DVector<uint8_t> pixels; + pixels.resize(datasize); + DVector<uint8_t>::Write wb = pixels.write(); + + int idx=0; + uint8_t byte; + while( idx<datasize*2) { + + CharType c=get_char(); + + ERR_FAIL_COND_V(c=='<',ERR_FILE_CORRUPT); + + if ( (c>='0' && c<='9') || (c>='A' && c<='F') || (c>='a' && c<='f') ) { + + if (idx&1) { + + byte|=HEX2CHR(c); + wb[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + } + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + wb=DVector<uint8_t>::Write(); + + r_v=Image(w,h,mipmaps,imgformat,pixels); + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + + return OK; + } + + ERR_FAIL_V(ERR_FILE_CORRUPT); + + } else if (type=="raw_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": RawArray missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector<uint8_t> bytes; + bytes.resize(len); + DVector<uint8_t>::Write w=bytes.write(); + uint8_t *bytesptr=w.ptr(); + int idx=0; + uint8_t byte; + + while( idx<len*2) { + + CharType c=get_char(); + + if (idx&1) { + + byte|=HEX2CHR(c); + bytesptr[idx>>1]=byte; + } else { + + byte=HEX2CHR(c)<<4; + } + + idx++; + } + + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + w=DVector<uint8_t>::Write(); + r_v=bytes; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="int_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector<int> ints; + ints.resize(len); + DVector<int>::Write w=ints.write(); + int *intsptr=w.ptr(); + int idx=0; + String str; +#if 0 + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + intsptr[idx]=str.to_int(); + str=""; + idx++; + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + +#else + + Vector<char> tmpdata; + + while( idx<len ) { + + bool end=false; + Error err = _parse_array_element(tmpdata,true,f,&end); + ERR_FAIL_COND_V(err,err); + + intsptr[idx]=String::to_int(&tmpdata[0]); + idx++; + if (end) + break; + + } + +#endif + w=DVector<int>::Write(); + + r_v=ints; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } else if (type=="real_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector<real_t> reals; + reals.resize(len); + DVector<real_t>::Write w=reals.write(); + real_t *realsptr=w.ptr(); + int idx=0; + String str; + + +#if 0 + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + realsptr[idx]=str.to_double(); + str=""; + idx++; + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + +#else + + + + Vector<char> tmpdata; + + while( idx<len ) { + + bool end=false; + Error err = _parse_array_element(tmpdata,true,f,&end); + ERR_FAIL_COND_V(err,err); + + realsptr[idx]=String::to_double(&tmpdata[0]); + idx++; + + if (end) + break; + } + +#endif + + w=DVector<real_t>::Write(); + r_v=reals; + + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } else if (type=="string_array") { +#if 0 + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int(); + + DVector<String> strings; + strings.resize(len); + DVector<String>::Write w=strings.write(); + String *stringsptr=w.ptr(); + int idx=0; + String str; + + bool inside_str=false; + CharString cs; + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c=='"') { + if (inside_str) { + + cs.push_back(0); + String str; + str.parse_utf8(cs.get_data()); + unquote(str); + stringsptr[idx]=str; + cs.clear(); + idx++; + inside_str=false; + } else { + inside_str=true; + } + } else if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + + + } else if (inside_str){ + + cs.push_back(c); + } + } + w=DVector<String>::Write(); + r_v=strings; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + + r_name=name; + + return OK; +#endif + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": String Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + + + int len=tag->args["len"].to_int(); + + StringArray array; + array.resize(len); + DVector<String>::Write w = array.write(); + + Error err; + Variant v; + String tagname; + int idx=0; + + + while( (err=parse_property(v,tagname))==OK ) { + + ERR_CONTINUE( idx <0 || idx >=len ); + String str = v; //convert back to string + w[idx]=str; + idx++; + } + + if (idx!=len) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array (size mismatch): "+name); + ERR_FAIL_COND_V(idx!=len,err); + } + + if (err!=ERR_FILE_EOF) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Error loading array: "+name); + ERR_FAIL_COND_V(err!=ERR_FILE_EOF,err); + } + + //err=parse_property_data(name); // skip the rest + //ERR_FAIL_COND_V(err,err); + + r_name=name; + r_v=array; + return OK; + + } else if (type=="vector3_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector<Vector3> vectors; + vectors.resize(len); + DVector<Vector3>::Write w=vectors.write(); + Vector3 *vectorsptr=w.ptr(); + int idx=0; + int subidx=0; + Vector3 auxvec; + String str; + +// uint64_t tbegin = OS::get_singleton()->get_ticks_usec(); +#if 0 + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + auxvec[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==3) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } +#else + + Vector<char> tmpdata; + + while( idx<len ) { + + bool end=false; + Error err = _parse_array_element(tmpdata,true,f,&end); + ERR_FAIL_COND_V(err,err); + + + auxvec[subidx]=String::to_double(&tmpdata[0]); + subidx++; + if (subidx==3) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + + if (end) + break; + } + + + +#endif + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Premature end of vector3 array"); + ERR_FAIL_COND_V(idx<len,ERR_FILE_CORRUPT); +// double time_taken = (OS::get_singleton()->get_ticks_usec() - tbegin)/1000000.0; + + + w=DVector<Vector3>::Write(); + r_v=vectors; + String sdfsdfg; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="vector2_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector<Vector2> vectors; + vectors.resize(len); + DVector<Vector2>::Write w=vectors.write(); + Vector2 *vectorsptr=w.ptr(); + int idx=0; + int subidx=0; + Vector2 auxvec; + String str; + +// uint64_t tbegin = OS::get_singleton()->get_ticks_usec(); +#if 0 + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c<22 || c==',' || c=='<') { + + if (str.length()) { + + auxvec[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==2) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } +#else + + Vector<char> tmpdata; + + while( idx<len ) { + + bool end=false; + Error err = _parse_array_element(tmpdata,true,f,&end); + ERR_FAIL_COND_V(err,err); + + + auxvec[subidx]=String::to_double(&tmpdata[0]); + subidx++; + if (subidx==2) { + vectorsptr[idx]=auxvec; + + idx++; + subidx=0; + } + + if (end) + break; + } + + + +#endif + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Premature end of vector2 array"); + ERR_FAIL_COND_V(idx<len,ERR_FILE_CORRUPT); +// double time_taken = (OS::get_singleton()->get_ticks_usec() - tbegin)/1000000.0; + + + w=DVector<Vector2>::Write(); + r_v=vectors; + String sdfsdfg; + Error err=goto_end_of_tag(); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + + } else if (type=="color_array") { + + if (!tag->args.has("len")) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Array missing 'len' field: "+name); + ERR_FAIL_COND_V(!tag->args.has("len"),ERR_FILE_CORRUPT); + } + int len=tag->args["len"].to_int();; + + DVector<Color> colors; + colors.resize(len); + DVector<Color>::Write w=colors.write(); + Color *colorsptr=w.ptr(); + int idx=0; + int subidx=0; + Color auxcol; + String str; + + while( idx<len ) { + + + CharType c=get_char(); + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + + + if (c<33 || c==',' || c=='<') { + + if (str.length()) { + + auxcol[subidx]=str.to_double(); + subidx++; + str=""; + if (subidx==4) { + colorsptr[idx]=auxcol; + idx++; + subidx=0; + } + } + + if (c=='<') { + + while(get_char()!='>' && !f->eof_reached()) {} + ERR_FAIL_COND_V(f->eof_reached(),ERR_FILE_CORRUPT); + break; + } + + } else { + + str+=c; + } + } + w=DVector<Color>::Write(); + r_v=colors; + String sdfsdfg; + Error err=parse_property_data(sdfsdfg); + ERR_FAIL_COND_V(err,err); + r_name=name; + + return OK; + } + + + String data; + Error err = parse_property_data(data); + ERR_FAIL_COND_V(err!=OK,err); + + if (type=="nil") { + // uh do nothing + + } else if (type=="bool") { + // uh do nothing + if (data.nocasecmp_to("true")==0 || data.to_int()!=0) + r_v=true; + else + r_v=false; + } else if (type=="int") { + + r_v=data.to_int(); + } else if (type=="real") { + + r_v=data.to_double(); + } else if (type=="string") { + + String str=data; + unquote(str); + r_v=str; + } else if (type=="vector3") { + + + r_v=Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ); + + } else if (type=="vector2") { + + + r_v=Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ); + + } else if (type=="plane") { + + r_v=Plane( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="quaternion") { + + r_v=Quat( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="rect2") { + + r_v=Rect2( + Vector2( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double() + ), + Vector2( + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ) + ); + + + } else if (type=="aabb") { + + r_v=AABB( + Vector3( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double() + ), + Vector3( + data.get_slice(",",3).to_double(), + data.get_slice(",",4).to_double(), + data.get_slice(",",5).to_double() + ) + ); + + } else if (type=="matrix32") { + + Matrix32 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + m3.elements[i][j]=data.get_slice(",",i*2+j).to_double(); + } + } + r_v=m3; + + } else if (type=="matrix3") { + + Matrix3 m3; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + m3.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + } + r_v=m3; + + } else if (type=="transform") { + + Transform tr; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + tr.basis.elements[i][j]=data.get_slice(",",i*3+j).to_double(); + } + + } + tr.origin=Vector3( + data.get_slice(",",9).to_double(), + data.get_slice(",",10).to_double(), + data.get_slice(",",11).to_double() + ); + r_v=tr; + + } else if (type=="color") { + + r_v=Color( + data.get_slice(",",0).to_double(), + data.get_slice(",",1).to_double(), + data.get_slice(",",2).to_double(), + data.get_slice(",",3).to_double() + ); + + } else if (type=="node_path") { + + String str=data; + unquote(str); + r_v=NodePath( str ); + } else if (type=="input_event") { + + // ? + } else { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unrecognized tag in file: "+type); + ERR_FAIL_V(ERR_FILE_CORRUPT); + } + r_name=name; + return OK; +} + + + +int ResourceInteractiveLoaderXML::get_current_line() const { + + return lines; +} + + +uint8_t ResourceInteractiveLoaderXML::get_char() const { + + uint8_t c = f->get_8(); + if (c=='\n') + lines++; + return c; + +} + + + + +/// + +void ResourceInteractiveLoaderXML::set_local_path(const String& p_local_path) { + + res_path=p_local_path; +} + +Ref<Resource> ResourceInteractiveLoaderXML::get_resource() { + + return resource; +} +Error ResourceInteractiveLoaderXML::poll() { + + if (error!=OK) + return error; + + + bool exit; + Tag *tag = parse_tag(&exit); + + + if (!tag) { + error=ERR_FILE_CORRUPT; + if (!exit) // shouldn't have exited + ERR_FAIL_V(error); + error=ERR_FILE_EOF; + return error; + } + + RES res; + //Object *obj=NULL; + + bool main; + + if (tag->name=="ext_resource") { + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <ext_resource> missing 'path' field."); + ERR_FAIL_COND_V(!tag->args.has("path"),ERR_FILE_CORRUPT); + + String type; + if (tag->args.has("type")) + type=tag->args["type"]; + + String path = tag->args["path"]; + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <ext_resource> can't use a local path, this is a bug?."); + ERR_FAIL_COND_V(path.begins_with("local://"),ERR_FILE_CORRUPT); + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + } + + + RES res = ResourceLoader::load(path,type); + + if (res.is_null()) { + + if (ResourceLoader::get_abort_on_missing_resources()) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <ext_resource> referenced unexisting resource at: "+path); + ERR_FAIL_V(error); + } else { + ResourceLoader::notify_load_error("Resource Not Found: "+path); + } + } else { + + resource_cache.push_back(res); + } + + Error err = close_tag("ext_resource"); + if (err) + return error; + + + error=OK; + resource_current++; + return error; + + } else if (tag->name=="resource") { + + main=false; + } else if (tag->name=="main_resource") { + main=true; + } else { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": unexpected main tag: "+tag->name); + error=ERR_FILE_CORRUPT; + ERR_FAIL_V(error); + } + + + String type; + String path; + + if (!main) { + //loading resource + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <resource> missing 'len' field."); + ERR_FAIL_COND_V(!tag->args.has("path"),ERR_FILE_CORRUPT); + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <resource> missing 'type' field."); + ERR_FAIL_COND_V(!tag->args.has("type"),ERR_FILE_CORRUPT); + path=tag->args["path"]; + error=OK; + + if (path.begins_with("local://")) { + //built-in resource (but really external) + path=path.replace("local://",local_path+"::"); + } + + + if (ResourceCache::has(path)) { + Error err = close_tag(tag->name); + if (err) { + error=ERR_FILE_CORRUPT; + } + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Unable to close <resource> tag."); + ERR_FAIL_COND_V( err, err ); + resource_current++; + error=OK; + return OK; + } + + type = tag->args["type"]; + } else { + type=resource_type; + } + + Object *obj = ObjectTypeDB::instance(type); + if (!obj) { + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object of unrecognized type in file: "+type); + } + ERR_FAIL_COND_V(!obj,ERR_FILE_CORRUPT); + + Resource *r = obj->cast_to<Resource>(); + if (!r) { + error=ERR_FILE_CORRUPT; + memdelete(obj); //bye + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": Object type in resource field not a resource, type is: "+obj->get_type()); + ERR_FAIL_COND_V(!r,ERR_FILE_CORRUPT); + } + + res = RES( r ); + if (path!="") + r->set_path(path); + + //load properties + + while(true) { + + String name; + Variant v; + Error err; + err = parse_property(v,name); + if (err==ERR_FILE_EOF) //tag closed + break; + if (err!=OK) { + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": XML Parsing aborted."); + ERR_FAIL_COND_V(err!=OK,ERR_FILE_CORRUPT); + } + + obj->set(name,v); + } +#ifdef TOOLS_ENABLED + res->set_edited(false); +#endif + resource_cache.push_back(res); //keep it in mem until finished loading + resource_current++; + if (main) { + f->close(); + resource=res; + resource->set_path(res_path); + error=ERR_FILE_EOF; + return error; + + } + error=OK; + return OK; +} + +int ResourceInteractiveLoaderXML::get_stage() const { + + return resource_current; +} +int ResourceInteractiveLoaderXML::get_stage_count() const { + + return resources_total; +} + +ResourceInteractiveLoaderXML::~ResourceInteractiveLoaderXML() { + + memdelete(f); +} + +void ResourceInteractiveLoaderXML::get_dependencies(FileAccess *f,List<String> *p_dependencies) { + + + open(f); + ERR_FAIL_COND(error!=OK); + + while(true) { + bool exit; + Tag *tag = parse_tag(&exit); + + + if (!tag) { + error=ERR_FILE_CORRUPT; + ERR_FAIL_COND(!exit); + error=ERR_FILE_EOF; + return; + } + + if (tag->name!="ext_resource") { + + return; + } + + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <ext_resource> missing 'path' field."); + ERR_FAIL_COND(!tag->args.has("path")); + + String path = tag->args["path"]; + + ERR_EXPLAIN(local_path+":"+itos(get_current_line())+": <ext_resource> can't use a local path, this is a bug?."); + ERR_FAIL_COND(path.begins_with("local://")); + + if (path.find("://")==-1 && path.is_rel_path()) { + // path is relative to file being loaded, so convert to a resource path + path=Globals::get_singleton()->localize_path(local_path.get_base_dir()+"/"+path); + } + + p_dependencies->push_back(path); + + Error err = close_tag("ext_resource"); + if (err) + return; + + error=OK; + } + +} + + +void ResourceInteractiveLoaderXML::open(FileAccess *p_f) { + + error=OK; + + lines=1; + f=p_f; + + + ResourceInteractiveLoaderXML::Tag *tag = parse_tag(); + if (!tag || tag->name!="?xml" || !tag->args.has("version") || !tag->args.has("encoding") || tag->args["encoding"]!="UTF-8") { + + error=ERR_FILE_CORRUPT; + ResourceLoader::notify_load_error("XML is invalid (missing header tags)"); + ERR_EXPLAIN("Not a XML:UTF-8 File: "+local_path); + ERR_FAIL(); + } + + tag_stack.clear(); + + tag = parse_tag(); + + + if (!tag || tag->name!="resource_file" || !tag->args.has("type") || !tag->args.has("version")) { + + ResourceLoader::notify_load_error(local_path+": XML is not a valid resource file."); + error=ERR_FILE_CORRUPT; + ERR_EXPLAIN("Unrecognized XML File: "+local_path); + ERR_FAIL(); + } + + + if (tag->args.has("subresource_count")) + resources_total=tag->args["subresource_count"].to_int(); + resource_current=0; + resource_type=tag->args["type"]; + + String version = tag->args["version"]; + if (version.get_slice_count(".")!=2) { + + error=ERR_FILE_CORRUPT; + ResourceLoader::notify_load_error(local_path+":XML version string is invalid: "+version); + ERR_EXPLAIN("Invalid Version String '"+version+"'' in file: "+local_path); + ERR_FAIL(); + } + + int major = version.get_slice(".",0).to_int(); + int minor = version.get_slice(".",1).to_int(); + + if (major>VERSION_MAJOR || (major==VERSION_MAJOR && minor>VERSION_MINOR)) { + + error=ERR_FILE_UNRECOGNIZED; + ResourceLoader::notify_load_error(local_path+": File Format '"+version+"' is too new. Please upgrade to a newer engine version."); + ERR_EXPLAIN("File Format '"+version+"' is too new! Please upgrade to a a new engine version: "+local_path); + ERR_FAIL(); + + } + +} + +String ResourceInteractiveLoaderXML::recognize(FileAccess *p_f) { + + error=OK; + + lines=1; + f=p_f; + + ResourceInteractiveLoaderXML::Tag *tag = parse_tag(); + if (!tag || tag->name!="?xml" || !tag->args.has("version") || !tag->args.has("encoding") || tag->args["encoding"]!="UTF-8") { + + + return ""; //unrecognized + } + + tag_stack.clear(); + + tag = parse_tag(); + + if (!tag || tag->name!="resource_file" || !tag->args.has("type") || !tag->args.has("version")) { + + return ""; //unrecognized + } + + return tag->args["type"]; + +} + +///////////////////// + +Ref<ResourceInteractiveLoader> ResourceFormatLoaderXML::load_interactive(const String &p_path) { + + Error err; + FileAccess *f = FileAccess::open(p_path,FileAccess::READ,&err); + + + if (err!=OK) { + + ERR_FAIL_COND_V(err!=OK,Ref<ResourceInteractiveLoader>()); + } + + Ref<ResourceInteractiveLoaderXML> ria = memnew( ResourceInteractiveLoaderXML ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->open(f); + + return ria; +} + +void ResourceFormatLoaderXML::get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) const { + + if (p_type=="") { + get_recognized_extensions(p_extensions); + return; + } + + List<String> extensions; + ObjectTypeDB::get_extensions_for_type(p_type,&extensions); + + extensions.sort(); + + for(List<String>::Element *E=extensions.front();E;E=E->next()) { + String ext = E->get().to_lower(); + if (ext=="res") + continue; + p_extensions->push_back("x"+ext); + p_extensions->push_back(ext); + } + + p_extensions->push_back("xml"); + + +} +void ResourceFormatLoaderXML::get_recognized_extensions(List<String> *p_extensions) const{ + + List<String> extensions; + ObjectTypeDB::get_resource_base_extensions(&extensions); + extensions.sort(); + + for(List<String>::Element *E=extensions.front();E;E=E->next()) { + String ext = E->get().to_lower(); + if (ext=="res") + continue; + p_extensions->push_back("x"+ext); + } + + p_extensions->push_back("xml"); +} + +bool ResourceFormatLoaderXML::handles_type(const String& p_type) const{ + + return true; +} +String ResourceFormatLoaderXML::get_resource_type(const String &p_path) const{ + + + String ext=p_path.extension().to_lower(); + if (!ext.begins_with("x")) //a lie but.. + return ""; + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + + return ""; //could not rwead + } + + Ref<ResourceInteractiveLoaderXML> ria = memnew( ResourceInteractiveLoaderXML ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + String r = ria->recognize(f); + return r; +} + + +void ResourceFormatLoaderXML::get_dependencies(const String& p_path,List<String> *p_dependencies) { + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + if (!f) { + + ERR_FAIL(); + } + + Ref<ResourceInteractiveLoaderXML> ria = memnew( ResourceInteractiveLoaderXML ); + ria->local_path=Globals::get_singleton()->localize_path(p_path); + ria->res_path=ria->local_path; +// ria->set_local_path( Globals::get_singleton()->localize_path(p_path) ); + ria->get_dependencies(f,p_dependencies); + + +} + +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ +/****************************************************************************************/ + + + +void ResourceFormatSaverXMLInstance::escape(String& p_str) { + + p_str=p_str.replace("&","&"); + p_str=p_str.replace("<",">"); + p_str=p_str.replace(">","<"); + p_str=p_str.replace("'","'"); + p_str=p_str.replace("\"","""); + for (int i=1;i<32;i++) { + + char chr[2]={i,0}; + p_str=p_str.replace(chr,"&#"+String::num(i)+";"); + } + + +} +void ResourceFormatSaverXMLInstance::write_tabs(int p_diff) { + + for (int i=0;i<depth+p_diff;i++) { + + f->store_8('\t'); + } +} + +void ResourceFormatSaverXMLInstance::write_string(String p_str,bool p_escape) { + + /* write an UTF8 string */ + if (p_escape) + escape(p_str); + + f->store_string(p_str);; + /* + CharString cs=p_str.utf8(); + const char *data=cs.get_data(); + + while (*data) { + f->store_8(*data); + data++; + }*/ + + +} + +void ResourceFormatSaverXMLInstance::enter_tag(const char* p_tag,const String& p_args) { + + f->store_8('<'); + int cc = 0; + const char *c=p_tag; + while(*c) { + cc++; + c++; + } + f->store_buffer((const uint8_t*)p_tag,cc); + if (p_args.length()) { + f->store_8(' '); + f->store_string(p_args); + } + f->store_8('>'); + depth++; + +} +void ResourceFormatSaverXMLInstance::exit_tag(const char* p_tag) { + + depth--; + f->store_8('<'); + f->store_8('/'); + int cc = 0; + const char *c=p_tag; + while(*c) { + cc++; + c++; + } + f->store_buffer((const uint8_t*)p_tag,cc); + f->store_8('>'); + +} + +/* +static bool _check_type(const Variant& p_property) { + + if (p_property.get_type()==Variant::_RID) + return false; + if (p_property.get_type()==Variant::OBJECT) { + RES res = p_property; + if (res.is_null()) + return false; + } + + return true; +}*/ + +void ResourceFormatSaverXMLInstance::write_property(const String& p_name,const Variant& p_property,bool *r_ok) { + + if (r_ok) + *r_ok=false; + + const char* type; + String params; + bool oneliner=true; + + switch( p_property.get_type() ) { + + case Variant::NIL: type="nil"; break; + case Variant::BOOL: type="bool"; break; + case Variant::INT: type="int"; break; + case Variant::REAL: type="real"; break; + case Variant::STRING: type="string"; break; + case Variant::VECTOR2: type="vector2"; break; + case Variant::RECT2: type="rect2"; break; + case Variant::VECTOR3: type="vector3"; break; + case Variant::PLANE: type="plane"; break; + case Variant::_AABB: type="aabb"; break; + case Variant::QUAT: type="quaternion"; break; + case Variant::MATRIX32: type="matrix32"; break; + case Variant::MATRIX3: type="matrix3"; break; + case Variant::TRANSFORM: type="transform"; break; + case Variant::COLOR: type="color"; break; + case Variant::IMAGE: { + type="image"; + Image img=p_property; + if (img.empty()) { + write_tabs(); + enter_tag(type,"name=\""+p_name+"\""); + exit_tag(type); + if (r_ok) + *r_ok=true; + return; + } + params+="encoding=\"raw\""; + params+=" width=\""+itos(img.get_width())+"\""; + params+=" height=\""+itos(img.get_height())+"\""; + params+=" mipmaps=\""+itos(img.get_mipmaps())+"\""; + + switch(img.get_format()) { + + case Image::FORMAT_GRAYSCALE: params+=" format=\"grayscale\""; break; + case Image::FORMAT_INTENSITY: params+=" format=\"intensity\""; break; + case Image::FORMAT_GRAYSCALE_ALPHA: params+=" format=\"grayscale_alpha\""; break; + case Image::FORMAT_RGB: params+=" format=\"rgb\""; break; + case Image::FORMAT_RGBA: params+=" format=\"rgba\""; break; + case Image::FORMAT_INDEXED : params+=" format=\"indexed\""; break; + case Image::FORMAT_INDEXED_ALPHA: params+=" format=\"indexed_alpha\""; break; + case Image::FORMAT_BC1: params+=" format=\"bc1\""; break; + case Image::FORMAT_BC2: params+=" format=\"bc2\""; break; + case Image::FORMAT_BC3: params+=" format=\"bc3\""; break; + case Image::FORMAT_BC4: params+=" format=\"bc4\""; break; + case Image::FORMAT_BC5: params+=" format=\"bc5\""; break; + case Image::FORMAT_PVRTC2: params+=" format=\"pvrtc2\""; break; + case Image::FORMAT_PVRTC2_ALPHA: params+=" format=\"pvrtc2a\""; break; + case Image::FORMAT_PVRTC4: params+=" format=\"pvrtc4\""; break; + case Image::FORMAT_PVRTC4_ALPHA: params+=" format=\"pvrtc4a\""; break; + case Image::FORMAT_ETC: params+=" format=\"etc\""; break; + case Image::FORMAT_CUSTOM: params+=" format=\"custom\" custom_size=\""+itos(img.get_data().size())+"\""; break; + default: {} + } + } break; + case Variant::NODE_PATH: type="node_path"; break; + case Variant::OBJECT: { + type="resource"; + RES res = p_property; + if (res.is_null()) { + write_tabs(); + enter_tag(type,"name=\""+p_name+"\""); + exit_tag(type); + if (r_ok) + *r_ok=true; + + return; // don't save it + } + + params="resource_type=\""+res->get_save_type()+"\""; + + if (res->get_path().length() && res->get_path().find("::")==-1) { + //external resource + String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path(); + if (no_extension) + path=path.basename()+".*"; + escape(path); + params+=" path=\""+path+"\""; + } else { + + //internal resource + ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?"); + ERR_FAIL_COND(!resource_map.has(res)); + + params+=" path=\"local://"+itos(resource_map[res])+"\""; + } + + } break; + case Variant::INPUT_EVENT: type="input_event"; break; + case Variant::DICTIONARY: type="dictionary"; params="shared=\""+String(p_property.is_shared()?"true":"false")+"\""; oneliner=false; break; + case Variant::ARRAY: type="array"; params="len=\""+itos(p_property.operator Array().size())+"\" shared=\""+String(p_property.is_shared()?"true":"false")+"\""; oneliner=false; break; + + case Variant::RAW_ARRAY: type="raw_array"; params="len=\""+itos(p_property.operator DVector < uint8_t >().size())+"\""; break; + case Variant::INT_ARRAY: type="int_array"; params="len=\""+itos(p_property.operator DVector < int >().size())+"\""; break; + case Variant::REAL_ARRAY: type="real_array"; params="len=\""+itos(p_property.operator DVector < real_t >().size())+"\""; break; + case Variant::STRING_ARRAY: oneliner=false; type="string_array"; params="len=\""+itos(p_property.operator DVector < String >().size())+"\""; break; + case Variant::VECTOR2_ARRAY: type="vector2_array"; params="len=\""+itos(p_property.operator DVector < Vector2 >().size())+"\""; break; + case Variant::VECTOR3_ARRAY: type="vector3_array"; params="len=\""+itos(p_property.operator DVector < Vector3 >().size())+"\""; break; + case Variant::COLOR_ARRAY: type="color_array"; params="len=\""+itos(p_property.operator DVector < Color >().size())+"\""; break; + default: { + + ERR_PRINT("Unknown Variant type."); + ERR_FAIL(); + } + + } + + write_tabs(); + + if (p_name!="") { + if (params.length()) + enter_tag(type,"name=\""+p_name+"\" "+params); + else + enter_tag(type,"name=\""+p_name+"\""); + } else { + if (params.length()) + enter_tag(type," "+params); + else + enter_tag(type,String()); + } + + if (!oneliner) + f->store_8('\n'); + else + f->store_8(' '); + + + switch( p_property.get_type() ) { + + case Variant::NIL: { + + } break; + case Variant::BOOL: { + + write_string( p_property.operator bool() ? "True":"False" ); + } break; + case Variant::INT: { + + write_string( itos(p_property.operator int()) ); + } break; + case Variant::REAL: { + + write_string( rtos(p_property.operator real_t()) ); + } break; + case Variant::STRING: { + + String str=p_property; + escape(str); + str="\""+str+"\""; + write_string( str,false ); + } break; + case Variant::VECTOR2: { + + Vector2 v = p_property; + write_string( rtoss(v.x) +", "+rtoss(v.y) ); + } break; + case Variant::RECT2: { + + Rect2 aabb = p_property; + write_string( rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) ); + + } break; + case Variant::VECTOR3: { + + Vector3 v = p_property; + write_string( rtoss(v.x) +", "+rtoss(v.y)+", "+rtoss(v.z) ); + } break; + case Variant::PLANE: { + + Plane p = p_property; + write_string( rtoss(p.normal.x) +", "+rtoss(p.normal.y)+", "+rtoss(p.normal.z)+", "+rtoss(p.d) ); + + } break; + case Variant::_AABB: { + + AABB aabb = p_property; + write_string( rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.pos.z) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) +", "+rtoss(aabb.size.z) ); + + } break; + case Variant::QUAT: { + + Quat quat = p_property; + write_string( rtoss(quat.x)+", "+rtoss(quat.y)+", "+rtoss(quat.z)+", "+rtoss(quat.w)+", "); + + } break; + case Variant::MATRIX32: { + + String s; + Matrix32 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<2;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + write_string(s); + + } break; + case Variant::MATRIX3: { + + String s; + Matrix3 m3 = p_property; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + write_string(s); + + } break; + case Variant::TRANSFORM: { + + String s; + Transform t = p_property; + Matrix3 &m3 = t.basis; + for (int i=0;i<3;i++) { + for (int j=0;j<3;j++) { + + if (i!=0 || j!=0) + s+=", "; + s+=rtoss( m3.elements[i][j] ); + } + } + + s=s+", "+rtoss(t.origin.x) +", "+rtoss(t.origin.y)+", "+rtoss(t.origin.z); + + write_string(s); + } break; + + // misc types + case Variant::COLOR: { + + Color c = p_property; + write_string( rtoss(c.r) +", "+rtoss(c.g)+", "+rtoss(c.b)+", "+rtoss(c.a) ); + + } break; + case Variant::IMAGE: { + + String s; + Image img = p_property; + DVector<uint8_t> data = img.get_data(); + int len = data.size(); + DVector<uint8_t>::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i<len;i++) { + + uint8_t byte = ptr[i]; + const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + char str[3]={ hex[byte>>4], hex[byte&0xF], 0}; + s+=str; + } + + write_string(s); + } break; + case Variant::NODE_PATH: { + + String str=p_property; + escape(str); + str="\""+str+"\""; + write_string( str,false); + + } break; + + case Variant::OBJECT: { + /* this saver does not save resources in here + RES res = p_property; + + if (!res.is_null()) { + + String path=res->get_path(); + if (!res->is_shared() || !path.length()) { + // if no path, or path is from inside a scene + write_object( *res ); + } + + } + */ + + } break; + case Variant::INPUT_EVENT: { + + write_string( p_property.operator String() ); + } break; + case Variant::DICTIONARY: { + + Dictionary dict = p_property; + + + List<Variant> keys; + dict.get_key_list(&keys); + + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + //if (!_check_type(dict[E->get()])) + // continue; + + bool ok; + write_property("",E->get(),&ok); + ERR_CONTINUE(!ok); + + write_property("",dict[E->get()],&ok); + if (!ok) + write_property("",Variant()); //at least make the file consistent.. + } + + + + + } break; + case Variant::ARRAY: { + + Array array = p_property; + int len=array.size(); + for (int i=0;i<len;i++) { + + write_property("",array[i]); + + } + + } break; + + case Variant::RAW_ARRAY: { + + String s; + DVector<uint8_t> data = p_property; + int len = data.size(); + DVector<uint8_t>::Read r = data.read(); + const uint8_t *ptr=r.ptr();; + for (int i=0;i<len;i++) { + + uint8_t byte = ptr[i]; + const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + char str[3]={ hex[byte>>4], hex[byte&0xF], 0}; + s+=str; + } + + write_string(s,false); + + } break; + case Variant::INT_ARRAY: { + + DVector<int> data = p_property; + int len = data.size(); + DVector<int>::Read r = data.read(); + const int *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + + write_string(itos(ptr[i]),false); + } + + + + } break; + case Variant::REAL_ARRAY: { + + DVector<real_t> data = p_property; + int len = data.size(); + DVector<real_t>::Read r = data.read(); + const real_t *ptr=r.ptr();; + write_tabs(); + String cm=", " ; + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(cm,false); + write_string(rtoss(ptr[i]),false); + } + + + } break; + case Variant::STRING_ARRAY: { + + DVector<String> data = p_property; + int len = data.size(); + DVector<String>::Read r = data.read(); + const String *ptr=r.ptr();; + String s; + //write_string("\n"); + + + + for (int i=0;i<len;i++) { + + write_tabs(0); + String str=ptr[i]; + escape(str); + write_string("<string> \""+str+"\" </string>\n",false); + } + } break; + case Variant::VECTOR2_ARRAY: { + + DVector<Vector2> data = p_property; + int len = data.size(); + DVector<Vector2>::Read r = data.read(); + const Vector2 *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + write_string(rtoss(ptr[i].x),false); + write_string(", "+rtoss(ptr[i].y),false); + + } + + + } break; + case Variant::VECTOR3_ARRAY: { + + DVector<Vector3> data = p_property; + int len = data.size(); + DVector<Vector3>::Read r = data.read(); + const Vector3 *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + write_string(rtoss(ptr[i].x),false); + write_string(", "+rtoss(ptr[i].y),false); + write_string(", "+rtoss(ptr[i].z),false); + + } + + + } break; + case Variant::COLOR_ARRAY: { + + DVector<Color> data = p_property; + int len = data.size(); + DVector<Color>::Read r = data.read(); + const Color *ptr=r.ptr();; + write_tabs(); + + for (int i=0;i<len;i++) { + + if (i>0) + write_string(", ",false); + + write_string(rtoss(ptr[i].r),false); + write_string(", "+rtoss(ptr[i].g),false); + write_string(", "+rtoss(ptr[i].b),false); + write_string(", "+rtoss(ptr[i].a),false); + + } + + } break; + default: {} + + } + if (oneliner) + f->store_8(' '); + else + write_tabs(-1); + exit_tag(type); + + f->store_8('\n'); + + if (r_ok) + *r_ok=true; + +} + + +void ResourceFormatSaverXMLInstance::_find_resources(const Variant& p_variant,bool p_main) { + + + switch(p_variant.get_type()) { + case Variant::OBJECT: { + + + RES res = p_variant.operator RefPtr(); + + if (res.is_null()) + return; + + if (!p_main && (!bundle_resources ) && res->get_path().length() && res->get_path().find("::") == -1 ) { + external_resources.insert(res); + return; + } + + if (resource_map.has(res)) + return; + + List<PropertyInfo> property_list; + + res->get_property_list( &property_list ); + + List<PropertyInfo>::Element *I=property_list.front(); + + while(I) { + + PropertyInfo pi=I->get(); + + if (pi.usage&PROPERTY_USAGE_STORAGE || (bundle_resources && pi.usage&PROPERTY_USAGE_BUNDLE)) { + + Variant v=res->get(I->get().name); + _find_resources(v); + } + + I=I->next(); + } + + resource_map[ res ] = resource_map.size(); //saved after, so the childs it needs are available when loaded + saved_resources.push_back(res); + + } break; + case Variant::ARRAY: { + + Array varray=p_variant; + int len=varray.size(); + for(int i=0;i<len;i++) { + + Variant v=varray.get(i); + _find_resources(v); + } + + } break; + case Variant::DICTIONARY: { + + Dictionary d=p_variant; + List<Variant> keys; + d.get_key_list(&keys); + for(List<Variant>::Element *E=keys.front();E;E=E->next()) { + + Variant v = d[E->get()]; + _find_resources(v); + } + } break; + default: {} + } + +} + + + +Error ResourceFormatSaverXMLInstance::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + Error err; + f = FileAccess::open(p_path, FileAccess::WRITE,&err); + ERR_FAIL_COND_V( err, ERR_CANT_OPEN ); + FileAccessRef _fref(f); + + local_path = Globals::get_singleton()->localize_path(p_path); + + relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS; + skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES; + bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES; + no_extension=p_flags&ResourceSaver::FLAG_NO_EXTENSION; + depth=0; + + // save resources + _find_resources(p_resource,true); + + ERR_FAIL_COND_V(err!=OK,err); + + write_string("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>",false); //no escape + write_string("\n",false); + enter_tag("resource_file","type=\""+p_resource->get_type()+"\" subresource_count=\""+itos(saved_resources.size()+external_resources.size())+"\" version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\" version_name=\""+VERSION_FULL_NAME+"\""); + write_string("\n",false); + + for(Set<RES>::Element *E=external_resources.front();E;E=E->next()) { + + write_tabs(); + String p = E->get()->get_path(); + if (no_extension) + p=p.basename()+".*"; + + enter_tag("ext_resource","path=\""+p+"\" type=\""+E->get()->get_save_type()+"\""); //bundled + exit_tag("ext_resource"); //bundled + write_string("\n",false); + } + + + + for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) { + + RES res = E->get(); + ERR_CONTINUE(!resource_map.has(res)); + bool main = (E->next()==NULL); + + write_tabs(); + + if (main) + enter_tag("main_resource",""); //bundled + else if (res->get_path().length() && res->get_path().find("::") == -1 ) + enter_tag("resource","type=\""+res->get_type()+"\" path=\""+res->get_path()+"\""); //bundled + else + enter_tag("resource","type=\""+res->get_type()+"\" path=\"local://"+itos(resource_map[res])+"\""); + write_string("\n",false); + + + List<PropertyInfo> property_list; + res->get_property_list(&property_list); + for(List<PropertyInfo>::Element *PE = property_list.front();PE;PE=PE->next()) { + + + if (skip_editor && PE->get().name.begins_with("__editor")) + continue; + + if (PE->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && PE->get().usage&PROPERTY_USAGE_BUNDLE)) { + + String name = PE->get().name; + Variant value = res->get(name); + if (PE->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero()) + continue; + + write_property(name,value); + } + + + } + + write_string("\n",false); + write_tabs(-1); + if (main) + exit_tag("main_resource"); + else + exit_tag("resource"); + + write_string("\n",false); + } + + exit_tag("resource_file"); + f->close(); + //memdelete(f); + + return OK; +} + + + +Error ResourceFormatSaverXML::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + ResourceFormatSaverXMLInstance saver; + return saver.save(p_path,p_resource,p_flags); + +} + +bool ResourceFormatSaverXML::recognize(const RES& p_resource) const { + + + return true; // all recognized! +} +void ResourceFormatSaverXML::get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const { + + + //here comes the sun, lalalala + String base = p_resource->get_base_extension().to_lower(); + p_extensions->push_back("xml"); + if (base!="res") { + + p_extensions->push_back("x"+base); + } + +} diff --git a/core/io/resource_format_xml.h b/core/io/resource_format_xml.h new file mode 100644 index 0000000000..05313ffbd7 --- /dev/null +++ b/core/io/resource_format_xml.h @@ -0,0 +1,155 @@ +/*************************************************************************/ +/* resource_format_xml.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. */ +/*************************************************************************/ +#ifndef RESOURCE_FORMAT_XML_H +#define RESOURCE_FORMAT_XML_H + +#include "io/resource_loader.h" +#include "io/resource_saver.h" +#include "os/file_access.h" + + + +class ResourceInteractiveLoaderXML : public ResourceInteractiveLoader { + + String local_path; + String res_path; + + FileAccess *f; + + struct Tag { + + String name; + HashMap<String,String> args; + }; + + _FORCE_INLINE_ Error _parse_array_element(Vector<char> &buff,bool p_number_only,FileAccess *f,bool *end); + + int resources_total; + int resource_current; + String resource_type; + + mutable int lines; + uint8_t get_char() const; + int get_current_line() const; + +friend class ResourceFormatLoaderXML; + List<Tag> tag_stack; + + List<RES> resource_cache; + Tag* parse_tag(bool* r_exit=NULL,bool p_printerr=true); + Error close_tag(const String& p_name); + void unquote(String& p_str); + Error goto_end_of_tag(); + Error parse_property_data(String &r_data); + Error parse_property(Variant& r_v, String &r_name); + + Error error; + + RES resource; + +public: + + virtual void set_local_path(const String& p_local_path); + virtual Ref<Resource> get_resource(); + virtual Error poll(); + virtual int get_stage() const; + virtual int get_stage_count() const; + + void open(FileAccess *p_f); + String recognize(FileAccess *p_f); + void get_dependencies(FileAccess *p_f,List<String> *p_dependencies); + + + ~ResourceInteractiveLoaderXML(); + +}; + +class ResourceFormatLoaderXML : public ResourceFormatLoader { +public: + + virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path); + virtual void get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) const; + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String& p_type) const; + virtual String get_resource_type(const String &p_path) const; + virtual void get_dependencies(const String& p_path,List<String> *p_dependencies); + + +}; + + +//////////////////////////////////////////////////////////////////////////////////////////// + + +class ResourceFormatSaverXMLInstance { + + String local_path; + + + + bool no_extension; + bool relative_paths; + bool bundle_resources; + bool skip_editor; + FileAccess *f; + int depth; + Map<RES,int> resource_map; + List<RES> saved_resources; + Set<RES> external_resources; + + void enter_tag(const char* p_tag,const String& p_args=String()); + void exit_tag(const char* p_tag); + + void _find_resources(const Variant& p_variant,bool p_main=false); + void write_property(const String& p_name,const Variant& p_property,bool *r_ok=NULL); + + + void escape(String& p_str); + void write_tabs(int p_diff=0); + void write_string(String p_str,bool p_escape=true); + + +public: + + Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + + +}; + +class ResourceFormatSaverXML : public ResourceFormatSaver { +public: + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + virtual bool recognize(const RES& p_resource) const; + virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const; + + +}; + + +#endif // RESOURCE_FORMAT_XML_H diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp new file mode 100644 index 0000000000..3bae6be83c --- /dev/null +++ b/core/io/resource_loader.cpp @@ -0,0 +1,378 @@ +/*************************************************************************/ +/* resource_loader.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 "resource_loader.h" +#include "print_string.h" +#include "globals.h" +#include "path_remap.h" +#include "os/file_access.h" +#include "os/os.h" +ResourceFormatLoader *ResourceLoader::loader[MAX_LOADERS]; + +int ResourceLoader::loader_count=0; + + +Error ResourceInteractiveLoader::wait() { + + Error err = poll(); + while (err==OK) { + err=poll(); + } + + return err; +} + + +bool ResourceFormatLoader::recognize(const String& p_extension) const { + + + List<String> extensions; + get_recognized_extensions(&extensions); + for (List<String>::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(p_extension.extension())==0) + return true; + } + + return false; +} + +void ResourceFormatLoader::get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) const { + + if (p_type=="" || handles_type(p_type)) + get_recognized_extensions(p_extensions); +} + +void ResourceLoader::get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) { + + for (int i=0;i<loader_count;i++) { + loader[i]->get_recognized_extensions_for_type(p_type,p_extensions); + } + +} + +void ResourceInteractiveLoader::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("get_resource"),&ResourceInteractiveLoader::get_resource); + ObjectTypeDB::bind_method(_MD("poll"),&ResourceInteractiveLoader::poll); + ObjectTypeDB::bind_method(_MD("wait"),&ResourceInteractiveLoader::wait); + ObjectTypeDB::bind_method(_MD("get_stage"),&ResourceInteractiveLoader::get_stage); + ObjectTypeDB::bind_method(_MD("get_stage_count"),&ResourceInteractiveLoader::get_stage_count); +} + +class ResourceInteractiveLoaderDefault : public ResourceInteractiveLoader { + + OBJ_TYPE( ResourceInteractiveLoaderDefault, ResourceInteractiveLoader ); +public: + + Ref<Resource> resource; + + virtual void set_local_path(const String& p_local_path) { /*scene->set_filename(p_local_path);*/ } + virtual Ref<Resource> get_resource() { return resource; } + virtual Error poll() { return ERR_FILE_EOF; } + virtual int get_stage() const { return 1; } + virtual int get_stage_count() const { return 1; } + + ResourceInteractiveLoaderDefault() {} +}; + + + +Ref<ResourceInteractiveLoader> ResourceFormatLoader::load_interactive(const String &p_path) { + + //either this + Ref<Resource> res = load(p_path); + if (res.is_null()) + return Ref<ResourceInteractiveLoader>(); + + Ref<ResourceInteractiveLoaderDefault> ril = Ref<ResourceInteractiveLoaderDefault>( memnew( ResourceInteractiveLoaderDefault )); + ril->resource=res; + return ril; +} + +RES ResourceFormatLoader::load(const String &p_path,const String& p_original_path) { + + + //or this must be implemented + Ref<ResourceInteractiveLoader> ril = load_interactive(p_path); + if (!ril.is_valid()) + return RES(); + ril->set_local_path(p_original_path); + + while(true) { + + Error err = ril->poll(); + + if (err==ERR_FILE_EOF) { + return ril->get_resource(); + } + + ERR_FAIL_COND_V(err!=OK,RES()); + } + + return RES(); + +} + +void ResourceFormatLoader::get_dependencies(const String& p_path,List<String> *p_dependencies) { + + //do nothing by default +} + + +/////////////////////////////////// + + +RES ResourceLoader::load(const String &p_path,const String& p_type_hint,bool p_no_cache) { + + String local_path = Globals::get_singleton()->localize_path(p_path); + + local_path=find_complete_path(p_path,p_type_hint); + ERR_FAIL_COND_V(local_path=="",RES()); + + if (!p_no_cache && ResourceCache::has(local_path)) { + + if (OS::get_singleton()->is_stdout_verbose()) + print_line("load resource: "+local_path+" (cached)"); + + return RES( ResourceCache::get(local_path ) ); + } + + String remapped_path = PathRemap::get_singleton()->get_remap(local_path); + + if (OS::get_singleton()->is_stdout_verbose()) + print_line("load resource: "); + + String extension=remapped_path.extension(); + bool found=false; + + for (int i=0;i<loader_count;i++) { + + if (!loader[i]->recognize(extension)) + continue; + if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + continue; + found=true; + RES res = loader[i]->load(remapped_path,local_path); + if (res.is_null()) + continue; + if (!p_no_cache) + res->set_path(local_path); +#ifdef TOOLS_ENABLED + + res->set_edited(false); + if (timestamp_on_load) { + uint64_t mt = FileAccess::get_modified_time(remapped_path); + //printf("mt %s: %lli\n",remapped_path.utf8().get_data(),mt); + res->set_last_modified_time(mt); + } +#endif + return res; + } + + if (found) { + ERR_EXPLAIN("Failed loading resource: "+p_path); + } else { + ERR_EXPLAIN("No loader found for resource: "+p_path); + } + ERR_FAIL_V(RES()); + return RES(); +} + + +Ref<ResourceImportMetadata> ResourceLoader::load_import_metadata(const String &p_path) { + + + String local_path = Globals::get_singleton()->localize_path(p_path); + + String extension=p_path.extension(); + bool found=false; + Ref<ResourceImportMetadata> ret; + + for (int i=0;i<loader_count;i++) { + + if (!loader[i]->recognize(extension)) + continue; + found=true; + + Error err = loader[i]->load_import_metadata(local_path,ret); + if (err==OK) + break; + } + + return ret; + +} + + + +String ResourceLoader::find_complete_path(const String& p_path,const String& p_type) { + + String local_path = p_path; + if (local_path.ends_with("*")) { + + //find the extension for resource that ends with * + local_path = local_path.substr(0,local_path.length()-1); + List<String> extensions; + get_recognized_extensions_for_type(p_type,&extensions); + List<String> candidates; + + for(List<String>::Element *E=extensions.front();E;E=E->next()) { + + String path = local_path+E->get(); + + if (FileAccess::exists(path)) { + candidates.push_back(path); + } + + } + + + if (candidates.size()==0) { + return ""; + } else if (candidates.size()==1 || p_type=="") { + return candidates.front()->get(); + } else { + + for(List<String>::Element *E=candidates.front();E;E=E->next()) { + + String rt = get_resource_type(E->get()); + if (ObjectTypeDB::is_type(rt,p_type)) { + return E->get(); + } + } + + return ""; + } + } + + return local_path; +} + +Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_path,const String& p_type_hint,bool p_no_cache) { + + + + String local_path = Globals::get_singleton()->localize_path(p_path); + + local_path=find_complete_path(p_path,p_type_hint); + ERR_FAIL_COND_V(local_path=="",Ref<ResourceInteractiveLoader>()); + + + + if (!p_no_cache && ResourceCache::has(local_path)) { + + if (OS::get_singleton()->is_stdout_verbose()) + print_line("load resource: "+local_path+" (cached)"); + + return RES( ResourceCache::get(local_path ) ); + } + + if (OS::get_singleton()->is_stdout_verbose()) + print_line("load resource: "); + + String remapped_path = PathRemap::get_singleton()->get_remap(local_path); + + String extension=remapped_path.extension(); + bool found=false; + + for (int i=0;i<loader_count;i++) { + + if (!loader[i]->recognize(extension)) + continue; + if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + continue; + found=true; + Ref<ResourceInteractiveLoader> ril = loader[i]->load_interactive(remapped_path); + if (ril.is_null()) + continue; + if (!p_no_cache) + ril->set_local_path(local_path); + + return ril; + } + + if (found) { + ERR_EXPLAIN("Failed loading resource: "+p_path); + } else { + ERR_EXPLAIN("No loader found for resource: "+p_path); + } + ERR_FAIL_V(Ref<ResourceInteractiveLoader>()); + return Ref<ResourceInteractiveLoader>(); + +} + +void ResourceLoader::add_resource_format_loader(ResourceFormatLoader *p_format_loader) { + + ERR_FAIL_COND( loader_count >= MAX_LOADERS ); + loader[loader_count++]=p_format_loader; +} + +void ResourceLoader::get_dependencies(const String& p_path,List<String> *p_dependencies) { + + String local_path = Globals::get_singleton()->localize_path(p_path); + String remapped_path = PathRemap::get_singleton()->get_remap(local_path); + + String extension=remapped_path.extension(); + + for (int i=0;i<loader_count;i++) { + + if (!loader[i]->recognize(extension)) + continue; + //if (p_type_hint!="" && !loader[i]->handles_type(p_type_hint)) + // continue; + + loader[i]->get_dependencies(remapped_path,p_dependencies); + + } +} + + +String ResourceLoader::get_resource_type(const String &p_path) { + + String local_path = Globals::get_singleton()->localize_path(p_path); + String remapped_path = PathRemap::get_singleton()->get_remap(local_path); + String extension=remapped_path.extension(); + + bool found=false; + for (int i=0;i<loader_count;i++) { + + String result = loader[i]->get_resource_type(local_path); + if (result!="") + return result; + } + + return ""; + +} +ResourceLoadErrorNotify ResourceLoader::err_notify=NULL; +void *ResourceLoader::err_notify_ud=NULL; + +bool ResourceLoader::abort_on_missing_resource=true; +bool ResourceLoader::timestamp_on_load=false; + diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h new file mode 100644 index 0000000000..70b1a79582 --- /dev/null +++ b/core/io/resource_loader.h @@ -0,0 +1,114 @@ +/*************************************************************************/ +/* resource_loader.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. */ +/*************************************************************************/ +#ifndef RESOURCE_LOADER_H +#define RESOURCE_LOADER_H + +#include "resource.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class ResourceInteractiveLoader : public Reference { + + OBJ_TYPE(ResourceInteractiveLoader,Reference); +protected: + + static void _bind_methods(); +public: + + virtual void set_local_path(const String& p_local_path)=0; + virtual Ref<Resource> get_resource()=0; + virtual Error poll()=0; + virtual int get_stage() const=0; + virtual int get_stage_count() const=0; + virtual Error wait(); + + ResourceInteractiveLoader() {} +}; + + +class ResourceFormatLoader { +public: + + virtual Ref<ResourceInteractiveLoader> load_interactive(const String &p_path); + virtual RES load(const String &p_path,const String& p_original_path=""); + virtual void get_recognized_extensions(List<String> *p_extensions) const=0; + virtual void get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions) const; + bool recognize(const String& p_extension) const; + virtual bool handles_type(const String& p_type) const=0; + virtual String get_resource_type(const String &p_path) const=0; + virtual void get_dependencies(const String& p_path,List<String> *p_dependencies); + virtual Error load_import_metadata(const String &p_path, Ref<ResourceImportMetadata>& r_var) const { return ERR_UNAVAILABLE; } + + virtual ~ResourceFormatLoader() {} +}; + + +typedef void (*ResourceLoadErrorNotify)(void *p_ud,const String& p_text); + + +class ResourceLoader { + + enum { + MAX_LOADERS=64 + }; + + static ResourceFormatLoader *loader[MAX_LOADERS]; + static int loader_count; + static bool timestamp_on_load; + + static void* err_notify_ud; + static ResourceLoadErrorNotify err_notify; + static bool abort_on_missing_resource; + + static String find_complete_path(const String& p_path,const String& p_type); +public: + + + + static Ref<ResourceInteractiveLoader> load_interactive(const String &p_path,const String& p_type_hint="",bool p_no_cache=false); + static RES load(const String &p_path,const String& p_type_hint="",bool p_no_cache=false); + static Ref<ResourceImportMetadata> load_import_metadata(const String &p_path); + + static void get_recognized_extensions_for_type(const String& p_type,List<String> *p_extensions); + static void add_resource_format_loader(ResourceFormatLoader *p_format_loader); + static String get_resource_type(const String &p_path); + static void get_dependencies(const String& p_path,List<String> *p_dependencies); + + + static void set_timestamp_on_load(bool p_timestamp) { timestamp_on_load=p_timestamp; } + + static void notify_load_error(const String& p_err) { if (err_notify) err_notify(err_notify_ud,p_err); } + static void set_error_notify_func(void* p_ud,ResourceLoadErrorNotify p_err_notify) { err_notify=p_err_notify; err_notify_ud=p_ud;} + static void set_abort_on_missing_resources(bool p_abort) { abort_on_missing_resource=p_abort; } + static bool get_abort_on_missing_resources() { return abort_on_missing_resource; } +}; + +#endif diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp new file mode 100644 index 0000000000..598f517d76 --- /dev/null +++ b/core/io/resource_saver.cpp @@ -0,0 +1,127 @@ +/*************************************************************************/ +/* resource_saver.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 "resource_saver.h" +#include "globals.h" +#include "os/file_access.h" +#include "script_language.h" +#include "resource_loader.h" + +ResourceFormatSaver *ResourceSaver::saver[MAX_SAVERS]; + +int ResourceSaver::saver_count=0; +bool ResourceSaver::timestamp_on_save=false; +ResourceSavedCallback ResourceSaver::save_callback=0; + +Error ResourceSaver::save(const String &p_path,const RES& p_resource,uint32_t p_flags) { + + String extension=p_path.extension(); + Error err=ERR_FILE_UNRECOGNIZED; + + for (int i=0;i<saver_count;i++) { + + if (!saver[i]->recognize(p_resource)) + continue; + + List<String> extensions; + bool recognized=false; + saver[i]->get_recognized_extensions(p_resource,&extensions); + + for (List<String>::Element *E=extensions.front();E;E=E->next()) { + + if (E->get().nocasecmp_to(extension.extension())==0) + recognized=true; + } + + if (!recognized) + continue; + + String old_path=p_resource->get_path(); + + + String local_path=Globals::get_singleton()->localize_path(p_path); + + RES rwcopy = p_resource; + if (p_flags&FLAG_CHANGE_PATH) + rwcopy->set_path(local_path); + + err = saver[i]->save(p_path,p_resource,p_flags); + + if (err == OK ) { + +#ifdef TOOLS_ENABLED + + ((Resource*)p_resource.ptr())->set_edited(false); + if (timestamp_on_save) { + uint64_t mt = FileAccess::get_modified_time(p_path); + + ((Resource*)p_resource.ptr())->set_last_modified_time(mt); + } +#endif + + if (p_flags&FLAG_CHANGE_PATH) + rwcopy->set_path(old_path); + + if (save_callback && p_path.begins_with("res://")) + save_callback(p_path); + + return OK; + } else { + + } + } + + return err; +} + + +void ResourceSaver::set_save_callback(ResourceSavedCallback p_callback) { + + save_callback=p_callback; +} + + +void ResourceSaver::get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) { + + + for (int i=0;i<saver_count;i++) { + + saver[i]->get_recognized_extensions(p_resource,p_extensions); + } + +} + +void ResourceSaver::add_resource_format_saver(ResourceFormatSaver *p_format_saver) { + + ERR_FAIL_COND( saver_count >= MAX_SAVERS ); + saver[saver_count++]=p_format_saver; +} + + + + diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h new file mode 100644 index 0000000000..4b794247e0 --- /dev/null +++ b/core/io/resource_saver.h @@ -0,0 +1,95 @@ +/*************************************************************************/ +/* resource_saver.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. */ +/*************************************************************************/ +#ifndef RESOURCE_SAVER_H +#define RESOURCE_SAVER_H + +#include "resource.h" + +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + + + + + + +class ResourceFormatSaver { +public: + + virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0)=0; + virtual bool recognize(const RES& p_resource) const=0; + virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const=0; + + virtual ~ResourceFormatSaver() {} +}; + +typedef void (*ResourceSavedCallback)(const String& p_path); + +class ResourceSaver { + + enum { + MAX_SAVERS=64 + }; + + static ResourceFormatSaver *saver[MAX_SAVERS]; + static int saver_count; + static bool timestamp_on_save; + static ResourceSavedCallback save_callback; + + +public: + + enum SaverFlags { + + FLAG_RELATIVE_PATHS=1, + FLAG_BUNDLE_RESOURCES=2, + FLAG_CHANGE_PATH=4, + FLAG_OMIT_EDITOR_PROPERTIES=8, + FLAG_SAVE_BIG_ENDIAN=16, + FLAG_COMPRESS=32, + FLAG_NO_EXTENSION=64, + + + }; + + + static Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0); + static void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions); + static void add_resource_format_saver(ResourceFormatSaver *p_format_saver); + + static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save=p_timestamp; } + static void set_save_callback(ResourceSavedCallback p_callback); + + + +}; + + +#endif diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp new file mode 100644 index 0000000000..0eae660373 --- /dev/null +++ b/core/io/stream_peer.cpp @@ -0,0 +1,126 @@ +/*************************************************************************/ +/* stream_peer.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 "stream_peer.h" + + +Error StreamPeer::_put_data(const DVector<uint8_t>& p_data) { + + int len = p_data.size(); + if (len==0) + return OK; + DVector<uint8_t>::Read r = p_data.read(); + return put_data(&r[0],len); +} + +Array StreamPeer::_put_partial_data(const DVector<uint8_t>& p_data) { + + Array ret; + + int len = p_data.size(); + if (len==0) { + ret.push_back(OK); + ret.push_back(0); + return ret; + } + + DVector<uint8_t>::Read r = p_data.read(); + int sent; + Error err = put_partial_data(&r[0],len,sent); + + if (err!=OK) { + sent=0; + } + ret.push_back(err); + ret.push_back(sent); + return ret; +} + + +Array StreamPeer::_get_data(int p_bytes) { + + Array ret; + + DVector<uint8_t> data; + data.resize(p_bytes); + if (data.size()!=p_bytes) { + + ret.push_back(ERR_OUT_OF_MEMORY); + ret.push_back(DVector<uint8_t>()); + return ret; + } + + DVector<uint8_t>::Write w = data.write(); + Error err = get_data(&w[0],p_bytes); + w = DVector<uint8_t>::Write(); + ret.push_back(err); + ret.push_back(data); + return ret; + +} + +Array StreamPeer::_get_partial_data(int p_bytes) { + + Array ret; + + DVector<uint8_t> data; + data.resize(p_bytes); + if (data.size()!=p_bytes) { + + ret.push_back(ERR_OUT_OF_MEMORY); + ret.push_back(DVector<uint8_t>()); + return ret; + } + + DVector<uint8_t>::Write w = data.write(); + int received; + Error err = get_partial_data(&w[0],p_bytes,received); + w = DVector<uint8_t>::Write(); + + if (err!=OK) { + data.resize(0); + } else if (received!=data.size()) { + + data.resize(received); + } + + ret.push_back(err); + ret.push_back(data); + return ret; + +} + + +void StreamPeer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("put_data","data"),&StreamPeer::_put_data); + ObjectTypeDB::bind_method(_MD("put_partial_data","data"),&StreamPeer::_put_partial_data); + + ObjectTypeDB::bind_method(_MD("get_data","bytes"),&StreamPeer::_get_data); + ObjectTypeDB::bind_method(_MD("get_partial_data","bytes"),&StreamPeer::_get_partial_data); +} diff --git a/core/io/stream_peer.h b/core/io/stream_peer.h new file mode 100644 index 0000000000..84552cfd3e --- /dev/null +++ b/core/io/stream_peer.h @@ -0,0 +1,58 @@ +/*************************************************************************/ +/* stream_peer.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. */ +/*************************************************************************/ +#ifndef STREAM_PEER_H +#define STREAM_PEER_H + +#include "reference.h" + +class StreamPeer : public Reference { + OBJ_TYPE( StreamPeer, Reference ); + OBJ_CATEGORY("Networking"); +protected: + static void _bind_methods(); + + //bind helpers + Error _put_data(const DVector<uint8_t>& p_data); + Array _put_partial_data(const DVector<uint8_t>& p_data); + + Array _get_data(int p_bytes); + Array _get_partial_data(int p_bytes); + +public: + + virtual Error put_data(const uint8_t* p_data,int p_bytes)=0; ///< put a whole chunk of data, blocking until it sent + virtual Error put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent)=0; ///< put as much data as possible, without blocking. + + virtual Error get_data(uint8_t* p_buffer, int p_bytes)=0; ///< read p_bytes of data, if p_bytes > available, it will block + virtual Error get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received)=0; ///< read as much data as p_bytes into buffer, if less was read, return in r_received + + StreamPeer() {} +}; + +#endif // STREAM_PEER_H diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp new file mode 100644 index 0000000000..0fdaab885a --- /dev/null +++ b/core/io/stream_peer_tcp.cpp @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* stream_peer_tcp.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 "stream_peer_tcp.h"
+
+StreamPeerTCP* (*StreamPeerTCP::_create)()=NULL;
+
+void StreamPeerTCP::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("connect","host","ip"),&StreamPeerTCP::connect);
+ ObjectTypeDB::bind_method(_MD("is_connected"),&StreamPeerTCP::is_connected);
+ ObjectTypeDB::bind_method(_MD("get_connected_host"),&StreamPeerTCP::get_connected_host);
+ ObjectTypeDB::bind_method(_MD("get_connected_port"),&StreamPeerTCP::get_connected_port);
+ ObjectTypeDB::bind_method(_MD("disconnect"),&StreamPeerTCP::disconnect);
+}
+
+Ref<StreamPeerTCP> StreamPeerTCP::create() {
+
+ if (!_create)
+ return Ref<StreamPeerTCP>();
+ return Ref<StreamPeerTCP>(_create());
+}
+
+StreamPeerTCP::StreamPeerTCP() {
+
+}
+
+StreamPeerTCP::~StreamPeerTCP() {
+
+};
+
diff --git a/core/io/stream_peer_tcp.h b/core/io/stream_peer_tcp.h new file mode 100644 index 0000000000..428ccd3d32 --- /dev/null +++ b/core/io/stream_peer_tcp.h @@ -0,0 +1,75 @@ +/*************************************************************************/ +/* stream_peer_tcp.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. */ +/*************************************************************************/ +#ifndef STREAM_PEER_TCP_H
+#define STREAM_PEER_TCP_H
+
+#include "stream_peer.h"
+
+#include "ip_address.h"
+
+class StreamPeerTCP : public StreamPeer {
+
+ OBJ_TYPE( StreamPeerTCP, StreamPeer );
+ OBJ_CATEGORY("Networking");
+
+public:
+
+ enum Status {
+
+ STATUS_NONE,
+ STATUS_CONNECTING,
+ STATUS_CONNECTED,
+ STATUS_ERROR,
+ };
+
+protected:
+
+ static StreamPeerTCP* (*_create)();
+ static void _bind_methods();
+
+public:
+
+ virtual Error connect(const IP_Address& p_host, uint16_t p_port)=0;
+
+ //read/write from streampeer
+
+ virtual bool is_connected() const=0;
+ virtual Status get_status() const=0;
+ virtual void disconnect()=0;
+ virtual IP_Address get_connected_host() const=0;
+ virtual uint16_t get_connected_port() const=0;
+ virtual void set_nodelay(bool p_enabled)=0;
+
+ static Ref<StreamPeerTCP> create();
+
+ StreamPeerTCP();
+ ~StreamPeerTCP();
+};
+
+#endif
diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp new file mode 100644 index 0000000000..06419b9c6b --- /dev/null +++ b/core/io/tcp_server.cpp @@ -0,0 +1,62 @@ +/*************************************************************************/ +/* tcp_server.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 "tcp_server.h" + +TCP_Server* (*TCP_Server::_create)()=NULL; + +Ref<TCP_Server> TCP_Server::create() { + + if (!_create) + return NULL; + return Ref<TCP_Server>(_create()); +} + +Error TCP_Server::_listen(uint16_t p_port,DVector<String> p_accepted_hosts) { + + List<String> hosts; + for(int i=0;i<p_accepted_hosts.size();i++) + hosts.push_back(p_accepted_hosts.get(i)); + + return listen(p_port,hosts.size()?&hosts:NULL); + +} + +void TCP_Server::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("listen","port","accepted_hosts"),&TCP_Server::_listen,DEFVAL(DVector<String>())); + ObjectTypeDB::bind_method(_MD("is_connection_available"),&TCP_Server::is_connection_available); + ObjectTypeDB::bind_method(_MD("take_connection"),&TCP_Server::take_connection); + ObjectTypeDB::bind_method(_MD("stop"),&TCP_Server::stop); + +} + + +TCP_Server::TCP_Server() +{ +} diff --git a/core/io/tcp_server.h b/core/io/tcp_server.h new file mode 100644 index 0000000000..31949be3b6 --- /dev/null +++ b/core/io/tcp_server.h @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* tcp_server.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. */ +/*************************************************************************/ +#ifndef TCP_SERVER_H +#define TCP_SERVER_H + +#include "io/stream_peer.h" +#include "io/ip.h" +#include "stream_peer_tcp.h" + +class TCP_Server : public Reference { + + OBJ_TYPE( TCP_Server, Reference ); +protected: + + static TCP_Server* (*_create)(); + + //bind helper + Error _listen(uint16_t p_port,DVector<String> p_accepted_hosts=DVector<String>()); + static void _bind_methods(); +public: + + virtual Error listen(uint16_t p_port,const List<String> *p_accepted_hosts=NULL)=0; + virtual bool is_connection_available() const=0; + virtual Ref<StreamPeerTCP> take_connection()=0; + + virtual void stop()=0; //stop listening + + static Ref<TCP_Server> create(); + + TCP_Server(); +}; + +#endif // TCP_SERVER_H diff --git a/core/io/translation_loader_po.cpp b/core/io/translation_loader_po.cpp new file mode 100644 index 0000000000..0d42cebb41 --- /dev/null +++ b/core/io/translation_loader_po.cpp @@ -0,0 +1,201 @@ +/*************************************************************************/ +/* translation_loader_po.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 "translation_loader_po.h" +#include "os/file_access.h" +#include "translation.h" + +RES TranslationLoaderPO::load(const String &p_path,const String& p_original_path) { + + FileAccess *f=FileAccess::open(p_path,FileAccess::READ); + ERR_FAIL_COND_V(!f,RES()); + + String l = f->get_line(); + + enum Status { + + STATUS_NONE, + STATUS_READING_ID, + STATUS_READING_STRING, + }; + + Status status=STATUS_NONE; + + String msg_id; + String msg_str; + String config; + + Ref<Translation> translation = Ref<Translation>( memnew( Translation )); + int line = 1; + + while(true) { + + String l = f->get_line(); + + if (f->eof_reached()) { + + if ( status == STATUS_READING_STRING) { + + if (msg_id!="") + translation->add_message(msg_id,msg_str); + else if (config=="") + config=msg_str; + break; + + } else if ( status==STATUS_NONE) + break; + + memdelete(f); + ERR_EXPLAIN(p_path+":"+itos(line)+" Unexpected EOF while reading 'msgid' at file: "); + ERR_FAIL_V(RES()); + } + + l=l.strip_edges(); + + if (l.begins_with("msgid")) { + + if (status==STATUS_READING_ID) { + + memdelete(f); + ERR_EXPLAIN(p_path+":"+itos(line)+" nexpected 'msgid', was expecting 'msgstr' while parsing: "); + ERR_FAIL_V(RES()); + } + + if (msg_id!="") + translation->add_message(msg_id,msg_str); + else if (config=="") + config=msg_str; + + l=l.substr(5,l.length()).strip_edges(); + status=STATUS_READING_ID; + msg_id=""; + msg_str=""; + } + + if (l.begins_with("msgstr")) { + + if (status!=STATUS_READING_ID) { + + memdelete(f); + ERR_EXPLAIN(p_path+":"+itos(line)+" Unexpected 'msgstr', was expecting 'msgid' while parsing: "); + ERR_FAIL_V(RES()); + } + + l=l.substr(6,l.length()).strip_edges(); + status=STATUS_READING_STRING; + } + + if (l=="" || l.begins_with("#")) { + line++; + continue; //nothing to read or comment + } + + if (!l.begins_with("\"") || status==STATUS_NONE) { + //not a string? failure! + ERR_EXPLAIN(p_path+":"+itos(line)+" Invalid line '"+l+"' while parsing: "); + ERR_FAIL_V(RES()); + + } + + l=l.substr(1,l.length()); + //find final quote + int end_pos=-1; + for(int i=0;i<l.length();i++) { + + if (l[i]=='"' && (i==0 || l[i-1]!='\\')) { + end_pos=i; + break; + } + } + + if (end_pos==-1) { + ERR_EXPLAIN(p_path+":"+itos(line)+" Expected '\"' at end of message while parsing file: "); + ERR_FAIL_V(RES()); + } + + l=l.substr(0,end_pos); + l=l.c_unescape(); + + + if (status==STATUS_READING_ID) + msg_id+=l; + else + msg_str+=l; + + line++; + } + + + f->close(); + memdelete(f); + + if (config=="") { + ERR_EXPLAIN("No config found in file: "+p_path); + ERR_FAIL_V(RES()); + } + + Vector<String> configs = config.split("\n"); + for(int i=0;i<configs.size();i++) { + + String c = configs[i].strip_edges(); + int p = c.find(":"); + if (p==-1) + continue; + String prop = c.substr(0,p).strip_edges(); + String value = c.substr(p+1,c.length()).strip_edges(); + + if (prop=="X-Language") { + translation->set_locale(value); + } + } + + + return translation; + +} + +void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions) const{ + + p_extensions->push_back("po"); + //p_extensions->push_back("mo"); //mo in the future... +} +bool TranslationLoaderPO::handles_type(const String& p_type) const{ + + return (p_type=="Translation"); +} + +String TranslationLoaderPO::get_resource_type(const String &p_path) const { + + if (p_path.extension().to_lower()=="po") + return "Translation"; + return ""; +} + +TranslationLoaderPO::TranslationLoaderPO() +{ +} diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h new file mode 100644 index 0000000000..7a0c4e10dc --- /dev/null +++ b/core/io/translation_loader_po.h @@ -0,0 +1,46 @@ +/*************************************************************************/ +/* translation_loader_po.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. */ +/*************************************************************************/ +#ifndef TRANSLATION_LOADER_PO_H +#define TRANSLATION_LOADER_PO_H + +#include "io/resource_loader.h" + +class TranslationLoaderPO : public ResourceFormatLoader { +public: + + virtual RES load(const String &p_path,const String& p_original_path=""); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String& p_type) const; + virtual String get_resource_type(const String &p_path) const; + + + TranslationLoaderPO(); +}; + +#endif // TRANSLATION_LOADER_PO_H diff --git a/core/io/unzip.c b/core/io/unzip.c new file mode 100644 index 0000000000..ac72457f38 --- /dev/null +++ b/core/io/unzip.c @@ -0,0 +1,2216 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include <stddef.h> +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include <errno.h> +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; + int extra_size; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1<c2) + return -1; + if (c1>c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize; + ZPOS64_T uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize; + ZPOS64_T uReadPos; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + return NULL; // standard i/o not supported + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) { + printf("no stream\n"); + return NULL; + }; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pos<us.offset_central_dir+us.size_central_dir) && + (err==UNZ_OK)) + err=UNZ_BADZIPFILE; + + if (err!=UNZ_OK) + { + printf("err is %i, %x\n", err, err); + ZCLOSE64(us.z_filefunc, us.filestream); + return NULL; + } + + us.byte_before_the_zipfile = central_pos - + (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + us.encrypted = 0; + + + s=(unz64_s*)ALLOC(sizeof(unz64_s)); + if( s != NULL) + { + *s=us; + unzGoToFirstFile((unzFile)s); + } + return (unzFile)s; +} + + +extern unzFile ZEXPORT unzOpen2 (const char *path, + zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return unzOpenInternal(path, &zlib_filefunc64_32_def_fill, 0); + } + else + return unzOpenInternal(path, NULL, 0); +} + +extern unzFile ZEXPORT unzOpen2_64 (const void *path, + zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return unzOpenInternal(path, &zlib_filefunc64_32_def_fill, 1); + } + else + return unzOpenInternal(path, NULL, 1); +} + +extern unzFile ZEXPORT unzOpen (const char *path) +{ + return unzOpenInternal(path, NULL, 0); +} + +extern unzFile ZEXPORT unzOpen64 (const void *path) +{ + return unzOpenInternal(path, NULL, 1); +} + +extern void* unzGetOpaque(unzFile file) { + + unz64_s* s; + if (file==NULL) + return NULL; + s=(unz64_s*)file; + + return s->z_filefunc.zfile_func64.opaque; +}; + +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzClose (unzFile file) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename<fileNameBufferSize) + { + *(szFileName+file_info.size_filename)='\0'; + uSizeRead = file_info.size_filename; + } + else + uSizeRead = fileNameBufferSize; + + if ((file_info.size_filename>0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extra<extraFieldBufferSize) + uSizeRead = file_info.size_file_extra; + else + uSizeRead = extraFieldBufferSize; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == (ZPOS64_T)(unsigned long)-1) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == (ZPOS64_T)(unsigned long)-1) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == (unsigned long)-1) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_comment<commentBufferSize) + { + *(szComment+file_info.size_file_comment)='\0'; + uSizeRead = file_info.size_file_comment; + } + else + uSizeRead = commentBufferSize; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if (err==UNZ_OK) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + pfile_in_zip_read_info->extra_size = iSizeVar; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzSeekCurrentFile(unzFile file, int pos) { + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) { // don't know how to support bzip + return UNZ_INTERNALERROR; + }; + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) { + + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size - pos; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size - pos; + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + pfile_in_zip_read_info->extra_size + pos; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + pfile_in_zip_read_info->stream.total_out = pos; + + return ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->byte_before_the_zipfile + pfile_in_zip_read_info->pos_in_zipfile, + ZLIB_FILEFUNC_SEEK_SET); + + } else { // gzip + + if (pos < pfile_in_zip_read_info->stream.total_out) { // negative seek, rewind + + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + pfile_in_zip_read_info->extra_size; + + (void)inflateReset(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + pfile_in_zip_read_info->stream.total_out = 0; + pfile_in_zip_read_info->stream.next_in = 0; + }; + + // not sure where to read, so read on the stack + { + char buf[512]; + int to_read = pos - pfile_in_zip_read_info->stream.total_out; + while (to_read) { + + int len = to_read > sizeof(buf)?sizeof(buf):to_read; + int read = unzReadCurrentFile(file, buf, len); + if (read < 0) { + return read; + }; + to_read -= read; + if (read == UNZ_EOF) { + return pos; + }; + }; + }; + }; + + return pos; +}; + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressed<uReadThis) + uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;i<uReadThis;i++) + pfile_in_zip_read_info->read_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;i<uDoCopy;i++) + *(pfile_in_zip_read_info->stream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/core/io/unzip.h b/core/io/unzip.h new file mode 100644 index 0000000000..fe7ad1ddf5 --- /dev/null +++ b/core/io/unzip.h @@ -0,0 +1,445 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern void* unzGetOpaque(unzFile file); + + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern int ZEXPORT unzSeekCurrentFile(unzFile file, int pos); +/* + Seek to position in uncompressed data +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp new file mode 100644 index 0000000000..150643b2e1 --- /dev/null +++ b/core/io/xml_parser.cpp @@ -0,0 +1,576 @@ +/*************************************************************************/ +/* xml_parser.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 "xml_parser.h" +#include "print_string.h" +//#define DEBUG_XML + +static bool _equalsn(const CharType* str1, const CharType* str2, int len) { + int i; + for(i=0; str1[i] && str2[i] && i < len; ++i) + if (str1[i] != str2[i]) + return false; + + // if one (or both) of the strings was smaller then they + // are only equal if they have the same lenght + return (i == len) || (str1[i] == 0 && str2[i] == 0); +} + + +String XMLParser::_replace_special_characters(const String& origstr) { + + int pos = origstr.find("&"); + int oldPos = 0; + + if (pos == -1) + return origstr; + + String newstr; + + while(pos != -1 && pos < origstr.length()-2) { + // check if it is one of the special characters + + int specialChar = -1; + for (int i=0; i<(int)special_characters.size(); ++i) + { + const CharType* p = &origstr[pos]+1; + + if (_equalsn(&special_characters[i][1], p, special_characters[i].length()-1)) + { + specialChar = i; + break; + } + } + + if (specialChar != -1) + { + newstr+=(origstr.substr(oldPos, pos - oldPos)); + newstr+=(special_characters[specialChar][0]); + pos += special_characters[specialChar].length(); + } + else + { + newstr+=(origstr.substr(oldPos, pos - oldPos + 1)); + pos += 1; + } + + // find next & + oldPos = pos; + pos = origstr.find("&", pos); + } + + if (oldPos < origstr.length()-1) + newstr+=(origstr.substr(oldPos, origstr.length()-oldPos)); + + return newstr; +} + + +static inline bool _is_white_space(char c) +{ + return (c==' ' || c=='\t' || c=='\n' || c=='\r'); +} + + +//! sets the state that text was found. Returns true if set should be set +bool XMLParser::_set_text(char* start, char* end) { + // check if text is more than 2 characters, and if not, check if there is + // only white space, so that this text won't be reported + if (end - start < 3) + { + char* p = start; + for(; p != end; ++p) + if (!_is_white_space(*p)) + break; + + if (p == end) + return false; + } + + // set current text to the parsed text, and replace xml special characters + String s = String::utf8(start, (int)(end - start)); + node_name = _replace_special_characters(s); + + // current XML node type is text + node_type = NODE_TEXT; + + return true; +} + +void XMLParser::_parse_closing_xml_element() { + node_type = NODE_ELEMENT_END; + node_empty = false; + attributes.clear(); + + ++P; + const char* pBeginClose = P; + + while(*P != '>') + ++P; + + node_name = String::utf8(pBeginClose, (int)(P - pBeginClose)); +#ifdef DEBUG_XML + print_line("XML CLOSE: "+node_name); +#endif + ++P; +} + +void XMLParser::_ignore_definition() { + node_type = NODE_UNKNOWN; + + char *F=P; + // move until end marked with '>' reached + while(*P != '>') + ++P; + node_name.parse_utf8(F,P-F); + ++P; +} + +bool XMLParser::_parse_cdata() { + + if (*(P+1) != '[') + return false; + + node_type = NODE_CDATA; + + // skip '<![CDATA[' + int count=0; + while( *P && count<8 ) + { + ++P; + ++count; + } + + if (!*P) + return true; + + char *cDataBegin = P; + char *cDataEnd = 0; + + // find end of CDATA + while(*P && !cDataEnd) { + if (*P == '>' && + (*(P-1) == ']') && + (*(P-2) == ']')) + { + cDataEnd = P - 2; + } + + ++P; + } + + if ( cDataEnd ) + node_name = String::utf8(cDataBegin, (int)(cDataEnd - cDataBegin)); + else + node_name = ""; +#ifdef DEBUG_XML + print_line("XML CDATA: "+node_name); +#endif + + return true; +} + +void XMLParser::_parse_comment() { + + node_type = NODE_COMMENT; + P += 1; + + char *pCommentBegin = P; + + int count = 1; + + // move until end of comment reached + while(count) + { + if (*P == '>') + --count; + else + if (*P == '<') + ++count; + + ++P; + } + + P -= 3; + node_name = String::utf8(pCommentBegin+2, (int)(P - pCommentBegin-2)); + P += 3; +#ifdef DEBUG_XML + print_line("XML COMMENT: "+node_name); +#endif + +} + +void XMLParser::_parse_opening_xml_element() { + + node_type = NODE_ELEMENT; + node_empty = false; + attributes.clear(); + + // find name + const char* startName = P; + + // find end of element + while(*P != '>' && !_is_white_space(*P)) + ++P; + + const char* endName = P; + + // find attributes + while(*P != '>') + { + if (_is_white_space(*P)) + ++P; + else + { + if (*P != '/') + { + // we've got an attribute + + // read the attribute names + const char* attributeNameBegin = P; + + while(!_is_white_space(*P) && *P != '=') + ++P; + + const char* attributeNameEnd = P; + ++P; + + // read the attribute value + // check for quotes and single quotes, thx to murphy + while( (*P != '\"') && (*P != '\'') && *P) + ++P; + + if (!*P) // malformatted xml file + return; + + const char attributeQuoteChar = *P; + + ++P; + const char* attributeValueBegin = P; + + while(*P != attributeQuoteChar && *P) + ++P; + + if (!*P) // malformatted xml file + return; + + const char* attributeValueEnd = P; + ++P; + + Attribute attr; + attr.name = String::utf8(attributeNameBegin, + (int)(attributeNameEnd - attributeNameBegin)); + + String s =String::utf8(attributeValueBegin, + (int)(attributeValueEnd - attributeValueBegin)); + + attr.value = _replace_special_characters(s); + attributes.push_back(attr); + } + else + { + // tag is closed directly + ++P; + node_empty = true; + break; + } + } + } + + // check if this tag is closing directly + if (endName > startName && *(endName-1) == '/') + { + // directly closing tag + node_empty = true; + endName--; + } + + node_name = String::utf8(startName, (int)(endName - startName)); +#ifdef DEBUG_XML + print_line("XML OPEN: "+node_name); +#endif + + ++P; +} + + +void XMLParser::_parse_current_node() { + + char* start = P; + node_offset = P - data; + + // more forward until '<' found + while(*P != '<' && *P) + ++P; + + if (!*P) + return; + + if (P - start > 0) + { + // we found some text, store it + if (_set_text(start, P)) + return; + } + + ++P; + + // based on current token, parse and report next element + switch(*P) + { + case '/': + _parse_closing_xml_element(); + break; + case '?': + _ignore_definition(); + break; + case '!': + if (!_parse_cdata()) + _parse_comment(); + break; + default: + _parse_opening_xml_element(); + break; + } +} + + +uint64_t XMLParser::get_node_offset() const { + + return node_offset; +}; + +Error XMLParser::seek(uint64_t p_pos) { + + ERR_FAIL_COND_V(!data, ERR_FILE_EOF) + ERR_FAIL_COND_V(p_pos >= length, ERR_FILE_EOF); + + P = data + p_pos; + + return read(); +}; + +void XMLParser::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("read"),&XMLParser::read); + ObjectTypeDB::bind_method(_MD("get_node_type"),&XMLParser::get_node_type); + ObjectTypeDB::bind_method(_MD("get_node_name"),&XMLParser::get_node_name); + ObjectTypeDB::bind_method(_MD("get_node_data"),&XMLParser::get_node_data); + ObjectTypeDB::bind_method(_MD("get_node_offset"),&XMLParser::get_node_offset); + ObjectTypeDB::bind_method(_MD("get_attribute_count"),&XMLParser::get_attribute_count); + ObjectTypeDB::bind_method(_MD("get_attribute_name"),&XMLParser::get_attribute_name); + ObjectTypeDB::bind_method(_MD("get_attribute_value"),(String (XMLParser::*)(int) const) &XMLParser::get_attribute_value); + ObjectTypeDB::bind_method(_MD("has_attribute"),&XMLParser::has_attribute); + ObjectTypeDB::bind_method(_MD("get_named_attribute_value"), (String (XMLParser::*)(const String&) const) &XMLParser::get_attribute_value); + ObjectTypeDB::bind_method(_MD("get_named_attribute_value_safe"), &XMLParser::get_attribute_value_safe); + ObjectTypeDB::bind_method(_MD("is_empty"),&XMLParser::is_empty); + ObjectTypeDB::bind_method(_MD("get_current_line"),&XMLParser::get_current_line); + ObjectTypeDB::bind_method(_MD("skip_section"),&XMLParser::skip_section); + ObjectTypeDB::bind_method(_MD("seek"),&XMLParser::seek); + ObjectTypeDB::bind_method(_MD("open"),&XMLParser::open); + + BIND_CONSTANT( NODE_NONE ); + BIND_CONSTANT( NODE_ELEMENT ); + BIND_CONSTANT( NODE_ELEMENT_END ); + BIND_CONSTANT( NODE_TEXT ); + BIND_CONSTANT( NODE_COMMENT ); + BIND_CONSTANT( NODE_CDATA ); + BIND_CONSTANT( NODE_UNKNOWN ); + +}; + + + +Error XMLParser::read() { + + // if not end reached, parse the node + if (P && (P - data) < length - 1 && *P != 0) + { + _parse_current_node(); + return OK; + } + + return ERR_FILE_EOF; +} + +XMLParser::NodeType XMLParser::get_node_type() { + + return node_type; +} +String XMLParser::get_node_data() const { + + ERR_FAIL_COND_V( node_type != NODE_TEXT, ""); + return node_name; +} + +String XMLParser::get_node_name() const { + ERR_FAIL_COND_V( node_type == NODE_TEXT, ""); + return node_name; +} +int XMLParser::get_attribute_count() const { + + return attributes.size(); +} +String XMLParser::get_attribute_name(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,attributes.size(),""); + return attributes[p_idx].name; +} +String XMLParser::get_attribute_value(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,attributes.size(),""); + return attributes[p_idx].value; +} +bool XMLParser::has_attribute(const String& p_name) const { + + for(int i=0;i<attributes.size();i++) { + if (attributes[i].name==p_name) + return true; + } + + return false; +} +String XMLParser::get_attribute_value(const String& p_name) const { + + int idx=-1; + for(int i=0;i<attributes.size();i++) { + if (attributes[i].name==p_name) { + idx=i; + break; + } + } + + if (idx<0) { + ERR_EXPLAIN("Attribute not found: "+p_name); + } + ERR_FAIL_COND_V(idx<0,""); + return attributes[idx].value; + +} + +String XMLParser::get_attribute_value_safe(const String& p_name) const { + + int idx=-1; + for(int i=0;i<attributes.size();i++) { + if (attributes[i].name==p_name) { + idx=i; + break; + } + } + + if (idx<0) + return ""; + return attributes[idx].value; + +} +bool XMLParser::is_empty() const { + + return node_empty; +} + +Error XMLParser::open(const String& p_path) { + + Error err; + FileAccess * file = FileAccess::open(p_path,FileAccess::READ,&err); + + if (err) { + ERR_FAIL_COND_V(err!=OK,err); + } + + length = file->get_len(); + ERR_FAIL_COND_V(length<1, ERR_FILE_CORRUPT); + + data = memnew_arr( char, length+1); + file->get_buffer((uint8_t*)data,length); + data[length]=0; + P=data; + + memdelete(file); + + return OK; + +} + +void XMLParser::skip_section() { + + // skip if this element is empty anyway. + if (is_empty()) + return; + + // read until we've reached the last element in this section + int tagcount = 1; + + while(tagcount && read()==OK) + { + if (get_node_type() == XMLParser::NODE_ELEMENT && + !is_empty()) + { + ++tagcount; + } + else + if (get_node_type() == XMLParser::NODE_ELEMENT_END) + --tagcount; + } + +} + +void XMLParser::close() { + + if (data) + memdelete_arr(data); + data=NULL; + length=0; + P=NULL; + node_empty=false; + node_type=NODE_NONE; + node_offset = 0; +} + +int XMLParser::get_current_line() const { + + return 0; +} + +XMLParser::XMLParser() { + + data=NULL; + close(); + special_characters.push_back("&"); + special_characters.push_back("<lt;"); + special_characters.push_back(">gt;"); + special_characters.push_back("\"quot;"); + special_characters.push_back("'apos;"); + + +} +XMLParser::~XMLParser() { + + + if (data) + memdelete_arr(data); +} diff --git a/core/io/xml_parser.h b/core/io/xml_parser.h new file mode 100644 index 0000000000..3025c041b3 --- /dev/null +++ b/core/io/xml_parser.h @@ -0,0 +1,123 @@ +/*************************************************************************/ +/* xml_parser.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. */ +/*************************************************************************/ +#ifndef XML_PARSER_H +#define XML_PARSER_H + +#include "ustring.h" +#include "vector.h" +#include "os/file_access.h" +#include "reference.h" + +/* + Based on irrXML (see their zlib license). Added mainly for compatibility with their Collada loader. +*/ + +class XMLParser : public Reference { + + OBJ_TYPE( XMLParser, Reference ); +public: + //! Enumeration of all supported source text file formats + enum SourceFormat { + SOURCE_ASCII, + SOURCE_UTF8, + SOURCE_UTF16_BE, + SOURCE_UTF16_LE, + SOURCE_UTF32_BE, + SOURCE_UTF32_LE + }; + + enum NodeType { + NODE_NONE, + NODE_ELEMENT, + NODE_ELEMENT_END, + NODE_TEXT, + NODE_COMMENT, + NODE_CDATA, + NODE_UNKNOWN + }; + +private: + + char *data; + char *P; + int length; + void unescape(String& p_str); + Vector<String> special_characters; + String node_name; + bool node_empty; + NodeType node_type; + uint64_t node_offset; + + struct Attribute { + String name; + String value; + }; + + Vector<Attribute> attributes; + + String _replace_special_characters(const String& origstr); + bool _set_text(char* start, char* end); + void _parse_closing_xml_element(); + void _ignore_definition(); + bool _parse_cdata(); + void _parse_comment(); + void _parse_opening_xml_element(); + void _parse_current_node(); + + static void _bind_methods(); + +public: + + + Error read(); + NodeType get_node_type(); + String get_node_name() const; + String get_node_data() const; + uint64_t get_node_offset() const; + int get_attribute_count() const; + String get_attribute_name(int p_idx) const; + String get_attribute_value(int p_idx) const; + bool has_attribute(const String& p_name) const; + String get_attribute_value(const String& p_name) const; + String get_attribute_value_safe(const String& p_name) const; // do not print error if doesn't exist + bool is_empty() const; + int get_current_line() const; + + void skip_section(); + Error seek(uint64_t p_pos); + + Error open(const String& p_path); + void close(); + + XMLParser(); + ~XMLParser(); +}; + +#endif + diff --git a/core/io/zip.c b/core/io/zip.c new file mode 100644 index 0000000000..edf5560ddb --- /dev/null +++ b/core/io/zip.c @@ -0,0 +1,2004 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include <stddef.h> +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include <errno.h> +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writting_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;i<copy_this;i++) + *(to_copy+i)=*(from_copy+i); + + ldi->filled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize; + ZPOS64_T uReadPos ; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackRead<uMaxBack) + { + uLong uReadSize; + ZPOS64_T uReadPos; + int i; + if (uBackRead+BUFREADCOMMENT>uMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_pos<offset_central_dir+size_central_dir) && + (err==ZIP_OK)) + err=ZIP_BADZIPFILE; + + if (err!=ZIP_OK) + { + ZCLOSE64(pziinit->z_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) { +// fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + } else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if ((level==2)) + zi->ci.flag |= 4; + if ((level==1)) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + + for (i=0;i<size_filename;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;i<size_extrafield_global;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;i<size_comment;i++) + *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;i<zi->ci.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/core/io/zip.h b/core/io/zip.h new file mode 100644 index 0000000000..3c723f093d --- /dev/null +++ b/core/io/zip.h @@ -0,0 +1,361 @@ +/* Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/core/io/zip_io.h b/core/io/zip_io.h new file mode 100644 index 0000000000..c7d5a70135 --- /dev/null +++ b/core/io/zip_io.h @@ -0,0 +1,129 @@ +/*************************************************************************/ +/* zip_io.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. */ +/*************************************************************************/ +#ifndef ZIP_IO_H +#define ZIP_IO_H + +#include "io/zip.h" +#include "io/unzip.h" +#include "os/file_access.h" + +static void* zipio_open(void* data, const char* p_fname, int mode) { + + FileAccess *&f = *(FileAccess**)data; + + if (mode & ZLIB_FILEFUNC_MODE_WRITE) { + f = FileAccess::open(p_fname,FileAccess::WRITE); + } else { + + f = FileAccess::open(p_fname,FileAccess::READ); + } + + if (!f) + return NULL; + + return data; + +}; + +static uLong zipio_read(void* data, void* fdata, void* buf, uLong size) { + + FileAccess* f = *(FileAccess**)data; + return f->get_buffer((uint8_t*)buf, size); + +}; + +static uLong zipio_write(voidpf opaque, voidpf stream, const void* buf, uLong size) { + + FileAccess* f = *(FileAccess**)opaque; + f->store_buffer((uint8_t*)buf, size); + return size; +}; + + +static long zipio_tell (voidpf opaque, voidpf stream) { + + FileAccess* f = *(FileAccess**)opaque; + return f->get_pos(); +}; + +static long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) { + + FileAccess* f = *(FileAccess**)opaque; + + int pos = offset; + switch (origin) { + + case ZLIB_FILEFUNC_SEEK_CUR: + pos = f->get_pos() + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + pos = f->get_len() + offset; + break; + default: + break; + }; + + f->seek(pos); + return 0; +}; + +static int zipio_close(voidpf opaque, voidpf stream) { + + FileAccess*& f = *(FileAccess**)opaque; + if (f) { + f->close(); + f=NULL; + } + return 0; +}; + +static int zipio_testerror(voidpf opaque, voidpf stream) { + + FileAccess* f = *(FileAccess**)opaque; + return (f && f->get_error()!=OK)?1:0; +}; + + +static zlib_filefunc_def zipio_create_io_from_file(FileAccess **p_file) { + + zlib_filefunc_def io; + io.opaque = p_file; + io.zopen_file = zipio_open; + io.zread_file = zipio_read; + io.zwrite_file = zipio_write; + io.ztell_file = zipio_tell; + io.zseek_file = zipio_seek; + io.zclose_file = zipio_close; + io.zerror_file = zipio_testerror; + return io; +} + + + +#endif // ZIP_IO_H |