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
45struct external_config {
46 char *file;
47 size_t length;
48};
49
50typedef 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 *comment;
59 struct _config_var *next;
60 } config_var;
61
62typedef 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
69typedef config_section *config_list;
70
71/* local variables */
72static int l_ConfigInit = 0;
73static char *l_DataDirOverride = NULL;
74static char *l_ConfigDirOverride = NULL;
75static config_list l_ConfigListActive = NULL;
76static config_list l_ConfigListSaved = NULL;
77
78/* --------------- */
79/* local functions */
80/* --------------- */
81
82static 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 */
101static 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 */
119static 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
131static config_section *find_section(config_list list, const char *ParamName)
132{
133 return *find_section_link(&list, ParamName);
134}
135
136static 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
176static 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
190static 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
210static 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
219static 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
238static 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
253static 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
276static 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
344static 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
366static 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
429m64p_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, *lastcomment;
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 *) &current_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
589m64p_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
619EXPORT 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
674EXPORT 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
686EXPORT 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
727EXPORT 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
747EXPORT 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
778EXPORT 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
803EXPORT 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
916EXPORT 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
942EXPORT 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
954EXPORT 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
994EXPORT 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
1038EXPORT 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
1099EXPORT 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
1127EXPORT 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
1180EXPORT 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
1205EXPORT 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
1230EXPORT 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
1261EXPORT 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
1292EXPORT 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
1323EXPORT 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
1359EXPORT 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
1403EXPORT 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
1447EXPORT 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
1491EXPORT 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
1544EXPORT 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
1561EXPORT 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
1572EXPORT const char * CALL ConfigGetUserDataPath(void)
1573{
1574 return osal_get_user_datapath();
1575}
1576
1577EXPORT const char * CALL ConfigGetUserCachePath(void)
1578{
1579 return osal_get_user_cachepath();
1580}
1581
1582