Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/io/config_file.cpp
9973 views
1
/**************************************************************************/
2
/* config_file.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
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
31
#include "config_file.h"
32
33
#include "core/io/file_access_encrypted.h"
34
#include "core/string/string_builder.h"
35
#include "core/variant/variant_parser.h"
36
37
void ConfigFile::set_value(const String &p_section, const String &p_key, const Variant &p_value) {
38
if (p_value.get_type() == Variant::NIL) { // Erase key.
39
if (!values.has(p_section)) {
40
return;
41
}
42
43
values[p_section].erase(p_key);
44
if (values[p_section].is_empty()) {
45
values.erase(p_section);
46
}
47
} else {
48
if (!values.has(p_section)) {
49
// Insert section-less keys at the beginning.
50
values.insert(p_section, HashMap<String, Variant>(), p_section.is_empty());
51
}
52
53
values[p_section][p_key] = p_value;
54
}
55
}
56
57
Variant ConfigFile::get_value(const String &p_section, const String &p_key, const Variant &p_default) const {
58
if (!values.has(p_section) || !values[p_section].has(p_key)) {
59
ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(),
60
vformat("Couldn't find the given section \"%s\" and key \"%s\", and no default was given.", p_section, p_key));
61
return p_default;
62
}
63
64
return values[p_section][p_key];
65
}
66
67
bool ConfigFile::has_section(const String &p_section) const {
68
return values.has(p_section);
69
}
70
71
bool ConfigFile::has_section_key(const String &p_section, const String &p_key) const {
72
if (!values.has(p_section)) {
73
return false;
74
}
75
return values[p_section].has(p_key);
76
}
77
78
Vector<String> ConfigFile::get_sections() const {
79
Vector<String> sections;
80
sections.resize(values.size());
81
82
int i = 0;
83
String *sections_write = sections.ptrw();
84
for (const KeyValue<String, HashMap<String, Variant>> &E : values) {
85
sections_write[i++] = E.key;
86
}
87
88
return sections;
89
}
90
91
Vector<String> ConfigFile::get_section_keys(const String &p_section) const {
92
Vector<String> keys;
93
ERR_FAIL_COND_V_MSG(!values.has(p_section), keys, vformat("Cannot get keys from nonexistent section \"%s\".", p_section));
94
95
const HashMap<String, Variant> &keys_map = values[p_section];
96
keys.resize(keys_map.size());
97
98
int i = 0;
99
String *keys_write = keys.ptrw();
100
for (const KeyValue<String, Variant> &E : keys_map) {
101
keys_write[i++] = E.key;
102
}
103
104
return keys;
105
}
106
107
void ConfigFile::erase_section(const String &p_section) {
108
ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase nonexistent section \"%s\".", p_section));
109
values.erase(p_section);
110
}
111
112
void ConfigFile::erase_section_key(const String &p_section, const String &p_key) {
113
ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase key \"%s\" from nonexistent section \"%s\".", p_key, p_section));
114
ERR_FAIL_COND_MSG(!values[p_section].has(p_key), vformat("Cannot erase nonexistent key \"%s\" from section \"%s\".", p_key, p_section));
115
116
values[p_section].erase(p_key);
117
if (values[p_section].is_empty()) {
118
values.erase(p_section);
119
}
120
}
121
122
String ConfigFile::encode_to_text() const {
123
StringBuilder sb;
124
bool first = true;
125
for (const KeyValue<String, HashMap<String, Variant>> &E : values) {
126
if (first) {
127
first = false;
128
} else {
129
sb.append("\n");
130
}
131
if (!E.key.is_empty()) {
132
sb.append("[" + E.key + "]\n\n");
133
}
134
135
for (const KeyValue<String, Variant> &F : E.value) {
136
String vstr;
137
VariantWriter::write_to_string(F.value, vstr);
138
sb.append(F.key.property_name_encode() + "=" + vstr + "\n");
139
}
140
}
141
return sb.as_string();
142
}
143
144
Error ConfigFile::save(const String &p_path) {
145
Error err;
146
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
147
148
if (err) {
149
return err;
150
}
151
152
return _internal_save(file);
153
}
154
155
Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_key) {
156
Error err;
157
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err);
158
159
if (err) {
160
return err;
161
}
162
163
Ref<FileAccessEncrypted> fae;
164
fae.instantiate();
165
err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_WRITE_AES256);
166
if (err) {
167
return err;
168
}
169
return _internal_save(fae);
170
}
171
172
Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass) {
173
Error err;
174
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err);
175
176
if (err) {
177
return err;
178
}
179
180
Ref<FileAccessEncrypted> fae;
181
fae.instantiate();
182
err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_WRITE_AES256);
183
if (err) {
184
return err;
185
}
186
187
return _internal_save(fae);
188
}
189
190
Error ConfigFile::_internal_save(Ref<FileAccess> file) {
191
bool first = true;
192
for (const KeyValue<String, HashMap<String, Variant>> &E : values) {
193
if (first) {
194
first = false;
195
} else {
196
file->store_string("\n");
197
}
198
if (!E.key.is_empty()) {
199
file->store_string("[" + E.key.replace("]", "\\]") + "]\n\n");
200
}
201
202
for (const KeyValue<String, Variant> &F : E.value) {
203
String vstr;
204
VariantWriter::write_to_string(F.value, vstr);
205
file->store_string(F.key.property_name_encode() + "=" + vstr + "\n");
206
}
207
}
208
209
return OK;
210
}
211
212
Error ConfigFile::load(const String &p_path) {
213
Error err;
214
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
215
216
if (f.is_null()) {
217
return err;
218
}
219
220
return _internal_load(p_path, f);
221
}
222
223
Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_key) {
224
Error err;
225
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
226
227
if (err) {
228
return err;
229
}
230
231
Ref<FileAccessEncrypted> fae;
232
fae.instantiate();
233
err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_READ);
234
if (err) {
235
return err;
236
}
237
return _internal_load(p_path, fae);
238
}
239
240
Error ConfigFile::load_encrypted_pass(const String &p_path, const String &p_pass) {
241
Error err;
242
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
243
244
if (err) {
245
return err;
246
}
247
248
Ref<FileAccessEncrypted> fae;
249
fae.instantiate();
250
err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_READ);
251
if (err) {
252
return err;
253
}
254
255
return _internal_load(p_path, fae);
256
}
257
258
Error ConfigFile::_internal_load(const String &p_path, Ref<FileAccess> f) {
259
VariantParser::StreamFile stream;
260
stream.f = f;
261
262
Error err = _parse(p_path, &stream);
263
264
return err;
265
}
266
267
Error ConfigFile::parse(const String &p_data) {
268
VariantParser::StreamString stream;
269
stream.s = p_data;
270
return _parse("<string>", &stream);
271
}
272
273
Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) {
274
String assign;
275
Variant value;
276
VariantParser::Tag next_tag;
277
278
int lines = 0;
279
String error_text;
280
281
String section;
282
283
while (true) {
284
assign = Variant();
285
next_tag.fields.clear();
286
next_tag.name = String();
287
288
Error err = VariantParser::parse_tag_assign_eof(p_stream, lines, error_text, next_tag, assign, value, nullptr, true);
289
if (err == ERR_FILE_EOF) {
290
return OK;
291
} else if (err != OK) {
292
ERR_PRINT(vformat("ConfigFile parse error at %s:%d: %s.", p_path, lines, error_text));
293
return err;
294
}
295
296
if (!assign.is_empty()) {
297
set_value(section, assign, value);
298
} else if (!next_tag.name.is_empty()) {
299
section = next_tag.name.replace("\\]", "]");
300
}
301
}
302
303
return OK;
304
}
305
306
void ConfigFile::clear() {
307
values.clear();
308
}
309
310
void ConfigFile::_bind_methods() {
311
ClassDB::bind_method(D_METHOD("set_value", "section", "key", "value"), &ConfigFile::set_value);
312
ClassDB::bind_method(D_METHOD("get_value", "section", "key", "default"), &ConfigFile::get_value, DEFVAL(Variant()));
313
314
ClassDB::bind_method(D_METHOD("has_section", "section"), &ConfigFile::has_section);
315
ClassDB::bind_method(D_METHOD("has_section_key", "section", "key"), &ConfigFile::has_section_key);
316
317
ClassDB::bind_method(D_METHOD("get_sections"), &ConfigFile::get_sections);
318
ClassDB::bind_method(D_METHOD("get_section_keys", "section"), &ConfigFile::get_section_keys);
319
320
ClassDB::bind_method(D_METHOD("erase_section", "section"), &ConfigFile::erase_section);
321
ClassDB::bind_method(D_METHOD("erase_section_key", "section", "key"), &ConfigFile::erase_section_key);
322
323
ClassDB::bind_method(D_METHOD("load", "path"), &ConfigFile::load);
324
ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse);
325
ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save);
326
327
ClassDB::bind_method(D_METHOD("encode_to_text"), &ConfigFile::encode_to_text);
328
329
BIND_METHOD_ERR_RETURN_DOC("load", ERR_FILE_CANT_OPEN);
330
331
ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted);
332
ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "password"), &ConfigFile::load_encrypted_pass);
333
334
ClassDB::bind_method(D_METHOD("save_encrypted", "path", "key"), &ConfigFile::save_encrypted);
335
ClassDB::bind_method(D_METHOD("save_encrypted_pass", "path", "password"), &ConfigFile::save_encrypted_pass);
336
337
ClassDB::bind_method(D_METHOD("clear"), &ConfigFile::clear);
338
}
339
340