saveload.cpp

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

Generated on Tue Sep 14 17:06:54 2010 for OpenTTD by  doxygen 1.6.1