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/>.
8 /** @file oldloader.cpp Functions for handling of TTO/TTD/TTDP savegames. */
10 #include "../stdafx.h"
12 #include "../strings_type.h"
13 #include "../string_func.h"
14 #include "../settings_type.h"
15 #include "../fileio_func.h"
17 #include "table/strings.h"
19 #include "saveload_internal.h"
20 #include "oldloader.h"
23 #include "../safeguards.h"
25 static const int TTO_HEADER_SIZE
= 41;
26 static const int TTD_HEADER_SIZE
= 49;
27 /** The size of the checksum in the name/header of the TTD/TTO savegames. */
28 static const int HEADER_CHECKSUM_SIZE
= 2;
30 uint32_t _bump_assert_value
;
32 static inline OldChunkType
GetOldChunkType(OldChunkType type
) {return (OldChunkType
)GB(type
, 0, 4);}
33 static inline OldChunkType
GetOldChunkVarType(OldChunkType type
) {return (OldChunkType
)(GB(type
, 8, 8) << 8);}
34 static inline OldChunkType
GetOldChunkFileType(OldChunkType type
) {return (OldChunkType
)(GB(type
, 16, 8) << 16);}
37 * Return expected size in bytes of a OldChunkType
38 * @param type OldChunkType to get size of.
39 * @return size of type in bytes.
41 static inline uint8_t CalcOldVarLen(OldChunkType type
)
43 switch (GetOldChunkVarType(type
)) {
44 case OC_VAR_I8
: return sizeof(int8_t);
45 case OC_VAR_U8
: return sizeof(uint8_t);
46 case OC_VAR_I16
: return sizeof(int16_t);
47 case OC_VAR_U16
: return sizeof(uint16_t);
48 case OC_VAR_I32
: return sizeof(int32_t);
49 case OC_VAR_U32
: return sizeof(uint32_t);
50 case OC_VAR_I64
: return sizeof(int64_t);
51 case OC_VAR_U64
: return sizeof(uint64_t);
52 default: NOT_REACHED();
58 * Reads a byte from a file (do not call yourself, use ReadByte())
61 static uint8_t ReadByteFromFile(LoadgameState
*ls
)
63 /* To avoid slow reads, we read BUFFER_SIZE of bytes per time
64 and just return a byte per time */
65 if (ls
->buffer_cur
>= ls
->buffer_count
) {
67 /* Read some new bytes from the file */
68 int count
= static_cast<int>(fread(ls
->buffer
, 1, BUFFER_SIZE
, *ls
->file
));
70 /* We tried to read, but there is nothing in the file anymore.. */
72 Debug(oldloader
, 0, "Read past end of file, loading failed");
73 throw std::exception();
76 ls
->buffer_count
= count
;
80 return ls
->buffer
[ls
->buffer_cur
++];
85 * Reads a byte from the buffer and decompress if needed
88 uint8_t ReadByte(LoadgameState
*ls
)
90 /* Old savegames have a nice compression algorithm (RLE)
91 which means that we have a chunk, which starts with a length
92 byte. If that byte is negative, we have to repeat the next byte
93 that many times ( + 1). Else, we need to read that amount of bytes.
94 Works pretty well if you have many zeros behind each other */
96 if (ls
->chunk_size
== 0) {
98 int8_t new_byte
= ReadByteFromFile(ls
);
101 /* Repeat next char for new_byte times */
103 ls
->decode_char
= ReadByteFromFile(ls
);
104 ls
->chunk_size
= -new_byte
+ 1;
106 ls
->decoding
= false;
107 ls
->chunk_size
= new_byte
+ 1;
114 return ls
->decoding
? ls
->decode_char
: ReadByteFromFile(ls
);
119 * Loads a chunk from the old savegame
122 bool LoadChunk(LoadgameState
*ls
, void *base
, const OldChunks
*chunks
)
124 for (const OldChunks
*chunk
= chunks
; chunk
->type
!= OC_END
; chunk
++) {
125 if (((chunk
->type
& OC_TTD
) && _savegame_type
== SGT_TTO
) ||
126 ((chunk
->type
& OC_TTO
) && _savegame_type
!= SGT_TTO
)) {
127 /* TTD(P)-only chunk, but TTO savegame || TTO-only chunk, but TTD/TTDP savegame */
131 uint8_t *ptr
= (uint8_t*)chunk
->ptr
;
132 if (chunk
->type
& OC_DEREFERENCE_POINTER
) ptr
= *(uint8_t**)ptr
;
134 for (uint i
= 0; i
< chunk
->amount
; i
++) {
135 /* Handle simple types */
136 if (GetOldChunkType(chunk
->type
) != 0) {
137 switch (GetOldChunkType(chunk
->type
)) {
138 /* Just read the byte and forget about it */
139 case OC_NULL
: ReadByte(ls
); break;
142 /* Call function, with 'i' as parameter to tell which item we
143 * are going to read */
144 if (!chunk
->proc(ls
, i
)) return false;
148 Debug(oldloader
, 4, "Assert point: 0x{:X} / 0x{:X}", ls
->total_read
, (uint
)(size_t)chunk
->ptr
+ _bump_assert_value
);
149 if (ls
->total_read
!= (size_t)chunk
->ptr
+ _bump_assert_value
) throw std::exception();
155 /* Reading from the file: bits 16 to 23 have the FILE type */
156 switch (GetOldChunkFileType(chunk
->type
)) {
157 case OC_FILE_I8
: res
= (int8_t)ReadByte(ls
); break;
158 case OC_FILE_U8
: res
= ReadByte(ls
); break;
159 case OC_FILE_I16
: res
= (int16_t)ReadUint16(ls
); break;
160 case OC_FILE_U16
: res
= ReadUint16(ls
); break;
161 case OC_FILE_I32
: res
= (int32_t)ReadUint32(ls
); break;
162 case OC_FILE_U32
: res
= ReadUint32(ls
); break;
163 default: NOT_REACHED();
166 /* When both pointers are nullptr, we are just skipping data */
167 if (base
== nullptr && chunk
->ptr
== nullptr) continue;
169 /* Chunk refers to a struct member, get address in base. */
170 if (chunk
->ptr
== nullptr) ptr
= (uint8_t *)chunk
->offset(base
);
173 switch (GetOldChunkVarType(chunk
->type
)) {
174 case OC_VAR_I8
: *(int8_t *)ptr
= GB(res
, 0, 8); break;
175 case OC_VAR_U8
: *(uint8_t *)ptr
= GB(res
, 0, 8); break;
176 case OC_VAR_I16
:*(int16_t *)ptr
= GB(res
, 0, 16); break;
177 case OC_VAR_U16
:*(uint16_t*)ptr
= GB(res
, 0, 16); break;
178 case OC_VAR_I32
:*(int32_t *)ptr
= res
; break;
179 case OC_VAR_U32
:*(uint32_t*)ptr
= res
; break;
180 case OC_VAR_I64
:*(int64_t *)ptr
= res
; break;
181 case OC_VAR_U64
:*(uint64_t*)ptr
= res
; break;
182 default: NOT_REACHED();
185 /* Increase pointer base for arrays when looping */
186 if (chunk
->amount
> 1 && chunk
->ptr
!= nullptr) ptr
+= CalcOldVarLen(chunk
->type
);
196 * Initialize some data before reading
199 static void InitLoading(LoadgameState
*ls
)
204 ls
->decoding
= false;
208 ls
->buffer_count
= 0;
209 memset(ls
->buffer
, 0, BUFFER_SIZE
);
211 _bump_assert_value
= 0;
213 _settings_game
.construction
.freeform_edges
= false; // disable so we can convert map array (SetTileType is still used)
217 * Verifies the title has a valid checksum
218 * @param title title and checksum
219 * @param len the length of the title to read/checksum
220 * @return true iff the title is valid
222 static bool VerifyOldNameChecksum(char *title
, uint len
)
225 for (uint i
= 0; i
< len
- HEADER_CHECKSUM_SIZE
; i
++) {
227 sum
= std::rotl(sum
, 1);
230 sum
^= 0xAAAA; // computed checksum
232 uint16_t sum2
= title
[len
- HEADER_CHECKSUM_SIZE
]; // checksum in file
233 SB(sum2
, 8, 8, title
[len
- HEADER_CHECKSUM_SIZE
+ 1]);
238 static std::tuple
<SavegameType
, std::string
> DetermineOldSavegameTypeAndName(FileHandle
&f
)
241 char buffer
[std::max(TTO_HEADER_SIZE
, TTD_HEADER_SIZE
)];
242 if (pos
< 0 || fread(buffer
, 1, lengthof(buffer
), f
) != lengthof(buffer
)) {
243 return { SGT_INVALID
, "(broken) Unable to read file" };
246 if (VerifyOldNameChecksum(buffer
, TTO_HEADER_SIZE
) && fseek(f
, pos
+ TTO_HEADER_SIZE
, SEEK_SET
) == 0) {
247 return { SGT_TTO
, "(TTO)" + StrMakeValid({buffer
, TTO_HEADER_SIZE
- HEADER_CHECKSUM_SIZE
}) };
250 if (VerifyOldNameChecksum(buffer
, TTD_HEADER_SIZE
) && fseek(f
, pos
+ TTD_HEADER_SIZE
, SEEK_SET
) == 0) {
251 return { SGT_TTD
, "(TTD)" + StrMakeValid({buffer
, TTD_HEADER_SIZE
- HEADER_CHECKSUM_SIZE
}) };
254 return { SGT_INVALID
, "(broken) Unknown" };
257 typedef bool LoadOldMainProc(LoadgameState
*ls
);
259 bool LoadOldSaveGame(const std::string
&file
)
263 Debug(oldloader
, 3, "Trying to load a TTD(Patch) savegame");
268 ls
.file
= FioFOpenFile(file
, "rb", NO_DIRECTORY
);
270 if (!ls
.file
.has_value()) {
271 Debug(oldloader
, 0, "Cannot open file '{}'", file
);
272 SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE
);
277 std::tie(type
, std::ignore
) = DetermineOldSavegameTypeAndName(*ls
.file
);
279 LoadOldMainProc
*proc
= nullptr;
282 case SGT_TTO
: proc
= &LoadTTOMain
; break;
283 case SGT_TTD
: proc
= &LoadTTDMain
; break;
285 Debug(oldloader
, 0, "Unknown savegame type; cannot be loaded");
289 _savegame_type
= type
;
293 game_loaded
= proc
!= nullptr && proc(&ls
);
299 SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED
);
304 _pause_mode
= PM_PAUSED_SAVELOAD
;
309 std::string
GetOldSaveGameName(const std::string
&file
)
311 auto f
= FioFOpenFile(file
, "rb", NO_DIRECTORY
);
312 if (!f
.has_value()) return {};
315 std::tie(std::ignore
, name
) = DetermineOldSavegameTypeAndName(*f
);