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 extern const uint16 SAVEGAME_VERSION = 157;
00227
00228 SavegameType _savegame_type;
00229
00230 uint32 _ttdp_version;
00231 uint16 _sl_version;
00232 byte _sl_minor_version;
00233 char _savegame_format[8];
00234 bool _do_autosave;
00235
00237 enum SaveLoadAction {
00238 SLA_LOAD,
00239 SLA_SAVE,
00240 SLA_PTRS,
00241 SLA_NULL,
00242 SLA_LOAD_CHECK,
00243 };
00244
00245 enum NeedLength {
00246 NL_NONE = 0,
00247 NL_WANTLENGTH = 1,
00248 NL_CALCLENGTH = 2,
00249 };
00250
00252 static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
00253
00255 struct ReadBuffer {
00256 byte buf[MEMORY_CHUNK_SIZE];
00257 byte *bufp;
00258 byte *bufe;
00259 LoadFilter *reader;
00260 size_t read;
00261
00266 ReadBuffer(LoadFilter *reader) : bufp(NULL), bufe(NULL), reader(reader), read(0)
00267 {
00268 }
00269
00270 FORCEINLINE byte ReadByte()
00271 {
00272 if (this->bufp == this->bufe) {
00273 size_t len = this->reader->Read(this->buf, lengthof(this->buf));
00274 if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
00275
00276 this->read += len;
00277 this->bufp = this->buf;
00278 this->bufe = this->buf + len;
00279 }
00280
00281 return *this->bufp++;
00282 }
00283
00288 size_t GetSize() const
00289 {
00290 return this->read - (this->bufe - this->bufp);
00291 }
00292 };
00293
00294
00296 struct MemoryDumper {
00297 AutoFreeSmallVector<byte *, 16> blocks;
00298 byte *buf;
00299 byte *bufe;
00300
00302 MemoryDumper() : buf(NULL), bufe(NULL)
00303 {
00304 }
00305
00310 FORCEINLINE void WriteByte(byte b)
00311 {
00312
00313 if (this->buf == this->bufe) {
00314 this->buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
00315 *this->blocks.Append() = this->buf;
00316 this->bufe = this->buf + MEMORY_CHUNK_SIZE;
00317 }
00318
00319 *this->buf++ = b;
00320 }
00321
00326 void Flush(SaveFilter *writer)
00327 {
00328 uint i = 0;
00329 size_t t = this->GetSize();
00330
00331 while (t > 0) {
00332 size_t to_write = min(MEMORY_CHUNK_SIZE, t);
00333
00334 writer->Write(this->blocks[i++], to_write);
00335 t -= to_write;
00336 }
00337
00338 writer->Finish();
00339 }
00340
00345 size_t GetSize() const
00346 {
00347 return this->blocks.Length() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
00348 }
00349 };
00350
00352 struct SaveLoadParams {
00353 SaveLoadAction action;
00354 NeedLength need_length;
00355 byte block_mode;
00356 bool error;
00357
00358 size_t obj_len;
00359 int array_index, last_array_index;
00360
00361 MemoryDumper *dumper;
00362 SaveFilter *sf;
00363
00364 ReadBuffer *reader;
00365 LoadFilter *lf;
00366
00367 StringID error_str;
00368 char *extra_msg;
00369
00370 byte ff_state;
00371 bool saveinprogress;
00372 };
00373
00374 static SaveLoadParams _sl;
00375
00376
00377 extern const ChunkHandler _gamelog_chunk_handlers[];
00378 extern const ChunkHandler _map_chunk_handlers[];
00379 extern const ChunkHandler _misc_chunk_handlers[];
00380 extern const ChunkHandler _name_chunk_handlers[];
00381 extern const ChunkHandler _cheat_chunk_handlers[] ;
00382 extern const ChunkHandler _setting_chunk_handlers[];
00383 extern const ChunkHandler _company_chunk_handlers[];
00384 extern const ChunkHandler _engine_chunk_handlers[];
00385 extern const ChunkHandler _veh_chunk_handlers[];
00386 extern const ChunkHandler _waypoint_chunk_handlers[];
00387 extern const ChunkHandler _depot_chunk_handlers[];
00388 extern const ChunkHandler _order_chunk_handlers[];
00389 extern const ChunkHandler _town_chunk_handlers[];
00390 extern const ChunkHandler _sign_chunk_handlers[];
00391 extern const ChunkHandler _station_chunk_handlers[];
00392 extern const ChunkHandler _industry_chunk_handlers[];
00393 extern const ChunkHandler _economy_chunk_handlers[];
00394 extern const ChunkHandler _subsidy_chunk_handlers[];
00395 extern const ChunkHandler _ai_chunk_handlers[];
00396 extern const ChunkHandler _animated_tile_chunk_handlers[];
00397 extern const ChunkHandler _newgrf_chunk_handlers[];
00398 extern const ChunkHandler _group_chunk_handlers[];
00399 extern const ChunkHandler _cargopacket_chunk_handlers[];
00400 extern const ChunkHandler _autoreplace_chunk_handlers[];
00401 extern const ChunkHandler _labelmaps_chunk_handlers[];
00402 extern const ChunkHandler _airport_chunk_handlers[];
00403 extern const ChunkHandler _object_chunk_handlers[];
00404
00406 static const ChunkHandler * const _chunk_handlers[] = {
00407 _gamelog_chunk_handlers,
00408 _map_chunk_handlers,
00409 _misc_chunk_handlers,
00410 _name_chunk_handlers,
00411 _cheat_chunk_handlers,
00412 _setting_chunk_handlers,
00413 _veh_chunk_handlers,
00414 _waypoint_chunk_handlers,
00415 _depot_chunk_handlers,
00416 _order_chunk_handlers,
00417 _industry_chunk_handlers,
00418 _economy_chunk_handlers,
00419 _subsidy_chunk_handlers,
00420 _engine_chunk_handlers,
00421 _town_chunk_handlers,
00422 _sign_chunk_handlers,
00423 _station_chunk_handlers,
00424 _company_chunk_handlers,
00425 _ai_chunk_handlers,
00426 _animated_tile_chunk_handlers,
00427 _newgrf_chunk_handlers,
00428 _group_chunk_handlers,
00429 _cargopacket_chunk_handlers,
00430 _autoreplace_chunk_handlers,
00431 _labelmaps_chunk_handlers,
00432 _airport_chunk_handlers,
00433 _object_chunk_handlers,
00434 NULL,
00435 };
00436
00441 #define FOR_ALL_CHUNK_HANDLERS(ch) \
00442 for (const ChunkHandler * const *chsc = _chunk_handlers; *chsc != NULL; chsc++) \
00443 for (const ChunkHandler *ch = *chsc; ch != NULL; ch = (ch->flags & CH_LAST) ? NULL : ch + 1)
00444
00446 static void SlNullPointers()
00447 {
00448 _sl.action = SLA_NULL;
00449
00450 DEBUG(sl, 1, "Nulling pointers");
00451
00452 FOR_ALL_CHUNK_HANDLERS(ch) {
00453 if (ch->ptrs_proc != NULL) {
00454 DEBUG(sl, 2, "Nulling pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
00455 ch->ptrs_proc();
00456 }
00457 }
00458
00459 DEBUG(sl, 1, "All pointers nulled");
00460
00461 assert(_sl.action == SLA_NULL);
00462 }
00463
00472 static void NORETURN SlError(StringID string, const char *extra_msg = NULL)
00473 {
00474
00475 if (_sl.action == SLA_LOAD_CHECK) {
00476 _load_check_data.error = string;
00477 free(_load_check_data.error_data);
00478 _load_check_data.error_data = (extra_msg == NULL) ? NULL : strdup(extra_msg);
00479 } else {
00480 _sl.error_str = string;
00481 free(_sl.extra_msg);
00482 _sl.extra_msg = (extra_msg == NULL) ? NULL : strdup(extra_msg);
00483
00484
00485
00486
00487 }
00488 if (_sl.action == SLA_LOAD || _sl.action == SLA_PTRS) SlNullPointers();
00489 throw std::exception();
00490 }
00491
00499 void NORETURN SlErrorCorrupt(const char *msg)
00500 {
00501 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
00502 }
00503
00504
00505 typedef void (*AsyncSaveFinishProc)();
00506 static AsyncSaveFinishProc _async_save_finish = NULL;
00507 static ThreadObject *_save_thread;
00508
00513 static void SetAsyncSaveFinish(AsyncSaveFinishProc proc)
00514 {
00515 if (_exit_game) return;
00516 while (_async_save_finish != NULL) CSleep(10);
00517
00518 _async_save_finish = proc;
00519 }
00520
00524 void ProcessAsyncSaveFinish()
00525 {
00526 if (_async_save_finish == NULL) return;
00527
00528 _async_save_finish();
00529
00530 _async_save_finish = NULL;
00531
00532 if (_save_thread != NULL) {
00533 _save_thread->Join();
00534 delete _save_thread;
00535 _save_thread = NULL;
00536 }
00537 }
00538
00543 byte SlReadByte()
00544 {
00545 return _sl.reader->ReadByte();
00546 }
00547
00552 void SlWriteByte(byte b)
00553 {
00554 _sl.dumper->WriteByte(b);
00555 }
00556
00557 static inline int SlReadUint16()
00558 {
00559 int x = SlReadByte() << 8;
00560 return x | SlReadByte();
00561 }
00562
00563 static inline uint32 SlReadUint32()
00564 {
00565 uint32 x = SlReadUint16() << 16;
00566 return x | SlReadUint16();
00567 }
00568
00569 static inline uint64 SlReadUint64()
00570 {
00571 uint32 x = SlReadUint32();
00572 uint32 y = SlReadUint32();
00573 return (uint64)x << 32 | y;
00574 }
00575
00576 static inline void SlWriteUint16(uint16 v)
00577 {
00578 SlWriteByte(GB(v, 8, 8));
00579 SlWriteByte(GB(v, 0, 8));
00580 }
00581
00582 static inline void SlWriteUint32(uint32 v)
00583 {
00584 SlWriteUint16(GB(v, 16, 16));
00585 SlWriteUint16(GB(v, 0, 16));
00586 }
00587
00588 static inline void SlWriteUint64(uint64 x)
00589 {
00590 SlWriteUint32((uint32)(x >> 32));
00591 SlWriteUint32((uint32)x);
00592 }
00593
00599 static inline void SlSkipBytes(size_t length)
00600 {
00601 for (; length != 0; length--) SlReadByte();
00602 }
00603
00613 static uint SlReadSimpleGamma()
00614 {
00615 uint i = SlReadByte();
00616 if (HasBit(i, 7)) {
00617 i &= ~0x80;
00618 if (HasBit(i, 6)) {
00619 i &= ~0x40;
00620 if (HasBit(i, 5)) {
00621 i &= ~0x20;
00622 if (HasBit(i, 4)) {
00623 SlErrorCorrupt("Unsupported gamma");
00624 }
00625 i = (i << 8) | SlReadByte();
00626 }
00627 i = (i << 8) | SlReadByte();
00628 }
00629 i = (i << 8) | SlReadByte();
00630 }
00631 return i;
00632 }
00633
00646 static void SlWriteSimpleGamma(size_t i)
00647 {
00648 if (i >= (1 << 7)) {
00649 if (i >= (1 << 14)) {
00650 if (i >= (1 << 21)) {
00651 assert(i < (1 << 28));
00652 SlWriteByte((byte)(0xE0 | (i >> 24)));
00653 SlWriteByte((byte)(i >> 16));
00654 } else {
00655 SlWriteByte((byte)(0xC0 | (i >> 16)));
00656 }
00657 SlWriteByte((byte)(i >> 8));
00658 } else {
00659 SlWriteByte((byte)(0x80 | (i >> 8)));
00660 }
00661 }
00662 SlWriteByte((byte)i);
00663 }
00664
00666 static inline uint SlGetGammaLength(size_t i)
00667 {
00668 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21));
00669 }
00670
00671 static inline uint SlReadSparseIndex()
00672 {
00673 return SlReadSimpleGamma();
00674 }
00675
00676 static inline void SlWriteSparseIndex(uint index)
00677 {
00678 SlWriteSimpleGamma(index);
00679 }
00680
00681 static inline uint SlReadArrayLength()
00682 {
00683 return SlReadSimpleGamma();
00684 }
00685
00686 static inline void SlWriteArrayLength(size_t length)
00687 {
00688 SlWriteSimpleGamma(length);
00689 }
00690
00691 static inline uint SlGetArrayLength(size_t length)
00692 {
00693 return SlGetGammaLength(length);
00694 }
00695
00702 static inline uint SlCalcConvMemLen(VarType conv)
00703 {
00704 static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0};
00705 byte length = GB(conv, 4, 4);
00706
00707 switch (length << 4) {
00708 case SLE_VAR_STRB:
00709 case SLE_VAR_STRBQ:
00710 case SLE_VAR_STR:
00711 case SLE_VAR_STRQ:
00712 return SlReadArrayLength();
00713
00714 default:
00715 assert(length < lengthof(conv_mem_size));
00716 return conv_mem_size[length];
00717 }
00718 }
00719
00726 static inline byte SlCalcConvFileLen(VarType conv)
00727 {
00728 static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2};
00729 byte length = GB(conv, 0, 4);
00730 assert(length < lengthof(conv_file_size));
00731 return conv_file_size[length];
00732 }
00733
00735 static inline size_t SlCalcRefLen()
00736 {
00737 return IsSavegameVersionBefore(69) ? 2 : 4;
00738 }
00739
00740 void SlSetArrayIndex(uint index)
00741 {
00742 _sl.need_length = NL_WANTLENGTH;
00743 _sl.array_index = index;
00744 }
00745
00746 static size_t _next_offs;
00747
00752 int SlIterateArray()
00753 {
00754 int index;
00755
00756
00757
00758 if (_next_offs != 0 && _sl.reader->GetSize() != _next_offs) SlErrorCorrupt("Invalid chunk size");
00759
00760 while (true) {
00761 uint length = SlReadArrayLength();
00762 if (length == 0) {
00763 _next_offs = 0;
00764 return -1;
00765 }
00766
00767 _sl.obj_len = --length;
00768 _next_offs = _sl.reader->GetSize() + length;
00769
00770 switch (_sl.block_mode) {
00771 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break;
00772 case CH_ARRAY: index = _sl.array_index++; break;
00773 default:
00774 DEBUG(sl, 0, "SlIterateArray error");
00775 return -1;
00776 }
00777
00778 if (length != 0) return index;
00779 }
00780 }
00781
00785 void SlSkipArray()
00786 {
00787 while (SlIterateArray() != -1) {
00788 SlSkipBytes(_next_offs - _sl.reader->GetSize());
00789 }
00790 }
00791
00797 void SlSetLength(size_t length)
00798 {
00799 assert(_sl.action == SLA_SAVE);
00800
00801 switch (_sl.need_length) {
00802 case NL_WANTLENGTH:
00803 _sl.need_length = NL_NONE;
00804 switch (_sl.block_mode) {
00805 case CH_RIFF:
00806
00807
00808
00809 assert(length < (1 << 28));
00810 SlWriteUint32((uint32)((length & 0xFFFFFF) | ((length >> 24) << 28)));
00811 break;
00812 case CH_ARRAY:
00813 assert(_sl.last_array_index <= _sl.array_index);
00814 while (++_sl.last_array_index <= _sl.array_index) {
00815 SlWriteArrayLength(1);
00816 }
00817 SlWriteArrayLength(length + 1);
00818 break;
00819 case CH_SPARSE_ARRAY:
00820 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index));
00821 SlWriteSparseIndex(_sl.array_index);
00822 break;
00823 default: NOT_REACHED();
00824 }
00825 break;
00826
00827 case NL_CALCLENGTH:
00828 _sl.obj_len += (int)length;
00829 break;
00830
00831 default: NOT_REACHED();
00832 }
00833 }
00834
00841 static void SlCopyBytes(void *ptr, size_t length)
00842 {
00843 byte *p = (byte *)ptr;
00844
00845 switch (_sl.action) {
00846 case SLA_LOAD_CHECK:
00847 case SLA_LOAD:
00848 for (; length != 0; length--) *p++ = SlReadByte();
00849 break;
00850 case SLA_SAVE:
00851 for (; length != 0; length--) SlWriteByte(*p++);
00852 break;
00853 default: NOT_REACHED();
00854 }
00855 }
00856
00858 size_t SlGetFieldLength()
00859 {
00860 return _sl.obj_len;
00861 }
00862
00870 int64 ReadValue(const void *ptr, VarType conv)
00871 {
00872 switch (GetVarMemType(conv)) {
00873 case SLE_VAR_BL: return (*(bool *)ptr != 0);
00874 case SLE_VAR_I8: return *(int8 *)ptr;
00875 case SLE_VAR_U8: return *(byte *)ptr;
00876 case SLE_VAR_I16: return *(int16 *)ptr;
00877 case SLE_VAR_U16: return *(uint16*)ptr;
00878 case SLE_VAR_I32: return *(int32 *)ptr;
00879 case SLE_VAR_U32: return *(uint32*)ptr;
00880 case SLE_VAR_I64: return *(int64 *)ptr;
00881 case SLE_VAR_U64: return *(uint64*)ptr;
00882 case SLE_VAR_NULL:return 0;
00883 default: NOT_REACHED();
00884 }
00885 }
00886
00894 void WriteValue(void *ptr, VarType conv, int64 val)
00895 {
00896 switch (GetVarMemType(conv)) {
00897 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break;
00898 case SLE_VAR_I8: *(int8 *)ptr = val; break;
00899 case SLE_VAR_U8: *(byte *)ptr = val; break;
00900 case SLE_VAR_I16: *(int16 *)ptr = val; break;
00901 case SLE_VAR_U16: *(uint16*)ptr = val; break;
00902 case SLE_VAR_I32: *(int32 *)ptr = val; break;
00903 case SLE_VAR_U32: *(uint32*)ptr = val; break;
00904 case SLE_VAR_I64: *(int64 *)ptr = val; break;
00905 case SLE_VAR_U64: *(uint64*)ptr = val; break;
00906 case SLE_VAR_NAME: *(char**)ptr = CopyFromOldName(val); break;
00907 case SLE_VAR_NULL: break;
00908 default: NOT_REACHED();
00909 }
00910 }
00911
00920 static void SlSaveLoadConv(void *ptr, VarType conv)
00921 {
00922 switch (_sl.action) {
00923 case SLA_SAVE: {
00924 int64 x = ReadValue(ptr, conv);
00925
00926
00927 switch (GetVarFileType(conv)) {
00928 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break;
00929 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break;
00930 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break;
00931 case SLE_FILE_STRINGID:
00932 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break;
00933 case SLE_FILE_I32:
00934 case SLE_FILE_U32: SlWriteUint32((uint32)x);break;
00935 case SLE_FILE_I64:
00936 case SLE_FILE_U64: SlWriteUint64(x);break;
00937 default: NOT_REACHED();
00938 }
00939 break;
00940 }
00941 case SLA_LOAD_CHECK:
00942 case SLA_LOAD: {
00943 int64 x;
00944
00945 switch (GetVarFileType(conv)) {
00946 case SLE_FILE_I8: x = (int8 )SlReadByte(); break;
00947 case SLE_FILE_U8: x = (byte )SlReadByte(); break;
00948 case SLE_FILE_I16: x = (int16 )SlReadUint16(); break;
00949 case SLE_FILE_U16: x = (uint16)SlReadUint16(); break;
00950 case SLE_FILE_I32: x = (int32 )SlReadUint32(); break;
00951 case SLE_FILE_U32: x = (uint32)SlReadUint32(); break;
00952 case SLE_FILE_I64: x = (int64 )SlReadUint64(); break;
00953 case SLE_FILE_U64: x = (uint64)SlReadUint64(); break;
00954 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break;
00955 default: NOT_REACHED();
00956 }
00957
00958
00959 WriteValue(ptr, conv, x);
00960 break;
00961 }
00962 case SLA_PTRS: break;
00963 case SLA_NULL: break;
00964 default: NOT_REACHED();
00965 }
00966 }
00967
00977 static inline size_t SlCalcNetStringLen(const char *ptr, size_t length)
00978 {
00979 if (ptr == NULL) return 0;
00980 return min(strlen(ptr), length - 1);
00981 }
00982
00992 static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv)
00993 {
00994 size_t len;
00995 const char *str;
00996
00997 switch (GetVarMemType(conv)) {
00998 default: NOT_REACHED();
00999 case SLE_VAR_STR:
01000 case SLE_VAR_STRQ:
01001 str = *(const char**)ptr;
01002 len = SIZE_MAX;
01003 break;
01004 case SLE_VAR_STRB:
01005 case SLE_VAR_STRBQ:
01006 str = (const char*)ptr;
01007 len = length;
01008 break;
01009 }
01010
01011 len = SlCalcNetStringLen(str, len);
01012 return len + SlGetArrayLength(len);
01013 }
01014
01021 static void SlString(void *ptr, size_t length, VarType conv)
01022 {
01023 switch (_sl.action) {
01024 case SLA_SAVE: {
01025 size_t len;
01026 switch (GetVarMemType(conv)) {
01027 default: NOT_REACHED();
01028 case SLE_VAR_STRB:
01029 case SLE_VAR_STRBQ:
01030 len = SlCalcNetStringLen((char *)ptr, length);
01031 break;
01032 case SLE_VAR_STR:
01033 case SLE_VAR_STRQ:
01034 ptr = *(char **)ptr;
01035 len = SlCalcNetStringLen((char *)ptr, SIZE_MAX);
01036 break;
01037 }
01038
01039 SlWriteArrayLength(len);
01040 SlCopyBytes(ptr, len);
01041 break;
01042 }
01043 case SLA_LOAD_CHECK:
01044 case SLA_LOAD: {
01045 size_t len = SlReadArrayLength();
01046
01047 switch (GetVarMemType(conv)) {
01048 default: NOT_REACHED();
01049 case SLE_VAR_STRB:
01050 case SLE_VAR_STRBQ:
01051 if (len >= length) {
01052 DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating");
01053 SlCopyBytes(ptr, length);
01054 SlSkipBytes(len - length);
01055 len = length - 1;
01056 } else {
01057 SlCopyBytes(ptr, len);
01058 }
01059 break;
01060 case SLE_VAR_STR:
01061 case SLE_VAR_STRQ:
01062 free(*(char **)ptr);
01063 if (len == 0) {
01064 *(char **)ptr = NULL;
01065 } else {
01066 *(char **)ptr = MallocT<char>(len + 1);
01067 ptr = *(char **)ptr;
01068 SlCopyBytes(ptr, len);
01069 }
01070 break;
01071 }
01072
01073 ((char *)ptr)[len] = '\0';
01074 str_validate((char *)ptr, (char *)ptr + len);
01075 break;
01076 }
01077 case SLA_PTRS: break;
01078 case SLA_NULL: break;
01079 default: NOT_REACHED();
01080 }
01081 }
01082
01088 static inline size_t SlCalcArrayLen(size_t length, VarType conv)
01089 {
01090 return SlCalcConvFileLen(conv) * length;
01091 }
01092
01099 void SlArray(void *array, size_t length, VarType conv)
01100 {
01101 if (_sl.action == SLA_PTRS || _sl.action == SLA_NULL) return;
01102
01103
01104 if (_sl.need_length != NL_NONE) {
01105 SlSetLength(SlCalcArrayLen(length, conv));
01106
01107 if (_sl.need_length == NL_CALCLENGTH) return;
01108 }
01109
01110
01111
01112 if (_sl.action != SLA_SAVE && _sl_version == 0) {
01113
01114 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID ||
01115 conv == SLE_INT32 || conv == SLE_UINT32) {
01116 SlCopyBytes(array, length * SlCalcConvFileLen(conv));
01117 return;
01118 }
01119
01120 if (conv == (SLE_FILE_I32 | SLE_VAR_I64)) {
01121 for (uint i = 0; i < length; i++) {
01122 ((int64*)array)[i] = (int32)BSWAP32(SlReadUint32());
01123 }
01124 return;
01125 }
01126 }
01127
01128
01129
01130 if (conv == SLE_INT8 || conv == SLE_UINT8) {
01131 SlCopyBytes(array, length);
01132 } else {
01133 byte *a = (byte*)array;
01134 byte mem_size = SlCalcConvMemLen(conv);
01135
01136 for (; length != 0; length --) {
01137 SlSaveLoadConv(a, conv);
01138 a += mem_size;
01139 }
01140 }
01141 }
01142
01143
01154 static size_t ReferenceToInt(const void *obj, SLRefType rt)
01155 {
01156 assert(_sl.action == SLA_SAVE);
01157
01158 if (obj == NULL) return 0;
01159
01160 switch (rt) {
01161 case REF_VEHICLE_OLD:
01162 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1;
01163 case REF_STATION: return ((const Station*)obj)->index + 1;
01164 case REF_TOWN: return ((const Town*)obj)->index + 1;
01165 case REF_ORDER: return ((const Order*)obj)->index + 1;
01166 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
01167 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
01168 case REF_CARGO_PACKET: return ((const CargoPacket*)obj)->index + 1;
01169 case REF_ORDERLIST: return ((const OrderList*)obj)->index + 1;
01170 default: NOT_REACHED();
01171 }
01172 }
01173
01184 static void *IntToReference(size_t index, SLRefType rt)
01185 {
01186 assert_compile(sizeof(size_t) <= sizeof(void *));
01187
01188 assert(_sl.action == SLA_PTRS);
01189
01190
01191
01192 if (rt == REF_VEHICLE_OLD && !IsSavegameVersionBefore(4, 4)) {
01193 rt = REF_VEHICLE;
01194 }
01195
01196
01197 if (index == (rt == REF_VEHICLE_OLD ? 0xFFFF : 0)) return NULL;
01198
01199
01200
01201 if (rt != REF_VEHICLE_OLD) index--;
01202
01203 switch (rt) {
01204 case REF_ORDERLIST:
01205 if (OrderList::IsValidID(index)) return OrderList::Get(index);
01206 SlErrorCorrupt("Referencing invalid OrderList");
01207
01208 case REF_ORDER:
01209 if (Order::IsValidID(index)) return Order::Get(index);
01210
01211 if (IsSavegameVersionBefore(5, 2)) return NULL;
01212 SlErrorCorrupt("Referencing invalid Order");
01213
01214 case REF_VEHICLE_OLD:
01215 case REF_VEHICLE:
01216 if (Vehicle::IsValidID(index)) return Vehicle::Get(index);
01217 SlErrorCorrupt("Referencing invalid Vehicle");
01218
01219 case REF_STATION:
01220 if (Station::IsValidID(index)) return Station::Get(index);
01221 SlErrorCorrupt("Referencing invalid Station");
01222
01223 case REF_TOWN:
01224 if (Town::IsValidID(index)) return Town::Get(index);
01225 SlErrorCorrupt("Referencing invalid Town");
01226
01227 case REF_ROADSTOPS:
01228 if (RoadStop::IsValidID(index)) return RoadStop::Get(index);
01229 SlErrorCorrupt("Referencing invalid RoadStop");
01230
01231 case REF_ENGINE_RENEWS:
01232 if (EngineRenew::IsValidID(index)) return EngineRenew::Get(index);
01233 SlErrorCorrupt("Referencing invalid EngineRenew");
01234
01235 case REF_CARGO_PACKET:
01236 if (CargoPacket::IsValidID(index)) return CargoPacket::Get(index);
01237 SlErrorCorrupt("Referencing invalid CargoPacket");
01238
01239 default: NOT_REACHED();
01240 }
01241 }
01242
01247 static inline size_t SlCalcListLen(const void *list)
01248 {
01249 std::list<void *> *l = (std::list<void *> *) list;
01250
01251 int type_size = IsSavegameVersionBefore(69) ? 2 : 4;
01252
01253
01254 return l->size() * type_size + type_size;
01255 }
01256
01257
01263 static void SlList(void *list, SLRefType conv)
01264 {
01265
01266 if (_sl.need_length != NL_NONE) {
01267 SlSetLength(SlCalcListLen(list));
01268
01269 if (_sl.need_length == NL_CALCLENGTH) return;
01270 }
01271
01272 typedef std::list<void *> PtrList;
01273 PtrList *l = (PtrList *)list;
01274
01275 switch (_sl.action) {
01276 case SLA_SAVE: {
01277 SlWriteUint32((uint32)l->size());
01278
01279 PtrList::iterator iter;
01280 for (iter = l->begin(); iter != l->end(); ++iter) {
01281 void *ptr = *iter;
01282 SlWriteUint32((uint32)ReferenceToInt(ptr, conv));
01283 }
01284 break;
01285 }
01286 case SLA_LOAD_CHECK:
01287 case SLA_LOAD: {
01288 size_t length = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01289
01290
01291 for (size_t i = 0; i < length; i++) {
01292 size_t data = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01293 l->push_back((void *)data);
01294 }
01295 break;
01296 }
01297 case SLA_PTRS: {
01298 PtrList temp = *l;
01299
01300 l->clear();
01301 PtrList::iterator iter;
01302 for (iter = temp.begin(); iter != temp.end(); ++iter) {
01303 void *ptr = IntToReference((size_t)*iter, conv);
01304 l->push_back(ptr);
01305 }
01306 break;
01307 }
01308 case SLA_NULL:
01309 l->clear();
01310 break;
01311 default: NOT_REACHED();
01312 }
01313 }
01314
01315
01317 static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
01318 {
01319 if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false;
01320 if (sld->conv & SLF_SAVE_NO) return false;
01321
01322 return true;
01323 }
01324
01330 static inline bool SlSkipVariableOnLoad(const SaveLoad *sld)
01331 {
01332 if ((sld->conv & SLF_NETWORK_NO) && _sl.action != SLA_SAVE && _networking && !_network_server) {
01333 SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length);
01334 return true;
01335 }
01336
01337 return false;
01338 }
01339
01346 size_t SlCalcObjLength(const void *object, const SaveLoad *sld)
01347 {
01348 size_t length = 0;
01349
01350
01351 for (; sld->cmd != SL_END; sld++) {
01352 length += SlCalcObjMemberLength(object, sld);
01353 }
01354 return length;
01355 }
01356
01357 size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld)
01358 {
01359 assert(_sl.action == SLA_SAVE);
01360
01361 switch (sld->cmd) {
01362 case SL_VAR:
01363 case SL_REF:
01364 case SL_ARR:
01365 case SL_STR:
01366 case SL_LST:
01367
01368 if (!SlIsObjectValidInSavegame(sld)) break;
01369
01370 switch (sld->cmd) {
01371 case SL_VAR: return SlCalcConvFileLen(sld->conv);
01372 case SL_REF: return SlCalcRefLen();
01373 case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv);
01374 case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv);
01375 case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld));
01376 default: NOT_REACHED();
01377 }
01378 break;
01379 case SL_WRITEBYTE: return 1;
01380 case SL_VEH_INCLUDE: return SlCalcObjLength(object, GetVehicleDescription(VEH_END));
01381 case SL_ST_INCLUDE: return SlCalcObjLength(object, GetBaseStationDescription());
01382 default: NOT_REACHED();
01383 }
01384 return 0;
01385 }
01386
01387
01388 bool SlObjectMember(void *ptr, const SaveLoad *sld)
01389 {
01390 VarType conv = GB(sld->conv, 0, 8);
01391 switch (sld->cmd) {
01392 case SL_VAR:
01393 case SL_REF:
01394 case SL_ARR:
01395 case SL_STR:
01396 case SL_LST:
01397
01398 if (!SlIsObjectValidInSavegame(sld)) return false;
01399 if (SlSkipVariableOnLoad(sld)) return false;
01400
01401 switch (sld->cmd) {
01402 case SL_VAR: SlSaveLoadConv(ptr, conv); break;
01403 case SL_REF:
01404 switch (_sl.action) {
01405 case SLA_SAVE:
01406 SlWriteUint32((uint32)ReferenceToInt(*(void **)ptr, (SLRefType)conv));
01407 break;
01408 case SLA_LOAD_CHECK:
01409 case SLA_LOAD:
01410 *(size_t *)ptr = IsSavegameVersionBefore(69) ? SlReadUint16() : SlReadUint32();
01411 break;
01412 case SLA_PTRS:
01413 *(void **)ptr = IntToReference(*(size_t *)ptr, (SLRefType)conv);
01414 break;
01415 case SLA_NULL:
01416 *(void **)ptr = NULL;
01417 break;
01418 default: NOT_REACHED();
01419 }
01420 break;
01421 case SL_ARR: SlArray(ptr, sld->length, conv); break;
01422 case SL_STR: SlString(ptr, sld->length, conv); break;
01423 case SL_LST: SlList(ptr, (SLRefType)conv); break;
01424 default: NOT_REACHED();
01425 }
01426 break;
01427
01428
01429
01430
01431
01432
01433 case SL_WRITEBYTE:
01434 switch (_sl.action) {
01435 case SLA_SAVE: SlWriteByte(sld->version_to); break;
01436 case SLA_LOAD_CHECK:
01437 case SLA_LOAD: *(byte *)ptr = sld->version_from; break;
01438 case SLA_PTRS: break;
01439 case SLA_NULL: break;
01440 default: NOT_REACHED();
01441 }
01442 break;
01443
01444
01445 case SL_VEH_INCLUDE:
01446 SlObject(ptr, GetVehicleDescription(VEH_END));
01447 break;
01448
01449 case SL_ST_INCLUDE:
01450 SlObject(ptr, GetBaseStationDescription());
01451 break;
01452
01453 default: NOT_REACHED();
01454 }
01455 return true;
01456 }
01457
01463 void SlObject(void *object, const SaveLoad *sld)
01464 {
01465
01466 if (_sl.need_length != NL_NONE) {
01467 SlSetLength(SlCalcObjLength(object, sld));
01468 if (_sl.need_length == NL_CALCLENGTH) return;
01469 }
01470
01471 for (; sld->cmd != SL_END; sld++) {
01472 void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld);
01473 SlObjectMember(ptr, sld);
01474 }
01475 }
01476
01481 void SlGlobList(const SaveLoadGlobVarList *sldg)
01482 {
01483 SlObject(NULL, (const SaveLoad*)sldg);
01484 }
01485
01491 void SlAutolength(AutolengthProc *proc, void *arg)
01492 {
01493 size_t offs;
01494
01495 assert(_sl.action == SLA_SAVE);
01496
01497
01498 _sl.need_length = NL_CALCLENGTH;
01499 _sl.obj_len = 0;
01500 proc(arg);
01501
01502
01503 _sl.need_length = NL_WANTLENGTH;
01504 SlSetLength(_sl.obj_len);
01505
01506 offs = _sl.dumper->GetSize() + _sl.obj_len;
01507
01508
01509 proc(arg);
01510
01511 if (offs != _sl.dumper->GetSize()) SlErrorCorrupt("Invalid chunk size");
01512 }
01513
01518 static void SlLoadChunk(const ChunkHandler *ch)
01519 {
01520 byte m = SlReadByte();
01521 size_t len;
01522 size_t endoffs;
01523
01524 _sl.block_mode = m;
01525 _sl.obj_len = 0;
01526
01527 switch (m) {
01528 case CH_ARRAY:
01529 _sl.array_index = 0;
01530 ch->load_proc();
01531 break;
01532 case CH_SPARSE_ARRAY:
01533 ch->load_proc();
01534 break;
01535 default:
01536 if ((m & 0xF) == CH_RIFF) {
01537
01538 len = (SlReadByte() << 16) | ((m >> 4) << 24);
01539 len += SlReadUint16();
01540 _sl.obj_len = len;
01541 endoffs = _sl.reader->GetSize() + len;
01542 ch->load_proc();
01543 if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size");
01544 } else {
01545 SlErrorCorrupt("Invalid chunk type");
01546 }
01547 break;
01548 }
01549 }
01550
01556 static void SlLoadCheckChunk(const ChunkHandler *ch)
01557 {
01558 byte m = SlReadByte();
01559 size_t len;
01560 size_t endoffs;
01561
01562 _sl.block_mode = m;
01563 _sl.obj_len = 0;
01564
01565 switch (m) {
01566 case CH_ARRAY:
01567 _sl.array_index = 0;
01568 if (ch->load_check_proc) {
01569 ch->load_check_proc();
01570 } else {
01571 SlSkipArray();
01572 }
01573 break;
01574 case CH_SPARSE_ARRAY:
01575 if (ch->load_check_proc) {
01576 ch->load_check_proc();
01577 } else {
01578 SlSkipArray();
01579 }
01580 break;
01581 default:
01582 if ((m & 0xF) == CH_RIFF) {
01583
01584 len = (SlReadByte() << 16) | ((m >> 4) << 24);
01585 len += SlReadUint16();
01586 _sl.obj_len = len;
01587 endoffs = _sl.reader->GetSize() + len;
01588 if (ch->load_check_proc) {
01589 ch->load_check_proc();
01590 } else {
01591 SlSkipBytes(len);
01592 }
01593 if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size");
01594 } else {
01595 SlErrorCorrupt("Invalid chunk type");
01596 }
01597 break;
01598 }
01599 }
01600
01605 static ChunkSaveLoadProc *_stub_save_proc;
01606
01612 static inline void SlStubSaveProc2(void *arg)
01613 {
01614 _stub_save_proc();
01615 }
01616
01622 static void SlStubSaveProc()
01623 {
01624 SlAutolength(SlStubSaveProc2, NULL);
01625 }
01626
01632 static void SlSaveChunk(const ChunkHandler *ch)
01633 {
01634 ChunkSaveLoadProc *proc = ch->save_proc;
01635
01636
01637 if (proc == NULL) return;
01638
01639 SlWriteUint32(ch->id);
01640 DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
01641
01642 if (ch->flags & CH_AUTO_LENGTH) {
01643
01644 _stub_save_proc = proc;
01645 proc = SlStubSaveProc;
01646 }
01647
01648 _sl.block_mode = ch->flags & CH_TYPE_MASK;
01649 switch (ch->flags & CH_TYPE_MASK) {
01650 case CH_RIFF:
01651 _sl.need_length = NL_WANTLENGTH;
01652 proc();
01653 break;
01654 case CH_ARRAY:
01655 _sl.last_array_index = 0;
01656 SlWriteByte(CH_ARRAY);
01657 proc();
01658 SlWriteArrayLength(0);
01659 break;
01660 case CH_SPARSE_ARRAY:
01661 SlWriteByte(CH_SPARSE_ARRAY);
01662 proc();
01663 SlWriteArrayLength(0);
01664 break;
01665 default: NOT_REACHED();
01666 }
01667 }
01668
01670 static void SlSaveChunks()
01671 {
01672 FOR_ALL_CHUNK_HANDLERS(ch) {
01673 SlSaveChunk(ch);
01674 }
01675
01676
01677 SlWriteUint32(0);
01678 }
01679
01686 static const ChunkHandler *SlFindChunkHandler(uint32 id)
01687 {
01688 FOR_ALL_CHUNK_HANDLERS(ch) if (ch->id == id) return ch;
01689 return NULL;
01690 }
01691
01693 static void SlLoadChunks()
01694 {
01695 uint32 id;
01696 const ChunkHandler *ch;
01697
01698 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
01699 DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
01700
01701 ch = SlFindChunkHandler(id);
01702 if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
01703 SlLoadChunk(ch);
01704 }
01705 }
01706
01708 static void SlLoadCheckChunks()
01709 {
01710 uint32 id;
01711 const ChunkHandler *ch;
01712
01713 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
01714 DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
01715
01716 ch = SlFindChunkHandler(id);
01717 if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
01718 SlLoadCheckChunk(ch);
01719 }
01720 }
01721
01723 static void SlFixPointers()
01724 {
01725 _sl.action = SLA_PTRS;
01726
01727 DEBUG(sl, 1, "Fixing pointers");
01728
01729 FOR_ALL_CHUNK_HANDLERS(ch) {
01730 if (ch->ptrs_proc != NULL) {
01731 DEBUG(sl, 2, "Fixing pointers for %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id);
01732 ch->ptrs_proc();
01733 }
01734 }
01735
01736 DEBUG(sl, 1, "All pointers fixed");
01737
01738 assert(_sl.action == SLA_PTRS);
01739 }
01740
01741
01743 struct FileReader : LoadFilter {
01744 FILE *file;
01745 long begin;
01746
01751 FileReader(FILE *file) : LoadFilter(NULL), file(file), begin(ftell(file))
01752 {
01753 }
01754
01756 ~FileReader()
01757 {
01758 if (this->file != NULL) fclose(this->file);
01759 this->file = NULL;
01760
01761
01762 _sl.sf = NULL;
01763 }
01764
01765 size_t Read(byte *buf, size_t size)
01766 {
01767
01768 if (this->file == NULL) return 0;
01769
01770 return fread(buf, 1, size, this->file);
01771 }
01772
01773 void Reset()
01774 {
01775 clearerr(this->file);
01776 fseek(this->file, this->begin, SEEK_SET);
01777 }
01778 };
01779
01781 struct FileWriter : SaveFilter {
01782 FILE *file;
01783
01788 FileWriter(FILE *file) : SaveFilter(NULL), file(file)
01789 {
01790 }
01791
01793 ~FileWriter()
01794 {
01795 this->Finish();
01796
01797
01798 _sl.sf = NULL;
01799 }
01800
01801 void Write(byte *buf, size_t size)
01802 {
01803
01804 if (this->file == NULL) return;
01805
01806 if (fwrite(buf, 1, size, this->file) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE);
01807 }
01808
01809 void Finish()
01810 {
01811 if (this->file != NULL) fclose(this->file);
01812 this->file = NULL;
01813 }
01814 };
01815
01816
01817
01818
01819
01820 #ifdef WITH_LZO
01821 #include <lzo/lzo1x.h>
01822
01824 static const uint LZO_BUFFER_SIZE = 8192;
01825
01827 struct LZOLoadFilter : LoadFilter {
01832 LZOLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01833 {
01834 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
01835 }
01836
01837 size_t Read(byte *buf, size_t ssize)
01838 {
01839 assert(ssize >= LZO_BUFFER_SIZE);
01840
01841
01842 byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2];
01843 uint32 tmp[2];
01844 uint32 size;
01845 lzo_uint len;
01846
01847
01848 if (this->chain->Read((byte*)tmp, sizeof(tmp)) != sizeof(tmp)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE, "File read failed");
01849
01850
01851 ((uint32*)out)[0] = size = tmp[1];
01852
01853 if (_sl_version != 0) {
01854 tmp[0] = TO_BE32(tmp[0]);
01855 size = TO_BE32(size);
01856 }
01857
01858 if (size >= sizeof(out)) SlErrorCorrupt("Inconsistent size");
01859
01860
01861 if (this->chain->Read(out + sizeof(uint32), size) != size) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
01862
01863
01864 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlErrorCorrupt("Bad checksum");
01865
01866
01867 lzo1x_decompress(out + sizeof(uint32) * 1, size, buf, &len, NULL);
01868 return len;
01869 }
01870 };
01871
01873 struct LZOSaveFilter : SaveFilter {
01879 LZOSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
01880 {
01881 if (lzo_init() != LZO_E_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
01882 }
01883
01884 void Write(byte *buf, size_t size)
01885 {
01886 const lzo_bytep in = buf;
01887
01888 byte out[LZO_BUFFER_SIZE + LZO_BUFFER_SIZE / 16 + 64 + 3 + sizeof(uint32) * 2];
01889 byte wrkmem[LZO1X_1_MEM_COMPRESS];
01890 lzo_uint outlen;
01891
01892 do {
01893
01894 lzo_uint len = size > LZO_BUFFER_SIZE ? LZO_BUFFER_SIZE : (lzo_uint)size;
01895 lzo1x_1_compress(in, len, out + sizeof(uint32) * 2, &outlen, wrkmem);
01896 ((uint32*)out)[1] = TO_BE32((uint32)outlen);
01897 ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32)));
01898 this->chain->Write(out, outlen + sizeof(uint32) * 2);
01899
01900
01901 size -= len;
01902 in += len;
01903 } while (size > 0);
01904 }
01905 };
01906
01907 #endif
01908
01909
01910
01911
01912
01914 struct NoCompLoadFilter : LoadFilter {
01919 NoCompLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01920 {
01921 }
01922
01923 size_t Read(byte *buf, size_t size)
01924 {
01925 return this->chain->Read(buf, size);
01926 }
01927 };
01928
01930 struct NoCompSaveFilter : SaveFilter {
01936 NoCompSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
01937 {
01938 }
01939
01940 void Write(byte *buf, size_t size)
01941 {
01942 this->chain->Write(buf, size);
01943 }
01944 };
01945
01946
01947
01948
01949
01950 #if defined(WITH_ZLIB)
01951 #include <zlib.h>
01952
01954 struct ZlibLoadFilter : LoadFilter {
01955 z_stream z;
01956 byte fread_buf[MEMORY_CHUNK_SIZE];
01957
01962 ZlibLoadFilter(LoadFilter *chain) : LoadFilter(chain)
01963 {
01964 memset(&this->z, 0, sizeof(this->z));
01965 if (inflateInit(&this->z) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
01966 }
01967
01969 ~ZlibLoadFilter()
01970 {
01971 inflateEnd(&this->z);
01972 }
01973
01974 size_t Read(byte *buf, size_t size)
01975 {
01976 this->z.next_out = buf;
01977 this->z.avail_out = (uint)size;
01978
01979 do {
01980
01981 if (this->z.avail_in == 0) {
01982 this->z.next_in = this->fread_buf;
01983 this->z.avail_in = (uint)this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
01984 }
01985
01986
01987 int r = inflate(&this->z, 0);
01988 if (r == Z_STREAM_END) break;
01989
01990 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "inflate() failed");
01991 } while (this->z.avail_out != 0);
01992
01993 return size - this->z.avail_out;
01994 }
01995 };
01996
01998 struct ZlibSaveFilter : SaveFilter {
01999 z_stream z;
02000
02006 ZlibSaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain)
02007 {
02008 memset(&this->z, 0, sizeof(this->z));
02009 if (deflateInit(&this->z, compression_level) != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
02010 }
02011
02018 void WriteLoop(byte *p, size_t len, int mode)
02019 {
02020 byte buf[MEMORY_CHUNK_SIZE];
02021 uint n;
02022 this->z.next_in = p;
02023 this->z.avail_in = (uInt)len;
02024 do {
02025 this->z.next_out = buf;
02026 this->z.avail_out = sizeof(buf);
02027
02035 int r = deflate(&this->z, mode);
02036
02037
02038 if ((n = sizeof(buf) - this->z.avail_out) != 0) {
02039 this->chain->Write(buf, n);
02040 }
02041 if (r == Z_STREAM_END) break;
02042
02043 if (r != Z_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "zlib returned error code");
02044 } while (this->z.avail_in || !this->z.avail_out);
02045 }
02046
02047 void Write(byte *buf, size_t size)
02048 {
02049 this->WriteLoop(buf, size, 0);
02050 }
02051
02052 void Finish()
02053 {
02054 this->WriteLoop(NULL, 0, Z_FINISH);
02055 this->chain->Finish();
02056 deflateEnd(&this->z);
02057 }
02058 };
02059
02060 #endif
02061
02062
02063
02064
02065
02066 #if defined(WITH_LZMA)
02067 #include <lzma.h>
02068
02075 static const lzma_stream _lzma_init = LZMA_STREAM_INIT;
02076
02078 struct LZMALoadFilter : LoadFilter {
02079 lzma_stream lzma;
02080 byte fread_buf[MEMORY_CHUNK_SIZE];
02081
02086 LZMALoadFilter(LoadFilter *chain) : LoadFilter(chain), lzma(_lzma_init)
02087 {
02088
02089 if (lzma_auto_decoder(&this->lzma, 1 << 28, 0) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize decompressor");
02090 }
02091
02093 ~LZMALoadFilter()
02094 {
02095 lzma_end(&this->lzma);
02096 }
02097
02098 size_t Read(byte *buf, size_t size)
02099 {
02100 this->lzma.next_out = buf;
02101 this->lzma.avail_out = size;
02102
02103 do {
02104
02105 if (this->lzma.avail_in == 0) {
02106 this->lzma.next_in = this->fread_buf;
02107 this->lzma.avail_in = this->chain->Read(this->fread_buf, sizeof(this->fread_buf));
02108 }
02109
02110
02111 lzma_ret r = lzma_code(&this->lzma, LZMA_RUN);
02112 if (r == LZMA_STREAM_END) break;
02113 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
02114 } while (this->lzma.avail_out != 0);
02115
02116 return size - this->lzma.avail_out;
02117 }
02118 };
02119
02121 struct LZMASaveFilter : SaveFilter {
02122 lzma_stream lzma;
02123
02129 LZMASaveFilter(SaveFilter *chain, byte compression_level) : SaveFilter(chain), lzma(_lzma_init)
02130 {
02131 if (lzma_easy_encoder(&this->lzma, compression_level, LZMA_CHECK_CRC32) != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
02132 }
02133
02140 void WriteLoop(byte *p, size_t len, lzma_action action)
02141 {
02142 byte buf[MEMORY_CHUNK_SIZE];
02143 size_t n;
02144 this->lzma.next_in = p;
02145 this->lzma.avail_in = len;
02146 do {
02147 this->lzma.next_out = buf;
02148 this->lzma.avail_out = sizeof(buf);
02149
02150 lzma_ret r = lzma_code(&this->lzma, action);
02151
02152
02153 if ((n = sizeof(buf) - this->lzma.avail_out) != 0) {
02154 this->chain->Write(buf, n);
02155 }
02156 if (r == LZMA_STREAM_END) break;
02157 if (r != LZMA_OK) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "liblzma returned error code");
02158 } while (this->lzma.avail_in || !this->lzma.avail_out);
02159 }
02160
02161 void Write(byte *buf, size_t size)
02162 {
02163 this->WriteLoop(buf, size, LZMA_RUN);
02164 }
02165
02166 void Finish()
02167 {
02168 this->WriteLoop(NULL, 0, LZMA_FINISH);
02169 this->chain->Finish();
02170 lzma_end(&this->lzma);
02171 }
02172 };
02173
02174 #endif
02175
02176
02177
02178
02179
02181 struct SaveLoadFormat {
02182 const char *name;
02183 uint32 tag;
02184
02185 LoadFilter *(*init_load)(LoadFilter *chain);
02186 SaveFilter *(*init_write)(SaveFilter *chain, byte compression);
02187
02188 byte min_compression;
02189 byte default_compression;
02190 byte max_compression;
02191 };
02192
02194 static const SaveLoadFormat _saveload_formats[] = {
02195 #if defined(WITH_LZO)
02196
02197 {"lzo", TO_BE32X('OTTD'), CreateLoadFilter<LZOLoadFilter>, CreateSaveFilter<LZOSaveFilter>, 0, 0, 0},
02198 #else
02199 {"lzo", TO_BE32X('OTTD'), NULL, NULL, 0, 0, 0},
02200 #endif
02201
02202 {"none", TO_BE32X('OTTN'), CreateLoadFilter<NoCompLoadFilter>, CreateSaveFilter<NoCompSaveFilter>, 0, 0, 0},
02203 #if defined(WITH_ZLIB)
02204
02205
02206
02207 {"zlib", TO_BE32X('OTTZ'), CreateLoadFilter<ZlibLoadFilter>, CreateSaveFilter<ZlibSaveFilter>, 0, 6, 9},
02208 #else
02209 {"zlib", TO_BE32X('OTTZ'), NULL, NULL, 0, 0, 0},
02210 #endif
02211 #if defined(WITH_LZMA)
02212
02213
02214
02215
02216
02217 {"lzma", TO_BE32X('OTTX'), CreateLoadFilter<LZMALoadFilter>, CreateSaveFilter<LZMASaveFilter>, 0, 2, 9},
02218 #else
02219 {"lzma", TO_BE32X('OTTX'), NULL, NULL, 0, 0, 0},
02220 #endif
02221 };
02222
02230 static const SaveLoadFormat *GetSavegameFormat(char *s, byte *compression_level)
02231 {
02232 const SaveLoadFormat *def = lastof(_saveload_formats);
02233
02234
02235 while (!def->init_write) def--;
02236
02237 if (!StrEmpty(s)) {
02238
02239 char *complevel = strrchr(s, ':');
02240 if (complevel != NULL) *complevel = '\0';
02241
02242 for (const SaveLoadFormat *slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) {
02243 if (slf->init_write != NULL && strcmp(s, slf->name) == 0) {
02244 *compression_level = slf->default_compression;
02245 if (complevel != NULL) {
02246
02247
02248
02249 *complevel = ':';
02250 complevel++;
02251
02252
02253 char *end;
02254 long level = strtol(complevel, &end, 10);
02255 if (end == complevel || level != Clamp(level, slf->min_compression, slf->max_compression)) {
02256 ShowInfoF("Compression level '%s' is not valid.", complevel);
02257 } else {
02258 *compression_level = level;
02259 }
02260 }
02261 return slf;
02262 }
02263 }
02264
02265 ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name);
02266
02267
02268 if (complevel != NULL) *complevel = ':';
02269 }
02270 *compression_level = def->default_compression;
02271 return def;
02272 }
02273
02274
02275 void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings);
02276 extern bool AfterLoadGame();
02277 extern bool LoadOldSaveGame(const char *file);
02278
02282 static inline void ClearSaveLoadState()
02283 {
02284 delete _sl.dumper;
02285 _sl.dumper = NULL;
02286
02287 delete _sl.sf;
02288 _sl.sf = NULL;
02289
02290 delete _sl.reader;
02291 _sl.reader = NULL;
02292
02293 delete _sl.lf;
02294 _sl.lf = NULL;
02295 }
02296
02302 static void SaveFileStart()
02303 {
02304 _sl.ff_state = _fast_forward;
02305 _fast_forward = 0;
02306 if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
02307
02308 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_START);
02309 _sl.saveinprogress = true;
02310 }
02311
02313 static void SaveFileDone()
02314 {
02315 if (_game_mode != GM_MENU) _fast_forward = _sl.ff_state;
02316 if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
02317
02318 InvalidateWindowData(WC_STATUS_BAR, 0, SBI_SAVELOAD_FINISH);
02319 _sl.saveinprogress = false;
02320 }
02321
02323 void SetSaveLoadError(StringID str)
02324 {
02325 _sl.error_str = str;
02326 }
02327
02329 const char *GetSaveLoadErrorString()
02330 {
02331 SetDParam(0, _sl.error_str);
02332 SetDParamStr(1, _sl.extra_msg);
02333
02334 static char err_str[512];
02335 GetString(err_str, _sl.action == SLA_SAVE ? STR_ERROR_GAME_SAVE_FAILED : STR_ERROR_GAME_LOAD_FAILED, lastof(err_str));
02336 return err_str;
02337 }
02338
02340 static void SaveFileError()
02341 {
02342 SetDParamStr(0, GetSaveLoadErrorString());
02343 ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
02344 SaveFileDone();
02345 }
02346
02351 static SaveOrLoadResult SaveFileToDisk(bool threaded)
02352 {
02353 try {
02354 byte compression;
02355 const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
02356
02357
02358 uint32 hdr[2] = { fmt->tag, TO_BE32(SAVEGAME_VERSION << 16) };
02359 _sl.sf->Write((byte*)hdr, sizeof(hdr));
02360
02361 _sl.sf = fmt->init_write(_sl.sf, compression);
02362 _sl.dumper->Flush(_sl.sf);
02363
02364 ClearSaveLoadState();
02365
02366 if (threaded) SetAsyncSaveFinish(SaveFileDone);
02367
02368 return SL_OK;
02369 } catch (...) {
02370 ClearSaveLoadState();
02371
02372
02373 DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
02374
02375 if (threaded) {
02376 SetAsyncSaveFinish(SaveFileError);
02377 } else {
02378 SaveFileError();
02379 }
02380 return SL_ERROR;
02381 }
02382 }
02383
02385 static void SaveFileToDiskThread(void *arg)
02386 {
02387 SaveFileToDisk(true);
02388 }
02389
02390 void WaitTillSaved()
02391 {
02392 if (_save_thread == NULL) return;
02393
02394 _save_thread->Join();
02395 delete _save_thread;
02396 _save_thread = NULL;
02397 }
02398
02407 static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded)
02408 {
02409 assert(!_sl.saveinprogress);
02410
02411 _sl.dumper = new MemoryDumper();
02412 _sl.sf = writer;
02413
02414 _sl_version = SAVEGAME_VERSION;
02415
02416 SaveViewportBeforeSaveGame();
02417 SlSaveChunks();
02418
02419 SaveFileStart();
02420 if (!threaded || !ThreadObject::New(&SaveFileToDiskThread, NULL, &_save_thread)) {
02421 if (threaded) DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode...");
02422
02423 SaveOrLoadResult result = SaveFileToDisk(false);
02424 SaveFileDone();
02425
02426 return result;
02427 }
02428
02429 return SL_OK;
02430 }
02431
02438 SaveOrLoadResult SaveWithFilter(SaveFilter *writer, bool threaded)
02439 {
02440 try {
02441 _sl.action = SLA_SAVE;
02442 return DoSave(writer, threaded);
02443 } catch (...) {
02444 ClearSaveLoadState();
02445 return SL_ERROR;
02446 }
02447 }
02448
02455 static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
02456 {
02457 _sl.lf = reader;
02458
02459 if (load_check) {
02460
02461 _load_check_data.Clear();
02462
02463 _load_check_data.checkable = true;
02464 }
02465
02466 uint32 hdr[2];
02467 if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
02468
02469
02470 const SaveLoadFormat *fmt = _saveload_formats;
02471 for (;;) {
02472
02473 if (fmt == endof(_saveload_formats)) {
02474 DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
02475 _sl.lf->Reset();
02476 _sl_version = 0;
02477 _sl_minor_version = 0;
02478
02479
02480 fmt = _saveload_formats;
02481 for (;;) {
02482 if (fmt == endof(_saveload_formats)) {
02483
02484 NOT_REACHED();
02485 }
02486 if (fmt->tag == TO_BE32X('OTTD')) break;
02487 fmt++;
02488 }
02489 break;
02490 }
02491
02492 if (fmt->tag == hdr[0]) {
02493
02494 _sl_version = TO_BE32(hdr[1]) >> 16;
02495
02496
02497
02498
02499 _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
02500
02501 DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
02502
02503
02504 if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
02505 break;
02506 }
02507
02508 fmt++;
02509 }
02510
02511
02512 if (fmt->init_load == NULL) {
02513 char err_str[64];
02514 snprintf(err_str, lengthof(err_str), "Loader for '%s' is not available.", fmt->name);
02515 SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, err_str);
02516 }
02517
02518 _sl.lf = fmt->init_load(_sl.lf);
02519 _sl.reader = new ReadBuffer(_sl.lf);
02520 _next_offs = 0;
02521
02522 if (!load_check) {
02523 _engine_mngr.ResetToDefaultMapping();
02524
02525
02526
02527
02528 InitializeGame(256, 256, true, true);
02529
02530 GamelogReset();
02531
02532 if (IsSavegameVersionBefore(4)) {
02533
02534
02535
02536
02537
02538
02539
02540
02541
02542
02543
02544
02545
02546
02547
02548
02549
02550
02551
02552
02553
02554 ClearGRFConfigList(&_grfconfig);
02555 }
02556 }
02557
02558 if (load_check) {
02559
02560
02561 SlLoadCheckChunks();
02562 } else {
02563
02564 SlLoadChunks();
02565 SlFixPointers();
02566 }
02567
02568 ClearSaveLoadState();
02569
02570 _savegame_type = SGT_OTTD;
02571
02572 if (load_check) {
02573
02574 _load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
02575 } else {
02576 GamelogStartAction(GLAT_LOAD);
02577
02578
02579
02580 if (!AfterLoadGame()) {
02581 GamelogStopAction();
02582 return SL_REINIT;
02583 }
02584
02585 GamelogStopAction();
02586 }
02587
02588 return SL_OK;
02589 }
02590
02596 SaveOrLoadResult LoadWithFilter(LoadFilter *reader)
02597 {
02598 try {
02599 _sl.action = SLA_LOAD;
02600 return DoLoad(reader, false);
02601 } catch (...) {
02602 ClearSaveLoadState();
02603 return SL_REINIT;
02604 }
02605 }
02606
02616 SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, bool threaded)
02617 {
02618
02619 if (_sl.saveinprogress && mode == SL_SAVE && threaded) {
02620
02621 if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR);
02622 return SL_OK;
02623 }
02624 WaitTillSaved();
02625
02626
02627 if (mode == SL_OLD_LOAD) {
02628 _engine_mngr.ResetToDefaultMapping();
02629 InitializeGame(256, 256, true, true);
02630
02631
02632
02633
02634
02635 ClearGRFConfigList(&_grfconfig);
02636 GamelogReset();
02637 if (!LoadOldSaveGame(filename)) return SL_REINIT;
02638 _sl_version = 0;
02639 _sl_minor_version = 0;
02640 GamelogStartAction(GLAT_LOAD);
02641 if (!AfterLoadGame()) {
02642 GamelogStopAction();
02643 return SL_REINIT;
02644 }
02645 GamelogStopAction();
02646 return SL_OK;
02647 }
02648
02649 switch (mode) {
02650 case SL_LOAD_CHECK: _sl.action = SLA_LOAD_CHECK; break;
02651 case SL_LOAD: _sl.action = SLA_LOAD; break;
02652 case SL_SAVE: _sl.action = SLA_SAVE; break;
02653 default: NOT_REACHED();
02654 }
02655
02656 try {
02657 FILE *fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
02658
02659
02660 if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
02661 if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
02662
02663 if (fh == NULL) {
02664 SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
02665 }
02666
02667 if (mode == SL_SAVE) {
02668 DEBUG(desync, 1, "save: %08x; %02x; %s", _date, _date_fract, filename);
02669 if (_network_server || !_settings_client.gui.threaded_saves) threaded = false;
02670
02671 return DoSave(new FileWriter(fh), threaded);
02672 }
02673
02674
02675 assert(mode == SL_LOAD || mode == SL_LOAD_CHECK);
02676 DEBUG(desync, 1, "load: %s", filename);
02677 return DoLoad(new FileReader(fh), mode == SL_LOAD_CHECK);
02678 } catch (...) {
02679 ClearSaveLoadState();
02680
02681
02682 if (mode != SL_LOAD_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
02683
02684
02685 return (mode == SL_LOAD) ? SL_REINIT : SL_ERROR;
02686 }
02687 }
02688
02690 void DoExitSave()
02691 {
02692 SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR);
02693 }
02694
02700 void GenerateDefaultSaveName(char *buf, const char *last)
02701 {
02702
02703
02704
02705 CompanyID cid = _local_company;
02706 if (!Company::IsValidID(cid)) {
02707 const Company *c;
02708 FOR_ALL_COMPANIES(c) {
02709 cid = c->index;
02710 break;
02711 }
02712 }
02713
02714 SetDParam(0, cid);
02715
02716
02717 switch (_settings_client.gui.date_format_in_default_names) {
02718 case 0: SetDParam(1, STR_JUST_DATE_LONG); break;
02719 case 1: SetDParam(1, STR_JUST_DATE_TINY); break;
02720 case 2: SetDParam(1, STR_JUST_DATE_ISO); break;
02721 default: NOT_REACHED();
02722 }
02723 SetDParam(2, _date);
02724
02725
02726 GetString(buf, !Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, last);
02727 SanitizeFilename(buf);
02728 }
02729
02730 #if 0
02731
02737 int GetSavegameType(char *file)
02738 {
02739 const SaveLoadFormat *fmt;
02740 uint32 hdr;
02741 FILE *f;
02742 int mode = SL_OLD_LOAD;
02743
02744 f = fopen(file, "rb");
02745 if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
02746 DEBUG(sl, 0, "Savegame is obsolete or invalid format");
02747 mode = SL_LOAD;
02748 } else {
02749
02750 for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) {
02751 if (fmt->tag == hdr) {
02752 mode = SL_LOAD;
02753 break;
02754 }
02755 }
02756 }
02757
02758 fclose(f);
02759 return mode;
02760 }
02761 #endif