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