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