# oldloader.cpp

Go to the documentation of this file.

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