From 659b6b73378ce8292c1f4999e7f92d33bc5ca6cd Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Wed, 26 Aug 2015 19:00:24 +0100 Subject: [PATCH 01/12] PATX settings: Various changes to support legacy compilers. --- src/settings.cpp | 83 +++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 203dccbea0..cdac4afc4a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2247,6 +2247,18 @@ static void SaveSettings(const SettingDesc *sd, void *object) /** Sorted list of PATX settings, generated by MakeSettingsPatxList */ static std::vector _sorted_patx_settings; +/** + * Internal structure used in LoadSettingsPatx() + * placed outside for legacy compiler compatibility + * this makes me miss lambdas :/ + */ +struct StringSorter { + bool operator()(const SettingDesc *a, const SettingDesc *b) + { + return strcmp(a->patx_name, b->patx_name) < 0; + } +}; + /** * Prepare a sorted list of settings to be potentially be loaded out of the PATX chunk * This is to enable efficient lookup of settings by name @@ -2264,16 +2276,38 @@ static void MakeSettingsPatxList(const SettingDesc *sd) _sorted_patx_settings.push_back(desc); } - // this makes me miss lambdas :/ - struct StringSorter { - bool operator()(const SettingDesc *a, const SettingDesc *b) - { - return strcmp(a->patx_name, b->patx_name) < 0; - } - }; 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 + * this is effectively a reference capture lambda + */ +struct StringSearcher { + bool &m_exact_match; + + StringSearcher(bool &exact_match) + : m_exact_match(exact_match) { } + + bool operator()(const SettingDesc *a, const char *b) + { + int result = strcmp(a->patx_name, b); + if (result == 0) m_exact_match = true; + return result < 0; + } +}; + /** * Load handler for settings which go in the PATX chunk * @param osd SettingDesc struct containing all information @@ -2284,11 +2318,6 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) { MakeSettingsPatxList(sd); - struct SettingsPatxLoad { - uint32 flags; - char name[256]; - uint32 setting_length; - }; SettingsPatxLoad current_setting; static const SaveLoad _settings_patx_desc[] = { @@ -2311,19 +2340,6 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) // now try to find corresponding setting, this would be much easier with C++11 support... bool exact_match = false; - struct StringSearcher { - bool &m_exact_match; - - StringSearcher(bool &exact_match) - : m_exact_match(exact_match) { } - - bool operator()(const SettingDesc *a, const char *b) - { - int result = strcmp(a->patx_name, b); - if (result == 0) m_exact_match = true; - return result < 0; - } - }; std::vector::iterator iter = std::lower_bound(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), current_setting.name, StringSearcher(exact_match)); if (exact_match) { @@ -2344,6 +2360,15 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) } } +/** + * Internal structure used in SaveSettingsPatx() + * placed outside for legacy compiler compatibility + */ +struct SettingToAdd { + const SettingDesc *setting; + uint32 setting_length; +}; + /** * Save handler for settings which go in the PATX chunk * @param sd SettingDesc struct containing all information @@ -2366,10 +2391,6 @@ static void SaveSettingsPatx(const SettingDesc *sd, void *object) SLE_END() }; - struct SettingToAdd { - const SettingDesc *setting; - uint32 setting_length; - }; std::vector settings_to_add; size_t length = 8; @@ -2386,7 +2407,9 @@ static void SaveSettingsPatx(const SettingDesc *sd, void *object) // add length of actual setting length += setting_length; - settings_to_add.push_back({ desc, setting_length }); + // duplicate copy made for compiler backwards compatibility + SettingToAdd new_setting = { desc, setting_length }; + settings_to_add.push_back(new_setting); } SlSetLength(length); From ddcb3bf560cfff6c0f9c56615b001d23ca93b41c Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 11 Sep 2015 15:16:19 +0100 Subject: [PATCH 02/12] Update project files. --- projects/openttd_vs140.vcxproj | 2 ++ projects/openttd_vs140.vcxproj.filters | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj index 88f7b44ddd..b82aba741c 100644 --- a/projects/openttd_vs140.vcxproj +++ b/projects/openttd_vs140.vcxproj @@ -886,6 +886,8 @@ + + diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters index 06800ffdaf..0b6dc573b4 100644 --- a/projects/openttd_vs140.vcxproj.filters +++ b/projects/openttd_vs140.vcxproj.filters @@ -1836,6 +1836,12 @@ Save/Load handlers + + Save/Load handlers + + + Save/Load handlers + Tables From 86dea90c4f46ed418e1c8e6ad7a45664a9337768 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 11 Sep 2015 19:44:16 +0100 Subject: [PATCH 03/12] save_ext: Initial support for save/load of RIFF chunk sizes > 28 bits. Add a chunk extension header with a flag for increased RIFF size. --- src/saveload/saveload.cpp | 63 +++++++++++++++++++++++++++++++++++++-- src/saveload/saveload.h | 7 +++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 74f6fba1e2..6fe2a8e636 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -888,9 +888,20 @@ void SlSetLength(size_t length) case CH_RIFF: /* Ugly encoding of >16M RIFF chunks * The lower 24 bits are normal - * The uppermost 4 bits are bits 24:27 */ - assert(length < (1 << 28)); + * The uppermost 4 bits are bits 24:27 + * + * If we have more than 28 bits, use an extra uint32 and + * signal this using the extended chunk header */ + assert(length < (1LL << 32)); + if (length >= (1 << 28)) { + /* write out extended chunk header */ + SlWriteByte(CH_EXT_HDR); + SlWriteUint32(static_cast(SLCEHF_BIG_RIFF)); + } SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28))); + if (length >= (1 << 28)) { + SlWriteUint32(length >> 28); + } break; case CH_ARRAY: assert(_sl.last_array_index <= _sl.array_index); @@ -1661,6 +1672,16 @@ void SlAutolength(AutolengthProc *proc, void *arg) if (offs != _sl.dumper->GetSize()) SlErrorCorrupt("Invalid chunk size"); } +/* + * Notes on extended chunk header: + * + * If the chunk type is CH_EXT_HDR (15), then a u32 flags field follows. + * This flag field may define additional fields which follow the flags field in future. + * The standard chunk header follows, though it my be modified by the flags field. + * At present SLCEHF_BIG_RIFF increases the RIFF size limit to a theoretical 60 bits, + * by adding a further u32 field for the high bits after the existing RIFF size field. + */ + /** * Load a chunk of data (eg vehicles, stations, etc.) * @param ch The chunkhandler that will be used for the operation @@ -1674,6 +1695,15 @@ static void SlLoadChunk(const ChunkHandler *ch) _sl.block_mode = m; _sl.obj_len = 0; + SaveLoadChunkExtHeaderFlags ext_flags = static_cast(0); + if ((m & 0xF) == CH_EXT_HDR) { + ext_flags = static_cast(SlReadUint32()); + + /* read in real header */ + m = SlReadByte(); + _sl.block_mode = m; + } + switch (m) { case CH_ARRAY: _sl.array_index = 0; @@ -1689,6 +1719,10 @@ static void SlLoadChunk(const ChunkHandler *ch) /* Read length */ len = (SlReadByte() << 16) | ((m >> 4) << 24); len += SlReadUint16(); + if (ext_flags & SLCEHF_BIG_RIFF) { + len |= SlReadUint32() << 28; + } + _sl.obj_len = len; endoffs = _sl.reader->GetSize() + len; ch->load_proc(); @@ -1714,9 +1748,21 @@ static void SlLoadCheckChunk(const ChunkHandler *ch) _sl.block_mode = m; _sl.obj_len = 0; + SaveLoadChunkExtHeaderFlags ext_flags = static_cast(0); + if ((m & 0xF) == CH_EXT_HDR) { + ext_flags = static_cast(SlReadUint32()); + + /* read in real header */ + m = SlReadByte(); + _sl.block_mode = m; + } + switch (m) { case CH_ARRAY: _sl.array_index = 0; + if (ext_flags) { + SlErrorCorruptFmt("CH_ARRAY does not take chunk header extension flags: 0x%X", ext_flags); + } if (ch && ch->load_check_proc) { ch->load_check_proc(); } else { @@ -1724,6 +1770,9 @@ static void SlLoadCheckChunk(const ChunkHandler *ch) } break; case CH_SPARSE_ARRAY: + if (ext_flags) { + SlErrorCorruptFmt("CH_SPARSE_ARRAY does not take chunk header extension flags: 0x%X", ext_flags); + } if (ch && ch->load_check_proc) { ch->load_check_proc(); } else { @@ -1732,9 +1781,19 @@ static void SlLoadCheckChunk(const ChunkHandler *ch) break; default: if ((m & 0xF) == CH_RIFF) { + if (ext_flags != (ext_flags & SLCEHF_BIG_RIFF)) { + SlErrorCorruptFmt("Unknown chunk header extension flags for CH_RIFF: 0x%X", ext_flags); + } /* Read length */ len = (SlReadByte() << 16) | ((m >> 4) << 24); len += SlReadUint16(); + if (ext_flags & SLCEHF_BIG_RIFF) { + uint64 full_len = len | (static_cast(SlReadUint32()) << 28); + if (full_len >= (1LL << 32)) { + SlErrorCorrupt("Chunk size too large: " OTTD_PRINTFHEX64, full_len); + } + len = static_cast(full_len); + } _sl.obj_len = len; endoffs = _sl.reader->GetSize() + len; if (ch && ch->load_check_proc) { diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index e34d4f3e9e..330cdde072 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -99,10 +99,17 @@ enum ChunkType { CH_ARRAY = 1, CH_SPARSE_ARRAY = 2, CH_TYPE_MASK = 3, + CH_EXT_HDR = 15, ///< Extended chunk header CH_LAST = 8, ///< Last chunk in this array. CH_AUTO_LENGTH = 16, }; +/** Flags for chunk extended headers */ +enum SaveLoadChunkExtHeaderFlags { + SLCEHF_BIG_RIFF = 1 << 0, ///< This block uses a 60-bit RIFF chunk size +}; +DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) + /** * VarTypes is the general bitmasked magic type that tells us * certain characteristics about the variable it refers to. For example From d6395b97a75331cb0a2635545456f323f73a2d94 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 12 Sep 2015 14:15:04 +0100 Subject: [PATCH 04/12] Enable extended feature versions for saving to be set at run time. --- src/saveload/extended_ver_sl.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index be40cad17b..6e13994e67 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -164,7 +164,6 @@ static void Save_SLXI() SlXvSetCurrentState(); static const SaveLoad _xlsi_sub_chunk_desc[] = { - SLE_VAR(SlxiSubChunkInfo, save_version, SLE_UINT16), SLE_STR(SlxiSubChunkInfo, name, SLE_STR, 0), SLE_END() }; @@ -178,9 +177,9 @@ static void Save_SLXI() chunk_counts.resize(XSLFI_SIZE); const SlxiSubChunkInfo *info = _sl_xv_sub_chunk_infos; for (; info->index != XSLFI_NULL; ++info) { - if (info->save_version > 0) { + if (_sl_xv_feature_versions[info->index] > 0) { item_count++; - length += 4; + length += 6; length += SlCalcObjLength(info, _xlsi_sub_chunk_desc); if (info->save_proc) { uint32 extra_data_length = info->save_proc(info, true); @@ -208,7 +207,8 @@ static void Save_SLXI() // write data info = _sl_xv_sub_chunk_infos; for (; info->index != XSLFI_NULL; ++info) { - if (info->save_version > 0) { + uint16 save_version = _sl_xv_feature_versions[info->index]; + if (save_version > 0) { SlxiSubChunkFlags flags = info->flags; assert(!(flags & (XSCF_EXTRA_DATA_PRESENT | XSCF_CHUNK_ID_LIST_PRESENT))); uint32 extra_data_length = extra_data_lengths[info->index]; @@ -216,6 +216,7 @@ static void Save_SLXI() if (extra_data_length > 0) flags |= XSCF_EXTRA_DATA_PRESENT; if (chunk_count > 0) flags |= XSCF_CHUNK_ID_LIST_PRESENT; SlWriteUint32(flags); + SlWriteUint16(save_version); SlObject(const_cast(info), _xlsi_sub_chunk_desc); if (extra_data_length > 0) { From e606a847c6ba65b3e2b625f26d3f98659632bbda Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 25 Oct 2015 21:35:40 +0000 Subject: [PATCH 05/12] Add an un-ordered chunk for extra company settings: PLYX. This is similar to the PATX chunk. Minor refactoring to enable some code sharing between two chunks. Fix MakeSettingsPatxList always regenerating cache. Update documentation of PATX chunk structure. --- src/saveload/company_sl.cpp | 22 ++- src/settings.cpp | 235 ++++++++++++++++++++++++++++----- src/table/company_settings.ini | 5 +- 3 files changed, 223 insertions(+), 39 deletions(-) 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 From 1234bdb07c0b6431aed3e67d15844014c878f829 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 31 Oct 2015 10:51:58 +0000 Subject: [PATCH 06/12] Fix compilation issue with MSVC due to ordering of noreturn and cdecl. --- src/saveload/saveload.cpp | 4 ++-- src/saveload/saveload.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 6fe2a8e636..d83fc74c72 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -564,7 +564,7 @@ void NORETURN SlError(StringID string, const char *extra_msg, bool already_mallo /** * As SlError, except that it takes a format string and additional parameters */ -void CDECL NORETURN SlErrorFmt(StringID string, const char *msg, ...) +void NORETURN CDECL SlErrorFmt(StringID string, const char *msg, ...) { va_list va; va_start(va, msg); @@ -588,7 +588,7 @@ void NORETURN SlErrorCorrupt(const char *msg, bool already_malloced) /** * As SlErrorCorruptFmt, except that it takes a format string and additional parameters */ -void CDECL NORETURN SlErrorCorruptFmt(const char *msg, ...) +void NORETURN CDECL SlErrorCorruptFmt(const char *msg, ...) { va_list va; va_start(va, msg); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 330cdde072..8df011958b 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -631,8 +631,8 @@ void SlObject(void *object, const SaveLoad *sld); bool SlObjectMember(void *object, const SaveLoad *sld); void NORETURN SlError(StringID string, const char *extra_msg = NULL, bool already_malloced = false); void NORETURN SlErrorCorrupt(const char *msg, bool already_malloced = false); -void CDECL NORETURN SlErrorFmt(StringID string, const char *msg, ...) WARN_FORMAT(2, 3); -void CDECL NORETURN SlErrorCorruptFmt(const char *msg, ...) WARN_FORMAT(1, 2); +void NORETURN CDECL SlErrorFmt(StringID string, const char *msg, ...) WARN_FORMAT(2, 3); +void NORETURN CDECL SlErrorCorruptFmt(const char *msg, ...) WARN_FORMAT(1, 2); bool SaveloadCrashWithMissingNewGRFs(); From 8e8d7e93d5d235b5233baee5d5e44978e0348a53 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 5 Sep 2016 23:26:36 +0100 Subject: [PATCH 07/12] Tidy up legacy-compatibility code in PATX/PLYX handlers. Use lambdas instead of manual emulation. Move an inner struct back into its owner function. --- src/settings.cpp | 56 +++++++++++------------------------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 18fbab98c6..39fba94cc6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -2247,18 +2247,6 @@ static void SaveSettings(const SettingDesc *sd, void *object) /** Sorted list of PATX settings, generated by MakeSettingsPatxList */ static std::vector _sorted_patx_settings; -/** - * Internal structure used in LoadSettingsPatx() - * placed outside for legacy compiler compatibility - * this makes me miss lambdas :/ - */ -struct StringSorter { - bool operator()(const SettingDesc *a, const SettingDesc *b) - { - return strcmp(a->patx_name, b->patx_name) < 0; - } -}; - /** * Prepare a sorted list of settings to be potentially be loaded out of the PATX chunk * This is to enable efficient lookup of settings by name @@ -2277,28 +2265,11 @@ static void MakeSettingsPatxList(const SettingDesc *sd) _sorted_patx_settings.push_back(desc); } - std::sort(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), StringSorter()); + std::sort(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), [](const SettingDesc *a, const SettingDesc *b) { + return strcmp(a->patx_name, b->patx_name) < 0; + }); } -/** - * Internal structure used in LoadSettingsPatx() - * placed outside for legacy compiler compatibility - * this is effectively a reference capture lambda - */ -struct StringSearcher { - bool &m_exact_match; - - StringSearcher(bool &exact_match) - : m_exact_match(exact_match) { } - - bool operator()(const SettingDesc *a, const char *b) - { - int result = strcmp(a->patx_name, b); - if (result == 0) m_exact_match = true; - return result < 0; - } -}; - /** * Internal structure used in LoadSettingsPatx() and LoadSettingsPlyx() */ @@ -2354,9 +2325,13 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) // 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); - // now try to find corresponding setting, this would be much easier with C++11 support... + // now try to find corresponding setting bool exact_match = false; - std::vector::iterator iter = std::lower_bound(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), current_setting.name, StringSearcher(exact_match)); + auto iter = std::lower_bound(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), current_setting.name, [&](const SettingDesc *a, const char *b) { + int result = strcmp(a->patx_name, b); + if (result == 0) exact_match = true; + return result < 0; + }); if (exact_match) { assert(iter != _sorted_patx_settings.end()); @@ -2376,15 +2351,6 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object) } } -/** - * Internal structure used in SaveSettingsPatx() - * placed outside for legacy compiler compatibility - */ -struct SettingToAdd { - const SettingDesc *setting; - uint32 setting_length; -}; - /** * Save handler for settings which go in the PATX chunk * @param sd SettingDesc struct containing all information @@ -2395,6 +2361,10 @@ static void SaveSettingsPatx(const SettingDesc *sd, void *object) { SettingsExtSave current_setting; + struct SettingToAdd { + const SettingDesc *setting; + uint32 setting_length; + }; std::vector settings_to_add; size_t length = 8; From a3d0fc5817a1ed8a81c3452cdb98cd94a9d12d44 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 5 Sep 2016 23:26:52 +0100 Subject: [PATCH 08/12] Remove an unused extern declaration, update comment. --- src/saveload/extended_ver_sl.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 6e13994e67..fb64afe2c9 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -115,9 +115,7 @@ void SlXvSetCurrentState() */ void SlXvCheckSpecialSavegameVersions() { - extern uint16 _sl_version; - - // TODO: check for savegame versions + // Checks for special savegame versions go here } /** From 13fb737bed0b94dc031db433bce04702b0d658a4 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 12 Sep 2016 22:48:58 +0100 Subject: [PATCH 09/12] Add a template parameter for the inner container of MultiMap. --- src/core/multimap.hpp | 45 ++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/core/multimap.hpp b/src/core/multimap.hpp index e906677141..da6a33ef45 100644 --- a/src/core/multimap.hpp +++ b/src/core/multimap.hpp @@ -15,7 +15,7 @@ #include #include -template +template class MultiMap; /** @@ -23,14 +23,15 @@ class MultiMap; * @tparam Tmap_iter Iterator type for the map in the MultiMap. * @tparam Tlist_iter Iterator type for the lists in the MultiMap. * @tparam Tkey Key type of the MultiMap. - * @tparam Tvalue Value type of the MultMap. + * @tparam Tvalue Value type of the MultiMap. + * @tparam Tcontainer Container type for the values of the MultiMap. * @tparam Tcompare Comparator type for keys of the MultiMap. */ -template +template class MultiMapIterator { protected: - friend class MultiMap; - typedef MultiMapIterator Self; + friend class MultiMap; + typedef MultiMapIterator Self; Tlist_iter list_iter; ///< Iterator pointing to current position in the current list of items with equal keys. Tmap_iter map_iter; ///< Iterator pointing to the position of the current list of items with equal keys in the map. @@ -201,8 +202,8 @@ public: * @param iter2 Second iterator to compare. * @return If iter1 and iter2 are equal. */ -template -bool operator==(const MultiMapIterator &iter1, const MultiMapIterator &iter2) +template +bool operator==(const MultiMapIterator &iter1, const MultiMapIterator &iter2) { if (iter1.GetMapIter() != iter2.GetMapIter()) return false; if (!iter1.ListValid()) return !iter2.ListValid(); @@ -218,8 +219,8 @@ bool operator==(const MultiMapIterator -bool operator!=(const MultiMapIterator &iter1, const MultiMapIterator &iter2) +template +bool operator!=(const MultiMapIterator &iter1, const MultiMapIterator &iter2) { return !(iter1 == iter2); } @@ -232,8 +233,8 @@ bool operator!=(const MultiMapIterator -bool operator==(const MultiMapIterator &iter1, const Tmap_iter2 &iter2) +template +bool operator==(const MultiMapIterator &iter1, const Tmap_iter2 &iter2) { return !iter1.ListValid() && iter1.GetMapIter() == iter2; } @@ -244,8 +245,8 @@ bool operator==(const MultiMapIterator -bool operator!=(const MultiMapIterator &iter1, const Tmap_iter2 &iter2) +template +bool operator!=(const MultiMapIterator &iter1, const Tmap_iter2 &iter2) { return iter1.ListValid() || iter1.GetMapIter() != iter2; } @@ -256,8 +257,8 @@ bool operator!=(const MultiMapIterator -bool operator==(const Tmap_iter2 &iter2, const MultiMapIterator &iter1) +template +bool operator==(const Tmap_iter2 &iter2, const MultiMapIterator &iter1) { return !iter1.ListValid() && iter1.GetMapIter() == iter2; } @@ -268,8 +269,8 @@ bool operator==(const Tmap_iter2 &iter2, const MultiMapIterator -bool operator!=(const Tmap_iter2 &iter2, const MultiMapIterator &iter1) +template +bool operator!=(const Tmap_iter2 &iter2, const MultiMapIterator &iter1) { return iter1.ListValid() || iter1.GetMapIter() != iter2; } @@ -282,10 +283,10 @@ bool operator!=(const Tmap_iter2 &iter2, const MultiMapIterator > -class MultiMap : public std::map, Tcompare > { +template, typename Tcompare = std::less > +class MultiMap : public std::map { public: - typedef typename std::list List; + typedef Tcontainer List; typedef typename List::iterator ListIterator; typedef typename List::const_iterator ConstListIterator; @@ -293,8 +294,8 @@ public: typedef typename Map::iterator MapIterator; typedef typename Map::const_iterator ConstMapIterator; - typedef MultiMapIterator iterator; - typedef MultiMapIterator const_iterator; + typedef MultiMapIterator iterator; + typedef MultiMapIterator const_iterator; /** * Erase the value pointed to by an iterator. The iterator may be invalid afterwards. From c752da3a9ee5e93de8b40f40650d34bf1822f6a5 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 12 Sep 2016 22:49:52 +0100 Subject: [PATCH 10/12] SaveLoad: Add support for saving/loading from a std::deque --- src/saveload/saveload.cpp | 20 +++++++++++++------- src/saveload/saveload.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index b5e88dc791..5b4410c6dd 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -52,6 +52,7 @@ #include "../safeguards.h" +#include #include /* @@ -1366,9 +1367,10 @@ static void *IntToReference(size_t index, SLRefType rt) * Return the size in bytes of a list * @param list The std::list to find the size of */ + template static inline size_t SlCalcListLen(const void *list) { - const std::list *l = (const std::list *) list; + const PtrList *l = (const PtrList *) list; int type_size = IsSavegameVersionBefore(69) ? 2 : 4; /* Each entry is saved as type_size bytes, plus type_size bytes are used for the length @@ -1382,23 +1384,23 @@ static inline size_t SlCalcListLen(const void *list) * @param list The list being manipulated * @param conv SLRefType type of the list (Vehicle *, Station *, etc) */ +template static void SlList(void *list, SLRefType conv) { /* Automatically calculate the length? */ if (_sl.need_length != NL_NONE) { - SlSetLength(SlCalcListLen(list)); + SlSetLength(SlCalcListLen(list)); /* Determine length only? */ if (_sl.need_length == NL_CALCLENGTH) return; } - typedef std::list PtrList; PtrList *l = (PtrList *)list; switch (_sl.action) { case SLA_SAVE: { SlWriteUint32((uint32)l->size()); - PtrList::iterator iter; + typename PtrList::iterator iter; for (iter = l->begin(); iter != l->end(); ++iter) { void *ptr = *iter; SlWriteUint32((uint32)ReferenceToInt(ptr, conv)); @@ -1420,7 +1422,7 @@ static void SlList(void *list, SLRefType conv) PtrList temp = *l; l->clear(); - PtrList::iterator iter; + typename PtrList::iterator iter; for (iter = temp.begin(); iter != temp.end(); ++iter) { void *ptr = IntToReference((size_t)*iter, conv); l->push_back(ptr); @@ -1486,6 +1488,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_ARR: case SL_STR: case SL_LST: + case SL_DEQ: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) break; @@ -1494,7 +1497,8 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_REF: return SlCalcRefLen(); case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv); case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv); - case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld)); + case SL_LST: return SlCalcListLen>(GetVariableAddress(object, sld)); + case SL_DEQ: return SlCalcListLen>(GetVariableAddress(object, sld)); default: NOT_REACHED(); } break; @@ -1557,6 +1561,7 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_ARR: case SL_STR: case SL_LST: + case SL_DEQ: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) return false; if (SlSkipVariableOnLoad(sld)) return false; @@ -1583,7 +1588,8 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) break; case SL_ARR: SlArray(ptr, sld->length, conv); break; case SL_STR: SlString(ptr, sld->length, sld->conv); break; - case SL_LST: SlList(ptr, (SLRefType)conv); break; + case SL_LST: SlList>(ptr, (SLRefType)conv); break; + case SL_DEQ: SlList>(ptr, (SLRefType)conv); break; default: NOT_REACHED(); } break; diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 42bc82c48f..46ed04237f 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -205,6 +205,7 @@ enum SaveLoadTypes { SL_ARR = 2, ///< Save/load an array. SL_STR = 3, ///< Save/load a string. SL_LST = 4, ///< Save/load a list. + SL_DEQ = 5, ///< Save/load a deque. /* non-normal save-load types */ SL_WRITEBYTE = 8, SL_VEH_INCLUDE = 9, @@ -310,6 +311,18 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLE_CONDLST_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_LST, base, variable, type, 0, from, to, extver) #define SLE_CONDLST(base, variable, type, from, to) SLE_CONDLST_X(base, variable, type, from, to, SlXvFeatureTest()) +/** + * Storage of a deque in some savegame versions. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDDEQ_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_DEQ, base, variable, type, 0, from, to, extver) +#define SLE_CONDDEQ(base, variable, type, from, to) SLE_CONDDEQ_X(base, variable, type, from, to, SlXvFeatureTest()) + /** * Storage of a variable in every version of a savegame. * @param base Name of the class or struct containing the variable. @@ -352,6 +365,14 @@ typedef SaveLoad SaveLoadGlobVarList; */ #define SLE_LST(base, variable, type) SLE_CONDLST(base, variable, type, 0, SL_MAX_VERSION) +/** + * Storage of a deque in every savegame version. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_DEQ(base, variable, type) SLE_CONDDEQ(base, variable, type, 0, SL_MAX_VERSION) + /** * Empty space in every savegame version. * @param length Length of the empty space. @@ -447,6 +468,17 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLEG_CONDLST_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_LST, variable, type, 0, from, to, extver) #define SLEG_CONDLST(variable, type, from, to) SLEG_CONDLST_X(variable, type, from, to, SlXvFeatureTest()) +/** + * Storage of a global deque in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDDEQ_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_DEQ, variable, type, 0, from, to, extver) +#define SLEG_CONDDEQ(variable, type, from, to) SLEG_CONDDEQ_X(variable, type, from, to, SlXvFeatureTest()) + /** * Storage of a global variable in every savegame version. * @param variable Name of the global variable. @@ -482,6 +514,13 @@ typedef SaveLoad SaveLoadGlobVarList; */ #define SLEG_LST(variable, type) SLEG_CONDLST(variable, type, 0, SL_MAX_VERSION) +/** + * Storage of a global deque in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_DEQ(variable, type) SLEG_CONDDEQ(variable, type, 0, SL_MAX_VERSION) + /** * Empty global space in some savegame versions. * @param length Length of the empty space. From 94d3700f7920dd680bc1ff38de8bd4f744d10da9 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 13 Sep 2016 18:30:55 +0100 Subject: [PATCH 11/12] SaveLoad: Add support for saving/loading from a std::vector --- src/saveload/saveload.cpp | 4 ++++ src/saveload/saveload.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 5b4410c6dd..f531944d63 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -1489,6 +1489,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_STR: case SL_LST: case SL_DEQ: + case SL_VEC: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) break; @@ -1499,6 +1500,7 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv); case SL_LST: return SlCalcListLen>(GetVariableAddress(object, sld)); case SL_DEQ: return SlCalcListLen>(GetVariableAddress(object, sld)); + case SL_VEC: return SlCalcListLen>(GetVariableAddress(object, sld)); default: NOT_REACHED(); } break; @@ -1562,6 +1564,7 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_STR: case SL_LST: case SL_DEQ: + case SL_VEC: /* CONDITIONAL saveload types depend on the savegame version */ if (!SlIsObjectValidInSavegame(sld)) return false; if (SlSkipVariableOnLoad(sld)) return false; @@ -1590,6 +1593,7 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld) case SL_STR: SlString(ptr, sld->length, sld->conv); break; case SL_LST: SlList>(ptr, (SLRefType)conv); break; case SL_DEQ: SlList>(ptr, (SLRefType)conv); break; + case SL_VEC: SlList>(ptr, (SLRefType)conv); break; default: NOT_REACHED(); } break; diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 46ed04237f..44e040aa1a 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -206,6 +206,7 @@ enum SaveLoadTypes { SL_STR = 3, ///< Save/load a string. SL_LST = 4, ///< Save/load a list. SL_DEQ = 5, ///< Save/load a deque. + SL_VEC = 6, ///< Save/load a vector. /* non-normal save-load types */ SL_WRITEBYTE = 8, SL_VEH_INCLUDE = 9, @@ -323,6 +324,18 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLE_CONDDEQ_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_DEQ, base, variable, type, 0, from, to, extver) #define SLE_CONDDEQ(base, variable, type, from, to) SLE_CONDDEQ_X(base, variable, type, from, to, SlXvFeatureTest()) +/** + * Storage of a vector in some savegame versions. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLE_CONDVEC_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VEC, base, variable, type, 0, from, to, extver) +#define SLE_CONDVEC(base, variable, type, from, to) SLE_CONDVEC_X(base, variable, type, from, to, SlXvFeatureTest()) + /** * Storage of a variable in every version of a savegame. * @param base Name of the class or struct containing the variable. @@ -373,6 +386,14 @@ typedef SaveLoad SaveLoadGlobVarList; */ #define SLE_DEQ(base, variable, type) SLE_CONDDEQ(base, variable, type, 0, SL_MAX_VERSION) +/** + * Storage of a vector in every savegame version. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_VEC(base, variable, type) SLE_CONDVEC(base, variable, type, 0, SL_MAX_VERSION) + /** * Empty space in every savegame version. * @param length Length of the empty space. @@ -479,6 +500,17 @@ typedef SaveLoad SaveLoadGlobVarList; #define SLEG_CONDDEQ_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_DEQ, variable, type, 0, from, to, extver) #define SLEG_CONDDEQ(variable, type, from, to) SLEG_CONDDEQ_X(variable, type, from, to, SlXvFeatureTest()) +/** + * Storage of a global vector in some savegame versions. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + * @param from First savegame version that has the list. + * @param to Last savegame version that has the list. + * @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field + */ +#define SLEG_CONDVEC_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VEC, variable, type, 0, from, to, extver) +#define SLEG_CONDVEC(variable, type, from, to) SLEG_CONDVEC_X(variable, type, from, to, SlXvFeatureTest()) + /** * Storage of a global variable in every savegame version. * @param variable Name of the global variable. @@ -521,6 +553,13 @@ typedef SaveLoad SaveLoadGlobVarList; */ #define SLEG_DEQ(variable, type) SLEG_CONDDEQ(variable, type, 0, SL_MAX_VERSION) +/** + * Storage of a global vector in every savegame version. + * @param variable Name of the global variable. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLEG_VEC(variable, type) SLEG_CONDVEC(variable, type, 0, SL_MAX_VERSION) + /** * Empty global space in some savegame versions. * @param length Length of the empty space. From d124089d2996f1e7affd4eb6e4b52994f3988c45 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 13 Sep 2016 19:14:19 +0100 Subject: [PATCH 12/12] Add extended version tag and feature versions to output of -q. --- src/openttd.cpp | 15 ++++++++++++++- src/saveload/extended_ver_sl.cpp | 14 ++++++++++++++ src/saveload/extended_ver_sl.h | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/openttd.cpp b/src/openttd.cpp index cc382bdbf0..85b56adc85 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -249,7 +249,20 @@ static void WriteSavegameInfo(const char *name) char buf[8192]; char *p = buf; p += seprintf(p, lastof(buf), "Name: %s\n", name); - p += seprintf(p, lastof(buf), "Savegame ver: %d\n", _sl_version); + const char *type = ""; + extern bool _sl_is_faked_ext; + extern bool _sl_is_ext_version; + if (_sl_is_faked_ext) { + type = " (fake extended)"; + } else if (_sl_is_ext_version) { + type = " (extended)"; + } + p += seprintf(p, lastof(buf), "Savegame ver: %d%s\n", _sl_version, type); + for (size_t i = 0; i < XSLFI_SIZE; i++) { + if (_sl_xv_feature_versions[i] > 0) { + p += seprintf(p, lastof(buf), " Feature: %s = %d\n", SlXvGetFeatureName((SlXvFeatureIndex) i), _sl_xv_feature_versions[i]); + } + } p += seprintf(p, lastof(buf), "NewGRF ver: 0x%08X\n", last_ottd_rev); p += seprintf(p, lastof(buf), "Modified: %d\n", ever_modified); diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index fb64afe2c9..7b083a7f21 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -86,6 +86,20 @@ bool SlXvIsFeaturePresent(SlXvFeatureIndex feature, uint16 min_version, uint16 m return _sl_xv_feature_versions[feature] >= min_version && _sl_xv_feature_versions[feature] <= max_version; } +/** + * Returns true if @p feature is present and has a version inclusively bounded by @p min_version and @p max_version + */ +const char *SlXvGetFeatureName(SlXvFeatureIndex feature) +{ + const SlxiSubChunkInfo *info = _sl_xv_sub_chunk_infos; + for (; info->index != XSLFI_NULL; ++info) { + if (info->index == feature) { + return info->name; + } + } + return "(unknown feature)"; +} + /** * Resets all extended feature versions to 0 */ diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 39a03478c2..b495c60c4e 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -65,6 +65,8 @@ inline bool SlXvIsFeatureMissing(SlXvFeatureIndex feature) return !SlXvIsFeaturePresent(feature); } +const char *SlXvGetFeatureName(SlXvFeatureIndex feature); + /** * sub chunk flags, this is saved as-is * (XSCF_EXTRA_DATA_PRESENT and XSCF_CHUNK_ID_LIST_PRESENT must only be set by the save code, and read by the load code)