~ubuntu-branches/debian/squeeze/openttd/squeeze

« back to all changes in this revision

Viewing changes to src/saveload/oldloader.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jordi Mallach, Matthijs Kooijman, Jordi Mallach
  • Date: 2009-04-15 18:22:10 UTC
  • mfrom: (1.1.6 upstream) (2.1.3 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090415182210-22ktb8kdbp2tf3bm
[ Matthijs Kooijman ]
* New upstream release.
* Remove Debian specific desktop file, upstream provides one now. 
* Add debian/watch file.

[ Jordi Mallach ]
* Bump Standards-Version to 3.8.1, with no changes required.
* Move to debhelper compat 7. Bump Build-Depends accordingly.
* Use dh_prep.
* Add "set -e" to config script.
* Remove a few extra doc files that get installed by upstream Makefile.
* Add more complete copyright information.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: oldloader.cpp 15903 2009-03-30 23:15:05Z rubidium $ */
 
2
 
 
3
/** @file oldloader.cpp Functions for handling of TTO/TTD/TTDP savegames. */
 
4
 
 
5
#include "../stdafx.h"
 
6
#include "../openttd.h"
 
7
#include "../tile_type.h"
 
8
#include "../debug.h"
 
9
#include "../strings_type.h"
 
10
#include "../string_func.h"
 
11
#include "../settings_type.h"
 
12
#include "../fileio_func.h"
 
13
 
 
14
#include "table/strings.h"
 
15
 
 
16
#include "saveload_internal.h"
 
17
#include "oldloader.h"
 
18
 
 
19
enum {
 
20
        TTO_HEADER_SIZE = 41,
 
21
        TTD_HEADER_SIZE = 49,
 
22
};
 
23
 
 
24
uint32 _bump_assert_value;
 
25
 
 
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);}
 
29
 
 
30
static inline byte CalcOldVarLen(OldChunkType type)
 
31
{
 
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];
 
36
}
 
37
 
 
38
/**
 
39
 *
 
40
 * Reads a byte from a file (do not call yourself, use ReadByte())
 
41
 *
 
42
 */
 
43
static byte ReadByteFromFile(LoadgameState *ls)
 
44
{
 
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);
 
50
 
 
51
                /* We tried to read, but there is nothing in the file anymore.. */
 
52
                if (count == 0) {
 
53
                        DEBUG(oldloader, 0, "Read past end of file, loading failed");
 
54
                        ls->failed = true;
 
55
                }
 
56
 
 
57
                ls->buffer_count = count;
 
58
                ls->buffer_cur   = 0;
 
59
        }
 
60
 
 
61
        return ls->buffer[ls->buffer_cur++];
 
62
}
 
63
 
 
64
/**
 
65
 *
 
66
 * Reads a byte from the buffer and decompress if needed
 
67
 *
 
68
 */
 
69
byte ReadByte(LoadgameState *ls)
 
70
{
 
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 */
 
76
 
 
77
        if (ls->chunk_size == 0) {
 
78
                /* Read new chunk */
 
79
                int8 new_byte = ReadByteFromFile(ls);
 
80
 
 
81
                if (new_byte < 0) {
 
82
                        /* Repeat next char for new_byte times */
 
83
                        ls->decoding    = true;
 
84
                        ls->decode_char = ReadByteFromFile(ls);
 
85
                        ls->chunk_size  = -new_byte + 1;
 
86
                } else {
 
87
                        ls->decoding    = false;
 
88
                        ls->chunk_size  = new_byte + 1;
 
89
                }
 
90
        }
 
91
 
 
92
        ls->total_read++;
 
93
        ls->chunk_size--;
 
94
 
 
95
        return ls->decoding ? ls->decode_char : ReadByteFromFile(ls);
 
96
}
 
97
 
 
98
/**
 
99
 *
 
100
 * Loads a chunk from the old savegame
 
101
 *
 
102
 */
 
103
bool LoadChunk(LoadgameState *ls, void *base, const OldChunks *chunks)
 
104
{
 
105
        byte *base_ptr = (byte*)base;
 
106
 
 
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 */
 
111
                        continue;
 
112
                }
 
113
 
 
114
                byte *ptr = (byte*)chunk->ptr;
 
115
                if (chunk->type & OC_DEREFERENCE_POINTER) ptr = *(byte**)ptr;
 
116
 
 
117
                for (uint i = 0; i < chunk->amount; i++) {
 
118
                        if (ls->failed) return false;
 
119
 
 
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;
 
125
 
 
126
                                        case OC_CHUNK:
 
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;
 
130
                                                break;
 
131
 
 
132
                                        case OC_ASSERT:
 
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;
 
135
                                        default: break;
 
136
                                }
 
137
                        } else {
 
138
                                uint64 res = 0;
 
139
 
 
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();
 
149
                                }
 
150
 
 
151
                                /* Sanity check */
 
152
                                assert(base_ptr != NULL || chunk->ptr != NULL);
 
153
 
 
154
                                /* Writing to the var: bits 8 to 15 have the VAR type */
 
155
                                if (chunk->ptr == NULL) ptr = base_ptr + chunk->offset;
 
156
 
 
157
                                /* Write the data */
 
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();
 
168
                                }
 
