# saveload.cpp

Go to the documentation of this file.

    00001 /* $Id: saveload.cpp 18719 2010-01-04 18:33:43Z yexo $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00023 #include "../stdafx.h"
    00024 #include "../openttd.h"
    00025 #include "../debug.h"
    00026 #include "../station_base.h"
    00027 #include "../thread/thread.h"
    00028 #include "../town.h"
    00029 #include "../network/network.h"
    00030 #include "../variables.h"
    00031 #include "../window_func.h"
    00032 #include "../strings_func.h"
    00033 #include "../gfx_func.h"
    00034 #include "../core/endian_func.hpp"
    00035 #include "../vehicle_base.h"
    00036 #include "../company_func.h"
    00037 #include "../date_func.h"
    00038 #include "../autoreplace_base.h"
    00039 #include "../roadstop_base.h"
    00040 #include "../statusbar_gui.h"
    00041 #include "../fileio_func.h"
    00042 #include "../gamelog.h"
    00043 #include "../string_func.h"
    00044 #include "../engine_base.h"
    00045
    00046 #include "table/strings.h"
    00047
    00048 #include "saveload_internal.h"
    00049
    00050 extern const uint16 SAVEGAME_VERSION = 135;
    00051
    00052 SavegameType _savegame_type;
    00053
    00054 uint32 _ttdp_version;
    00055 uint16 _sl_version;
    00056 byte   _sl_minor_version;
    00057 char _savegame_format[8];
    00058
    00059 typedef void WriterProc(size_t len);
    00060 typedef size_t ReaderProc();
    00061
    00063 enum SaveLoadAction {
    00064   SLA_LOAD,
    00065   SLA_SAVE,
    00066   SLA_PTRS,
    00067   SLA_NULL,
    00068 };
    00069
    00070 enum NeedLength {
    00071   NL_NONE = 0,
    00072   NL_WANTLENGTH = 1,
    00073   NL_CALCLENGTH = 2,
    00074 };
    00075
    00077 struct SaveLoadParams {
    00078   SaveLoadAction action;
    00079   NeedLength need_length;
    00080   byte block_mode;
    00081   bool error;
    00082
    00083   size_t obj_len;
    00084   int array_index, last_array_index;
    00085
    00086   size_t offs_base;
    00087
    00088   WriterProc *write_bytes;
    00089   ReaderProc *read_bytes;
    00090
    00091   /* When saving/loading savegames, they are always saved to a temporary memory-place
    00092    * to be flushed to file (save) or to final place (load) when full. */
    00093   byte *bufp, *bufe;
    00094
    00095   /* these 3 may be used by compressor/decompressors. */
    00096   byte *buf;
    00097   byte *buf_ori;
    00098   uint bufsize;
    00099   FILE *fh;
    00100
    00101   void (*excpt_uninit)();
    00102   StringID error_str;
    00103   char *extra_msg;
    00104 };
    00105
    00106 /* these define the chunks */
    00107 extern const ChunkHandler _gamelog_chunk_handlers[];
    00108 extern const ChunkHandler _map_chunk_handlers[];
    00109 extern const ChunkHandler _misc_chunk_handlers[];
    00110 extern const ChunkHandler _name_chunk_handlers[];
    00111 extern const ChunkHandler _cheat_chunk_handlers[] ;
    00112 extern const ChunkHandler _setting_chunk_handlers[];
    00113 extern const ChunkHandler _company_chunk_handlers[];
    00114 extern const ChunkHandler _engine_chunk_handlers[];
    00115 extern const ChunkHandler _veh_chunk_handlers[];
    00116 extern const ChunkHandler _waypoint_chunk_handlers[];
    00117 extern const ChunkHandler _depot_chunk_handlers[];
    00118 extern const ChunkHandler _order_chunk_handlers[];
    00119 extern const ChunkHandler _town_chunk_handlers[];
    00120 extern const ChunkHandler _sign_chunk_handlers[];
    00121 extern const ChunkHandler _station_chunk_handlers[];
    00122 extern const ChunkHandler _industry_chunk_handlers[];
    00123 extern const ChunkHandler _economy_chunk_handlers[];
    00124 extern const ChunkHandler _subsidy_chunk_handlers[];
    00125 extern const ChunkHandler _ai_chunk_handlers[];
    00126 extern const ChunkHandler _animated_tile_chunk_handlers[];
    00127 extern const ChunkHandler _newgrf_chunk_handlers[];
    00128 extern const ChunkHandler _group_chunk_handlers[];
    00129 extern const ChunkHandler _cargopacket_chunk_handlers[];
    00130 extern const ChunkHandler _autoreplace_chunk_handlers[];
    00131 extern const ChunkHandler _labelmaps_chunk_handlers[];
    00132
    00133 static const ChunkHandler * const _chunk_handlers[] = {
    00134   _gamelog_chunk_handlers,
    00135   _map_chunk_handlers,
    00136   _misc_chunk_handlers,
    00137   _name_chunk_handlers,
    00138   _cheat_chunk_handlers,
    00139   _setting_chunk_handlers,
    00140   _veh_chunk_handlers,
    00141   _waypoint_chunk_handlers,
    00142   _depot_chunk_handlers,
    00143   _order_chunk_handlers,
    00144   _industry_chunk_handlers,
    00145   _economy_chunk_handlers,
    00146   _subsidy_chunk_handlers,
    00147   _engine_chunk_handlers,
    00148   _town_chunk_handlers,
    00149   _sign_chunk_handlers,
    00150   _station_chunk_handlers,
    00151   _company_chunk_handlers,
    00152   _ai_chunk_handlers,
    00153   _animated_tile_chunk_handlers,
    00154   _newgrf_chunk_handlers,
    00155   _group_chunk_handlers,
    00156   _cargopacket_chunk_handlers,
    00157   _autoreplace_chunk_handlers,
    00158   _labelmaps_chunk_handlers,
    00159   NULL,
    00160 };
    00161
    00166 #define FOR_ALL_CHUNK_HANDLERS(ch) \
    00167   for (const ChunkHandler * const *chsc = _chunk_handlers; *chsc != NULL; chsc++) \
    00168     for (const ChunkHandler *ch = *chsc; ch != NULL; ch = (ch->flags & CH_LAST) ? NULL : ch + 1)
    00169
    00170 static SaveLoadParams _sl;
    00171
    00173 static void SlNullPointers()
    00174 {
    00175   _sl.action = SLA_NULL;
    00176
    00177   DEBUG(sl, 1, "Nulling pointers");
    00178
    00179   FOR_ALL_CHUNK_HANDLERS(ch) {
    00180     if (ch->ptrs_proc != NULL) {
    00181       DEBUG(sl, 2, "Nulling pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
    00182       ch->ptrs_proc();
    00183     }
    00184   }
    00185
    00186   DEBUG(sl, 1, "All pointers nulled");
    00187
    00188   assert(_sl.action == SLA_NULL);
    00189 }
    00190
    00194 static void NORETURN SlError(StringID string, const char *extra_msg = NULL)
    00195 {
    00196   _sl.error_str = string;
    00197   free(_sl.extra_msg);
    00198   _sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg);
    00199   /* We have to NULL all pointers here; we might be in a state where
    00200    * the pointers are actually filled with indices, which means that
    00201    * when we access them during cleaning the pool dereferences of
    00202    * those indices will be made with segmentation faults as result. */
    00203   SlNullPointers();
    00204   throw std::exception();
    00205 }
    00206
    00207 typedef void (*AsyncSaveFinishProc)();
    00208 static AsyncSaveFinishProc _async_save_finish = NULL;
    00209 static ThreadObject *_save_thread;
    00210
    00214 static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
    00215 {
    00216   if (_exit_game) return;
    00217   while (_async_save_finish != NULL) CSleep(10);
    00218
    00219   _async_save_finish = proc;
    00220 }
    00221
    00225 void ProcessAsyncSaveFinish()
    00226 {
    00227   if (_async_save_finish == NULL) return;
    00228
    00229   _async_save_finish();
    00230
    00231   _async_save_finish = NULL;
    00232
    00233   if (_save_thread != NULL) {
    00234     _save_thread->Join();
    00235     delete _save_thread;
    00236     _save_thread = NULL;
    00237   }
    00238 }
    00239
    00243 static void SlReadFill()
    00244 {
    00245   size_t len = _sl.read_bytes();
    00246   if (len == 0) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected end of chunk");
    00247
    00248   _sl.bufp = _sl.buf;
    00249   _sl.bufe = _sl.buf + len;
    00250   _sl.offs_base += len;
    00251 }
    00252
    00253 static inline size_t SlGetOffs() {return _sl.offs_base - (_sl.bufe - _sl.bufp);}
    00254 static inline uint SlReadArrayLength();
    00255
    00260 static inline uint SlCalcConvMemLen(VarType conv)
    00261 {
    00262   static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0};
    00263   byte length = GB(conv, 4, 4);
    00264
    00265   switch (length << 4) {
    00266     case SLE_VAR_STRB:
    00267     case SLE_VAR_STRBQ:
    00268     case SLE_VAR_STR:
    00269     case SLE_VAR_STRQ:
    00270       return SlReadArrayLength();
    00271
    00272     default:
    00273       assert(length < lengthof(conv_mem_size));
    00274       return conv_mem_size[length];
    00275   }
    00276 }
    00277
    00282 static inline byte SlCalcConvFileLen(VarType conv)
    00283 {
    00284   static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2};
    00285   byte length = GB(conv, 0, 4);
    00286   assert(length < lengthof(conv_file_size));
    00287   return conv_file_size[length];
    00288 }
    00289
    00291 static inline size_t SlCalcRefLen() {return CheckSavegameVersion(69) ? 2 : 4;}
    00292
    00297 static void SlWriteFill()
    00298 {
    00299   /* flush the buffer to disk (the writer) */
    00300   if (_sl.bufp != NULL) {
    00301     uint len = _sl.bufp - _sl.buf;
    00302     _sl.offs_base += len;
    00303     if (len) _sl.write_bytes(len);
    00304   }
    00305
    00306   /* All the data from the buffer has been written away, rewind to the beginning
    00307    * to start reading in more data */
    00308   _sl.bufp = _sl.buf;
    00309   _sl.bufe = _sl.buf + _sl.bufsize;
    00310 }
    00311
    00316 static inline byte SlReadByteInternal()
    00317 {
    00318   if (_sl.bufp == _sl.bufe) SlReadFill();
    00319   return *_sl.bufp++;
    00320 }
    00321
    00323 byte SlReadByte() {return SlReadByteInternal();}
    00324
    00329 static inline void SlWriteByteInternal(byte b)
    00330 {
    00331   if (_sl.bufp == _sl.bufe) SlWriteFill();
    00332   *_sl.bufp++ = b;
    00333 }
    00334
    00336 void SlWriteByte(byte b) {SlWriteByteInternal(b);}
    00337
    00338 static inline int SlReadUint16()
    00339 {
    00340   int x = SlReadByte() << 8;
    00341   return x | SlReadByte();
    00342 }
    00343
    00344 static inline uint32 SlReadUint32()
    00345 {
    00346   uint32 x = SlReadUint16() << 16;
    00347   return x | SlReadUint16();
    00348 }
    00349
    00350 static inline uint64 SlReadUint64()
    00351 {
    00352   uint32 x = SlReadUint32();
    00353   uint32 y = SlReadUint32();
    00354   return (uint64)x << 32 | y;
    00355 }
    00356
    00357 static inline void SlWriteUint16(uint16 v)
    00358 {
    00359   SlWriteByte(GB(v, 8, 8));
    00360   SlWriteByte(GB(v, 0, 8));
    00361 }
    00362
    00363 static inline void SlWriteUint32(uint32 v)
    00364 {
    00365   SlWriteUint16(GB(v, 16, 16));
    00366   SlWriteUint16(GB(v,  0, 16));
    00367 }
    00368
    00369 static inline void SlWriteUint64(uint64 x)
    00370 {
    00371   SlWriteUint32((uint32)(x >> 32));
    00372   SlWriteUint32((uint32)x);
    00373 }
    00374
    00384 static uint SlReadSimpleGamma()
    00385 {
    00386   uint i = SlReadByte();
    00387   if (HasBit(i, 7)) {
    00388     i &= ~0x80;
    00389     if (HasBit(i, 6)) {
    00390       i &= ~0x40;
    00391       if (HasBit(i, 5)) {
    00392         i &= ~0x20;
    00393         if (HasBit(i, 4))
    00394           SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unsupported gamma");
    00395         i = (i << 8) | SlReadByte();
    00396       }
    00397       i = (i << 8) | SlReadByte();
    00398     }
    00399     i = (i << 8) | SlReadByte();
    00400   }
    00401   return i;
    00402 }
    00403
    00416 static void SlWriteSimpleGamma(size_t i)
    00417 {
    00418   if (i >= (1 << 7)) {
    00419     if (i >= (1 << 14)) {
    00420       if (i >= (1 << 21)) {
    00421         assert(i < (1 << 28));
    00422         SlWriteByte((byte)(0xE0 | (i >> 24)));
    00423         SlWriteByte((byte)(i >> 16));
    00424       } else {
    00425         SlWriteByte((byte)(0xC0 | (i >> 16)));
    00426       }
    00427       SlWriteByte((byte)(i >> 8));
    00428     } else {
    00429       SlWriteByte((byte)(0x80 | (i >> 8)));
    00430     }
    00431   }
    00432   SlWriteByte((byte)i);
    00433 }
    00434
    00436 static inline uint SlGetGammaLength(size_t i)
    00437 {
    00438   return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21));
    00439 }
    00440
    00441 static inline uint SlReadSparseIndex() {return SlReadSimpleGamma();}
    00442 static inline void SlWriteSparseIndex(uint index) {SlWriteSimpleGamma(index);}
    00443
    00444 static inline uint SlReadArrayLength() {return SlReadSimpleGamma();}
    00445 static inline void SlWriteArrayLength(size_t length) {SlWriteSimpleGamma(length);}
    00446 static inline uint SlGetArrayLength(size_t length) {return SlGetGammaLength(length);}
    00447
    00448 void SlSetArrayIndex(uint index)
    00449 {
    00450   _sl.need_length = NL_WANTLENGTH;
    00451   _sl.array_index = index;
    00452 }
    00453
    00454 static size_t _next_offs;
    00455
    00460 int SlIterateArray()
    00461 {
    00462   int index;
    00463
    00464   /* After reading in the whole array inside the loop
    00465    * we must have read in all the data, so we must be at end of current block. */
    00466   if (_next_offs != 0 && SlGetOffs() != _next_offs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
    00467
    00468   while (true) {
    00469     uint length = SlReadArrayLength();
    00470     if (length == 0) {
    00471       _next_offs = 0;
    00472       return -1;
    00473     }
    00474
    00475     _sl.obj_len = --length;
    00476     _next_offs = SlGetOffs() + length;
    00477
    00478     switch (_sl.block_mode) {
    00479       case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
    00480       case CH_ARRAY:        index = _sl.array_index++; break;
    00481       default:
    00482         DEBUG(sl, 0, "SlIterateArray error");
    00483         return -1; // error
    00484     }
    00485
    00486     if (length != 0) return index;
    00487   }
    00488 }
    00489
    00495 void SlSetLength(size_t length)
    00496 {
    00497   assert(_sl.action == SLA_SAVE);
    00498
    00499   switch (_sl.need_length) {
    00500     case NL_WANTLENGTH:
    00501       _sl.need_length = NL_NONE;
    00502       switch (_sl.block_mode) {
    00503         case CH_RIFF:
    00504           /* Ugly encoding of >16M RIFF chunks
    00505            * The lower 24 bits are normal
    00506            * The uppermost 4 bits are bits 24:27 */
    00507           assert(length < (1 << 28));
    00508           SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28)));
    00509           break;
    00510         case CH_ARRAY:
    00511           assert(_sl.last_array_index <= _sl.array_index);
    00512           while (++_sl.last_array_index <= _sl.array_index)
    00513             SlWriteArrayLength(1);
    00514           SlWriteArrayLength(length + 1);
    00515           break;
    00516         case CH_SPARSE_ARRAY:
    00517           SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index.
    00518           SlWriteSparseIndex(_sl.array_index);
    00519           break;
    00520         default: NOT_REACHED();
    00521       }
    00522       break;
    00523
    00524     case NL_CALCLENGTH:
    00525       _sl.obj_len += (int)length;
    00526       break;
    00527
    00528     default: NOT_REACHED();
    00529   }
    00530 }
    00531
    00538 static void SlCopyBytes(void *ptr, size_t length)
    00539 {
    00540   byte *p = (byte *)ptr;
    00541
    00542   switch (_sl.action) {
    00543     case SLA_LOAD:
    00544       for (; length != 0; length--) { *p++ = SlReadByteInternal(); }
    00545       break;
    00546     case SLA_SAVE:
    00547       for (; length != 0; length--) { SlWriteByteInternal(*p++); }
    00548       break;
    00549     default: NOT_REACHED();
    00550   }
    00551 }
    00552
    00557 static inline void SlSkipBytes(size_t length)
    00558 {
    00559   for (; length != 0; length--) SlReadByte();
    00560 }
    00561
    00562 /* Get the length of the current object */
    00563 size_t SlGetFieldLength() {return _sl.obj_len;}
    00564
    00570 int64 ReadValue(const void *ptr, VarType conv)
    00571 {
    00572   switch (GetVarMemType(conv)) {
    00573     case SLE_VAR_BL:  return (*(bool *)ptr != 0);
    00574     case SLE_VAR_I8:  return *(int8  *)ptr;
    00575     case SLE_VAR_U8:  return *(byte  *)ptr;
    00576     case SLE_VAR_I16: return *(int16 *)ptr;
    00577     case SLE_VAR_U16: return *(uint16*)ptr;
    00578     case SLE_VAR_I32: return *(int32 *)ptr;
    00579     case SLE_VAR_U32: return *(uint32*)ptr;
    00580     case SLE_VAR_I64: return *(int64 *)ptr;
    00581     case SLE_VAR_U64: return *(uint64*)ptr;
    00582     case SLE_VAR_NULL:return 0;
    00583     default: NOT_REACHED();
    00584   }
    00585 }
    00586
    00592 void WriteValue(void *ptr, VarType conv, int64 val)
    00593 {
    00594   switch (GetVarMemType(conv)) {
    00595     case SLE_VAR_BL:  *(bool  *)ptr = (val != 0);  break;
    00596     case SLE_VAR_I8:  *(int8  *)ptr = val; break;
    00597     case SLE_VAR_U8:  *(byte  *)ptr = val; break;
    00598     case SLE_VAR_I16: *(int16 *)ptr = val; break;
    00599     case SLE_VAR_U16: *(uint16*)ptr = val; break;
    00600     case SLE_VAR_I32: *(int32 *)ptr = val; break;
    00601     case SLE_VAR_U32: *(uint32*)ptr = val; break;
    00602     case SLE_VAR_I64: *(int64 *)ptr = val; break;
    00603     case SLE_VAR_U64: *(uint64*)ptr = val; break;
    00604     case SLE_VAR_NAME: *(char**)ptr = CopyFromOldName(val); break;
    00605     case SLE_VAR_NULL: break;
    00606     default: NOT_REACHED();
    00607   }
    00608 }
    00609
    00618 static void SlSaveLoadConv(void *ptr, VarType conv)
    00619 {
    00620   switch (_sl.action) {
    00621     case SLA_SAVE: {
    00622       int64 x = ReadValue(ptr, conv);
    00623
    00624       /* Write the value to the file and check if its value is in the desired range */
    00625       switch (GetVarFileType(conv)) {
    00626         case SLE_FILE_I8: assert(x >= -128 && x <= 127);     SlWriteByte(x);break;
    00627         case SLE_FILE_U8: assert(x >= 0 && x <= 255);        SlWriteByte(x);break;
    00628         case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
    00629         case SLE_FILE_STRINGID:
    00630         case SLE_FILE_U16:assert(x >= 0 && x <= 65535);      SlWriteUint16(x);break;
    00631         case SLE_FILE_I32:
    00632         case SLE_FILE_U32:                                   SlWriteUint32((uint32)x);break;
    00633         case SLE_FILE_I64:
    00634         case SLE_FILE_U64:                                   SlWriteUint64(x);break;
    00635         default: NOT_REACHED();
    00636       }
    00637       break;
    00638     }
    00639     case SLA_LOAD: {
    00640       int64 x;
    00641       /* Read a value from the file */
    00642       switch (GetVarFileType(conv)) {
    00643         case SLE_FILE_I8:  x = (int8  )SlReadByte();   break;
    00644         case SLE_FILE_U8:  x = (byte  )SlReadByte();   break;
    00645         case SLE_FILE_I16: x = (int16 )SlReadUint16(); break;
    00646         case SLE_FILE_U16: x = (uint16)SlReadUint16(); break;
    00647         case SLE_FILE_I32: x = (int32 )SlReadUint32(); break;
    00648         case SLE_FILE_U32: x = (uint32)SlReadUint32(); break;
    00649         case SLE_FILE_I64: x = (int64 )SlReadUint64(); break;
    00650         case SLE_FILE_U64: x = (uint64)SlReadUint64(); break;
    00651         case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break;
    00652         default: NOT_REACHED();
    00653       }
    00654
    00655       /* Write The value to the struct. These ARE endian safe. */
    00656       WriteValue(ptr, conv, x);
    00657       break;
    00658     }
    00659     case SLA_PTRS: break;
    00660     case SLA_NULL: break;
    00661     default: NOT_REACHED();
    00662   }
    00663 }
    00664
    00672 static inline size_t SlCalcNetStringLen(const char *ptr, size_t length)
    00673 {
    00674   if (ptr == NULL) return 0;
    00675   return min(strlen(ptr), length - 1);
    00676 }
    00677
    00685 static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv)
    00686 {
    00687   size_t len;
    00688   const char *str;
    00689
    00690   switch (GetVarMemType(conv)) {
    00691     default: NOT_REACHED();
    00692     case SLE_VAR_STR:
    00693     case SLE_VAR_STRQ:
    00694       str = *(const char**)ptr;
    00695       len = SIZE_MAX;
    00696       break;
    00697     case SLE_VAR_STRB:
    00698     case SLE_VAR_STRBQ:
    00699       str = (const char*)ptr;
    00700       len = length;
    00701       break;
    00702   }
    00703
    00704   len = SlCalcNetStringLen(str, len);
    00705   return len + SlGetArrayLength(len); // also include the length of the index
    00706 }
    00707
    00713 static void SlString(void *ptr, size_t length, VarType conv)
    00714 {
    00715   switch (_sl.action) {
    00716     case SLA_SAVE: {
    00717       size_t len;
    00718       switch (GetVarMemType(conv)) {
    00719         default: NOT_REACHED();
    00720         case SLE_VAR_STRB:
    00721         case SLE_VAR_STRBQ:
    00722           len = SlCalcNetStringLen((char *)ptr, length);
    00723           break;
    00724         case SLE_VAR_STR:
    00725         case SLE_VAR_STRQ:
    00726           ptr = *(char **)ptr;
    00727           len = SlCalcNetStringLen((char *)ptr, SIZE_MAX);
    00728           break;
    00729       }
    00730
    00731       SlWriteArrayLength(len);
    00732       SlCopyBytes(ptr, len);
    00733       break;
    00734     }
    00735     case SLA_LOAD: {
    00736       size_t len = SlReadArrayLength();
    00737
    00738       switch (GetVarMemType(conv)) {
    00739         default: NOT_REACHED();
    00740         case SLE_VAR_STRB:
    00741         case SLE_VAR_STRBQ:
    00742           if (len >= length) {
    00743             DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating");
    00744             SlCopyBytes(ptr, length);
    00745             SlSkipBytes(len - length);
    00746             len = length - 1;
    00747           } else {
    00748             SlCopyBytes(ptr, len);
    00749           }
    00750           break;
    00751         case SLE_VAR_STR:
    00752         case SLE_VAR_STRQ: // Malloc'd string, free previous incarnation, and allocate
    00753           free(*(char **)ptr);
    00754           if (len == 0) {
    00755             *(char **)ptr = NULL;
    00756           } else {
    00757             *(char **)ptr = MallocT<char>(len + 1); // terminating '\0'
    00758             ptr = *(char **)ptr;
    00759             SlCopyBytes(ptr, len);
    00760           }
    00761           break;
    00762       }
    00763
    00764       ((char *)ptr)[len] = '\0'; // properly terminate the string
    00765       str_validate((char *)ptr, (char *)ptr + len);
    00766       break;
    00767     }
    00768     case SLA_PTRS: break;
    00769     case SLA_NULL: break;
    00770     default: NOT_REACHED();
    00771   }
    00772 }
    00773
    00779 static inline size_t SlCalcArrayLen(size_t length, VarType conv)
    00780 {
    00781   return SlCalcConvFileLen(conv) * length;
    00782 }
    00783
    00790 void SlArray(void *array, size_t length, VarType conv)
    00791 {
    00792   if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
    00793
    00794   /* Automatically calculate the length? */
    00795   if (_sl.need_length != NL_NONE) {
    00796     SlSetLength(SlCalcArrayLen(length, conv));
    00797     /* Determine length only? */
    00798     if (_sl.need_length == NL_CALCLENGTH) return;
    00799   }
    00800
    00801   /* NOTICE - handle some buggy stuff, in really old versions everything was saved
    00802    * as a byte-type. So detect this, and adjust array size accordingly */
    00803   if (_sl.action != SLA_SAVE && _sl_version == 0) {
    00804     /* all arrays except difficulty settings */
    00805     if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
    00806         conv == SLE_INT32 || conv == SLE_UINT32) {
    00807       SlCopyBytes(array, length * SlCalcConvFileLen(conv));
    00808       return;
    00809     }
    00810     /* used for conversion of Money 32bit->64bit */
    00811     if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
    00812       for (uint i = 0; i < length; i++) {
    00813         ((int64*)array)[i] = (int32)BSWAP32(SlReadUint32());
    00814       }
    00815       return;
    00816     }
    00817   }
    00818
    00819   /* If the size of elements is 1 byte both in file and memory, no special
    00820    * conversion is needed, use specialized copy-copy function to speed up things */
    00821   if (conv == SLE_INT8 || conv == SLE_UINT8) {
    00822     SlCopyBytes(array, length);
    00823   } else {
    00824     byte *a = (byte*)array;
    00825     byte mem_size = SlCalcConvMemLen(conv);
    00826
    00827     for (; length != 0; length --) {
    00828       SlSaveLoadConv(a, conv);
    00829       a += mem_size; // get size
    00830     }
    00831   }
    00832 }
    00833
    00834
    00835 static size_t ReferenceToInt(const void *obj, SLRefType rt);
    00836 static void *IntToReference(size_t index, SLRefType rt);
    00837
    00838
    00843 static inline size_t SlCalcListLen(const void *list)
    00844 {
    00845   std::list<void *> *l = (std::list<void *> *) list;
    00846
    00847   int type_size = CheckSavegameVersion(69) ? 2 : 4;
    00848   /* Each entry is saved as type_size bytes, plus type_size bytes are used for the length
    00849    * of the list */
    00850   return l->size() * type_size + type_size;
    00851 }
    00852
    00853
    00859 static void SlList(void *list, SLRefType conv)
    00860 {
    00861   /* Automatically calculate the length? */
    00862   if (_sl.need_length != NL_NONE) {
    00863     SlSetLength(SlCalcListLen(list));
    00864     /* Determine length only? */
    00865     if (_sl.need_length == NL_CALCLENGTH) return;
    00866   }
    00867
    00868   typedef std::list<void *> PtrList;
    00869   PtrList *l = (PtrList *)list;
    00870
    00871   switch (_sl.action) {
    00872     case SLA_SAVE: {
    00873       SlWriteUint32((uint32)l->size());
    00874
    00875       PtrList::iterator iter;
    00876       for (iter = l->begin(); iter != l->end(); ++iter) {
    00877         void *ptr = *iter;
    00878         SlWriteUint32((uint32)ReferenceToInt(ptr, conv));
    00879       }
    00880       break;
    00881     }
    00882     case SLA_LOAD: {
    00883       size_t length = CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32();
    00884
    00885       /* Load each reference and push to the end of the list */
    00886       for (size_t i = 0; i < length; i++) {
    00887         size_t data = CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32();
    00888         l->push_back((void *)data);
    00889       }
    00890       break;
    00891     }
    00892     case SLA_PTRS: {
    00893       PtrList temp = *l;
    00894
    00895       l->clear();
    00896       PtrList::iterator iter;
    00897       for (iter = temp.begin(); iter != temp.end(); ++iter) {
    00898         void *ptr = IntToReference((size_t)*iter, conv);
    00899         l->push_back(ptr);
    00900       }
    00901       break;
    00902     }
    00903     case SLA_NULL:
    00904       l->clear();
    00905       break;
    00906     default: NOT_REACHED();
    00907   }
    00908 }
    00909
    00910
    00912 static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
    00913 {
    00914   if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false;
    00915   if (sld->conv & SLF_SAVE_NO) return false;
    00916
    00917   return true;
    00918 }
    00919
    00923 static inline bool SlSkipVariableOnLoad(const SaveLoad *sld)
    00924 {
    00925   if ((sld->conv & SLF_NETWORK_NO) && _sl.action != SLA_SAVE && _networking && !_network_server) {
    00926     SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length);
    00927     return true;
    00928   }
    00929
    00930   return false;
    00931 }
    00932
    00939 size_t SlCalcObjLength(const void *object, const SaveLoad *sld)
    00940 {
    00941   size_t length = 0;
    00942
    00943   /* Need to determine the length and write a length tag. */
    00944   for (; sld->cmd != SL_END; sld++) {
    00945     length += SlCalcObjMemberLength(object, sld);
    00946   }
    00947   return length;
    00948 }
    00949
    00950 size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
    00951 {
    00952   assert(_sl.action == SLA_SAVE);
    00953
    00954   switch (sld->cmd) {
    00955     case SL_VAR:
    00956     case SL_REF:
    00957     case SL_ARR:
    00958     case SL_STR:
    00959     case SL_LST:
    00960       /* CONDITIONAL saveload types depend on the savegame version */
    00961       if (!SlIsObjectValidInSavegame(sld)) break;
    00962
    00963       switch (sld->cmd) {
    00964         case SL_VAR: return SlCalcConvFileLen(sld->conv);
    00965         case SL_REF: return SlCalcRefLen();
    00966         case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv);
    00967         case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv);
    00968         case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld));
    00969         default: NOT_REACHED();
    00970       }
    00971       break;
    00972     case SL_WRITEBYTE: return 1; // a byte is logically of size 1
    00973     case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END));
    00974     case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription());
    00975     default: NOT_REACHED();
    00976   }
    00977   return 0;
    00978 }
    00979
    00980
    00981 bool SlObjectMember(void *ptr, const SaveLoad *sld)
    00982 {
    00983   VarType conv = GB(sld->conv, 0, 8);
    00984   switch (sld->cmd) {
    00985     case SL_VAR:
    00986     case SL_REF:
    00987     case SL_ARR:
    00988     case SL_STR:
    00989     case SL_LST:
    00990       /* CONDITIONAL saveload types depend on the savegame version */
    00991       if (!SlIsObjectValidInSavegame(sld)) return false;
    00992       if (SlSkipVariableOnLoad(sld)) return false;
    00993
    00994       switch (sld->cmd) {
    00995         case SL_VAR: SlSaveLoadConv(ptr, conv); break;
    00996         case SL_REF: // Reference variable, translate
    00997           switch (_sl.action) {
    00998             case SLA_SAVE:
    00999               SlWriteUint32((uint32)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
    01000               break;
    01001             case SLA_LOAD:
    01002               *(size_t *)ptr = CheckSavegameVersion(69) ? SlReadUint16() : SlReadUint32();
    01003               break;
    01004             case SLA_PTRS:
    01005               *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
    01006               break;
    01007             case SLA_NULL:
    01008               *(void **)ptr = NULL;
    01009               break;
    01010             default: NOT_REACHED();
    01011           }
    01012           break;
    01013         case SL_ARR: SlArray(ptr, sld->length, conv); break;
    01014         case SL_STR: SlString(ptr, sld->length, conv); break;
    01015         case SL_LST: SlList(ptr, (SLRefType)conv); break;
    01016         default: NOT_REACHED();
    01017       }
    01018       break;
    01019
    01020     /* SL_WRITEBYTE translates a value of a variable to another one upon
    01021      * saving or loading.
    01022      * XXX - variable renaming abuse
    01023      * game_value: the value of the variable ingame is abused by sld->version_from
    01024      * file_value: the value of the variable in the savegame is abused by sld->version_to */
    01025     case SL_WRITEBYTE:
    01026       switch (_sl.action) {
    01027         case SLA_SAVE: SlWriteByte(sld->version_to); break;
    01028         case SLA_LOAD: *(byte *)ptr = sld->version_from; break;
    01029         case SLA_PTRS: break;
    01030         case SLA_NULL: break;
    01031         default: NOT_REACHED();
    01032       }
    01033       break;
    01034
    01035     /* SL_VEH_INCLUDE loads common code for vehicles */
    01036     case SL_VEH_INCLUDE:
    01037       SlObject(ptr, GetVehicleDescription(VEH_END));
    01038       break;
    01039
    01040     case SL_ST_INCLUDE:
    01041       SlObject(ptr, GetBaseStationDescription());
    01042       break;
    01043
    01044     default: NOT_REACHED();
    01045   }
    01046   return true;
    01047 }
    01048
    01054 void SlObject(void *object, const SaveLoad *sld)
    01055 {
    01056   /* Automatically calculate the length? */
    01057   if (_sl.need_length != NL_NONE) {
    01058     SlSetLength(SlCalcObjLength(object, sld));
    01059     if (_sl.need_length == NL_CALCLENGTH) return;
    01060   }
    01061
    01062   for (; sld->cmd != SL_END; sld++) {
    01063     void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld);
    01064     SlObjectMember(ptr, sld);
    01065   }
    01066 }
    01067
    01072 void SlGlobList(const SaveLoadGlobVarList *sldg)
    01073 {
    01074   SlObject(NULL, (const SaveLoad*)sldg);
    01075 }
    01076
    01082 void SlAutolength(AutolengthProc *proc, void *arg)
    01083 {
    01084   size_t offs;
    01085
    01086   assert(_sl.action == SLA_SAVE);
    01087
    01088   /* Tell it to calculate the length */
    01089   _sl.need_length = NL_CALCLENGTH;
    01090   _sl.obj_len = 0;
    01091   proc(arg);
    01092
    01093   /* Setup length */
    01094   _sl.need_length = NL_WANTLENGTH;
    01095   SlSetLength(_sl.obj_len);
    01096
    01097   offs = SlGetOffs() + _sl.obj_len;
    01098
    01099   /* And write the stuff */
    01100   proc(arg);
    01101
    01102   if (offs != SlGetOffs()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
    01103 }
    01104
    01109 static void SlLoadChunk(const ChunkHandler *ch)
    01110 {
    01111   byte m = SlReadByte();
    01112   size_t len;
    01113   size_t endoffs;
    01114
    01115   _sl.block_mode = m;
    01116   _sl.obj_len = 0;
    01117
    01118   switch (m) {
    01119     case CH_ARRAY:
    01120       _sl.array_index = 0;
    01121       ch->load_proc();
    01122       break;
    01123     case CH_SPARSE_ARRAY:
    01124       ch->load_proc();
    01125       break;
    01126     default:
    01127       if ((m & 0xF) == CH_RIFF) {
    01128         /* Read length */
    01129         len = (SlReadByte() << 16) | ((m >> 4) << 24);
    01130         len += SlReadUint16();
    01131         _sl.obj_len = len;
    01132         endoffs = SlGetOffs() + len;
    01133         ch->load_proc();
    01134         if (SlGetOffs() != endoffs) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk size");
    01135       } else {
    01136         SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Invalid chunk type");
    01137       }
    01138       break;
    01139   }
    01140 }
    01141
    01142 /* Stub Chunk handlers to only calculate length and do nothing else */
    01143 static ChunkSaveLoadProc *_tmp_proc_1;
    01144 static inline void SlStubSaveProc2(void *arg) {_tmp_proc_1();}
    01145 static void SlStubSaveProc() {SlAutolength(SlStubSaveProc2, NULL);}
    01146
    01151 static void SlSaveChunk(const ChunkHandler *ch)
    01152 {
    01153   ChunkSaveLoadProc *proc = ch->save_proc;
    01154
    01155   /* Don't save any chunk information if there is no save handler. */
    01156   if (proc == NULL) return;
    01157
    01158   SlWriteUint32(ch->id);
    01159   DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
    01160
    01161   if (ch->flags & CH_AUTO_LENGTH) {
    01162     /* Need to calculate the length. Solve that by calling SlAutoLength in the save_proc. */
    01163     _tmp_proc_1 = proc;
    01164     proc = SlStubSaveProc;
    01165   }
    01166
    01167   _sl.block_mode = ch->flags & CH_TYPE_MASK;
    01168   switch (ch->flags & CH_TYPE_MASK) {
    01169     case CH_RIFF:
    01170       _sl.need_length = NL_WANTLENGTH;
    01171       proc();
    01172       break;
    01173     case CH_ARRAY:
    01174       _sl.last_array_index = 0;
    01175       SlWriteByte(CH_ARRAY);
    01176       proc();
    01177       SlWriteArrayLength(0); // Terminate arrays
    01178       break;
    01179     case CH_SPARSE_ARRAY:
    01180       SlWriteByte(CH_SPARSE_ARRAY);
    01181       proc();
    01182       SlWriteArrayLength(0); // Terminate arrays
    01183       break;
    01184     default: NOT_REACHED();
    01185   }
    01186 }
    01187
    01189 static void SlSaveChunks()
    01190 {
    01191   FOR_ALL_CHUNK_HANDLERS(ch) {
    01192     SlSaveChunk(ch);
    01193   }
    01194
    01195   /* Terminator */
    01196   SlWriteUint32(0);
    01197 }
    01198
    01204 static const ChunkHandler *SlFindChunkHandler(uint32 id)
    01205 {
    01206   FOR_ALL_CHUNK_HANDLERS(ch) if (ch->id == id) return ch;
    01207   return NULL;
    01208 }
    01209
    01211 static void SlLoadChunks()
    01212 {
    01213   uint32 id;
    01214   const ChunkHandler *ch;
    01215
    01216   for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
    01217     DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
    01218
    01219     ch = SlFindChunkHandler(id);
    01220     if (ch == NULL) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unknown chunk type");
    01221     SlLoadChunk(ch);
    01222   }
    01223 }
    01224
    01226 static void SlFixPointers()
    01227 {
    01228   _sl.action = SLA_PTRS;
    01229
    01230   DEBUG(sl, 1, "Fixing pointers");
    01231
    01232   FOR_ALL_CHUNK_HANDLERS(ch) {
    01233     if (ch->ptrs_proc != NULL) {
    01234       DEBUG(sl, 2, "Fixing pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
    01235       ch->ptrs_proc();
    01236     }
    01237   }
    01238
    01239   DEBUG(sl, 1, "All pointers fixed");
    01240
    01241   assert(_sl.action == SLA_PTRS);
    01242 }
    01243
    01244 /*******************************************
    01245  ********** START OF LZO CODE **************
    01246  *******************************************/
    01247 #define LZO_SIZE 8192
    01248
    01249 #include "../3rdparty/minilzo/minilzo.h"
    01250
    01251 static size_t ReadLZO()
    01252 {
    01253   byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8];
    01254   uint32 tmp[2];
    01255   uint32 size;
    01256   lzo_uint len;
    01257
    01258   /* Read header*/
    01259   if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
    01260
    01261   /* Check if size is bad */
    01262   ((uint32*)out)[0] = size = tmp[1];
    01263
    01264   if (_sl_version != 0) {
    01265     tmp[0] = TO_BE32(tmp[0]);
    01266     size = TO_BE32(size);
    01267   }
    01268
    01269   if (size >= sizeof(out)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Inconsistent size");
    01270
    01271   /* Read block */
    01272   if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
    01273
    01274   /* Verify checksum */
    01275   if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Bad checksum");
    01276
    01277   /* Decompress */
    01278   lzo1x_decompress(out + sizeof(uint32) * 1, size, _sl.buf, &len, NULL);
    01279   return len;
    01280 }
    01281
    01282 /* p contains the pointer to the buffer, len contains the pointer to the length.
    01283  * len bytes will be written, p and l will be updated to reflect the next buffer. */
    01284 static void WriteLZO(size_t size)
    01285 {
    01286   byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8];
    01287   byte wrkmem[sizeof(byte*) * 4096];
    01288   lzo_uint outlen;
    01289
    01290   lzo1x_1_compress(_sl.buf, (lzo_uint)size, out + sizeof(uint32) * 2, &outlen, wrkmem);
    01291   ((uint32*)out)[1] = TO_BE32((uint32)outlen);
    01292   ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32)));
    01293   if (fwrite(out, outlen + sizeof(uint32) * 2, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
    01294 }
    01295
    01296 static bool InitLZO()
    01297 {
    01298   _sl.bufsize = LZO_SIZE;
    01299   _sl.buf = _sl.buf_ori = MallocT<byte>(LZO_SIZE);
    01300   return true;
    01301 }
    01302
    01303 static void UninitLZO()
    01304 {
    01305   free(_sl.buf_ori);
    01306 }
    01307
    01308 /*********************************************
    01309  ******** START OF NOCOMP CODE (uncompressed)*
    01310  *********************************************/
    01311 static size_t ReadNoComp()
    01312 {
    01313   return fread(_sl.buf, 1, LZO_SIZE, _sl.fh);
    01314 }
    01315
    01316 static void WriteNoComp(size_t size)
    01317 {
    01318   if (fwrite(_sl.buf, 1, size, _sl.fh) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
    01319 }
    01320
    01321 static bool InitNoComp()
    01322 {
    01323   _sl.bufsize = LZO_SIZE;
    01324   _sl.buf = _sl.buf_ori = MallocT<byte>(LZO_SIZE);
    01325   return true;
    01326 }
    01327
    01328 static void UninitNoComp()
    01329 {
    01330   free(_sl.buf_ori);
    01331 }
    01332
    01333 /********************************************
    01334  ********** START OF MEMORY CODE (in ram)****
    01335  ********************************************/
    01336
    01337 #include "../gui.h"
    01338
    01339 struct ThreadedSave {
    01340   uint count;
    01341   byte ff_state;
    01342   bool saveinprogress;
    01343   CursorID cursor;
    01344 };
    01345
    01347 static const int MEMORY_CHUNK_SIZE = 128 * 1024;
    01349 static AutoFreeSmallVector<byte *, 16> _memory_savegame;
    01350
    01351 static ThreadedSave _ts;
    01352
    01353 static void WriteMem(size_t size)
    01354 {
    01355   _ts.count += (uint)size;
    01356
    01357   _sl.buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
    01358   *_memory_savegame.Append() = _sl.buf;
    01359 }
    01360
    01361 static void UnInitMem()
    01362 {
    01363   _memory_savegame.Clear();
    01364 }
    01365
    01366 static bool InitMem()
    01367 {
    01368   _ts.count = 0;
    01369   _sl.bufsize = MEMORY_CHUNK_SIZE;
    01370
    01371   UnInitMem();
    01372   WriteMem(0);
    01373   return true;
    01374 }
    01375
    01376 /********************************************
    01377  ********** START OF ZLIB CODE **************
    01378  ********************************************/
    01379
    01380 #if defined(WITH_ZLIB)
    01381 #include <zlib.h>
    01382
    01383 static z_stream _z;
    01384
    01385 static bool InitReadZlib()
    01386 {
    01387   memset(&_z, 0, sizeof(_z));
    01388   if (inflateInit(&_z) != Z_OK) return false;
    01389
    01390   _sl.bufsize = 4096;
    01391   _sl.buf = _sl.buf_ori = MallocT<byte>(4096 + 4096); // also contains fread buffer
    01392   return true;
    01393 }
    01394
    01395 static size_t ReadZlib()
    01396 {
    01397   int r;
    01398
    01399   _z.next_out = _sl.buf;
    01400   _z.avail_out = 4096;
    01401
    01402   do {
    01403     /* read more bytes from the file? */
    01404     if (_z.avail_in == 0) {
    01405       _z.avail_in = (uint)fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh);
    01406     }
    01407
    01408     /* inflate the data */
    01409     r = inflate(&_z, 0);
    01410     if (r == Z_STREAM_END)
    01411       break;
    01412
    01413     if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
    01414   } while (_z.avail_out);
    01415
    01416   return 4096 - _z.avail_out;
    01417 }
    01418
    01419 static void UninitReadZlib()
    01420 {
    01421   inflateEnd(&_z);
    01422   free(_sl.buf_ori);
    01423 }
    01424
    01425 static bool InitWriteZlib()
    01426 {
    01427   memset(&_z, 0, sizeof(_z));
    01428   if (deflateInit(&_z, 6) != Z_OK) return false;
    01429
    01430   _sl.bufsize = 4096;
    01431   _sl.buf = _sl.buf_ori = MallocT<byte>(4096);
    01432   return true;
    01433 }
    01434
    01435 static void WriteZlibLoop(z_streamp z, byte *p, size_t len, int mode)
    01436 {
    01437   byte buf[1024]; // output buffer
    01438   int r;
    01439   uint n;
    01440   z->next_in = p;
    01441   z->avail_in = (uInt)len;
    01442   do {
    01443     z->next_out = buf;
    01444     z->avail_out = sizeof(buf);
    01445
    01453     r = deflate(z, mode);
    01454
    01455     /* bytes were emitted? */
    01456     if ((n = sizeof(buf) - z->avail_out) != 0) {
    01457       if (fwrite(buf, n, 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
    01458     }
    01459     if (r == Z_STREAM_END)
    01460       break;
    01461     if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
    01462   } while (z->avail_in || !z->avail_out);
    01463 }
    01464
    01465 static void WriteZlib(size_t len)
    01466 {
    01467   WriteZlibLoop(&_z, _sl.buf, len, 0);
    01468 }
    01469
    01470 static void UninitWriteZlib()
    01471 {
    01472   /* flush any pending output. */
    01473   if (_sl.fh) WriteZlibLoop(&_z, NULL, 0, Z_FINISH);
    01474   deflateEnd(&_z);
    01475   free(_sl.buf_ori);
    01476 }
    01477
    01478 #endif /* WITH_ZLIB */
    01479
    01480 /*******************************************
    01481  ************* END OF CODE *****************
    01482  *******************************************/
    01483
    01494 static size_t ReferenceToInt(const void *obj, SLRefType rt)
    01495 {
    01496   assert(_sl.action == SLA_SAVE);
    01497
    01498   if (obj == NULL) return 0;
    01499
    01500   switch (rt) {
    01501     case REF_VEHICLE_OLD: // Old vehicles we save as new onces
    01502     case REF_VEHICLE:   return ((const  Vehicle*)obj)->index + 1;
    01503     case REF_STATION:   return ((const  Station*)obj)->index + 1;
    01504     case REF_TOWN:      return ((const     Town*)obj)->index + 1;
    01505     case REF_ORDER:     return ((const    Order*)obj)->index + 1;
    01506     case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
    01507     case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
    01508     case REF_CARGO_PACKET:  return ((const CargoPacket*)obj)->index + 1;
    01509     case REF_ORDERLIST:     return ((const   OrderList*)obj)->index + 1;
    01510     default: NOT_REACHED();
    01511   }
    01512 }
    01513
    01524 static void *IntToReference(size_t index, SLRefType rt)
    01525 {
    01526   assert_compile(sizeof(size_t) <= sizeof(void *));
    01527
    01528   assert(_sl.action == SLA_PTRS);
    01529
    01530   /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE,
    01531    * and should be loaded like that */
    01532   if (rt == REF_VEHICLE_OLD && !CheckSavegameVersionOldStyle(4, 4)) {
    01533     rt = REF_VEHICLE;
    01534   }
    01535
    01536   /* No need to look up NULL pointers, just return immediately */
    01537   if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return NULL;
    01538
    01539   /* Correct index. Old vehicles were saved differently:
    01540    * invalid vehicle was 0xFFFF, now we use 0x0000 for everything invalid. */
    01541   if (rt != REF_VEHICLE_OLD) index--;
    01542
    01543   switch (rt) {
    01544     case REF_ORDERLIST:
    01545       if (OrderList::IsValidID(index)) return OrderList::Get(index);
    01546       SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid OrderList");
    01547
    01548     case REF_ORDER:
    01549       if (Order::IsValidID(index)) return Order::Get(index);
    01550       /* in old versions, invalid order was used to mark end of order list */
    01551       if (CheckSavegameVersionOldStyle(5, 2)) return NULL;
    01552       SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Order");
    01553
    01554     case REF_VEHICLE_OLD:
    01555     case REF_VEHICLE:
    01556       if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
    01557       SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Vehicle");
    01558
    01559     case REF_STATION:
    01560       if (Station::IsValidID(index)) return Station::Get(index);
    01561       SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Station");
    01562
    01563     case REF_TOWN:
    01564       if (Town::IsValidID(index)) return Town::Get(index);
    01565       SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid Town");
    01566
    01567     case REF_ROADSTOPS:
    01568       if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
    01569       SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid RoadStop");
    01570
    01571     case REF_ENGINE_RENEWS:
    01572       if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
    01573       SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid EngineRenew");
    01574
    01575     case REF_CARGO_PACKET:
    01576       if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
    01577       SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Referencing invalid CargoPacket");
    01578
    01579     default: NOT_REACHED();
    01580   }
    01581 }
    01582
    01584 struct SaveLoadFormat {
    01585   const char *name;
    01586   uint32 tag;
    01587
    01588   bool (*init_read)();
    01589   ReaderProc *reader;
    01590   void (*uninit_read)();
    01591
    01592   bool (*init_write)();
    01593   WriterProc *writer;
    01594   void (*uninit_write)();
    01595 };
    01596
    01597 static const SaveLoadFormat _saveload_formats[] = {
    01598   {"memory", 0,                NULL,         NULL,       NULL,           InitMem,       WriteMem,    UnInitMem},
    01599   {"lzo",    TO_BE32X('OTTD'), InitLZO,      ReadLZO,    UninitLZO,      InitLZO,       WriteLZO,    UninitLZO},
    01600   {"none",   TO_BE32X('OTTN'), InitNoComp,   ReadNoComp, UninitNoComp,   InitNoComp,    WriteNoComp, UninitNoComp},
    01601 #if defined(WITH_ZLIB)
    01602   {"zlib",   TO_BE32X('OTTZ'), InitReadZlib, ReadZlib,   UninitReadZlib, InitWriteZlib, WriteZlib,   UninitWriteZlib},
    01603 #else
    01604   {"zlib",   TO_BE32X('OTTZ'), NULL,         NULL,       NULL,           NULL,          NULL,        NULL},
    01605 #endif
    01606 };
    01607
    01614 static const SaveLoadFormat *GetSavegameFormat(const char *s)
    01615 {
    01616   const SaveLoadFormat *def = endof(_saveload_formats) - 1;
    01617
    01618   /* find default savegame format, the highest one with which files can be written */
    01619   while (!def->init_write) def--;
    01620
    01621   if (s != NULL && s[0] != '\0') {
    01622     const SaveLoadFormat *slf;
    01623     for (slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) {
    01624       if (slf->init_write != NULL && strcmp(s, slf->name) == 0)
    01625         return slf;
    01626     }
    01627
    01628     ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
    01629   }
    01630   return def;
    01631 }
    01632
    01633 /* actual loader/saver function */
    01634 void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
    01635 extern bool AfterLoadGame();
    01636 extern bool LoadOldSaveGame(const char *file);
    01637
    01639 static inline SaveOrLoadResult AbortSaveLoad()
    01640 {
    01641   if (_sl.fh != NULL) fclose(_sl.fh);
    01642
    01643   _sl.fh = NULL;
    01644   return SL_ERROR;
    01645 }
    01646
    01650 static void SaveFileStart()
    01651 {
    01652   _ts.ff_state = _fast_forward;
    01653   _fast_forward = 0;
    01654   if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
    01655
    01656   InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START);
    01657   _ts.saveinprogress = true;
    01658 }
    01659
    01662 static void SaveFileDone()
    01663 {
    01664   if (_game_mode != GM_MENU) _fast_forward = _ts.ff_state;
    01665   if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
    01666
    01667   InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH);
    01668   _ts.saveinprogress = false;
    01669 }
    01670
    01672 void SetSaveLoadError(StringID str)
    01673 {
    01674   _sl.error_str = str;
    01675 }
    01676
    01678 const char *GetSaveLoadErrorString()
    01679 {
    01680   SetDParam(0, _sl.error_str);
    01681   SetDParamStr(1, _sl.extra_msg);
    01682
    01683   static char err_str[512];
    01684   GetString(err_str, _sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED, lastof(err_str));
    01685   return err_str;
    01686 }
    01687
    01689 static void SaveFileError()
    01690 {
    01691   SetDParamStr(0, GetSaveLoadErrorString());
    01692   ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, 0, 0);
    01693   SaveFileDone();
    01694 }
    01695
    01699 static SaveOrLoadResult SaveFileToDisk(bool threaded)
    01700 {
    01701   const SaveLoadFormat *fmt;
    01702   uint32 hdr[2];
    01703
    01704   _sl.excpt_uninit = NULL;
    01705   try {
    01706     fmt = GetSavegameFormat(_savegame_format);
    01707
    01708     /* We have written our stuff to memory, now write it to file! */
    01709     hdr[0] = fmt->tag;
    01710     hdr[1] = TO_BE32(SAVEGAME_VERSION << 16);
    01711     if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
    01712
    01713     if (!fmt->init_write()) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
    01714
    01715     {
    01716       uint i;
    01717
    01718       if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
    01719       for (i = 0; i != _memory_savegame.Length() - 1; i++) {
    01720         _sl.buf = _memory_savegame[i];
    01721         fmt->writer(MEMORY_CHUNK_SIZE);
    01722       }
    01723
    01724       /* The last block is (almost) always not fully filled, so only write away
    01725        * as much data as it is in there */
    01726       _sl.buf = _memory_savegame[i];
    01727       fmt->writer(_ts.count % MEMORY_CHUNK_SIZE);
    01728     }
    01729
    01730     fmt->uninit_write();
    01731     if (_ts.count != _sl.offs_base) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, "Unexpected size of chunk");
    01732     GetSavegameFormat("memory")->uninit_write(); // clean the memorypool
    01733     fclose(_sl.fh);
    01734
    01735     if (threaded) SetAsyncSaveFinish(SaveFileDone);
    01736
    01737     return SL_OK;
    01738   }
    01739   catch (...) {
    01740     AbortSaveLoad();
    01741     if (_sl.excpt_uninit != NULL) _sl.excpt_uninit();
    01742
    01743     /* Skip the "colour" character */
    01744     DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
    01745
    01746     if (threaded) {
    01747       SetAsyncSaveFinish(SaveFileError);
    01748     } else {
    01749       SaveFileError();
    01750     }
    01751     return SL_ERROR;
    01752   }
    01753 }
    01754
    01755 static void SaveFileToDiskThread(void *arg)
    01756 {
    01757   SaveFileToDisk(true);
    01758 }
    01759
    01760 void WaitTillSaved()
    01761 {
    01762   if (_save_thread == NULL) return;
    01763
    01764   _save_thread->Join();
    01765   delete _save_thread;
    01766   _save_thread = NULL;
    01767 }
    01768
    01778 SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, bool threaded)
    01779 {
    01780   uint32 hdr[2];
    01781   const SaveLoadFormat *fmt;
    01782
    01783   /* An instance of saving is already active, so don't go saving again */
    01784   if (_ts.saveinprogress && mode == SL_SAVE) {
    01785     /* if not an autosave, but a user action, show error message */
    01786     if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, 0, 0);
    01787     return SL_OK;
    01788   }
    01789   WaitTillSaved();
    01790
    01791   _next_offs = 0;
    01792
    01793   /* Load a TTDLX or TTDPatch game */
    01794   if (mode == SL_OLD_LOAD) {
    01795     _engine_mngr.ResetToDefaultMapping();
    01796     InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
    01797     GamelogReset();
    01798     if (!LoadOldSaveGame(filename)) return SL_REINIT;
    01799     _sl_version = 0;
    01800     _sl_minor_version = 0;
    01801     GamelogStartAction(GLAT_LOAD);
    01802     if (!AfterLoadGame()) {
    01803       GamelogStopAction();
    01804       return SL_REINIT;
    01805     }
    01806     GamelogStopAction();
    01807     return SL_OK;
    01808   }
    01809
    01810   _sl.excpt_uninit = NULL;
    01811   try {
    01812     _sl.fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
    01813
    01814     /* Make it a little easier to load savegames from the console */
    01815     if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", SAVE_DIR);
    01816     if (_sl.fh == NULL && mode == SL_LOAD) _sl.fh = FioFOpenFile(filename, "rb", BASE_DIR);
    01817
    01818     if (_sl.fh == NULL) {
    01819       SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
    01820     }
    01821
    01822     _sl.bufe = _sl.bufp = NULL;
    01823     _sl.offs_base = 0;
    01824     _sl.action = (mode != 0) ? SLA_SAVE : SLA_LOAD;
    01825
    01826     /* General tactic is to first save the game to memory, then use an available writer
    01827      * to write it to file, either in threaded mode if possible, or single-threaded */
    01828     if (mode == SL_SAVE) { // SAVE game
    01829       DEBUG(desync, 1, "save: %s\n", filename);
    01830       fmt = GetSavegameFormat("memory"); // write to memory
    01831
    01832       _sl.write_bytes = fmt->writer;
    01833       _sl.excpt_uninit = fmt->uninit_write;
    01834       if (!fmt->init_write()) {
    01835         DEBUG(sl, 0, "Initializing writer '%s' failed.", fmt->name);
    01836         return AbortSaveLoad();
    01837       }
    01838
    01839       _sl_version = SAVEGAME_VERSION;
    01840
    01841       SaveViewportBeforeSaveGame();
    01842       SlSaveChunks();
    01843       SlWriteFill(); // flush the save buffer
    01844
    01845       SaveFileStart();
    01846       if (_network_server || !_settings_client.gui.threaded_saves) threaded = false;
    01847       if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread)) {
    01848         if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
    01849
    01850         SaveOrLoadResult result = SaveFileToDisk(false);
    01851         SaveFileDone();
    01852
    01853         return result;
    01854       }
    01855     } else { // LOAD game
    01856       assert(mode == SL_LOAD);
    01857       DEBUG(desync, 1, "load: %s\n", filename);
    01858
    01859       /* Can't fseek to 0 as in tar files that is not correct */
    01860       long pos = ftell(_sl.fh);
    01861       if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
    01862
    01863       /* see if we have any loader for this type. */
    01864       for (fmt = _saveload_formats; ; fmt++) {
    01865         /* No loader found, treat as version 0 and use LZO format */
    01866         if (fmt == endof(_saveload_formats)) {
    01867           DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
    01868           clearerr(_sl.fh);
    01869           fseek(_sl.fh, pos, SEEK_SET);
    01870           _sl_version = 0;
    01871           _sl_minor_version = 0;
    01872           fmt = _saveload_formats + 1; // LZO
    01873           break;
    01874         }
    01875
    01876         if (fmt->tag == hdr[0]) {
    01877           /* check version number */
    01878           _sl_version = TO_BE32(hdr[1]) >> 16;
    01879           /* Minor is not used anymore from version 18.0, but it is still needed
    01880            * in versions before that (4 cases) which can't be removed easy.
    01881            * Therefor it is loaded, but never saved (or, it saves a 0 in any scenario).
    01882            * So never EVER use this minor version again. -- TrueLight -- 22-11-2005 */
    01883           _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
    01884
    01885           DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
    01886
    01887           /* Is the version higher than the current? */
    01888           if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
    01889           break;
    01890         }
    01891       }
    01892
    01893       _sl.read_bytes = fmt->reader;
    01894       _sl.excpt_uninit = fmt->uninit_read;
    01895
    01896       /* loader for this savegame type is not implemented? */
    01897       if (fmt->init_read == NULL) {
    01898         char err_str[64];
    01899         snprintf(err_str, lengthof(err_str), "Loader for '%s' is not available.", fmt->name);
    01900         SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
    01901       }
    01902
    01903       if (!fmt->init_read()) {
    01904         char err_str[64];
    01905         snprintf(err_str, lengthof(err_str), "Initializing loader '%s' failed", fmt->name);
    01906         SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
    01907       }
    01908
    01909       _engine_mngr.ResetToDefaultMapping();
    01910
    01911       /* Old maps were hardcoded to 256x256 and thus did not contain
    01912        * any mapsize information. Pre-initialize to 256x256 to not to
    01913        * confuse old games */
    01914       InitializeGame(256, 256, true, true);
    01915
    01916       GamelogReset();
    01917
    01918       SlLoadChunks();
    01919       SlFixPointers();
    01920       fmt->uninit_read();
    01921       fclose(_sl.fh);
    01922
    01923       GamelogStartAction(GLAT_LOAD);
    01924
    01925       _savegame_type = SGT_OTTD;
    01926
    01927       /* After loading fix up savegame for any internal changes that
    01928        * might've occured since then. If it fails, load back the old game */
    01929       if (!AfterLoadGame()) {
    01930         GamelogStopAction();
    01931         return SL_REINIT;
    01932       }
    01933
    01934       GamelogStopAction();
    01935     }
    01936
    01937     return SL_OK;
    01938   }
    01939   catch (...) {
    01940     AbortSaveLoad();
    01941
    01942     /* deinitialize compressor. */
    01943     if (_sl.excpt_uninit != NULL) _sl.excpt_uninit();
    01944
    01945     /* Skip the "colour" character */
    01946     DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
    01947
    01948     /* A saver/loader exception!! reinitialize all variables to prevent crash! */
    01949     return (mode == SL_LOAD) ? SL_REINIT : SL_ERROR;
    01950   }
    01951 }
    01952
    01954 void DoExitSave()
    01955 {
    01956   SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR);
    01957 }
    01958
    01964 void GenerateDefaultSaveName(char *buf, const char *last)
    01965 {
    01966   /* Check if we have a name for this map, which is the name of the first
    01967    * available company. When there's no company available we'll use
    01968    * 'Spectator' as "company" name. */
    01969   CompanyID cid = _local_company;
    01970   if (!Company::IsValidID(cid)) {
    01971     const Company *c;
    01972     FOR_ALL_COMPANIES(c) {
    01973       cid = c->index;
    01974       break;
    01975     }
    01976   }
    01977
    01978   SetDParam(0, cid);
    01979
    01980   /* Insert current date */
    01981   switch (_settings_client.gui.date_format_in_default_names) {
    01982     case 0: SetDParam(1, STR_JUST_DATE_LONG); break;
    01983     case 1: SetDParam(1, STR_JUST_DATE_TINY); break;
    01984     case 2: SetDParam(1, STR_JUST_DATE_ISO); break;
    01985     default: NOT_REACHED();
    01986   }
    01987   SetDParam(2, _date);
    01988
    01989   /* Get the correct string (special string for when there's not company) */
    01990   GetString(buf, !Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, last);
    01991   SanitizeFilename(buf);
    01992 }
    01993
    01994 #if 0
    01995
    02001 int GetSavegameType(char *file)
    02002 {
    02003   const SaveLoadFormat *fmt;
    02004   uint32 hdr;
    02005   FILE *f;
    02006   int mode = SL_OLD_LOAD;
    02007
    02008   f = fopen(file, "rb");
    02009   if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
    02010     DEBUG(sl, 0, "Savegame is obsolete or invalid format");
    02011     mode = SL_LOAD; // don't try to get filename, just show name as it is written
    02012   } else {
    02013     /* see if we have any loader for this type. */
    02014     for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) {
    02015       if (fmt->tag == hdr) {
    02016         mode = SL_LOAD; // new type of savegame
    02017         break;
    02018       }
    02019     }
    02020   }
    02021
    02022   fclose(f);
    02023   return mode;
    02024 }
    02025 #endif