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 DEBUG(sl, 1, "Nulling pointers");
00454
00455 FOR_ALL_CHUNK_HANDLERS(ch) {
00456 if (ch->ptrs_proc != NULL) {
00457 DEBUG(sl, 2, "Nulling pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
00458 ch->ptrs_proc();
00459 }
00460 }
00461
00462 DEBUG(sl, 1, "All pointers nulled");
00463
00464 assert(_sl.action == SLA_NULL);
00465 }
00466
00475 void NORETURN SlError(StringID string, const char *extra_msg)
00476 {
00477
00478 if (_sl.action == SLA_LOAD_CHECK) {
00479 _load_check_data.error = string;
00480 free(_load_check_data.error_data);
00481 _load_check_data.error_data = (extra_msg == NULL) ? NULL : strdup(extra_msg);
00482 } else {
00483 _sl.error_str = string;
00484 free(_sl.extra_msg);
00485 _sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg);
00486
00487
00488
00489
00490 }
00491 if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers();
00492 throw std::exception();
00493 }
00494
00502 void NORETURN SlErrorCorrupt(const char *msg)
00503 {
00504 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
00505 }
00506
00507
00508 typedef void (*AsyncSaveFinishProc)();
00509 static AsyncSaveFinishProc _async_save_finish = NULL;
00510 static ThreadObject *_save_thread;
00511
00516 static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
00517 {
00518 if (_exit_game) return;
00519 while (_async_save_finish != NULL) CSleep(10);
00520
00521 _async_save_finish = proc;
00522 }
00523
00527 void ProcessAsyncSaveFinish()
00528 {
00529 if (_async_save_finish == NULL) return;
00530
00531 _async_save_finish();
00532
00533 _async_save_finish = NULL;
00534
00535 if (_save_thread != NULL) {
00536 _save_thread->Join();
00537 delete _save_thread;
00538 _save_thread = NULL;
00539 }
00540 }
00541
00546 byte SlReadByte()
00547 {
00548 return _sl.reader->ReadByte();
00549 }
00550
00555 void SlWriteByte(byte b)
00556 {
00557 _sl.dumper->WriteByte(b);
00558 }
00559
00560 static inline int SlReadUint16()
00561 {
00562 int x = SlReadByte() << 8;
00563 return x | SlReadByte();
00564 }
00565
00566 static inline uint32 SlReadUint32()
00567 {
00568 uint32 x = SlReadUint16() << 16;
00569 return x | SlReadUint16();
00570 }
00571
00572 static inline uint64 SlReadUint64()
00573 {
00574 uint32 x = SlReadUint32();
00575 uint32 y = SlReadUint32();
00576 return (uint64)x << 32 | y;
00577 }
00578
00579 static inline void SlWriteUint16(uint16 v)
00580 {
00581 SlWriteByte(GB(v, 8, 8));
00582 SlWriteByte(GB(v, 0, 8));
00583 }
00584
00585 static inline void SlWriteUint32(uint32 v)
00586 {
00587 SlWriteUint16(GB(v, 16, 16));
00588 SlWriteUint16(GB(v, 0, 16));
00589 }
00590
00591 static inline void SlWriteUint64(uint64 x)
00592 {
00593 SlWriteUint32((uint32)(x >> 32));
00594 SlWriteUint32((uint32)x);
00595 }
00596
00602 static inline void SlSkipBytes(size_t length)
00603 {
00604 for (; length != 0; length--) SlReadByte();
00605 }
00606
00616 static uint SlReadSimpleGamma()
00617 {
00618 uint i = SlReadByte();
00619 if (HasBit(i, 7)) {
00620 i &= ~0x80;
00621 if (HasBit(i, 6)) {
00622 i &= ~0x40;
00623 if (HasBit(i, 5)) {
00624 i &= ~0x20;
00625 if (HasBit(i, 4)) {
00626 SlErrorCorrupt("Unsupported gamma");
00627 }
00628 i = (i << 8) | SlReadByte();
00629 }
00630 i = (i << 8) | SlReadByte();
00631 }
00632 i = (i << 8) | SlReadByte();
00633 }
00634 return i;
00635 }
00636
00649 static void SlWriteSimpleGamma(size_t i)
00650 {
00651 if (i >= (1 << 7)) {
00652 if (i >= (1 << 14)) {
00653 if (i >= (1 << 21)) {
00654 assert(i < (1 << 28));
00655 SlWriteByte((byte)(0xE0 | (i >> 24)));
00656 SlWriteByte((byte)(i >> 16));
00657 } else {
00658 SlWriteByte((byte)(0xC0 | (i >> 16)));
00659 }
00660 SlWriteByte((byte)(i >> 8));
00661 } else {
00662 SlWriteByte((byte)(0x80 | (i >> 8)));
00663 }
00664 }
00665 SlWriteByte((byte)i);
00666 }
00667
00669 static inline uint SlGetGammaLength(size_t i)
00670 {
00671 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21));
00672 }
00673
00674 static inline uint SlReadSparseIndex()
00675 {
00676 return SlReadSimpleGamma();
00677 }
00678
00679 static inline void SlWriteSparseIndex(uint index)
00680 {
00681 SlWriteSimpleGamma(index);
00682 }
00683
00684 static inline uint SlReadArrayLength()
00685 {
00686 return SlReadSimpleGamma();
00687 }
00688
00689 static inline void SlWriteArrayLength(size_t length)
00690 {
00691 SlWriteSimpleGamma(length);
00692 }
00693
00694 static inline uint SlGetArrayLength(size_t length)
00695 {
00696 return SlGetGammaLength(length);
00697 }
00698
00705 static inline uint SlCalcConvMemLen(VarType conv)
00706 {
00707 static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0};
00708 byte length = GB(conv, 4, 4);
00709
00710 switch (length << 4) {
00711 case SLE_VAR_STRB:
00712 case SLE_VAR_STRBQ:
00713 case SLE_VAR_STR:
00714 case SLE_VAR_STRQ:
00715 return SlReadArrayLength();
00716
00717 default:
00718 assert(length < lengthof(conv_mem_size));
00719 return conv_mem_size[length];
00720 }
00721 }
00722
00729 static inline byte SlCalcConvFileLen(VarType conv)
00730 {
00731 static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2};
00732 byte length = GB(conv, 0, 4);
00733 assert(length < lengthof(conv_file_size));
00734 return conv_file_size[length];
00735 }
00736
00738 static inline size_t SlCalcRefLen()
00739 {
00740 return IsSavegameVersionBefore(69) ? 2 : 4;
00741 }
00742
00743 void SlSetArrayIndex(uint index)
00744 {
00745 _sl.need_length = NL_WANTLENGTH;
00746 _sl.array_index = index;
00747 }
00748
00749 static size_t _next_offs;
00750
00755 int SlIterateArray()
00756 {
00757 int index;
00758
00759
00760
00761 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) SlErrorCorrupt("Invalid chunk size");
00762
00763 while (true) {
00764 uint length = SlReadArrayLength();
00765 if (length == 0) {
00766 _next_offs = 0;
00767 return -1;
00768 }
00769
00770 _sl.obj_len = --length;
00771 _next_offs = _sl.reader->GetSize() + length;
00772
00773 switch (_sl.block_mode) {
00774 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
00775 case CH_ARRAY: index = _sl.array_index++; break;
00776 default:
00777 DEBUG(sl, 0, "SlIterateArray error");
00778 return -1;
00779 }
00780
00781 if (length != 0) return index;
00782 }
00783 }
00784
00788 void SlSkipArray()
00789 {
00790 while (SlIterateArray() != -1) {
00791 SlSkipBytes(_next_offs - _sl.reader->GetSize());
00792 }
00793 }
00794
00800 void SlSetLength(size_t length)
00801 {
00802 assert(_sl.action == SLA_SAVE);
00803
00804 switch (_sl.need_length) {
00805 case NL_WANTLENGTH:
00806 _sl.need_length = NL_NONE;
00807 switch (_sl.block_mode) {
00808 case CH_RIFF:
00809
00810
00811
00812 assert(length < (1 << 28));
00813 SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28)));
00814 break;
00815 case CH_ARRAY:
00816 assert(_sl.last_array_index <= _sl.array_index);
00817 while (++_sl.last_array_index <= _sl.array_index) {
00818 SlWriteArrayLength(1);
00819 }
00820 SlWriteArrayLength(length + 1);
00821 break;
00822 case CH_SPARSE_ARRAY:
00823 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index));
00824 SlWriteSparseIndex(_sl.array_index);
00825 break;
00826 default: NOT_REACHED();
00827 }
00828 break;
00829
00830 case NL_CALCLENGTH:
00831 _sl.obj_len += (int)length;
00832 break;
00833
00834 default: NOT_REACHED();
00835 }
00836 }
00837
00844 static void SlCopyBytes(void *ptr, size_t length)
00845 {
00846 byte *p = (byte *)ptr;
00847
00848 switch (_sl.action) {
00849 case SLA_LOAD_CHECK:
00850 case SLA_LOAD:
00851 for (; length != 0; length--) *p++ = SlReadByte();
00852 break;
00853 case SLA_SAVE:
00854 for (; length != 0; length--) SlWriteByte(*p++);
00855 break;
00856 default: NOT_REACHED();
00857 }
00858 }
00859
00861 size_t SlGetFieldLength()
00862 {
00863 return _sl.obj_len;
00864 }
00865
00873 int64 ReadValue(const void *ptr, VarType conv)
00874 {
00875 switch (GetVarMemType(conv)) {
00876 case SLE_VAR_BL: return (*(bool *)ptr != 0);
00877 case SLE_VAR_I8: return *(int8 *)ptr;
00878 case SLE_VAR_U8: return *(byte *)ptr;
00879 case SLE_VAR_I16: return *(int16 *)ptr;
00880 case SLE_VAR_U16: return *(uint16*)ptr;
00881 case SLE_VAR_I32: return *(int32 *)ptr;
00882 case SLE_VAR_U32: return *(uint32*)ptr;
00883 case SLE_VAR_I64: return *(int64 *)ptr;
00884 case SLE_VAR_U64: return *(uint64*)ptr;
00885 case SLE_VAR_NULL:return 0;
00886 default: NOT_REACHED();
00887 }
00888 }
00889
00897 void WriteValue(void *ptr, VarType conv, int64 val)
00898 {
00899 switch (GetVarMemType(conv)) {
00900 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
00901 case SLE_VAR_I8: *(int8 *)ptr = val; break;
00902 case SLE_VAR_U8: *(byte *)ptr = val; break;
00903 case SLE_VAR_I16: *(int16 *)ptr = val; break;
00904 case SLE_VAR_U16: *(uint16*)ptr = val; break;
00905 case SLE_VAR_I32: *(int32 *)ptr = val; break;
00906 case SLE_VAR_U32: *(uint32*)ptr = val; break;
00907 case SLE_VAR_I64: *(int64 *)ptr = val; break;
00908 case SLE_VAR_U64: *(uint64*)ptr = val; break;
00909 case SLE_VAR_NAME: *(char**)ptr = CopyFromOldName(val); break;
00910 case SLE_VAR_NULL: break;
00911 default: NOT_REACHED();
00912 }
00913 }
00914
00923 static void SlSaveLoadConv(void *ptr, VarType conv)
00924 {
00925 switch (_sl.action) {
00926 case SLA_SAVE: {
00927 int64 x = ReadValue(ptr, conv);
00928
00929
00930 switch (GetVarFileType(conv)) {
00931 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
00932 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
00933 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
00934 case SLE_FILE_STRINGID:
00935 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
00936 case SLE_FILE_I32:
00937 case SLE_FILE_U32: SlWriteUint32((uint32)x);break;
00938 case SLE_FILE_I64:
00939 case SLE_FILE_U64: SlWriteUint64(x);break;
00940 default: NOT_REACHED();
00941 }
00942 break;
00943 }
00944 case SLA_LOAD_CHECK:
00945 case SLA_LOAD: {
00946 int64 x;
00947
00948 switch (GetVarFileType(conv)) {
00949 case SLE_FILE_I8: x = (int8 )SlReadByte(); break;
00950 case SLE_FILE_U8: x = (byte )SlReadByte(); break;
00951 case SLE_FILE_I16: x = (int16 )SlReadUint16(); break;
00952 case SLE_FILE_U16: x = (uint16)SlReadUint16(); break;
00953 case SLE_FILE_I32: x = (int32 )SlReadUint32(); break;
00954 case SLE_FILE_U32: x = (uint32)SlReadUint32(); break;
00955 case SLE_FILE_I64: x = (int64 )SlReadUint64(); break;
00956 case SLE_FILE_U64: x = (uint64)SlReadUint64(); break;
00957 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break;
00958 default: NOT_REACHED();
00959 }
00960
00961
00962 WriteValue(ptr, conv, x);
00963 break;
00964 }
00965 case SLA_PTRS: break;
00966 case SLA_NULL: break;
00967 default: NOT_REACHED();
00968 }
00969 }
00970
00980 static inline size_t SlCalcNetStringLen(const char *ptr, size_t length)
00981 {
00982 if (ptr == NULL) return 0;
00983 return min(strlen(ptr), length - 1);
00984 }
00985
00995 static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv)
00996 {
00997 size_t len;
00998 const char *str;
00999
01000 switch (GetVarMemType(conv)) {
01001 default: NOT_REACHED();
01002 case SLE_VAR_STR:
01003 case SLE_VAR_STRQ:
01004 str = *(const char**)ptr;
01005 len = SIZE_MAX;
01006 break;
01007 case SLE_VAR_STRB:
01008 case SLE_VAR_STRBQ:
01009 str = (const char*)ptr;
01010 len = length;
01011 break;
01012 }
01013
01014 len = SlCalcNetStringLen(str, len);
01015 return len + SlGetArrayLength(len);
01016 }
01017
01024 static void SlString(void *ptr, size_t length, VarType conv)
01025 {
01026 switch (_sl.action) {
01027 case SLA_SAVE: {
01028 size_t len;
01029 switch (GetVarMemType(conv)) {
01030 default: NOT_REACHED();
01031 case SLE_VAR_STRB:
01032 case SLE_VAR_STRBQ:
01033 len = SlCalcNetStringLen((char *)ptr, length);
01034 break;
01035 case SLE_VAR_STR:
01036 case SLE_VAR_STRQ:
01037 ptr = *(char **)ptr;
01038 len = SlCalcNetStringLen((char *)ptr, SIZE_MAX);
01039 break;
01040 }
01041
01042 SlWriteArrayLength(len);
01043 SlCopyBytes(ptr, len);
01044 break;
01045 }
01046 case SLA_LOAD_CHECK:
01047 case SLA_LOAD: {
01048 size_t len = SlReadArrayLength();
01049
01050 switch (GetVarMemType(conv)) {
01051 default: NOT_REACHED();
01052 case SLE_VAR_STRB:
01053 case SLE_VAR_STRBQ:
01054 if (len >= length) {
01055 DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating");
01056 SlCopyBytes(ptr, length);
01057 SlSkipBytes(len - length);
01058 len = length - 1;
01059 } else {
01060 SlCopyBytes(ptr, len);
01061 }
01062 break;
01063 case SLE_VAR_STR:
01064 case SLE_VAR_STRQ:
01065 free(*(char **)ptr);
01066 if (len == 0) {
01067 *(char **)ptr = NULL;
01068 } else {
01069 *(char **)ptr = MallocT<char>(len + 1);
01070 ptr = *(char **)ptr;
01071 SlCopyBytes(ptr, len);
01072 }
01073 break;
01074 }
01075
01076 ((char *)ptr)[len] = '\0';
01077 str_validate((char *)ptr, (char *)ptr + len);
01078 break;
01079 }
01080 case SLA_PTRS: break;
01081 case SLA_NULL: break;
01082 default: NOT_REACHED();
01083 }
01084 }
01085
01091 static inline size_t SlCalcArrayLen(size_t length, VarType conv)
01092 {
01093 return SlCalcConvFileLen(conv) * length;
01094 }
01095
01102 void SlArray(void *array, size_t length, VarType conv)
01103 {
01104 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
01105
01106
01107 if (_sl.need_length != NL_NONE) {
01108 SlSetLength(SlCalcArrayLen(length, conv));
01109
01110 if (_sl.need_length == NL_CALCLENGTH) return;
01111 }
01112
01113
01114
01115 if (_sl.action != SLA_SAVE && _sl_version == 0) {
01116
01117 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
01118 conv == SLE_INT32 || conv == SLE_UINT32) {
01119 SlCopyBytes(array, length * SlCalcConvFileLen(conv));
01120 return;
01121 }
01122
01123 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
01124 for (uint i = 0; i < length; i++) {
01125 ((int64*)array)[i] = (int32)BSWAP32(SlReadUint32());
01126 }
01127 return;
01128 }
01129 }
01130
01131
01132
01133 if (conv == SLE_INT8 || conv == SLE_UINT8) {
01134 SlCopyBytes(array, length);
01135 } else {
01136 byte *a = (byte*)array;
01137 byte mem_size = SlCalcConvMemLen(conv);
01138
01139 for (; length != 0; length --) {
01140 SlSaveLoadConv(a, conv);
01141 a += mem_size;
01142 }
01143 }
01144 }
01145
01146
01157 static size_t ReferenceToInt(const void *obj, SLRefType rt)
01158 {
01159 assert(_sl.action == SLA_SAVE);
01160
01161 if (obj == NULL) return 0;
01162
01163 switch (rt) {
01164 case REF_VEHICLE_OLD:
01165 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
01166 case REF_STATION: return ((const Station*)obj)->index + 1;
01167 case REF_TOWN: return ((const Town*)obj)->index + 1;
01168 case REF_ORDER: return ((const Order*)obj)->index + 1;
01169 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
01170 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
01171 case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
01172 case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
01173 default: NOT_REACHED();
01174 }
01175 }
01176
01187 static void *IntToReference(size_t index, SLRefType rt)
01188 {
01189 assert_compile(sizeof(size_t) <= sizeof(void *));
01190
01191 assert(_sl.action == SLA_PTRS);
01192
01193
01194
01195 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(4, 4)) {
01196 rt = REF_VEHICLE;
01197 }
01198
01199
01200 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return NULL;
01201
01202
01203
01204 if (rt != REF_VEHICLE_OLD) index--;
01205
01206 switch (rt) {
01207 case REF_ORDERLIST:
01208 if (OrderList::IsValidID(index)) return OrderList::Get(index);
01209 SlErrorCorrupt("Referencing invalid OrderList");
01210
01211 case REF_ORDER:
01212 if (Order::IsValidID(index)) return Order::Get(index);
01213
01214 if (IsSavegameVersionBefore(5, 2)) return NULL;
01215 SlErrorCorrupt("Referencing invalid Order");
01216
01217 case REF_VEHICLE_OLD:
01218 case REF_VEHICLE:
01219 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
01220 SlErrorCorrupt("Referencing invalid Vehicle");
01221
01222 case REF_STATION:
01223 if (Station::IsValidID(index)) return Station::Get(index);
01224 SlErrorCorrupt("Referencing invalid Station");
01225
01226 case REF_TOWN:
01227 if (Town::IsValidID(index)) return Town::Get(index);
01228 SlErrorCorrupt("Referencing invalid Town");
01229
01230 case REF_ROADSTOPS:
01231 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
01232 SlErrorCorrupt("Referencing invalid RoadStop");
01233
01234 case REF_ENGINE_RENEWS:
01235 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
01236 SlErrorCorrupt("Referencing invalid EngineRenew");
01237
01238 case REF_CARGO_PACKET:
01239 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
01240 SlErrorCorrupt("Referencing invalid CargoPacket");
01241
01242 default: NOT_REACHED();
01243 }
01244 }
01245
01250 static inline size_t SlCalcListLen(const void *list)
01251 {
01252 std::list<void *> *l = (std::list<void *> *) list;
01253
01254 int type_size = IsSavegameVersionBefore(69) ? 2 : 4;
01255
01256
01257 return l->size() * type_size + type_size;
01258 }
01259
01260
01266 static void SlList(void *list, SLRefType conv)
01267 {
01268
01269 if (_sl.need_length != NL_NONE) {
01270 SlSetLength(SlCalcListLen(list));
01271
01272 if (_sl.need_length == NL_CALCLENGTH) return;
01273 }
01274
01275 typedef std::list<void *> PtrList;
01276 PtrList *l = (PtrList *)list;
01277
01278 switch (_sl.action) {
01279 case SLA_SAVE: {
01280 SlWriteUint32((uint32)l->size());
01281
01282 PtrList::iterator iter;
01283 for (iter = l->begin(); iter != l->end(); ++iter) {
01284 void *ptr = *iter;
01285 SlWriteUint32((uint32)ReferenceToInt(ptr, conv));
01286 }
01287 break;
01288 }
01289 case SLA_LOAD_CHECK:
01290 case SLA_LOAD: {
01291 size_t length = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01292
01293
01294 for (size_t i = 0; i < length; i++) {
01295 size_t data = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01296 l->push_back((void *)data);
01297 }
01298 break;
01299 }
01300 case SLA_PTRS: {
01301 PtrList temp = *l;
01302
01303 l->clear();
01304 PtrList::iterator iter;
01305 for (iter = temp.begin(); iter != temp.end(); ++iter) {
01306 void *ptr = IntToReference((size_t)*iter, conv);
01307 l->push_back(ptr);
01308 }
01309 break;
01310 }
01311 case SLA_NULL:
01312 l->clear();
01313 break;
01314 default: NOT_REACHED();
01315 }
01316 }
01317
01318
01320 static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
01321 {
01322 if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false;
01323 if (sld->conv & SLF_SAVE_NO) return false;
01324
01325 return true;
01326 }
01327
01333 static inline bool SlSkipVariableOnLoad(const SaveLoad *sld)
01334 {
01335 if ((sld->conv & SLF_NETWORK_NO) && _sl.action != SLA_SAVE && _networking && !_network_server) {
01336 SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length);
01337 return true;
01338 }
01339
01340 return false;
01341 }
01342
01349 size_t SlCalcObjLength(const void *object, const SaveLoad *sld)
01350 {
01351 size_t length = 0;
01352
01353
01354 for (; sld->cmd != SL_END; sld++) {
01355 length += SlCalcObjMemberLength(object, sld);
01356 }
01357 return length;
01358 }
01359
01360 size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
01361 {
01362 assert(_sl.action == SLA_SAVE);
01363
01364 switch (sld->cmd) {
01365 case SL_VAR:
01366 case SL_REF:
01367 case SL_ARR:
01368 case SL_STR:
01369 case SL_LST:
01370
01371 if (!SlIsObjectValidInSavegame(sld)) break;
01372
01373 switch (sld->cmd) {
01374 case SL_VAR: return SlCalcConvFileLen(sld->conv);
01375 case SL_REF: return SlCalcRefLen();
01376 case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv);
01377 case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv);
01378 case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld));
01379 default: NOT_REACHED();
01380 }
01381 break;
01382 case SL_WRITEBYTE: return 1;
01383 case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END));
01384 case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription());
01385 default: NOT_REACHED();
01386 }
01387 return 0;
01388 }
01389
01390
01391 bool SlObjectMember(void *ptr, const SaveLoad *sld)
01392 {
01393 VarType conv = GB(sld->conv, 0, 8);
01394 switch (sld->cmd) {
01395 case SL_VAR:
01396 case SL_REF:
01397 case SL_ARR:
01398 case SL_STR:
01399 case SL_LST:
01400
01401 if (!SlIsObjectValidInSavegame(sld)) return false;
01402 if (SlSkipVariableOnLoad(sld)) return false;
01403
01404 switch (sld->cmd) {
01405 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
01406 case SL_REF:
01407 switch (_sl.action) {
01408 case SLA_SAVE:
01409 SlWriteUint32((uint32)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
01410 break;
01411 case SLA_LOAD_CHECK:
01412 case SLA_LOAD:
01413 *(size_t *)ptr = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01414 break;
01415 case SLA_PTRS:
01416 *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
01417 break;
01418 case SLA_NULL:
01419 *(void **)ptr = NULL;
01420 break;
01421 default: NOT_REACHED();
01422 }
01423 break;
01424 case SL_ARR: SlArray(ptr, sld->length, conv); break;
01425 case SL_STR: SlString(ptr, sld->length, conv); break;
01426 case SL_LST: SlList(ptr, (SLRefType)conv); break;
01427 default: NOT_REACHED();
01428 }
01429 break;
01430
01431
01432
01433
01434
01435
01436 case SL_WRITEBYTE:
01437 switch (_sl.action) {
01438 case SLA_SAVE: SlWriteByte(sld->version_to); break;
01439 case SLA_LOAD_CHECK:
01440 case SLA_LOAD: *(byte *)ptr = sld->version_from; break;
01441 case SLA_PTRS: break;
01442 case SLA_NULL: break;
01443 default: NOT_REACHED();
01444 }
01445 break;
01446
01447
01448 case SL_VEH_INCLUDE:
01449 SlObject(ptr, GetVehicleDescription(VEH_END));
01450 break;
01451
01452 case SL_ST_INCLUDE:
01453 SlObject(ptr, GetBaseStationDescription());
01454 break;
01455
01456 default: NOT_REACHED();
01457 }
01458 return true;
01459 }
01460
01466 void SlObject(void *object, const SaveLoad *sld)
01467 {
01468
01469 if (_sl.need_length != NL_NONE) {
01470 SlSetLength(SlCalcObjLength(object, sld));
01471 if (_sl.need_length == NL_CALCLENGTH) return;
01472 }
01473
01474 for (; sld->cmd != SL_END; sld++) {
01475 void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld);
01476 SlObjectMember(ptr, sld);
01477 }
01478 }
01479
01484 void SlGlobList(const SaveLoadGlobVarList *sldg)
01485 {
01486 SlObject(NULL, (const SaveLoad*)sldg);
01487 }
01488
01494 void SlAutolength(AutolengthProc *proc, void *arg)
01495 {
01496 size_t offs;
01497
01498 assert(_sl.action == SLA_SAVE);
01499
01500
01501 _sl.need_length = NL_CALCLENGTH;
01502 _sl.obj_len = 0;
01503 proc(arg);
01504
01505
01506 _sl.need_length = NL_WANTLENGTH;
01507 SlSetLength(_sl.obj_len);
01508
01509 offs = _sl.dumper->GetSize() + _sl.obj_len;
01510
01511
01512 proc(arg);
01513
01514 if (offs != _sl.dumper->GetSize()) SlErrorCorrupt("Invalid chunk size");
01515 }
01516
01521 static void SlLoadChunk(const ChunkHandler *ch)
01522 {
01523 byte m = SlReadByte();
01524 size_t len;
01525 size_t endoffs;
01526
01527 _sl.block_mode = m;
01528 _sl.obj_len = 0;
01529
01530 switch (m) {
01531 case CH_ARRAY:
01532 _sl.array_index = 0;
01533 ch->load_proc();
01534 break;
01535 case CH_SPARSE_ARRAY:
01536 ch->load_proc();
01537 break;
01538 default:
01539 if ((m & 0xF) == CH_RIFF) {
01540
01541 len = (SlReadByte() << 16) | ((m >> 4) << 24);
01542 len += SlReadUint16();
01543 _sl.obj_len = len;
01544 endoffs = _sl.reader->GetSize() + len;
01545 ch->load_proc();
01546 if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size");
01547 } else {
01548 SlErrorCorrupt("Invalid chunk type");
01549 }
01550 break;
01551 }
01552 }
01553
01559 static void SlLoadCheckChunk(const ChunkHandler *ch)
01560 {
01561 byte m = SlReadByte();
01562 size_t len;
01563 size_t endoffs;
01564
01565 _sl.block_mode = m;
01566 _sl.obj_len = 0;
01567
01568 switch (m) {
01569 case CH_ARRAY:
01570 _sl.array_index = 0;
01571 if (ch->load_check_proc) {
01572 ch->load_check_proc();
01573 } else {
01574 SlSkipArray();
01575 }
01576 break;
01577 case CH_SPARSE_ARRAY:
01578 if (ch->load_check_proc) {
01579 ch->load_check_proc();
01580 } else {
01581 SlSkipArray();
01582 }
01583 break;
01584 default:
01585 if ((m & 0xF) == CH_RIFF) {
01586
01587 len = (SlReadByte() << 16) | ((m >> 4) << 24);
01588 len += SlReadUint16();
01589 _sl.obj_len = len;
01590 endoffs = _sl.reader->GetSize() + len;
01591 if (ch->load_check_proc) {
01592 ch->load_check_proc();
01593 } else {
01594 SlSkipBytes(len);
01595 }
01596 if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size");
01597 } else {
01598 SlErrorCorrupt("Invalid chunk type");
01599 }
01600 break;
01601 }
01602 }
01603
01608 static ChunkSaveLoadProc *_stub_save_proc;
01609
01615 static inline void SlStubSaveProc2(void *arg)
01616 {
01617 _stub_save_proc();
01618 }
01619
01625 static void SlStubSaveProc()
01626 {
01627 SlAutolength(SlStubSaveProc2, NULL);
01628 }
01629
01635 static void SlSaveChunk(const ChunkHandler *ch)
01636 {
01637 ChunkSaveLoadProc *proc = ch->save_proc;
01638
01639
01640 if (proc == NULL) return;
01641
01642 SlWriteUint32(ch->id);
01643 DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
01644
01645 if (ch->flags & CH_AUTO_LENGTH) {
01646
01647 _stub_save_proc = proc;
01648 proc = SlStubSaveProc;
01649 }
01650
01651 _sl.block_mode = ch->flags & CH_TYPE_MASK;
01652 switch (ch->flags & CH_TYPE_MASK) {
01653 case CH_RIFF:
01654 _sl.need_length = NL_WANTLENGTH;
01655 proc();
01656 break;
01657 case CH_ARRAY:
01658 _sl.last_array_index = 0;
01659 SlWriteByte(CH_ARRAY);
01660 proc();
01661 SlWriteArrayLength(0);
01662 break;
01663 case CH_SPARSE_ARRAY:
01664 SlWriteByte(CH_SPARSE_ARRAY);
01665 proc();
01666 SlWriteArrayLength(0);
01667 break;
01668 default: NOT_REACHED();
01669 }
01670 }
01671
01673 static void SlSaveChunks()
01674 {
01675 FOR_ALL_CHUNK_HANDLERS(ch) {
01676 SlSaveChunk(ch);
01677 }
01678
01679
01680 SlWriteUint32(0);
01681 }
01682
01689 static const ChunkHandler *SlFindChunkHandler(uint32 id)
01690 {
01691 FOR_ALL_CHUNK_HANDLERS(ch) if (ch->id == id) return ch;
01692 return NULL;
01693 }
01694
01696 static void SlLoadChunks()
01697 {
01698 uint32 id;
01699 const ChunkHandler *ch;
01700
01701 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
01702 DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
01703
01704 ch = SlFindChunkHandler(id);
01705 if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
01706 SlLoadChunk(ch);
01707 }
01708 }
01709
01711 static void SlLoadCheckChunks()
01712 {
01713 uint32 id;
01714 const ChunkHandler *ch;
01715
01716 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
01717 DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
01718
01719 ch = SlFindChunkHandler(id);
01720 if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
01721 SlLoadCheckChunk(ch);
01722 }
01723 }
01724
01726 static void SlFixPointers()
01727 {
01728 _sl.action = SLA_PTRS;
01729
01730 DEBUG(sl, 1, "Fixing pointers");
01731
01732 FOR_ALL_CHUNK_HANDLERS(ch) {
01733 if (ch->ptrs_proc != NULL) {
01734 DEBUG(sl, 2, "Fixing pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
01735 ch->ptrs_proc();
01736 }
01737 }
01738
01739 DEBUG(sl, 1, "All pointers fixed");
01740
01741 assert(_sl.action == SLA_PTRS);
01742 }
01743
01744
01746 struct FileReader : LoadFilter {
01747 FILE *file;
01748 long begin;
01749
01754 FileReader(FILE *file) : LoadFilter(NULL), file(file), begin(ftell(file))
01755 {
01756 }
01757
01759 ~FileReader()
01760 {
01761 if (this->file != NULL) fclose(this->file);
01762 this->file = NULL;
01763
01764
01765 _sl.sf = NULL;
01766 }
01767
01768 size_t Read(byte *buf, size_t size)
01769 {
01770
01771 if (this->file == NULL) return 0;
01772
01773 return fread(buf, 1, size, this->file);
01774 }
01775
01776 void Reset()
01777 {
01778 clearerr(this->file);
01779 fseek(this->file, this->begin, SEEK_SET);
01780 }
01781 };
01782
01784 struct FileWriter : SaveFilter {
01785 FILE *file;
01786
01791 FileWriter(FILE *file) : SaveFilter(NULL), file(file)
01792 {
01793 }
01794
01796 ~FileWriter()
01797 {
01798 this->Finish();
01799
01800
01801 _sl.sf = NULL;
01802 }
01803
01804 void Write(byte *buf, size_t size)
01805 {
01806
01807 if (this->file == NULL) return;
01808
01809 if (fwrite(buf, 1, size, this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
01810 }
01811
01812 void Finish()
01813 {
01814 if (this->file != NULL) fclose(this->file);
01815 this->file = NULL;
01816 }
01817 };
01818
01819
01820
01821
01822
01823 #ifdef WITH_LZO
01824 #include <lzo/lzo1x.h>
01825
01827 static const uint LZO_BUFFER_SIZE = 8192;
01828
01830 struct LZOLoadFilter : LoadFilter {
01835 LZOLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01836 {
01837 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
01838 }
01839
01840 size_t Read(byte *buf, size_t ssize)
01841 {
01842 assert(ssize >= LZO_BUFFER_SIZE);
01843
01844
01845 byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2];
01846 uint32 tmp[2];
01847 uint32 size;
01848 lzo_uint len;
01849
01850
01851 if (this->chain->Read((byte*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
01852
01853
01854 ((uint32*)out)[0] = size = tmp[1];
01855
01856 if (_sl_version != 0) {
01857 tmp[0] = TO_BE32(tmp[0]);
01858 size = TO_BE32(size);
01859 }
01860
01861 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
01862
01863
01864 if (this->chain->Read(out + sizeof(uint32), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
01865
01866
01867 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlErrorCorrupt("Bad checksum");
01868
01869
01870 lzo1x_decompress(out + sizeof(uint32) * 1, size, buf, &len, NULL);
01871 return len;
01872 }
01873 };
01874
01876 struct LZOSaveFilter : SaveFilter {
01882 LZOSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
01883 {
01884 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
01885 }
01886
01887 void Write(byte *buf, size_t size)
01888 {
01889 const lzo_bytep in = buf;
01890
01891 byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2];
01892 byte wrkmem[LZO1X_1_MEM_COMPRESS];
01893 lzo_uint outlen;
01894
01895 do {
01896
01897 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size;
01898 lzo1x_1_compress(in, len, out + sizeof(uint32) * 2, &outlen, wrkmem);
01899 ((uint32*)out)[1] = TO_BE32((uint32)outlen);
01900 ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32)));
01901 this->chain->Write(out, outlen + sizeof(uint32) * 2);
01902
01903
01904 size -= len;
01905 in += len;
01906 } while (size > 0);
01907 }
01908 };
01909
01910 #endif
01911
01912
01913
01914
01915
01917 struct NoCompLoadFilter : LoadFilter {
01922 NoCompLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01923 {
01924 }
01925
01926 size_t Read(byte *buf, size_t size)
01927 {
01928 return this->chain->Read(buf, size);
01929 }
01930 };
01931
01933 struct NoCompSaveFilter : SaveFilter {
01939 NoCompSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
01940 {
01941 }
01942
01943 void Write(byte *buf, size_t size)
01944 {
01945 this->chain->Write(buf, size);
01946 }
01947 };
01948
01949
01950
01951
01952
01953 #if defined(WITH_ZLIB)
01954 #include <zlib.h>
01955
01957 struct ZlibLoadFilter : LoadFilter {
01958 z_stream z;
01959 byte fread_buf[MEMORY_CHUNK_SIZE];
01960
01965 ZlibLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01966 {
01967 memset(&this->z, 0, sizeof(this->z));
01968 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
01969 }
01970
01972 ~ZlibLoadFilter()
01973 {
01974 inflateEnd(&this->z);
01975 }
01976
01977 size_t Read(byte *buf, size_t size)
01978 {
01979 this->z.next_out = buf;
01980 this->z.avail_out = (uint)size;
01981
01982 do {
01983
01984 if (this->z.avail_in == 0) {
01985 this->z.next_in = this->fread_buf;
01986 this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
01987 }
01988
01989
01990 int r = inflate(&this->z, 0);
01991 if (r == Z_STREAM_END) break;
01992
01993 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
01994 } while (this->z.avail_out != 0);
01995
01996 return size - this->z.avail_out;
01997 }
01998 };
01999
02001 struct ZlibSaveFilter : SaveFilter {
02002 z_stream z;
02003
02009 ZlibSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
02010 {
02011 memset(&this->z, 0, sizeof(this->z));
02012 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
02013 }
02014
02016 ~ZlibSaveFilter()
02017 {
02018 deflateEnd(&this->z);
02019 }
02020
02027 void WriteLoop(byte *p, size_t len, int mode)
02028 {
02029 byte buf[MEMORY_CHUNK_SIZE];
02030 uint n;
02031 this->z.next_in = p;
02032 this->z.avail_in = (uInt)len;
02033 do {
02034 this->z.next_out = buf;
02035 this->z.avail_out = sizeof(buf);
02036
02044 int r = deflate(&this->z, mode);
02045
02046
02047 if ((n = sizeof(buf) - this->z.avail_out) != 0) {
02048 this->chain->Write(buf, n);
02049 }
02050 if (r == Z_STREAM_END) break;
02051
02052 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
02053 } while (this->z.avail_in || !this->z.avail_out);
02054 }
02055
02056 void Write(byte *buf, size_t size)
02057 {
02058 this->WriteLoop(buf, size, 0);
02059 }
02060
02061 void Finish()
02062 {
02063 this->WriteLoop(NULL, 0, Z_FINISH);
02064 this->chain->Finish();
02065 }
02066 };
02067
02068 #endif
02069
02070
02071
02072
02073
02074 #if defined(WITH_LZMA)
02075 #include <lzma.h>
02076
02083 static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
02084
02086 struct LZMALoadFilter : LoadFilter {
02087 lzma_stream lzma;
02088 byte fread_buf[MEMORY_CHUNK_SIZE];
02089
02094 LZMALoadFilter(LoadFilter *chain) : LoadFilter(chain), lzma(_lzma_init)
02095 {
02096
02097 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
02098 }
02099
02101 ~LZMALoadFilter()
02102 {
02103 lzma_end(&this->lzma);
02104 }
02105
02106 size_t Read(byte *buf, size_t size)
02107 {
02108 this->lzma.next_out = buf;
02109 this->lzma.avail_out = size;
02110
02111 do {
02112
02113 if (this->lzma.avail_in == 0) {
02114 this->lzma.next_in = this->fread_buf;
02115 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
02116 }
02117
02118
02119 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
02120 if (r == LZMA_STREAM_END) break;
02121 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
02122 } while (this->lzma.avail_out != 0);
02123
02124 return size - this->lzma.avail_out;
02125 }
02126 };
02127
02129 struct LZMASaveFilter : SaveFilter {
02130 lzma_stream lzma;
02131
02137 LZMASaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain), lzma(_lzma_init)
02138 {
02139 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
02140 }
02141
02143 ~LZMASaveFilter()
02144 {
02145 lzma_end(&this->lzma);
02146 }
02147
02154 void WriteLoop(byte *p, size_t len, lzma_action action)
02155 {
02156 byte buf[MEMORY_CHUNK_SIZE];
02157 size_t n;
02158 this->lzma.next_in = p;
02159 this->lzma.avail_in = len;
02160 do {
02161 this->lzma.next_out = buf;
02162 this->lzma.avail_out = sizeof(buf);
02163
02164 lzma_ret r = lzma_code(&this->lzma, action);
02165
02166
02167 if ((n = sizeof(buf) - this->lzma.avail_out) != 0) {
02168 this->chain->Write(buf, n);
02169 }
02170 if (r == LZMA_STREAM_END) break;
02171 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
02172 } while (this->lzma.avail_in || !this->lzma.avail_out);
02173 }
02174
02175 void Write(byte *buf, size_t size)
02176 {
02177 this->WriteLoop(buf, size, LZMA_RUN);
02178 }
02179
02180 void Finish()
02181 {
02182 this->WriteLoop(NULL, 0, LZMA_FINISH);
02183 this->chain->Finish();
02184 }
02185 };
02186
02187 #endif
02188
02189
02190
02191
02192
02194 struct SaveLoadFormat {
02195 const char *name;
02196 uint32 tag;
02197
02198 LoadFilter *(*init_load)(LoadFilter *chain);
02199 SaveFilter *(*init_write)(SaveFilter *chain, byte compression);
02200
02201 byte min_compression;
02202 byte default_compression;
02203 byte max_compression;
02204 };
02205
02207 static const SaveLoadFormat _saveload_formats[] = {
02208 #if defined(WITH_LZO)
02209
02210 {"lzo", TO_BE32X('OTTD'), CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
02211 #else
02212 {"lzo", TO_BE32X('OTTD'), NULL, NULL, 0, 0, 0},
02213 #endif
02214
02215 {"none", TO_BE32X('OTTN'), CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
02216 #if defined(WITH_ZLIB)
02217
02218
02219
02220 {"zlib", TO_BE32X('OTTZ'), CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
02221 #else
02222 {"zlib", TO_BE32X('OTTZ'), NULL, NULL, 0, 0, 0},
02223 #endif
02224 #if defined(WITH_LZMA)
02225
02226
02227
02228
02229
02230 {"lzma", TO_BE32X('OTTX'), CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
02231 #else
02232 {"lzma", TO_BE32X('OTTX'), NULL, NULL, 0, 0, 0},
02233 #endif
02234 };
02235
02243 static const SaveLoadFormat *GetSavegameFormat(char *s, byte *compression_level)
02244 {
02245 const SaveLoadFormat *def = lastof(_saveload_formats);
02246
02247
02248 while (!def->init_write) def--;
02249
02250 if (!StrEmpty(s)) {
02251
02252 char *complevel = strrchr(s, ':');
02253 if (complevel != NULL) *complevel = '\0';
02254
02255 for (const SaveLoadFormat *slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) {
02256 if (slf->init_write != NULL && strcmp(s, slf->name) == 0) {
02257 *compression_level = slf->default_compression;
02258 if (complevel != NULL) {
02259
02260
02261
02262 *complevel = ':';
02263 complevel++;
02264
02265
02266 char *end;
02267 long level = strtol(complevel, &end, 10);
02268 if (end == complevel || level != Clamp(level, slf->min_compression, slf->max_compression)) {
02269 ShowInfoF("Compression level '%s' is not valid.", complevel);
02270 } else {
02271 *compression_level = level;
02272 }
02273 }
02274 return slf;
02275 }
02276 }
02277
02278 ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
02279
02280
02281 if (complevel != NULL) *complevel = ':';
02282 }
02283 *compression_level = def->default_compression;
02284 return def;
02285 }
02286
02287
02288 void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
02289 extern bool AfterLoadGame();
02290 extern bool LoadOldSaveGame(const char *file);
02291
02295 static inline void ClearSaveLoadState()
02296 {
02297 delete _sl.dumper;
02298 _sl.dumper = NULL;
02299
02300 delete _sl.sf;
02301 _sl.sf = NULL;
02302
02303 delete _sl.reader;
02304 _sl.reader = NULL;
02305
02306 delete _sl.lf;
02307 _sl.lf = NULL;
02308 }
02309
02315 static void SaveFileStart()
02316 {
02317 _sl.ff_state = _fast_forward;
02318 _fast_forward = 0;
02319 if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
02320
02321 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START);
02322 _sl.saveinprogress = true;
02323 }
02324
02326 static void SaveFileDone()
02327 {
02328 if (_game_mode != GM_MENU) _fast_forward = _sl.ff_state;
02329 if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
02330
02331 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH);
02332 _sl.saveinprogress = false;
02333 }
02334
02336 void SetSaveLoadError(StringID str)
02337 {
02338 _sl.error_str = str;
02339 }
02340
02342 const char *GetSaveLoadErrorString()
02343 {
02344 SetDParam(0, _sl.error_str);
02345 SetDParamStr(1, _sl.extra_msg);
02346
02347 static char err_str[512];
02348 GetString(err_str, _sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED, lastof(err_str));
02349 return err_str;
02350 }
02351
02353 static void SaveFileError()
02354 {
02355 SetDParamStr(0, GetSaveLoadErrorString());
02356 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02357 SaveFileDone();
02358 }
02359
02364 static SaveOrLoadResult SaveFileToDisk(bool threaded)
02365 {
02366 try {
02367 byte compression;
02368 const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
02369
02370
02371 uint32 hdr[2] = { fmt->tag, TO_BE32(SAVEGAME_VERSION << 16) };
02372 _sl.sf->Write((byte*)hdr, sizeof(hdr));
02373
02374 _sl.sf = fmt->init_write(_sl.sf, compression);
02375 _sl.dumper->Flush(_sl.sf);
02376
02377 ClearSaveLoadState();
02378
02379 if (threaded) SetAsyncSaveFinish(SaveFileDone);
02380
02381 return SL_OK;
02382 } catch (...) {
02383 ClearSaveLoadState();
02384
02385 AsyncSaveFinishProc asfp = SaveFileDone;
02386
02387
02388
02389 if (_sl.error_str != STR_NETWORK_ERROR_LOSTCONNECTION) {
02390
02391 DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
02392 asfp = SaveFileError;
02393 }
02394
02395 if (threaded) {
02396 SetAsyncSaveFinish(asfp);
02397 } else {
02398 asfp();
02399 }
02400 return SL_ERROR;
02401 }
02402 }
02403
02405 static void SaveFileToDiskThread(void *arg)
02406 {
02407 SaveFileToDisk(true);
02408 }
02409
02410 void WaitTillSaved()
02411 {
02412 if (_save_thread == NULL) return;
02413
02414 _save_thread->Join();
02415 delete _save_thread;
02416 _save_thread = NULL;
02417 }
02418
02427 static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded)
02428 {
02429 assert(!_sl.saveinprogress);
02430
02431 _sl.dumper = new MemoryDumper();
02432 _sl.sf = writer;
02433
02434 _sl_version = SAVEGAME_VERSION;
02435
02436 SaveViewportBeforeSaveGame();
02437 SlSaveChunks();
02438
02439 SaveFileStart();
02440 if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread)) {
02441 if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
02442
02443 SaveOrLoadResult result = SaveFileToDisk(false);
02444 SaveFileDone();
02445
02446 return result;
02447 }
02448
02449 return SL_OK;
02450 }
02451
02458 SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded)
02459 {
02460 try {
02461 _sl.action = SLA_SAVE;
02462 return DoSave(writer, threaded);
02463 } catch (...) {
02464 ClearSaveLoadState();
02465 return SL_ERROR;
02466 }
02467 }
02468
02475 static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
02476 {
02477 _sl.lf = reader;
02478
02479 if (load_check) {
02480
02481 _load_check_data.Clear();
02482
02483 _load_check_data.checkable = true;
02484 }
02485
02486 uint32 hdr[2];
02487 if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
02488
02489
02490 const SaveLoadFormat *fmt = _saveload_formats;
02491 for (;;) {
02492
02493 if (fmt == endof(_saveload_formats)) {
02494 DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
02495 _sl.lf->Reset();
02496 _sl_version = 0;
02497 _sl_minor_version = 0;
02498
02499
02500 fmt = _saveload_formats;
02501 for (;;) {
02502 if (fmt == endof(_saveload_formats)) {
02503
02504 NOT_REACHED();
02505 }
02506 if (fmt->tag == TO_BE32X('OTTD')) break;
02507 fmt++;
02508 }
02509 break;
02510 }
02511
02512 if (fmt->tag == hdr[0]) {
02513
02514 _sl_version = TO_BE32(hdr[1]) >> 16;
02515
02516
02517
02518
02519 _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
02520
02521 DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
02522
02523
02524 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
02525 break;
02526 }
02527
02528 fmt++;
02529 }
02530
02531
02532 if (fmt->init_load == NULL) {
02533 char err_str[64];
02534 snprintf(err_str, lengthof(err_str), "Loader for '%s' is not available.", fmt->name);
02535 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
02536 }
02537
02538 _sl.lf = fmt->init_load(_sl.lf);
02539 _sl.reader = new ReadBuffer(_sl.lf);
02540 _next_offs = 0;
02541
02542 if (!load_check) {
02543 _engine_mngr.ResetToDefaultMapping();
02544
02545
02546
02547
02548 InitializeGame(256, 256, true, true);
02549
02550 GamelogReset();
02551
02552 if (IsSavegameVersionBefore(4)) {
02553
02554
02555
02556
02557
02558
02559
02560
02561
02562
02563
02564
02565
02566
02567
02568
02569
02570
02571
02572
02573
02574 ClearGRFConfigList(&_grfconfig);
02575 }
02576 }
02577
02578 if (load_check) {
02579
02580
02581 SlLoadCheckChunks();
02582 } else {
02583
02584 SlLoadChunks();
02585 SlFixPointers();
02586 }
02587
02588 ClearSaveLoadState();
02589
02590 _savegame_type = SGT_OTTD;
02591
02592 if (load_check) {
02593
02594 _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
02595 } else {
02596 GamelogStartAction(GLAT_LOAD);
02597
02598
02599
02600 if (!AfterLoadGame()) {
02601 GamelogStopAction();
02602 return SL_REINIT;
02603 }
02604
02605 GamelogStopAction();
02606 }
02607
02608 return SL_OK;
02609 }
02610
02616 SaveOrLoadResult LoadWithFilter(LoadFilter *reader)
02617 {
02618 try {
02619 _sl.action = SLA_LOAD;
02620 return DoLoad(reader, false);
02621 } catch (...) {
02622 ClearSaveLoadState();
02623 return SL_REINIT;
02624 }
02625 }
02626
02636 SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, bool threaded)
02637 {
02638
02639 if (_sl.saveinprogress && mode == SL_SAVE && threaded) {
02640
02641 if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR);
02642 return SL_OK;
02643 }
02644 WaitTillSaved();
02645
02646
02647 if (mode == SL_OLD_LOAD) {
02648 _engine_mngr.ResetToDefaultMapping();
02649 InitializeGame(256, 256, true, true);
02650
02651
02652
02653
02654
02655 ClearGRFConfigList(&_grfconfig);
02656 GamelogReset();
02657 if (!LoadOldSaveGame(filename)) return SL_REINIT;
02658 _sl_version = 0;
02659 _sl_minor_version = 0;
02660 GamelogStartAction(GLAT_LOAD);
02661 if (!AfterLoadGame()) {
02662 GamelogStopAction();
02663 return SL_REINIT;
02664 }
02665 GamelogStopAction();
02666 return SL_OK;
02667 }
02668
02669 switch (mode) {
02670 case SL_LOAD_CHECK: _sl.action = SLA_LOAD_CHECK; break;
02671 case SL_LOAD: _sl.action = SLA_LOAD; break;
02672 case SL_SAVE: _sl.action = SLA_SAVE; break;
02673 default: NOT_REACHED();
02674 }
02675
02676 try {
02677 FILE *fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
02678
02679
02680 if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
02681 if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
02682
02683 if (fh == NULL) {
02684 SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
02685 }
02686
02687 if (mode == SL_SAVE) {
02688 DEBUG(desync, 1, "save: %08x; %02x; %s", _date, _date_fract, filename);
02689 if (_network_server || !_settings_client.gui.threaded_saves) threaded = false;
02690
02691 return DoSave(new FileWriter(fh), threaded);
02692 }
02693
02694
02695 assert(mode == SL_LOAD || mode == SL_LOAD_CHECK);
02696 DEBUG(desync, 1, "load: %s", filename);
02697 return DoLoad(new FileReader(fh), mode == SL_LOAD_CHECK);
02698 } catch (...) {
02699 ClearSaveLoadState();
02700
02701
02702 if (mode != SL_LOAD_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
02703
02704
02705 return (mode == SL_LOAD) ? SL_REINIT : SL_ERROR;
02706 }
02707 }
02708
02710 void DoExitSave()
02711 {
02712 SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR);
02713 }
02714
02720 void GenerateDefaultSaveName(char *buf, const char *last)
02721 {
02722
02723
02724
02725 CompanyID cid = _local_company;
02726 if (!Company::IsValidID(cid)) {
02727 const Company *c;
02728 FOR_ALL_COMPANIES(c) {
02729 cid = c->index;
02730 break;
02731 }
02732 }
02733
02734 SetDParam(0, cid);
02735
02736
02737 switch (_settings_client.gui.date_format_in_default_names) {
02738 case 0: SetDParam(1, STR_JUST_DATE_LONG); break;
02739 case 1: SetDParam(1, STR_JUST_DATE_TINY); break;
02740 case 2: SetDParam(1, STR_JUST_DATE_ISO); break;
02741 default: NOT_REACHED();
02742 }
02743 SetDParam(2, _date);
02744
02745
02746 GetString(buf, !Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, last);
02747 SanitizeFilename(buf);
02748 }
02749
02750 #if 0
02751
02757 int GetSavegameType(char *file)
02758 {
02759 const SaveLoadFormat *fmt;
02760 uint32 hdr;
02761 FILE *f;
02762 int mode = SL_OLD_LOAD;
02763
02764 f = fopen(file, "rb");
02765 if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
02766 DEBUG(sl, 0, "Savegame is obsolete or invalid format");
02767 mode = SL_LOAD;
02768 } else {
02769
02770 for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) {
02771 if (fmt->tag == hdr) {
02772 mode = SL_LOAD;
02773 break;
02774 }
02775 }
02776 }
02777
02778 fclose(f);
02779 return mode;
02780 }
02781 #endif