oldloader.cpp

Go to the documentation of this file.
00001 /* $Id: oldloader.cpp 20247 2010-07-30 22:57:46Z 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 
00025 #include <exception>
00026 
00027 static const int TTO_HEADER_SIZE = 41;
00028 static const int TTD_HEADER_SIZE = 49;
00029 
00030 uint32 _bump_assert_value;
00031 
00032 static inline OldChunkType GetOldChunkType(OldChunkType type)     {return (OldChunkType)GB(type, 0, 4);}
00033 static inline OldChunkType GetOldChunkVarType(OldChunkType type)  {return (OldChunkType)(GB(type, 8, 8) << 8);}
00034 static inline OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);}
00035 
00036 static inline byte CalcOldVarLen(OldChunkType type)
00037 {
00038   static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8};
00039   byte length = GB(type, 8, 8);
00040   assert(length != 0 && length < lengthof(type_mem_size));
00041   return type_mem_size[length];
00042 }
00043 
00049 static byte ReadByteFromFile(LoadgameState *ls)
00050 {
00051   /* To avoid slow reads, we read BUFFER_SIZE of bytes per time
00052   and just return a byte per time */
00053   if (ls->buffer_cur >= ls->buffer_count) {
00054 
00055     /* Read some new bytes from the file */
00056     int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file);
00057 
00058     /* We tried to read, but there is nothing in the file anymore.. */
00059     if (count == 0) {
00060       DEBUG(oldloader, 0, "Read past end of file, loading failed");
00061       throw std::exception();
00062     }
00063 
00064     ls->buffer_count = count;
00065     ls->buffer_cur   = 0;
00066   }
00067 
00068   return ls->buffer[ls->buffer_cur++];
00069 }
00070 
00076 byte ReadByte(LoadgameState *ls)
00077 {
00078   /* Old savegames have a nice compression algorithm (RLE)
00079   which means that we have a chunk, which starts with a length
00080   byte. If that byte is negative, we have to repeat the next byte
00081   that many times ( + 1). Else, we need to read that amount of bytes.
00082   Works pretty good if you have many zero's behind eachother */
00083 
00084   if (ls->chunk_size == 0) {
00085     /* Read new chunk */
00086     int8 new_byte = ReadByteFromFile(ls);
00087 
00088     if (new_byte < 0) {
00089       /* Repeat next char for new_byte times */
00090       ls->decoding    = true;
00091       ls->decode_char = ReadByteFromFile(ls);
00092       ls->chunk_size  = -new_byte + 1;
00093     } else {
00094       ls->decoding    = false;
00095       ls->chunk_size  = new_byte + 1;
00096     }
00097   }
00098 
00099   ls->total_read++;
00100   ls->chunk_size--;
00101 
00102   return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
00103 }
00104 
00110 bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
00111 {
00112   byte *base_ptr = (byte*)base;
00113 
00114   for (const OldChunks *chunk = chunks; chunk->type != OC_END; chunk++) {
00115     if (((chunk->type & OC_TTD) && _savegame_type == SGT_TTO) ||
00116         ((chunk->type & OC_TTO) && _savegame_type != SGT_TTO)) {
00117       /* TTD(P)-only chunk, but TTO savegame || TTO-only chunk, but TTD/TTDP savegame */
00118       continue;
00119     }
00120 
00121     byte *ptr = (byte*)chunk->ptr;
00122     if (chunk->type & OC_DEREFERENCE_POINTER) ptr = *(byte**)ptr;
00123 
00124     for (uint i = 0; i < chunk->amount; i++) {
00125       /* Handle simple types */
00126       if (GetOldChunkType(chunk->type) != 0) {
00127         switch (GetOldChunkType(chunk->type)) {
00128           /* Just read the byte and forget about it */
00129           case OC_NULL: ReadByte(ls); break;
00130 
00131           case OC_CHUNK:
00132             /* Call function, with 'i' as parameter to tell which item we
00133              * are going to read */
00134             if (!chunk->proc(ls, i)) return false;
00135             break;
00136 
00137           case OC_ASSERT:
00138             DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value);
00139             if (ls->total_read != chunk->offset + _bump_assert_value) throw std::exception();
00140           default: break;
00141         }
00142       } else {
00143         uint64 res = 0;
00144 
00145         /* Reading from the file: bits 16 to 23 have the FILE type */
00146         switch (GetOldChunkFileType(chunk->type)) {
00147           case OC_FILE_I8:  res = (int8)ReadByte(ls); break;
00148           case OC_FILE_U8:  res = ReadByte(ls); break;
00149           case OC_FILE_I16: res = (int16)ReadUint16(ls); break;
00150           case OC_FILE_U16: res = ReadUint16(ls); break;
00151           case OC_FILE_I32: res = (int32)ReadUint32(ls); break;
00152           case OC_FILE_U32: res = ReadUint32(ls); break;
00153           default: NOT_REACHED();
00154         }
00155 
00156         /* When both pointers are NULL, we are just skipping data */
00157         if (base_ptr == NULL && chunk->ptr == NULL) continue;
00158 
00159         /* Writing to the var: bits 8 to 15 have the VAR type */
00160         if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
00161 
00162         /* Write the data */
00163         switch (GetOldChunkVarType(chunk->type)) {
00164           case OC_VAR_I8: *(int8  *)ptr = GB(res, 0, 8); break;
00165           case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break;
00166           case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break;
00167           case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break;
00168           case OC_VAR_I32:*(int32 *)ptr = res; break;
00169           case OC_VAR_U32:*(uint32*)ptr = res; break;
00170           case OC_VAR_I64:*(int64 *)ptr = res; break;
00171           case OC_VAR_U64:*(uint64*)ptr = res; break;
00172           default: NOT_REACHED();
00173         }
00174 
00175         /* Increase pointer base for arrays when looping */
00176         if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type);
00177       }
00178     }
00179   }
00180 
00181   return true;
00182 }
00183 
00189 static void InitLoading(LoadgameState *ls)
00190 {
00191   ls->chunk_size   = 0;
00192   ls->total_read   = 0;
00193 
00194   ls->decoding     = false;
00195   ls->decode_char  = 0;
00196 
00197   ls->buffer_cur   = 0;
00198   ls->buffer_count = 0;
00199   memset(ls->buffer, 0, BUFFER_SIZE);
00200 
00201   _bump_assert_value = 0;
00202 
00203   _settings_game.construction.freeform_edges = false; // disable so we can convert map array (SetTileType is still used)
00204 }
00205 
00213 static bool VerifyOldNameChecksum(char *title, uint len)
00214 {
00215   uint16 sum = 0;
00216   for (uint i = 0; i < len - 2; i++) {
00217     sum += title[i];
00218     sum = ROL(sum, 1);
00219   }
00220 
00221   sum ^= 0xAAAA; // computed checksum
00222 
00223   uint16 sum2 = title[len - 2]; // checksum in file
00224   SB(sum2, 8, 8, title[len - 1]);
00225 
00226   return sum == sum2;
00227 }
00228 
00229 static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len)
00230 {
00231   assert(last - temp + 1 >= (int)len);
00232 
00233   if (fread(temp, 1, len, f) != len) {
00234     temp[0] = '\0'; // if reading failed, make the name empty
00235     return false;
00236   }
00237 
00238   bool ret = VerifyOldNameChecksum(temp, len);
00239   temp[len - 2] = '\0'; // name is nul-terminated in savegame, but it's better to be sure
00240   str_validate(temp, last);
00241 
00242   return ret;
00243 }
00244 
00245 static SavegameType DetermineOldSavegameType(FILE *f, char *title, const char *last)
00246 {
00247   assert_compile(TTD_HEADER_SIZE >= TTO_HEADER_SIZE);
00248   char temp[TTD_HEADER_SIZE];
00249 
00250   SavegameType type = SGT_TTO;
00251 
00252   /* Can't fseek to 0 as in tar files that is not correct */
00253   long pos = ftell(f);
00254   if (!CheckOldSavegameType(f, temp, lastof(temp), TTO_HEADER_SIZE)) {
00255     type = SGT_TTD;
00256     fseek(f, pos, SEEK_SET);
00257     if (!CheckOldSavegameType(f, temp, lastof(temp), TTD_HEADER_SIZE)) {
00258       type = SGT_INVALID;
00259     }
00260   }
00261 
00262   if (title != NULL) {
00263     switch (type) {
00264       case SGT_TTO: title = strecpy(title, "(TTO) ", last);    break;
00265       case SGT_TTD: title = strecpy(title, "(TTD) ", last);    break;
00266       default:      title = strecpy(title, "(broken) ", last); break;
00267     }
00268     title = strecpy(title, temp, last);
00269   }
00270 
00271   return type;
00272 }
00273 
00274 typedef bool LoadOldMainProc(LoadgameState *ls);
00275 
00276 bool LoadOldSaveGame(const char *file)
00277 {
00278   LoadgameState ls;
00279 
00280   DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame");
00281 
00282   InitLoading(&ls);
00283 
00284   /* Open file */
00285   ls.file = FioFOpenFile(file, "rb");
00286 
00287   if (ls.file == NULL) {
00288     DEBUG(oldloader, 0, "Cannot open file '%s'", file);
00289     return false;
00290   }
00291 
00292   SavegameType type = DetermineOldSavegameType(ls.file, NULL, NULL);
00293 
00294   LoadOldMainProc *proc = NULL;
00295 
00296   switch (type) {
00297     case SGT_TTO: proc = &LoadTTOMain; break;
00298     case SGT_TTD: proc = &LoadTTDMain; break;
00299     default: break;
00300   }
00301 
00302   _savegame_type = type;
00303 
00304   bool game_loaded;
00305   try {
00306     game_loaded = proc != NULL && proc(&ls);
00307   } catch (...) {
00308     game_loaded = false;
00309   }
00310 
00311   if (!game_loaded) {
00312     SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED);
00313     fclose(ls.file);
00314     return false;
00315   }
00316 
00317   _pause_mode = 2;
00318 
00319   return true;
00320 }
00321 
00322 void GetOldSaveGameName(const char *file, char *title, const char *last)
00323 {
00324   FILE *f = FioFOpenFile(file, "rb");
00325 
00326   if (f == NULL) {
00327     *title = '\0';
00328     return;
00329   }
00330 
00331   DetermineOldSavegameType(f, title, last);
00332 
00333   fclose(f);
00334 }

Generated on Fri Dec 31 17:15:37 2010 for OpenTTD by  doxygen 1.6.1