169
 
 
170
                                /* Increase pointer base for arrays when looping */
 
171
                                if (chunk->amount > 1 && chunk->ptr != NULL) ptr += CalcOldVarLen(chunk->type);
 
172
                        }
 
173
                }
 
174
        }
 
175
 
 
176
        return true;
 
177
}
 
178
 
 
179
/**
 
180
 *
 
181
 * Initialize some data before reading
 
182
 *
 
183
 */
 
184
static void InitLoading(LoadgameState *ls)
 
185
{
 
186
        ls->chunk_size   = 0;
 
187
        ls->total_read   = 0;
 
188
        ls->failed       = false;
 
189
 
 
190
        ls->decoding     = false;
 
191
        ls->decode_char  = 0;
 
192
 
 
193
        ls->buffer_cur   = 0;
 
194
        ls->buffer_count = 0;
 
195
        memset(ls->buffer, 0, BUFFER_SIZE);
 
196
 
 
197
        _bump_assert_value = 0;
 
198
 
 
199
        _settings_game.construction.freeform_edges = false; // disable so we can convert map array (SetTileType is still used)
 
200
}
 
201
 
 
202
/**
 
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!
 
207
 */
 
208
static bool VerifyOldNameChecksum(char *title, uint len)
 
209
{
 
210
        uint16 sum = 0;
 
211
        for (uint i = 0; i < len - 2; i++) {
 
212
                sum += title[i];
 
213
                sum = ROL(sum, 1);
 
214
        }
 
215
 
 
216
        sum ^= 0xAAAA; // computed checksum
 
217
 
 
218
        uint16 sum2 = title[len - 2]; // checksum in file
 
219
        SB(sum2, 8, 8, title[len - 1]);
 
220
 
 
221
        return sum == sum2;
 
222
}
 
223
 
 
224
static inline bool CheckOldSavegameType(FILE *f, char *temp, const char *last, uint len)
 
225
{
 
226
        assert(last - temp + 1 >= (int)len);
 
227
 
 
228
        if (fread(temp, 1, len, f) != len) {
 
229
                temp[0] = '\0'; // if reading failed, make the name empty
 
230
                return false;
 
231
        }
 
232
 
 
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);
 
236
 
 
237
        return ret;
 
238
}
 
239
 
 
240
assert_compile(TTD_HEADER_SIZE >= TTO_HEADER_SIZE);
 
241
static SavegameType DetermineOldSavegameType(FILE *f, char *title, const char *last)
 
242
{
 
243
        char temp[TTD_HEADER_SIZE];
 
244
 
 
245
        SavegameType type = SGT_TTO;
 
246
 
 
247
        /* Can't fseek to 0 as in tar files that is not correct */
 
248
        long pos = ftell(f);
 
249
        if (!CheckOldSavegameType(f, temp, lastof(temp), TTO_HEADER_SIZE)) {
 
250
                type = SGT_TTD;
 
251
                fseek(f, pos, SEEK_SET);
 
252
                if (!CheckOldSavegameType(f, temp, lastof(temp), TTD_HEADER_SIZE)) {
 
253
                        type = SGT_INVALID;
 
254
                }
 
255
        }
 
256
 
 
257
        if (title != NULL) {
 
258
                switch (type) {
 
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;
 
262
                }
 
263
                title = strecpy(title, temp, last);
 
264
        }
 
265
 
 
266
        return type;
 
267
}
 
268
 
 
269
typedef bool LoadOldMainProc(LoadgameState *ls);
 
270
 
 
271
bool LoadOldSaveGame(const char *file)
 
272
{
 
273
        LoadgameState ls;
 
274
 
 
275
        DEBUG(oldloader, 3, "Trying to load a TTD(Patch) savegame");
 
276
 
 
277
        InitLoading(&ls);
 
278
 
 
279
        /* Open file */
 
280
        ls.file = FioFOpenFile(file, "rb");
 
281
 
 
282
        if (ls.file == NULL) {
 
283
                DEBUG(oldloader, 0, "Cannot open file '%s'", file);
 
284
                return false;
 
285
        }
 
286
 
 
287
        SavegameType type = DetermineOldSavegameType(ls.file, NULL, NULL);
 
288
 
 
289
        LoadOldMainProc *proc = NULL;
 
290
 
 
291
        switch (type) {
 
292
                case SGT_TTO: proc = &LoadTTOMain; break;
 
293
                case SGT_TTD: proc = &LoadTTDMain; break;
 
294
                default: break;
 
295
        }
 
296
 
 
297
        _savegame_type = type;
 
298
 
 
299
        if (proc == NULL || !proc(&ls)) {
 
300
                SetSaveLoadError(STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED);
 
301
                fclose(ls.file);
 
302
                return false;
 
303
        }
 
304
 
 
305
        _pause_game = 2;
 
306
 
 
307
        return true;
 
308
}
 
309
 
 
310
void GetOldSaveGameName(const char *file, char *title, const char *last)
 
311
{
 
312
        FILE *f = FioFOpenFile(file, "rb");
 
313
 
 
314
        if (f == NULL) {
 
315
                *title = '\0';
 
316
                return;
 
317
        }
 
318
 
 
319
        DetermineOldSavegameType(f, title, last);
 
320
 
 
321
        fclose(f);
 
322
}