summaryrefslogtreecommitdiff
path: root/settings.c
diff options
context:
space:
mode:
authortruelight <truelight@openttd.org>2004-08-09 17:04:08 +0000
committertruelight <truelight@openttd.org>2004-08-09 17:04:08 +0000
commitefaeb275f78e18d594d9ee8ff04eccd2dc59512c (patch)
treebc8e1f56d77706d14d048cb2d99e53291930b520 /settings.c
downloadopenttd-efaeb275f78e18d594d9ee8ff04eccd2dc59512c.tar.xz
(svn r1) Import of revision 975 of old (crashed) SVN
Diffstat (limited to 'settings.c')
-rw-r--r--settings.c922
1 files changed, 922 insertions, 0 deletions
diff --git a/settings.c b/settings.c
new file mode 100644
index 000000000..4dd5f3538
--- /dev/null
+++ b/settings.c
@@ -0,0 +1,922 @@
+#include "stdafx.h"
+#include "ttd.h"
+#include "sound.h"
+
+enum SettingDescType {
+ SDT_INTX, // must be 0
+ SDT_ONEOFMANY,
+ SDT_MANYOFMANY,
+ SDT_BOOLX,
+ SDT_STRING,
+ SDT_STRINGBUF,
+ SDT_INTLIST,
+
+ SDT_INT8 = 0 << 4,
+ SDT_UINT8 = 1 << 4,
+ SDT_INT16 = 2 << 4,
+ SDT_UINT16 = 3 << 4,
+ SDT_INT32 = 4 << 4,
+ SDT_UINT32 = 5 << 4,
+ SDT_CALLBX = 6 << 4,
+
+ SDT_UINT = SDT_UINT32,
+ SDT_INT = SDT_INT32,
+
+ SDT_NOSAVE = 1 << 8,
+
+ SDT_CALLB = SDT_INTX | SDT_CALLBX,
+
+ SDT_BOOL = SDT_BOOLX | SDT_UINT8,
+};
+
+typedef struct IniFile IniFile;
+typedef struct IniItem IniItem;
+typedef struct IniGroup IniGroup;
+typedef struct SettingDesc SettingDesc;
+typedef struct MemoryPool MemoryPool;
+
+static void pool_init(MemoryPool **pool);
+static void *pool_alloc(MemoryPool **pool, uint size);
+static void *pool_strdup(MemoryPool **pool, const char *mem, uint size);
+static void pool_free(MemoryPool **pool);
+
+struct MemoryPool {
+ uint pos,size;
+ MemoryPool *next;
+ byte mem[1];
+};
+
+static MemoryPool *pool_new(uint minsize)
+{
+ MemoryPool *p;
+ if (minsize < 4096 - 12) minsize = 4096 - 12;
+
+ p = malloc(sizeof(MemoryPool) - 1 + minsize);
+ p->pos = 0;
+ p->size = minsize;
+ p->next = NULL;
+ return p;
+}
+
+static void pool_init(MemoryPool **pool)
+{
+ *pool = pool_new(0);
+}
+
+static void *pool_alloc(MemoryPool **pool, uint size)
+{
+ uint pos;
+ MemoryPool *p = *pool;
+
+ size = (size + 3) & ~3; // align everything to a 32 bit boundary
+
+ // first check if there's memory in the next pool
+ if (p->next && p->next->pos + size <= p->next->size) {
+ p = p->next;
+ // then check if there's not memory in the cur pool
+ } else if (p->pos + size > p->size) {
+ MemoryPool *n = pool_new(size);
+ *pool = n;
+ n->next = p;
+ p = n;
+ }
+
+ pos = p->pos;
+ p->pos += size;
+ return p->mem + pos;
+}
+
+static void *pool_strdup(MemoryPool **pool, const char *mem, uint size)
+{
+ byte *p = pool_alloc(pool, size + 1);
+ p[size] = 0;
+ memcpy(p, mem, size);
+ return p;
+}
+
+static void pool_free(MemoryPool **pool)
+{
+ MemoryPool *p = *pool, *n;
+ *pool = NULL;
+ while (p) {
+ n = p->next;
+ free(p);
+ p = n;
+ }
+}
+
+// structs describing the ini format.
+struct IniItem {
+ char *name;
+ char *value;
+ char *comment;
+ IniItem *next;
+};
+
+struct IniGroup {
+ char *name; // name of group
+ char *comment; //comment for group
+ IniItem *item, **last_item;
+ IniGroup *next;
+ IniFile *ini;
+};
+
+struct IniFile {
+ MemoryPool *pool;
+ IniGroup *group, **last_group;
+ char *comment; // last comment in file
+};
+
+// allocate an inifile object
+static IniFile *ini_alloc()
+{
+ IniFile *ini;
+ MemoryPool *pool;
+ pool_init(&pool);
+ ini = (IniFile*)pool_alloc(&pool, sizeof(IniFile));
+ ini->pool = pool;
+ ini->group = NULL;
+ ini->last_group = &ini->group;
+ ini->comment = NULL;
+ return ini;
+}
+
+// allocate an ini group object
+static IniGroup *ini_group_alloc(IniFile *ini, const char *grpt, int len)
+{
+ IniGroup *grp = pool_alloc(&ini->pool, sizeof(IniGroup));
+ grp->ini = ini;
+ grp->name = pool_strdup(&ini->pool, grpt, len);
+ grp->next = NULL;
+ grp->item = NULL;
+ grp->comment = NULL;
+ grp->last_item = &grp->item;
+ *ini->last_group = grp;
+ ini->last_group = &grp->next;
+ return grp;
+}
+
+static IniItem *ini_item_alloc(IniGroup *group, const char *name, int len)
+{
+ IniItem *item = pool_alloc(&group->ini->pool, sizeof(IniItem));
+ item->name = pool_strdup(&group->ini->pool, name, len);
+ item->next = NULL;
+ item->comment = NULL;
+ item->value = NULL;
+ *group->last_item = item;
+ group->last_item = &item->next;
+ return item;
+}
+
+// load an ini file into the "abstract" format
+static IniFile *ini_load(const char *filename)
+{
+ char buffer[1024], c, *s, *t, *e;
+ FILE *in;
+ IniFile *ini;
+ IniGroup *group = NULL;
+ IniItem *item;
+
+ byte *comment = NULL;
+ uint comment_size = 0;
+ uint comment_alloc = 0;
+
+ ini = ini_alloc();
+
+ in = fopen(filename, "r");
+ if (in == NULL) return ini;
+
+ // for each line in the file
+ while (fgets(buffer, sizeof(buffer), in)) {
+
+ // trim whitespace from the left side
+ for(s=buffer; *s == ' ' || *s == '\t'; s++);
+
+ // trim whitespace from right side.
+ e = s + strlen(s);
+ while (e > s && ((c=e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
+ *e = 0;
+
+ // skip comments and empty lines
+ if (*s == '#' || *s == 0) {
+ uint ns = comment_size + (e - s + 1);
+ uint a = comment_alloc;
+ uint pos;
+ // add to comment
+ if (ns > a) {
+ a = max(a, 128);
+ do a*=2; while (a < ns);
+ comment = realloc(comment, comment_alloc = a);
+ }
+ pos = comment_size;
+ comment_size += (e - s + 1);
+ comment[pos + e - s] = '\n'; // comment newline
+ memcpy(comment + pos, s, e - s); // copy comment contents
+ continue;
+ }
+
+ // it's a group?
+ if (s[0] == '[') {
+ if (e[-1] != ']')
+ ShowInfoF("ini: invalid group name '%s'\n", buffer);
+ else
+ e--;
+ s++; // skip [
+ group = ini_group_alloc(ini, s, e - s);
+ if (comment_size) {
+ group->comment = pool_strdup(&ini->pool, comment, comment_size);
+ comment_size = 0;
+ }
+ } else if (group) {
+ // find end of keyname
+ for(t=s; *t != 0 && *t != '=' && *t != '\t' && *t != ' '; t++) {}
+
+ // it's an item in an existing group
+ item = ini_item_alloc(group, s, t-s);
+ if (comment_size) {
+ item->comment = pool_strdup(&ini->pool, comment, comment_size);
+ comment_size = 0;
+ }
+
+ // find start of parameter
+ while (*t == '=' || *t == ' ' || *t == '\t') t++;
+ item->value = pool_strdup(&ini->pool, t, e - t);
+ } else {
+ // it's an orphan item
+ ShowInfoF("ini: '%s' outside of group\n", buffer);
+ }
+ }
+
+ if (comment_size) {
+ ini->comment = pool_strdup(&ini->pool, comment, comment_size);
+ comment_size = 0;
+ }
+
+ free(comment);
+ fclose(in);
+
+ return ini;
+}
+
+// lookup a group or make a new one
+static IniGroup *ini_getgroup(IniFile *ini, const char *name, int len)
+{
+ IniGroup *group;
+
+ if (len == -1) len = strlen(name);
+
+ // does it exist already?
+ for(group = ini->group; group; group = group->next)
+ if (!memcmp(group->name, name, len) && group->name[len] == 0)
+ return group;
+
+ // otherwise make a new one
+ group = ini_group_alloc(ini, name, len);
+ group->comment = pool_strdup(&ini->pool, "\n", 1);
+ return group;
+}
+
+// lookup an item or make a new one
+static IniItem *ini_getitem(IniGroup *group, const char *name, bool create)
+{
+ IniItem *item;
+ uint len = strlen(name);
+
+ for(item = group->item; item; item = item->next)
+ if (!strcmp(item->name, name))
+ return item;
+
+ if (!create) return NULL;
+
+ // otherwise make a new one
+ item = ini_item_alloc(group, name, len);
+ return item;
+}
+
+// save ini file from the "abstract" format.
+static bool ini_save(const char *filename, IniFile *ini)
+{
+ FILE *f;
+ IniGroup *group;
+ IniItem *item;
+
+ f = fopen(filename, "w");
+ if (f == NULL) return false;
+
+ for(group = ini->group; group; group = group->next) {
+ if (group->comment) fputs(group->comment, f);
+ fprintf(f, "[%s]\n", group->name);
+ for(item = group->item; item; item = item->next) {
+ if (item->comment) fputs(item->comment, f);
+ fprintf(f, "%s = %s\n", item->name, item->value ? item->value : "");
+ }
+ }
+ if (ini->comment) fputs(ini->comment, f);
+
+ fclose(f);
+ return true;
+}
+
+static void ini_free(IniFile *ini)
+{
+ pool_free(&ini->pool);
+}
+
+struct SettingDesc {
+ const char *name;
+ int flags;
+ void *def;
+ void *ptr;
+ void *b;
+
+};
+
+static int lookup_oneofmany(const char *many, const char *one, int onelen)
+{
+ const char *s;
+ int idx;
+
+ if (onelen == -1) onelen = strlen(one);
+
+ // check if it's an integer
+ if (*one >= '0' && *one <= '9')
+ return strtoul(one, NULL, 0);
+
+ idx = 0;
+ for(;;) {
+ // find end of item
+ s = many;
+ while (*s != '|' && *s != 0) s++;
+ if (s - many == onelen && !memcmp(one, many, onelen)) return idx;
+ if (*s == 0) return -1;
+ many = s + 1;
+ idx++;
+ }
+}
+
+static uint32 lookup_manyofmany(const char *many, const char *str)
+{
+ const char *s;
+ int r;
+ uint32 res = 0;
+
+ for(;;) {
+ // skip "whitespace"
+ while (*str == ' ' || *str == '\t' || *str == '|') str++;
+ if (*str == 0) break;
+
+ s = str;
+ while (*s != 0 && *s != ' ' && *s != '\t' && *s != '|') s++;
+
+ r = lookup_oneofmany(many, str, s - str);
+ if (r == -1) return (uint32)-1;
+
+ res |= (1 << r);
+ if (*s == 0) break;
+ str = s + 1;
+ }
+ return res;
+}
+
+static int parse_intlist(const char *p, int *items, int maxitems)
+{
+ int n = 0, v;
+ char *end;
+
+ for(;;) {
+ v = strtol(p, &end, 0);
+ if (p == end || n == maxitems) return -1;
+ p = end;
+ items[n++] = v;
+ if (*p == 0) break;
+ if (*p != ',') return -1;
+ p++;
+ }
+
+ return n;
+}
+
+static bool load_intlist(const char *str, void *array, int nelems, int type)
+{
+ int items[64];
+ int i,nitems;
+
+ if (str == NULL) {
+ memset(items, 0, sizeof(items));
+ nitems = nelems;
+ } else {
+ nitems = parse_intlist(str, items, lengthof(items));
+ if (nitems != nelems)
+ return false;
+ }
+
+ switch(type) {
+ case SDT_INT8 >> 4:
+ case SDT_UINT8 >> 4:
+ for(i=0; i!=nitems; i++) ((byte*)array)[i] = items[i];
+ break;
+ case SDT_INT16 >> 4:
+ case SDT_UINT16 >> 4:
+ for(i=0; i!=nitems; i++) ((uint16*)array)[i] = items[i];
+ break;
+ case SDT_INT32 >> 4:
+ case SDT_UINT32 >> 4:
+ for(i=0; i!=nitems; i++) ((uint32*)array)[i] = items[i];
+ break;
+ default:
+ NOT_REACHED();
+ }
+
+ return true;
+}
+
+static void make_intlist(char *buf, void *array, int nelems, int type)
+{
+ int i, v = 0;
+ byte *p = (byte*)array;
+ for(i=0; i!=nelems; i++) {
+ switch(type) {
+ case SDT_INT8 >> 4: v = *(int8*)p; p += 1; break;
+ case SDT_UINT8 >> 4:v = *(byte*)p; p += 1; break;
+ case SDT_INT16 >> 4:v = *(int16*)p; p += 2; break;
+ case SDT_UINT16 >> 4:v = *(uint16*)p; p += 2; break;
+ case SDT_INT32 >> 4:v = *(int32*)p; p += 4; break;
+ case SDT_UINT32 >> 4:v = *(uint32*)p; p += 4; break;
+ default: NOT_REACHED();
+ }
+ buf += sprintf(buf, i ? ",%d" : "%d", v);
+ }
+}
+
+static void make_oneofmany(char *buf, const char *many, int i)
+{
+ int orig_i = i;
+ char c;
+
+ while (--i >= 0) {
+ do {
+ many++;
+ if (many[-1] == 0) {
+ sprintf(buf, "%d", orig_i);
+ return;
+ }
+ } while (many[-1] != '|');
+ }
+
+ // copy until | or 0
+ while ((c=*many++) != 0 && c != '|')
+ *buf++ = c;
+ *buf = 0;
+}
+
+static void make_manyofmany(char *buf, const char *many, uint32 x)
+{
+ const char *start;
+ int i = 0;
+ bool init = true;
+
+ do {
+ start = many;
+ while (*many != 0 && *many != '|') many++;
+ if (x & 1) {
+ if (!init) *buf++ = '|';
+ init = false;
+ if (start == many) {
+ buf += sprintf(buf, "%d", i);
+ } else {
+ memcpy(buf, start, many - start);
+ buf += many - start;
+ }
+ }
+ if (*many == '|') many++;
+ } while (++i, x>>=1);
+ *buf = 0;
+}
+
+static void *string_to_val(const SettingDesc *desc, const char *str)
+{
+ unsigned long val;
+ char *end;
+
+ switch(desc->flags & 0xF) {
+ case SDT_INTX:
+ val = strtol(str, &end, 0);
+ if (*end != 0) ShowInfoF("ini: trailing characters at end of setting '%s'", desc->name);
+ return (void*)val;
+ case SDT_ONEOFMANY: {
+ int r = lookup_oneofmany((char*)desc->b, str, -1);
+ if (r != -1) return (void*)r;
+ ShowInfoF("ini: invalid value '%s' for '%s'", str, desc->name);
+ return 0;
+ }
+ case SDT_MANYOFMANY: {
+ uint32 r = lookup_manyofmany(desc->b, str);
+ if (r != (uint32)-1) return (void*)r;
+ ShowInfoF("ini: invalid value '%s' for '%s'", str, desc->name);
+ return 0;
+ }
+ case SDT_BOOLX:
+ if (!strcmp(str, "true") || !strcmp(str, "on") || !strcmp(str, "1"))
+ return (void*)true;
+ if (!strcmp(str, "false") || !strcmp(str, "off") || !strcmp(str, "0"))
+ return (void*)false;
+ ShowInfoF("ini: invalid setting value '%s' for '%s'", str, desc->name);
+ break;
+
+ case SDT_STRING:
+ case SDT_STRINGBUF:
+ case SDT_INTLIST:
+ return (void*)str;
+ }
+
+ return NULL;
+}
+
+static void load_setting_desc(IniFile *ini, const SettingDesc *desc, void *grpname, void *base)
+{
+ IniGroup *group_def = ini_getgroup(ini, grpname, -1), *group;
+ IniItem *item;
+ void *p;
+ void *ptr;
+
+ for (;desc->name;desc++) {
+ // group override?
+ const char *s = strchr(desc->name, '.');
+ if (s) {
+ group = ini_getgroup(ini, desc->name, s - desc->name);
+ s++;
+ } else {
+ s = desc->name;
+ group = group_def;
+ }
+
+ item = ini_getitem(group, s, false);
+ if (!item) {
+ p = desc->def;
+ } else {
+ p = string_to_val(desc, item->value);
+ }
+
+ // get ptr to array
+ ptr = desc->ptr;
+ if ( (uint32)ptr < 0x10000)
+ ptr = (byte*)base + (uint32)ptr;
+
+ switch(desc->flags & 0xF) {
+ // all these are stored in the same way
+ case SDT_INTX:
+ case SDT_ONEOFMANY:
+ case SDT_MANYOFMANY:
+ case SDT_BOOLX:
+ switch(desc->flags >> 4 & 7) {
+ case SDT_INT8 >> 4:
+ case SDT_UINT8 >> 4:
+ *(byte*)ptr = (byte)(uint)p;
+ break;
+ case SDT_INT16 >> 4:
+ case SDT_UINT16 >> 4:
+ *(uint16*)ptr = (uint16)(uint)p;
+ break;
+ case SDT_INT32 >> 4:
+ case SDT_UINT32 >> 4:
+ *(uint32*)ptr = (uint32)p;
+ break;
+ default:
+ NOT_REACHED();
+ }
+ break;
+ case SDT_STRING:
+ if (*(char**)ptr) free(*(char**)ptr);
+ *(char**)ptr = strdup((char*)p);
+ break;
+ case SDT_STRINGBUF:
+ if (p) ttd_strlcpy((char*)ptr, p, desc->flags >> 16);
+ break;
+ case SDT_INTLIST: {
+ if (!load_intlist(p, ptr, desc->flags >> 16, desc->flags >> 4 & 7))
+ ShowInfoF("ini: error in array '%s'", desc->name);
+ break;
+ }
+ default:
+ NOT_REACHED();
+ }
+ }
+}
+
+static void save_setting_desc(IniFile *ini, const SettingDesc *desc, void *grpname, void *base)
+{
+ IniGroup *group_def = NULL, *group;
+ IniItem *item;
+ void *p, *ptr;
+ int i = 0;
+ char buf[512]; // setting buffer
+ const char *s;
+
+ for (;desc->name;desc++) {
+ if (desc->flags & SDT_NOSAVE)
+ continue;
+
+ // group override?
+ s = strchr(desc->name, '.');
+ if (s) {
+ group = ini_getgroup(ini, desc->name, s - desc->name);
+ s++;
+ } else {
+ if (group_def == NULL)
+ group_def = ini_getgroup(ini, grpname, -1);
+ s = desc->name;
+ group = group_def;
+ }
+
+ item = ini_getitem(group, s, true);
+
+ // get ptr to array
+ ptr = desc->ptr;
+ if ( (uint32)ptr < 0x10000)
+ ptr = (byte*)base + (uint32)ptr;
+
+ if (item->value != NULL) {
+ // check if the value is the same as the old value
+ p = string_to_val(desc, item->value);
+
+ switch(desc->flags & 0xF) {
+ case SDT_INTX:
+ case SDT_ONEOFMANY:
+ case SDT_MANYOFMANY:
+ case SDT_BOOLX:
+ switch(desc->flags >> 4 & 7) {
+ case SDT_INT8 >> 4:
+ case SDT_UINT8 >> 4:
+ if (*(byte*)ptr == (byte)(uint)p)
+ continue;
+ break;
+ case SDT_INT16 >> 4:
+ case SDT_UINT16 >> 4:
+ if (*(uint16*)ptr == (uint16)(uint)p)
+ continue;
+ break;
+ case SDT_INT32 >> 4:
+ case SDT_UINT32 >> 4:
+ if (*(uint32*)ptr == (uint32)p)
+ continue;
+ break;
+ default:
+ NOT_REACHED();
+ }
+ break;
+ case SDT_STRING:
+ assert(0);
+ break;
+ case SDT_INTLIST:
+ // assume intlist is always changed.
+ break;
+ }
+ }
+
+ switch(desc->flags & 0xF) {
+ case SDT_INTX:
+ case SDT_ONEOFMANY:
+ case SDT_MANYOFMANY:
+ case SDT_BOOLX:
+ switch(desc->flags >> 4 & 7) {
+ case SDT_INT8 >> 4: i = *(int8*)ptr; break;
+ case SDT_UINT8 >> 4:i = *(byte*)ptr; break;
+ case SDT_INT16 >> 4:i = *(int16*)ptr; break;
+ case SDT_UINT16 >> 4:i = *(uint16*)ptr; break;
+ case SDT_INT32 >> 4:i = *(int32*)ptr; break;
+ case SDT_UINT32 >> 4:i = *(uint32*)ptr; break;
+ default:
+ NOT_REACHED();
+ }
+ switch(desc->flags & 0xF) {
+ case SDT_INTX:
+ sprintf(buf, "%d", i);
+ break;
+ case SDT_ONEOFMANY:
+ make_oneofmany(buf, (char*)desc->b, i);
+ break;
+ case SDT_MANYOFMANY:
+ make_manyofmany(buf, (char*)desc->b, i);
+ break;
+ case SDT_BOOLX:
+ strcpy(buf, i ? "true" : "false");
+ break;
+ default:
+ NOT_REACHED();
+ }
+ break;
+ case SDT_STRINGBUF:
+ strcpy(buf, (char*)ptr);
+ break;
+ case SDT_STRING:
+ strcpy(buf, *(char**)ptr);
+ break;
+ case SDT_INTLIST:
+ make_intlist(buf, ptr, desc->flags >> 16, desc->flags >> 4 & 7);
+ break;
+ }
+ // the value is different, that means we have to write it to the ini
+ item->value = pool_strdup(&ini->pool, buf, strlen(buf));
+ }
+}
+
+//***************************
+// TTD specific INI stuff
+//***************************
+
+static const SettingDesc music_settings[] = {
+ {"playlist", SDT_UINT8, (void*)0, (void*)offsetof(MusicFileSettings, playlist) },
+ {"music_vol", SDT_UINT8, (void*)128, (void*)offsetof(MusicFileSettings, music_vol) },
+ {"effect_vol", SDT_UINT8, (void*)128, (void*)offsetof(MusicFileSettings, effect_vol) },
+ {"custom_1", SDT_INTLIST | SDT_UINT8 | lengthof(msf.custom_1) << 16, NULL, (void*)offsetof(MusicFileSettings, custom_1) },
+ {"custom_2", SDT_INTLIST | SDT_UINT8 | lengthof(msf.custom_2) << 16, NULL, (void*)offsetof(MusicFileSettings, custom_2) },
+ {"playing", SDT_BOOL, (void*)true, (void*)offsetof(MusicFileSettings, btn_down) },
+ {"shuffle", SDT_BOOL, (void*)false, (void*)offsetof(MusicFileSettings, shuffle) },
+ {NULL}
+};
+
+static const SettingDesc win32_settings[] = {
+ {"display_hz", SDT_UINT, (void*)0, &_display_hz},
+ {"force_full_redraw", SDT_BOOL, (void*)false, &_force_full_redraw},
+ {"fullscreen_bpp", SDT_UINT, (void*)8, &_fullscreen_bpp},
+ {"double_size", SDT_BOOL, (void*)false, &_double_size},
+ {NULL}
+};
+
+static const SettingDesc misc_settings[] = {
+ {"display_opt", SDT_MANYOFMANY | SDT_UINT8, (void*)(DO_SHOW_TOWN_NAMES|DO_SHOW_STATION_NAMES|DO_SHOW_SIGNS|DO_FULL_ANIMATION|DO_FULL_DETAIL|DO_TRANS_BUILDINGS|DO_CHECKPOINTS), &_display_opt, "SHOW_TOWN_NAMES|SHOW_STATION_NAMES|SHOW_SIGNS|FULL_ANIMATION|TRANS_BUILDINGS|FULL_DETAIL|CHECKPOINTS"},
+ {"news_display_opt", SDT_UINT16, (void*)-1, &_news_display_opt},
+ {"fullscreen", SDT_BOOL, (void*)false, &_fullscreen},
+ {"videodriver", SDT_STRINGBUF | (lengthof(_ini_videodriver)<<16) | SDT_NOSAVE, NULL, _ini_videodriver},
+ {"musicdriver", SDT_STRINGBUF | (lengthof(_ini_musicdriver)<<16) | SDT_NOSAVE, NULL, _ini_musicdriver},
+ {"sounddriver", SDT_STRINGBUF | (lengthof(_ini_sounddriver)<<16) | SDT_NOSAVE, NULL, _ini_sounddriver},
+ {"language", SDT_STRINGBUF | lengthof(_dynlang.curr_file)<<16, NULL, _dynlang.curr_file },
+ {"resolution", SDT_UINT16 | SDT_INTLIST | lengthof(_cur_resolution) << 16, "640,480", _cur_resolution},
+ {"cache_sprites", SDT_BOOL, (void*)false, &_cache_sprites},
+ {"screenshot_format", SDT_STRINGBUF | (lengthof(_screenshot_format_name)<<16), NULL, _screenshot_format_name},
+ {"savegame_format", SDT_STRINGBUF | (lengthof(_savegame_format)<<16), NULL, _savegame_format},
+ {"rightclick_emulate", SDT_BOOL, (void*)false, &_rightclick_emulate},
+ {NULL}
+};
+
+static const SettingDesc network_settings[] = {
+ {"port", SDT_UINT | SDT_NOSAVE, (void*)3979, &_network_port},
+ {"sync_freq", SDT_UINT | SDT_NOSAVE, (void*)4, &_network_sync_freq},
+ {"ahead_frames", SDT_UINT | SDT_NOSAVE, (void*)5, &_network_ahead_frames},
+ {NULL}
+};
+
+static const SettingDesc debug_settings[] = {
+ {"savedump_path", SDT_STRINGBUF | (lengthof(_savedump_path)<<16) | SDT_NOSAVE, NULL, _savedump_path},
+ {"savedump_first", SDT_UINT | SDT_NOSAVE, 0, &_savedump_first},
+ {"savedump_freq", SDT_UINT | SDT_NOSAVE, (void*)1, &_savedump_freq},
+ {"savedump_last", SDT_UINT | SDT_NOSAVE, 0, &_savedump_last},
+ {NULL}
+};
+
+
+static const SettingDesc gameopt_settings[] = {
+ {"diff_level", SDT_UINT8, (void*)9, (void*)offsetof(GameOptions, diff_level) },
+ {"diff_custom", SDT_INTLIST | SDT_UINT32 | (sizeof(GameDifficulty)/4) << 16, NULL, (void*)offsetof(GameOptions, diff) },
+ {"currency", SDT_UINT8 | SDT_ONEOFMANY, (void*)0, (void*)offsetof(GameOptions, currency), "GBP|USD|FF|DM|YEN|PT|FT|ZL|ATS|BEF|DKK|FIM|GRD|CHF|NLG|ITL|SEK|RUR|CZK|ISK|NOK|EUR" },
+ {"distances", SDT_UINT8 | SDT_ONEOFMANY, (void*)0, (void*)offsetof(GameOptions, kilometers), "imperial|metric" },
+ {"town_names", SDT_UINT8 | SDT_ONEOFMANY, (void*)0, (void*)offsetof(GameOptions, town_name), "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|hungarian" },
+ {"landscape", SDT_UINT8 | SDT_ONEOFMANY, (void*)0, (void*)offsetof(GameOptions, landscape), "normal|hilly|desert|candy" },
+ {"autosave", SDT_UINT8 | SDT_ONEOFMANY, (void*)1, (void*)offsetof(GameOptions, autosave), "off|monthly|quarterly|half year|yearly" },
+ {"road_side", SDT_UINT8 | SDT_ONEOFMANY, (void*)0, (void*)offsetof(GameOptions, road_side), "left|right" },
+
+ {NULL}
+};
+
+static const SettingDesc patch_settings[] = {
+ {"vehicle_speed", SDT_BOOL, (void*)true, (void*)offsetof(Patches, vehicle_speed) },
+ {"build_on_slopes", SDT_BOOL, (void*)true, (void*)offsetof(Patches, build_on_slopes) },
+ {"mammoth_trains", SDT_BOOL, (void*)true, (void*)offsetof(Patches, mammoth_trains) },
+ {"join_stations", SDT_BOOL, (void*)true, (void*)offsetof(Patches, join_stations) },
+ {"station_spread", SDT_UINT8, (void*)12, (void*)offsetof(Patches, station_spread) },
+ {"full_load_any", SDT_BOOL, (void*)true, (void*)offsetof(Patches, full_load_any)},
+
+ {"inflation", SDT_BOOL, (void*)true, (void*)offsetof(Patches, inflation)},
+ {"no_train_service", SDT_BOOL, (void*)false, (void*)offsetof(Patches, no_train_service)},
+ {"selectgoods", SDT_BOOL, (void*)true, (void*)offsetof(Patches, selectgoods)},
+ {"longbridges", SDT_BOOL, (void*)false, (void*)offsetof(Patches, longbridges)},
+ {"gotodepot", SDT_BOOL, (void*)true, (void*)offsetof(Patches, gotodepot)},
+
+ {"build_rawmaterial_ind", SDT_BOOL, (void*)false, (void*)offsetof(Patches, build_rawmaterial_ind)},
+ {"multiple_industry_per_town", SDT_BOOL, (void*)false, (void*)offsetof(Patches, multiple_industry_per_town)},
+ {"same_industry_close", SDT_BOOL, (void*)false, (void*)offsetof(Patches, same_industry_close)},
+
+ {"lost_train_days", SDT_UINT16, (void*)180, (void*)offsetof(Patches, lost_train_days)},
+ {"train_income_warn", SDT_BOOL, (void*)true, (void*)offsetof(Patches, train_income_warn)},
+
+ {"status_long_date", SDT_BOOL, (void*)true, (void*)offsetof(Patches, status_long_date)},
+ {"signal_side", SDT_BOOL, (void*)true, (void*)offsetof(Patches, signal_side)},
+ {"show_finances", SDT_BOOL, (void*)true, (void*)offsetof(Patches, show_finances)},
+
+ {"new_nonstop", SDT_BOOL, (void*)false, (void*)offsetof(Patches, new_nonstop)},
+ {"roadveh_queue", SDT_BOOL, (void*)false, (void*)offsetof(Patches, roadveh_queue)},
+
+ {"autoscroll", SDT_BOOL, (void*)false, (void*)offsetof(Patches, autoscroll)},
+ {"errmsg_duration", SDT_UINT8, (void*)5, (void*)offsetof(Patches, errmsg_duration)},
+ {"snow_line_height", SDT_UINT8, (void*)7, (void*)offsetof(Patches, snow_line_height)},
+
+ {"bribe", SDT_BOOL, (void*)false, (void*)offsetof(Patches, bribe)},
+ {"new_depot_finding", SDT_BOOL, (void*)false, (void*)offsetof(Patches, new_depot_finding)},
+
+ {"nonuniform_stations", SDT_BOOL, (void*)false, (void*)offsetof(Patches, nonuniform_stations)},
+ {"always_small_airport", SDT_BOOL, (void*)false, (void*)offsetof(Patches, always_small_airport)},
+ {"realistic_acceleration", SDT_BOOL, (void*)false, (void*)offsetof(Patches, realistic_acceleration)},
+
+ {"max_trains", SDT_UINT8, (void*)80,(void*)offsetof(Patches, max_trains)},
+ {"max_roadveh", SDT_UINT8, (void*)80,(void*)offsetof(Patches, max_roadveh)},
+ {"max_aircraft", SDT_UINT8, (void*)40,(void*)offsetof(Patches, max_aircraft)},
+ {"max_ships", SDT_UINT8, (void*)50,(void*)offsetof(Patches, max_ships)},
+
+ {"servint_trains", SDT_UINT16, (void*)150,(void*)offsetof(Patches, servint_trains)},
+ {"servint_roadveh", SDT_UINT16, (void*)150,(void*)offsetof(Patches, servint_roadveh)},
+ {"servint_ships", SDT_UINT16, (void*)360,(void*)offsetof(Patches, servint_ships)},
+ {"servint_aircraft", SDT_UINT16, (void*)100,(void*)offsetof(Patches, servint_aircraft)},
+
+ {"autorenew", SDT_BOOL, (void*)false,(void*)offsetof(Patches, autorenew)},
+ {"autorenew_months", SDT_UINT16, (void*)-12, (void*)offsetof(Patches, autorenew_months)},
+
+ {"new_pathfinding", SDT_BOOL, (void*)false, (void*)offsetof(Patches, new_pathfinding)},
+ {"pf_maxlength", SDT_UINT16, (void*)512, (void*)offsetof(Patches, pf_maxlength)},
+ {"pf_maxdepth", SDT_UINT8, (void*)16, (void*)offsetof(Patches, pf_maxdepth)},
+
+ {"build_in_pause", SDT_BOOL, (void*)false, (void*)offsetof(Patches, build_in_pause)},
+
+ {"ai_disable_veh", SDT_UINT8, (void*)0, (void*)offsetof(Patches, ai_disable_veh)},
+ {"starting_date", SDT_UINT32, (void*)1950, (void*)offsetof(Patches, starting_date)},
+
+ {"colored_news_date", SDT_UINT32, (void*)2000, (void*)offsetof(Patches, colored_news_date)},
+
+ {"bridge_pillars", SDT_BOOL, (void*)true, (void*)offsetof(Patches, bridge_pillars)},
+
+ {"keep_all_autosave", SDT_BOOL, (void*)false, (void*)offsetof(Patches, keep_all_autosave)},
+
+ {"extra_dynamite", SDT_BOOL, (void*)false, (void*)offsetof(Patches, extra_dynamite)},
+
+ {"never_expire_vehicles", SDT_BOOL, (void*)false, (void*)offsetof(Patches, never_expire_vehicles)},
+ {"extend_vehicle_life", SDT_UINT8, (void*)0, (void*)offsetof(Patches, extend_vehicle_life)},
+
+ {"auto_euro", SDT_BOOL, (void*)true, (void*)offsetof(Patches, auto_euro)},
+
+ {"serviceathelipad", SDT_BOOL, (void*)true, (void*)offsetof(Patches, serviceathelipad)},
+ {"smooth_economy", SDT_BOOL, (void*)false, (void*)offsetof(Patches, smooth_economy)},
+ {"dist_local_authority", SDT_UINT8, (void*)20, (void*)offsetof(Patches, dist_local_authority)},
+
+ {"wait_oneway_signal", SDT_UINT8, (void*)15, (void*)offsetof(Patches, wait_oneway_signal)},
+ {"wait_twoway_signal", SDT_UINT8, (void*)41, (void*)offsetof(Patches, wait_twoway_signal)},
+
+ {NULL}
+};
+
+typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, void *grpname, void *base);
+
+static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc)
+{
+ proc(ini, misc_settings, "misc", NULL);
+ proc(ini, win32_settings, "win32", NULL);
+ proc(ini, network_settings, "network", NULL);
+ proc(ini, music_settings, "music", &msf);
+ proc(ini, gameopt_settings, "gameopt", &_new_opt);
+ proc(ini, patch_settings, "patches", &_patches);
+
+ proc(ini, debug_settings, "debug", NULL);
+}
+
+void LoadGrfSettings(IniFile *ini)
+{
+ IniGroup *group = ini_getgroup(ini, "newgrf", -1);
+ IniItem *item;
+ int i;
+
+ if (!group)
+ return;
+
+ for(i=0; i!=lengthof(_newgrf_files); i++) {
+ char buf[3];
+ sprintf(buf, "%d", i);
+ item = ini_getitem(group, buf, false);
+ if (!item)
+ break;
+ _newgrf_files[i] = strdup(item->value);
+ }
+}
+
+void LoadFromConfig()
+{
+ IniFile *ini = ini_load(_config_file);
+ HandleSettingDescs(ini, load_setting_desc);
+ LoadGrfSettings(ini);
+ ini_free(ini);
+}
+
+void SaveToConfig()
+{
+ IniFile *ini = ini_load(_config_file);
+ HandleSettingDescs(ini, save_setting_desc);
+ ini_save(_config_file, ini);
+ ini_free(ini);
+}