OpenTTD
oldloader.cpp
Go to the documentation of this file.
1 /* $Id: oldloader.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "../stdafx.h"
13 #include "../debug.h"
14 #include "../strings_type.h"
15 #include "../string_func.h"
16 #include "../settings_type.h"
17 #include "../fileio_func.h"
18 
19 #include "table/strings.h"
20 
21 #include "saveload_internal.h"
22 #include "oldloader.h"
23 
24 #include <exception>
25 
26 #include "../safeguards.h"
27 
28 static const int TTO_HEADER_SIZE = 41;
29 static const int TTD_HEADER_SIZE = 49;
30 
31 uint32 _bump_assert_value;
32 
33 static inline OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 4);}
34 static inline OldChunkType GetOldChunkVarType(OldChunkType type) {return (OldChunkType)(GB(type, 8, 8) << 8);}
35 static inline OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);}
36 
37 static inline byte CalcOldVarLen(OldChunkType type)
38 {
39  static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8};
40  byte length = GB(type, 8, 8);
41  assert(length != 0 && length < lengthof(type_mem_size));
42  return type_mem_size[length];
43 }
44 
51 {
52  /* To avoid slow reads, we read BUFFER_SIZE of bytes per time
53  and just return a byte per time */
54  if (ls->buffer_cur >= ls->buffer_count) {
55 
56  /* Read some new bytes from the file */
57  int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file);
58 
59  /* We tried to read, but there is nothing in the file anymore.. */
60  if (count == 0) {
61  DEBUG(oldloader, 0, "Read past end of file, loading failed");
62  throw std::exception();
63  }
64 
65  ls->buffer_count = count;
66  ls->buffer_cur = 0;
67  }
68 
69  return ls->buffer[ls->buffer_cur++];
70 }
71 
78 {
79  /* Old savegames have a nice compression algorithm (RLE)
80  which means that we have a chunk, which starts with a length
81  byte. If that byte is negative, we have to repeat the next byte
82  that many times ( + 1). Else, we need to read that amount of bytes.
83  Works pretty well if you have many zeros behind each other */
84 
85  if (ls->chunk_size == 0) {
86  /* Read new chunk */
87  int8 new_byte = ReadByteFromFile(ls);
88 
89  if (new_byte < 0) {
90  /* Repeat next char for new_byte times */
91  ls->decoding = true;
92  ls->decode_char = ReadByteFromFile(ls);
93  ls->chunk_size = -new_byte + 1;
94  } else {
95  ls->decoding = false;
96  ls->chunk_size = new_byte + 1;
97  }
98  }
99 
100  ls->total_read++;
101  ls->chunk_size--;
102 
103  return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
104 }
105 
111 bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
112 {
113  byte *base_ptr = (byte*)base;
114 
115  for (const OldChunks *chunk = chunks; chunk->type != OC_END; chunk++) {
116  if (((chunk->type & OC_TTD) && _savegame_type == SGT_TTO) ||
117  ((chunk->type & OC_TTO) && _savegame_type != SGT_TTO)) {
118  /* TTD(P)-only chunk, but TTO savegame || TTO-only chunk, but TTD/TTDP savegame */
119  continue;
120  }
121 
122  byte *ptr = (byte*)chunk->ptr;
123  if (chunk->type & OC_DEREFERENCE_POINTER) ptr = *(byte**)ptr;
124 
125  for (uint i = 0; i < chunk->amount; i++) {
126  /* Handle simple types */
127  if (GetOldChunkType(chunk->type) != 0) {
128  switch (GetOldChunkType(chunk->type)) {
129  /* Just read the byte and forget about it */
130  case OC_NULL: ReadByte(ls); break;
131 
132  case OC_CHUNK:
133  /* Call function, with 'i' as parameter to tell which item we
134  * are going to read */
135  if (!chunk->proc(ls, i)) return false;
136  break;
137 
138  case OC_ASSERT:
139  DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value);
140  if (ls->total_read != chunk->offset + _bump_assert_value) throw std::exception();
141  default: break;
142  }
143  } else {
144  uint64 res = 0;
145 
146  /* Reading from the file: bits 16 to 23 have the FILE type */
147  switch (GetOldChunkFileType(chunk->type)) {
148  case OC_FILE_I8: res = (int8)ReadByte(ls); break;
149  case OC_FILE_U8: res = ReadByte(ls); break;
150  case OC_FILE_I16: res = (int16)ReadUint16(ls); break;
151  case OC_FILE_U16: res = ReadUint16(ls); break;
152  case OC_FILE_I32: res = (int32)ReadUint32(ls); break;
153  case OC_FILE_U32: res = ReadUint32(ls); break;
154  default: NOT_REACHED();
155  }
156 
157  /* When both pointers are NULL, we are just skipping data */
158  if (base_ptr == NULL && chunk->ptr == NULL) continue;
159 
160  /* Writing to the var: bits 8 to 15 have the VAR type */
161  if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
162 
163  /* Write the data */
164  switch (GetOldChunkVarType(chunk->type)) {
165  case OC_VAR_I8: *(int8 *)ptr = GB(res, 0, 8); break;
166  case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break;
167  case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break;
168  case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break;
169  case OC_VAR_I32:*(int32 *)ptr = res; break;
170  case OC_VAR_U32:*(uint32*)ptr = res; break;
171  case OC_VAR_I64:*(int64 *)ptr = res; break;
172  case OC_VAR_U64:*(uint64*)ptr = res; break;
173  default: NOT_REACHED();
174  }
175 
176  /* Increase pointer base for arrays when looping */
177  if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type);
178  }
179  }
180  }
181 
182  return true;
183 }
184 
190 static void InitLoading(LoadgameState *ls)
191 {
192  ls->chunk_size = 0;
193  ls->total_read = 0;
194 
195  ls->decoding = false;
196  ls->decode_char = 0;
197 
198  ls->buffer_cur = 0;
199  ls->buffer_count = 0;
200  memset(ls->buffer, 0, BUFFER_SIZE);
201 
202  _bump_assert_value = 0;
203 
204  _settings_game.construction.freeform_edges = false; // disable so we can convert map array (SetTileType is still used)
205 }
206 
214 static bool VerifyOldNameChecksum(char *title, uint len)
215 {
216  uint16 sum = 0;
217  for (uint i = 0; i < len - 2; i++) {
218  sum += title[i];
219  sum = ROL(sum, 1);
220  }
221 
222  sum ^= 0xAAAA; // computed checksum
223 
224  uint16 sum2 = title[len - 2]; // checksum in file
225  SB(sum2, 8, 8, title[len - 1]);
226 
227  return sum == sum2;
228 }
229 
230 static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len)
231 {
232  assert(last - temp + 1 >= (int)len);
233 
234  if (fread(temp, 1, len, f) != len) {
235  temp[0] = '\0'; // if reading failed, make the name empty
236  return false;
237  }
238 
239  bool ret = VerifyOldNameChecksum(temp, len);
240  temp[len - 2] = '\0'; // name is null-terminated in savegame, but it's better to be sure
241  str_validate(temp, last);
242 
243  return ret;
244 }
245 
246 static SavegameType DetermineOldSavegameType(FILE *f, char *title, const char *last)
247 {
248  assert_compile(TTD_HEADER_SIZE >= TTO_HEADER_SIZE);
249  char temp[TTD_HEADER_SIZE] = "Unknown";
250 
251  SavegameType type = SGT_TTO;
252 
253  /* Can't fseek to 0 as in tar files that is not correct */
254  long pos = ftell(f);
255  if (pos >= 0 && !CheckOldSavegameType(f, temp, lastof(temp), TTO_HEADER_SIZE)) {
256  type = SGT_TTD;
257  if (fseek(f, pos, SEEK_SET) < 0 || !CheckOldSavegameType(f, temp, lastof(temp), TTD_HEADER_SIZE)) {
258  type = SGT_INVALID;
259  }
260  }
261 
262  if (title != NULL) {
263  switch (type) {
264  case SGT_TTO: title = strecpy(title, "(TTO) ", last); break;
265  case SGT_TTD: title = strecpy(title, "(TTD) ", last); break;
266  default: title = strecpy(title, "(broken) ", last); break;
267  }
268  title = strecpy(title, temp, last);
269  }
270 
271  return type;
272 }
273 
274 typedef bool LoadOldMainProc(LoadgameState *ls);
275 
276 bool LoadOldSaveGame(const char *file)
277 {
278  LoadgameState ls;
279 
280  DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame");
281 
282  InitLoading(&ls);
283 
284  /* Open file */
285  ls.file = FioFOpenFile(file, "rb", NO_DIRECTORY);
286 
287  if (ls.file == NULL) {
288  DEBUG(oldloader, 0, "Cannot open file '%s'", file);
289  return false;
290  }
291 
292  SavegameType type = DetermineOldSavegameType(ls.file, NULL, NULL);
293 
294  LoadOldMainProc *proc = NULL;
295 
296  switch (type) {
297  case SGT_TTO: proc = &LoadTTOMain; break;
298  case SGT_TTD: proc = &LoadTTDMain; break;
299  default: break;
300  }
301 
302  _savegame_type = type;
303 
304  bool game_loaded;
305  try {
306  game_loaded = proc != NULL && proc(&ls);
307  } catch (...) {
308  game_loaded = false;
309  }
310 
311  if (!game_loaded) {
312  SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED);
313  fclose(ls.file);
314  return false;
315  }
316 
317  _pause_mode = 2;
318 
319  return true;
320 }
321 
322 void GetOldSaveGameName(const char *file, char *title, const char *last)
323 {
324  FILE *f = FioFOpenFile(file, "rb", NO_DIRECTORY);
325 
326  if (f == NULL) {
327  *title = '\0';
328  return;
329  }
330 
331  DetermineOldSavegameType(f, title, last);
332 
333  fclose(f);
334 }
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition: settings.cpp:77
static T ROL(const T x, const uint8 n)
ROtate x Left by n.
-//- TTO (default is neither of these)
Definition: oldloader.h:45
OldChunkType
Definition: oldloader.h:37
chunk is valid ONLY for TTD savegames
Definition: oldloader.h:44
TTD savegame (can be detected incorrectly)
Definition: saveload.h:41
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
SavegameType
Types of save games.
Definition: saveload.h:40
static T SB(T &x, const uint8 s, const uint8 n, const U d)
Set n bits in x starting at bit s to d.
static bool VerifyOldNameChecksum(char *title, uint len)
Verifies the title has a valid checksum.
Definition: oldloader.cpp:214
OldChunkType type
Type of field.
Definition: oldloader.h:89
bool freeform_edges
allow terraforming the tiles at the map edges
static void InitLoading(LoadgameState *ls)
Initialize some data before reading.
Definition: oldloader.cpp:190
TTO savegame.
Definition: saveload.h:45
void str_validate(char *str, const char *last, StringValidationSettings settings)
Scans the string for valid characters and if it finds invalid ones, replaces them with a question mar...
Definition: string.cpp:184
byte ReadByte(LoadgameState *ls)
Reads a byte from the buffer and decompress if needed.
Definition: oldloader.cpp:77
FILE * FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition: fileio.cpp:474
A path without any base directory.
Definition: fileio_type.h:127
SavegameType _savegame_type
type of savegame we are loading
Definition: saveload.cpp:271
void SetSaveLoadError(StringID str)
Set the error message from outside of the actual loading/saving of the game (AfterLoadGame and friend...
Definition: saveload.cpp:2490
bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
Loads a chunk from the old savegame.
Definition: oldloader.cpp:111
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
PauseModeByte _pause_mode
The current pause mode.
Definition: gfx.cpp:48
Declarations of strctures and function used in loader of old savegames.
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:39
broken savegame (used internally)
Definition: saveload.h:46
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: depend.cpp:68
End of the whole chunk, all 32 bits set to zero.
Definition: oldloader.h:81
ConstructionSettings construction
construction of things in-game
Declaration of functions used in more save/load files.
static byte ReadByteFromFile(LoadgameState *ls)
Reads a byte from a file (do not call yourself, use ReadByte())
Definition: oldloader.cpp:50
Dereference the pointer once before writing to it, so we do not have to use big static arrays...
Definition: oldloader.h:79