Clean up GameSave somewhat

Namely:

 - get rid of unsafe memory management;
   - use vectors / Planes everywhere;
   - return a vector from serialization functions;
   - have read functions take a vector;
 - improve constness;
 - hide a few implementation details from GameSave.h;
 - get rid of GameSave copy constructor;
 - better member initialization;
 - use the slightly more C++-looking BZ2 wrappers.

The BSON library still takes ownership of the data it parses, and GameSave
ownership is still a joke. Those will need to be fixed later.
This commit is contained in:
Tamás Bálint Misius 2022-11-10 14:59:09 +01:00
parent f70cc705cb
commit ab600780d0
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
10 changed files with 242 additions and 401 deletions

View File

@ -153,5 +153,6 @@
#define GLASS_DISP 0.07 #define GLASS_DISP 0.07
#define SDEUT #define SDEUT
#define R_TEMP 22
#endif /* CONFIG_H */ #endif /* CONFIG_H */

View File

@ -36,7 +36,14 @@ BZ2WCompressResult BZ2WCompress(std::vector<char> &dest, const char *srcData, si
{ {
return BZ2WCompressLimit; return BZ2WCompressLimit;
} }
dest.resize(newSize); try
{
dest.resize(newSize);
}
catch (const std::bad_alloc &)
{
return BZ2WCompressNomem;
}
stream.next_out = &dest[stream.total_out_lo32]; stream.next_out = &dest[stream.total_out_lo32];
stream.avail_out = dest.size() - stream.total_out_lo32; stream.avail_out = dest.size() - stream.total_out_lo32;
if (BZ2_bzCompress(&stream, BZ_FINISH) == BZ_STREAM_END) if (BZ2_bzCompress(&stream, BZ_FINISH) == BZ_STREAM_END)
@ -75,7 +82,14 @@ BZ2WDecompressResult BZ2WDecompress(std::vector<char> &dest, const char *srcData
{ {
return BZ2WDecompressLimit; return BZ2WDecompressLimit;
} }
dest.resize(newSize); try
{
dest.resize(newSize);
}
catch (const std::bad_alloc &)
{
return BZ2WDecompressNomem;
}
stream.next_out = &dest[stream.total_out_lo32]; stream.next_out = &dest[stream.total_out_lo32];
stream.avail_out = dest.size() - stream.total_out_lo32; stream.avail_out = dest.size() - stream.total_out_lo32;
switch (BZ2_bzDecompress(&stream)) switch (BZ2_bzDecompress(&stream))

View File

@ -2,6 +2,7 @@
#include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows #include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows
#include <cstring>
#include <cstdlib> #include <cstdlib>
#include <vector> #include <vector>
#include <map> #include <map>
@ -496,8 +497,6 @@ User Client::GetAuthUser()
RequestStatus Client::UploadSave(SaveInfo & save) RequestStatus Client::UploadSave(SaveInfo & save)
{ {
lastError = ""; lastError = "";
unsigned int gameDataLength;
char * gameData = NULL;
int dataStatus; int dataStatus;
ByteString data; ByteString data;
ByteString userID = ByteString::Build(authUser.UserID); ByteString userID = ByteString::Build(authUser.UserID);
@ -511,15 +510,15 @@ RequestStatus Client::UploadSave(SaveInfo & save)
save.SetID(0); save.SetID(0);
gameData = save.GetGameSave()->Serialise(gameDataLength); auto [ fromNewerVersion, gameData ] = save.GetGameSave()->Serialise();
if (!gameData) if (!gameData.size())
{ {
lastError = "Cannot serialize game save"; lastError = "Cannot serialize game save";
return RequestFailure; return RequestFailure;
} }
#if defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0 #if defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0
else if (save.gameSave->fromNewerVersion && save.GetPublished()) else if (fromNewerVersion && save.GetPublished())
{ {
lastError = "Cannot publish save, incompatible with latest release version."; lastError = "Cannot publish save, incompatible with latest release version.";
return RequestFailure; return RequestFailure;
@ -529,7 +528,7 @@ RequestStatus Client::UploadSave(SaveInfo & save)
data = http::Request::SimpleAuth(SCHEME SERVER "/Save.api", &dataStatus, userID, authUser.SessionID, { data = http::Request::SimpleAuth(SCHEME SERVER "/Save.api", &dataStatus, userID, authUser.SessionID, {
{ "Name", save.GetName().ToUtf8() }, { "Name", save.GetName().ToUtf8() },
{ "Description", save.GetDescription().ToUtf8() }, { "Description", save.GetDescription().ToUtf8() },
{ "Data:save.bin", ByteString(gameData, gameData + gameDataLength) }, { "Data:save.bin", ByteString(gameData.begin(), gameData.end()) },
{ "Publish", save.GetPublished() ? "Public" : "Private" }, { "Publish", save.GetPublished() ? "Public" : "Private" },
}); });
} }
@ -551,7 +550,6 @@ RequestStatus Client::UploadSave(SaveInfo & save)
else else
save.SetID(saveID); save.SetID(saveID);
} }
delete[] gameData;
return ret; return ret;
} }
@ -623,17 +621,11 @@ ByteString Client::AddStamp(GameSave * saveData)
} }
saveData->authors = stampInfo; saveData->authors = stampInfo;
unsigned int gameDataLength; auto [ _, gameData ] = saveData->Serialise();
char * gameData = saveData->Serialise(gameDataLength); if (!gameData.size())
if (gameData == NULL)
return ""; return "";
std::ofstream stampStream; Platform::WriteFile(gameData, filename);
stampStream.open(filename.c_str(), std::ios::binary);
stampStream.write((const char *)gameData, gameDataLength);
stampStream.close();
delete[] gameData;
stampIDs.push_front(saveID); stampIDs.push_front(saveID);

View File

