# saveload.cpp

Go to the documentation of this file.

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