Move upstream saveload to src/saveload/, move jgrpp saveload to src/sl/

Leave afterload in src/saveload/
This commit is contained in:
Jonathan G Rennison
2023-06-07 23:29:52 +01:00
parent 3c9ce6f9a5
commit ac2f9a21e8
181 changed files with 16887 additions and 16888 deletions

58
src/sl/CMakeLists.txt Normal file
View File

@@ -0,0 +1,58 @@
add_files(
ai_sl.cpp
airport_sl.cpp
animated_tile_sl.cpp
autoreplace_sl.cpp
bridge_signal_sl.cpp
cargomonitor_sl.cpp
cargopacket_sl.cpp
cheat_sl.cpp
company_sl.cpp
debug_sl.cpp
depot_sl.cpp
economy_sl.cpp
engine_sl.cpp
extended_ver_sl.cpp
extended_ver_sl.h
game_sl.cpp
gamelog_sl.cpp
goal_sl.cpp
group_sl.cpp
industry_sl.cpp
labelmaps_sl.cpp
league_sl.cpp
linkgraph_sl.cpp
map_sl.cpp
misc_sl.cpp
newgrf_sl.cpp
newgrf_sl.h
newsignals_sl.cpp
object_sl.cpp
oldloader.cpp
oldloader.h
oldloader_sl.cpp
order_sl.cpp
plans_sl.cpp
saveload.cpp
saveload.h
saveload_buffer.h
saveload_common.h
saveload_filter.h
saveload_internal.h
saveload_types.h
signal_sl.cpp
signs_sl.cpp
station_sl.cpp
storage_sl.cpp
strings_sl.cpp
story_sl.cpp
subsidy_sl.cpp
tbtr_template_replacement_sl.cpp
tbtr_template_veh_sl.cpp
town_sl.cpp
tracerestrict_sl.cpp
train_speed_adaptation.cpp
tunnel_sl.cpp
vehicle_sl.cpp
waypoint_sl.cpp
)

124
src/sl/ai_sl.cpp Normal file
View File

@@ -0,0 +1,124 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file ai_sl.cpp Handles the saveload part of the AIs */
#include "../stdafx.h"
#include "../company_base.h"
#include "../debug.h"
#include "saveload.h"
#include "../string_func.h"
#include "../ai/ai.hpp"
#include "../ai/ai_config.hpp"
#include "../network/network.h"
#include "../ai/ai_instance.hpp"
#include "../safeguards.h"
static std::string _ai_saveload_name;
static int _ai_saveload_version;
static std::string _ai_saveload_settings;
static bool _ai_saveload_is_random;
static const SaveLoad _ai_company[] = {
SLEG_SSTR(_ai_saveload_name, SLE_STR),
SLEG_SSTR(_ai_saveload_settings, SLE_STR),
SLEG_CONDVAR(_ai_saveload_version, SLE_UINT32, SLV_108, SL_MAX_VERSION),
SLEG_CONDVAR(_ai_saveload_is_random, SLE_BOOL, SLV_136, SL_MAX_VERSION),
};
static void SaveReal_AIPL(int *index_ptr)
{
CompanyID index = (CompanyID)*index_ptr;
AIConfig *config = AIConfig::GetConfig(index);
if (config->HasScript()) {
_ai_saveload_name = config->GetName();
_ai_saveload_version = config->GetVersion();
} else {
/* No AI is configured for this so store an empty string as name. */
_ai_saveload_name.clear();
_ai_saveload_version = -1;
}
_ai_saveload_is_random = config->IsRandom();
_ai_saveload_settings = config->SettingsToString();
SlObject(nullptr, _ai_company);
/* If the AI was active, store its data too */
if (Company::IsValidAiID(index)) AI::Save(index);
}
static void Load_AIPL()
{
/* Free all current data */
for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
AIConfig::GetConfig(c, AIConfig::SSS_FORCE_GAME)->Change(nullptr);
}
CompanyID index;
while ((index = (CompanyID)SlIterateArray()) != (CompanyID)-1) {
if (index >= MAX_COMPANIES) SlErrorCorrupt("Too many AI configs");
_ai_saveload_is_random = false;
_ai_saveload_version = -1;
SlObject(nullptr, _ai_company);
if (_game_mode == GM_MENU || (_networking && !_network_server)) {
if (Company::IsValidAiID(index)) AIInstance::LoadEmpty();
continue;
}
AIConfig *config = AIConfig::GetConfig(index, AIConfig::SSS_FORCE_GAME);
if (_ai_saveload_name.empty()) {
/* A random AI. */
config->Change(nullptr, -1, false, true);
} else {
config->Change(_ai_saveload_name.c_str(), _ai_saveload_version, false, _ai_saveload_is_random);
if (!config->HasScript()) {
/* No version of the AI available that can load the data. Try to load the
* latest version of the AI instead. */
config->Change(_ai_saveload_name.c_str(), -1, false, _ai_saveload_is_random);
if (!config->HasScript()) {
if (_ai_saveload_name.compare("%_dummy") != 0) {
DEBUG(script, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version);
DEBUG(script, 0, "A random other AI will be loaded in its place.");
} else {
DEBUG(script, 0, "The savegame had no AIs available at the time of saving.");
DEBUG(script, 0, "A random available AI will be loaded now.");
}
} else {
DEBUG(script, 0, "The savegame has an AI by the name '%s', version %d which is no longer available.", _ai_saveload_name.c_str(), _ai_saveload_version);
DEBUG(script, 0, "The latest version of that AI has been loaded instead, but it'll not get the savegame data as it's incompatible.");
}
/* Make sure the AI doesn't get the saveload data, as it was not the
* writer of the saveload data in the first place */
_ai_saveload_version = -1;
}
}
config->StringToSettings(_ai_saveload_settings);
/* Load the AI saved data */
if (Company::IsValidAiID(index)) config->SetToLoadData(AIInstance::Load(_ai_saveload_version));
}
}
static void Save_AIPL()
{
for (int i = COMPANY_FIRST; i < MAX_COMPANIES; i++) {
SlSetArrayIndex(i);
SlAutolength((AutolengthProc *)SaveReal_AIPL, &i);
}
}
static const ChunkHandler ai_chunk_handlers[] = {
{ 'AIPL', Save_AIPL, Load_AIPL, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _ai_chunk_handlers(ai_chunk_handlers);

42
src/sl/airport_sl.cpp Normal file
View File

@@ -0,0 +1,42 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file airport_sl.cpp Code handling saving and loading airport ids */
#include "../stdafx.h"
#include "saveload.h"
#include "newgrf_sl.h"
#include "../safeguards.h"
static void Save_APID()
{
Save_NewGRFMapping(_airport_mngr);
}
static void Load_APID()
{
Load_NewGRFMapping(_airport_mngr);
}
static void Save_ATID()
{
Save_NewGRFMapping(_airporttile_mngr);
}
static void Load_ATID()
{
Load_NewGRFMapping(_airporttile_mngr);
}
static const ChunkHandler airport_chunk_handlers[] = {
{ 'ATID', Save_ATID, Load_ATID, nullptr, nullptr, CH_ARRAY },
{ 'APID', Save_APID, Load_APID, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _airport_chunk_handlers(airport_chunk_handlers);

View File

@@ -0,0 +1,80 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file animated_tile_sl.cpp Code handling saving and loading of animated tiles */
#include "../stdafx.h"
#include "../animated_tile.h"
#include "../tile_type.h"
#include "../core/alloc_func.hpp"
#include "../core/smallvec_type.hpp"
#include "saveload.h"
#include "../safeguards.h"
/**
* Save the ANIT chunk.
*/
static void Save_ANIT()
{
uint count = 0;
for (const auto &it : _animated_tiles) {
if (!it.second.pending_deletion) count++;
}
SlSetLength(count * 5);
for (const auto &it : _animated_tiles) {
if (it.second.pending_deletion) continue;
SlWriteUint32(it.first);
SlWriteByte(it.second.speed);
}
}
/**
* Load the ANIT chunk; the chunk containing the animated tiles.
*/
static void Load_ANIT()
{
/* Before version 80 we did NOT have a variable length animated tile table */
if (IsSavegameVersionBefore(SLV_80)) {
/* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */
TileIndex anim_list[256];
SlArray(anim_list, 256, IsSavegameVersionBefore(SLV_6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32);
for (int i = 0; i < 256; i++) {
if (anim_list[i] == 0) break;
_animated_tiles[anim_list[i]] = {};
}
return;
}
_animated_tiles.clear();
if (SlXvIsFeaturePresent(XSLFI_ANIMATED_TILE_EXTRA)) {
uint count = (uint)SlGetFieldLength() / 5;
for (uint i = 0; i < count; i++) {
TileIndex tile = SlReadUint32();
AnimatedTileInfo info = {};
info.speed = SlReadByte();
_animated_tiles[tile] = info;
}
} else {
uint count = (uint)SlGetFieldLength() / 4;
for (uint i = 0; i < count; i++) {
_animated_tiles[SlReadUint32()] = {};
}
}
}
/**
* "Definition" imported by the saveload code to be able to load and save
* the animated tile table.
*/
static const ChunkHandler animated_tile_chunk_handlers[] = {
{ 'ANIT', Save_ANIT, Load_ANIT, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _animated_tile_chunk_handlers(animated_tile_chunk_handlers);

62
src/sl/autoreplace_sl.cpp Normal file
View File

@@ -0,0 +1,62 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file autoreplace_sl.cpp Code handling saving and loading of autoreplace rules */
#include "../stdafx.h"
#include "../autoreplace_base.h"
#include "saveload.h"
#include "../safeguards.h"
static const SaveLoad _engine_renew_desc[] = {
SLE_VAR(EngineRenew, from, SLE_UINT16),
SLE_VAR(EngineRenew, to, SLE_UINT16),
SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, SLV_60, SL_MAX_VERSION),
SLE_CONDVAR(EngineRenew, replace_when_old, SLE_BOOL, SLV_175, SL_MAX_VERSION),
};
static void Save_ERNW()
{
for (EngineRenew *er : EngineRenew::Iterate()) {
SlSetArrayIndex(er->index);
SlObject(er, _engine_renew_desc);
}
}
static void Load_ERNW()
{
int index;
while ((index = SlIterateArray()) != -1) {
EngineRenew *er = new (index) EngineRenew();
SlObject(er, _engine_renew_desc);
/* Advanced vehicle lists, ungrouped vehicles got added */
if (IsSavegameVersionBefore(SLV_60)) {
er->group_id = ALL_GROUP;
} else if (IsSavegameVersionBefore(SLV_71)) {
if (er->group_id == DEFAULT_GROUP) er->group_id = ALL_GROUP;
}
}
}
static void Ptrs_ERNW()
{
for (EngineRenew *er : EngineRenew::Iterate()) {
SlObject(er, _engine_renew_desc);
}
}
static const ChunkHandler autoreplace_chunk_handlers[] = {
{ 'ERNW', Save_ERNW, Load_ERNW, Ptrs_ERNW, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _autoreplace_chunk_handlers(autoreplace_chunk_handlers);

View File

@@ -0,0 +1,74 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file bridge_signal_sl.cpp Code handling saving and loading of data for signal on bridges */
#include "../stdafx.h"
#include "../bridge_signal_map.h"
#include "saveload.h"
#include <vector>
/** stub save header struct */
struct LongBridgeSignalStorageStub {
uint32 length;
};
static const SaveLoad _long_bridge_signal_storage_stub_desc[] = {
SLE_VAR(LongBridgeSignalStorageStub, length, SLE_UINT32),
};
static void Load_XBSS()
{
int index;
LongBridgeSignalStorageStub stub;
while ((index = SlIterateArray()) != -1) {
LongBridgeSignalStorage &lbss = _long_bridge_signal_sim_map[index];
SlObject(&stub, _long_bridge_signal_storage_stub_desc);
lbss.signal_red_bits.resize(stub.length);
SlArray(lbss.signal_red_bits.data(), stub.length, SLE_UINT64);
}
}
static void RealSave_XBSS(const LongBridgeSignalStorage *lbss)
{
LongBridgeSignalStorageStub stub;
stub.length = (uint32)lbss->signal_red_bits.size();
SlObject(&stub, _long_bridge_signal_storage_stub_desc);
SlArray(const_cast<uint64*>(lbss->signal_red_bits.data()), stub.length, SLE_UINT64);
}
static void Save_XBSS()
{
for (const auto &it : _long_bridge_signal_sim_map) {
const LongBridgeSignalStorage &lbss = it.second;
SlSetArrayIndex(it.first);
SlAutolength((AutolengthProc*) RealSave_XBSS, const_cast<LongBridgeSignalStorage*>(&lbss));
}
}
static void Load_XBST()
{
size_t count = SlGetFieldLength() / sizeof(uint32);
for (size_t i = 0; i < count; i++) {
_bridge_signal_style_map.insert(SlReadUint32());
}
}
static void Save_XBST()
{
SlSetLength(_bridge_signal_style_map.size() * sizeof(uint32));
for (uint32 val : _bridge_signal_style_map) {
SlWriteUint32(val);
}
}
extern const ChunkHandler bridge_signal_chunk_handlers[] = {
{ 'XBSS', Save_XBSS, Load_XBSS, nullptr, nullptr, CH_SPARSE_ARRAY },
{ 'XBST', Save_XBST, Load_XBST, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _bridge_signal_chunk_handlers(bridge_signal_chunk_handlers);

125
src/sl/cargomonitor_sl.cpp Normal file
View File

@@ -0,0 +1,125 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file cargomonitor_sl.cpp Code handling saving and loading of Cargo monitoring. */
#include "../stdafx.h"
#include "../cargomonitor.h"
#include "saveload.h"
#include "../safeguards.h"
/** Temporary storage of cargo monitoring data for loading or saving it. */
struct TempStorage {
CargoMonitorID number;
uint32 amount;
};
/** Description of the #TempStorage structure for the purpose of load and save. */
static const SaveLoad _cargomonitor_pair_desc[] = {
SLE_VAR(TempStorage, number, SLE_UINT32),
SLE_VAR(TempStorage, amount, SLE_UINT32),
};
static CargoMonitorID FixupCargoMonitor(CargoMonitorID number)
{
/* Between SLV_EXTEND_CARGOTYPES and SLV_FIX_CARGO_MONITOR, the
* CargoMonitorID structure had insufficient packing for more
* than 32 cargo types. Here we have to shuffle bits to account
* for the change.
* Company moved from bits 24-31 to 25-28.
* Cargo type increased from bits 19-23 to 19-24.
*/
SB(number, 25, 4, GB(number, 24, 4));
SB(number, 29, 3, 0);
ClrBit(number, 24);
return number;
}
/** Save the #_cargo_deliveries monitoring map. */
static void SaveDelivery()
{
TempStorage storage;
int i = 0;
CargoMonitorMap::const_iterator iter = _cargo_deliveries.begin();
while (iter != _cargo_deliveries.end()) {
storage.number = iter->first;
storage.amount = iter->second;
SlSetArrayIndex(i);
SlObject(&storage, _cargomonitor_pair_desc);
i++;
iter++;
}
}
/** Load the #_cargo_deliveries monitoring map. */
static void LoadDelivery()
{
TempStorage storage;
bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR);
ClearCargoDeliveryMonitoring();
for (;;) {
if (SlIterateArray() < 0) break;
SlObject(&storage, _cargomonitor_pair_desc);
if (fix) storage.number = FixupCargoMonitor(storage.number);
std::pair<CargoMonitorID, uint32> p(storage.number, storage.amount);
_cargo_deliveries.insert(p);
}
}
/** Save the #_cargo_pickups monitoring map. */
static void SavePickup()
{
TempStorage storage;
int i = 0;
CargoMonitorMap::const_iterator iter = _cargo_pickups.begin();
while (iter != _cargo_pickups.end()) {
storage.number = iter->first;
storage.amount = iter->second;
SlSetArrayIndex(i);
SlObject(&storage, _cargomonitor_pair_desc);
i++;
iter++;
}
}
/** Load the #_cargo_pickups monitoring map. */
static void LoadPickup()
{
TempStorage storage;
bool fix = IsSavegameVersionBefore(SLV_FIX_CARGO_MONITOR);
ClearCargoPickupMonitoring();
for (;;) {
if (SlIterateArray() < 0) break;
SlObject(&storage, _cargomonitor_pair_desc);
if (fix) storage.number = FixupCargoMonitor(storage.number);
std::pair<CargoMonitorID, uint32> p(storage.number, storage.amount);
_cargo_pickups.insert(p);
}
}
/** Chunk definition of the cargomonitoring maps. */
static const ChunkHandler cargomonitor_chunk_handlers[] = {
{ 'CMDL', SaveDelivery, LoadDelivery, nullptr, nullptr, CH_ARRAY },
{ 'CMPU', SavePickup, LoadPickup, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _cargomonitor_chunk_handlers(cargomonitor_chunk_handlers);

195
src/sl/cargopacket_sl.cpp Normal file
View File

@@ -0,0 +1,195 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file cargopacket_sl.cpp Code handling saving and loading of cargo packets */
#include "../stdafx.h"
#include "../vehicle_base.h"
#include "../station_base.h"
#include "../scope_info.h"
#include "../3rdparty/cpp-btree/btree_map.h"
#include "saveload.h"
#include "../safeguards.h"
extern btree::btree_map<uint64, Money> _cargo_packet_deferred_payments;
/**
* Savegame conversion for cargopackets.
*/
/* static */ void CargoPacket::AfterLoad()
{
if (IsSavegameVersionBefore(SLV_44)) {
/* If we remove a station while cargo from it is still en route, payment calculation will assume
* 0, 0 to be the source of the cargo, resulting in very high payments usually. v->source_xy
* stores the coordinates, preserving them even if the station is removed. However, if a game is loaded
* where this situation exists, the cargo-source information is lost. in this case, we set the source
* to the current tile of the vehicle to prevent excessive profits
*/
for (const Vehicle *v : Vehicle::Iterate()) {
const CargoPacketList *packets = v->cargo.Packets();
for (VehicleCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
CargoPacket *cp = *it;
cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : v->tile;
cp->loaded_at_xy = cp->source_xy;
}
}
/* Store position of the station where the goods come from, so there
* are no very high payments when stations get removed. However, if the
* station where the goods came from is already removed, the source
* information is lost. In that case we set it to the position of this
* station */
for (Station *st : Station::Iterate()) {
for (CargoID c = 0; c < NUM_CARGO; c++) {
GoodsEntry *ge = &st->goods[c];
const StationCargoPacketMap *packets = ge->cargo.Packets();
for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
CargoPacket *cp = *it;
cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : st->xy;
cp->loaded_at_xy = cp->source_xy;
}
}
}
}
if (IsSavegameVersionBefore(SLV_120)) {
/* CargoPacket's source should be either INVALID_STATION or a valid station */
for (CargoPacket *cp : CargoPacket::Iterate()) {
if (!Station::IsValidID(cp->source)) cp->source = INVALID_STATION;
}
}
if (!IsSavegameVersionBefore(SLV_68)) {
/* Only since version 68 we have cargo packets. Savegames from before used
* 'new CargoPacket' + cargolist.Append so their caches are already
* correct and do not need rebuilding. */
for (Vehicle *v : Vehicle::Iterate()) v->cargo.InvalidateCache();
for (Station *st : Station::Iterate()) {
for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache();
}
}
if (IsSavegameVersionBefore(SLV_181)) {
for (Vehicle *v : Vehicle::Iterate()) v->cargo.KeepAll();
}
}
/**
* Savegame conversion for cargopackets.
*/
/* static */ void CargoPacket::PostVehiclesAfterLoad()
{
if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) {
extern std::map<VehicleID, CargoPacketList> _veh_cpp_packets;
for (auto &iter : _veh_cpp_packets) {
if (iter.second.empty()) continue;
Vehicle *v = Vehicle::Get(iter.first);
Station *st = Station::Get(v->First()->last_station_visited);
assert_msg(st != nullptr, "%s", scope_dumper().VehicleInfo(v));
for (CargoPacket *cp : iter.second) {
st->goods[v->cargo_type].cargo.AfterLoadIncreaseReservationCount(cp->count);
v->cargo.Append(cp, VehicleCargoList::MTA_LOAD);
}
}
_veh_cpp_packets.clear();
}
}
/**
* Wrapper function to get the CargoPacket's internal structure while
* some of the variables itself are private.
* @return the saveload description for CargoPackets.
*/
SaveLoadTable GetCargoPacketDesc()
{
static const SaveLoad _cargopacket_desc[] = {
SLE_VAR(CargoPacket, source, SLE_UINT16),
SLE_VAR(CargoPacket, source_xy, SLE_UINT32),
SLE_VAR(CargoPacket, loaded_at_xy, SLE_UINT32),
SLE_VAR(CargoPacket, count, SLE_UINT16),
SLE_CONDVAR_X(CargoPacket, days_in_transit, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_CARGO_AGE, 0, 0)),
SLE_CONDVAR_X(CargoPacket, days_in_transit, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_CARGO_AGE)),
SLE_VAR(CargoPacket, feeder_share, SLE_INT64),
SLE_CONDVAR(CargoPacket, source_type, SLE_UINT8, SLV_125, SL_MAX_VERSION),
SLE_CONDVAR(CargoPacket, source_id, SLE_UINT16, SLV_125, SL_MAX_VERSION),
/* Used to be paid_for, but that got changed. */
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_121),
};
return _cargopacket_desc;
}
/**
* Save the cargo packets.
*/
static void Save_CAPA()
{
std::vector<SaveLoad> filtered_packet_desc = SlFilterObject(GetCargoPacketDesc());
for (CargoPacket *cp : CargoPacket::Iterate()) {
SlSetArrayIndex(cp->index);
SlObjectSaveFiltered(cp, filtered_packet_desc);
}
}
/**
* Load the cargo packets.
*/
static void Load_CAPA()
{
std::vector<SaveLoad> filtered_packet_desc = SlFilterObject(GetCargoPacketDesc());
int index;
while ((index = SlIterateArray()) != -1) {
CargoPacket *cp = new (index) CargoPacket();
SlObjectLoadFiltered(cp, filtered_packet_desc);
}
}
/**
* Save cargo packet deferred payments.
*/
void Save_CPDP()
{
SlSetLength(16 * _cargo_packet_deferred_payments.size());
for (auto &it : _cargo_packet_deferred_payments) {
SlWriteUint64(it.first);
SlWriteUint64(it.second);
}
}
/**
* Load cargo packet deferred payments.
*/
void Load_CPDP()
{
uint count = static_cast<uint>(SlGetFieldLength() / 16);
uint last_cargo_packet_id = std::numeric_limits<uint32_t>::max();
for (uint i = 0; i < count; i++) {
uint64 k = SlReadUint64();
uint64 v = SlReadUint64();
_cargo_packet_deferred_payments[k] = v;
if (k >> 32 != last_cargo_packet_id) {
last_cargo_packet_id = k >> 32;
CargoPacket::Get(last_cargo_packet_id)->flags |= CargoPacket::CPF_HAS_DEFERRED_PAYMENT;
}
}
}
/** Chunk handlers related to cargo packets. */
static const ChunkHandler cargopacket_chunk_handlers[] = {
{ 'CAPA', Save_CAPA, Load_CAPA, nullptr, nullptr, CH_ARRAY },
{ 'CPDP', Save_CPDP, Load_CPDP, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _cargopacket_chunk_handlers(cargopacket_chunk_handlers);

186
src/sl/cheat_sl.cpp Normal file
View File

@@ -0,0 +1,186 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file cheat_sl.cpp Code handling saving and loading of cheats */
#include "../stdafx.h"
#include "../cheat_type.h"
#include "../debug.h"
#include "saveload.h"
#include <map>
#include <string>
#include "../safeguards.h"
extern std::map<std::string, Cheat> _unknown_cheats;
struct ExtraCheatNameDesc {
const char *name;
Cheat *cht;
};
static ExtraCheatNameDesc _extra_cheat_descs[] = {
{ "inflation_cost", &_extra_cheats.inflation_cost },
{ "inflation_income", &_extra_cheats.inflation_income },
{ "station_rating", &_extra_cheats.station_rating },
{ "town_rating", &_extra_cheats.town_rating },
};
static const SaveLoad _cheats_desc[] = {
SLE_VAR(Cheats, magic_bulldozer.been_used, SLE_BOOL),
SLE_VAR(Cheats, magic_bulldozer.value, SLE_BOOL),
SLE_VAR(Cheats, switch_company.been_used, SLE_BOOL),
SLE_VAR(Cheats, switch_company.value, SLE_BOOL),
SLE_VAR(Cheats, money.been_used, SLE_BOOL),
SLE_VAR(Cheats, money.value, SLE_BOOL),
SLE_VAR(Cheats, crossing_tunnels.been_used, SLE_BOOL),
SLE_VAR(Cheats, crossing_tunnels.value, SLE_BOOL),
SLE_NULL(1),
SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS().
SLE_VAR(Cheats, no_jetcrash.been_used, SLE_BOOL),
SLE_VAR(Cheats, no_jetcrash.value, SLE_BOOL),
SLE_NULL(1),
SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS().
SLE_VAR(Cheats, change_date.been_used, SLE_BOOL),
SLE_VAR(Cheats, change_date.value, SLE_BOOL),
SLE_VAR(Cheats, setup_prod.been_used, SLE_BOOL),
SLE_VAR(Cheats, setup_prod.value, SLE_BOOL),
SLE_NULL(1),
SLE_NULL(1), // Needs to be two NULL fields. See Load_CHTS().
SLE_VAR(Cheats, edit_max_hl.been_used, SLE_BOOL),
SLE_VAR(Cheats, edit_max_hl.value, SLE_BOOL),
};
/**
* Save the cheat values.
*/
static void Save_CHTS()
{
SlSetLength(std::size(_cheats_desc));
SlObject(&_cheats, _cheats_desc);
}
/**
* Load the cheat values.
*/
static void Load_CHTS()
{
size_t count = SlGetFieldLength();
std::vector<SaveLoad> slt;
/* Cheats were added over the years without a savegame bump. They are
* stored as 2 SLE_BOOLs per entry. "count" indicates how many SLE_BOOLs
* are stored for this savegame. So read only "count" SLE_BOOLs (and in
* result "count / 2" cheats). */
for (auto &sld : _cheats_desc) {
count--;
slt.push_back(sld);
if (count == 0) break;
}
SlObject(&_cheats, slt);
}
/**
* Load the extra cheat values.
*/
static void Load_CHTX()
{
struct CheatsExtLoad {
char name[256];
Cheat cht;
};
static const SaveLoad _cheats_ext_load_desc[] = {
SLE_STR(CheatsExtLoad, name, SLE_STRB, 256),
SLE_VAR(CheatsExtLoad, cht.been_used, SLE_BOOL),
SLE_VAR(CheatsExtLoad, cht.value, SLE_BOOL),
};
CheatsExtLoad current_cheat;
uint32 chunk_flags = SlReadUint32();
// flags are not in use yet, reserve for future expansion
if (chunk_flags != 0) SlErrorCorruptFmt("CHTX chunk: unknown chunk header flags: 0x%X", chunk_flags);
uint32 cheat_count = SlReadUint32();
for (uint32 i = 0; i < cheat_count; i++) {
SlObject(&current_cheat, _cheats_ext_load_desc);
bool found = false;
for (uint j = 0; j < lengthof(_extra_cheat_descs); j++) {
const ExtraCheatNameDesc &desc = _extra_cheat_descs[j];
if (strcmp(desc.name, current_cheat.name) == 0) {
*(desc.cht) = current_cheat.cht;
found = true;
break;
}
}
if (!found) {
DEBUG(sl, 1, "CHTX chunk: Could not find cheat: '%s'", current_cheat.name);
_unknown_cheats[current_cheat.name] = current_cheat.cht;
}
}
}
/**
* Save the extra cheat values.
*/
static void Save_CHTX()
{
struct CheatsExtSave {
const char *name;
Cheat cht;
};
static const SaveLoad _cheats_ext_save_desc[] = {
SLE_STR(CheatsExtSave, name, SLE_STR, 0),
SLE_VAR(CheatsExtSave, cht.been_used, SLE_BOOL),
SLE_VAR(CheatsExtSave, cht.value, SLE_BOOL),
};
SlAutolength([](void *) {
SlWriteUint32(0); // flags
SlWriteUint32((uint32)(lengthof(_extra_cheat_descs) + _unknown_cheats.size())); // cheat count
for (uint j = 0; j < lengthof(_extra_cheat_descs); j++) {
CheatsExtSave save = { _extra_cheat_descs[j].name, *(_extra_cheat_descs[j].cht) };
SlObject(&save, _cheats_ext_save_desc);
}
for (const auto &iter : _unknown_cheats) {
CheatsExtSave save = { iter.first.c_str(), iter.second };
SlObject(&save, _cheats_ext_save_desc);
}
}, nullptr);
}
/**
* 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),
};
/** Chunk handlers related to cheats. */
static const ChunkHandler cheat_chunk_handlers[] = {
{ 'CHTS', Save_CHTS, Load_CHTS, nullptr, nullptr, CH_RIFF },
{ 'CHTX', Save_CHTX, Load_CHTX, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _cheat_chunk_handlers(cheat_chunk_handlers);

703
src/sl/company_sl.cpp Normal file
View File

@@ -0,0 +1,703 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file company_sl.cpp Code handling saving and loading of company data */
#include "../stdafx.h"
#include "../company_func.h"
#include "../company_manager_face.h"
#include "../fios.h"
#include "../tunnelbridge_map.h"
#include "../tunnelbridge.h"
#include "../station_base.h"
#include "../settings_func.h"
#include "../strings_func.h"
#include "../network/network.h"
#include "../network/network_func.h"
#include "../network/network_server.h"
#include "../3rdparty/randombytes/randombytes.h"
#include "../3rdparty/monocypher/monocypher.h"
#include "saveload.h"
#include "saveload_buffer.h"
#include "table/strings.h"
#include "../safeguards.h"
/**
* Converts an old company manager's face format to the new company manager's face format
*
* Meaning of the bits in the old face (some bits are used in several times):
* - 4 and 5: chin
* - 6 to 9: eyebrows
* - 10 to 13: nose
* - 13 to 15: lips (also moustache for males)
* - 16 to 19: hair
* - 20 to 22: eye colour
* - 20 to 27: tie, ear rings etc.
* - 28 to 30: glasses
* - 19, 26 and 27: race (bit 27 set and bit 19 equal to bit 26 = black, otherwise white)
* - 31: gender (0 = male, 1 = female)
*
* @param face the face in the old format
* @return the face in the new format
*/
CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face)
{
CompanyManagerFace cmf = 0;
GenderEthnicity ge = GE_WM;
if (HasBit(face, 31)) SetBit(ge, GENDER_FEMALE);
if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) SetBit(ge, ETHNICITY_BLACK);
SetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, ge, ge);
SetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge, GB(face, 28, 3) <= 1);
SetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge, HasBit(ge, ETHNICITY_BLACK) ? 0 : ClampU(GB(face, 20, 3), 5, 7) - 5);
SetCompanyManagerFaceBits(cmf, CMFV_CHIN, ge, ScaleCompanyManagerFaceValue(CMFV_CHIN, ge, GB(face, 4, 2)));
SetCompanyManagerFaceBits(cmf, CMFV_EYEBROWS, ge, ScaleCompanyManagerFaceValue(CMFV_EYEBROWS, ge, GB(face, 6, 4)));
SetCompanyManagerFaceBits(cmf, CMFV_HAIR, ge, ScaleCompanyManagerFaceValue(CMFV_HAIR, ge, GB(face, 16, 4)));
SetCompanyManagerFaceBits(cmf, CMFV_JACKET, ge, ScaleCompanyManagerFaceValue(CMFV_JACKET, ge, GB(face, 20, 2)));
SetCompanyManagerFaceBits(cmf, CMFV_COLLAR, ge, ScaleCompanyManagerFaceValue(CMFV_COLLAR, ge, GB(face, 22, 2)));
SetCompanyManagerFaceBits(cmf, CMFV_GLASSES, ge, GB(face, 28, 1));
uint lips = GB(face, 10, 4);
if (!HasBit(ge, GENDER_FEMALE) && lips < 4) {
SetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge, true);
SetCompanyManagerFaceBits(cmf, CMFV_MOUSTACHE, ge, std::max(lips, 1U) - 1);
} else {
if (!HasBit(ge, GENDER_FEMALE)) {
lips = lips * 15 / 16;
lips -= 3;
if (HasBit(ge, ETHNICITY_BLACK) && lips > 8) lips = 0;
} else {
lips = ScaleCompanyManagerFaceValue(CMFV_LIPS, ge, lips);
}
SetCompanyManagerFaceBits(cmf, CMFV_LIPS, ge, lips);
uint nose = GB(face, 13, 3);
if (ge == GE_WF) {
nose = (nose * 3 >> 3) * 3 >> 2; // There is 'hole' in the nose sprites for females
} else {
nose = ScaleCompanyManagerFaceValue(CMFV_NOSE, ge, nose);
}
SetCompanyManagerFaceBits(cmf, CMFV_NOSE, ge, nose);
}
uint tie_earring = GB(face, 24, 4);
if (!HasBit(ge, GENDER_FEMALE) || tie_earring < 3) { // Not all females have an earring
if (HasBit(ge, GENDER_FEMALE)) SetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge, true);
SetCompanyManagerFaceBits(cmf, CMFV_TIE_EARRING, ge, HasBit(ge, GENDER_FEMALE) ? tie_earring : ScaleCompanyManagerFaceValue(CMFV_TIE_EARRING, ge, tie_earring / 2));
}
return cmf;
}
/** Rebuilding of company statistics after loading a savegame. */
void AfterLoadCompanyStats()
{
/* Reset infrastructure statistics to zero. */
for (Company *c : Company::Iterate()) MemSetT(&c->infrastructure, 0);
/* Collect airport count. */
for (const Station *st : Station::Iterate()) {
if ((st->facilities & FACIL_AIRPORT) && Company::IsValidID(st->owner)) {
Company::Get(st->owner)->infrastructure.airport++;
}
}
Company *c;
for (TileIndex tile = 0; tile < MapSize(); tile++) {
switch (GetTileType(tile)) {
case MP_RAILWAY:
c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) {
uint pieces = 1;
if (IsPlainRail(tile)) {
TrackBits bits = GetTrackBits(tile);
if (bits == TRACK_BIT_HORZ || bits == TRACK_BIT_VERT) {
c->infrastructure.rail[GetSecondaryRailType(tile)]++;
} else {
pieces = CountBits(bits);
if (TracksOverlap(bits)) pieces *= pieces;
}
}
c->infrastructure.rail[GetRailType(tile)] += pieces;
if (HasSignals(tile)) c->infrastructure.signal += CountBits(GetPresentSignals(tile));
}
break;
case MP_ROAD: {
if (IsLevelCrossing(tile)) {
c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) c->infrastructure.rail[GetRailType(tile)] += LEVELCROSSING_TRACKBIT_FACTOR;
}
/* Iterate all present road types as each can have a different owner. */
for (RoadTramType rtt : _roadtramtypes) {
RoadType rt = GetRoadType(tile, rtt);
if (rt == INVALID_ROADTYPE) continue;
c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rtt));
/* A level crossings and depots have two road bits. */
if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rtt)) : 2;
}
break;
}
case MP_STATION:
c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr && GetStationType(tile) != STATION_AIRPORT && !IsBuoy(tile)) c->infrastructure.station++;
switch (GetStationType(tile)) {
case STATION_RAIL:
case STATION_WAYPOINT:
if (c != nullptr && !IsStationTileBlocked(tile)) c->infrastructure.rail[GetRailType(tile)]++;
break;
case STATION_BUS:
case STATION_TRUCK:
case STATION_ROADWAYPOINT: {
/* Iterate all present road types as each can have a different owner. */
for (RoadTramType rtt : _roadtramtypes) {
RoadType rt = GetRoadType(tile, rtt);
if (rt == INVALID_ROADTYPE) continue;
c = Company::GetIfValid(GetRoadOwner(tile, rtt));
if (c != nullptr) c->infrastructure.road[rt] += 2; // A road stop has two road bits.
}
break;
}
case STATION_DOCK:
case STATION_BUOY:
if (GetWaterClass(tile) == WATER_CLASS_CANAL) {
if (c != nullptr) c->infrastructure.water++;
}
break;
default:
break;
}
break;
case MP_WATER:
if (IsShipDepot(tile) || IsLock(tile)) {
c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) {
if (IsShipDepot(tile)) c->infrastructure.water += LOCK_DEPOT_TILE_FACTOR;
if (IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE) {
/* The middle tile specifies the owner of the lock. */
c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // the middle tile specifies the owner of the
break; // do not count the middle tile as canal
}
}
}
FALLTHROUGH;
case MP_OBJECT:
if (GetWaterClass(tile) == WATER_CLASS_CANAL) {
c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) c->infrastructure.water++;
}
break;
case MP_TUNNELBRIDGE: {
/* Only count the tunnel/bridge if we're on the western end tile. */
if (GetTunnelBridgeDirection(tile) < DIAGDIR_SW) {
TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
/* Count each tunnel/bridge TUNNELBRIDGE_TRACKBIT_FACTOR times to simulate
* the higher structural maintenance needs, and don't forget the end tiles. */
const uint middle_len = GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR;
switch (GetTunnelBridgeTransportType(tile)) {
case TRANSPORT_RAIL:
AddRailTunnelBridgeInfrastructure(tile, other_end);
break;
case TRANSPORT_ROAD: {
AddRoadTunnelBridgeInfrastructure(tile, other_end);
break;
}
case TRANSPORT_WATER:
c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) c->infrastructure.water += middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR);
break;
default:
break;
}
}
break;
}
default:
break;
}
}
}
/* Save/load of companies */
static const SaveLoad _company_desc[] = {
SLE_VAR(CompanyProperties, name_2, SLE_UINT32),
SLE_VAR(CompanyProperties, name_1, SLE_STRINGID),
SLE_CONDSSTR(CompanyProperties, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION),
SLE_VAR(CompanyProperties, president_name_1, SLE_STRINGID),
SLE_VAR(CompanyProperties, president_name_2, SLE_UINT32),
SLE_CONDSSTR(CompanyProperties, president_name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION),
SLE_VAR(CompanyProperties, face, SLE_UINT32),
/* money was changed to a 64 bit field in savegame version 1. */
SLE_CONDVAR(CompanyProperties, money, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_1),
SLE_CONDVAR(CompanyProperties, money, SLE_INT64, SLV_1, SL_MAX_VERSION),
SLE_CONDVAR(CompanyProperties, current_loan, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_65),
SLE_CONDVAR(CompanyProperties, current_loan, SLE_INT64, SLV_65, SL_MAX_VERSION),
SLE_VAR(CompanyProperties, colour, SLE_UINT8),
SLE_VAR(CompanyProperties, money_fraction, SLE_UINT8),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_58), ///< avail_railtypes
SLE_VAR(CompanyProperties, block_preview, SLE_UINT8),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_94), ///< cargo_types
SLE_CONDNULL(4, SLV_94, SLV_170), ///< cargo_types
SLE_CONDVAR(CompanyProperties, location_of_HQ, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(CompanyProperties, location_of_HQ, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDVAR(CompanyProperties, last_build_coordinate, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(CompanyProperties, last_build_coordinate, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDVAR(CompanyProperties, inaugurated_year, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),
SLE_CONDVAR(CompanyProperties, inaugurated_year, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLE_ARR(CompanyProperties, share_owners, SLE_UINT8, 4),
SLE_VAR(CompanyProperties, num_valid_stat_ent, SLE_UINT8),
SLE_VAR(CompanyProperties, months_of_bankruptcy, SLE_UINT8),
SLE_CONDVAR_X(CompanyProperties, bankrupt_last_asked, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BANKRUPTCY_EXTRA)),
SLE_CONDVAR_X(CompanyProperties, bankrupt_flags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BANKRUPTCY_EXTRA, 2)),
SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104),
SLE_CONDVAR(CompanyProperties, bankrupt_asked, SLE_UINT16, SLV_104, SL_MAX_VERSION),
SLE_VAR(CompanyProperties, bankrupt_timeout, SLE_INT16),
SLE_CONDVAR(CompanyProperties, bankrupt_value, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_65),
SLE_CONDVAR(CompanyProperties, bankrupt_value, SLE_INT64, SLV_65, SL_MAX_VERSION),
/* yearly expenses was changed to 64-bit in savegame version 2. */
SLE_CONDARR(CompanyProperties, yearly_expenses, SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, SL_MIN_VERSION, SLV_2),
SLE_CONDARR_X(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 13, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING, 0, 0)),
SLE_CONDARR_X(CompanyProperties, yearly_expenses, SLE_INT64, 3 * 15, SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_INFRA_SHARING)),
SLE_CONDVAR(CompanyProperties, is_ai, SLE_BOOL, SLV_2, SL_MAX_VERSION),
SLE_CONDNULL(1, SLV_107, SLV_112), ///< is_noai
SLE_CONDNULL(1, SLV_4, SLV_100),
SLE_CONDVAR(CompanyProperties, terraform_limit, SLE_UINT32, SLV_156, SL_MAX_VERSION),
SLE_CONDVAR(CompanyProperties, clear_limit, SLE_UINT32, SLV_156, SL_MAX_VERSION),
SLE_CONDVAR(CompanyProperties, tree_limit, SLE_UINT32, SLV_175, SL_MAX_VERSION),
SLE_CONDVAR_X(CompanyProperties, purchase_land_limit, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BUY_LAND_RATE_LIMIT)),
SLE_CONDVAR_X(CompanyProperties, build_object_limit, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_BUILD_OBJECT_RATE_LIMIT)),
};
static const SaveLoad _company_settings_desc[] = {
/* Engine renewal settings */
SLE_CONDNULL(512, SLV_16, SLV_19),
SLE_CONDREF(Company, engine_renew_list, REF_ENGINE_RENEWS, SLV_19, SL_MAX_VERSION),
SLE_CONDVAR(Company, settings.engine_renew, SLE_BOOL, SLV_16, SL_MAX_VERSION),
SLE_CONDVAR(Company, settings.engine_renew_months, SLE_INT16, SLV_16, SL_MAX_VERSION),
SLE_CONDVAR(Company, settings.engine_renew_money, SLE_UINT32, SLV_16, SL_MAX_VERSION),
SLE_CONDVAR(Company, settings.renew_keep_length, SLE_BOOL, SLV_2, SL_MAX_VERSION),
/* Default vehicle settings */
SLE_CONDVAR(Company, settings.vehicle.servint_ispercent, SLE_BOOL, SLV_120, SL_MAX_VERSION),
SLE_CONDVAR(Company, settings.vehicle.servint_trains, SLE_UINT16, SLV_120, SL_MAX_VERSION),
SLE_CONDVAR(Company, settings.vehicle.servint_roadveh, SLE_UINT16, SLV_120, SL_MAX_VERSION),
SLE_CONDVAR(Company, settings.vehicle.servint_aircraft, SLE_UINT16, SLV_120, SL_MAX_VERSION),
SLE_CONDVAR(Company, settings.vehicle.servint_ships, SLE_UINT16, SLV_120, SL_MAX_VERSION),
SLE_CONDVAR_X(Company, settings.vehicle.auto_timetable_by_default, SLE_BOOL, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 2, 2)),
SLE_CONDNULL(63, SLV_2, SLV_144), // old reserved space
};
static const SaveLoad _company_settings_skip_desc[] = {
/* Engine renewal settings */
SLE_CONDNULL(512, SLV_16, SLV_19),
SLE_CONDNULL(2, SLV_19, SLV_69), // engine_renew_list
SLE_CONDNULL(4, SLV_69, SL_MAX_VERSION), // engine_renew_list
SLE_CONDNULL(1, SLV_16, SL_MAX_VERSION), // settings.engine_renew
SLE_CONDNULL(2, SLV_16, SL_MAX_VERSION), // settings.engine_renew_months
SLE_CONDNULL(4, SLV_16, SL_MAX_VERSION), // settings.engine_renew_money
SLE_CONDNULL(1, SLV_2, SL_MAX_VERSION), // settings.renew_keep_length
/* Default vehicle settings */
SLE_CONDNULL(1, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_ispercent
SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_trains
SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_roadveh
SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_aircraft
SLE_CONDNULL(2, SLV_120, SL_MAX_VERSION), // settings.vehicle.servint_ships
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUTO_TIMETABLE, 2, 2)), // settings.vehicle.auto_timetable_by_default
SLE_CONDNULL(63, SLV_2, SLV_144), // old reserved space
};
static const SaveLoad _company_economy_desc[] = {
/* these were changed to 64-bit in savegame format 2 */
SLE_CONDVAR(CompanyEconomyEntry, income, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2),
SLE_CONDVAR(CompanyEconomyEntry, income, SLE_INT64, SLV_2, SL_MAX_VERSION),
SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2),
SLE_CONDVAR(CompanyEconomyEntry, expenses, SLE_INT64, SLV_2, SL_MAX_VERSION),
SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_2),
SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, SLV_2, SL_MAX_VERSION),
SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, SL_MIN_VERSION, SLV_170),
SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, SLV_170, SLV_EXTEND_CARGOTYPES),
SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION),
SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32),
};
/* We do need to read this single value, as the bigger it gets, the more data is stored */
struct CompanyOldAI {
uint8 num_build_rec;
};
static const SaveLoad _company_ai_desc[] = {
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_13),
SLE_CONDNULL(4, SLV_13, SLV_107),
SLE_CONDNULL(8, SL_MIN_VERSION, SLV_107),
SLE_CONDVAR(CompanyOldAI, num_build_rec, SLE_UINT8, SL_MIN_VERSION, SLV_107),
SLE_CONDNULL(3, SL_MIN_VERSION, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6),
SLE_CONDNULL(4, SLV_6, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6),
SLE_CONDNULL(4, SLV_6, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6),
SLE_CONDNULL(4, SLV_6, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6),
SLE_CONDNULL(4, SLV_6, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_69),
SLE_CONDNULL(4, SLV_69, SLV_107),
SLE_CONDNULL(18, SL_MIN_VERSION, SLV_107),
SLE_CONDNULL(20, SL_MIN_VERSION, SLV_107),
SLE_CONDNULL(32, SL_MIN_VERSION, SLV_107),
SLE_CONDNULL(64, SLV_2, SLV_107),
};
static const SaveLoad _company_ai_build_rec_desc[] = {
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6),
SLE_CONDNULL(4, SLV_6, SLV_107),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6),
SLE_CONDNULL(4, SLV_6, SLV_107),
SLE_CONDNULL(8, SL_MIN_VERSION, SLV_107),
};
static const SaveLoad _company_livery_desc[] = {
SLE_CONDVAR(Livery, in_use, SLE_UINT8, SLV_34, SL_MAX_VERSION),
SLE_CONDVAR(Livery, colour1, SLE_UINT8, SLV_34, SL_MAX_VERSION),
SLE_CONDVAR(Livery, colour2, SLE_UINT8, SLV_34, SL_MAX_VERSION),
};
static void SaveLoad_PLYR_common(Company *c, CompanyProperties *cprops)
{
int i;
SlObject(cprops, _company_desc);
if (c != nullptr) {
SlObject(c, _company_settings_desc);
} else {
char nothing;
SlObject(&nothing, _company_settings_skip_desc);
}
/* Keep backwards compatible for savegames, so load the old AI block */
if (IsSavegameVersionBefore(SLV_107) && cprops->is_ai) {
CompanyOldAI old_ai;
char nothing;
SlObject(&old_ai, _company_ai_desc);
for (i = 0; i != old_ai.num_build_rec; i++) {
SlObject(&nothing, _company_ai_build_rec_desc);
}
}
/* Write economy */
SlObject(&cprops->cur_economy, _company_economy_desc);
/* Write old economy entries. */
if (cprops->num_valid_stat_ent > lengthof(cprops->old_economy)) SlErrorCorrupt("Too many old economy entries");
for (i = 0; i < cprops->num_valid_stat_ent; i++) {
SlObject(&cprops->old_economy[i], _company_economy_desc);
}
/* Write each livery entry. */
int num_liveries = IsSavegameVersionBefore(SLV_63) ? LS_END - 4 : (IsSavegameVersionBefore(SLV_85) ? LS_END - 2: LS_END);
bool update_in_use = IsSavegameVersionBefore(SLV_GROUP_LIVERIES);
if (c != nullptr) {
for (i = 0; i < num_liveries; i++) {
SlObject(&c->livery[i], _company_livery_desc);
if (update_in_use && i != LS_DEFAULT) {
if (c->livery[i].in_use == 0) {
c->livery[i].colour1 = c->livery[LS_DEFAULT].colour1;
c->livery[i].colour2 = c->livery[LS_DEFAULT].colour2;
} else {
c->livery[i].in_use = 3;
}
}
}
if (num_liveries < LS_END) {
/* We want to insert some liveries somewhere in between. This means some have to be moved. */
memmove(&c->livery[LS_FREIGHT_WAGON], &c->livery[LS_PASSENGER_WAGON_MONORAIL], (LS_END - LS_FREIGHT_WAGON) * sizeof(c->livery[0]));
c->livery[LS_PASSENGER_WAGON_MONORAIL] = c->livery[LS_MONORAIL];
c->livery[LS_PASSENGER_WAGON_MAGLEV] = c->livery[LS_MAGLEV];
}
if (num_liveries == LS_END - 4) {
/* Copy bus/truck liveries over to trams */
c->livery[LS_PASSENGER_TRAM] = c->livery[LS_BUS];
c->livery[LS_FREIGHT_TRAM] = c->livery[LS_TRUCK];
}
} else {
/* Skip liveries */
Livery dummy_livery;
for (i = 0; i < num_liveries; i++) {
SlObject(&dummy_livery, _company_livery_desc);
}
}
}
static void SaveLoad_PLYR(Company *c)
{
SaveLoad_PLYR_common(c, c);
}
static void Save_PLYR()
{
for (Company *c : Company::Iterate()) {
SlSetArrayIndex(c->index);
SlAutolength((AutolengthProc*)SaveLoad_PLYR, c);
}
}
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;
// settings moved from game settings to company settings
if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 2)) {
c->settings.auto_timetable_separation_rate = _settings_game.order.old_timetable_separation_rate;
}
if (SlXvIsFeaturePresent(XSLFI_AUTO_TIMETABLE, 1, 3)) {
c->settings.vehicle.auto_separation_by_default = _settings_game.order.old_timetable_separation;
}
}
}
static void Check_PLYR()
{
int index;
while ((index = SlIterateArray()) != -1) {
CompanyProperties *cprops = new CompanyProperties();
SaveLoad_PLYR_common(nullptr, cprops);
/* We do not load old custom names */
if (IsSavegameVersionBefore(SLV_84)) {
if (GetStringTab(cprops->name_1) == TEXT_TAB_OLD_CUSTOM) {
cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE;
}
if (GetStringTab(cprops->president_name_1) == TEXT_TAB_OLD_CUSTOM) {
cprops->president_name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE;
}
}
if (cprops->name.empty() && !IsInsideMM(cprops->name_1, SPECSTR_COMPANY_NAME_START, SPECSTR_COMPANY_NAME_LAST + 1) &&
cprops->name_1 != STR_GAME_SAVELOAD_NOT_AVAILABLE && cprops->name_1 != STR_SV_UNNAMED &&
cprops->name_1 != SPECSTR_ANDCO_NAME && cprops->name_1 != SPECSTR_PRESIDENT_NAME &&
cprops->name_1 != SPECSTR_SILLY_NAME) {
cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE;
}
if (!_load_check_data.companies.Insert(index, cprops)) delete cprops;
}
}
static void Ptrs_PLYR()
{
for (Company *c : Company::Iterate()) {
SlObject(c, _company_settings_desc);
}
}
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();
}
static void Load_PLYP()
{
size_t size = SlGetFieldLength();
CompanyMask invalid_mask = 0;
if (SlXvIsFeaturePresent(XSLFI_COMPANY_PW, 2)) {
if (size <= 2) return;
invalid_mask = SlReadUint16();
size -= 2;
}
if (size <= 16 + 24 + 16 || (_networking && !_network_server)) {
SlSkipBytes(size);
return;
}
if (!_network_server) {
extern CompanyMask _saved_PLYP_invalid_mask;
extern std::vector<uint8> _saved_PLYP_data;
_saved_PLYP_invalid_mask = invalid_mask;
_saved_PLYP_data.resize(size);
ReadBuffer::GetCurrent()->CopyBytes(_saved_PLYP_data.data(), _saved_PLYP_data.size());
return;
}
uint8 token[16];
ReadBuffer::GetCurrent()->CopyBytes(token, 16);
if (memcmp(token, _network_company_password_storage_token, 16) != 0) {
DEBUG(sl, 2, "Skipping encrypted company passwords");
SlSkipBytes(size - 16);
return;
}
uint8 nonce[24];
uint8 mac[16];
ReadBuffer::GetCurrent()->CopyBytes(nonce, 24);
ReadBuffer::GetCurrent()->CopyBytes(mac, 16);
std::vector<uint8> buffer(size - 16 - 24 - 16);
ReadBuffer::GetCurrent()->CopyBytes(buffer.data(), buffer.size());
if (crypto_unlock(buffer.data(), _network_company_password_storage_key, nonce, mac, buffer.data(), buffer.size()) == 0) {
SlLoadFromBuffer(buffer.data(), buffer.size(), [invalid_mask]() {
_network_company_server_id.resize(SlReadUint32());
ReadBuffer::GetCurrent()->CopyBytes((uint8 *)_network_company_server_id.data(), _network_company_server_id.size());
while (true) {
uint16 cid = SlReadUint16();
if (cid >= MAX_COMPANIES) break;
std::string password;
password.resize(SlReadUint32());
ReadBuffer::GetCurrent()->CopyBytes((uint8 *)password.data(), password.size());
if (!HasBit(invalid_mask, cid)) {
NetworkServerSetCompanyPassword((CompanyID)cid, password, true);
}
}
ReadBuffer::GetCurrent()->SkipBytes(SlReadByte()); // Skip padding
});
DEBUG(sl, 2, "Decrypted company passwords");
} else {
DEBUG(sl, 2, "Failed to decrypt company passwords");
}
}
static void Save_PLYP()
{
if ((_networking && !_network_server) || IsNetworkServerSave()) {
SlSetLength(0);
return;
}
if (!_network_server) {
extern CompanyMask _saved_PLYP_invalid_mask;
extern std::vector<uint8> _saved_PLYP_data;
if (_saved_PLYP_data.empty()) {
SlSetLength(0);
} else {
SlSetLength(2 + _saved_PLYP_data.size());
SlWriteUint16(_saved_PLYP_invalid_mask);
MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)_saved_PLYP_data.data(), _saved_PLYP_data.size());
}
return;
}
uint8 nonce[24]; /* Use only once per key: random */
if (randombytes(nonce, 24) < 0) {
/* Can't get a random nonce, just give up */
SlSetLength(0);
return;
}
std::vector<byte> buffer = SlSaveToVector([](void *) {
SlWriteUint32((uint32)_network_company_server_id.size());
MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)_network_company_server_id.data(), _network_company_server_id.size());
for (const Company *c : Company::Iterate()) {
SlWriteUint16(c->index);
const std::string &password = _network_company_states[c->index].password;
SlWriteUint32((uint32)password.size());
MemoryDumper::GetCurrent()->CopyBytes((const uint8 *)password.data(), password.size());
}
SlWriteUint16(0xFFFF);
/* Add some random length padding to not make it too obvious from the length whether passwords are set or not */
uint8 padding[256];
if (randombytes(padding, 256) >= 0) {
SlWriteByte(padding[0]);
MemoryDumper::GetCurrent()->CopyBytes(padding + 1, padding[0]);
} else {
SlWriteByte(0);
}
}, nullptr);
uint8 mac[16]; /* Message authentication code */
/* Encrypt in place */
crypto_lock(mac, buffer.data(), _network_company_password_storage_key, nonce, buffer.data(), buffer.size());
SlSetLength(2 + 16 + 24 + 16 + buffer.size());
SlWriteUint16(0); // Invalid mask
MemoryDumper::GetCurrent()->CopyBytes(_network_company_password_storage_token, 16);
MemoryDumper::GetCurrent()->CopyBytes(nonce, 24);
MemoryDumper::GetCurrent()->CopyBytes(mac, 16);
MemoryDumper::GetCurrent()->CopyBytes(buffer.data(), buffer.size());
}
static const ChunkHandler company_chunk_handlers[] = {
{ 'PLYR', Save_PLYR, Load_PLYR, Ptrs_PLYR, Check_PLYR, CH_ARRAY },
{ 'PLYX', Save_PLYX, Load_PLYX, nullptr, Check_PLYX, CH_RIFF },
{ 'PLYP', Save_PLYP, Load_PLYP, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _company_chunk_handlers(company_chunk_handlers);

94
src/sl/debug_sl.cpp Normal file
View File

@@ -0,0 +1,94 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file debug_sl.cpp Code handling saving and loading of debugging information */
#include "../stdafx.h"
#include "../debug.h"
#include "saveload.h"
#include "saveload_buffer.h"
#include "../fios.h"
#include "../safeguards.h"
static void Save_DBGL()
{
if (_savegame_DBGL_data != nullptr) {
size_t length = strlen(_savegame_DBGL_data);
SlSetLength(length);
MemoryDumper::GetCurrent()->CopyBytes(reinterpret_cast<const byte *>(_savegame_DBGL_data), length);
} else {
SlSetLength(0);
}
}
static void Load_DBGL()
{
size_t length = SlGetFieldLength();
if (length) {
_loadgame_DBGL_data.resize(length);
ReadBuffer::GetCurrent()->CopyBytes(reinterpret_cast<byte *>(_loadgame_DBGL_data.data()), length);
}
}
static void Check_DBGL()
{
if (!_load_check_data.want_debug_data) {
SlSkipBytes(SlGetFieldLength());
return;
}
size_t length = SlGetFieldLength();
if (length) {
_load_check_data.debug_log_data.resize(length);
ReadBuffer::GetCurrent()->CopyBytes(reinterpret_cast<byte *>(_load_check_data.debug_log_data.data()), length);
}
}
static void Save_DBGC()
{
extern std::string _config_file_text;
const char header[] = "*** openttd.cfg start ***\n";
const char footer[] = "*** openttd.cfg end ***\n";
if (_save_DBGC_data) {
SlSetLength(lengthof(header) + _config_file_text.size() + lengthof(footer) - 2);
MemoryDumper::GetCurrent()->CopyBytes(reinterpret_cast<const byte *>(header), lengthof(header) - 1);
MemoryDumper::GetCurrent()->CopyBytes(reinterpret_cast<const byte *>(_config_file_text.data()), _config_file_text.size());
MemoryDumper::GetCurrent()->CopyBytes(reinterpret_cast<const byte *>(footer), lengthof(footer) - 1);
} else {
SlSetLength(0);
}
}
static void Load_DBGC()
{
size_t length = SlGetFieldLength();
if (length) {
_loadgame_DBGC_data.resize(length);
ReadBuffer::GetCurrent()->CopyBytes(reinterpret_cast<byte *>(_loadgame_DBGC_data.data()), length);
}
}
static void Check_DBGC()
{
if (!_load_check_data.want_debug_data) {
SlSkipBytes(SlGetFieldLength());
return;
}
size_t length = SlGetFieldLength();
if (length) {
_load_check_data.debug_config_data.resize(length);
ReadBuffer::GetCurrent()->CopyBytes(reinterpret_cast<byte *>(_load_check_data.debug_config_data.data()), length);
}
}
extern const ChunkHandler debug_chunk_handlers[] = {
{ 'DBGL', Save_DBGL, Load_DBGL, nullptr, Check_DBGL, CH_RIFF },
{ 'DBGC', Save_DBGC, Load_DBGC, nullptr, Check_DBGC, CH_RIFF },
};
extern const ChunkHandlerTable _debug_chunk_handlers(debug_chunk_handlers);

64
src/sl/depot_sl.cpp Normal file
View File

@@ -0,0 +1,64 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file depot_sl.cpp Code handling saving and loading of depots */
#include "../stdafx.h"
#include "../depot_base.h"
#include "../town.h"
#include "saveload.h"
#include "../safeguards.h"
static TownID _town_index;
static const SaveLoad _depot_desc[] = {
SLE_CONDVAR(Depot, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Depot, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLEG_CONDVAR(_town_index, SLE_UINT16, SL_MIN_VERSION, SLV_141),
SLE_CONDREF(Depot, town, REF_TOWN, SLV_141, SL_MAX_VERSION),
SLE_CONDVAR(Depot, town_cn, SLE_UINT16, SLV_141, SL_MAX_VERSION),
SLE_CONDSTR(Depot, name, SLE_STR, 0, SLV_141, SL_MAX_VERSION),
SLE_CONDVAR(Depot, build_date, SLE_INT32, SLV_142, SL_MAX_VERSION),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 5)),
};
static void Save_DEPT()
{
for (Depot *depot : Depot::Iterate()) {
SlSetArrayIndex(depot->index);
SlObject(depot, _depot_desc);
}
}
static void Load_DEPT()
{
int index;
while ((index = SlIterateArray()) != -1) {
Depot *depot = new (index) Depot();
SlObject(depot, _depot_desc);
/* Set the town 'pointer' so we can restore it later. */
if (IsSavegameVersionBefore(SLV_141)) depot->town = (Town *)(size_t)_town_index;
}
}
static void Ptrs_DEPT()
{
for (Depot *depot : Depot::Iterate()) {
SlObject(depot, _depot_desc);
if (IsSavegameVersionBefore(SLV_141)) depot->town = Town::Get((size_t)depot->town);
}
}
static const ChunkHandler depot_chunk_handlers[] = {
{ 'DEPT', Save_DEPT, Load_DEPT, Ptrs_DEPT, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _depot_chunk_handlers(depot_chunk_handlers);

105
src/sl/economy_sl.cpp Normal file
View File

@@ -0,0 +1,105 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file economy_sl.cpp Code handling saving and loading of economy data */
#include "../stdafx.h"
#include "../economy_func.h"
#include "../economy_base.h"
#include "saveload.h"
#include "../safeguards.h"
/** Prices in pre 126 savegames */
static void Load_PRIC()
{
/* Old games store 49 base prices, very old games store them as int32 */
int vt = IsSavegameVersionBefore(SLV_65) ? SLE_FILE_I32 : SLE_FILE_I64;
SlArray(nullptr, 49, vt | SLE_VAR_NULL);
SlArray(nullptr, 49, SLE_FILE_U16 | SLE_VAR_NULL);
}
/** Cargo payment rates in pre 126 savegames */
static void Load_CAPR()
{
uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO;
int vt = IsSavegameVersionBefore(SLV_65) ? SLE_FILE_I32 : SLE_FILE_I64;
SlArray(nullptr, num_cargo, vt | SLE_VAR_NULL);
SlArray(nullptr, num_cargo, SLE_FILE_U16 | SLE_VAR_NULL);
}
static const SaveLoad _economy_desc[] = {
SLE_CONDNULL(4, SL_MIN_VERSION, SLV_65), // max_loan
SLE_CONDNULL(8, SLV_65, SLV_144), // max_loan
SLE_CONDVAR(Economy, old_max_loan_unround, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SLV_65),
SLE_CONDVAR(Economy, old_max_loan_unround, SLE_INT64, SLV_65, SLV_126),
SLE_CONDVAR(Economy, old_max_loan_unround_fract, SLE_UINT16, SLV_70, SLV_126),
SLE_CONDVAR(Economy, inflation_prices, SLE_UINT64, SLV_126, SL_MAX_VERSION),
SLE_CONDVAR(Economy, inflation_payment, SLE_UINT64, SLV_126, SL_MAX_VERSION),
SLE_VAR(Economy, fluct, SLE_INT16),
SLE_VAR(Economy, interest_rate, SLE_UINT8),
SLE_VAR(Economy, infl_amount, SLE_UINT8),
SLE_VAR(Economy, infl_amount_pr, SLE_UINT8),
SLE_CONDVAR(Economy, industry_daily_change_counter, SLE_UINT32, SLV_102, SL_MAX_VERSION),
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
};
/** Economy variables */
static void Save_ECMY()
{
SlObject(&_economy, _economy_desc);
}
/** Economy variables */
static void Load_ECMY()
{
SlObject(&_economy, _economy_desc);
StartupIndustryDailyChanges(IsSavegameVersionBefore(SLV_102)); // old savegames will need to be initialized
}
static const SaveLoad _cargopayment_desc[] = {
SLE_REF(CargoPayment, front, REF_VEHICLE),
SLE_VAR(CargoPayment, route_profit, SLE_INT64),
SLE_VAR(CargoPayment, visual_profit, SLE_INT64),
SLE_CONDVAR_X(CargoPayment, visual_transfer, SLE_INT64, SLV_181, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP)),
};
static void Save_CAPY()
{
for (CargoPayment *cp : CargoPayment::Iterate()) {
SlSetArrayIndex(cp->index);
SlObject(cp, _cargopayment_desc);
}
}
static void Load_CAPY()
{
int index;
while ((index = SlIterateArray()) != -1) {
CargoPayment *cp = new (index) CargoPayment();
SlObject(cp, _cargopayment_desc);
}
}
static void Ptrs_CAPY()
{
for (CargoPayment *cp : CargoPayment::Iterate()) {
SlObject(cp, _cargopayment_desc);
}
}
static const ChunkHandler economy_chunk_handlers[] = {
{ 'CAPY', Save_CAPY, Load_CAPY, Ptrs_CAPY, nullptr, CH_ARRAY },
{ 'PRIC', nullptr, Load_PRIC, nullptr, nullptr, CH_RIFF },
{ 'CAPR', nullptr, Load_CAPR, nullptr, nullptr, CH_RIFF },
{ 'ECMY', Save_ECMY, Load_ECMY, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _economy_chunk_handlers(economy_chunk_handlers);

208
src/sl/engine_sl.cpp Normal file
View File

@@ -0,0 +1,208 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file engine_sl.cpp Code handling saving and loading of engines */
#include "../stdafx.h"
#include "saveload_internal.h"
#include "../engine_base.h"
#include "../engine_func.h"
#include "../string_func.h"
#include <vector>
#include "../safeguards.h"
static const SaveLoad _engine_desc[] = {
SLE_CONDVAR(Engine, intro_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),
SLE_CONDVAR(Engine, intro_date, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLE_CONDVAR(Engine, age, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),
SLE_CONDVAR(Engine, age, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLE_VAR(Engine, reliability, SLE_UINT16),
SLE_VAR(Engine, reliability_spd_dec, SLE_UINT16),
SLE_VAR(Engine, reliability_start, SLE_UINT16),
SLE_VAR(Engine, reliability_max, SLE_UINT16),
SLE_VAR(Engine, reliability_final, SLE_UINT16),
SLE_VAR(Engine, duration_phase_1, SLE_UINT16),
SLE_VAR(Engine, duration_phase_2, SLE_UINT16),
SLE_VAR(Engine, duration_phase_3, SLE_UINT16),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_121),
SLE_VAR(Engine, flags, SLE_UINT8),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_179), // old preview_company_rank
SLE_CONDVAR(Engine, preview_asked, SLE_UINT16, SLV_179, SL_MAX_VERSION),
SLE_CONDVAR(Engine, preview_company, SLE_UINT8, SLV_179, SL_MAX_VERSION),
SLE_VAR(Engine, preview_wait, SLE_UINT8),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_45),
SLE_CONDVAR(Engine, company_avail, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104),
SLE_CONDVAR(Engine, company_avail, SLE_UINT16, SLV_104, SL_MAX_VERSION),
SLE_CONDVAR(Engine, company_hidden, SLE_UINT16, SLV_193, SL_MAX_VERSION),
SLE_CONDSTR(Engine, name, SLE_STR, 0, SLV_84, SL_MAX_VERSION),
SLE_CONDNULL(16, SLV_2, SLV_144), // old reserved space
};
static std::vector<Engine*> _temp_engine;
/**
* Allocate an Engine structure, but not using the pools.
* The allocated Engine must be freed using FreeEngine;
* @return Allocated engine.
*/
static Engine* CallocEngine()
{
uint8 *zero = CallocT<uint8>(sizeof(Engine));
Engine *engine = new (zero) Engine();
return engine;
}
/**
* Deallocate an Engine constructed by CallocEngine.
* @param e Engine to free.
*/
static void FreeEngine(Engine *e)
{
if (e != nullptr) {
e->~Engine();
free(e);
}
}
Engine *GetTempDataEngine(EngineID index)
{
if (index < _temp_engine.size()) {
return _temp_engine[index];
} else if (index == _temp_engine.size()) {
_temp_engine.push_back(CallocEngine());
return _temp_engine[index];
} else {
NOT_REACHED();
}
}
static void Save_ENGN()
{
for (Engine *e : Engine::Iterate()) {
SlSetArrayIndex(e->index);
SlObject(e, _engine_desc);
}
}
static void Load_ENGN()
{
/* As engine data is loaded before engines are initialized we need to load
* this information into a temporary array. This is then copied into the
* engine pool after processing NewGRFs by CopyTempEngineData(). */
int index;
while ((index = SlIterateArray()) != -1) {
Engine *e = GetTempDataEngine(index);
SlObject(e, _engine_desc);
if (IsSavegameVersionBefore(SLV_179)) {
/* preview_company_rank was replaced with preview_company and preview_asked.
* Just cancel any previews. */
e->flags &= ~4; // ENGINE_OFFER_WINDOW_OPEN
e->preview_company = INVALID_COMPANY;
e->preview_asked = MAX_UVALUE(CompanyMask);
}
}
}
/**
* Copy data from temporary engine array into the real engine pool.
*/
void CopyTempEngineData()
{
for (Engine *e : Engine::Iterate()) {
if (e->index >= _temp_engine.size()) break;
const Engine *se = GetTempDataEngine(e->index);
e->intro_date = se->intro_date;
e->age = se->age;
e->reliability = se->reliability;
e->reliability_spd_dec = se->reliability_spd_dec;
e->reliability_start = se->reliability_start;
e->reliability_max = se->reliability_max;
e->reliability_final = se->reliability_final;
e->duration_phase_1 = se->duration_phase_1;
e->duration_phase_2 = se->duration_phase_2;
e->duration_phase_3 = se->duration_phase_3;
e->flags = se->flags;
e->preview_asked = se->preview_asked;
e->preview_company = se->preview_company;
e->preview_wait = se->preview_wait;
e->company_avail = se->company_avail;
e->company_hidden = se->company_hidden;
e->name = se->name;
}
ResetTempEngineData();
}
void ResetTempEngineData()
{
/* Get rid of temporary data */
for (std::vector<Engine*>::iterator it = _temp_engine.begin(); it != _temp_engine.end(); ++it) {
FreeEngine(*it);
}
_temp_engine.clear();
}
static void Load_ENGS()
{
/* Load old separate String ID list into a temporary array. This
* was always 256 entries. */
StringID names[256];
SlArray(names, lengthof(names), SLE_STRINGID);
/* Copy each string into the temporary engine array. */
for (EngineID engine = 0; engine < lengthof(names); engine++) {
Engine *e = GetTempDataEngine(engine);
e->name = CopyFromOldName(names[engine]);
}
}
/** Save and load the mapping between the engine id in the pool, and the grf file it came from. */
static const SaveLoad _engine_id_mapping_desc[] = {
SLE_VAR(EngineIDMapping, grfid, SLE_UINT32),
SLE_VAR(EngineIDMapping, internal_id, SLE_UINT16),
SLE_VAR(EngineIDMapping, type, SLE_UINT8),
SLE_VAR(EngineIDMapping, substitute_id, SLE_UINT8),
};
static void Save_EIDS()
{
uint index = 0;
for (EngineIDMapping &eid : _engine_mngr) {
SlSetArrayIndex(index);
SlObject(&eid, _engine_id_mapping_desc);
index++;
}
}
static void Load_EIDS()
{
_engine_mngr.clear();
while (SlIterateArray() != -1) {
EngineIDMapping *eid = &_engine_mngr.emplace_back();
SlObject(eid, _engine_id_mapping_desc);
}
}
void AfterLoadEngines()
{
AnalyseEngineCallbacks();
}
static const ChunkHandler engine_chunk_handlers[] = {
{ 'EIDS', Save_EIDS, Load_EIDS, nullptr, nullptr, CH_ARRAY },
{ 'ENGN', Save_ENGN, Load_ENGN, nullptr, nullptr, CH_ARRAY },
{ 'ENGS', nullptr, Load_ENGS, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _engine_chunk_handlers(engine_chunk_handlers);

758
src/sl/extended_ver_sl.cpp Normal file
View File

@@ -0,0 +1,758 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file extended_ver_sl.cpp Functions related to handling save/load extended version info.
*
* Known extended features are stored in _sl_xv_feature_versions, features which are currently enabled/in use and their versions are stored in the savegame.
* On load, the list of features and their versions are loaded from the savegame. If the savegame contains a feature which is either unknown, or has too high a version,
* loading can be either aborted, or the feature can be ignored if the the feature flags in the savegame indicate that it can be ignored. The savegame may also list any additional
* chunk IDs which are associated with an extended feature, these can be discarded if the feature is discarded.
* This information is stored in the SLXI chunk, the contents of which has the following format:
*
* uint32 chunk version
* uint32 chunk flags
* uint32 number of sub chunks/features
* For each of N sub chunk/feature:
* uint32 feature flags (SlxiSubChunkFlags)
* uint16 feature version
* SLE_STR feature name
* uint32* extra data length [only present iff feature flags & XSCF_EXTRA_DATA_PRESENT]
* N bytes extra data
* uint32* chunk ID list count [only present iff feature flags & XSCF_CHUNK_ID_LIST_PRESENT]
* N x uint32 chunk ID list
*
* Extended features as recorded in the SLXI chunk, above, MAY add, remove, change, or otherwise modify fields in chunks
* not owned by the feature and therefore not listed in the sub chunk/feature information in the SLXI chunk.
* In this case the XSCF_IGNORABLE_UNKNOWN flag SHOULD NOT be set, as it is not possible to correctly load the modified chunk without
* knowledge of the feature.
* In the case where the modifications to other chunks vary with respect to lower feature versions, the XSCF_IGNORABLE_VERSION flag
* also SHOULD NOT be set.
* Use of the XSCF_IGNORABLE_UNKNOWN and XSCF_IGNORABLE_VERSION flags MUST ONLY be used in the cases where the feature and any
* associated chunks can be cleanly dropped, and the savegame can be correctly loaded by a client with no knowledge of the feature.
*/
#include "../stdafx.h"
#include "../debug.h"
#include "saveload.h"
#include "saveload_buffer.h"
#include "extended_ver_sl.h"
#include "../timetable.h"
#include "../map_func.h"
#include "../rev.h"
#include "../strings_func.h"
#include "../company_func.h"
#include "table/strings.h"
#include <vector>
#include "../safeguards.h"
std::array<uint16, XSLFI_SIZE> _sl_xv_feature_versions; ///< array of all known feature types and their current versions
std::array<uint16, XSLFI_SIZE> _sl_xv_feature_static_versions; ///< array of all known feature types and their static current version versions
bool _sl_is_ext_version; ///< is this an extended savegame version, with more info in the SLXI chunk?
bool _sl_is_faked_ext; ///< is this a faked extended savegame version, with no SLXI chunk? See: SlXvCheckSpecialSavegameVersions.
bool _sl_maybe_springpp; ///< is this possibly a SpringPP savegame?
bool _sl_maybe_chillpp; ///< is this possibly a ChillPP v8 savegame?
bool _sl_upstream_mode; ///< load game using upstream loader
std::vector<uint32> _sl_xv_discardable_chunk_ids; ///< list of chunks IDs which we can discard if no chunk loader exists
std::string _sl_xv_version_label; ///< optional SLXI version label
SaveLoadVersion _sl_xv_upstream_version; ///< optional SLXI upstream version
static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version of SLXI chunk
static void loadVL(const SlxiSubChunkInfo *info, uint32 length);
static uint32 saveVL(const SlxiSubChunkInfo *info, bool dry_run);
static void loadUV(const SlxiSubChunkInfo *info, uint32 length);
static uint32 saveUV(const SlxiSubChunkInfo *info, bool dry_run);
static void loadLC(const SlxiSubChunkInfo *info, uint32 length);
static uint32 saveLC(const SlxiSubChunkInfo *info, bool dry_run);
const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_VERSION_LABEL, XSCF_IGNORABLE_ALL, 1, 1, "version_label", saveVL, loadVL, nullptr },
{ XSLFI_UPSTREAM_VERSION, XSCF_NULL, 1, 1, "upstream_version", saveUV, loadUV, nullptr },
{ XSLFI_TRACE_RESTRICT, XSCF_NULL, 15, 15, "tracerestrict", nullptr, nullptr, "TRRM,TRRP,TRRS" },
{ XSLFI_TRACE_RESTRICT_OWNER, XSCF_NULL, 1, 1, "tracerestrict_owner", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_ORDRCND, XSCF_NULL, 4, 4, "tracerestrict_order_cond", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_STATUSCND, XSCF_NULL, 2, 2, "tracerestrict_status_cond", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_REVERSE, XSCF_NULL, 1, 1, "tracerestrict_reverse", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_NEWSCTRL, XSCF_NULL, 1, 1, "tracerestrict_newsctrl", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_COUNTER, XSCF_NULL, 1, 1, "tracerestrict_counter", nullptr, nullptr, "TRRC" },
{ XSLFI_TRACE_RESTRICT_TIMEDATE, XSCF_NULL, 2, 2, "tracerestrict_timedate", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_BRKCND, XSCF_NULL, 3, 3, "tracerestrict_braking_cond", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_CTGRYCND, XSCF_NULL, 1, 1, "tracerestrict_ctgry_cond", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_PENCTRL, XSCF_NULL, 1, 1, "tracerestrict_pfpenctrl", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_TUNBRIDGE, XSCF_NULL, 1, 1, "tracerestrict_sigtunbridge", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_SPDADAPTCTRL, XSCF_NULL, 1, 1, "tracerestrict_spdadaptctrl", nullptr, nullptr, nullptr },
{ XSLFI_PROG_SIGS, XSCF_NULL, 2, 2, "programmable_signals", nullptr, nullptr, "SPRG" },
{ XSLFI_ADJACENT_CROSSINGS, XSCF_NULL, 1, 1, "adjacent_crossings", nullptr, nullptr, nullptr },
{ XSLFI_SAFER_CROSSINGS, XSCF_NULL, 1, 1, "safer_crossings", nullptr, nullptr, nullptr },
{ XSLFI_DEPARTURE_BOARDS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "departure_boards", nullptr, nullptr, nullptr },
{ XSLFI_TIMETABLES_START_TICKS, XSCF_NULL, 2, 2, "timetable_start_ticks", nullptr, nullptr, nullptr },
{ XSLFI_TOWN_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 2, 2, "town_cargo_adj", nullptr, nullptr, nullptr },
{ XSLFI_SIG_TUNNEL_BRIDGE, XSCF_NULL, 10, 10, "signal_tunnel_bridge", nullptr, nullptr, "XBSS" },
{ XSLFI_IMPROVED_BREAKDOWNS, XSCF_NULL, 8, 8, "improved_breakdowns", nullptr, nullptr, nullptr },
{ XSLFI_CONSIST_BREAKDOWN_FLAG, XSCF_NULL, 1, 1, "consist_breakdown_flag", nullptr, nullptr, nullptr },
{ XSLFI_TT_WAIT_IN_DEPOT, XSCF_NULL, 1, 1, "tt_wait_in_depot", nullptr, nullptr, nullptr },
{ XSLFI_AUTO_TIMETABLE, XSCF_NULL, 5, 5, "auto_timetables", nullptr, nullptr, nullptr },
{ XSLFI_VEHICLE_REPAIR_COST, XSCF_NULL, 2, 2, "vehicle_repair_cost", nullptr, nullptr, nullptr },
{ XSLFI_ENH_VIEWPORT_PLANS, XSCF_IGNORABLE_ALL, 4, 4, "enh_viewport_plans", nullptr, nullptr, "PLAN" },
{ XSLFI_INFRA_SHARING, XSCF_NULL, 2, 2, "infra_sharing", nullptr, nullptr, "CPDP" },
{ XSLFI_VARIABLE_DAY_LENGTH, XSCF_NULL, 3, 3, "variable_day_length", nullptr, nullptr, nullptr },
{ XSLFI_ORDER_OCCUPANCY, XSCF_NULL, 2, 2, "order_occupancy", nullptr, nullptr, nullptr },
{ XSLFI_MORE_COND_ORDERS, XSCF_NULL, 14, 14, "more_cond_orders", nullptr, nullptr, nullptr },
{ XSLFI_EXTRA_LARGE_MAP, XSCF_NULL, 0, 1, "extra_large_map", nullptr, nullptr, nullptr },
{ XSLFI_REVERSE_AT_WAYPOINT, XSCF_NULL, 1, 1, "reverse_at_waypoint", nullptr, nullptr, nullptr },
{ XSLFI_VEH_LIFETIME_PROFIT, XSCF_NULL, 1, 1, "veh_lifetime_profit", nullptr, nullptr, nullptr },
{ XSLFI_LINKGRAPH_DAY_SCALE, XSCF_NULL, 3, 3, "linkgraph_day_scale", nullptr, nullptr, nullptr },
{ XSLFI_TEMPLATE_REPLACEMENT, XSCF_NULL, 9, 9, "template_replacement", nullptr, nullptr, "TRPL,TMPL" },
{ XSLFI_MORE_RAIL_TYPES, XSCF_NULL, 0, 1, "more_rail_types", nullptr, nullptr, nullptr },
{ XSLFI_CARGO_TYPE_ORDERS, XSCF_NULL, 3, 3, "cargo_type_orders", nullptr, nullptr, "ORDX,VEOX" },
{ XSLFI_EXTENDED_GAMELOG, XSCF_NULL, 1, 1, "extended_gamelog", nullptr, nullptr, nullptr },
{ XSLFI_STATION_CATCHMENT_INC, XSCF_NULL, 1, 1, "station_catchment_inc", nullptr, nullptr, nullptr },
{ XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 4, 4, "custom_bridge_heads", nullptr, nullptr, nullptr },
{ XSLFI_CHUNNEL, XSCF_NULL, 2, 2, "chunnel", nullptr, nullptr, "TUNN" },
{ XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 4, 4, "scheduled_dispatch", nullptr, nullptr, nullptr },
{ XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL, 1, 1, "more_town_growth_rates", nullptr, nullptr, nullptr },
{ XSLFI_MULTIPLE_DOCKS, XSCF_NULL, 2, 2, "multiple_docks", nullptr, nullptr, nullptr },
{ XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 7, 7, "timetable_extra", nullptr, nullptr, "ORDX" },
{ XSLFI_TRAIN_FLAGS_EXTRA, XSCF_NULL, 1, 1, "train_flags_extra", nullptr, nullptr, nullptr },
{ XSLFI_VEHICLE_FLAGS_EXTRA, XSCF_NULL, 1, 1, "veh_flags_extra", nullptr, nullptr, nullptr },
{ XSLFI_TRAIN_THROUGH_LOAD, XSCF_NULL, 2, 2, "train_through_load", nullptr, nullptr, nullptr },
{ XSLFI_ORDER_EXTRA_DATA, XSCF_NULL, 2, 2, "order_extra_data", nullptr, nullptr, nullptr },
{ XSLFI_WHOLE_MAP_CHUNK, XSCF_NULL, 2, 2, "whole_map_chunk", nullptr, nullptr, "WMAP" },
{ XSLFI_ST_LAST_VEH_TYPE, XSCF_NULL, 1, 1, "station_last_veh_type", nullptr, nullptr, nullptr },
{ XSLFI_SELL_AT_DEPOT_ORDER, XSCF_NULL, 1, 1, "sell_at_depot_order", nullptr, nullptr, nullptr },
{ XSLFI_BUY_LAND_RATE_LIMIT, XSCF_NULL, 1, 1, "buy_land_rate_limit", nullptr, nullptr, nullptr },
{ XSLFI_DUAL_RAIL_TYPES, XSCF_NULL, 1, 1, "dual_rail_types", nullptr, nullptr, nullptr },
{ XSLFI_CONSIST_SPEED_RD_FLAG, XSCF_NULL, 1, 1, "consist_speed_rd_flag", nullptr, nullptr, nullptr },
{ XSLFI_SAVEGAME_UNIQUE_ID, XSCF_IGNORABLE_ALL, 1, 1, "savegame_unique_id", nullptr, nullptr, nullptr },
{ XSLFI_RV_OVERTAKING, XSCF_NULL, 2, 2, "roadveh_overtaking", nullptr, nullptr, nullptr },
{ XSLFI_LINKGRAPH_MODES, XSCF_NULL, 1, 1, "linkgraph_modes", nullptr, nullptr, nullptr },
{ XSLFI_GAME_EVENTS, XSCF_NULL, 1, 1, "game_events", nullptr, nullptr, nullptr },
{ XSLFI_ROAD_LAYOUT_CHANGE_CTR, XSCF_NULL, 1, 1, "road_layout_change_ctr", nullptr, nullptr, nullptr },
{ XSLFI_TOWN_CARGO_MATRIX, XSCF_NULL, 0, 1, "town_cargo_matrix", nullptr, nullptr, nullptr },
{ XSLFI_STATE_CHECKSUM, XSCF_NULL, 1, 1, "state_checksum", nullptr, nullptr, nullptr },
{ XSLFI_DEBUG, XSCF_IGNORABLE_ALL, 1, 1, "debug", nullptr, nullptr, "DBGL,DBGC" },
{ XSLFI_FLOW_STAT_FLAGS, XSCF_NULL, 1, 1, "flow_stat_flags", nullptr, nullptr, nullptr },
{ XSLFI_SPEED_RESTRICTION, XSCF_NULL, 1, 1, "speed_restriction", nullptr, nullptr, "VESR" },
{ XSLFI_STATION_GOODS_EXTRA, XSCF_NULL, 1, 1, "station_goods_extra", nullptr, nullptr, nullptr },
{ XSLFI_DOCKING_CACHE_VER, XSCF_IGNORABLE_ALL, 3, 3, "docking_cache_ver", nullptr, nullptr, nullptr },
{ XSLFI_EXTRA_CHEATS, XSCF_NULL, 1, 1, "extra_cheats", nullptr, nullptr, "CHTX" },
{ XSLFI_TOWN_MULTI_BUILDING, XSCF_NULL, 1, 1, "town_multi_building", nullptr, nullptr, nullptr },
{ XSLFI_SHIP_LOST_COUNTER, XSCF_NULL, 1, 1, "ship_lost_counter", nullptr, nullptr, nullptr },
{ XSLFI_BUILD_OBJECT_RATE_LIMIT, XSCF_NULL, 1, 1, "build_object_rate_limit", nullptr, nullptr, nullptr },
{ XSLFI_LOCAL_COMPANY, XSCF_IGNORABLE_ALL, 1, 1, "local_company", saveLC, loadLC, nullptr },
{ XSLFI_THROUGH_TRAIN_DEPOT, XSCF_NULL, 1, 1, "drive_through_train_depot", nullptr, nullptr, nullptr },
{ XSLFI_MORE_VEHICLE_ORDERS, XSCF_NULL, 1, 1, "more_veh_orders", nullptr, nullptr, nullptr },
{ XSLFI_ORDER_FLAGS_EXTRA, XSCF_NULL, 1, 1, "order_flags_extra", nullptr, nullptr, nullptr },
{ XSLFI_ONE_WAY_DT_ROAD_STOP, XSCF_NULL, 1, 1, "one_way_dt_road_stop", nullptr, nullptr, nullptr },
{ XSLFI_ONE_WAY_ROAD_STATE, XSCF_NULL, 1, 1, "one_way_road_state", nullptr, nullptr, nullptr },
{ XSLFI_VENC_CHUNK, XSCF_IGNORABLE_ALL, 0, 1, "venc_chunk", nullptr, nullptr, "VENC" },
{ XSLFI_ANIMATED_TILE_EXTRA, XSCF_NULL, 1, 1, "animated_tile_extra", nullptr, nullptr, nullptr },
{ XSLFI_NEWGRF_INFO_EXTRA, XSCF_NULL, 1, 1, "newgrf_info_extra", nullptr, nullptr, nullptr },
{ XSLFI_INDUSTRY_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 1, 1, "industry_cargo_adj", nullptr, nullptr, nullptr },
{ XSLFI_REALISTIC_TRAIN_BRAKING, XSCF_NULL, 9, 9, "realistic_train_braking", nullptr, nullptr, "VLKA" },
{ XSLFI_INFLATION_FIXED_DATES, XSCF_IGNORABLE_ALL, 1, 1, "inflation_fixed_dates", nullptr, nullptr, nullptr },
{ XSLFI_WATER_FLOODING, XSCF_NULL, 2, 2, "water_flooding", nullptr, nullptr, nullptr },
{ XSLFI_MORE_HOUSES, XSCF_NULL, 2, 2, "more_houses", nullptr, nullptr, nullptr },
{ XSLFI_CUSTOM_TOWN_ZONE, XSCF_IGNORABLE_UNKNOWN, 1, 1, "custom_town_zone", nullptr, nullptr, nullptr },
{ XSLFI_STATION_CARGO_HISTORY, XSCF_NULL, 2, 2, "station_cargo_history", nullptr, nullptr, nullptr },
{ XSLFI_TRAIN_SPEED_ADAPTATION, XSCF_NULL, 2, 2, "train_speed_adaptation", nullptr, nullptr, "TSAS" },
{ XSLFI_EXTRA_STATION_NAMES, XSCF_NULL, 1, 1, "extra_station_names", nullptr, nullptr, nullptr },
{ XSLFI_DEPOT_ORDER_EXTRA_FLAGS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "depot_order_extra_flags", nullptr, nullptr, nullptr },
{ XSLFI_EXTRA_SIGNAL_TYPES, XSCF_NULL, 1, 1, "extra_signal_types", nullptr, nullptr, nullptr },
{ XSLFI_BANKRUPTCY_EXTRA, XSCF_NULL, 2, 2, "bankruptcy_extra", nullptr, nullptr, nullptr },
{ XSLFI_OBJECT_GROUND_TYPES, XSCF_NULL, 3, 3, "object_ground_types", nullptr, nullptr, nullptr },
{ XSLFI_LINKGRAPH_AIRCRAFT, XSCF_NULL, 1, 1, "linkgraph_aircraft", nullptr, nullptr, nullptr },
{ XSLFI_COMPANY_PW, XSCF_IGNORABLE_ALL, 2, 2, "company_password", nullptr, nullptr, "PLYP" },
{ XSLFI_ST_INDUSTRY_CARGO_MODE, XSCF_IGNORABLE_UNKNOWN, 1, 1, "st_industry_cargo_mode", nullptr, nullptr, nullptr },
{ XSLFI_TL_SPEED_LIMIT, XSCF_IGNORABLE_UNKNOWN, 1, 1, "tl_speed_limit", nullptr, nullptr, nullptr },
{ XSLFI_RAIL_DEPOT_SPEED_LIMIT, XSCF_IGNORABLE_UNKNOWN, 1, 1, "rail_depot_speed_limit", nullptr, nullptr, nullptr },
{ XSLFI_WAYPOINT_FLAGS, XSCF_NULL, 1, 1, "waypoint_flags", nullptr, nullptr, nullptr },
{ XSLFI_ROAD_WAYPOINTS, XSCF_NULL, 1, 1, "road_waypoints", nullptr, nullptr, nullptr },
{ XSLFI_MORE_STATION_TYPES, XSCF_NULL, 1, 1, "more_station_types", nullptr, nullptr, nullptr },
{ XSLFI_RV_ORDER_EXTRA_FLAGS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "rv_order_extra_flags", nullptr, nullptr, nullptr },
{ XSLFI_GRF_ROADSTOPS, XSCF_NULL, 3, 3, "grf_road_stops", nullptr, nullptr, nullptr },
{ XSLFI_INDUSTRY_ANIM_MASK, XSCF_IGNORABLE_ALL, 1, 1, "industry_anim_mask", nullptr, nullptr, nullptr },
{ XSLFI_NEW_SIGNAL_STYLES, XSCF_NULL, 2, 2, "new_signal_styles", nullptr, nullptr, "XBST,NSID" },
{ XSLFI_NO_TREE_COUNTER, XSCF_IGNORABLE_ALL, 1, 1, "no_tree_counter", nullptr, nullptr, nullptr },
{ XSLFI_TOWN_SETTING_OVERRIDE, XSCF_NULL, 1, 1, "town_setting_override", nullptr, nullptr, nullptr },
{ XSLFI_LINKGRAPH_SPARSE_EDGES, XSCF_NULL, 1, 1, "linkgraph_sparse_edges", nullptr, nullptr, nullptr },
{ XSLFI_AUX_TILE_LOOP, XSCF_NULL, 1, 1, "aux_tile_loop", nullptr, nullptr, nullptr },
{ XSLFI_NEWGRF_ENTITY_EXTRA, XSCF_NULL, 2, 2, "newgrf_entity_extra", nullptr, nullptr, nullptr },
{ XSLFI_TNNC_CHUNK, XSCF_IGNORABLE_ALL, 0, 1, "tnnc_chunk", nullptr, nullptr, "TNNC" },
{ XSLFI_MULTI_CARGO_SHIPS, XSCF_NULL, 1, 1, "multi_cargo_ships", nullptr, nullptr, nullptr },
{ XSLFI_REMAIN_NEXT_ORDER_STATION, XSCF_IGNORABLE_UNKNOWN, 1, 1, "remain_next_order_station", nullptr, nullptr, nullptr },
{ XSLFI_LABEL_ORDERS, XSCF_NULL, 2, 2, "label_orders", nullptr, nullptr, nullptr },
{ XSLFI_VARIABLE_TICK_RATE, XSCF_IGNORABLE_ALL, 1, 1, "variable_tick_rate", nullptr, nullptr, nullptr },
{ XSLFI_SCRIPT_INT64, XSCF_NULL, 1, 1, "script_int64", nullptr, nullptr, nullptr },
{ XSLFI_U64_TICK_COUNTER, XSCF_NULL, 1, 1, "u64_tick_counter", nullptr, nullptr, nullptr },
{ XSLFI_LINKGRAPH_TRAVEL_TIME, XSCF_NULL, 1, 1, "linkgraph_travel_time", nullptr, nullptr, nullptr },
{ XSLFI_LAST_LOADING_TICK, XSCF_NULL, 2, 2, "last_loading_tick", nullptr, nullptr, nullptr },
{ XSLFI_SCRIPT_LEAGUE_TABLES, XSCF_NULL, 1, 1, "script_league_tables", nullptr, nullptr, "LEAE,LEAT" },
{ XSLFI_VELOCITY_NAUTICAL, XSCF_IGNORABLE_ALL, 1, 1, "velocity_nautical", nullptr, nullptr, nullptr },
{ XSLFI_CONSISTENT_PARTIAL_Z, XSCF_NULL, 1, 1, "consistent_partial_z", nullptr, nullptr, nullptr },
{ XSLFI_MORE_CARGO_AGE, XSCF_NULL, 1, 1, "more_cargo_age", nullptr, nullptr, nullptr },
{ XSLFI_AI_START_DATE, XSCF_NULL, 1, 1, "slv_ai_start_date", nullptr, nullptr, nullptr },
{ XSLFI_EXTEND_VEHICLE_RANDOM, XSCF_NULL, 1, 1, "slv_extend_vehicle_random", nullptr, nullptr, nullptr },
{ XSLFI_DISASTER_VEH_STATE, XSCF_NULL, 1, 1, "slv_disaster_veh_state", nullptr, nullptr, nullptr },
{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
};
/**
* Extended save/load feature test
*
* First performs a tradional check on the provided @p savegame_version against @p savegame_version_from and @p savegame_version_to.
* Then, if the feature set in the constructor is not XSLFI_NULL, also check than the feature version is inclusively bounded by @p min_version and @p max_version,
* and return the combination of the two tests using the operator defined in the constructor.
* Otherwise just returns the result of the savegame version test
*/
bool SlXvFeatureTest::IsFeaturePresent(const std::array<uint16, XSLFI_SIZE> &feature_versions, SaveLoadVersion savegame_version, SaveLoadVersion savegame_version_from, SaveLoadVersion savegame_version_to) const
{
bool savegame_version_ok = savegame_version >= savegame_version_from && savegame_version < savegame_version_to;
if (this->functor) return (*this->functor)(savegame_version, savegame_version_ok, feature_versions);
if (this->feature == XSLFI_NULL) return savegame_version_ok;
bool feature_ok = SlXvIsFeaturePresent(feature_versions, this->feature, this->min_version, this->max_version);
switch (op) {
case XSLFTO_OR:
return savegame_version_ok || feature_ok;
case XSLFTO_AND:
return savegame_version_ok && feature_ok;
default:
NOT_REACHED();
return false;
}
}
/**
* Returns true if @p feature is present and has a version inclusively bounded by @p min_version and @p max_version
*/
bool SlXvIsFeaturePresent(const std::array<uint16, XSLFI_SIZE> &feature_versions, SlXvFeatureIndex feature, uint16 min_version, uint16 max_version)
{
assert(feature < XSLFI_SIZE);
return feature_versions[feature] >= min_version && 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
*/
void SlXvResetState()
{
_sl_is_ext_version = false;
_sl_is_faked_ext = false;
_sl_maybe_springpp = false;
_sl_maybe_chillpp = false;
_sl_upstream_mode = false;
_sl_xv_discardable_chunk_ids.clear();
std::fill(_sl_xv_feature_versions.begin(), _sl_xv_feature_versions.end(), 0);
_sl_xv_version_label.clear();
_sl_xv_upstream_version = SL_MIN_VERSION;
}
/**
* Resets all extended feature versions to their currently enabled versions, i.e. versions suitable for saving
*/
void SlXvSetCurrentState()
{
SlXvResetState();
_sl_is_ext_version = true;
const SlxiSubChunkInfo *info = _sl_xv_sub_chunk_infos;
for (; info->index != XSLFI_NULL; ++info) {
_sl_xv_feature_versions[info->index] = info->save_version;
}
if (MapSizeX() > 8192 || MapSizeY() > 8192) {
_sl_xv_feature_versions[XSLFI_EXTRA_LARGE_MAP] = 1;
}
if (IsScenarioSave()) {
_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] = 0;
}
if (IsNetworkServerSave()) {
_sl_xv_feature_versions[XSLFI_VENC_CHUNK] = 1;
_sl_xv_feature_versions[XSLFI_TNNC_CHUNK] = 1;
}
}
/**
* Set all extended feature versions in the current static version array to their currently enabled versions, i.e. versions suitable for saving
*/
void SlXvSetStaticCurrentVersions()
{
std::fill(_sl_xv_feature_static_versions.begin(), _sl_xv_feature_static_versions.end(), 0);
const SlxiSubChunkInfo *info = _sl_xv_sub_chunk_infos;
for (; info->index != XSLFI_NULL; ++info) {
_sl_xv_feature_static_versions[info->index] = info->save_version;
}
}
/**
* Check for "special" savegame versions (i.e. known patchpacks) and set correct savegame version, settings, etc.
*/
bool SlXvCheckSpecialSavegameVersions()
{
// Checks for special savegame versions go here
extern SaveLoadVersion _sl_version;
if (_sl_version == SL_TRACE_RESTRICT_2000) {
DEBUG(sl, 1, "Loading a trace restrict patch savegame version %d as version 194", _sl_version);
_sl_version = SLV_194;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_TRACE_RESTRICT] = 1;
return true;
}
if (_sl_version == SL_TRACE_RESTRICT_2001) {
DEBUG(sl, 1, "Loading a trace restrict patch savegame version %d as version 195", _sl_version);
_sl_version = SLV_195;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_TRACE_RESTRICT] = 6;
return true;
}
if (_sl_version == SL_TRACE_RESTRICT_2002) {
DEBUG(sl, 1, "Loading a trace restrict patch savegame version %d as version 196", _sl_version);
_sl_version = SLV_196;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_TRACE_RESTRICT] = 6;
return true;
}
if (_sl_version >= SL_SPRING_2013_v2_0_102 && _sl_version <= SL_SPRING_2013_v2_4) { /* 220 - 227 */
_sl_maybe_springpp = true;
return true;
}
if (_sl_version >= SL_JOKER_1_19 && _sl_version <= SL_JOKER_1_27) { /* 278 - 286 */
DEBUG(sl, 1, "Loading a JokerPP savegame version %d as version 197", _sl_version);
_sl_xv_feature_versions[XSLFI_JOKERPP] = _sl_version;
_sl_xv_feature_versions[XSLFI_TOWN_CARGO_ADJ] = 1;
_sl_xv_feature_versions[XSLFI_TEMPLATE_REPLACEMENT] = 1;
_sl_xv_feature_versions[XSLFI_VEH_LIFETIME_PROFIT] = 1;
_sl_xv_feature_versions[XSLFI_TRAIN_FLAGS_EXTRA] = 1;
_sl_xv_feature_versions[XSLFI_SIG_TUNNEL_BRIDGE] = 5;
_sl_xv_feature_versions[XSLFI_REVERSE_AT_WAYPOINT] = 1;
_sl_xv_feature_versions[XSLFI_MULTIPLE_DOCKS] = 1;
_sl_xv_feature_versions[XSLFI_ST_LAST_VEH_TYPE] = 1;
_sl_xv_feature_versions[XSLFI_MORE_RAIL_TYPES] = 1;
_sl_xv_feature_versions[XSLFI_CHUNNEL] = 1;
_sl_xv_feature_versions[XSLFI_MORE_COND_ORDERS] = 1;
_sl_xv_feature_versions[XSLFI_TRACE_RESTRICT] = 1;
_sl_xv_feature_versions[XSLFI_CARGO_TYPE_ORDERS] = 1;
_sl_xv_feature_versions[XSLFI_RAIL_AGEING] = 1;
if (_sl_version >= SL_JOKER_1_21) _sl_xv_feature_versions[XSLFI_LINKGRAPH_DAY_SCALE] = 1;
if (_sl_version >= SL_JOKER_1_24) _sl_xv_feature_versions[XSLFI_TIMETABLE_EXTRA] = 1;
if (_sl_version >= SL_JOKER_1_24) _sl_xv_feature_versions[XSLFI_ORDER_EXTRA_DATA] = 1;
_sl_xv_discardable_chunk_ids.push_back('SPRG');
_sl_xv_discardable_chunk_ids.push_back('SLNK');
_sl_version = SLV_197;
_sl_is_faked_ext = true;
return true;
}
if (_sl_version == SL_CHILLPP_201) { /* 232 - 233 */
_sl_maybe_chillpp = true;
return true;
}
if (_sl_version >= SL_CHILLPP_232 && _sl_version <= SL_CHILLPP_233) { /* 232 - 233 */
DEBUG(sl, 1, "Loading a ChillPP v14.7 savegame version %d as version 160", _sl_version);
_sl_xv_feature_versions[XSLFI_CHILLPP] = _sl_version;
_sl_xv_feature_versions[XSLFI_ZPOS_32_BIT] = 1;
_sl_xv_feature_versions[XSLFI_TOWN_CARGO_ADJ] = 1;
_sl_xv_feature_versions[XSLFI_TRAFFIC_LIGHTS] = 1;
_sl_xv_feature_versions[XSLFI_IMPROVED_BREAKDOWNS] = 1;
_sl_xv_feature_versions[XSLFI_INFRA_SHARING] = 1;
_sl_xv_feature_versions[XSLFI_AUTO_TIMETABLE] = 1;
_sl_xv_feature_versions[XSLFI_SIG_TUNNEL_BRIDGE] = 1;
_sl_xv_feature_versions[XSLFI_RAIL_AGEING] = 1;
_sl_xv_discardable_chunk_ids.push_back('LGRP');
_sl_xv_discardable_chunk_ids.push_back('SSIG');
_sl_version = SLV_160;
_sl_is_faked_ext = true;
return true;
}
return false;
}
void SlXvSpringPPSpecialSavegameVersions()
{
extern SaveLoadVersion _sl_version;
if (_sl_version == SL_SPRING_2013_v2_0_102) { /* 220 */
DEBUG(sl, 1, "Loading a SpringPP 2013 v2.0.102 savegame version %d as version 187", _sl_version);
_sl_version = SLV_187;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_SPRINGPP] = 1;
} else if (_sl_version == SL_SPRING_2013_v2_1_108) { /* 221 */
DEBUG(sl, 1, "Loading a SpringPP 2013 v2.1.108 savegame version %d as version 188", _sl_version);
_sl_version = SLV_188;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_SPRINGPP] = 2;
} else if (_sl_version == SL_SPRING_2013_v2_1_147) { /* 222 */
DEBUG(sl, 1, "Loading a SpringPP 2013 v2.1.147 savegame version %d as version 194", _sl_version);
_sl_version = SLV_194;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_SPRINGPP] = 4; // Note that this break in numbering is deliberate
} else if (_sl_version == SL_SPRING_2013_v2_3_XXX) { /* 223 */
DEBUG(sl, 1, "Loading a SpringPP 2013 v2.3.xxx savegame version %d as version 194", _sl_version);
_sl_version = SLV_194;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_SPRINGPP] = 3; // Note that this break in numbering is deliberate
} else if (_sl_version == SL_SPRING_2013_v2_3_b3) { /* 224 */
DEBUG(sl, 1, "Loading a SpringPP 2013 v2.3.b3 savegame version %d as version 194", _sl_version);
_sl_version = SLV_194;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_SPRINGPP] = 5;
} else if (_sl_version == SL_SPRING_2013_v2_3_b4) { /* 225 */
DEBUG(sl, 1, "Loading a SpringPP 2013 v2.3.b4 savegame version %d as version 194", _sl_version);
_sl_version = SLV_194;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_SPRINGPP] = 6;
} else if (_sl_version == SL_SPRING_2013_v2_3_b5) { /* 226 */
DEBUG(sl, 1, "Loading a SpringPP 2013 v2.3.b5 savegame version %d as version 195", _sl_version);
_sl_version = SLV_195;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_SPRINGPP] = 7;
} else if (_sl_version == SL_SPRING_2013_v2_4) { /* 227 */
DEBUG(sl, 1, "Loading a SpringPP 2013 v2.4 savegame version %d as version 195", _sl_version);
_sl_version = SLV_195;
_sl_is_faked_ext = true;
_sl_xv_feature_versions[XSLFI_SPRINGPP] = 8;
}
if (_sl_xv_feature_versions[XSLFI_SPRINGPP]) {
_sl_xv_feature_versions[XSLFI_RIFF_HEADER_60_BIT] = 1;
_sl_xv_feature_versions[XSLFI_HEIGHT_8_BIT] = 1;
_sl_xv_feature_versions[XSLFI_MIGHT_USE_PAX_SIGNALS] = 1;
_sl_xv_feature_versions[XSLFI_TRAFFIC_LIGHTS] = 1;
_sl_xv_feature_versions[XSLFI_RAIL_AGEING] = 1;
_sl_xv_feature_versions[XSLFI_TIMETABLES_START_TICKS] = 1;
_sl_xv_feature_versions[XSLFI_VEHICLE_REPAIR_COST] = 1;
_sl_xv_feature_versions[XSLFI_IMPROVED_BREAKDOWNS] = 1;
_sl_xv_feature_versions[XSLFI_INFRA_SHARING] = 1;
_sl_xv_feature_versions[XSLFI_AUTO_TIMETABLE] = 1;
_sl_xv_feature_versions[XSLFI_MORE_COND_ORDERS] = 1;
_sl_xv_feature_versions[XSLFI_SIG_TUNNEL_BRIDGE] = 1;
_sl_xv_discardable_chunk_ids.push_back('SNOW');
}
}
void SlXvChillPPSpecialSavegameVersions()
{
extern SaveLoadVersion _sl_version;
if (_sl_version == SL_CHILLPP_201) { /* 201 */
DEBUG(sl, 1, "Loading a ChillPP v8 savegame version %d as version 143", _sl_version);
_sl_xv_feature_versions[XSLFI_CHILLPP] = _sl_version;
_sl_xv_feature_versions[XSLFI_ZPOS_32_BIT] = 1;
_sl_xv_feature_versions[XSLFI_TOWN_CARGO_ADJ] = 1;
_sl_xv_feature_versions[XSLFI_AUTO_TIMETABLE] = 1;
_sl_xv_feature_versions[XSLFI_SIG_TUNNEL_BRIDGE] = 1;
_sl_xv_feature_versions[XSLFI_RAIL_AGEING] = 1;
_sl_xv_discardable_chunk_ids.push_back('LGRP');
_sl_version = SLV_143;
_sl_is_faked_ext = true;
}
}
/**
* Return true if this chunk has been marked as discardable
*/
bool SlXvIsChunkDiscardable(uint32 id)
{
for (size_t i = 0; i < _sl_xv_discardable_chunk_ids.size(); i++) {
if (_sl_xv_discardable_chunk_ids[i] == id) {
return true;
}
}
return false;
}
/**
* Writes a chunk ID list string to the savegame, returns the number of chunks written
* In dry run mode, only returns the number of chunk which would have been written
*/
static uint32 WriteChunkIdList(const char *chunk_list, bool dry_run)
{
unsigned int chunk_count = 0; // number of chunks output
unsigned int id_offset = 0; // how far are we into the ID
for (; *chunk_list != 0; chunk_list++) {
if (id_offset == 4) {
assert(*chunk_list == ',');
id_offset = 0;
} else {
if (!dry_run) {
SlWriteByte(*chunk_list);
}
if (id_offset == 3) {
chunk_count++;
}
id_offset++;
}
}
assert(id_offset == 4);
return chunk_count;
}
static void Save_SLXI()
{
SlXvSetCurrentState();
static const SaveLoad _xlsi_sub_chunk_desc[] = {
SLE_STR(SlxiSubChunkInfo, name, SLE_STR, 0),
};
// calculate lengths
uint32 item_count = 0;
uint32 length = 12;
std::vector<uint32> extra_data_lengths;
std::vector<uint32> chunk_counts;
extra_data_lengths.resize(XSLFI_SIZE);
chunk_counts.resize(XSLFI_SIZE);
const SlxiSubChunkInfo *info = _sl_xv_sub_chunk_infos;
for (; info->index != XSLFI_NULL; ++info) {
if (_sl_xv_feature_versions[info->index] > 0) {
item_count++;
length += 6;
length += (uint32)SlCalcObjLength(info, _xlsi_sub_chunk_desc);
if (info->save_proc) {
uint32 extra_data_length = info->save_proc(info, true);
if (extra_data_length) {
extra_data_lengths[info->index] = extra_data_length;
length += 4 + extra_data_length;
}
}
if (info->chunk_list) {
uint32 chunk_count = WriteChunkIdList(info->chunk_list, true);
if (chunk_count) {
chunk_counts[info->index] = chunk_count;
length += 4 * (1 + chunk_count);
}
}
}
}
// write header
SlSetLength(length);
SlWriteUint32(_sl_xv_slxi_chunk_version); // chunk version
SlWriteUint32(0); // flags
SlWriteUint32(item_count); // item count
// write data
info = _sl_xv_sub_chunk_infos;
for (; info->index != XSLFI_NULL; ++info) {
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];
uint32 chunk_count = chunk_counts[info->index];
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<SlxiSubChunkInfo *>(info), _xlsi_sub_chunk_desc);
if (extra_data_length > 0) {
SlWriteUint32(extra_data_length);
size_t written = SlGetBytesWritten();
info->save_proc(info, false);
assert(SlGetBytesWritten() == written + extra_data_length);
}
if (chunk_count > 0) {
SlWriteUint32(chunk_count);
size_t written = SlGetBytesWritten();
WriteChunkIdList(info->chunk_list, false);
assert(SlGetBytesWritten() == written + (chunk_count * 4));
}
}
}
}
static void Load_SLXI()
{
if (_sl_is_faked_ext || !_sl_is_ext_version) {
SlErrorCorrupt("SLXI chunk is unexpectedly present");
}
SlXvResetState();
_sl_is_ext_version = true;
uint32 version = SlReadUint32();
if (version > _sl_xv_slxi_chunk_version) SlErrorCorruptFmt("SLXI chunk: version: %u is too new (expected max: %u)", version, _sl_xv_slxi_chunk_version);
uint32 chunk_flags = SlReadUint32();
// flags are not in use yet, reserve for future expansion
if (chunk_flags != 0) SlErrorCorruptFmt("SLXI chunk: unknown chunk header flags: 0x%X", chunk_flags);
char name_buffer[256];
const SaveLoad xlsi_sub_chunk_name_desc[] = {
SLEG_STR(name_buffer, SLE_STRB),
};
auto version_error = [](StringID str, const char *feature, int64 p1, int64 p2) {
char buf[256];
int64 args_array[] = { _sl_xv_version_label.empty() ? STR_EMPTY : STR_GAME_SAVELOAD_FROM_VERSION, (int64)(size_t)_sl_xv_version_label.c_str(), (int64)(size_t)feature, p1, p2 };
StringParameters tmp_params(args_array);
GetStringWithArgs(buf, str, &tmp_params, lastof(buf));
SlError(STR_JUST_RAW_STRING, buf);
};
uint32 item_count = SlReadUint32();
for (uint32 i = 0; i < item_count; i++) {
SlxiSubChunkFlags flags = static_cast<SlxiSubChunkFlags>(SlReadUint32());
uint16 version = SlReadUint16();
SlGlobList(xlsi_sub_chunk_name_desc);
// linearly scan through feature list until found name match
bool found = false;
const SlxiSubChunkInfo *info = _sl_xv_sub_chunk_infos;
for (; info->index != XSLFI_NULL; ++info) {
if (strcmp(name_buffer, info->name) == 0) {
found = true;
break;
}
}
bool discard_chunks = false;
if (found) {
if (version > info->max_version) {
if (flags & XSCF_IGNORABLE_VERSION) {
// version too large but carry on regardless
discard_chunks = true;
if (flags & XSCF_EXTRA_DATA_PRESENT) {
SlSkipBytes(SlReadUint32()); // skip extra data field
}
DEBUG(sl, 1, "SLXI chunk: too large version for feature: '%s', version: %d, max version: %d, ignoring", name_buffer, version, info->max_version);
} else {
version_error(STR_GAME_SAVELOAD_ERROR_TOO_NEW_FEATURE_VERSION, name_buffer, version, info->max_version);
}
} else {
// success path :)
_sl_xv_feature_versions[info->index] = version;
if (flags & XSCF_EXTRA_DATA_PRESENT) {
uint32 extra_data_size = SlReadUint32();
if (extra_data_size) {
if (info->load_proc) {
size_t read = SlGetBytesRead();
info->load_proc(info, extra_data_size);
if (SlGetBytesRead() != read + extra_data_size) {
SlErrorCorruptFmt("SLXI chunk: feature: %s, version: %d, extra data length mismatch", name_buffer, version);
}
} else {
SlErrorCorruptFmt("SLXI chunk: feature: %s, version: %d, unexpectedly includes extra data", name_buffer, version);
}
}
}
DEBUG(sl, 1, "SLXI chunk: found known feature: '%s', version: %d, max version: %d", name_buffer, version, info->max_version);
}
} else {
if (flags & XSCF_IGNORABLE_UNKNOWN) {
// not found but carry on regardless
discard_chunks = true;
if (flags & XSCF_EXTRA_DATA_PRESENT) {
SlSkipBytes(SlReadUint32()); // skip extra data field
}
DEBUG(sl, 1, "SLXI chunk: unknown feature: '%s', version: %d, ignoring", name_buffer, version);
} else {
version_error(STR_GAME_SAVELOAD_ERROR_UNKNOWN_FEATURE, name_buffer, version, 0);
}
}
// at this point the extra data field should have been consumed
// handle chunk ID list field
if (flags & XSCF_CHUNK_ID_LIST_PRESENT) {
uint32 chunk_count = SlReadUint32();
for (uint32 j = 0; j < chunk_count; j++) {
uint32 chunk_id = SlReadUint32();
if (discard_chunks) {
_sl_xv_discardable_chunk_ids.push_back(chunk_id);
DEBUG(sl, 2, "SLXI chunk: unknown feature: '%s', discarding chunk: %c%c%c%c", name_buffer, chunk_id >> 24, chunk_id >> 16, chunk_id >> 8, chunk_id);
}
}
}
}
}
static void loadVL(const SlxiSubChunkInfo *info, uint32 length)
{
_sl_xv_version_label.resize(length);
ReadBuffer::GetCurrent()->CopyBytes(reinterpret_cast<byte *>(_sl_xv_version_label.data()), length);
DEBUG(sl, 2, "SLXI version label: %s", _sl_xv_version_label.c_str());
}
static uint32 saveVL(const SlxiSubChunkInfo *info, bool dry_run)
{
const size_t length = strlen(_openttd_revision);
if (!dry_run) MemoryDumper::GetCurrent()->CopyBytes(reinterpret_cast<const byte *>(_openttd_revision), length);
return static_cast<uint32>(length);
}
static void loadUV(const SlxiSubChunkInfo *info, uint32 length)
{
if (length == 2) {
_sl_xv_upstream_version = (SaveLoadVersion)SlReadUint16();
DEBUG(sl, 2, "SLXI upstream version: %u", _sl_xv_upstream_version);
} else {
DEBUG(sl, 1, "SLXI chunk: feature: '%s', version: %d, has data of wrong length: %u", info->name, _sl_xv_feature_versions[info->index], length);
ReadBuffer::GetCurrent()->SkipBytes(length);
}
}
static uint32 saveUV(const SlxiSubChunkInfo *info, bool dry_run)
{
if (!dry_run) SlWriteUint16(SL_MAX_VERSION - 1);
return 2;
}
static void loadLC(const SlxiSubChunkInfo *info, uint32 length)
{
if (length == 1) {
_loaded_local_company = (CompanyID) ReadBuffer::GetCurrent()->ReadByte();
} else {
DEBUG(sl, 1, "SLXI chunk: feature: '%s', version: %d, has data of wrong length: %u", info->name, _sl_xv_feature_versions[info->index], length);
ReadBuffer::GetCurrent()->SkipBytes(length);
}
}
static uint32 saveLC(const SlxiSubChunkInfo *info, bool dry_run)
{
if (!dry_run) MemoryDumper::GetCurrent()->WriteByte(_local_company);
return 1;
}
extern const ChunkHandler version_ext_chunk_handlers[] = {
{ 'SLXI', Save_SLXI, Load_SLXI, nullptr, Load_SLXI, CH_RIFF },
};
extern const ChunkHandlerTable _version_ext_chunk_handlers(version_ext_chunk_handlers);

277
src/sl/extended_ver_sl.h Normal file
View File

@@ -0,0 +1,277 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file extended_ver_sl.h Functions/types related to handling save/load extended version info. */
#ifndef SL_EXTENDED_VER_SL_H
#define SL_EXTENDED_VER_SL_H
#include "../core/bitmath_func.hpp"
#include "../core/enum_type.hpp"
#include <array>
#include <vector>
enum SaveLoadVersion : uint16;
/**
* List of extended features, each feature has its own (16 bit) version
*/
enum SlXvFeatureIndex {
XSLFI_NULL = 0, ///< Unused value, to indicate that no extended feature test is in use
XSLFI_VERSION_LABEL, ///< Version label
XSLFI_UPSTREAM_VERSION, ///< Corresponding upstream savegame version
XSLFI_TRACE_RESTRICT, ///< Trace restrict
XSLFI_TRACE_RESTRICT_OWNER, ///< Trace restrict: train owner test
XSLFI_TRACE_RESTRICT_ORDRCND, ///< Trace restrict: slot conditional order
XSLFI_TRACE_RESTRICT_STATUSCND, ///< Trace restrict: train status condition
XSLFI_TRACE_RESTRICT_REVERSE, ///< Trace restrict: reverse
XSLFI_TRACE_RESTRICT_NEWSCTRL, ///< Trace restrict: news control
XSLFI_TRACE_RESTRICT_COUNTER, ///< Trace restrict: counters
XSLFI_TRACE_RESTRICT_TIMEDATE, ///< Trace restrict: time/date
XSLFI_TRACE_RESTRICT_BRKCND, ///< Trace restrict: realistic braking related conditionals
XSLFI_TRACE_RESTRICT_CTGRYCND, ///< Trace restrict: category conditionals
XSLFI_TRACE_RESTRICT_PENCTRL, ///< Trace restrict: PF penalty control
XSLFI_TRACE_RESTRICT_TUNBRIDGE, ///< Trace restrict: restricted signalled tunnel/bridge support
XSLFI_TRACE_RESTRICT_SPDADAPTCTRL, ///< Trace restrict: speed adaptation control
XSLFI_PROG_SIGS, ///< programmable pre-signals patch
XSLFI_ADJACENT_CROSSINGS, ///< Adjacent level crossings closure patch
XSLFI_SAFER_CROSSINGS, ///< Safer level crossings
XSLFI_DEPARTURE_BOARDS, ///< Departure boards patch, in ticks mode
XSLFI_TIMETABLES_START_TICKS, ///< Timetable start time is in ticks, instead of days (from departure boards patch)
XSLFI_TOWN_CARGO_ADJ, ///< Town cargo adjustment patch
XSLFI_SIG_TUNNEL_BRIDGE, ///< Signals on tunnels and bridges
XSLFI_IMPROVED_BREAKDOWNS, ///< Improved breakdowns patch
XSLFI_CONSIST_BREAKDOWN_FLAG, ///< Consist breakdown flag
XSLFI_TT_WAIT_IN_DEPOT, ///< Timetabling waiting time in depot patch
XSLFI_AUTO_TIMETABLE, ///< Auto timetables and separation patch
XSLFI_VEHICLE_REPAIR_COST, ///< Vehicle repair costs patch
XSLFI_ENH_VIEWPORT_PLANS, ///< Enhanced viewport patch: plans
XSLFI_INFRA_SHARING, ///< Infrastructure sharing patch
XSLFI_VARIABLE_DAY_LENGTH, ///< Variable day length patch
XSLFI_ORDER_OCCUPANCY, ///< Running average of order occupancy
XSLFI_MORE_COND_ORDERS, ///< More conditional orders patch
XSLFI_EXTRA_LARGE_MAP, ///< Extra large map
XSLFI_REVERSE_AT_WAYPOINT, ///< Reverse at waypoint orders
XSLFI_VEH_LIFETIME_PROFIT, ///< Vehicle lifetime profit patch
XSLFI_LINKGRAPH_DAY_SCALE, ///< Linkgraph job duration & interval may be in non-scaled days
XSLFI_TEMPLATE_REPLACEMENT, ///< Template-based train replacement
XSLFI_MORE_RAIL_TYPES, ///< Increased number of rail types
XSLFI_CARGO_TYPE_ORDERS, ///< Cargo-specific load/unload order flags
XSLFI_EXTENDED_GAMELOG, ///< Extended gamelog
XSLFI_STATION_CATCHMENT_INC, ///< Station catchment radius increase
XSLFI_CUSTOM_BRIDGE_HEADS, ///< Custom bridge heads
XSLFI_CHUNNEL, ///< Tunnels under water (channel tunnel)
XSLFI_SCHEDULED_DISPATCH, ///< Scheduled vehicle dispatching
XSLFI_MORE_TOWN_GROWTH_RATES, ///< More town growth rates
XSLFI_MULTIPLE_DOCKS, ///< Multiple docks
XSLFI_TIMETABLE_EXTRA, ///< Vehicle timetable extra fields
XSLFI_TRAIN_FLAGS_EXTRA, ///< Train flags field extra size
XSLFI_VEHICLE_FLAGS_EXTRA, ///< Vehicle flags field extra size
XSLFI_TRAIN_THROUGH_LOAD, ///< Train through load/unload
XSLFI_ORDER_EXTRA_DATA, ///< Order extra data field(s)
XSLFI_WHOLE_MAP_CHUNK, ///< Whole map chunk
XSLFI_ST_LAST_VEH_TYPE, ///< Per-cargo station last vehicle type
XSLFI_SELL_AT_DEPOT_ORDER, ///< Sell vehicle on arrival at depot orders
XSLFI_BUY_LAND_RATE_LIMIT, ///< Buy land rate limit
XSLFI_DUAL_RAIL_TYPES, ///< Two rail-types per tile
XSLFI_CONSIST_SPEED_RD_FLAG, ///< Consist speed reduction flag
XSLFI_SAVEGAME_UNIQUE_ID, ///< Savegame unique ID
XSLFI_RV_OVERTAKING, ///< Roadvehicle overtaking
XSLFI_LINKGRAPH_MODES, ///< Linkgraph additional distribution modes
XSLFI_GAME_EVENTS, ///< Game event flags
XSLFI_ROAD_LAYOUT_CHANGE_CTR, ///< Road layout change counter
XSLFI_TOWN_CARGO_MATRIX, ///< Town cargo matrix savegame format changes (now obsolete)
XSLFI_STATE_CHECKSUM, ///< State checksum
XSLFI_DEBUG, ///< Debugging info
XSLFI_FLOW_STAT_FLAGS, ///< FlowStat flags
XSLFI_SPEED_RESTRICTION, ///< Train speed restrictions
XSLFI_STATION_GOODS_EXTRA, ///< Extra station goods entry statuses
XSLFI_DOCKING_CACHE_VER, ///< Multiple docks - docking tile cache version
XSLFI_EXTRA_CHEATS, ///< Extra cheats
XSLFI_TOWN_MULTI_BUILDING, ///< Allow multiple stadium/church buildings in a single town
XSLFI_SHIP_LOST_COUNTER, ///< Ship lost counter
XSLFI_BUILD_OBJECT_RATE_LIMIT, ///< Build object rate limit
XSLFI_LOCAL_COMPANY, ///< Local company ID
XSLFI_THROUGH_TRAIN_DEPOT, ///< Drive-through train depots
XSLFI_MORE_VEHICLE_ORDERS, ///< More vehicle orders - VehicleOrderID is 16 bits instead of 8
XSLFI_ORDER_FLAGS_EXTRA, ///< Order flags field extra size
XSLFI_ONE_WAY_DT_ROAD_STOP, ///< One-way drive-through road stops
XSLFI_ONE_WAY_ROAD_STATE, ///< One-way road state cache
XSLFI_VENC_CHUNK, ///< VENC chunk
XSLFI_ANIMATED_TILE_EXTRA, ///< Animated tile extra info
XSLFI_NEWGRF_INFO_EXTRA, ///< Extra NewGRF info in savegame
XSLFI_INDUSTRY_CARGO_ADJ, ///< Industry cargo adjustment patch
XSLFI_REALISTIC_TRAIN_BRAKING, ///< Realistic train braking
XSLFI_INFLATION_FIXED_DATES, ///< Inflation is applied between fixed dates
XSLFI_WATER_FLOODING, ///< Water flooding map bit
XSLFI_MORE_HOUSES, ///< More house types
XSLFI_CUSTOM_TOWN_ZONE, ///< Custom town zones
XSLFI_STATION_CARGO_HISTORY, ///< Station waiting cargo history
XSLFI_TRAIN_SPEED_ADAPTATION, ///< Train speed adaptation
XSLFI_EXTRA_STATION_NAMES, ///< Extra station names
XSLFI_DEPOT_ORDER_EXTRA_FLAGS, ///< Depot order extra flags
XSLFI_EXTRA_SIGNAL_TYPES, ///< Extra signal types
XSLFI_BANKRUPTCY_EXTRA, ///< Extra company bankruptcy fields
XSLFI_OBJECT_GROUND_TYPES, ///< Object ground types
XSLFI_LINKGRAPH_AIRCRAFT, ///< Link graph last aircraft update field and aircraft link scaling setting
XSLFI_COMPANY_PW, ///< Company passwords
XSLFI_ST_INDUSTRY_CARGO_MODE, ///< Station industry cargo mode setting
XSLFI_TL_SPEED_LIMIT, ///< Through load maximum speed setting
XSLFI_RAIL_DEPOT_SPEED_LIMIT, ///< Rail depot maximum speed setting
XSLFI_WAYPOINT_FLAGS, ///< Waypoint flags
XSLFI_ROAD_WAYPOINTS, ///< Road waypoints
XSLFI_MORE_STATION_TYPES, ///< More station types (field widening)
XSLFI_RV_ORDER_EXTRA_FLAGS, ///< Road vehicle order extra flags
XSLFI_GRF_ROADSTOPS, ///< NewGRF road stops
XSLFI_INDUSTRY_ANIM_MASK, ///< Industry tile animation masking
XSLFI_NEW_SIGNAL_STYLES, ///< New signal styles
XSLFI_NO_TREE_COUNTER, ///< No tree counter
XSLFI_TOWN_SETTING_OVERRIDE, ///< Town setting overrides
XSLFI_LINKGRAPH_SPARSE_EDGES, ///< Link graph edge matrix is stored in sparse format, and saved in order
XSLFI_AUX_TILE_LOOP, ///< Auxiliary tile loop
XSLFI_NEWGRF_ENTITY_EXTRA, ///< NewGRF entity mappings are 16 bit
XSLFI_TNNC_CHUNK, ///< TNNC chunk
XSLFI_MULTI_CARGO_SHIPS, ///< Multi-cargo ships
XSLFI_REMAIN_NEXT_ORDER_STATION, ///< Remain in station if next order is for same station
XSLFI_LABEL_ORDERS, ///< Label orders
XSLFI_VARIABLE_TICK_RATE, ///< Variable tick rate
XSLFI_SCRIPT_INT64, ///< See: SLV_SCRIPT_INT64
XSLFI_U64_TICK_COUNTER, ///< See: SLV_U64_TICK_COUNTER
XSLFI_LINKGRAPH_TRAVEL_TIME, ///< See: SLV_LINKGRAPH_TRAVEL_TIME
XSLFI_LAST_LOADING_TICK, ///< See: SLV_LAST_LOADING_TICK
XSLFI_SCRIPT_LEAGUE_TABLES, ///< See: Scriptable league tables (PR #10001)
XSLFI_VELOCITY_NAUTICAL, ///< See: SLV_VELOCITY_NAUTICAL (PR #10594)
XSLFI_CONSISTENT_PARTIAL_Z, ///< See: SLV_CONSISTENT_PARTIAL_Z (PR #10570)
XSLFI_MORE_CARGO_AGE, ///< See: SLV_MORE_CARGO_AGE (PR #10596)
XSLFI_AI_START_DATE, ///< See: SLV_AI_START_DATE (PR #10653)
XSLFI_EXTEND_VEHICLE_RANDOM, ///< See: SLV_EXTEND_VEHICLE_RANDOM (PR #10701)
XSLFI_DISASTER_VEH_STATE, ///< See: SLV_DISASTER_VEH_STATE (PR #10798)
XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit
XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk
XSLFI_ZPOS_32_BIT, ///< Vehicle/sign z_pos is 32 bit instead of 8 bit, but savegame version may be before this became true in trunk
XSLFI_MIGHT_USE_PAX_SIGNALS, ///< This save game might use the pax-signals feature
XSLFI_TRAFFIC_LIGHTS, ///< This save game uses road traffic lights
XSLFI_RAIL_AGEING, ///< This save game uses the rail aging patch
XSLFI_SPRINGPP, ///< This is a SpringPP game, use this for loading some settings
XSLFI_JOKERPP, ///< This is a JokerPP game, use this for loading some settings
XSLFI_CHILLPP, ///< This is a ChillPP game, use this for loading some settings
XSLFI_SIZE, ///< Total count of features, including null feature
};
extern std::array<uint16, XSLFI_SIZE> _sl_xv_feature_versions;
extern std::array<uint16, XSLFI_SIZE> _sl_xv_feature_static_versions;
/**
* Operator to use when combining traditional savegame number test with an extended feature version test
*/
enum SlXvFeatureTestOperator {
XSLFTO_OR = 0, ///< Test if traditional savegame version is in bounds OR extended feature is in version bounds
XSLFTO_AND ///< Test if traditional savegame version is in bounds AND extended feature is in version bounds
};
/**
* Structure to describe an extended feature version test, and how it combines with a traditional savegame version test
*/
struct SlXvFeatureTest {
using TestFunctorPtr = bool (*)(uint16, bool, const std::array<uint16, XSLFI_SIZE> &); ///< Return true if feature present, first parameter is standard savegame version, second is whether standard savegame version is within bounds
private:
uint16 min_version;
uint16 max_version;
SlXvFeatureIndex feature;
SlXvFeatureTestOperator op;
TestFunctorPtr functor = nullptr;
public:
SlXvFeatureTest()
: min_version(0), max_version(0), feature(XSLFI_NULL), op(XSLFTO_OR) { }
SlXvFeatureTest(SlXvFeatureTestOperator op_, SlXvFeatureIndex feature_, uint16 min_version_ = 1, uint16 max_version_ = 0xFFFF)
: min_version(min_version_), max_version(max_version_), feature(feature_), op(op_) { }
SlXvFeatureTest(TestFunctorPtr functor_)
: min_version(0), max_version(0), feature(XSLFI_NULL), op(XSLFTO_OR), functor(functor_) { }
bool IsFeaturePresent(const std::array<uint16, XSLFI_SIZE> &feature_versions, SaveLoadVersion savegame_version, SaveLoadVersion savegame_version_from, SaveLoadVersion savegame_version_to) const;
inline bool IsFeaturePresent(SaveLoadVersion savegame_version, SaveLoadVersion savegame_version_from, SaveLoadVersion savegame_version_to) const
{
return this->IsFeaturePresent(_sl_xv_feature_versions, savegame_version, savegame_version_from, savegame_version_to);
}
};
bool SlXvIsFeaturePresent(const std::array<uint16, XSLFI_SIZE> &feature_versions, SlXvFeatureIndex feature, uint16 min_version = 1, uint16 max_version = 0xFFFF);
inline bool SlXvIsFeaturePresent(SlXvFeatureIndex feature, uint16 min_version = 1, uint16 max_version = 0xFFFF)
{
return SlXvIsFeaturePresent(_sl_xv_feature_versions, feature, min_version, max_version);
}
/**
* Returns true if @p feature is missing (i.e. has a version of 0, or less than the specified minimum version)
*/
inline bool SlXvIsFeatureMissing(SlXvFeatureIndex feature, uint16 min_version = 1)
{
return !SlXvIsFeaturePresent(feature, min_version);
}
/**
* Returns true if @p feature is missing (i.e. has a version of 0, or less than the specified minimum version)
*/
inline bool SlXvIsFeatureMissing(const std::array<uint16, XSLFI_SIZE> &feature_versions, SlXvFeatureIndex feature, uint16 min_version = 1)
{
return !SlXvIsFeaturePresent(feature_versions, feature, min_version);
}
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)
*/
enum SlxiSubChunkFlags {
XSCF_NULL = 0, ///< zero value
XSCF_IGNORABLE_UNKNOWN = 1 << 0, ///< the loader is free to ignore this without aborting the load if it doesn't know what it is at all
XSCF_IGNORABLE_VERSION = 1 << 1, ///< the loader is free to ignore this without aborting the load if the version is greater than the maximum that can be loaded
XSCF_EXTRA_DATA_PRESENT = 1 << 2, ///< extra data field is present, extra data in some sub-chunk/feature specific format
XSCF_CHUNK_ID_LIST_PRESENT = 1 << 3, ///< chunk ID list field is present, list of chunks which this sub-chunk/feature adds to the save game, this can be used to discard the chunks if the feature is unknown
XSCF_IGNORABLE_ALL = XSCF_IGNORABLE_UNKNOWN | XSCF_IGNORABLE_VERSION, ///< all "ignorable" flags
};
DECLARE_ENUM_AS_BIT_SET(SlxiSubChunkFlags)
struct SlxiSubChunkInfo;
typedef uint32 SlxiSubChunkSaveProc(const SlxiSubChunkInfo *info, bool dry_run); ///< sub chunk save procedure type, must return length and write no data when dry_run is true
typedef void SlxiSubChunkLoadProc(const SlxiSubChunkInfo *info, uint32 length); ///< sub chunk load procedure, must consume length bytes
/** Handlers and description of chunk. */
struct SlxiSubChunkInfo {
SlXvFeatureIndex index; ///< feature index, this is saved
SlxiSubChunkFlags flags; ///< flags, this is saved
uint16 save_version; ///< version to save
uint16 max_version; ///< maximum version to accept on load
const char *name; ///< feature name, this *IS* saved, so must be globally unique
SlxiSubChunkSaveProc *save_proc; ///< save procedure of the sub chunk, this may be nullptr in which case no extra chunk data is saved
SlxiSubChunkLoadProc *load_proc; ///< load procedure of the sub chunk, this may be nullptr in which case the extra chunk data must be missing or of 0 length
const char *chunk_list; ///< this is a list of chunks that this feature uses, which should be written to the savegame, this must be a comma-seperated list of 4-character IDs, with no spaces, or nullptr
};
void SlXvResetState();
void SlXvSetCurrentState();
void SlXvSetStaticCurrentVersions();
bool SlXvCheckSpecialSavegameVersions();
bool SlXvIsChunkDiscardable(uint32 id);
#endif /* SL_EXTENDED_VER_SL_H */

180
src/sl/game_sl.cpp Normal file
View File

@@ -0,0 +1,180 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file game_sl.cpp Handles the saveload part of the GameScripts */
#include "../stdafx.h"
#include "../debug.h"
#include "saveload.h"
#include "../string_func.h"
#include "../game/game.hpp"
#include "../game/game_config.hpp"
#include "../network/network.h"
#include "../game/game_instance.hpp"
#include "../game/game_text.hpp"
#include "../safeguards.h"
static std::string _game_saveload_name;
static int _game_saveload_version;
static std::string _game_saveload_settings;
static bool _game_saveload_is_random;
static const SaveLoad _game_script[] = {
SLEG_SSTR(_game_saveload_name, SLE_STR),
SLEG_SSTR(_game_saveload_settings, SLE_STR),
SLEG_VAR(_game_saveload_version, SLE_UINT32),
SLEG_VAR(_game_saveload_is_random, SLE_BOOL),
};
static void SaveReal_GSDT(int *index_ptr)
{
GameConfig *config = GameConfig::GetConfig();
if (config->HasScript()) {
_game_saveload_name = config->GetName();
_game_saveload_version = config->GetVersion();
} else {
/* No GameScript is configured for this so store an empty string as name. */
_game_saveload_name.clear();
_game_saveload_version = -1;
}
_game_saveload_is_random = config->IsRandom();
_game_saveload_settings = config->SettingsToString();
SlObject(nullptr, _game_script);
Game::Save();
}
static void Load_GSDT()
{
/* Free all current data */
GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME)->Change(nullptr);
if ((CompanyID)SlIterateArray() == (CompanyID)-1) return;
_game_saveload_version = -1;
SlObject(nullptr, _game_script);
if (_game_mode == GM_MENU || (_networking && !_network_server)) {
GameInstance::LoadEmpty();
if ((CompanyID)SlIterateArray() != (CompanyID)-1) SlErrorCorrupt("Too many GameScript configs");
return;
}
GameConfig *config = GameConfig::GetConfig(GameConfig::SSS_FORCE_GAME);
if (!_game_saveload_name.empty()) {
config->Change(_game_saveload_name.c_str(), _game_saveload_version, false, _game_saveload_is_random);
if (!config->HasScript()) {
/* No version of the GameScript available that can load the data. Try to load the
* latest version of the GameScript instead. */
config->Change(_game_saveload_name.c_str(), -1, false, _game_saveload_is_random);
if (!config->HasScript()) {
if (_game_saveload_name.compare("%_dummy") != 0) {
DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %d which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version);
DEBUG(script, 0, "This game will continue to run without GameScript.");
} else {
DEBUG(script, 0, "The savegame had no GameScript available at the time of saving.");
DEBUG(script, 0, "This game will continue to run without GameScript.");
}
} else {
DEBUG(script, 0, "The savegame has an GameScript by the name '%s', version %d which is no longer available.", _game_saveload_name.c_str(), _game_saveload_version);
DEBUG(script, 0, "The latest version of that GameScript has been loaded instead, but it'll not get the savegame data as it's incompatible.");
}
/* Make sure the GameScript doesn't get the saveload data, as it was not the
* writer of the saveload data in the first place */
_game_saveload_version = -1;
}
}
config->StringToSettings(_game_saveload_settings);
/* Load the GameScript saved data */
config->SetToLoadData(GameInstance::Load(_game_saveload_version));
if ((CompanyID)SlIterateArray() != (CompanyID)-1) SlErrorCorrupt("Too many GameScript configs");
}
static void Save_GSDT()
{
SlSetArrayIndex(0);
SlAutolength((AutolengthProc *)SaveReal_GSDT, nullptr);
}
extern GameStrings *_current_data;
static std::string _game_saveload_string;
static uint _game_saveload_strings;
static const SaveLoad _game_language_header[] = {
SLEG_SSTR(_game_saveload_string, SLE_STR),
SLEG_VAR(_game_saveload_strings, SLE_UINT32),
};
static const SaveLoad _game_language_string[] = {
SLEG_SSTR(_game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL),
};
static void SaveReal_GSTR(const LanguageStrings *ls)
{
_game_saveload_string = ls->language.c_str();
_game_saveload_strings = (uint)ls->lines.size();
SlObject(nullptr, _game_language_header);
for (const auto &i : ls->lines) {
_game_saveload_string = i.c_str();
SlObject(nullptr, _game_language_string);
}
}
static void Load_GSTR()
{
delete _current_data;
_current_data = new GameStrings();
while (SlIterateArray() != -1) {
_game_saveload_string.clear();
SlObject(nullptr, _game_language_header);
LanguageStrings ls(_game_saveload_string);
for (uint i = 0; i < _game_saveload_strings; i++) {
SlObject(nullptr, _game_language_string);
ls.lines.emplace_back(_game_saveload_string);
}
_current_data->raw_strings.push_back(std::move(ls));
}
/* If there were no strings in the savegame, set GameStrings to nullptr */
if (_current_data->raw_strings.size() == 0) {
delete _current_data;
_current_data = nullptr;
return;
}
_current_data->Compile();
ReconsiderGameScriptLanguage();
}
static void Save_GSTR()
{
if (_current_data == nullptr) return;
for (uint i = 0; i < _current_data->raw_strings.size(); i++) {
SlSetArrayIndex(i);
SlAutolength((AutolengthProc *)SaveReal_GSTR, &_current_data->raw_strings[i]);
}
}
static const ChunkHandler game_chunk_handlers[] = {
{ 'GSTR', Save_GSTR, Load_GSTR, nullptr, nullptr, CH_ARRAY },
{ 'GSDT', Save_GSDT, Load_GSDT, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _game_chunk_handlers(game_chunk_handlers);

183
src/sl/gamelog_sl.cpp Normal file
View File

@@ -0,0 +1,183 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file gamelog_sl.cpp Code handling saving and loading of gamelog data */
#include "../stdafx.h"
#include "../gamelog_internal.h"
#include "../fios.h"
#include "../string_func.h"
#include "saveload.h"
#include "../safeguards.h"
static const SaveLoad _glog_action_desc[] = {
SLE_CONDVAR_X(LoggedAction, tick, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)),
SLE_CONDVAR_X(LoggedAction, tick, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)),
};
static const SaveLoad _glog_mode_desc[] = {
SLE_VAR(LoggedChange, mode.mode, SLE_UINT8),
SLE_VAR(LoggedChange, mode.landscape, SLE_UINT8),
};
static char old_revision_text[GAMELOG_REVISION_LENGTH];
static const SaveLoad _glog_revision_desc[] = {
SLEG_CONDARR_X(old_revision_text, SLE_UINT8, GAMELOG_REVISION_LENGTH, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTENDED_GAMELOG, 0, 0)),
SLE_CONDSTR_X(LoggedChange, revision.text, SLE_STR, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTENDED_GAMELOG)),
SLE_VAR(LoggedChange, revision.newgrf, SLE_UINT32),
SLE_VAR(LoggedChange, revision.slver, SLE_UINT16),
SLE_VAR(LoggedChange, revision.modified, SLE_UINT8),
};
static const SaveLoad _glog_oldver_desc[] = {
SLE_VAR(LoggedChange, oldver.type, SLE_UINT32),
SLE_VAR(LoggedChange, oldver.version, SLE_UINT32),
};
static const SaveLoad _glog_setting_desc[] = {
SLE_STR(LoggedChange, setting.name, SLE_STR, 128),
SLE_VAR(LoggedChange, setting.oldval, SLE_INT32),
SLE_VAR(LoggedChange, setting.newval, SLE_INT32),
};
static const SaveLoad _glog_grfadd_desc[] = {
SLE_VAR(LoggedChange, grfadd.grfid, SLE_UINT32 ),
SLE_ARR(LoggedChange, grfadd.md5sum, SLE_UINT8, 16),
};
static const SaveLoad _glog_grfrem_desc[] = {
SLE_VAR(LoggedChange, grfrem.grfid, SLE_UINT32),
};
static const SaveLoad _glog_grfcompat_desc[] = {
SLE_VAR(LoggedChange, grfcompat.grfid, SLE_UINT32 ),
SLE_ARR(LoggedChange, grfcompat.md5sum, SLE_UINT8, 16),
};
static const SaveLoad _glog_grfparam_desc[] = {
SLE_VAR(LoggedChange, grfparam.grfid, SLE_UINT32),
};
static const SaveLoad _glog_grfmove_desc[] = {
SLE_VAR(LoggedChange, grfmove.grfid, SLE_UINT32),
SLE_VAR(LoggedChange, grfmove.offset, SLE_INT32),
};
static const SaveLoad _glog_grfbug_desc[] = {
SLE_VAR(LoggedChange, grfbug.data, SLE_UINT64),
SLE_VAR(LoggedChange, grfbug.grfid, SLE_UINT32),
SLE_VAR(LoggedChange, grfbug.bug, SLE_UINT8),
};
static const SaveLoad _glog_emergency_desc[] = {
SLE_CONDNULL(0, SL_MIN_VERSION, SL_MIN_VERSION), // Just an empty list, to keep the rest of the code easier.
};
static const SaveLoadTable _glog_desc[] = {
_glog_mode_desc,
_glog_revision_desc,
_glog_oldver_desc,
_glog_setting_desc,
_glog_grfadd_desc,
_glog_grfrem_desc,
_glog_grfcompat_desc,
_glog_grfparam_desc,
_glog_grfmove_desc,
_glog_grfbug_desc,
_glog_emergency_desc,
};
static_assert(lengthof(_glog_desc) == GLCT_END);
static void Load_GLOG_common(LoggedAction *&gamelog_action, uint &gamelog_actions)
{
assert(gamelog_action == nullptr);
assert(gamelog_actions == 0);
byte type;
while ((type = SlReadByte()) != GLAT_NONE) {
if (type >= GLAT_END) SlErrorCorrupt("Invalid gamelog action type");
GamelogActionType at = (GamelogActionType)type;
gamelog_action = ReallocT(gamelog_action, gamelog_actions + 1);
LoggedAction *la = &gamelog_action[gamelog_actions++];
la->at = at;
SlObject(la, _glog_action_desc); // has to be saved after 'DATE'!
la->change = nullptr;
la->changes = 0;
while ((type = SlReadByte()) != GLCT_NONE) {
if (type >= GLCT_END) SlErrorCorrupt("Invalid gamelog change type");
GamelogChangeType ct = (GamelogChangeType)type;
la->change = ReallocT(la->change, la->changes + 1);
LoggedChange *lc = &la->change[la->changes++];
/* for SLE_STR, pointer has to be valid! so make it nullptr */
memset(lc, 0, sizeof(*lc));
lc->ct = ct;
SlObject(lc, _glog_desc[ct]);
if (ct == GLCT_REVISION && SlXvIsFeatureMissing(XSLFI_EXTENDED_GAMELOG)) {
lc->revision.text = stredup(old_revision_text, lastof(old_revision_text));
}
}
}
}
static void Save_GLOG()
{
const LoggedAction *laend = &_gamelog_action[_gamelog_actions];
size_t length = 0;
for (const LoggedAction *la = _gamelog_action; la != laend; la++) {
const LoggedChange *lcend = &la->change[la->changes];
for (LoggedChange *lc = la->change; lc != lcend; lc++) {
assert((uint)lc->ct < lengthof(_glog_desc));
length += SlCalcObjLength(lc, _glog_desc[lc->ct]) + 1;
}
length += 10;
}
length++;
SlSetLength(length);
for (LoggedAction *la = _gamelog_action; la != laend; la++) {
SlWriteByte(la->at);
SlObject(la, _glog_action_desc);
const LoggedChange *lcend = &la->change[la->changes];
for (LoggedChange *lc = la->change; lc != lcend; lc++) {
SlWriteByte(lc->ct);
assert((uint)lc->ct < GLCT_END);
SlObject(lc, _glog_desc[lc->ct]);
}
SlWriteByte(GLCT_NONE);
}
SlWriteByte(GLAT_NONE);
}
static void Load_GLOG()
{
Load_GLOG_common(_gamelog_action, _gamelog_actions);
}
static void Check_GLOG()
{
Load_GLOG_common(_load_check_data.gamelog_action, _load_check_data.gamelog_actions);
}
static const ChunkHandler gamelog_chunk_handlers[] = {
{ 'GLOG', Save_GLOG, Load_GLOG, nullptr, Check_GLOG, CH_RIFF }
};
extern const ChunkHandlerTable _gamelog_chunk_handlers(gamelog_chunk_handlers);

47
src/sl/goal_sl.cpp Normal file
View File

@@ -0,0 +1,47 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file goal_sl.cpp Code handling saving and loading of goals */
#include "../stdafx.h"
#include "../goal_base.h"
#include "saveload.h"
#include "../safeguards.h"
static const SaveLoad _goals_desc[] = {
SLE_VAR(Goal, company, SLE_FILE_U16 | SLE_VAR_U8),
SLE_VAR(Goal, type, SLE_FILE_U16 | SLE_VAR_U8),
SLE_VAR(Goal, dst, SLE_UINT32),
SLE_SSTR(Goal, text, SLE_STR | SLF_ALLOW_CONTROL),
SLE_CONDSSTR(Goal, progress, SLE_STR | SLF_ALLOW_CONTROL, SLV_182, SL_MAX_VERSION),
SLE_CONDVAR(Goal, completed, SLE_BOOL, SLV_182, SL_MAX_VERSION),
};
static void Save_GOAL()
{
for (Goal *s : Goal::Iterate()) {
SlSetArrayIndex(s->index);
SlObject(s, _goals_desc);
}
}
static void Load_GOAL()
{
int index;
while ((index = SlIterateArray()) != -1) {
Goal *s = new (index) Goal();
SlObject(s, _goals_desc);
}
}
static const ChunkHandler goal_chunk_handlers[] = {
{ 'GOAL', Save_GOAL, Load_GOAL, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _goal_chunk_handlers(goal_chunk_handlers);

62
src/sl/group_sl.cpp Normal file
View File

@@ -0,0 +1,62 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file group_sl.cpp Code handling saving and loading of economy data */
#include "../stdafx.h"
#include "../group.h"
#include "../company_base.h"
#include "saveload.h"
#include "../safeguards.h"
static const SaveLoad _group_desc[] = {
SLE_CONDVAR(Group, name, SLE_NAME, SL_MIN_VERSION, SLV_84),
SLE_CONDSSTR(Group, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), // num_vehicle
SLE_VAR(Group, owner, SLE_UINT8),
SLE_VAR(Group, vehicle_type, SLE_UINT8),
SLE_VAR(Group, flags, SLE_UINT8),
SLE_CONDVAR(Group, livery.in_use, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION),
SLE_CONDVAR(Group, livery.colour1, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION),
SLE_CONDVAR(Group, livery.colour2, SLE_UINT8, SLV_GROUP_LIVERIES, SL_MAX_VERSION),
SLE_CONDVAR(Group, parent, SLE_UINT16, SLV_189, SL_MAX_VERSION),
};
static void Save_GRPS()
{
for (Group *g : Group::Iterate()) {
SlSetArrayIndex(g->index);
SlObject(g, _group_desc);
}
}
static void Load_GRPS()
{
int index;
while ((index = SlIterateArray()) != -1) {
Group *g = new (index) Group();
SlObject(g, _group_desc);
if (IsSavegameVersionBefore(SLV_189)) g->parent = INVALID_GROUP;
if (IsSavegameVersionBefore(SLV_GROUP_LIVERIES)) {
const Company *c = Company::Get(g->owner);
g->livery.colour1 = c->livery[LS_DEFAULT].colour1;
g->livery.colour2 = c->livery[LS_DEFAULT].colour2;
}
}
}
static const ChunkHandler group_chunk_handlers[] = {
{ 'GRPS', Save_GRPS, Load_GRPS, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _group_chunk_handlers(group_chunk_handlers);

187
src/sl/industry_sl.cpp Normal file
View File

@@ -0,0 +1,187 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file industry_sl.cpp Code handling saving and loading of industries */
#include "../stdafx.h"
#include "../industry.h"
#include "saveload.h"
#include "newgrf_sl.h"
#include "../safeguards.h"
static OldPersistentStorage _old_ind_persistent_storage;
static const SaveLoad _industry_desc[] = {
SLE_CONDVAR(Industry, location.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Industry, location.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_VAR(Industry, location.w, SLE_FILE_U8 | SLE_VAR_U16),
SLE_VAR(Industry, location.h, SLE_FILE_U8 | SLE_VAR_U16),
SLE_REF(Industry, town, REF_TOWN),
SLE_CONDREF(Industry, neutral_station, REF_STATION, SLV_SERVE_NEUTRAL_INDUSTRIES, SL_MAX_VERSION),
SLE_CONDNULL( 2, SL_MIN_VERSION, SLV_61), ///< used to be industry's produced_cargo
SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, produced_cargo_waiting, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDARR(Industry, production_rate, SLE_UINT8, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, production_rate, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDNULL( 3, SL_MIN_VERSION, SLV_61), ///< used to be industry's accepts_cargo
SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_VAR(Industry, prod_level, SLE_UINT8),
SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, this_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, this_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, last_month_production, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, last_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_VAR(Industry, counter, SLE_UINT16),
SLE_VAR(Industry, type, SLE_UINT8),
SLE_VAR(Industry, owner, SLE_UINT8),
SLE_VAR(Industry, random_colour, SLE_UINT8),
SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),
SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8),
SLE_CONDVAR(Industry, ctlflags, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION),
SLE_CONDVAR(Industry, founder, SLE_UINT8, SLV_70, SL_MAX_VERSION),
SLE_CONDVAR(Industry, construction_date, SLE_INT32, SLV_70, SL_MAX_VERSION),
SLE_CONDVAR(Industry, construction_type, SLE_UINT8, SLV_70, SL_MAX_VERSION),
SLE_CONDVAR(Industry, last_cargo_accepted_at[0], SLE_INT32, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
SLE_CONDARR(Industry, last_cargo_accepted_at, SLE_INT32, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, SLV_73, SL_MAX_VERSION),
SLE_CONDVAR(Industry, exclusive_supplier, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION),
SLE_CONDVAR(Industry, exclusive_consumer, SLE_UINT8, SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION),
SLEG_CONDARR(_old_ind_persistent_storage.storage, SLE_UINT32, 16, SLV_76, SLV_161),
SLE_CONDREF(Industry, psa, REF_STORAGE, SLV_161, SL_MAX_VERSION),
SLE_CONDNULL(1, SLV_82, SLV_197), // random_triggers
SLE_CONDVAR(Industry, random, SLE_UINT16, SLV_82, SL_MAX_VERSION),
SLE_CONDSSTR(Industry, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_INDUSTRY_TEXT, SL_MAX_VERSION),
SLE_CONDNULL(32, SLV_2, SLV_144), // old reserved space
};
static void Save_INDY()
{
/* Write the industries */
for (Industry *ind : Industry::Iterate()) {
SlSetArrayIndex(ind->index);
SlObject(ind, _industry_desc);
}
}
static void Save_IIDS()
{
Save_NewGRFMapping(_industry_mngr);
}
static void Save_TIDS()
{
Save_NewGRFMapping(_industile_mngr);
}
static void Load_INDY()
{
int index;
Industry::ResetIndustryCounts();
while ((index = SlIterateArray()) != -1) {
Industry *i = new (index) Industry();
SlObject(i, _industry_desc);
/* Before savegame version 161, persistent storages were not stored in a pool. */
if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_76)) {
/* Store the old persistent storage. The GRFID will be added later. */
assert(PersistentStorage::CanAllocateItem());
i->psa = new PersistentStorage(0, 0, 0);
memcpy(i->psa->storage, _old_ind_persistent_storage.storage, sizeof(_old_ind_persistent_storage.storage));
}
Industry::IncIndustryTypeCount(i->type);
}
}
static void Load_IIDS()
{
Load_NewGRFMapping(_industry_mngr);
}
static void Load_TIDS()
{
Load_NewGRFMapping(_industile_mngr);
}
static void Ptrs_INDY()
{
for (Industry *i : Industry::Iterate()) {
SlObject(i, _industry_desc);
}
}
/** Description of the data to save and load in #IndustryBuildData. */
static const SaveLoad _industry_builder_desc[] = {
SLEG_VAR(_industry_builder.wanted_inds, SLE_UINT32),
};
/** Load/save industry builder. */
static void LoadSave_IBLD()
{
SlGlobList(_industry_builder_desc);
}
/** Description of the data to save and load in #IndustryTypeBuildData. */
static const SaveLoad _industrytype_builder_desc[] = {
SLE_VAR(IndustryTypeBuildData, probability, SLE_UINT32),
SLE_VAR(IndustryTypeBuildData, min_number, SLE_UINT8),
SLE_VAR(IndustryTypeBuildData, target_count, SLE_UINT16),
SLE_VAR(IndustryTypeBuildData, max_wait, SLE_UINT16),
SLE_VAR(IndustryTypeBuildData, wait_count, SLE_UINT16),
};
/** Save industry-type build data. */
static void Save_ITBL()
{
for (int i = 0; i < NUM_INDUSTRYTYPES; i++) {
SlSetArrayIndex(i);
SlObject(_industry_builder.builddata + i, _industrytype_builder_desc);
}
}
/** Load industry-type build data. */
static void Load_ITBL()
{
for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
_industry_builder.builddata[it].Reset();
}
int index;
while ((index = SlIterateArray()) != -1) {
if ((uint)index >= NUM_INDUSTRYTYPES) SlErrorCorrupt("Too many industry builder datas");
SlObject(_industry_builder.builddata + index, _industrytype_builder_desc);
}
}
static const ChunkHandler industry_chunk_handlers[] = {
{ 'INDY', Save_INDY, Load_INDY, Ptrs_INDY, nullptr, CH_ARRAY },
{ 'IIDS', Save_IIDS, Load_IIDS, nullptr, nullptr, CH_ARRAY },
{ 'TIDS', Save_TIDS, Load_TIDS, nullptr, nullptr, CH_ARRAY },
{ 'IBLD', LoadSave_IBLD, LoadSave_IBLD, nullptr, nullptr, CH_RIFF },
{ 'ITBL', Save_ITBL, Load_ITBL, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _industry_chunk_handlers(industry_chunk_handlers);

138
src/sl/labelmaps_sl.cpp Normal file
View File

@@ -0,0 +1,138 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file labelmaps_sl.cpp Code handling saving and loading of rail type label mappings */
#include "../stdafx.h"
#include "../station_map.h"
#include "../tunnelbridge_map.h"
#include "saveload.h"
#include "saveload_internal.h"
#include "../safeguards.h"
std::vector<RailTypeLabel> _railtype_list;
/**
* Test if any saved rail type labels are different to the currently loaded
* rail types, which therefore requires conversion.
* @return true if (and only if) conversion due to rail type changes is needed.
*/
static bool NeedRailTypeConversion()
{
for (uint i = 0; i < _railtype_list.size(); i++) {
if ((RailType)i < RAILTYPE_END) {
const RailtypeInfo *rti = GetRailTypeInfo((RailType)i);
if (rti->label != _railtype_list[i]) return true;
} else {
if (_railtype_list[i] != 0) return true;
}
}
/* No rail type conversion is necessary */
return false;
}
void AfterLoadLabelMaps()
{
if (NeedRailTypeConversion()) {
RailType railtype_conversion_map[RAILTYPE_END];
for (uint i = 0; i < _railtype_list.size(); i++) {
RailType r = GetRailTypeByLabel(_railtype_list[i]);
if (r == INVALID_RAILTYPE) r = RAILTYPE_BEGIN;
railtype_conversion_map[i] = r;
}
for (uint i = (uint)_railtype_list.size(); i < RAILTYPE_END; i++) {
railtype_conversion_map[i] = RAILTYPE_RAIL;
}
auto convert = [&](TileIndex t) {
SetRailType(t, railtype_conversion_map[GetRailType(t)]);
RailType secondary = GetTileSecondaryRailTypeIfValid(t);
if (secondary != INVALID_RAILTYPE) SetSecondaryRailType(t, railtype_conversion_map[secondary]);
};
for (TileIndex t = 0; t < MapSize(); t++) {
switch (GetTileType(t)) {
case MP_RAILWAY:
convert(t);
break;
case MP_ROAD:
if (IsLevelCrossing(t)) {
convert(t);
}
break;
case MP_STATION:
if (HasStationRail(t)) {
convert(t);
}
break;
case MP_TUNNELBRIDGE:
if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) {
convert(t);
}
break;
default:
break;
}
}
}
ResetLabelMaps();
}
void ResetLabelMaps()
{
_railtype_list.clear();
}
/** Container for a label for SaveLoad system */
struct LabelObject {
uint32 label;
};
static const SaveLoad _label_object_desc[] = {
SLE_VAR(LabelObject, label, SLE_UINT32),
};
static void Save_RAIL()
{
LabelObject lo;
for (RailType r = RAILTYPE_BEGIN; r != RAILTYPE_END; r++) {
lo.label = GetRailTypeInfo(r)->label;
SlSetArrayIndex(r);
SlObject(&lo, _label_object_desc);
}
}
static void Load_RAIL()
{
ResetLabelMaps();
LabelObject lo;
while (SlIterateArray() != -1) {
SlObject(&lo, _label_object_desc);
_railtype_list.push_back((RailTypeLabel)lo.label);
}
}
static const ChunkHandler labelmaps_chunk_handlers[] = {
{ 'RAIL', Save_RAIL, Load_RAIL, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _labelmaps_chunk_handlers(labelmaps_chunk_handlers);

28
src/sl/league_sl.cpp Normal file
View File

@@ -0,0 +1,28 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file league_sl.cpp Code handling saving and loading of league tables */
#include "../stdafx.h"
#include "saveload.h"
extern SaveLoadVersion _sl_xv_upstream_version;
struct GetLeagueChunkLoadInfo
{
static SaveLoadVersion GetLoadVersion()
{
return _sl_xv_upstream_version != SL_MIN_VERSION ? _sl_xv_upstream_version : SLV_MULTITRACK_LEVEL_CROSSINGS;
}
};
static const ChunkHandler league_chunk_handlers[] = {
MakeUpstreamChunkHandler<'LEAE', GetLeagueChunkLoadInfo>(),
MakeUpstreamChunkHandler<'LEAT', GetLeagueChunkLoadInfo>(),
};
extern const ChunkHandlerTable _league_chunk_handlers(league_chunk_handlers);

371
src/sl/linkgraph_sl.cpp Normal file
View File

@@ -0,0 +1,371 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file linkgraph_sl.cpp Code handling saving and loading of link graphs */
#include "../stdafx.h"
#include "../linkgraph/linkgraph.h"
#include "../linkgraph/linkgraphjob.h"
#include "../linkgraph/linkgraphschedule.h"
#include "../network/network.h"
#include "../settings_internal.h"
#include "saveload.h"
#include "../safeguards.h"
typedef LinkGraph::BaseNode Node;
typedef LinkGraph::BaseEdge Edge;
const SettingDesc *GetSettingDescription(uint index);
static uint16 _num_nodes;
/**
* Get a SaveLoad array for a link graph.
* @return SaveLoad array for link graph.
*/
SaveLoadTable GetLinkGraphDesc()
{
static const SaveLoad link_graph_desc[] = {
SLE_VAR(LinkGraph, last_compression, SLE_INT32),
SLEG_VAR(_num_nodes, SLE_UINT16),
SLE_VAR(LinkGraph, cargo, SLE_UINT8),
};
return link_graph_desc;
}
void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj)
{
lgj->join_date_ticks *= DAY_TICKS;
lgj->join_date_ticks += LinkGraphSchedule::SPAWN_JOIN_TICK;
lgj->start_date_ticks = lgj->join_date_ticks - (lgj->Settings().recalc_time * DAY_TICKS);
}
/**
* Get a SaveLoad array for a link graph job. The settings struct is derived from
* the global settings saveload array. The exact entries are calculated when the function
* is called the first time.
* It's necessary to keep a copy of the settings for each link graph job so that you can
* change the settings while in-game and still not mess with current link graph runs.
* Of course the settings have to be saved and loaded, too, to avoid desyncs.
* @return Array of SaveLoad structs.
*/
SaveLoadTable GetLinkGraphJobDesc()
{
static std::vector<SaveLoad> saveloads;
static const char *prefix = "linkgraph.";
/* Build the SaveLoad array on first call and don't touch it later on */
if (saveloads.size() == 0) {
size_t offset_gamesettings = cpp_offsetof(GameSettings, linkgraph);
size_t offset_component = cpp_offsetof(LinkGraphJob, settings);
size_t prefixlen = strlen(prefix);
int setting = 0;
const SettingDesc *desc = GetSettingDescription(setting);
while (desc != nullptr) {
if (desc->name != nullptr && strncmp(desc->name, prefix, prefixlen) == 0) {
SaveLoad sl = desc->save;
if (GetVarMemType(sl.conv) != SLE_VAR_NULL) {
char *&address = reinterpret_cast<char *&>(sl.address);
address -= offset_gamesettings;
address += offset_component;
}
saveloads.push_back(sl);
}
desc = GetSettingDescription(++setting);
}
const SaveLoad job_desc[] = {
SLE_VAR(LinkGraphJob, join_date_ticks, SLE_INT32),
SLE_CONDVAR_X(LinkGraphJob, start_date_ticks, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_DAY_SCALE)),
SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16),
};
for (auto &sld : job_desc) {
saveloads.push_back(sld);
}
}
return saveloads;
}
/**
* Get a SaveLoad array for the link graph schedule.
* @return SaveLoad array for the link graph schedule.
*/
SaveLoadTable GetLinkGraphScheduleDesc()
{
static const SaveLoad schedule_desc[] = {
SLE_REFLIST(LinkGraphSchedule, schedule, REF_LINK_GRAPH),
SLE_REFLIST(LinkGraphSchedule, running, REF_LINK_GRAPH_JOB),
};
return schedule_desc;
}
/* Edges and nodes are saved in the correct order, so we don't need to save their IDs. */
/**
* SaveLoad desc for a link graph node.
*/
static const SaveLoad _node_desc[] = {
SLE_CONDVAR(Node, xy, SLE_UINT32, SLV_191, SL_MAX_VERSION),
SLE_VAR(Node, supply, SLE_UINT32),
SLE_VAR(Node, demand, SLE_UINT32),
SLE_VAR(Node, station, SLE_UINT16),
SLE_VAR(Node, last_update, SLE_INT32),
};
/**
* SaveLoad desc for a link graph edge.
*/
static const SaveLoad _edge_desc[] = {
SLE_CONDNULL(4, SL_MIN_VERSION, SLV_191), // distance
SLE_VAR(Edge, capacity, SLE_UINT32),
SLE_VAR(Edge, usage, SLE_UINT32),
SLE_CONDVAR_X(Edge, travel_time_sum, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_TRAVEL_TIME)),
SLE_VAR(Edge, last_unrestricted_update, SLE_INT32),
SLE_CONDVAR(Edge, last_restricted_update, SLE_INT32, SLV_187, SL_MAX_VERSION),
SLE_CONDVAR_X(Edge, last_aircraft_update, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_LINKGRAPH_AIRCRAFT)),
// SLE_VAR(Edge, next_edge, SLE_UINT16), // Removed since XSLFI_LINKGRAPH_SPARSE_EDGES
};
std::vector<SaveLoad> _filtered_node_desc;
std::vector<SaveLoad> _filtered_edge_desc;
std::vector<SaveLoad> _filtered_job_desc;
static void FilterDescs()
{
_filtered_node_desc = SlFilterObject(_node_desc);
_filtered_edge_desc = SlFilterObject(_edge_desc);
_filtered_job_desc = SlFilterObject(GetLinkGraphJobDesc());
}
/**
* Save a link graph.
* @param lg Link graph to be saved or loaded.
*/
void Save_LinkGraph(LinkGraph &lg)
{
uint16 size = lg.Size();
auto edge_iter = lg.edges.begin();
auto edge_end = lg.edges.end();
for (NodeID from = 0; from < size; ++from) {
Node *node = &lg.nodes[from];
SlObjectSaveFiltered(node, _filtered_node_desc);
while (edge_iter != edge_end && edge_iter->first.first == from) {
SlWriteUint16(edge_iter->first.second);
Edge *edge = &edge_iter->second;
SlObjectSaveFiltered(edge, _filtered_edge_desc);
++edge_iter;
}
SlWriteUint16(INVALID_NODE);
}
}
/**
* Load a link graph.
* @param lg Link graph to be saved or loaded.
*/
void Load_LinkGraph(LinkGraph &lg)
{
uint size = lg.Size();
if (SlXvIsFeaturePresent(XSLFI_LINKGRAPH_SPARSE_EDGES)) {
for (NodeID from = 0; from < size; ++from) {
Node *node = &lg.nodes[from];
SlObjectLoadFiltered(node, _filtered_node_desc);
while (true) {
NodeID to = SlReadUint16();
if (to == INVALID_NODE) break;
SlObjectLoadFiltered(&lg.edges[std::make_pair(from, to)], _filtered_edge_desc);
}
}
} else if (IsSavegameVersionBefore(SLV_191)) {
std::vector<Edge> temp_edges;
std::vector<NodeID> temp_next_edges;
temp_edges.resize(size);
temp_next_edges.resize(size);
for (NodeID from = 0; from < size; ++from) {
Node *node = &lg.nodes[from];
SlObjectLoadFiltered(node, _filtered_node_desc);
/* We used to save the full matrix ... */
for (NodeID to = 0; to < size; ++to) {
SlObjectLoadFiltered(&temp_edges[to], _filtered_edge_desc);
temp_next_edges[to] = SlReadUint16();
}
for (NodeID to = from; to != INVALID_NODE; to = temp_next_edges[to]) {
lg.edges[std::make_pair(from, to)] = temp_edges[to];
}
}
} else {
for (NodeID from = 0; from < size; ++from) {
Node *node = &lg.nodes[from];
SlObjectLoadFiltered(node, _filtered_node_desc);
/* ... but as that wasted a lot of space we save a sparse matrix now. */
for (NodeID to = from; to != INVALID_NODE;) {
if (to >= size) SlErrorCorrupt("Link graph structure overflow");
SlObjectLoadFiltered(&lg.edges[std::make_pair(from, to)], _filtered_edge_desc);
to = SlReadUint16();
}
}
}
}
/**
* Save a link graph job.
* @param lgj LinkGraphJob to be saved.
*/
static void DoSave_LGRJ(LinkGraphJob *lgj)
{
SlObjectSaveFiltered(lgj, _filtered_job_desc);
_num_nodes = lgj->Size();
SlObjectSaveFiltered(const_cast<LinkGraph *>(&lgj->Graph()), GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals
Save_LinkGraph(const_cast<LinkGraph &>(lgj->Graph()));
}
/**
* Save a link graph.
* @param lg LinkGraph to be saved.
*/
static void DoSave_LGRP(LinkGraph *lg)
{
_num_nodes = lg->Size();
SlObjectSaveFiltered(lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals
Save_LinkGraph(*lg);
}
/**
* Load all link graphs.
*/
static void Load_LGRP()
{
FilterDescs();
int index;
while ((index = SlIterateArray()) != -1) {
if (!LinkGraph::CanAllocateItem()) {
/* Impossible as they have been present in previous game. */
NOT_REACHED();
}
LinkGraph *lg = new (index) LinkGraph();
SlObjectLoadFiltered(lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals
lg->Init(_num_nodes);
Load_LinkGraph(*lg);
}
}
/**
* Load all link graph jobs.
*/
static void Load_LGRJ()
{
FilterDescs();
int index;
while ((index = SlIterateArray()) != -1) {
if (!LinkGraphJob::CanAllocateItem()) {
/* Impossible as they have been present in previous game. */
NOT_REACHED();
}
LinkGraphJob *lgj = new (index) LinkGraphJob();
SlObjectLoadFiltered(lgj, _filtered_job_desc);
if (SlXvIsFeatureMissing(XSLFI_LINKGRAPH_DAY_SCALE)) {
extern void GetLinkGraphJobDayLengthScaleAfterLoad(LinkGraphJob *lgj);
GetLinkGraphJobDayLengthScaleAfterLoad(lgj);
}
LinkGraph &lg = const_cast<LinkGraph &>(lgj->Graph());
SlObjectLoadFiltered(&lg, GetLinkGraphDesc()); // GetLinkGraphDesc has no conditionals
lg.Init(_num_nodes);
Load_LinkGraph(lg);
}
}
/**
* Load the link graph schedule.
*/
static void Load_LGRS()
{
SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc());
}
/**
* Spawn the threads for running link graph calculations.
* Has to be done after loading as the cargo classes might have changed.
*/
void AfterLoadLinkGraphs()
{
if (IsSavegameVersionBefore(SLV_191)) {
for (LinkGraph *lg : LinkGraph::Iterate()) {
for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) {
const Station *st = Station::GetIfValid((*lg)[node_id].Station());
if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy);
}
}
for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) {
LinkGraph *lg = &(const_cast<LinkGraph &>(lgj->Graph()));
for (NodeID node_id = 0; node_id < lg->Size(); ++node_id) {
const Station *st = Station::GetIfValid((*lg)[node_id].Station());
if (st != nullptr) (*lg)[node_id].UpdateLocation(st->xy);
}
}
}
LinkGraphSchedule::instance.SpawnAll();
if (!_networking || _network_server) {
AfterLoad_LinkGraphPauseControl();
}
}
/**
* Save all link graphs.
*/
static void Save_LGRP()
{
FilterDescs();
for (LinkGraph *lg : LinkGraph::Iterate()) {
SlSetArrayIndex(lg->index);
SlAutolength((AutolengthProc*)DoSave_LGRP, lg);
}
}
/**
* Save all link graph jobs.
*/
static void Save_LGRJ()
{
FilterDescs();
for (LinkGraphJob *lgj : LinkGraphJob::Iterate()) {
SlSetArrayIndex(lgj->index);
SlAutolength((AutolengthProc*)DoSave_LGRJ, lgj);
}
}
/**
* Save the link graph schedule.
*/
static void Save_LGRS()
{
SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc());
}
/**
* Substitute pointers in link graph schedule.
*/
static void Ptrs_LGRS()
{
SlObject(&LinkGraphSchedule::instance, GetLinkGraphScheduleDesc());
}
static const ChunkHandler linkgraph_chunk_handlers[] = {
{ 'LGRP', Save_LGRP, Load_LGRP, nullptr, nullptr, CH_ARRAY },
{ 'LGRJ', Save_LGRJ, Load_LGRJ, nullptr, nullptr, CH_ARRAY },
{ 'LGRS', Save_LGRS, Load_LGRS, Ptrs_LGRS, nullptr, CH_RIFF }
};
extern const ChunkHandlerTable _linkgraph_chunk_handlers(linkgraph_chunk_handlers);

420
src/sl/map_sl.cpp Normal file
View File

@@ -0,0 +1,420 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file map_sl.cpp Code handling saving and loading of map */
#include "../stdafx.h"
#include "../map_func.h"
#include "../core/bitmath_func.hpp"
#include "../core/endian_func.hpp"
#include "../core/endian_type.hpp"
#include "../fios.h"
#include <array>
#include "saveload.h"
#include "saveload_buffer.h"
#include "../safeguards.h"
static uint32 _map_dim_x;
static uint32 _map_dim_y;
extern bool _sl_maybe_chillpp;
static const SaveLoad _map_dimensions[] = {
SLEG_CONDVAR(_map_dim_x, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLEG_CONDVAR(_map_dim_y, SLE_UINT32, SLV_6, SL_MAX_VERSION),
};
static void Save_MAPS()
{
_map_dim_x = MapSizeX();
_map_dim_y = MapSizeY();
SlGlobList(_map_dimensions);
}
static void Load_MAPS()
{
SlGlobList(_map_dimensions);
if (!ValidateMapSize(_map_dim_x, _map_dim_y)) {
SlErrorCorruptFmt("Invalid map size: %u x %u", _map_dim_x, _map_dim_y);
}
AllocateMap(_map_dim_x, _map_dim_y);
}
static void Check_MAPS()
{
SlGlobList(_map_dimensions);
_load_check_data.map_size_x = _map_dim_x;
_load_check_data.map_size_y = _map_dim_y;
}
static const uint MAP_SL_BUF_SIZE = 4096;
static void Load_MAPT()
{
std::array<byte, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].type = buf[j];
}
}
static void Check_MAPH_common()
{
if (_sl_maybe_chillpp && (SlGetFieldLength() == 0 || SlGetFieldLength() == (size_t)_map_dim_x * (size_t)_map_dim_y * 2)) {
_sl_maybe_chillpp = false;
extern void SlXvChillPPSpecialSavegameVersions();
SlXvChillPPSpecialSavegameVersions();
}
}
static void Check_MAPH()
{
Check_MAPH_common();
SlSkipBytes(SlGetFieldLength());
}
static void Load_MAPH()
{
Check_MAPH_common();
if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) {
if (SlGetFieldLength() != 0) {
_sl_xv_feature_versions[XSLFI_HEIGHT_8_BIT] = 2;
std::array<uint16, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j];
}
}
return;
}
std::array<byte, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].height = buf[j];
}
}
static void Load_MAP1()
{
std::array<byte, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m1 = buf[j];
}
}
static void Load_MAP2()
{
std::array<uint16, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE,
/* In those versions the m2 was 8 bits */
IsSavegameVersionBefore(SLV_5) ? SLE_FILE_U8 | SLE_VAR_U16 : SLE_UINT16
);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m2 = buf[j];
}
}
static void Load_MAP3()
{
std::array<byte, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m3 = buf[j];
}
}
static void Load_MAP4()
{
std::array<byte, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m4 = buf[j];
}
}
static void Load_MAP5()
{
std::array<byte, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _m[i++].m5 = buf[j];
}
}
static void Load_MAP6()
{
std::array<byte, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
if (IsSavegameVersionBefore(SLV_42)) {
for (TileIndex i = 0; i != size;) {
/* 1024, otherwise we overflow on 64x64 maps! */
SlArray(buf.data(), 1024, SLE_UINT8);
for (uint j = 0; j != 1024; j++) {
_me[i++].m6 = GB(buf[j], 0, 2);
_me[i++].m6 = GB(buf[j], 2, 2);
_me[i++].m6 = GB(buf[j], 4, 2);
_me[i++].m6 = GB(buf[j], 6, 2);
}
}
} else {
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m6 = buf[j];
}
}
}
static void Load_MAP7()
{
std::array<byte, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT8);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m7 = buf[j];
}
}
static void Load_MAP8()
{
std::array<uint16, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
for (TileIndex i = 0; i != size;) {
SlArray(buf.data(), MAP_SL_BUF_SIZE, SLE_UINT16);
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) _me[i++].m8 = buf[j];
}
}
static void Load_WMAP()
{
static_assert(sizeof(Tile) == 8);
static_assert(sizeof(TileExtended) == 4);
assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 1 || _sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2);
ReadBuffer *reader = ReadBuffer::GetCurrent();
const TileIndex size = MapSize();
#if TTD_ENDIAN == TTD_LITTLE_ENDIAN
reader->CopyBytes((byte *) _m, size * 8);
#else
for (TileIndex i = 0; i != size; i++) {
reader->CheckBytes(8);
_m[i].type = reader->RawReadByte();
_m[i].height = reader->RawReadByte();
uint16 m2 = reader->RawReadByte();
m2 |= ((uint16) reader->RawReadByte()) << 8;
_m[i].m2 = m2;
_m[i].m1 = reader->RawReadByte();
_m[i].m3 = reader->RawReadByte();
_m[i].m4 = reader->RawReadByte();
_m[i].m5 = reader->RawReadByte();
}
#endif
if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 1) {
for (TileIndex i = 0; i != size; i++) {
reader->CheckBytes(2);
_me[i].m6 = reader->RawReadByte();
_me[i].m7 = reader->RawReadByte();
}
} else if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2) {
#if TTD_ENDIAN == TTD_LITTLE_ENDIAN
reader->CopyBytes((byte *) _me, size * 4);
#else
for (TileIndex i = 0; i != size; i++) {
reader->CheckBytes(4);
_me[i].m6 = reader->RawReadByte();
_me[i].m7 = reader->RawReadByte();
uint16 m8 = reader->RawReadByte();
m8 |= ((uint16) reader->RawReadByte()) << 8;
_me[i].m8 = m8;
}
#endif
} else {
NOT_REACHED();
}
}
static void Save_WMAP()
{
static_assert(sizeof(Tile) == 8);
static_assert(sizeof(TileExtended) == 4);
assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 2);
MemoryDumper *dumper = MemoryDumper::GetCurrent();
const TileIndex size = MapSize();
SlSetLength(size * 12);
#if TTD_ENDIAN == TTD_LITTLE_ENDIAN
dumper->CopyBytes((byte *) _m, size * 8);
dumper->CopyBytes((byte *) _me, size * 4);
#else
for (TileIndex i = 0; i != size; i++) {
dumper->CheckBytes(8);
dumper->RawWriteByte(_m[i].type);
dumper->RawWriteByte(_m[i].height);
dumper->RawWriteByte(GB(_m[i].m2, 0, 8));
dumper->RawWriteByte(GB(_m[i].m2, 8, 8));
dumper->RawWriteByte(_m[i].m1);
dumper->RawWriteByte(_m[i].m3);
dumper->RawWriteByte(_m[i].m4);
dumper->RawWriteByte(_m[i].m5);
}
for (TileIndex i = 0; i != size; i++) {
dumper->CheckBytes(4);
dumper->RawWriteByte(_me[i].m6);
dumper->RawWriteByte(_me[i].m7);
dumper->RawWriteByte(GB(_me[i].m8, 0, 8));
dumper->RawWriteByte(GB(_me[i].m8, 8, 8));
}
#endif
}
struct MAPT {
typedef uint8 FieldT;
static const FieldT &GetField(TileIndex t) { return _m[t].type; }
};
struct MAPH {
typedef uint8 FieldT;
static const FieldT &GetField(TileIndex t) { return _m[t].height; }
};
struct MAP1 {
typedef uint8 FieldT;
static const FieldT &GetField(TileIndex t) { return _m[t].m1; }
};
struct MAP2 {
typedef uint16 FieldT;
static const FieldT &GetField(TileIndex t) { return _m[t].m2; }
};
struct MAP3 {
typedef uint8 FieldT;
static const FieldT &GetField(TileIndex t) { return _m[t].m3; }
};
struct MAP4 {
typedef uint8 FieldT;
static const FieldT &GetField(TileIndex t) { return _m[t].m4; }
};
struct MAP5 {
typedef uint8 FieldT;
static const FieldT &GetField(TileIndex t) { return _m[t].m5; }
};
struct MAP6 {
typedef uint8 FieldT;
static const FieldT &GetField(TileIndex t) { return _me[t].m6; }
};
struct MAP7 {
typedef uint8 FieldT;
static const FieldT &GetField(TileIndex t) { return _me[t].m7; }
};
struct MAP8 {
typedef uint16 FieldT;
static const FieldT &GetField(TileIndex t) { return _me[t].m8; }
};
template <typename T>
struct MAP_VarType {};
template <>
struct MAP_VarType<uint8>
{
static const VarType var_type = SLE_UINT8;
};
template <>
struct MAP_VarType<uint16>
{
static const VarType var_type = SLE_UINT16;
};
template <typename T>
static void Save_MAP()
{
assert(_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 0);
std::array<typename T::FieldT, MAP_SL_BUF_SIZE> buf;
TileIndex size = MapSize();
SlSetLength(size * sizeof(typename T::FieldT));
for (TileIndex i = 0; i != size;) {
for (uint j = 0; j != MAP_SL_BUF_SIZE; j++) buf[j] = T::GetField(i++);
SlArray(buf.data(), MAP_SL_BUF_SIZE, MAP_VarType<typename T::FieldT>::var_type);
}
}
static ChunkSaveLoadSpecialOpResult Special_WMAP(uint32 chunk_id, ChunkSaveLoadSpecialOp op)
{
switch (op) {
case CSLSO_SHOULD_SAVE_CHUNK:
if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] == 0) return CSLSOR_DONT_SAVE_CHUNK;
break;
default:
break;
}
return CSLSOR_NONE;
}
static ChunkSaveLoadSpecialOpResult Special_MAP_Chunks(uint32 chunk_id, ChunkSaveLoadSpecialOp op)
{
switch (op) {
case CSLSO_SHOULD_SAVE_CHUNK:
if (_sl_xv_feature_versions[XSLFI_WHOLE_MAP_CHUNK] != 0) return CSLSOR_DONT_SAVE_CHUNK;
break;
default:
break;
}
return CSLSOR_NONE;
}
static const ChunkHandler map_chunk_handlers[] = {
{ 'MAPS', Save_MAPS, Load_MAPS, nullptr, Check_MAPS, CH_RIFF },
{ 'MAPT', Save_MAP<MAPT>, Load_MAPT, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'MAPH', Save_MAP<MAPH>, Load_MAPH, nullptr, Check_MAPH, CH_RIFF, Special_MAP_Chunks },
{ 'MAPO', Save_MAP<MAP1>, Load_MAP1, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'MAP2', Save_MAP<MAP2>, Load_MAP2, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'M3LO', Save_MAP<MAP3>, Load_MAP3, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'M3HI', Save_MAP<MAP4>, Load_MAP4, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'MAP5', Save_MAP<MAP5>, Load_MAP5, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'MAPE', Save_MAP<MAP6>, Load_MAP6, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'MAP7', Save_MAP<MAP7>, Load_MAP7, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'MAP8', Save_MAP<MAP8>, Load_MAP8, nullptr, nullptr, CH_RIFF, Special_MAP_Chunks },
{ 'WMAP', Save_WMAP, Load_WMAP, nullptr, nullptr, CH_RIFF, Special_WMAP },
};
extern const ChunkHandlerTable _map_chunk_handlers(map_chunk_handlers);

186
src/sl/misc_sl.cpp Normal file
View File

@@ -0,0 +1,186 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file misc_sl.cpp Saving and loading of things that didn't fit anywhere else */
#include "../stdafx.h"
#include "../date_func.h"
#include "../zoom_func.h"
#include "../window_gui.h"
#include "../window_func.h"
#include "../viewport_func.h"
#include "../gfx_func.h"
#include "../core/random_func.hpp"
#include "../fios.h"
#include "../road_type.h"
#include "../core/checksum_func.hpp"
#include "../event_logs.h"
#include "../timer/timer.h"
#include "../timer/timer_game_tick.h"
#include "saveload.h"
#include "../safeguards.h"
extern TileIndex _cur_tileloop_tile;
extern TileIndex _aux_tileloop_tile;
extern uint16 _disaster_delay;
extern byte _trees_tick_ctr;
extern uint64 _aspect_cfg_hash;
/* Keep track of current game position */
int _saved_scrollpos_x;
int _saved_scrollpos_y;
ZoomLevel _saved_scrollpos_zoom;
void SaveViewportBeforeSaveGame()
{
const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
if (w != nullptr) {
_saved_scrollpos_x = w->viewport->scrollpos_x;
_saved_scrollpos_y = w->viewport->scrollpos_y;
_saved_scrollpos_zoom = w->viewport->zoom;
}
}
void ResetViewportAfterLoadGame()
{
Window *w = GetMainWindow();
w->viewport->scrollpos_x = _saved_scrollpos_x;
w->viewport->scrollpos_y = _saved_scrollpos_y;
w->viewport->dest_scrollpos_x = _saved_scrollpos_x;
w->viewport->dest_scrollpos_y = _saved_scrollpos_y;
Viewport *vp = w->viewport;
vp->zoom = std::min(_saved_scrollpos_zoom, ZOOM_LVL_MAX);
vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
/* If zoom_max is ZOOM_LVL_MIN then the setting has not been loaded yet, therefore all levels are allowed. */
if (_settings_client.gui.zoom_max != ZOOM_LVL_MIN) {
/* Ensure zoom level is allowed */
while (vp->zoom < _settings_client.gui.zoom_min) DoZoomInOutWindow(ZOOM_OUT, w);
while (vp->zoom > _settings_client.gui.zoom_max) DoZoomInOutWindow(ZOOM_IN, w);
}
DoZoomInOutWindow(ZOOM_NONE, w); // update button status
MarkWholeScreenDirty();
}
byte _age_cargo_skip_counter; ///< Skip aging of cargo? Used before savegame version 162.
extern TimeoutTimer<TimerGameTick> _new_competitor_timeout;
static const SaveLoad _date_desc[] = {
SLEG_CONDVAR(_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),
SLEG_CONDVAR(_date, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLEG_VAR(_date_fract, SLE_UINT16),
SLEG_CONDVAR_X(_tick_counter, SLE_FILE_U16 | SLE_VAR_U64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)),
SLEG_CONDVAR_X(_tick_counter, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)),
SLEG_CONDVAR_X(_tick_skip_counter, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH)),
SLEG_CONDVAR_X(_scaled_tick_counter, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)),
SLEG_CONDVAR_X(_scaled_date_ticks_offset, SLE_INT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_157), // _vehicle_id_ctr_day
SLEG_CONDVAR(_age_cargo_skip_counter, SLE_UINT8, SL_MIN_VERSION, SLV_162),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_46),
SLEG_CONDVAR(_cur_tileloop_tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLEG_CONDVAR(_cur_tileloop_tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLEG_VAR(_disaster_delay, SLE_UINT16),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120),
SLEG_VAR(_random.state[0], SLE_UINT32),
SLEG_VAR(_random.state[1], SLE_UINT32),
SLEG_CONDVAR_X(_state_checksum.state, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATE_CHECKSUM)),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_10),
SLE_CONDNULL(4, SLV_10, SLV_120),
SLEG_VAR(_cur_company_tick_index, SLE_FILE_U8 | SLE_VAR_U32),
SLEG_CONDVAR(_new_competitor_timeout.period, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_109),
SLEG_CONDVAR_X(_new_competitor_timeout.period, SLE_UINT32, SLV_109, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE, 0, 0)),
SLEG_VAR(_trees_tick_ctr, SLE_UINT8),
SLEG_CONDVAR(_pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION),
SLEG_CONDVAR_X(_game_events_overall, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)),
SLEG_CONDVAR_X(_road_layout_change_counter, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 4, 6)), // _extra_aspects
SLEG_CONDVAR_X(_aspect_cfg_hash, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 7)),
SLEG_CONDVAR_X(_aux_tileloop_tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUX_TILE_LOOP)),
SLE_CONDNULL(4, SLV_11, SLV_120),
SLEG_CONDVAR_X(_new_competitor_timeout.period, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)),
SLEG_CONDVAR_X(_new_competitor_timeout.storage.elapsed, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)),
SLEG_CONDVAR_X(_new_competitor_timeout.fired, SLE_BOOL, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)),
};
static const SaveLoad _date_check_desc[] = {
SLEG_CONDVAR(_load_check_data.current_date, SLE_FILE_U16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_31),
SLEG_CONDVAR(_load_check_data.current_date, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLE_NULL(2), // _date_fract
SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER, 0, 0)), // _tick_counter
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_U64_TICK_COUNTER)), // _tick_counter
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH)), // _tick_skip_counter
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), // _scaled_tick_counter
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VARIABLE_DAY_LENGTH, 3)), // _scaled_date_ticks_offset
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_157), // _vehicle_id_ctr_day
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_162), // _age_cargo_skip_counter
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_46),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), // _cur_tileloop_tile
SLE_CONDNULL(4, SLV_6, SL_MAX_VERSION), // _cur_tileloop_tile
SLE_NULL(2), // _disaster_delay
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_120),
SLE_NULL(4), // _random.state[0]
SLE_NULL(4), // _random.state[1]
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATE_CHECKSUM)), // _state_checksum.state
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_10),
SLE_CONDNULL(4, SLV_10, SLV_120),
SLE_NULL(1), // _cur_company_tick_index
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_109), // _new_competitor_timeout.period
SLE_CONDNULL_X(4, SLV_109, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE, 0, 0)), // _new_competitor_timeout.period
SLE_NULL(1), // _trees_tick_ctr
SLE_CONDNULL(1, SLV_4, SL_MAX_VERSION), // _pause_mode
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)), // _game_events_overall
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), // _road_layout_change_counter
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 4, 6)), // _extra_aspects
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 7)), // _aspect_cfg_hash
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AUX_TILE_LOOP)), // _aux_tileloop_tile
SLE_CONDNULL(4, SLV_11, SLV_120),
SLE_CONDNULL_X(9, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_AI_START_DATE)), // _new_competitor_timeout
};
/* Save load date related variables as well as persistent tick counters
* XXX: currently some unrelated stuff is just put here */
static void SaveLoad_DATE()
{
SlGlobList(_date_desc);
SetScaledTickVariables();
}
static void Check_DATE()
{
SlGlobList(_date_check_desc);
if (IsSavegameVersionBefore(SLV_31)) {
_load_check_data.current_date += DAYS_TILL_ORIGINAL_BASE_YEAR;
}
}
static const SaveLoad _view_desc[] = {
SLEG_CONDVAR(_saved_scrollpos_x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6),
SLEG_CONDVAR(_saved_scrollpos_x, SLE_INT32, SLV_6, SL_MAX_VERSION),
SLEG_CONDVAR(_saved_scrollpos_y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_6),
SLEG_CONDVAR(_saved_scrollpos_y, SLE_INT32, SLV_6, SL_MAX_VERSION),
SLEG_VAR(_saved_scrollpos_zoom, SLE_UINT8),
};
static void SaveLoad_VIEW()
{
SlGlobList(_view_desc);
}
static const ChunkHandler misc_chunk_handlers[] = {
{ 'DATE', SaveLoad_DATE, SaveLoad_DATE, nullptr, Check_DATE, CH_RIFF },
{ 'VIEW', SaveLoad_VIEW, SaveLoad_VIEW, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _misc_chunk_handlers(misc_chunk_handlers);

133
src/sl/newgrf_sl.cpp Normal file
View File

@@ -0,0 +1,133 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file newgrf_sl.cpp Code handling saving and loading of newgrf config */
#include "../stdafx.h"
#include "../fios.h"
#include "../string_func.h"
#include "saveload.h"
#include "newgrf_sl.h"
#include "../safeguards.h"
/** Save and load the mapping between a spec and the NewGRF it came from. */
static const SaveLoad _newgrf_mapping_desc_old[] = {
SLE_VAR(EntityIDMapping, grfid, SLE_UINT32),
SLE_VAR(EntityIDMapping, entity_id, SLE_FILE_U8 | SLE_VAR_U16),
SLE_VAR(EntityIDMapping, substitute_id, SLE_FILE_U8 | SLE_VAR_U16),
};
static const SaveLoad _newgrf_mapping_desc_new[] = {
SLE_VAR(EntityIDMapping, grfid, SLE_UINT32),
SLE_VAR(EntityIDMapping, entity_id, SLE_UINT16),
SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT16),
};
/**
* Save a GRF ID + local id -> OpenTTD's id mapping.
* @param mapping The mapping to save.
*/
void Save_NewGRFMapping(const OverrideManagerBase &mapping)
{
for (uint i = 0; i < mapping.GetMaxMapping(); i++) {
if (mapping.mappings[i].grfid == 0 &&
mapping.mappings[i].entity_id == 0) continue;
SlSetArrayIndex(i);
SlSetLength(4 + 2 + 2);
SlObjectSaveFiltered(const_cast<EntityIDMapping *>(&mapping.mappings[i]), _newgrf_mapping_desc_new); // _newgrf_mapping_desc_new has no conditionals
}
}
/**
* Load a GRF ID + local id -> OpenTTD's id mapping.
* @param mapping The mapping to load.
*/
void Load_NewGRFMapping(OverrideManagerBase &mapping)
{
/* Clear the current mapping stored.
* This will create the manager if ever it is not yet done */
mapping.ResetMapping();
uint max_id = mapping.GetMaxMapping();
SaveLoadTable slt = SlXvIsFeaturePresent(XSLFI_NEWGRF_ENTITY_EXTRA) ? SaveLoadTable(_newgrf_mapping_desc_new) : SaveLoadTable(_newgrf_mapping_desc_old);
int index;
while ((index = SlIterateArray()) != -1) {
if (unlikely((uint)index >= max_id)) SlErrorCorrupt("Too many NewGRF entity mappings");
SlObjectLoadFiltered(&mapping.mappings[index], slt); // _newgrf_mapping_desc_old/_newgrf_mapping_desc_new has no conditionals
}
}
static std::string _grf_name;
static const SaveLoad _grfconfig_desc[] = {
SLE_SSTR(GRFConfig, filename, SLE_STR),
SLE_VAR(GRFConfig, ident.grfid, SLE_UINT32),
SLE_ARR(GRFConfig, ident.md5sum, SLE_UINT8, 16),
SLE_CONDVAR(GRFConfig, version, SLE_UINT32, SLV_151, SL_MAX_VERSION),
SLE_ARR(GRFConfig, param, SLE_UINT32, 0x80),
SLE_VAR(GRFConfig, num_params, SLE_UINT8),
SLE_CONDVAR(GRFConfig, palette, SLE_UINT8, SLV_101, SL_MAX_VERSION),
SLEG_CONDSSTR_X(_grf_name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_INFO_EXTRA)),
};
static void Save_NGRF()
{
int index = 0;
for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
if (HasBit(c->flags, GCF_STATIC) || HasBit(c->flags, GCF_INIT_ONLY)) continue;
SlSetArrayIndex(index++);
_grf_name = str_strip_all_scc(GetDefaultLangGRFStringFromGRFText(c->name));
SlObject(c, _grfconfig_desc);
}
}
static void Load_NGRF_common(GRFConfig *&grfconfig)
{
ClearGRFConfigList(&grfconfig);
while (SlIterateArray() != -1) {
GRFConfig *c = new GRFConfig();
SlObject(c, _grfconfig_desc);
if (SlXvIsFeaturePresent(XSLFI_NEWGRF_INFO_EXTRA)) {
AddGRFTextToList(c->name, 0x7F, c->ident.grfid, false, _grf_name.c_str());
}
if (IsSavegameVersionBefore(SLV_101)) c->SetSuitablePalette();
AppendToGRFConfigList(&grfconfig, c);
}
}
static void Load_NGRF()
{
Load_NGRF_common(_grfconfig);
if (_game_mode == GM_MENU) {
/* Intro game must not have NewGRF. */
if (_grfconfig != nullptr) SlErrorCorrupt("The intro game must not use NewGRF");
/* Activate intro NewGRFs (townnames) */
ResetGRFConfig(false);
} else {
/* Append static NewGRF configuration */
AppendStaticGRFConfigs(&_grfconfig);
}
}
static void Check_NGRF()
{
Load_NGRF_common(_load_check_data.grfconfig);
}
static const ChunkHandler newgrf_chunk_handlers[] = {
{ 'NGRF', Save_NGRF, Load_NGRF, nullptr, Check_NGRF, CH_ARRAY }
};
extern const ChunkHandlerTable _newgrf_chunk_handlers(newgrf_chunk_handlers);

18
src/sl/newgrf_sl.h Normal file
View File

@@ -0,0 +1,18 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file newgrf_sl.h Code handling saving and loading of NewGRF mappings. */
#ifndef SL_NEWGRF_SL_H
#define SL_NEWGRF_SL_H
#include "../newgrf_commons.h"
void Save_NewGRFMapping(const OverrideManagerBase &mapping);
void Load_NewGRFMapping(OverrideManagerBase &mapping);
#endif /* SL_NEWGRF_SL_H */

42
src/sl/newsignals_sl.cpp Normal file
View File

@@ -0,0 +1,42 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file newsignals_sl.cpp Code handling saving and loading of new signals */
#include "../stdafx.h"
#include "../newgrf_newsignals.h"
#include "saveload.h"
#include "../safeguards.h"
static void Save_NSID()
{
SlSetLength(4 + (lengthof(_new_signal_style_mapping) * 5));
SlWriteUint32(lengthof(_new_signal_style_mapping));
for (const NewSignalStyleMapping &mapping : _new_signal_style_mapping) {
SlWriteUint32(mapping.grfid);
SlWriteByte(mapping.grf_local_id);
}
}
static void Load_NSID()
{
uint count = SlReadUint32();
for (uint i = 0; i < count; i++) {
NewSignalStyleMapping mapping;
mapping.grfid = SlReadUint32();
mapping.grf_local_id = SlReadByte();
if (i < lengthof(_new_signal_style_mapping)) _new_signal_style_mapping[i] = mapping;
}
}
static const ChunkHandler new_signal_chunk_handlers[] = {
{ 'NSID', Save_NSID, Load_NSID, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _new_signal_chunk_handlers(new_signal_chunk_handlers);

74
src/sl/object_sl.cpp Normal file
View File

@@ -0,0 +1,74 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file object_sl.cpp Code handling saving and loading of objects */
#include "../stdafx.h"
#include "../object_base.h"
#include "../object_map.h"
#include "saveload.h"
#include "newgrf_sl.h"
#include "../safeguards.h"
static const SaveLoad _object_desc[] = {
SLE_VAR(Object, location.tile, SLE_UINT32),
SLE_VAR(Object, location.w, SLE_FILE_U8 | SLE_VAR_U16),
SLE_VAR(Object, location.h, SLE_FILE_U8 | SLE_VAR_U16),
SLE_REF(Object, town, REF_TOWN),
SLE_VAR(Object, build_date, SLE_UINT32),
SLE_CONDVAR(Object, colour, SLE_UINT8, SLV_148, SL_MAX_VERSION),
SLE_CONDVAR(Object, view, SLE_UINT8, SLV_155, SL_MAX_VERSION),
SLE_CONDVAR(Object, type, SLE_UINT16, SLV_186, SL_MAX_VERSION),
};
static void Save_OBJS()
{
/* Write the objects */
for (Object *o : Object::Iterate()) {
SlSetArrayIndex(o->index);
SlObject(o, _object_desc);
}
}
static void Load_OBJS()
{
int index;
while ((index = SlIterateArray()) != -1) {
Object *o = new (index) Object();
SlObject(o, _object_desc);
}
}
static void Ptrs_OBJS()
{
for (Object *o : Object::Iterate()) {
SlObject(o, _object_desc);
if (IsSavegameVersionBefore(SLV_148) && !IsTileType(o->location.tile, MP_OBJECT)) {
/* Due to a small bug stale objects could remain. */
delete o;
}
}
}
static void Save_OBID()
{
Save_NewGRFMapping(_object_mngr);
}
static void Load_OBID()
{
Load_NewGRFMapping(_object_mngr);
}
static const ChunkHandler object_chunk_handlers[] = {
{ 'OBID', Save_OBID, Load_OBID, nullptr, nullptr, CH_ARRAY },
{ 'OBJS', Save_OBJS, Load_OBJS, Ptrs_OBJS, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _object_chunk_handlers(object_chunk_handlers);

333
src/sl/oldloader.cpp Normal file
View File

@@ -0,0 +1,333 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file oldloader.cpp Functions for handling of TTO/TTD/TTDP savegames. */
#include "../stdafx.h"
#include "../debug.h"
#include "../strings_type.h"
#include "../string_func.h"
#include "../settings_type.h"
#include "../fileio_func.h"
#include "table/strings.h"
#include "saveload_internal.h"
#include "oldloader.h"
#include <exception>
#include "../safeguards.h"
static const int TTO_HEADER_SIZE = 41;
static const int TTD_HEADER_SIZE = 49;
uint32 _bump_assert_value;
static inline OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 4);}
static inline OldChunkType GetOldChunkVarType(OldChunkType type) {return (OldChunkType)(GB(type, 8, 8) << 8);}
static inline OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);}
static inline byte CalcOldVarLen(OldChunkType type)
{
static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8};
byte length = GB(type, 8, 8);
assert(length != 0 && length < lengthof(type_mem_size));
return type_mem_size[length];
}
/**
*
* Reads a byte from a file (do not call yourself, use ReadByte())
*
*/
static byte ReadByteFromFile(LoadgameState *ls)
{
/* To avoid slow reads, we read BUFFER_SIZE of bytes per time
and just return a byte per time */
if (ls->buffer_cur >= ls->buffer_count) {
/* Read some new bytes from the file */
int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file);
/* We tried to read, but there is nothing in the file anymore.. */
if (count == 0) {
DEBUG(oldloader, 0, "Read past end of file, loading failed");
throw std::exception();
}
ls->buffer_count = count;
ls->buffer_cur = 0;
}
return ls->buffer[ls->buffer_cur++];
}
/**
*
* Reads a byte from the buffer and decompress if needed
*
*/
byte ReadByte(LoadgameState *ls)
{
/* Old savegames have a nice compression algorithm (RLE)
which means that we have a chunk, which starts with a length
byte. If that byte is negative, we have to repeat the next byte
that many times ( + 1). Else, we need to read that amount of bytes.
Works pretty well if you have many zeros behind each other */
if (ls->chunk_size == 0) {
/* Read new chunk */
int8 new_byte = ReadByteFromFile(ls);
if (new_byte < 0) {
/* Repeat next char for new_byte times */
ls->decoding = true;
ls->decode_char = ReadByteFromFile(ls);
ls->chunk_size = -new_byte + 1;
} else {
ls->decoding = false;
ls->chunk_size = new_byte + 1;
}
}
ls->total_read++;
ls->chunk_size--;
return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
}
/**
*
* Loads a chunk from the old savegame
*
*/
bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
{
byte *base_ptr = (byte*)base;
for (const OldChunks *chunk = chunks; chunk->type != OC_END; chunk++) {
if (((chunk->type & OC_TTD) && _savegame_type == SGT_TTO) ||
((chunk->type & OC_TTO) && _savegame_type != SGT_TTO)) {
/* TTD(P)-only chunk, but TTO savegame || TTO-only chunk, but TTD/TTDP savegame */
continue;
}
byte *ptr = (byte*)chunk->ptr;
if (chunk->type & OC_DEREFERENCE_POINTER) ptr = *(byte**)ptr;
for (uint i = 0; i < chunk->amount; i++) {
/* Handle simple types */
if (GetOldChunkType(chunk->type) != 0) {
switch (GetOldChunkType(chunk->type)) {
/* Just read the byte and forget about it */
case OC_NULL: ReadByte(ls); break;
case OC_CHUNK:
/* Call function, with 'i' as parameter to tell which item we
* are going to read */
if (!chunk->proc(ls, i)) return false;
break;
case OC_ASSERT:
DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value);
if (ls->total_read != chunk->offset + _bump_assert_value) throw std::exception();
default: break;
}
} else {
uint64 res = 0;
/* Reading from the file: bits 16 to 23 have the FILE type */
switch (GetOldChunkFileType(chunk->type)) {
case OC_FILE_I8: res = (int8)ReadByte(ls); break;
case OC_FILE_U8: res = ReadByte(ls); break;
case OC_FILE_I16: res = (int16)ReadUint16(ls); break;
case OC_FILE_U16: res = ReadUint16(ls); break;
case OC_FILE_I32: res = (int32)ReadUint32(ls); break;
case OC_FILE_U32: res = ReadUint32(ls); break;
default: NOT_REACHED();
}
/* When both pointers are nullptr, we are just skipping data */
if (base_ptr == nullptr && chunk->ptr == nullptr) continue;
/* Writing to the var: bits 8 to 15 have the VAR type */
if (chunk->ptr == nullptr) ptr = base_ptr + chunk->offset;
/* Write the data */
switch (GetOldChunkVarType(chunk->type)) {
case OC_VAR_I8: *(int8 *)ptr = GB(res, 0, 8); break;
case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break;
case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break;
case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break;
case OC_VAR_I32:*(int32 *)ptr = res; break;
case OC_VAR_U32:*(uint32*)ptr = res; break;
case OC_VAR_I64:*(int64 *)ptr = res; break;
case OC_VAR_U64:*(uint64*)ptr = res; break;
default: NOT_REACHED();
}
/* Increase pointer base for arrays when looping */
if (chunk->amount > 1 && chunk->ptr != nullptr) ptr += CalcOldVarLen(chunk->type);
}
}
}
return true;
}
/**
*
* Initialize some data before reading
*
*/
static void InitLoading(LoadgameState *ls)
{
ls->chunk_size = 0;
ls->total_read = 0;
ls->decoding = false;
ls->decode_char = 0;
ls->buffer_cur = 0;
ls->buffer_count = 0;
memset(ls->buffer, 0, BUFFER_SIZE);
_bump_assert_value = 0;
_settings_game.construction.freeform_edges = false; // disable so we can convert map array (SetTileType is still used)
}
/**
* Verifies the title has a valid checksum
* @param title title and checksum
* @param len the length of the title to read/checksum
* @return true iff the title is valid
* @note the title (incl. checksum) has to be at least 41/49 (HEADER_SIZE) bytes long!
*/
static bool VerifyOldNameChecksum(char *title, uint len)
{
uint16 sum = 0;
for (uint i = 0; i < len - 2; i++) {
sum += title[i];
sum = ROL(sum, 1);
}
sum ^= 0xAAAA; // computed checksum
uint16 sum2 = title[len - 2]; // checksum in file
SB(sum2, 8, 8, title[len - 1]);
return sum == sum2;
}
static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len)
{
assert(last - temp + 1 >= (int)len);
if (fread(temp, 1, len, f) != len) {
temp[0] = '\0'; // if reading failed, make the name empty
return false;
}
bool ret = VerifyOldNameChecksum(temp, len);
temp[len - 2] = '\0'; // name is null-terminated in savegame, but it's better to be sure
StrMakeValidInPlace(temp, last);
return ret;
}
static SavegameType DetermineOldSavegameType(FILE *f, char *title, const char *last)
{
static_assert(TTD_HEADER_SIZE >= TTO_HEADER_SIZE);
char temp[TTD_HEADER_SIZE] = "Unknown";
SavegameType type = SGT_TTO;
/* Can't fseek to 0 as in tar files that is not correct */
long pos = ftell(f);
if (pos >= 0 && !CheckOldSavegameType(f, temp, lastof(temp), TTO_HEADER_SIZE)) {
type = SGT_TTD;
if (fseek(f, pos, SEEK_SET) < 0 || !CheckOldSavegameType(f, temp, lastof(temp), TTD_HEADER_SIZE)) {
type = SGT_INVALID;
}
}
if (title != nullptr) {
switch (type) {
case SGT_TTO: title = strecpy(title, "(TTO) ", last); break;
case SGT_TTD: title = strecpy(title, "(TTD) ", last); break;
default: title = strecpy(title, "(broken) ", last); break;
}
strecpy(title, temp, last);
}
return type;
}
typedef bool LoadOldMainProc(LoadgameState *ls);
bool LoadOldSaveGame(const std::string &file)
{
LoadgameState ls;
DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame");
InitLoading(&ls);
/* Open file */
ls.file = FioFOpenFile(file, "rb", NO_DIRECTORY);
if (ls.file == nullptr) {
DEBUG(oldloader, 0, "Cannot open file '%s'", file.c_str());
return false;
}
SavegameType type = DetermineOldSavegameType(ls.file, nullptr, nullptr);
LoadOldMainProc *proc = nullptr;
switch (type) {
case SGT_TTO: proc = &LoadTTOMain; break;
case SGT_TTD: proc = &LoadTTDMain; break;
default: break;
}
_savegame_type = type;
bool game_loaded;
try {
game_loaded = proc != nullptr && proc(&ls);
} catch (...) {
game_loaded = false;
}
fclose(ls.file);
if (!game_loaded) {
SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED);
return false;
}
_pause_mode = PM_PAUSED_SAVELOAD;
return true;
}
void GetOldSaveGameName(const std::string &file, char *title, const char *last)
{
FILE *f = FioFOpenFile(file, "rb", NO_DIRECTORY);
if (f == nullptr) {
*title = '\0';
return;
}
DetermineOldSavegameType(f, title, last);
fclose(f);
}

135
src/sl/oldloader.h Normal file
View File

@@ -0,0 +1,135 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file oldloader.h Declarations of strctures and function used in loader of old savegames */
#ifndef SL_OLDLOADER_H
#define SL_OLDLOADER_H
#include "saveload.h"
#include "../tile_type.h"
static const uint BUFFER_SIZE = 4096;
static const uint OLD_MAP_SIZE = 256 * 256;
struct LoadgameState {
FILE *file;
uint chunk_size;
bool decoding;
byte decode_char;
uint buffer_count;
uint buffer_cur;
byte buffer[BUFFER_SIZE];
uint total_read;
};
/* OldChunk-Type */
enum OldChunkType {
OC_SIMPLE = 0,
OC_NULL = 1,
OC_CHUNK = 2,
OC_ASSERT = 3,
/* 4 bits allocated (16 max) */
OC_TTD = 1 << 4, ///< chunk is valid ONLY for TTD savegames
OC_TTO = 1 << 5, ///< -//- TTO (default is neither of these)
/* 4 bits allocated */
OC_VAR_I8 = 1 << 8,
OC_VAR_U8 = 2 << 8,
OC_VAR_I16 = 3 << 8,
OC_VAR_U16 = 4 << 8,
OC_VAR_I32 = 5 << 8,
OC_VAR_U32 = 6 << 8,
OC_VAR_I64 = 7 << 8,
OC_VAR_U64 = 8 << 8,
/* 8 bits allocated (256 max) */
OC_FILE_I8 = 1 << 16,
OC_FILE_U8 = 2 << 16,
OC_FILE_I16 = 3 << 16,
OC_FILE_U16 = 4 << 16,
OC_FILE_I32 = 5 << 16,
OC_FILE_U32 = 6 << 16,
/* 8 bits allocated (256 max) */
OC_INT8 = OC_VAR_I8 | OC_FILE_I8,
OC_UINT8 = OC_VAR_U8 | OC_FILE_U8,
OC_INT16 = OC_VAR_I16 | OC_FILE_I16,
OC_UINT16 = OC_VAR_U16 | OC_FILE_U16,
OC_INT32 = OC_VAR_I32 | OC_FILE_I32,
OC_UINT32 = OC_VAR_U32 | OC_FILE_U32,
OC_TILE = OC_VAR_U32 | OC_FILE_U16,
/**
* Dereference the pointer once before writing to it,
* so we do not have to use big static arrays.
*/
OC_DEREFERENCE_POINTER = 1 << 31,
OC_END = 0, ///< End of the whole chunk, all 32 bits set to zero
};
DECLARE_ENUM_AS_BIT_SET(OldChunkType)
typedef bool OldChunkProc(LoadgameState *ls, int num);
struct OldChunks {
OldChunkType type; ///< Type of field
uint32 amount; ///< Amount of fields
void *ptr; ///< Pointer where to save the data (may only be set if offset is 0)
uint offset; ///< Offset from basepointer (may only be set if ptr is nullptr)
OldChunkProc *proc; ///< Pointer to function that is called with OC_CHUNK
};
/* If it fails, check lines above.. */
static_assert(sizeof(TileIndex) == 4);
extern uint _bump_assert_value;
byte ReadByte(LoadgameState *ls);
bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks);
bool LoadTTDMain(LoadgameState *ls);
bool LoadTTOMain(LoadgameState *ls);
static inline uint16 ReadUint16(LoadgameState *ls)
{
byte x = ReadByte(ls);
return x | ReadByte(ls) << 8;
}
static inline uint32 ReadUint32(LoadgameState *ls)
{
uint16 x = ReadUint16(ls);
return x | ReadUint16(ls) << 16;
}
/* Help:
* - OCL_SVAR: load 'type' to offset 'offset' in a struct of type 'base', which must also
* be given via base in LoadChunk() as real pointer
* - OCL_VAR: load 'type' to a global var
* - OCL_END: every struct must end with this
* - OCL_NULL: read 'amount' of bytes and send them to /dev/null or something
* - OCL_CHUNK: load another proc to load a part of the savegame, 'amount' times
* - OCL_ASSERT: to check if we are really at the place we expect to be.. because old savegames are too binary to be sure ;)
*/
#define OCL_SVAR(type, base, offset) { type, 1, nullptr, (uint)cpp_offsetof(base, offset), nullptr }
#define OCL_VAR(type, amount, pointer) { type, amount, pointer, 0, nullptr }
#define OCL_END() { OC_END, 0, nullptr, 0, nullptr }
#define OCL_CNULL(type, amount) { OC_NULL | type, amount, nullptr, 0, nullptr }
#define OCL_CCHUNK(type, amount, proc) { OC_CHUNK | type, amount, nullptr, 0, proc }
#define OCL_ASSERT(type, size) { OC_ASSERT | type, 1, nullptr, size, nullptr }
#define OCL_NULL(amount) OCL_CNULL((OldChunkType)0, amount)
#define OCL_CHUNK(amount, proc) OCL_CCHUNK((OldChunkType)0, amount, proc)
#endif /* SL_OLDLOADER_H */

1840
src/sl/oldloader_sl.cpp Normal file

File diff suppressed because it is too large Load Diff

416
src/sl/order_sl.cpp Normal file
View File

@@ -0,0 +1,416 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file order_sl.cpp Code handling saving and loading of orders */
#include "../stdafx.h"
#include "../order_backup.h"
#include "../settings_type.h"
#include "../network/network.h"
#include "saveload_internal.h"
#include "../safeguards.h"
static uint32 _jokerpp_separation_mode;
std::vector<OrderList *> _jokerpp_auto_separation;
std::vector<OrderList *> _jokerpp_non_auto_separation;
/**
* Converts this order from an old savegame's version;
* it moves all bits to the new location.
*/
void Order::ConvertFromOldSavegame()
{
uint8 old_flags = this->flags;
this->flags = 0;
/* First handle non-stop - use value from savegame if possible, else use value from config file */
if (_settings_client.gui.sg_new_nonstop || (IsSavegameVersionBefore(SLV_22) && _savegame_type != SGT_TTO && _savegame_type != SGT_TTD && (_settings_client.gui.new_nonstop || _settings_game.order.nonstop_only))) {
/* OFB_NON_STOP */
this->SetNonStopType((old_flags & 8) ? ONSF_NO_STOP_AT_ANY_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
} else {
this->SetNonStopType((old_flags & 8) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
}
switch (this->GetType()) {
/* Only a few types need the other savegame conversions. */
case OT_GOTO_DEPOT: case OT_GOTO_STATION: case OT_LOADING: break;
default: return;
}
if (this->GetType() != OT_GOTO_DEPOT) {
/* Then the load flags */
if ((old_flags & 2) != 0) { // OFB_UNLOAD
this->SetLoadType(OLFB_NO_LOAD);
} else if ((old_flags & 4) == 0) { // !OFB_FULL_LOAD
this->SetLoadType(OLF_LOAD_IF_POSSIBLE);
} else {
/* old OTTD versions stored full_load_any in config file - assume it was enabled when loading */
this->SetLoadType(_settings_client.gui.sg_full_load_any || IsSavegameVersionBefore(SLV_22) ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD);
}
if (this->IsType(OT_GOTO_STATION)) this->SetStopLocation(OSL_PLATFORM_FAR_END);
/* Finally fix the unload flags */
if ((old_flags & 1) != 0) { // OFB_TRANSFER
this->SetUnloadType(OUFB_TRANSFER);
} else if ((old_flags & 2) != 0) { // OFB_UNLOAD
this->SetUnloadType(OUFB_UNLOAD);
} else {
this->SetUnloadType(OUF_UNLOAD_IF_POSSIBLE);
}
} else {
/* Then the depot action flags */
this->SetDepotActionType(((old_flags & 6) == 4) ? ODATFB_HALT : ODATF_SERVICE_ONLY);
/* Finally fix the depot type flags */
uint t = ((old_flags & 6) == 6) ? ODTFB_SERVICE : ODTF_MANUAL;
if ((old_flags & 2) != 0) t |= ODTFB_PART_OF_ORDERS;
this->SetDepotOrderType((OrderDepotTypeFlags)t);
}
}
/**
* Unpacks a order from savegames with version 4 and lower
* @param packed packed order
* @return unpacked order
*/
static Order UnpackVersion4Order(uint16 packed)
{
return Order(((uint64) GB(packed, 8, 8)) << 24 | ((uint64) GB(packed, 4, 4)) << 8 | ((uint64) GB(packed, 0, 4)));
}
/**
* Unpacks a order from savegames with version 5.1 and lower
* @param packed packed order
* @return unpacked order
*/
static Order UnpackVersion5Order(uint32 packed)
{
return Order(((uint64) GB(packed, 16, 16)) << 24 | ((uint64) GB(packed, 8, 8)) << 8 | ((uint64) GB(packed, 0, 8)));
}
/**
* Unpacks a order from savegames made with TTD(Patch)
* @param packed packed order
* @return unpacked order
*/
Order UnpackOldOrder(uint16 packed)
{
Order order = UnpackVersion4Order(packed);
/*
* Sanity check
* TTD stores invalid orders as OT_NOTHING with non-zero flags/station
*/
if (order.IsType(OT_NOTHING) && packed != 0) order.MakeDummy();
return order;
}
SaveLoadTable GetOrderDescription()
{
static const SaveLoad _order_desc[] = {
SLE_VAR(Order, type, SLE_UINT8),
SLE_CONDVAR_X(Order, flags, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 0, 0)),
SLE_CONDVAR_X(Order, flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_FLAGS_EXTRA, 1)),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)),
SLE_VAR(Order, dest, SLE_UINT16),
SLE_REF(Order, next, REF_ORDER),
SLE_CONDVAR(Order, refit_cargo, SLE_UINT8, SLV_36, SL_MAX_VERSION),
SLE_CONDNULL(1, SLV_36, SLV_182), // refit_subtype
SLE_CONDVAR_X(Order, occupancy, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_OCCUPANCY)),
SLE_CONDVAR_X(Order, wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)),
SLE_CONDVAR_X(Order, wait_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)),
SLE_CONDVAR_X(Order, travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)),
SLE_CONDVAR_X(Order, travel_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)),
SLE_CONDVAR(Order, max_speed, SLE_UINT16, SLV_172, SL_MAX_VERSION),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MORE_COND_ORDERS, 1, 6)), // jump_counter
/* Leftover from the minor savegame version stuff
* We will never use those free bytes, but we have to keep this line to allow loading of old savegames */
SLE_CONDNULL(10, SLV_5, SLV_36),
};
return _order_desc;
}
static std::vector<SaveLoad> _filtered_desc;
static void Save_ORDR()
{
_filtered_desc = SlFilterObject(GetOrderDescription());
for (Order *order : Order::Iterate()) {
SlSetArrayIndex(order->index);
SlObjectSaveFiltered(order, _filtered_desc);
}
}
static void Load_ORDR()
{
if (IsSavegameVersionBefore(SLV_5, 2)) {
/* Version older than 5.2 did not have a ->next pointer. Convert them
* (in the old days, the orderlist was 5000 items big) */
size_t len = SlGetFieldLength();
if (IsSavegameVersionBefore(SLV_5)) {
/* Pre-version 5 had another layout for orders
* (uint16 instead of uint32) */
len /= sizeof(uint16);
uint16 *orders = MallocT<uint16>(len + 1);
SlArray(orders, len, SLE_UINT16);
for (size_t i = 0; i < len; ++i) {
Order *o = new (i) Order();
o->AssignOrder(UnpackVersion4Order(orders[i]));
}
free(orders);
} else if (IsSavegameVersionBefore(SLV_5, 2)) {
len /= sizeof(uint32);
uint32 *orders = MallocT<uint32>(len + 1);
SlArray(orders, len, SLE_UINT32);
for (size_t i = 0; i < len; ++i) {
Order *o = new (i) Order();
o->AssignOrder(UnpackVersion5Order(orders[i]));
}
free(orders);
}
/* Update all the next pointer */
for (Order *o : Order::Iterate()) {
size_t order_index = o->index;
/* Delete invalid orders */
if (o->IsType(OT_NOTHING)) {
delete o;
continue;
}
/* The orders were built like this:
* While the order is valid, set the previous will get its next pointer set */
Order *prev = Order::GetIfValid(order_index - 1);
if (prev != nullptr) prev->next = o;
}
} else {
_filtered_desc = SlFilterObject(GetOrderDescription());
int index;
while ((index = SlIterateArray()) != -1) {
Order *order = new (index) Order();
SlObjectLoadFiltered(order, _filtered_desc);
}
}
}
const SaveLoadTable GetOrderExtraInfoDescription()
{
static const SaveLoad _order_extra_info_desc[] = {
SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, 32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 1, 2)),
SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, NUM_CARGO, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 3)),
SLE_CONDVAR_X(OrderExtraInfo, xflags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)),
SLE_CONDVAR_X(OrderExtraInfo, xdata, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA)),
SLE_CONDVAR_X(OrderExtraInfo, dispatch_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 3)),
SLE_CONDVAR_X(OrderExtraInfo, colour, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA, 2)),
};
return _order_extra_info_desc;
}
void Save_ORDX()
{
_filtered_desc = SlFilterObject(GetOrderExtraInfoDescription());
for (Order *order : Order::Iterate()) {
if (order->extra) {
SlSetArrayIndex(order->index);
SlObjectSaveFiltered(order->extra.get(), _filtered_desc);
}
}
}
void Load_ORDX()
{
_filtered_desc = SlFilterObject(GetOrderExtraInfoDescription());
int index;
while ((index = SlIterateArray()) != -1) {
Order *order = Order::GetIfValid(index);
assert(order != nullptr);
order->AllocExtraInfo();
SlObjectLoadFiltered(order->extra.get(), _filtered_desc);
}
}
static void Ptrs_ORDR()
{
/* Orders from old savegames have pointers corrected in Load_ORDR */
if (IsSavegameVersionBefore(SLV_5, 2)) return;
for (Order *o : Order::Iterate()) {
SlObject(o, GetOrderDescription());
}
}
SaveLoadTable GetDispatchScheduleDescription()
{
static const SaveLoad _order_extra_info_desc[] = {
SLE_VARVEC(DispatchSchedule, scheduled_dispatch, SLE_UINT32),
SLE_VAR(DispatchSchedule, scheduled_dispatch_duration, SLE_UINT32),
SLE_VAR(DispatchSchedule, scheduled_dispatch_start_date, SLE_INT32),
SLE_VAR(DispatchSchedule, scheduled_dispatch_start_full_date_fract, SLE_UINT16),
SLE_VAR(DispatchSchedule, scheduled_dispatch_last_dispatch, SLE_INT32),
SLE_VAR(DispatchSchedule, scheduled_dispatch_max_delay, SLE_INT32),
SLE_CONDSSTR_X(DispatchSchedule, name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 4)),
};
return _order_extra_info_desc;
}
SaveLoadTable GetOrderListDescription()
{
static const SaveLoad _orderlist_desc[] = {
SLE_REF(OrderList, first, REF_ORDER),
SLEG_CONDVAR_X(_jokerpp_separation_mode, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
SLE_CONDNULL_X(21, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
};
return _orderlist_desc;
}
static void Save_ORDL()
{
for (OrderList *list : OrderList::Iterate()) {
SlSetArrayIndex(list->index);
SlAutolength([](void *data) {
OrderList *list = static_cast<OrderList *>(data);
SlObject(list, GetOrderListDescription());
SlWriteUint32(list->GetScheduledDispatchScheduleCount());
for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) {
SlObject(&ds, GetDispatchScheduleDescription());
}
}, list);
}
}
static void Load_ORDL()
{
_jokerpp_auto_separation.clear();
_jokerpp_non_auto_separation.clear();
int index;
while ((index = SlIterateArray()) != -1) {
/* set num_orders to 0 so it's a valid OrderList */
OrderList *list = new (index) OrderList(0);
SlObject(list, GetOrderListDescription());
if (SlXvIsFeaturePresent(XSLFI_JOKERPP)) {
if (_jokerpp_separation_mode == 0) {
_jokerpp_auto_separation.push_back(list);
} else {
_jokerpp_non_auto_separation.push_back(list);
}
}
if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH)) {
uint count = SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3) ? SlReadUint32() : 1;
list->GetScheduledDispatchScheduleSet().resize(count);
for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) {
SlObject(&ds, GetDispatchScheduleDescription());
}
}
}
}
void Ptrs_ORDL()
{
for (OrderList *list : OrderList::Iterate()) {
SlObject(list, GetOrderListDescription());
list->ReindexOrderList();
}
}
SaveLoadTable GetOrderBackupDescription()
{
static const SaveLoad _order_backup_desc[] = {
SLE_VAR(OrderBackup, user, SLE_UINT32),
SLE_VAR(OrderBackup, tile, SLE_UINT32),
SLE_VAR(OrderBackup, group, SLE_UINT16),
SLE_CONDVAR(OrderBackup, service_interval, SLE_FILE_U32 | SLE_VAR_U16, SL_MIN_VERSION, SLV_192),
SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, SLV_192, SL_MAX_VERSION),
SLE_STR(OrderBackup, name, SLE_STR, 0),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_192), // clone (2 bytes of pointer, i.e. garbage)
SLE_CONDREF(OrderBackup, clone, REF_VEHICLE, SLV_192, SL_MAX_VERSION),
SLE_VAR(OrderBackup, cur_real_order_index, SLE_VEHORDERID),
SLE_CONDVAR(OrderBackup, cur_implicit_order_index, SLE_VEHORDERID, SLV_176, SL_MAX_VERSION),
SLE_CONDVAR_X(OrderBackup, cur_timetable_order_index, SLE_VEHORDERID, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)),
SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, SLV_176, SL_MAX_VERSION),
SLE_CONDVAR(OrderBackup, lateness_counter, SLE_INT32, SLV_176, SL_MAX_VERSION),
SLE_CONDVAR(OrderBackup, timetable_start, SLE_INT32, SLV_176, SL_MAX_VERSION),
SLE_CONDVAR_X(OrderBackup,timetable_start_subticks, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)),
SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_176, SLV_180),
SLE_CONDVAR_X(OrderBackup, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 0, 0)),
SLE_CONDVAR_X(OrderBackup, vehicle_flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 1)),
SLE_REF(OrderBackup, orders, REF_ORDER),
SLE_CONDNULL_X(18, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2, 2)),
};
return _order_backup_desc;
}
void Save_BKOR()
{
/* We only save this when we're a network server
* as we want this information on our clients. For
* normal games this information isn't needed. */
if (!_networking || !_network_server) return;
for (OrderBackup *ob : OrderBackup::Iterate()) {
SlSetArrayIndex(ob->index);
SlAutolength([](void *data) {
OrderBackup *ob = static_cast<OrderBackup *>(data);
SlObject(ob, GetOrderBackupDescription());
SlWriteUint32((uint)ob->dispatch_schedules.size());
for (DispatchSchedule &ds : ob->dispatch_schedules) {
SlObject(&ds, GetDispatchScheduleDescription());
}
}, ob);
}
}
void Load_BKOR()
{
int index;
while ((index = SlIterateArray()) != -1) {
/* set num_orders to 0 so it's a valid OrderList */
OrderBackup *ob = new (index) OrderBackup();
SlObject(ob, GetOrderBackupDescription());
if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3)) {
uint count = SlReadUint32();
ob->dispatch_schedules.resize(count);
for (DispatchSchedule &ds : ob->dispatch_schedules) {
SlObject(&ds, GetDispatchScheduleDescription());
}
}
}
}
static void Ptrs_BKOR()
{
for (OrderBackup *ob : OrderBackup::Iterate()) {
SlObject(ob, GetOrderBackupDescription());
}
}
static const ChunkHandler order_chunk_handlers[] = {
{ 'BKOR', Save_BKOR, Load_BKOR, Ptrs_BKOR, nullptr, CH_ARRAY },
{ 'ORDR', Save_ORDR, Load_ORDR, Ptrs_ORDR, nullptr, CH_ARRAY },
{ 'ORDL', Save_ORDL, Load_ORDL, Ptrs_ORDL, nullptr, CH_ARRAY },
{ 'ORDX', Save_ORDX, Load_ORDX, nullptr, nullptr, CH_SPARSE_ARRAY },
};
extern const ChunkHandlerTable _order_chunk_handlers(order_chunk_handlers);

97
src/sl/plans_sl.cpp Normal file
View File

@@ -0,0 +1,97 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file plans_sl.cpp Code handling saving and loading of plans data. */
#include "../stdafx.h"
#include "../plans_base.h"
#include "../fios.h"
#include "saveload.h"
/** Description of a plan within the savegame. */
static const SaveLoad _plan_desc[] = {
SLE_VAR(Plan, owner, SLE_UINT8),
SLE_VAR(Plan, visible, SLE_BOOL),
SLE_VAR(Plan, visible_by_all, SLE_BOOL),
SLE_VAR(Plan, creation_date, SLE_INT32),
SLE_CONDSSTR_X(Plan, name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ENH_VIEWPORT_PLANS, 3)),
SLE_CONDSSTR_X(Plan, name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_20)),
SLE_CONDVAR_X(Plan, colour, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ENH_VIEWPORT_PLANS, 4)),
};
static void RealSave_PLAN(Plan *p)
{
SlObject(p, _plan_desc);
SlWriteUint32((uint32)p->lines.size());
for (size_t i = 0; i < p->lines.size(); i++) {
PlanLine *pl = p->lines[i];
SlWriteUint32((uint32)pl->tiles.size());
SlArray(pl->tiles.data(), pl->tiles.size(), SLE_UINT32);
}
}
/** Save all plans. */
static void Save_PLAN()
{
for (Plan *p : Plan::Iterate()) {
SlSetArrayIndex(p->index);
SlAutolength((AutolengthProc*) RealSave_PLAN, p);
}
}
/** Load all plans. */
static void Load_PLAN()
{
int index;
while ((index = SlIterateArray()) != -1) {
Plan *p = new (index) Plan();
SlObject(p, _plan_desc);
if (SlXvIsFeaturePresent(XSLFI_ENH_VIEWPORT_PLANS, 2)) {
const size_t line_count = SlReadUint32();
p->lines.resize(line_count);
for (size_t i = 0; i < line_count; i++) {
PlanLine *pl = new PlanLine();
p->lines[i] = pl;
const size_t tile_count = SlReadUint32();
pl->tiles.resize(tile_count);
SlArray(pl->tiles.data(), tile_count, SLE_UINT32);
pl->UpdateVisualExtents();
}
p->SetVisibility(false);
}
}
}
/** Load all plan lines. */
static void Load_PLANLINE()
{
int index;
while ((index = SlIterateArray()) != -1) {
Plan *p = Plan::Get((uint) index >> 16);
uint line_index = index & 0xFFFF;
if (p->lines.size() <= line_index) p->lines.resize(line_index + 1);
PlanLine *pl = new PlanLine();
p->lines[line_index] = pl;
size_t plsz = SlGetFieldLength() / sizeof(TileIndex);
pl->tiles.resize(plsz);
SlArray(pl->tiles.data(), plsz, SLE_UINT32);
pl->UpdateVisualExtents();
}
for (Plan *p : Plan::Iterate()) {
p->SetVisibility(false);
}
}
/** Chunk handlers related to plans. */
static const ChunkHandler plan_chunk_handlers[] = {
{ 'PLAN', Save_PLAN, Load_PLAN, nullptr, nullptr, CH_ARRAY },
{ 'PLLN', nullptr, Load_PLANLINE, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _plan_chunk_handlers(plan_chunk_handlers);

3830
src/sl/saveload.cpp Normal file

File diff suppressed because it is too large Load Diff

765
src/sl/saveload.h Normal file
View File

@@ -0,0 +1,765 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file saveload.h Functions/types related to saving and loading games. */
#ifndef SL_SAVELOAD_H
#define SL_SAVELOAD_H
#include "saveload_types.h"
#include "../fileio_type.h"
#include "../fios.h"
#include "../strings_type.h"
#include "../scope.h"
#include <stdarg.h>
#include <vector>
#include <string>
#include <vector>
/** Save or load result codes. */
enum SaveOrLoadResult {
SL_OK = 0, ///< completed successfully
SL_ERROR = 1, ///< error that was caught before internal structures were modified
SL_REINIT = 2, ///< error that was caught in the middle of updating game state, need to clear it. (can only happen during load)
};
/** Deals with the type of the savegame, independent of extension */
struct FileToSaveLoad {
SaveLoadOperation file_op; ///< File operation to perform.
DetailedFileType detail_ftype; ///< Concrete file type (PNG, BMP, old save, etc).
AbstractFileType abstract_ftype; ///< Abstract type of file (scenario, heightmap, etc).
std::string name; ///< Name of the file.
std::string title; ///< Internal name of the game.
void SetMode(FiosType ft);
void SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft);
void Set(const FiosItem &item);
};
/** Types of save games. */
enum SavegameType {
SGT_TTD, ///< TTD savegame (can be detected incorrectly)
SGT_TTDP1, ///< TTDP savegame ( -//- ) (data at NW border)
SGT_TTDP2, ///< TTDP savegame in new format (data at SE border)
SGT_OTTD, ///< OTTD savegame
SGT_TTO, ///< TTO savegame
SGT_INVALID = 0xFF, ///< broken savegame (used internally)
};
enum SaveModeFlags : byte {
SMF_NONE = 0,
SMF_NET_SERVER = 1 << 0, ///< Network server save
SMF_ZSTD_OK = 1 << 1, ///< Zstd OK
SMF_SCENARIO = 1 << 2, ///< Scenario save
};
DECLARE_ENUM_AS_BIT_SET(SaveModeFlags);
extern FileToSaveLoad _file_to_saveload;
void GenerateDefaultSaveName(char *buf, const char *last);
void SetSaveLoadError(StringID str);
const char *GetSaveLoadErrorString();
SaveOrLoadResult SaveOrLoad(const std::string &filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true, SaveModeFlags flags = SMF_NONE);
void WaitTillSaved();
void ProcessAsyncSaveFinish();
void DoExitSave();
void DoAutoOrNetsave(FiosNumberedSaveName &counter, bool threaded);
SaveOrLoadResult SaveWithFilter(struct SaveFilter *writer, bool threaded, SaveModeFlags flags);
SaveOrLoadResult LoadWithFilter(struct LoadFilter *reader);
bool IsNetworkServerSave();
bool IsScenarioSave();
typedef void ChunkSaveLoadProc();
typedef void AutolengthProc(void *arg);
void SlUnreachablePlaceholder();
enum ChunkSaveLoadSpecialOp {
CSLSO_PRE_LOAD,
CSLSO_PRE_LOADCHECK,
CSLSO_SHOULD_SAVE_CHUNK,
};
enum ChunkSaveLoadSpecialOpResult {
CSLSOR_NONE,
CSLSOR_LOAD_CHUNK_CONSUMED,
CSLSOR_DONT_SAVE_CHUNK,
};
typedef ChunkSaveLoadSpecialOpResult ChunkSaveLoadSpecialProc(uint32, ChunkSaveLoadSpecialOp);
/** Type of a chunk. */
enum ChunkType {
CH_RIFF = 0,
CH_ARRAY = 1,
CH_SPARSE_ARRAY = 2,
CH_EXT_HDR = 15, ///< Extended chunk header
CH_UPSTREAM_SAVE = 0x80,
};
/** Handlers and description of chunk. */
struct ChunkHandler {
uint32 id; ///< Unique ID (4 letters).
ChunkSaveLoadProc *save_proc; ///< Save procedure of the chunk.
ChunkSaveLoadProc *load_proc; ///< Load procedure of the chunk.
ChunkSaveLoadProc *ptrs_proc; ///< Manipulate pointers in the chunk.
ChunkSaveLoadProc *load_check_proc; ///< Load procedure for game preview.
ChunkType type; ///< Type of the chunk. @see ChunkType
ChunkSaveLoadSpecialProc *special_proc = nullptr;
};
template <typename F>
void SlExecWithSlVersion(SaveLoadVersion use_version, F proc)
{
extern SaveLoadVersion _sl_version;
SaveLoadVersion old_ver = _sl_version;
_sl_version = use_version;
auto guard = scope_guard([&]() {
_sl_version = old_ver;
});
proc();
}
namespace upstream_sl {
template <uint32 id, typename F>
ChunkHandler MakeUpstreamChunkHandler()
{
extern void SlLoadChunkByID(uint32);
extern void SlLoadCheckChunkByID(uint32);
extern void SlFixPointerChunkByID(uint32);
ChunkHandler ch = {
id,
nullptr,
SlUnreachablePlaceholder,
[]() {
SlExecWithSlVersion(F::GetLoadVersion(), []() {
SlFixPointerChunkByID(id);
});
},
SlUnreachablePlaceholder,
CH_UPSTREAM_SAVE
};
ch.special_proc = [](uint32 chunk_id, ChunkSaveLoadSpecialOp op) -> ChunkSaveLoadSpecialOpResult {
assert(id == chunk_id);
switch (op) {
case CSLSO_PRE_LOAD:
SlExecWithSlVersion(F::GetLoadVersion(), []() {
SlLoadChunkByID(id);
});
return CSLSOR_LOAD_CHUNK_CONSUMED;
case CSLSO_PRE_LOADCHECK:
SlExecWithSlVersion(F::GetLoadVersion(), []() {
SlLoadCheckChunkByID(id);
});
return CSLSOR_LOAD_CHUNK_CONSUMED;
default:
return CSLSOR_NONE;
}
};
return ch;
}
}
using upstream_sl::MakeUpstreamChunkHandler;
struct NullStruct {
byte null;
};
/** A table of ChunkHandler entries. */
using ChunkHandlerTable = span<const ChunkHandler>;
/** Type of reference (#SLE_REF, #SLE_CONDREF). */
enum SLRefType {
REF_ORDER = 0, ///< Load/save a reference to an order.
REF_VEHICLE = 1, ///< Load/save a reference to a vehicle.
REF_STATION = 2, ///< Load/save a reference to a station.
REF_TOWN = 3, ///< Load/save a reference to a town.
REF_VEHICLE_OLD = 4, ///< Load/save an old-style reference to a vehicle (for pre-4.4 savegames).
REF_ROADSTOPS = 5, ///< Load/save a reference to a bus/truck stop.
REF_ENGINE_RENEWS = 6, ///< Load/save a reference to an engine renewal (autoreplace).
REF_CARGO_PACKET = 7, ///< Load/save a reference to a cargo packet.
REF_ORDERLIST = 8, ///< Load/save a reference to an orderlist.
REF_STORAGE = 9, ///< Load/save a reference to a persistent storage.
REF_LINK_GRAPH = 10, ///< Load/save a reference to a link graph.
REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job.
REF_TEMPLATE_VEHICLE = 12, ///< Load/save a reference to a template vehicle
};
/** 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)
/**
* Storage of simple variables, references (pointers), and arrays.
* @param cmd Load/save type. @see SaveLoadType
* @param base Name of the class or struct containing the variable.
* @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 field.
* @param to Last savegame version that has the field.
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
* @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) SaveLoad {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, SlXvFeatureTest())
/**
* Storage of a variable in some savegame versions.
* @param base Name of the class or struct containing the variable.
* @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 field.
* @param to Last savegame version that has the field.
* @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, SlXvFeatureTest())
/**
* Storage of a reference in some savegame versions.
* @param base Name of the class or struct containing the variable.
* @param variable Name of the variable in the class or struct referenced by \a base.
* @param type Type of the reference, a value from #SLRefType.
* @param from First savegame version that has the field.
* @param to Last savegame version that has the field.
* @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, SlXvFeatureTest())
/**
* Storage of a fixed-size array of #SL_VAR elements in some savegame versions.
* @param base Name of the class or struct containing the array.
* @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 length Number of elements in the array.
* @param from First savegame version that has the array.
* @param to Last savegame version that has the array.
* @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, SlXvFeatureTest())
/**
* Storage of a string in some savegame versions.
* @param base Name of the class or struct containing the string.
* @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 length Number of elements in the string (only used for fixed size buffers).
* @param from First savegame version that has the string.
* @param to Last savegame version that has the string.
* @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, SlXvFeatureTest())
/**
* Storage of a \c std::string in some savegame versions.
* @param base Name of the class or struct containing the string.
* @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 string.
* @param to Last savegame version that has the string.
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
*/
#define SLE_CONDSSTR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_STDSTR, base, variable, type, 0, from, to, extver)
#define SLE_CONDSSTR(base, variable, type, from, to) SLE_GENERAL(SL_STDSTR, base, variable, type, 0, from, to)
/**
* Storage of a list of #SL_REF elements 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_CONDREFLIST_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REFLIST, base, variable, type, 0, from, to, extver)
#define SLE_CONDREFLIST(base, variable, type, from, to) SLE_CONDREFLIST_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_CONDPTRDEQ_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_PTRDEQ, base, variable, type, 0, from, to, extver)
#define SLE_CONDPTRDEQ(base, variable, type, from, to) SLE_CONDPTRDEQ_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 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_CONDVARVEC_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VARVEC, base, variable, type, 0, from, to, extver)
#define SLE_CONDVARVEC(base, variable, type, from, to) SLE_CONDVARVEC_X(base, variable, type, from, to, SlXvFeatureTest())
/**
* Storage of a deque of #SL_VAR elements 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_CONDDEQUE_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_DEQUE, base, variable, type, 0, from, to, extver)
#define SLE_CONDDEQUE(base, variable, type, from, to) SLE_CONDDEQUE_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.
* @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_VAR(base, variable, type) SLE_CONDVAR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a reference in every version of a savegame.
* @param base Name of the class or struct containing the variable.
* @param variable Name of the variable in the class or struct referenced by \a base.
* @param type Type of the reference, a value from #SLRefType.
*/
#define SLE_REF(base, variable, type) SLE_CONDREF(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of fixed-size array of #SL_VAR elements in every version of a savegame.
* @param base Name of the class or struct containing the array.
* @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 length Number of elements in the array.
*/
#define SLE_ARR(base, variable, type, length) SLE_CONDARR(base, variable, type, length, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a string in every savegame version.
* @param base Name of the class or struct containing the string.
* @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 length Number of elements in the string (only used for fixed size buffers).
*/
#define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a \c std::string in every savegame version.
* @param base Name of the class or struct containing the string.
* @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_SSTR(base, variable, type) SLE_CONDSSTR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a list of #SL_REF elements 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_REFLIST(base, variable, type) SLE_CONDREFLIST(base, variable, type, SL_MIN_VERSION, 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_PTRDEQ(base, variable, type) SLE_CONDPTRDEQ(base, variable, type, SL_MIN_VERSION, 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, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a variable 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_VARVEC(base, variable, type) SLE_CONDVARVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Empty space in every savegame version.
* @param length Length of the empty space.
*/
#define SLE_NULL(length) SLE_CONDNULL(length, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Empty space in some savegame versions.
* @param length Length of the empty space.
* @param from First savegame version that has the empty space.
* @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 SLE_CONDNULL_X(length, from, to, extver) SLE_CONDARR_X(NullStruct, null, SLE_FILE_U8 | SLE_VAR_NULL, length, from, to, extver)
#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) SLE_GENERAL(SL_WRITEBYTE, base, variable, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION)
#define SLE_VEH_INCLUDE() {false, SL_VEH_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, SlXvFeatureTest()}
#define SLE_ST_INCLUDE() {false, SL_ST_INCLUDE, 0, 0, SL_MIN_VERSION, SL_MAX_VERSION, nullptr, 0, SlXvFeatureTest()}
/**
* Storage of global simple variables, references (pointers), and arrays.
* @param cmd Load/save type. @see SaveLoadType
* @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 field.
* @param to Last savegame version that has the field.
* @param extver SlXvFeatureTest to test (along with from and to) which savegames have the field
* @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) SaveLoad {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, SlXvFeatureTest())
/**
* Storage of a global variable 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 field.
* @param to Last savegame version that has the field.
* @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, SlXvFeatureTest())
/**
* Storage of a global reference 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 field.
* @param to Last savegame version that has the field.
* @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, SlXvFeatureTest())
/**
* Storage of a global fixed-size array of #SL_VAR elements in some savegame versions.
* @param variable Name of the global variable.
* @param type Storage of the data in memory and in the savegame.
* @param length Number of elements in the array.
* @param from First savegame version that has the array.
* @param to Last savegame version that has the array.
* @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, SlXvFeatureTest())
/**
* Storage of a global string in some savegame versions.
* @param variable Name of the global variable.
* @param type Storage of the data in memory and in the savegame.
* @param length Number of elements in the string (only used for fixed size buffers).
* @param from First savegame version that has the string.
* @param to Last savegame version that has the string.
* @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, SlXvFeatureTest())
/**
* Storage of a global \c std::string 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 string.
* @param to Last savegame version that has the string.
*/
#define SLEG_CONDSSTR_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_STDSTR, variable, type, 0, from, to, extver)
#define SLEG_CONDSSTR(variable, type, from, to) SLEG_GENERAL(SL_STDSTR, variable, type, 0, from, to)
/**
* Storage of a global reference list 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_CONDREFLIST_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_REFLIST, variable, type, 0, from, to, extver)
#define SLEG_CONDREFLIST(variable, type, from, to) SLEG_CONDREFLIST_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_CONDPTRDEQ_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_PTRDEQ, variable, type, 0, from, to, extver)
#define SLEG_CONDPTRDEQ(variable, type, from, to) SLEG_CONDPTRDEQ_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 variable 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_CONDVARVEC_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_VARVEC, variable, type, 0, from, to, extver)
#define SLEG_CONDVARVEC(variable, type, from, to) SLEG_CONDVARVEC_X(variable, type, from, to, SlXvFeatureTest())
/**
* Storage of a global variable 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_VAR(variable, type) SLEG_CONDVAR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a global reference 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_REF(variable, type) SLEG_CONDREF(variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a global fixed-size array of #SL_VAR elements 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_ARR(variable, type) SLEG_CONDARR(variable, type, lengthof(variable), SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a global string 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_STR(variable, type) SLEG_CONDSTR(variable, type, sizeof(variable), SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a global \c std::string 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_SSTR(variable, type) SLEG_CONDSSTR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Storage of a global reference list 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_REFLIST(variable, type) SLEG_CONDREFLIST(variable, type, SL_MIN_VERSION, 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_PTRDEQ(variable, type) SLEG_CONDPTRDEQ(variable, type, SL_MIN_VERSION, 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, SL_MIN_VERSION, SL_MAX_VERSION)
/**
* Empty global space in some savegame versions.
* @param length Length of the empty space.
* @param from First savegame version that has the empty space.
* @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, length, from, to, (void*)nullptr, SlXvFeatureTest()}
/**
* Checks whether the savegame is below \a major.\a minor.
* @param major Major number of the version to check against.
* @param minor Minor number of the version to check against. If \a minor is 0 or not specified, only the major number is checked.
* @return Savegame version is earlier than the specified version.
*/
static inline bool IsSavegameVersionBefore(SaveLoadVersion major, byte minor = 0)
{
extern SaveLoadVersion _sl_version;
extern byte _sl_minor_version;
return _sl_version < major || (minor > 0 && _sl_version == major && _sl_minor_version < minor);
}
/**
* Checks whether the savegame is below or at \a major. This should be used to repair data from existing
* savegames which is no longer corrupted in new savegames, but for which otherwise no savegame
* bump is required.
* @param major Major number of the version to check against.
* @return Savegame version is at most the specified version.
*/
static inline bool IsSavegameVersionUntil(SaveLoadVersion major)
{
extern SaveLoadVersion _sl_version;
return _sl_version <= major;
}
/**
* Checks if some version from/to combination falls within the range of the
* active savegame version.
* @param version_from Inclusive savegame version lower bound.
* @param version_to Exclusive savegame version upper bound. SL_MAX_VERSION if no upper bound.
* @return Active savegame version falls within the given range.
*/
static inline bool SlIsObjectCurrentlyValid(SaveLoadVersion version_from, SaveLoadVersion version_to, SlXvFeatureTest ext_feature_test)
{
extern const SaveLoadVersion SAVEGAME_VERSION;
if (!ext_feature_test.IsFeaturePresent(_sl_xv_feature_static_versions, SAVEGAME_VERSION, version_from, version_to)) return false;
return true;
}
/**
* Get the NumberType of a setting. This describes the integer type
* as it is represented in memory
* @param type VarType holding information about the variable-type
* @return the SLE_VAR_* part of a variable-type description
*/
static inline VarType GetVarMemType(VarType type)
{
return type & 0xF0; // GB(type, 4, 4) << 4;
}
/**
* Get the FileType of a setting. This describes the integer type
* as it is represented in a savegame/file
* @param type VarType holding information about the file-type
* @return the SLE_FILE_* part of a variable-type description
*/
static inline VarType GetVarFileType(VarType type)
{
return type & 0xF; // GB(type, 0, 4);
}
/**
* Check if the given saveload type is a numeric type.
* @param conv the type to check
* @return True if it's a numeric type.
*/
static inline bool IsNumericType(VarType conv)
{
return GetVarMemType(conv) <= SLE_VAR_U64;
}
/**
* Get the address of the variable. Which one to pick depends on the object
* pointer. If it is nullptr we are dealing with global variables so the address
* is taken. If non-null only the offset is stored in the union and we need
* to add this to the address of the object
*/
static inline void *GetVariableAddress(const void *object, const SaveLoad &sld)
{
/* Entry is a global address. */
if (sld.global) return sld.address;
#ifdef _DEBUG
/* Entry is a null-variable, mostly used to read old savegames etc. */
if (GetVarMemType(sld.conv) == SLE_VAR_NULL) {
assert(sld.address == nullptr);
return nullptr;
}
/* Everything else should be a non-null pointer. */
assert(object != nullptr);
#endif
return const_cast<byte *>((const byte *)object + (ptrdiff_t)sld.address);
}
int64 ReadValue(const void *ptr, VarType conv);
void WriteValue(void *ptr, VarType conv, int64 val);
void SlSetArrayIndex(uint index);
int SlIterateArray();
void SlAutolength(AutolengthProc *proc, void *arg);
std::vector<uint8> SlSaveToVector(AutolengthProc *proc, void *arg);
size_t SlGetFieldLength();
void SlSetLength(size_t length);
size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld);
size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt);
struct SlLoadFromBufferState {
size_t old_obj_len;
byte *old_bufp;
byte *old_bufe;
};
/**
* Run proc, loading exactly length bytes from the contents of buffer
* @param proc The callback procedure that is called
*/
template <typename F>
void SlLoadFromBuffer(const byte *buffer, size_t length, F proc)
{
extern SlLoadFromBufferState SlLoadFromBufferSetup(const byte *buffer, size_t length);
extern void SlLoadFromBufferRestore(const SlLoadFromBufferState &state, const byte *buffer, size_t length);
SlLoadFromBufferState state = SlLoadFromBufferSetup(buffer, length);
proc();
SlLoadFromBufferRestore(state, buffer, length);
}
void SlGlobList(const SaveLoadTable &slt);
void SlArray(void *array, size_t length, VarType conv);
void SlObject(void *object, const SaveLoadTable &slt);
bool SlObjectMember(void *object, const SaveLoad &sld);
std::vector<SaveLoad> SlFilterObject(const SaveLoadTable &slt);
void SlObjectSaveFiltered(void *object, const SaveLoadTable &slt);
void SlObjectLoadFiltered(void *object, const SaveLoadTable &slt);
void SlObjectPtrOrNullFiltered(void *object, const SaveLoadTable &slt);
void NORETURN CDECL SlErrorFmt(StringID string, const char *msg, ...) WARN_FORMAT(2, 3);
bool SaveloadCrashWithMissingNewGRFs();
void SlResetVENC();
void SlProcessVENC();
void SlResetTNNC();
extern std::string _savegame_format;
extern bool _do_autosave;
#endif /* SL_SAVELOAD_H */

264
src/sl/saveload_buffer.h Normal file
View File

@@ -0,0 +1,264 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file saveload_buffer.h Functions/types related to buffers used for saving and loading games. */
#ifndef SL_SAVELOAD_BUFFER_H
#define SL_SAVELOAD_BUFFER_H
#include "../core/alloc_func.hpp"
#include "../core/endian_type.hpp"
#include "../core/endian_func.hpp"
#include "../core/math_func.hpp"
#include <vector>
#include <utility>
struct LoadFilter;
struct SaveFilter;
/** Save in chunks of 128 KiB. */
static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
/** A buffer for reading (and buffering) savegame data. */
struct ReadBuffer {
byte buf[MEMORY_CHUNK_SIZE]; ///< Buffer we're going to read from.
byte *bufp; ///< Location we're at reading the buffer.
byte *bufe; ///< End of the buffer we can read from.
LoadFilter *reader; ///< The filter used to actually read.
size_t read; ///< The amount of read bytes so far from the filter.
/**
* Initialise our variables.
* @param reader The filter to actually read data.
*/
ReadBuffer(LoadFilter *reader) : bufp(nullptr), bufe(nullptr), reader(reader), read(0)
{
}
static ReadBuffer *GetCurrent();
void SkipBytesSlowPath(size_t bytes);
void AcquireBytes();
inline void SkipBytes(size_t bytes)
{
byte *b = this->bufp + bytes;
if (likely(b <= this->bufe)) {
this->bufp = b;
} else {
SkipBytesSlowPath(bytes);
}
}
inline byte RawReadByte()
{
return *this->bufp++;
}
inline byte ReadByte()
{
if (unlikely(this->bufp == this->bufe)) {
this->AcquireBytes();
}
return RawReadByte();
}
inline void CheckBytes(size_t bytes)
{
while (unlikely(this->bufp + bytes > this->bufe)) this->AcquireBytes();
}
inline int RawReadUint16()
{
#if OTTD_ALIGNMENT == 0
int x = FROM_BE16(*((const unaligned_uint16*) this->bufp));
this->bufp += 2;
return x;
#else
int x = this->RawReadByte() << 8;
return x | this->RawReadByte();
#endif
}
inline uint32 RawReadUint32()
{
#if OTTD_ALIGNMENT == 0
uint32 x = FROM_BE32(*((const unaligned_uint32*) this->bufp));
this->bufp += 4;
return x;
#else
uint32 x = this->RawReadUint16() << 16;
return x | this->RawReadUint16();
#endif
}
inline uint64 RawReadUint64()
{
#if OTTD_ALIGNMENT == 0
uint64 x = FROM_BE64(*((const unaligned_uint64*) this->bufp));
this->bufp += 8;
return x;
#else
uint32 x = this->RawReadUint32();
uint32 y = this->RawReadUint32();
return (uint64)x << 32 | y;
#endif
}
inline void CopyBytes(byte *ptr, size_t length)
{
while (length) {
if (unlikely(this->bufp == this->bufe)) {
this->AcquireBytes();
}
size_t to_copy = std::min<size_t>(this->bufe - this->bufp, length);
memcpy(ptr, this->bufp, to_copy);
this->bufp += to_copy;
ptr += to_copy;
length -= to_copy;
}
}
/**
* Get the size of the memory dump made so far.
* @return The size.
*/
inline size_t GetSize() const
{
return this->read - (this->bufe - this->bufp);
}
};
/** Container for dumping the savegame (quickly) to memory. */
struct MemoryDumper {
struct BufferInfo {
byte *data;
size_t size = 0;
BufferInfo(byte *d) : data(d) {}
~BufferInfo() { free(this->data); }
BufferInfo(const BufferInfo &) = delete;
BufferInfo(BufferInfo &&other) : data(other.data), size(other.size) { other.data = nullptr; };
};
std::vector<BufferInfo> blocks; ///< Buffer with blocks of allocated memory.
byte *buf = nullptr; ///< Buffer we're going to write to.
byte *bufe = nullptr; ///< End of the buffer we write to.
size_t completed_block_bytes = 0; ///< Total byte count of completed blocks.
byte *autolen_buf = nullptr;
byte *autolen_buf_end = nullptr;
byte *saved_buf = nullptr;
byte *saved_bufe = nullptr;
MemoryDumper()
{
const size_t size = 8192;
this->autolen_buf = CallocT<byte>(size);
this->autolen_buf_end = this->autolen_buf + size;
}
~MemoryDumper()
{
free(this->autolen_buf);
}
static MemoryDumper *GetCurrent();
void FinaliseBlock();
void AllocateBuffer();
inline void CheckBytes(size_t bytes)
{
if (unlikely(this->buf + bytes > this->bufe)) this->AllocateBuffer();
}
/**
* Write a single byte into the dumper.
* @param b The byte to write.
*/
inline void WriteByte(byte b)
{
/* Are we at the end of this chunk? */
if (unlikely(this->buf == this->bufe)) {
this->AllocateBuffer();
}
*this->buf++ = b;
}
inline void CopyBytes(const byte *ptr, size_t length)
{
while (length) {
if (unlikely(this->buf == this->bufe)) {
this->AllocateBuffer();
}
size_t to_copy = std::min<size_t>(this->bufe - this->buf, length);
memcpy(this->buf, ptr, to_copy);
this->buf += to_copy;
ptr += to_copy;
length -= to_copy;
}
}
inline void RawWriteByte(byte b)
{
*this->buf++ = b;
}
inline void RawWriteUint16(uint16 v)
{
#if OTTD_ALIGNMENT == 0
*((unaligned_uint16 *) this->buf) = TO_BE16(v);
#else
this->buf[0] = GB(v, 8, 8);
this->buf[1] = GB(v, 0, 8);
#endif
this->buf += 2;
}
inline void RawWriteUint32(uint32 v)
{
#if OTTD_ALIGNMENT == 0
*((unaligned_uint32 *) this->buf) = TO_BE32(v);
#else
this->buf[0] = GB(v, 24, 8);
this->buf[1] = GB(v, 16, 8);
this->buf[2] = GB(v, 8, 8);
this->buf[3] = GB(v, 0, 8);
#endif
this->buf += 4;
}
inline void RawWriteUint64(uint64 v)
{
#if OTTD_ALIGNMENT == 0
*((unaligned_uint64 *) this->buf) = TO_BE64(v);
#else
this->buf[0] = GB(v, 56, 8);
this->buf[1] = GB(v, 48, 8);
this->buf[2] = GB(v, 40, 8);
this->buf[3] = GB(v, 32, 8);
this->buf[4] = GB(v, 24, 8);
this->buf[5] = GB(v, 16, 8);
this->buf[6] = GB(v, 8, 8);
this->buf[7] = GB(v, 0, 8);
#endif
this->buf += 8;
}
void Flush(SaveFilter *writer);
size_t GetSize() const;
void StartAutoLength();
std::pair<byte *, size_t> StopAutoLength();
};
#endif /* SL_SAVELOAD_BUFFER_H */

422
src/sl/saveload_common.h Normal file
View File

@@ -0,0 +1,422 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file saveload_common.h Common functions/types for saving and loading games. */
#ifndef SL_SAVELOAD_COMMON_H
#define SL_SAVELOAD_COMMON_H
#include "../strings_type.h"
#include "../core/span_type.hpp"
struct SaveLoad;
/** A table of SaveLoad entries. */
using SaveLoadTable = span<const SaveLoad>;
namespace upstream_sl {
struct SaveLoad;
/** A table of SaveLoad entries. */
using SaveLoadTable = span<const SaveLoad>;
}
/** SaveLoad versions
* Previous savegame versions, the trunk revision where they were
* introduced and the released version that had that particular
* savegame version.
* Up to savegame version 18 there is a minor version as well.
*
* Older entries keep their original numbering.
*
* Newer entries should use a descriptive labels, numeric version
* and PR can be added to comment.
*
* Note that this list must not be reordered.
*/
enum SaveLoadVersion : uint16 {
SL_MIN_VERSION, ///< First savegame version
SLV_1, ///< 1.0 0.1.x, 0.2.x
SLV_2, /**< 2.0 0.3.0
* 2.1 0.3.1, 0.3.2 */
SLV_3, ///< 3.x lost
SLV_4, /**< 4.0 1
* 4.1 122 0.3.3, 0.3.4
* 4.2 1222 0.3.5
* 4.3 1417
* 4.4 1426 */
SLV_5, /**< 5.0 1429
* 5.1 1440
* 5.2 1525 0.3.6 */
SLV_6, /**< 6.0 1721
* 6.1 1768 */
SLV_7, ///< 7.0 1770
SLV_8, ///< 8.0 1786
SLV_9, ///< 9.0 1909
SLV_10, ///< 10.0 2030
SLV_11, /**< 11.0 2033
* 11.1 2041 */
SLV_12, ///< 12.1 2046
SLV_13, ///< 13.1 2080 0.4.0, 0.4.0.1
SLV_14, ///< 14.0 2441
SLV_15, ///< 15.0 2499
SLV_16, /**< 16.0 2817
* 16.1 3155 */
SLV_17, /**< 17.0 3212
* 17.1 3218 */
SLV_18, ///< 18 3227
SLV_19, ///< 19 3396
SLV_20, ///< 20 3403
SLV_21, ///< 21 3472 0.4.x
SLV_22, ///< 22 3726
SLV_23, ///< 23 3915
SLV_24, ///< 24 4150
SLV_25, ///< 25 4259
SLV_26, ///< 26 4466
SLV_27, ///< 27 4757
SLV_28, ///< 28 4987
SLV_29, ///< 29 5070
SLV_30, ///< 30 5946
SLV_31, ///< 31 5999
SLV_32, ///< 32 6001
SLV_33, ///< 33 6440
SLV_34, ///< 34 6455
SLV_35, ///< 35 6602
SLV_36, ///< 36 6624
SLV_37, ///< 37 7182
SLV_38, ///< 38 7195
SLV_39, ///< 39 7269
SLV_40, ///< 40 7326
SLV_41, ///< 41 7348 0.5.x
SLV_42, ///< 42 7573
SLV_43, ///< 43 7642
SLV_44, ///< 44 8144
SLV_45, ///< 45 8501
SLV_46, ///< 46 8705
SLV_47, ///< 47 8735
SLV_48, ///< 48 8935
SLV_49, ///< 49 8969
SLV_50, ///< 50 8973
SLV_51, ///< 51 8978
SLV_52, ///< 52 9066
SLV_53, ///< 53 9316
SLV_54, ///< 54 9613
SLV_55, ///< 55 9638
SLV_56, ///< 56 9667
SLV_57, ///< 57 9691
SLV_58, ///< 58 9762
SLV_59, ///< 59 9779
SLV_60, ///< 60 9874
SLV_61, ///< 61 9892
SLV_62, ///< 62 9905
SLV_63, ///< 63 9956
SLV_64, ///< 64 10006
SLV_65, ///< 65 10210
SLV_66, ///< 66 10211
SLV_67, ///< 67 10236
SLV_68, ///< 68 10266
SLV_69, ///< 69 10319
SLV_70, ///< 70 10541
SLV_71, ///< 71 10567
SLV_72, ///< 72 10601
SLV_73, ///< 73 10903
SLV_74, ///< 74 11030
SLV_75, ///< 75 11107
SLV_76, ///< 76 11139
SLV_77, ///< 77 11172
SLV_78, ///< 78 11176
SLV_79, ///< 79 11188
SLV_80, ///< 80 11228
SLV_81, ///< 81 11244
SLV_82, ///< 82 11410
SLV_83, ///< 83 11589
SLV_84, ///< 84 11822
SLV_85, ///< 85 11874
SLV_86, ///< 86 12042
SLV_87, ///< 87 12129
SLV_88, ///< 88 12134
SLV_89, ///< 89 12160
SLV_90, ///< 90 12293
SLV_91, ///< 91 12347
SLV_92, ///< 92 12381 0.6.x
SLV_93, ///< 93 12648
SLV_94, ///< 94 12816
SLV_95, ///< 95 12924
SLV_96, ///< 96 13226
SLV_97, ///< 97 13256
SLV_98, ///< 98 13375
SLV_99, ///< 99 13838
SLV_100, ///< 100 13952
SLV_101, ///< 101 14233
SLV_102, ///< 102 14332
SLV_103, ///< 103 14598
SLV_104, ///< 104 14735
SLV_105, ///< 105 14803
SLV_106, ///< 106 14919
SLV_107, ///< 107 15027
SLV_108, ///< 108 15045
SLV_109, ///< 109 15075
SLV_110, ///< 110 15148
SLV_111, ///< 111 15190
SLV_112, ///< 112 15290
SLV_113, ///< 113 15340
SLV_114, ///< 114 15601
SLV_115, ///< 115 15695
SLV_116, ///< 116 15893 0.7.x
SLV_117, ///< 117 16037
SLV_118, ///< 118 16129
SLV_119, ///< 119 16242
SLV_120, ///< 120 16439
SLV_121, ///< 121 16694
SLV_122, ///< 122 16855
SLV_123, ///< 123 16909
SLV_124, ///< 124 16993
SLV_125, ///< 125 17113
SLV_126, ///< 126 17433
SLV_127, ///< 127 17439
SLV_128, ///< 128 18281
SLV_129, ///< 129 18292
SLV_130, ///< 130 18404
SLV_131, ///< 131 18481
SLV_132, ///< 132 18522
SLV_133, ///< 133 18674
SLV_134, ///< 134 18703
SLV_135, ///< 135 18719
SLV_136, ///< 136 18764
SLV_137, ///< 137 18912
SLV_138, ///< 138 18942 1.0.x
SLV_139, ///< 139 19346
SLV_140, ///< 140 19382
SLV_141, ///< 141 19799
SLV_142, ///< 142 20003
SLV_143, ///< 143 20048
SLV_144, ///< 144 20334
SLV_145, ///< 145 20376
SLV_146, ///< 146 20446
SLV_147, ///< 147 20621
SLV_148, ///< 148 20659
SLV_149, ///< 149 20832
SLV_150, ///< 150 20857
SLV_151, ///< 151 20918
SLV_152, ///< 152 21171
SLV_153, ///< 153 21263
SLV_154, ///< 154 21426
SLV_155, ///< 155 21453
SLV_156, ///< 156 21728
SLV_157, ///< 157 21862
SLV_158, ///< 158 21933
SLV_159, ///< 159 21962
SLV_160, ///< 160 21974 1.1.x
SLV_161, ///< 161 22567
SLV_162, ///< 162 22713
SLV_163, ///< 163 22767
SLV_164, ///< 164 23290
SLV_165, ///< 165 23304
SLV_166, ///< 166 23415
SLV_167, ///< 167 23504
SLV_168, ///< 168 23637
SLV_169, ///< 169 23816
SLV_170, ///< 170 23826
SLV_171, ///< 171 23835
SLV_172, ///< 172 23947
SLV_173, ///< 173 23967 1.2.0-RC1
SLV_174, ///< 174 23973 1.2.x
SLV_175, ///< 175 24136
SLV_176, ///< 176 24446
SLV_177, ///< 177 24619
SLV_178, ///< 178 24789
SLV_179, ///< 179 24810
SLV_180, ///< 180 24998 1.3.x
SLV_181, ///< 181 25012
SLV_182, ///< 182 25115 FS#5492, r25259, r25296 Goal status
SLV_183, ///< 183 25363 Cargodist
SLV_184, ///< 184 25508 Unit localisation split
SLV_185, ///< 185 25620 Storybooks
SLV_186, ///< 186 25833 Objects storage
SLV_187, ///< 187 25899 Linkgraph - restricted flows
SLV_188, ///< 188 26169 v1.4 FS#5831 Unify RV travel time
SLV_189, ///< 189 26450 Hierarchical vehicle subgroups
SLV_190, ///< 190 26547 Separate order travel and wait times
SLV_191, ///< 191 26636 FS#6026 Fix disaster vehicle storage (No bump)
///< 191 26646 FS#6041 Linkgraph - store locations
SLV_192, ///< 192 26700 FS#6066 Fix saving of order backups
SLV_193, ///< 193 26802
SLV_194, ///< 194 26881 v1.5
SLV_195, ///< 195 27572 v1.6.1
SLV_196, ///< 196 27778 v1.7
SLV_197, ///< 197 27978 v1.8
SLV_198, ///< 198 PR#6763 Switch town growth rate and counter to actual game ticks
SLV_EXTEND_CARGOTYPES, ///< 199 PR#6802 Extend cargotypes to 64
SLV_EXTEND_RAILTYPES, ///< 200 PR#6805 Extend railtypes to 64, adding uint16 to map array.
SLV_EXTEND_PERSISTENT_STORAGE, ///< 201 PR#6885 Extend NewGRF persistent storages.
SLV_EXTEND_INDUSTRY_CARGO_SLOTS, ///< 202 PR#6867 Increase industry cargo slots to 16 in, 16 out
SLV_SHIP_PATH_CACHE, ///< 203 PR#7072 Add path cache for ships
SLV_SHIP_ROTATION, ///< 204 PR#7065 Add extra rotation stages for ships.
SLV_GROUP_LIVERIES, ///< 205 PR#7108 Livery storage change and group liveries.
SLV_SHIPS_STOP_IN_LOCKS, ///< 206 PR#7150 Ship/lock movement changes.
SLV_FIX_CARGO_MONITOR, ///< 207 PR#7175 v1.9 Cargo monitor data packing fix to support 64 cargotypes.
SLV_TOWN_CARGOGEN, ///< 208 PR#6965 New algorithms for town building cargo generation.
SLV_SHIP_CURVE_PENALTY, ///< 209 PR#7289 Configurable ship curve penalties.
SLV_SERVE_NEUTRAL_INDUSTRIES, ///< 210 PR#7234 Company stations can serve industries with attached neutral stations.
SLV_ROADVEH_PATH_CACHE, ///< 211 PR#7261 Add path cache for road vehicles.
SLV_REMOVE_OPF, ///< 212 PR#7245 Remove OPF.
SLV_TREES_WATER_CLASS, ///< 213 PR#7405 WaterClass update for tree tiles.
SLV_ROAD_TYPES, ///< 214 PR#6811 NewGRF road types.
SLV_SCRIPT_MEMLIMIT, ///< 215 PR#7516 Limit on AI/GS memory consumption.
SLV_MULTITILE_DOCKS, ///< 216 PR#7380 Multiple docks per station.
SLV_TRADING_AGE, ///< 217 PR#7780 Configurable company trading age.
SLV_ENDING_YEAR, ///< 218 PR#7747 v1.10 Configurable ending year.
SLV_REMOVE_TOWN_CARGO_CACHE, ///< 219 PR#8258 Remove town cargo acceptance and production caches.
/* Patchpacks for a while considered it a good idea to jump a few versions
* above our version for their savegames. But as time continued, this gap
* has been closing, up to the point we would start to reuse versions from
* their patchpacks. This is not a problem from our perspective: the
* savegame will simply fail to load because they all contain chunks we
* cannot digest. But, this gives for ugly errors. As we have plenty of
* versions anyway, we simply skip the versions we know belong to
* patchpacks. This way we can present the user with a clean error
* indicate they are loading a savegame from a patchpack.
* For future patchpack creators: please follow a system like JGRPP, where
* the version is masked with 0x8000, and the true version is stored in
* its own chunk with feature toggles.
*/
SLV_START_PATCHPACKS, ///< 220 First known patchpack to use a version just above ours.
SLV_END_PATCHPACKS = 286, ///< 286 Last known patchpack to use a version just above ours.
SLV_GS_INDUSTRY_CONTROL, ///< 287 PR#7912 and PR#8115 GS industry control.
SLV_VEH_MOTION_COUNTER, ///< 288 PR#8591 Desync safe motion counter
SLV_INDUSTRY_TEXT, ///< 289 PR#8576 v1.11.0-RC1 Additional GS text for industries.
SLV_MAPGEN_SETTINGS_REVAMP, ///< 290 PR#8891 v1.11 Revamp of some mapgen settings (snow coverage, desert coverage, heightmap height, custom terrain type).
SLV_GROUP_REPLACE_WAGON_REMOVAL, ///< 291 PR#7441 Per-group wagon removal flag.
SLV_CUSTOM_SUBSIDY_DURATION, ///< 292 PR#9081 Configurable subsidy duration.
/* upstream savegame versions follow */
SLV_SAVELOAD_LIST_LENGTH, ///< 293 PR#9374 Consistency in list length with SL_STRUCT / SL_STRUCTLIST / SL_DEQUE / SL_REFLIST.
SLV_RIFF_TO_ARRAY, ///< 294 PR#9375 Changed many CH_RIFF chunks to CH_ARRAY chunks.
SLV_TABLE_CHUNKS, ///< 295 PR#9322 Introduction of CH_TABLE and CH_SPARSE_TABLE.
SLV_SCRIPT_INT64, ///< 296 PR#9415 SQInteger is 64bit but was saved as 32bit.
SLV_LINKGRAPH_TRAVEL_TIME, ///< 297 PR#9457 v12.0-RC1 Store travel time in the linkgraph.
SLV_DOCK_DOCKINGTILES, ///< 298 PR#9578 All tiles around docks may be docking tiles.
SLV_REPAIR_OBJECT_DOCKING_TILES, ///< 299 PR#9594 v12.0 Fixing issue with docking tiles overlapping objects.
SLV_U64_TICK_COUNTER, ///< 300 PR#10035 Make _tick_counter 64bit to avoid wrapping.
SLV_LAST_LOADING_TICK, ///< 301 PR#9693 Store tick of last loading for vehicles.
SLV_MULTITRACK_LEVEL_CROSSINGS, ///< 302 PR#9931 v13.0 Multi-track level crossings.
SLV_NEWGRF_ROAD_STOPS, ///< 303 PR#10144 NewGRF road stops.
SLV_LINKGRAPH_EDGES, ///< 304 PR#10314 Explicitly store link graph edges destination.
SLV_VELOCITY_NAUTICAL, ///< 305 PR#10594 Separation of land and nautical velocity (knots!)
SLV_CONSISTENT_PARTIAL_Z, ///< 306 PR#10570 Conversion from an inconsistent partial Z calculation for slopes, to one that is (more) consistent.
SLV_MORE_CARGO_AGE, ///< 307 PR#10596 Track cargo age for a longer period.
SLV_LINKGRAPH_SECONDS, ///< 308 PR#10610 Store linkgraph update intervals in seconds instead of days.
SLV_AI_START_DATE, ///< 309 PR#10653 Removal of individual AI start dates and added a generic one.
SLV_EXTEND_VEHICLE_RANDOM, ///< 310 PR#10701 Extend vehicle random bits.
SLV_EXTEND_ENTITY_MAPPING, ///< 311 PR#10672 Extend entity mapping range.
SLV_DISASTER_VEH_STATE, ///< 312 PR#10798 Explicit storage of disaster vehicle state.
SLV_SAVEGAME_ID, ///< 313 PR#10719 Add an unique ID to every savegame (used to deduplicate surveys).
SLV_STRING_GAMELOG, ///< 314 PR#10801 Use std::string in gamelog.
SLV_INDUSTRY_CARGO_REORGANISE, ///< 315 PR#10853 Industry accepts/produced data reorganised.
SL_MAX_VERSION, ///< Highest possible saveload version
SL_SPRING_2013_v2_0_102 = 220,
SL_SPRING_2013_v2_1_108 = 221,
SL_SPRING_2013_v2_1_147 = 222,
SL_SPRING_2013_v2_3_XXX = 223,
SL_SPRING_2013_v2_3_b3 = 224,
SL_SPRING_2013_v2_3_b4 = 225,
SL_SPRING_2013_v2_3_b5 = 226,
SL_SPRING_2013_v2_4 = 227,
SL_TRACE_RESTRICT_2000 = 2000,
SL_TRACE_RESTRICT_2001 = 2001,
SL_TRACE_RESTRICT_2002 = 2002,
SL_JOKER_1_19 = 278,
SL_JOKER_1_20 = 279,
SL_JOKER_1_21 = 280,
SL_JOKER_1_22 = 281,
SL_JOKER_1_23 = 282,
SL_JOKER_1_24 = 283,
SL_JOKER_1_25 = 284,
SL_JOKER_1_26 = 285,
SL_JOKER_1_27 = 286,
SL_CHILLPP_201 = 201,
SL_CHILLPP_232 = 232,
SL_CHILLPP_233 = 233,
};
byte SlReadByte();
void SlWriteByte(byte b);
int SlReadUint16();
uint32 SlReadUint32();
uint64 SlReadUint64();
void SlWriteUint16(uint16 v);
void SlWriteUint32(uint32 v);
void SlWriteUint64(uint64 v);
void SlSkipBytes(size_t length);
size_t SlGetBytesRead();
size_t SlGetBytesWritten();
void NORETURN SlError(StringID string, std::string extra_msg = {});
void NORETURN SlErrorCorrupt(std::string msg);
void NORETURN CDECL SlErrorCorruptFmt(const char *format, ...) WARN_FORMAT(1, 2);
bool SaveLoadFileTypeIsScenario();
#endif /* SL_SAVELOAD_COMMON_H */

105
src/sl/saveload_filter.h Normal file
View File

@@ -0,0 +1,105 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file saveload_filter.h Declaration of filters used for saving and loading savegames. */
#ifndef SL_SAVELOAD_FILTER_H
#define SL_SAVELOAD_FILTER_H
/** Interface for filtering a savegame till it is loaded. */
struct LoadFilter {
/** Chained to the (savegame) filters. */
LoadFilter *chain;
/**
* Initialise this filter.
* @param chain The next filter in this chain.
*/
LoadFilter(LoadFilter *chain) : chain(chain)
{
}
/** Make sure the writers are properly closed. */
virtual ~LoadFilter()
{
delete this->chain;
}
/**
* Read a given number of bytes from the savegame.
* @param buf The bytes to read.
* @param len The number of bytes to read.
* @return The number of actually read bytes.
*/
virtual size_t Read(byte *buf, size_t len) = 0;
/**
* Reset this filter to read from the beginning of the file.
*/
virtual void Reset()
{
this->chain->Reset();
}
};
/**
* Instantiator for a load filter.
* @param chain The next filter in this chain.
* @tparam T The type of load filter to create.
*/
template <typename T> LoadFilter *CreateLoadFilter(LoadFilter *chain)
{
return new T(chain);
}
/** Interface for filtering a savegame till it is written. */
struct SaveFilter {
/** Chained to the (savegame) filters. */
SaveFilter *chain;
/**
* Initialise this filter.
* @param chain The next filter in this chain.
*/
SaveFilter(SaveFilter *chain) : chain(chain)
{
}
/** Make sure the writers are properly closed. */
virtual ~SaveFilter()
{
delete this->chain;
}
/**
* Write a given number of bytes into the savegame.
* @param buf The bytes to write.
* @param len The number of bytes to write.
*/
virtual void Write(byte *buf, size_t len) = 0;
/**
* Prepare everything to finish writing the savegame.
*/
virtual void Finish()
{
if (this->chain != nullptr) this->chain->Finish();
}
};
/**
* Instantiator for a save filter.
* @param chain The next filter in this chain.
* @param compression_level The requested level of compression.
* @tparam T The type of save filter to create.
*/
template <typename T> SaveFilter *CreateSaveFilter(SaveFilter *chain, byte compression_level)
{
return new T(chain, compression_level);
}
#endif /* SL_SAVELOAD_FILTER_H */

View File

@@ -0,0 +1,70 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file saveload_internal.h Declaration of functions used in more save/load files */
#ifndef SL_SAVELOAD_INTERNAL_H
#define SL_SAVELOAD_INTERNAL_H
#include "../company_manager_face.h"
#include "../order_base.h"
#include "../engine_type.h"
#include "saveload.h"
void InitializeOldNames();
StringID RemapOldStringID(StringID s);
std::string CopyFromOldName(StringID id);
void ResetOldNames();
void ResetOldWaypoints();
void MoveBuoysToWaypoints();
void MoveWaypointsToBaseStations();
SaveLoadTable GetBaseStationDescription();
void AfterLoadVehicles(bool part_of_load);
void AfterLoadVehiclesRemoveAnyFoundInvalid();
void AfterLoadEngines();
void FixupTrainLengths();
void AfterLoadTemplateVehicles();
void AfterLoadStations();
void AfterLoadRoadStops();
void ResetLabelMaps();
void AfterLoadLabelMaps();
void AfterLoadStoryBook();
void AfterLoadLinkGraphs();
void AfterLoadCompanyStats();
void AfterLoadTraceRestrict();
void UpdateHousesAndTowns(bool cargo_update_required, bool old_map_position);
void UpdateOldAircraft();
void SaveViewportBeforeSaveGame();
void ResetViewportAfterLoadGame();
void ConvertOldMultiheadToNew();
void ConnectMultiheadedTrains();
void ResetTempEngineData();
Engine *GetTempDataEngine(EngineID index);
void CopyTempEngineData();
void AfterLoadTemplateVehiclesUpdate();
void AfterLoadTemplateVehiclesUpdateImages();
void AfterLoadTemplateVehiclesUpdateProperties();
extern int32 _saved_scrollpos_x;
extern int32 _saved_scrollpos_y;
extern ZoomLevel _saved_scrollpos_zoom;
extern SavegameType _savegame_type;
extern uint32 _ttdp_version;
CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32 face);
Order UnpackOldOrder(uint16 packed);
#endif /* SL_SAVELOAD_INTERNAL_H */

135
src/sl/saveload_types.h Normal file
View File

@@ -0,0 +1,135 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file saveload_common.h Common functions/types for saving and loading games. */
#ifndef SL_SAVELOAD_TYPES_H
#define SL_SAVELOAD_TYPES_H
#include "saveload_common.h"
#include "extended_ver_sl.h"
/**
* VarTypes is the general bitmasked magic type that tells us
* certain characteristics about the variable it refers to. For example
* SLE_FILE_* gives the size(type) as it would be in the savegame and
* SLE_VAR_* the size(type) as it is in memory during runtime. These are
* the first 8 bits (0-3 SLE_FILE, 4-7 SLE_VAR).
* Bits 8-15 are reserved for various flags as explained below
*/
enum VarTypes {
/* 4 bits allocated a maximum of 16 types for NumberType */
SLE_FILE_I8 = 0,
SLE_FILE_U8 = 1,
SLE_FILE_I16 = 2,
SLE_FILE_U16 = 3,
SLE_FILE_I32 = 4,
SLE_FILE_U32 = 5,
SLE_FILE_I64 = 6,
SLE_FILE_U64 = 7,
SLE_FILE_STRINGID = 8, ///< StringID offset into strings-array
SLE_FILE_STRING = 9,
SLE_FILE_VEHORDERID = 10,
/* 5 more possible file-primitives */
/* 4 bits allocated a maximum of 16 types for NumberType */
SLE_VAR_BL = 0 << 4,
SLE_VAR_I8 = 1 << 4,
SLE_VAR_U8 = 2 << 4,
SLE_VAR_I16 = 3 << 4,
SLE_VAR_U16 = 4 << 4,
SLE_VAR_I32 = 5 << 4,
SLE_VAR_U32 = 6 << 4,
SLE_VAR_I64 = 7 << 4,
SLE_VAR_U64 = 8 << 4,
SLE_VAR_NULL = 9 << 4, ///< useful to write zeros in savegame.
SLE_VAR_STRB = 10 << 4, ///< string (with pre-allocated buffer)
SLE_VAR_STR = 12 << 4, ///< string pointer
SLE_VAR_STRQ = 13 << 4, ///< string pointer enclosed in quotes
SLE_VAR_NAME = 14 << 4, ///< old custom name to be converted to a std::string
SLE_VAR_CNAME = 15 << 4, ///< old custom name to be converted to a char pointer
/* 0 more possible memory-primitives */
/* Shortcut values */
SLE_VAR_CHAR = SLE_VAR_I8,
/* Default combinations of variables. As savegames change, so can variables
* and thus it is possible that the saved value and internal size do not
* match and you need to specify custom combo. The defaults are listed here */
SLE_BOOL = SLE_FILE_I8 | SLE_VAR_BL,
SLE_INT8 = SLE_FILE_I8 | SLE_VAR_I8,
SLE_UINT8 = SLE_FILE_U8 | SLE_VAR_U8,
SLE_INT16 = SLE_FILE_I16 | SLE_VAR_I16,
SLE_UINT16 = SLE_FILE_U16 | SLE_VAR_U16,
SLE_INT32 = SLE_FILE_I32 | SLE_VAR_I32,
SLE_UINT32 = SLE_FILE_U32 | SLE_VAR_U32,
SLE_INT64 = SLE_FILE_I64 | SLE_VAR_I64,
SLE_UINT64 = SLE_FILE_U64 | SLE_VAR_U64,
SLE_CHAR = SLE_FILE_I8 | SLE_VAR_CHAR,
SLE_STRINGID = SLE_FILE_STRINGID | SLE_VAR_U32,
SLE_STRINGBUF = SLE_FILE_STRING | SLE_VAR_STRB,
SLE_STRING = SLE_FILE_STRING | SLE_VAR_STR,
SLE_STRINGQUOTE = SLE_FILE_STRING | SLE_VAR_STRQ,
SLE_NAME = SLE_FILE_STRINGID | SLE_VAR_NAME,
SLE_CNAME = SLE_FILE_STRINGID | SLE_VAR_CNAME,
SLE_VEHORDERID = SLE_FILE_VEHORDERID | SLE_VAR_U16,
/* Shortcut values */
SLE_UINT = SLE_UINT32,
SLE_INT = SLE_INT32,
SLE_STRB = SLE_STRINGBUF,
SLE_STR = SLE_STRING,
SLE_STRQ = SLE_STRINGQUOTE,
/* 8 bits allocated for a maximum of 8 flags
* Flags directing saving/loading of a variable */
SLF_ALLOW_CONTROL = 1 << 8, ///< Allow control codes in the strings.
SLF_ALLOW_NEWLINE = 1 << 9, ///< Allow new lines in the strings.
};
typedef uint32 VarType;
/** Type of data saved. */
enum SaveLoadTypes {
SL_VAR = 0, ///< Save/load a variable.
SL_REF = 1, ///< Save/load a reference.
SL_ARR = 2, ///< Save/load a fixed-size array of #SL_VAR elements.
SL_STR = 3, ///< Save/load a string.
SL_REFLIST = 4, ///< Save/load a list of #SL_REF elements.
SL_DEQUE = 5, ///< Save/load a deque of #SL_VAR elements.
SL_VEC = 6, ///< Save/load a vector of #SL_REF elements.
SL_STDSTR = 7, ///< Save/load a std::string.
/* non-normal save-load types */
SL_WRITEBYTE = 8,
SL_VEH_INCLUDE = 9,
SL_ST_INCLUDE = 10,
SL_PTRDEQ = 13, ///< Save/load a deque of #SL_REF elements.
SL_VARVEC = 14, ///< Save/load a primitive type vector.
};
typedef byte SaveLoadType; ///< Save/load type. @see SaveLoadTypes
/** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */
struct SaveLoad {
bool global; ///< should we load a global variable or a non-global one
SaveLoadType cmd; ///< the action to take with the saved/loaded type, All types need different action
VarType conv; ///< type of the variable to be saved, int
uint16 length; ///< (conditional) length of the variable (eg. arrays) (max array size is 65536 elements)
SaveLoadVersion version_from; ///< save/load the variable starting from this savegame version
SaveLoadVersion version_to; ///< save/load the variable until this savegame version
/* NOTE: This element either denotes the address of the variable for a global
* variable, or the offset within a struct which is then bound to a variable
* during runtime. Decision on which one to use is controlled by the function
* that is called to save it. address: global=true, offset: global=false */
void *address; ///< address of variable OR offset of variable in the struct (max offset is 65536)
size_t size; ///< the sizeof size.
SlXvFeatureTest ext_feature_test; ///< extended feature test
};
#endif /* SL_SAVELOAD_TYPES_H */

314
src/sl/signal_sl.cpp Normal file
View File

@@ -0,0 +1,314 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file signal_sl.cpp Code handling saving and loading of signals */
#include "../stdafx.h"
#include "../programmable_signals.h"
#include "../core/alloc_type.hpp"
#include "../core/bitmath_func.hpp"
#include <vector>
#include "saveload.h"
#include "saveload_buffer.h"
typedef std::vector<byte> Buffer;
// Variable length integers are stored in Variable Length Quantity
// format (http://en.wikipedia.org/wiki/Variable-length_quantity)
static void WriteVLI(Buffer &b, uint i)
{
uint lsmask = 0x7F;
uint msmask = ~0x7F;
while(i & msmask) {
byte part = (i & lsmask) | 0x80;
b.push_back(part);
i >>= 7;
}
b.push_back((byte) i);
}
static uint ReadVLI()
{
uint shift = 0;
uint val = 0;
byte b;
b = SlReadByte();
while(b & 0x80) {
val |= uint(b & 0x7F) << shift;
shift += 7;
b = SlReadByte();
}
val |= uint(b) << shift;
return val;
}
static void WriteCondition(Buffer &b, SignalCondition *c)
{
WriteVLI(b, c->ConditionCode());
switch(c->ConditionCode()) {
case PSC_NUM_GREEN:
case PSC_NUM_RED: {
SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(c);
WriteVLI(b, vc->comparator);
WriteVLI(b, vc->value);
} break;
case PSC_SIGNAL_STATE: {
SignalStateCondition *sc = static_cast<SignalStateCondition*>(c);
WriteVLI(b, sc->sig_tile);
WriteVLI(b, sc->sig_track);
} break;
case PSC_SLOT_OCC:
case PSC_SLOT_OCC_REM: {
SignalSlotCondition *cc = static_cast<SignalSlotCondition*>(c);
WriteVLI(b, cc->slot_id);
WriteVLI(b, cc->comparator);
WriteVLI(b, cc->value);
} break;
case PSC_COUNTER: {
SignalCounterCondition *cc = static_cast<SignalCounterCondition*>(c);
WriteVLI(b, cc->ctr_id);
WriteVLI(b, cc->comparator);
WriteVLI(b, cc->value);
} break;
default:
break;
}
}
static SignalCondition *ReadCondition(SignalReference this_sig)
{
SignalConditionCode code = (SignalConditionCode) ReadVLI();
switch(code) {
case PSC_NUM_GREEN:
case PSC_NUM_RED: {
SignalVariableCondition *c = new SignalVariableCondition(code);
c->comparator = (SignalComparator) ReadVLI();
if(c->comparator > SGC_LAST) NOT_REACHED();
c->value = ReadVLI();
return c;
}
case PSC_SIGNAL_STATE: {
TileIndex ti = (TileIndex) ReadVLI();
Trackdir td = (Trackdir) ReadVLI();
return new SignalStateCondition(this_sig, ti, td);
}
case PSC_SLOT_OCC:
case PSC_SLOT_OCC_REM: {
TraceRestrictSlotID slot_id = (TraceRestrictSlotID) ReadVLI();
SignalSlotCondition *c = new SignalSlotCondition(code, this_sig, slot_id);
c->comparator = (SignalComparator) ReadVLI();
if(c->comparator > SGC_LAST) NOT_REACHED();
c->value = ReadVLI();
return c;
} break;
case PSC_COUNTER: {
TraceRestrictCounterID ctr_id = (TraceRestrictCounterID) ReadVLI();
SignalCounterCondition *c = new SignalCounterCondition(this_sig, ctr_id);
c->comparator = (SignalComparator) ReadVLI();
if(c->comparator > SGC_LAST) NOT_REACHED();
c->value = ReadVLI();
return c;
}
default:
return new SignalSimpleCondition(code);
}
}
static void Save_SPRG()
{
// Check for, and dispose of, any signal information on a tile which doesn't have signals.
// This indicates that someone removed the signals from the tile but didn't clean them up.
// (This code is to detect bugs and limit their consquences, not to cover them up!)
for(ProgramList::iterator i = _signal_programs.begin(), e = _signal_programs.end();
i != e; ++i) {
SignalReference ref = i->first;
if(!HasProgrammableSignals(ref)) {
DEBUG(sl, 0, "Programmable pre-signal information for (%x, %d) has been leaked!",
ref.tile, ref.track);
++i;
FreeSignalProgram(ref);
if(i == e) break;
}
}
// OK, we can now write out our programs
Buffer b;
WriteVLI(b, (uint)_signal_programs.size());
for(ProgramList::iterator i = _signal_programs.begin(), e = _signal_programs.end();
i != e; ++i) {
SignalProgram *prog = i->second;
WriteVLI(b, prog->tile);
WriteVLI(b, prog->track);
WriteVLI(b, (uint)prog->instructions.size());
for (SignalInstruction *insn : prog->instructions) {
WriteVLI(b, insn->Opcode());
if(insn->Opcode() != PSO_FIRST)
WriteVLI(b, insn->Previous()->Id());
switch(insn->Opcode()) {
case PSO_FIRST: {
SignalSpecial *s = static_cast<SignalSpecial*>(insn);
WriteVLI(b, s->next->Id());
break;
}
case PSO_LAST: break;
case PSO_IF: {
SignalIf *i = static_cast<SignalIf*>(insn);
WriteCondition(b, i->condition);
WriteVLI(b, i->if_true->Id());
WriteVLI(b, i->if_false->Id());
WriteVLI(b, i->after->Id());
break;
}
case PSO_IF_ELSE:
case PSO_IF_ENDIF: {
SignalIf::PseudoInstruction *p = static_cast<SignalIf::PseudoInstruction*>(insn);
WriteVLI(b, p->block->Id());
break;
}
case PSO_SET_SIGNAL: {
SignalSet *s = static_cast<SignalSet*>(insn);
WriteVLI(b, s->next->Id());
WriteVLI(b, s->to_state ? 1 : 0);
break;
}
default: NOT_REACHED();
}
}
}
uint size = (uint)b.size();
SlSetLength(size);
MemoryDumper::GetCurrent()->CopyBytes(b.data(), size);
}
// We don't know the pointer values that need to be stored in various
// instruction fields at load time, so we need to instead store the IDs and
// then fix them up once all of the instructions have been loaded.
//
// Additionally, we store the opcode type we expect (if we expect a specific one)
// to check for consistency (For example, an If Pseudo Instruction's block should
// point at an If!)
struct Fixup {
Fixup(SignalInstruction **p, SignalOpcode type)
: type(type), ptr(p)
{}
SignalOpcode type;
SignalInstruction **ptr;
};
typedef std::vector<Fixup> FixupList;
template<typename T>
static void MakeFixup(FixupList &l, T *&ir, uint id, SignalOpcode op = PSO_INVALID)
{
ir = reinterpret_cast<T*>((size_t)id);
l.emplace_back(reinterpret_cast<SignalInstruction**>(&ir), op);
}
static void DoFixups(FixupList &l, InstructionList &il)
{
for (Fixup &i : l) {
uint id = (uint)reinterpret_cast<size_t>(*(i.ptr));
if (id >= il.size())
NOT_REACHED();
*(i.ptr) = il[id];
if (i.type != PSO_INVALID && (*(i.ptr))->Opcode() != i.type) {
DEBUG(sl, 0, "Expected Id %d to be %d, but was in fact %d", id, i.type, (*(i.ptr))->Opcode());
NOT_REACHED();
}
}
}
static void Load_SPRG()
{
uint count = ReadVLI();
for(uint i = 0; i < count; i++) {
FixupList l;
TileIndex tile = ReadVLI();
Track track = (Track) ReadVLI();
uint instructions = ReadVLI();
SignalReference ref(tile, track);
SignalProgram *sp = new SignalProgram(tile, track, true);
_signal_programs[ref] = sp;
for(uint j = 0; j < instructions; j++) {
SignalOpcode op = (SignalOpcode) ReadVLI();
switch(op) {
case PSO_FIRST: {
sp->first_instruction = new SignalSpecial(sp, PSO_FIRST);
sp->first_instruction->GetPrevHandle() = nullptr;
MakeFixup(l, sp->first_instruction->next, ReadVLI());
break;
}
case PSO_LAST: {
sp->last_instruction = new SignalSpecial(sp, PSO_LAST);
sp->last_instruction->next = nullptr;
MakeFixup(l, sp->last_instruction->GetPrevHandle(), ReadVLI());
break;
}
case PSO_IF: {
SignalIf *i = new SignalIf(sp, true);
MakeFixup(l, i->GetPrevHandle(), ReadVLI());
i->condition = ReadCondition(ref);
MakeFixup(l, i->if_true, ReadVLI());
MakeFixup(l, i->if_false, ReadVLI());
MakeFixup(l, i->after, ReadVLI());
break;
}
case PSO_IF_ELSE:
case PSO_IF_ENDIF: {
SignalIf::PseudoInstruction *p = new SignalIf::PseudoInstruction(sp, op);
MakeFixup(l, p->GetPrevHandle(), ReadVLI());
MakeFixup(l, p->block, ReadVLI(), PSO_IF);
break;
}
case PSO_SET_SIGNAL: {
SignalSet *s = new SignalSet(sp);
MakeFixup(l, s->GetPrevHandle(), ReadVLI());
MakeFixup(l, s->next, ReadVLI());
s->to_state = (SignalState) ReadVLI();
if(s->to_state > SIGNAL_STATE_MAX) NOT_REACHED();
break;
}
default: NOT_REACHED();
}
}
DoFixups(l, sp->instructions);
}
}
extern const ChunkHandler signal_chunk_handlers[] = {
{ 'SPRG', Save_SPRG, Load_SPRG, nullptr, nullptr, CH_RIFF },
};
extern const ChunkHandlerTable _signal_chunk_handlers(signal_chunk_handlers);

69
src/sl/signs_sl.cpp Normal file
View File

@@ -0,0 +1,69 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file signs_sl.cpp Code handling saving and loading of economy data */
#include "../stdafx.h"
#include "../signs_base.h"
#include "../fios.h"
#include "saveload.h"
#include "../safeguards.h"
/** Description of a sign within the savegame. */
static const SaveLoad _sign_desc[] = {
SLE_CONDVAR(Sign, name, SLE_NAME, SL_MIN_VERSION, SLV_84),
SLE_CONDSSTR(Sign, name, SLE_STR | SLF_ALLOW_CONTROL, SLV_84, SL_MAX_VERSION),
SLE_CONDVAR(Sign, x, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5),
SLE_CONDVAR(Sign, y, SLE_FILE_I16 | SLE_VAR_I32, SL_MIN_VERSION, SLV_5),
SLE_CONDVAR(Sign, x, SLE_INT32, SLV_5, SL_MAX_VERSION),
SLE_CONDVAR(Sign, y, SLE_INT32, SLV_5, SL_MAX_VERSION),
SLE_CONDVAR(Sign, owner, SLE_UINT8, SLV_6, SL_MAX_VERSION),
SLE_CONDVAR_X(Sign, z, SLE_FILE_U8 | SLE_VAR_I32, SL_MIN_VERSION, SLV_164, SlXvFeatureTest(XSLFTO_AND, XSLFI_ZPOS_32_BIT, 0, 0)),
SLE_CONDVAR_X(Sign, z, SLE_INT32, SLV_164, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_ZPOS_32_BIT)),
};
/** Save all signs */
static void Save_SIGN()
{
for (Sign *si : Sign::Iterate()) {
SlSetArrayIndex(si->index);
SlObject(si, _sign_desc);
}
}
/** Load all signs */
static void Load_SIGN()
{
int index;
while ((index = SlIterateArray()) != -1) {
Sign *si = new (index) Sign();
SlObject(si, _sign_desc);
/* Before version 6.1, signs didn't have owner.
* Before version 83, invalid signs were determined by si->str == 0.
* Before version 103, owner could be a bankrupted company.
* - we can't use IsValidCompany() now, so this is fixed in AfterLoadGame()
* All signs that were saved are valid (including those with just 'Sign' and INVALID_OWNER).
* - so set owner to OWNER_NONE if needed (signs from pre-version 6.1 would be lost) */
if (IsSavegameVersionBefore(SLV_6, 1) || (IsSavegameVersionBefore(SLV_83) && si->owner == INVALID_OWNER)) {
si->owner = OWNER_NONE;
}
/* Signs placed in scenario editor shall now be OWNER_DEITY */
if (IsSavegameVersionBefore(SLV_171) && si->owner == OWNER_NONE && _file_to_saveload.abstract_ftype == FT_SCENARIO) {
si->owner = OWNER_DEITY;
}
}
}
/** Chunk handlers related to signs. */
static const ChunkHandler sign_chunk_handlers[] = {
{ 'SIGN', Save_SIGN, Load_SIGN, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _sign_chunk_handlers(sign_chunk_handlers);

836
src/sl/station_sl.cpp Normal file
View File

@@ -0,0 +1,836 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file station_sl.cpp Code handling saving and loading of stations. */
#include "../stdafx.h"
#include "../station_base.h"
#include "../waypoint_base.h"
#include "../roadstop_base.h"
#include "../vehicle_base.h"
#include "../newgrf_station.h"
#include "../newgrf_roadstop.h"
#include "../core/math_func.hpp"
#include "saveload.h"
#include "saveload_buffer.h"
#include "table/strings.h"
#include "../safeguards.h"
static byte _old_last_vehicle_type;
static uint8 _num_specs;
static uint8 _num_roadstop_specs;
static uint32 _num_roadstop_custom_tiles;
static std::vector<TileIndex> _custom_road_stop_tiles;
static std::vector<uint16> _custom_road_stop_data;
/**
* Update the buoy orders to be waypoint orders.
* @param o the order 'list' to check.
*/
static void UpdateWaypointOrder(Order *o)
{
if (!o->IsType(OT_GOTO_STATION)) return;
const Station *st = Station::Get(o->GetDestination());
if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) return;
o->MakeGoToWaypoint(o->GetDestination());
}
/**
* Perform all steps to upgrade from the old station buoys to the new version
* that uses waypoints. This includes some old saveload mechanics.
*/
void MoveBuoysToWaypoints()
{
/* Buoy orders become waypoint orders */
for (OrderList *ol : OrderList::Iterate()) {
VehicleType vt = ol->GetFirstSharedVehicle()->type;
if (vt != VEH_SHIP && vt != VEH_TRAIN) continue;
for (Order *o = ol->GetFirstOrder(); o != nullptr; o = o->next) UpdateWaypointOrder(o);
}
for (Vehicle *v : Vehicle::Iterate()) {
VehicleType vt = v->type;
if (vt != VEH_SHIP && vt != VEH_TRAIN) continue;
UpdateWaypointOrder(&v->current_order);
}
/* Now make the stations waypoints */
for (Station *st : Station::Iterate()) {
if ((st->had_vehicle_of_type & HVOT_WAYPOINT) == 0) continue;
StationID index = st->index;
TileIndex xy = st->xy;
Town *town = st->town;
StringID string_id = st->string_id;
TinyString name = std::move(st->name);
Date build_date = st->build_date;
/* TTDPatch could use "buoys with rail station" for rail waypoints */
bool train = st->train_station.tile != INVALID_TILE;
TileArea train_st = st->train_station;
/* Delete the station, so we can make it a real waypoint. */
delete st;
/* Stations and waypoints are in the same pool, so if a station
* is deleted there must be place for a Waypoint. */
assert(Waypoint::CanAllocateItem());
Waypoint *wp = new (index) Waypoint(xy);
wp->town = town;
wp->string_id = train ? STR_SV_STNAME_WAYPOINT : STR_SV_STNAME_BUOY;
wp->name = std::move(name);
wp->delete_ctr = 0; // Just reset delete counter for once.
wp->build_date = build_date;
wp->owner = train ? GetTileOwner(xy) : OWNER_NONE;
if (IsInsideBS(string_id, STR_SV_STNAME_BUOY, 9)) wp->town_cn = string_id - STR_SV_STNAME_BUOY;
if (train) {
/* When we make a rail waypoint of the station, convert the map as well. */
for (TileIndex t : train_st) {
if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != index) continue;
SB(_me[t].m6, 3, 3, STATION_WAYPOINT);
wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE);
}
wp->train_station = train_st;
wp->facilities |= FACIL_TRAIN;
} else if (IsBuoyTile(xy) && GetStationIndex(xy) == index) {
wp->rect.BeforeAddTile(xy, StationRect::ADD_FORCE);
wp->facilities |= FACIL_DOCK;
}
}
}
void AfterLoadStations()
{
/* Update the speclists of all stations to point to the currently loaded custom stations. */
for (BaseStation *st : BaseStation::Iterate()) {
for (uint i = 0; i < st->speclist.size(); i++) {
if (st->speclist[i].grfid == 0) continue;
st->speclist[i].spec = StationClass::GetByGrf(st->speclist[i].grfid, st->speclist[i].localidx, nullptr);
}
for (uint i = 0; i < st->roadstop_speclist.size(); i++) {
if (st->roadstop_speclist[i].grfid == 0) continue;
st->roadstop_speclist[i].spec = RoadStopClass::GetByGrf(st->roadstop_speclist[i].grfid, st->roadstop_speclist[i].localidx, nullptr);
}
if (Station::IsExpected(st)) {
Station *sta = Station::From(st);
for (const RoadStop *rs = sta->bus_stops; rs != nullptr; rs = rs->next) sta->bus_station.Add(rs->xy);
for (const RoadStop *rs = sta->truck_stops; rs != nullptr; rs = rs->next) sta->truck_station.Add(rs->xy);
}
StationUpdateCachedTriggers(st);
StationUpdateRoadStopCachedTriggers(st);
}
}
/**
* (Re)building of road stop caches after loading a savegame.
*/
void AfterLoadRoadStops()
{
/* First construct the drive through entries */
for (RoadStop *rs : RoadStop::Iterate()) {
if (IsDriveThroughStopTile(rs->xy)) rs->MakeDriveThrough();
}
/* And then rebuild the data in those entries */
for (RoadStop *rs : RoadStop::Iterate()) {
if (!HasBit(rs->status, RoadStop::RSSFB_BASE_ENTRY)) continue;
rs->GetEntry(DIAGDIR_NE)->Rebuild(rs);
rs->GetEntry(DIAGDIR_NW)->Rebuild(rs);
}
}
static const SaveLoad _roadstop_desc[] = {
SLE_VAR(RoadStop, xy, SLE_UINT32),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_45),
SLE_VAR(RoadStop, status, SLE_UINT8),
/* Index was saved in some versions, but this is not needed */
SLE_CONDNULL(4, SL_MIN_VERSION, SLV_9),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_45),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_26),
SLE_REF(RoadStop, next, REF_ROADSTOPS),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_45),
SLE_CONDNULL(4, SL_MIN_VERSION, SLV_25),
SLE_CONDNULL(1, SLV_25, SLV_26),
};
static const SaveLoad _old_station_desc[] = {
SLE_CONDVAR(Station, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Station, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDNULL(4, SL_MIN_VERSION, SLV_6), ///< bus/lorry tile
SLE_CONDVAR(Station, train_station.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Station, train_station.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDVAR(Station, airport.tile, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Station, airport.tile, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6),
SLE_CONDNULL(4, SLV_6, SLV_MULTITILE_DOCKS),
SLE_REF(Station, town, REF_TOWN),
SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16),
SLE_CONDVAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_2, SL_MAX_VERSION),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_4), ///< alpha_order
SLE_VAR(Station, string_id, SLE_STRINGID),
SLE_CONDSTR(Station, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION),
SLE_CONDVAR(Station, indtype, SLE_UINT8, SLV_103, SL_MAX_VERSION),
SLE_CONDVAR(Station, had_vehicle_of_type, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SLV_122),
SLE_CONDVAR(Station, had_vehicle_of_type, SLE_UINT8, SLV_122, SL_MAX_VERSION),
SLE_VAR(Station, time_since_load, SLE_UINT8),
SLE_VAR(Station, time_since_unload, SLE_UINT8),
SLE_CONDVAR_X(Station, delete_ctr, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 0, 3)),
SLE_CONDVAR_X(Station, delete_ctr, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)),
SLE_VAR(Station, owner, SLE_UINT8),
SLE_VAR(Station, facilities, SLE_UINT8),
SLE_VAR(Station, airport.type, SLE_UINT8),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_6), ///< Truck/bus stop status
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_5), ///< Blocked months
SLE_CONDVAR(Station, airport.flags, SLE_VAR_U64 | SLE_FILE_U16, SL_MIN_VERSION, SLV_3),
SLE_CONDVAR(Station, airport.flags, SLE_VAR_U64 | SLE_FILE_U32, SLV_3, SLV_46),
SLE_CONDVAR(Station, airport.flags, SLE_UINT64, SLV_46, SL_MAX_VERSION),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_26), ///< last-vehicle
SLEG_CONDVAR_X(_old_last_vehicle_type, SLE_UINT8, SLV_26, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 0, 0)),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
SLE_CONDNULL(2, SLV_3, SLV_26), ///< custom station class and id
SLE_CONDVAR(Station, build_date, SLE_FILE_U16 | SLE_VAR_I32, SLV_3, SLV_31),
SLE_CONDVAR(Station, build_date, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLE_CONDREF(Station, bus_stops, REF_ROADSTOPS, SLV_6, SL_MAX_VERSION),
SLE_CONDREF(Station, truck_stops, REF_ROADSTOPS, SLV_6, SL_MAX_VERSION),
/* Used by newstations for graphic variations */
SLE_CONDVAR(Station, random_bits, SLE_UINT16, SLV_27, SL_MAX_VERSION),
SLE_CONDVAR(Station, waiting_triggers, SLE_UINT8, SLV_27, SL_MAX_VERSION),
SLEG_CONDVAR(_num_specs, SLE_UINT8, SLV_27, SL_MAX_VERSION),
SLE_CONDVEC(Station, loading_vehicles, REF_VEHICLE, SLV_57, SL_MAX_VERSION),
/* reserve extra space in savegame here. (currently 32 bytes) */
SLE_CONDNULL(32, SLV_2, SL_MAX_VERSION),
};
static uint16 _waiting_acceptance;
static uint32 _num_flows;
static uint16 _cargo_source;
static uint32 _cargo_source_xy;
static uint8 _cargo_days;
static Money _cargo_feeder_share;
static const SaveLoad _station_speclist_desc[] = {
SLE_CONDVAR(StationSpecList, grfid, SLE_UINT32, SLV_27, SL_MAX_VERSION),
SLE_CONDVAR_X(StationSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 0, 1)),
SLE_CONDVAR_X(StationSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEWGRF_ENTITY_EXTRA, 2)),
};
static const SaveLoad _roadstop_speclist_desc[] = {
SLE_CONDVAR(RoadStopSpecList, grfid, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION),
SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 0, 2)),
SLE_CONDVAR_X(RoadStopSpecList, localidx, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 3)),
};
CargoPacketList _packets;
uint32 _num_dests;
struct FlowSaveLoad {
FlowSaveLoad() : source(0), via(0), share(0), restricted(false) {}
StationID source;
StationID via;
uint32 share;
bool restricted;
};
#if 0
static const SaveLoad _flow_desc[] = {
SLE_VAR(FlowSaveLoad, source, SLE_UINT16),
SLE_VAR(FlowSaveLoad, via, SLE_UINT16),
SLE_VAR(FlowSaveLoad, share, SLE_UINT32),
SLE_CONDVAR(FlowSaveLoad, restricted, SLE_BOOL, SLV_187, SL_MAX_VERSION),
};
#endif
/**
* Wrapper function to get the GoodsEntry's internal structure while
* some of the variables itself are private.
* @return the saveload description for GoodsEntry.
*/
SaveLoadTable GetGoodsDesc()
{
static const SaveLoad goods_desc[] = {
SLEG_CONDVAR( _waiting_acceptance, SLE_UINT16, SL_MIN_VERSION, SLV_68),
SLE_CONDVAR(GoodsEntry, status, SLE_UINT8, SLV_68, SL_MAX_VERSION),
SLE_CONDNULL(2, SLV_51, SLV_68),
SLE_VAR(GoodsEntry, time_since_pickup, SLE_UINT8),
SLE_CONDNULL_X(6, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)),
SLE_VAR(GoodsEntry, rating, SLE_UINT8),
SLEG_CONDVAR( _cargo_source, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_7),
SLEG_CONDVAR( _cargo_source, SLE_UINT16, SLV_7, SLV_68),
SLEG_CONDVAR( _cargo_source_xy, SLE_UINT32, SLV_44, SLV_68),
SLEG_CONDVAR( _cargo_days, SLE_UINT8, SL_MIN_VERSION, SLV_68),
SLE_VAR(GoodsEntry, last_speed, SLE_UINT8),
SLE_VAR(GoodsEntry, last_age, SLE_UINT8),
SLEG_CONDVAR( _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, SLV_14, SLV_65),
SLEG_CONDVAR( _cargo_feeder_share, SLE_INT64, SLV_65, SLV_68),
SLE_CONDVAR(GoodsEntry, amount_fract, SLE_UINT8, SLV_150, SL_MAX_VERSION),
SLEG_CONDPTRDEQ_X( _packets, REF_CARGO_PACKET, SLV_68, SLV_183, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, 0, 0)),
SLEG_CONDVAR_X( _num_dests, SLE_UINT32, SLV_183, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_OR, XSLFI_CHILLPP)),
SLE_CONDVAR(GoodsEntry, cargo.reserved_count, SLE_UINT, SLV_181, SL_MAX_VERSION),
SLE_CONDVAR(GoodsEntry, link_graph, SLE_UINT16, SLV_183, SL_MAX_VERSION),
SLE_CONDVAR(GoodsEntry, node, SLE_UINT16, SLV_183, SL_MAX_VERSION),
SLEG_CONDVAR( _num_flows, SLE_UINT32, SLV_183, SL_MAX_VERSION),
SLE_CONDVAR(GoodsEntry, max_waiting_cargo, SLE_UINT32, SLV_183, SL_MAX_VERSION),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
SLE_CONDVAR_X(GoodsEntry, last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 1)),
};
return goods_desc;
}
typedef std::pair<const StationID, CargoPacketList> StationCargoPair;
static const SaveLoad _cargo_list_desc[] = {
SLE_VAR(StationCargoPair, first, SLE_UINT16),
SLE_PTRDEQ(StationCargoPair, second, REF_CARGO_PACKET),
};
/**
* Swap the temporary packets with the packets without specific destination in
* the given goods entry. Assert that at least one of those is empty.
* @param ge Goods entry to swap with.
*/
static void SwapPackets(GoodsEntry *ge)
{
StationCargoPacketMap &ge_packets = const_cast<StationCargoPacketMap &>(*ge->cargo.Packets());
if (_packets.empty()) {
std::map<StationID, CargoPacketList>::iterator it(ge_packets.find(INVALID_STATION));
if (it == ge_packets.end()) {
return;
} else {
it->second.swap(_packets);
}
} else {
assert(ge_packets[INVALID_STATION].empty());
ge_packets[INVALID_STATION].swap(_packets);
}
}
static void Load_STNS()
{
_cargo_source_xy = 0;
_cargo_days = 0;
_cargo_feeder_share = 0;
_num_specs = 0;
uint num_cargo = IsSavegameVersionBefore(SLV_55) ? 12 : IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO;
int index;
while ((index = SlIterateArray()) != -1) {
Station *st = new (index) Station();
SlObject(st, _old_station_desc);
_waiting_acceptance = 0;
for (CargoID i = 0; i < num_cargo; i++) {
GoodsEntry *ge = &st->goods[i];
SlObject(ge, GetGoodsDesc());
SwapPackets(ge);
if (IsSavegameVersionBefore(SLV_68)) {
SB(ge->status, GoodsEntry::GES_ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15));
if (GB(_waiting_acceptance, 0, 12) != 0) {
/* In old versions, enroute_from used 0xFF as INVALID_STATION */
StationID source = (IsSavegameVersionBefore(SLV_7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
/* Make sure we can allocate the CargoPacket. This is safe
* as there can only be ~64k stations and 32 cargoes in these
* savegame versions. As the CargoPacketPool has more than
* 16 million entries; it fits by an order of magnitude. */
assert(CargoPacket::CanAllocateItem());
/* Don't construct the packet with station here, because that'll fail with old savegames */
CargoPacket *cp = new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share);
ge->cargo.Append(cp, INVALID_STATION);
SB(ge->status, GoodsEntry::GES_RATING, 1, 1);
}
}
if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) ge->last_vehicle_type = _old_last_vehicle_type;
}
if (_num_specs != 0) {
/* Allocate speclist memory when loading a game */
st->speclist.resize(_num_specs);
for (uint i = 0; i < st->speclist.size(); i++) {
SlObject(&st->speclist[i], _station_speclist_desc);
}
}
}
}
static void Ptrs_STNS()
{
/* Don't run when savegame version is higher than or equal to 123. */
if (!IsSavegameVersionBefore(SLV_123)) return;
uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO;
for (Station *st : Station::Iterate()) {
if (!IsSavegameVersionBefore(SLV_68)) {
for (CargoID i = 0; i < num_cargo; i++) {
GoodsEntry *ge = &st->goods[i];
SwapPackets(ge);
SlObject(ge, GetGoodsDesc());
SwapPackets(ge);
}
}
SlObject(st, _old_station_desc);
}
}
static const SaveLoad _base_station_desc[] = {
SLE_VAR(BaseStation, xy, SLE_UINT32),
SLE_REF(BaseStation, town, REF_TOWN),
SLE_VAR(BaseStation, string_id, SLE_STRINGID),
SLE_STR(BaseStation, name, SLE_STR | SLF_ALLOW_CONTROL, 0),
SLE_CONDVAR_X(Station, delete_ctr, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 0, 3)),
SLE_CONDVAR_X(Station, delete_ctr, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 4)),
SLE_VAR(BaseStation, owner, SLE_UINT8),
SLE_VAR(BaseStation, facilities, SLE_UINT8),
SLE_VAR(BaseStation, build_date, SLE_INT32),
/* Used by newstations for graphic variations */
SLE_VAR(BaseStation, random_bits, SLE_UINT16),
SLE_VAR(BaseStation, waiting_triggers, SLE_UINT8),
SLEG_VAR(_num_specs, SLE_UINT8),
SLEG_CONDVAR_X(_num_roadstop_specs, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS)),
SLEG_CONDVARVEC_X(_custom_road_stop_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1)),
SLEG_CONDVARVEC_X(_custom_road_stop_data, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 1, 1)),
SLEG_CONDVAR_X(_num_roadstop_custom_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GRF_ROADSTOPS, 2)),
};
static OldPersistentStorage _old_st_persistent_storage;
static const SaveLoad _station_desc[] = {
SLE_WRITEBYTE(Station, facilities),
SLE_ST_INCLUDE(),
SLE_VAR(Station, train_station.tile, SLE_UINT32),
SLE_VAR(Station, train_station.w, SLE_FILE_U8 | SLE_VAR_U16),
SLE_VAR(Station, train_station.h, SLE_FILE_U8 | SLE_VAR_U16),
SLE_REF(Station, bus_stops, REF_ROADSTOPS),
SLE_REF(Station, truck_stops, REF_ROADSTOPS),
SLE_CONDVAR_X(Station, ship_station.tile, SLE_UINT32, SL_MIN_VERSION, SLV_MULTITILE_DOCKS, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 0, 0)),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 1, 1)),
SLE_CONDVAR(Station, ship_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
SLE_CONDVAR(Station, ship_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
SLE_CONDVAR(Station, ship_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
SLE_CONDVAR(Station, docking_station.tile, SLE_UINT32, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
SLE_CONDVAR(Station, docking_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
SLE_CONDVAR(Station, docking_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_MULTITILE_DOCKS, SL_MAX_VERSION),
SLE_CONDVARVEC_X(Station, docking_tiles, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_MULTIPLE_DOCKS, 2)),
SLE_VAR(Station, airport.tile, SLE_UINT32),
SLE_CONDVAR(Station, airport.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION),
SLE_CONDVAR(Station, airport.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_140, SL_MAX_VERSION),
SLE_VAR(Station, airport.type, SLE_UINT8),
SLE_CONDVAR(Station, airport.layout, SLE_UINT8, SLV_145, SL_MAX_VERSION),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6)),
SLE_VAR(Station, airport.flags, SLE_UINT64),
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP, 1, 6)),
SLE_CONDVAR(Station, airport.rotation, SLE_UINT8, SLV_145, SL_MAX_VERSION),
SLEG_CONDARR(_old_st_persistent_storage.storage, SLE_UINT32, 16, SLV_145, SLV_161),
SLE_CONDREF(Station, airport.psa, REF_STORAGE, SLV_161, SL_MAX_VERSION),
SLE_VAR(Station, indtype, SLE_UINT8),
SLE_CONDVAR_X(Station, extra_name_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_STATION_NAMES)),
SLE_VAR(Station, time_since_load, SLE_UINT8),
SLE_VAR(Station, time_since_unload, SLE_UINT8),
SLEG_CONDVAR_X(_old_last_vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ST_LAST_VEH_TYPE, 0, 0)),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8),
SLE_VEC(Station, loading_vehicles, REF_VEHICLE),
SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES),
SLE_CONDVAR(Station, always_accepted, SLE_UINT64, SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION),
SLE_CONDNULL_X(32 * 24, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_22)),
SLE_CONDVAR_X(Station, station_cargo_history_cargoes, SLE_UINT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_STATION_CARGO_HISTORY)),
};
static const SaveLoad _waypoint_desc[] = {
SLE_WRITEBYTE(Waypoint, facilities),
SLE_ST_INCLUDE(),
SLE_VAR(Waypoint, town_cn, SLE_UINT16),
SLE_CONDVAR(Waypoint, train_station.tile, SLE_UINT32, SLV_124, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, train_station.w, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION),
SLE_CONDVAR(Waypoint, train_station.h, SLE_FILE_U8 | SLE_VAR_U16, SLV_124, SL_MAX_VERSION),
SLE_CONDVAR_X(Waypoint, waypoint_flags, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_WAYPOINT_FLAGS)),
SLE_CONDVAR_X(Waypoint, road_waypoint_area.tile, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)),
SLE_CONDVAR_X(Waypoint, road_waypoint_area.w, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)),
SLE_CONDVAR_X(Waypoint, road_waypoint_area.h, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_WAYPOINTS)),
};
static const SaveLoad _custom_roadstop_tile_data_desc[] = {
SLE_VAR(RoadStopTileData, tile, SLE_UINT32),
SLE_VAR(RoadStopTileData, random_bits, SLE_UINT8),
SLE_VAR(RoadStopTileData, animation_frame, SLE_UINT8),
};
/**
* Get the base station description to be used for SL_ST_INCLUDE
* @return the base station description.
*/
SaveLoadTable GetBaseStationDescription()
{
return _base_station_desc;
}
std::vector<SaveLoad> _filtered_station_desc;
std::vector<SaveLoad> _filtered_waypoint_desc;
std::vector<SaveLoad> _filtered_goods_desc;
std::vector<SaveLoad> _filtered_station_speclist_desc;
std::vector<SaveLoad> _filtered_roadstop_speclist_desc;
static void SetupDescs_STNN()
{
_filtered_station_desc = SlFilterObject(_station_desc);
_filtered_waypoint_desc = SlFilterObject(_waypoint_desc);
_filtered_goods_desc = SlFilterObject(GetGoodsDesc());
_filtered_station_speclist_desc = SlFilterObject(_station_speclist_desc);
_filtered_roadstop_speclist_desc = SlFilterObject(_roadstop_speclist_desc);
}
std::vector<SaveLoad> _filtered_roadstop_desc;
static void SetupDescs_ROADSTOP()
{
_filtered_roadstop_desc = SlFilterObject(_roadstop_desc);
}
static void RealSave_STNN(BaseStation *bst)
{
_num_specs = (uint8)bst->speclist.size();
_num_roadstop_specs = (uint8)bst->roadstop_speclist.size();
_num_roadstop_custom_tiles = (uint32)bst->custom_roadstop_tile_data.size();
bool waypoint = (bst->facilities & FACIL_WAYPOINT) != 0;
SlObjectSaveFiltered(bst, waypoint ? SaveLoadTable(_filtered_waypoint_desc) : SaveLoadTable(_filtered_station_desc));
MemoryDumper *dumper = MemoryDumper::GetCurrent();
if (!waypoint) {
Station *st = Station::From(bst);
for (CargoID i = 0; i < NUM_CARGO; i++) {
_num_dests = (uint32)st->goods[i].cargo.Packets()->MapSize();
_num_flows = (uint32)st->goods[i].flows.size();
SlObjectSaveFiltered(&st->goods[i], _filtered_goods_desc);
for (FlowStatMap::const_iterator outer_it(st->goods[i].flows.begin()); outer_it != st->goods[i].flows.end(); ++outer_it) {
uint32 sum_shares = 0;
FlowSaveLoad flow;
flow.source = outer_it->GetOrigin();
dumper->CheckBytes(2 + 4);
dumper->RawWriteUint16(flow.source);
dumper->RawWriteUint32((uint32)outer_it->size());
FlowStat::const_iterator inner_it(outer_it->begin());
const FlowStat::const_iterator end(outer_it->end());
for (; inner_it != end; ++inner_it) {
flow.via = inner_it->second;
flow.share = inner_it->first - sum_shares;
flow.restricted = inner_it->first > outer_it->GetUnrestricted();
sum_shares = inner_it->first;
assert(flow.share > 0);
// SlObject(&flow, _flow_desc); /* this is highly performance-sensitive, manually unroll */
dumper->CheckBytes(2 + 4 + 1);
dumper->RawWriteUint16(flow.via);
dumper->RawWriteUint32(flow.share);
dumper->RawWriteByte(flow.restricted != 0);
}
SlWriteUint16(outer_it->GetRawFlags());
}
for (StationCargoPacketMap::ConstMapIterator it(st->goods[i].cargo.Packets()->begin()); it != st->goods[i].cargo.Packets()->end(); ++it) {
SlObjectSaveFiltered(const_cast<StationCargoPacketMap::value_type *>(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals
}
}
assert(st->station_cargo_history.size() == CountBits(st->station_cargo_history_cargoes));
dumper->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2);
for (const auto &history : st->station_cargo_history) {
uint i = st->station_cargo_history_offset;
do {
dumper->RawWriteUint16(history[i]);
i++;
if (i == MAX_STATION_CARGO_HISTORY_DAYS) i = 0;
} while (i != st->station_cargo_history_offset);
}
}
for (uint i = 0; i < bst->speclist.size(); i++) {
SlObjectSaveFiltered(&bst->speclist[i], _filtered_station_speclist_desc);
}
for (uint i = 0; i < bst->roadstop_speclist.size(); i++) {
SlObjectSaveFiltered(&bst->roadstop_speclist[i], _filtered_roadstop_speclist_desc);
}
for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) {
SlObjectSaveFiltered(&bst->custom_roadstop_tile_data[i], _custom_roadstop_tile_data_desc); // _custom_roadstop_tile_data_desc has no conditionals
}
}
static void Save_STNN()
{
SetupDescs_STNN();
/* Write the stations */
for (BaseStation *st : BaseStation::Iterate()) {
SlSetArrayIndex(st->index);
SlAutolength((AutolengthProc*)RealSave_STNN, st);
}
}
static void Load_STNN()
{
SetupDescs_STNN();
_num_flows = 0;
_num_specs = 0;
_num_roadstop_specs = 0;
_num_roadstop_custom_tiles = 0;
const uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO;
ReadBuffer *buffer = ReadBuffer::GetCurrent();
int index;
while ((index = SlIterateArray()) != -1) {
bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0;
BaseStation *bst = waypoint ? (BaseStation *)new (index) Waypoint() : new (index) Station();
SlObjectLoadFiltered(bst, waypoint ? SaveLoadTable(_filtered_waypoint_desc) : SaveLoadTable(_filtered_station_desc));
if (!waypoint) {
Station *st = Station::From(bst);
/* Before savegame version 161, persistent storages were not stored in a pool. */
if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_145) && st->facilities & FACIL_AIRPORT) {
/* Store the old persistent storage. The GRFID will be added later. */
assert(PersistentStorage::CanAllocateItem());
st->airport.psa = new PersistentStorage(0, 0, 0);
memcpy(st->airport.psa->storage, _old_st_persistent_storage.storage, sizeof(_old_st_persistent_storage.storage));
}
for (CargoID i = 0; i < num_cargo; i++) {
SlObjectLoadFiltered(&st->goods[i], _filtered_goods_desc);
StationID prev_source = INVALID_STATION;
if (SlXvIsFeaturePresent(XSLFI_FLOW_STAT_FLAGS)) {
for (uint32 j = 0; j < _num_flows; ++j) {
FlowSaveLoad flow;
buffer->CheckBytes(2 + 4);
flow.source = buffer->RawReadUint16();
uint32 flow_count = buffer->RawReadUint32();
buffer->CheckBytes(2 + 4 + 1);
flow.via = buffer->RawReadUint16();
flow.share = buffer->RawReadUint32();
flow.restricted = (buffer->RawReadByte() != 0);
FlowStat *fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted))));
for (uint32 k = 1; k < flow_count; ++k) {
buffer->CheckBytes(2 + 4 + 1);
flow.via = buffer->RawReadUint16();
flow.share = buffer->RawReadUint32();
flow.restricted = (buffer->RawReadByte() != 0);
fs->AppendShare(flow.via, flow.share, flow.restricted);
}
fs->SetRawFlags(SlReadUint16());
}
} else if (SlXvIsFeatureMissing(XSLFI_CHILLPP)) {
FlowSaveLoad flow;
FlowStat *fs = nullptr;
for (uint32 j = 0; j < _num_flows; ++j) {
// SlObject(&flow, _flow_desc); /* this is highly performance-sensitive, manually unroll */
buffer->CheckBytes(2 + 2 + 4);
flow.source = buffer->RawReadUint16();
flow.via = buffer->RawReadUint16();
flow.share = buffer->RawReadUint32();
if (!IsSavegameVersionBefore(SLV_187)) flow.restricted = (buffer->ReadByte() != 0);
if (fs == nullptr || prev_source != flow.source) {
fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted))));
} else {
fs->AppendShare(flow.via, flow.share, flow.restricted);
}
prev_source = flow.source;
}
}
if (IsSavegameVersionBefore(SLV_183) && SlXvIsFeatureMissing(XSLFI_CHILLPP)) {
SwapPackets(&st->goods[i]);
} else {
if (SlXvIsFeaturePresent(XSLFI_CHILLPP)) {
SlSkipBytes(8);
uint num_links = SlReadUint16();
uint num_flows = SlReadUint32();
SlSkipBytes(6);
SlSkipBytes(18 * num_links);
SlSkipBytes(16 * num_flows);
}
StationCargoPair pair;
for (uint j = 0; j < _num_dests; ++j) {
SlObjectLoadFiltered(&pair, _cargo_list_desc); // _cargo_list_desc has no conditionals
const_cast<StationCargoPacketMap &>(*(st->goods[i].cargo.Packets()))[pair.first].swap(pair.second);
assert(pair.second.empty());
}
}
if (SlXvIsFeatureMissing(XSLFI_ST_LAST_VEH_TYPE)) st->goods[i].last_vehicle_type = _old_last_vehicle_type;
}
st->station_cargo_history.resize(CountBits(st->station_cargo_history_cargoes));
buffer->CheckBytes(st->station_cargo_history.size() * MAX_STATION_CARGO_HISTORY_DAYS * 2);
for (auto &history : st->station_cargo_history) {
for (uint16 &amount : history) {
amount = buffer->RawReadUint16();
}
}
if (SlXvIsFeaturePresent(XSLFI_STATION_CARGO_HISTORY, 1, 1)) {
for (auto &history : st->station_cargo_history) {
for (uint16 &amount : history) {
amount = RXCompressUint(amount);
}
}
}
st->station_cargo_history_offset = 0;
}
if (_num_specs != 0) {
/* Allocate speclist memory when loading a game */
bst->speclist.resize(_num_specs);
for (uint i = 0; i < bst->speclist.size(); i++) {
SlObjectLoadFiltered(&bst->speclist[i], _filtered_station_speclist_desc);
}
}
if (_num_roadstop_specs != 0) {
/* Allocate speclist memory when loading a game */
bst->roadstop_speclist.resize(_num_roadstop_specs);
for (uint i = 0; i < bst->roadstop_speclist.size(); i++) {
SlObjectLoadFiltered(&bst->roadstop_speclist[i], _filtered_roadstop_speclist_desc);
}
}
if (_num_roadstop_custom_tiles != 0) {
/* Allocate custom road stop tile data memory when loading a game */
bst->custom_roadstop_tile_data.resize(_num_roadstop_custom_tiles);
for (uint i = 0; i < bst->custom_roadstop_tile_data.size(); i++) {
SlObjectLoadFiltered(&bst->custom_roadstop_tile_data[i], _custom_roadstop_tile_data_desc); // _custom_roadstop_tile_data_desc has no conditionals
}
}
if (SlXvIsFeaturePresent(XSLFI_GRF_ROADSTOPS, 1, 1)) {
for (size_t i = 0; i < _custom_road_stop_tiles.size(); i++) {
bst->custom_roadstop_tile_data.push_back({ _custom_road_stop_tiles[i], (uint8)GB(_custom_road_stop_data[i], 0, 8), (uint8)GB(_custom_road_stop_data[i], 8, 8) });
}
_custom_road_stop_tiles.clear();
_custom_road_stop_data.clear();
}
}
}
static void Ptrs_STNN()
{
/* Don't run when savegame version lower than 123. */
if (IsSavegameVersionBefore(SLV_123)) return;
SetupDescs_STNN();
if (!IsSavegameVersionBefore(SLV_183)) {
assert(_filtered_goods_desc.size() == 0);
}
uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO;
for (Station *st : Station::Iterate()) {
for (CargoID i = 0; i < num_cargo; i++) {
GoodsEntry *ge = &st->goods[i];
if (IsSavegameVersionBefore(SLV_183) && SlXvIsFeatureMissing(XSLFI_CHILLPP)) {
SwapPackets(ge);
SlObjectPtrOrNullFiltered(ge, _filtered_goods_desc);
SwapPackets(ge);
} else {
//SlObject(ge, GetGoodsDesc());
for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) {
SlObjectPtrOrNullFiltered(const_cast<StationCargoPair *>(&(*it)), _cargo_list_desc); // _cargo_list_desc has no conditionals
}
}
}
SlObjectPtrOrNullFiltered(st, _filtered_station_desc);
}
for (Waypoint *wp : Waypoint::Iterate()) {
SlObjectPtrOrNullFiltered(wp, _filtered_waypoint_desc);
}
}
static void Save_ROADSTOP()
{
SetupDescs_ROADSTOP();
for (RoadStop *rs : RoadStop::Iterate()) {
SlSetArrayIndex(rs->index);
SlObjectSaveFiltered(rs, _filtered_roadstop_desc);
}
}
static void Load_ROADSTOP()
{
SetupDescs_ROADSTOP();
int index;
while ((index = SlIterateArray()) != -1) {
RoadStop *rs = new (index) RoadStop(INVALID_TILE);
SlObjectLoadFiltered(rs, _filtered_roadstop_desc);
}
}
static void Ptrs_ROADSTOP()
{
SetupDescs_ROADSTOP();
for (RoadStop *rs : RoadStop::Iterate()) {
SlObjectPtrOrNullFiltered(rs, _filtered_roadstop_desc);
}
}
static void Load_DOCK()
{
extern void SlSkipArray();
SlSkipArray();
}
static const ChunkHandler station_chunk_handlers[] = {
{ 'STNS', nullptr, Load_STNS, Ptrs_STNS, nullptr, CH_ARRAY },
{ 'STNN', Save_STNN, Load_STNN, Ptrs_STNN, nullptr, CH_ARRAY },
{ 'ROAD', Save_ROADSTOP, Load_ROADSTOP, Ptrs_ROADSTOP, nullptr, CH_ARRAY },
{ 'DOCK', nullptr, Load_DOCK, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _station_chunk_handlers(station_chunk_handlers);

51
src/sl/storage_sl.cpp Normal file
View File

@@ -0,0 +1,51 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file storage_sl.cpp Code handling saving and loading of persistent storages. */
#include "../stdafx.h"
#include "../newgrf_storage.h"
#include "saveload.h"
#include "../safeguards.h"
/** Description of the data to save and load in #PersistentStorage. */
static const SaveLoad _storage_desc[] = {
SLE_CONDVAR(PersistentStorage, grfid, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDARR(PersistentStorage, storage, SLE_UINT32, 16, SLV_161, SLV_EXTEND_PERSISTENT_STORAGE),
SLE_CONDARR(PersistentStorage, storage, SLE_UINT32, 256, SLV_EXTEND_PERSISTENT_STORAGE, SL_MAX_VERSION),
};
/** Load persistent storage data. */
static void Load_PSAC()
{
int index;
while ((index = SlIterateArray()) != -1) {
assert(PersistentStorage::CanAllocateItem());
PersistentStorage *ps = new (index) PersistentStorage(0, 0, 0);
SlObject(ps, _storage_desc);
}
}
/** Save persistent storage data. */
static void Save_PSAC()
{
/* Write the industries */
for (PersistentStorage *ps : PersistentStorage::Iterate()) {
ps->ClearChanges();
SlSetArrayIndex(ps->index);
SlObject(ps, _storage_desc);
}
}
/** Chunk handler for persistent storages. */
static const ChunkHandler persistent_storage_chunk_handlers[] = {
{ 'PSAC', Save_PSAC, Load_PSAC, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _persistent_storage_chunk_handlers(persistent_storage_chunk_handlers);

103
src/sl/story_sl.cpp Normal file
View File

@@ -0,0 +1,103 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file story_sl.cpp Code handling saving and loading of story pages */
#include "../stdafx.h"
#include "../story_base.h"
#include "saveload.h"
#include "../safeguards.h"
/** Called after load to trash broken pages. */
void AfterLoadStoryBook()
{
if (IsSavegameVersionBefore(SLV_185)) {
/* Trash all story pages and page elements because
* they were saved with wrong data types.
*/
_story_page_element_pool.CleanPool();
_story_page_pool.CleanPool();
}
}
static const SaveLoad _story_page_elements_desc[] = {
SLE_CONDVAR(StoryPageElement, sort_value, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_185),
SLE_CONDVAR(StoryPageElement, sort_value, SLE_UINT32, SLV_185, SL_MAX_VERSION),
SLE_VAR(StoryPageElement, page, SLE_UINT16),
SLE_CONDVAR(StoryPageElement, type, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SLV_185),
SLE_CONDVAR(StoryPageElement, type, SLE_UINT8, SLV_185, SL_MAX_VERSION),
SLE_VAR(StoryPageElement, referenced_id, SLE_UINT32),
SLE_SSTR(StoryPageElement, text, SLE_STR | SLF_ALLOW_CONTROL),
};
static void Save_STORY_PAGE_ELEMENT()
{
for (StoryPageElement *s : StoryPageElement::Iterate()) {
SlSetArrayIndex(s->index);
SlObject(s, _story_page_elements_desc);
}
}
static void Load_STORY_PAGE_ELEMENT()
{
int index;
uint32 max_sort_value = 0;
while ((index = SlIterateArray()) != -1) {
StoryPageElement *s = new (index) StoryPageElement();
SlObject(s, _story_page_elements_desc);
if (s->sort_value > max_sort_value) {
max_sort_value = s->sort_value;
}
}
/* Update the next sort value, so that the next
* created page is shown after all existing pages.
*/
_story_page_element_next_sort_value = max_sort_value + 1;
}
static const SaveLoad _story_pages_desc[] = {
SLE_CONDVAR(StoryPage, sort_value, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_185),
SLE_CONDVAR(StoryPage, sort_value, SLE_UINT32, SLV_185, SL_MAX_VERSION),
SLE_VAR(StoryPage, date, SLE_UINT32),
SLE_CONDVAR(StoryPage, company, SLE_FILE_U16 | SLE_VAR_U8, SL_MIN_VERSION, SLV_185),
SLE_CONDVAR(StoryPage, company, SLE_UINT8, SLV_185, SL_MAX_VERSION),
SLE_SSTR(StoryPage, title, SLE_STR | SLF_ALLOW_CONTROL),
};
static void Save_STORY_PAGE()
{
for (StoryPage *s : StoryPage::Iterate()) {
SlSetArrayIndex(s->index);
SlObject(s, _story_pages_desc);
}
}
static void Load_STORY_PAGE()
{
int index;
uint32 max_sort_value = 0;
while ((index = SlIterateArray()) != -1) {
StoryPage *s = new (index) StoryPage();
SlObject(s, _story_pages_desc);
if (s->sort_value > max_sort_value) {
max_sort_value = s->sort_value;
}
}
/* Update the next sort value, so that the next
* created page is shown after all existing pages.
*/
_story_page_next_sort_value = max_sort_value + 1;
}
static const ChunkHandler story_page_chunk_handlers[] = {
{ 'STPE', Save_STORY_PAGE_ELEMENT, Load_STORY_PAGE_ELEMENT, nullptr, nullptr, CH_ARRAY },
{ 'STPA', Save_STORY_PAGE, Load_STORY_PAGE, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _story_page_chunk_handlers(story_page_chunk_handlers);

138
src/sl/strings_sl.cpp Normal file
View File

@@ -0,0 +1,138 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file strings_sl.cpp Code handling saving and loading of strings */
#include "../stdafx.h"
#include "../string_func.h"
#include "../strings_func.h"
#include "saveload_internal.h"
#include <sstream>
#include "table/strings.h"
#include "../safeguards.h"
static const int NUM_OLD_STRINGS = 512; ///< The number of custom strings stored in old savegames.
static const int LEN_OLD_STRINGS = 32; ///< The number of characters per string.
static const int LEN_OLD_STRINGS_TTO = 24; ///< The number of characters per string in TTO savegames.
/**
* Remap a string ID from the old format to the new format
* @param s StringID that requires remapping
* @return translated ID
*/
StringID RemapOldStringID(StringID s)
{
switch (s) {
case 0x0006: return STR_SV_EMPTY;
case 0x7000: return STR_SV_UNNAMED;
case 0x70E4: return SPECSTR_COMPANY_NAME_START;
case 0x70E9: return SPECSTR_COMPANY_NAME_START;
case 0x8864: return STR_SV_TRAIN_NAME;
case 0x902B: return STR_SV_ROAD_VEHICLE_NAME;
case 0x9830: return STR_SV_SHIP_NAME;
case 0xA02F: return STR_SV_AIRCRAFT_NAME;
default:
if (IsInsideMM(s, 0x300F, 0x3030)) {
return s - 0x300F + STR_SV_STNAME;
} else {
return s;
}
}
}
/** Location to load the old names to. */
char *_old_name_array = nullptr;
/**
* Copy and convert old custom names to UTF-8.
* They were all stored in a 512 by 32 (200 by 24 for TTO) long string array
* and are now stored with stations, waypoints and other places with names.
* @param id the StringID of the custom name to clone.
* @return the clones custom name.
*/
std::string CopyFromOldName(StringID id)
{
/* Is this name an (old) custom name? */
if (GetStringTab(id) != TEXT_TAB_OLD_CUSTOM) return std::string();
if (IsSavegameVersionBefore(SLV_37)) {
uint offs = _savegame_type == SGT_TTO ? LEN_OLD_STRINGS_TTO * GB(id, 0, 8) : LEN_OLD_STRINGS * GB(id, 0, 9);
const char *strfrom = &_old_name_array[offs];
std::ostringstream tmp;
std::ostreambuf_iterator<char> strto(tmp);
for (; *strfrom != '\0'; strfrom++) {
WChar c = (byte)*strfrom;
/* Map from non-ISO8859-15 characters to UTF-8. */
switch (c) {
case 0xA4: c = 0x20AC; break; // Euro
case 0xA6: c = 0x0160; break; // S with caron
case 0xA8: c = 0x0161; break; // s with caron
case 0xB4: c = 0x017D; break; // Z with caron
case 0xB8: c = 0x017E; break; // z with caron
case 0xBC: c = 0x0152; break; // OE ligature
case 0xBD: c = 0x0153; break; // oe ligature
case 0xBE: c = 0x0178; break; // Y with diaeresis
default: break;
}
Utf8Encode(strto, c);
}
return tmp.str();
} else {
/* Name will already be in UTF-8. */
return std::string(&_old_name_array[LEN_OLD_STRINGS * GB(id, 0, 9)]);
}
}
/**
* Free the memory of the old names array.
* Should be called once the old names have all been converted.
*/
void ResetOldNames()
{
free(_old_name_array);
_old_name_array = nullptr;
}
/**
* Initialize the old names table memory.
*/
void InitializeOldNames()
{
free(_old_name_array);
_old_name_array = CallocT<char>(NUM_OLD_STRINGS * LEN_OLD_STRINGS); // 200 * 24 would be enough for TTO savegames
}
/**
* Load the NAME chunk.
*/
static void Load_NAME()
{
int index;
while ((index = SlIterateArray()) != -1) {
if (index >= NUM_OLD_STRINGS) SlErrorCorrupt("Invalid old name index");
if (SlGetFieldLength() > (uint)LEN_OLD_STRINGS) SlErrorCorrupt("Invalid old name length");
SlArray(&_old_name_array[LEN_OLD_STRINGS * index], SlGetFieldLength(), SLE_UINT8);
/* Make sure the old name is null terminated */
_old_name_array[LEN_OLD_STRINGS * index + LEN_OLD_STRINGS - 1] = '\0';
}
}
/** Chunk handlers related to strings. */
static const ChunkHandler name_chunk_handlers[] = {
{ 'NAME', nullptr, Load_NAME, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _name_chunk_handlers(name_chunk_handlers);

51
src/sl/subsidy_sl.cpp Normal file
View File

@@ -0,0 +1,51 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file subsidy_sl.cpp Code handling saving and loading of subsidies */
#include "../stdafx.h"
#include "../subsidy_base.h"
#include "saveload.h"
#include "../safeguards.h"
static const SaveLoad _subsidies_desc[] = {
SLE_VAR(Subsidy, cargo_type, SLE_UINT8),
SLE_CONDVAR(Subsidy, remaining, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_CUSTOM_SUBSIDY_DURATION),
SLE_CONDVAR(Subsidy, remaining, SLE_UINT16, SLV_CUSTOM_SUBSIDY_DURATION, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, awarded, SLE_UINT8, SLV_125, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, src_type, SLE_UINT8, SLV_125, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, dst_type, SLE_UINT8, SLV_125, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, src, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5),
SLE_CONDVAR(Subsidy, src, SLE_UINT16, SLV_5, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, dst, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_5),
SLE_CONDVAR(Subsidy, dst, SLE_UINT16, SLV_5, SL_MAX_VERSION),
};
static void Save_SUBS()
{
for (Subsidy *s : Subsidy::Iterate()) {
SlSetArrayIndex(s->index);
SlObject(s, _subsidies_desc);
}
}
static void Load_SUBS()
{
int index;
while ((index = SlIterateArray()) != -1) {
Subsidy *s = new (index) Subsidy();
SlObject(s, _subsidies_desc);
}
}
static const ChunkHandler subsidy_chunk_handlers[] = {
{ 'SUBS', Save_SUBS, Load_SUBS, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _subsidy_chunk_handlers(subsidy_chunk_handlers);

View File

@@ -0,0 +1,35 @@
#include "../stdafx.h"
#include "../tbtr_template_vehicle.h"
#include "saveload.h"
static const SaveLoad _template_replacement_desc[] = {
SLE_VAR(TemplateReplacement, sel_template, SLE_UINT16),
SLE_VAR(TemplateReplacement, group, SLE_UINT16),
};
static void Save_TMPL_RPLS()
{
for (TemplateReplacement *tr : TemplateReplacement::Iterate()) {
SlSetArrayIndex(tr->index);
SlObject(tr, _template_replacement_desc);
}
}
static void Load_TMPL_RPLS()
{
int index;
while ((index = SlIterateArray()) != -1) {
TemplateReplacement *tr = new (index) TemplateReplacement();
SlObject(tr, _template_replacement_desc);
}
ReindexTemplateReplacements();
}
extern const ChunkHandler template_replacement_chunk_handlers[] = {
{ 'TRPL', Save_TMPL_RPLS, Load_TMPL_RPLS, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _template_replacement_chunk_handlers(template_replacement_chunk_handlers);

View File

@@ -0,0 +1,159 @@
#include "../stdafx.h"
#include "../tbtr_template_vehicle.h"
#include "../tbtr_template_vehicle_func.h"
#include "../train.h"
#include "../company_base.h"
#include "../core/backup_type.hpp"
#include "../core/random_func.hpp"
#include "saveload.h"
const SaveLoadTable GTD() {
static const SaveLoad _template_veh_desc[] = {
SLE_REF(TemplateVehicle, next, REF_TEMPLATE_VEHICLE),
SLE_VAR(TemplateVehicle, reuse_depot_vehicles, SLE_UINT8),
SLE_VAR(TemplateVehicle, keep_remaining_vehicles, SLE_UINT8),
SLE_VAR(TemplateVehicle, refit_as_template, SLE_UINT8),
SLE_CONDVAR_X(TemplateVehicle, replace_old_only, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 5)),
SLE_CONDVAR_X(TemplateVehicle, owner, SLE_VAR_U8 | SLE_FILE_U32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 0, 3)),
SLE_CONDVAR_X(TemplateVehicle, owner, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 4)),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 0, 3)),
SLE_VAR(TemplateVehicle, engine_type, SLE_UINT16),
SLE_VAR(TemplateVehicle, cargo_type, SLE_UINT8),
SLE_VAR(TemplateVehicle, cargo_cap, SLE_UINT16),
SLE_VAR(TemplateVehicle, cargo_subtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, subtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, railtype, SLE_UINT8),
SLE_VAR(TemplateVehicle, index, SLE_UINT32),
SLE_VAR(TemplateVehicle, real_consist_length, SLE_UINT16),
SLE_VAR(TemplateVehicle, max_speed, SLE_UINT16),
SLE_VAR(TemplateVehicle, power, SLE_UINT32),
SLE_VAR(TemplateVehicle, empty_weight, SLE_UINT32),
SLE_CONDVAR_X(TemplateVehicle, full_weight, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 6)),
SLE_VAR(TemplateVehicle, max_te, SLE_UINT32),
SLE_CONDVAR_X(TemplateVehicle, air_drag, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 8)),
SLE_CONDVAR_X(TemplateVehicle, ctrl_flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 7)),
SLE_CONDSSTR_X(TemplateVehicle, name, SLE_STR | SLF_ALLOW_CONTROL, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 9)),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 0, 3)),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 0, 1)),
SLE_CONDNULL_X(36, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 2, 3)),
SLE_CONDNULL_X(36, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TEMPLATE_REPLACEMENT, 0, 3)),
};
return _template_veh_desc;
}
static void Save_TMPLS()
{
for (TemplateVehicle *tv : TemplateVehicle::Iterate()) {
SlSetArrayIndex(tv->index);
SlObject(tv, GTD());
}
}
static void Load_TMPLS()
{
int index;
while ((index = SlIterateArray()) != -1) {
TemplateVehicle *tv = new (index) TemplateVehicle();
SlObject(tv, GTD());
}
}
static void Ptrs_TMPLS()
{
for (TemplateVehicle *tv : TemplateVehicle::Iterate()) {
SlObject(tv, GTD());
}
}
void AfterLoadTemplateVehicles()
{
for (TemplateVehicle *tv : TemplateVehicle::Iterate()) {
/* Reinstate the previous pointer */
if (tv->next != nullptr) tv->next->previous = tv;
tv->first = nullptr;
}
for (TemplateVehicle *tv : TemplateVehicle::Iterate()) {
/* Fill the first pointers */
if (tv->previous == nullptr) {
for (TemplateVehicle *u = tv; u != nullptr; u = u->Next()) {
u->first = tv;
}
}
}
}
void AfterLoadTemplateVehiclesUpdate()
{
SavedRandomSeeds saved_seeds;
SaveRandomSeeds(&saved_seeds);
if (!SlXvIsFeaturePresent(XSLFI_TEMPLATE_REPLACEMENT, 3)) {
for (TemplateVehicle *tv : TemplateVehicle::Iterate()) {
if (tv->Prev() == nullptr && !Company::IsValidID(tv->owner)) {
// clean up leftover template vehicles which no longer have a valid owner
delete tv;
}
}
}
RestoreRandomSeeds(saved_seeds);
InvalidateTemplateReplacementImages();
}
void AfterLoadTemplateVehiclesUpdateImages()
{
InvalidateTemplateReplacementImages();
}
void AfterLoadTemplateVehiclesUpdateProperties()
{
SavedRandomSeeds saved_seeds;
SaveRandomSeeds(&saved_seeds);
for (TemplateVehicle *tv : TemplateVehicle::Iterate()) {
if (tv->Prev() == nullptr) {
Backup<CompanyID> cur_company(_current_company, tv->owner, FILE_LINE);
StringID err;
Train* t = VirtualTrainFromTemplateVehicle(tv, err, 0);
if (t != nullptr) {
uint32 full_cargo_weight = 0;
for (Train *u = t; u != nullptr; u = u->Next()) {
full_cargo_weight += u->GetCargoWeight(u->cargo_cap);
}
const GroundVehicleCache *gcache = t->GetGroundVehicleCache();
tv->max_speed = t->GetDisplayMaxSpeed();
tv->power = gcache->cached_power;
tv->empty_weight = gcache->cached_weight;
tv->full_weight = gcache->cached_weight + full_cargo_weight;
tv->max_te = gcache->cached_max_te;
tv->air_drag = gcache->cached_air_drag;
delete t;
}
cur_company.Restore();
}
}
RestoreRandomSeeds(saved_seeds);
}
extern const ChunkHandler template_vehicle_chunk_handlers[] = {
{ 'TMPL', Save_TMPLS, Load_TMPLS, Ptrs_TMPLS, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _template_vehicle_chunk_handlers(template_vehicle_chunk_handlers);

439
src/sl/town_sl.cpp Normal file
View File

@@ -0,0 +1,439 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file town_sl.cpp Code handling saving and loading of towns and houses */
#include "../stdafx.h"
#include "../newgrf_house.h"
#include "../town.h"
#include "../landscape.h"
#include "../subsidy_func.h"
#include "../strings_func.h"
#include "../network/network.h"
#include "saveload.h"
#include "newgrf_sl.h"
#include "../safeguards.h"
static bool _town_zone_radii_no_update = false;
extern bool IsGetTownZonesCallbackHandlerPresent();
HouseID SLGetCleanHouseType(TileIndex t, bool old_map_position)
{
if (old_map_position && SlXvIsFeatureMissing(XSLFI_MORE_HOUSES)) {
return _m[t].m4 | (GB(_m[t].m3, 6, 1) << 8);
} else {
return GetCleanHouseType(t);
}
}
/**
* Rebuild all the cached variables of towns.
*/
void RebuildTownCaches(bool cargo_update_required, bool old_map_position)
{
InitializeBuildingCounts();
RebuildTownKdtree();
/* Reset town population and num_houses */
for (Town *town : Town::Iterate()) {
town->cache.population = 0;
town->cache.num_houses = 0;
}
for (TileIndex t = 0; t < MapSize(); t++) {
if (!IsTileType(t, MP_HOUSE)) continue;
HouseID house_id = GetTranslatedHouseID(SLGetCleanHouseType(t, old_map_position));
Town *town = Town::GetByTile(t);
IncreaseBuildingCount(town, house_id);
if (IsHouseCompleted(t)) town->cache.population += HouseSpec::Get(house_id)->population;
/* Increase the number of houses for every house, but only once. */
if (GetHouseNorthPart(house_id) == 0) town->cache.num_houses++;
}
if (!_town_zone_radii_no_update) {
/* Update the population and num_house dependent values */
for (Town *town : Town::Iterate()) {
UpdateTownRadius(town);
}
}
}
static void CheckMultiTileHouseTypes(bool &cargo_update_required, bool old_map_position, bool translate_house_types)
{
auto get_clean_house_type = [&](TileIndex t) -> HouseID {
HouseID type = SLGetCleanHouseType(t, old_map_position);
if (translate_house_types) type = GetTranslatedHouseID(type);
return type;
};
/* Check for cases when a NewGRF has set a wrong house substitute type. */
for (TileIndex t = 0; t < MapSize(); t++) {
if (!IsTileType(t, MP_HOUSE)) continue;
HouseID house_type = get_clean_house_type(t);
TileIndex north_tile = t + GetHouseNorthPart(house_type); // modifies 'house_type'!
if (t == north_tile) {
const HouseSpec *hs = HouseSpec::Get(house_type);
bool valid_house = true;
if (hs->building_flags & TILE_SIZE_2x1) {
TileIndex tile = t + TileDiffXY(1, 0);
if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false;
} else if (hs->building_flags & TILE_SIZE_1x2) {
TileIndex tile = t + TileDiffXY(0, 1);
if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false;
} else if (hs->building_flags & TILE_SIZE_2x2) {
TileIndex tile = t + TileDiffXY(0, 1);
if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 1) valid_house = false;
tile = t + TileDiffXY(1, 0);
if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 2) valid_house = false;
tile = t + TileDiffXY(1, 1);
if (!IsTileType(tile, MP_HOUSE) || get_clean_house_type(tile) != house_type + 3) valid_house = false;
}
/* If not all tiles of this house are present remove the house.
* The other tiles will get removed later in this loop because
* their north tile is not the correct type anymore. */
if (!valid_house) {
DoClearSquare(t);
cargo_update_required = true;
}
} else if (!IsTileType(north_tile, MP_HOUSE) || get_clean_house_type(north_tile) != house_type) {
/* This tile should be part of a multi-tile building but the
* north tile of this house isn't on the map. */
DoClearSquare(t);
cargo_update_required = true;
}
}
}
/**
* Check and update town and house values.
*
* Checked are the HouseIDs. Updated are the
* town population the number of houses per
* town, the town radius and the max passengers
* of the town.
*/
void UpdateHousesAndTowns(bool cargo_update_required, bool old_map_position)
{
auto get_clean_house_type = [&](TileIndex t) -> HouseID {
return SLGetCleanHouseType(t, old_map_position);
};
for (TileIndex t = 0; t < MapSize(); t++) {
if (!IsTileType(t, MP_HOUSE)) continue;
HouseID house_id = get_clean_house_type(t);
if (!HouseSpec::Get(house_id)->enabled && house_id >= NEW_HOUSE_OFFSET) {
/* The specs for this type of house are not available any more, so
* replace it with the substitute original house type. */
house_id = _house_mngr.GetSubstituteID(house_id);
if (old_map_position && SlXvIsFeatureMissing(XSLFI_MORE_HOUSES)) {
_m[t].m4 = GB(house_id, 0, 8);
SB(_m[t].m3, 6, 1, GB(house_id, 8, 1));
} else {
SetHouseType(t, house_id);
}
cargo_update_required = true;
}
}
CheckMultiTileHouseTypes(cargo_update_required, old_map_position, false);
if (cargo_update_required || SlXvIsFeatureMissing(XSLFI_MORE_HOUSES, 2)) CheckMultiTileHouseTypes(cargo_update_required, old_map_position, true);
RebuildTownCaches(cargo_update_required, old_map_position);
}
/** Save and load of towns. */
static const SaveLoad _town_desc[] = {
SLE_CONDVAR(Town, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(Town, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_3), ///< population, no longer in use
SLE_CONDNULL(4, SLV_3, SLV_85), ///< population, no longer in use
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_92), ///< num_houses, no longer in use
SLE_CONDVAR(Town, townnamegrfid, SLE_UINT32, SLV_66, SL_MAX_VERSION),
SLE_VAR(Town, townnametype, SLE_UINT16),
SLE_VAR(Town, townnameparts, SLE_UINT32),
SLE_CONDSTR(Town, name, SLE_STR | SLF_ALLOW_CONTROL, 0, SLV_84, SL_MAX_VERSION),
SLE_VAR(Town, flags, SLE_UINT8),
SLE_CONDVAR_X(Town, church_count, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_MULTI_BUILDING)),
SLE_CONDVAR_X(Town, stadium_count, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_MULTI_BUILDING)),
SLE_CONDVAR(Town, statues, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104),
SLE_CONDVAR(Town, statues, SLE_UINT16, SLV_104, SL_MAX_VERSION),
SLE_CONDNULL(1, SL_MIN_VERSION, SLV_2), ///< sort_index, no longer in use
SLE_CONDVAR(Town, have_ratings, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_104),
SLE_CONDVAR(Town, have_ratings, SLE_UINT16, SLV_104, SL_MAX_VERSION),
SLE_CONDARR(Town, ratings, SLE_INT16, 8, SL_MIN_VERSION, SLV_104),
SLE_CONDARR(Town, ratings, SLE_INT16, MAX_COMPANIES, SLV_104, SL_MAX_VERSION),
SLE_CONDNULL_X(MAX_COMPANIES, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPRINGPP)),
/* failed bribe attempts are stored since savegame format 4 */
SLE_CONDARR(Town, unwanted, SLE_INT8, 8, SLV_4, SLV_104),
SLE_CONDARR(Town, unwanted, SLE_INT8, MAX_COMPANIES, SLV_104, SL_MAX_VERSION),
SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_9),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_max, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, supplied[CT_MAIL].old_max, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_max, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, supplied[CT_MAIL].new_max, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDNULL_X(8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, supplied[CT_PASSENGERS].old_act, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, supplied[CT_MAIL].old_act, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, supplied[CT_PASSENGERS].new_act, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, supplied[CT_MAIL].new_act, SLE_UINT32, SLV_9, SLV_165),
SLE_CONDNULL(2, SL_MIN_VERSION, SLV_164), ///< pct_pass_transported / pct_mail_transported, now computed on the fly
SLE_CONDNULL_X(3, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, received[TE_FOOD].old_act, SLE_UINT16, SL_MIN_VERSION, SLV_165),
SLE_CONDVAR(Town, received[TE_WATER].old_act, SLE_UINT16, SL_MIN_VERSION, SLV_165),
SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, received[TE_FOOD].new_act, SLE_UINT16, SL_MIN_VERSION, SLV_165),
SLE_CONDVAR(Town, received[TE_WATER].new_act, SLE_UINT16, SL_MIN_VERSION, SLV_165),
SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDARR(Town, goal, SLE_UINT32, NUM_TE, SLV_165, SL_MAX_VERSION),
SLE_CONDSSTR(Town, text, SLE_STR | SLF_ALLOW_CONTROL, SLV_168, SL_MAX_VERSION),
SLE_CONDVAR(Town, time_until_rebuild, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54),
SLE_CONDVAR(Town, grow_counter, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SLV_54),
SLE_CONDVAR(Town, growth_rate, SLE_FILE_U8 | SLE_VAR_I16, SL_MIN_VERSION, SLV_54),
SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
SLE_CONDVAR(Town, time_until_rebuild, SLE_UINT16, SLV_54, SL_MAX_VERSION),
SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_26)),
SLE_CONDVAR(Town, grow_counter, SLE_UINT16, SLV_54, SL_MAX_VERSION),
SLE_CONDVAR(Town, growth_rate, SLE_FILE_I16 | SLE_VAR_U16, SLV_54, SLV_165),
SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP, SL_JOKER_1_26)),
SLE_CONDVAR(Town, growth_rate, SLE_UINT16, SLV_165, SL_MAX_VERSION),
SLE_VAR(Town, fund_buildings_months, SLE_UINT8),
SLE_VAR(Town, road_build_months, SLE_UINT8),
SLE_CONDVAR(Town, exclusivity, SLE_UINT8, SLV_2, SL_MAX_VERSION),
SLE_CONDNULL_X(1, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CHILLPP, SL_CHILLPP_232)),
SLE_CONDVAR(Town, exclusive_counter, SLE_UINT8, SLV_2, SL_MAX_VERSION),
SLE_CONDVAR(Town, larger_town, SLE_BOOL, SLV_56, SL_MAX_VERSION),
SLE_CONDVAR(Town, layout, SLE_UINT8, SLV_113, SL_MAX_VERSION),
SLE_CONDREFLIST(Town, psa_list, REF_STORAGE, SLV_161, SL_MAX_VERSION),
SLE_CONDNULL(4, SLV_166, SLV_EXTEND_CARGOTYPES), ///< cargo_produced, no longer in use
SLE_CONDNULL(8, SLV_EXTEND_CARGOTYPES, SLV_REMOVE_TOWN_CARGO_CACHE), ///< cargo_produced, no longer in use
SLE_CONDNULL(30, SLV_2, SLV_REMOVE_TOWN_CARGO_CACHE), ///< old reserved space
SLE_CONDVAR_X(Town, override_flags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)),
SLE_CONDVAR_X(Town, override_values, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)),
SLE_CONDVAR_X(Town, build_tunnels, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)),
SLE_CONDVAR_X(Town, max_road_slope, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TOWN_SETTING_OVERRIDE)),
};
static const SaveLoad _town_supplied_desc[] = {
SLE_CONDVAR(TransportedCargoStat<uint32>, old_max, SLE_UINT32, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint32>, new_max, SLE_UINT32, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint32>, old_act, SLE_UINT32, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint32>, new_act, SLE_UINT32, SLV_165, SL_MAX_VERSION),
};
static const SaveLoad _town_received_desc[] = {
SLE_CONDVAR(TransportedCargoStat<uint16>, old_max, SLE_UINT16, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint16>, new_max, SLE_UINT16, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint16>, old_act, SLE_UINT16, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint16>, new_act, SLE_UINT16, SLV_165, SL_MAX_VERSION),
};
static const SaveLoad _town_received_desc_spp[] = {
SLE_CONDVAR(TransportedCargoStat<uint16>, old_max, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint16>, new_max, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint16>, old_act, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION),
SLE_CONDVAR(TransportedCargoStat<uint16>, new_act, SLE_FILE_U32 | SLE_VAR_U16, SLV_165, SL_MAX_VERSION),
};
std::vector<SaveLoad> _filtered_town_desc;
std::vector<SaveLoad> _filtered_town_supplied_desc;
std::vector<SaveLoad> _filtered_town_received_desc;
static void SetupDescs_TOWN()
{
_filtered_town_desc = SlFilterObject(_town_desc);
_filtered_town_supplied_desc = SlFilterObject(_town_supplied_desc);
_filtered_town_received_desc = SlFilterObject(_town_received_desc);
}
static void Save_HIDS()
{
Save_NewGRFMapping(_house_mngr);
}
static void Load_HIDS()
{
Load_NewGRFMapping(_house_mngr);
}
static void RealSave_Town(Town *t)
{
SlObjectSaveFiltered(t, _filtered_town_desc);
for (CargoID i = 0; i < NUM_CARGO; i++) {
SlObjectSaveFiltered(&t->supplied[i], _filtered_town_supplied_desc);
}
for (int i = TE_BEGIN; i < NUM_TE; i++) {
SlObjectSaveFiltered(&t->received[i], _filtered_town_received_desc);
}
}
static void Save_TOWN()
{
SetupDescs_TOWN();
for (Town *t : Town::Iterate()) {
SlSetArrayIndex(t->index);
SlAutolength((AutolengthProc*)RealSave_Town, t);
}
}
static void Load_TOWN()
{
SetupDescs_TOWN();
int index;
uint num_cargo = IsSavegameVersionBefore(SLV_EXTEND_CARGOTYPES) ? 32 : NUM_CARGO;
while ((index = SlIterateArray()) != -1) {
Town *t = new (index) Town();
SlObjectLoadFiltered(t, _filtered_town_desc);
for (CargoID i = 0; i < num_cargo; i++) {
SlObjectLoadFiltered(&t->supplied[i], _filtered_town_supplied_desc);
}
if (SlXvIsFeaturePresent(XSLFI_SPRINGPP)) {
for (int i = TE_BEGIN; i < NUM_TE; i++) {
SlObject(&t->received[i], _town_received_desc_spp);
}
} else {
for (int i = TE_BEGIN; i < NUM_TE; i++) {
SlObjectLoadFiltered(&t->received[i], _filtered_town_received_desc);
}
}
if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GetStringTab(t->townnametype) != TEXT_TAB_OLD_CUSTOM) {
SlErrorCorrupt("Invalid town name generator");
}
if ((!IsSavegameVersionBefore(SLV_166) && IsSavegameVersionBefore(SLV_REMOVE_TOWN_CARGO_CACHE)) || SlXvIsFeaturePresent(XSLFI_TOWN_CARGO_MATRIX)) {
SlSkipBytes(4); // tile
uint16 w = SlReadUint16();
uint16 h = SlReadUint16();
if (w != 0) {
SlSkipBytes((SlXvIsFeaturePresent(XSLFI_TOWN_CARGO_MATRIX) ? 8 : 4) * ((uint)(w / 4) * (uint)(h / 4)));
}
}
}
}
/** Fix pointers when loading town data. */
static void Ptrs_TOWN()
{
/* Don't run when savegame version lower than 161. */
if (IsSavegameVersionBefore(SLV_161)) return;
SetupDescs_TOWN();
for (Town *t : Town::Iterate()) {
SlObjectPtrOrNullFiltered(t, _filtered_town_desc);
}
}
void SlResetTNNC()
{
_town_zone_radii_no_update = false;
}
void Save_TNNC()
{
assert(_sl_xv_feature_versions[XSLFI_TNNC_CHUNK] != 0);
if (!IsNetworkServerSave() || !IsGetTownZonesCallbackHandlerPresent()) {
SlSetLength(0);
return;
}
SlSetLength(4 + (Town::GetNumItems() * (1 + lengthof(TownCache::squared_town_zone_radius)) * 4));
SlWriteUint32((uint32)Town::GetNumItems());
for (const Town *t : Town::Iterate()) {
SlWriteUint32(t->index);
for (uint i = 0; i < lengthof(TownCache::squared_town_zone_radius); i++) {
SlWriteUint32(t->cache.squared_town_zone_radius[i]);
}
}
}
void Load_TNNC()
{
if (SlGetFieldLength() == 0) return;
if (!_networking || _network_server) {
SlSkipBytes(SlGetFieldLength());
return;
}
_town_zone_radii_no_update = true;
const uint32 count = SlReadUint32();
for (uint32 idx = 0; idx < count; idx++) {
Town *t = Town::Get(SlReadUint32());
for (uint i = 0; i < lengthof(TownCache::squared_town_zone_radius); i++) {
t->cache.squared_town_zone_radius[i] = SlReadUint32();
}
}
}
static ChunkSaveLoadSpecialOpResult Special_TNNC(uint32 chunk_id, ChunkSaveLoadSpecialOp op)
{
switch (op) {
case CSLSO_SHOULD_SAVE_CHUNK:
if (_sl_xv_feature_versions[XSLFI_TNNC_CHUNK] == 0) return CSLSOR_DONT_SAVE_CHUNK;
break;
default:
break;
}
return CSLSOR_NONE;
}
/** Chunk handler for towns. */
static const ChunkHandler town_chunk_handlers[] = {
{ 'HIDS', Save_HIDS, Load_HIDS, nullptr, nullptr, CH_ARRAY },
{ 'CITY', Save_TOWN, Load_TOWN, Ptrs_TOWN, nullptr, CH_ARRAY },
{ 'TNNC', Save_TNNC, Load_TNNC, nullptr, nullptr, CH_RIFF, Special_TNNC },
};
extern const ChunkHandlerTable _town_chunk_handlers(town_chunk_handlers);

225
src/sl/tracerestrict_sl.cpp Normal file
View File

@@ -0,0 +1,225 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file tracerestrict_sl.cpp Code handling saving and loading of trace restrict programs */
#include "../stdafx.h"
#include "../tracerestrict.h"
#include "../strings_func.h"
#include "../string_func.h"
#include "saveload.h"
#include <vector>
static const SaveLoad _trace_restrict_mapping_desc[] = {
SLE_VAR(TraceRestrictMappingItem, program_id, SLE_UINT32),
};
/**
* Load mappings
*/
static void Load_TRRM()
{
int index;
while ((index = SlIterateArray()) != -1) {
TraceRestrictMappingItem &item = _tracerestrictprogram_mapping[index];
SlObject(&item, _trace_restrict_mapping_desc);
}
}
/**
* Save mappings
*/
static void Save_TRRM()
{
for (TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.begin();
iter != _tracerestrictprogram_mapping.end(); ++iter) {
SlSetArrayIndex(iter->first);
SlObject(&(iter->second), _trace_restrict_mapping_desc);
}
}
/** program length save header struct */
struct TraceRestrictProgramStub {
uint32 length;
};
static const SaveLoad _trace_restrict_program_stub_desc[] = {
SLE_VAR(TraceRestrictProgramStub, length, SLE_UINT32),
};
/**
* Load program pool
*/
static void Load_TRRP()
{
int index;
TraceRestrictProgramStub stub;
while ((index = SlIterateArray()) != -1) {
TraceRestrictProgram *prog = new (index) TraceRestrictProgram();
SlObject(&stub, _trace_restrict_program_stub_desc);
prog->items.resize(stub.length);
if (stub.length > 0) SlArray(prog->items.data(), stub.length, SLE_UINT32);
if (SlXvIsFeaturePresent(XSLFI_JOKERPP)) {
for (size_t i = 0; i < prog->items.size(); i++) {
TraceRestrictItem &item = prog->items[i]; // note this is a reference,
if (GetTraceRestrictType(item) == 19 || GetTraceRestrictType(item) == 20) {
SetTraceRestrictType(item, (TraceRestrictItemType)(GetTraceRestrictType(item) + 2));
}
if (IsTraceRestrictDoubleItem(item)) i++;
}
}
CommandCost validation_result = prog->Validate();
if (validation_result.Failed()) {
char str[4096];
char *strend = str + seprintf(str, lastof(str), "Trace restrict program %d: %s\nProgram dump:",
index, GetStringPtr(validation_result.GetErrorMessage()));
uint fail_offset = validation_result.HasResultData() ? validation_result.GetResultData() : UINT32_MAX;
for (uint i = 0; i < (uint)prog->items.size(); i++) {
if ((i % 3) == 0) {
strend += seprintf(strend, lastof(str), "\n%4u:", i);
}
strend += seprintf(strend, lastof(str), (i == fail_offset) ? " [%08X]" : " %08X", prog->items[i]);
}
SlErrorCorrupt(str);
}
}
}
/**
* Save a program, used by SlAutolength
*/
static void RealSave_TRRP(TraceRestrictProgram *prog)
{
TraceRestrictProgramStub stub;
stub.length = (uint32)prog->items.size();
SlObject(&stub, _trace_restrict_program_stub_desc);
SlArray(prog->items.data(), stub.length, SLE_UINT32);
}
/**
* Save program pool
*/
static void Save_TRRP()
{
for (TraceRestrictProgram *prog : TraceRestrictProgram::Iterate()) {
SlSetArrayIndex(prog->index);
SlAutolength((AutolengthProc*) RealSave_TRRP, prog);
}
}
/** program length save header struct */
struct TraceRestrictSlotStub {
uint32 length;
};
static const SaveLoad _trace_restrict_slot_stub_desc[] = {
SLE_VAR(TraceRestrictSlotStub, length, SLE_UINT32),
};
static const SaveLoad _trace_restrict_slot_desc[] = {
SLE_VAR(TraceRestrictSlot, max_occupancy, SLE_UINT32),
SLE_SSTR(TraceRestrictSlot, name, SLF_ALLOW_CONTROL),
SLE_VAR(TraceRestrictSlot, owner, SLE_UINT8),
SLE_CONDVAR_X(TraceRestrictSlot, vehicle_type, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRACE_RESTRICT, 13)),
};
/**
* Load slot pool
*/
static void Load_TRRS()
{
int index;
TraceRestrictSlotStub stub;
while ((index = SlIterateArray()) != -1) {
TraceRestrictSlot *slot = new (index) TraceRestrictSlot();
SlObject(slot, _trace_restrict_slot_desc);
SlObject(&stub, _trace_restrict_slot_stub_desc);
slot->occupants.resize(stub.length);
if (stub.length) SlArray(slot->occupants.data(), stub.length, SLE_UINT32);
}
TraceRestrictSlot::RebuildVehicleIndex();
}
/**
* Save a slot, used by SlAutolength
*/
static void RealSave_TRRS(TraceRestrictSlot *slot)
{
SlObject(slot, _trace_restrict_slot_desc);
TraceRestrictSlotStub stub;
stub.length = (uint32)slot->occupants.size();
SlObject(&stub, _trace_restrict_slot_stub_desc);
if (stub.length) SlArray(slot->occupants.data(), stub.length, SLE_UINT32);
}
/**
* Save slot pool
*/
static void Save_TRRS()
{
for (TraceRestrictSlot *slot : TraceRestrictSlot::Iterate()) {
SlSetArrayIndex(slot->index);
SlAutolength((AutolengthProc*) RealSave_TRRS, slot);
}
}
static const SaveLoad _trace_restrict_counter_desc[] = {
SLE_VAR(TraceRestrictCounter, value, SLE_INT32),
SLE_SSTR(TraceRestrictCounter, name, SLF_ALLOW_CONTROL),
SLE_VAR(TraceRestrictCounter, owner, SLE_UINT8),
};
/**
* Load counter pool
*/
static void Load_TRRC()
{
int index;
while ((index = SlIterateArray()) != -1) {
TraceRestrictCounter *ctr = new (index) TraceRestrictCounter();
SlObject(ctr, _trace_restrict_counter_desc);
}
}
/**
* Save a counter, used by SlAutolength
*/
static void RealSave_TRRC(TraceRestrictCounter *ctr)
{
SlObject(ctr, _trace_restrict_counter_desc);
}
/**
* Save counter pool
*/
static void Save_TRRC()
{
for (TraceRestrictCounter *ctr : TraceRestrictCounter::Iterate()) {
SlSetArrayIndex(ctr->index);
SlAutolength((AutolengthProc*) RealSave_TRRC, ctr);
}
}
/**
* Update program reference counts from just-loaded mapping
*/
void AfterLoadTraceRestrict()
{
for (TraceRestrictMapping::iterator iter = _tracerestrictprogram_mapping.begin();
iter != _tracerestrictprogram_mapping.end(); ++iter) {
_tracerestrictprogram_pool.Get(iter->second.program_id)->IncrementRefCount(iter->first);
}
}
extern const ChunkHandler trace_restrict_chunk_handlers[] = {
{ 'TRRM', Save_TRRM, Load_TRRM, nullptr, nullptr, CH_SPARSE_ARRAY }, // Trace Restrict Mapping chunk
{ 'TRRP', Save_TRRP, Load_TRRP, nullptr, nullptr, CH_ARRAY }, // Trace Restrict Mapping Program Pool chunk
{ 'TRRS', Save_TRRS, Load_TRRS, nullptr, nullptr, CH_ARRAY }, // Trace Restrict Slot Pool chunk
{ 'TRRC', Save_TRRC, Load_TRRC, nullptr, nullptr, CH_ARRAY }, // Trace Restrict Counter Pool chunk
};
extern const ChunkHandlerTable _trace_restrict_chunk_handlers(trace_restrict_chunk_handlers);

View File

@@ -0,0 +1,59 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file train_speed_adaptation.cpp Code handling saving and loading of data for train speed adaptation */
#include "../stdafx.h"
#include "../train_speed_adaptation.h"
#include "saveload.h"
using SignalSpeedType = std::pair<const SignalSpeedKey, SignalSpeedValue>;
static const SaveLoad _train_speed_adaptation_map_desc[] = {
SLE_CONDVAR_X(SignalSpeedType, first.signal_track, SLE_FILE_U8 | SLE_VAR_U16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_SPEED_ADAPTATION, 1, 1)),
SLE_CONDVAR_X(SignalSpeedType, first.signal_track, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TRAIN_SPEED_ADAPTATION, 2)),
SLE_VAR(SignalSpeedType, first.last_passing_train_dir, SLE_UINT8),
SLE_VAR(SignalSpeedType, second.train_speed, SLE_UINT16),
SLE_VAR(SignalSpeedType, second.time_stamp, SLE_UINT64),
};
static std::vector<SaveLoad> _filtered_train_speed_adaptation_map_desc = SlFilterObject(_train_speed_adaptation_map_desc);
static void Load_TSAS()
{
_filtered_train_speed_adaptation_map_desc = SlFilterObject(_train_speed_adaptation_map_desc);
int index;
SignalSpeedType data;
while ((index = SlIterateArray()) != -1) {
const_cast<SignalSpeedKey &>(data.first).signal_tile = index;
SlObjectLoadFiltered(&data, _filtered_train_speed_adaptation_map_desc);
_signal_speeds.insert(data);
}
_filtered_train_speed_adaptation_map_desc.clear();
}
static void RealSave_TSAS(SignalSpeedType *data)
{
SlObjectSaveFiltered(data, _filtered_train_speed_adaptation_map_desc);
}
static void Save_TSAS()
{
_filtered_train_speed_adaptation_map_desc = SlFilterObject(_train_speed_adaptation_map_desc);
for (auto &it : _signal_speeds) {
SlSetArrayIndex(it.first.signal_tile);
SignalSpeedType *data = &it;
SlAutolength((AutolengthProc*) RealSave_TSAS, data);
}
_filtered_train_speed_adaptation_map_desc.clear();
}
extern const ChunkHandler train_speed_adaptation_chunk_handlers[] = {
{ 'TSAS', Save_TSAS, Load_TSAS, nullptr, nullptr, CH_SPARSE_ARRAY },
};
extern const ChunkHandlerTable _train_speed_adaptation_chunk_handlers(train_speed_adaptation_chunk_handlers);

50
src/sl/tunnel_sl.cpp Normal file
View File

@@ -0,0 +1,50 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file tunnel_sl.cpp Code handling saving and loading of tunnels */
#include "../stdafx.h"
#include "../tunnel_base.h"
#include "saveload.h"
#include "../safeguards.h"
static const SaveLoad _tunnel_desc[] = {
SLE_CONDVAR(Tunnel, tile_n, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION),
SLE_CONDVAR(Tunnel, tile_s, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION),
SLE_CONDVAR(Tunnel, height, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION),
SLE_CONDVAR(Tunnel, is_chunnel, SLE_BOOL, SL_MIN_VERSION, SL_MAX_VERSION),
SLE_CONDVAR_X(Tunnel, style, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_NEW_SIGNAL_STYLES)),
};
static void Save_TUNN()
{
for (Tunnel *tunnel : Tunnel::Iterate()) {
SlSetArrayIndex(tunnel->index);
SlObject(tunnel, _tunnel_desc);
}
}
static void Load_TUNN()
{
int index;
while ((index = SlIterateArray()) != -1) {
Tunnel *tunnel = new (index) Tunnel();
SlObject(tunnel, _tunnel_desc);
tunnel->UpdateIndexes();
}
}
extern const ChunkHandler tunnel_chunk_handlers[] = {
{ 'TUNN', Save_TUNN, Load_TUNN, nullptr, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _tunnel_chunk_handlers(tunnel_chunk_handlers);

1533
src/sl/vehicle_sl.cpp Normal file

File diff suppressed because it is too large Load Diff

231
src/sl/waypoint_sl.cpp Normal file
View File

@@ -0,0 +1,231 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/** @file waypoint_sl.cpp Code handling saving and loading of waypoints */
#include "../stdafx.h"
#include "../waypoint_base.h"
#include "../debug.h"
#include "../newgrf_station.h"
#include "../vehicle_base.h"
#include "../town.h"
#include "../newgrf.h"
#include "table/strings.h"
#include "saveload_internal.h"
#include "../safeguards.h"
/** Helper structure to convert from the old waypoint system. */
struct OldWaypoint {
size_t index;
TileIndex xy;
TownID town_index;
Town *town;
uint16 town_cn;
StringID string_id;
TinyString name;
uint8 delete_ctr;
Date build_date;
uint8 localidx;
uint32 grfid;
const StationSpec *spec;
Owner owner;
size_t new_index;
};
/** Temporary array with old waypoints. */
static std::vector<OldWaypoint> _old_waypoints;
/**
* Update the waypoint orders to get the new waypoint ID.
* @param o the order 'list' to check.
*/
static void UpdateWaypointOrder(Order *o)
{
if (!o->IsType(OT_GOTO_WAYPOINT)) return;
for (OldWaypoint &wp : _old_waypoints) {
if (wp.index != o->GetDestination()) continue;
o->SetDestination((DestinationID)wp.new_index);
return;
}
}
/**
* Perform all steps to upgrade from the old waypoints to the new version
* that uses station. This includes some old saveload mechanics.
*/
void MoveWaypointsToBaseStations()
{
/* In version 17, ground type is moved from m2 to m4 for depots and
* waypoints to make way for storing the index in m2. The custom graphics
* id which was stored in m4 is now saved as a grf/id reference in the
* waypoint struct. */
if (IsSavegameVersionBefore(SLV_17)) {
for (OldWaypoint &wp : _old_waypoints) {
if (wp.delete_ctr != 0) continue; // The waypoint was deleted
/* Waypoint indices were not added to the map prior to this. */
_m[wp.xy].m2 = (StationID)wp.index;
if (HasBit(_m[wp.xy].m3, 4)) {
wp.spec = StationClass::Get(STAT_CLASS_WAYP)->GetSpec(_m[wp.xy].m4 + 1);
}
}
} else {
/* As of version 17, we recalculate the custom graphic ID of waypoints
* from the GRF ID / station index. */
for (OldWaypoint &wp : _old_waypoints) {
StationClass* stclass = StationClass::Get(STAT_CLASS_WAYP);
for (uint i = 0; i < stclass->GetSpecCount(); i++) {
const StationSpec *statspec = stclass->GetSpec(i);
if (statspec != nullptr && statspec->grf_prop.grffile->grfid == wp.grfid && statspec->grf_prop.local_id == wp.localidx) {
wp.spec = statspec;
break;
}
}
}
}
if (!Waypoint::CanAllocateItem(_old_waypoints.size())) SlError(STR_ERROR_TOO_MANY_STATIONS_LOADING);
/* All saveload conversions have been done. Create the new waypoints! */
for (OldWaypoint &wp : _old_waypoints) {
TileIndex t = wp.xy;
/* Sometimes waypoint (sign) locations became disconnected from their actual location in
* the map array. If this is the case, try to locate the actual location in the map array */
if (!IsTileType(t, MP_RAILWAY) || GetRailTileType(t) != 2 /* RAIL_TILE_WAYPOINT */ || _m[t].m2 != wp.index) {
DEBUG(sl, 0, "Found waypoint tile %u with invalid position", t);
for (t = 0; t < MapSize(); t++) {
if (IsTileType(t, MP_RAILWAY) && GetRailTileType(t) == 2 /* RAIL_TILE_WAYPOINT */ && _m[t].m2 == wp.index) {
DEBUG(sl, 0, "Found actual waypoint position at %u", t);
break;
}
}
}
if (t == MapSize()) {
SlErrorCorrupt("Waypoint with invalid tile");
}
Waypoint *new_wp = new Waypoint(t);
new_wp->town = wp.town;
new_wp->town_cn = wp.town_cn;
new_wp->name = std::move(wp.name);
new_wp->delete_ctr = 0; // Just reset delete counter for once.
new_wp->build_date = wp.build_date;
new_wp->owner = wp.owner;
new_wp->string_id = STR_SV_STNAME_WAYPOINT;
/* The tile might've been reserved! */
bool reserved = !IsSavegameVersionBefore(SLV_100) && HasBit(_m[t].m5, 4);
/* The tile really has our waypoint, so reassign the map array */
MakeRailWaypoint(t, GetTileOwner(t), new_wp->index, (Axis)GB(_m[t].m5, 0, 1), 0, GetRailType(t));
new_wp->facilities |= FACIL_TRAIN;
new_wp->owner = GetTileOwner(t);
SetRailStationReservation(t, reserved);
if (wp.spec != nullptr) {
SetCustomStationSpecIndex(t, AllocateSpecToStation(wp.spec, new_wp, true));
}
new_wp->rect.BeforeAddTile(t, StationRect::ADD_FORCE);
wp.new_index = new_wp->index;
}
/* Update the orders of vehicles */
for (OrderList *ol : OrderList::Iterate()) {
if (ol->GetFirstSharedVehicle()->type != VEH_TRAIN) continue;
for (Order *o = ol->GetFirstOrder(); o != nullptr; o = o->next) UpdateWaypointOrder(o);
}
for (Vehicle *v : Vehicle::Iterate()) {
if (v->type != VEH_TRAIN) continue;
UpdateWaypointOrder(&v->current_order);
}
ResetOldWaypoints();
}
void ResetOldWaypoints()
{
_old_waypoints.clear();
_old_waypoints.shrink_to_fit();
}
static const SaveLoad _old_waypoint_desc[] = {
SLE_CONDVAR(OldWaypoint, xy, SLE_FILE_U16 | SLE_VAR_U32, SL_MIN_VERSION, SLV_6),
SLE_CONDVAR(OldWaypoint, xy, SLE_UINT32, SLV_6, SL_MAX_VERSION),
SLE_CONDVAR(OldWaypoint, town_index, SLE_UINT16, SLV_12, SLV_122),
SLE_CONDREF(OldWaypoint, town, REF_TOWN, SLV_122, SL_MAX_VERSION),
SLE_CONDVAR(OldWaypoint, town_cn, SLE_FILE_U8 | SLE_VAR_U16, SLV_12, SLV_89),
SLE_CONDVAR(OldWaypoint, town_cn, SLE_UINT16, SLV_89, SL_MAX_VERSION),
SLE_CONDVAR(OldWaypoint, string_id, SLE_STRINGID, SL_MIN_VERSION, SLV_84),
SLE_CONDSTR(OldWaypoint, name, SLE_STR, 0, SLV_84, SL_MAX_VERSION),
SLE_VAR(OldWaypoint, delete_ctr, SLE_UINT8),
SLE_CONDVAR(OldWaypoint, build_date, SLE_FILE_U16 | SLE_VAR_I32, SLV_3, SLV_31),
SLE_CONDVAR(OldWaypoint, build_date, SLE_INT32, SLV_31, SL_MAX_VERSION),
SLE_CONDVAR(OldWaypoint, localidx, SLE_UINT8, SLV_3, SL_MAX_VERSION),
SLE_CONDVAR(OldWaypoint, grfid, SLE_UINT32, SLV_17, SL_MAX_VERSION),
SLE_CONDVAR(OldWaypoint, owner, SLE_UINT8, SLV_101, SL_MAX_VERSION),
};
static void Load_WAYP()
{
/* Precaution for when loading failed and it didn't get cleared */
ResetOldWaypoints();
int index;
while ((index = SlIterateArray()) != -1) {
OldWaypoint *wp = &_old_waypoints.emplace_back();
wp->index = index;
SlObject(wp, _old_waypoint_desc);
}
}
static void Ptrs_WAYP()
{
for (OldWaypoint &wp : _old_waypoints) {
SlObject(&wp, _old_waypoint_desc);
if (IsSavegameVersionBefore(SLV_12)) {
wp.town_cn = (wp.string_id & 0xC000) == 0xC000 ? (wp.string_id >> 8) & 0x3F : 0;
wp.town = ClosestTownFromTile(wp.xy, UINT_MAX);
} else if (IsSavegameVersionBefore(SLV_122)) {
/* Only for versions 12 .. 122 */
if (!Town::IsValidID(wp.town_index)) {
/* Upon a corrupted waypoint we'll likely get here. The next step will be to
* loop over all Ptrs procs to nullptr the pointers. However, we don't know
* whether we're in the nullptr or "normal" Ptrs proc. So just clear the list
* of old waypoints we constructed and then this waypoint (and the other
* possibly corrupt ones) will not be queried in the nullptr Ptrs proc run. */
_old_waypoints.clear();
SlErrorCorrupt("Referencing invalid Town");
}
wp.town = Town::Get(wp.town_index);
}
if (IsSavegameVersionBefore(SLV_84)) {
wp.name = CopyFromOldName(wp.string_id);
}
}
}
static const ChunkHandler waypoint_chunk_handlers[] = {
{ 'CHKP', nullptr, Load_WAYP, Ptrs_WAYP, nullptr, CH_ARRAY },
};
extern const ChunkHandlerTable _waypoint_chunk_handlers(waypoint_chunk_handlers);