@ -7,7 +7,7 @@
#include <set> #include <set>
#include <cmath> #include <cmath>
#include <bzlib.h> #include "bzip2/bz2wrap.h"
#include "Config.h" #include "Config.h"
#include "Format.h" #include "Format.h"
#include "hmap.h" #include "hmap.h"
@ -17,49 +17,17 @@
#include "common/tpt-minmax.h" #include "common/tpt-minmax.h"
#include "common/tpt-compat.h" #include "common/tpt-compat.h"
#include "bson/BSON.h"
GameSave::GameSave(const GameSave & save): static void ConvertJsonToBson(bson *b, Json::Value j, int depth = 0);
majorVersion(save.majorVersion), static void ConvertBsonToJson(bson_iterator *b, Json::Value *j, int depth = 0);
waterEEnabled(save.waterEEnabled), static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen);
legacyEnable(save.legacyEnable), static void CheckBsonFieldBool(bson_iterator iter, const char *field, bool *flag);
gravityEnable(save.gravityEnable), static void CheckBsonFieldInt(bson_iterator iter, const char *field, int *setting);
aheatEnable(save.aheatEnable), static void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting);
paused(save.paused),
gravityMode(save.gravityMode),
customGravityX(save.customGravityX),
customGravityY(save.customGravityY),
airMode(save.airMode),
ambientAirTemp(save.ambientAirTemp),
edgeMode(save.edgeMode),
signs(save.signs),
stkm(save.stkm),
palette(save.palette),
pmapbits(save.pmapbits)
{
InitData();
hasPressure = save.hasPressure;
hasAmbientHeat = save.hasAmbientHeat;
setSize(save.blockWidth, save.blockHeight);
std::copy(save.particles, save.particles+NPART, particles);
for (int j = 0; j < blockHeight; j++)
{
std::copy(save.blockMap[j], save.blockMap[j]+blockWidth, blockMap[j]);
std::copy(save.fanVelX[j], save.fanVelX[j]+blockWidth, fanVelX[j]);
std::copy(save.fanVelY[j], save.fanVelY[j]+blockWidth, fanVelY[j]);
std::copy(save.pressure[j], save.pressure[j]+blockWidth, pressure[j]);
std::copy(save.velocityX[j], save.velocityX[j]+blockWidth, velocityX[j]);
std::copy(save.velocityY[j], save.velocityY[j]+blockWidth, velocityY[j]);
std::copy(save.ambientHeat[j], save.ambientHeat[j]+blockWidth, ambientHeat[j]);
}
particlesCount = save.particlesCount;
authors = save.authors;
}
GameSave::GameSave(int width, int height) GameSave::GameSave(int width, int height)
{ {
InitData();
InitVars();
setSize(width, height); setSize(width, height);
} }
@ -68,8 +36,6 @@ GameSave::GameSave(const std::vector<char> &data)
blockWidth = 0; blockWidth = 0;
blockHeight = 0; blockHeight = 0;
InitData();
InitVars();
try try
{ {
Expand(data); Expand(data);
@ -77,67 +43,25 @@ GameSave::GameSave(const std::vector<char> &data)
catch(ParseException & e) catch(ParseException & e)
{ {
std::cout << e.what() << std::endl; std::cout << e.what() << std::endl;
dealloc(); //Free any allocated memory
throw; throw;
} }
} }
// Called on every new GameSave, including the copy constructor
void GameSave::InitData()
{
blockMap = NULL;
fanVelX = NULL;
fanVelY = NULL;
particles = NULL;
pressure = NULL;
velocityX = NULL;
velocityY = NULL;
ambientHeat = NULL;
fromNewerVersion = false;
hasPressure = false;
hasAmbientHeat = false;
authors.clear();
}
// Called on every new GameSave, except the copy constructor
void GameSave::InitVars()
{
majorVersion = 0;
minorVersion = 0;
waterEEnabled = false;
legacyEnable = false;
gravityEnable = false;
aheatEnable = false;
paused = false;
gravityMode = 0;
customGravityX = 0.0f;
customGravityY = 0.0f;
airMode = 0;
ambientAirTemp = R_TEMP + 273.15f;
edgeMode = 0;
translated.x = translated.y = 0;
pmapbits = 8; // default to 8 bits for older saves
}
void GameSave::Expand(const std::vector<char> &data) void GameSave::Expand(const std::vector<char> &data)
{ {
InitVars(); try
read(&data[0], data.size()); {
} if(data.size() > 15)
void GameSave::read(const char * data, int dataSize)
{
if(dataSize > 15)
{ {
if ((data[0]==0x66 && data[1]==0x75 && data[2]==0x43) || (data[0]==0x50 && data[1]==0x53 && data[2]==0x76)) if ((data[0]==0x66 && data[1]==0x75 && data[2]==0x43) || (data[0]==0x50 && data[1]==0x53 && data[2]==0x76))
{ {
readPSv(data, dataSize); readPSv(data);
} }
else if(data[0] == 'O' && data[1] == 'P' && data[2] == 'S') else if(data[0] == 'O' && data[1] == 'P' && data[2] == 'S')
{ {
if (data[3] != '1') if (data[3] != '1')
throw ParseException(ParseException::WrongVersion, "Save format from newer version"); throw ParseException(ParseException::WrongVersion, "Save format from newer version");
readOPS(data, dataSize); readOPS(data);
} }
else else
{ {
@ -149,59 +73,45 @@ void GameSave::read(const char * data, int dataSize)
{ {
throw ParseException(ParseException::Corrupt, "No data"); throw ParseException(ParseException::Corrupt, "No data");
} }
}
template <typename T>
T ** GameSave::Allocate2DArray(int blockWidth, int blockHeight, T defaultVal)
{
T ** temp = new T*[blockHeight];
for (int y = 0; y < blockHeight; y++)
{
temp[y] = new T[blockWidth];
std::fill(&temp[y][0], &temp[y][0]+blockWidth, defaultVal);
} }
return temp; catch (const std::bad_alloc &)
{
throw ParseException(ParseException::Corrupt, "Cannot allocate memory");
}
} }
void GameSave::setSize(int newWidth, int newHeight) void GameSave::setSize(int newWidth, int newHeight)
{ {
this->blockWidth = newWidth; blockWidth = newWidth;
this->blockHeight = newHeight; blockHeight = newHeight;
particlesCount = 0; particlesCount = 0;
particles = new Particle[NPART]; particles = std::vector<Particle>(NPART);
blockMap = Allocate2DArray<unsigned char>(blockWidth, blockHeight, 0); blockMap = Plane<unsigned char>(blockWidth, blockHeight, 0);
fanVelX = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f); fanVelX = Plane<float>(blockWidth, blockHeight, 0.0f);
fanVelY = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f); fanVelY = Plane<float>(blockWidth, blockHeight, 0.0f);
pressure = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f); pressure = Plane<float>(blockWidth, blockHeight, 0.0f);
velocityX = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f); velocityX = Plane<float>(blockWidth, blockHeight, 0.0f);
velocityY = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f); velocityY = Plane<float>(blockWidth, blockHeight, 0.0f);
ambientHeat = Allocate2DArray<float>(blockWidth, blockHeight, 0.0f); ambientHeat = Plane<float>(blockWidth, blockHeight, 0.0f);
} }
std::vector<char> GameSave::Serialise() std::pair<bool, std::vector<char>> GameSave::Serialise() const
{
unsigned int dataSize;
char * data = Serialise(dataSize);
if (data == NULL)
return std::vector<char>();
std::vector<char> dataVect(data, data+dataSize);
delete[] data;
return dataVect;
}
char * GameSave::Serialise(unsigned int & dataSize)
{ {
try try
{ {
return serialiseOPS(dataSize); return serialiseOPS();
}
catch (const std::bad_alloc &)
{
std::cout << "Save error, out of memory" << std::endl;
} }
catch (BuildException & e) catch (BuildException & e)
{ {
std::cout << e.what() << std::endl; std::cout << e.what() << std::endl;
return NULL;
} }
return { false, {} };
} }
vector2d GameSave::Translate(vector2d translate) vector2d GameSave::Translate(vector2d translate)
@ -312,16 +222,13 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl
int x, y, nx, ny, newBlockWidth = newWidth / CELL, newBlockHeight = newHeight / CELL; int x, y, nx, ny, newBlockWidth = newWidth / CELL, newBlockHeight = newHeight / CELL;
vector2d pos, vel; vector2d pos, vel;
unsigned char ** blockMapNew; Plane<unsigned char> blockMapNew(newBlockWidth, newBlockHeight, 0);
float **fanVelXNew, **fanVelYNew, **pressureNew, **velocityXNew, **velocityYNew, **ambientHeatNew; Plane<float> fanVelXNew(newBlockWidth, newBlockHeight, 0.0f);
Plane<float> fanVelYNew(newBlockWidth, newBlockHeight, 0.0f);
blockMapNew = Allocate2DArray<unsigned char>(newBlockWidth, newBlockHeight, 0); Plane<float> pressureNew(newBlockWidth, newBlockHeight, 0.0f);
fanVelXNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f); Plane<float> velocityXNew(newBlockWidth, newBlockHeight, 0.0f);
fanVelYNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f); Plane<float> velocityYNew(newBlockWidth, newBlockHeight, 0.0f);
pressureNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f); Plane<float> ambientHeatNew(newBlockWidth, newBlockHeight, 0.0f);
velocityXNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
velocityYNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
ambientHeatNew = Allocate2DArray<float>(newBlockWidth, newBlockHeight, 0.0f);
// Match these up with the matrices provided in GameView::OnKeyPress. // Match these up with the matrices provided in GameView::OnKeyPress.
bool patchPipeR = transform.a == 0 && transform.b == 1 && transform.c == -1 && transform.d == 0; bool patchPipeR = transform.a == 0 && transform.b == 1 && transform.c == -1 && transform.d == 0;
@ -423,28 +330,9 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl
} }
translated = v2d_add(m2d_multiply_v2d(transform, translated), translateReal); translated = v2d_add(m2d_multiply_v2d(transform, translated), translateReal);
for (int j = 0; j < blockHeight; j++)
{
delete[] blockMap[j];
delete[] fanVelX[j];
delete[] fanVelY[j];
delete[] pressure[j];
delete[] velocityX[j];
delete[] velocityY[j];
delete[] ambientHeat[j];
}
blockWidth = newBlockWidth; blockWidth = newBlockWidth;
blockHeight = newBlockHeight; blockHeight = newBlockHeight;
delete[] blockMap;
delete[] fanVelX;
delete[] fanVelY;
delete[] pressure;
delete[] velocityX;
delete[] velocityY;
delete[] ambientHeat;
blockMap = blockMapNew; blockMap = blockMapNew;
fanVelX = fanVelXNew; fanVelX = fanVelXNew;
fanVelY = fanVelYNew; fanVelY = fanVelYNew;
@ -454,7 +342,7 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl
ambientHeat = ambientHeatNew; ambientHeat = ambientHeatNew;
} }
void GameSave::CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen) static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen)
{ {
if (!strcmp(bson_iterator_key(&iter), field)) if (!strcmp(bson_iterator_key(&iter), field))
{ {
@ -469,7 +357,7 @@ void GameSave::CheckBsonFieldUser(bson_iterator iter, const char *field, unsigne
} }
} }
void GameSave::CheckBsonFieldBool(bson_iterator iter, const char *field, bool *flag) static void CheckBsonFieldBool(bson_iterator iter, const char *field, bool *flag)
{ {
if (!strcmp(bson_iterator_key(&iter), field)) if (!strcmp(bson_iterator_key(&iter), field))
{ {
@ -484,7 +372,7 @@ void GameSave::CheckBsonFieldBool(bson_iterator iter, const char *field, bool *f
} }
} }
void GameSave::CheckBsonFieldInt(bson_iterator iter, const char *field, int *setting) static void CheckBsonFieldInt(bson_iterator iter, const char *field, int *setting)
{ {
if (!strcmp(bson_iterator_key(&iter), field)) if (!strcmp(bson_iterator_key(&iter), field))
{ {
@ -499,7 +387,7 @@ void GameSave::CheckBsonFieldInt(bson_iterator iter, const char *field, int *set
} }
} }
void GameSave::CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting) static void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting)
{ {
if (!strcmp(bson_iterator_key(&iter), field)) if (!strcmp(bson_iterator_key(&iter), field))
{ {
@ -514,11 +402,12 @@ void GameSave::CheckBsonFieldFloat(bson_iterator iter, const char *field, float
} }
} }
void GameSave::readOPS(const char * data, int dataLength) void GameSave::readOPS(const std::vector<char> &data)
{ {
unsigned char *inputData = (unsigned char*)data, *bsonData = NULL, *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *soapLinkData = NULL;
unsigned char *inputData = (unsigned char*)&data[0], *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *soapLinkData = NULL;
unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL; unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL;
unsigned int inputDataLen = dataLength, bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen; unsigned int inputDataLen = data.size(), bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen;
unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen; unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen;
unsigned partsCount = 0; unsigned partsCount = 0;
unsigned int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH; unsigned int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH;
@ -572,26 +461,32 @@ void GameSave::readOPS(const char * data, int dataLength)
bsonDataLen |= ((unsigned)inputData[11]) << 24; bsonDataLen |= ((unsigned)inputData[11]) << 24;
//Check for overflows, don't load saves larger than 200MB //Check for overflows, don't load saves larger than 200MB
unsigned int toAlloc = bsonDataLen+1; unsigned int toAlloc = bsonDataLen;
if (toAlloc > 209715200 || !toAlloc) if (toAlloc > 209715200 || !toAlloc)
throw ParseException(ParseException::InvalidDimensions, "Save data too large, refusing"); throw ParseException(ParseException::InvalidDimensions, "Save data too large, refusing");
bsonData = (unsigned char*)malloc(toAlloc);
if (!bsonData)
throw ParseException(ParseException::InternalError, "Unable to allocate memory");
//Make sure bsonData is null terminated, since all string functions need null terminated strings
//(bson_iterator_key returns a pointer into bsonData, which is then used with strcmp)
bsonData[bsonDataLen] = 0;
int bz2ret;
if ((bz2ret = BZ2_bzBuffToBuffDecompress((char*)bsonData, &bsonDataLen, (char*)(inputData+12), inputDataLen-12, 0, 0)) != BZ_OK)
{ {
throw ParseException(ParseException::Corrupt, String::Build("Unable to decompress (ret ", bz2ret, ")")); std::vector<char> bsonData;
switch (auto status = BZ2WDecompress(bsonData, (char *)(inputData + 12), inputDataLen - 12, toAlloc))
{
case BZ2WDecompressOk: break;
case BZ2WDecompressNomem: throw ParseException(ParseException::Corrupt, "Cannot allocate memory");
default: throw ParseException(ParseException::Corrupt, String::Build("Cannot decompress: status ", int(status)));
}
bsonDataLen = bsonData.size();
//Make sure bsonData is null terminated, since all string functions need null terminated strings
//(bson_iterator_key returns a pointer into bsonData, which is then used with strcmp)
bsonData.push_back(0);
// apparently bson_* takes ownership of the data passed into it?????????
auto *pleaseFixMe = (char *)malloc(bsonData.size());
std::copy(bsonData.begin(), bsonData.end(), pleaseFixMe);
bson_init_data_size(&b, pleaseFixMe, bsonDataLen);
} }
set_bson_err_handler([](const char* err) { throw ParseException(ParseException::Corrupt, "BSON error when parsing save: " + ByteString(err).FromUtf8()); }); set_bson_err_handler([](const char* err) { throw ParseException(ParseException::Corrupt, "BSON error when parsing save: " + ByteString(err).FromUtf8()); });
bson_init_data_size(&b, (char*)bsonData, bsonDataLen);
bson_iterator_init(&iter, &b); bson_iterator_init(&iter, &b);
std::vector<sign> tempSigns; std::vector<sign> tempSigns;
@ -1346,9 +1241,10 @@ void GameSave::readOPS(const char * data, int dataLength)
} }
} }
void GameSave::readPSv(const char * saveDataChar, int dataLength) void GameSave::readPSv(const std::vector<char> &dataVec)
{ {
unsigned char * saveData = (unsigned char *)saveDataChar; unsigned char * saveData = (unsigned char *)&dataVec[0];
auto dataLength = int(dataVec.size());
int q,j,k,x,y,p=0, ver, pty, ty, legacy_beta=0; int q,j,k,x,y,p=0, ver, pty, ty, legacy_beta=0;
int bx0=0, by0=0, bw, bh, w, h, y0 = 0, x0 = 0; int bx0=0, by0=0, bw, bh, w, h, y0 = 0, x0 = 0;
int new_format = 0, ttv = 0; int new_format = 0, ttv = 0;
@ -1421,17 +1317,16 @@ void GameSave::readPSv(const char * saveDataChar, int dataLength)
if (size > 209715200 || !size) if (size > 209715200 || !size)
throw ParseException(ParseException::InvalidDimensions, "Save data too large"); throw ParseException(ParseException::InvalidDimensions, "Save data too large");
auto dataPtr = std::unique_ptr<unsigned char[]>(new unsigned char[size]); std::vector<char> data;
unsigned char *data = dataPtr.get(); switch (auto status = BZ2WDecompress(data, (char *)(saveData + 12), dataLength - 12, size))
if (!data) {
throw ParseException(ParseException::Corrupt, "Cannot allocate memory"); case BZ2WDecompressOk: break;
case BZ2WDecompressNomem: throw ParseException(ParseException::Corrupt, "Cannot allocate memory");
default: throw ParseException(ParseException::Corrupt, String::Build("Cannot decompress: status ", int(status)));
}
setSize(bw, bh); setSize(bw, bh);
dataLength = data.size();
int bzStatus = 0;
if ((bzStatus = BZ2_bzBuffToBuffDecompress((char *)data, (unsigned *)&size, (char *)(saveData+12), dataLength-12, 0, 0)))
throw ParseException(ParseException::Corrupt, String::Build("Cannot decompress: ", bzStatus));
dataLength = size;
#ifdef DEBUG #ifdef DEBUG
std::cout << "Parsing " << dataLength << " bytes of data, version " << ver << std::endl; std::cout << "Parsing " << dataLength << " bytes of data, version " << ver << std::endl;
@ -1451,11 +1346,7 @@ void GameSave::readPSv(const char * saveDataChar, int dataLength)
airMode = 0; airMode = 0;
} }
auto particleIDMapPtr = std::unique_ptr<int[]>(new int[XRES*YRES]); std::vector<int> particleIDMap(XRES * YRES, 0);
int *particleIDMap = particleIDMapPtr.get();
std::fill(&particleIDMap[0], &particleIDMap[XRES*YRES], 0);
if (!particleIDMap)
throw ParseException(ParseException::Corrupt, "Cannot allocate memory");
// load the required air state // load the required air state
for (y=by0; y<by0+bh; y++) for (y=by0; y<by0+bh; y++)
@ -1575,7 +1466,7 @@ void GameSave::readPSv(const char * saveDataChar, int dataLength)
} }
if (j) if (j)
{ {
memset(particles+k, 0, sizeof(Particle)); memset(&particles[0]+k, 0, sizeof(Particle));
particles[k].type = j; particles[k].type = j;
if (j == PT_COAL) if (j == PT_COAL)
particles[k].tmp = 50; particles[k].tmp = 50;
@ -1980,7 +1871,7 @@ void GameSave::readPSv(const char * saveDataChar, int dataLength)
throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
if(x>254) if(x>254)
x = 254; x = 254;
memcpy(tempSignText, data+p, x); memcpy(tempSignText, &data[0]+p, x);
tempSignText[x] = 0; tempSignText[x] = 0;
tempSign.text = format::CleanString(ByteString(tempSignText).FromUtf8(), true, true, true).Substr(0, 45); tempSign.text = format::CleanString(ByteString(tempSignText).FromUtf8(), true, true, true).Substr(0, 45);
if (tempSign.text == "{t}") if (tempSign.text == "{t}")
@ -2009,7 +1900,7 @@ void GameSave::readPSv(const char * saveDataChar, int dataLength)
minimumMinorVersion = minor;\ minimumMinorVersion = minor;\
} }
char * GameSave::serialiseOPS(unsigned int & dataLength) std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
{ {
int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH; int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH;
int x, y, i; int x, y, i;
@ -2033,16 +1924,13 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
fullH = blockH*CELL; fullH = blockH*CELL;
// Copy fan and wall data // Copy fan and wall data
auto wallData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight]); std::vector<unsigned char> wallData(blockWidth*blockHeight);
bool hasWallData = false; bool hasWallData = false;
auto fanData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]); std::vector<unsigned char> fanData(blockWidth*blockHeight*2);
auto pressData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]); std::vector<unsigned char> pressData(blockWidth*blockHeight*2);
auto vxData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]); std::vector<unsigned char> vxData(blockWidth*blockHeight*2);
auto vyData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]); std::vector<unsigned char> vyData(blockWidth*blockHeight*2);
auto ambientData = std::unique_ptr<unsigned char[]>(new unsigned char[blockWidth*blockHeight*2]); std::vector<unsigned char> ambientData(blockWidth*blockHeight*2, 0);
std::fill(&ambientData[0], &ambientData[blockWidth*blockHeight*2], 0);
if (!wallData || !fanData || !pressData || !vxData || !vyData || !ambientData)
throw BuildException("Save error, out of memory (blockmaps)");
unsigned int wallDataLen = blockWidth*blockHeight, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0; unsigned int wallDataLen = blockWidth*blockHeight, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0;
for (x = blockX; x < blockX+blockW; x++) for (x = blockX; x < blockX+blockW; x++)
@ -2099,16 +1987,10 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
//partsPosLastMap is pmap for the last particle in each position //partsPosLastMap is pmap for the last particle in each position
//partsPosCount is the number of particles in each position //partsPosCount is the number of particles in each position
//partsPosLink contains, for each particle, (i<<8)|1 of the next particle in the same position //partsPosLink contains, for each particle, (i<<8)|1 of the next particle in the same position
auto partsPosFirstMap = std::unique_ptr<unsigned[]>(new unsigned[fullW*fullH]); std::vector<unsigned> partsPosFirstMap(fullW*fullH, 0);
auto partsPosLastMap = std::unique_ptr<unsigned[]>(new unsigned[fullW*fullH]); std::vector<unsigned> partsPosLastMap(fullW*fullH, 0);
auto partsPosCount = std::unique_ptr<unsigned[]>(new unsigned[fullW*fullH]); std::vector<unsigned> partsPosCount(fullW*fullH, 0);
auto partsPosLink = std::unique_ptr<unsigned[]>(new unsigned[NPART]); std::vector<unsigned> partsPosLink(NPART, 0);
if (!partsPosFirstMap || !partsPosLastMap || !partsPosCount || !partsPosLink)
throw BuildException("Save error, out of memory (partmaps)");
std::fill(&partsPosFirstMap[0], &partsPosFirstMap[fullW*fullH], 0);
std::fill(&partsPosLastMap[0], &partsPosLastMap[fullW*fullH], 0);
std::fill(&partsPosCount[0], &partsPosCount[fullW*fullH], 0);
std::fill(&partsPosLink[0], &partsPosLink[NPART], 0);
unsigned int soapCount = 0; unsigned int soapCount = 0;
for(i = 0; i < particlesCount; i++) for(i = 0; i < particlesCount; i++)
{ {
@ -2136,10 +2018,8 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
} }
//Store number of particles in each position //Store number of particles in each position
auto partsPosData = std::unique_ptr<unsigned char[]>(new unsigned char[fullW*fullH*3]); std::vector<unsigned char> partsPosData(fullW*fullH*3);
unsigned int partsPosDataLen = 0; unsigned int partsPosDataLen = 0;
if (!partsPosData)
throw BuildException("Save error, out of memory (partposdata)");
for (y=0;y<fullH;y++) for (y=0;y<fullH;y++)
{ {
for (x=0;x<fullW;x++) for (x=0;x<fullW;x++)
@ -2166,12 +2046,10 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
// Allocate enough space to store all Particles and 3 bytes on top of that per Particle, for the field descriptors. // Allocate enough space to store all Particles and 3 bytes on top of that per Particle, for the field descriptors.
// In practice, a Particle will never need as much space in the save as in memory; this is just an upper bound to simplify allocation. // In practice, a Particle will never need as much space in the save as in memory; this is just an upper bound to simplify allocation.
auto partsData = std::unique_ptr<unsigned char[]>(new unsigned char[NPART * (sizeof(Particle)+3)]); std::vector<unsigned char> partsData(NPART * (sizeof(Particle)+3));
unsigned int partsDataLen = 0; unsigned int partsDataLen = 0;
auto partsSaveIndex = std::unique_ptr<unsigned[]>(new unsigned[NPART]); std::vector<unsigned> partsSaveIndex(NPART);
unsigned int partsCount = 0; unsigned int partsCount = 0;
if (!partsData || !partsSaveIndex)
throw BuildException("Save error, out of memory (partsdata)");
std::fill(&partsSaveIndex[0], &partsSaveIndex[NPART], 0); std::fill(&partsSaveIndex[0], &partsSaveIndex[NPART], 0);
for (y=0;y<fullH;y++) for (y=0;y<fullH;y++)
{ {
@ -2443,15 +2321,10 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
} }
} }
unsigned char *soapLinkData = NULL;
auto soapLinkDataPtr = std::unique_ptr<unsigned char[]>();
unsigned int soapLinkDataLen = 0; unsigned int soapLinkDataLen = 0;
std::vector<unsigned char> soapLinkData(3*soapCount);
if (soapCount) if (soapCount)
{ {
soapLinkData = new unsigned char[3*soapCount];
if (!soapLinkData)
throw BuildException("Save error, out of memory (SOAP)");
soapLinkDataPtr = std::unique_ptr<unsigned char[]>(soapLinkData);
//Iterate through particles in the same order that they were saved //Iterate through particles in the same order that they were saved
for (y=0;y<fullH;y++) for (y=0;y<fullH;y++)
@ -2503,10 +2376,11 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
} }
} }
bool fakeFromNewerVersion = false;
#if defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0 #if defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0
// Mark save as incompatible with latest release // Mark save as incompatible with latest release
if (minimumMajorVersion > SAVE_VERSION || (minimumMajorVersion == SAVE_VERSION && minimumMinorVersion > MINOR_VERSION)) if (minimumMajorVersion > SAVE_VERSION || (minimumMajorVersion == SAVE_VERSION && minimumMinorVersion > MINOR_VERSION))
fromNewerVersion = true; fakeFromNewerVersion = true;
#endif #endif
bson b; bson b;
@ -2583,37 +2457,37 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
} }
bson_append_int(&b, "pmapbits", pmapbits); bson_append_int(&b, "pmapbits", pmapbits);
if (partsData && partsDataLen) if (partsDataLen)
{ {
bson_append_binary(&b, "parts", (char)BSON_BIN_USER, (const char *)partsData.get(), partsDataLen); bson_append_binary(&b, "parts", (char)BSON_BIN_USER, (const char *)&partsData[0], partsDataLen);
if (palette.size()) if (palette.size())
{ {
bson_append_start_array(&b, "palette"); bson_append_start_array(&b, "palette");
for(std::vector<PaletteItem>::iterator iter = palette.begin(), end = palette.end(); iter != end; ++iter) for(auto iter = palette.begin(), end = palette.end(); iter != end; ++iter)
{ {
bson_append_int(&b, (*iter).first.c_str(), (*iter).second); bson_append_int(&b, (*iter).first.c_str(), (*iter).second);
} }
bson_append_finish_array(&b); bson_append_finish_array(&b);
} }
if (partsPosData && partsPosDataLen) if (partsPosDataLen)
bson_append_binary(&b, "partsPos", (char)BSON_BIN_USER, (const char *)partsPosData.get(), partsPosDataLen); bson_append_binary(&b, "partsPos", (char)BSON_BIN_USER, (const char *)&partsPosData[0], partsPosDataLen);
} }
if (wallData && hasWallData) if (hasWallData)
bson_append_binary(&b, "wallMap", (char)BSON_BIN_USER, (const char *)wallData.get(), wallDataLen); bson_append_binary(&b, "wallMap", (char)BSON_BIN_USER, (const char *)&wallData[0], wallDataLen);
if (fanData && fanDataLen) if (fanDataLen)
bson_append_binary(&b, "fanMap", (char)BSON_BIN_USER, (const char *)fanData.get(), fanDataLen); bson_append_binary(&b, "fanMap", (char)BSON_BIN_USER, (const char *)&fanData[0], fanDataLen);
if (pressData && hasPressure && pressDataLen) if (hasPressure && pressDataLen)
bson_append_binary(&b, "pressMap", (char)BSON_BIN_USER, (const char*)pressData.get(), pressDataLen); bson_append_binary(&b, "pressMap", (char)BSON_BIN_USER, (const char*)&pressData[0], pressDataLen);
if (vxData && hasPressure && vxDataLen) if (hasPressure && vxDataLen)
bson_append_binary(&b, "vxMap", (char)BSON_BIN_USER, (const char*)vxData.get(), vxDataLen); bson_append_binary(&b, "vxMap", (char)BSON_BIN_USER, (const char*)&vxData[0], vxDataLen);
if (vyData && hasPressure && vyDataLen) if (hasPressure && vyDataLen)
bson_append_binary(&b, "vyMap", (char)BSON_BIN_USER, (const char*)vyData.get(), vyDataLen); bson_append_binary(&b, "vyMap", (char)BSON_BIN_USER, (const char*)&vyData[0], vyDataLen);
if (ambientData && hasAmbientHeat && this->aheatEnable && ambientDataLen) if (hasAmbientHeat && this->aheatEnable && ambientDataLen)
bson_append_binary(&b, "ambientMap", (char)BSON_BIN_USER, (const char*)ambientData.get(), ambientDataLen); bson_append_binary(&b, "ambientMap", (char)BSON_BIN_USER, (const char*)&ambientData[0], ambientDataLen);
if (soapLinkData && soapLinkDataLen) if (soapLinkDataLen)
bson_append_binary(&b, "soapLinks", (char)BSON_BIN_USER, (const char *)soapLinkData, soapLinkDataLen); bson_append_binary(&b, "soapLinks", (char)BSON_BIN_USER, (const char *)&soapLinkData[0], soapLinkDataLen);
unsigned int signsCount = 0; unsigned int signsCount = 0;
for (size_t i = 0; i < signs.size(); i++) for (size_t i = 0; i < signs.size(); i++)
{ {
@ -2650,40 +2524,43 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
unsigned char *finalData = (unsigned char*)bson_data(&b); unsigned char *finalData = (unsigned char*)bson_data(&b);
unsigned int finalDataLen = bson_size(&b); unsigned int finalDataLen = bson_size(&b);
auto outputData = std::unique_ptr<unsigned char[]>(new unsigned char[finalDataLen*2+12]);
if (!outputData)
throw BuildException(String::Build("Save error, out of memory (finalData): ", finalDataLen*2+12));
outputData[0] = 'O';
outputData[1] = 'P';
outputData[2] = 'S';
outputData[3] = '1';
outputData[4] = SAVE_VERSION;
outputData[5] = CELL;
outputData[6] = blockW;
outputData[7] = blockH;
outputData[8] = finalDataLen;
outputData[9] = finalDataLen >> 8;
outputData[10] = finalDataLen >> 16;
outputData[11] = finalDataLen >> 24;
unsigned int compressedSize = finalDataLen*2, bz2ret; std::vector<char> outputData;
if ((bz2ret = BZ2_bzBuffToBuffCompress((char*)(outputData.get()+12), &compressedSize, (char*)finalData, bson_size(&b), 9, 0, 0)) != BZ_OK) switch (auto status = BZ2WCompress(outputData, (char *)finalData, finalDataLen))
{ {
throw BuildException(String::Build("Save error, could not compress (ret ", bz2ret, ")")); case BZ2WCompressOk: break;
case BZ2WCompressNomem: throw BuildException(String::Build("Save error, out of memory"));
default: throw BuildException(String::Build("Cannot compress: status ", int(status)));
} }
auto compressedSize = int(outputData.size());
#ifdef DEBUG #ifdef DEBUG
printf("compressed data: %d\n", compressedSize); printf("compressed data: %d\n", compressedSize);
#endif #endif
dataLength = compressedSize + 12; outputData.resize(compressedSize + 12);
char *saveData = new char[dataLength]; auto header = (unsigned char *)&outputData[compressedSize];
std::copy(&outputData[0], &outputData[dataLength], &saveData[0]); header[0] = 'O';
return saveData; header[1] = 'P';
header[2] = 'S';
header[3] = '1';
header[4] = SAVE_VERSION;
header[5] = CELL;
header[6] = blockW;
header[7] = blockH;
header[8] = finalDataLen;
header[9] = finalDataLen >> 8;
header[10] = finalDataLen >> 16;
header[11] = finalDataLen >> 24;
// move header to front
std::rotate(outputData.begin(), outputData.begin() + compressedSize, outputData.end());
return { fakeFromNewerVersion, outputData };
} }
void GameSave::ConvertBsonToJson(bson_iterator *iter, Json::Value *j, int depth) static void ConvertBsonToJson(bson_iterator *iter, Json::Value *j, int depth)
{ {
bson_iterator subiter; bson_iterator subiter;
bson_iterator_subiterator(iter, &subiter); bson_iterator_subiterator(iter, &subiter);
@ -2754,7 +2631,7 @@ std::set<int> GetNestedSaveIDs(Json::Value j)
} }
// converts a json object to bson // converts a json object to bson
void GameSave::ConvertJsonToBson(bson *b, Json::Value j, int depth) static void ConvertJsonToBson(bson *b, Json::Value j, int depth)
{ {
Json::Value::Members members = j.getMemberNames(); Json::Value::Members members = j.getMemberNames();
for (Json::Value::Members::iterator iter = members.begin(), end = members.end(); iter != end; ++iter) for (Json::Value::Members::iterator iter = members.begin(), end = members.end(); iter != end; ++iter)
@ -2805,19 +2682,6 @@ void GameSave::ConvertJsonToBson(bson *b, Json::Value j, int depth)
} }
} }
// deallocates a pointer to a 2D array and sets it to NULL
template <typename T>
void GameSave::Deallocate2DArray(T ***array, int blockHeight)
{
if (*array)
{
for (int y = 0; y < blockHeight; y++)
delete[] (*array)[y];
delete[] (*array);
*array = NULL;
}
}
bool GameSave::TypeInCtype(int type, int ctype) bool GameSave::TypeInCtype(int type, int ctype)
{ {
return ctype >= 0 && ctype < PT_NUM && return ctype >= 0 && ctype < PT_NUM &&
@ -2843,27 +2707,6 @@ bool GameSave::PressureInTmp3(int type)
return type == PT_QRTZ || type == PT_GLAS || type == PT_TUNG; return type == PT_QRTZ || type == PT_GLAS || type == PT_TUNG;
} }
void GameSave::dealloc()
{
if (particles)
{
delete[] particles;
particles = NULL;
}
Deallocate2DArray<unsigned char>(&blockMap, blockHeight);
Deallocate2DArray<float>(&fanVelX, blockHeight);
Deallocate2DArray<float>(&fanVelY, blockHeight);
Deallocate2DArray<float>(&pressure, blockHeight);
Deallocate2DArray<float>(&velocityX, blockHeight);
Deallocate2DArray<float>(&velocityY, blockHeight);
Deallocate2DArray<float>(&ambientHeat, blockHeight);
}
GameSave::~GameSave()
{
dealloc();
}
GameSave& GameSave::operator << (Particle &v) GameSave& GameSave::operator << (Particle &v)
{ {
if(particlesCount<NPART && v.type) if(particlesCount<NPART && v.type)

View File

@ -1,12 +1,12 @@
#ifndef The_Powder_Toy_GameSave_h #pragma once
#define The_Powder_Toy_GameSave_h
#include "Config.h" #include "Config.h"
#include <vector> #include <vector>
#include "common/String.h" #include "common/String.h"
#include "simulation/Sign.h"
#include "simulation/Particle.h"
#include "Misc.h" #include "Misc.h"
#include "bson/BSON.h"
#include <json/json.h> #include <json/json.h>
struct sign; struct sign;
@ -43,63 +43,80 @@ public:
bool rocketBoots2 = false; bool rocketBoots2 = false;
bool fan1 = false; bool fan1 = false;
bool fan2 = false; bool fan2 = false;
std::vector<unsigned int> rocketBootsFigh = std::vector<unsigned int>(); std::vector<unsigned int> rocketBootsFigh;
std::vector<unsigned int> fanFigh = std::vector<unsigned int>(); std::vector<unsigned int> fanFigh;
StkmData() = default; bool hasData() const
StkmData(const StkmData & stkmData):
rocketBoots1(stkmData.rocketBoots1),
rocketBoots2(stkmData.rocketBoots2),
fan1(stkmData.fan1),
fan2(stkmData.fan2),
rocketBootsFigh(stkmData.rocketBootsFigh),
fanFigh(stkmData.fanFigh)
{
}
bool hasData()
{ {
return rocketBoots1 || rocketBoots2 || fan1 || fan2 return rocketBoots1 || rocketBoots2 || fan1 || fan2
|| rocketBootsFigh.size() || fanFigh.size(); || rocketBootsFigh.size() || fanFigh.size();
} }
}; };
template<class Item>
struct Plane
{
int width = 0;
int height = 0;
std::vector<Item> items;
// invariant: items.size() == width * height
Item *operator [](int y)
{
return &items[y * width];
}
const Item *operator [](int y) const
{
return &items[y * width];
}
Plane() = default;
Plane(int newWidth, int newHeight, Item defaultVal) : width(newWidth), height(newHeight), items(width * height, defaultVal)
{
}
};
class GameSave class GameSave
{ {
public: // number of pixels translated. When translating CELL pixels, shift all CELL grids
vector2d translated = { 0, 0 };
void readOPS(const std::vector<char> &data);
void readPSv(const std::vector<char> &data);
std::pair<bool, std::vector<char>> serialiseOPS() const;
int blockWidth, blockHeight; public:
bool fromNewerVersion; int blockWidth = 0;
int majorVersion, minorVersion; int blockHeight = 0;
bool hasPressure; bool fromNewerVersion = false;
bool hasAmbientHeat; int majorVersion = 0;
int minorVersion = 0;
bool hasPressure = false;
bool hasAmbientHeat = false;
//Simulation data //Simulation data
//int ** particleMap; int particlesCount = 0;
int particlesCount; std::vector<Particle> particles;
Particle * particles; Plane<unsigned char> blockMap;
unsigned char ** blockMap; Plane<float> fanVelX;
float ** fanVelX; Plane<float> fanVelY;
float ** fanVelY; Plane<float> pressure;
float ** pressure; Plane<float> velocityX;
float ** velocityX; Plane<float> velocityY;
float ** velocityY; Plane<float> ambientHeat;
float ** ambientHeat;
//Simulation Options //Simulation Options
bool waterEEnabled; bool waterEEnabled = false;
bool legacyEnable; bool legacyEnable = false;
bool gravityEnable; bool gravityEnable = false;
bool aheatEnable; bool aheatEnable = false;
bool paused; bool paused = false;
int gravityMode; int gravityMode = 0;
float customGravityX; float customGravityX = 0.0f;
float customGravityY; float customGravityY = 0.0f;
int airMode; int airMode = 0;
float ambientAirTemp; float ambientAirTemp = R_TEMP + 273.15f;
int edgeMode; int edgeMode = 0;
//Signs //Signs
std::vector<sign> signs; std::vector<sign> signs;
@ -112,16 +129,13 @@ public:
// author information // author information
Json::Value authors; Json::Value authors;
int pmapbits; int pmapbits = 8; // default to 8 bits for older saves
GameSave();
GameSave(const GameSave & save);
GameSave(int width, int height); GameSave(int width, int height);
GameSave(const std::vector<char> &data); GameSave(const std::vector<char> &data);
~GameSave();
void setSize(int width, int height); void setSize(int width, int height);
char * Serialise(unsigned int & dataSize); // return value is [ fakeFromNewerVersion, gameData ]
std::vector<char> Serialise(); std::pair<bool, std::vector<char>> Serialise() const;
vector2d Translate(vector2d translate); vector2d Translate(vector2d translate);
void Transform(matrix2d transform, vector2d translate); void Transform(matrix2d transform, vector2d translate);
void Transform(matrix2d transform, vector2d translate, vector2d translateReal, int newWidth, int newHeight); void Transform(matrix2d transform, vector2d translate, vector2d translateReal, int newWidth, int newHeight);
@ -135,26 +149,4 @@ public:
GameSave& operator << (Particle &v); GameSave& operator << (Particle &v);
GameSave& operator << (sign &v); GameSave& operator << (sign &v);
private:
// number of pixels translated. When translating CELL pixels, shift all CELL grids
vector2d translated;
void InitData();
void InitVars();
void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen);
void CheckBsonFieldBool(bson_iterator iter, const char *field, bool *flag);
void CheckBsonFieldInt(bson_iterator iter, const char *field, int *setting);
void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting);
template <typename T> T ** Allocate2DArray(int blockWidth, int blockHeight, T defaultVal);
template <typename T> void Deallocate2DArray(T ***array, int blockHeight);
void dealloc();
void read(const char * data, int dataSize);
void readOPS(const char * data, int dataLength);
void readPSv(const char * data, int dataLength);
char * serialiseOPS(unsigned int & dataSize);
void ConvertJsonToBson(bson *b, Json::Value j, int depth = 0);
void ConvertBsonToJson(bson_iterator *b, Json::Value *j, int depth = 0);
}; };
#endif

