mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-12 10:30:28 +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
|
||||
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 size_t ReaderProc();
|
||||
|
||||
@ -246,6 +245,63 @@ enum NeedLength {
|
||||
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. */
|
||||
struct SaveLoadParams {
|
||||
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)
|
||||
|
||||
DumperProc *dump_bytes; ///< savegame dumper function
|
||||
MemoryDumper *dumper; ///< Memory dumper to write the savegame to.
|
||||
ReaderProc *read_bytes; ///< savegame loader function
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* flush it to its final destination
|
||||
@ -494,19 +530,14 @@ static inline byte SlReadByteInternal()
|
||||
byte SlReadByte() {return SlReadByteInternal();}
|
||||
|
||||
/**
|
||||
* Write away a single byte from memory. If the temporary buffer is full,
|
||||
* flush it to its destination (file)
|
||||
* @param b the byte that is currently written
|
||||
* Wrapper for writing a byte to the dumper.
|
||||
* @param b The byte to write.
|
||||
*/
|
||||
static inline void SlWriteByteInternal(byte b)
|
||||
void SlWriteByte(byte b)
|
||||
{
|
||||
if (_sl.bufp == _sl.bufe) SlWriteFill();
|
||||
*_sl.bufp++ = b;
|
||||
_sl.dumper->WriteByte(b);
|
||||
}
|
||||
|
||||
/** Wrapper for SlWriteByteInternal */
|
||||
void SlWriteByte(byte b) {SlWriteByteInternal(b);}
|
||||
|
||||
static inline int SlReadUint16()
|
||||
{
|
||||
int x = SlReadByte() << 8;
|
||||
@ -801,7 +832,7 @@ static void SlCopyBytes(void *ptr, size_t length)
|
||||
for (; length != 0; length--) *p++ = SlReadByteInternal();
|
||||
break;
|
||||
case SLA_SAVE:
|
||||
for (; length != 0; length--) SlWriteByteInternal(*p++);
|
||||
for (; length != 0; length--) SlWriteByte(*p++);
|
||||
break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
@ -1456,12 +1487,12 @@ void SlAutolength(AutolengthProc *proc, void *arg)
|
||||
_sl.need_length = NL_WANTLENGTH;
|
||||
SlSetLength(_sl.obj_len);
|
||||
|
||||
offs = SlGetOffs() + _sl.obj_len;
|
||||
offs = _sl.dumper->GetSize() + _sl.obj_len;
|
||||
|
||||
/* And write the stuff */
|
||||
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);
|
||||
}
|
||||
|
||||
/********************************************
|
||||
********** START OF MEMORY CODE (in ram)****
|
||||
********************************************/
|
||||
|
||||
#include "../gui.h"
|
||||
|
||||
struct ThreadedSave {
|
||||
uint count;
|
||||
byte ff_state;
|
||||
bool saveinprogress;
|
||||
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 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 **************
|
||||
@ -2156,6 +2155,15 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
|
||||
extern bool AfterLoadGame();
|
||||
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 */
|
||||
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");
|
||||
|
||||
uint i = 0;
|
||||
size_t t = _ts.count;
|
||||
|
||||
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);
|
||||
}
|
||||
_sl.dumper->Flush(fmt->writer);
|
||||
ClearMemoryDumper();
|
||||
|
||||
fmt->uninit_write();
|
||||
if (_ts.count != _sl.offs_base) SlErrorCorrupt("Unexpected size of chunk");
|
||||
UnInitMem();
|
||||
fclose(_sl.fh);
|
||||
|
||||
if (threaded) SetAsyncSaveFinish(SaveFileDone);
|
||||
@ -2364,15 +2357,13 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, boo
|
||||
if (mode == SL_SAVE) { // SAVE game
|
||||
DEBUG(desync, 1, "save: %08x; %02x; %s", _date, _date_fract, filename);
|
||||
|
||||
_sl.dump_bytes = WriteMem;
|
||||
_sl.excpt_uninit = UnInitMem;
|
||||
InitMem();
|
||||
_sl.dumper = new MemoryDumper();
|
||||
_sl.excpt_uninit = ClearMemoryDumper;
|
||||
|
||||
_sl_version = SAVEGAME_VERSION;
|
||||
|
||||
SaveViewportBeforeSaveGame();
|
||||
SlSaveChunks();
|
||||
SlWriteFill(); // flush the save buffer
|
||||
|
||||
SaveFileStart();
|
||||
if (_network_server || !_settings_client.gui.threaded_saves) threaded = false;
|
||||
|
Loading…
Reference in New Issue
Block a user