# HG changeset patch # User Darkvater # Date 2006-03-01 23:53:20 # Node ID f1eba3dacb95462681c4ea81c75281c87a6b43d7 # Parent 822c88e2fee873fd035d667eee5d4e3772e1a973 (svn r3719) - [1/4] Present the game with a unified structure for the configuration-ini, saveload, console and gui representations of the settings. This first part rewrites the configuration section to use the SaveLoad VarType in general. - This unified structure consists of a SaveLoad type which stores all data relevant about the variable internals such as type, mem/filesize, address, version-control. The SettingDesc type is concerned more about the representation. Things like default value, string-name, minimum/maximum values, gui-behaviour etc. - The SaveLoad type has received a few flags controlling saving/loading. These are: SLF_SAVE_NO: the setting is not saved with the savegame, effectively making this setting player-based. Eg. it will NOT be overwritten when joining a network-game SLF_CONFIG_NO: the setting is not saved to the configuration file so you cannot overwrite it ingame. SLF_NETWORK_NO: the setting is not synchronised with the local settings when the game is loaded during network-play. Note that when SLF_SAVE_NO is set, SLF_NETWORK_NO is also set (which is logical), at least if the proper macros are used (in [2/4]). - NOTE! The game is not compilable after this commit. diff --git a/saveload.h b/saveload.h --- a/saveload.h +++ b/saveload.h @@ -126,6 +126,13 @@ enum VarTypes { SLE_INT = SLE_INT32, SLE_STRB = SLE_STRINGBUF, SLE_STRQ = SLE_STRINGQUOTE, + + /* 8 bytes allocated for a maximum of 8 flags + * Flags directing saving/loading of a variable */ + SLF_SAVE_NO = 1 << 8, ///< do not save with savegame, basically player-based + SLF_CONFIG_NO = 1 << 9, ///< do not save to config file + SLF_NETWORK_NO = 1 << 10, ///< do not synchronize over network (but it is saved if SSF_SAVE_NO is not set) + /* 5 more possible flags */ }; typedef uint32 VarType; diff --git a/settings.c b/settings.c --- a/settings.c +++ b/settings.c @@ -413,7 +413,7 @@ static int parse_intlist(const char *p, * @param nelems the number of elements the array holds. Maximum is 64 elements * @param type the type of elements the array holds (eg INT8, UINT16, etc.) * @return return true on success and false on error */ -static bool load_intlist(const char *str, void *array, int nelems, int type) +static bool load_intlist(const char *str, void *array, int nelems, VarType type) { int items[64]; int i, nitems; @@ -427,16 +427,17 @@ static bool load_intlist(const char *str } switch (type) { - case SDT_INT8: - case SDT_UINT8: + case SLE_VAR_BL: + case SLE_VAR_I8: + case SLE_VAR_U8: for (i = 0; i != nitems; i++) ((byte*)array)[i] = items[i]; break; - case SDT_INT16: - case SDT_UINT16: + case SLE_VAR_I16: + case SLE_VAR_U16: for (i = 0; i != nitems; i++) ((uint16*)array)[i] = items[i]; break; - case SDT_INT32: - case SDT_UINT32: + case SLE_VAR_I32: + case SLE_VAR_U32: for (i = 0; i != nitems; i++) ((uint32*)array)[i] = items[i]; break; default: NOT_REACHED(); @@ -451,19 +452,20 @@ static bool load_intlist(const char *str * @param array pointer to the integer-arrays that is read from * @param nelems the number of elements the array holds. * @param type the type of elements the array holds (eg INT8, UINT16, etc.) */ -static void make_intlist(char *buf, const void *array, int nelems, int type) +static void make_intlist(char *buf, const void *array, int nelems, VarType type) { int i, v = 0; const byte *p = (const byte*)array; for (i = 0; i != nelems; i++) { switch (type) { - case SDT_INT8: v = *(int8*)p; p += 1; break; - case SDT_UINT8: v = *(byte*)p; p += 1; break; - case SDT_INT16: v = *(int16*)p; p += 2; break; - case SDT_UINT16: v = *(uint16*)p; p += 2; break; - case SDT_INT32: v = *(int32*)p; p += 4; break; - case SDT_UINT32: v = *(uint32*)p; p += 4; break; + case SLE_VAR_BL: + case SLE_VAR_I8: v = *(int8*)p; p += 1; break; + case SLE_VAR_U8: v = *(byte*)p; p += 1; break; + case SLE_VAR_I16: v = *(int16*)p; p += 2; break; + case SLE_VAR_U16: v = *(uint16*)p; p += 2; break; + case SLE_VAR_I32: v = *(int32*)p; p += 4; break; + case SLE_VAR_U32: v = *(uint32*)p; p += 4; break; default: NOT_REACHED(); } buf += sprintf(buf, (i == 0) ? "%d" : ",%d", v); @@ -526,29 +528,13 @@ static void make_manyofmany(char *buf, c *buf = '\0'; } -/* Get the GenericType of a setting. This describes the main type - * @param desc pointer to SettingDesc structure - * @return return GenericType, see SettingDescType */ -static inline int GetSettingGenericType(const SettingDesc *desc) -{ - return desc->flags & 0xFF00; // GB(desc->flags, 8, 8) << 8; -} - -/* Get the NumberType of a setting. This describes the integer type - * @param desc pointer to SettingDesc structure - * @return return NumberType, see SettingDescType */ -static inline int GetSettingNumberType(const SettingDesc *desc) -{ - return desc->flags & 0xF0; // GB(desc->flags, 4, 8); << 4 -} - /** Convert a string representation (external) of a setting to the internal rep. * @param desc SettingDesc struct that holds all information about the variable * @param str input string that will be parsed based on the type of desc * @return return the parsed value of the setting */ -static const void *string_to_val(const SettingDesc *desc, const char *str) +static const void *string_to_val(const SettingDescBase *desc, const char *str) { - switch (GetSettingGenericType(desc)) { + switch (desc->cmd) { case SDT_NUMX: { char *end; unsigned long val = strtoul(str, &end, 0); @@ -575,81 +561,89 @@ static const void *string_to_val(const S ShowInfoF("ini: invalid setting value '%s' for '%s'", str, desc->name); break; - case SDT_STR: - case SDT_STRB: - case SDT_STRQ: - case SDT_INTLIST: - case SDT_CHAR: - return str; + case SDT_STRING: + case SDT_INTLIST: return str; } return NULL; } +/** Set the value of a setting and if needed clamp the value to + * the preset minimum and maximum. + * @param ptr the variable itself + * @param sd pointer to the 'information'-database of the variable + * @param val signed long version of the new value + * @pre SettingDesc is of type SDT_BOOLX, SDT_NUMX, + * SDT_ONEOFMANY or SDT_MANYOFMANY. Other types are not supported as of now */ +static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val) +{ + const SettingDescBase *sdb = &sd->desc; + int32 min; + + if (sdb->cmd != SDT_BOOLX && sdb->cmd != SDT_NUMX && + sdb->cmd != SDT_ONEOFMANY && sdb->cmd != SDT_MANYOFMANY) return; + + /* Override the minimum value. No value below sdb->min, except special value 0 */ + min = ((sdb->flags & SGF_0ISDISABLED) && val <= sdb->min) ? 0 : sdb->min; + /* We cannot know the maximum value of a bitset variable, so just have faith */ + val = (sdb->cmd == SDT_MANYOFMANY) ? val : clamp(val, min, sdb->max); + + WriteValue(ptr, sd->save.conv, (int64)val); +} + /** Load values from a group of an IniFile structure into the internal representation * @param ini pointer to IniFile structure that holds administrative information * @param desc pointer to SettingDesc structure whose internally pointed variables will * be given values * @param grpname the group of the IniFile to search in for the new values */ -static void load_setting_desc(IniFile *ini, const SettingDesc *desc, const char *grpname) +static void ini_load_settings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object) { - IniGroup *group_def = ini_getgroup(ini, grpname, -1), *group; + IniGroup *group; + IniGroup *group_def = ini_getgroup(ini, grpname, -1); IniItem *item; const void *p; void *ptr; + const char *s; - for (;desc->name != NULL; desc++) { + for (; sd->save.cmd != SL_END; sd++) { + const SettingDescBase *sdb = &sd->desc; + const SaveLoad *sld = &sd->save; + // XXX - wtf is this?? (group override?) - const char *s = strchr(desc->name, '.'); + s = strchr(sdb->name, '.'); if (s != NULL) { - group = ini_getgroup(ini, desc->name, s - desc->name); + group = ini_getgroup(ini, sdb->name, s - sdb->name); s++; } else { - s = desc->name; + s = sdb->name; group = group_def; } item = ini_getitem(group, s, false); - p = (item == NULL) ? desc->def : string_to_val(desc, item->value); + p = (item == NULL) ? sdb->def : string_to_val(sdb, item->value); + ptr = ini_get_variable(sld, object); - /* get pointer to the variable */ - ptr = desc->ptr; - - /* The main type of a variable/setting is in bytes 8-15 - * The subtype (what kind of numbers do we have there) is in 0-7 */ - switch (GetSettingGenericType(desc)) { + switch (sdb->cmd) { case SDT_BOOLX: /* All four are various types of (integer) numbers */ case SDT_NUMX: case SDT_ONEOFMANY: case SDT_MANYOFMANY: - switch (GetSettingNumberType(desc)) { - case SDT_INT8: - case SDT_UINT8: *(byte*)ptr = (byte)(unsigned long)p; break; - case SDT_INT16: - case SDT_UINT16: *(uint16*)ptr = (uint16)(unsigned long)p; break; - case SDT_INT32: - case SDT_UINT32: *(uint32*)ptr = (uint32)(unsigned long)p; break; - default: NOT_REACHED(); break; + Write_ValidateSetting(ptr, sd, (unsigned long)p); break; + + case SDT_STRING: + switch (GetVarMemType(sld->conv)) { + case SLE_VAR_STRB: + case SLE_VAR_STRQ: + if (p != NULL) ttd_strlcpy((char*)ptr, p, sld->length); + break; + case SLE_VAR_CHAR: *(char*)ptr = *(char*)p; break; + default: NOT_REACHED(); break; } break; - case SDT_STR: - free(*(char**)ptr); - *(char**)ptr = strdup((const char*)p); - break; - - case SDT_STRB: - case SDT_STRQ: - if (p != NULL) ttd_strlcpy((char*)ptr, p, GB(desc->flags, 16, 16)); - break; - - case SDT_CHAR: - *(char*)ptr = *(char*)p; - break; - case SDT_INTLIST: { - if (!load_intlist(p, ptr, GB(desc->flags, 16, 16), GetSettingNumberType(desc))) - ShowInfoF("ini: error in array '%s'", desc->name); + if (!load_intlist(p, ptr, sld->length, GetVarMemType(sld->conv))) + ShowInfoF("ini: error in array '%s'", sdb->name); break; } default: NOT_REACHED(); break; @@ -665,115 +659,98 @@ static void load_setting_desc(IniFile *i * The function works as follows: for each item in the SettingDesc structure we have * a look if the value has changed since we started the game (the original values * are reloaded when saving). If settings indeed have changed, we get these and save them.*/ -static void save_setting_desc(IniFile *ini, const SettingDesc *desc, const char *grpname) +static void ini_save_settings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object) { IniGroup *group_def = NULL, *group; IniItem *item; - const void *p; + char buf[512]; + const char *s; void *ptr; - uint32 i = 0; - char buf[512]; // setting buffer - const char *s; - for (; desc->name != NULL; desc++) { - if (desc->flags & SDT_NOSAVE) continue; + for (; sd->save.cmd != SL_END; sd++) { + const SettingDescBase *sdb = &sd->desc; + const SaveLoad *sld = &sd->save; + + /* If the setting is not saved to the configuration + * file, just continue with the next setting */ + if (sld->conv & SLF_CONFIG_NO) continue; // XXX - wtf is this?? (group override?) - s = strchr(desc->name, '.'); + s = strchr(sdb->name, '.'); if (s != NULL) { - group = ini_getgroup(ini, desc->name, s - desc->name); + group = ini_getgroup(ini, sdb->name, s - sdb->name); s++; } else { if (group_def == NULL) group_def = ini_getgroup(ini, grpname, -1); - s = desc->name; + s = sdb->name; group = group_def; } item = ini_getitem(group, s, true); - - /* get pointer to the variable */ - ptr = desc->ptr; + ptr = ini_get_variable(sld, object); if (item->value != NULL) { // check if the value is the same as the old value - p = string_to_val(desc, item->value); + const void *p = string_to_val(sdb, item->value); /* The main type of a variable/setting is in bytes 8-15 * The subtype (what kind of numbers do we have there) is in 0-7 */ - switch (GetSettingGenericType(desc)) { + switch (sdb->cmd) { case SDT_BOOLX: case SDT_NUMX: case SDT_ONEOFMANY: case SDT_MANYOFMANY: - switch (GetSettingNumberType(desc)) { - case SDT_INT8: - case SDT_UINT8: + switch (GetVarMemType(sld->conv)) { + case SLE_VAR_BL: + case SLE_VAR_I8: + case SLE_VAR_U8: if (*(byte*)ptr == (byte)(unsigned long)p) continue; break; - case SDT_INT16: - case SDT_UINT16: + case SLE_VAR_I16: + case SLE_VAR_U16: if (*(uint16*)ptr == (uint16)(unsigned long)p) continue; break; - case SDT_INT32: - case SDT_UINT32: + case SLE_VAR_I32: + case SLE_VAR_U32: if (*(uint32*)ptr == (uint32)(unsigned long)p) continue; break; default: NOT_REACHED(); } break; - case SDT_STR: assert(0); break; default: break; /* Assume the other types are always changed */ } } /* Value has changed, get the new value and put it into a buffer */ - switch (GetSettingGenericType(desc)) { + switch (sdb->cmd) { case SDT_BOOLX: case SDT_NUMX: case SDT_ONEOFMANY: - case SDT_MANYOFMANY: - switch (GetSettingNumberType(desc)) { - case SDT_INT8: i = *(int8*)ptr; break; - case SDT_UINT8: i = *(byte*)ptr; break; - case SDT_INT16: i = *(int16*)ptr; break; - case SDT_UINT16: i = *(uint16*)ptr; break; - case SDT_INT32: i = *(int32*)ptr; break; - case SDT_UINT32: i = *(uint32*)ptr; break; + case SDT_MANYOFMANY: { + uint32 i = (uint32)ReadValue(ptr, sld->conv); + + switch (sdb->cmd) { + case SDT_BOOLX: strcpy(buf, (i != 0) ? "true" : "false"); break; + case SDT_NUMX: sprintf(buf, "%u", i); break; + case SDT_ONEOFMANY: make_oneofmany(buf, sdb->many, i); break; + case SDT_MANYOFMANY: make_manyofmany(buf, sdb->many, i); break; default: NOT_REACHED(); } + } break; - switch (GetSettingGenericType(desc)) { - case SDT_BOOLX: - strcpy(buf, (i != 0) ? "true" : "false"); - break; - case SDT_NUMX: - sprintf(buf, "%u", i); - break; - case SDT_ONEOFMANY: - make_oneofmany(buf, desc->many, i); - break; - case SDT_MANYOFMANY: - make_manyofmany(buf, desc->many, i); - break; + case SDT_STRING: + switch (GetVarMemType(sld->conv)) { + case SLE_VAR_STRB: strcpy(buf, (char*)ptr); break; + case SLE_VAR_STRQ: sprintf(buf, "\"%s\"", (char*)ptr); break; + case SLE_VAR_CHAR: sprintf(buf, "\"%c\"", *(char*)ptr); break; default: NOT_REACHED(); } break; - case SDT_STR: - strcpy(buf, *(char**)ptr); - break; - case SDT_STRB: - strcpy(buf, (char*)ptr); + case SDT_INTLIST: + make_intlist(buf, ptr, sld->length, GetVarMemType(sld->conv)); break; - case SDT_STRQ: - sprintf(buf, "\"%s\"", (char*)ptr); - break; - case SDT_CHAR: - sprintf(buf, "\"%c\"", *(char*)ptr); - break; - case SDT_INTLIST: - make_intlist(buf, ptr, GB(desc->flags, 16, 16), GetSettingNumberType(desc)); - break; + default: NOT_REACHED(); } /* The value is different, that means we have to write it to the ini */ @@ -781,6 +758,49 @@ static void save_setting_desc(IniFile *i } } +// loads all items from a *grpname section into the **list +static void ini_load_setting_list(IniFile *ini, const char *grpname, char **list, uint len) +{ + IniGroup *group = ini_getgroup(ini, grpname, -1); + IniItem *item; + uint i; + + if (group == NULL) return; + + item = group->item; + for (i = 0; i != len; i++) { + if (item == NULL) break; + list[i] = strdup(item->value); + item = item->next; + } +} + +static void ini_save_setting_list(IniFile *ini, const char *grpname, char **list, uint len) +{ + IniGroup *group = ini_getgroup(ini, grpname, -1); + IniItem *item = NULL; + uint i; + bool first = true; + + if (group == NULL) return; + group->item = NULL; + + for (i = 0; i != len; i++) { + if (list[i] == NULL || list[i][0] == '\0') continue; + + if (first) { // add first item to the head of the group + item = ini_item_alloc(group, list[i], strlen(list[i])); + item->value = item->name; + group->item = item; + first = false; + } else { // all other items are attached to the previous one + item->next = ini_item_alloc(group, list[i], strlen(list[i])); + item = item->next; + item->value = item->name; + } + } +} + //*************************** // OTTD specific INI stuff //*************************** @@ -1084,7 +1104,8 @@ static const SettingDesc currency_settin { NULL, 0, NULL, NULL, NULL } }; -typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const char *grpname); +typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const char *grpname, void *object); +typedef void SettingDescProcList(IniFile *ini, const char *grpname, char **list, uint len); static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc) { @@ -1102,49 +1123,6 @@ static void HandleSettingDescs(IniFile * proc(ini, currency_settings,"currency"); } -// loads all items from a *grpname section into the **list -static void LoadList(IniFile *ini, const char *grpname, char **list, int len) -{ - IniGroup *group = ini_getgroup(ini, grpname, -1); - IniItem *item; - int i; - - if (!group) - return; - item = group->item; - for ( i=0; i != len; i++) { - if (item == NULL) break; - list[i] = strdup(item->value); - item = item->next; - } -} - -static void SaveList(IniFile *ini, const char *grpname, char **list, int len) -{ - IniGroup *group = ini_getgroup(ini, grpname, -1); - IniItem *item = NULL; - int i; - bool first = true; - - if (group == NULL) return; - group->item = NULL; - - for (i = 0; i != len; i++) { - if (list[i] == NULL || list[i][0] == '\0') continue; - - if (first) { // add first item to the head of the group - item = ini_item_alloc(group, list[i], strlen(list[i])); - item->value = item->name; - group->item = item; - first = false; - } else { // all other items are attached to the previous one - item->next = ini_item_alloc(group, list[i], strlen(list[i])); - item = item->next; - item->value = item->name; - } - } -} - void LoadFromConfig(void) { IniFile *ini = ini_load(_config_file); diff --git a/settings.h b/settings.h --- a/settings.h +++ b/settings.h @@ -3,61 +3,77 @@ #ifndef SETTINGS_H #define SETTINGS_H -/* Convention/Type of settings. This will be merged mostly with the SaveLoad - * SLE_ enums. So it looks a bit strange. The layout is as follows: - * bits 0-7: the type (size) of the variable. Eg int8, uint8, bool, etc. Same as VarTypes - * bits 8-15: the generic variable type. Eg string, oneofmany, number, intlist - * bits 16-31: the size of a string, an intlist (which is an implicit array). */ -/* XXX - the GenericType will NOT be shifted in the final implementation, just for compatility */ +#include "saveload.h" + +/** Convention/Type of settings. This is then further specified if necessary + * with the SLE_ (SLE_VAR/SLE_FILE) enums in saveload.h + * @see VarTypes + * @see SettingDescBase */ enum SettingDescType { /* 4 bytes allocated a maximum of 16 types for GenericType */ - SDT_NUMX = 0 << 8, // value must be 0!!, refers to any number-type - SDT_BOOLX = 1 << 8, // a boolean number - SDT_ONEOFMANY = 2 << 8, // bitmasked number where only ONE bit may be set - SDT_MANYOFMANY = 3 << 8, // bitmasked number where MULTIPLE bits may be set - SDT_INTLIST = 4 << 8, // list of integers seperated by a comma ',' - SDT_STRING = 5 << 8, // string which is only a pointer, so needs dynamic allocation - SDT_STRINGBUF = 6 << 8, // string with a fixed length, preset buffer - SDT_STRINGQUOT = 7 << 8, // string with quotation marks around it (enables spaces in string) - SDT_CHAR = 8 << 8, // single character - /* 7 more possible primitives */ + SDT_NUMX = 0, // any number-type + SDT_BOOLX = 1, // a boolean number + SDT_ONEOFMANY = 2, // bitmasked number where only ONE bit may be set + SDT_MANYOFMANY = 3, // bitmasked number where MULTIPLE bits may be set + SDT_INTLIST = 4, // list of integers seperated by a comma ',' + SDT_STRING = 5, // string with a pre-allocated buffer + /* 10 more possible primitives */ +}; - /* 4 bytes allocated a maximum of 16 types for NumberType */ - 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_INT64 = 6 << 4, - SDT_UINT64 = 7 << 4, - /* 8 more possible primitives */ - - /* Shortcut values */ - SDT_BOOL = SDT_BOOLX | SDT_UINT8, - SDT_UINT = SDT_UINT32, - SDT_INT = SDT_INT32, - SDT_STR = SDT_STRING, - SDT_STRB = SDT_STRINGBUF, - SDT_STRQ = SDT_STRINGQUOT, - - /* The value is read from the configuration file but not saved */ - SDT_NOSAVE = 1 << 31, +enum SettingGuiFlag { + /* 8 bytes allocated for a maximum of 8 flags + * Flags directing saving/loading of a variable */ + SGF_0ISDISABLED = 1 << 0, ///< a value of zero means the feature is disabled + SGF_NOCOMMA = 1 << 1, ///< number without any thousand seperators (no formatting) + SGF_MULTISTRING = 1 << 2, ///< the value represents a limited number of string-options (internally integer) + SGF_NETWORK_ONLY = 1 << 3, ///< this setting only applies to network games + SGF_CURRENCY = 1 << 4, ///< the number represents money, so when reading value multiply by exchange rate + /* 3 more possible flags */ }; -typedef enum { - IGT_VARIABLES = 0, // values of the form "landscape = hilly" - IGT_LIST = 1, // a list of values, seperated by \n and terminated by the next group block -} IniGroupType; +typedef int32 OnChange(int32 var); +typedef byte SettingDescType; +typedef byte SettingGuiFlag; + +typedef struct SettingDescBase { + const char *name; ///< name of the setting. Used in configuration file and for console + const void *def; ///< default value given when none is present + SettingDescType cmd; ///< various flags for the variable + SettingGuiFlag flags; ///< handles how a setting would show up in the GUI (text/currency, etc.) + int32 min, max; ///< minimum and maximum values + const char *many; ///< ONE/MANY_OF_MANY: string of possible values for this type + StringID str; ///< (translated) string with descriptive text; gui and console + OnChange *proc; ///< callback procedure for when the value is changed +} SettingDescBase; typedef struct SettingDesc { - const char *name; - uint32 flags; - const void *def; - void *ptr; - const void *many; + SettingDescBase desc; ///, Settings structure (going to configuration file) + SaveLoad save; ///< Internal structure (going to savegame, parts to config) } SettingDesc; +/* NOTE: The only difference between SettingDesc and SettingDescGlob is + * that one uses global variables as a source and the other offsets + * in a struct which are bound to a certain variable during runtime. + * The only way to differentiate between these two is to check if an object + * has been passed to the function or not. If not, then it is a global variable + * and save->variable has its address, otherwise save->variable only holds the + * offset in a certain struct */ +typedef SettingDesc SettingDescGlobVarList; + +typedef enum { + IGT_VARIABLES = 0, ///< values of the form "landscape = hilly" + IGT_LIST = 1, ///< a list of values, seperated by \n and terminated by the next group block +} IniGroupType; + +/** Get the address of the variable. Which one to pick depends on the object + * pointer. If it is NULL we are dealing with global variables so the address + * is taken. If non-null only the offset is stored in the union and we need + * to add this to the address of the object */ +static inline void *ini_get_variable(const SaveLoad *sld, const void *object) +{ + return (object == NULL) ? sld->address : (byte*)object + (unsigned long)sld->address; +} + void IConsoleSetPatchSetting(const char *name, const char *value); void IConsoleGetPatchSetting(const char *name);