OpenTTD: src/saveload/oldloader.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

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
50 static byte ReadByteFromFile(LoadgameState *ls)
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
77 byte ReadByte(LoadgameState *ls)
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 }