Use compatibility table for loading non-table OPTS, PATS chunks

This commit is contained in:
Jonathan G Rennison
2023-11-18 11:50:28 +00:00
parent d948abd040
commit 581ed5b98f
7 changed files with 521 additions and 612 deletions

View File

@@ -91,6 +91,7 @@
#include "table/strings.h"
#include "table/settings.h"
#include "table/settings_compat.h"
#include <algorithm>
#include <vector>
@@ -3113,55 +3114,79 @@ void IConsoleListSettings(const char *prefilter, bool show_defaults)
IConsolePrintF(CC_WARNING, "Use 'setting' command to change a value");
}
/**
* Load handler for settings, which don't go in the PATX chunk, and which are a cross-reference to another setting
* @param osd SettingDesc struct containing all information
* @param object can be either nullptr in which case we load global variables or
* a pointer to a struct which is getting saved
*/
static void LoadSettingsXref(const SettingDesc *osd, void *object) {
DEBUG(sl, 3, "PATS chunk: Loading xref setting: '%s'", osd->xref.target);
const SettingDesc *setting_xref = GetSettingFromFullName(osd->xref.target);
assert(setting_xref != nullptr);
// Generate a new SaveLoad from the xref target using the version params from the source
SaveLoad sld = setting_xref->save;
sld.version_from = osd->save.version_from;
sld.version_to = osd->save.version_to;
sld.ext_feature_test = osd->save.ext_feature_test;
if (!SlObjectMember(object, sld)) return;
if (setting_xref->IsIntSetting()) {
const IntSettingDesc *int_setting = setting_xref->AsIntSetting();
int64 val = int_setting->Read(object);
if (osd->xref.conv != nullptr) val = osd->xref.conv(val);
int_setting->MakeValueValidAndWrite(object, val);
}
}
struct LoadSettingsItem {
SettingsCompat compat;
const SettingDesc *setting;
};
std::vector<LoadSettingsItem> _gameopt_compat_items;
std::vector<LoadSettingsItem> _settings_compat_items;
/**
* Save and load handler for settings, except for those which go in the PATX chunk
* Load handler for settings from old-style non-table OPTS and PATS chunks
* @param settings SettingDesc struct containing all information
* @param compat Compatibility table
* @param items Load items (filled in on first run)
* @param object can be either nullptr in which case we load global variables or
* a pointer to a struct which is getting saved
*/
static void LoadSettings(const SettingTable &settings, void *object)
static void LoadSettings(const SettingTable &settings, std::initializer_list<SettingsCompat> compat, std::vector<LoadSettingsItem> &items, void *object)
{
extern SaveLoadVersion _sl_version;
if (items.empty()) {
/* Populate setting references */
for (auto &osd : settings) {
if (osd->flags & SF_NOT_IN_SAVE) continue;
if (osd->patx_name != nullptr) continue;
const SaveLoad &sld = osd->save;
if (osd->xref.target != nullptr) {
if (sld.ext_feature_test.IsFeaturePresent(_sl_version, sld.version_from, sld.version_to)) LoadSettingsXref(osd.get(), object);
continue;
btree::btree_multimap<std::string_view, const SettingDesc *> names;
for (auto &osd : settings) {
if (osd->flags & SF_NOT_IN_SAVE) continue;
if (osd->name == nullptr) continue;
names.insert({osd->name, osd.get()});
}
if (!SlObjectMember(object, osd->save)) continue;
if (osd->IsIntSetting()) {
const IntSettingDesc *int_setting = osd->AsIntSetting();
int_setting->MakeValueValidAndWrite(object, int_setting->Read(object));
for (const SettingsCompat &c : compat) {
if (c.type == SettingsCompatType::Setting || c.type == SettingsCompatType::Xref) {
auto iters = names.equal_range(c.name);
assert_msg(iters.first != iters.second, "Setting: %s", c.name.c_str());
for (auto it = iters.first; it != iters.second; ++it) {
items.push_back({ c, it->second });
}
} else {
items.push_back({ c, nullptr });
}
}
}
extern SaveLoadVersion _sl_version;
for (LoadSettingsItem &item : items) {
switch (item.compat.type) {
case SettingsCompatType::Null:
if (item.compat.ext_feature_test.IsFeaturePresent(_sl_version, item.compat.version_from, item.compat.version_to)) SlSkipBytes(item.compat.length);
break;
case SettingsCompatType::Setting:
if (!SlObjectMember(object, item.setting->save)) continue;
if (item.setting->IsIntSetting()) {
const IntSettingDesc *int_setting = item.setting->AsIntSetting();
int_setting->MakeValueValidAndWrite(object, int_setting->Read(object));
}
break;
case SettingsCompatType::Xref:
if (item.compat.ext_feature_test.IsFeaturePresent(_sl_version, item.compat.version_from, item.compat.version_to)) {
DEBUG(sl, 3, "PATS chunk: Loading xref setting: '%s'", item.compat.name.c_str());
/* Generate a new SaveLoad from the xref target using the version params from the source */
SaveLoad sld = item.setting->save;
sld.version_from = item.compat.version_from;
sld.version_to = item.compat.version_to;
sld.ext_feature_test = item.compat.ext_feature_test;
if (!SlObjectMember(object, sld)) continue;
if (item.setting->IsIntSetting()) {
const IntSettingDesc *int_setting = item.setting->AsIntSetting();
int64 val = int_setting->Read(object);
if (item.compat.xrefconv != nullptr) val = item.compat.xrefconv(val);
int_setting->MakeValueValidAndWrite(object, val);
}
}
break;
}
}
}
@@ -3450,7 +3475,7 @@ static void Load_OPTS()
* a networking environment. This ensures for example that the local
* autosave-frequency stays when joining a network-server */
PrepareOldDiffCustom();
LoadSettings(_old_gameopt_settings, &_settings_game);
LoadSettings(_old_gameopt_settings, _gameopt_compat, _gameopt_compat_items, &_settings_game);
HandleOldDiffCustom(true);
}
@@ -3459,12 +3484,12 @@ static void Load_PATS()
/* Copy over default setting since some might not get loaded in
* a networking environment. This ensures for example that the local
* currency setting stays when joining a network-server */
LoadSettings(_settings, &_settings_game);
LoadSettings(_settings, _settings_compat, _settings_compat_items, &_settings_game);
}
static void Check_PATS()
{
LoadSettings(_settings, &_load_check_data.settings);
LoadSettings(_settings, _settings_compat, _settings_compat_items, &_load_check_data.settings);
}
static void Load_PATX()