/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
*/
/** @file settings_sl.cpp Handles the saveload part of the settings. */
#include "../stdafx.h"
#include "saveload.h"
#include "compat/settings_sl_compat.h"
#include "../settings_type.h"
#include "../settings_table.h"
#include "../network/network.h"
#include "../fios.h"
#include "../safeguards.h"
/**
* Prepare for reading and old diff_custom by zero-ing the memory.
*/
void PrepareOldDiffCustom()
{
memset(_old_diff_custom, 0, sizeof(_old_diff_custom));
}
/**
* Reading of the old diff_custom array and transforming it to the new format.
* @param savegame is it read from the config or savegame. In the latter case
* we are sure there is an array; in the former case we have
* to check that.
*/
void HandleOldDiffCustom(bool savegame)
{
/* Savegames before v4 didn't have "town_council_tolerance" in savegame yet. */
bool has_no_town_council_tolerance = savegame && IsSavegameVersionBefore(SLV_4);
uint options_to_load = GAME_DIFFICULTY_NUM - (has_no_town_council_tolerance ? 1 : 0);
if (!savegame) {
/* If we did read to old_diff_custom, then at least one value must be non 0. */
bool old_diff_custom_used = false;
for (uint i = 0; i < options_to_load && !old_diff_custom_used; i++) {
old_diff_custom_used = (_old_diff_custom[i] != 0);
}
if (!old_diff_custom_used) return;
}
/* Iterate over all the old difficulty settings, and convert the list-value to the new setting. */
uint i = 0;
for (const auto &name : _old_diff_settings) {
if (has_no_town_council_tolerance && name == "town_council_tolerance") continue;
std::string fullname = "difficulty." + name;
const SettingDesc *sd = GetSettingFromName(fullname);
/* Some settings are no longer in use; skip reading those. */
if (sd == nullptr) {
i++;
continue;
}
int32 value = (int32)((name == "max_loan" ? 1000 : 1) * _old_diff_custom[i++]);
sd->AsIntSetting()->MakeValueValidAndWrite(savegame ? &_settings_game : &_settings_newgame, value);
}
}
/**
* Get the SaveLoad description for the SettingTable.
* @param settings SettingDesc struct containing all information.
* @param is_loading True iff the SaveLoad table is for loading.
* @return Vector with SaveLoad entries for the SettingTable.
*/
static std::vector GetSettingsDesc(const SettingTable &settings, bool is_loading)
{
std::vector saveloads;
for (auto &desc : settings) {
const SettingDesc *sd = GetSettingDesc(desc);
if (sd->flags & SF_NOT_IN_SAVE) continue;
if (is_loading && (sd->flags & SF_NO_NETWORK_SYNC) && _networking && !_network_server) {
if (IsSavegameVersionBefore(SLV_TABLE_CHUNKS)) {
/* We don't want to read this setting, so we do need to skip over it. */
saveloads.push_back({sd->GetName(), sd->save.cmd, GetVarFileType(sd->save.conv) | SLE_VAR_NULL, sd->save.length, sd->save.version_from, sd->save.version_to, 0, nullptr, 0, nullptr});
}
continue;
}
saveloads.push_back(sd->save);
}
return saveloads;
}
/**
* Save and load handler for settings
* @param settings 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 LoadSettings(const SettingTable &settings, void *object, const SaveLoadCompatTable &slct)
{
const std::vector slt = SlCompatTableHeader(GetSettingsDesc(settings, true), slct);
if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() == -1) return;
SlObject(object, slt);
if (!IsSavegameVersionBefore(SLV_RIFF_TO_ARRAY) && SlIterateArray() != -1) SlErrorCorrupt("Too many settings entries");
/* Ensure all IntSettings are valid (min/max could have changed between versions etc). */
for (auto &desc : settings) {
const SettingDesc *sd = GetSettingDesc(desc);
if (sd->flags & SF_NOT_IN_SAVE) continue;
if ((sd->flags & SF_NO_NETWORK_SYNC) && _networking && !_network_server) continue;
if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
if (sd->IsIntSetting()) {
const IntSettingDesc *int_setting = sd->AsIntSetting();
int_setting->MakeValueValidAndWrite(object, int_setting->Read(object));
}
}
}
/**
* Save and load handler for settings
* @param settings 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 SaveSettings(const SettingTable &settings, void *object)
{
const std::vector slt = GetSettingsDesc(settings, false);
SlTableHeader(slt);
SlSetArrayIndex(0);
SlObject(object, slt);
}
struct OPTSChunkHandler : ChunkHandler {
OPTSChunkHandler() : ChunkHandler('OPTS', CH_READONLY) {}
void Load() const override
{
/* Copy over default setting since some might not get loaded in
* 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, _gameopt_sl_compat);
HandleOldDiffCustom(true);
}
};
struct PATSChunkHandler : ChunkHandler {
PATSChunkHandler() : ChunkHandler('PATS', CH_TABLE) {}
/**
* Create a single table with all settings that should be stored/loaded
* in the savegame.
*/
SettingTable GetSettingTable() const
{
static const SettingTable saveload_settings_tables[] = {
_difficulty_settings,
_economy_settings,
_game_settings,
_linkgraph_settings,
_locale_settings,
_pathfinding_settings,
_script_settings,
_world_settings,
};
static std::vector settings_table;
if (settings_table.empty()) {
for (auto &saveload_settings_table : saveload_settings_tables) {
for (auto &saveload_setting : saveload_settings_table) {
settings_table.push_back(saveload_setting);
}
}
}
return settings_table;
}
void Load() const override
{
/* 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(this->GetSettingTable(), &_settings_game, _settings_sl_compat);
}
void LoadCheck(size_t) const override
{
LoadSettings(this->GetSettingTable(), &_load_check_data.settings, _settings_sl_compat);
}
void Save() const override
{
SaveSettings(this->GetSettingTable(), &_settings_game);
}
};
static const OPTSChunkHandler OPTS;
static const PATSChunkHandler PATS;
static const ChunkHandlerRef setting_chunk_handlers[] = {
OPTS,
PATS,
};
extern const ChunkHandlerTable _setting_chunk_handlers(setting_chunk_handlers);