1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | * Mupen64plus-core - api/config.c * |
3 | * Mupen64Plus homepage: https://mupen64plus.org/ * |
4 | * Copyright (C) 2009 Richard Goedeken * |
5 | * * |
6 | * This program is free software; you can redistribute it and/or modify * |
7 | * it under the terms of the GNU General Public License as published by * |
8 | * the Free Software Foundation; either version 2 of the License, or * |
9 | * (at your option) any later version. * |
10 | * * |
11 | * This program is distributed in the hope that it will be useful, * |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
14 | * GNU General Public License for more details. * |
15 | * * |
16 | * You should have received a copy of the GNU General Public License * |
17 | * along with this program; if not, write to the * |
18 | * Free Software Foundation, Inc., * |
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
20 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
21 | |
22 | /* This file contains the Core config functions which will be exported |
23 | * outside of the core library. |
24 | */ |
25 | |
26 | #include <stddef.h> |
27 | #include <stdio.h> |
28 | #include <stdlib.h> |
29 | #include <string.h> |
30 | |
31 | #define M64P_CORE_PROTOTYPES 1 |
32 | #include "callbacks.h" |
33 | #include "config.h" |
34 | #include "m64p_config.h" |
35 | #include "m64p_types.h" |
36 | #include "main/util.h" |
37 | #include "osal/files.h" |
38 | #include "osal/preproc.h" |
39 | |
40 | /* local types */ |
41 | #define MUPEN64PLUS_CFG_NAME "mupen64plus.cfg" |
42 | |
43 | #define SECTION_MAGIC 0xDBDC0580 |
44 | |
45 | struct external_config { |
46 | char *file; |
47 | size_t length; |
48 | }; |
49 | |
50 | typedef struct _config_var { |
51 | char *name; |
52 | m64p_type type; |
53 | union { |
54 | int integer; |
55 | float number; |
56 | char *string; |
57 | } val; |
58 | char *; |
59 | struct _config_var *next; |
60 | } config_var; |
61 | |
62 | typedef struct _config_section { |
63 | unsigned int magic; |
64 | char *name; |
65 | struct _config_var *first_var; |
66 | struct _config_section *next; |
67 | } config_section; |
68 | |
69 | typedef config_section *config_list; |
70 | |
71 | /* local variables */ |
72 | static int l_ConfigInit = 0; |
73 | static char *l_DataDirOverride = NULL; |
74 | static char *l_ConfigDirOverride = NULL; |
75 | static config_list l_ConfigListActive = NULL; |
76 | static config_list l_ConfigListSaved = NULL; |
77 | |
78 | /* --------------- */ |
79 | /* local functions */ |
80 | /* --------------- */ |
81 | |
82 | static int is_numeric(const char *string) |
83 | { |
84 | char chTemp[16]; |
85 | float fTemp; |
86 | int rval = sscanf(string, "%f%8s" , &fTemp, chTemp); |
87 | |
88 | /* I want to find exactly one matched input item: a number with no garbage on the end */ |
89 | /* I use sscanf() instead of a custom loop because this routine must handle locales in which the decimal separator is not '.' */ |
90 | return (rval == 1); |
91 | } |
92 | |
93 | /* This function returns a pointer to the pointer of the requested section |
94 | * (i.e. a pointer the next field of the previous element, or to the first node). |
95 | * |
96 | * If there's no section named 'ParamName', returns the pointer to the next |
97 | * field of the last element in the list (such that derefencing it is NULL). |
98 | * |
99 | * Useful for operations that need to modify the links, e.g. deleting a section. |
100 | */ |
101 | static config_section **find_section_link(config_list *list, const char *ParamName) |
102 | { |
103 | config_section **curr_sec_link; |
104 | for (curr_sec_link = list; *curr_sec_link != NULL; curr_sec_link = &(*curr_sec_link)->next) |
105 | { |
106 | if (osal_insensitive_strcmp(ParamName, (*curr_sec_link)->name) == 0) |
107 | break; |
108 | } |
109 | |
110 | return curr_sec_link; |
111 | } |
112 | |
113 | /* This function is similar to the previous function, but instead it returns a |
114 | * pointer to the pointer to the next section whose name is alphabetically |
115 | * greater than or equal to 'ParamName'. |
116 | * |
117 | * Useful for inserting a section in its alphabetical position. |
118 | */ |
119 | static config_section **find_alpha_section_link(config_list *list, const char *ParamName) |
120 | { |
121 | config_section **curr_sec_link; |
122 | for (curr_sec_link = list; *curr_sec_link != NULL; curr_sec_link = &(*curr_sec_link)->next) |
123 | { |
124 | if (osal_insensitive_strcmp((*curr_sec_link)->name, ParamName) >= 0) |
125 | break; |
126 | } |
127 | |
128 | return curr_sec_link; |
129 | } |
130 | |
131 | static config_section *find_section(config_list list, const char *ParamName) |
132 | { |
133 | return *find_section_link(&list, ParamName); |
134 | } |
135 | |
136 | static config_var *config_var_create(const char *ParamName, const char *ParamHelp) |
137 | { |
138 | config_var *var; |
139 | |
140 | if (ParamName == NULL) |
141 | return NULL; |
142 | |
143 | var = (config_var *) malloc(sizeof(config_var)); |
144 | if (var == NULL) |
145 | return NULL; |
146 | |
147 | memset(var, 0, sizeof(config_var)); |
148 | |
149 | var->name = strdup(ParamName); |
150 | if (var->name == NULL) |
151 | { |
152 | free(var); |
153 | return NULL; |
154 | } |
155 | |
156 | var->type = M64TYPE_INT; |
157 | var->val.integer = 0; |
158 | |
159 | if (ParamHelp != NULL) |
160 | { |
161 | var->comment = strdup(ParamHelp); |
162 | if (var->comment == NULL) |
163 | { |
164 | free(var->name); |
165 | free(var); |
166 | return NULL; |
167 | } |
168 | } |
169 | else |
170 | var->comment = NULL; |
171 | |
172 | var->next = NULL; |
173 | return var; |
174 | } |
175 | |
176 | static config_var *find_section_var(config_section *section, const char *ParamName) |
177 | { |
178 | /* walk through the linked list of variables in the section */ |
179 | config_var *curr_var; |
180 | for (curr_var = section->first_var; curr_var != NULL; curr_var = curr_var->next) |
181 | { |
182 | if (osal_insensitive_strcmp(ParamName, curr_var->name) == 0) |
183 | return curr_var; |
184 | } |
185 | |
186 | /* couldn't find this configuration parameter */ |
187 | return NULL; |
188 | } |
189 | |
190 | static void append_var_to_section(config_section *section, config_var *var) |
191 | { |
192 | config_var *last_var; |
193 | |
194 | if (section == NULL || var == NULL || section->magic != SECTION_MAGIC) |
195 | return; |
196 | |
197 | if (section->first_var == NULL) |
198 | { |
199 | section->first_var = var; |
200 | return; |
201 | } |
202 | |
203 | last_var = section->first_var; |
204 | while (last_var->next != NULL) |
205 | last_var = last_var->next; |
206 | |
207 | last_var->next = var; |
208 | } |
209 | |
210 | static void delete_var(config_var *var) |
211 | { |
212 | if (var->type == M64TYPE_STRING) |
213 | free(var->val.string); |
214 | free(var->name); |
215 | free(var->comment); |
216 | free(var); |
217 | } |
218 | |
219 | static void delete_section(config_section *pSection) |
220 | { |
221 | config_var *curr_var; |
222 | |
223 | if (pSection == NULL) |
224 | return; |
225 | |
226 | curr_var = pSection->first_var; |
227 | while (curr_var != NULL) |
228 | { |
229 | config_var *next_var = curr_var->next; |
230 | delete_var(curr_var); |
231 | curr_var = next_var; |
232 | } |
233 | |
234 | free(pSection->name); |
235 | free(pSection); |
236 | } |
237 | |
238 | static void delete_list(config_list *pConfigList) |
239 | { |
240 | config_section *curr_section = *pConfigList; |
241 | while (curr_section != NULL) |
242 | { |
243 | config_section *next_section = curr_section->next; |
244 | /* delete the section itself */ |
245 | delete_section(curr_section); |
246 | |
247 | curr_section = next_section; |
248 | } |
249 | |
250 | *pConfigList = NULL; |
251 | } |
252 | |
253 | static config_section *config_section_create(const char *ParamName) |
254 | { |
255 | config_section *sec; |
256 | |
257 | if (ParamName == NULL) |
258 | return NULL; |
259 | |
260 | sec = (config_section *) malloc(sizeof(config_section)); |
261 | if (sec == NULL) |
262 | return NULL; |
263 | |
264 | sec->magic = SECTION_MAGIC; |
265 | sec->name = strdup(ParamName); |
266 | if (sec->name == NULL) |
267 | { |
268 | free(sec); |
269 | return NULL; |
270 | } |
271 | sec->first_var = NULL; |
272 | sec->next = NULL; |
273 | return sec; |
274 | } |
275 | |
276 | static config_section * section_deepcopy(config_section *orig_section) |
277 | { |
278 | config_section *new_section; |
279 | config_var *orig_var, *last_new_var; |
280 | |
281 | /* Input validation */ |
282 | if (orig_section == NULL) |
283 | return NULL; |
284 | |
285 | /* create and copy section struct */ |
286 | new_section = config_section_create(orig_section->name); |
287 | if (new_section == NULL) |
288 | return NULL; |
289 | |
290 | /* create and copy all section variables */ |
291 | orig_var = orig_section->first_var; |
292 | last_new_var = NULL; |
293 | while (orig_var != NULL) |
294 | { |
295 | config_var *new_var = config_var_create(orig_var->name, orig_var->comment); |
296 | if (new_var == NULL) |
297 | { |
298 | delete_section(new_section); |
299 | return NULL; |
300 | } |
301 | |
302 | new_var->type = orig_var->type; |
303 | switch (orig_var->type) |
304 | { |
305 | case M64TYPE_INT: |
306 | case M64TYPE_BOOL: |
307 | new_var->val.integer = orig_var->val.integer; |
308 | break; |
309 | |
310 | case M64TYPE_FLOAT: |
311 | new_var->val.number = orig_var->val.number; |
312 | break; |
313 | |
314 | case M64TYPE_STRING: |
315 | if (orig_var->val.string != NULL) |
316 | { |
317 | new_var->val.string = strdup(orig_var->val.string); |
318 | if (new_var->val.string == NULL) |
319 | { |
320 | delete_section(new_section); |
321 | delete_var(new_var); |
322 | return NULL; |
323 | } |
324 | } |
325 | else |
326 | new_var->val.string = NULL; |
327 | |
328 | break; |
329 | } |
330 | |
331 | /* add the new variable to the new section */ |
332 | if (last_new_var == NULL) |
333 | new_section->first_var = new_var; |
334 | else |
335 | last_new_var->next = new_var; |
336 | last_new_var = new_var; |
337 | /* advance variable pointer in original section variable list */ |
338 | orig_var = orig_var->next; |
339 | } |
340 | |
341 | return new_section; |
342 | } |
343 | |
344 | static void copy_configlist_active_to_saved(void) |
345 | { |
346 | config_section *curr_section = l_ConfigListActive; |
347 | config_section *last_section = NULL; |
348 | |
349 | /* delete any pre-existing Saved config list */ |
350 | delete_list(&l_ConfigListSaved); |
351 | |
352 | /* duplicate all of the config sections in the Active list, adding them to the Saved list */ |
353 | while (curr_section != NULL) |
354 | { |
355 | config_section *new_section = section_deepcopy(curr_section); |
356 | if (new_section == NULL) break; |
357 | if (last_section == NULL) |
358 | l_ConfigListSaved = new_section; |
359 | else |
360 | last_section->next = new_section; |
361 | last_section = new_section; |
362 | curr_section = curr_section->next; |
363 | } |
364 | } |
365 | |
366 | static m64p_error write_configlist_file(void) |
367 | { |
368 | config_section *curr_section; |
369 | const char *configpath; |
370 | char *filepath; |
371 | FILE *fPtr; |
372 | |
373 | /* get the full pathname to the config file and try to open it */ |
374 | configpath = ConfigGetUserConfigPath(); |
375 | if (configpath == NULL) |
376 | return M64ERR_FILES; |
377 | |
378 | filepath = combinepath(configpath, MUPEN64PLUS_CFG_NAME); |
379 | if (filepath == NULL) |
380 | return M64ERR_NO_MEMORY; |
381 | |
382 | fPtr = fopen(filepath, "wb" ); |
383 | if (fPtr == NULL) |
384 | { |
385 | DebugMessage(M64MSG_ERROR, "Couldn't open configuration file '%s' for writing." , filepath); |
386 | free(filepath); |
387 | return M64ERR_FILES; |
388 | } |
389 | free(filepath); |
390 | |
391 | /* write out header */ |
392 | fprintf(fPtr, "# Mupen64Plus Configuration File\n" ); |
393 | fprintf(fPtr, "# This file is automatically read and written by the Mupen64Plus Core library\n" ); |
394 | |
395 | /* write out all of the config parameters from the Saved list */ |
396 | curr_section = l_ConfigListSaved; |
397 | while (curr_section != NULL) |
398 | { |
399 | config_var *curr_var = curr_section->first_var; |
400 | fprintf(fPtr, "\n[%s]\n\n" , curr_section->name); |
401 | while (curr_var != NULL) |
402 | { |
403 | if (curr_var->comment != NULL && strlen(curr_var->comment) > 0) |
404 | fprintf(fPtr, "# %s\n" , curr_var->comment); |
405 | if (curr_var->type == M64TYPE_INT) |
406 | fprintf(fPtr, "%s = %i\n" , curr_var->name, curr_var->val.integer); |
407 | else if (curr_var->type == M64TYPE_FLOAT) |
408 | fprintf(fPtr, "%s = %f\n" , curr_var->name, curr_var->val.number); |
409 | else if (curr_var->type == M64TYPE_BOOL && curr_var->val.integer) |
410 | fprintf(fPtr, "%s = True\n" , curr_var->name); |
411 | else if (curr_var->type == M64TYPE_BOOL && !curr_var->val.integer) |
412 | fprintf(fPtr, "%s = False\n" , curr_var->name); |
413 | else if (curr_var->type == M64TYPE_STRING && curr_var->val.string != NULL) |
414 | fprintf(fPtr, "%s = \"%s\"\n" , curr_var->name, curr_var->val.string); |
415 | curr_var = curr_var->next; |
416 | } |
417 | fprintf(fPtr, "\n" ); |
418 | curr_section = curr_section->next; |
419 | } |
420 | |
421 | fclose(fPtr); |
422 | return M64ERR_SUCCESS; |
423 | } |
424 | |
425 | /* ----------------------------------------------------------- */ |
426 | /* these functions are only to be used within the Core library */ |
427 | /* ----------------------------------------------------------- */ |
428 | |
429 | m64p_error ConfigInit(const char *ConfigDirOverride, const char *DataDirOverride) |
430 | { |
431 | m64p_error rval; |
432 | const char *configpath = NULL; |
433 | char *filepath; |
434 | long ftell_result; |
435 | size_t filelen; |
436 | FILE *fPtr; |
437 | char *configtext; |
438 | |
439 | config_section *current_section = NULL; |
440 | char *line, *end, *; |
441 | |
442 | if (l_ConfigInit) |
443 | return M64ERR_ALREADY_INIT; |
444 | l_ConfigInit = 1; |
445 | |
446 | /* if a data directory was specified, make a copy of it */ |
447 | if (DataDirOverride != NULL) |
448 | { |
449 | l_DataDirOverride = strdup(DataDirOverride); |
450 | if (l_DataDirOverride == NULL) |
451 | return M64ERR_NO_MEMORY; |
452 | } |
453 | |
454 | /* if a config directory was specified, make a copy of it */ |
455 | if (ConfigDirOverride != NULL) |
456 | { |
457 | l_ConfigDirOverride = strdup(ConfigDirOverride); |
458 | if (l_ConfigDirOverride == NULL) |
459 | return M64ERR_NO_MEMORY; |
460 | } |
461 | |
462 | /* get the full pathname to the config file and try to open it */ |
463 | configpath = ConfigGetUserConfigPath(); |
464 | if (configpath == NULL) |
465 | return M64ERR_FILES; |
466 | |
467 | filepath = combinepath(configpath, MUPEN64PLUS_CFG_NAME); |
468 | if (filepath == NULL) |
469 | return M64ERR_NO_MEMORY; |
470 | |
471 | fPtr = fopen(filepath, "rb" ); |
472 | if (fPtr == NULL) |
473 | { |
474 | DebugMessage(M64MSG_INFO, "Couldn't open configuration file '%s'. Using defaults." , filepath); |
475 | free(filepath); |
476 | return M64ERR_SUCCESS; |
477 | } |
478 | free(filepath); |
479 | |
480 | /* read the entire config file */ |
481 | if (fseek(fPtr, 0L, SEEK_END) != 0) |
482 | { |
483 | fclose(fPtr); |
484 | return M64ERR_FILES; |
485 | } |
486 | ftell_result = ftell(fPtr); |
487 | if (ftell_result == -1) |
488 | { |
489 | fclose(fPtr); |
490 | return M64ERR_FILES; |
491 | } |
492 | filelen = (size_t)ftell_result; |
493 | if (fseek(fPtr, 0L, SEEK_SET) != 0) |
494 | { |
495 | fclose(fPtr); |
496 | return M64ERR_FILES; |
497 | } |
498 | |
499 | configtext = (char *) malloc(filelen + 1); |
500 | if (configtext == NULL) |
501 | { |
502 | fclose(fPtr); |
503 | return M64ERR_NO_MEMORY; |
504 | } |
505 | if (fread(configtext, 1, filelen, fPtr) != filelen) |
506 | { |
507 | free(configtext); |
508 | fclose(fPtr); |
509 | return M64ERR_FILES; |
510 | } |
511 | fclose(fPtr); |
512 | |
513 | /* parse the file data */ |
514 | current_section = NULL; |
515 | line = configtext; |
516 | end = configtext + filelen; |
517 | lastcomment = NULL; |
518 | *end = 0; |
519 | while (line < end) |
520 | { |
521 | ini_line l = ini_parse_line(&line); |
522 | switch (l.type) |
523 | { |
524 | case INI_COMMENT: |
525 | lastcomment = l.value; |
526 | break; |
527 | |
528 | case INI_SECTION: |
529 | rval = ConfigOpenSection(l.name, (m64p_handle *) ¤t_section); |
530 | if (rval != M64ERR_SUCCESS) |
531 | { |
532 | free(configtext); |
533 | return rval; |
534 | } |
535 | lastcomment = NULL; |
536 | break; |
537 | |
538 | case INI_PROPERTY: |
539 | if (l.value[0] == '"' && l.value[strlen(l.value)-1] == '"') |
540 | { |
541 | l.value++; |
542 | l.value[strlen(l.value)-1] = 0; |
543 | ConfigSetDefaultString((m64p_handle) current_section, l.name, l.value, lastcomment); |
544 | } |
545 | else if (osal_insensitive_strcmp(l.value, "false" ) == 0) |
546 | { |
547 | ConfigSetDefaultBool((m64p_handle) current_section, l.name, 0, lastcomment); |
548 | } |
549 | else if (osal_insensitive_strcmp(l.value, "true" ) == 0) |
550 | { |
551 | ConfigSetDefaultBool((m64p_handle) current_section, l.name, 1, lastcomment); |
552 | } |
553 | else if (is_numeric(l.value)) |
554 | { |
555 | /* preserve values as floats if they are written in the config as floats */ |
556 | if (strchr(l.value, '.')) |
557 | { |
558 | float val_float = (float) strtod(l.value, NULL); |
559 | ConfigSetDefaultFloat((m64p_handle) current_section, l.name, val_float, lastcomment); |
560 | } |
561 | else |
562 | { |
563 | int val_int = (int) strtol(l.value, NULL, 10); |
564 | ConfigSetDefaultInt((m64p_handle) current_section, l.name, val_int, lastcomment); |
565 | } |
566 | } |
567 | else |
568 | { |
569 | /* assume that it's a string */ |
570 | ConfigSetDefaultString((m64p_handle) current_section, l.name, l.value, lastcomment); |
571 | } |
572 | lastcomment = NULL; |
573 | break; |
574 | |
575 | default: |
576 | break; |
577 | } |
578 | } |
579 | |
580 | /* release memory used for config file text */ |
581 | free(configtext); |
582 | |
583 | /* duplicate the entire config data list, to store a copy of the list which represents the state of the file on disk */ |
584 | copy_configlist_active_to_saved(); |
585 | |
586 | return M64ERR_SUCCESS; |
587 | } |
588 | |
589 | m64p_error ConfigShutdown(void) |
590 | { |
591 | /* reset the initialized flag */ |
592 | if (!l_ConfigInit) |
593 | return M64ERR_NOT_INIT; |
594 | l_ConfigInit = 0; |
595 | |
596 | /* free any malloc'd local variables */ |
597 | if (l_DataDirOverride != NULL) |
598 | { |
599 | free(l_DataDirOverride); |
600 | l_DataDirOverride = NULL; |
601 | } |
602 | if (l_ConfigDirOverride != NULL) |
603 | { |
604 | free(l_ConfigDirOverride); |
605 | l_ConfigDirOverride = NULL; |
606 | } |
607 | |
608 | /* free all of the memory in the 2 lists */ |
609 | delete_list(&l_ConfigListActive); |
610 | delete_list(&l_ConfigListSaved); |
611 | |
612 | return M64ERR_SUCCESS; |
613 | } |
614 | |
615 | /* ------------------------------------------------ */ |
616 | /* Selector functions, exported outside of the Core */ |
617 | /* ------------------------------------------------ */ |
618 | |
619 | EXPORT m64p_error CALL ConfigExternalOpen(const char *FileName, m64p_handle *Handle) |
620 | { |
621 | FILE *fPtr; |
622 | struct external_config* ext_config; |
623 | long ftell_result; |
624 | size_t filelen = 0; |
625 | if (FileName == NULL || (fPtr = fopen(FileName, "rb" )) == NULL) |
626 | { |
627 | DebugMessage(M64MSG_ERROR, "Unable to open config file '%s'." , FileName); |
628 | return M64ERR_INPUT_INVALID; |
629 | } |
630 | /* read the entire config file */ |
631 | if (fseek(fPtr, 0L, SEEK_END) != 0) |
632 | { |
633 | fclose(fPtr); |
634 | return M64ERR_INPUT_INVALID; |
635 | } |
636 | ftell_result = ftell(fPtr); |
637 | if (ftell_result == -1) |
638 | { |
639 | fclose(fPtr); |
640 | return M64ERR_INPUT_INVALID; |
641 | } |
642 | filelen = (size_t)ftell_result; |
643 | if (fseek(fPtr, 0L, SEEK_SET) != 0) |
644 | { |
645 | fclose(fPtr); |
646 | return M64ERR_INPUT_INVALID; |
647 | } |
648 | ext_config = malloc(sizeof(struct external_config)); |
649 | if (ext_config == NULL) |
650 | { |
651 | fclose(fPtr); |
652 | return M64ERR_INPUT_INVALID; |
653 | } |
654 | ext_config->file = malloc(filelen + 1); |
655 | if (ext_config->file == NULL) |
656 | { |
657 | free(ext_config); |
658 | fclose(fPtr); |
659 | return M64ERR_INPUT_INVALID; |
660 | } |
661 | if (fread(ext_config->file, 1, filelen, fPtr) != filelen) |
662 | { |
663 | free(ext_config->file); |
664 | free(ext_config); |
665 | fclose(fPtr); |
666 | return M64ERR_INPUT_INVALID; |
667 | } |
668 | fclose(fPtr); |
669 | ext_config->length = filelen; |
670 | *Handle = ext_config; |
671 | return M64ERR_SUCCESS; |
672 | } |
673 | |
674 | EXPORT m64p_error CALL ConfigExternalClose(m64p_handle Handle) |
675 | { |
676 | struct external_config* ext_config = Handle; |
677 | if (ext_config != NULL) { |
678 | if (ext_config->file != NULL) |
679 | free(ext_config->file); |
680 | free(ext_config); |
681 | return M64ERR_SUCCESS; |
682 | } |
683 | return M64ERR_INPUT_INVALID; |
684 | } |
685 | |
686 | EXPORT m64p_error CALL ConfigExternalGetParameter(m64p_handle Handle, const char *SectionName, const char *ParamName, char* ParamPtr, int ParamMaxLength) |
687 | { |
688 | struct external_config* ext_config = Handle; |
689 | int foundSection = 0; |
690 | if (ParamPtr == NULL || SectionName == NULL || ParamName == NULL) |
691 | return M64ERR_INPUT_INVALID; |
692 | |
693 | void *buffer = malloc(ext_config->length + 1); |
694 | memcpy(buffer, ext_config->file, ext_config->length + 1); |
695 | char *line = buffer; |
696 | char *end = line + ext_config->length; |
697 | while (line < end) |
698 | { |
699 | ini_line l = ini_parse_line(&line); |
700 | switch (l.type) |
701 | { |
702 | case INI_SECTION: |
703 | if (osal_insensitive_strcmp(SectionName, l.name) == 0) |
704 | foundSection = 1; |
705 | else |
706 | foundSection = 0; |
707 | break; |
708 | case INI_PROPERTY: |
709 | if (foundSection) |
710 | { |
711 | if (osal_insensitive_strcmp(ParamName, l.name) == 0) |
712 | { |
713 | strncpy(ParamPtr, l.value, ParamMaxLength); |
714 | free(buffer); |
715 | return M64ERR_SUCCESS; |
716 | } |
717 | } |
718 | break; |
719 | default: |
720 | break; |
721 | } |
722 | } |
723 | free(buffer); |
724 | return M64ERR_INPUT_NOT_FOUND; |
725 | } |
726 | |
727 | EXPORT m64p_error CALL ConfigListSections(void *context, void (*SectionListCallback)(void * context, const char * SectionName)) |
728 | { |
729 | config_section *curr_section; |
730 | |
731 | if (!l_ConfigInit) |
732 | return M64ERR_NOT_INIT; |
733 | if (SectionListCallback == NULL) |
734 | return M64ERR_INPUT_ASSERT; |
735 | |
736 | /* just walk through the section list, making a callback for each section name */ |
737 | curr_section = l_ConfigListActive; |
738 | while (curr_section != NULL) |
739 | { |
740 | (*SectionListCallback)(context, curr_section->name); |
741 | curr_section = curr_section->next; |
742 | } |
743 | |
744 | return M64ERR_SUCCESS; |
745 | } |
746 | |
747 | EXPORT m64p_error CALL ConfigOpenSection(const char *SectionName, m64p_handle *ConfigSectionHandle) |
748 | { |
749 | config_section **curr_section; |
750 | config_section *new_section; |
751 | |
752 | if (!l_ConfigInit) |
753 | return M64ERR_NOT_INIT; |
754 | if (SectionName == NULL || ConfigSectionHandle == NULL) |
755 | return M64ERR_INPUT_ASSERT; |
756 | |
757 | /* walk through the section list, looking for a case-insensitive name match */ |
758 | curr_section = find_alpha_section_link(&l_ConfigListActive, SectionName); |
759 | if (*curr_section != NULL && osal_insensitive_strcmp(SectionName, (*curr_section)->name) == 0) |
760 | { |
761 | *ConfigSectionHandle = *curr_section; |
762 | return M64ERR_SUCCESS; |
763 | } |
764 | |
765 | /* didn't find the section, so create new one */ |
766 | new_section = config_section_create(SectionName); |
767 | if (new_section == NULL) |
768 | return M64ERR_NO_MEMORY; |
769 | |
770 | /* add section to list in alphabetical order */ |
771 | new_section->next = *curr_section; |
772 | *curr_section = new_section; |
773 | |
774 | *ConfigSectionHandle = new_section; |
775 | return M64ERR_SUCCESS; |
776 | } |
777 | |
778 | EXPORT m64p_error CALL ConfigListParameters(m64p_handle ConfigSectionHandle, void *context, void (*ParameterListCallback)(void * context, const char *ParamName, m64p_type ParamType)) |
779 | { |
780 | config_section *section; |
781 | config_var *curr_var; |
782 | |
783 | if (!l_ConfigInit) |
784 | return M64ERR_NOT_INIT; |
785 | if (ConfigSectionHandle == NULL || ParameterListCallback == NULL) |
786 | return M64ERR_INPUT_ASSERT; |
787 | |
788 | section = (config_section *) ConfigSectionHandle; |
789 | if (section->magic != SECTION_MAGIC) |
790 | return M64ERR_INPUT_INVALID; |
791 | |
792 | /* walk through this section's parameter list, making a callback for each parameter */ |
793 | curr_var = section->first_var; |
794 | while (curr_var != NULL) |
795 | { |
796 | (*ParameterListCallback)(context, curr_var->name, curr_var->type); |
797 | curr_var = curr_var->next; |
798 | } |
799 | |
800 | return M64ERR_SUCCESS; |
801 | } |
802 | |
803 | EXPORT int CALL ConfigHasUnsavedChanges(const char *SectionName) |
804 | { |
805 | config_section *input_section, *curr_section; |
806 | config_var *active_var, *saved_var; |
807 | |
808 | /* check input conditions */ |
809 | if (!l_ConfigInit) |
810 | { |
811 | DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): Core config not initialized!" ); |
812 | return 0; |
813 | } |
814 | |
815 | /* if SectionName is NULL or blank, then check all sections */ |
816 | if (SectionName == NULL || strlen(SectionName) < 1) |
817 | { |
818 | int iNumActiveSections = 0, iNumSavedSections = 0; |
819 | /* first, search through all sections in Active list. Recursively call ourself and return 1 if changed */ |
820 | curr_section = l_ConfigListActive; |
821 | while (curr_section != NULL) |
822 | { |
823 | if (ConfigHasUnsavedChanges(curr_section->name)) |
824 | return 1; |
825 | curr_section = curr_section->next; |
826 | iNumActiveSections++; |
827 | } |
828 | /* Next, count the number of Saved sections and see if the count matches */ |
829 | curr_section = l_ConfigListSaved; |
830 | while (curr_section != NULL) |
831 | { |
832 | curr_section = curr_section->next; |
833 | iNumSavedSections++; |
834 | } |
835 | if (iNumActiveSections == iNumSavedSections) |
836 | return 0; /* no changes */ |
837 | else |
838 | return 1; |
839 | } |
840 | |
841 | /* walk through the Active section list, looking for a case-insensitive name match with input string */ |
842 | input_section = find_section(l_ConfigListActive, SectionName); |
843 | if (input_section == NULL) |
844 | { |
845 | DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): section name '%s' not found!" , SectionName); |
846 | return 0; |
847 | } |
848 | |
849 | /* walk through the Saved section list, looking for a case-insensitive name match */ |
850 | curr_section = find_section(l_ConfigListSaved, SectionName); |
851 | if (curr_section == NULL) |
852 | { |
853 | /* if this section isn't present in saved list, then it has been newly created */ |
854 | return 1; |
855 | } |
856 | |
857 | /* compare all of the variables in the two sections. They are expected to be in the same order */ |
858 | active_var = input_section->first_var; |
859 | saved_var = curr_section->first_var; |
860 | while (active_var != NULL && saved_var != NULL) |
861 | { |
862 | if (strcmp(active_var->name, saved_var->name) != 0) |
863 | return 1; |
864 | if (active_var->type != saved_var->type) |
865 | return 1; |
866 | switch(active_var->type) |
867 | { |
868 | case M64TYPE_INT: |
869 | if (active_var->val.integer != saved_var->val.integer) |
870 | return 1; |
871 | break; |
872 | case M64TYPE_FLOAT: |
873 | if (active_var->val.number != saved_var->val.number) |
874 | return 1; |
875 | break; |
876 | case M64TYPE_BOOL: |
877 | if ((active_var->val.integer != 0) != (saved_var->val.integer != 0)) |
878 | return 1; |
879 | break; |
880 | case M64TYPE_STRING: |
881 | if (active_var->val.string == NULL) |
882 | { |
883 | DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): Variable '%s' NULL Active string pointer!" , active_var->name); |
884 | return 1; |
885 | } |
886 | if (saved_var->val.string == NULL) |
887 | { |
888 | DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): Variable '%s' NULL Saved string pointer!" , active_var->name); |
889 | return 1; |
890 | } |
891 | if (strcmp(active_var->val.string, saved_var->val.string) != 0) |
892 | return 1; |
893 | break; |
894 | default: |
895 | DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): Invalid variable '%s' type %i!" , active_var->name, active_var->type); |
896 | return 1; |
897 | } |
898 | if (active_var->comment != NULL && saved_var->comment != NULL && strcmp(active_var->comment, saved_var->comment) != 0) |
899 | return 1; |
900 | active_var = active_var->next; |
901 | saved_var = saved_var->next; |
902 | } |
903 | |
904 | /* any extra new variables on the end, or deleted variables? */ |
905 | if (active_var != NULL || saved_var != NULL) |
906 | return 1; |
907 | |
908 | /* exactly the same */ |
909 | return 0; |
910 | } |
911 | |
912 | /* ------------------------------------------------------- */ |
913 | /* Modifier functions, exported outside of the Core */ |
914 | /* ------------------------------------------------------- */ |
915 | |
916 | EXPORT m64p_error CALL ConfigDeleteSection(const char *SectionName) |
917 | { |
918 | config_section **curr_section_link; |
919 | config_section *next_section; |
920 | |
921 | if (!l_ConfigInit) |
922 | return M64ERR_NOT_INIT; |
923 | if (l_ConfigListActive == NULL) |
924 | return M64ERR_INPUT_NOT_FOUND; |
925 | |
926 | /* find the named section and pull it out of the list */ |
927 | curr_section_link = find_section_link(&l_ConfigListActive, SectionName); |
928 | if (*curr_section_link == NULL) |
929 | return M64ERR_INPUT_NOT_FOUND; |
930 | |
931 | next_section = (*curr_section_link)->next; |
932 | |
933 | /* delete the named section */ |
934 | delete_section(*curr_section_link); |
935 | |
936 | /* fix the pointer to point to the next section after the deleted one */ |
937 | *curr_section_link = next_section; |
938 | |
939 | return M64ERR_SUCCESS; |
940 | } |
941 | |
942 | EXPORT m64p_error CALL ConfigSaveFile(void) |
943 | { |
944 | if (!l_ConfigInit) |
945 | return M64ERR_NOT_INIT; |
946 | |
947 | /* copy the active config list to the saved config list */ |
948 | copy_configlist_active_to_saved(); |
949 | |
950 | /* write the saved config list out to a file */ |
951 | return (write_configlist_file()); |
952 | } |
953 | |
954 | EXPORT m64p_error CALL ConfigSaveSection(const char *SectionName) |
955 | { |
956 | config_section *curr_section, *new_section; |
957 | config_section **insertion_point; |
958 | |
959 | if (!l_ConfigInit) |
960 | return M64ERR_NOT_INIT; |
961 | if (SectionName == NULL || strlen(SectionName) < 1) |
962 | return M64ERR_INPUT_ASSERT; |
963 | |
964 | /* walk through the Active section list, looking for a case-insensitive name match */ |
965 | curr_section = find_section(l_ConfigListActive, SectionName); |
966 | if (curr_section == NULL) |
967 | return M64ERR_INPUT_NOT_FOUND; |
968 | |
969 | /* duplicate this section */ |
970 | new_section = section_deepcopy(curr_section); |
971 | if (new_section == NULL) |
972 | return M64ERR_NO_MEMORY; |
973 | |
974 | /* update config section that's in the Saved list with the new one */ |
975 | insertion_point = find_alpha_section_link(&l_ConfigListSaved, SectionName); |
976 | if (*insertion_point != NULL && osal_insensitive_strcmp((*insertion_point)->name, SectionName) == 0) |
977 | { |
978 | /* the section exists in the saved list and will be replaced */ |
979 | new_section->next = (*insertion_point)->next; |
980 | delete_section(*insertion_point); |
981 | *insertion_point = new_section; |
982 | } |
983 | else |
984 | { |
985 | /* the section didn't exist in the saved list and has to be inserted */ |
986 | new_section->next = *insertion_point; |
987 | *insertion_point = new_section; |
988 | } |
989 | |
990 | /* write the saved config list out to a file */ |
991 | return (write_configlist_file()); |
992 | } |
993 | |
994 | EXPORT m64p_error CALL ConfigRevertChanges(const char *SectionName) |
995 | { |
996 | config_section **active_section_link, *active_section, *saved_section, *new_section; |
997 | |
998 | /* check input conditions */ |
999 | if (!l_ConfigInit) |
1000 | return M64ERR_NOT_INIT; |
1001 | if (SectionName == NULL) |
1002 | return M64ERR_INPUT_ASSERT; |
1003 | |
1004 | /* walk through the Active section list, looking for a case-insensitive name match with input string */ |
1005 | active_section_link = find_section_link(&l_ConfigListActive, SectionName); |
1006 | active_section = *active_section_link; |
1007 | if (active_section == NULL) |
1008 | return M64ERR_INPUT_NOT_FOUND; |
1009 | |
1010 | /* walk through the Saved section list, looking for a case-insensitive name match */ |
1011 | saved_section = find_section(l_ConfigListSaved, SectionName); |
1012 | if (saved_section == NULL) |
1013 | { |
1014 | /* if this section isn't present in saved list, then it has been newly created */ |
1015 | return M64ERR_INPUT_NOT_FOUND; |
1016 | } |
1017 | |
1018 | /* copy the section as it is on the disk */ |
1019 | new_section = section_deepcopy(saved_section); |
1020 | if (new_section == NULL) |
1021 | return M64ERR_NO_MEMORY; |
1022 | |
1023 | /* replace active_section with saved_section in the linked list */ |
1024 | *active_section_link = new_section; |
1025 | new_section->next = active_section->next; |
1026 | |
1027 | /* release memory associated with active_section */ |
1028 | delete_section(active_section); |
1029 | |
1030 | return M64ERR_SUCCESS; |
1031 | } |
1032 | |
1033 | |
1034 | /* ------------------------------------------------------- */ |
1035 | /* Generic Get/Set functions, exported outside of the Core */ |
1036 | /* ------------------------------------------------------- */ |
1037 | |
1038 | EXPORT m64p_error CALL ConfigSetParameter(m64p_handle ConfigSectionHandle, const char *ParamName, m64p_type ParamType, const void *ParamValue) |
1039 | { |
1040 | config_section *section; |
1041 | config_var *var; |
1042 | |
1043 | /* check input conditions */ |
1044 | if (!l_ConfigInit) |
1045 | return M64ERR_NOT_INIT; |
1046 | if (ConfigSectionHandle == NULL || ParamName == NULL || ParamValue == NULL || (int) ParamType < 1 || (int) ParamType > 4) |
1047 | return M64ERR_INPUT_ASSERT; |
1048 | |
1049 | section = (config_section *) ConfigSectionHandle; |
1050 | if (section->magic != SECTION_MAGIC) |
1051 | return M64ERR_INPUT_INVALID; |
1052 | |
1053 | /* if this parameter doesn't already exist, then create it and add it to the section */ |
1054 | var = find_section_var(section, ParamName); |
1055 | if (var == NULL) |
1056 | { |
1057 | var = config_var_create(ParamName, NULL); |
1058 | if (var == NULL) |
1059 | return M64ERR_NO_MEMORY; |
1060 | append_var_to_section(section, var); |
1061 | } |
1062 | |
1063 | /* cleanup old values */ |
1064 | switch (var->type) |
1065 | { |
1066 | case M64TYPE_STRING: |
1067 | free(var->val.string); |
1068 | break; |
1069 | default: |
1070 | break; |
1071 | } |
1072 | |
1073 | /* set this parameter's value */ |
1074 | var->type = ParamType; |
1075 | switch(ParamType) |
1076 | { |
1077 | case M64TYPE_INT: |
1078 | var->val.integer = *((int *) ParamValue); |
1079 | break; |
1080 | case M64TYPE_FLOAT: |
1081 | var->val.number = *((float *) ParamValue); |
1082 | break; |
1083 | case M64TYPE_BOOL: |
1084 | var->val.integer = (*((int *) ParamValue) != 0); |
1085 | break; |
1086 | case M64TYPE_STRING: |
1087 | var->val.string = strdup((char *)ParamValue); |
1088 | if (var->val.string == NULL) |
1089 | return M64ERR_NO_MEMORY; |
1090 | break; |
1091 | default: |
1092 | /* this is logically impossible because of the ParamType check at the top of this function */ |
1093 | break; |
1094 | } |
1095 | |
1096 | return M64ERR_SUCCESS; |
1097 | } |
1098 | |
1099 | EXPORT m64p_error CALL ConfigSetParameterHelp(m64p_handle ConfigSectionHandle, const char *ParamName, const char *ParamHelp) |
1100 | { |
1101 | config_section *section; |
1102 | config_var *var; |
1103 | |
1104 | /* check input conditions */ |
1105 | if (!l_ConfigInit) |
1106 | return M64ERR_NOT_INIT; |
1107 | if (ConfigSectionHandle == NULL || ParamName == NULL || ParamHelp == NULL) |
1108 | return M64ERR_INPUT_ASSERT; |
1109 | |
1110 | section = (config_section *) ConfigSectionHandle; |
1111 | if (section->magic != SECTION_MAGIC) |
1112 | return M64ERR_INPUT_INVALID; |
1113 | |
1114 | /* if this parameter doesn't already exist, return an error */ |
1115 | var = find_section_var(section, ParamName); |
1116 | if (var == NULL) |
1117 | return M64ERR_INPUT_NOT_FOUND; |
1118 | |
1119 | if (var->comment != NULL) |
1120 | free(var->comment); |
1121 | |
1122 | var->comment = strdup(ParamHelp); |
1123 | |
1124 | return M64ERR_SUCCESS; |
1125 | } |
1126 | |
1127 | EXPORT m64p_error CALL ConfigGetParameter(m64p_handle ConfigSectionHandle, const char *ParamName, m64p_type ParamType, void *ParamValue, int MaxSize) |
1128 | { |
1129 | config_section *section; |
1130 | config_var *var; |
1131 | |
1132 | /* check input conditions */ |
1133 | if (!l_ConfigInit) |
1134 | return M64ERR_NOT_INIT; |
1135 | if (ConfigSectionHandle == NULL || ParamName == NULL || ParamValue == NULL || (int) ParamType < 1 || (int) ParamType > 4) |
1136 | return M64ERR_INPUT_ASSERT; |
1137 | |
1138 | section = (config_section *) ConfigSectionHandle; |
1139 | if (section->magic != SECTION_MAGIC) |
1140 | return M64ERR_INPUT_INVALID; |
1141 | |
1142 | /* if this parameter doesn't already exist, return an error */ |
1143 | var = find_section_var(section, ParamName); |
1144 | if (var == NULL) |
1145 | return M64ERR_INPUT_NOT_FOUND; |
1146 | |
1147 | /* call the specific Get function to translate the parameter to the desired type */ |
1148 | switch(ParamType) |
1149 | { |
1150 | case M64TYPE_INT: |
1151 | if (MaxSize < (int)sizeof(int)) return M64ERR_INPUT_INVALID; |
1152 | *((int *) ParamValue) = ConfigGetParamInt(ConfigSectionHandle, ParamName); |
1153 | break; |
1154 | case M64TYPE_FLOAT: |
1155 | if (MaxSize < (int)sizeof(float)) return M64ERR_INPUT_INVALID; |
1156 | *((float *) ParamValue) = ConfigGetParamFloat(ConfigSectionHandle, ParamName); |
1157 | break; |
1158 | case M64TYPE_BOOL: |
1159 | if (MaxSize < (int)sizeof(int)) return M64ERR_INPUT_INVALID; |
1160 | *((int *) ParamValue) = ConfigGetParamBool(ConfigSectionHandle, ParamName); |
1161 | break; |
1162 | case M64TYPE_STRING: |
1163 | { |
1164 | const char *string; |
1165 | if (MaxSize < 1) return M64ERR_INPUT_INVALID; |
1166 | if (var->type != M64TYPE_STRING && var->type != M64TYPE_BOOL) return M64ERR_WRONG_TYPE; |
1167 | string = ConfigGetParamString(ConfigSectionHandle, ParamName); |
1168 | strncpy((char *) ParamValue, string, MaxSize); |
1169 | *((char *) ParamValue + MaxSize - 1) = 0; |
1170 | break; |
1171 | } |
1172 | default: |
1173 | /* this is logically impossible because of the ParamType check at the top of this function */ |
1174 | break; |
1175 | } |
1176 | |
1177 | return M64ERR_SUCCESS; |
1178 | } |
1179 | |
1180 | EXPORT m64p_error CALL ConfigGetParameterType(m64p_handle ConfigSectionHandle, const char *ParamName, m64p_type *ParamType) |
1181 | { |
1182 | config_section *section; |
1183 | config_var *var; |
1184 | |
1185 | /* check input conditions */ |
1186 | if (!l_ConfigInit) |
1187 | return M64ERR_NOT_INIT; |
1188 | if (ConfigSectionHandle == NULL || ParamName == NULL || ParamType == NULL) |
1189 | return M64ERR_INPUT_ASSERT; |
1190 | |
1191 | section = (config_section *) ConfigSectionHandle; |
1192 | if (section->magic != SECTION_MAGIC) |
1193 | return M64ERR_INPUT_INVALID; |
1194 | |
1195 | /* if this parameter doesn't already exist, return an error */ |
1196 | var = find_section_var(section, ParamName); |
1197 | if (var == NULL) |
1198 | return M64ERR_INPUT_NOT_FOUND; |
1199 | |
1200 | *ParamType = var->type; |
1201 | return M64ERR_SUCCESS; |
1202 | } |
1203 | |
1204 | |
1205 | EXPORT const char * CALL ConfigGetParameterHelp(m64p_handle ConfigSectionHandle, const char *ParamName) |
1206 | { |
1207 | config_section *section; |
1208 | config_var *var; |
1209 | |
1210 | /* check input conditions */ |
1211 | if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL) |
1212 | return NULL; |
1213 | |
1214 | section = (config_section *) ConfigSectionHandle; |
1215 | if (section->magic != SECTION_MAGIC) |
1216 | return NULL; |
1217 | |
1218 | /* if this parameter doesn't exist, return an error */ |
1219 | var = find_section_var(section, ParamName); |
1220 | if (var == NULL) |
1221 | return NULL; |
1222 | |
1223 | return var->comment; |
1224 | } |
1225 | |
1226 | /* ------------------------------------------------------- */ |
1227 | /* Special Get/Set functions, exported outside of the Core */ |
1228 | /* ------------------------------------------------------- */ |
1229 | |
1230 | EXPORT m64p_error CALL ConfigSetDefaultInt(m64p_handle ConfigSectionHandle, const char *ParamName, int ParamValue, const char *ParamHelp) |
1231 | { |
1232 | config_section *section; |
1233 | config_var *var; |
1234 | |
1235 | /* check input conditions */ |
1236 | if (!l_ConfigInit) |
1237 | return M64ERR_NOT_INIT; |
1238 | if (ConfigSectionHandle == NULL || ParamName == NULL) |
1239 | return M64ERR_INPUT_ASSERT; |
1240 | |
1241 | section = (config_section *) ConfigSectionHandle; |
1242 | if (section->magic != SECTION_MAGIC) |
1243 | return M64ERR_INPUT_INVALID; |
1244 | |
1245 | /* if this parameter already exists, then just return successfully */ |
1246 | var = find_section_var(section, ParamName); |
1247 | if (var != NULL) |
1248 | return M64ERR_SUCCESS; |
1249 | |
1250 | /* otherwise create a new config_var object and add it to this section */ |
1251 | var = config_var_create(ParamName, ParamHelp); |
1252 | if (var == NULL) |
1253 | return M64ERR_NO_MEMORY; |
1254 | var->type = M64TYPE_INT; |
1255 | var->val.integer = ParamValue; |
1256 | append_var_to_section(section, var); |
1257 | |
1258 | return M64ERR_SUCCESS; |
1259 | } |
1260 | |
1261 | EXPORT m64p_error CALL ConfigSetDefaultFloat(m64p_handle ConfigSectionHandle, const char *ParamName, float ParamValue, const char *ParamHelp) |
1262 | { |
1263 | config_section *section; |
1264 | config_var *var; |
1265 | |
1266 | /* check input conditions */ |
1267 | if (!l_ConfigInit) |
1268 | return M64ERR_NOT_INIT; |
1269 | if (ConfigSectionHandle == NULL || ParamName == NULL) |
1270 | return M64ERR_INPUT_ASSERT; |
1271 | |
1272 | section = (config_section *) ConfigSectionHandle; |
1273 | if (section->magic != SECTION_MAGIC) |
1274 | return M64ERR_INPUT_INVALID; |
1275 | |
1276 | /* if this parameter already exists, then just return successfully */ |
1277 | var = find_section_var(section, ParamName); |
1278 | if (var != NULL) |
1279 | return M64ERR_SUCCESS; |
1280 | |
1281 | /* otherwise create a new config_var object and add it to this section */ |
1282 | var = config_var_create(ParamName, ParamHelp); |
1283 | if (var == NULL) |
1284 | return M64ERR_NO_MEMORY; |
1285 | var->type = M64TYPE_FLOAT; |
1286 | var->val.number = ParamValue; |
1287 | append_var_to_section(section, var); |
1288 | |
1289 | return M64ERR_SUCCESS; |
1290 | } |
1291 | |
1292 | EXPORT m64p_error CALL ConfigSetDefaultBool(m64p_handle ConfigSectionHandle, const char *ParamName, int ParamValue, const char *ParamHelp) |
1293 | { |
1294 | config_section *section; |
1295 | config_var *var; |
1296 | |
1297 | /* check input conditions */ |
1298 | if (!l_ConfigInit) |
1299 | return M64ERR_NOT_INIT; |
1300 | if (ConfigSectionHandle == NULL || ParamName == NULL) |
1301 | return M64ERR_INPUT_ASSERT; |
1302 | |
1303 | section = (config_section *) ConfigSectionHandle; |
1304 | if (section->magic != SECTION_MAGIC) |
1305 | return M64ERR_INPUT_INVALID; |
1306 | |
1307 | /* if this parameter already exists, then just return successfully */ |
1308 | var = find_section_var(section, ParamName); |
1309 | if (var != NULL) |
1310 | return M64ERR_SUCCESS; |
1311 | |
1312 | /* otherwise create a new config_var object and add it to this section */ |
1313 | var = config_var_create(ParamName, ParamHelp); |
1314 | if (var == NULL) |
1315 | return M64ERR_NO_MEMORY; |
1316 | var->type = M64TYPE_BOOL; |
1317 | var->val.integer = ParamValue ? 1 : 0; |
1318 | append_var_to_section(section, var); |
1319 | |
1320 | return M64ERR_SUCCESS; |
1321 | } |
1322 | |
1323 | EXPORT m64p_error CALL ConfigSetDefaultString(m64p_handle ConfigSectionHandle, const char *ParamName, const char * ParamValue, const char *ParamHelp) |
1324 | { |
1325 | config_section *section; |
1326 | config_var *var; |
1327 | |
1328 | /* check input conditions */ |
1329 | if (!l_ConfigInit) |
1330 | return M64ERR_NOT_INIT; |
1331 | if (ConfigSectionHandle == NULL || ParamName == NULL || ParamValue == NULL) |
1332 | return M64ERR_INPUT_ASSERT; |
1333 | |
1334 | section = (config_section *) ConfigSectionHandle; |
1335 | if (section->magic != SECTION_MAGIC) |
1336 | return M64ERR_INPUT_INVALID; |
1337 | |
1338 | /* if this parameter already exists, then just return successfully */ |
1339 | var = find_section_var(section, ParamName); |
1340 | if (var != NULL) |
1341 | return M64ERR_SUCCESS; |
1342 | |
1343 | /* otherwise create a new config_var object and add it to this section */ |
1344 | var = config_var_create(ParamName, ParamHelp); |
1345 | if (var == NULL) |
1346 | return M64ERR_NO_MEMORY; |
1347 | var->type = M64TYPE_STRING; |
1348 | var->val.string = strdup(ParamValue); |
1349 | if (var->val.string == NULL) |
1350 | { |
1351 | delete_var(var); |
1352 | return M64ERR_NO_MEMORY; |
1353 | } |
1354 | append_var_to_section(section, var); |
1355 | |
1356 | return M64ERR_SUCCESS; |
1357 | } |
1358 | |
1359 | EXPORT int CALL ConfigGetParamInt(m64p_handle ConfigSectionHandle, const char *ParamName) |
1360 | { |
1361 | config_section *section; |
1362 | config_var *var; |
1363 | |
1364 | /* check input conditions */ |
1365 | if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL) |
1366 | { |
1367 | DebugMessage(M64MSG_ERROR, "ConfigGetParamInt(): Input assertion!" ); |
1368 | return 0; |
1369 | } |
1370 | |
1371 | section = (config_section *) ConfigSectionHandle; |
1372 | if (section->magic != SECTION_MAGIC) |
1373 | { |
1374 | DebugMessage(M64MSG_ERROR, "ConfigGetParamInt(): ConfigSectionHandle invalid!" ); |
1375 | return 0; |
1376 | } |
1377 | |
1378 | /* if this parameter doesn't already exist, return an error */ |
1379 | var = find_section_var(section, ParamName); |
1380 | if (var == NULL) |
1381 | { |
1382 | DebugMessage(M64MSG_ERROR, "ConfigGetParamInt(): Parameter '%s' not found!" , ParamName); |
1383 | return 0; |
1384 | } |
1385 | |
1386 | /* translate the actual variable type to an int */ |
1387 | switch(var->type) |
1388 | { |
1389 | case M64TYPE_INT: |
1390 | return var->val.integer; |
1391 | case M64TYPE_FLOAT: |
1392 | return (int) var->val.number; |
1393 | case M64TYPE_BOOL: |
1394 | return (var->val.integer != 0); |
1395 | case M64TYPE_STRING: |
1396 | return atoi(var->val.string); |
1397 | default: |
1398 | DebugMessage(M64MSG_ERROR, "ConfigGetParamInt(): invalid internal parameter type for '%s'" , ParamName); |
1399 | return 0; |
1400 | } |
1401 | } |
1402 | |
1403 | EXPORT float CALL ConfigGetParamFloat(m64p_handle ConfigSectionHandle, const char *ParamName) |
1404 | { |
1405 | config_section *section; |
1406 | config_var *var; |
1407 | |
1408 | /* check input conditions */ |
1409 | if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL) |
1410 | { |
1411 | DebugMessage(M64MSG_ERROR, "ConfigGetParamFloat(): Input assertion!" ); |
1412 | return 0.0; |
1413 | } |
1414 | |
1415 | section = (config_section *) ConfigSectionHandle; |
1416 | if (section->magic != SECTION_MAGIC) |
1417 | { |
1418 | DebugMessage(M64MSG_ERROR, "ConfigGetParamFloat(): ConfigSectionHandle invalid!" ); |
1419 | return 0.0; |
1420 | } |
1421 | |
1422 | /* if this parameter doesn't already exist, return an error */ |
1423 | var = find_section_var(section, ParamName); |
1424 | if (var == NULL) |
1425 | { |
1426 | DebugMessage(M64MSG_ERROR, "ConfigGetParamFloat(): Parameter '%s' not found!" , ParamName); |
1427 | return 0.0; |
1428 | } |
1429 | |
1430 | /* translate the actual variable type to a float */ |
1431 | switch(var->type) |
1432 | { |
1433 | case M64TYPE_INT: |
1434 | return (float) var->val.integer; |
1435 | case M64TYPE_FLOAT: |
1436 | return var->val.number; |
1437 | case M64TYPE_BOOL: |
1438 | return (var->val.integer != 0) ? 1.0f : 0.0f; |
1439 | case M64TYPE_STRING: |
1440 | return (float) atof(var->val.string); |
1441 | default: |
1442 | DebugMessage(M64MSG_ERROR, "ConfigGetParamFloat(): invalid internal parameter type for '%s'" , ParamName); |
1443 | return 0.0; |
1444 | } |
1445 | } |
1446 | |
1447 | EXPORT int CALL ConfigGetParamBool(m64p_handle ConfigSectionHandle, const char *ParamName) |
1448 | { |
1449 | config_section *section; |
1450 | config_var *var; |
1451 | |
1452 | /* check input conditions */ |
1453 | if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL) |
1454 | { |
1455 | DebugMessage(M64MSG_ERROR, "ConfigGetParamBool(): Input assertion!" ); |
1456 | return 0; |
1457 | } |
1458 | |
1459 | section = (config_section *) ConfigSectionHandle; |
1460 | if (section->magic != SECTION_MAGIC) |
1461 | { |
1462 | DebugMessage(M64MSG_ERROR, "ConfigGetParamBool(): ConfigSectionHandle invalid!" ); |
1463 | return 0; |
1464 | } |
1465 | |
1466 | /* if this parameter doesn't already exist, return an error */ |
1467 | var = find_section_var(section, ParamName); |
1468 | if (var == NULL) |
1469 | { |
1470 | DebugMessage(M64MSG_ERROR, "ConfigGetParamBool(): Parameter '%s' not found!" , ParamName); |
1471 | return 0; |
1472 | } |
1473 | |
1474 | /* translate the actual variable type to an int (0 or 1) */ |
1475 | switch(var->type) |
1476 | { |
1477 | case M64TYPE_INT: |
1478 | return (var->val.integer != 0); |
1479 | case M64TYPE_FLOAT: |
1480 | return (var->val.number != 0.0); |
1481 | case M64TYPE_BOOL: |
1482 | return var->val.integer; |
1483 | case M64TYPE_STRING: |
1484 | return (osal_insensitive_strcmp(var->val.string, "true" ) == 0); |
1485 | default: |
1486 | DebugMessage(M64MSG_ERROR, "ConfigGetParamBool(): invalid internal parameter type for '%s'" , ParamName); |
1487 | return 0; |
1488 | } |
1489 | } |
1490 | |
1491 | EXPORT const char * CALL ConfigGetParamString(m64p_handle ConfigSectionHandle, const char *ParamName) |
1492 | { |
1493 | static char outstr[64]; /* warning: not thread safe */ |
1494 | config_section *section; |
1495 | config_var *var; |
1496 | |
1497 | /* check input conditions */ |
1498 | if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL) |
1499 | { |
1500 | DebugMessage(M64MSG_ERROR, "ConfigGetParamString(): Input assertion!" ); |
1501 | return "" ; |
1502 | } |
1503 | |
1504 | section = (config_section *) ConfigSectionHandle; |
1505 | if (section->magic != SECTION_MAGIC) |
1506 | { |
1507 | DebugMessage(M64MSG_ERROR, "ConfigGetParamString(): ConfigSectionHandle invalid!" ); |
1508 | return "" ; |
1509 | } |
1510 | |
1511 | /* if this parameter doesn't already exist, return an error */ |
1512 | var = find_section_var(section, ParamName); |
1513 | if (var == NULL) |
1514 | { |
1515 | DebugMessage(M64MSG_ERROR, "ConfigGetParamString(): Parameter '%s' not found!" , ParamName); |
1516 | return "" ; |
1517 | } |
1518 | |
1519 | /* translate the actual variable type to a string */ |
1520 | switch(var->type) |
1521 | { |
1522 | case M64TYPE_INT: |
1523 | snprintf(outstr, 63, "%i" , var->val.integer); |
1524 | outstr[63] = 0; |
1525 | return outstr; |
1526 | case M64TYPE_FLOAT: |
1527 | snprintf(outstr, 63, "%f" , var->val.number); |
1528 | outstr[63] = 0; |
1529 | return outstr; |
1530 | case M64TYPE_BOOL: |
1531 | return (var->val.integer ? "True" : "False" ); |
1532 | case M64TYPE_STRING: |
1533 | return var->val.string; |
1534 | default: |
1535 | DebugMessage(M64MSG_ERROR, "ConfigGetParamString(): invalid internal parameter type for '%s'" , ParamName); |
1536 | return "" ; |
1537 | } |
1538 | } |
1539 | |
1540 | /* ------------------------------------------------------ */ |
1541 | /* OS Abstraction functions, exported outside of the Core */ |
1542 | /* ------------------------------------------------------ */ |
1543 | |
1544 | EXPORT const char * CALL ConfigGetSharedDataFilepath(const char *filename) |
1545 | { |
1546 | const char *configsharepath = NULL; |
1547 | m64p_handle CoreHandle = NULL; |
1548 | |
1549 | /* check input parameter */ |
1550 | if (filename == NULL) return NULL; |
1551 | |
1552 | /* try to get the SharedDataPath string variable in the Core configuration section */ |
1553 | if (ConfigOpenSection("Core" , &CoreHandle) == M64ERR_SUCCESS) |
1554 | { |
1555 | configsharepath = ConfigGetParamString(CoreHandle, "SharedDataPath" ); |
1556 | } |
1557 | |
1558 | return osal_get_shared_filepath(filename, l_DataDirOverride, configsharepath); |
1559 | } |
1560 | |
1561 | EXPORT const char * CALL ConfigGetUserConfigPath(void) |
1562 | { |
1563 | if (l_ConfigDirOverride != NULL) |
1564 | { |
1565 | osal_mkdirp(l_ConfigDirOverride, 0700); |
1566 | return l_ConfigDirOverride; |
1567 | } |
1568 | else |
1569 | return osal_get_user_configpath(); |
1570 | } |
1571 | |
1572 | EXPORT const char * CALL ConfigGetUserDataPath(void) |
1573 | { |
1574 | return osal_get_user_datapath(); |
1575 | } |
1576 | |
1577 | EXPORT const char * CALL ConfigGetUserCachePath(void) |
1578 | { |
1579 | return osal_get_user_cachepath(); |
1580 | } |
1581 | |
1582 | |