00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "../stdafx.h"
00013 #include "../openttd.h"
00014 #include "../debug.h"
00015 #include "../strings_type.h"
00016 #include "../string_func.h"
00017 #include "../settings_type.h"
00018 #include "../fileio_func.h"
00019
00020 #include "table/strings.h"
00021
00022 #include "saveload_internal.h"
00023 #include "oldloader.h"
00024 #include <exception>
00025
00026 enum {
00027 TTO_HEADER_SIZE = 41,
00028 TTD_HEADER_SIZE = 49,
00029 };
00030
00031 uint32 _bump_assert_value;
00032
00033 static inline OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 4);}
00034 static inline OldChunkType GetOldChunkVarType(OldChunkType type) {return (OldChunkType)(GB(type, 8, 8) << 8);}
00035 static inline OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);}
00036
00037 static inline byte CalcOldVarLen(OldChunkType type)
00038 {
00039 static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8};
00040 byte length = GB(type, 8, 8);
00041 assert(length != 0 && length < lengthof(type_mem_size));
00042 return type_mem_size[length];
00043 }
00044
00050 static byte ReadByteFromFile(LoadgameState *ls)
00051 {
00052
00053
00054 if (ls->buffer_cur >= ls->buffer_count) {
00055
00056
00057 int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file);
00058
00059
00060 if (count == 0) {
00061 DEBUG(oldloader, 0, "Read past end of file, loading failed");
00062 throw std::exception();
00063 }
00064
00065 ls->buffer_count = count;
00066 ls->buffer_cur = 0;
00067 }
00068
00069 return ls->buffer[ls->buffer_cur++];
00070 }
00071
00077 byte ReadByte(LoadgameState *ls)
00078 {
00079
00080
00081
00082
00083
00084
00085 if (ls->chunk_size == 0) {
00086
00087 int8 new_byte = ReadByteFromFile(ls);
00088
00089 if (new_byte < 0) {
00090
00091 ls->decoding = true;
00092 ls->decode_char = ReadByteFromFile(ls);
00093 ls->chunk_size = -new_byte + 1;
00094 } else {
00095 ls->decoding = false;
00096 ls->chunk_size = new_byte + 1;
00097 }
00098 }
00099
00100 ls->total_read++;
00101 ls->chunk_size--;
00102
00103 return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
00104 }
00105
00111 bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
00112 {
00113 byte *base_ptr = (byte*)base;
00114
00115 for (const OldChunks *chunk = chunks; chunk->type != OC_END; chunk++) {
00116 if (((chunk->type & OC_TTD) && _savegame_type == SGT_TTO) ||
00117 ((chunk->type & OC_TTO) && _savegame_type != SGT_TTO)) {
00118
00119 continue;
00120 }
00121
00122 byte *ptr = (byte*)chunk->ptr;
00123 if (chunk->type & OC_DEREFERENCE_POINTER) ptr = *(byte**)ptr;
00124
00125 for (uint i = 0; i < chunk->amount; i++) {
00126
00127 if (GetOldChunkType(chunk->type) != 0) {
00128 switch (GetOldChunkType(chunk->type)) {
00129
00130 case OC_NULL: ReadByte(ls); break;
00131
00132 case OC_CHUNK:
00133
00134
00135 if (!chunk->proc(ls, i)) return false;
00136 break;
00137
00138 case OC_ASSERT:
00139 DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value);
00140 if (ls->total_read != chunk->offset + _bump_assert_value) throw std::exception();
00141 default: break;
00142 }
00143 } else {
00144 uint64 res = 0;
00145
00146
00147 switch (GetOldChunkFileType(chunk->type)) {
00148 case OC_FILE_I8: res = (int8)ReadByte(ls); break;
00149 case OC_FILE_U8: res = ReadByte(ls); break;
00150 case OC_FILE_I16: res = (int16)ReadUint16(ls); break;
00151 case OC_FILE_U16: res = ReadUint16(ls); break;
00152 case OC_FILE_I32: res = (int32)ReadUint32(ls); break;
00153 case OC_FILE_U32: res = ReadUint32(ls); break;
00154 default: NOT_REACHED();
00155 }
00156
00157
00158 if (base_ptr == NULL && chunk->ptr == NULL) continue;
00159
00160
00161 if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
00162
00163
00164 switch (GetOldChunkVarType(chunk->type)) {
00165 case OC_VAR_I8: *(int8 *)ptr = GB(res, 0, 8); break;
00166 case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break;
00167 case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break;
00168 case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break;
00169 case OC_VAR_I32:*(int32 *)ptr = res; break;
00170 case OC_VAR_U32:*(uint32*)ptr = res; break;
00171 case OC_VAR_I64:*(int64 *)ptr = res; break;
00172 case OC_VAR_U64:*(uint64*)ptr = res; break;
00173 default: NOT_REACHED();
00174 }
00175
00176
00177 if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type);
00178 }
00179 }
00180 }
00181
00182 return true;
00183 }
00184
00190 static void InitLoading(LoadgameState *ls)
00191 {
00192 ls->chunk_size = 0;
00193 ls->total_read = 0;
00194
00195 ls->decoding = false;
00196 ls->decode_char = 0;
00197
00198 ls->buffer_cur = 0;
00199 ls->buffer_count = 0;
00200 memset(ls->buffer, 0, BUFFER_SIZE);
00201
00202 _bump_assert_value = 0;
00203
00204 _settings_game.construction.freeform_edges = false;
00205 }
00206
00214 static bool VerifyOldNameChecksum(char *title, uint len)
00215 {
00216 uint16 sum = 0;
00217 for (uint i = 0; i < len - 2; i++) {
00218 sum += title[i];
00219 sum = ROL(sum, 1);
00220 }
00221
00222 sum ^= 0xAAAA;
00223
00224 uint16 sum2 = title[len - 2];
00225 SB(sum2, 8, 8, title[len - 1]);
00226
00227 return sum == sum2;
00228 }
00229
00230 static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len)
00231 {
00232 assert(last - temp + 1 >= (int)len);
00233
00234 if (fread(temp, 1, len, f) != len) {
00235 temp[0] = '\0';
00236 return false;
00237 }
00238
00239 bool ret = VerifyOldNameChecksum(temp, len);
00240 temp[len - 2] = '\0';
00241 str_validate(temp, last);
00242
00243 return ret;
00244 }
00245
00246 static SavegameType DetermineOldSavegameType(FILE *f, char *title, const char *last)
00247 {
00248 assert_compile(TTD_HEADER_SIZE >= TTO_HEADER_SIZE);
00249 char temp[TTD_HEADER_SIZE];
00250
00251 SavegameType type = SGT_TTO;
00252
00253
00254 long pos = ftell(f);
00255 if (!CheckOldSavegameType(f, temp, lastof(temp), TTO_HEADER_SIZE)) {
00256 type = SGT_TTD;
00257 fseek(f, pos, SEEK_SET);
00258 if (!CheckOldSavegameType(f, temp, lastof(temp), TTD_HEADER_SIZE)) {
00259 type = SGT_INVALID;
00260 }
00261 }
00262
00263 if (title != NULL) {
00264 switch (type) {
00265 case SGT_TTO: title = strecpy(title, "(TTO) ", last); break;
00266 case SGT_TTD: title = strecpy(title, "(TTD) ", last); break;
00267 default: title = strecpy(title, "(broken) ", last); break;
00268 }
00269 title = strecpy(title, temp, last);
00270 }
00271
00272 return type;
00273 }
00274
00275 typedef bool LoadOldMainProc(LoadgameState *ls);
00276
00277 bool LoadOldSaveGame(const char *file)
00278 {
00279 LoadgameState ls;
00280
00281 DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame");
00282
00283 InitLoading(&ls);
00284
00285
00286 ls.file = FioFOpenFile(file, "rb");
00287
00288 if (ls.file == NULL) {
00289 DEBUG(oldloader, 0, "Cannot open file '%s'", file);
00290 return false;
00291 }
00292
00293 SavegameType type = DetermineOldSavegameType(ls.file, NULL, NULL);
00294
00295 LoadOldMainProc *proc = NULL;
00296
00297 switch (type) {
00298 case SGT_TTO: proc = &LoadTTOMain; break;
00299 case SGT_TTD: proc = &LoadTTDMain; break;
00300 default: break;
00301 }
00302
00303 _savegame_type = type;
00304
00305 bool game_loaded;
00306 try {
00307 game_loaded = proc != NULL && proc(&ls);
00308 } catch (...) {
00309 game_loaded = false;
00310 }
00311
00312 if (!game_loaded) {
00313 SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED);
00314 fclose(ls.file);
00315 return false;
00316 }
00317
00318 _pause_mode = 2;
00319
00320 return true;
00321 }
00322
00323 void GetOldSaveGameName(const char *file, char *title, const char *last)
00324 {
00325 FILE *f = FioFOpenFile(file, "rb");
00326
00327 if (f == NULL) {
00328 *title = '\0';
00329 return;
00330 }
00331
00332 DetermineOldSavegameType(f, title, last);
00333
00334 fclose(f);
00335 }