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