|
1 /* $Id$ */ |
|
2 |
|
3 /** @file |
|
4 * All actions handling saving and loading goes on in this file. The general actions |
|
5 * are as follows for saving a game (loading is analogous): |
|
6 * <ol> |
|
7 * <li>initialize the writer by creating a temporary memory-buffer for it |
|
8 * <li>go through all to-be saved elements, each 'chunk' (ChunkHandler) prefixed by a label |
|
9 * <li>use their description array (SaveLoad) to know what elements to save and in what version |
|
10 * of the game it was active (used when loading) |
|
11 * <li>write all data byte-by-byte to the temporary buffer so it is endian-safe |
|
12 * <li>when the buffer is full; flush it to the output (eg save to file) (_sl.buf, _sl.bufp, _sl.bufe) |
|
13 * <li>repeat this until everything is done, and flush any remaining output to file |
|
14 * </ol> |
|
15 * @see ChunkHandler |
|
16 * @see SaveLoad |
|
17 */ |
|
18 #include "stdafx.h" |
|
19 #include "openttd.h" |
|
20 #include "debug.h" |
|
21 #include "functions.h" |
|
22 #include "hal.h" |
|
23 #include "vehicle.h" |
|
24 #include "station.h" |
|
25 #include "thread.h" |
|
26 #include "town.h" |
|
27 #include "player.h" |
|
28 #include "saveload.h" |
|
29 #include "network/network.h" |
|
30 #include "variables.h" |
|
31 #include <setjmp.h> |
|
32 |
|
33 const uint16 SAVEGAME_VERSION = 43; |
|
34 uint16 _sl_version; /// the major savegame version identifier |
|
35 byte _sl_minor_version; /// the minor savegame version, DO NOT USE! |
|
36 |
|
37 typedef void WriterProc(uint len); |
|
38 typedef uint ReaderProc(void); |
|
39 |
|
40 /** The saveload struct, containing reader-writer functions, bufffer, version, etc. */ |
|
41 static struct { |
|
42 bool save; /// are we doing a save or a load atm. True when saving |
|
43 byte need_length; /// ??? |
|
44 byte block_mode; /// ??? |
|
45 bool error; /// did an error occur or not |
|
46 |
|
47 int obj_len; /// the length of the current object we are busy with |
|
48 int array_index, last_array_index; /// in the case of an array, the current and last positions |
|
49 |
|
50 uint32 offs_base; /// the offset in number of bytes since we started writing data (eg uncompressed savegame size) |
|
51 |
|
52 WriterProc *write_bytes; /// savegame writer function |
|
53 ReaderProc *read_bytes; /// savegame loader function |
|
54 |
|
55 const ChunkHandler* const *chs; /// the chunk of data that is being processed atm (vehicles, signs, etc.) |
|
56 const SaveLoad* const *includes; /// the internal layouf of the given chunk |
|
57 |
|
58 /** When saving/loading savegames, they are always saved to a temporary memory-place |
|
59 * to be flushed to file (save) or to final place (load) when full. */ |
|
60 byte *bufp, *bufe; /// bufp(ointer) gives the current position in the buffer bufe(nd) gives the end of the buffer |
|
61 |
|
62 // these 3 may be used by compressor/decompressors. |
|
63 byte *buf; /// pointer to temporary memory to read/write, initialized by SaveLoadFormat->initread/write |
|
64 byte *buf_ori; /// pointer to the original memory location of buf, used to free it afterwards |
|
65 uint bufsize; /// the size of the temporary memory *buf |
|
66 FILE *fh; /// the file from which is read or written to |
|
67 |
|
68 void (*excpt_uninit)(void); /// the function to execute on any encountered error |
|
69 const char *excpt_msg; /// the error message |
|
70 jmp_buf excpt; /// @todo used to jump to "exception handler"; really ugly |
|
71 } _sl; |
|
72 |
|
73 |
|
74 enum NeedLengthValues {NL_NONE = 0, NL_WANTLENGTH = 1, NL_CALCLENGTH = 2}; |
|
75 |
|
76 /** |
|
77 * Fill the input buffer by reading from the file with the given reader |
|
78 */ |
|
79 static void SlReadFill(void) |
|
80 { |
|
81 uint len = _sl.read_bytes(); |
|
82 assert(len != 0); |
|
83 |
|
84 _sl.bufp = _sl.buf; |
|
85 _sl.bufe = _sl.buf + len; |
|
86 _sl.offs_base += len; |
|
87 } |
|
88 |
|
89 static inline uint32 SlGetOffs(void) {return _sl.offs_base - (_sl.bufe - _sl.bufp);} |
|
90 |
|
91 /** Return the size in bytes of a certain type of normal/atomic variable |
|
92 * as it appears in memory. @see VarTypes |
|
93 * @param conv @VarType type of variable that is used for calculating the size |
|
94 * @return Return the size of this type in bytes */ |
|
95 static inline byte SlCalcConvMemLen(VarType conv) |
|
96 { |
|
97 static const byte conv_mem_size[] = {1, 1, 1, 2, 2, 4, 4, 8, 8, 0}; |
|
98 byte length = GB(conv, 4, 4); |
|
99 assert(length < lengthof(conv_mem_size)); |
|
100 return conv_mem_size[length]; |
|
101 } |
|
102 |
|
103 /** Return the size in bytes of a certain type of normal/atomic variable |
|
104 * as it appears in a saved game. @see VarTypes |
|
105 * @param conv @VarType type of variable that is used for calculating the size |
|
106 * @return Return the size of this type in bytes */ |
|
107 static inline byte SlCalcConvFileLen(VarType conv) |
|
108 { |
|
109 static const byte conv_file_size[] = {1, 1, 2, 2, 4, 4, 8, 8, 2}; |
|
110 byte length = GB(conv, 0, 4); |
|
111 assert(length < lengthof(conv_file_size)); |
|
112 return conv_file_size[length]; |
|
113 } |
|
114 |
|
115 /* Return the size in bytes of a reference (pointer) */ |
|
116 static inline size_t SlCalcRefLen(void) {return 2;} |
|
117 |
|
118 /** Flush the output buffer by writing to disk with the given reader. |
|
119 * If the buffer pointer has not yet been set up, set it up now. Usually |
|
120 * only called when the buffer is full, or there is no more data to be processed |
|
121 */ |
|
122 static void SlWriteFill(void) |
|
123 { |
|
124 // flush the buffer to disk (the writer) |
|
125 if (_sl.bufp != NULL) { |
|
126 uint len = _sl.bufp - _sl.buf; |
|
127 _sl.offs_base += len; |
|
128 if (len) _sl.write_bytes(len); |
|
129 } |
|
130 |
|
131 /* All the data from the buffer has been written away, rewind to the beginning |
|
132 * to start reading in more data */ |
|
133 _sl.bufp = _sl.buf; |
|
134 _sl.bufe = _sl.buf + _sl.bufsize; |
|
135 } |
|
136 |
|
137 /** Error handler, calls longjmp to simulate an exception. |
|
138 * @todo this was used to have a central place to handle errors, but it is |
|
139 * pretty ugly, and seriously interferes with any multithreaded approaches */ |
|
140 static void NORETURN SlError(const char *msg) |
|
141 { |
|
142 _sl.excpt_msg = msg; |
|
143 longjmp(_sl.excpt, 0); |
|
144 } |
|
145 |
|
146 /** Read in a single byte from file. If the temporary buffer is full, |
|
147 * flush it to its final destination |
|
148 * @return return the read byte from file |
|
149 */ |
|
150 static inline byte SlReadByteInternal(void) |
|
151 { |
|
152 if (_sl.bufp == _sl.bufe) SlReadFill(); |
|
153 return *_sl.bufp++; |
|
154 } |
|
155 |
|
156 /** Wrapper for SlReadByteInternal */ |
|
157 byte SlReadByte(void) {return SlReadByteInternal();} |
|
158 |
|
159 /** Write away a single byte from memory. If the temporary buffer is full, |
|
160 * flush it to its destination (file) |
|
161 * @param b the byte that is currently written |
|
162 */ |
|
163 static inline void SlWriteByteInternal(byte b) |
|
164 { |
|
165 if (_sl.bufp == _sl.bufe) SlWriteFill(); |
|
166 *_sl.bufp++ = b; |
|
167 } |
|
168 |
|
169 /** Wrapper for SlWriteByteInternal */ |
|
170 void SlWriteByte(byte b) {SlWriteByteInternal(b);} |
|
171 |
|
172 static inline int SlReadUint16(void) |
|
173 { |
|
174 int x = SlReadByte() << 8; |
|
175 return x | SlReadByte(); |
|
176 } |
|
177 |
|
178 static inline uint32 SlReadUint32(void) |
|
179 { |
|
180 uint32 x = SlReadUint16() << 16; |
|
181 return x | SlReadUint16(); |
|
182 } |
|
183 |
|
184 static inline uint64 SlReadUint64(void) |
|
185 { |
|
186 uint32 x = SlReadUint32(); |
|
187 uint32 y = SlReadUint32(); |
|
188 return (uint64)x << 32 | y; |
|
189 } |
|
190 |
|
191 static inline void SlWriteUint16(uint16 v) |
|
192 { |
|
193 SlWriteByte(GB(v, 8, 8)); |
|
194 SlWriteByte(GB(v, 0, 8)); |
|
195 } |
|
196 |
|
197 static inline void SlWriteUint32(uint32 v) |
|
198 { |
|
199 SlWriteUint16(GB(v, 16, 16)); |
|
200 SlWriteUint16(GB(v, 0, 16)); |
|
201 } |
|
202 |
|
203 static inline void SlWriteUint64(uint64 x) |
|
204 { |
|
205 SlWriteUint32((uint32)(x >> 32)); |
|
206 SlWriteUint32((uint32)x); |
|
207 } |
|
208 |
|
209 /** |
|
210 * Read in the header descriptor of an object or an array. |
|
211 * If the highest bit is set (7), then the index is bigger than 127 |
|
212 * elements, so use the next byte to read in the real value. |
|
213 * The actual value is then both bytes added with the first shifted |
|
214 * 8 bits to the left, and dropping the highest bit (which only indicated a big index). |
|
215 * x = ((x & 0x7F) << 8) + SlReadByte(); |
|
216 * @return Return the value of the index |
|
217 */ |
|
218 static uint SlReadSimpleGamma(void) |
|
219 { |
|
220 uint i = SlReadByte(); |
|
221 if (HASBIT(i, 7)) { |
|
222 i &= ~0x80; |
|
223 if (HASBIT(i, 6)) { |
|
224 i &= ~0x40; |
|
225 if (HASBIT(i, 5)) { |
|
226 i &= ~0x20; |
|
227 if (HASBIT(i, 4)) |
|
228 SlError("Unsupported gamma"); |
|
229 i = (i << 8) | SlReadByte(); |
|
230 } |
|
231 i = (i << 8) | SlReadByte(); |
|
232 } |
|
233 i = (i << 8) | SlReadByte(); |
|
234 } |
|
235 return i; |
|
236 } |
|
237 |
|
238 /** |
|
239 * Write the header descriptor of an object or an array. |
|
240 * If the element is bigger than 127, use 2 bytes for saving |
|
241 * and use the highest byte of the first written one as a notice |
|
242 * that the length consists of 2 bytes, etc.. like this: |
|
243 * 0xxxxxxx |
|
244 * 10xxxxxx xxxxxxxx |
|
245 * 110xxxxx xxxxxxxx xxxxxxxx |
|
246 * 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx |
|
247 * @param i Index being written |
|
248 */ |
|
249 |
|
250 static void SlWriteSimpleGamma(uint i) |
|
251 { |
|
252 if (i >= (1 << 7)) { |
|
253 if (i >= (1 << 14)) { |
|
254 if (i >= (1 << 21)) { |
|
255 assert(i < (1 << 28)); |
|
256 SlWriteByte((byte)0xE0 | (i>>24)); |
|
257 SlWriteByte((byte)(i>>16)); |
|
258 } else { |
|
259 SlWriteByte((byte)0xC0 | (i>>16)); |
|
260 } |
|
261 SlWriteByte((byte)(i>>8)); |
|
262 } else { |
|
263 SlWriteByte((byte)(0x80 | (i>>8))); |
|
264 } |
|
265 } |
|
266 SlWriteByte(i); |
|
267 } |
|
268 |
|
269 /** Return how many bytes used to encode a gamma value */ |
|
270 static inline uint SlGetGammaLength(uint i) { |
|
271 return 1 + (i >= (1 << 7)) + (i >= (1 << 14)) + (i >= (1 << 21)); |
|
272 } |
|
273 |
|
274 static inline uint SlReadSparseIndex(void) {return SlReadSimpleGamma();} |
|
275 static inline void SlWriteSparseIndex(uint index) {SlWriteSimpleGamma(index);} |
|
276 |
|
277 static inline uint SlReadArrayLength(void) {return SlReadSimpleGamma();} |
|
278 static inline void SlWriteArrayLength(uint length) {SlWriteSimpleGamma(length);} |
|
279 static inline uint SlGetArrayLength(uint length) {return SlGetGammaLength(length);} |
|
280 |
|
281 void SlSetArrayIndex(uint index) |
|
282 { |
|
283 _sl.need_length = NL_WANTLENGTH; |
|
284 _sl.array_index = index; |
|
285 } |
|
286 |
|
287 /** |
|
288 * Iterate through the elements of an array and read the whole thing |
|
289 * @return The index of the object, or -1 if we have reached the end of current block |
|
290 */ |
|
291 int SlIterateArray(void) |
|
292 { |
|
293 int index; |
|
294 static uint32 next_offs; |
|
295 |
|
296 /* After reading in the whole array inside the loop |
|
297 * we must have read in all the data, so we must be at end of current block. */ |
|
298 assert(next_offs == 0 || SlGetOffs() == next_offs); |
|
299 |
|
300 while (true) { |
|
301 uint length = SlReadArrayLength(); |
|
302 if (length == 0) { |
|
303 next_offs = 0; |
|
304 return -1; |
|
305 } |
|
306 |
|
307 _sl.obj_len = --length; |
|
308 next_offs = SlGetOffs() + length; |
|
309 |
|
310 switch (_sl.block_mode) { |
|
311 case CH_SPARSE_ARRAY: index = (int)SlReadSparseIndex(); break; |
|
312 case CH_ARRAY: index = _sl.array_index++; break; |
|
313 default: |
|
314 DEBUG(sl, 0, "SlIterateArray error"); |
|
315 return -1; // error |
|
316 } |
|
317 |
|
318 if (length != 0) return index; |
|
319 } |
|
320 } |
|
321 |
|
322 /** |
|
323 * Sets the length of either a RIFF object or the number of items in an array. |
|
324 * This lets us load an object or an array of arbitrary size |
|
325 * @param length The length of the sought object/array |
|
326 */ |
|
327 void SlSetLength(size_t length) |
|
328 { |
|
329 assert(_sl.save); |
|
330 |
|
331 switch (_sl.need_length) { |
|
332 case NL_WANTLENGTH: |
|
333 _sl.need_length = NL_NONE; |
|
334 switch (_sl.block_mode) { |
|
335 case CH_RIFF: |
|
336 // Ugly encoding of >16M RIFF chunks |
|
337 // The lower 24 bits are normal |
|
338 // The uppermost 4 bits are bits 24:27 |
|
339 assert(length < (1<<28)); |
|
340 SlWriteUint32((length & 0xFFFFFF) | ((length >> 24) << 28)); |
|
341 break; |
|
342 case CH_ARRAY: |
|
343 assert(_sl.last_array_index <= _sl.array_index); |
|
344 while (++_sl.last_array_index <= _sl.array_index) |
|
345 SlWriteArrayLength(1); |
|
346 SlWriteArrayLength(length + 1); |
|
347 break; |
|
348 case CH_SPARSE_ARRAY: |
|
349 SlWriteArrayLength(length + 1 + SlGetArrayLength(_sl.array_index)); // Also include length of sparse index. |
|
350 SlWriteSparseIndex(_sl.array_index); |
|
351 break; |
|
352 default: NOT_REACHED(); |
|
353 } break; |
|
354 case NL_CALCLENGTH: |
|
355 _sl.obj_len += length; |
|
356 break; |
|
357 } |
|
358 } |
|
359 |
|
360 /** |
|
361 * Save/Load bytes. These do not need to be converted to Little/Big Endian |
|
362 * so directly write them or read them to/from file |
|
363 * @param ptr The source or destination of the object being manipulated |
|
364 * @param length number of bytes this fast CopyBytes lasts |
|
365 */ |
|
366 static void SlCopyBytes(void *ptr, size_t length) |
|
367 { |
|
368 byte *p = (byte*)ptr; |
|
369 |
|
370 if (_sl.save) { |
|
371 for (; length != 0; length--) {SlWriteByteInternal(*p++);} |
|
372 } else { |
|
373 for (; length != 0; length--) {*p++ = SlReadByteInternal();} |
|
374 } |
|
375 } |
|
376 |
|
377 /** Read in bytes from the file/data structure but don't do |
|
378 * anything with them, discarding them in effect |
|
379 * @param length The amount of bytes that is being treated this way |
|
380 */ |
|
381 static inline void SlSkipBytes(size_t length) |
|
382 { |
|
383 for (; length != 0; length--) SlReadByte(); |
|
384 } |
|
385 |
|
386 /* Get the length of the current object */ |
|
387 uint SlGetFieldLength(void) {return _sl.obj_len;} |
|
388 |
|
389 /** Return a signed-long version of the value of a setting |
|
390 * @param ptr pointer to the variable |
|
391 * @param conv type of variable, can be a non-clean |
|
392 * type, eg one with other flags because it is parsed |
|
393 * @return returns the value of the pointer-setting */ |
|
394 int64 ReadValue(const void *ptr, VarType conv) |
|
395 { |
|
396 switch (GetVarMemType(conv)) { |
|
397 case SLE_VAR_BL: return (*(bool*)ptr != 0); |
|
398 case SLE_VAR_I8: return *(int8* )ptr; |
|
399 case SLE_VAR_U8: return *(byte* )ptr; |
|
400 case SLE_VAR_I16: return *(int16* )ptr; |
|
401 case SLE_VAR_U16: return *(uint16*)ptr; |
|
402 case SLE_VAR_I32: return *(int32* )ptr; |
|
403 case SLE_VAR_U32: return *(uint32*)ptr; |
|
404 case SLE_VAR_I64: return *(int64* )ptr; |
|
405 case SLE_VAR_U64: return *(uint64*)ptr; |
|
406 case SLE_VAR_NULL:return 0; |
|
407 default: NOT_REACHED(); |
|
408 } |
|
409 |
|
410 /* useless, but avoids compiler warning this way */ |
|
411 return 0; |
|
412 } |
|
413 |
|
414 /** Write the value of a setting |
|
415 * @param ptr pointer to the variable |
|
416 * @param conv type of variable, can be a non-clean type, eg |
|
417 * with other flags. It is parsed upon read |
|
418 * @param var the new value being given to the variable */ |
|
419 void WriteValue(void *ptr, VarType conv, int64 val) |
|
420 { |
|
421 switch (GetVarMemType(conv)) { |
|
422 case SLE_VAR_BL: *(bool *)ptr = (val != 0); break; |
|
423 case SLE_VAR_I8: *(int8 *)ptr = val; break; |
|
424 case SLE_VAR_U8: *(byte *)ptr = val; break; |
|
425 case SLE_VAR_I16: *(int16 *)ptr = val; break; |
|
426 case SLE_VAR_U16: *(uint16*)ptr = val; break; |
|
427 case SLE_VAR_I32: *(int32 *)ptr = val; break; |
|
428 case SLE_VAR_U32: *(uint32*)ptr = val; break; |
|
429 case SLE_VAR_I64: *(int64 *)ptr = val; break; |
|
430 case SLE_VAR_U64: *(uint64*)ptr = val; break; |
|
431 case SLE_VAR_NULL: break; |
|
432 default: NOT_REACHED(); |
|
433 } |
|
434 } |
|
435 |
|
436 /** |
|
437 * Handle all conversion and typechecking of variables here. |
|
438 * In the case of saving, read in the actual value from the struct |
|
439 * and then write them to file, endian safely. Loading a value |
|
440 * goes exactly the opposite way |
|
441 * @param ptr The object being filled/read |
|
442 * @param conv @VarType type of the current element of the struct |
|
443 */ |
|
444 static void SlSaveLoadConv(void *ptr, VarType conv) |
|
445 { |
|
446 int64 x = 0; |
|
447 |
|
448 if (_sl.save) { /* SAVE values */ |
|
449 /* Read a value from the struct. These ARE endian safe. */ |
|
450 x = ReadValue(ptr, conv); |
|
451 |
|
452 /* Write the value to the file and check if its value is in the desired range */ |
|
453 switch (GetVarFileType(conv)) { |
|
454 case SLE_FILE_I8: assert(x >= -128 && x <= 127); SlWriteByte(x);break; |
|
455 case SLE_FILE_U8: assert(x >= 0 && x <= 255); SlWriteByte(x);break; |
|
456 case SLE_FILE_I16:assert(x >= -32768 && x <= 32767); SlWriteUint16(x);break; |
|
457 case SLE_FILE_STRINGID: |
|
458 case SLE_FILE_U16:assert(x >= 0 && x <= 65535); SlWriteUint16(x);break; |
|
459 case SLE_FILE_I32: |
|
460 case SLE_FILE_U32: SlWriteUint32((uint32)x);break; |
|
461 case SLE_FILE_I64: |
|
462 case SLE_FILE_U64: SlWriteUint64(x);break; |
|
463 default: NOT_REACHED(); |
|
464 } |
|
465 } else { /* LOAD values */ |
|
466 |
|
467 /* Read a value from the file */ |
|
468 switch (GetVarFileType(conv)) { |
|
469 case SLE_FILE_I8: x = (int8 )SlReadByte(); break; |
|
470 case SLE_FILE_U8: x = (byte )SlReadByte(); break; |
|
471 case SLE_FILE_I16: x = (int16 )SlReadUint16(); break; |
|
472 case SLE_FILE_U16: x = (uint16)SlReadUint16(); break; |
|
473 case SLE_FILE_I32: x = (int32 )SlReadUint32(); break; |
|
474 case SLE_FILE_U32: x = (uint32)SlReadUint32(); break; |
|
475 case SLE_FILE_I64: x = (int64 )SlReadUint64(); break; |
|
476 case SLE_FILE_U64: x = (uint64)SlReadUint64(); break; |
|
477 case SLE_FILE_STRINGID: x = RemapOldStringID((uint16)SlReadUint16()); break; |
|
478 default: NOT_REACHED(); |
|
479 } |
|
480 |
|
481 /* Write The value to the struct. These ARE endian safe. */ |
|
482 WriteValue(ptr, conv, x); |
|
483 } |
|
484 } |
|
485 |
|
486 /** Calculate the net length of a string. This is in almost all cases |
|
487 * just strlen(), but if the string is not properly terminated, we'll |
|
488 * resort to the maximum length of the buffer. |
|
489 * @param ptr pointer to the stringbuffer |
|
490 * @param length maximum length of the string (buffer). If -1 we don't care |
|
491 * about a maximum length, but take string length as it is. |
|
492 * @return return the net length of the string */ |
|
493 static inline size_t SlCalcNetStringLen(const char *ptr, size_t length) |
|
494 { |
|
495 return minu(strlen(ptr), length - 1); |
|
496 } |
|
497 |
|
498 /** Calculate the gross length of the string that it |
|
499 * will occupy in the savegame. This includes the real length, returned |
|
500 * by SlCalcNetStringLen and the length that the index will occupy. |
|
501 * @param ptr pointer to the stringbuffer |
|
502 * @param length maximum length of the string (buffer size, etc.) |
|
503 * @return return the gross length of the string */ |
|
504 static inline size_t SlCalcStringLen(const void *ptr, size_t length, VarType conv) |
|
505 { |
|
506 size_t len; |
|
507 const char *str; |
|
508 |
|
509 switch (GetVarMemType(conv)) { |
|
510 default: NOT_REACHED(); |
|
511 case SLE_VAR_STR: |
|
512 case SLE_VAR_STRQ: |
|
513 str = *(const char**)ptr; |
|
514 len = -1; |
|
515 break; |
|
516 case SLE_VAR_STRB: |
|
517 case SLE_VAR_STRBQ: |
|
518 str = (const char*)ptr; |
|
519 len = length; |
|
520 break; |
|
521 } |
|
522 |
|
523 len = SlCalcNetStringLen(str, len); |
|
524 return len + SlGetArrayLength(len); // also include the length of the index |
|
525 } |
|
526 |
|
527 /** |
|
528 * Save/Load a string. |
|
529 * @param ptr the string being manipulated |
|
530 * @param the length of the string (full length) |
|
531 * @param conv must be SLE_FILE_STRING */ |
|
532 static void SlString(void *ptr, size_t length, VarType conv) |
|
533 { |
|
534 size_t len; |
|
535 |
|
536 if (_sl.save) { /* SAVE string */ |
|
537 switch (GetVarMemType(conv)) { |
|
538 default: NOT_REACHED(); |
|
539 case SLE_VAR_STRB: |
|
540 case SLE_VAR_STRBQ: |
|
541 len = SlCalcNetStringLen(ptr, length); |
|
542 break; |
|
543 case SLE_VAR_STR: |
|
544 case SLE_VAR_STRQ: |
|
545 ptr = *(char**)ptr; |
|
546 len = SlCalcNetStringLen(ptr, -1); |
|
547 break; |
|
548 } |
|
549 |
|
550 SlWriteArrayLength(len); |
|
551 SlCopyBytes(ptr, len); |
|
552 } else { /* LOAD string */ |
|
553 len = SlReadArrayLength(); |
|
554 |
|
555 switch (GetVarMemType(conv)) { |
|
556 default: NOT_REACHED(); |
|
557 case SLE_VAR_STRB: |
|
558 case SLE_VAR_STRBQ: |
|
559 if (len >= length) { |
|
560 DEBUG(sl, 1, "String length in savegame is bigger than buffer, truncating"); |
|
561 SlCopyBytes(ptr, length); |
|
562 SlSkipBytes(len - length); |
|
563 len = length - 1; |
|
564 } else { |
|
565 SlCopyBytes(ptr, len); |
|
566 } |
|
567 break; |
|
568 case SLE_VAR_STR: |
|
569 case SLE_VAR_STRQ: /* Malloc'd string, free previous incarnation, and allocate */ |
|
570 free(*(char**)ptr); |
|
571 *(char**)ptr = malloc(len + 1); // terminating '\0' |
|
572 ptr = *(char**)ptr; |
|
573 SlCopyBytes(ptr, len); |
|
574 break; |
|
575 } |
|
576 |
|
577 ((char*)ptr)[len] = '\0'; // properly terminate the string |
|
578 } |
|
579 } |
|
580 |
|
581 /** |
|
582 * Return the size in bytes of a certain type of atomic array |
|
583 * @param length The length of the array counted in elements |
|
584 * @param conv @VarType type of the variable that is used in calculating the size |
|
585 */ |
|
586 static inline size_t SlCalcArrayLen(uint length, VarType conv) |
|
587 { |
|
588 return SlCalcConvFileLen(conv) * length; |
|
589 } |
|
590 |
|
591 /** |
|
592 * Save/Load an array. |
|
593 * @param array The array being manipulated |
|
594 * @param length The length of the array in elements |
|
595 * @param conv @VarType type of the atomic array (int, byte, uint64, etc.) |
|
596 */ |
|
597 void SlArray(void *array, uint length, VarType conv) |
|
598 { |
|
599 // Automatically calculate the length? |
|
600 if (_sl.need_length != NL_NONE) { |
|
601 SlSetLength(SlCalcArrayLen(length, conv)); |
|
602 // Determine length only? |
|
603 if (_sl.need_length == NL_CALCLENGTH) return; |
|
604 } |
|
605 |
|
606 /* NOTICE - handle some buggy stuff, in really old versions everything was saved |
|
607 * as a byte-type. So detect this, and adjust array size accordingly */ |
|
608 if (!_sl.save && _sl_version == 0) { |
|
609 if (conv == SLE_INT16 || conv == SLE_UINT16 || conv == SLE_STRINGID || |
|
610 conv == SLE_INT32 || conv == SLE_UINT32) { |
|
611 length *= SlCalcConvFileLen(conv); |
|
612 conv = SLE_INT8; |
|
613 } |
|
614 } |
|
615 |
|
616 /* If the size of elements is 1 byte both in file and memory, no special |
|
617 * conversion is needed, use specialized copy-copy function to speed up things */ |
|
618 if (conv == SLE_INT8 || conv == SLE_UINT8) { |
|
619 SlCopyBytes(array, length); |
|
620 } else { |
|
621 byte *a = (byte*)array; |
|
622 byte mem_size = SlCalcConvMemLen(conv); |
|
623 |
|
624 for (; length != 0; length --) { |
|
625 SlSaveLoadConv(a, conv); |
|
626 a += mem_size; // get size |
|
627 } |
|
628 } |
|
629 } |
|
630 |
|
631 /* Are we going to save this object or not? */ |
|
632 static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld) |
|
633 { |
|
634 if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false; |
|
635 if (sld->conv & SLF_SAVE_NO) return false; |
|
636 |
|
637 return true; |
|
638 } |
|
639 |
|
640 /** Are we going to load this variable when loading a savegame or not? |
|
641 * @note If the variable is skipped it is skipped in the savegame |
|
642 * bytestream itself as well, so there is no need to skip it somewhere else */ |
|
643 static inline bool SlSkipVariableOnLoad(const SaveLoad *sld) |
|
644 { |
|
645 if ((sld->conv & SLF_NETWORK_NO) && !_sl.save && _networking && !_network_server) { |
|
646 SlSkipBytes(SlCalcConvMemLen(sld->conv) * sld->length); |
|
647 return true; |
|
648 } |
|
649 |
|
650 return false; |
|
651 } |
|
652 |
|
653 /** |
|
654 * Calculate the size of an object. |
|
655 * @param sld The @SaveLoad description of the object so we know how to manipulate it |
|
656 */ |
|
657 static size_t SlCalcObjLength(const void *object, const SaveLoad *sld) |
|
658 { |
|
659 size_t length = 0; |
|
660 |
|
661 // Need to determine the length and write a length tag. |
|
662 for (; sld->cmd != SL_END; sld++) { |
|
663 length += SlCalcObjMemberLength(object, sld); |
|
664 } |
|
665 return length; |
|
666 } |
|
667 |
|
668 size_t SlCalcObjMemberLength(const void *object, const SaveLoad *sld) |
|
669 { |
|
670 assert(_sl.save); |
|
671 |
|
672 switch (sld->cmd) { |
|
673 case SL_VAR: |
|
674 case SL_REF: |
|
675 case SL_ARR: |
|
676 case SL_STR: |
|
677 /* CONDITIONAL saveload types depend on the savegame version */ |
|
678 if (!SlIsObjectValidInSavegame(sld)) break; |
|
679 |
|
680 switch (sld->cmd) { |
|
681 case SL_VAR: return SlCalcConvFileLen(sld->conv); |
|
682 case SL_REF: return SlCalcRefLen(); |
|
683 case SL_ARR: return SlCalcArrayLen(sld->length, sld->conv); |
|
684 case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv); |
|
685 default: NOT_REACHED(); |
|
686 } |
|
687 break; |
|
688 case SL_WRITEBYTE: return 1; // a byte is logically of size 1 |
|
689 case SL_INCLUDE: return SlCalcObjLength(object, _sl.includes[sld->version_from]); |
|
690 default: NOT_REACHED(); |
|
691 } |
|
692 return 0; |
|
693 } |
|
694 |
|
695 |
|
696 static uint ReferenceToInt(const void* obj, SLRefType rt); |
|
697 static void* IntToReference(uint index, SLRefType rt); |
|
698 |
|
699 |
|
700 bool SlObjectMember(void *ptr, const SaveLoad *sld) |
|
701 { |
|
702 VarType conv = GB(sld->conv, 0, 8); |
|
703 switch (sld->cmd) { |
|
704 case SL_VAR: |
|
705 case SL_REF: |
|
706 case SL_ARR: |
|
707 case SL_STR: |
|
708 /* CONDITIONAL saveload types depend on the savegame version */ |
|
709 if (!SlIsObjectValidInSavegame(sld)) return false; |
|
710 if (SlSkipVariableOnLoad(sld)) return false; |
|
711 |
|
712 switch (sld->cmd) { |
|
713 case SL_VAR: SlSaveLoadConv(ptr, conv); break; |
|
714 case SL_REF: /* Reference variable, translate */ |
|
715 /// @todo XXX - another artificial limitof 65K elements of pointers? |
|
716 if (_sl.save) { // XXX - read/write pointer as uint16? What is with higher indeces? |
|
717 SlWriteUint16(ReferenceToInt(*(void**)ptr, conv)); |
|
718 } else { |
|
719 *(void**)ptr = IntToReference(SlReadUint16(), conv); |
|
720 } |
|
721 break; |
|
722 case SL_ARR: SlArray(ptr, sld->length, conv); break; |
|
723 case SL_STR: SlString(ptr, sld->length, conv); break; |
|
724 default: NOT_REACHED(); |
|
725 } |
|
726 break; |
|
727 |
|
728 /* SL_WRITEBYTE translates a value of a variable to another one upon |
|
729 * saving or loading. |
|
730 * XXX - variable renaming abuse |
|
731 * game_value: the value of the variable ingame is abused by sld->version_from |
|
732 * file_value: the value of the variable in the savegame is abused by sld->version_to */ |
|
733 case SL_WRITEBYTE: |
|
734 if (_sl.save) { |
|
735 SlWriteByte(sld->version_to); |
|
736 } else { |
|
737 *(byte*)ptr = sld->version_from; |
|
738 } |
|
739 break; |
|
740 |
|
741 /* SL_INCLUDE loads common code for a type |
|
742 * XXX - variable renaming abuse |
|
743 * include_index: common code to include from _desc_includes[], abused by sld->version_from */ |
|
744 case SL_INCLUDE: |
|
745 SlObject(ptr, _sl.includes[sld->version_from]); |
|
746 break; |
|
747 default: NOT_REACHED(); |
|
748 } |
|
749 return true; |
|
750 } |
|
751 |
|
752 /** |
|
753 * Main SaveLoad function. |
|
754 * @param object The object that is being saved or loaded |
|
755 * @param sld The @SaveLoad description of the object so we know how to manipulate it |
|
756 */ |
|
757 void SlObject(void *object, const SaveLoad *sld) |
|
758 { |
|
759 // Automatically calculate the length? |
|
760 if (_sl.need_length != NL_NONE) { |
|
761 SlSetLength(SlCalcObjLength(object, sld)); |
|
762 if (_sl.need_length == NL_CALCLENGTH) return; |
|
763 } |
|
764 |
|
765 for (; sld->cmd != SL_END; sld++) { |
|
766 void *ptr = GetVariableAddress(object, sld); |
|
767 SlObjectMember(ptr, sld); |
|
768 } |
|
769 } |
|
770 |
|
771 /** |
|
772 * Save or Load (a list of) global variables |
|
773 * @param desc The global variable that is being loaded or saved |
|
774 */ |
|
775 void SlGlobList(const SaveLoadGlobVarList *sldg) |
|
776 { |
|
777 if (_sl.need_length != NL_NONE) { |
|
778 SlSetLength(SlCalcObjLength(NULL, (const SaveLoad*)sldg)); |
|
779 if (_sl.need_length == NL_CALCLENGTH) return; |
|
780 } |
|
781 |
|
782 for (; sldg->cmd != SL_END; sldg++) { |
|
783 SlObjectMember(sldg->address, (const SaveLoad*)sldg); |
|
784 } |
|
785 } |
|
786 |
|
787 /** |
|
788 * Do something of which I have no idea what it is :P |
|
789 * @param proc The callback procedure that is called |
|
790 * @param arg The variable that will be used for the callback procedure |
|
791 */ |
|
792 void SlAutolength(AutolengthProc *proc, void *arg) |
|
793 { |
|
794 uint32 offs; |
|
795 |
|
796 assert(_sl.save); |
|
797 |
|
798 // Tell it to calculate the length |
|
799 _sl.need_length = NL_CALCLENGTH; |
|
800 _sl.obj_len = 0; |
|
801 proc(arg); |
|
802 |
|
803 // Setup length |
|
804 _sl.need_length = NL_WANTLENGTH; |
|
805 SlSetLength(_sl.obj_len); |
|
806 |
|
807 offs = SlGetOffs() + _sl.obj_len; |
|
808 |
|
809 // And write the stuff |
|
810 proc(arg); |
|
811 |
|
812 assert(offs == SlGetOffs()); |
|
813 } |
|
814 |
|
815 /** |
|
816 * Load a chunk of data (eg vehicles, stations, etc.) |
|
817 * @param ch The chunkhandler that will be used for the operation |
|
818 */ |
|
819 static void SlLoadChunk(const ChunkHandler *ch) |
|
820 { |
|
821 byte m = SlReadByte(); |
|
822 size_t len; |
|
823 uint32 endoffs; |
|
824 |
|
825 _sl.block_mode = m; |
|
826 _sl.obj_len = 0; |
|
827 |
|
828 switch (m) { |
|
829 case CH_ARRAY: |
|
830 _sl.array_index = 0; |
|
831 ch->load_proc(); |
|
832 break; |
|
833 case CH_SPARSE_ARRAY: |
|
834 ch->load_proc(); |
|
835 break; |
|
836 default: |
|
837 if ((m & 0xF) == CH_RIFF) { |
|
838 // Read length |
|
839 len = (SlReadByte() << 16) | ((m >> 4) << 24); |
|
840 len += SlReadUint16(); |
|
841 _sl.obj_len = len; |
|
842 endoffs = SlGetOffs() + len; |
|
843 ch->load_proc(); |
|
844 assert(SlGetOffs() == endoffs); |
|
845 } else { |
|
846 SlError("Invalid chunk type"); |
|
847 } |
|
848 break; |
|
849 } |
|
850 } |
|
851 |
|
852 /* Stub Chunk handlers to only calculate length and do nothing else */ |
|
853 static ChunkSaveLoadProc *_tmp_proc_1; |
|
854 static inline void SlStubSaveProc2(void *arg) {_tmp_proc_1();} |
|
855 static void SlStubSaveProc(void) {SlAutolength(SlStubSaveProc2, NULL);} |
|
856 |
|
857 /** Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is |
|
858 * prefixed by an ID identifying it, followed by data, and terminator where appropiate |
|
859 * @param ch The chunkhandler that will be used for the operation |
|
860 */ |
|
861 static void SlSaveChunk(const ChunkHandler *ch) |
|
862 { |
|
863 ChunkSaveLoadProc *proc = ch->save_proc; |
|
864 |
|
865 SlWriteUint32(ch->id); |
|
866 DEBUG(sl, 2, "Saving chunk %c%c%c%c", ch->id >> 24, ch->id >> 16, ch->id >> 8, ch->id); |
|
867 |
|
868 if (ch->flags & CH_AUTO_LENGTH) { |
|
869 // Need to calculate the length. Solve that by calling SlAutoLength in the save_proc. |
|
870 _tmp_proc_1 = proc; |
|
871 proc = SlStubSaveProc; |
|
872 } |
|
873 |
|
874 _sl.block_mode = ch->flags & CH_TYPE_MASK; |
|
875 switch (ch->flags & CH_TYPE_MASK) { |
|
876 case CH_RIFF: |
|
877 _sl.need_length = NL_WANTLENGTH; |
|
878 proc(); |
|
879 break; |
|
880 case CH_ARRAY: |
|
881 _sl.last_array_index = 0; |
|
882 SlWriteByte(CH_ARRAY); |
|
883 proc(); |
|
884 SlWriteArrayLength(0); // Terminate arrays |
|
885 break; |
|
886 case CH_SPARSE_ARRAY: |
|
887 SlWriteByte(CH_SPARSE_ARRAY); |
|
888 proc(); |
|
889 SlWriteArrayLength(0); // Terminate arrays |
|
890 break; |
|
891 default: NOT_REACHED(); |
|
892 } |
|
893 } |
|
894 |
|
895 /** Save all chunks */ |
|
896 static void SlSaveChunks(void) |
|
897 { |
|
898 const ChunkHandler *ch; |
|
899 const ChunkHandler* const *chsc; |
|
900 uint p; |
|
901 |
|
902 for (p = 0; p != CH_NUM_PRI_LEVELS; p++) { |
|
903 for (chsc = _sl.chs; (ch = *chsc++) != NULL;) { |
|
904 while (true) { |
|
905 if (((ch->flags >> CH_PRI_SHL) & (CH_NUM_PRI_LEVELS - 1)) == p) |
|
906 SlSaveChunk(ch); |
|
907 if (ch->flags & CH_LAST) |
|
908 break; |
|
909 ch++; |
|
910 } |
|
911 } |
|
912 } |
|
913 |
|
914 // Terminator |
|
915 SlWriteUint32(0); |
|
916 } |
|
917 |
|
918 /** Find the ChunkHandler that will be used for processing the found |
|
919 * chunk in the savegame or in memory |
|
920 * @param id the chunk in question |
|
921 * @return returns the appropiate chunkhandler |
|
922 */ |
|
923 static const ChunkHandler *SlFindChunkHandler(uint32 id) |
|
924 { |
|
925 const ChunkHandler *ch; |
|
926 const ChunkHandler *const *chsc; |
|
927 for (chsc = _sl.chs; (ch=*chsc++) != NULL;) { |
|
928 for (;;) { |
|
929 if (ch->id == id) return ch; |
|
930 if (ch->flags & CH_LAST) break; |
|
931 ch++; |
|
932 } |
|
933 } |
|
934 return NULL; |
|
935 } |
|
936 |
|
937 /** Load all chunks */ |
|
938 static void SlLoadChunks(void) |
|
939 { |
|
940 uint32 id; |
|
941 const ChunkHandler *ch; |
|
942 |
|
943 for (id = SlReadUint32(); id != 0; id = SlReadUint32()) { |
|
944 DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id); |
|
945 |
|
946 ch = SlFindChunkHandler(id); |
|
947 if (ch == NULL) SlError("found unknown tag in savegame (sync error)"); |
|
948 SlLoadChunk(ch); |
|
949 } |
|
950 } |
|
951 |
|
952 //******************************************* |
|
953 //********** START OF LZO CODE ************** |
|
954 //******************************************* |
|
955 #define LZO_SIZE 8192 |
|
956 |
|
957 #include "minilzo.h" |
|
958 |
|
959 static uint ReadLZO(void) |
|
960 { |
|
961 byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; |
|
962 uint32 tmp[2]; |
|
963 uint32 size; |
|
964 uint len; |
|
965 |
|
966 // Read header |
|
967 if (fread(tmp, sizeof(tmp), 1, _sl.fh) != 1) SlError("file read failed"); |
|
968 |
|
969 // Check if size is bad |
|
970 ((uint32*)out)[0] = size = tmp[1]; |
|
971 |
|
972 if (_sl_version != 0) { |
|
973 tmp[0] = TO_BE32(tmp[0]); |
|
974 size = TO_BE32(size); |
|
975 } |
|
976 |
|
977 if (size >= sizeof(out)) SlError("inconsistent size"); |
|
978 |
|
979 // Read block |
|
980 if (fread(out + sizeof(uint32), size, 1, _sl.fh) != 1) SlError("file read failed"); |
|
981 |
|
982 // Verify checksum |
|
983 if (tmp[0] != lzo_adler32(0, out, size + sizeof(uint32))) SlError("bad checksum"); |
|
984 |
|
985 // Decompress |
|
986 lzo1x_decompress(out + sizeof(uint32)*1, size, _sl.buf, &len, NULL); |
|
987 return len; |
|
988 } |
|
989 |
|
990 // p contains the pointer to the buffer, len contains the pointer to the length. |
|
991 // len bytes will be written, p and l will be updated to reflect the next buffer. |
|
992 static void WriteLZO(uint size) |
|
993 { |
|
994 byte out[LZO_SIZE + LZO_SIZE / 64 + 16 + 3 + 8]; |
|
995 byte wrkmem[sizeof(byte*)*4096]; |
|
996 uint outlen; |
|
997 |
|
998 lzo1x_1_compress(_sl.buf, size, out + sizeof(uint32)*2, &outlen, wrkmem); |
|
999 ((uint32*)out)[1] = TO_BE32(outlen); |
|
1000 ((uint32*)out)[0] = TO_BE32(lzo_adler32(0, out + sizeof(uint32), outlen + sizeof(uint32))); |
|
1001 if (fwrite(out, outlen + sizeof(uint32)*2, 1, _sl.fh) != 1) SlError("file write failed"); |
|
1002 } |
|
1003 |
|
1004 static bool InitLZO(void) |
|
1005 { |
|
1006 _sl.bufsize = LZO_SIZE; |
|
1007 _sl.buf = _sl.buf_ori = (byte*)malloc(LZO_SIZE); |
|
1008 return true; |
|
1009 } |
|
1010 |
|
1011 static void UninitLZO(void) |
|
1012 { |
|
1013 free(_sl.buf_ori); |
|
1014 } |
|
1015 |
|
1016 //********************************************* |
|
1017 //******** START OF NOCOMP CODE (uncompressed)* |
|
1018 //********************************************* |
|
1019 static uint ReadNoComp(void) |
|
1020 { |
|
1021 return fread(_sl.buf, 1, LZO_SIZE, _sl.fh); |
|
1022 } |
|
1023 |
|
1024 static void WriteNoComp(uint size) |
|
1025 { |
|
1026 fwrite(_sl.buf, 1, size, _sl.fh); |
|
1027 } |
|
1028 |
|
1029 static bool InitNoComp(void) |
|
1030 { |
|
1031 _sl.bufsize = LZO_SIZE; |
|
1032 _sl.buf = _sl.buf_ori =(byte*)malloc(LZO_SIZE); |
|
1033 return true; |
|
1034 } |
|
1035 |
|
1036 static void UninitNoComp(void) |
|
1037 { |
|
1038 free(_sl.buf_ori); |
|
1039 } |
|
1040 |
|
1041 //******************************************** |
|
1042 //********** START OF MEMORY CODE (in ram)**** |
|
1043 //******************************************** |
|
1044 |
|
1045 #include "table/strings.h" |
|
1046 #include "table/sprites.h" |
|
1047 #include "gfx.h" |
|
1048 #include "gui.h" |
|
1049 |
|
1050 typedef struct ThreadedSave { |
|
1051 uint count; |
|
1052 bool ff_state; |
|
1053 bool saveinprogress; |
|
1054 CursorID cursor; |
|
1055 } ThreadedSave; |
|
1056 |
|
1057 /* A maximum size of of 128K * 500 = 64.000KB savegames */ |
|
1058 STATIC_OLD_POOL(Savegame, byte, 17, 500, NULL, NULL) |
|
1059 static ThreadedSave _ts; |
|
1060 |
|
1061 static bool InitMem(void) |
|
1062 { |
|
1063 _ts.count = 0; |
|
1064 |
|
1065 CleanPool(&_Savegame_pool); |
|
1066 AddBlockToPool(&_Savegame_pool); |
|
1067 |
|
1068 /* A block from the pool is a contigious area of memory, so it is safe to write to it sequentially */ |
|
1069 _sl.bufsize = GetSavegamePoolSize(); |
|
1070 _sl.buf = GetSavegame(_ts.count); |
|
1071 return true; |
|
1072 } |
|
1073 |
|
1074 static void UnInitMem(void) |
|
1075 { |
|
1076 CleanPool(&_Savegame_pool); |
|
1077 } |
|
1078 |
|
1079 static void WriteMem(uint size) |
|
1080 { |
|
1081 _ts.count += size; |
|
1082 /* Allocate new block and new buffer-pointer */ |
|
1083 AddBlockIfNeeded(&_Savegame_pool, _ts.count); |
|
1084 _sl.buf = GetSavegame(_ts.count); |
|
1085 } |
|
1086 |
|
1087 //******************************************** |
|
1088 //********** START OF ZLIB CODE ************** |
|
1089 //******************************************** |
|
1090 |
|
1091 #if defined(WITH_ZLIB) |
|
1092 #include <zlib.h> |
|
1093 |
|
1094 static z_stream _z; |
|
1095 |
|
1096 static bool InitReadZlib(void) |
|
1097 { |
|
1098 memset(&_z, 0, sizeof(_z)); |
|
1099 if (inflateInit(&_z) != Z_OK) return false; |
|
1100 |
|
1101 _sl.bufsize = 4096; |
|
1102 _sl.buf = _sl.buf_ori = (byte*)malloc(4096 + 4096); // also contains fread buffer |
|
1103 return true; |
|
1104 } |
|
1105 |
|
1106 static uint ReadZlib(void) |
|
1107 { |
|
1108 int r; |
|
1109 |
|
1110 _z.next_out = _sl.buf; |
|
1111 _z.avail_out = 4096; |
|
1112 |
|
1113 do { |
|
1114 // read more bytes from the file? |
|
1115 if (_z.avail_in == 0) { |
|
1116 _z.avail_in = fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh); |
|
1117 } |
|
1118 |
|
1119 // inflate the data |
|
1120 r = inflate(&_z, 0); |
|
1121 if (r == Z_STREAM_END) |
|
1122 break; |
|
1123 |
|
1124 if (r != Z_OK) |
|
1125 SlError("inflate() failed"); |
|
1126 } while (_z.avail_out); |
|
1127 |
|
1128 return 4096 - _z.avail_out; |
|
1129 } |
|
1130 |
|
1131 static void UninitReadZlib(void) |
|
1132 { |
|
1133 inflateEnd(&_z); |
|
1134 free(_sl.buf_ori); |
|
1135 } |
|
1136 |
|
1137 static bool InitWriteZlib(void) |
|
1138 { |
|
1139 memset(&_z, 0, sizeof(_z)); |
|
1140 if (deflateInit(&_z, 6) != Z_OK) return false; |
|
1141 |
|
1142 _sl.bufsize = 4096; |
|
1143 _sl.buf = _sl.buf_ori = (byte*)malloc(4096); // also contains fread buffer |
|
1144 return true; |
|
1145 } |
|
1146 |
|
1147 static void WriteZlibLoop(z_streamp z, byte *p, uint len, int mode) |
|
1148 { |
|
1149 byte buf[1024]; // output buffer |
|
1150 int r; |
|
1151 uint n; |
|
1152 z->next_in = p; |
|
1153 z->avail_in = len; |
|
1154 do { |
|
1155 z->next_out = buf; |
|
1156 z->avail_out = sizeof(buf); |
|
1157 r = deflate(z, mode); |
|
1158 // bytes were emitted? |
|
1159 if ((n=sizeof(buf) - z->avail_out) != 0) { |
|
1160 if (fwrite(buf, n, 1, _sl.fh) != 1) SlError("file write error"); |
|
1161 } |
|
1162 if (r == Z_STREAM_END) |
|
1163 break; |
|
1164 if (r != Z_OK) SlError("zlib returned error code"); |
|
1165 } while (z->avail_in || !z->avail_out); |
|
1166 } |
|
1167 |
|
1168 static void WriteZlib(uint len) |
|
1169 { |
|
1170 WriteZlibLoop(&_z, _sl.buf, len, 0); |
|
1171 } |
|
1172 |
|
1173 static void UninitWriteZlib(void) |
|
1174 { |
|
1175 // flush any pending output. |
|
1176 if (_sl.fh) WriteZlibLoop(&_z, NULL, 0, Z_FINISH); |
|
1177 deflateEnd(&_z); |
|
1178 free(_sl.buf_ori); |
|
1179 } |
|
1180 |
|
1181 #endif /* WITH_ZLIB */ |
|
1182 |
|
1183 //******************************************* |
|
1184 //************* END OF CODE ***************** |
|
1185 //******************************************* |
|
1186 |
|
1187 // these define the chunks |
|
1188 extern const ChunkHandler _misc_chunk_handlers[]; |
|
1189 extern const ChunkHandler _setting_chunk_handlers[]; |
|
1190 extern const ChunkHandler _player_chunk_handlers[]; |
|
1191 extern const ChunkHandler _engine_chunk_handlers[]; |
|
1192 extern const ChunkHandler _veh_chunk_handlers[]; |
|
1193 extern const ChunkHandler _waypoint_chunk_handlers[]; |
|
1194 extern const ChunkHandler _depot_chunk_handlers[]; |
|
1195 extern const ChunkHandler _order_chunk_handlers[]; |
|
1196 extern const ChunkHandler _town_chunk_handlers[]; |
|
1197 extern const ChunkHandler _sign_chunk_handlers[]; |
|
1198 extern const ChunkHandler _station_chunk_handlers[]; |
|
1199 extern const ChunkHandler _industry_chunk_handlers[]; |
|
1200 extern const ChunkHandler _economy_chunk_handlers[]; |
|
1201 extern const ChunkHandler _animated_tile_chunk_handlers[]; |
|
1202 extern const ChunkHandler _newgrf_chunk_handlers[]; |
|
1203 |
|
1204 static const ChunkHandler * const _chunk_handlers[] = { |
|
1205 _misc_chunk_handlers, |
|
1206 _setting_chunk_handlers, |
|
1207 _veh_chunk_handlers, |
|
1208 _waypoint_chunk_handlers, |
|
1209 _depot_chunk_handlers, |
|
1210 _order_chunk_handlers, |
|
1211 _industry_chunk_handlers, |
|
1212 _economy_chunk_handlers, |
|
1213 _engine_chunk_handlers, |
|
1214 _town_chunk_handlers, |
|
1215 _sign_chunk_handlers, |
|
1216 _station_chunk_handlers, |
|
1217 _player_chunk_handlers, |
|
1218 _animated_tile_chunk_handlers, |
|
1219 _newgrf_chunk_handlers, |
|
1220 NULL, |
|
1221 }; |
|
1222 |
|
1223 // used to include a vehicle desc in another desc. |
|
1224 extern const SaveLoad _common_veh_desc[]; |
|
1225 static const SaveLoad* const _desc_includes[] = { |
|
1226 _common_veh_desc |
|
1227 }; |
|
1228 |
|
1229 /** |
|
1230 * Pointers cannot be saved to a savegame, so this functions gets |
|
1231 * the index of the item, and if not available, it hussles with |
|
1232 * pointers (looks really bad :() |
|
1233 * Remember that a NULL item has value 0, and all |
|
1234 * indeces have +1, so vehicle 0 is saved as index 1. |
|
1235 * @param obj The object that we want to get the index of |
|
1236 * @param rt @SLRefType type of the object the index is being sought of |
|
1237 * @return Return the pointer converted to an index of the type pointed to |
|
1238 */ |
|
1239 static uint ReferenceToInt(const void *obj, SLRefType rt) |
|
1240 { |
|
1241 if (obj == NULL) return 0; |
|
1242 |
|
1243 switch (rt) { |
|
1244 case REF_VEHICLE_OLD: // Old vehicles we save as new onces |
|
1245 case REF_VEHICLE: return ((const Vehicle*)obj)->index + 1; |
|
1246 case REF_STATION: return ((const Station*)obj)->index + 1; |
|
1247 case REF_TOWN: return ((const Town*)obj)->index + 1; |
|
1248 case REF_ORDER: return ((const Order*)obj)->index + 1; |
|
1249 case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1; |
|
1250 case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1; |
|
1251 default: NOT_REACHED(); |
|
1252 } |
|
1253 |
|
1254 return 0; // avoid compiler warning |
|
1255 } |
|
1256 |
|
1257 /** |
|
1258 * Pointers cannot be loaded from a savegame, so this function |
|
1259 * gets the index from the savegame and returns the appropiate |
|
1260 * pointer from the already loaded base. |
|
1261 * Remember that an index of 0 is a NULL pointer so all indeces |
|
1262 * are +1 so vehicle 0 is saved as 1. |
|
1263 * @param index The index that is being converted to a pointer |
|
1264 * @param rt @SLRefType type of the object the pointer is sought of |
|
1265 * @return Return the index converted to a pointer of any type |
|
1266 */ |
|
1267 static void *IntToReference(uint index, SLRefType rt) |
|
1268 { |
|
1269 /* After version 4.3 REF_VEHICLE_OLD is saved as REF_VEHICLE, |
|
1270 * and should be loaded like that */ |
|
1271 if (rt == REF_VEHICLE_OLD && !CheckSavegameVersionOldStyle(4, 4)) |
|
1272 rt = REF_VEHICLE; |
|
1273 |
|
1274 /* No need to look up NULL pointers, just return immediately */ |
|
1275 if (rt != REF_VEHICLE_OLD && index == 0) |
|
1276 return NULL; |
|
1277 |
|
1278 index--; // correct for the NULL index |
|
1279 |
|
1280 switch (rt) { |
|
1281 case REF_ORDER: { |
|
1282 if (!AddBlockIfNeeded(&_Order_pool, index)) |
|
1283 error("Orders: failed loading savegame: too many orders"); |
|
1284 return GetOrder(index); |
|
1285 } |
|
1286 case REF_VEHICLE: { |
|
1287 if (!AddBlockIfNeeded(&_Vehicle_pool, index)) |
|
1288 error("Vehicles: failed loading savegame: too many vehicles"); |
|
1289 return GetVehicle(index); |
|
1290 } |
|
1291 case REF_STATION: { |
|
1292 if (!AddBlockIfNeeded(&_Station_pool, index)) |
|
1293 error("Stations: failed loading savegame: too many stations"); |
|
1294 return GetStation(index); |
|
1295 } |
|
1296 case REF_TOWN: { |
|
1297 if (!AddBlockIfNeeded(&_Town_pool, index)) |
|
1298 error("Towns: failed loading savegame: too many towns"); |
|
1299 return GetTown(index); |
|
1300 } |
|
1301 case REF_ROADSTOPS: { |
|
1302 if (!AddBlockIfNeeded(&_RoadStop_pool, index)) |
|
1303 error("RoadStops: failed loading savegame: too many RoadStops"); |
|
1304 return GetRoadStop(index); |
|
1305 } |
|
1306 case REF_ENGINE_RENEWS: { |
|
1307 if (!AddBlockIfNeeded(&_EngineRenew_pool, index)) |
|
1308 error("EngineRenews: failed loading savegame: too many EngineRenews"); |
|
1309 return GetEngineRenew(index); |
|
1310 } |
|
1311 |
|
1312 case REF_VEHICLE_OLD: { |
|
1313 /* Old vehicles were saved differently: |
|
1314 * invalid vehicle was 0xFFFF, |
|
1315 * and the index was not - 1.. correct for this */ |
|
1316 index++; |
|
1317 if (index == INVALID_VEHICLE) |
|
1318 return NULL; |
|
1319 |
|
1320 if (!AddBlockIfNeeded(&_Vehicle_pool, index)) |
|
1321 error("Vehicles: failed loading savegame: too many vehicles"); |
|
1322 return GetVehicle(index); |
|
1323 } |
|
1324 default: NOT_REACHED(); |
|
1325 } |
|
1326 |
|
1327 return NULL; |
|
1328 } |
|
1329 |
|
1330 /** The format for a reader/writer type of a savegame */ |
|
1331 typedef struct { |
|
1332 const char *name; /// name of the compressor/decompressor (debug-only) |
|
1333 uint32 tag; /// the 4-letter tag by which it is identified in the savegame |
|
1334 |
|
1335 bool (*init_read)(void); /// function executed upon initalization of the loader |
|
1336 ReaderProc *reader; /// function that loads the data from the file |
|
1337 void (*uninit_read)(void); /// function executed when reading is finished |
|
1338 |
|
1339 bool (*init_write)(void); /// function executed upon intialization of the saver |
|
1340 WriterProc *writer; /// function that saves the data to the file |
|
1341 void (*uninit_write)(void); /// function executed when writing is done |
|
1342 } SaveLoadFormat; |
|
1343 |
|
1344 static const SaveLoadFormat _saveload_formats[] = { |
|
1345 {"memory", 0, NULL, NULL, NULL, InitMem, WriteMem, UnInitMem}, |
|
1346 {"lzo", TO_BE32X('OTTD'), InitLZO, ReadLZO, UninitLZO, InitLZO, WriteLZO, UninitLZO}, |
|
1347 {"none", TO_BE32X('OTTN'), InitNoComp, ReadNoComp, UninitNoComp, InitNoComp, WriteNoComp, UninitNoComp}, |
|
1348 #if defined(WITH_ZLIB) |
|
1349 {"zlib", TO_BE32X('OTTZ'), InitReadZlib, ReadZlib, UninitReadZlib, InitWriteZlib, WriteZlib, UninitWriteZlib}, |
|
1350 #else |
|
1351 {"zlib", TO_BE32X('OTTZ'), NULL, NULL, NULL, NULL, NULL, NULL}, |
|
1352 #endif |
|
1353 }; |
|
1354 |
|
1355 /** |
|
1356 * Return the savegameformat of the game. Whether it was create with ZLIB compression |
|
1357 * uncompressed, or another type |
|
1358 * @param s Name of the savegame format. If NULL it picks the first available one |
|
1359 * @return Pointer to @SaveLoadFormat struct giving all characteristics of this type of savegame |
|
1360 */ |
|
1361 static const SaveLoadFormat *GetSavegameFormat(const char *s) |
|
1362 { |
|
1363 const SaveLoadFormat *def = endof(_saveload_formats) - 1; |
|
1364 |
|
1365 // find default savegame format, the highest one with which files can be written |
|
1366 while (!def->init_write) def--; |
|
1367 |
|
1368 if (s != NULL && s[0] != '\0') { |
|
1369 const SaveLoadFormat *slf; |
|
1370 for (slf = &_saveload_formats[0]; slf != endof(_saveload_formats); slf++) { |
|
1371 if (slf->init_write != NULL && strcmp(s, slf->name) == 0) |
|
1372 return slf; |
|
1373 } |
|
1374 |
|
1375 ShowInfoF("Savegame format '%s' is not available. Reverting to '%s'.", s, def->name); |
|
1376 } |
|
1377 return def; |
|
1378 } |
|
1379 |
|
1380 // actual loader/saver function |
|
1381 void InitializeGame(int mode, uint size_x, uint size_y); |
|
1382 extern bool AfterLoadGame(void); |
|
1383 extern void BeforeSaveGame(void); |
|
1384 extern bool LoadOldSaveGame(const char *file); |
|
1385 |
|
1386 /** Small helper function to close the to be loaded savegame an signal error */ |
|
1387 static inline SaveOrLoadResult AbortSaveLoad(void) |
|
1388 { |
|
1389 if (_sl.fh != NULL) fclose(_sl.fh); |
|
1390 |
|
1391 _sl.fh = NULL; |
|
1392 return SL_ERROR; |
|
1393 } |
|
1394 |
|
1395 /** Update the gui accordingly when starting saving |
|
1396 * and set locks on saveload. Also turn off fast-forward cause with that |
|
1397 * saving takes Aaaaages */ |
|
1398 void SaveFileStart(void) |
|
1399 { |
|
1400 _ts.ff_state = _fast_forward; |
|
1401 _fast_forward = false; |
|
1402 if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ); |
|
1403 |
|
1404 SendWindowMessage(WC_STATUS_BAR, 0, true, 0, 0); |
|
1405 _ts.saveinprogress = true; |
|
1406 } |
|
1407 |
|
1408 /** Update the gui accordingly when saving is done and release locks |
|
1409 * on saveload */ |
|
1410 void SaveFileDone(void) |
|
1411 { |
|
1412 _fast_forward = _ts.ff_state; |
|
1413 if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE); |
|
1414 |
|
1415 SendWindowMessage(WC_STATUS_BAR, 0, false, 0, 0); |
|
1416 _ts.saveinprogress = false; |
|
1417 } |
|
1418 |
|
1419 /** Show a gui message when saving has failed */ |
|
1420 void SaveFileError(void) |
|
1421 { |
|
1422 ShowErrorMessage(STR_4007_GAME_SAVE_FAILED, STR_NULL, 0, 0); |
|
1423 SaveFileDone(); |
|
1424 } |
|
1425 |
|
1426 static OTTDThread* save_thread; |
|
1427 |
|
1428 /** We have written the whole game into memory, _Savegame_pool, now find |
|
1429 * and appropiate compressor and start writing to file. |
|
1430 */ |
|
1431 static void* SaveFileToDisk(void *arg) |
|
1432 { |
|
1433 const SaveLoadFormat *fmt; |
|
1434 uint32 hdr[2]; |
|
1435 |
|
1436 /* XXX - Setup setjmp error handler if an error occurs anywhere deep during |
|
1437 * loading/saving execute a longjmp() and continue execution here */ |
|
1438 if (setjmp(_sl.excpt)) { |
|
1439 AbortSaveLoad(); |
|
1440 _sl.excpt_uninit(); |
|
1441 |
|
1442 fprintf(stderr, "Save game failed: %s.", _sl.excpt_msg); |
|
1443 if (arg != NULL) { |
|
1444 OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_ERROR); |
|
1445 } else { |
|
1446 SaveFileError(); |
|
1447 } |
|
1448 return NULL; |
|
1449 } |
|
1450 |
|
1451 fmt = GetSavegameFormat(_savegame_format); |
|
1452 |
|
1453 /* We have written our stuff to memory, now write it to file! */ |
|
1454 hdr[0] = fmt->tag; |
|
1455 hdr[1] = TO_BE32(SAVEGAME_VERSION << 16); |
|
1456 if (fwrite(hdr, sizeof(hdr), 1, _sl.fh) != 1) SlError("file write failed"); |
|
1457 |
|
1458 if (!fmt->init_write()) SlError("cannot initialize compressor"); |
|
1459 |
|
1460 { |
|
1461 uint i; |
|
1462 uint count = 1 << Savegame_POOL_BLOCK_SIZE_BITS; |
|
1463 |
|
1464 assert(_ts.count == _sl.offs_base); |
|
1465 for (i = 0; i != _Savegame_pool.current_blocks - 1; i++) { |
|
1466 _sl.buf = _Savegame_pool.blocks[i]; |
|
1467 fmt->writer(count); |
|
1468 } |
|
1469 |
|
1470 /* The last block is (almost) always not fully filled, so only write away |
|
1471 * as much data as it is in there */ |
|
1472 _sl.buf = _Savegame_pool.blocks[i]; |
|
1473 fmt->writer(_ts.count - (i * count)); |
|
1474 } |
|
1475 |
|
1476 fmt->uninit_write(); |
|
1477 assert(_ts.count == _sl.offs_base); |
|
1478 GetSavegameFormat("memory")->uninit_write(); // clean the memorypool |
|
1479 fclose(_sl.fh); |
|
1480 |
|
1481 if (arg != NULL) OTTD_SendThreadMessage(MSG_OTTD_SAVETHREAD_DONE); |
|
1482 return NULL; |
|
1483 } |
|
1484 |
|
1485 void WaitTillSaved(void) |
|
1486 { |
|
1487 OTTDJoinThread(save_thread); |
|
1488 save_thread = NULL; |
|
1489 } |
|
1490 |
|
1491 /** |
|
1492 * Main Save or Load function where the high-level saveload functions are |
|
1493 * handled. It opens the savegame, selects format and checks versions |
|
1494 * @param filename The name of the savegame being created/loaded |
|
1495 * @param mode Save or load. Load can also be a TTD(Patch) game. Use SL_LOAD, SL_OLD_LOAD or SL_SAVE |
|
1496 * @return Return the results of the action. SL_OK, SL_ERROR or SL_REINIT ("unload" the game) |
|
1497 */ |
|
1498 SaveOrLoadResult SaveOrLoad(const char *filename, int mode) |
|
1499 { |
|
1500 uint32 hdr[2]; |
|
1501 const SaveLoadFormat *fmt; |
|
1502 |
|
1503 /* An instance of saving is already active, so don't go saving again */ |
|
1504 if (_ts.saveinprogress && mode == SL_SAVE) { |
|
1505 // if not an autosave, but a user action, show error message |
|
1506 if (!_do_autosave) ShowErrorMessage(INVALID_STRING_ID, STR_SAVE_STILL_IN_PROGRESS, 0, 0); |
|
1507 return SL_OK; |
|
1508 } |
|
1509 WaitTillSaved(); |
|
1510 |
|
1511 /* Load a TTDLX or TTDPatch game */ |
|
1512 if (mode == SL_OLD_LOAD) { |
|
1513 InitializeGame(IG_DATE_RESET, 256, 256); // set a mapsize of 256x256 for TTDPatch games or it might get confused |
|
1514 if (!LoadOldSaveGame(filename)) return SL_REINIT; |
|
1515 _sl_version = 0; |
|
1516 AfterLoadGame(); |
|
1517 return SL_OK; |
|
1518 } |
|
1519 |
|
1520 _sl.fh = (mode == SL_SAVE) ? fopen(filename, "wb") : fopen(filename, "rb"); |
|
1521 if (_sl.fh == NULL) { |
|
1522 DEBUG(sl, 0, "Cannot open savegame '%s' for saving/loading.", filename); |
|
1523 return SL_ERROR; |
|
1524 } |
|
1525 |
|
1526 _sl.bufe = _sl.bufp = NULL; |
|
1527 _sl.offs_base = 0; |
|
1528 _sl.save = mode; |
|
1529 _sl.includes = _desc_includes; |
|
1530 _sl.chs = _chunk_handlers; |
|
1531 |
|
1532 /* XXX - Setup setjmp error handler if an error occurs anywhere deep during |
|
1533 * loading/saving execute a longjmp() and continue execution here */ |
|
1534 if (setjmp(_sl.excpt)) { |
|
1535 AbortSaveLoad(); |
|
1536 |
|
1537 // deinitialize compressor. |
|
1538 _sl.excpt_uninit(); |
|
1539 |
|
1540 /* A saver/loader exception!! reinitialize all variables to prevent crash! */ |
|
1541 if (mode == SL_LOAD) { |
|
1542 ShowInfoF("Load game failed: %s.", _sl.excpt_msg); |
|
1543 return SL_REINIT; |
|
1544 } |
|
1545 |
|
1546 ShowInfoF("Save game failed: %s.", _sl.excpt_msg); |
|
1547 return SL_ERROR; |
|
1548 } |
|
1549 |
|
1550 /* General tactic is to first save the game to memory, then use an available writer |
|
1551 * to write it to file, either in threaded mode if possible, or single-threaded */ |
|
1552 if (mode == SL_SAVE) { /* SAVE game */ |
|
1553 fmt = GetSavegameFormat("memory"); // write to memory |
|
1554 |
|
1555 _sl.write_bytes = fmt->writer; |
|
1556 _sl.excpt_uninit = fmt->uninit_write; |
|
1557 if (!fmt->init_write()) { |
|
1558 DEBUG(sl, 0, "Initializing writer '%s' failed.", fmt->name); |
|
1559 return AbortSaveLoad(); |
|
1560 } |
|
1561 |
|
1562 _sl_version = SAVEGAME_VERSION; |
|
1563 |
|
1564 BeforeSaveGame(); |
|
1565 SlSaveChunks(); |
|
1566 SlWriteFill(); // flush the save buffer |
|
1567 |
|
1568 SaveFileStart(); |
|
1569 if (_network_server || |
|
1570 (save_thread = OTTDCreateThread(&SaveFileToDisk, (void*)"")) == NULL) { |
|
1571 DEBUG(sl, 1, "Cannot create savegame thread, reverting to single-threaded mode..."); |
|
1572 SaveFileToDisk(NULL); |
|
1573 SaveFileDone(); |
|
1574 } |
|
1575 |
|
1576 } else { /* LOAD game */ |
|
1577 assert(mode == SL_LOAD); |
|
1578 |
|
1579 if (fread(hdr, sizeof(hdr), 1, _sl.fh) != 1) { |
|
1580 DEBUG(sl, 0, "Cannot read savegame header, aborting"); |
|
1581 return AbortSaveLoad(); |
|
1582 } |
|
1583 |
|
1584 // see if we have any loader for this type. |
|
1585 for (fmt = _saveload_formats; ; fmt++) { |
|
1586 /* No loader found, treat as version 0 and use LZO format */ |
|
1587 if (fmt == endof(_saveload_formats)) { |
|
1588 DEBUG(sl, 0, "Unknown savegame type, trying to load it as the buggy format"); |
|
1589 rewind(_sl.fh); |
|
1590 _sl_version = 0; |
|
1591 _sl_minor_version = 0; |
|
1592 fmt = _saveload_formats + 1; // LZO |
|
1593 break; |
|
1594 } |
|
1595 |
|
1596 if (fmt->tag == hdr[0]) { |
|
1597 // check version number |
|
1598 _sl_version = TO_BE32(hdr[1]) >> 16; |
|
1599 /* Minor is not used anymore from version 18.0, but it is still needed |
|
1600 * in versions before that (4 cases) which can't be removed easy. |
|
1601 * Therefor it is loaded, but never saved (or, it saves a 0 in any scenario). |
|
1602 * So never EVER use this minor version again. -- TrueLight -- 22-11-2005 */ |
|
1603 _sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF; |
|
1604 |
|
1605 DEBUG(sl, 1, "Loading savegame version %d", _sl_version); |
|
1606 |
|
1607 /* Is the version higher than the current? */ |
|
1608 if (_sl_version > SAVEGAME_VERSION) { |
|
1609 DEBUG(sl, 0, "Savegame version invalid"); |
|
1610 return AbortSaveLoad(); |
|
1611 } |
|
1612 break; |
|
1613 } |
|
1614 } |
|
1615 |
|
1616 _sl.read_bytes = fmt->reader; |
|
1617 _sl.excpt_uninit = fmt->uninit_read; |
|
1618 |
|
1619 // loader for this savegame type is not implemented? |
|
1620 if (fmt->init_read == NULL) { |
|
1621 ShowInfoF("Loader for '%s' is not available.", fmt->name); |
|
1622 return AbortSaveLoad(); |
|
1623 } |
|
1624 |
|
1625 if (!fmt->init_read()) { |
|
1626 DEBUG(sl, 0, "Initializing loader '%s' failed", fmt->name); |
|
1627 return AbortSaveLoad(); |
|
1628 } |
|
1629 |
|
1630 /* Old maps were hardcoded to 256x256 and thus did not contain |
|
1631 * any mapsize information. Pre-initialize to 256x256 to not to |
|
1632 * confuse old games */ |
|
1633 InitializeGame(IG_DATE_RESET, 256, 256); |
|
1634 |
|
1635 SlLoadChunks(); |
|
1636 fmt->uninit_read(); |
|
1637 fclose(_sl.fh); |
|
1638 |
|
1639 /* After loading fix up savegame for any internal changes that |
|
1640 * might've occured since then. If it fails, load back the old game */ |
|
1641 if (!AfterLoadGame()) return SL_REINIT; |
|
1642 } |
|
1643 |
|
1644 return SL_OK; |
|
1645 } |
|
1646 |
|
1647 /** Do a save when exiting the game (patch option) _patches.autosave_on_exit */ |
|
1648 void DoExitSave(void) |
|
1649 { |
|
1650 char buf[200]; |
|
1651 snprintf(buf, sizeof(buf), "%s%sexit.sav", _paths.autosave_dir, PATHSEP); |
|
1652 SaveOrLoad(buf, SL_SAVE); |
|
1653 } |
|
1654 |
|
1655 #if 0 |
|
1656 /** |
|
1657 * Function to get the type of the savegame by looking at the file header. |
|
1658 * NOTICE: Not used right now, but could be used if extensions of savegames are garbled |
|
1659 * @param file Savegame to be checked |
|
1660 * @return SL_OLD_LOAD or SL_LOAD of the file |
|
1661 */ |
|
1662 int GetSavegameType(char *file) |
|
1663 { |
|
1664 const SaveLoadFormat *fmt; |
|
1665 uint32 hdr; |
|
1666 FILE *f; |
|
1667 int mode = SL_OLD_LOAD; |
|
1668 |
|
1669 f = fopen(file, "rb"); |
|
1670 if (fread(&hdr, sizeof(hdr), 1, f) != 1) { |
|
1671 DEBUG(sl, 0, "Savegame is obsolete or invalid format"); |
|
1672 mode = SL_LOAD; // don't try to get filename, just show name as it is written |
|
1673 } else { |
|
1674 // see if we have any loader for this type. |
|
1675 for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) { |
|
1676 if (fmt->tag == hdr) { |
|
1677 mode = SL_LOAD; // new type of savegame |
|
1678 break; |
|
1679 } |
|
1680 } |
|
1681 } |
|
1682 |
|
1683 fclose(f); |
|
1684 return mode; |
|
1685 } |
|
1686 #endif |