diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 6ac22c2685..27c069fe08 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -16,6 +16,7 @@ #include "../tunnelbridge_map.h" #include "../tunnelbridge.h" #include "../station_base.h" +#include "../settings_func.h" #include "saveload.h" @@ -486,6 +487,7 @@ static void Load_PLYR() int index; while ((index = SlIterateArray()) != -1) { Company *c = new (index) Company(); + SetDefaultCompanySettings(c->index); SaveLoad_PLYR(c); _company_colours[index] = (Colours)c->colour; } @@ -529,7 +531,25 @@ static void Ptrs_PLYR() } } +extern void LoadSettingsPlyx(bool skip); +extern void SaveSettingsPlyx(); + +static void Load_PLYX() +{ + LoadSettingsPlyx(false); +} + +static void Check_PLYX() +{ + LoadSettingsPlyx(true); +} + +static void Save_PLYX() +{ + SaveSettingsPlyx(); +} extern const ChunkHandler _company_chunk_handlers[] = { - { 'PLYR', Save_PLYR, Load_PLYR, Ptrs_PLYR, Check_PLYR, CH_ARRAY | CH_LAST}, + { 'PLYR', Save_PLYR, Load_PLYR, Ptrs_PLYR, Check_PLYR, CH_ARRAY }, + { 'PLYX', Save_PLYX, Load_PLYX, NULL, Check_PLYX, CH_RIFF | CH_LAST}, }; diff --git a/src/settings.cpp b/src/settings.cpp index cdac4afc4a..18fbab98c6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2235,10 +2235,10 @@ static void SaveSettings(const SettingDesc *sd, void *object) * * The PATX chunk contents has the following format: * - * uint32 chunk flags + * uint32 chunk flags (unused) * uint32 number of settings * For each of N settings: - * uint32 setting flags + * uint32 setting flags (unused) * SLE_STR setting name * uint32 length of setting field * N bytes setting field @@ -2269,6 +2269,7 @@ static void MakeSettingsPatxList(const SettingDesc *sd) static const SettingDesc *previous = NULL; if (sd == previous) return; + previous = sd; _sorted_patx_settings.clear(); for (const SettingDesc *desc = sd; desc->save.cmd != SL_END; desc++) { @@ -2279,16 +2280,6 @@ static void MakeSettingsPatxList(const SettingDesc *sd) std::sort(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), StringSorter()); } -/** - * Internal structure used in LoadSettingsPatx() - * placed outside for legacy compiler compatibility - */ -struct SettingsPatxLoad { - uint32 flags; - char name[256]; - uint32 setting_length; -}; - /** * Internal structure used in LoadSettingsPatx() * placed outside for legacy compiler compatibility @@ -2308,6 +2299,38 @@ struct StringSearcher { } }; +/** + * Internal structure used in LoadSettingsPatx() and LoadSettingsPlyx() + */ +struct SettingsExtLoad { + uint32 flags; + char name[256]; + uint32 setting_length; +}; + +static const SaveLoad _settings_ext_load_desc[] = { + SLE_VAR(SettingsExtLoad, flags, SLE_UINT32), + SLE_STR(SettingsExtLoad, name, SLE_STRB, 256), + SLE_VAR(SettingsExtLoad, setting_length, SLE_UINT32), + SLE_END() +}; + +/** + * Internal structure used in SaveSettingsPatx() and SaveSettingsPlyx() + */ +struct SettingsExtSave { + uint32 flags; + const char *name; + uint32 setting_length; +}; + +static const SaveLoad _settings_ext_save_desc[] = { + SLE_VAR(SettingsExtSave, flags, SLE_UINT32), + SLE_STR(SettingsExtSave, name, SLE_STR, 0), + SLE_VAR(SettingsExtSave, setting_length, SLE_UINT32), + SLE_END() +}; + /** * Load handler for settings which go in the PATX chunk * @param osd SettingDesc struct containing all information @@ -2318,14 +2341,7 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) { MakeSettingsPatxList(sd); - SettingsPatxLoad current_setting; - - static const SaveLoad _settings_patx_desc[] = { - SLE_VAR(SettingsPatxLoad, flags, SLE_UINT32), - SLE_STR(SettingsPatxLoad, name, SLE_STRB, 256), - SLE_VAR(SettingsPatxLoad, setting_length, SLE_UINT32), - SLE_END() - }; + SettingsExtLoad current_setting; uint32 flags = SlReadUint32(); // flags are not in use yet, reserve for future expansion @@ -2333,7 +2349,7 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) uint32 settings_count = SlReadUint32(); for (uint32 i = 0; i < settings_count; i++) { - SlObject(¤t_setting, _settings_patx_desc); + SlObject(¤t_setting, _settings_ext_load_desc); // flags are not in use yet, reserve for future expansion if (current_setting.flags != 0) SlErrorCorruptFmt("PATX chunk: unknown setting header flags: 0x%X", current_setting.flags); @@ -2377,19 +2393,7 @@ struct SettingToAdd { */ static void SaveSettingsPatx(const SettingDesc *sd, void *object) { - struct SettingsPatxSave { - uint32 flags; - const char *name; - uint32 setting_length; - }; - SettingsPatxSave current_setting; - - static const SaveLoad _settings_patx_desc[] = { - SLE_VAR(SettingsPatxSave, flags, SLE_UINT32), - SLE_STR(SettingsPatxSave, name, SLE_STR, 0), - SLE_VAR(SettingsPatxSave, setting_length, SLE_UINT32), - SLE_END() - }; + SettingsExtSave current_setting; std::vector settings_to_add; @@ -2402,7 +2406,7 @@ static void SaveSettingsPatx(const SettingDesc *sd, void *object) current_setting.name = desc->patx_name; // add length of setting header - length += SlCalcObjLength(¤t_setting, _settings_patx_desc); + length += SlCalcObjLength(¤t_setting, _settings_ext_save_desc); // add length of actual setting length += setting_length; @@ -2421,12 +2425,171 @@ static void SaveSettingsPatx(const SettingDesc *sd, void *object) current_setting.flags = 0; current_setting.name = desc->patx_name; current_setting.setting_length = settings_to_add[i].setting_length; - SlObject(¤t_setting, _settings_patx_desc); + SlObject(¤t_setting, _settings_ext_save_desc); void *ptr = GetVariableAddress(object, &desc->save); SlObjectMember(ptr, &desc->save); } } +/** @file + * + * The PLYX chunk stores additional company settings in an unordered + * format which is tolerant of extra, missing or reordered settings. + * The format is similar to the PATX chunk. + * Additional settings generally means those that aren't in trunk. + * + * The PLYX chunk contents has the following format: + * + * uint32 chunk flags (unused) + * uint32 number of companies + * For each of N companies: + * uint32 company ID + * uint32 company flags (unused) + * uint32 number of settings + * For each of N settings: + * uint32 setting flags (unused) + * SLE_STR setting name + * uint32 length of setting field + * N bytes setting field + */ + +/** + * Load handler for company settings which go in the PLYX chunk + * @param check_mode Whether to skip over settings without reading + */ +void LoadSettingsPlyx(bool skip) +{ + SettingsExtLoad current_setting; + + uint32 chunk_flags = SlReadUint32(); + // flags are not in use yet, reserve for future expansion + if (chunk_flags != 0) SlErrorCorruptFmt("PLYX chunk: unknown chunk header flags: 0x%X", chunk_flags); + + uint32 company_count = SlReadUint32(); + for (uint32 i = 0; i < company_count; i++) { + uint32 company_id = SlReadUint32(); + if (company_id >= MAX_COMPANIES) SlErrorCorruptFmt("PLYX chunk: invalid company ID: %u", company_id); + + const Company *c = NULL; + if (!skip) { + c = Company::GetIfValid(company_id); + if (c == NULL) SlErrorCorruptFmt("PLYX chunk: non-existant company ID: %u", company_id); + } + + uint32 company_flags = SlReadUint32(); + // flags are not in use yet, reserve for future expansion + if (company_flags != 0) SlErrorCorruptFmt("PLYX chunk: unknown company flags: 0x%X", company_flags); + + uint32 settings_count = SlReadUint32(); + for (uint32 j = 0; j < settings_count; j++) { + SlObject(¤t_setting, _settings_ext_load_desc); + + // flags are not in use yet, reserve for future expansion + if (current_setting.flags != 0) SlErrorCorruptFmt("PLYX chunk: unknown setting header flags: 0x%X", current_setting.flags); + + if (skip) { + SlSkipBytes(current_setting.setting_length); + continue; + } + + const SettingDesc *setting = NULL; + + // not many company settings, so perform a linear scan + for (const SettingDesc *desc = _company_settings; desc->save.cmd != SL_END; desc++) { + if (desc->patx_name != NULL && strcmp(desc->patx_name, current_setting.name) == 0) { + setting = desc; + break; + } + } + + if (setting != NULL) { + // found setting + const SaveLoad *sld = &(setting->save); + size_t read = SlGetBytesRead(); + void *ptr = GetVariableAddress(&(c->settings), sld); + SlObjectMember(ptr, sld); + if (SlGetBytesRead() != read + current_setting.setting_length) { + SlErrorCorruptFmt("PLYX chunk: setting read length mismatch for setting: '%s'", current_setting.name); + } + if (IsNumericType(sld->conv)) Write_ValidateSetting(ptr, setting, ReadValue(ptr, sld->conv)); + } else { + DEBUG(sl, 1, "PLYX chunk: Could not find company setting: '%s', ignoring", current_setting.name); + SlSkipBytes(current_setting.setting_length); + } + } + } +} + +/** + * Save handler for settings which go in the PLYX chunk + */ +void SaveSettingsPlyx() +{ + SettingsExtSave current_setting; + + static const SaveLoad _settings_plyx_desc[] = { + SLE_VAR(SettingsExtSave, flags, SLE_UINT32), + SLE_STR(SettingsExtSave, name, SLE_STR, 0), + SLE_VAR(SettingsExtSave, setting_length, SLE_UINT32), + SLE_END() + }; + + std::vector company_setting_counts; + + size_t length = 8; + uint32 companies_count = 0; + + Company *c; + FOR_ALL_COMPANIES(c) { + length += 12; + companies_count++; + uint32 setting_count = 0; + for (const SettingDesc *desc = _company_settings; desc->save.cmd != SL_END; desc++) { + if (desc->patx_name == NULL) continue; + uint32 setting_length = SlCalcObjMemberLength(&(c->settings), &desc->save); + if (!setting_length) continue; + + current_setting.name = desc->patx_name; + + // add length of setting header + length += SlCalcObjLength(¤t_setting, _settings_ext_save_desc); + + // add length of actual setting + length += setting_length; + + setting_count++; + } + company_setting_counts.push_back(setting_count); + } + SlSetLength(length); + + SlWriteUint32(0); // flags + SlWriteUint32(companies_count); // companies count + + size_t index = 0; + FOR_ALL_COMPANIES(c) { + length += 12; + companies_count++; + SlWriteUint32(c->index); // company ID + SlWriteUint32(0); // flags + SlWriteUint32(company_setting_counts[index]); // setting count + index++; + + for (const SettingDesc *desc = _company_settings; desc->save.cmd != SL_END; desc++) { + if (desc->patx_name == NULL) continue; + uint32 setting_length = SlCalcObjMemberLength(&(c->settings), &desc->save); + if (!setting_length) continue; + + current_setting.flags = 0; + current_setting.name = desc->patx_name; + current_setting.setting_length = setting_length; + SlObject(¤t_setting, _settings_plyx_desc); + void *ptr = GetVariableAddress(&(c->settings), &desc->save); + SlObjectMember(ptr, &desc->save); + } + } +} + static void Load_OPTS() { /* Copy over default setting since some might not get loaded in diff --git a/src/table/company_settings.ini b/src/table/company_settings.ini index e49f4094fa..8296890a74 100644 --- a/src/table/company_settings.ini +++ b/src/table/company_settings.ini @@ -18,8 +18,8 @@ static const SettingDesc _company_settings[] = { [post-amble] }; [templates] -SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, NULL), -SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, NULL), +SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), +SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), SDT_END = SDT_END() [defaults] @@ -35,6 +35,7 @@ from = 0 to = SL_MAX_VERSION cat = SC_ADVANCED extver = SlXvFeatureTest() +patxname = NULL