View File

@ -1203,7 +1203,7 @@ void GameController::OpenLocalSaveWindow(bool asCurrent)
gameModel->SetSaveFile(&tempSave, gameView->ShiftBehaviour()); gameModel->SetSaveFile(&tempSave, gameView->ShiftBehaviour());
Platform::MakeDirectory(LOCAL_SAVE_DIR); Platform::MakeDirectory(LOCAL_SAVE_DIR);
std::vector<char> saveData = gameSave->Serialise(); auto [ _, saveData ] = gameSave->Serialise();
if (saveData.size() == 0) if (saveData.size() == 0)
new ErrorMessage("Error", "Unable to serialize game data."); new ErrorMessage("Error", "Unable to serialize game data.");
else if (!Platform::WriteFile(saveData, gameModel->GetSaveFile()->GetName())) else if (!Platform::WriteFile(saveData, gameModel->GetSaveFile()->GetName()))

View File

@ -112,7 +112,7 @@ void LocalSaveActivity::saveWrite(ByteString finalFilename)
localSaveInfo["date"] = (Json::Value::UInt64)time(NULL); localSaveInfo["date"] = (Json::Value::UInt64)time(NULL);
Client::Ref().SaveAuthorInfo(&localSaveInfo); Client::Ref().SaveAuthorInfo(&localSaveInfo);
gameSave->authors = localSaveInfo; gameSave->authors = localSaveInfo;
std::vector<char> saveData = gameSave->Serialise(); auto [ _, saveData ] = gameSave->Serialise();
if (saveData.size() == 0) if (saveData.size() == 0)
new ErrorMessage("Error", "Unable to serialize game data."); new ErrorMessage("Error", "Unable to serialize game data.");
else if (!Platform::WriteFile(saveData, finalFilename)) else if (!Platform::WriteFile(saveData, finalFilename))

