1
/* $Id: oldloader.cpp 15903 2009-03-30 23:15:05Z rubidium $ */
3
/** @file oldloader.cpp Functions for handling of TTO/TTD/TTDP savegames. */
6
#include "../openttd.h"
7
#include "../tile_type.h"
9
#include "../strings_type.h"
10
#include "../string_func.h"
11
#include "../settings_type.h"
12
#include "../fileio_func.h"
14
#include "table/strings.h"
16
#include "saveload_internal.h"
17
#include "oldloader.h"
24
uint32 _bump_assert_value;
26
static inline OldChunkType GetOldChunkType(OldChunkType type) {return (OldChunkType)GB(type, 0, 4);}
27
static inline OldChunkType GetOldChunkVarType(OldChunkType type) {return (OldChunkType)(GB(type, 8, 8) << 8);}
28
static inline OldChunkType GetOldChunkFileType(OldChunkType type) {return (OldChunkType)(GB(type, 16, 8) << 16);}
30
static inline byte CalcOldVarLen(OldChunkType type)
32
static const byte type_mem_size[] = {0, 1, 1, 2, 2, 4, 4, 8};
33
byte length = GB(type, 8, 8);
34
assert(length != 0 && length < lengthof(type_mem_size));
35
return type_mem_size[length];
40
* Reads a byte from a file (do not call yourself, use ReadByte())
43
static byte ReadByteFromFile(LoadgameState *ls)
45
/* To avoid slow reads, we read BUFFER_SIZE of bytes per time
46
and just return a byte per time */
47
if (ls->buffer_cur >= ls->buffer_count) {
48
/* Read some new bytes from the file */
49
int count = (int)fread(ls->buffer, 1, BUFFER_SIZE, ls->file);
51
/* We tried to read, but there is nothing in the file anymore.. */
53
DEBUG(oldloader, 0, "Read past end of file, loading failed");
57
ls->buffer_count = count;
61
return ls->buffer[ls->buffer_cur++];
66
* Reads a byte from the buffer and decompress if needed
69
byte ReadByte(LoadgameState *ls)
71
/* Old savegames have a nice compression algorithm (RLE)
72
which means that we have a chunk, which starts with a length
73
byte. If that byte is negative, we have to repeat the next byte
74
that many times ( + 1). Else, we need to read that amount of bytes.
75
Works pretty good if you have many zero's behind eachother */
77
if (ls->chunk_size == 0) {
79
int8 new_byte = ReadByteFromFile(ls);
82
/* Repeat next char for new_byte times */
84
ls->decode_char = ReadByteFromFile(ls);
85
ls->chunk_size = -new_byte + 1;
88
ls->chunk_size = new_byte + 1;
95
return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
100
* Loads a chunk from the old savegame
103
bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
105
byte *base_ptr = (byte*)base;
107
for (const OldChunks *chunk = chunks; chunk->type != OC_END; chunk++) {
108
if (((chunk->type & OC_TTD) && (_savegame_type == SGT_TTO)) ||
109
((chunk->type & OC_TTO) && (_savegame_type != SGT_TTO))) {
110
/* TTD(P)-only chunk, but TTO savegame || TTO-only chunk, but TTD/TTDP savegame */
114
byte *ptr = (byte*)chunk->ptr;
115
if (chunk->type & OC_DEREFERENCE_POINTER) ptr = *(byte**)ptr;
117
for (uint i = 0; i < chunk->amount; i++) {
118
if (ls->failed) return false;
120
/* Handle simple types */
121
if (GetOldChunkType(chunk->type) != 0) {
122
switch (GetOldChunkType(chunk->type)) {
123
/* Just read the byte and forget about it */
124
case OC_NULL: ReadByte(ls); break;
127
/* Call function, with 'i' as parameter to tell which item we
128
* are going to read */
129
if (!chunk->proc(ls, i)) return false;
133
DEBUG(oldloader, 4, "Assert point: 0x%X / 0x%X", ls->total_read, chunk->offset + _bump_assert_value);
134
if (ls->total_read != chunk->offset + _bump_assert_value) ls->failed = true;
140
/* Reading from the file: bits 16 to 23 have the FILE type */
141
switch (GetOldChunkFileType(chunk->type)) {
142
case OC_FILE_I8: res = (int8)ReadByte(ls); break;
143
case OC_FILE_U8: res = ReadByte(ls); break;
144
case OC_FILE_I16: res = (int16)ReadUint16(ls); break;
145
case OC_FILE_U16: res = ReadUint16(ls); break;
146
case OC_FILE_I32: res = (int32)ReadUint32(ls); break;
147
case OC_FILE_U32: res = ReadUint32(ls); break;
148
default: NOT_REACHED();
152
assert(base_ptr != NULL || chunk->ptr != NULL);
154
/* Writing to the var: bits 8 to 15 have the VAR type */
155
if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
158
switch (GetOldChunkVarType(chunk->type)) {
159
case OC_VAR_I8: *(int8 *)ptr = GB(res, 0, 8); break;
160
case OC_VAR_U8: *(uint8 *)ptr = GB(res, 0, 8); break;
161
case OC_VAR_I16:*(int16 *)ptr = GB(res, 0, 16); break;
162
case OC_VAR_U16:*(uint16*)ptr = GB(res, 0, 16); break;
163
case OC_VAR_I32:*(int32 *)ptr = res; break;
164
case OC_VAR_U32:*(uint32*)ptr = res; break;
165
case OC_VAR_I64:*(int64 *)ptr = res; break;
166
case OC_VAR_U64:*(uint64*)ptr = res; break;
167
default: NOT_REACHED();
170
/* Increase pointer base for arrays when looping */
171
if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type);
181
* Initialize some data before reading
184
static void InitLoading(LoadgameState *ls)
190
ls->decoding = false;
194
ls->buffer_count = 0;
195
memset(ls->buffer, 0, BUFFER_SIZE);
197
_bump_assert_value = 0;
199
_settings_game.construction.freeform_edges = false; // disable so we can convert map array (SetTileType is still used)
203
* Verifies the title has a valid checksum
204
* @param title title and checksum
205
* @return true iff the title is valid
206
* @note the title (incl. checksum) has to be at least 41/49 (HEADER_SIZE) bytes long!
208
static bool VerifyOldNameChecksum(char *title, uint len)
211
for (uint i = 0; i < len - 2; i++) {
216
sum ^= 0xAAAA; // computed checksum
218
uint16 sum2 = title[len - 2]; // checksum in file
219
SB(sum2, 8, 8, title[len - 1]);
224
static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len)
226
assert(last - temp + 1 >= (int)len);
228
if (fread(temp, 1, len, f) != len) {
229
temp[0] = '\0'; // if reading failed, make the name empty
233
bool ret = VerifyOldNameChecksum(temp, len);
234
temp[len - 2] = '\0'; // name is nul-terminated in savegame, but it's better to be sure
235
str_validate(temp, last);
240
assert_compile(TTD_HEADER_SIZE >= TTO_HEADER_SIZE);
241
static SavegameType DetermineOldSavegameType(FILE *f, char *title, const char *last)
243
char temp[TTD_HEADER_SIZE];
245
SavegameType type = SGT_TTO;
247
/* Can't fseek to 0 as in tar files that is not correct */
249
if (!CheckOldSavegameType(f, temp, lastof(temp), TTO_HEADER_SIZE)) {
251
fseek(f, pos, SEEK_SET);
252
if (!CheckOldSavegameType(f, temp, lastof(temp), TTD_HEADER_SIZE)) {
259
case SGT_TTO: title = strecpy(title, "(TTO) ", last); break;
260
case SGT_TTD: title = strecpy(title, "(TTD) ", last); break;
261
default: title = strecpy(title, "(broken) ", last); break;
263
title = strecpy(title, temp, last);
269
typedef bool LoadOldMainProc(LoadgameState *ls);
271
bool LoadOldSaveGame(const char *file)
275
DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame");
280
ls.file = FioFOpenFile(file, "rb");
282
if (ls.file == NULL) {
283
DEBUG(oldloader, 0, "Cannot open file '%s'", file);
287
SavegameType type = DetermineOldSavegameType(ls.file, NULL, NULL);
289
LoadOldMainProc *proc = NULL;
292
case SGT_TTO: proc = &LoadTTOMain; break;
293
case SGT_TTD: proc = &LoadTTDMain; break;
297
_savegame_type = type;
299
if (proc == NULL || !proc(&ls)) {
300
SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED);
310
void GetOldSaveGameName(const char *file, char *title, const char *last)
312
FILE *f = FioFOpenFile(file, "rb");
319
DetermineOldSavegameType(f, title, last);