Mon, 25 Mar 2013 01:04:20 +0200
57181.dat (Philo's model of the XL-motor) showcased a new problem.. there was no handling of unknown colors which led into crashes. Added stdout warnings, also added mid and dark stone colors
30
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
1 | /* |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
2 | * LDForge: LDraw parts authoring CAD |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
3 | * Copyright (C) 2013 Santeri `arezey` Piippo |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
4 | * |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
5 | * This program is free software: you can redistribute it and/or modify |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
6 | * it under the terms of the GNU General Public License as published by |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
7 | * the Free Software Foundation, either version 3 of the License, or |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
8 | * (at your option) any later version. |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
9 | * |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
10 | * This program is distributed in the hope that it will be useful, |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
13 | * GNU General Public License for more details. |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
14 | * |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
15 | * You should have received a copy of the GNU General Public License |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
17 | */ |
31ff9aabd506
Licensed LDForge GPL3, added some more icons
Santeri Piippo <crimsondusk64@gmail.com>
parents:
29
diff
changeset
|
18 | |
0 | 19 | #include <stdio.h> |
20 | // #include <stdlib.h> | |
21 | #include <errno.h> | |
22 | #include <time.h> | |
23 | #include "str.h" | |
24 | #include "config.h" | |
25 | ||
26 | #ifdef CONFIG_WITH_QT | |
27 | #include <QDir> | |
28 | #endif // CONFIG_WITH_QT | |
29 | ||
30 | // ============================================================================= | |
31 | // Define the configs | |
32 | #define CFG(TYPE, SECT, NAME, DESCR, DEFAULT) \ | |
33 | TYPE##config SECT##_##NAME (CFGSECTNAME (SECT), DESCR, \ | |
34 | DEFAULT, #NAME, #SECT "_" #NAME, #TYPE, #DEFAULT); | |
35 | ||
36 | #define SECT(...) | |
37 | #include "cfgdef.h" | |
38 | #undef CFG | |
39 | #undef SECT | |
40 | ||
41 | // ============================================================================= | |
42 | config* config::pointers[] = { | |
43 | #define CFG(TYPE, SECT, NAME, DESCR, DEFAULT) &SECT##_##NAME, | |
44 | #define SECT(...) | |
45 | #include "cfgdef.h" | |
46 | #undef CFG | |
47 | #undef SECT | |
48 | }; | |
49 | ||
50 | // ============================================================================= | |
51 | const char* config::sections[] = { | |
52 | #define CFG(...) | |
53 | #define SECT(A,B) #A, | |
54 | #include "cfgdef.h" | |
55 | #undef CFG | |
56 | #undef SECT | |
57 | }; | |
58 | ||
59 | // ============================================================================= | |
60 | const char* config::sectionNames[] = { | |
61 | #define CFG(...) | |
62 | #define SECT(A,B) #B, | |
63 | #include "cfgdef.h" | |
64 | #undef CFG | |
65 | #undef SECT | |
66 | }; | |
67 | ||
68 | // ============================================================================= | |
69 | const char* g_WeekdayNames[7] = { | |
70 | "Sunday", | |
71 | "Monday", | |
72 | "Tuesday", | |
73 | "Wednesday", | |
74 | "Thursday", | |
75 | "Friday", | |
76 | "Saturday", | |
77 | }; | |
78 | ||
79 | // ============================================================================= | |
80 | static const char* g_MonthNames[12] = { | |
81 | "Januray", | |
82 | "February", | |
83 | "March", | |
84 | "April", | |
85 | "May", | |
86 | "June", | |
87 | "July", | |
88 | "August", | |
89 | "September", | |
90 | "October", | |
91 | "November" | |
92 | "December", | |
93 | }; | |
94 | ||
95 | static const char* g_ConfigTypeNames[] = { | |
96 | "None", | |
97 | "Integer", | |
98 | "String", | |
99 | "Float", | |
100 | "Boolean", | |
101 | }; | |
102 | ||
103 | // ============================================================================= | |
104 | // Load the configuration from file | |
105 | bool config::load () { | |
106 | FILE* fp = fopen (filepath().chars(), "r"); | |
107 | char linedata[MAX_INI_LINE]; | |
108 | char* line; | |
109 | size_t ln = 0; | |
110 | configsection_e section = NO_CONFIG_SECTION; | |
111 | ||
112 | if (!fp) | |
113 | return false; // can't open for reading | |
114 | ||
115 | // Read the values. | |
116 | while (fgets (linedata, MAX_INI_LINE, fp)) { | |
117 | ln++; | |
118 | line = linedata; | |
119 | ||
120 | while (*line != 0 && (*line <= 32 || *line >= 127)) | |
121 | line++; // Skip junk | |
122 | ||
123 | if (*line == '\0' || line[0] == '#') | |
124 | continue; // Empty line or comment. | |
125 | ||
126 | if (line[0] == '[') { | |
127 | // Section | |
128 | char* endbracket = strchr (line, ']'); | |
129 | ||
130 | if (!endbracket) { | |
131 | fprintf (stderr, "badly formed section: %s", line); | |
132 | continue; | |
133 | } | |
134 | ||
135 | str sectionName = str (line).substr (1, endbracket - line); | |
136 | const configsection_e oldsection = section; | |
137 | section = NO_CONFIG_SECTION; | |
138 | ||
139 | // Find the section | |
140 | for (unsigned i = 0; i < NUM_ConfigSections && section == NO_CONFIG_SECTION; i++) | |
141 | if (sectionName.compare (sectionNames[i]) == 0) | |
142 | section = (configsection_e)i; | |
143 | ||
144 | if (section == NO_CONFIG_SECTION) { | |
145 | fprintf (stderr, "unknown config section `%s`\n", sectionName.chars()); | |
146 | section = oldsection; | |
147 | } | |
148 | ||
149 | continue; | |
150 | } | |
151 | ||
152 | // Find the equals sign. | |
153 | char* equals = strchr (line, '='); | |
154 | if (!equals) { | |
155 | fprintf (stderr, "couldn't find `=` sign in entry `%s`\n", line); | |
156 | continue; | |
157 | } | |
158 | ||
159 | str entry = str (line).substr (0, equals - line); | |
160 | ||
161 | str configname; | |
162 | configname.format ("%s_%s", sections[section], entry.chars ()); | |
163 | ||
164 | // Find the config entry for this. | |
165 | config* cfg = NULL; | |
166 | for (size_t i = 0; i < NUM_CONFIG && !cfg; i++) | |
167 | if (configname.compare (pointers[i]->fullname) == 0) | |
168 | cfg = pointers[i]; | |
169 | ||
170 | if (!cfg) { | |
171 | fprintf (stderr, "unknown config `%s`\n", configname.chars()); | |
172 | continue; | |
173 | } | |
174 | ||
175 | str valstring = str (line).substr (equals - line + 1, -1); | |
176 | ||
177 | // Trim the crap off the end | |
178 | while (~valstring) { | |
179 | char c = valstring[~valstring - 1]; | |
180 | if (c <= 32 || c >= 127) | |
181 | valstring -= 1; | |
182 | else | |
183 | break; | |
184 | } | |
185 | ||
186 | switch (const_cast<config*> (cfg)->getType()) { | |
187 | case CONFIG_int: | |
188 | static_cast<intconfig*> (cfg)->value = atoi (valstring.chars()); | |
189 | break; | |
190 | case CONFIG_str: | |
191 | static_cast<strconfig*> (cfg)->value = valstring; | |
192 | break; | |
193 | case CONFIG_float: | |
194 | static_cast<floatconfig*> (cfg)->value = atof (valstring.chars()); | |
195 | break; | |
196 | case CONFIG_bool: | |
197 | { | |
198 | bool& val = static_cast<boolconfig*> (cfg)->value; | |
199 | ||
200 | if (+valstring == "TRUE" || valstring == "1") | |
201 | val = true; | |
202 | else if (+valstring == "FALSE" || valstring == "0") | |
203 | val = false; | |
204 | break; | |
205 | } | |
206 | default: | |
207 | break; | |
208 | } | |
209 | } | |
210 | ||
211 | fclose (fp); | |
212 | return true; | |
213 | } | |
214 | ||
215 | // ============================================================================= | |
216 | // Write a given formatted string to the given file stream | |
217 | static size_t writef (FILE* fp, const char* fmt, ...) { | |
218 | va_list va; | |
219 | ||
220 | va_start (va, fmt); | |
221 | char* buf = vdynformat (fmt, va, 256); | |
222 | va_end (va); | |
223 | ||
224 | size_t len = fwrite (buf, 1, strlen (buf), fp); | |
225 | delete[] buf; | |
226 | ||
227 | return len; | |
228 | } | |
229 | ||
230 | // ============================================================================= | |
231 | // Save the configuration to disk | |
232 | bool config::save () { | |
233 | #ifdef APPNAME | |
234 | #ifdef CONFIG_WITH_QT | |
235 | // If the directory doesn't exist, create it now. | |
236 | if (!QDir (dirpath().chars()).exists ()) { | |
237 | fprintf (stderr, "Creating config path %s...\n", dirpath().chars()); | |
238 | if (!QDir ().mkpath (dirpath().chars())) { | |
239 | fprintf (stderr, "Failed to create the directory. Configuration cannot be saved!\n"); | |
240 | return false; // Couldn't create directory | |
241 | } | |
242 | } | |
243 | #else | |
244 | #warning Need QT to check for directories. Config will not be able | |
245 | #warning to save properly if ~/.APPNAME/ does not exist. | |
246 | #endif // CONFIG_WITH_QT | |
247 | #endif // CONFIG_DIRECTORY | |
248 | ||
249 | FILE* fp = fopen (filepath().chars(), "w"); | |
250 | printf ("writing cfg to %s\n", filepath().chars()); | |
251 | ||
252 | if (!fp) { | |
253 | printf ("Couldn't open %s for writing\n", filepath().chars()); | |
254 | return false; | |
255 | } | |
256 | ||
257 | const time_t curtime = time (NULL); | |
258 | const struct tm* timeinfo = localtime (&curtime); | |
259 | const char* daysuffix = | |
260 | (timeinfo->tm_mday % 10 == 1) ? "st" : | |
261 | (timeinfo->tm_mday % 10 == 2) ? "nd" : | |
262 | (timeinfo->tm_mday % 10 == 3) ? "rd" : "th"; | |
263 | ||
264 | writef (fp, "# Configuration file for " APPNAME "\n"); | |
265 | writef (fp, "# Written on %s, %s %d%s %d %.2d:%.2d:%.2d\n", | |
266 | g_WeekdayNames[timeinfo->tm_wday], g_MonthNames[timeinfo->tm_mon], | |
267 | timeinfo->tm_mday, daysuffix, timeinfo->tm_year + 1900, | |
268 | timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); | |
269 | writef (fp, "\n"); | |
270 | ||
271 | for (int i = 0; i < NUM_ConfigSections; i++) { | |
272 | if (i > 0) | |
273 | writef (fp, "\n"); | |
274 | ||
275 | writef (fp, "[%s]\n", sectionNames[i]); | |
276 | bool first = true; | |
277 | ||
278 | for (size_t j = 0; j < NUM_CONFIG; j++) { | |
279 | config* cfg = pointers[j]; | |
280 | ||
281 | if (cfg->sect != i) | |
282 | continue; | |
283 | ||
284 | if (!first) | |
285 | writef (fp, "\n"); | |
286 | ||
287 | str valstring; | |
288 | switch (cfg->getType()) { | |
289 | case CONFIG_int: | |
290 | valstring.format ("%d", static_cast<intconfig*> (cfg)->value); | |
291 | break; | |
292 | case CONFIG_str: | |
293 | valstring = static_cast<strconfig*> (cfg)->value; | |
294 | break; | |
295 | case CONFIG_float: | |
296 | valstring.format ("%f", static_cast<floatconfig*> (cfg)->value); | |
297 | ||
298 | // Trim any trailing zeros | |
299 | if (valstring.first (".") != -1) { | |
300 | while (valstring[~valstring - 1] == '0') | |
301 | valstring -= 1; | |
302 | ||
303 | // But don't trim the only one out... | |
304 | if (valstring[~valstring - 1] == '.') | |
305 | valstring += '0'; | |
306 | } | |
307 | ||
308 | break; | |
309 | case CONFIG_bool: | |
310 | valstring = (static_cast<boolconfig*> (cfg)->value) ? "true" : "false"; | |
311 | break; | |
312 | default: | |
313 | break; | |
314 | } | |
315 | ||
316 | // Write the entry now. | |
317 | writef (fp, "# %s, default: %s\n", g_ConfigTypeNames[cfg->getType()], cfg->defaultstring); | |
318 | writef (fp, "# %s: %s\n", cfg->fullname, cfg->description); | |
319 | writef (fp, "%s=%s\n", cfg->name, valstring.chars()); | |
320 | first = false; | |
321 | } | |
322 | } | |
323 | ||
324 | fclose (fp); | |
325 | return true; | |
326 | } | |
327 | ||
328 | // ============================================================================= | |
329 | void config::reset () { | |
330 | for (size_t i = 0; i < NUM_CONFIG; i++) | |
331 | pointers[i]->resetValue (); | |
332 | } | |
333 | ||
334 | // ============================================================================= | |
335 | str config::filepath () { | |
336 | #ifdef APPNAME | |
337 | str path; | |
338 | path.format ("%s" APPNAME ".ini", dirpath().chars()); | |
339 | return path; | |
340 | #else // APPNAME | |
341 | return "config.ini"; | |
342 | #endif // APPNAME | |
343 | } | |
344 | ||
345 | // ============================================================================= | |
346 | str config::dirpath () { | |
347 | #ifdef APPNAME | |
348 | str path; | |
349 | ||
350 | #ifdef CONFIG_WITH_QT | |
351 | path = (QDir::homePath ().toStdString().c_str()); | |
352 | #else // CONFIG_WITH_QT | |
353 | path = "~"; | |
354 | #endif // CONFIG_WITH_QT | |
355 | ||
356 | path += "/." APPNAME "/"; | |
357 | #else | |
358 | path = "./"; | |
359 | #endif // APPNAME | |
360 | ||
361 | return path; | |
362 | } |