Search
lxdream.org :: lxdream/src/config.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/config.c
changeset 1077:136fc24d17ef
prev1072:d82e04e6d497
next1144:00dd49743974
author nkeynes
date Wed Oct 07 17:53:56 2009 +1000 (12 years ago)
permissions -rw-r--r--
last change Create a host attachment for the SCIF serial port. By default, uses /dev/console
Add general fd listening to netutil, and rename to ioutil
Add SCIF update on port read/write - fixes KOS timing problems but needs to
be redone properly.
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * User configuration support
     5  *
     6  * Copyright (c) 2005 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    19 #include <unistd.h>
    20 #include <stdio.h>
    21 #include <errno.h>
    22 #include <stdlib.h>
    23 #include <string.h>
    24 #include <glib/gmem.h>
    25 #include <glib/gstrfuncs.h>
    26 #include <sys/types.h>
    27 #include <sys/stat.h>
    28 #include "dreamcast.h"
    29 #include "config.h"
    30 #include "lxpaths.h"
    31 #include "maple/maple.h"
    33 #define MAX_ROOT_GROUPS 16
    35 extern struct lxdream_config_group hotkeys_group;
    36 extern struct lxdream_config_group serial_group;
    38 gboolean lxdream_load_config_file( const gchar *filename );
    39 gboolean lxdream_save_config_file( const gchar *filename );
    40 gboolean lxdream_load_config_stream( FILE *f );
    41 gboolean lxdream_save_config_stream( FILE *f );
    43 static struct lxdream_config_group global_group =
    44     { "global", dreamcast_config_changed, NULL, NULL,
    45        {{ "bios", N_("Bios ROM"), CONFIG_TYPE_FILE, NULL },
    46         { "flash", N_("Flash ROM"), CONFIG_TYPE_FILE, NULL },
    47         { "default path", N_("Default disc path"), CONFIG_TYPE_PATH, "." },
    48         { "save path", N_("Save-state path"), CONFIG_TYPE_PATH, NULL },
    49         { "vmu path", N_("VMU path"), CONFIG_TYPE_PATH, NULL },
    50         { "bootstrap", N_("Bootstrap IP.BIN"), CONFIG_TYPE_FILE, NULL },
    51         { "gdrom", NULL, CONFIG_TYPE_FILE, NULL },
    52         { "recent", NULL, CONFIG_TYPE_FILELIST, NULL },
    53         { "vmu", NULL, CONFIG_TYPE_FILELIST, NULL },
    54         { "quick state", NULL, CONFIG_TYPE_INTEGER, "0" },
    55         { NULL, CONFIG_TYPE_NONE }} };
    57 /**
    58  * Dummy group for controllers (handled specially)
    59  */
    60 static struct lxdream_config_group controllers_group =
    61     { "controllers", NULL, NULL, NULL, {{NULL, CONFIG_TYPE_NONE}} };
    63 struct lxdream_config_group *lxdream_config_root[MAX_ROOT_GROUPS+1] = 
    64        { &global_group, &controllers_group, &hotkeys_group, &serial_group, NULL };
    66 static gchar *lxdream_config_load_filename = NULL;
    67 static gchar *lxdream_config_save_filename = NULL;
    69 void lxdream_register_config_group( const gchar *key, lxdream_config_group_t group )
    70 {
    71     int i;
    72     for( i=0; i<MAX_ROOT_GROUPS; i++ ) {
    73         if( lxdream_config_root[i] == NULL ) {
    74             lxdream_config_root[i] = group;
    75             lxdream_config_root[i+1] = NULL;
    76             return;
    77         }
    78     }
    79     ERROR( "Unable to register config group '%s': Too many configuration groups", key );
    80 }
    82 gboolean lxdream_find_config()
    83 {
    84     gboolean result = TRUE;
    85     char *home = getenv("HOME");
    86     if( lxdream_config_save_filename == NULL ) {
    87         /* For compatibility, look for the old ~/.lxdreamrc first. Use it if 
    88          * found, otherwise use the new ~/.lxdream/lxdreamrc.
    89          */
    90         lxdream_config_save_filename = g_strdup_printf("%s/.%s", home, DEFAULT_CONFIG_FILENAME);
    91         if( access(lxdream_config_save_filename, R_OK) != 0 ) {
    92             g_free(lxdream_config_save_filename);
    93             const char *user_path = get_user_data_path();
    94             lxdream_config_save_filename = g_strdup_printf("%s/%s", user_path, DEFAULT_CONFIG_FILENAME);
    95         }
    96     }
    97     if( lxdream_config_load_filename == NULL ) {
    98         char *sysconfig = g_strdup_printf("%s/%s", get_sysconf_path(), DEFAULT_CONFIG_FILENAME);
    99         if( access(lxdream_config_save_filename, R_OK) == 0 ) {
   100             lxdream_config_load_filename = g_strdup(lxdream_config_save_filename);
   101             g_free(sysconfig);
   102         } else if( access( sysconfig, R_OK ) == 0 ) {
   103             lxdream_config_load_filename = sysconfig;
   104         } else {
   105             lxdream_config_load_filename = g_strdup(lxdream_config_save_filename);
   106             g_free(sysconfig);
   107             result = FALSE;
   108         }	
   109     }
   110     return result;
   111 }
   113 void lxdream_set_config_filename( const gchar *filename )
   114 {
   115     if( lxdream_config_load_filename != NULL ) {
   116         g_free(lxdream_config_load_filename);
   117     }
   118     lxdream_config_load_filename = g_strdup(filename);
   119     if( lxdream_config_save_filename != NULL ) {
   120         g_free(lxdream_config_save_filename);
   121     }
   122     lxdream_config_save_filename = g_strdup(filename);
   123 }
   125 void lxdream_set_default_config( )
   126 {
   127     /* Construct platform dependent defaults */
   128     const gchar *user_path = get_user_data_path();
   129     global_group.params[CONFIG_BIOS_PATH].default_value = g_strdup_printf( "%s/dcboot.rom", user_path ); 
   130     global_group.params[CONFIG_FLASH_PATH].default_value = g_strdup_printf( "%s/dcflash.rom", user_path ); 
   131     global_group.params[CONFIG_SAVE_PATH].default_value = g_strdup_printf( "%s/save", user_path ); 
   132     global_group.params[CONFIG_VMU_PATH].default_value = g_strdup_printf( "%s/vmu", user_path ); 
   133     global_group.params[CONFIG_BOOTSTRAP].default_value = g_strdup_printf( "%s/IP.BIN", user_path ); 
   135     /* Copy defaults into main values */
   136     for( int i=0; lxdream_config_root[i] != NULL; i++ ) {
   137         struct lxdream_config_entry *param = lxdream_config_root[i]->params;
   138         if( param != NULL ) {
   139             while( param->key != NULL ) {
   140                 if( param->value != param->default_value ) {
   141                     if( param->value != NULL )
   142                         free( param->value );
   143                     param->value = (gchar *)param->default_value;
   144                 }
   145                 param++;
   146             }
   147         }
   148     }
   149     maple_detach_all();
   150 }
   152 const gchar *lxdream_get_config_value( lxdream_config_group_t group, int key )
   153 {
   154     return group->params[key].value;
   155 }
   158 gboolean lxdream_set_config_value( lxdream_config_group_t group, int key, const gchar *value )
   159 {
   160     lxdream_config_entry_t param = &group->params[key];
   161     if( param->value != value &&
   162         (param->value == NULL || value == NULL || strcmp(param->value,value) != 0)  ) {
   164         gchar *new_value = g_strdup(value);
   166         /* If the group defines an on_change handler, it can block the change
   167          * (ie due to an invalid setting).
   168          */
   169         if( group->on_change == NULL ||
   170             group->on_change(group->data, group,key, param->value, new_value) ) {
   172             /* Don't free the default value, but otherwise need to release the
   173              * old value.
   174              */
   175             if( param->value != param->default_value && param->value != NULL ) {
   176                 free( param->value );
   177             }
   178             param->value = new_value;
   179         } else { /* on_change handler said no. */
   180             g_free(new_value);
   181             return FALSE;
   182         }
   183     }
   184     return TRUE;
   185 }
   187 const gchar *lxdream_get_global_config_value( int key )
   188 {
   189     return global_group.params[key].value;
   190 }
   192 void lxdream_set_global_config_value( int key, const gchar *value )
   193 {
   194     lxdream_set_config_value(&global_group, key, value);
   195 }
   197 GList *lxdream_get_global_config_list_value( int key )
   198 {
   199     GList *result = NULL;
   200     const gchar *str = lxdream_get_global_config_value( key );
   201     if( str != NULL ) {
   202         gchar **strv = g_strsplit(str, ":",0);
   203         int i;
   204         for( i=0; strv[i] != NULL; i++ ) {
   205             result = g_list_append( result, g_strdup(strv[i]) );
   206         }
   207         g_strfreev(strv);
   208     }
   209     return result;
   210 }
   212 void lxdream_set_global_config_list_value( int key, const GList *list )
   213 {
   214     if( list == NULL ) {
   215         lxdream_set_global_config_value( key, NULL );
   216     } else {
   217         const GList *ptr;
   218         int size = 0;
   220         for( ptr = list; ptr != NULL; ptr = g_list_next(ptr) ) {
   221             size += strlen( (gchar *)ptr->data ) + 1;
   222         }
   223         char buf[size];
   224         strcpy( buf, (gchar *)list->data );
   225         for( ptr = g_list_next(list); ptr != NULL; ptr = g_list_next(ptr) ) {
   226             strcat( buf, ":" );
   227             strcat( buf, (gchar *)ptr->data );
   228         }
   229         lxdream_set_global_config_value( key, buf );
   230     }
   231 }
   233 gchar *lxdream_get_global_config_path_value( int key )
   234 {
   235     const gchar *str = lxdream_get_global_config_value(key);
   236     if( str == NULL ) {
   237         return NULL;
   238     } else {
   239         return get_expanded_path(str);
   240     }
   241 }
   243 const gchar *lxdream_set_global_config_path_value( int key, const gchar *value )
   244 {
   245     gchar *temp = get_escaped_path(value);
   246     lxdream_set_global_config_value(key,temp);
   247     g_free(temp);
   248     return lxdream_get_global_config_value(key);
   249 }
   251 struct lxdream_config_group * lxdream_get_config_group( int group )
   252 {
   253     return lxdream_config_root[group];
   254 }
   256 void lxdream_copy_config_group( lxdream_config_group_t dest, lxdream_config_group_t src )
   257 {
   258     int i;
   259     for( i=0; src->params[i].key != NULL; i++ ) {
   260         lxdream_set_config_value( dest, i, src->params[i].value );
   261     }
   262 }
   264 void lxdream_clone_config_group( lxdream_config_group_t dest, lxdream_config_group_t src )
   265 {
   266     int i;
   268     dest->key = src->key;
   269     dest->on_change = NULL;
   270     dest->key_binding = NULL;
   271     dest->data = NULL;
   272     for( i=0; src->params[i].key != NULL; i++ ) {
   273         dest->params[i].key = src->params[i].key;
   274         dest->params[i].label = src->params[i].label;
   275         dest->params[i].type = src->params[i].type;
   276         dest->params[i].tag = src->params[i].tag;
   277         dest->params[i].default_value = src->params[i].default_value;
   278         dest->params[i].value = NULL;
   279         lxdream_set_config_value( dest, i, src->params[i].value );
   280     }
   281     dest->params[i].key = NULL;
   282     dest->params[i].label = NULL;
   283 }
   285 gboolean lxdream_load_config( )
   286 {
   287     if( lxdream_config_load_filename == NULL ) {
   288         lxdream_find_config();
   289     }
   290     return lxdream_load_config_file(lxdream_config_load_filename);
   291 }
   293 gboolean lxdream_save_config( )
   294 {
   295     if( lxdream_config_save_filename == NULL ) {
   296         lxdream_find_config();
   297     }
   298     return lxdream_save_config_file(lxdream_config_save_filename);
   299 }
   301 gboolean lxdream_load_config_file( const gchar *filename )
   302 {
   303     FILE *f;
   304     gboolean result;
   306     if( access(filename, F_OK) != 0 ) {
   307         INFO( "Configuration file '%s' does not exist, creating from defaults" );
   308         lxdream_set_default_config();
   309         lxdream_save_config();
   310     }
   312     f = fopen(filename, "ro");
   313     if( f == NULL ) {
   314         ERROR( "Unable to open configuration file '%s': %s", filename, strerror(errno) );
   315         lxdream_set_default_config();
   316         return FALSE;
   317     }
   319     result = lxdream_load_config_stream( f );
   320     fclose(f);
   321     return result;
   322 }
   324 gboolean lxdream_load_config_stream( FILE *f )
   325 {
   327     char buf[512];
   328     int maple_device = -1, maple_subdevice = -1, i;
   329     struct lxdream_config_group *group = NULL;
   330     struct lxdream_config_group *top_group = NULL;
   331     maple_device_t device = NULL;
   332     lxdream_set_default_config();
   334     while( fgets( buf, sizeof(buf), f ) != NULL ) {
   335         g_strstrip(buf);
   336         if( buf[0] == '#' )
   337             continue;
   338         if( *buf == '[' ) {
   339             char *p = strchr(buf, ']');
   340             if( p != NULL ) {
   341                 maple_device = maple_subdevice = -1;
   342                 *p = '\0';
   343                 g_strstrip(buf+1);
   344                 for( i=0; lxdream_config_root[i] != NULL; i++ ) {
   345                     if( strcasecmp(lxdream_config_root[i]->key, buf+1) == 0 ) {
   346                         top_group = group = lxdream_config_root[i];
   347                         break;
   348                     }
   349                 }
   350             }
   351         } else if( group != NULL ) {
   352             char *value = strchr( buf, '=' );
   353             if( value != NULL ) {
   354                 struct lxdream_config_entry *param = group->params;
   355                 *value = '\0';
   356                 value++;
   357                 g_strstrip(buf);
   358                 g_strstrip(value);
   359                 if( top_group == &controllers_group ) {
   360                     if( g_strncasecmp( buf, "device ", 7 ) == 0 ) {
   361                         maple_device = strtoul( buf+7, NULL, 0 );
   362                         if( maple_device < 0 || maple_device > 3 ) {
   363                             ERROR( "Device number must be between 0..3 (not '%s')", buf+7);
   364                             continue;
   365                         }
   366                         maple_subdevice = 0;
   367                         device = maple_new_device( value );
   368                         if( device == NULL ) {
   369                             ERROR( "Unrecognized device '%s'", value );
   370                         } else {
   371                             group = maple_get_device_config(device);
   372                             maple_attach_device( device, maple_device, maple_subdevice );
   373                         }
   374                         continue;
   375                     } else if( g_strncasecmp( buf, "subdevice ", 10 ) == 0 ) {
   376                         maple_subdevice = strtoul( buf+10, NULL, 0 );
   377                         if( maple_device == -1 ) {
   378                             ERROR( "Subdevice not allowed without primary device" );
   379                         } else if( maple_subdevice < 1 || maple_subdevice > 5 ) {
   380                             ERROR( "Subdevice must be between 1..5 (not '%s')", buf+10 );
   381                         } else if( (device = maple_new_device(value)) == NULL ) {
   382                             ERROR( "Unrecognized subdevice '%s'", value );
   383                         } else {
   384                             group = maple_get_device_config(device);
   385                             maple_attach_device( device, maple_device, maple_subdevice );
   386                         }
   387                         continue;
   388                     }
   389                 }
   390                 while( param->key != NULL ) {
   391                     if( strcasecmp( param->key, buf ) == 0 ) {
   392                         param->value = g_strdup(value);
   393                         break;
   394                     }
   395                     param++;
   396                 }
   397             }
   398         }
   399     }
   400     return TRUE;
   401 }
   403 gboolean lxdream_save_config_file( const gchar *filename )
   404 {
   405     FILE *f = fopen(filename, "wo");
   406     gboolean result;
   407     if( f == NULL ) {
   408         ERROR( "Unable to open '%s': %s", filename, strerror(errno) );
   409         return FALSE;
   410     }
   411     result = lxdream_save_config_stream(f);
   412     fclose(f);
   413     return TRUE;
   414 }    
   416 gboolean lxdream_save_config_stream( FILE *f )
   417 {
   418     int i;
   419     for( i=0; lxdream_config_root[i] != NULL; i++ ) {
   420         fprintf( f, "[%s]\n", lxdream_config_root[i]->key );
   422         if( lxdream_config_root[i] == &controllers_group ) {
   423             int i,j;
   424             for( i=0; i<4; i++ ) {
   425                 for( j=0; j<6; j++ ) {
   426                     maple_device_t dev = maple_get_device( i, j );
   427                     if( dev != NULL ) {
   428                         if( j == 0 )
   429                             fprintf( f, "Device %d = %s\n", i, dev->device_class->name );
   430                         else 
   431                             fprintf( f, "Subdevice %d = %s\n", j, dev->device_class->name );
   432                         lxdream_config_group_t group = maple_get_device_config(dev);
   433                         if( group != NULL ) {
   434                             lxdream_config_entry_t entry = group->params;
   435                             while( entry->key != NULL ) {
   436                                 if( entry->value != NULL ) {
   437                                     fprintf( f, "%*c%s = %s\n", j==0?4:8, ' ',entry->key, entry->value );
   438                                 }
   439                                 entry++;
   440                             }
   441                         }
   442                     }
   443                 }
   444             }
   445         } else {
   446             struct lxdream_config_entry *entry = lxdream_config_root[i]->params;
   447             while( entry->key != NULL ) {
   448                 if( entry->value != NULL ) {
   449                     fprintf( f, "%s = %s\n", entry->key, entry->value );
   450                 }
   451                 entry++;
   452             }
   453         }
   454         fprintf( f, "\n" );
   455     }
   456     return TRUE;
   457 }
   459 void lxdream_make_config_dir( )
   460 {
   461     const char *user_path = get_user_data_path();
   462     struct stat st;
   464     if( access( user_path, R_OK|X_OK ) == 0 && lstat( user_path, &st ) == 0 &&
   465             (st.st_mode & S_IFDIR) != 0 ) {
   466         /* All good */
   467         return;
   468     }
   470     if( mkdir( user_path, 0777 ) != 0 ) {
   471         ERROR( "Unable to create user configuration directory %s: %s", user_path, strerror(errno) );
   472         return;
   473     }
   475     char *vmupath = g_strdup_printf( "%s/vmu", user_path );
   476     mkdir( vmupath, 0777 );
   477     g_free( vmupath );
   479     char *savepath = g_strdup_printf( "%s/save", user_path );
   480     mkdir( savepath, 0777 );
   481     g_free( vmupath );
   482 }
.