Merge branch 'save_ext' into progsig-sx

Conflicts:
	source.list
	src/saveload/saveload.cpp
This commit is contained in:
Jonathan G Rennison
2015-08-01 17:21:38 +01:00
21 changed files with 981 additions and 176 deletions

View File

@@ -48,9 +48,12 @@
#include "saveload_internal.h"
#include "saveload_filter.h"
#include "extended_ver_sl.h"
#include "../safeguards.h"
#include <vector>
/*
* Previous savegame versions, the trunk revision where they were
* introduced and the released version that had that particular
@@ -262,9 +265,9 @@
* 192 26700
* 193 26802
* 194 26881 1.5.x
* 200 Programmable Signals patch added
*/
extern const uint16 SAVEGAME_VERSION = 200; ///< Current savegame version of OpenTTD.
extern const uint16 SAVEGAME_VERSION = 194; ///< Current savegame version of OpenTTD.
const uint16 SAVEGAME_VERSION_EXT = 0x8000; ///< Savegame extension indicator mask
SavegameType _savegame_type; ///< type of savegame we are loading
@@ -274,6 +277,8 @@ byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
char _savegame_format[8]; ///< how to compress savegames
bool _do_autosave; ///< are we doing an autosave at the moment?
extern bool _sl_is_ext_version;
/** What are we currently doing? */
enum SaveLoadAction {
SLA_LOAD, ///< loading
@@ -415,6 +420,7 @@ struct SaveLoadParams {
static SaveLoadParams _sl; ///< Parameters used for/at saveload.
/* these define the chunks */
extern const ChunkHandler _version_ext_chunk_handlers[];
extern const ChunkHandler _gamelog_chunk_handlers[];
extern const ChunkHandler _map_chunk_handlers[];
extern const ChunkHandler _misc_chunk_handlers[];
@@ -452,6 +458,7 @@ extern const ChunkHandler _signal_chunk_handlers[];
/** Array of all chunks in a savegame, \c NULL terminated. */
static const ChunkHandler * const _chunk_handlers[] = {
_version_ext_chunk_handlers, // this should be first, such that it is saved first, as when loading it affects the loading of subsequent chunks
_gamelog_chunk_handlers,
_map_chunk_handlers,
_misc_chunk_handlers,
@@ -506,6 +513,7 @@ static void SlNullPointers()
* during NULLing; especially those that try to get
* pointers from other pools. */
_sl_version = SAVEGAME_VERSION;
SlXvSetCurrentState();
DEBUG(sl, 1, "Nulling pointers");
@@ -529,17 +537,22 @@ static void SlNullPointers()
* @note This function does never return as it throws an exception to
* break out of all the saveload code.
*/
void NORETURN SlError(StringID string, const char *extra_msg)
void NORETURN SlError(StringID string, const char *extra_msg, bool already_malloced)
{
char *str = NULL;
if (extra_msg != NULL) {
str = already_malloced ? const_cast<char *>(extra_msg) : stredup(extra_msg);
}
/* Distinguish between loading into _load_check_data vs. normal save/load. */
if (_sl.action == SLA_LOAD_CHECK) {
_load_check_data.error = string;
free(_load_check_data.error_data);
_load_check_data.error_data = (extra_msg == NULL) ? NULL : stredup(extra_msg);
_load_check_data.error_data = str;
} else {
_sl.error_str = string;
free(_sl.extra_msg);
_sl.extra_msg = (extra_msg == NULL) ? NULL : stredup(extra_msg);
_sl.extra_msg = str;
}
/* We have to NULL all pointers here; we might be in a state where
@@ -550,6 +563,18 @@ void NORETURN SlError(StringID string, const char *extra_msg)
throw std::exception();
}
/**
* As SlError, except that it takes a format string and additional parameters
*/
void CDECL NORETURN SlErrorFmt(StringID string, const char *msg, ...)
{
va_list va;
va_start(va, msg);
char *str = str_vfmt(msg, va);
va_end(va);
SlError(string, str, true);
}
/**
* Error handler for corrupt savegames. Sets everything up to show the
* error message and to clean up the mess of a partial savegame load.
@@ -557,9 +582,21 @@ void NORETURN SlError(StringID string, const char *extra_msg)
* @note This function does never return as it throws an exception to
* break out of all the saveload code.
*/
void NORETURN SlErrorCorrupt(const char *msg)
void NORETURN SlErrorCorrupt(const char *msg, bool already_malloced)
{
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, msg, already_malloced);
}
/**
* As SlErrorCorruptFmt, except that it takes a format string and additional parameters
*/
void CDECL NORETURN SlErrorCorruptFmt(const char *msg, ...)
{
va_list va;
va_start(va, msg);
char *str = str_vfmt(msg, va);
va_end(va);
SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME, str, true);
}
@@ -615,51 +652,24 @@ void SlWriteByte(byte b)
_sl.dumper->WriteByte(b);
}
static inline int SlReadUint16()
/**
* Returns number of bytes read so far
* May only be called during a load/load check action
*/
size_t SlGetBytesRead()
{
int x = SlReadByte() << 8;
return x | SlReadByte();
}
static inline uint32 SlReadUint32()
{
uint32 x = SlReadUint16() << 16;
return x | SlReadUint16();
}
static inline uint64 SlReadUint64()
{
uint32 x = SlReadUint32();
uint32 y = SlReadUint32();
return (uint64)x << 32 | y;
}
static inline void SlWriteUint16(uint16 v)
{
SlWriteByte(GB(v, 8, 8));
SlWriteByte(GB(v, 0, 8));
}
static inline void SlWriteUint32(uint32 v)
{
SlWriteUint16(GB(v, 16, 16));
SlWriteUint16(GB(v, 0, 16));
}
static inline void SlWriteUint64(uint64 x)
{
SlWriteUint32((uint32)(x >> 32));
SlWriteUint32((uint32)x);
assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
return _sl.reader->GetSize();
}
/**
* Read in bytes from the file/data structure but don't do
* anything with them, discarding them in effect
* @param length The amount of bytes that is being treated this way
* Returns number of bytes written so far
* May only be called during a save action
*/
static inline void SlSkipBytes(size_t length)
size_t SlGetBytesWritten()
{
for (; length != 0; length--) SlReadByte();
assert(_sl.action == SLA_SAVE);
return _sl.dumper->GetSize();
}
/**
@@ -1417,7 +1427,7 @@ static void SlList(void *list, SLRefType conv)
/** Are we going to save this object or not? */
static inline bool SlIsObjectValidInSavegame(const SaveLoad *sld)
{
if (_sl_version < sld->version_from || _sl_version > sld->version_to) return false;
if (!sld->ext_feature_test.IsFeaturePresent(_sl_version, sld->version_from, sld->version_to)) return false;
if (sld->conv & SLF_NOT_IN_SAVE) return false;
return true;
@@ -1695,7 +1705,7 @@ static void SlLoadChunk(const ChunkHandler *ch)
/**
* Load a chunk of data for checking savegames.
* If the chunkhandler is NULL, the chunk is skipped.
* @param ch The chunkhandler that will be used for the operation
* @param ch The chunkhandler that will be used for the operation, this may be NULL
*/
static void SlLoadCheckChunk(const ChunkHandler *ch)
{
@@ -1709,14 +1719,14 @@ static void SlLoadCheckChunk(const ChunkHandler *ch)
switch (m) {
case CH_ARRAY:
_sl.array_index = 0;
if (ch->load_check_proc) {
if (ch && ch->load_check_proc) {
ch->load_check_proc();
} else {
SlSkipArray();
}
break;
case CH_SPARSE_ARRAY:
if (ch->load_check_proc) {
if (ch && ch->load_check_proc) {
ch->load_check_proc();
} else {
SlSkipArray();
@@ -1729,7 +1739,7 @@ static void SlLoadCheckChunk(const ChunkHandler *ch)
len += SlReadUint16();
_sl.obj_len = len;
endoffs = _sl.reader->GetSize() + len;
if (ch->load_check_proc) {
if (ch && ch->load_check_proc) {
ch->load_check_proc();
} else {
SlSkipBytes(len);
@@ -1843,8 +1853,16 @@ static void SlLoadChunks()
DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
ch = SlFindChunkHandler(id);
if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
SlLoadChunk(ch);
if (ch == NULL) {
if (SlXvIsChunkDiscardable(id)) {
DEBUG(sl, 1, "Discarding chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
SlLoadCheckChunk(NULL);
} else {
SlErrorCorrupt("Unknown chunk type");
}
} else {
SlLoadChunk(ch);
}
}
}
@@ -1858,7 +1876,7 @@ static void SlLoadCheckChunks()
DEBUG(sl, 2, "Loading chunk %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
ch = SlFindChunkHandler(id);
if (ch == NULL) SlErrorCorrupt("Unknown chunk type");
if (ch == NULL && !SlXvIsChunkDiscardable(id)) SlErrorCorrupt("Unknown chunk type");
SlLoadCheckChunk(ch);
}
}
@@ -2514,7 +2532,7 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded)
const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
/* We have written our stuff to memory, now write it to file! */
uint32 hdr[2] = { fmt->tag, TO_BE32(SAVEGAME_VERSION << 16) };
uint32 hdr[2] = { fmt->tag, TO_BE32((SAVEGAME_VERSION | SAVEGAME_VERSION_EXT) << 16) };
_sl.sf->Write((byte*)hdr, sizeof(hdr));
_sl.sf = fmt->init_write(_sl.sf, compression);
@@ -2581,6 +2599,7 @@ static SaveOrLoadResult DoSave(SaveFilter *writer, bool threaded)
_sl.sf = writer;
_sl_version = SAVEGAME_VERSION;
SlXvSetCurrentState();
SaveViewportBeforeSaveGame();
SlSaveChunks();
@@ -2632,6 +2651,8 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
_load_check_data.checkable = true;
}
SlXvResetState();
uint32 hdr[2];
if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
@@ -2644,6 +2665,7 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
_sl.lf->Reset();
_sl_version = 0;
_sl_minor_version = 0;
SlXvResetState();
/* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
fmt = _saveload_formats;
@@ -2666,7 +2688,14 @@ static SaveOrLoadResult DoLoad(LoadFilter *reader, bool load_check)
* Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
_sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
DEBUG(sl, 1, "Loading savegame version %d", _sl_version);
if (_sl_version & SAVEGAME_VERSION_EXT) {
_sl_version &= ~SAVEGAME_VERSION_EXT;
_sl_is_ext_version = true;
} else {
SlXvCheckSpecialSavegameVersions();
}
DEBUG(sl, 1, "Loading savegame version %d%s", _sl_version, _sl_is_ext_version ? " (extended)" : "");
/* Is the version higher than the current? */
if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
@@ -2803,6 +2832,7 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo
if (!LoadOldSaveGame(filename)) return SL_REINIT;
_sl_version = 0;
_sl_minor_version = 0;
SlXvResetState();
GamelogStartAction(GLAT_LOAD);
if (!AfterLoadGame()) {
GamelogStopAction();