mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-12 18:40:29 +00:00
(svn r21373) -Codechange: rewrite the game state to memory dumper
This commit is contained in:
parent
22d0ce0366
commit
b250888aa3
@ -227,7 +227,6 @@ byte _sl_minor_version; ///< the minor savegame version, DO NOT USE!
|
|||||||
char _savegame_format[8]; ///< how to compress savegames
|
char _savegame_format[8]; ///< how to compress savegames
|
||||||
bool _do_autosave; ///< are we doing an autosave at the moment?
|
bool _do_autosave; ///< are we doing an autosave at the moment?
|
||||||
|
|
||||||
typedef void DumperProc(size_t len);
|
|
||||||
typedef void WriterProc(byte *buf, size_t len);
|
typedef void WriterProc(byte *buf, size_t len);
|
||||||
typedef size_t ReaderProc();
|
typedef size_t ReaderProc();
|
||||||
|
|
||||||
@ -246,6 +245,63 @@ enum NeedLength {
|
|||||||
NL_CALCLENGTH = 2, ///< need to calculate the length
|
NL_CALCLENGTH = 2, ///< need to calculate the length
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Save in chunks of 128 KiB. */
|
||||||
|
static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
|
||||||
|
|
||||||
|
/** Container for dumping the savegame (quickly) to memory. */
|
||||||
|
struct MemoryDumper {
|
||||||
|
AutoFreeSmallVector<byte *, 16> blocks; ///< Buffer with blocks of allocated memory.
|
||||||
|
byte *buf; ///< Buffer we're going to write to.
|
||||||
|
byte *bufe; ///< End of the buffer we write to.
|
||||||
|
|
||||||
|
/** Initialise our variables. */
|
||||||
|
MemoryDumper() : buf(NULL), bufe(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a single byte into the dumper.
|
||||||
|
* @param b The byte to write.
|
||||||
|
*/
|
||||||
|
FORCEINLINE void WriteByte(byte b)
|
||||||
|
{
|
||||||
|
/* Are we at the end of this chunk? */
|
||||||
|
if (this->buf == this->bufe) {
|
||||||
|
this->buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
|
||||||
|
*this->blocks.Append() = this->buf;
|
||||||
|
this->bufe = this->buf + MEMORY_CHUNK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*this->buf++ = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush this dumper into a writer.
|
||||||
|
* @param writer The writer we want to use.
|
||||||
|
*/
|
||||||
|
void Flush(WriterProc writer)
|
||||||
|
{
|
||||||
|
uint i = 0;
|
||||||
|
size_t t = this->GetSize();
|
||||||
|
|
||||||
|
while (t > 0) {
|
||||||
|
size_t to_write = min(MEMORY_CHUNK_SIZE, t);
|
||||||
|
|
||||||
|
writer(this->blocks[i++], to_write);
|
||||||
|
t -= to_write;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the memory dump made so far.
|
||||||
|
* @return The size.
|
||||||
|
*/
|
||||||
|
size_t GetSize()
|
||||||
|
{
|
||||||
|
return this->blocks.Length() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** The saveload struct, containing reader-writer functions, buffer, version, etc. */
|
/** The saveload struct, containing reader-writer functions, buffer, version, etc. */
|
||||||
struct SaveLoadParams {
|
struct SaveLoadParams {
|
||||||
SaveLoadAction action; ///< are we doing a save or a load atm.
|
SaveLoadAction action; ///< are we doing a save or a load atm.
|
||||||
@ -258,7 +314,7 @@ struct SaveLoadParams {
|
|||||||
|
|
||||||
size_t offs_base; ///< the offset in number of bytes since we started writing data (eg uncompressed savegame size)
|
size_t offs_base; ///< the offset in number of bytes since we started writing data (eg uncompressed savegame size)
|
||||||
|
|
||||||
DumperProc *dump_bytes; ///< savegame dumper function
|
MemoryDumper *dumper; ///< Memory dumper to write the savegame to.
|
||||||
ReaderProc *read_bytes; ///< savegame loader function
|
ReaderProc *read_bytes; ///< savegame loader function
|
||||||
|
|
||||||
/* When saving/loading savegames, they are always saved to a temporary memory-place
|
/* When saving/loading savegames, they are always saved to a temporary memory-place
|
||||||
@ -459,26 +515,6 @@ static inline size_t SlGetOffs()
|
|||||||
return _sl.offs_base - (_sl.bufe - _sl.bufp);
|
return _sl.offs_base - (_sl.bufe - _sl.bufp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Flush the output buffer by writing to disk with the given reader.
|
|
||||||
* If the buffer pointer has not yet been set up, set it up now. Usually
|
|
||||||
* only called when the buffer is full, or there is no more data to be processed
|
|
||||||
*/
|
|
||||||
static void SlWriteFill()
|
|
||||||
{
|
|
||||||
/* flush the buffer to disk (the writer) */
|
|
||||||
if (_sl.bufp != NULL) {
|
|
||||||
uint len = _sl.bufp - _sl.buf;
|
|
||||||
_sl.offs_base += len;
|
|
||||||
if (len) _sl.dump_bytes(len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All the data from the buffer has been written away, rewind to the beginning
|
|
||||||
* to start reading in more data */
|
|
||||||
_sl.bufp = _sl.buf;
|
|
||||||
_sl.bufe = _sl.buf + _sl.bufsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read in a single byte from file. If the temporary buffer is full,
|
* Read in a single byte from file. If the temporary buffer is full,
|
||||||
* flush it to its final destination
|
* flush it to its final destination
|
||||||
@ -494,19 +530,14 @@ static inline byte SlReadByteInternal()
|
|||||||
byte SlReadByte() {return SlReadByteInternal();}
|
byte SlReadByte() {return SlReadByteInternal();}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write away a single byte from memory. If the temporary buffer is full,
|
* Wrapper for writing a byte to the dumper.
|
||||||
* flush it to its destination (file)
|
* @param b The byte to write.
|
||||||
* @param b the byte that is currently written
|
|
||||||
*/
|
*/
|
||||||
static inline void SlWriteByteInternal(byte b)
|
void SlWriteByte(byte b)
|
||||||
{
|
{
|
||||||
if (_sl.bufp == _sl.bufe) SlWriteFill();
|
_sl.dumper->WriteByte(b);
|
||||||
*_sl.bufp++ = b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Wrapper for SlWriteByteInternal */
|
|
||||||
void SlWriteByte(byte b) {SlWriteByteInternal(b);}
|
|
||||||
|
|
||||||
static inline int SlReadUint16()
|
static inline int SlReadUint16()
|
||||||
{
|
{
|
||||||
int x = SlReadByte() << 8;
|
int x = SlReadByte() << 8;
|
||||||
@ -801,7 +832,7 @@ static void SlCopyBytes(void *ptr, size_t length)
|
|||||||
for (; length != 0; length--) *p++ = SlReadByteInternal();
|
for (; length != 0; length--) *p++ = SlReadByteInternal();
|
||||||
break;
|
break;
|
||||||
case SLA_SAVE:
|
case SLA_SAVE:
|
||||||
for (; length != 0; length--) SlWriteByteInternal(*p++);
|
for (; length != 0; length--) SlWriteByte(*p++);
|
||||||
break;
|
break;
|
||||||
default: NOT_REACHED();
|
default: NOT_REACHED();
|
||||||
}
|
}
|
||||||
@ -1456,12 +1487,12 @@ void SlAutolength(AutolengthProc *proc, void *arg)
|
|||||||
_sl.need_length = NL_WANTLENGTH;
|
_sl.need_length = NL_WANTLENGTH;
|
||||||
SlSetLength(_sl.obj_len);
|
SlSetLength(_sl.obj_len);
|
||||||
|
|
||||||
offs = SlGetOffs() + _sl.obj_len;
|
offs = _sl.dumper->GetSize() + _sl.obj_len;
|
||||||
|
|
||||||
/* And write the stuff */
|
/* And write the stuff */
|
||||||
proc(arg);
|
proc(arg);
|
||||||
|
|
||||||
if (offs != SlGetOffs()) SlErrorCorrupt("Invalid chunk size");
|
if (offs != _sl.dumper->GetSize()) SlErrorCorrupt("Invalid chunk size");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1799,48 +1830,16 @@ static void UninitNoComp()
|
|||||||
free(_sl.buf_ori);
|
free(_sl.buf_ori);
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************
|
|
||||||
********** START OF MEMORY CODE (in ram)****
|
|
||||||
********************************************/
|
|
||||||
|
|
||||||
#include "../gui.h"
|
#include "../gui.h"
|
||||||
|
|
||||||
struct ThreadedSave {
|
struct ThreadedSave {
|
||||||
uint count;
|
|
||||||
byte ff_state;
|
byte ff_state;
|
||||||
bool saveinprogress;
|
bool saveinprogress;
|
||||||
CursorID cursor;
|
CursorID cursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Save in chunks of 128 KiB. */
|
|
||||||
static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
|
|
||||||
/** Memory allocation for storing savegames in memory. */
|
|
||||||
static AutoFreeSmallVector<byte *, 16> _memory_savegame;
|
|
||||||
|
|
||||||
static ThreadedSave _ts;
|
static ThreadedSave _ts;
|
||||||
|
|
||||||
static void WriteMem(size_t size)
|
|
||||||
{
|
|
||||||
_ts.count += (uint)size;
|
|
||||||
|
|
||||||
_sl.buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
|
|
||||||
*_memory_savegame.Append() = _sl.buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UnInitMem()
|
|
||||||
{
|
|
||||||
_memory_savegame.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool InitMem()
|
|
||||||
{
|
|
||||||
_ts.count = 0;
|
|
||||||
_sl.bufsize = MEMORY_CHUNK_SIZE;
|
|
||||||
|
|
||||||
UnInitMem();
|
|
||||||
WriteMem(0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/********************************************
|
/********************************************
|
||||||
********** START OF ZLIB CODE **************
|
********** START OF ZLIB CODE **************
|
||||||
@ -2156,6 +2155,15 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
|
|||||||
extern bool AfterLoadGame();
|
extern bool AfterLoadGame();
|
||||||
extern bool LoadOldSaveGame(const char *file);
|
extern bool LoadOldSaveGame(const char *file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear/free the memory dumper.
|
||||||
|
*/
|
||||||
|
static inline void ClearMemoryDumper()
|
||||||
|
{
|
||||||
|
delete _sl.dumper;
|
||||||
|
_sl.dumper = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/** Small helper function to close the to be loaded savegame and signal error */
|
/** Small helper function to close the to be loaded savegame and signal error */
|
||||||
static inline SaveOrLoadResult AbortSaveLoad()
|
static inline SaveOrLoadResult AbortSaveLoad()
|
||||||
{
|
{
|
||||||
@ -2232,25 +2240,10 @@ static SaveOrLoadResult SaveFileToDisk(bool threaded)
|
|||||||
|
|
||||||
if (!fmt->init_write(compression)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
|
if (!fmt->init_write(compression)) SlError(STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR, "cannot initialize compressor");
|
||||||
|
|
||||||
uint i = 0;
|
_sl.dumper->Flush(fmt->writer);
|
||||||
size_t t = _ts.count;
|
ClearMemoryDumper();
|
||||||
|
|
||||||
if (_ts.count != _sl.offs_base) SlErrorCorrupt("Unexpected size of chunk");
|
|
||||||
while (t >= MEMORY_CHUNK_SIZE) {
|
|
||||||
fmt->writer(_memory_savegame[i++], MEMORY_CHUNK_SIZE);
|
|
||||||
t -= MEMORY_CHUNK_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t != 0) {
|
|
||||||
/* The last block is (almost) always not fully filled, so only write away
|
|
||||||
* as much data as it is in there */
|
|
||||||
assert(t == _ts.count % MEMORY_CHUNK_SIZE);
|
|
||||||
fmt->writer(_memory_savegame[i], t);
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt->uninit_write();
|
fmt->uninit_write();
|
||||||
if (_ts.count != _sl.offs_base) SlErrorCorrupt("Unexpected size of chunk");
|
|
||||||
UnInitMem();
|
|
||||||
fclose(_sl.fh);
|
fclose(_sl.fh);
|
||||||
|
|
||||||
if (threaded) SetAsyncSaveFinish(SaveFileDone);
|
if (threaded) SetAsyncSaveFinish(SaveFileDone);
|
||||||
@ -2364,15 +2357,13 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo
|
|||||||
if (mode == SL_SAVE) { // SAVE game
|
if (mode == SL_SAVE) { // SAVE game
|
||||||
DEBUG(desync, 1, "save: %08x; %02x; %s", _date, _date_fract, filename);
|
DEBUG(desync, 1, "save: %08x; %02x; %s", _date, _date_fract, filename);
|
||||||
|
|
||||||
_sl.dump_bytes = WriteMem;
|
_sl.dumper = new MemoryDumper();
|
||||||
_sl.excpt_uninit = UnInitMem;
|
_sl.excpt_uninit = ClearMemoryDumper;
|
||||||
InitMem();
|
|
||||||
|
|
||||||
_sl_version = SAVEGAME_VERSION;
|
_sl_version = SAVEGAME_VERSION;
|
||||||
|
|
||||||
SaveViewportBeforeSaveGame();
|
SaveViewportBeforeSaveGame();
|
||||||
SlSaveChunks();
|
SlSaveChunks();
|
||||||
SlWriteFill(); // flush the save buffer
|
|
||||||
|
|
||||||
SaveFileStart();
|
SaveFileStart();
|
||||||
if (_network_server || !_settings_client.gui.threaded_saves) threaded = false;
|
if (_network_server || !_settings_client.gui.threaded_saves) threaded = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user