# oldloader.cpp

Go to the documentation of this file.

    00001 /* $Id: oldloader.cpp 20430 2010-08-09 21:48:19Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    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   /* To avoid slow reads, we read BUFFER_SIZE of bytes per time
    00053   and just return a byte per time */
    00054   if (ls->buffer_cur >= ls->buffer_count) {
    00055
    00056     /* Read some new bytes from the file */
    00057     int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file);
    00058
    00059     /* We tried to read, but there is nothing in the file anymore.. */
    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   /* Old savegames have a nice compression algorithm (RLE)
    00080   which means that we have a chunk, which starts with a length
    00081   byte. If that byte is negative, we have to repeat the next byte
    00082   that many times ( + 1). Else, we need to read that amount of bytes.
    00083   Works pretty good if you have many zero's behind eachother */
    00084
    00085   if (ls->chunk_size == 0) {
    00086     /* Read new chunk */
    00087     int8 new_byte = ReadByteFromFile(ls);
    00088
    00089     if (new_byte < 0) {
    00090       /* Repeat next char for new_byte times */
    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       /* TTD(P)-only chunk, but TTO savegame || TTO-only chunk, but TTD/TTDP savegame */
    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       /* Handle simple types */
    00127       if (GetOldChunkType(chunk->type) != 0) {
    00128         switch (GetOldChunkType(chunk->type)) {
    00129           /* Just read the byte and forget about it */
    00130           case OC_NULL: ReadByte(ls); break;
    00131
    00132           case OC_CHUNK:
    00133             /* Call function, with 'i' as parameter to tell which item we
    00134              * are going to read */
    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         /* Reading from the file: bits 16 to 23 have the FILE type */
    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         /* When both pointers are NULL, we are just skipping data */
    00158         if (base_ptr == NULL && chunk->ptr == NULL) continue;
    00159
    00160         /* Writing to the var: bits 8 to 15 have the VAR type */
    00161         if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
    00162
    00163         /* Write the data */
    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         /* Increase pointer base for arrays when looping */
    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; // disable so we can convert map array (SetTileType is still used)
    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; // computed checksum
    00223
    00224   uint16 sum2 = title[len - 2]; // checksum in file
    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'; // if reading failed, make the name empty
    00236     return false;
    00237   }
    00238
    00239   bool ret = VerifyOldNameChecksum(temp, len);
    00240   temp[len - 2] = '\0'; // name is nul-terminated in savegame, but it's better to be sure
    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   /* Can't fseek to 0 as in tar files that is not correct */
    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   /* Open file */
    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 }