/*************************************************************************/ /* globals.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2017 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 "globals.h" #include "os/dir_access.h" #include "os/file_access.h" #include "os/keyboard.h" #include "io/marshalls.h" #include "bind/core_bind.h" #include "os/os.h" #include "io/file_access_pack.h" #include "io/file_access_network.h" GlobalConfig *GlobalConfig::singleton=NULL; GlobalConfig *GlobalConfig::get_singleton() { return singleton; } String GlobalConfig::get_resource_path() const { return resource_path; }; String GlobalConfig::localize_path(const String& p_path) const { if (resource_path=="") return p_path; //not initialied yet if (p_path.begins_with("res://") || p_path.begins_with("user://") || (p_path.is_abs_path() && !p_path.begins_with(resource_path))) return p_path.simplify_path(); DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); String path = p_path.replace("\\","/").simplify_path(); if (dir->change_dir(path)==OK) { String cwd = dir->get_current_dir(); cwd = cwd.replace("\\","/"); memdelete(dir); if (!cwd.begins_with(resource_path)) { return p_path; }; return cwd.replace_first(resource_path, "res:/"); } else { memdelete(dir); int sep = path.find_last("/"); if (sep == -1) { return "res://"+path; }; String parent = path.substr(0, sep); String plocal = localize_path(parent); if (plocal == "") { return ""; }; return plocal + path.substr(sep, path.size() - sep); }; } void GlobalConfig::set_initial_value(const String& p_name, const Variant & p_value) { ERR_FAIL_COND(!props.has(p_name)); props[p_name].initial=p_value; } String GlobalConfig::globalize_path(const String& p_path) const { if (p_path.begins_with("res://")) { if (resource_path != "") { return p_path.replace("res:/",resource_path); }; return p_path.replace("res://", ""); }; return p_path; } bool GlobalConfig::_set(const StringName& p_name, const Variant& p_value) { _THREAD_SAFE_METHOD_ if (p_value.get_type()==Variant::NIL) props.erase(p_name); else { if (props.has(p_name)) { if (!props[p_name].overrided) props[p_name].variant=p_value; if (props[p_name].order>=NO_ORDER_BASE && registering_order) { props[p_name].order=last_order++; } } else { props[p_name]=VariantContainer(p_value,last_order++ + (registering_order?0:NO_ORDER_BASE)); } } if (!disable_platform_override) { String s=String(p_name); int sl = s.find("/"); int p = s.find("."); if (p!=-1 && sl!=-1 && p < sl) { Vector ps = s.substr(0,sl).split("."); String prop=s.substr(sl,s.length()-sl); for(int i=1;iget_name()) { String fullprop=ps[0]+prop; set(fullprop,p_value); props[fullprop].overrided=true; } } } } return true; } bool GlobalConfig::_get(const StringName& p_name,Variant &r_ret) const { _THREAD_SAFE_METHOD_ if (!props.has(p_name)) return false; r_ret=props[p_name].variant; return true; } struct _VCSort { String name; Variant::Type type; int order; int flags; bool operator<(const _VCSort& p_vcs) const{ return order==p_vcs.order?name *p_list) const { _THREAD_SAFE_METHOD_ Set<_VCSort> vclist; for(Map::Element *E=props.front();E;E=E->next()) { const VariantContainer *v=&E->get(); if (v->hide_from_editor) continue; _VCSort vc; vc.name=E->key(); vc.order=v->order; vc.type=v->variant.get_type(); if (vc.name.begins_with("input/") || vc.name.begins_with("import/") || vc.name.begins_with("export/") || vc.name.begins_with("/remap") || vc.name.begins_with("/locale") || vc.name.begins_with("/autoload")) vc.flags=PROPERTY_USAGE_STORAGE; else vc.flags=PROPERTY_USAGE_EDITOR|PROPERTY_USAGE_STORAGE; vclist.insert(vc); } for(Set<_VCSort>::Element *E=vclist.front();E;E=E->next()) { if (custom_prop_info.has(E->get().name)) { PropertyInfo pi=custom_prop_info[E->get().name]; pi.name=E->get().name; pi.usage=E->get().flags; p_list->push_back( pi ); } else p_list->push_back( PropertyInfo(E->get().type, E->get().name,PROPERTY_HINT_NONE,"",E->get().flags) ); } } bool GlobalConfig::_load_resource_pack(const String& p_pack) { if (PackedData::get_singleton()->is_disabled()) return false; bool ok = PackedData::get_singleton()->add_pack(p_pack)==OK; if (!ok) return false; //if data.pck is found, all directory access will be from here DirAccess::make_default(DirAccess::ACCESS_RESOURCES); using_datapack=true; return true; } Error GlobalConfig::setup(const String& p_path,const String & p_main_pack) { //an absolute mess of a function, must be cleaned up and reorganized somehow at some point //_load_settings(p_path+"/override.cfg"); if (p_main_pack!="") { bool ok = _load_resource_pack(p_main_pack); ERR_FAIL_COND_V(!ok,ERR_CANT_OPEN); if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { _load_settings("res://override.cfg"); } return OK; } if (OS::get_singleton()->get_executable_path()!="") { if (_load_resource_pack(OS::get_singleton()->get_executable_path())) { if (p_path!="") { resource_path=p_path; } else { DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); resource_path=d->get_current_dir(); memdelete(d); } if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { _load_settings("res://override.cfg"); } return OK; } } if (FileAccessNetworkClient::get_singleton()) { if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { _load_settings("res://override.cfg"); } return OK; } if (OS::get_singleton()->get_resource_dir()!="") { //OS will call Globals->get_resource_path which will be empty if not overriden! //if the OS would rather use somewhere else, then it will not be empty. resource_path=OS::get_singleton()->get_resource_dir().replace("\\","/"); if (resource_path.length() && resource_path[ resource_path.length()-1]=='/') resource_path=resource_path.substr(0,resource_path.length()-1); // chop end print_line("has res dir: "+resource_path); if (!_load_resource_pack("res://data.pck")) _load_resource_pack("res://data.zip"); // make sure this is load from the resource path print_line("exists engine cfg? "+itos(FileAccess::exists("/engine.cfg"))); if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { print_line("loaded engine.cfg"); _load_settings("res://override.cfg"); } return OK; } DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); if (!d) { resource_path = p_path; } else { d->change_dir(p_path); String candidate = d->get_current_dir(); String current_dir = d->get_current_dir(); String exec_name = OS::get_singleton()->get_executable_path().get_file().basename(); bool found = false; bool first_time=true; while(true) { //try to load settings in ascending through dirs shape! //tries to open pack, but only first time if (first_time && (_load_resource_pack(current_dir+"/"+exec_name+".pck") || _load_resource_pack(current_dir+"/"+exec_name+".zip") )) { if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { _load_settings("res://override.cfg"); found=true; } break; } else if (first_time && (_load_resource_pack(current_dir+"/data.pck") || _load_resource_pack(current_dir+"/data.zip") )) { if (_load_settings("res://engine.cfg")==OK || _load_settings_binary("res://engine.cfb")==OK) { _load_settings("res://override.cfg"); found=true; } break; } else if (_load_settings(current_dir+"/engine.cfg")==OK || _load_settings_binary(current_dir+"/engine.cfb")==OK) { _load_settings(current_dir+"/override.cfg"); candidate=current_dir; found=true; break; } d->change_dir(".."); if (d->get_current_dir()==current_dir) break; //not doing anything useful current_dir=d->get_current_dir(); first_time=false; } resource_path=candidate; resource_path = resource_path.replace("\\","/"); // windows path to unix path just in case memdelete(d); if (!found) return ERR_FILE_NOT_FOUND; }; if (resource_path.length() && resource_path[ resource_path.length()-1]=='/') resource_path=resource_path.substr(0,resource_path.length()-1); // chop end return OK; } bool GlobalConfig::has(String p_var) const { _THREAD_SAFE_METHOD_ return props.has(p_var); } 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; end++; 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(); if (scode<10) scode+=KEY_0; } 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(); int axis = params[1].to_int();; ie.joy_motion.axis=axis>>1; ie.joy_motion.axis_value=axis&1?1:-1; 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_L8; } else if (format=="intensity") { imgformat=Image::FORMAT_INTENSITY; } else if (format=="grayscale_alpha") { imgformat=Image::FORMAT_LA8; } else if (format=="rgb") { imgformat=Image::FORMAT_RGB8; } else if (format=="rgba") { imgformat=Image::FORMAT_RGBA8; } 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_DXT1; } else if (format=="bc2") { imgformat=Image::FORMAT_DXT3; } else if (format=="bc3") { imgformat=Image::FORMAT_DXT5; } else if (format=="bc4") { imgformat=Image::FORMAT_ATI1; } else if (format=="bc5") { imgformat=Image::FORMAT_ATI2; } 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 && h == 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(); } void GlobalConfig::set_registering_order(bool p_enable) { registering_order=p_enable; } Error GlobalConfig::_load_settings_binary(const String p_path) { Error err; FileAccess *f= FileAccess::open(p_path,FileAccess::READ,&err); if (err!=OK) { return err; } uint8_t hdr[4]; f->get_buffer(hdr,4); if (hdr[0]!='E'|| hdr[1]!='C' || hdr[2]!='F' || hdr[3]!='G') { memdelete(f); ERR_EXPLAIN("Corrupted header in binary engine.cfb (not ECFG)"); ERR_FAIL_V(ERR_FILE_CORRUPT;) } set_registering_order(false); uint32_t count=f->get_32(); for(int i=0;iget_32(); CharString cs; cs.resize(slen+1); cs[slen]=0; f->get_buffer((uint8_t*)cs.ptr(),slen); String key; key.parse_utf8(cs.ptr()); uint32_t vlen=f->get_32(); Vector d; d.resize(vlen); f->get_buffer(d.ptr(),vlen); Variant value; Error err = decode_variant(value,d.ptr(),d.size()); ERR_EXPLAIN("Error decoding property: "+key); ERR_CONTINUE(err!=OK); set(key,value); } set_registering_order(true); return OK; } Error GlobalConfig::_load_settings(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; set_registering_order(false); 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); set_registering_order(true); return OK; } 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::VECTOR2: { Vector2 val = p_variant; return String("Vector2(")+rtos(val.x)+String(", ")+rtos(val.y)+String(")"); } break; case Variant::VECTOR3: { Vector3 val = p_variant; return String("Vector3(")+rtos(val.x)+ String(", ") +rtos(val.y)+ String(", ") +rtos(val.z)+String(")"); } 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_L8: format="grayscale"; break; case Image::FORMAT_INTENSITY: format="intensity"; break; case Image::FORMAT_LA8: format="grayscale_alpha"; break; case Image::FORMAT_RGB8: format="rgb"; break; case Image::FORMAT_RGBA8: format="rgba"; break; case Image::FORMAT_INDEXED : format="indexed"; break; case Image::FORMAT_INDEXED_ALPHA: format="indexed_alpha"; break; case Image::FORMAT_DXT1: format="bc1"; break; case Image::FORMAT_DXT3: format="bc2"; break; case Image::FORMAT_DXT5: format="bc3"; break; case Image::FORMAT_ATI1: format="bc4"; break; case Image::FORMAT_ATI2: format="bc5"; break; case Image::FORMAT_CUSTOM: format="custom custom_size="+itos(img.get_data().size())+""; break; default: {} } */ str+=format+", "; str+=itos(img.has_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 * 2 + (ev.joy_motion.axis_value<0?0:1))+")"; } break; default: { return "nil"; } break; } } break; default: {} } return "nil"; //don't know wha to do with this } int GlobalConfig::get_order(const String& p_name) const { ERR_FAIL_COND_V(!props.has(p_name),-1); return props[p_name].order; } void GlobalConfig::set_order(const String& p_name, int p_order){ ERR_FAIL_COND(!props.has(p_name)); props[p_name].order=p_order; } void GlobalConfig::clear(const String& p_name) { ERR_FAIL_COND(!props.has(p_name)); props.erase(p_name); } Error GlobalConfig::save() { return save_custom(get_resource_path()+"/engine.cfg"); } Error GlobalConfig::_save_settings_binary(const String& p_file,const Map > &props,const CustomMap& p_custom) { Error err; FileAccess *file = FileAccess::open(p_file,FileAccess::WRITE,&err); if (err!=OK) { ERR_EXPLAIN("Coudln't save engine.cfb at "+p_file); ERR_FAIL_COND_V(err,err) } uint8_t hdr[4]={'E','C','F','G'}; file->store_buffer(hdr,4); int count=0; for(Map >::Element *E=props.front();E;E=E->next()) { for(List::Element *F=E->get().front();F;F=F->next()) { count++; } } file->store_32(count); //store how many properties are saved for(Map >::Element *E=props.front();E;E=E->next()) { for(List::Element *F=E->get().front();F;F=F->next()) { String key = F->get(); if (E->key()!="") key=E->key()+"/"+key; Variant value; if (p_custom.has(key)) value=p_custom[key]; else value = get(key); file->store_32(key.length()); file->store_string(key); int len; Error err = encode_variant(value,NULL,len); if (err!=OK) memdelete(file); ERR_FAIL_COND_V( err != OK, ERR_INVALID_DATA ); Vector buff; buff.resize(len); err = encode_variant(value,&buff[0],len); if (err!=OK) memdelete(file); ERR_FAIL_COND_V( err != OK, ERR_INVALID_DATA ); file->store_32(len); file->store_buffer(buff.ptr(),buff.size()); } } file->close(); memdelete(file); return OK; } Error GlobalConfig::_save_settings_text(const String& p_file,const Map > &props,const CustomMap& p_custom) { Error err; FileAccess *file = FileAccess::open(p_file,FileAccess::WRITE,&err); if (err) { ERR_EXPLAIN("Coudln't save engine.cfg - "+p_file); ERR_FAIL_COND_V(err,err) } for(Map >::Element *E=props.front();E;E=E->next()) { if (E!=props.front()) file->store_string("\n"); if (E->key()!="") file->store_string("["+E->key()+"]\n\n"); for(List::Element *F=E->get().front();F;F=F->next()) { String key = F->get(); if (E->key()!="") key=E->key()+"/"+key; Variant value; if (p_custom.has(key)) value=p_custom[key]; else value = get(key); file->store_string(F->get()+"="+_encode_variant(value)+"\n"); } } file->close(); memdelete(file); return OK; } Error GlobalConfig::_save_custom_bnd(const String &p_file) { // add other params as dictionary and array? return save_custom(p_file); }; Error GlobalConfig::save_custom(const String& p_path,const CustomMap& p_custom,const Set& p_ignore_masks) { ERR_FAIL_COND_V(p_path=="",ERR_INVALID_PARAMETER); Set<_VCSort> vclist; for(Map::Element *G=props.front();G;G=G->next()) { const VariantContainer *v=&G->get(); if (v->hide_from_editor) continue; if (p_custom.has(G->key())) continue; bool discard=false; for(const Set::Element *E=p_ignore_masks.front();E;E=E->next()) { if ( String(G->key()).match(E->get())) { discard=true; break; } } if (discard) continue; _VCSort vc; vc.name=G->key();//*k; vc.order=v->order; vc.type=v->variant.get_type(); vc.flags=PROPERTY_USAGE_EDITOR|PROPERTY_USAGE_STORAGE; if (v->variant==v->initial) continue; vclist.insert(vc); } for(const Map::Element *E=p_custom.front();E;E=E->next()) { _VCSort vc; vc.name=E->key(); vc.order=0xFFFFFFF; vc.type=E->get().get_type(); vc.flags=PROPERTY_USAGE_STORAGE; vclist.insert(vc); } Map > props; for(Set<_VCSort>::Element *E=vclist.front();E;E=E->next()) { String category = E->get().name; String name = E->get().name; int div = category.find("/"); if (div<0) category=""; else { category=category.substr(0,div); name=name.substr(div+1,name.size()); } props[category].push_back(name); } if (p_path.ends_with(".cfg")) return _save_settings_text(p_path,props,p_custom); else if (p_path.ends_with(".cfb")) return _save_settings_binary(p_path,props,p_custom); else { ERR_EXPLAIN("Unknown config file format: "+p_path); ERR_FAIL_V( ERR_FILE_UNRECOGNIZED ); } return OK; #if 0 Error err = file->open(dst_file,FileAccess::WRITE); if (err) { memdelete(file); ERR_EXPLAIN("Coudln't save engine.cfg"); ERR_FAIL_COND_V(err,err) } for(Map >::Element *E=props.front();E;E=E->next()) { if (E!=props.front()) file->store_string("\n"); if (E->key()!="") file->store_string("["+E->key()+"]\n\n"); for(List::Element *F=E->get().front();F;F=F->next()) { String key = F->get(); if (E->key()!="") key=E->key()+"/"+key; Variant value; if (p_custom.has(key)) value=p_custom[key]; else value = get(key); file->store_string(F->get()+"="+_encode_variant(value)+"\n"); } } file->close(); memdelete(file); return OK; #endif } Variant _GLOBAL_DEF( const String& p_var, const Variant& p_default) { if (GlobalConfig::get_singleton()->has(p_var)) { GlobalConfig::get_singleton()->set_initial_value(p_var,p_default); return GlobalConfig::get_singleton()->get(p_var); } GlobalConfig::get_singleton()->set(p_var,p_default); GlobalConfig::get_singleton()->set_initial_value(p_var,p_default); return p_default; } void GlobalConfig::add_singleton(const Singleton &p_singleton) { singletons.push_back(p_singleton); singleton_ptrs[p_singleton.name]=p_singleton.ptr; } Object* GlobalConfig::get_singleton_object(const String& p_name) const { const Map::Element *E=singleton_ptrs.find(p_name); if (!E) return NULL; else return E->get(); }; bool GlobalConfig::has_singleton(const String& p_name) const { return get_singleton_object(p_name) != NULL; }; void GlobalConfig::get_singletons(List *p_singletons) { for(List::Element *E=singletons.front();E;E=E->next()) p_singletons->push_back(E->get()); } Vector GlobalConfig::get_optimizer_presets() const { List pi; GlobalConfig::get_singleton()->get_property_list(&pi); Vector names; for (List::Element *E=pi.front();E;E=E->next()) { if (!E->get().name.begins_with("optimizer_presets/")) continue; names.push_back(E->get().name.get_slicec('/',1)); } names.sort(); return names; } void GlobalConfig::_add_property_info_bind(const Dictionary& p_info) { ERR_FAIL_COND(!p_info.has("name")); ERR_FAIL_COND(!p_info.has("type")); PropertyInfo pinfo; pinfo.name = p_info["name"]; ERR_FAIL_COND(!props.has(pinfo.name)); pinfo.type = Variant::Type(p_info["type"].operator int()); ERR_FAIL_INDEX(pinfo.type, Variant::VARIANT_MAX); if (p_info.has("hint")) pinfo.hint = PropertyHint(p_info["hint"].operator int()); if (p_info.has("hint_string")) pinfo.hint_string = p_info["hint_string"]; set_custom_property_info(pinfo.name, pinfo); } void GlobalConfig::set_custom_property_info(const String& p_prop,const PropertyInfo& p_info) { ERR_FAIL_COND(!props.has(p_prop)); custom_prop_info[p_prop]=p_info; custom_prop_info[p_prop].name=p_prop; } void GlobalConfig::set_disable_platform_override(bool p_disable) { disable_platform_override=p_disable; } bool GlobalConfig::is_using_datapack() const { return using_datapack; } bool GlobalConfig::property_can_revert(const String& p_name) { if (!props.has(p_name)) return false; return props[p_name].initial!=props[p_name].variant; } Variant GlobalConfig::property_get_revert(const String& p_name) { if (!props.has(p_name)) return Variant(); return props[p_name].initial; } void GlobalConfig::_bind_methods() { ClassDB::bind_method(_MD("has","name"),&GlobalConfig::has); ClassDB::bind_method(_MD("set_order","name","pos"),&GlobalConfig::set_order); ClassDB::bind_method(_MD("get_order","name"),&GlobalConfig::get_order); ClassDB::bind_method(_MD("set_initial_value","name","value"),&GlobalConfig::set_initial_value); ClassDB::bind_method(_MD("add_property_info", "hint"),&GlobalConfig::_add_property_info_bind); ClassDB::bind_method(_MD("clear","name"),&GlobalConfig::clear); ClassDB::bind_method(_MD("localize_path","path"),&GlobalConfig::localize_path); ClassDB::bind_method(_MD("globalize_path","path"),&GlobalConfig::globalize_path); ClassDB::bind_method(_MD("save"),&GlobalConfig::save); ClassDB::bind_method(_MD("has_singleton","name"),&GlobalConfig::has_singleton); ClassDB::bind_method(_MD("get_singleton","name"),&GlobalConfig::get_singleton_object); ClassDB::bind_method(_MD("load_resource_pack","pack"),&GlobalConfig::_load_resource_pack); ClassDB::bind_method(_MD("property_can_revert","name"),&GlobalConfig::property_can_revert); ClassDB::bind_method(_MD("property_get_revert","name"),&GlobalConfig::property_get_revert); ClassDB::bind_method(_MD("save_custom","file"),&GlobalConfig::_save_custom_bnd); } GlobalConfig::GlobalConfig() { singleton=this; last_order=0; disable_platform_override=false; registering_order=true; Array va; InputEvent key; key.type=InputEvent::KEY; InputEvent joyb; joyb.type=InputEvent::JOYSTICK_BUTTON; GLOBAL_DEF("application/name","" ); GLOBAL_DEF("application/main_scene",""); custom_prop_info["application/main_scene"]=PropertyInfo(Variant::STRING,"application/main_scene",PROPERTY_HINT_FILE,"tscn,scn,xscn,xml,res"); GLOBAL_DEF("application/disable_stdout",false); GLOBAL_DEF("application/use_shared_user_dir",true); key.key.scancode=KEY_RETURN; va.push_back(key); key.key.scancode=KEY_ENTER; va.push_back(key); key.key.scancode=KEY_SPACE; va.push_back(key); joyb.joy_button.button_index=JOY_BUTTON_0; va.push_back(joyb); GLOBAL_DEF("input/ui_accept",va); input_presets.push_back("input/ui_accept"); va=Array(); key.key.scancode=KEY_SPACE; va.push_back(key); joyb.joy_button.button_index=JOY_BUTTON_3; va.push_back(joyb); GLOBAL_DEF("input/ui_select",va); input_presets.push_back("input/ui_select"); va=Array(); key.key.scancode=KEY_ESCAPE; va.push_back(key); joyb.joy_button.button_index=JOY_BUTTON_1; va.push_back(joyb); GLOBAL_DEF("input/ui_cancel",va); input_presets.push_back("input/ui_cancel"); va=Array(); key.key.scancode=KEY_TAB; va.push_back(key); GLOBAL_DEF("input/ui_focus_next",va); input_presets.push_back("input/ui_focus_next"); va=Array(); key.key.scancode=KEY_TAB; key.key.mod.shift=true; va.push_back(key); GLOBAL_DEF("input/ui_focus_prev",va); input_presets.push_back("input/ui_focus_prev"); key.key.mod.shift=false; va=Array(); key.key.scancode=KEY_LEFT; va.push_back(key); joyb.joy_button.button_index=JOY_DPAD_LEFT; va.push_back(joyb); GLOBAL_DEF("input/ui_left",va); input_presets.push_back("input/ui_left"); va=Array(); key.key.scancode=KEY_RIGHT; va.push_back(key); joyb.joy_button.button_index=JOY_DPAD_RIGHT; va.push_back(joyb); GLOBAL_DEF("input/ui_right",va); input_presets.push_back("input/ui_right"); va=Array(); key.key.scancode=KEY_UP; va.push_back(key); joyb.joy_button.button_index=JOY_DPAD_UP; va.push_back(joyb); GLOBAL_DEF("input/ui_up",va); input_presets.push_back("input/ui_up"); va=Array(); key.key.scancode=KEY_DOWN; va.push_back(key); joyb.joy_button.button_index=JOY_DPAD_DOWN; va.push_back(joyb); GLOBAL_DEF("input/ui_down",va); input_presets.push_back("input/ui_down"); va=Array(); key.key.scancode=KEY_PAGEUP; va.push_back(key); GLOBAL_DEF("input/ui_page_up",va); input_presets.push_back("input/ui_page_up"); va=Array(); key.key.scancode=KEY_PAGEDOWN; va.push_back(key); GLOBAL_DEF("input/ui_page_down",va); input_presets.push_back("input/ui_page_down"); // GLOBAL_DEF("display/handheld/orientation", "landscape"); custom_prop_info["display/handheld/orientation"]=PropertyInfo(Variant::STRING,"display/handheld/orientation",PROPERTY_HINT_ENUM,"landscape,portrait,reverse_landscape,reverse_portrait,sensor_landscape,sensor_portrait,sensor"); custom_prop_info["rendering/threads/thread_model"]=PropertyInfo(Variant::INT,"rendering/threads/thread_model",PROPERTY_HINT_ENUM,"Single-Unsafe,Single-Safe,Multi-Threaded"); custom_prop_info["physics/2d/thread_model"]=PropertyInfo(Variant::INT,"physics/2d/thread_model",PROPERTY_HINT_ENUM,"Single-Unsafe,Single-Safe,Multi-Threaded"); GLOBAL_DEF("debug/profiler/max_functions",16384); using_datapack=false; } GlobalConfig::~GlobalConfig() { singleton=NULL; }