/*************************************************************************/ /* 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 s; get_sections(&s); StringArray arr; arr.resize(s.size()); int idx=0; for(const List::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 s; get_section_keys(p_section,&s); StringArray arr; arr.resize(s.size()); int idx=0; for(const List::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(); } 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 *r_sections) const{ for(const Map< String, Map >::Element *E=values.front();E;E=E->next()) { r_sections->push_back(E->key()); } } void ConfigFile::get_section_keys(const String& p_section,List *r_keys) const{ ERR_FAIL_COND(!values.has(p_section)); for(const Map ::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;i0) str+=", "; str+=_encode_variant(arr[i]); } str+="]"; return str; } break; case Variant::DICTIONARY: { Dictionary d = p_variant; String str="{"; List keys; d.get_key_list(&keys); for(List::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 data = img.get_data(); int ds=data.size(); DVector::Read r = data.read(); for(int i=0;i>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 >::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::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 _decode_params(const String& p_string) { int begin=p_string.find("("); ERR_FAIL_COND_V(begin==-1,Vector()); begin++; int end=p_string.find(")"); ERR_FAIL_COND_V(end()); 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 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 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 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 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 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 pixels; pixels.resize(datasize); DVector::Write wb = pixels.write(); const CharType *cptr=data.c_str(); int idx=0; uint8_t byte; while( idx='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::Write(); return Image(w,h,mipmaps,imgformat,pixels); } if (str.find(",")!=-1) { //vector2 or vector3 Vector 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 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() { }