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/>.
10 /** @file oldloader.cpp Functions for handling of TTO/TTD/TTDP savegames. */
12 #include "../stdafx.h"
14 #include "../strings_type.h"
15 #include "../string_func.h"
16 #include "../settings_type.h"
17 #include "../fileio_func.h"
19 #include "table/strings.h"
21 #include "saveload_internal.h"
22 #include "oldloader.h"
26 #include "../safeguards.h"
28 static const int TTO_HEADER_SIZE
= 41;
29 static const int TTD_HEADER_SIZE
= 49;
31 uint32 _bump_assert_value
;
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);}
37 static inline byte
CalcOldVarLen(OldChunkType type
)
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
];
47 * Reads a byte from a file (do not call yourself, use ReadByte())
50 static byte
ReadByteFromFile(LoadgameState
*ls
)
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
) {
56 /* Read some new bytes from the file */
57 int count
= (int)fread(ls
->buffer
, 1, BUFFER_SIZE
, ls
->file
);
59 /* We tried to read, but there is nothing in the file anymore.. */
61 DEBUG(oldloader
, 0, "Read past end of file, loading failed");
62 throw std::exception();
65 ls
->buffer_count
= count
;
69 return ls
->buffer
[ls
->buffer_cur
++];
74 * Reads a byte from the buffer and decompress if needed
77 byte
ReadByte(LoadgameState
*ls
)
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 */
85 if (ls
->chunk_size
== 0) {
87 int8 new_byte
= ReadByteFromFile(ls
);
90 /* Repeat next char for new_byte times */
92 ls
->decode_char
= ReadByteFromFile(ls
);
93 ls
->chunk_size
= -new_byte
+ 1;
96 ls
->chunk_size
= new_byte
+ 1;
103 return ls
->decoding
? ls
->decode_char
: ReadByteFromFile(ls
);
108 * Loads a chunk from the old savegame
111 bool LoadChunk(LoadgameState
*ls
, void *base
, const OldChunks
*chunks
)
113 byte
*base_ptr
= (byte
*)base
;
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 */
122 byte
*ptr
= (byte
*)chunk
->ptr
;
123 if (chunk
->type
& OC_DEREFERENCE_POINTER
) ptr
= *(byte
**)ptr
;
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;
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;
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();
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();
157 /* When both pointers are nullptr, we are just skipping data */
158 if (base_ptr
== nullptr && chunk
->ptr
== nullptr) continue;
160 /* Writing to the var: bits 8 to 15 have the VAR type */
161 if (chunk
->ptr
== nullptr) ptr
= base_ptr
+ chunk
->offset
;
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();
176 /* Increase pointer base for arrays when looping */
177 if (chunk
->amount
> 1 && chunk
->ptr
!= nullptr) ptr
+= CalcOldVarLen(chunk
->type
);
187 * Initialize some data before reading
190 static void InitLoading(LoadgameState
*ls
)
195 ls
->decoding
= false;
199 ls
->buffer_count
= 0;
200 memset(ls
->buffer
, 0, BUFFER_SIZE
);
202 _bump_assert_value
= 0;
204 _settings_game
.construction
.freeform_edges
= false; // disable so we can convert map array (SetTileType is still used)
208 * Verifies the title has a valid checksum
209 * @param title title and checksum
210 * @param len the length of the title to read/checksum
211 * @return true iff the title is valid
212 * @note the title (incl. checksum) has to be at least 41/49 (HEADER_SIZE) bytes long!
214 static bool VerifyOldNameChecksum(char *title
, uint len
)
217 for (uint i
= 0; i
< len
- 2; i
++) {
222 sum
^= 0xAAAA; // computed checksum
224 uint16 sum2
= title
[len
- 2]; // checksum in file
225 SB(sum2
, 8, 8, title
[len
- 1]);
230 static inline bool CheckOldSavegameType(FILE *f
, char *temp
, const char *last
, uint len
)
232 assert(last
- temp
+ 1 >= (int)len
);
234 if (fread(temp
, 1, len
, f
) != len
) {
235 temp
[0] = '\0'; // if reading failed, make the name empty
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
);
246 static SavegameType
DetermineOldSavegameType(FILE *f
, char *title
, const char *last
)
248 assert_compile(TTD_HEADER_SIZE
>= TTO_HEADER_SIZE
);
249 char temp
[TTD_HEADER_SIZE
] = "Unknown";
251 SavegameType type
= SGT_TTO
;
253 /* Can't fseek to 0 as in tar files that is not correct */
255 if (pos
>= 0 && !CheckOldSavegameType(f
, temp
, lastof(temp
), TTO_HEADER_SIZE
)) {
257 if (fseek(f
, pos
, SEEK_SET
) < 0 || !CheckOldSavegameType(f
, temp
, lastof(temp
), TTD_HEADER_SIZE
)) {
262 if (title
!= nullptr) {
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;
268 strecpy(title
, temp
, last
);
274 typedef bool LoadOldMainProc(LoadgameState
*ls
);
276 bool LoadOldSaveGame(const char *file
)
280 DEBUG(oldloader
, 3, "Trying to load a TTD(Patch) savegame");
285 ls
.file
= FioFOpenFile(file
, "rb", NO_DIRECTORY
);
287 if (ls
.file
== nullptr) {
288 DEBUG(oldloader
, 0, "Cannot open file '%s'", file
);
292 SavegameType type
= DetermineOldSavegameType(ls
.file
, nullptr, nullptr);
294 LoadOldMainProc
*proc
= nullptr;
297 case SGT_TTO
: proc
= &LoadTTOMain
; break;
298 case SGT_TTD
: proc
= &LoadTTDMain
; break;
302 _savegame_type
= type
;
306 game_loaded
= proc
!= nullptr && proc(&ls
);
312 SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED
);
322 void GetOldSaveGameName(const char *file
, char *title
, const char *last
)
324 FILE *f
= FioFOpenFile(file
, "rb", NO_DIRECTORY
);
331 DetermineOldSavegameType(f
, title
, last
);