oldloader.cpp

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

Generated on Wed Dec 23 20:12:51 2009 for OpenTTD by  doxygen 1.5.6