diff --git a/findversion.sh b/findversion.sh
index 6be52b696f..639d2a0eeb 100755
--- a/findversion.sh
+++ b/findversion.sh
@@ -99,7 +99,7 @@ elif [ -d "$ROOT_DIR/.git" ]; then
# No rev? Maybe it is a custom git-svn clone
REV_NR=`LC_ALL=C git log --pretty=format:%b --grep="git-svn-id:.*@[0-9]*" -1 | sed "s@.*\@\([0-9]*\).*@\1@"`
fi
- TAG="`git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null | sed 's@\^0$@@'`"
+ TAG="`git describe --tags 2>/dev/null`"
if [ -n "$TAG" ]; then
BRANCH=""
REV="$TAG"
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
diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp
index f90203e228..1966892a92 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"
@@ -487,6 +488,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;
}
@@ -530,7 +532,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/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp
index e534b7c697..7db9ef673f 100644
--- a/src/saveload/extended_ver_sl.cpp
+++ b/src/saveload/extended_ver_sl.cpp
@@ -165,7 +165,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()
};
@@ -179,9 +178,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);
@@ -209,7 +208,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];
@@ -217,6 +217,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) {
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index 74f6fba1e2..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);
@@ -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 711d9ac7ac..8df011958b 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
@@ -234,7 +241,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @note In general, it is better to use one of the SLE_* macros below.
*/
#define SLE_GENERAL_X(cmd, base, variable, type, length, from, to, extver) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable), cpp_sizeof(base, variable), extver}
-#define SLE_GENERAL(cmd, base, variable, type, length, from, to) SLE_GENERAL_X(cmd, base, variable, type, length, from, to, {})
+#define SLE_GENERAL(cmd, base, variable, type, length, from, to) SLE_GENERAL_X(cmd, base, variable, type, length, from, to, SlXvFeatureTest())
/**
* Storage of a variable in some savegame versions.
@@ -246,7 +253,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDVAR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VAR, base, variable, type, 0, from, to, extver)
-#define SLE_CONDVAR(base, variable, type, from, to) SLE_CONDVAR_X(base, variable, type, from, to, {})
+#define SLE_CONDVAR(base, variable, type, from, to) SLE_CONDVAR_X(base, variable, type, from, to, SlXvFeatureTest())
/**
* Storage of a reference in some savegame versions.
@@ -258,7 +265,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDREF_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REF, base, variable, type, 0, from, to, extver)
-#define SLE_CONDREF(base, variable, type, from, to) SLE_CONDREF_X(base, variable, type, from, to, {})
+#define SLE_CONDREF(base, variable, type, from, to) SLE_CONDREF_X(base, variable, type, from, to, SlXvFeatureTest())
/**
* Storage of an array in some savegame versions.
@@ -271,7 +278,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDARR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_ARR, base, variable, type, length, from, to, extver)
-#define SLE_CONDARR(base, variable, type, length, from, to) SLE_CONDARR_X(base, variable, type, length, from, to, {})
+#define SLE_CONDARR(base, variable, type, length, from, to) SLE_CONDARR_X(base, variable, type, length, from, to, SlXvFeatureTest())
/**
* Storage of a string in some savegame versions.
@@ -284,7 +291,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDSTR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_STR, base, variable, type, length, from, to, extver)
-#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_CONDSTR_X(base, variable, type, length, from, to, {})
+#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_CONDSTR_X(base, variable, type, length, from, to, SlXvFeatureTest())
/**
* Storage of a list in some savegame versions.
@@ -296,7 +303,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#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, {})
+#define SLE_CONDLST(base, variable, type, from, to) SLE_CONDLST_X(base, variable, type, from, to, SlXvFeatureTest())
/**
* Storage of a variable in every version of a savegame.
@@ -354,16 +361,16 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have empty space
*/
#define SLE_CONDNULL_X(length, from, to, extver) SLE_CONDARR_X(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL | SLF_NOT_IN_CONFIG, length, from, to, extver)
-#define SLE_CONDNULL(length, from, to) SLE_CONDNULL_X(length, from, to, {})
+#define SLE_CONDNULL(length, from, to) SLE_CONDNULL_X(length, from, to, SlXvFeatureTest())
/** Translate values ingame to different values in the savegame and vv. */
#define SLE_WRITEBYTE(base, variable, value) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, value, value)
-#define SLE_VEH_INCLUDE() {false, SL_VEH_INCLUDE, 0, 0, 0, SL_MAX_VERSION, NULL, 0, {}}
-#define SLE_ST_INCLUDE() {false, SL_ST_INCLUDE, 0, 0, 0, SL_MAX_VERSION, NULL, 0, {}}
+#define SLE_VEH_INCLUDE() {false, SL_VEH_INCLUDE, 0, 0, 0, SL_MAX_VERSION, NULL, 0, SlXvFeatureTest()}
+#define SLE_ST_INCLUDE() {false, SL_ST_INCLUDE, 0, 0, 0, SL_MAX_VERSION, NULL, 0, SlXvFeatureTest()}
/** End marker of a struct/class save or load. */
-#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL, 0, {}}
+#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL, 0, SlXvFeatureTest()}
/**
* Storage of global simple variables, references (pointers), and arrays.
@@ -376,7 +383,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @note In general, it is better to use one of the SLEG_* macros below.
*/
#define SLEG_GENERAL_X(cmd, variable, type, length, from, to, extver) {true, cmd, type, length, from, to, (void*)&variable, sizeof(variable), extver}
-#define SLEG_GENERAL(cmd, variable, type, length, from, to) SLEG_GENERAL_X(cmd, variable, type, length, from, to, {})
+#define SLEG_GENERAL(cmd, variable, type, length, from, to) SLEG_GENERAL_X(cmd, variable, type, length, from, to, SlXvFeatureTest())
/**
* Storage of a global variable in some savegame versions.
@@ -387,7 +394,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDVAR_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VAR, variable, type, 0, from, to, extver)
-#define SLEG_CONDVAR(variable, type, from, to) SLEG_CONDVAR_X(variable, type, from, to, {})
+#define SLEG_CONDVAR(variable, type, from, to) SLEG_CONDVAR_X(variable, type, from, to, SlXvFeatureTest())
/**
* Storage of a global reference in some savegame versions.
@@ -398,7 +405,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDREF_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_REF, variable, type, 0, from, to, extver)
-#define SLEG_CONDREF(variable, type, from, to) SLEG_CONDREF_X(variable, type, from, to, {})
+#define SLEG_CONDREF(variable, type, from, to) SLEG_CONDREF_X(variable, type, from, to, SlXvFeatureTest())
/**
* Storage of a global array in some savegame versions.
@@ -410,7 +417,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDARR_X(variable, type, length, from, to, extver) SLEG_GENERAL_X(SL_ARR, variable, type, length, from, to, extver)
-#define SLEG_CONDARR(variable, type, length, from, to) SLEG_CONDARR_X(variable, type, length, from, to, {})
+#define SLEG_CONDARR(variable, type, length, from, to) SLEG_CONDARR_X(variable, type, length, from, to, SlXvFeatureTest())
/**
* Storage of a global string in some savegame versions.
@@ -422,7 +429,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLEG_CONDSTR_X(variable, type, length, from, to, extver) SLEG_GENERAL_X(SL_STR, variable, type, length, from, to, extver)
-#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_CONDSTR_X(variable, type, length, from, to, {})
+#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_CONDSTR_X(variable, type, length, from, to, SlXvFeatureTest())
/**
* Storage of a global list in some savegame versions.
@@ -433,7 +440,7 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#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, {})
+#define SLEG_CONDLST(variable, type, from, to) SLEG_CONDLST_X(variable, type, from, to, SlXvFeatureTest())
/**
* Storage of a global variable in every savegame version.
@@ -477,10 +484,10 @@ typedef SaveLoad SaveLoadGlobVarList;
* @param to Last savegame version that has the empty space.
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have empty space
*/
-#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_NOT_IN_CONFIG, length, from, to, (void*)NULL, {}}
+#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_NOT_IN_CONFIG, length, from, to, (void*)NULL, SlXvFeatureTest()}
/** End marker of global variables save or load. */
-#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL, 0, {}}
+#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL, 0, SlXvFeatureTest()}
/**
* Checks whether the savegame is below \a major.\a minor.
@@ -624,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();
diff --git a/src/settings.cpp b/src/settings.cpp
index eb6d93792e..cbbc9797e3 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -2257,10 +2257,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,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
@@ -2279,6 +2291,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++) {
@@ -2286,16 +2299,60 @@ 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
+ * 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()
+ */
+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
@@ -2306,19 +2363,7 @@ 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[] = {
- 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
@@ -2326,26 +2371,13 @@ 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);
// 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) {
@@ -2366,6 +2398,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
@@ -2374,24 +2415,8 @@ static void LoadSettingsPatx(const SettingDesc *sd, void *object)
*/
static void SaveSettingsPatx(const SettingDesc *sd, void *object)
{
- struct SettingsPatxSave {
- uint32 flags;
- const char *name;
- uint32 setting_length;
- };
- SettingsPatxSave current_setting;
+ SettingsExtSave 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()
- };
-
- struct SettingToAdd {
- const SettingDesc *setting;
- uint32 setting_length;
- };
std::vector settings_to_add;
size_t length = 8;
@@ -2403,12 +2428,14 @@ 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;
- 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);
@@ -2420,12 +2447,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 5c839c2af7..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]
@@ -34,7 +34,8 @@ load = NULL
from = 0
to = SL_MAX_VERSION
cat = SC_ADVANCED
-extver = {}
+extver = SlXvFeatureTest()
+patxname = NULL
diff --git a/src/table/currency_settings.ini b/src/table/currency_settings.ini
index 3c476ee5df..6a8665379a 100644
--- a/src/table/currency_settings.ini
+++ b/src/table/currency_settings.ini
@@ -28,7 +28,7 @@ load = NULL
from = 0
to = SL_MAX_VERSION
cat = SC_ADVANCED
-extver = {}
+extver = SlXvFeatureTest()
diff --git a/src/table/gameopt_settings.ini b/src/table/gameopt_settings.ini
index 9e5fa3d0ff..a5bc766ecb 100644
--- a/src/table/gameopt_settings.ini
+++ b/src/table/gameopt_settings.ini
@@ -62,7 +62,7 @@ load = NULL
from = 0
to = SL_MAX_VERSION
cat = SC_ADVANCED
-extver = {}
+extver = SlXvFeatureTest()
diff --git a/src/table/misc_settings.ini b/src/table/misc_settings.ini
index 11197692c8..06bae18013 100644
--- a/src/table/misc_settings.ini
+++ b/src/table/misc_settings.ini
@@ -35,7 +35,7 @@ load = NULL
from = 0
to = SL_MAX_VERSION
cat = SC_ADVANCED
-extver = {}
+extver = SlXvFeatureTest()
diff --git a/src/table/settings.ini b/src/table/settings.ini
index 352569a8ca..eaa9b8ccc2 100644
--- a/src/table/settings.ini
+++ b/src/table/settings.ini
@@ -95,7 +95,7 @@ load = NULL
from = 0
to = SL_MAX_VERSION
cat = SC_ADVANCED
-extver = {}
+extver = SlXvFeatureTest()
patxname = NULL
diff --git a/src/table/win32_settings.ini b/src/table/win32_settings.ini
index 6f62db63f6..a22e1a54e7 100644
--- a/src/table/win32_settings.ini
+++ b/src/table/win32_settings.ini
@@ -33,7 +33,7 @@ load = NULL
from = 0
to = SL_MAX_VERSION
cat = SC_ADVANCED
-extver = {}
+extver = SlXvFeatureTest()
diff --git a/src/table/window_settings.ini b/src/table/window_settings.ini
index 4565d9b35f..a7b1742040 100644
--- a/src/table/window_settings.ini
+++ b/src/table/window_settings.ini
@@ -29,7 +29,7 @@ load = NULL
from = 0
to = SL_MAX_VERSION
cat = SC_ADVANCED
-extver = {}
+extver = SlXvFeatureTest()