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