OpenTTD Source: src/saveload/oldloader.cpp Source File

OpenTTD Source 1.11.0-RC1

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