00001
00002
00003
00004
00005
00006
00007
00008
00009
00024 #include "../stdafx.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 "../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 "../fios.h"
00044 #include "../gui.h"
00045
00046 #include "table/strings.h"
00047
00048 #include "saveload_internal.h"
00049 #include "saveload_filter.h"
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229 extern const uint16 SAVEGAME_VERSION = 160;
00230
00231 SavegameType _savegame_type;
00232
00233 uint32 _ttdp_version;
00234 uint16 _sl_version;
00235 byte _sl_minor_version;
00236 char _savegame_format[8];
00237 bool _do_autosave;
00238
00240 enum SaveLoadAction {
00241 SLA_LOAD,
00242 SLA_SAVE,
00243 SLA_PTRS,
00244 SLA_NULL,
00245 SLA_LOAD_CHECK,
00246 };
00247
00248 enum NeedLength {
00249 NL_NONE = 0,
00250 NL_WANTLENGTH = 1,
00251 NL_CALCLENGTH = 2,
00252 };
00253
00255 static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
00256
00258 struct ReadBuffer {
00259 byte buf[MEMORY_CHUNK_SIZE];
00260 byte *bufp;
00261 byte *bufe;
00262 LoadFilter *reader;
00263 size_t read;
00264
00269 ReadBuffer(LoadFilter *reader) : bufp(NULL), bufe(NULL), reader(reader), read(0)
00270 {
00271 }
00272
00273 FORCEINLINE byte ReadByte()
00274 {
00275 if (this->bufp == this->bufe) {
00276 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
00277 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
00278
00279 this->read += len;
00280 this->bufp = this->buf;
00281 this->bufe = this->buf + len;
00282 }
00283
00284 return *this->bufp++;
00285 }
00286
00291 size_t GetSize() const
00292 {
00293 return this->read - (this->bufe - this->bufp);
00294 }
00295 };
00296
00297
00299 struct MemoryDumper {
00300 AutoFreeSmallVector<byte *, 16> blocks;
00301 byte *buf;
00302 byte *bufe;
00303
00305 MemoryDumper() : buf(NULL), bufe(NULL)
00306 {
00307 }
00308
00313 FORCEINLINE void WriteByte(byte b)
00314 {
00315
00316 if (this->buf == this->bufe) {
00317 this->buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
00318 *this->blocks.Append() = this->buf;
00319 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
00320 }
00321
00322 *this->buf++ = b;
00323 }
00324
00329 void Flush(SaveFilter *writer)
00330 {
00331 uint i = 0;
00332 size_t t = this->GetSize();
00333
00334 while (t > 0) {
00335 size_t to_write = min(MEMORY_CHUNK_SIZE, t);
00336
00337 writer->Write(this->blocks[i++], to_write);
00338 t -= to_write;
00339 }
00340
00341 writer->Finish();
00342 }
00343
00348 size_t GetSize() const
00349 {
00350 return this->blocks.Length() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
00351 }
00352 };
00353
00355 struct SaveLoadParams {
00356 SaveLoadAction action;
00357 NeedLength need_length;
00358 byte block_mode;
00359 bool error;
00360
00361 size_t obj_len;
00362 int array_index, last_array_index;
00363
00364 MemoryDumper *dumper;
00365 SaveFilter *sf;
00366
00367 ReadBuffer *reader;
00368 LoadFilter *lf;
00369
00370 StringID error_str;
00371 char *extra_msg;
00372
00373 byte ff_state;
00374 bool saveinprogress;
00375 };
00376
00377 static SaveLoadParams _sl;
00378
00379
00380 extern const ChunkHandler _gamelog_chunk_handlers[];
00381 extern const ChunkHandler _map_chunk_handlers[];
00382 extern const ChunkHandler _misc_chunk_handlers[];
00383 extern const ChunkHandler _name_chunk_handlers[];
00384 extern const ChunkHandler _cheat_chunk_handlers[] ;
00385 extern const ChunkHandler _setting_chunk_handlers[];
00386 extern const ChunkHandler _company_chunk_handlers[];
00387 extern const ChunkHandler _engine_chunk_handlers[];
00388 extern const ChunkHandler _veh_chunk_handlers[];
00389 extern const ChunkHandler _waypoint_chunk_handlers[];
00390 extern const ChunkHandler _depot_chunk_handlers[];
00391 extern const ChunkHandler _order_chunk_handlers[];
00392 extern const ChunkHandler _town_chunk_handlers[];
00393 extern const ChunkHandler _sign_chunk_handlers[];
00394 extern const ChunkHandler _station_chunk_handlers[];
00395 extern const ChunkHandler _industry_chunk_handlers[];
00396 extern const ChunkHandler _economy_chunk_handlers[];
00397 extern const ChunkHandler _subsidy_chunk_handlers[];
00398 extern const ChunkHandler _ai_chunk_handlers[];
00399 extern const ChunkHandler _animated_tile_chunk_handlers[];
00400 extern const ChunkHandler _newgrf_chunk_handlers[];
00401 extern const ChunkHandler _group_chunk_handlers[];
00402 extern const ChunkHandler _cargopacket_chunk_handlers[];
00403 extern const ChunkHandler _autoreplace_chunk_handlers[];
00404 extern const ChunkHandler _labelmaps_chunk_handlers[];
00405 extern const ChunkHandler _airport_chunk_handlers[];
00406 extern const ChunkHandler _object_chunk_handlers[];
00407
00409 static const ChunkHandler * const _chunk_handlers[] = {
00410 _gamelog_chunk_handlers,
00411 _map_chunk_handlers,
00412 _misc_chunk_handlers,
00413 _name_chunk_handlers,
00414 _cheat_chunk_handlers,
00415 _setting_chunk_handlers,
00416 _veh_chunk_handlers,
00417 _waypoint_chunk_handlers,
00418 _depot_chunk_handlers,
00419 _order_chunk_handlers,
00420 _industry_chunk_handlers,
00421 _economy_chunk_handlers,
00422 _subsidy_chunk_handlers,
00423 _engine_chunk_handlers,
00424 _town_chunk_handlers,
00425 _sign_chunk_handlers,
00426 _station_chunk_handlers,
00427 _company_chunk_handlers,
00428 _ai_chunk_handlers,
00429 _animated_tile_chunk_handlers,
00430 _newgrf_chunk_handlers,
00431 _group_chunk_handlers,
00432 _cargopacket_chunk_handlers,
00433 _autoreplace_chunk_handlers,
00434 _labelmaps_chunk_handlers,
00435 _airport_chunk_handlers,
00436 _object_chunk_handlers,
00437 NULL,
00438 };
00439
00444 #define FOR_ALL_CHUNK_HANDLERS(ch) \
00445 for (const ChunkHandler * const *chsc = _chunk_handlers; *chsc != NULL; chsc++) \
00446 for (const ChunkHandler *ch = *chsc; ch != NULL; ch = (ch->flags & CH_LAST) ? NULL : ch + 1)
00447
00449 static void SlNullPointers()
00450 {
00451 _sl.action = SLA_NULL;
00452
00453
00454
00455
00456 _sl_version = SAVEGAME_VERSION;
00457
00458 DEBUG(sl, 1, "Nulling pointers");
00459
00460 FOR_ALL_CHUNK_HANDLERS(ch) {
00461 if (ch->ptrs_proc != NULL) {
00462 DEBUG(sl, 2, "Nulling pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
00463 ch->ptrs_proc();
00464 }
00465 }
00466
00467 DEBUG(sl, 1, "All pointers nulled");
00468
00469 assert(_sl.action == SLA_NULL);
00470 }
00471
00480 void NORETURN SlError(StringID string, const char *extra_msg)
00481 {
00482
00483 if (_sl.action == SLA_LOAD_CHECK) {
00484 _load_check_data.error = string;
00485 free(_load_check_data.error_data);
00486 _load_check_data.error_data = (extra_msg == NULL) ? NULL : strdup(extra_msg);
00487 } else {
00488 _sl.error_str = string;
00489 free(_sl.extra_msg);
00490 _sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg);
00491
00492
00493
00494
00495 }
00496 if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers();
00497 throw std::exception();
00498 }
00499
00507 void NORETURN SlErrorCorrupt(const char *msg)
00508 {
00509 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
00510 }
00511
00512
00513 typedef void (*AsyncSaveFinishProc)();
00514 static AsyncSaveFinishProc _async_save_finish = NULL;
00515 static ThreadObject *_save_thread;
00516
00521 static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
00522 {
00523 if (_exit_game) return;
00524 while (_async_save_finish != NULL) CSleep(10);
00525
00526 _async_save_finish = proc;
00527 }
00528
00532 void ProcessAsyncSaveFinish()
00533 {
00534 if (_async_save_finish == NULL) return;
00535
00536 _async_save_finish();
00537
00538 _async_save_finish = NULL;
00539
00540 if (_save_thread != NULL) {
00541 _save_thread->Join();
00542 delete _save_thread;
00543 _save_thread = NULL;
00544 }
00545 }
00546
00551 byte SlReadByte()
00552 {
00553 return _sl.reader->ReadByte();
00554 }
00555
00560 void SlWriteByte(byte b)
00561 {
00562 _sl.dumper->WriteByte(b);
00563 }
00564
00565 static inline int SlReadUint16()
00566 {
00567 int x = SlReadByte() << 8;
00568 return x | SlReadByte();
00569 }
00570
00571 static inline uint32 SlReadUint32()
00572 {
00573 uint32 x = SlReadUint16() << 16;
00574 return x | SlReadUint16();
00575 }
00576
00577 static inline uint64 SlReadUint64()
00578 {
00579 uint32 x = SlReadUint32();
00580 uint32 y = SlReadUint32();
00581 return (uint64)x << 32 | y;
00582 }
00583
00584 static inline void SlWriteUint16(uint16 v)
00585 {
00586 SlWriteByte(GB(v, 8, 8));
00587 SlWriteByte(GB(v, 0, 8));
00588 }
00589
00590 static inline void SlWriteUint32(uint32 v)
00591 {
00592 SlWriteUint16(GB(v, 16, 16));
00593 SlWriteUint16(GB(v, 0, 16));
00594 }
00595
00596 static inline void SlWriteUint64(uint64 x)
00597 {
00598 SlWriteUint32((uint32)(x >> 32));
00599 SlWriteUint32((uint32)x);
00600 }
00601
00607 static inline void SlSkipBytes(size_t length)
00608 {
00609 for (; length != 0; length--) SlReadByte();
00610 }
00611
00621 static uint SlReadSimpleGamma()
00622 {
00623 uint i = SlReadByte();
00624 if (HasBit(i, 7)) {
00625 i &= ~0x80;
00626 if (HasBit(i, 6)) {
00627 i &= ~0x40;
00628 if (HasBit(i, 5)) {
00629 i &= ~0x20;
00630 if (HasBit(i, 4)) {
00631 SlErrorCorrupt("Unsupported gamma");
00632 }
00633 i = (i << 8) | SlReadByte();
00634 }
00635 i = (i << 8) | SlReadByte();
00636 }
00637 i = (i << 8) | SlReadByte();
00638 }
00639 return i;
00640 }
00641
00654 static void SlWriteSimpleGamma(size_t i)
00655 {
00656 if (i >= (1 << 7)) {
00657 if (i >= (1 << 14)) {
00658 if (i >= (1 << 21)) {
00659 assert(i < (1 << 28));
00660 SlWriteByte((byte)(0xE0 | (i >> 24)));
00661 SlWriteByte((byte)(i >> 16));
00662 } else {
00663 SlWriteByte((byte)(0xC0 | (i >> 16)));
00664 }
00665 SlWriteByte((byte)(i >> 8));
00666 } else {
00667 SlWriteByte((byte)(0x80 | (i >> 8)));
00668 }
00669 }
00670 SlWriteByte((byte)i);
00671 }
00672
00674 static inline uint SlGetGammaLength(size_t i)
00675 {
00676 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21));
00677 }
00678
00679 static inline uint SlReadSparseIndex()
00680 {
00681 return SlReadSimpleGamma();
00682 }
00683
00684 static inline void SlWriteSparseIndex(uint index)
00685 {
00686 SlWriteSimpleGamma(index);
00687 }
00688
00689 static inline uint SlReadArrayLength()
00690 {
00691 return SlReadSimpleGamma();
00692 }
00693
00694 static inline void SlWriteArrayLength(size_t length)
00695 {
00696 SlWriteSimpleGamma(length);
00697 }
00698
00699 static inline uint SlGetArrayLength(size_t length)
00700 {
00701 return SlGetGammaLength(length);
00702 }
00703
00710 static inline uint SlCalcConvMemLen(VarType conv)
00711 {
00712 static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0};
00713 byte length = GB(conv, 4, 4);
00714
00715 switch (length << 4) {
00716 case SLE_VAR_STRB:
00717 case SLE_VAR_STRBQ:
00718 case SLE_VAR_STR:
00719 case SLE_VAR_STRQ:
00720 return SlReadArrayLength();
00721
00722 default:
00723 assert(length < lengthof(conv_mem_size));
00724 return conv_mem_size[length];
00725 }
00726 }
00727
00734 static inline byte SlCalcConvFileLen(VarType conv)
00735 {
00736 static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2};
00737 byte length = GB(conv, 0, 4);
00738 assert(length < lengthof(conv_file_size));
00739 return conv_file_size[length];
00740 }
00741
00743 static inline size_t SlCalcRefLen()
00744 {
00745 return IsSavegameVersionBefore(69) ? 2 : 4;
00746 }
00747
00748 void SlSetArrayIndex(uint index)
00749 {
00750 _sl.need_length = NL_WANTLENGTH;
00751 _sl.array_index = index;
00752 }
00753
00754 static size_t _next_offs;
00755
00760 int SlIterateArray()
00761 {
00762 int index;
00763
00764
00765
00766 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) SlErrorCorrupt("Invalid chunk size");
00767
00768 while (true) {
00769 uint length = SlReadArrayLength();
00770 if (length == 0) {
00771 _next_offs = 0;
00772 return -1;
00773 }
00774
00775 _sl.obj_len = --length;
00776 _next_offs = _sl.reader->GetSize() + length;
00777
00778 switch (_sl.block_mode) {
00779 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
00780 case CH_ARRAY: index = _sl.array_index++; break;
00781 default:
00782 DEBUG(sl, 0, "SlIterateArray error");
00783 return -1;
00784 }
00785
00786 if (length != 0) return index;
00787 }
00788 }
00789
00793 void SlSkipArray()
00794 {
00795 while (SlIterateArray() != -1) {
00796 SlSkipBytes(_next_offs - _sl.reader->GetSize());
00797 }
00798 }
00799
00805 void SlSetLength(size_t length)
00806 {
00807 assert(_sl.action == SLA_SAVE);
00808
00809 switch (_sl.need_length) {
00810 case NL_WANTLENGTH:
00811 _sl.need_length = NL_NONE;
00812 switch (_sl.block_mode) {
00813 case CH_RIFF:
00814
00815
00816
00817 assert(length < (1 << 28));
00818 SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28)));
00819 break;
00820 case CH_ARRAY:
00821 assert(_sl.last_array_index <= _sl.array_index);
00822 while (++_sl.last_array_index <= _sl.array_index) {
00823 SlWriteArrayLength(1);
00824 }
00825 SlWriteArrayLength(length + 1);
00826 break;
00827 case CH_SPARSE_ARRAY:
00828 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index));
00829 SlWriteSparseIndex(_sl.array_index);
00830 break;
00831 default: NOT_REACHED();
00832 }
00833 break;
00834
00835 case NL_CALCLENGTH:
00836 _sl.obj_len += (int)length;
00837 break;
00838
00839 default: NOT_REACHED();
00840 }
00841 }
00842
00849 static void SlCopyBytes(void *ptr, size_t length)
00850 {
00851 byte *p = (byte *)ptr;
00852
00853 switch (_sl.action) {
00854 case SLA_LOAD_CHECK:
00855 case SLA_LOAD:
00856 for (; length != 0; length--) *p++ = SlReadByte();
00857 break;
00858 case SLA_SAVE:
00859 for (; length != 0; length--) SlWriteByte(*p++);
00860 break;
00861 default: NOT_REACHED();
00862 }
00863 }
00864
00866 size_t SlGetFieldLength()
00867 {
00868 return _sl.obj_len;
00869 }
00870
00878 int64 ReadValue(const void *ptr, VarType conv)
00879 {
00880 switch (GetVarMemType(conv)) {
00881 case SLE_VAR_BL: return (*(bool *)ptr != 0);
00882 case SLE_VAR_I8: return *(int8 *)ptr;
00883 case SLE_VAR_U8: return *(byte *)ptr;
00884 case SLE_VAR_I16: return *(int16 *)ptr;
00885 case SLE_VAR_U16: return *(uint16*)ptr;
00886 case SLE_VAR_I32: return *(int32 *)ptr;
00887 case SLE_VAR_U32: return *(uint32*)ptr;
00888 case SLE_VAR_I64: return *(int64 *)ptr;
00889 case SLE_VAR_U64: return *(uint64*)ptr;
00890 case SLE_VAR_NULL:return 0;
00891 default: NOT_REACHED();
00892 }
00893 }
00894
00902 void WriteValue(void *ptr, VarType conv, int64 val)
00903 {
00904 switch (GetVarMemType(conv)) {
00905 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
00906 case SLE_VAR_I8: *(int8 *)ptr = val; break;
00907 case SLE_VAR_U8: *(byte *)ptr = val; break;
00908 case SLE_VAR_I16: *(int16 *)ptr = val; break;
00909 case SLE_VAR_U16: *(uint16*)ptr = val; break;
00910 case SLE_VAR_I32: *(int32 *)ptr = val; break;
00911 case SLE_VAR_U32: *(uint32*)ptr = val; break;
00912 case SLE_VAR_I64: *(int64 *)ptr = val; break;
00913 case SLE_VAR_U64: *(uint64*)ptr = val; break;
00914 case SLE_VAR_NAME: *(char**)ptr = CopyFromOldName(val); break;
00915 case SLE_VAR_NULL: break;
00916 default: NOT_REACHED();
00917 }
00918 }
00919
00928 static void SlSaveLoadConv(void *ptr, VarType conv)
00929 {
00930 switch (_sl.action) {
00931 case SLA_SAVE: {
00932 int64 x = ReadValue(ptr, conv);
00933
00934
00935 switch (GetVarFileType(conv)) {
00936 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
00937 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
00938 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
00939 case SLE_FILE_STRINGID:
00940 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
00941 case SLE_FILE_I32:
00942 case SLE_FILE_U32: SlWriteUint32((uint32)x);break;
00943 case SLE_FILE_I64:
00944 case SLE_FILE_U64: SlWriteUint64(x);break;
00945 default: NOT_REACHED();
00946 }
00947 break;
00948 }
00949 case SLA_LOAD_CHECK:
00950 case SLA_LOAD: {
00951 int64 x;
00952
00953 switch (GetVarFileType(conv)) {
00954 case SLE_FILE_I8: x = (int8 )SlReadByte(); break;
00955 case SLE_FILE_U8: x = (byte )SlReadByte(); break;
00956 case SLE_FILE_I16: x = (int16 )SlReadUint16(); break;
00957 case SLE_FILE_U16: x = (uint16)SlReadUint16(); break;
00958 case SLE_FILE_I32: x = (int32 )SlReadUint32(); break;
00959 case SLE_FILE_U32: x = (uint32)SlReadUint32(); break;
00960 case SLE_FILE_I64: x = (int64 )SlReadUint64(); break;
00961 case SLE_FILE_U64: x = (uint64)SlReadUint64(); break;
00962 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break;
00963 default: NOT_REACHED();
00964 }
00965
00966
00967 WriteValue(ptr, conv, x);
00968 break;
00969 }
00970 case SLA_PTRS: break;
00971 case SLA_NULL: break;
00972 default: NOT_REACHED();
00973 }
00974 }
00975
00985 static inline size_t SlCalcNetStringLen(const char *ptr, size_t length)
00986 {
00987 if (ptr == NULL) return 0;
00988 return min(strlen(ptr), length - 1);
00989 }
00990
01000 static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv)
01001 {
01002 size_t len;
01003 const char *str;
01004
01005 switch (GetVarMemType(conv)) {
01006 default: NOT_REACHED();
01007 case SLE_VAR_STR:
01008 case SLE_VAR_STRQ:
01009 str = *(const char**)ptr;
01010 len = SIZE_MAX;
01011 break;
01012 case SLE_VAR_STRB:
01013 case SLE_VAR_STRBQ:
01014 str = (const char*)ptr;
01015 len = length;
01016 break;
01017 }
01018
01019 len = SlCalcNetStringLen(str, len);
01020 return len + SlGetArrayLength(len);
01021 }
01022
01029 static void SlString(void *ptr, size_t length, VarType conv)
01030 {
01031 switch (_sl.action) {
01032 case SLA_SAVE: {
01033 size_t len;
01034 switch (GetVarMemType(conv)) {
01035 default: NOT_REACHED();
01036 case SLE_VAR_STRB:
01037 case SLE_VAR_STRBQ:
01038 len = SlCalcNetStringLen((char *)ptr, length);
01039 break;
01040 case SLE_VAR_STR:
01041 case SLE_VAR_STRQ:
01042 ptr = *(char **)ptr;
01043 len = SlCalcNetStringLen((char *)ptr, SIZE_MAX);
01044 break;
01045 }
01046
01047 SlWriteArrayLength(len);
01048 SlCopyBytes(ptr, len);
01049 break;
01050 }
01051 case SLA_LOAD_CHECK:
01052 case SLA_LOAD: {
01053 size_t len = SlReadArrayLength();
01054
01055 switch (GetVarMemType(conv)) {
01056 default: NOT_REACHED();
01057 case SLE_VAR_STRB:
01058 case SLE_VAR_STRBQ:
01059 if (len >= length) {
01060 DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating");
01061 SlCopyBytes(ptr, length);
01062 SlSkipBytes(len - length);
01063 len = length - 1;
01064 } else {
01065 SlCopyBytes(ptr, len);
01066 }
01067 break;
01068 case SLE_VAR_STR:
01069 case SLE_VAR_STRQ:
01070 free(*(char **)ptr);
01071 if (len == 0) {
01072 *(char **)ptr = NULL;
01073 } else {
01074 *(char **)ptr = MallocT<char>(len + 1);
01075 ptr = *(char **)ptr;
01076 SlCopyBytes(ptr, len);
01077 }
01078 break;
01079 }
01080
01081 ((char *)ptr)[len] = '\0';
01082 str_validate((char *)ptr, (char *)ptr + len);
01083 break;
01084 }
01085 case SLA_PTRS: break;
01086 case SLA_NULL: break;
01087 default: NOT_REACHED();
01088 }
01089 }
01090
01096 static inline size_t SlCalcArrayLen(size_t length, VarType conv)
01097 {
01098 return SlCalcConvFileLen(conv) * length;
01099 }
01100
01107 void SlArray(void *array, size_t length, VarType conv)
01108 {
01109 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
01110
01111
01112 if (_sl.need_length != NL_NONE) {
01113 SlSetLength(SlCalcArrayLen(length, conv));
01114
01115 if (_sl.need_length == NL_CALCLENGTH) return;
01116 }
01117
01118
01119
01120 if (_sl.action != SLA_SAVE && _sl_version == 0) {
01121
01122 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
01123 conv == SLE_INT32 || conv == SLE_UINT32) {
01124 SlCopyBytes(array, length * SlCalcConvFileLen(conv));
01125 return;
01126 }
01127
01128 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
01129 for (uint i = 0; i < length; i++) {
01130 ((int64*)array)[i] = (int32)BSWAP32(SlReadUint32());
01131 }
01132 return;
01133 }
01134 }
01135
01136
01137
01138 if (conv == SLE_INT8 || conv == SLE_UINT8) {
01139 SlCopyBytes(array, length);
01140 } else {
01141 byte *a = (byte*)array;
01142 byte mem_size = SlCalcConvMemLen(conv);
01143
01144 for (; length != 0; length --) {
01145 SlSaveLoadConv(a, conv);
01146 a += mem_size;
01147 }
01148 }
01149 }
01150
01151
01162 static size_t ReferenceToInt(const void *obj, SLRefType rt)
01163 {
01164 assert(_sl.action == SLA_SAVE);
01165
01166 if (obj == NULL) return 0;
01167
01168 switch (rt) {
01169 case REF_VEHICLE_OLD:
01170 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
01171 case REF_STATION: return ((const Station*)obj)->index + 1;
01172 case REF_TOWN: return ((const Town*)obj)->index + 1;
01173 case REF_ORDER: return ((const Order*)obj)->index + 1;
01174 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
01175 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
01176 case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
01177 case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
01178 default: NOT_REACHED();
01179 }
01180 }
01181
01192 static void *IntToReference(size_t index, SLRefType rt)
01193 {
01194 assert_compile(sizeof(size_t) <= sizeof(void *));
01195
01196 assert(_sl.action == SLA_PTRS);
01197
01198
01199
01200 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(4, 4)) {
01201 rt = REF_VEHICLE;
01202 }
01203
01204
01205 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return NULL;
01206
01207
01208
01209 if (rt != REF_VEHICLE_OLD) index--;
01210
01211 switch (rt) {
01212 case REF_ORDERLIST:
01213 if (OrderList::IsValidID(index)) return OrderList::Get(index);
01214 SlErrorCorrupt("Referencing invalid OrderList");
01215
01216 case REF_ORDER:
01217 if (Order::IsValidID(index)) return Order::Get(index);
01218
01219 if (IsSavegameVersionBefore(5, 2)) return NULL;
01220 SlErrorCorrupt("Referencing invalid Order");
01221
01222 case REF_VEHICLE_OLD:
01223 case REF_VEHICLE:
01224 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
01225 SlErrorCorrupt("Referencing invalid Vehicle");
01226
01227 case REF_STATION:
01228 if (Station::IsValidID(index)) return Station::Get(index);
01229 SlErrorCorrupt("Referencing invalid Station");
01230
01231 case REF_TOWN:
01232 if (Town::IsValidID(index)) return Town::Get(index);
01233 SlErrorCorrupt("Referencing invalid Town");
01234
01235 case REF_ROADSTOPS:
01236 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
01237 SlErrorCorrupt("Referencing invalid RoadStop");
01238
01239 case REF_ENGINE_RENEWS:
01240 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
01241 SlErrorCorrupt("Referencing invalid EngineRenew");
01242
01243 case REF_CARGO_PACKET:
01244 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
01245 SlErrorCorrupt("Referencing invalid CargoPacket");
01246
01247 default: NOT_REACHED();
01248 }
01249 }
01250
01255 static inline size_t SlCalcListLen(const void *list)
01256 {
01257 std::list<void *> *l = (std::list<void *> *) list;
01258
01259 int type_size = IsSavegameVersionBefore(69) ? 2 : 4;
01260
01261
01262 return l->size() * type_size + type_size;
01263 }
01264
01265
01271 static void SlList(void *list, SLRefType conv)
01272 {
01273
01274 if (_sl.need_length != NL_NONE) {
01275 SlSetLength(SlCalcListLen(list));
01276
01277 if (_sl.need_length == NL_CALCLENGTH) return;
01278 }
01279
01280 typedef std::list<void *> PtrList;
01281 PtrList *l = (PtrList *)list;
01282
01283 switch (_sl.action) {
01284 case SLA_SAVE: {
01285 SlWriteUint32((uint32)l->size());
01286
01287 PtrList::iterator iter;
01288 for (iter = l->begin(); iter != l->end(); ++iter) {
01289 void *ptr = *iter;
01290 SlWriteUint32((uint32)ReferenceToInt(ptr, conv));
01291 }
01292 break;
01293 }
01294 case SLA_LOAD_CHECK:
01295 case SLA_LOAD: {
01296 size_t length = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01297
01298
01299 for (size_t i = 0; i < length; i++) {
01300 size_t data = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01301 l->push_back((void *)data);
01302 }
01303 break;
01304 }
01305 case SLA_PTRS: {
01306 PtrList temp = *l;
01307
01308 l->clear();
01309 PtrList::iterator iter;
01310 for (iter = temp.begin(); iter != temp.end(); ++iter) {
01311 void *ptr = IntToReference((size_t)*iter, conv);
01312 l->push_back(ptr);
01313 }
01314 break;
01315 }
01316 case SLA_NULL:
01317 l->clear();
01318 break;
01319 default: NOT_REACHED();
01320 }
01321 }
01322
01323
01325 static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
01326 {
01327 if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false;
01328 if (sld->conv & SLF_SAVE_NO) return false;
01329
01330 return true;
01331 }
01332
01338 static inline bool SlSkipVariableOnLoad(const SaveLoad *sld)
01339 {
01340 if ((sld->conv & SLF_NETWORK_NO) && _sl.action != SLA_SAVE && _networking && !_network_server) {
01341 SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length);
01342 return true;
01343 }
01344
01345 return false;
01346 }
01347
01354 size_t SlCalcObjLength(const void *object, const SaveLoad *sld)
01355 {
01356 size_t length = 0;
01357
01358
01359 for (; sld->cmd != SL_END; sld++) {
01360 length += SlCalcObjMemberLength(object, sld);
01361 }
01362 return length;
01363 }
01364
01365 size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
01366 {
01367 assert(_sl.action == SLA_SAVE);
01368
01369 switch (sld->cmd) {
01370 case SL_VAR:
01371 case SL_REF:
01372 case SL_ARR:
01373 case SL_STR:
01374 case SL_LST:
01375
01376 if (!SlIsObjectValidInSavegame(sld)) break;
01377
01378 switch (sld->cmd) {
01379 case SL_VAR: return SlCalcConvFileLen(sld->conv);
01380 case SL_REF: return SlCalcRefLen();
01381 case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv);
01382 case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv);
01383 case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld));
01384 default: NOT_REACHED();
01385 }
01386 break;
01387 case SL_WRITEBYTE: return 1;
01388 case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END));
01389 case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription());
01390 default: NOT_REACHED();
01391 }
01392 return 0;
01393 }
01394
01395
01396 bool SlObjectMember(void *ptr, const SaveLoad *sld)
01397 {
01398 VarType conv = GB(sld->conv, 0, 8);
01399 switch (sld->cmd) {
01400 case SL_VAR:
01401 case SL_REF:
01402 case SL_ARR:
01403 case SL_STR:
01404 case SL_LST:
01405
01406 if (!SlIsObjectValidInSavegame(sld)) return false;
01407 if (SlSkipVariableOnLoad(sld)) return false;
01408
01409 switch (sld->cmd) {
01410 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
01411 case SL_REF:
01412 switch (_sl.action) {
01413 case SLA_SAVE:
01414 SlWriteUint32((uint32)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
01415 break;
01416 case SLA_LOAD_CHECK:
01417 case SLA_LOAD:
01418 *(size_t *)ptr = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01419 break;
01420 case SLA_PTRS:
01421 *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
01422 break;
01423 case SLA_NULL:
01424 *(void **)ptr = NULL;
01425 break;
01426 default: NOT_REACHED();
01427 }
01428 break;
01429 case SL_ARR: SlArray(ptr, sld->length, conv); break;
01430 case SL_STR: SlString(ptr, sld->length, conv); break;
01431 case SL_LST: SlList(ptr, (SLRefType)conv); break;
01432 default: NOT_REACHED();
01433 }
01434 break;
01435
01436
01437
01438
01439
01440
01441 case SL_WRITEBYTE:
01442 switch (_sl.action) {
01443 case SLA_SAVE: SlWriteByte(sld->version_to); break;
01444 case SLA_LOAD_CHECK:
01445 case SLA_LOAD: *(byte *)ptr = sld->version_from; break;
01446 case SLA_PTRS: break;
01447 case SLA_NULL: break;
01448 default: NOT_REACHED();
01449 }
01450 break;
01451
01452
01453 case SL_VEH_INCLUDE:
01454 SlObject(ptr, GetVehicleDescription(VEH_END));
01455 break;
01456
01457 case SL_ST_INCLUDE:
01458 SlObject(ptr, GetBaseStationDescription());
01459 break;
01460
01461 default: NOT_REACHED();
01462 }
01463 return true;
01464 }
01465
01471 void SlObject(void *object, const SaveLoad *sld)
01472 {
01473
01474 if (_sl.need_length != NL_NONE) {
01475 SlSetLength(SlCalcObjLength(object, sld));
01476 if (_sl.need_length == NL_CALCLENGTH) return;
01477 }
01478
01479 for (; sld->cmd != SL_END; sld++) {
01480 void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld);
01481 SlObjectMember(ptr, sld);
01482 }
01483 }
01484
01489 void SlGlobList(const SaveLoadGlobVarList *sldg)
01490 {
01491 SlObject(NULL, (const SaveLoad*)sldg);
01492 }
01493
01499 void SlAutolength(AutolengthProc *proc, void *arg)
01500 {
01501 size_t offs;
01502
01503 assert(_sl.action == SLA_SAVE);
01504
01505
01506 _sl.need_length = NL_CALCLENGTH;
01507 _sl.obj_len = 0;
01508 proc(arg);
01509
01510
01511 _sl.need_length = NL_WANTLENGTH;
01512 SlSetLength(_sl.obj_len);
01513
01514 offs = _sl.dumper->GetSize() + _sl.obj_len;
01515
01516
01517 proc(arg);
01518
01519 if (offs != _sl.dumper->GetSize()) SlErrorCorrupt("Invalid chunk size");
01520 }
01521
01526 static void SlLoadChunk(const ChunkHandler *ch)
01527 {
01528 byte m = SlReadByte();
01529 size_t len;
01530 size_t endoffs;
01531
01532 _sl.block_mode = m;
01533 _sl.obj_len = 0;
01534
01535 switch (m) {
01536 case CH_ARRAY:
01537 _sl.array_index = 0;
01538 ch->load_proc();
01539 break;
01540 case CH_SPARSE_ARRAY:
01541 ch->load_proc();
01542 break;
01543 default:
01544 if ((m & 0xF) == CH_RIFF) {
01545
01546 len = (SlReadByte() << 16) | ((m >> 4) << 24);
01547 len += SlReadUint16();
01548 _sl.obj_len = len;
01549 endoffs = _sl.reader->GetSize() + len;
01550 ch->load_proc();
01551 if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size");
01552 } else {
01553 SlErrorCorrupt("Invalid chunk type");
01554 }
01555 break;
01556 }
01557 }
01558
01564 static void SlLoadCheckChunk(const ChunkHandler *ch)
01565 {
01566 byte m = SlReadByte();
01567 size_t len;
01568 size_t endoffs;
01569
01570 _sl.block_mode = m;
01571 _sl.obj_len = 0;
01572
01573 switch (m) {
01574 case CH_ARRAY:
01575 _sl.array_index = 0;
01576 if (ch->load_check_proc) {
01577 ch->load_check_proc();
01578 } else {
01579 SlSkipArray();
01580 }
01581 break;
01582 case CH_SPARSE_ARRAY:
01583 if (ch->load_check_proc) {
01584 ch->load_check_proc();
01585 } else {
01586 SlSkipArray();
01587 }
01588 break;
01589 default:
01590 if ((m & 0xF) == CH_RIFF) {
01591
01592 len = (SlReadByte() << 16) | ((m >> 4) << 24);
01593 len += SlReadUint16();
01594 _sl.obj_len = len;
01595 endoffs = _sl.reader->GetSize() + len;
01596 if (ch->load_check_proc) {
01597 ch->load_check_proc();
01598 } else {
01599 SlSkipBytes(len);
01600 }
01601 if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size");
01602 } else {
01603 SlErrorCorrupt("Invalid chunk type");
01604 }
01605 break;
01606 }
01607 }
01608
01613 static ChunkSaveLoadProc *_stub_save_proc;
01614
01620 static inline void SlStubSaveProc2(void *arg)
01621 {
01622 _stub_save_proc();
01623 }
01624
01630 static void SlStubSaveProc()
01631 {
01632 SlAutolength(SlStubSaveProc2, NULL);
01633 }
01634
01640 static void SlSaveChunk(const ChunkHandler *ch)
01641 {
01642 ChunkSaveLoadProc *proc = ch->save_proc;
01643
01644
01645 if (proc == NULL) return;
01646
01647 SlWriteUint32(ch->id);
01648 DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
01649
01650 if (ch->flags & CH_AUTO_LENGTH) {
01651
01652 _stub_save_proc = proc;
01653 proc = SlStubSaveProc;
01654 }
01655
01656 _sl.block_mode = ch->flags & CH_TYPE_MASK;
01657 switch (ch->flags & CH_TYPE_MASK) {
01658 case CH_RIFF:
01659 _sl.need_length = NL_WANTLENGTH;
01660 proc();
01661 break;
01662 case CH_ARRAY:
01663 _sl.last_array_index = 0;
01664 SlWriteByte(CH_ARRAY);
01665 proc();
01666 SlWriteArrayLength(0);
01667 break;
01668 case CH_SPARSE_ARRAY:
01669 SlWriteByte(CH_SPARSE_ARRAY);
01670 proc();
01671 SlWriteArrayLength(0);
01672 break;
01673 default: NOT_REACHED();
01674 }
01675 }
01676
01678 static void SlSaveChunks()
01679 {
01680 FOR_ALL_CHUNK_HANDLERS(ch) {
01681 SlSaveChunk(ch);
01682 }
01683
01684
01685 SlWriteUint32(0);
01686 }
01687
01694 static const ChunkHandler *SlFindChunkHandler(uint32 id)
01695 {
01696 FOR_ALL_CHUNK_HANDLERS(ch) if (ch->id == id) return ch;
01697 return NULL;
01698 }
01699
01701 static void SlLoadChunks()
01702 {
01703 uint32 id;
01704 const ChunkHandler *ch;
01705
01706 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
01707 DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
01708
01709 ch = SlFindChunkHandler(id);
01710 if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
01711 SlLoadChunk(ch);
01712 }
01713 }
01714
01716 static void SlLoadCheckChunks()
01717 {
01718 uint32 id;
01719 const ChunkHandler *ch;
01720
01721 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
01722 DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
01723
01724 ch = SlFindChunkHandler(id);
01725 if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
01726 SlLoadCheckChunk(ch);
01727 }
01728 }
01729
01731 static void SlFixPointers()
01732 {
01733 _sl.action = SLA_PTRS;
01734
01735 DEBUG(sl, 1, "Fixing pointers");
01736
01737 FOR_ALL_CHUNK_HANDLERS(ch) {
01738 if (ch->ptrs_proc != NULL) {
01739 DEBUG(sl, 2, "Fixing pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
01740 ch->ptrs_proc();
01741 }
01742 }
01743
01744 DEBUG(sl, 1, "All pointers fixed");
01745
01746 assert(_sl.action == SLA_PTRS);
01747 }
01748
01749
01751 struct FileReader : LoadFilter {
01752 FILE *file;
01753 long begin;
01754
01759 FileReader(FILE *file) : LoadFilter(NULL), file(file), begin(ftell(file))
01760 {
01761 }
01762
01764 ~FileReader()
01765 {
01766 if (this->file != NULL) fclose(this->file);
01767 this->file = NULL;
01768
01769
01770 _sl.sf = NULL;
01771 }
01772
01773 size_t Read(byte *buf, size_t size)
01774 {
01775
01776 if (this->file == NULL) return 0;
01777
01778 return fread(buf, 1, size, this->file);
01779 }
01780
01781 void Reset()
01782 {
01783 clearerr(this->file);
01784 fseek(this->file, this->begin, SEEK_SET);
01785 }
01786 };
01787
01789 struct FileWriter : SaveFilter {
01790 FILE *file;
01791
01796 FileWriter(FILE *file) : SaveFilter(NULL), file(file)
01797 {
01798 }
01799
01801 ~FileWriter()
01802 {
01803 this->Finish();
01804
01805
01806 _sl.sf = NULL;
01807 }
01808
01809 void Write(byte *buf, size_t size)
01810 {
01811
01812 if (this->file == NULL) return;
01813
01814 if (fwrite(buf, 1, size, this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
01815 }
01816
01817 void Finish()
01818 {
01819 if (this->file != NULL) fclose(this->file);
01820 this->file = NULL;
01821 }
01822 };
01823
01824
01825
01826
01827
01828 #ifdef WITH_LZO
01829 #include <lzo/lzo1x.h>
01830
01832 static const uint LZO_BUFFER_SIZE = 8192;
01833
01835 struct LZOLoadFilter : LoadFilter {
01840 LZOLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01841 {
01842 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
01843 }
01844
01845 size_t Read(byte *buf, size_t ssize)
01846 {
01847 assert(ssize >= LZO_BUFFER_SIZE);
01848
01849
01850 byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2];
01851 uint32 tmp[2];
01852 uint32 size;
01853 lzo_uint len;
01854
01855
01856 if (this->chain->Read((byte*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
01857
01858
01859 ((uint32*)out)[0] = size = tmp[1];
01860
01861 if (_sl_version != 0) {
01862 tmp[0] = TO_BE32(tmp[0]);
01863 size = TO_BE32(size);
01864 }
01865
01866 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
01867
01868
01869 if (this->chain->Read(out + sizeof(uint32), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
01870
01871
01872 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlErrorCorrupt("Bad checksum");
01873
01874
01875 lzo1x_decompress(out + sizeof(uint32) * 1, size, buf, &len, NULL);
01876 return len;
01877 }
01878 };
01879
01881 struct LZOSaveFilter : SaveFilter {
01887 LZOSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
01888 {
01889 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
01890 }
01891
01892 void Write(byte *buf, size_t size)
01893 {
01894 const lzo_bytep in = buf;
01895
01896 byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2];
01897 byte wrkmem[LZO1X_1_MEM_COMPRESS];
01898 lzo_uint outlen;
01899
01900 do {
01901
01902 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size;
01903 lzo1x_1_compress(in, len, out + sizeof(uint32) * 2, &outlen, wrkmem);
01904 ((uint32*)out)[1] = TO_BE32((uint32)outlen);
01905 ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32)));
01906 this->chain->Write(out, outlen + sizeof(uint32) * 2);
01907
01908
01909 size -= len;
01910 in += len;
01911 } while (size > 0);
01912 }
01913 };
01914
01915 #endif
01916
01917
01918
01919
01920
01922 struct NoCompLoadFilter : LoadFilter {
01927 NoCompLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01928 {
01929 }
01930
01931 size_t Read(byte *buf, size_t size)
01932 {
01933 return this->chain->Read(buf, size);
01934 }
01935 };
01936
01938 struct NoCompSaveFilter : SaveFilter {
01944 NoCompSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
01945 {
01946 }
01947
01948 void Write(byte *buf, size_t size)
01949 {
01950 this->chain->Write(buf, size);
01951 }
01952 };
01953
01954
01955
01956
01957
01958 #if defined(WITH_ZLIB)
01959 #include <zlib.h>
01960
01962 struct ZlibLoadFilter : LoadFilter {
01963 z_stream z;
01964 byte fread_buf[MEMORY_CHUNK_SIZE];
01965
01970 ZlibLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01971 {
01972 memset(&this->z, 0, sizeof(this->z));
01973 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
01974 }
01975
01977 ~ZlibLoadFilter()
01978 {
01979 inflateEnd(&this->z);
01980 }
01981
01982 size_t Read(byte *buf, size_t size)
01983 {
01984 this->z.next_out = buf;
01985 this->z.avail_out = (uint)size;
01986
01987 do {
01988
01989 if (this->z.avail_in == 0) {
01990 this->z.next_in = this->fread_buf;
01991 this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
01992 }
01993
01994
01995 int r = inflate(&this->z, 0);
01996 if (r == Z_STREAM_END) break;
01997
01998 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
01999 } while (this->z.avail_out != 0);
02000
02001 return size - this->z.avail_out;
02002 }
02003 };
02004
02006 struct ZlibSaveFilter : SaveFilter {
02007 z_stream z;
02008
02014 ZlibSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
02015 {
02016 memset(&this->z, 0, sizeof(this->z));
02017 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
02018 }
02019
02021 ~ZlibSaveFilter()
02022 {
02023 deflateEnd(&this->z);
02024 }
02025
02032 void WriteLoop(byte *p, size_t len, int mode)
02033 {
02034 byte buf[MEMORY_CHUNK_SIZE];
02035 uint n;
02036 this->z.next_in = p;
02037 this->z.avail_in = (uInt)len;
02038 do {
02039 this->z.next_out = buf;
02040 this->z.avail_out = sizeof(buf);
02041
02049 int r = deflate(&this->z, mode);
02050
02051
02052 if ((n = sizeof(buf) - this->z.avail_out) != 0) {
02053 this->chain->Write(buf, n);
02054 }
02055 if (r == Z_STREAM_END) break;
02056
02057 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
02058 } while (this->z.avail_in || !this->z.avail_out);
02059 }
02060
02061 void Write(byte *buf, size_t size)
02062 {
02063 this->WriteLoop(buf, size, 0);
02064 }
02065
02066 void Finish()
02067 {
02068 this->WriteLoop(NULL, 0, Z_FINISH);
02069 this->chain->Finish();
02070 }
02071 };
02072
02073 #endif
02074
02075
02076
02077
02078
02079 #if defined(WITH_LZMA)
02080 #include <lzma.h>
02081
02088 static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
02089
02091 struct LZMALoadFilter : LoadFilter {
02092 lzma_stream lzma;
02093 byte fread_buf[MEMORY_CHUNK_SIZE];
02094
02099 LZMALoadFilter(LoadFilter *chain) : LoadFilter(chain), lzma(_lzma_init)
02100 {
02101
02102 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
02103 }
02104
02106 ~LZMALoadFilter()
02107 {
02108 lzma_end(&this->lzma);
02109 }
02110
02111 size_t Read(byte *buf, size_t size)
02112 {
02113 this->lzma.next_out = buf;
02114 this->lzma.avail_out = size;
02115
02116 do {
02117
02118 if (this->lzma.avail_in == 0) {
02119 this->lzma.next_in = this->fread_buf;
02120 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
02121 }
02122
02123
02124 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
02125 if (r == LZMA_STREAM_END) break;
02126 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
02127 } while (this->lzma.avail_out != 0);
02128
02129 return size - this->lzma.avail_out;
02130 }
02131 };
02132
02134 struct LZMASaveFilter : SaveFilter {
02135 lzma_stream lzma;
02136
02142 LZMASaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain), lzma(_lzma_init)
02143 {
02144 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
02145 }
02146
02148 ~LZMASaveFilter()
02149 {
02150 lzma_end(&this->lzma);
02151 }
02152
02159 void WriteLoop(byte *p, size_t len, lzma_action action)
02160 {
02161 byte buf[MEMORY_CHUNK_SIZE];
02162 size_t n;
02163 this->lzma.next_in = p;
02164 this->lzma.avail_in = len;
02165 do {
02166 this->lzma.next_out = buf;
02167 this->lzma.avail_out = sizeof(buf);
02168
02169 lzma_ret r = lzma_code(&this->lzma, action);
02170
02171
02172 if ((n = sizeof(buf) - this->lzma.avail_out) != 0) {
02173 this->chain->Write(buf, n);
02174 }
02175 if (r == LZMA_STREAM_END) break;
02176 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
02177 } while (this->lzma.avail_in || !this->lzma.avail_out);
02178 }
02179
02180 void Write(byte *buf, size_t size)
02181 {
02182 this->WriteLoop(buf, size, LZMA_RUN);
02183 }
02184
02185 void Finish()
02186 {
02187 this->WriteLoop(NULL, 0, LZMA_FINISH);
02188 this->chain->Finish();
02189 }
02190 };
02191
02192 #endif
02193
02194
02195
02196
02197
02199 struct SaveLoadFormat {
02200 const char *name;
02201 uint32 tag;
02202
02203 LoadFilter *(*init_load)(LoadFilter *chain);
02204 SaveFilter *(*init_write)(SaveFilter *chain, byte compression);
02205
02206 byte min_compression;
02207 byte default_compression;
02208 byte max_compression;
02209 };
02210
02212 static const SaveLoadFormat _saveload_formats[] = {
02213 #if defined(WITH_LZO)
02214
02215 {"lzo", TO_BE32X('OTTD'), CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
02216 #else
02217 {"lzo", TO_BE32X('OTTD'), NULL, NULL, 0, 0, 0},
02218 #endif
02219
02220 {"none", TO_BE32X('OTTN'), CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
02221 #if defined(WITH_ZLIB)
02222
02223
02224
02225 {"zlib", TO_BE32X('OTTZ'), CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
02226 #else
02227 {"zlib", TO_BE32X('OTTZ'), NULL, NULL, 0, 0, 0},
02228 #endif
02229 #if defined(WITH_LZMA)
02230
02231
02232
02233
02234
02235 {"lzma", TO_BE32X('OTTX'), CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
02236 #else
02237 {"lzma", TO_BE32X('OTTX'), NULL, NULL, 0, 0, 0},
02238 #endif
02239 };
02240
02248 static const SaveLoadFormat *GetSavegameFormat(char *s, byte *compression_level)
02249 {
02250 const SaveLoadFormat *def = lastof(_saveload_formats);
02251
02252
02253 while (!def->init_write) def--;
02254
02255 if (!StrEmpty(s)) {
02256
02257 char *complevel = strrchr(s, ':');
02258 if (complevel != NULL) *complevel = '\0';
02259
02260 for (const SaveLoadFormat *slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) {
02261 if (slf->init_write != NULL && strcmp(s, slf->name) == 0) {
02262 *compression_level = slf->default_compression;
02263 if (complevel != NULL) {
02264
02265
02266
02267 *complevel = ':';
02268 complevel++;
02269
02270
02271 char *end;
02272 long level = strtol(complevel, &end, 10);
02273 if (end == complevel || level != Clamp(level, slf->min_compression, slf->max_compression)) {
02274 ShowInfoF("Compression level '%s' is not valid.", complevel);
02275 } else {
02276 *compression_level = level;
02277 }
02278 }
02279 return slf;
02280 }
02281 }
02282
02283 ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
02284
02285
02286 if (complevel != NULL) *complevel = ':';
02287 }
02288 *compression_level = def->default_compression;
02289 return def;
02290 }
02291
02292
02293 void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
02294 extern bool AfterLoadGame();
02295 extern bool LoadOldSaveGame(const char *file);
02296
02300 static inline void ClearSaveLoadState()
02301 {
02302 delete _sl.dumper;
02303 _sl.dumper = NULL;
02304
02305 delete _sl.sf;
02306 _sl.sf = NULL;
02307
02308 delete _sl.reader;
02309 _sl.reader = NULL;
02310
02311 delete _sl.lf;
02312 _sl.lf = NULL;
02313 }
02314
02320 static void SaveFileStart()
02321 {
02322 _sl.ff_state = _fast_forward;
02323 _fast_forward = 0;
02324 if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
02325
02326 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START);
02327 _sl.saveinprogress = true;
02328 }
02329
02331 static void SaveFileDone()
02332 {
02333 if (_game_mode != GM_MENU) _fast_forward = _sl.ff_state;
02334 if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
02335
02336 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH);
02337 _sl.saveinprogress = false;
02338 }
02339
02341 void SetSaveLoadError(StringID str)
02342 {
02343 _sl.error_str = str;
02344 }
02345
02347 const char *GetSaveLoadErrorString()
02348 {
02349 SetDParam(0, _sl.error_str);
02350 SetDParamStr(1, _sl.extra_msg);
02351
02352 static char err_str[512];
02353 GetString(err_str, _sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED, lastof(err_str));
02354 return err_str;
02355 }
02356
02358 static void SaveFileError()
02359 {
02360 SetDParamStr(0, GetSaveLoadErrorString());
02361 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02362 SaveFileDone();
02363 }
02364
02369 static SaveOrLoadResult SaveFileToDisk(bool threaded)
02370 {
02371 try {
02372 byte compression;
02373 const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
02374
02375
02376 uint32 hdr[2] = { fmt->tag, TO_BE32(SAVEGAME_VERSION << 16) };
02377 _sl.sf->Write((byte*)hdr, sizeof(hdr));
02378
02379 _sl.sf = fmt->init_write(_sl.sf, compression);
02380 _sl.dumper->Flush(_sl.sf);
02381
02382 ClearSaveLoadState();
02383
02384 if (threaded) SetAsyncSaveFinish(SaveFileDone);
02385
02386 return SL_OK;
02387 } catch (...) {
02388 ClearSaveLoadState();
02389
02390 AsyncSaveFinishProc asfp = SaveFileDone;
02391
02392
02393
02394 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
02395
02396 DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
02397 asfp = SaveFileError;
02398 }
02399
02400 if (threaded) {
02401 SetAsyncSaveFinish(asfp);
02402 } else {
02403 asfp();
02404 }
02405 return SL_ERROR;
02406 }
02407 }
02408
02410 static void SaveFileToDiskThread(void *arg)
02411 {
02412 SaveFileToDisk(true);
02413 }
02414
02415 void WaitTillSaved()
02416 {
02417 if (_save_thread == NULL) return;
02418
02419 _save_thread->Join();
02420 delete _save_thread;
02421 _save_thread = NULL;
02422
02423
02424 ProcessAsyncSaveFinish();
02425 }
02426
02435 static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded)
02436 {
02437 assert(!_sl.saveinprogress);
02438
02439 _sl.dumper = new MemoryDumper();
02440 _sl.sf = writer;
02441
02442 _sl_version = SAVEGAME_VERSION;
02443
02444 SaveViewportBeforeSaveGame();
02445 SlSaveChunks();
02446
02447 SaveFileStart();
02448 if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread)) {
02449 if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
02450
02451 SaveOrLoadResult result = SaveFileToDisk(false);
02452 SaveFileDone();
02453
02454 return result;
02455 }
02456
02457 return SL_OK;
02458 }
02459
02466 SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded)
02467 {
02468 try {
02469 _sl.action = SLA_SAVE;
02470 return DoSave(writer, threaded);
02471 } catch (...) {
02472 ClearSaveLoadState();
02473 return SL_ERROR;
02474 }
02475 }
02476
02483 static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
02484 {
02485 _sl.lf = reader;
02486
02487 if (load_check) {
02488
02489 _load_check_data.Clear();
02490
02491 _load_check_data.checkable = true;
02492 }
02493
02494 uint32 hdr[2];
02495 if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
02496
02497
02498 const SaveLoadFormat *fmt = _saveload_formats;
02499 for (;;) {
02500
02501 if (fmt == endof(_saveload_formats)) {
02502 DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
02503 _sl.lf->Reset();
02504 _sl_version = 0;
02505 _sl_minor_version = 0;
02506
02507
02508 fmt = _saveload_formats;
02509 for (;;) {
02510 if (fmt == endof(_saveload_formats)) {
02511
02512 NOT_REACHED();
02513 }
02514 if (fmt->tag == TO_BE32X('OTTD')) break;
02515 fmt++;
02516 }
02517 break;
02518 }
02519
02520 if (fmt->tag == hdr[0]) {
02521
02522 _sl_version = TO_BE32(hdr[1]) >> 16;
02523
02524
02525
02526
02527 _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
02528
02529 DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
02530
02531
02532 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
02533 break;
02534 }
02535
02536 fmt++;
02537 }
02538
02539
02540 if (fmt->init_load == NULL) {
02541 char err_str[64];
02542 snprintf(err_str, lengthof(err_str), "Loader for '%s' is not available.", fmt->name);
02543 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
02544 }
02545
02546 _sl.lf = fmt->init_load(_sl.lf);
02547 _sl.reader = new ReadBuffer(_sl.lf);
02548 _next_offs = 0;
02549
02550 if (!load_check) {
02551 _engine_mngr.ResetToDefaultMapping();
02552
02553
02554
02555
02556 InitializeGame(256, 256, true, true);
02557
02558 GamelogReset();
02559
02560 if (IsSavegameVersionBefore(4)) {
02561
02562
02563
02564
02565
02566
02567
02568
02569
02570
02571
02572
02573
02574
02575
02576
02577
02578
02579
02580
02581
02582 ClearGRFConfigList(&_grfconfig);
02583 }
02584 }
02585
02586 if (load_check) {
02587
02588
02589 SlLoadCheckChunks();
02590 } else {
02591
02592 SlLoadChunks();
02593 SlFixPointers();
02594 }
02595
02596 ClearSaveLoadState();
02597
02598 _savegame_type = SGT_OTTD;
02599
02600 if (load_check) {
02601
02602 _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
02603 } else {
02604 GamelogStartAction(GLAT_LOAD);
02605
02606
02607
02608 if (!AfterLoadGame()) {
02609 GamelogStopAction();
02610 return SL_REINIT;
02611 }
02612
02613 GamelogStopAction();
02614 }
02615
02616 return SL_OK;
02617 }
02618
02624 SaveOrLoadResult LoadWithFilter(LoadFilter *reader)
02625 {
02626 try {
02627 _sl.action = SLA_LOAD;
02628 return DoLoad(reader, false);
02629 } catch (...) {
02630 ClearSaveLoadState();
02631 return SL_REINIT;
02632 }
02633 }
02634
02644 SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, bool threaded)
02645 {
02646
02647 if (_sl.saveinprogress && mode == SL_SAVE && threaded) {
02648
02649 if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR);
02650 return SL_OK;
02651 }
02652 WaitTillSaved();
02653
02654
02655 if (mode == SL_OLD_LOAD) {
02656 _engine_mngr.ResetToDefaultMapping();
02657 InitializeGame(256, 256, true, true);
02658
02659
02660
02661
02662
02663 ClearGRFConfigList(&_grfconfig);
02664 GamelogReset();
02665 if (!LoadOldSaveGame(filename)) return SL_REINIT;
02666 _sl_version = 0;
02667 _sl_minor_version = 0;
02668 GamelogStartAction(GLAT_LOAD);
02669 if (!AfterLoadGame()) {
02670 GamelogStopAction();
02671 return SL_REINIT;
02672 }
02673 GamelogStopAction();
02674 return SL_OK;
02675 }
02676
02677 switch (mode) {
02678 case SL_LOAD_CHECK: _sl.action = SLA_LOAD_CHECK; break;
02679 case SL_LOAD: _sl.action = SLA_LOAD; break;
02680 case SL_SAVE: _sl.action = SLA_SAVE; break;
02681 default: NOT_REACHED();
02682 }
02683
02684 try {
02685 FILE *fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
02686
02687
02688 if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
02689 if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
02690
02691 if (fh == NULL) {
02692 SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
02693 }
02694
02695 if (mode == SL_SAVE) {
02696 DEBUG(desync, 1, "save: %08x; %02x; %s", _date, _date_fract, filename);
02697 if (_network_server || !_settings_client.gui.threaded_saves) threaded = false;
02698
02699 return DoSave(new FileWriter(fh), threaded);
02700 }
02701
02702
02703 assert(mode == SL_LOAD || mode == SL_LOAD_CHECK);
02704 DEBUG(desync, 1, "load: %s", filename);
02705 return DoLoad(new FileReader(fh), mode == SL_LOAD_CHECK);
02706 } catch (...) {
02707 ClearSaveLoadState();
02708
02709
02710 if (mode != SL_LOAD_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
02711
02712
02713 return (mode == SL_LOAD) ? SL_REINIT : SL_ERROR;
02714 }
02715 }
02716
02718 void DoExitSave()
02719 {
02720 SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR);
02721 }
02722
02728 void GenerateDefaultSaveName(char *buf, const char *last)
02729 {
02730
02731
02732
02733 CompanyID cid = _local_company;
02734 if (!Company::IsValidID(cid)) {
02735 const Company *c;
02736 FOR_ALL_COMPANIES(c) {
02737 cid = c->index;
02738 break;
02739 }
02740 }
02741
02742 SetDParam(0, cid);
02743
02744
02745 switch (_settings_client.gui.date_format_in_default_names) {
02746 case 0: SetDParam(1, STR_JUST_DATE_LONG); break;
02747 case 1: SetDParam(1, STR_JUST_DATE_TINY); break;
02748 case 2: SetDParam(1, STR_JUST_DATE_ISO); break;
02749 default: NOT_REACHED();
02750 }
02751 SetDParam(2, _date);
02752
02753
02754 GetString(buf, !Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, last);
02755 SanitizeFilename(buf);
02756 }
02757
02758 #if 0
02759
02765 int GetSavegameType(char *file)
02766 {
02767 const SaveLoadFormat *fmt;
02768 uint32 hdr;
02769 FILE *f;
02770 int mode = SL_OLD_LOAD;
02771
02772 f = fopen(file, "rb");
02773 if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
02774 DEBUG(sl, 0, "Savegame is obsolete or invalid format");
02775 mode = SL_LOAD;
02776 } else {
02777
02778 for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) {
02779 if (fmt->tag == hdr) {
02780 mode = SL_LOAD;
02781 break;
02782 }
02783 }
02784 }
02785
02786 fclose(f);
02787 return mode;
02788 }
02789 #endif