Settings update: add PATX chunk to store additional settings

in an unordered format which is tolerant of extra, missing or reordered
settings.
This commit is contained in:
Jonathan G Rennison
2015-08-01 12:19:18 +01:00
parent bde094fe01
commit c57ffc64bc
10 changed files with 283 additions and 80 deletions

View File

@@ -64,6 +64,8 @@
#include "roadveh.h"
#include "fios.h"
#include "strings_func.h"
#include "string_func.h"
#include "debug.h"
#include "void_map.h"
#include "station_base.h"
@@ -71,6 +73,9 @@
#include "table/strings.h"
#include "table/settings.h"
#include <algorithm>
#include <vector>
#include "safeguards.h"
ClientSettings _settings_client;
@@ -2180,7 +2185,7 @@ void IConsoleListSettings(const char *prefilter)
}
/**
* Save and load handler for settings
* Save and load handler for settings, except for those which go in the PATX chunk
* @param osd SettingDesc struct containing all information
* @param object can be either NULL in which case we load global variables or
* a pointer to a struct which is getting saved
@@ -2188,6 +2193,7 @@ void IConsoleListSettings(const char *prefilter)
static void LoadSettings(const SettingDesc *osd, void *object)
{
for (; osd->save.cmd != SL_END; osd++) {
if (osd->patx_name != NULL) continue;
const SaveLoad *sld = &osd->save;
void *ptr = GetVariableAddress(object, sld);
@@ -2197,7 +2203,7 @@ static void LoadSettings(const SettingDesc *osd, void *object)
}
/**
* Save and load handler for settings
* Save and load handler for settings, except for those which go in the PATX chunk
* @param sd SettingDesc struct containing all information
* @param object can be either NULL in which case we load global variables or
* a pointer to a struct which is getting saved
@@ -2209,16 +2215,195 @@ static void SaveSettings(const SettingDesc *sd, void *object)
const SettingDesc *i;
size_t length = 0;
for (i = sd; i->save.cmd != SL_END; i++) {
if (i->patx_name != NULL) continue;
length += SlCalcObjMemberLength(object, &i->save);
}
SlSetLength(length);
for (i = sd; i->save.cmd != SL_END; i++) {
if (i->patx_name != NULL) continue;
void *ptr = GetVariableAddress(object, &i->save);
SlObjectMember(ptr, &i->save);
}
}
/** @file
*
* The PATX chunk stores additional settings in an unordered format
* which is tolerant of extra, missing or reordered settings.
* Additional settings generally means those that aren't in trunk.
*
* The PATX chunk contents has the following format:
*
* uint32 chunk flags
* uint32 number of settings
* For each of N settings:
* uint32 setting flags
* SLE_STR setting name
* uint32 length of setting field
* N bytes setting field
*/
/** Sorted list of PATX settings, generated by MakeSettingsPatxList */
static std::vector<const SettingDesc *> _sorted_patx_settings;
/**
* 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
* This is stored in _sorted_patx_settings
*/
static void MakeSettingsPatxList(const SettingDesc *sd)
{
static const SettingDesc *previous = NULL;
if (sd == previous) return;
_sorted_patx_settings.clear();
for (const SettingDesc *desc = sd; desc->save.cmd != SL_END; desc++) {
if (desc->patx_name == NULL) continue;
_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());
}
/**
* Load handler for settings which go in the PATX chunk
* @param osd SettingDesc struct containing all information
* @param object can be either NULL in which case we load global variables or
* a pointer to a struct which is getting saved
*/
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, 255),
SLE_VAR(SettingsPatxLoad, setting_length, SLE_UINT32),
SLE_END()
};
uint32 flags = SlReadUint32();
// flags are not in use yet, reserve for future expansion
if (flags != 0) SlErrorCorruptFmt("PATX chunk: unknown chunk header flags: 0x%X", flags);
uint32 settings_count = SlReadUint32();
for (uint32 i = 0; i < settings_count; i++) {
SlObject(&current_setting, _settings_patx_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<const SettingDesc *>::iterator iter = std::lower_bound(_sorted_patx_settings.begin(), _sorted_patx_settings.end(), current_setting.name, StringSearcher(exact_match));
if (exact_match) {
assert(iter != _sorted_patx_settings.end());
// found setting
const SaveLoad *sld = &((*iter)->save);
size_t read = SlGetBytesRead();
void *ptr = GetVariableAddress(object, sld);
SlObjectMember(ptr, sld);
if (SlGetBytesRead() != read + current_setting.setting_length) {
SlErrorCorruptFmt("PATX chunk: setting read length mismatch for setting: '%s'", current_setting.name);
}
if (IsNumericType(sld->conv)) Write_ValidateSetting(ptr, *iter, ReadValue(ptr, sld->conv));
} else {
DEBUG(sl, 1, "PATX chunk: Could not find setting: '%s', ignoring", current_setting.name);
SlSkipBytes(current_setting.setting_length);
}
}
}
/**
* Save handler for settings which go in the PATX chunk
* @param sd SettingDesc struct containing all information
* @param object can be either NULL in which case we load global variables or
* a pointer to a struct which is getting saved
*/
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()
};
struct SettingToAdd {
const SettingDesc *setting;
uint32 setting_length;
};
std::vector<SettingToAdd> settings_to_add;
size_t length = 8;
for (const SettingDesc *desc = sd; desc->save.cmd != SL_END; desc++) {
if (desc->patx_name == NULL) continue;
uint32 setting_length = SlCalcObjMemberLength(object, &desc->save);
if (!setting_length) continue;
current_setting.name = desc->patx_name;
// add length of setting header
length += SlCalcObjLength(&current_setting, _settings_patx_desc);
// add length of actual setting
length += setting_length;
settings_to_add.push_back({ desc, setting_length });
}
SlSetLength(length);
SlWriteUint32(0); // flags
SlWriteUint32(settings_to_add.size()); // settings count
for (size_t i = 0; i < settings_to_add.size(); i++) {
const SettingDesc *desc = settings_to_add[i].setting;
current_setting.flags = 0;
current_setting.name = desc->patx_name;
current_setting.setting_length = settings_to_add[i].setting_length;
SlObject(&current_setting, _settings_patx_desc);
void *ptr = GetVariableAddress(object, &desc->save);
SlObjectMember(ptr, &desc->save);
}
}
static void Load_OPTS()
{
/* Copy over default setting since some might not get loaded in
@@ -2247,6 +2432,21 @@ static void Save_PATS()
SaveSettings(_settings, &_settings_game);
}
static void Load_PATX()
{
LoadSettingsPatx(_settings, &_settings_game);
}
static void Check_PATX()
{
LoadSettingsPatx(_settings, &_load_check_data.settings);
}
static void Save_PATX()
{
SaveSettingsPatx(_settings, &_settings_game);
}
void CheckConfig()
{
/*
@@ -2261,7 +2461,8 @@ void CheckConfig()
extern const ChunkHandler _setting_chunk_handlers[] = {
{ 'OPTS', NULL, Load_OPTS, NULL, NULL, CH_RIFF},
{ 'PATS', Save_PATS, Load_PATS, NULL, Check_PATS, CH_RIFF | CH_LAST},
{ 'PATS', Save_PATS, Load_PATS, NULL, Check_PATS, CH_RIFF},
{ 'PATX', Save_PATX, Load_PATX, NULL, Check_PATX, CH_RIFF | CH_LAST},
};
static bool IsSignedVarMemType(VarType vt)