1 /*
2 C-Dogs SDL
3 A port of the legendary (and fun) action/arcade cdogs.
4
5 Copyright (c) 2013-2014, 2016, Cong Xu
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10
11 Redistributions of source code must retain the above copyright notice, this
12 list of conditions and the following disclaimer.
13 Redistributions in binary form must reproduce the above copyright notice,
14 this list of conditions and the following disclaimer in the documentation
15 and/or other materials provided with the distribution.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
28 */
29 #include "config_json.h"
30
31 #include <stdio.h>
32
33 #include <json/json.h>
34
35 #include "config.h"
36 #include "json_utils.h"
37 #include "keyboard.h"
38 #include "log.h"
39
40 #ifdef __EMSCRIPTEN__
41 #include <emscripten.h>
42 #endif
43
44 static void ConfigLoadVisit(Config *c, json_t *node);
ConfigLoadJSON(Config * config,const char * filename)45 void ConfigLoadJSON(Config *config, const char *filename)
46 {
47 FILE *f = fopen(filename, "r");
48 json_t *root = NULL;
49 int version;
50
51 if (f == NULL)
52 {
53 printf("Error loading config '%s'\n", filename);
54 goto bail;
55 }
56
57 if (json_stream_parse(f, &root) != JSON_OK)
58 {
59 printf("Error parsing config '%s'\n", filename);
60 goto bail;
61 }
62 LoadInt(&version, root, "Version");
63 ConfigLoadVisit(config, root);
64
65 // Old config version stuff
66 if (version < 8)
67 {
68 json_t *node = JSONFindNode(root, "Game");
69 if (node != NULL)
70 {
71 Config *c = ConfigGet(config, "Graphics.Shadows");
72 LoadBool(&c->u.Bool.Value, node, c->Name);
73 c = ConfigGet(config, "Graphics.Gore");
74 JSON_UTILS_LOAD_ENUM(
75 c->u.Enum.Value, node, c->Name, c->u.Enum.StrToEnum);
76 }
77 }
78 if (version < 5)
79 {
80 json_t *node = JSONFindNode(root, "Game");
81 if (node != NULL)
82 {
83 bool moveWhenShooting = false;
84 LoadBool(&moveWhenShooting, node, "MoveWhenShooting");
85 Config *c = ConfigGet(config, "Game.FireMoveStyle");
86 c->u.Enum.Value = moveWhenShooting ? FIREMOVE_NORMAL : FIREMOVE_STOP;
87 }
88 }
89 if (version < 4)
90 {
91 json_t *node = JSONFindNode(root, "Interface");
92 if (node != NULL)
93 {
94 bool splitscreenAlways;
95 LoadBool(&splitscreenAlways, node, "SplitscreenAlways");
96 Config *c = ConfigGet(config, "Interface.Splitscreen");
97 c->u.Enum.Value =
98 splitscreenAlways ? SPLITSCREEN_ALWAYS : SPLITSCREEN_NORMAL;
99 }
100 }
101
102 bail:
103 json_free_value(&root);
104 if (f != NULL)
105 {
106 fclose(f);
107 }
108 }
ConfigLoadVisit(Config * c,json_t * node)109 static void ConfigLoadVisit(Config *c, json_t *node)
110 {
111 if (node == NULL)
112 {
113 LOG(LM_MAIN, LL_WARN, "Error loading config: node %s not found",
114 c->Name);
115 return;
116 }
117 switch (c->Type)
118 {
119 case CONFIG_TYPE_STRING:
120 CASSERT(false, "not implemented");
121 break;
122 case CONFIG_TYPE_INT:
123 LoadInt(&c->u.Int.Value, node, c->Name);
124 if (c->u.Int.Min > 0)
125 c->u.Int.Value = MAX(c->u.Int.Value, c->u.Int.Min);
126 if (c->u.Int.Max > 0)
127 c->u.Int.Value = MIN(c->u.Int.Value, c->u.Int.Max);
128 break;
129 case CONFIG_TYPE_FLOAT:
130 LoadDouble(&c->u.Float.Value, node, c->Name);
131 if (c->u.Float.Min > 0)
132 c->u.Float.Value = MAX(c->u.Float.Value, c->u.Float.Min);
133 if (c->u.Float.Max > 0)
134 c->u.Float.Value = MIN(c->u.Float.Value, c->u.Float.Max);
135 break;
136 case CONFIG_TYPE_BOOL:
137 LoadBool(&c->u.Bool.Value, node, c->Name);
138 break;
139 case CONFIG_TYPE_ENUM:
140 JSON_UTILS_LOAD_ENUM(
141 c->u.Enum.Value, node, c->Name, c->u.Enum.StrToEnum);
142 if (c->u.Enum.Min > 0)
143 c->u.Enum.Value = MAX(c->u.Enum.Value, c->u.Enum.Min);
144 if (c->u.Enum.Max > 0)
145 c->u.Enum.Value = MIN(c->u.Enum.Value, c->u.Enum.Max);
146 break;
147 case CONFIG_TYPE_GROUP:
148 {
149 // If the config has no name, then it is the root element
150 // Load children directly to the node
151 // Otherwise, find the named child
152 if (c->Name != NULL)
153 {
154 node = json_find_first_label(node, c->Name);
155 if (node == NULL)
156 {
157 LOG(LM_MAIN, LL_WARN,
158 "Error loading config: node %s not found", c->Name);
159 return;
160 }
161 node = node->child;
162 }
163 CA_FOREACH(Config, child, c->u.Group)
164 ConfigLoadVisit(child, node);
165 CA_FOREACH_END()
166 }
167 break;
168 default:
169 CASSERT(false, "Unknown config type");
170 break;
171 }
172 }
173
174 static void ConfigSaveVisit(const Config *c, json_t *node);
ConfigSaveJSON(const Config * config,const char * filename)175 void ConfigSaveJSON(const Config *config, const char *filename)
176 {
177 FILE *f = fopen(filename, "w");
178 char *text = NULL;
179 json_t *root;
180
181 if (f == NULL)
182 {
183 printf("Error saving config '%s'\n", filename);
184 return;
185 }
186
187 root = json_new_object();
188 json_insert_pair_into_object(
189 root, "Version", json_new_number(TOSTRING(CONFIG_VERSION)));
190 ConfigSaveVisit(config, root);
191
192 json_tree_to_string(root, &text);
193 char *formatText = json_format_string(text);
194 fputs(formatText, f);
195
196 // clean up
197 CFREE(formatText);
198 CFREE(text);
199 json_free_value(&root);
200
201 fclose(f);
202
203 #ifdef __EMSCRIPTEN__
204 EM_ASM(
205 //persist changes
206 FS.syncfs(false,function (err) {
207 assert(!err);
208 });
209 );
210 #endif
211 }
ConfigSaveVisit(const Config * c,json_t * node)212 static void ConfigSaveVisit(const Config *c, json_t *node)
213 {
214 switch (c->Type)
215 {
216 case CONFIG_TYPE_STRING:
217 CASSERT(false, "not implemented");
218 break;
219 case CONFIG_TYPE_INT:
220 AddIntPair(node, c->Name, c->u.Int.Value);
221 break;
222 case CONFIG_TYPE_FLOAT:
223 CASSERT(false, "not implemented");
224 break;
225 case CONFIG_TYPE_BOOL:
226 json_insert_pair_into_object(
227 node, c->Name, json_new_bool(c->u.Bool.Value));
228 break;
229 case CONFIG_TYPE_ENUM:
230 JSON_UTILS_ADD_ENUM_PAIR(
231 node, c->Name, c->u.Enum.Value, c->u.Enum.EnumToStr);
232 break;
233 case CONFIG_TYPE_GROUP:
234 {
235 json_t *child = node;
236 // If the config has no name, then it is the root element
237 // Add children directly to the node
238 // Otherwise, create a new child
239 if (c->Name != NULL)
240 {
241 child = json_new_object();
242 }
243 CA_FOREACH(Config, cg, c->u.Group)
244 ConfigSaveVisit(cg, child);
245 CA_FOREACH_END()
246 if (c->Name != NULL)
247 {
248 json_insert_pair_into_object(node, c->Name, child);
249 }
250 }
251 break;
252 default:
253 CASSERT(false, "Unknown config type");
254 break;
255 }
256 }
257
ConfigGetJSONVersion(FILE * f)258 int ConfigGetJSONVersion(FILE *f)
259 {
260 json_t *root = NULL;
261 json_t *child = NULL;
262 int version = -1;
263
264 if (json_stream_parse(f, &root) != JSON_OK)
265 {
266 printf("Error parsing JSON config\n");
267 goto bail;
268 }
269 child = json_find_first_label(root, "Version");
270 if (child == NULL)
271 {
272 printf("Error parsing JSON config version\n");
273 goto bail;
274 }
275 version = atoi(child->child->text);
276
277 bail:
278 json_free_value(&root);
279 return version;
280 }
281