Merge branch 'save_ext' into cargo_type_order
# Conflicts: # src/core/smallstack_type.hpp
This commit is contained in:
@@ -154,7 +154,7 @@ static void ConvertTownOwner()
|
||||
if (GB(_m[tile].m5, 4, 2) == ROAD_TILE_CROSSING && HasBit(_m[tile].m3, 7)) {
|
||||
_m[tile].m3 = OWNER_TOWN;
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
|
||||
case MP_TUNNELBRIDGE:
|
||||
if (_m[tile].m1 & 0x80) SetTileOwner(tile, OWNER_TOWN);
|
||||
@@ -718,12 +718,14 @@ bool AfterLoadGame()
|
||||
if (IsSavegameVersionBefore(95)) _settings_game.vehicle.dynamic_engines = 0;
|
||||
if (IsSavegameVersionBefore(96)) _settings_game.economy.station_noise_level = false;
|
||||
if (IsSavegameVersionBefore(133)) {
|
||||
_settings_game.vehicle.roadveh_acceleration_model = 0;
|
||||
_settings_game.vehicle.train_slope_steepness = 3;
|
||||
}
|
||||
if (IsSavegameVersionBefore(134)) _settings_game.economy.feeder_payment_share = 75;
|
||||
if (IsSavegameVersionBefore(138)) _settings_game.vehicle.plane_crashes = 2;
|
||||
if (IsSavegameVersionBefore(139)) _settings_game.vehicle.roadveh_slope_steepness = 7;
|
||||
if (IsSavegameVersionBefore(139)) {
|
||||
_settings_game.vehicle.roadveh_acceleration_model = 0;
|
||||
_settings_game.vehicle.roadveh_slope_steepness = 7;
|
||||
}
|
||||
if (IsSavegameVersionBefore(143)) _settings_game.economy.allow_town_level_crossings = true;
|
||||
if (IsSavegameVersionBefore(159)) {
|
||||
_settings_game.vehicle.max_train_length = 50;
|
||||
@@ -2155,22 +2157,21 @@ bool AfterLoadGame()
|
||||
/* Animated tiles would sometimes not be actually animated or
|
||||
* in case of old savegames duplicate. */
|
||||
|
||||
extern TileIndex *_animated_tile_list;
|
||||
extern uint _animated_tile_count;
|
||||
extern SmallVector<TileIndex, 256> _animated_tiles;
|
||||
|
||||
for (uint i = 0; i < _animated_tile_count; /* Nothing */) {
|
||||
for (TileIndex *tile = _animated_tiles.Begin(); tile < _animated_tiles.End(); /* Nothing */) {
|
||||
/* Remove if tile is not animated */
|
||||
bool remove = _tile_type_procs[GetTileType(_animated_tile_list[i])]->animate_tile_proc == NULL;
|
||||
bool remove = _tile_type_procs[GetTileType(*tile)]->animate_tile_proc == NULL;
|
||||
|
||||
/* and remove if duplicate */
|
||||
for (uint j = 0; !remove && j < i; j++) {
|
||||
remove = _animated_tile_list[i] == _animated_tile_list[j];
|
||||
for (TileIndex *j = _animated_tiles.Begin(); !remove && j < tile; j++) {
|
||||
remove = *tile == *j;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
DeleteAnimatedTile(_animated_tile_list[i]);
|
||||
DeleteAnimatedTile(*tile);
|
||||
} else {
|
||||
i++;
|
||||
tile++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2968,6 +2969,19 @@ bool AfterLoadGame()
|
||||
#endif
|
||||
}
|
||||
|
||||
if (IsSavegameVersionBefore(198)) {
|
||||
/* Convert towns growth_rate and grow_counter to ticks */
|
||||
Town *t;
|
||||
FOR_ALL_TOWNS(t) {
|
||||
/* 0x8000 = TOWN_GROWTH_RATE_CUSTOM previously */
|
||||
if (t->growth_rate & 0x8000) SetBit(t->flags, TOWN_CUSTOM_GROWTH);
|
||||
if (t->growth_rate != TOWN_GROWTH_RATE_NONE) {
|
||||
t->growth_rate = TownTicksToGameTicks(t->growth_rate & ~0x8000);
|
||||
}
|
||||
/* Add t->index % TOWN_GROWTH_TICKS to spread growth across ticks. */
|
||||
t->grow_counter = TownTicksToGameTicks(t->grow_counter) + t->index % TOWN_GROWTH_TICKS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Station acceptance is some kind of cache */
|
||||
if (IsSavegameVersionBefore(127)) {
|
||||
|
@@ -12,22 +12,21 @@
|
||||
#include "../stdafx.h"
|
||||
#include "../tile_type.h"
|
||||
#include "../core/alloc_func.hpp"
|
||||
#include "../core/smallvec_type.hpp"
|
||||
|
||||
#include "saveload.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
extern TileIndex *_animated_tile_list;
|
||||
extern uint _animated_tile_count;
|
||||
extern uint _animated_tile_allocated;
|
||||
extern SmallVector<TileIndex, 256> _animated_tiles;
|
||||
|
||||
/**
|
||||
* Save the ANIT chunk.
|
||||
*/
|
||||
static void Save_ANIT()
|
||||
{
|
||||
SlSetLength(_animated_tile_count * sizeof(*_animated_tile_list));
|
||||
SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
|
||||
SlSetLength(_animated_tiles.Length() * sizeof(*_animated_tiles.Begin()));
|
||||
SlArray(_animated_tiles.Begin(), _animated_tiles.Length(), SLE_UINT32);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,22 +37,20 @@ static void Load_ANIT()
|
||||
/* Before version 80 we did NOT have a variable length animated tile table */
|
||||
if (IsSavegameVersionBefore(80)) {
|
||||
/* In pre version 6, we has 16bit per tile, now we have 32bit per tile, convert it ;) */
|
||||
SlArray(_animated_tile_list, 256, IsSavegameVersionBefore(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32);
|
||||
TileIndex anim_list[256];
|
||||
SlArray(anim_list, 256, IsSavegameVersionBefore(6) ? (SLE_FILE_U16 | SLE_VAR_U32) : SLE_UINT32);
|
||||
|
||||
for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) {
|
||||
if (_animated_tile_list[_animated_tile_count] == 0) break;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (anim_list[i] == 0) break;
|
||||
*_animated_tiles.Append() = anim_list[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_animated_tile_count = (uint)SlGetFieldLength() / sizeof(*_animated_tile_list);
|
||||
|
||||
/* Determine a nice rounded size for the amount of allocated tiles */
|
||||
_animated_tile_allocated = 256;
|
||||
while (_animated_tile_allocated < _animated_tile_count) _animated_tile_allocated *= 2;
|
||||
|
||||
_animated_tile_list = ReallocT<TileIndex>(_animated_tile_list, _animated_tile_allocated);
|
||||
SlArray(_animated_tile_list, _animated_tile_count, SLE_UINT32);
|
||||
uint count = (uint)SlGetFieldLength() / sizeof(*_animated_tiles.Begin());
|
||||
_animated_tiles.Clear();
|
||||
_animated_tiles.Append(count);
|
||||
SlArray(_animated_tiles.Begin(), count, SLE_UINT32);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include "../tunnelbridge_map.h"
|
||||
#include "../tunnelbridge.h"
|
||||
#include "../station_base.h"
|
||||
#include "../strings_func.h"
|
||||
#include "../settings_func.h"
|
||||
|
||||
#include "saveload.h"
|
||||
@@ -185,7 +186,7 @@ void AfterLoadCompanyStats()
|
||||
}
|
||||
}
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
|
||||
case MP_OBJECT:
|
||||
if (GetWaterClass(tile) == WATER_CLASS_CANAL) {
|
||||
@@ -244,7 +245,7 @@ static const SaveLoad _company_desc[] = {
|
||||
SLE_VAR(CompanyProperties, name_1, SLE_STRINGID),
|
||||
SLE_CONDSTR(CompanyProperties, name, SLE_STR | SLF_ALLOW_CONTROL, 0, 84, SL_MAX_VERSION),
|
||||
|
||||
SLE_VAR(CompanyProperties, president_name_1, SLE_UINT16),
|
||||
SLE_VAR(CompanyProperties, president_name_1, SLE_STRINGID),
|
||||
SLE_VAR(CompanyProperties, president_name_2, SLE_UINT32),
|
||||
SLE_CONDSTR(CompanyProperties, president_name, SLE_STR | SLF_ALLOW_CONTROL, 0, 84, SL_MAX_VERSION),
|
||||
|
||||
@@ -259,7 +260,7 @@ static const SaveLoad _company_desc[] = {
|
||||
|
||||
SLE_VAR(CompanyProperties, colour, SLE_UINT8),
|
||||
SLE_VAR(CompanyProperties, money_fraction, SLE_UINT8),
|
||||
SLE_CONDVAR(CompanyProperties, avail_railtypes, SLE_VAR_I32 | SLE_FILE_I8, 0, 57),
|
||||
SLE_CONDNULL(1, 0, 57), ///< avail_railtypes
|
||||
SLE_VAR(CompanyProperties, block_preview, SLE_UINT8),
|
||||
|
||||
SLE_CONDNULL(2, 0, 93), ///< cargo_types
|
||||
@@ -350,7 +351,8 @@ static const SaveLoad _company_economy_desc[] = {
|
||||
SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, 2, SL_MAX_VERSION),
|
||||
|
||||
SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, 0, 169),
|
||||
SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, 170, SL_MAX_VERSION),
|
||||
SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, 170, 198),
|
||||
SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, 199, SL_MAX_VERSION),
|
||||
SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32),
|
||||
|
||||
SLE_END()
|
||||
@@ -498,16 +500,15 @@ static void Check_PLYR()
|
||||
int index;
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
CompanyProperties *cprops = new CompanyProperties();
|
||||
memset(cprops, 0, sizeof(*cprops));
|
||||
SaveLoad_PLYR_common(NULL, cprops);
|
||||
|
||||
/* We do not load old custom names */
|
||||
if (IsSavegameVersionBefore(84)) {
|
||||
if (GB(cprops->name_1, 11, 5) == 15) {
|
||||
if (GetStringTab(cprops->name_1) == TEXT_TAB_OLD_CUSTOM) {
|
||||
cprops->name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (GB(cprops->president_name_1, 11, 5) == 15) {
|
||||
if (GetStringTab(cprops->president_name_1) == TEXT_TAB_OLD_CUSTOM) {
|
||||
cprops->president_name_1 = STR_GAME_SAVELOAD_NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ static void Load_PRIC()
|
||||
/** Cargo payment rates in pre 126 savegames */
|
||||
static void Load_CAPR()
|
||||
{
|
||||
uint num_cargo = IsSavegameVersionBefore(55) ? 12 : NUM_CARGO;
|
||||
uint num_cargo = IsSavegameVersionBefore(55) ? 12 : IsSavegameVersionBefore(199) ? 32 : NUM_CARGO;
|
||||
int vt = IsSavegameVersionBefore(65) ? SLE_FILE_I32 : SLE_FILE_I64;
|
||||
SlArray(NULL, num_cargo, vt | SLE_VAR_NULL);
|
||||
SlArray(NULL, num_cargo, SLE_FILE_U16 | SLE_VAR_NULL);
|
||||
@@ -102,7 +102,7 @@ static void Ptrs_CAPY()
|
||||
|
||||
extern const ChunkHandler _economy_chunk_handlers[] = {
|
||||
{ 'CAPY', Save_CAPY, Load_CAPY, Ptrs_CAPY, NULL, CH_ARRAY},
|
||||
{ 'PRIC', NULL, Load_PRIC, NULL, NULL, CH_RIFF | CH_AUTO_LENGTH},
|
||||
{ 'CAPR', NULL, Load_CAPR, NULL, NULL, CH_RIFF | CH_AUTO_LENGTH},
|
||||
{ 'PRIC', NULL, Load_PRIC, NULL, NULL, CH_RIFF },
|
||||
{ 'CAPR', NULL, Load_CAPR, NULL, NULL, CH_RIFF },
|
||||
{ 'ECMY', Save_ECMY, Load_ECMY, NULL, NULL, CH_RIFF | CH_LAST},
|
||||
};
|
||||
|
@@ -62,6 +62,8 @@ bool SlXvFeatureTest::IsFeaturePresent(uint16 savegame_version, uint16 savegame_
|
||||
{
|
||||
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);
|
||||
|
||||
if (this->feature == XSLFI_NULL) return savegame_version_ok;
|
||||
|
||||
bool feature_ok = SlXvIsFeaturePresent(this->feature, this->min_version, this->max_version);
|
||||
@@ -88,6 +90,20 @@ bool SlXvIsFeaturePresent(SlXvFeatureIndex feature, uint16 min_version, uint16 m
|
||||
return _sl_xv_feature_versions[feature] >= min_version && _sl_xv_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
|
||||
*/
|
||||
@@ -95,6 +111,7 @@ void SlXvResetState()
|
||||
{
|
||||
_sl_is_ext_version = false;
|
||||
_sl_is_faked_ext = false;
|
||||
_sl_xv_discardable_chunk_ids.clear();
|
||||
memset(_sl_xv_feature_versions, 0, sizeof(_sl_xv_feature_versions));
|
||||
}
|
||||
|
||||
|
@@ -40,11 +40,14 @@ enum SlXvFeatureTestOperator {
|
||||
* 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); ///< 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()
|
||||
@@ -53,6 +56,9 @@ struct SlXvFeatureTest {
|
||||
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(uint16 savegame_version, uint16 savegame_version_from, uint16 savegame_version_to) const;
|
||||
};
|
||||
|
||||
@@ -66,6 +72,8 @@ inline bool SlXvIsFeatureMissing(SlXvFeatureIndex feature)
|
||||
return !SlXvIsFeaturePresent(feature);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@@ -57,7 +57,7 @@ static const SaveLoad _industry_desc[] = {
|
||||
SLEG_CONDARR(_old_ind_persistent_storage.storage, SLE_UINT32, 16, 76, 160),
|
||||
SLE_CONDREF(Industry, psa, REF_STORAGE, 161, SL_MAX_VERSION),
|
||||
|
||||
SLE_CONDVAR(Industry, random_triggers, SLE_UINT8, 82, SL_MAX_VERSION),
|
||||
SLE_CONDNULL(1, 82, 196), // random_triggers
|
||||
SLE_CONDVAR(Industry, random, SLE_UINT16, 82, SL_MAX_VERSION),
|
||||
|
||||
SLE_CONDNULL(32, 2, 143), // old reserved space
|
||||
|
@@ -96,8 +96,16 @@ static void Load_NGRF()
|
||||
{
|
||||
Load_NGRF_common(_grfconfig);
|
||||
|
||||
/* Append static NewGRF configuration, but only if there are some NewGRFs. */
|
||||
if (_game_mode != GM_MENU || _all_grfs != NULL) AppendStaticGRFConfigs(&_grfconfig);
|
||||
if (_game_mode == GM_MENU) {
|
||||
/* Intro game must not have NewGRF. */
|
||||
if (_grfconfig != NULL) 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()
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "../engine_func.h"
|
||||
#include "../company_base.h"
|
||||
#include "../disaster_vehicle.h"
|
||||
#include "../core/smallvec_type.hpp"
|
||||
#include "saveload_internal.h"
|
||||
#include "oldloader.h"
|
||||
|
||||
@@ -490,8 +491,7 @@ static inline uint RemapOrderIndex(uint x)
|
||||
return _savegame_type == SGT_TTO ? (x - 0x1AC4) / 2 : (x - 0x1C18) / 2;
|
||||
}
|
||||
|
||||
extern TileIndex *_animated_tile_list;
|
||||
extern uint _animated_tile_count;
|
||||
extern SmallVector<TileIndex, 256> _animated_tiles;
|
||||
extern char *_old_name_array;
|
||||
|
||||
static uint32 _old_town_index;
|
||||
@@ -640,22 +640,18 @@ static bool LoadOldOrder(LoadgameState *ls, int num)
|
||||
|
||||
static bool LoadOldAnimTileList(LoadgameState *ls, int num)
|
||||
{
|
||||
/* This is slightly hackish - we must load a chunk into an array whose
|
||||
* address isn't static, but instead pointed to by _animated_tile_list.
|
||||
* To achieve that, create an OldChunks list on the stack on the fly.
|
||||
* The list cannot be static because the value of _animated_tile_list
|
||||
* can change between calls. */
|
||||
|
||||
TileIndex anim_list[256];
|
||||
const OldChunks anim_chunk[] = {
|
||||
OCL_VAR ( OC_TILE, 256, _animated_tile_list ),
|
||||
OCL_VAR ( OC_TILE, 256, anim_list ),
|
||||
OCL_END ()
|
||||
};
|
||||
|
||||
if (!LoadChunk(ls, NULL, anim_chunk)) return false;
|
||||
|
||||
/* Update the animated tile counter by counting till the first zero in the array */
|
||||
for (_animated_tile_count = 0; _animated_tile_count < 256; _animated_tile_count++) {
|
||||
if (_animated_tile_list[_animated_tile_count] == 0) break;
|
||||
/* The first zero in the loaded array indicates the end of the list. */
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (anim_list[i] == 0) break;
|
||||
*_animated_tiles.Append() = anim_list[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -950,7 +946,7 @@ static const OldChunks _company_chunk[] = {
|
||||
|
||||
OCL_SVAR( OC_UINT8, Company, block_preview ),
|
||||
OCL_CNULL( OC_TTD, 1 ), // Old AI
|
||||
OCL_SVAR( OC_TTD | OC_UINT8, Company, avail_railtypes ),
|
||||
OCL_CNULL( OC_TTD, 1 ), // avail_railtypes
|
||||
OCL_SVAR( OC_TILE, Company, location_of_HQ ),
|
||||
OCL_SVAR( OC_TTD | OC_UINT8, Company, share_owners[0] ),
|
||||
OCL_SVAR( OC_TTD | OC_UINT8, Company, share_owners[1] ),
|
||||
@@ -1165,7 +1161,7 @@ static const OldChunks vehicle_chunk[] = {
|
||||
|
||||
OCL_SVAR( OC_UINT8, Vehicle, owner ),
|
||||
OCL_SVAR( OC_TILE, Vehicle, tile ),
|
||||
OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, cur_image ),
|
||||
OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, sprite_seq.seq[0].sprite ),
|
||||
|
||||
OCL_NULL( 8 ), ///< Vehicle sprite box, calculated automatically
|
||||
|
||||
@@ -1258,7 +1254,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
|
||||
if (v == NULL) continue;
|
||||
v->refit_cap = v->cargo_cap;
|
||||
|
||||
SpriteID sprite = v->cur_image;
|
||||
SpriteID sprite = v->sprite_seq.seq[0].sprite;
|
||||
/* no need to override other sprites */
|
||||
if (IsInsideMM(sprite, 1460, 1465)) {
|
||||
sprite += 580; // aircraft smoke puff
|
||||
@@ -1269,7 +1265,7 @@ bool LoadOldVehicle(LoadgameState *ls, int num)
|
||||
} else if (IsInsideMM(sprite, 2516, 2539)) {
|
||||
sprite += 1385; // rotor or disaster-related vehicles
|
||||
}
|
||||
v->cur_image = sprite;
|
||||
v->sprite_seq.seq[0].sprite = sprite;
|
||||
|
||||
switch (v->type) {
|
||||
case VEH_TRAIN: {
|
||||
|
@@ -41,6 +41,7 @@
|
||||
#include "../fileio_func.h"
|
||||
#include "../gamelog.h"
|
||||
#include "../string_func.h"
|
||||
#include "../string_func_extra.h"
|
||||
#include "../fios.h"
|
||||
#include "../error.h"
|
||||
|
||||
@@ -48,10 +49,12 @@
|
||||
|
||||
#include "saveload_internal.h"
|
||||
#include "saveload_filter.h"
|
||||
#include "saveload_buffer.h"
|
||||
#include "extended_ver_sl.h"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
@@ -266,8 +269,12 @@
|
||||
* 193 26802
|
||||
* 194 26881 1.5.x, 1.6.0
|
||||
* 195 27572 1.6.x
|
||||
* 196 27778 1.7.x
|
||||
* 197 27978 1.8.x
|
||||
* 198
|
||||
* 199
|
||||
*/
|
||||
extern const uint16 SAVEGAME_VERSION = 195; ///< Current savegame version of OpenTTD.
|
||||
extern const uint16 SAVEGAME_VERSION = 199; ///< Current savegame version of OpenTTD.
|
||||
const uint16 SAVEGAME_VERSION_EXT = 0x8000; ///< Savegame extension indicator mask
|
||||
|
||||
SavegameType _savegame_type; ///< type of savegame we are loading
|
||||
@@ -293,108 +300,113 @@ enum SaveLoadAction {
|
||||
enum NeedLength {
|
||||
NL_NONE = 0, ///< not working in NeedLength mode
|
||||
NL_WANTLENGTH = 1, ///< writing length and data
|
||||
NL_CALCLENGTH = 2, ///< need to calculate the length
|
||||
};
|
||||
|
||||
/** 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(NULL), bufe(NULL), reader(reader), read(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline byte ReadByte()
|
||||
{
|
||||
if (this->bufp == this->bufe) {
|
||||
size_t len = this->reader->Read(this->buf, lengthof(this->buf));
|
||||
if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
|
||||
|
||||
this->read += len;
|
||||
this->bufp = this->buf;
|
||||
void ReadBuffer::SkipBytesSlowPath(size_t bytes)
|
||||
{
|
||||
bytes -= (this->bufe - this->bufp);
|
||||
while (true) {
|
||||
size_t len = this->reader->Read(this->buf, lengthof(this->buf));
|
||||
if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
|
||||
this->read += len;
|
||||
if (len >= bytes) {
|
||||
this->bufp = this->buf + bytes;
|
||||
this->bufe = this->buf + len;
|
||||
return;
|
||||
} else {
|
||||
bytes -= len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this->bufp++;
|
||||
void ReadBuffer::AcquireBytes()
|
||||
{
|
||||
size_t remainder = this->bufe - this->bufp;
|
||||
if (remainder) {
|
||||
memmove(this->buf, this->bufp, remainder);
|
||||
}
|
||||
size_t len = this->reader->Read(this->buf + remainder, lengthof(this->buf) - remainder);
|
||||
if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
|
||||
|
||||
this->read += len;
|
||||
this->bufp = this->buf;
|
||||
this->bufe = this->buf + remainder + len;
|
||||
}
|
||||
|
||||
void MemoryDumper::FinaliseBlock()
|
||||
{
|
||||
assert(this->saved_buf == nullptr);
|
||||
if (!this->blocks.empty()) {
|
||||
size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
|
||||
this->blocks.back().size = s;
|
||||
this->completed_block_bytes += s;
|
||||
}
|
||||
this->buf = this->bufe = nullptr;
|
||||
}
|
||||
|
||||
void MemoryDumper::AllocateBuffer()
|
||||
{
|
||||
if (this->saved_buf) {
|
||||
const size_t offset = this->buf - this->autolen_buf;
|
||||
const size_t size = (this->autolen_buf_end - this->autolen_buf) * 2;
|
||||
this->autolen_buf = ReallocT<byte>(this->autolen_buf, size);
|
||||
this->autolen_buf_end = this->autolen_buf + size;
|
||||
this->buf = this->autolen_buf + offset;
|
||||
this->bufe = this->autolen_buf_end;
|
||||
return;
|
||||
}
|
||||
this->FinaliseBlock();
|
||||
this->buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
|
||||
this->blocks.emplace_back(this->buf);
|
||||
this->bufe = this->buf + MEMORY_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush this dumper into a writer.
|
||||
* @param writer The filter we want to use.
|
||||
*/
|
||||
void MemoryDumper::Flush(SaveFilter *writer)
|
||||
{
|
||||
this->FinaliseBlock();
|
||||
|
||||
uint block_count = this->blocks.size();
|
||||
for (uint i = 0; i < block_count; i++) {
|
||||
writer->Write(this->blocks[i].data, this->blocks[i].size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the memory dump made so far.
|
||||
* @return The size.
|
||||
*/
|
||||
size_t GetSize() const
|
||||
{
|
||||
return this->read - (this->bufe - this->bufp);
|
||||
}
|
||||
};
|
||||
writer->Finish();
|
||||
}
|
||||
|
||||
void MemoryDumper::StartAutoLength()
|
||||
{
|
||||
assert(this->saved_buf == nullptr);
|
||||
|
||||
/** Container for dumping the savegame (quickly) to memory. */
|
||||
struct MemoryDumper {
|
||||
AutoFreeSmallVector<byte *, 16> blocks; ///< Buffer with blocks of allocated memory.
|
||||
byte *buf; ///< Buffer we're going to write to.
|
||||
byte *bufe; ///< End of the buffer we write to.
|
||||
this->saved_buf = this->buf;
|
||||
this->saved_bufe = this->bufe;
|
||||
this->buf = this->autolen_buf;
|
||||
this->bufe = this->autolen_buf_end;
|
||||
}
|
||||
|
||||
/** Initialise our variables. */
|
||||
MemoryDumper() : buf(NULL), bufe(NULL)
|
||||
{
|
||||
}
|
||||
std::pair<byte *, size_t> MemoryDumper::StopAutoLength()
|
||||
{
|
||||
assert(this->saved_buf != nullptr);
|
||||
auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf);
|
||||
|
||||
/**
|
||||
* 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 (this->buf == this->bufe) {
|
||||
this->buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
|
||||
*this->blocks.Append() = this->buf;
|
||||
this->bufe = this->buf + MEMORY_CHUNK_SIZE;
|
||||
}
|
||||
this->buf = this->saved_buf;
|
||||
this->bufe = this->saved_bufe;
|
||||
this->saved_buf = this->saved_bufe = nullptr;
|
||||
return res;
|
||||
}
|
||||
|
||||
*this->buf++ = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush this dumper into a writer.
|
||||
* @param writer The filter we want to use.
|
||||
*/
|
||||
void Flush(SaveFilter *writer)
|
||||
{
|
||||
uint i = 0;
|
||||
size_t t = this->GetSize();
|
||||
|
||||
while (t > 0) {
|
||||
size_t to_write = min(MEMORY_CHUNK_SIZE, t);
|
||||
|
||||
writer->Write(this->blocks[i++], to_write);
|
||||
t -= to_write;
|
||||
}
|
||||
|
||||
writer->Finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the memory dump made so far.
|
||||
* @return The size.
|
||||
*/
|
||||
size_t GetSize() const
|
||||
{
|
||||
return this->blocks.Length() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Get the size of the memory dump made so far.
|
||||
* @return The size.
|
||||
*/
|
||||
size_t MemoryDumper::GetSize() const
|
||||
{
|
||||
assert(this->saved_buf == nullptr);
|
||||
return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0);
|
||||
}
|
||||
|
||||
/** The saveload struct, containing reader-writer functions, buffer, version, etc. */
|
||||
struct SaveLoadParams {
|
||||
@@ -421,6 +433,16 @@ struct SaveLoadParams {
|
||||
|
||||
static SaveLoadParams _sl; ///< Parameters used for/at saveload.
|
||||
|
||||
ReadBuffer *ReadBuffer::GetCurrent()
|
||||
{
|
||||
return _sl.reader;
|
||||
}
|
||||
|
||||
MemoryDumper *MemoryDumper::GetCurrent()
|
||||
{
|
||||
return _sl.dumper;
|
||||
}
|
||||
|
||||
/* these define the chunks */
|
||||
extern const ChunkHandler _version_ext_chunk_handlers[];
|
||||
extern const ChunkHandler _gamelog_chunk_handlers[];
|
||||
@@ -643,6 +665,34 @@ byte SlReadByte()
|
||||
return _sl.reader->ReadByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in bytes from the file/data structure but don't do
|
||||
* anything with them, discarding them in effect
|
||||
* @param length The amount of bytes that is being treated this way
|
||||
*/
|
||||
void SlSkipBytes(size_t length)
|
||||
{
|
||||
return _sl.reader->SkipBytes(length);
|
||||
}
|
||||
|
||||
int SlReadUint16()
|
||||
{
|
||||
_sl.reader->CheckBytes(2);
|
||||
return _sl.reader->RawReadUint16();
|
||||
}
|
||||
|
||||
uint32 SlReadUint32()
|
||||
{
|
||||
_sl.reader->CheckBytes(4);
|
||||
return _sl.reader->RawReadUint32();
|
||||
}
|
||||
|
||||
uint64 SlReadUint64()
|
||||
{
|
||||
_sl.reader->CheckBytes(8);
|
||||
return _sl.reader->RawReadUint64();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for writing a byte to the dumper.
|
||||
* @param b The byte to write.
|
||||
@@ -652,6 +702,24 @@ void SlWriteByte(byte b)
|
||||
_sl.dumper->WriteByte(b);
|
||||
}
|
||||
|
||||
void SlWriteUint16(uint16 v)
|
||||
{
|
||||
_sl.dumper->CheckBytes(2);
|
||||
_sl.dumper->RawWriteUint16(v);
|
||||
}
|
||||
|
||||
void SlWriteUint32(uint32 v)
|
||||
{
|
||||
_sl.dumper->CheckBytes(4);
|
||||
_sl.dumper->RawWriteUint32(v);
|
||||
}
|
||||
|
||||
void SlWriteUint64(uint64 v)
|
||||
{
|
||||
_sl.dumper->CheckBytes(8);
|
||||
_sl.dumper->RawWriteUint64(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns number of bytes read so far
|
||||
* May only be called during a load/load check action
|
||||
@@ -920,10 +988,6 @@ void SlSetLength(size_t length)
|
||||
}
|
||||
break;
|
||||
|
||||
case NL_CALCLENGTH:
|
||||
_sl.obj_len += (int)length;
|
||||
break;
|
||||
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
@@ -941,10 +1005,10 @@ static void SlCopyBytes(void *ptr, size_t length)
|
||||
switch (_sl.action) {
|
||||
case SLA_LOAD_CHECK:
|
||||
case SLA_LOAD:
|
||||
for (; length != 0; length--) *p++ = SlReadByte();
|
||||
_sl.reader->CopyBytes(p, length);
|
||||
break;
|
||||
case SLA_SAVE:
|
||||
for (; length != 0; length--) SlWriteByte(*p++);
|
||||
_sl.dumper->CopyBytes(p, length);
|
||||
break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
@@ -1076,6 +1140,18 @@ static inline size_t SlCalcNetStringLen(const char *ptr, size_t length)
|
||||
return min(strlen(ptr), length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the gross length of the std::string that it
|
||||
* will occupy in the savegame. This includes the real length,
|
||||
* and the length that the index will occupy.
|
||||
* @param str reference to the std::string
|
||||
* @return return the gross length of the string
|
||||
*/
|
||||
static inline size_t SlCalcStdStrLen(const std::string &str)
|
||||
{
|
||||
return str.size() + SlGetArrayLength(str.size()); // also include the length of the index
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the gross length of the string that it
|
||||
* will occupy in the savegame. This includes the real length, returned
|
||||
@@ -1187,6 +1263,41 @@ static void SlString(void *ptr, size_t length, VarType conv)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save/Load a std::string.
|
||||
* @param ptr the std::string being manipulated
|
||||
* @param conv must be SLE_FILE_STRING
|
||||
*/
|
||||
static void SlStdString(std::string &str, VarType conv)
|
||||
{
|
||||
switch (_sl.action) {
|
||||
case SLA_SAVE: {
|
||||
SlWriteArrayLength(str.size());
|
||||
SlCopyBytes(const_cast<char *>(str.data()), str.size());
|
||||
break;
|
||||
}
|
||||
case SLA_LOAD_CHECK:
|
||||
case SLA_LOAD: {
|
||||
size_t len = SlReadArrayLength();
|
||||
str.resize(len);
|
||||
SlCopyBytes(const_cast<char *>(str.c_str()), len);
|
||||
|
||||
StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK;
|
||||
if ((conv & SLF_ALLOW_CONTROL) != 0) {
|
||||
settings = settings | SVS_ALLOW_CONTROL_CODE;
|
||||
}
|
||||
if ((conv & SLF_ALLOW_NEWLINE) != 0) {
|
||||
settings = settings | SVS_ALLOW_NEWLINE;
|
||||
}
|
||||
str_validate(str, settings);
|
||||
break;
|
||||
}
|
||||
case SLA_PTRS: break;
|
||||
case SLA_NULL: break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size in bytes of a certain type of atomic array
|
||||
* @param length The length of the array counted in elements
|
||||
@@ -1210,8 +1321,6 @@ void SlArray(void *array, size_t length, VarType conv)
|
||||
/* Automatically calculate the length? */
|
||||
if (_sl.need_length != NL_NONE) {
|
||||
SlSetLength(SlCalcArrayLen(length, conv));
|
||||
/* Determine length only? */
|
||||
if (_sl.need_length == NL_CALCLENGTH) return;
|
||||
}
|
||||
|
||||
/* NOTICE - handle some buggy stuff, in really old versions everything was saved
|
||||
@@ -1366,9 +1475,10 @@ static void *IntToReference(size_t index, SLRefType rt)
|
||||
* Return the size in bytes of a list
|
||||
* @param list The std::list to find the size of
|
||||
*/
|
||||
template<typename PtrList>
|
||||
static inline size_t SlCalcListLen(const void *list)
|
||||
{
|
||||
const std::list<void *> *l = (const std::list<void *> *) list;
|
||||
const PtrList *l = (const PtrList *) list;
|
||||
|
||||
int type_size = IsSavegameVersionBefore(69) ? 2 : 4;
|
||||
/* Each entry is saved as type_size bytes, plus type_size bytes are used for the length
|
||||
@@ -1376,29 +1486,39 @@ static inline size_t SlCalcListLen(const void *list)
|
||||
return l->size() * type_size + type_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size in bytes of a list
|
||||
* @param list The std::list to find the size of
|
||||
*/
|
||||
template<typename PtrList>
|
||||
static inline size_t SlCalcVarListLen(const void *list, size_t item_size)
|
||||
{
|
||||
const PtrList *l = (const PtrList *) list;
|
||||
/* Each entry is saved as item_size bytes, plus 4 bytes are used for the length
|
||||
* of the list */
|
||||
return l->size() * item_size + 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save/Load a list.
|
||||
* @param list The list being manipulated
|
||||
* @param conv SLRefType type of the list (Vehicle *, Station *, etc)
|
||||
*/
|
||||
template<typename PtrList>
|
||||
static void SlList(void *list, SLRefType conv)
|
||||
{
|
||||
/* Automatically calculate the length? */
|
||||
if (_sl.need_length != NL_NONE) {
|
||||
SlSetLength(SlCalcListLen(list));
|
||||
/* Determine length only? */
|
||||
if (_sl.need_length == NL_CALCLENGTH) return;
|
||||
SlSetLength(SlCalcListLen<PtrList>(list));
|
||||
}
|
||||
|
||||
typedef std::list<void *> PtrList;
|
||||
PtrList *l = (PtrList *)list;
|
||||
|
||||
switch (_sl.action) {
|
||||
case SLA_SAVE: {
|
||||
SlWriteUint32((uint32)l->size());
|
||||
|
||||
PtrList::iterator iter;
|
||||
typename PtrList::iterator iter;
|
||||
for (iter = l->begin(); iter != l->end(); ++iter) {
|
||||
void *ptr = *iter;
|
||||
SlWriteUint32((uint32)ReferenceToInt(ptr, conv));
|
||||
@@ -1420,7 +1540,7 @@ static void SlList(void *list, SLRefType conv)
|
||||
PtrList temp = *l;
|
||||
|
||||
l->clear();
|
||||
PtrList::iterator iter;
|
||||
typename PtrList::iterator iter;
|
||||
for (iter = temp.begin(); iter != temp.end(); ++iter) {
|
||||
void *ptr = IntToReference((size_t)*iter, conv);
|
||||
l->push_back(ptr);
|
||||
@@ -1434,6 +1554,53 @@ static void SlList(void *list, SLRefType conv)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save/Load a list.
|
||||
* @param list The list being manipulated
|
||||
* @param conv VarType type of the list
|
||||
*/
|
||||
template<typename PtrList>
|
||||
static void SlVarList(void *list, VarType conv)
|
||||
{
|
||||
const size_t size_len = SlCalcConvMemLen(conv);
|
||||
/* Automatically calculate the length? */
|
||||
if (_sl.need_length != NL_NONE) {
|
||||
SlSetLength(SlCalcVarListLen<PtrList>(list, size_len));
|
||||
}
|
||||
|
||||
PtrList *l = (PtrList *)list;
|
||||
|
||||
switch (_sl.action) {
|
||||
case SLA_SAVE: {
|
||||
SlWriteUint32((uint32)l->size());
|
||||
|
||||
typename PtrList::iterator iter;
|
||||
for (iter = l->begin(); iter != l->end(); ++iter) {
|
||||
SlSaveLoadConv(&(*iter), conv);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SLA_LOAD_CHECK:
|
||||
case SLA_LOAD: {
|
||||
size_t length = SlReadUint32();
|
||||
l->resize(length);
|
||||
|
||||
typename PtrList::iterator iter;
|
||||
iter = l->begin();
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
SlSaveLoadConv(&(*iter), conv);
|
||||
++iter;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SLA_PTRS: break;
|
||||
case SLA_NULL:
|
||||
l->clear();
|
||||
break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/** Are we going to save this object or not? */
|
||||
static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
|
||||
@@ -1486,6 +1653,10 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
|
||||
case SL_ARR:
|
||||
case SL_STR:
|
||||
case SL_LST:
|
||||
case SL_DEQ:
|
||||
case SL_VEC:
|
||||
case SL_STDSTR:
|
||||
case SL_VARVEC:
|
||||
/* CONDITIONAL saveload types depend on the savegame version */
|
||||
if (!SlIsObjectValidInSavegame(sld)) break;
|
||||
|
||||
@@ -1494,7 +1665,20 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
|
||||
case SL_REF: return SlCalcRefLen();
|
||||
case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv);
|
||||
case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv);
|
||||
case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld));
|
||||
case SL_LST: return SlCalcListLen<std::list<void *>>(GetVariableAddress(object, sld));
|
||||
case SL_DEQ: return SlCalcListLen<std::deque<void *>>(GetVariableAddress(object, sld));
|
||||
case SL_VEC: return SlCalcListLen<std::vector<void *>>(GetVariableAddress(object, sld));
|
||||
case SL_VARVEC: {
|
||||
const size_t size_len = SlCalcConvMemLen(sld->conv);
|
||||
switch (size_len) {
|
||||
case 1: return SlCalcVarListLen<std::vector<byte>>(GetVariableAddress(object, sld), 1);
|
||||
case 2: return SlCalcVarListLen<std::vector<uint16>>(GetVariableAddress(object, sld), 2);
|
||||
case 4: return SlCalcVarListLen<std::vector<uint32>>(GetVariableAddress(object, sld), 4);
|
||||
case 8: return SlCalcVarListLen<std::vector<uint64>>(GetVariableAddress(object, sld), 8);
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
case SL_STDSTR: return SlCalcStdStrLen(*static_cast<std::string *>(GetVariableAddress(object, sld)));
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
break;
|
||||
@@ -1506,6 +1690,8 @@ size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef OTTD_ASSERT
|
||||
|
||||
/**
|
||||
* Check whether the variable size of the variable in the saveload configuration
|
||||
* matches with the actual variable size.
|
||||
@@ -1541,14 +1727,21 @@ static bool IsVariableSizeRight(const SaveLoad *sld)
|
||||
/* These should be pointer sized, or fixed array. */
|
||||
return sld->size == sizeof(void *) || sld->size == sld->length;
|
||||
|
||||
case SL_STDSTR:
|
||||
return sld->size == sizeof(std::string);
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* OTTD_ASSERT */
|
||||
|
||||
bool SlObjectMember(void *ptr, const SaveLoad *sld)
|
||||
{
|
||||
#ifdef OTTD_ASSERT
|
||||
assert(IsVariableSizeRight(sld));
|
||||
#endif
|
||||
|
||||
VarType conv = GB(sld->conv, 0, 8);
|
||||
switch (sld->cmd) {
|
||||
@@ -1557,6 +1750,10 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld)
|
||||
case SL_ARR:
|
||||
case SL_STR:
|
||||
case SL_LST:
|
||||
case SL_DEQ:
|
||||
case SL_VEC:
|
||||
case SL_STDSTR:
|
||||
case SL_VARVEC:
|
||||
/* CONDITIONAL saveload types depend on the savegame version */
|
||||
if (!SlIsObjectValidInSavegame(sld)) return false;
|
||||
if (SlSkipVariableOnLoad(sld)) return false;
|
||||
@@ -1583,7 +1780,21 @@ bool SlObjectMember(void *ptr, const SaveLoad *sld)
|
||||
break;
|
||||
case SL_ARR: SlArray(ptr, sld->length, conv); break;
|
||||
case SL_STR: SlString(ptr, sld->length, sld->conv); break;
|
||||
case SL_LST: SlList(ptr, (SLRefType)conv); break;
|
||||
case SL_LST: SlList<std::list<void *>>(ptr, (SLRefType)conv); break;
|
||||
case SL_DEQ: SlList<std::deque<void *>>(ptr, (SLRefType)conv); break;
|
||||
case SL_VEC: SlList<std::vector<void *>>(ptr, (SLRefType)conv); break;
|
||||
case SL_VARVEC: {
|
||||
const size_t size_len = SlCalcConvMemLen(sld->conv);
|
||||
switch (size_len) {
|
||||
case 1: SlVarList<std::vector<byte>>(ptr, conv); break;
|
||||
case 2: SlVarList<std::vector<uint16>>(ptr, conv); break;
|
||||
case 4: SlVarList<std::vector<uint32>>(ptr, conv); break;
|
||||
case 8: SlVarList<std::vector<uint64>>(ptr, conv); break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SL_STDSTR: SlStdString(*static_cast<std::string *>(ptr), sld->conv); break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
break;
|
||||
@@ -1628,7 +1839,6 @@ void SlObject(void *object, const SaveLoad *sld)
|
||||
/* Automatically calculate the length? */
|
||||
if (_sl.need_length != NL_NONE) {
|
||||
SlSetLength(SlCalcObjLength(object, sld));
|
||||
if (_sl.need_length == NL_CALCLENGTH) return;
|
||||
}
|
||||
|
||||
for (; sld->cmd != SL_END; sld++) {
|
||||
@@ -1653,25 +1863,17 @@ void SlGlobList(const SaveLoadGlobVarList *sldg)
|
||||
*/
|
||||
void SlAutolength(AutolengthProc *proc, void *arg)
|
||||
{
|
||||
size_t offs;
|
||||
|
||||
assert(_sl.action == SLA_SAVE);
|
||||
assert(_sl.need_length == NL_WANTLENGTH);
|
||||
|
||||
/* Tell it to calculate the length */
|
||||
_sl.need_length = NL_CALCLENGTH;
|
||||
_sl.obj_len = 0;
|
||||
_sl.need_length = NL_NONE;
|
||||
_sl.dumper->StartAutoLength();
|
||||
proc(arg);
|
||||
|
||||
auto result = _sl.dumper->StopAutoLength();
|
||||
/* Setup length */
|
||||
_sl.need_length = NL_WANTLENGTH;
|
||||
SlSetLength(_sl.obj_len);
|
||||
|
||||
offs = _sl.dumper->GetSize() + _sl.obj_len;
|
||||
|
||||
/* And write the stuff */
|
||||
proc(arg);
|
||||
|
||||
if (offs != _sl.dumper->GetSize()) SlErrorCorrupt("Invalid chunk size");
|
||||
SlSetLength(result.second);
|
||||
_sl.dumper->CopyBytes(result.first, result.second);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1811,32 +2013,6 @@ static void SlLoadCheckChunk(const ChunkHandler *ch)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub Chunk handlers to only calculate length and do nothing else.
|
||||
* The intended chunk handler that should be called.
|
||||
*/
|
||||
static ChunkSaveLoadProc *_stub_save_proc;
|
||||
|
||||
/**
|
||||
* Stub Chunk handlers to only calculate length and do nothing else.
|
||||
* Actually call the intended chunk handler.
|
||||
* @param arg ignored parameter.
|
||||
*/
|
||||
static inline void SlStubSaveProc2(void *arg)
|
||||
{
|
||||
_stub_save_proc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub Chunk handlers to only calculate length and do nothing else.
|
||||
* Call SlAutoLenth with our stub save proc that will eventually
|
||||
* call the intended chunk handler.
|
||||
*/
|
||||
static void SlStubSaveProc()
|
||||
{
|
||||
SlAutolength(SlStubSaveProc2, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is
|
||||
* prefixed by an ID identifying it, followed by data, and terminator where appropriate
|
||||
@@ -1852,11 +2028,8 @@ static void SlSaveChunk(const ChunkHandler *ch)
|
||||
SlWriteUint32(ch->id);
|
||||
DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
|
||||
|
||||
if (ch->flags & CH_AUTO_LENGTH) {
|
||||
/* Need to calculate the length. Solve that by calling SlAutoLength in the save_proc. */
|
||||
_stub_save_proc = proc;
|
||||
proc = SlStubSaveProc;
|
||||
}
|
||||
size_t written = 0;
|
||||
if (_debug_sl_level >= 3) written = SlGetBytesWritten();
|
||||
|
||||
_sl.block_mode = ch->flags & CH_TYPE_MASK;
|
||||
switch (ch->flags & CH_TYPE_MASK) {
|
||||
@@ -1877,6 +2050,8 @@ static void SlSaveChunk(const ChunkHandler *ch)
|
||||
break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
|
||||
DEBUG(sl, 3, "Saved chunk %c%c%c%c (" PRINTF_SIZE " bytes)", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id, SlGetBytesWritten() - written);
|
||||
}
|
||||
|
||||
/** Save all chunks */
|
||||
@@ -1910,6 +2085,8 @@ static void SlLoadChunks()
|
||||
|
||||
for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
|
||||
DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
|
||||
size_t read = 0;
|
||||
if (_debug_sl_level >= 3) read = SlGetBytesRead();
|
||||
|
||||
ch = SlFindChunkHandler(id);
|
||||
if (ch == NULL) {
|
||||
@@ -1922,6 +2099,7 @@ static void SlLoadChunks()
|
||||
} else {
|
||||
SlLoadChunk(ch);
|
||||
}
|
||||
DEBUG(sl, 3, "Loaded chunk %c%c%c%c (" PRINTF_SIZE " bytes)", id >> 24, id >> 16, id >> 8, id, SlGetBytesRead() - read);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1933,10 +2111,13 @@ static void SlLoadCheckChunks()
|
||||
|
||||
for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
|
||||
DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
|
||||
size_t read = 0;
|
||||
if (_debug_sl_level >= 3) read = SlGetBytesRead();
|
||||
|
||||
ch = SlFindChunkHandler(id);
|
||||
if (ch == NULL && !SlXvIsChunkDiscardable(id)) SlErrorCorrupt("Unknown chunk type");
|
||||
SlLoadCheckChunk(ch);
|
||||
DEBUG(sl, 3, "Loaded chunk %c%c%c%c (" PRINTF_SIZE " bytes)", id >> 24, id >> 16, id >> 8, id, SlGetBytesRead() - read);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2065,7 +2246,7 @@ struct LZOLoadFilter : LoadFilter {
|
||||
byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2];
|
||||
uint32 tmp[2];
|
||||
uint32 size;
|
||||
lzo_uint len;
|
||||
lzo_uint len = ssize;
|
||||
|
||||
/* Read header*/
|
||||
if (this->chain->Read((byte*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
|
||||
@@ -2087,7 +2268,8 @@ struct LZOLoadFilter : LoadFilter {
|
||||
if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlErrorCorrupt("Bad checksum");
|
||||
|
||||
/* Decompress */
|
||||
lzo1x_decompress_safe(out + sizeof(uint32) * 1, size, buf, &len, NULL);
|
||||
int ret = lzo1x_decompress_safe(out + sizeof(uint32) * 1, size, buf, &len, NULL);
|
||||
if (ret != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
|
||||
return len;
|
||||
}
|
||||
};
|
||||
@@ -2591,7 +2773,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded)
|
||||
const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
|
||||
|
||||
/* We have written our stuff to memory, now write it to file! */
|
||||
uint32 hdr[2] = { fmt->tag, TO_BE32((SAVEGAME_VERSION | SAVEGAME_VERSION_EXT) << 16) };
|
||||
uint32 hdr[2] = { fmt->tag, TO_BE32((uint32) (SAVEGAME_VERSION | SAVEGAME_VERSION_EXT) << 16) };
|
||||
_sl.sf->Write((byte*)hdr, sizeof(hdr));
|
||||
|
||||
_sl.sf = fmt->init_write(_sl.sf, compression);
|
||||
@@ -2664,7 +2846,7 @@ static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded)
|
||||
SlSaveChunks();
|
||||
|
||||
SaveFileStart();
|
||||
if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread)) {
|
||||
if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread, "ottd:savegame")) {
|
||||
if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
|
||||
|
||||
SaveOrLoadResult result = SaveFileToDisk(false);
|
||||
@@ -2837,10 +3019,9 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
|
||||
}
|
||||
|
||||
GamelogStopAction();
|
||||
SlXvSetCurrentState();
|
||||
}
|
||||
|
||||
SlXvSetCurrentState();
|
||||
|
||||
return SL_OK;
|
||||
}
|
||||
|
||||
|
@@ -52,7 +52,7 @@ enum SavegameType {
|
||||
extern FileToSaveLoad _file_to_saveload;
|
||||
|
||||
void GenerateDefaultSaveName(char *buf, const char *last);
|
||||
void SetSaveLoadError(uint16 str);
|
||||
void SetSaveLoadError(StringID str);
|
||||
const char *GetSaveLoadErrorString();
|
||||
SaveOrLoadResult SaveOrLoad(const char *filename, SaveLoadOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true);
|
||||
void WaitTillSaved();
|
||||
@@ -106,7 +106,6 @@ enum ChunkType {
|
||||
CH_TYPE_MASK = 3,
|
||||
CH_EXT_HDR = 15, ///< Extended chunk header
|
||||
CH_LAST = 8, ///< Last chunk in this array.
|
||||
CH_AUTO_LENGTH = 16,
|
||||
};
|
||||
|
||||
/** Flags for chunk extended headers */
|
||||
@@ -171,7 +170,7 @@ enum VarTypes {
|
||||
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_U16,
|
||||
SLE_STRINGID = SLE_FILE_STRINGID | SLE_VAR_U32,
|
||||
SLE_STRINGBUF = SLE_FILE_STRING | SLE_VAR_STRB,
|
||||
SLE_STRINGBQUOTE = SLE_FILE_STRING | SLE_VAR_STRBQ,
|
||||
SLE_STRING = SLE_FILE_STRING | SLE_VAR_STR,
|
||||
@@ -205,10 +204,15 @@ enum SaveLoadTypes {
|
||||
SL_ARR = 2, ///< Save/load an array.
|
||||
SL_STR = 3, ///< Save/load a string.
|
||||
SL_LST = 4, ///< Save/load a list.
|
||||
SL_DEQ = 5, ///< Save/load a deque.
|
||||
SL_VEC = 6, ///< Save/load a vector.
|
||||
SL_STDSTR = 7, ///< Save/load a std::string.
|
||||
/* non-normal save-load types */
|
||||
SL_WRITEBYTE = 8,
|
||||
SL_VEH_INCLUDE = 9,
|
||||
SL_ST_INCLUDE = 10,
|
||||
/* primitive type vector */
|
||||
SL_VARVEC = 14,
|
||||
SL_END = 15
|
||||
};
|
||||
|
||||
@@ -298,6 +302,18 @@ typedef SaveLoad SaveLoadGlobVarList;
|
||||
#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 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_CONDSTDSTR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_STDSTR, base, variable, type, 0, from, to, extver)
|
||||
#define SLE_CONDSTDSTR(base, variable, type, from, to) SLE_CONDSTDSTR_X(base, variable, type, from, to, SlXvFeatureTest())
|
||||
|
||||
/**
|
||||
* Storage of a list in some savegame versions.
|
||||
* @param base Name of the class or struct containing the list.
|
||||
@@ -310,6 +326,42 @@ typedef SaveLoad SaveLoadGlobVarList;
|
||||
#define SLE_CONDLST_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_LST, base, variable, type, 0, from, to, extver)
|
||||
#define SLE_CONDLST(base, variable, type, from, to) SLE_CONDLST_X(base, variable, type, from, to, 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_CONDDEQ_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_DEQ, base, variable, type, 0, from, to, extver)
|
||||
#define SLE_CONDDEQ(base, variable, type, from, to) SLE_CONDDEQ_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 variable in every version of a savegame.
|
||||
* @param base Name of the class or struct containing the variable.
|
||||
@@ -344,6 +396,14 @@ typedef SaveLoad SaveLoadGlobVarList;
|
||||
*/
|
||||
#define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, 0, SL_MAX_VERSION)
|
||||
|
||||
/**
|
||||
* Storage of a 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_STDSTR(base, variable, type) SLE_CONDSTDSTR(base, variable, type, 0, SL_MAX_VERSION)
|
||||
|
||||
/**
|
||||
* Storage of a list in every savegame version.
|
||||
* @param base Name of the class or struct containing the list.
|
||||
@@ -352,6 +412,22 @@ typedef SaveLoad SaveLoadGlobVarList;
|
||||
*/
|
||||
#define SLE_LST(base, variable, type) SLE_CONDLST(base, variable, type, 0, 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_DEQ(base, variable, type) SLE_CONDDEQ(base, variable, type, 0, 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, 0, SL_MAX_VERSION)
|
||||
|
||||
/**
|
||||
* Empty space in every savegame version.
|
||||
* @param length Length of the empty space.
|
||||
@@ -447,6 +523,28 @@ typedef SaveLoad SaveLoadGlobVarList;
|
||||
#define SLEG_CONDLST_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_LST, variable, type, 0, from, to, extver)
|
||||
#define SLEG_CONDLST(variable, type, from, to) SLEG_CONDLST_X(variable, type, from, to, 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_CONDDEQ_X(variable, type, from, to, extver) SLEG_GENERAL_X(SL_DEQ, variable, type, 0, from, to, extver)
|
||||
#define SLEG_CONDDEQ(variable, type, from, to) SLEG_CONDDEQ_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 global variable in every savegame version.
|
||||
* @param variable Name of the global variable.
|
||||
@@ -473,7 +571,7 @@ typedef SaveLoad SaveLoadGlobVarList;
|
||||
* @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, lengthof(variable), 0, SL_MAX_VERSION)
|
||||
#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, sizeof(variable), 0, SL_MAX_VERSION)
|
||||
|
||||
/**
|
||||
* Storage of a global list in every savegame version.
|
||||
@@ -482,6 +580,20 @@ typedef SaveLoad SaveLoadGlobVarList;
|
||||
*/
|
||||
#define SLEG_LST(variable, type) SLEG_CONDLST(variable, type, 0, 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_DEQ(variable, type) SLEG_CONDDEQ(variable, type, 0, 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, 0, SL_MAX_VERSION)
|
||||
|
||||
/**
|
||||
* Empty global space in some savegame versions.
|
||||
* @param length Length of the empty space.
|
||||
@@ -580,52 +692,15 @@ size_t SlCalcObjLength(const void *object, const SaveLoad *sld);
|
||||
byte SlReadByte();
|
||||
void SlWriteByte(byte b);
|
||||
|
||||
static inline int SlReadUint16()
|
||||
{
|
||||
int x = SlReadByte() << 8;
|
||||
return x | SlReadByte();
|
||||
}
|
||||
int SlReadUint16();
|
||||
uint32 SlReadUint32();
|
||||
uint64 SlReadUint64();
|
||||
|
||||
static inline uint32 SlReadUint32()
|
||||
{
|
||||
uint32 x = SlReadUint16() << 16;
|
||||
return x | SlReadUint16();
|
||||
}
|
||||
void SlWriteUint16(uint16 v);
|
||||
void SlWriteUint32(uint32 v);
|
||||
void SlWriteUint64(uint64 v);
|
||||
|
||||
static inline uint64 SlReadUint64()
|
||||
{
|
||||
uint32 x = SlReadUint32();
|
||||
uint32 y = SlReadUint32();
|
||||
return (uint64)x << 32 | y;
|
||||
}
|
||||
|
||||
static inline void SlWriteUint16(uint16 v)
|
||||
{
|
||||
SlWriteByte(GB(v, 8, 8));
|
||||
SlWriteByte(GB(v, 0, 8));
|
||||
}
|
||||
|
||||
static inline void SlWriteUint32(uint32 v)
|
||||
{
|
||||
SlWriteUint16(GB(v, 16, 16));
|
||||
SlWriteUint16(GB(v, 0, 16));
|
||||
}
|
||||
|
||||
static inline void SlWriteUint64(uint64 x)
|
||||
{
|
||||
SlWriteUint32((uint32)(x >> 32));
|
||||
SlWriteUint32((uint32)x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read in bytes from the file/data structure but don't do
|
||||
* anything with them, discarding them in effect
|
||||
* @param length The amount of bytes that is being treated this way
|
||||
*/
|
||||
static inline void SlSkipBytes(size_t length)
|
||||
{
|
||||
for (; length != 0; length--) SlReadByte();
|
||||
}
|
||||
void SlSkipBytes(size_t length);
|
||||
|
||||
size_t SlGetBytesRead();
|
||||
size_t SlGetBytesWritten();
|
||||
|
266
src/saveload/saveload_buffer.h
Normal file
266
src/saveload/saveload_buffer.h
Normal file
@@ -0,0 +1,266 @@
|
||||
/* $Id$ */
|
||||
|
||||
/*
|
||||
* 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 SAVELOAD_BUFFER_H
|
||||
#define 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(NULL), bufe(NULL), 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 = 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(byte *ptr, size_t length)
|
||||
{
|
||||
while (length) {
|
||||
if (unlikely(this->buf == this->bufe)) {
|
||||
this->AllocateBuffer();
|
||||
}
|
||||
size_t to_copy = 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
|
@@ -325,6 +325,11 @@ static void SwapPackets(GoodsEntry *ge)
|
||||
|
||||
static void Load_STNS()
|
||||
{
|
||||
_cargo_source_xy = 0;
|
||||
_cargo_days = 0;
|
||||
_cargo_feeder_share = 0;
|
||||
|
||||
uint num_cargo = IsSavegameVersionBefore(55) ? 12 : IsSavegameVersionBefore(199) ? 32 : NUM_CARGO;
|
||||
int index;
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
Station *st = new (index) Station();
|
||||
@@ -333,7 +338,6 @@ static void Load_STNS()
|
||||
|
||||
_waiting_acceptance = 0;
|
||||
|
||||
uint num_cargo = IsSavegameVersionBefore(55) ? 12 : NUM_CARGO;
|
||||
for (CargoID i = 0; i < num_cargo; i++) {
|
||||
GoodsEntry *ge = &st->goods[i];
|
||||
SlObject(ge, GetGoodsDesc());
|
||||
@@ -373,10 +377,11 @@ static void Ptrs_STNS()
|
||||
/* Don't run when savegame version is higher than or equal to 123. */
|
||||
if (!IsSavegameVersionBefore(123)) return;
|
||||
|
||||
uint num_cargo = IsSavegameVersionBefore(199) ? 32 : NUM_CARGO;
|
||||
Station *st;
|
||||
FOR_ALL_STATIONS(st) {
|
||||
if (!IsSavegameVersionBefore(68)) {
|
||||
for (CargoID i = 0; i < NUM_CARGO; i++) {
|
||||
for (CargoID i = 0; i < num_cargo; i++) {
|
||||
GoodsEntry *ge = &st->goods[i];
|
||||
SwapPackets(ge);
|
||||
SlObject(ge, GetGoodsDesc());
|
||||
@@ -436,7 +441,8 @@ static const SaveLoad _station_desc[] = {
|
||||
SLE_VAR(Station, last_vehicle_type, SLE_UINT8),
|
||||
SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8),
|
||||
SLE_LST(Station, loading_vehicles, REF_VEHICLE),
|
||||
SLE_CONDVAR(Station, always_accepted, SLE_UINT32, 127, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, 127, 198),
|
||||
SLE_CONDVAR(Station, always_accepted, SLE_UINT64, 199, SL_MAX_VERSION),
|
||||
|
||||
SLE_END()
|
||||
};
|
||||
@@ -514,8 +520,10 @@ static void Save_STNN()
|
||||
|
||||
static void Load_STNN()
|
||||
{
|
||||
int index;
|
||||
_num_flows = 0;
|
||||
|
||||
uint num_cargo = IsSavegameVersionBefore(199) ? 32 : NUM_CARGO;
|
||||
int index;
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0;
|
||||
|
||||
@@ -533,7 +541,7 @@ static void Load_STNN()
|
||||
memcpy(st->airport.psa->storage, _old_st_persistent_storage.storage, sizeof(st->airport.psa->storage));
|
||||
}
|
||||
|
||||
for (CargoID i = 0; i < NUM_CARGO; i++) {
|
||||
for (CargoID i = 0; i < num_cargo; i++) {
|
||||
SlObject(&st->goods[i], GetGoodsDesc());
|
||||
FlowSaveLoad flow;
|
||||
FlowStat *fs = NULL;
|
||||
@@ -575,9 +583,10 @@ static void Ptrs_STNN()
|
||||
/* Don't run when savegame version lower than 123. */
|
||||
if (IsSavegameVersionBefore(123)) return;
|
||||
|
||||
uint num_cargo = IsSavegameVersionBefore(199) ? 32 : NUM_CARGO;
|
||||
Station *st;
|
||||
FOR_ALL_STATIONS(st) {
|
||||
for (CargoID i = 0; i < NUM_CARGO; i++) {
|
||||
for (CargoID i = 0; i < num_cargo; i++) {
|
||||
GoodsEntry *ge = &st->goods[i];
|
||||
if (IsSavegameVersionBefore(183)) {
|
||||
SwapPackets(ge);
|
||||
|
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../string_func.h"
|
||||
#include "../strings_func.h"
|
||||
#include "saveload_internal.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
@@ -60,7 +61,7 @@ char *_old_name_array = NULL;
|
||||
char *CopyFromOldName(StringID id)
|
||||
{
|
||||
/* Is this name an (old) custom name? */
|
||||
if (GB(id, 11, 5) != 15) return NULL;
|
||||
if (GetStringTab(id) != TEXT_TAB_OLD_CUSTOM) return NULL;
|
||||
|
||||
if (IsSavegameVersionBefore(37)) {
|
||||
/* Allow for expansion when converted to UTF-8. */
|
||||
|
@@ -14,6 +14,7 @@
|
||||
#include "../town.h"
|
||||
#include "../landscape.h"
|
||||
#include "../subsidy_func.h"
|
||||
#include "../strings_func.h"
|
||||
|
||||
#include "saveload.h"
|
||||
#include "newgrf_sl.h"
|
||||
@@ -191,7 +192,8 @@ static const SaveLoad _town_desc[] = {
|
||||
|
||||
SLE_CONDLST(Town, psa_list, REF_STORAGE, 161, SL_MAX_VERSION),
|
||||
|
||||
SLE_CONDVAR(Town, cargo_produced, SLE_UINT32, 166, SL_MAX_VERSION),
|
||||
SLE_CONDVAR(Town, cargo_produced, SLE_FILE_U32 | SLE_VAR_U64, 166, 198),
|
||||
SLE_CONDVAR(Town, cargo_produced, SLE_UINT64, 199, SL_MAX_VERSION),
|
||||
|
||||
/* reserve extra space in savegame here. (currently 30 bytes) */
|
||||
SLE_CONDNULL(30, 2, SL_MAX_VERSION),
|
||||
@@ -273,19 +275,20 @@ static void Save_TOWN()
|
||||
static void Load_TOWN()
|
||||
{
|
||||
int index;
|
||||
uint num_cargo = IsSavegameVersionBefore(199) ? 32 : NUM_CARGO;
|
||||
|
||||
while ((index = SlIterateArray()) != -1) {
|
||||
Town *t = new (index) Town();
|
||||
SlObject(t, _town_desc);
|
||||
|
||||
for (CargoID i = 0; i < NUM_CARGO; i++) {
|
||||
for (CargoID i = 0; i < num_cargo; i++) {
|
||||
SlObject(&t->supplied[i], _town_supplied_desc);
|
||||
}
|
||||
for (int i = TE_BEGIN; i < TE_END; i++) {
|
||||
SlObject(&t->received[i], _town_received_desc);
|
||||
}
|
||||
|
||||
if (t->townnamegrfid == 0 && !IsInsideMM(t->townnametype, SPECSTR_TOWNNAME_START, SPECSTR_TOWNNAME_LAST + 1) && GB(t->townnametype, 11, 5) != 15) {
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -294,7 +297,7 @@ static void Load_TOWN()
|
||||
SlObject(&t->cargo_accepted, GetTileMatrixDesc());
|
||||
if (t->cargo_accepted.area.w != 0) {
|
||||
uint arr_len = t->cargo_accepted.area.w / AcceptanceMatrix::GRID * t->cargo_accepted.area.h / AcceptanceMatrix::GRID;
|
||||
t->cargo_accepted.data = MallocT<uint32>(arr_len);
|
||||
t->cargo_accepted.data = MallocT<CargoTypes>(arr_len);
|
||||
SlArray(t->cargo_accepted.data, arr_len, SLE_UINT32);
|
||||
|
||||
/* Rebuild total cargo acceptance. */
|
||||
|
@@ -375,6 +375,8 @@ void AfterLoadVehicles(bool part_of_load)
|
||||
FOR_ALL_VEHICLES(v) {
|
||||
assert(v->first != NULL);
|
||||
|
||||
v->trip_occupancy = CalcPercentVehicleFilled(v, NULL);
|
||||
|
||||
switch (v->type) {
|
||||
case VEH_TRAIN: {
|
||||
Train *t = Train::From(v);
|
||||
@@ -431,26 +433,26 @@ void AfterLoadVehicles(bool part_of_load)
|
||||
RoadVehicle *rv = RoadVehicle::From(v);
|
||||
rv->roadtype = HasBit(EngInfo(v->First()->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
|
||||
rv->compatible_roadtypes = RoadTypeToRoadTypes(rv->roadtype);
|
||||
/* FALL THROUGH */
|
||||
FALLTHROUGH;
|
||||
}
|
||||
|
||||
case VEH_TRAIN:
|
||||
case VEH_SHIP:
|
||||
v->cur_image = v->GetImage(v->direction, EIT_ON_MAP);
|
||||
v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq);
|
||||
break;
|
||||
|
||||
case VEH_AIRCRAFT:
|
||||
if (Aircraft::From(v)->IsNormalAircraft()) {
|
||||
v->cur_image = v->GetImage(v->direction, EIT_ON_MAP);
|
||||
v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq);
|
||||
|
||||
/* The plane's shadow will have the same image as the plane */
|
||||
/* The plane's shadow will have the same image as the plane, but no colour */
|
||||
Vehicle *shadow = v->Next();
|
||||
shadow->cur_image = v->cur_image;
|
||||
shadow->sprite_seq.CopyWithoutPalette(v->sprite_seq);
|
||||
|
||||
/* In the case of a helicopter we will update the rotor sprites */
|
||||
if (v->subtype == AIR_HELICOPTER) {
|
||||
Vehicle *rotor = shadow->Next();
|
||||
rotor->cur_image = GetRotorImage(Aircraft::From(v), EIT_ON_MAP);
|
||||
GetRotorImage(Aircraft::From(v), EIT_ON_MAP, &rotor->sprite_seq);
|
||||
}
|
||||
|
||||
UpdateAircraftCache(Aircraft::From(v), true);
|
||||
@@ -459,7 +461,7 @@ void AfterLoadVehicles(bool part_of_load)
|
||||
default: break;
|
||||
}
|
||||
|
||||
v->UpdateDeltaXY(v->direction);
|
||||
v->UpdateDeltaXY();
|
||||
v->coord.left = INVALID_COORD;
|
||||
v->UpdatePosition();
|
||||
v->UpdateViewport(false);
|
||||
@@ -796,7 +798,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
|
||||
SLE_CONDVAR(Vehicle, z_pos, SLE_FILE_U8 | SLE_VAR_I32, 0, 163),
|
||||
SLE_CONDVAR(Vehicle, z_pos, SLE_INT32, 164, SL_MAX_VERSION),
|
||||
|
||||
SLE_VAR(Vehicle, cur_image, SLE_FILE_U16 | SLE_VAR_U32),
|
||||
SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32),
|
||||
SLE_CONDNULL(5, 0, 57),
|
||||
SLE_VAR(Vehicle, progress, SLE_UINT8),
|
||||
SLE_VAR(Vehicle, vehstatus, SLE_UINT8),
|
||||
@@ -836,7 +838,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
|
||||
SLE_CONDVAR(Vehicle, current_order.dest, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
|
||||
SLE_CONDVAR(Vehicle, current_order.dest, SLE_UINT16, 5, SL_MAX_VERSION),
|
||||
|
||||
SLE_VAR(Vehicle, cur_image, SLE_FILE_U16 | SLE_VAR_U32),
|
||||
SLE_VAR(Vehicle, sprite_seq.seq[0].sprite, SLE_FILE_U16 | SLE_VAR_U32),
|
||||
SLE_CONDVAR(Vehicle, age, SLE_FILE_U16 | SLE_VAR_I32, 0, 30),
|
||||
SLE_CONDVAR(Vehicle, age, SLE_INT32, 31, SL_MAX_VERSION),
|
||||
SLE_VAR(Vehicle, tick_counter, SLE_UINT8),
|
||||
|
Reference in New Issue
Block a user