Fix: Error message not set when unable load an old save. (#13106)
[openttd-github.git] / src / saveload / oldloader.cpp
blob973f3ee9f5a628c1cec92b3f0228f027c3c58356
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 */
8 /** @file oldloader.cpp Functions for handling of TTO/TTD/TTDP savegames. */
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"
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);}
36 /**
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();
56 /**
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.. */
71 if (count == 0) {
72 Debug(oldloader, 0, "Read past end of file, loading failed");
73 throw std::exception();
76 ls->buffer_count = count;
77 ls->buffer_cur = 0;
80 return ls->buffer[ls->buffer_cur++];
83 /**
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) {
97 /* Read new chunk */
98 int8_t new_byte = ReadByteFromFile(ls);
100 if (new_byte < 0) {
101 /* Repeat next char for new_byte times */
102 ls->decoding = true;
103 ls->decode_char = ReadByteFromFile(ls);
104 ls->chunk_size = -new_byte + 1;
105 } else {
106 ls->decoding = false;
107 ls->chunk_size = new_byte + 1;
111 ls->total_read++;
112 ls->chunk_size--;
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 */
128 continue;
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;
141 case OC_CHUNK:
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;
145 break;
147 case OC_ASSERT:
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();
150 default: break;
152 } else {
153 uint64_t res = 0;
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);
172 /* Write the data */
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);
191 return true;
196 * Initialize some data before reading
199 static void InitLoading(LoadgameState *ls)
201 ls->chunk_size = 0;
202 ls->total_read = 0;
204 ls->decoding = false;
205 ls->decode_char = 0;
207 ls->buffer_cur = 0;
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)
224 uint16_t sum = 0;
225 for (uint i = 0; i < len - HEADER_CHECKSUM_SIZE; i++) {
226 sum += title[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]);
235 return sum == sum2;
238 static std::tuple<SavegameType, std::string> DetermineOldSavegameTypeAndName(FileHandle &f)
240 long pos = ftell(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)
261 LoadgameState ls;
263 Debug(oldloader, 3, "Trying to load a TTD(Patch) savegame");
265 InitLoading(&ls);
267 /* Open file */
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);
273 return false;
276 SavegameType type;
277 std::tie(type, std::ignore) = DetermineOldSavegameTypeAndName(*ls.file);
279 LoadOldMainProc *proc = nullptr;
281 switch (type) {
282 case SGT_TTO: proc = &LoadTTOMain; break;
283 case SGT_TTD: proc = &LoadTTDMain; break;
284 default:
285 Debug(oldloader, 0, "Unknown savegame type; cannot be loaded");
286 break;
289 _savegame_type = type;
291 bool game_loaded;
292 try {
293 game_loaded = proc != nullptr && proc(&ls);
294 } catch (...) {
295 game_loaded = false;
298 if (!game_loaded) {
299 SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED);
300 ls.file.reset();
301 return false;
304 _pause_mode = PM_PAUSED_SAVELOAD;
306 return true;
309 std::string GetOldSaveGameName(const std::string &file)
311 auto f = FioFOpenFile(file, "rb", NO_DIRECTORY);
312 if (!f.has_value()) return {};
314 std::string name;
315 std::tie(std::ignore, name) = DetermineOldSavegameTypeAndName(*f);
316 return name;