View File

@ -4,7 +4,6 @@
#include "Config.h" #include "Config.h"
//#include "Simulation.h" //#include "Simulation.h"
#define R_TEMP 22
#define MAX_TEMP 9999 #define MAX_TEMP 9999
#define MIN_TEMP 0 #define MIN_TEMP 0
#define O_MAX_TEMP 3500 #define O_MAX_TEMP 3500

View File

@ -11,7 +11,7 @@ sign::sign(String text_, int x_, int y_, Justification justification_):
{ {
} }
String sign::getDisplayText(Simulation *sim, int &x0, int &y0, int &w, int &h, bool colorize, bool *v95) String sign::getDisplayText(Simulation *sim, int &x0, int &y0, int &w, int &h, bool colorize, bool *v95) const
{ {
String drawable_text; String drawable_text;
auto si = std::make_pair(0, Type::Normal); auto si = std::make_pair(0, Type::Normal);
@ -142,7 +142,7 @@ String sign::getDisplayText(Simulation *sim, int &x0, int &y0, int &w, int &h, b
return drawable_text; return drawable_text;
} }
std::pair<int, sign::Type> sign::split() std::pair<int, sign::Type> sign::split() const
{ {
String::size_type pipe = 0; String::size_type pipe = 0;
if (text.size() >= 4 && text.front() == '{' && text.back() == '}') if (text.size() >= 4 && text.front() == '{' && text.back() == '}')

View File

@ -32,8 +32,8 @@ struct sign
String text; String text;
sign(String text_, int x_, int y_, Justification justification_); sign(String text_, int x_, int y_, Justification justification_);
String getDisplayText(Simulation *sim, int &x, int &y, int &w, int &h, bool colorize = true, bool *v95 = nullptr); String getDisplayText(Simulation *sim, int &x, int &y, int &w, int &h, bool colorize = true, bool *v95 = nullptr) const;
std::pair<int, Type> split(); std::pair<int, Type> split() const;
}; };
#endif #endif