1 /*************************************************************************/
2 /*  config_file.cpp                                                      */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
10 /*                                                                       */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the       */
13 /* "Software"), to deal in the Software without restriction, including   */
14 /* without limitation the rights to use, copy, modify, merge, publish,   */
15 /* distribute, sublicense, and/or sell copies of the Software, and to    */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions:                                             */
18 /*                                                                       */
19 /* The above copyright notice and this permission notice shall be        */
20 /* included in all copies or substantial portions of the Software.       */
21 /*                                                                       */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
29 /*************************************************************************/
30 #include "config_file.h"
31 #include "os/file_access.h"
32 #include "os/keyboard.h"
33 #include "variant_parser.h"
34 
_get_sections() const35 StringArray ConfigFile::_get_sections() const {
36 
37 	List<String> s;
38 	get_sections(&s);
39 	StringArray arr;
40 	arr.resize(s.size());
41 	int idx = 0;
42 	for (const List<String>::Element *E = s.front(); E; E = E->next()) {
43 
44 		arr.set(idx++, E->get());
45 	}
46 
47 	return arr;
48 }
49 
_get_section_keys(const String & p_section) const50 StringArray ConfigFile::_get_section_keys(const String &p_section) const {
51 
52 	List<String> s;
53 	get_section_keys(p_section, &s);
54 	StringArray arr;
55 	arr.resize(s.size());
56 	int idx = 0;
57 	for (const List<String>::Element *E = s.front(); E; E = E->next()) {
58 
59 		arr.set(idx++, E->get());
60 	}
61 
62 	return arr;
63 }
64 
set_value(const String & p_section,const String & p_key,const Variant & p_value)65 void ConfigFile::set_value(const String &p_section, const String &p_key, const Variant &p_value) {
66 
67 	if (p_value.get_type() == Variant::NIL) {
68 		//erase
69 		if (!values.has(p_section))
70 			return; // ?
71 		values[p_section].erase(p_key);
72 		if (values[p_section].empty()) {
73 			values.erase(p_section);
74 		}
75 
76 	} else {
77 		if (!values.has(p_section)) {
78 			values[p_section] = Map<String, Variant>();
79 		}
80 
81 		values[p_section][p_key] = p_value;
82 	}
83 }
get_value(const String & p_section,const String & p_key,Variant p_default) const84 Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const {
85 
86 	if (!values.has(p_section) || !values[p_section].has(p_key)) {
87 		if (p_default.get_type() == Variant::NIL) {
88 			ERR_EXPLAIN("Couldn't find the given section/key and no default was given");
89 			ERR_FAIL_V(p_default);
90 		}
91 		return p_default;
92 	}
93 	return values[p_section][p_key];
94 }
95 
has_section(const String & p_section) const96 bool ConfigFile::has_section(const String &p_section) const {
97 
98 	return values.has(p_section);
99 }
has_section_key(const String & p_section,const String & p_key) const100 bool ConfigFile::has_section_key(const String &p_section, const String &p_key) const {
101 
102 	if (!values.has(p_section))
103 		return false;
104 	return values[p_section].has(p_key);
105 }
106 
get_sections(List<String> * r_sections) const107 void ConfigFile::get_sections(List<String> *r_sections) const {
108 
109 	for (const Map<String, Map<String, Variant> >::Element *E = values.front(); E; E = E->next()) {
110 		r_sections->push_back(E->key());
111 	}
112 }
get_section_keys(const String & p_section,List<String> * r_keys) const113 void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const {
114 
115 	ERR_FAIL_COND(!values.has(p_section));
116 
117 	for (const Map<String, Variant>::Element *E = values[p_section].front(); E; E = E->next()) {
118 		r_keys->push_back(E->key());
119 	}
120 }
121 
save(const String & p_path)122 Error ConfigFile::save(const String &p_path) {
123 
124 	Error err;
125 	FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err);
126 
127 	if (err) {
128 		if (file)
129 			memdelete(file);
130 		return err;
131 	}
132 
133 	for (Map<String, Map<String, Variant> >::Element *E = values.front(); E; E = E->next()) {
134 
135 		if (E != values.front())
136 			file->store_string("\n");
137 		file->store_string("[" + E->key() + "]\n\n");
138 
139 		for (Map<String, Variant>::Element *F = E->get().front(); F; F = F->next()) {
140 
141 			String vstr;
142 			VariantWriter::write_to_string(F->get(), vstr);
143 			file->store_string(F->key() + "=" + vstr + "\n");
144 		}
145 	}
146 
147 	memdelete(file);
148 
149 	return OK;
150 }
151 
load(const String & p_path)152 Error ConfigFile::load(const String &p_path) {
153 
154 	Error err;
155 	FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
156 
157 	if (!f)
158 		return ERR_CANT_OPEN;
159 
160 	VariantParser::StreamFile stream;
161 	stream.f = f;
162 
163 	String assign;
164 	Variant value;
165 	VariantParser::Tag next_tag;
166 
167 	int lines = 0;
168 	String error_text;
169 
170 	String section;
171 
172 	while (true) {
173 
174 		assign = Variant();
175 		next_tag.fields.clear();
176 		next_tag.name = String();
177 
178 		err = VariantParser::parse_tag_assign_eof(&stream, lines, error_text, next_tag, assign, value, NULL, true);
179 		if (err == ERR_FILE_EOF) {
180 			memdelete(f);
181 			return OK;
182 		} else if (err != OK) {
183 			ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text);
184 			memdelete(f);
185 			return err;
186 		}
187 
188 		if (assign != String()) {
189 			set_value(section, assign, value);
190 		} else if (next_tag.name != String()) {
191 			section = next_tag.name;
192 		}
193 	}
194 
195 	memdelete(f);
196 
197 	return OK;
198 }
199 
_bind_methods()200 void ConfigFile::_bind_methods() {
201 
202 	ObjectTypeDB::bind_method(_MD("set_value", "section", "key", "value"), &ConfigFile::set_value);
203 	ObjectTypeDB::bind_method(_MD("get_value:Variant", "section", "key", "default"), &ConfigFile::get_value, DEFVAL(Variant()));
204 
205 	ObjectTypeDB::bind_method(_MD("has_section", "section"), &ConfigFile::has_section);
206 	ObjectTypeDB::bind_method(_MD("has_section_key", "section", "key"), &ConfigFile::has_section_key);
207 
208 	ObjectTypeDB::bind_method(_MD("get_sections"), &ConfigFile::_get_sections);
209 	ObjectTypeDB::bind_method(_MD("get_section_keys", "section"), &ConfigFile::_get_section_keys);
210 
211 	ObjectTypeDB::bind_method(_MD("load:Error", "path"), &ConfigFile::load);
212 	ObjectTypeDB::bind_method(_MD("save:Error", "path"), &ConfigFile::save);
213 }
214 
ConfigFile()215 ConfigFile::ConfigFile() {
216 }
217