@@ -160,159 +160,162 @@ static void Load_ORDR()
SlArray(orders, len, SLE_UINT32);
for (size_t i = 0; i < len; ++i) {
new (i) Order(orders[i]);
}
free(orders);
/* Update all the next pointer */
Order *o;
FOR_ALL_ORDERS(o) {
/* 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 != NULL) prev->next = o;
} else {
int index;
while ((index = SlIterateArray()) != -1) {
Order *order = new (index) Order();
SlObject(order, GetOrderDescription());
if (IsSavegameVersionBefore(190)) {
order->SetTravelTimetabled(order->GetTravelTime() > 0);
order->SetWaitTimetabled(order->GetWaitTime() > 0);
static void Ptrs_ORDR()
{
/* Orders from old savegames have pointers corrected in Load_ORDR */
if (IsSavegameVersionBefore(5, 2)) return;
SlObject(o, GetOrderDescription());
const SaveLoad *GetOrderListDescription()
static const SaveLoad _orderlist_desc[] = {
SLE_REF(OrderList, first, REF_ORDER),
SLE_END()
};
return _orderlist_desc;
static void Save_ORDL()
OrderList *list;
FOR_ALL_ORDER_LISTS(list) {
SlSetArrayIndex(list->index);
SlObject(list, GetOrderListDescription());
static void Load_ORDL()
/* set num_orders to 0 so it's a valid OrderList */
OrderList *list = new (index) OrderList(0);
static void Ptrs_ORDL()
const SaveLoad *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_VAR(OrderBackup, service_interval, SLE_UINT32),
SLE_CONDVAR(OrderBackup, service_interval, SLE_FILE_U32 | SLE_VAR_U16, 0, 191),
SLE_CONDVAR(OrderBackup, service_interval, SLE_UINT16, 192, SL_MAX_VERSION),
SLE_STR(OrderBackup, name, SLE_STR, 0),
SLE_VAR(OrderBackup, clone, SLE_UINT16),
SLE_CONDNULL(2, 0, 191), // clone (2 bytes of pointer, i.e. garbage)
SLE_CONDREF(OrderBackup, clone, REF_VEHICLE, 192, SL_MAX_VERSION),
SLE_VAR(OrderBackup, cur_real_order_index, SLE_UINT8),
SLE_CONDVAR(OrderBackup, cur_implicit_order_index, SLE_UINT8, 176, SL_MAX_VERSION),
SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, 176, SL_MAX_VERSION),
SLE_CONDVAR(OrderBackup, lateness_counter, SLE_INT32, 176, SL_MAX_VERSION),
SLE_CONDVAR(OrderBackup, timetable_start, SLE_INT32, 176, SL_MAX_VERSION),
SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U16, 176, 179),
SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_UINT16, 180, SL_MAX_VERSION),
SLE_REF(OrderBackup, orders, REF_ORDER),
return _order_backup_desc;
static 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;
OrderBackup *ob;
FOR_ALL_ORDER_BACKUPS(ob) {
SlSetArrayIndex(ob->index);
SlObject(ob, GetOrderBackupDescription());
void Load_BKOR()
OrderBackup *ob = new (index) OrderBackup();
/* Only load order-backups for network clients.
* If we are a network server or not networking, then we just loaded
* a previously saved-by-server savegame. There are
* no clients with a backup anymore, so clear it. */
if (!_networking || _network_server) {
* If we are a network server or not networking, then we just loaded a previously
* saved-by-server savegame. There are no clients with a backup, so clear it.
* Furthermore before savegame version 192 the actual content was always corrupt.
*/
if (!_networking || _network_server || IsSavegameVersionBefore(192)) {
_order_backup_pool.CleanPool();
static void Ptrs_BKOR()
extern const ChunkHandler _order_chunk_handlers[] = {
{ 'BKOR', Save_BKOR, Load_BKOR, Ptrs_BKOR, NULL, CH_ARRAY},
{ 'ORDR', Save_ORDR, Load_ORDR, Ptrs_ORDR, NULL, CH_ARRAY},
{ 'ORDL', Save_ORDL, Load_ORDL, Ptrs_ORDL, NULL, CH_ARRAY | CH_LAST},
@@ -166,194 +166,195 @@
* 96 13226
* 97 13256
* 98 13375
* 99 13838
* 100 13952
* 101 14233
* 102 14332
* 103 14598
* 104 14735
* 105 14803
* 106 14919
* 107 15027
* 108 15045
* 109 15075
* 110 15148
* 111 15190
* 112 15290
* 113 15340
* 114 15601
* 115 15695
* 116 15893 0.7.x
* 117 16037
* 118 16129
* 119 16242
* 120 16439
* 121 16694
* 122 16855
* 123 16909
* 124 16993
* 125 17113
* 126 17433
* 127 17439
* 128 18281
* 129 18292
* 130 18404
* 131 18481
* 132 18522
* 133 18674
* 134 18703
* 135 18719
* 136 18764
* 137 18912
* 138 18942 1.0.x
* 139 19346
* 140 19382
* 141 19799
* 142 20003
* 143 20048
* 144 20334
* 145 20376
* 146 20446
* 147 20621
* 148 20659
* 149 20832
* 150 20857
* 151 20918
* 152 21171
* 153 21263
* 154 21426
* 155 21453
* 156 21728
* 157 21862
* 158 21933
* 159 21962
* 160 21974 1.1.x
* 161 22567
* 162 22713
* 163 22767
* 164 23290
* 165 23304
* 166 23415
* 167 23504
* 168 23637
* 169 23816
* 170 23826
* 171 23835
* 172 23947
* 173 23967 1.2.0-RC1
* 174 23973 1.2.x
* 175 24136
* 176 24446
* 177 24619
* 178 24789
* 179 24810
* 180 24998 1.3.x
* 181 25012
* 182 25296
* 183 25363
* 184 25508
* 185 25620
* 186 25833
* 187 25899
* 188 26169 1.4.x
* 189 26450
* 190 26547
* 191 26646
* 192 26700
extern const uint16 SAVEGAME_VERSION = 191; ///< Current savegame version of OpenTTD.
extern const uint16 SAVEGAME_VERSION = 192; ///< Current savegame version of OpenTTD.
SavegameType _savegame_type; ///< type of savegame we are loading
uint32 _ttdp_version; ///< version of TTDP savegame (if applicable)
uint16 _sl_version; ///< the major savegame version identifier
byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
char _savegame_format[8]; ///< how to compress savegames
bool _do_autosave; ///< are we doing an autosave at the moment?
/** What are we currently doing? */
enum SaveLoadAction {
SLA_LOAD, ///< loading
SLA_SAVE, ///< saving
SLA_PTRS, ///< fixing pointers
SLA_NULL, ///< null all pointers (on loading error)
SLA_LOAD_CHECK, ///< partial loading into #_load_check_data
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;
this->bufe = this->buf + len;
return *this->bufp++;
* Get the size of the memory dump made so far.
* @return The size.
size_t GetSize() const
return this->read - (this->bufe - this->bufp);
/** 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.
/** Initialise our variables. */
MemoryDumper() : buf(NULL), bufe(NULL)
* 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++ = b;
Status change: