summaryrefslogtreecommitdiffstats
path: root/core/io/config_file.cpp
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-02-09 22:10:30 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-02-09 22:10:30 -0300
commit0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch)
tree276c4d099e178eb67fbd14f61d77b05e3808e9e3 /core/io/config_file.cpp
parent0e49da1687bc8192ed210947da52c9e5c5f301bb (diff)
downloadredot-engine-0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac.tar.gz
GODOT IS OPEN SOURCE
Diffstat (limited to 'core/io/config_file.cpp')
-rw-r--r--core/io/config_file.cpp744
1 files changed, 744 insertions, 0 deletions
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()
+{
+}