1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus-core - api/config.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2009 Richard Goedeken *
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. *
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. *
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 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22 /* This file contains the Core config functions which will be exported
23 * outside of the core library.
30 #define M64P_CORE_PROTOTYPES 1
31 #include "m64p_types.h"
32 #include "m64p_config.h"
34 #include "callbacks.h"
36 #include "main/util.h"
38 #include "osal/files.h"
39 #include "osal/preproc.h"
42 #define MUPEN64PLUS_CFG_NAME "mupen64plus.cfg"
44 #define SECTION_MAGIC 0xDBDC0580
46 typedef struct _config_var {
55 struct _config_var *next;
58 typedef struct _config_section {
61 struct _config_var *first_var;
62 struct _config_section *next;
65 typedef config_section *config_list;
68 static int l_ConfigInit = 0;
69 static int l_SaveConfigOnExit = 0;
70 static char *l_DataDirOverride = NULL;
71 static char *l_ConfigDirOverride = NULL;
72 static config_list l_ConfigListActive = NULL;
73 static config_list l_ConfigListSaved = NULL;
79 static int is_numeric(const char *string)
83 int rval = sscanf(string, "%f%8s", &fTemp, chTemp);
85 /* I want to find exactly one matched input item: a number with no garbage on the end */
86 /* I use sscanf() instead of a custom loop because this routine must handle locales in which the decimal separator is not '.' */
90 /* This function returns a pointer to the pointer of the requested section
91 * (i.e. a pointer the next field of the previous element, or to the first node).
93 * If there's no section named 'ParamName', returns the pointer to the next
94 * field of the last element in the list (such that derefencing it is NULL).
96 * Useful for operations that need to modify the links, e.g. deleting a section.
98 static config_section **find_section_link(config_list *list, const char *ParamName)
100 config_section **curr_sec_link;
101 for (curr_sec_link = list; *curr_sec_link != NULL; curr_sec_link = &(*curr_sec_link)->next)
103 if (osal_insensitive_strcmp(ParamName, (*curr_sec_link)->name) == 0)
107 return curr_sec_link;
110 /* This function is similar to the previous function, but instead it returns a
111 * pointer to the pointer to the next section whose name is alphabetically
112 * greater than or equal to 'ParamName'.
114 * Useful for inserting a section in its alphabetical position.
116 static config_section **find_alpha_section_link(config_list *list, const char *ParamName)
118 config_section **curr_sec_link;
119 for (curr_sec_link = list; *curr_sec_link != NULL; curr_sec_link = &(*curr_sec_link)->next)
121 if (osal_insensitive_strcmp((*curr_sec_link)->name, ParamName) >= 0)
125 return curr_sec_link;
128 static config_section *find_section(config_list list, const char *ParamName)
130 return *find_section_link(&list, ParamName);
133 static config_var *config_var_create(const char *ParamName, const char *ParamHelp)
135 config_var *var = (config_var *) malloc(sizeof(config_var));
137 if (var == NULL || ParamName == NULL)
140 memset(var, 0, sizeof(config_var));
142 var->name = strdup(ParamName);
143 if (var->name == NULL)
149 var->type = M64TYPE_INT;
150 var->val.integer = 0;
152 if (ParamHelp != NULL)
154 var->comment = strdup(ParamHelp);
155 if (var->comment == NULL)
169 static config_var *find_section_var(config_section *section, const char *ParamName)
171 /* walk through the linked list of variables in the section */
172 config_var *curr_var;
173 for (curr_var = section->first_var; curr_var != NULL; curr_var = curr_var->next)
175 if (osal_insensitive_strcmp(ParamName, curr_var->name) == 0)
179 /* couldn't find this configuration parameter */
183 static void append_var_to_section(config_section *section, config_var *var)
185 config_var *last_var;
187 if (section == NULL || var == NULL || section->magic != SECTION_MAGIC)
190 if (section->first_var == NULL)
192 section->first_var = var;
196 last_var = section->first_var;
197 while (last_var->next != NULL)
198 last_var = last_var->next;
200 last_var->next = var;
203 static void delete_var(config_var *var)
205 if (var->type == M64TYPE_STRING)
206 free(var->val.string);
212 static void delete_section(config_section *pSection)
214 config_var *curr_var;
216 if (pSection == NULL)
219 curr_var = pSection->first_var;
220 while (curr_var != NULL)
222 config_var *next_var = curr_var->next;
223 delete_var(curr_var);
227 free(pSection->name);
231 static void delete_list(config_list *pConfigList)
233 config_section *curr_section = *pConfigList;
234 while (curr_section != NULL)
236 config_section *next_section = curr_section->next;
237 /* delete the section itself */
238 delete_section(curr_section);
240 curr_section = next_section;
246 static config_section *config_section_create(const char *ParamName)
250 if (ParamName == NULL)
253 sec = (config_section *) malloc(sizeof(config_section));
257 sec->magic = SECTION_MAGIC;
258 sec->name = strdup(ParamName);
259 if (sec->name == NULL)
264 sec->first_var = NULL;
269 static config_section * section_deepcopy(config_section *orig_section)
271 config_section *new_section;
272 config_var *orig_var, *last_new_var;
274 /* Input validation */
275 if (orig_section == NULL)
278 /* create and copy section struct */
279 new_section = config_section_create(orig_section->name);
280 if (new_section == NULL)
283 /* create and copy all section variables */
284 orig_var = orig_section->first_var;
286 while (orig_var != NULL)
288 config_var *new_var = config_var_create(orig_var->name, orig_var->comment);
291 delete_section(new_section);
295 new_var->type = orig_var->type;
296 switch (orig_var->type)
300 new_var->val.integer = orig_var->val.integer;
304 new_var->val.number = orig_var->val.number;
308 if (orig_var->val.string != NULL)
310 new_var->val.string = strdup(orig_var->val.string);
311 if (new_var->val.string == NULL)
313 delete_section(new_section);
318 new_var->val.string = NULL;
323 /* add the new variable to the new section */
324 if (last_new_var == NULL)
325 new_section->first_var = new_var;
327 last_new_var->next = new_var;
328 last_new_var = new_var;
329 /* advance variable pointer in original section variable list */
330 orig_var = orig_var->next;
336 static void copy_configlist_active_to_saved(void)
338 config_section *curr_section = l_ConfigListActive;
339 config_section *last_section = NULL;
341 /* delete any pre-existing Saved config list */
342 delete_list(&l_ConfigListSaved);
344 /* duplicate all of the config sections in the Active list, adding them to the Saved list */
345 while (curr_section != NULL)
347 config_section *new_section = section_deepcopy(curr_section);
348 if (new_section == NULL) break;
349 if (last_section == NULL)
350 l_ConfigListSaved = new_section;
352 last_section->next = new_section;
353 last_section = new_section;
354 curr_section = curr_section->next;
358 static m64p_error write_configlist_file(void)
360 config_section *curr_section;
361 const char *configpath;
365 /* get the full pathname to the config file and try to open it */
366 configpath = ConfigGetUserConfigPath();
367 if (configpath == NULL)
370 filepath = combinepath(configpath, MUPEN64PLUS_CFG_NAME);
371 if (filepath == NULL)
372 return M64ERR_NO_MEMORY;
374 fPtr = fopen(filepath, "wb");
377 DebugMessage(M64MSG_ERROR, "Couldn't open configuration file '%s' for writing.", filepath);
383 /* write out header */
384 fprintf(fPtr, "# Mupen64Plus Configuration File\n");
385 fprintf(fPtr, "# This file is automatically read and written by the Mupen64Plus Core library\n");
387 /* write out all of the config parameters from the Saved list */
388 curr_section = l_ConfigListSaved;
389 while (curr_section != NULL)
391 config_var *curr_var = curr_section->first_var;
392 fprintf(fPtr, "\n[%s]\n\n", curr_section->name);
393 while (curr_var != NULL)
395 if (curr_var->comment != NULL && strlen(curr_var->comment) > 0)
396 fprintf(fPtr, "# %s\n", curr_var->comment);
397 if (curr_var->type == M64TYPE_INT)
398 fprintf(fPtr, "%s = %i\n", curr_var->name, curr_var->val.integer);
399 else if (curr_var->type == M64TYPE_FLOAT)
400 fprintf(fPtr, "%s = %f\n", curr_var->name, curr_var->val.number);
401 else if (curr_var->type == M64TYPE_BOOL && curr_var->val.integer)
402 fprintf(fPtr, "%s = True\n", curr_var->name);
403 else if (curr_var->type == M64TYPE_BOOL && !curr_var->val.integer)
404 fprintf(fPtr, "%s = False\n", curr_var->name);
405 else if (curr_var->type == M64TYPE_STRING && curr_var->val.string != NULL)
406 fprintf(fPtr, "%s = \"%s\"\n", curr_var->name, curr_var->val.string);
407 curr_var = curr_var->next;
410 curr_section = curr_section->next;
414 return M64ERR_SUCCESS;
417 /* ----------------------------------------------------------- */
418 /* these functions are only to be used within the Core library */
419 /* ----------------------------------------------------------- */
421 m64p_error ConfigInit(const char *ConfigDirOverride, const char *DataDirOverride)
424 const char *configpath = NULL;
430 config_section *current_section = NULL;
431 char *line, *end, *lastcomment;
434 return M64ERR_ALREADY_INIT;
437 /* if a data directory was specified, make a copy of it */
438 if (DataDirOverride != NULL)
440 l_DataDirOverride = strdup(DataDirOverride);
441 if (l_DataDirOverride == NULL)
442 return M64ERR_NO_MEMORY;
445 /* if a config directory was specified, make a copy of it */
446 if (ConfigDirOverride != NULL)
448 l_ConfigDirOverride = strdup(ConfigDirOverride);
449 if (l_ConfigDirOverride == NULL)
450 return M64ERR_NO_MEMORY;
453 /* get the full pathname to the config file and try to open it */
454 configpath = ConfigGetUserConfigPath();
455 if (configpath == NULL)
458 filepath = combinepath(configpath, MUPEN64PLUS_CFG_NAME);
459 if (filepath == NULL)
460 return M64ERR_NO_MEMORY;
462 fPtr = fopen(filepath, "rb");
465 DebugMessage(M64MSG_INFO, "Couldn't open configuration file '%s'. Using defaults.", filepath);
467 l_SaveConfigOnExit = 1; /* auto-save the config file so that the defaults will be saved to disk */
468 return M64ERR_SUCCESS;
472 /* read the entire config file */
473 fseek(fPtr, 0L, SEEK_END);
474 filelen = ftell(fPtr);
475 fseek(fPtr, 0L, SEEK_SET);
477 configtext = (char *) malloc(filelen + 1);
478 if (configtext == NULL)
481 return M64ERR_NO_MEMORY;
483 if (fread(configtext, 1, filelen, fPtr) != filelen)
491 /* parse the file data */
492 current_section = NULL;
494 end = configtext + filelen;
499 ini_line l = ini_parse_line(&line);
503 lastcomment = l.value;
507 rval = ConfigOpenSection(l.name, (m64p_handle *) ¤t_section);
508 if (rval != M64ERR_SUCCESS)
517 if (l.value[0] == '"' && l.value[strlen(l.value)-1] == '"')
520 l.value[strlen(l.value)-1] = 0;
521 ConfigSetDefaultString((m64p_handle) current_section, l.name, l.value, lastcomment);
523 else if (osal_insensitive_strcmp(l.value, "false") == 0)
525 ConfigSetDefaultBool((m64p_handle) current_section, l.name, 0, lastcomment);
527 else if (osal_insensitive_strcmp(l.value, "true") == 0)
529 ConfigSetDefaultBool((m64p_handle) current_section, l.name, 1, lastcomment);
531 else if (is_numeric(l.value))
533 int val_int = (int) strtol(l.value, NULL, 10);
534 float val_float = (float) strtod(l.value, NULL);
535 if ((val_float - val_int) != 0.0)
536 ConfigSetDefaultFloat((m64p_handle) current_section, l.name, val_float, lastcomment);
538 ConfigSetDefaultInt((m64p_handle) current_section, l.name, val_int, lastcomment);
542 /* assume that it's a string */
543 ConfigSetDefaultString((m64p_handle) current_section, l.name, l.value, lastcomment);
553 /* release memory used for config file text */
556 /* duplicate the entire config data list, to store a copy of the list which represents the state of the file on disk */
557 copy_configlist_active_to_saved();
559 return M64ERR_SUCCESS;
562 m64p_error ConfigShutdown(void)
564 /* first, save the file if necessary */
565 if (l_SaveConfigOnExit)
568 /* reset the initialized flag */
570 return M64ERR_NOT_INIT;
573 /* free any malloc'd local variables */
574 if (l_DataDirOverride != NULL)
576 free(l_DataDirOverride);
577 l_DataDirOverride = NULL;
579 if (l_ConfigDirOverride != NULL)
581 free(l_ConfigDirOverride);
582 l_ConfigDirOverride = NULL;
585 /* free all of the memory in the 2 lists */
586 delete_list(&l_ConfigListActive);
587 delete_list(&l_ConfigListSaved);
589 return M64ERR_SUCCESS;
592 /* ------------------------------------------------ */
593 /* Selector functions, exported outside of the Core */
594 /* ------------------------------------------------ */
596 EXPORT m64p_error CALL ConfigListSections(void *context, void (*SectionListCallback)(void * context, const char * SectionName))
598 config_section *curr_section;
601 return M64ERR_NOT_INIT;
602 if (SectionListCallback == NULL)
603 return M64ERR_INPUT_ASSERT;
605 /* just walk through the section list, making a callback for each section name */
606 curr_section = l_ConfigListActive;
607 while (curr_section != NULL)
609 (*SectionListCallback)(context, curr_section->name);
610 curr_section = curr_section->next;
613 return M64ERR_SUCCESS;
616 EXPORT m64p_error CALL ConfigOpenSection(const char *SectionName, m64p_handle *ConfigSectionHandle)
618 config_section **curr_section;
619 config_section *new_section;
622 return M64ERR_NOT_INIT;
623 if (SectionName == NULL || ConfigSectionHandle == NULL)
624 return M64ERR_INPUT_ASSERT;
626 /* walk through the section list, looking for a case-insensitive name match */
627 curr_section = find_alpha_section_link(&l_ConfigListActive, SectionName);
628 if (*curr_section != NULL && osal_insensitive_strcmp(SectionName, (*curr_section)->name) == 0)
630 *ConfigSectionHandle = *curr_section;
631 return M64ERR_SUCCESS;
634 /* didn't find the section, so create new one */
635 new_section = config_section_create(SectionName);
636 if (new_section == NULL)
637 return M64ERR_NO_MEMORY;
639 /* add section to list in alphabetical order */
640 new_section->next = *curr_section;
641 *curr_section = new_section;
643 *ConfigSectionHandle = new_section;
644 return M64ERR_SUCCESS;
647 EXPORT m64p_error CALL ConfigListParameters(m64p_handle ConfigSectionHandle, void *context, void (*ParameterListCallback)(void * context, const char *ParamName, m64p_type ParamType))
649 config_section *section;
650 config_var *curr_var;
653 return M64ERR_NOT_INIT;
654 if (ConfigSectionHandle == NULL || ParameterListCallback == NULL)
655 return M64ERR_INPUT_ASSERT;
657 section = (config_section *) ConfigSectionHandle;
658 if (section->magic != SECTION_MAGIC)
659 return M64ERR_INPUT_INVALID;
661 /* walk through this section's parameter list, making a callback for each parameter */
662 curr_var = section->first_var;
663 while (curr_var != NULL)
665 (*ParameterListCallback)(context, curr_var->name, curr_var->type);
666 curr_var = curr_var->next;
669 return M64ERR_SUCCESS;
672 EXPORT int CALL ConfigHasUnsavedChanges(const char *SectionName)
674 config_section *input_section, *curr_section;
675 config_var *active_var, *saved_var;
677 /* check input conditions */
680 DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): Core config not initialized!");
684 /* if SectionName is NULL or blank, then check all sections */
685 if (SectionName == NULL || strlen(SectionName) < 1)
687 int iNumActiveSections = 0, iNumSavedSections = 0;
688 /* first, search through all sections in Active list. Recursively call ourself and return 1 if changed */
689 curr_section = l_ConfigListActive;
690 while (curr_section != NULL)
692 if (ConfigHasUnsavedChanges(curr_section->name))
694 curr_section = curr_section->next;
695 iNumActiveSections++;
697 /* Next, count the number of Saved sections and see if the count matches */
698 curr_section = l_ConfigListSaved;
699 while (curr_section != NULL)
701 curr_section = curr_section->next;
704 if (iNumActiveSections == iNumSavedSections)
705 return 0; /* no changes */
710 /* walk through the Active section list, looking for a case-insensitive name match with input string */
711 input_section = find_section(l_ConfigListActive, SectionName);
712 if (input_section == NULL)
714 DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): section name '%s' not found!", SectionName);
718 /* walk through the Saved section list, looking for a case-insensitive name match */
719 curr_section = find_section(l_ConfigListSaved, SectionName);
720 if (curr_section == NULL)
722 /* if this section isn't present in saved list, then it has been newly created */
726 /* compare all of the variables in the two sections. They are expected to be in the same order */
727 active_var = input_section->first_var;
728 saved_var = curr_section->first_var;
729 while (active_var != NULL && saved_var != NULL)
731 if (strcmp(active_var->name, saved_var->name) != 0)
733 if (active_var->type != saved_var->type)
735 switch(active_var->type)
738 if (active_var->val.integer != saved_var->val.integer)
742 if (active_var->val.number != saved_var->val.number)
746 if ((active_var->val.integer != 0) != (saved_var->val.integer != 0))
750 if (active_var->val.string == NULL)
752 DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): Variable '%s' NULL Active string pointer!", active_var->name);
755 if (saved_var->val.string == NULL)
757 DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): Variable '%s' NULL Saved string pointer!", active_var->name);
760 if (strcmp(active_var->val.string, saved_var->val.string) != 0)
764 DebugMessage(M64MSG_ERROR, "ConfigHasUnsavedChanges(): Invalid variable '%s' type %i!", active_var->name, active_var->type);
767 if (active_var->comment != NULL && saved_var->comment != NULL && strcmp(active_var->comment, saved_var->comment) != 0)
769 active_var = active_var->next;
770 saved_var = saved_var->next;
773 /* any extra new variables on the end, or deleted variables? */
774 if (active_var != NULL || saved_var != NULL)
777 /* exactly the same */
781 /* ------------------------------------------------------- */
782 /* Modifier functions, exported outside of the Core */
783 /* ------------------------------------------------------- */
785 EXPORT m64p_error CALL ConfigDeleteSection(const char *SectionName)
787 config_section **curr_section_link;
788 config_section *next_section;
791 return M64ERR_NOT_INIT;
792 if (l_ConfigListActive == NULL)
793 return M64ERR_INPUT_NOT_FOUND;
795 /* find the named section and pull it out of the list */
796 curr_section_link = find_section_link(&l_ConfigListActive, SectionName);
797 if (*curr_section_link == NULL)
798 return M64ERR_INPUT_NOT_FOUND;
800 next_section = (*curr_section_link)->next;
802 /* delete the named section */
803 delete_section(*curr_section_link);
805 /* fix the pointer to point to the next section after the deleted one */
806 *curr_section_link = next_section;
808 return M64ERR_SUCCESS;
811 EXPORT m64p_error CALL ConfigSaveFile(void)
814 return M64ERR_NOT_INIT;
816 /* copy the active config list to the saved config list */
817 copy_configlist_active_to_saved();
819 /* write the saved config list out to a file */
820 return (write_configlist_file());
823 EXPORT m64p_error CALL ConfigSaveSection(const char *SectionName)
825 config_section *curr_section, *new_section;
826 config_section **insertion_point;
829 return M64ERR_NOT_INIT;
830 if (SectionName == NULL || strlen(SectionName) < 1)
831 return M64ERR_INPUT_ASSERT;
833 /* walk through the Active section list, looking for a case-insensitive name match */
834 curr_section = find_section(l_ConfigListActive, SectionName);
835 if (curr_section == NULL)
836 return M64ERR_INPUT_NOT_FOUND;
838 /* duplicate this section */
839 new_section = section_deepcopy(curr_section);
840 if (new_section == NULL)
841 return M64ERR_NO_MEMORY;
843 /* update config section that's in the Saved list with the new one */
844 insertion_point = find_alpha_section_link(&l_ConfigListSaved, SectionName);
845 if (*insertion_point != NULL && osal_insensitive_strcmp((*insertion_point)->name, SectionName) == 0)
847 /* the section exists in the saved list and will be replaced */
848 new_section->next = (*insertion_point)->next;
849 delete_section(*insertion_point);
850 *insertion_point = new_section;
854 /* the section didn't exist in the saved list and has to be inserted */
855 new_section->next = *insertion_point;
856 *insertion_point = new_section;
859 /* write the saved config list out to a file */
860 return (write_configlist_file());
863 EXPORT m64p_error CALL ConfigRevertChanges(const char *SectionName)
865 config_section **active_section_link, *active_section, *saved_section, *new_section;
867 /* check input conditions */
869 return M64ERR_NOT_INIT;
870 if (SectionName == NULL)
871 return M64ERR_INPUT_ASSERT;
873 /* walk through the Active section list, looking for a case-insensitive name match with input string */
874 active_section_link = find_section_link(&l_ConfigListActive, SectionName);
875 active_section = *active_section_link;
876 if (active_section == NULL)
877 return M64ERR_INPUT_NOT_FOUND;
879 /* walk through the Saved section list, looking for a case-insensitive name match */
880 saved_section = find_section(l_ConfigListSaved, SectionName);
881 if (saved_section == NULL)
883 /* if this section isn't present in saved list, then it has been newly created */
884 return M64ERR_INPUT_NOT_FOUND;
887 /* copy the section as it is on the disk */
888 new_section = section_deepcopy(saved_section);
889 if (new_section == NULL)
890 return M64ERR_NO_MEMORY;
892 /* replace active_section with saved_section in the linked list */
893 *active_section_link = new_section;
894 new_section->next = active_section->next;
896 /* release memory associated with active_section */
897 delete_section(active_section);
899 return M64ERR_SUCCESS;
903 /* ------------------------------------------------------- */
904 /* Generic Get/Set functions, exported outside of the Core */
905 /* ------------------------------------------------------- */
907 EXPORT m64p_error CALL ConfigSetParameter(m64p_handle ConfigSectionHandle, const char *ParamName, m64p_type ParamType, const void *ParamValue)
909 config_section *section;
912 /* check input conditions */
914 return M64ERR_NOT_INIT;
915 if (ConfigSectionHandle == NULL || ParamName == NULL || ParamValue == NULL || (int) ParamType < 1 || (int) ParamType > 4)
916 return M64ERR_INPUT_ASSERT;
918 section = (config_section *) ConfigSectionHandle;
919 if (section->magic != SECTION_MAGIC)
920 return M64ERR_INPUT_INVALID;
922 /* if this parameter doesn't already exist, then create it and add it to the section */
923 var = find_section_var(section, ParamName);
926 var = config_var_create(ParamName, NULL);
928 return M64ERR_NO_MEMORY;
929 append_var_to_section(section, var);
932 /* cleanup old values */
936 free(var->val.string);
942 /* set this parameter's value */
943 var->type = ParamType;
947 var->val.integer = *((int *) ParamValue);
950 var->val.number = *((float *) ParamValue);
953 var->val.integer = (*((int *) ParamValue) != 0);
956 var->val.string = strdup((char *)ParamValue);
957 if (var->val.string == NULL)
958 return M64ERR_NO_MEMORY;
961 /* this is logically impossible because of the ParamType check at the top of this function */
965 return M64ERR_SUCCESS;
968 EXPORT m64p_error CALL ConfigGetParameter(m64p_handle ConfigSectionHandle, const char *ParamName, m64p_type ParamType, void *ParamValue, int MaxSize)
970 config_section *section;
973 /* check input conditions */
975 return M64ERR_NOT_INIT;
976 if (ConfigSectionHandle == NULL || ParamName == NULL || ParamValue == NULL || (int) ParamType < 1 || (int) ParamType > 4)
977 return M64ERR_INPUT_ASSERT;
979 section = (config_section *) ConfigSectionHandle;
980 if (section->magic != SECTION_MAGIC)
981 return M64ERR_INPUT_INVALID;
983 /* if this parameter doesn't already exist, return an error */
984 var = find_section_var(section, ParamName);
986 return M64ERR_INPUT_NOT_FOUND;
988 /* call the specific Get function to translate the parameter to the desired type */
992 if (MaxSize < sizeof(int)) return M64ERR_INPUT_INVALID;
993 if (var->type != M64TYPE_INT && var->type != M64TYPE_FLOAT) return M64ERR_WRONG_TYPE;
994 *((int *) ParamValue) = ConfigGetParamInt(ConfigSectionHandle, ParamName);
997 if (MaxSize < sizeof(float)) return M64ERR_INPUT_INVALID;
998 if (var->type != M64TYPE_INT && var->type != M64TYPE_FLOAT) return M64ERR_WRONG_TYPE;
999 *((float *) ParamValue) = ConfigGetParamFloat(ConfigSectionHandle, ParamName);
1002 if (MaxSize < sizeof(int)) return M64ERR_INPUT_INVALID;
1003 if (var->type != M64TYPE_BOOL && var->type != M64TYPE_INT) return M64ERR_WRONG_TYPE;
1004 *((int *) ParamValue) = ConfigGetParamBool(ConfigSectionHandle, ParamName);
1006 case M64TYPE_STRING:
1009 if (MaxSize < 1) return M64ERR_INPUT_INVALID;
1010 if (var->type != M64TYPE_STRING && var->type != M64TYPE_BOOL) return M64ERR_WRONG_TYPE;
1011 string = ConfigGetParamString(ConfigSectionHandle, ParamName);
1012 strncpy((char *) ParamValue, string, MaxSize);
1013 *((char *) ParamValue + MaxSize - 1) = 0;
1017 /* this is logically impossible because of the ParamType check at the top of this function */
1021 return M64ERR_SUCCESS;
1024 EXPORT m64p_error CALL ConfigGetParameterType(m64p_handle ConfigSectionHandle, const char *ParamName, m64p_type *ParamType)
1026 config_section *section;
1029 /* check input conditions */
1031 return M64ERR_NOT_INIT;
1032 if (ConfigSectionHandle == NULL || ParamName == NULL || ParamType == NULL)
1033 return M64ERR_INPUT_ASSERT;
1035 section = (config_section *) ConfigSectionHandle;
1036 if (section->magic != SECTION_MAGIC)
1037 return M64ERR_INPUT_INVALID;
1039 /* if this parameter doesn't already exist, return an error */
1040 var = find_section_var(section, ParamName);
1042 return M64ERR_INPUT_NOT_FOUND;
1044 *ParamType = var->type;
1045 return M64ERR_SUCCESS;
1049 EXPORT const char * CALL ConfigGetParameterHelp(m64p_handle ConfigSectionHandle, const char *ParamName)
1051 config_section *section;
1054 /* check input conditions */
1055 if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL)
1058 section = (config_section *) ConfigSectionHandle;
1059 if (section->magic != SECTION_MAGIC)
1062 /* if this parameter doesn't exist, return an error */
1063 var = find_section_var(section, ParamName);
1067 return var->comment;
1070 /* ------------------------------------------------------- */
1071 /* Special Get/Set functions, exported outside of the Core */
1072 /* ------------------------------------------------------- */
1074 EXPORT m64p_error CALL ConfigSetDefaultInt(m64p_handle ConfigSectionHandle, const char *ParamName, int ParamValue, const char *ParamHelp)
1076 config_section *section;
1079 /* check input conditions */
1081 return M64ERR_NOT_INIT;
1082 if (ConfigSectionHandle == NULL || ParamName == NULL)
1083 return M64ERR_INPUT_ASSERT;
1085 section = (config_section *) ConfigSectionHandle;
1086 if (section->magic != SECTION_MAGIC)
1087 return M64ERR_INPUT_INVALID;
1089 /* if this parameter already exists, then just return successfully */
1090 var = find_section_var(section, ParamName);
1092 return M64ERR_SUCCESS;
1094 /* otherwise create a new config_var object and add it to this section */
1095 var = config_var_create(ParamName, ParamHelp);
1097 return M64ERR_NO_MEMORY;
1098 var->type = M64TYPE_INT;
1099 var->val.integer = ParamValue;
1100 append_var_to_section(section, var);
1102 return M64ERR_SUCCESS;
1105 EXPORT m64p_error CALL ConfigSetDefaultFloat(m64p_handle ConfigSectionHandle, const char *ParamName, float ParamValue, const char *ParamHelp)
1107 config_section *section;
1110 /* check input conditions */
1112 return M64ERR_NOT_INIT;
1113 if (ConfigSectionHandle == NULL || ParamName == NULL)
1114 return M64ERR_INPUT_ASSERT;
1116 section = (config_section *) ConfigSectionHandle;
1117 if (section->magic != SECTION_MAGIC)
1118 return M64ERR_INPUT_INVALID;
1120 /* if this parameter already exists, then just return successfully */
1121 var = find_section_var(section, ParamName);
1123 return M64ERR_SUCCESS;
1125 /* otherwise create a new config_var object and add it to this section */
1126 var = config_var_create(ParamName, ParamHelp);
1128 return M64ERR_NO_MEMORY;
1129 var->type = M64TYPE_FLOAT;
1130 var->val.number = ParamValue;
1131 append_var_to_section(section, var);
1133 return M64ERR_SUCCESS;
1136 EXPORT m64p_error CALL ConfigSetDefaultBool(m64p_handle ConfigSectionHandle, const char *ParamName, int ParamValue, const char *ParamHelp)
1138 config_section *section;
1141 /* check input conditions */
1143 return M64ERR_NOT_INIT;
1144 if (ConfigSectionHandle == NULL || ParamName == NULL)
1145 return M64ERR_INPUT_ASSERT;
1147 section = (config_section *) ConfigSectionHandle;
1148 if (section->magic != SECTION_MAGIC)
1149 return M64ERR_INPUT_INVALID;
1151 /* if this parameter already exists, then just return successfully */
1152 var = find_section_var(section, ParamName);
1154 return M64ERR_SUCCESS;
1156 /* otherwise create a new config_var object and add it to this section */
1157 var = config_var_create(ParamName, ParamHelp);
1159 return M64ERR_NO_MEMORY;
1160 var->type = M64TYPE_BOOL;
1161 var->val.integer = ParamValue ? 1 : 0;
1162 append_var_to_section(section, var);
1164 return M64ERR_SUCCESS;
1167 EXPORT m64p_error CALL ConfigSetDefaultString(m64p_handle ConfigSectionHandle, const char *ParamName, const char * ParamValue, const char *ParamHelp)
1169 config_section *section;
1172 /* check input conditions */
1174 return M64ERR_NOT_INIT;
1175 if (ConfigSectionHandle == NULL || ParamName == NULL || ParamValue == NULL)
1176 return M64ERR_INPUT_ASSERT;
1178 section = (config_section *) ConfigSectionHandle;
1179 if (section->magic != SECTION_MAGIC)
1180 return M64ERR_INPUT_INVALID;
1182 /* if this parameter already exists, then just return successfully */
1183 var = find_section_var(section, ParamName);
1185 return M64ERR_SUCCESS;
1187 /* otherwise create a new config_var object and add it to this section */
1188 var = config_var_create(ParamName, ParamHelp);
1190 return M64ERR_NO_MEMORY;
1191 var->type = M64TYPE_STRING;
1192 var->val.string = strdup(ParamValue);
1193 if (var->val.string == NULL)
1196 return M64ERR_NO_MEMORY;
1198 append_var_to_section(section, var);
1200 return M64ERR_SUCCESS;
1203 EXPORT int CALL ConfigGetParamInt(m64p_handle ConfigSectionHandle, const char *ParamName)
1205 config_section *section;
1208 /* check input conditions */
1209 if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL)
1211 DebugMessage(M64MSG_ERROR, "ConfigGetParamInt(): Input assertion!");
1215 section = (config_section *) ConfigSectionHandle;
1216 if (section->magic != SECTION_MAGIC)
1218 DebugMessage(M64MSG_ERROR, "ConfigGetParamInt(): ConfigSectionHandle invalid!");
1222 /* if this parameter doesn't already exist, return an error */
1223 var = find_section_var(section, ParamName);
1226 DebugMessage(M64MSG_ERROR, "ConfigGetParamInt(): Parameter '%s' not found!", ParamName);
1230 /* translate the actual variable type to an int */
1234 return var->val.integer;
1236 return (int) var->val.number;
1238 return (var->val.integer != 0);
1239 case M64TYPE_STRING:
1240 return atoi(var->val.string);
1242 DebugMessage(M64MSG_ERROR, "ConfigGetParamInt(): invalid internal parameter type for '%s'", ParamName);
1249 EXPORT float CALL ConfigGetParamFloat(m64p_handle ConfigSectionHandle, const char *ParamName)
1251 config_section *section;
1254 /* check input conditions */
1255 if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL)
1257 DebugMessage(M64MSG_ERROR, "ConfigGetParamFloat(): Input assertion!");
1261 section = (config_section *) ConfigSectionHandle;
1262 if (section->magic != SECTION_MAGIC)
1264 DebugMessage(M64MSG_ERROR, "ConfigGetParamFloat(): ConfigSectionHandle invalid!");
1268 /* if this parameter doesn't already exist, return an error */
1269 var = find_section_var(section, ParamName);
1272 DebugMessage(M64MSG_ERROR, "ConfigGetParamFloat(): Parameter '%s' not found!", ParamName);
1276 /* translate the actual variable type to an int */
1280 return (float) var->val.integer;
1282 return var->val.number;
1284 return (var->val.integer != 0) ? 1.0f : 0.0f;
1285 case M64TYPE_STRING:
1286 return (float) atof(var->val.string);
1288 DebugMessage(M64MSG_ERROR, "ConfigGetParamFloat(): invalid internal parameter type for '%s'", ParamName);
1295 EXPORT int CALL ConfigGetParamBool(m64p_handle ConfigSectionHandle, const char *ParamName)
1297 config_section *section;
1300 /* check input conditions */
1301 if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL)
1303 DebugMessage(M64MSG_ERROR, "ConfigGetParamBool(): Input assertion!");
1307 section = (config_section *) ConfigSectionHandle;
1308 if (section->magic != SECTION_MAGIC)
1310 DebugMessage(M64MSG_ERROR, "ConfigGetParamBool(): ConfigSectionHandle invalid!");
1314 /* if this parameter doesn't already exist, return an error */
1315 var = find_section_var(section, ParamName);
1318 DebugMessage(M64MSG_ERROR, "ConfigGetParamBool(): Parameter '%s' not found!", ParamName);
1322 /* translate the actual variable type to an int */
1326 return (var->val.integer != 0);
1328 return (var->val.number != 0.0);
1330 return var->val.integer;
1331 case M64TYPE_STRING:
1332 return (osal_insensitive_strcmp(var->val.string, "true") == 0);
1334 DebugMessage(M64MSG_ERROR, "ConfigGetParamBool(): invalid internal parameter type for '%s'", ParamName);
1341 EXPORT const char * CALL ConfigGetParamString(m64p_handle ConfigSectionHandle, const char *ParamName)
1343 static char outstr[64]; /* warning: not thread safe */
1344 config_section *section;
1347 /* check input conditions */
1348 if (!l_ConfigInit || ConfigSectionHandle == NULL || ParamName == NULL)
1350 DebugMessage(M64MSG_ERROR, "ConfigGetParamString(): Input assertion!");
1354 section = (config_section *) ConfigSectionHandle;
1355 if (section->magic != SECTION_MAGIC)
1357 DebugMessage(M64MSG_ERROR, "ConfigGetParamString(): ConfigSectionHandle invalid!");
1361 /* if this parameter doesn't already exist, return an error */
1362 var = find_section_var(section, ParamName);
1365 DebugMessage(M64MSG_ERROR, "ConfigGetParamString(): Parameter '%s' not found!", ParamName);
1369 /* translate the actual variable type to an int */
1373 snprintf(outstr, 63, "%i", var->val.integer);
1377 snprintf(outstr, 63, "%f", var->val.number);
1381 return (var->val.integer ? "True" : "False");
1382 case M64TYPE_STRING:
1383 return var->val.string;
1385 DebugMessage(M64MSG_ERROR, "ConfigGetParamString(): invalid internal parameter type for '%s'", ParamName);
1392 /* ------------------------------------------------------ */
1393 /* OS Abstraction functions, exported outside of the Core */
1394 /* ------------------------------------------------------ */
1396 EXPORT const char * CALL ConfigGetSharedDataFilepath(const char *filename)
1398 const char *configsharepath = NULL;
1399 m64p_handle CoreHandle = NULL;
1401 /* check input parameter */
1402 if (filename == NULL) return NULL;
1404 /* try to get the SharedDataPath string variable in the Core configuration section */
1405 if (ConfigOpenSection("Core", &CoreHandle) == M64ERR_SUCCESS)
1407 configsharepath = ConfigGetParamString(CoreHandle, "SharedDataPath");
1410 return osal_get_shared_filepath(filename, l_DataDirOverride, configsharepath);
1413 EXPORT const char * CALL ConfigGetUserConfigPath(void)
1415 if (l_ConfigDirOverride != NULL)
1417 osal_mkdirp(l_ConfigDirOverride, 0700);
1418 return l_ConfigDirOverride;
1421 return osal_get_user_configpath();
1424 EXPORT const char * CALL ConfigGetUserDataPath(void)
1426 return osal_get_user_datapath();
1429 EXPORT const char * CALL ConfigGetUserCachePath(void)
1431 return osal_get_user_cachepath();