From c8bc8a7285d14f6d5adaf4bd8a1e85428e7047bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20B=C3=A1lint=20Misius?= Date: Fri, 12 May 2023 09:00:06 +0200 Subject: [PATCH] Fix some deprecation warnings (#920) Namely: - [[deprecated("Use PlaneAdapter")]] - [[deprecated("Use operator[](Vec2)")]] --- src/client/GameSave.cpp | 1905 +++++++++++++++---------------- src/client/GameSave.h | 52 +- src/gui/render/RenderView.cpp | 2 +- src/simulation/SaveRenderer.cpp | 2 +- src/simulation/Simulation.cpp | 97 +- 5 files changed, 968 insertions(+), 1090 deletions(-) diff --git a/src/client/GameSave.cpp b/src/client/GameSave.cpp index eac313155..92f635e3c 100644 --- a/src/client/GameSave.cpp +++ b/src/client/GameSave.cpp @@ -23,16 +23,14 @@ static void CheckBsonFieldInt(bson_iterator iter, const char *field, int *settin static void CheckBsonFieldLong(bson_iterator iter, const char *field, int64_t *setting); static void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting); -GameSave::GameSave(int width, int height) +GameSave::GameSave(Vec2 newBlockSize) { - setSize(width, height); + setSize(newBlockSize); } GameSave::GameSave(const std::vector &data, bool newWantAuthors) { wantAuthors = newWantAuthors; - blockWidth = 0; - blockHeight = 0; try { @@ -78,23 +76,22 @@ void GameSave::Expand(const std::vector &data) } } -void GameSave::setSize(int newWidth, int newHeight) +void GameSave::setSize(Vec2 newBlockSize) { - blockWidth = newWidth; - blockHeight = newHeight; + blockSize = newBlockSize; particlesCount = 0; particles = std::vector(NPART); - blockMap = Plane(blockWidth, blockHeight, 0); - fanVelX = Plane(blockWidth, blockHeight, 0.0f); - fanVelY = Plane(blockWidth, blockHeight, 0.0f); - pressure = Plane(blockWidth, blockHeight, 0.0f); - velocityX = Plane(blockWidth, blockHeight, 0.0f); - velocityY = Plane(blockWidth, blockHeight, 0.0f); - ambientHeat = Plane(blockWidth, blockHeight, 0.0f); - blockAir = Plane(blockWidth, blockHeight, 0); - blockAirh = Plane(blockWidth, blockHeight, 0); + blockMap = PlaneAdapter>(blockSize, 0); + fanVelX = PlaneAdapter>(blockSize, 0.0f); + fanVelY = PlaneAdapter>(blockSize, 0.0f); + pressure = PlaneAdapter>(blockSize, 0.0f); + velocityX = PlaneAdapter>(blockSize, 0.0f); + velocityY = PlaneAdapter>(blockSize, 0.0f); + ambientHeat = PlaneAdapter>(blockSize, 0.0f); + blockAir = PlaneAdapter>(blockSize, 0); + blockAirh = PlaneAdapter>(blockSize, 0); } std::pair> GameSave::Serialise() const @@ -159,13 +156,13 @@ vector2d GameSave::Translate(vector2d translate) ); int blockBoundsX = int(maxx / CELL) + 1, blockBoundsY = int(maxy / CELL) + 1; vector2d frontCorrection = v2d_new( - float((blockBoundsX > blockWidth) ? (blockBoundsX - blockWidth) : 0), - float((blockBoundsY > blockHeight) ? (blockBoundsY - blockHeight) : 0) + float((blockBoundsX > blockSize.X) ? (blockBoundsX - blockSize.X) : 0), + float((blockBoundsY > blockSize.Y) ? (blockBoundsY - blockSize.Y) : 0) ); // get new width based on corrections - auto newWidth = int((blockWidth + backCorrection.x + frontCorrection.x) * CELL); - auto newHeight = int((blockHeight + backCorrection.y + frontCorrection.y) * CELL); + auto newWidth = int((blockSize.X + backCorrection.x + frontCorrection.x) * CELL); + auto newHeight = int((blockSize.Y + backCorrection.y + frontCorrection.y) * CELL); if (newWidth > XRES) frontCorrection.x = backCorrection.x = 0; if (newHeight > YRES) @@ -173,10 +170,10 @@ vector2d GameSave::Translate(vector2d translate) // call Transform to do the transformation we wanted when calling this function translate = v2d_add(translate, v2d_multiply_float(backCorrection, CELL)); - Transform(m2d_identity, translate, translateReal, - int((blockWidth + backCorrection.x + frontCorrection.x) * CELL), - int((blockHeight + backCorrection.y + frontCorrection.y) * CELL) - ); + Transform(m2d_identity, translate, translateReal, { + int((blockSize.X + backCorrection.x + frontCorrection.x) * CELL), + int((blockSize.Y + backCorrection.y + frontCorrection.y) * CELL) + }); // return how much we corrected. This is used to offset the position of the current stamp // otherwise it would attempt to recenter it with the current size @@ -185,7 +182,7 @@ vector2d GameSave::Translate(vector2d translate) void GameSave::Transform(matrix2d transform, vector2d translate) { - int width = blockWidth*CELL, height = blockHeight*CELL, newWidth, newHeight; + int width = blockSize.X*CELL, height = blockSize.Y*CELL; vector2d tmp, ctl, cbr; vector2d cornerso[4]; vector2d translateReal = translate; @@ -206,31 +203,30 @@ void GameSave::Transform(matrix2d transform, vector2d translate) // casting as int doesn't quite do what we want with negative numbers, so use floor() tmp = v2d_new(floor(ctl.x+0.5f),floor(ctl.y+0.5f)); translate = v2d_sub(translate,tmp); - newWidth = int(floor(cbr.x+0.5f))-int(floor(ctl.x+0.5f))+1; - newHeight = int(floor(cbr.y+0.5f))-int(floor(ctl.y+0.5f))+1; - Transform(transform, translate, translateReal, newWidth, newHeight); + auto newWidth = int(floor(cbr.x+0.5f))-int(floor(ctl.x+0.5f))+1; + auto newHeight = int(floor(cbr.y+0.5f))-int(floor(ctl.y+0.5f))+1; + Transform(transform, translate, translateReal, { newWidth, newHeight }); } // transform is a matrix describing how we want to rotate this save // translate can vary depending on whether the save is bring rotated, or if a normal translate caused it to expand // translateReal is the original amount we tried to translate, used to calculate wall shifting -void GameSave::Transform(matrix2d transform, vector2d translate, vector2d translateReal, int newWidth, int newHeight) +void GameSave::Transform(matrix2d transform, vector2d translate, vector2d translateReal, Vec2 newPartSize) { - if (newWidth>XRES) newWidth = XRES; - if (newHeight>YRES) newHeight = YRES; + if (newPartSize.X>XRES) newPartSize.X = XRES; + if (newPartSize.Y>YRES) newPartSize.Y = YRES; - int x, y, nx, ny, newBlockWidth = newWidth / CELL, newBlockHeight = newHeight / CELL; - vector2d pos, vel; + auto newBlockSize = newPartSize / CELL; - Plane blockMapNew(newBlockWidth, newBlockHeight, 0); - Plane fanVelXNew(newBlockWidth, newBlockHeight, 0.0f); - Plane fanVelYNew(newBlockWidth, newBlockHeight, 0.0f); - Plane pressureNew(newBlockWidth, newBlockHeight, 0.0f); - Plane velocityXNew(newBlockWidth, newBlockHeight, 0.0f); - Plane velocityYNew(newBlockWidth, newBlockHeight, 0.0f); - Plane ambientHeatNew(newBlockWidth, newBlockHeight, 0.0f); - Plane blockAirNew(newBlockWidth, newBlockHeight, 0); - Plane blockAirhNew(newBlockWidth, newBlockHeight, 0); + PlaneAdapter> blockMapNew(newBlockSize, 0); + PlaneAdapter> fanVelXNew(newBlockSize, 0.0f); + PlaneAdapter> fanVelYNew(newBlockSize, 0.0f); + PlaneAdapter> pressureNew(newBlockSize, 0.0f); + PlaneAdapter> velocityXNew(newBlockSize, 0.0f); + PlaneAdapter> velocityYNew(newBlockSize, 0.0f); + PlaneAdapter> ambientHeatNew(newBlockSize, 0.0f); + PlaneAdapter> blockAirNew(newBlockSize, 0); + PlaneAdapter> blockAirhNew(newBlockSize, 0); // Match these up with the matrices provided in GameView::OnKeyPress. bool patchPipeR = transform.a == 0 && transform.b == 1 && transform.c == -1 && transform.d == 0; @@ -240,33 +236,31 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl // rotate and translate signs, parts, walls for (size_t i = 0; i < signs.size(); i++) { - pos = v2d_new(float(signs[i].x), float(signs[i].y)); + auto pos = v2d_new(float(signs[i].x), float(signs[i].y)); pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); - nx = int(floor(pos.x+0.5f)); - ny = int(floor(pos.y+0.5f)); - if (nx<0 || nx>=newWidth || ny<0 || ny>=newHeight) + auto newPos = Vec2{ int(floor(pos.x+0.5f)), int(floor(pos.y+0.5f)) }; + if (!newPartSize.OriginRect().Contains(newPos)) { signs[i].text[0] = 0; continue; } - signs[i].x = nx; - signs[i].y = ny; + signs[i].x = newPos.X; + signs[i].y = newPos.Y; } for (int i = 0; i < particlesCount; i++) { if (!particles[i].type) continue; - pos = v2d_new(particles[i].x, particles[i].y); + auto pos = v2d_new(particles[i].x, particles[i].y); pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); - nx = int(floor(pos.x+0.5f)); - ny = int(floor(pos.y+0.5f)); - if (nx<0 || nx>=newWidth || ny<0 || ny>=newHeight) + auto newPos = Vec2{ int(floor(pos.x+0.5f)), int(floor(pos.y+0.5f)) }; + if (!newPartSize.OriginRect().Contains(newPos)) { particles[i].type = PT_NONE; continue; } - particles[i].x = float(nx); - particles[i].y = float(ny); - vel = v2d_new(particles[i].vx, particles[i].vy); + particles[i].x = float(newPos.X); + particles[i].y = float(newPos.Y); + auto vel = v2d_new(particles[i].vx, particles[i].vy); vel = m2d_multiply_v2d(transform, vel); particles[i].vx = vel.x; particles[i].vy = vel.y; @@ -305,37 +299,34 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl || (translated.y > 0 && (int)translated.y%CELL == 0))) translateY = -CELL; - for (y=0; y=newBlockWidth || pos.y<0 || ny>=newBlockHeight) - continue; - if (blockMap[y][x]) + blockMapNew[newBpos] = blockMap[bpos]; + if (blockMap[bpos]==WL_FAN) { - blockMapNew[ny][nx] = blockMap[y][x]; - if (blockMap[y][x]==WL_FAN) - { - vel = v2d_new(fanVelX[y][x], fanVelY[y][x]); - vel = m2d_multiply_v2d(transform, vel); - fanVelXNew[ny][nx] = vel.x; - fanVelYNew[ny][nx] = vel.y; - } + auto vel = v2d_new(fanVelX[bpos], fanVelY[bpos]); + vel = m2d_multiply_v2d(transform, vel); + fanVelXNew[newBpos] = vel.x; + fanVelYNew[newBpos] = vel.y; } - pressureNew[ny][nx] = pressure[y][x]; - velocityXNew[ny][nx] = velocityX[y][x]; - velocityYNew[ny][nx] = velocityY[y][x]; - ambientHeatNew[ny][nx] = ambientHeat[y][x]; - blockAirNew[ny][nx] = blockAir[y][x]; - blockAirhNew[ny][nx] = blockAirh[y][x]; } + pressureNew[newBpos] = pressure[bpos]; + velocityXNew[newBpos] = velocityX[bpos]; + velocityYNew[newBpos] = velocityY[bpos]; + ambientHeatNew[newBpos] = ambientHeat[bpos]; + blockAirNew[newBpos] = blockAir[bpos]; + blockAirhNew[newBpos] = blockAirh[bpos]; + } translated = v2d_add(m2d_multiply_v2d(transform, translated), translateReal); - blockWidth = newBlockWidth; - blockHeight = newBlockHeight; + blockSize = newBlockSize; blockMap = blockMapNew; fanVelX = fanVelXNew; @@ -432,7 +423,6 @@ void GameSave::readOPS(const std::vector &data) unsigned int inputDataLen = data.size(), bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen; unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen, blockAirDataLen; unsigned partsCount = 0; - unsigned int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH; int savedVersion = inputData[4]; majorVersion = savedVersion; minorVersion = 0; @@ -446,16 +436,12 @@ void GameSave::readOPS(const std::vector &data) std::unique_ptr b_ptr(&b, bson_deleter); //Block sizes - blockX = 0; - blockY = 0; - blockW = inputData[6]; - blockH = inputData[7]; + auto blockP = Vec2{ 0, 0 }; + auto blockS = Vec2{ int(inputData[6]), int(inputData[7]) }; //Full size, normalised - fullX = blockX*CELL; - fullY = blockY*CELL; - fullW = blockW*CELL; - fullH = blockH*CELL; + auto partP = blockP * CELL; + auto partS = blockS * CELL; //From newer version if (savedVersion > SAVE_VERSION) @@ -468,14 +454,14 @@ void GameSave::readOPS(const std::vector &data) if (inputData[5] != CELL) throw ParseException(ParseException::InvalidDimensions, "Incorrect CELL size"); - if (blockW <= 0 || blockH <= 0) - throw ParseException(ParseException::InvalidDimensions, "Save too small"); + if (!RectBetween({ 0, 0 }, CELLS).Contains(blockS)) + throw ParseException(ParseException::InvalidDimensions, "Save is of invalid size"); //Too large/off screen - if (blockX+blockW > XCELLS || blockY+blockH > YCELLS) - throw ParseException(ParseException::InvalidDimensions, "Save too large"); + if (!RectBetween({ 0, 0 }, CELLS).Contains(blockP + blockS)) + throw ParseException(ParseException::InvalidDimensions, "Save extends beyond canvas"); - setSize(blockW, blockH); + setSize(blockS); bsonDataLen = ((unsigned)inputData[8]); bsonDataLen |= ((unsigned)inputData[9]) << 8; @@ -592,11 +578,11 @@ void GameSave::readOPS(const std::vector &data) } else if (!strcmp(bson_iterator_key(&signiter), "x") && bson_iterator_type(&signiter) == BSON_INT) { - tempSign.x = bson_iterator_int(&signiter)+fullX; + tempSign.x = bson_iterator_int(&signiter)+partP.X; } else if (!strcmp(bson_iterator_key(&signiter), "y") && bson_iterator_type(&signiter) == BSON_INT) { - tempSign.y = bson_iterator_int(&signiter)+fullY; + tempSign.y = bson_iterator_int(&signiter)+partP.Y; } else { @@ -749,62 +735,51 @@ void GameSave::readOPS(const std::vector &data) //Read wall and fan data if(wallData) { + // TODO: use PlaneAdapter> once we're C++20 + auto wallDataPlane = PlaneAdapter>(blockS, std::in_place, wallData, blockS.X * blockS.Y); unsigned int j = 0; - if (blockW * blockH > wallDataLen) + if (blockS.X * blockS.Y > int(wallDataLen)) throw ParseException(ParseException::Corrupt, "Not enough wall data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) + unsigned char bm = 0; + if (wallDataPlane[bpos]) + bm = wallDataPlane[bpos]; + + switch (bm) { - if (wallData[y*blockW+x]) - blockMap[blockY+y][blockX+x] = wallData[y*blockW+x]; - - if (blockMap[y][x]==O_WL_WALLELEC) - blockMap[y][x]=WL_WALLELEC; - if (blockMap[y][x]==O_WL_EWALL) - blockMap[y][x]=WL_EWALL; - if (blockMap[y][x]==O_WL_DETECT) - blockMap[y][x]=WL_DETECT; - if (blockMap[y][x]==O_WL_STREAM) - blockMap[y][x]=WL_STREAM; - if (blockMap[y][x]==O_WL_FAN||blockMap[y][x]==O_WL_FANHELPER) - blockMap[y][x]=WL_FAN; - if (blockMap[y][x]==O_WL_ALLOWLIQUID) - blockMap[y][x]=WL_ALLOWLIQUID; - if (blockMap[y][x]==O_WL_DESTROYALL) - blockMap[y][x]=WL_DESTROYALL; - if (blockMap[y][x]==O_WL_ERASE) - blockMap[y][x]=WL_ERASE; - if (blockMap[y][x]==O_WL_WALL) - blockMap[y][x]=WL_WALL; - if (blockMap[y][x]==O_WL_ALLOWAIR) - blockMap[y][x]=WL_ALLOWAIR; - if (blockMap[y][x]==O_WL_ALLOWSOLID) - blockMap[y][x]=WL_ALLOWPOWDER; - if (blockMap[y][x]==O_WL_ALLOWALLELEC) - blockMap[y][x]=WL_ALLOWALLELEC; - if (blockMap[y][x]==O_WL_EHOLE) - blockMap[y][x]=WL_EHOLE; - if (blockMap[y][x]==O_WL_ALLOWGAS) - blockMap[y][x]=WL_ALLOWGAS; - if (blockMap[y][x]==O_WL_GRAV) - blockMap[y][x]=WL_GRAV; - if (blockMap[y][x]==O_WL_ALLOWENERGY) - blockMap[y][x]=WL_ALLOWENERGY; - - if (blockMap[y][x] == WL_FAN && fanData) - { - if(j+1 >= fanDataLen) - { - fprintf(stderr, "Not enough fan data\n"); - } - fanVelX[blockY+y][blockX+x] = (fanData[j++]-127.0f)/64.0f; - fanVelY[blockY+y][blockX+x] = (fanData[j++]-127.0f)/64.0f; - } - - if (blockMap[y][x] >= UI_WALLCOUNT) - blockMap[y][x] = 0; + case O_WL_WALLELEC: bm = WL_WALLELEC; break; + case O_WL_EWALL: bm = WL_EWALL; break; + case O_WL_DETECT: bm = WL_DETECT; break; + case O_WL_STREAM: bm = WL_STREAM; break; + case O_WL_FAN: + case O_WL_FANHELPER: bm = WL_FAN; break; + case O_WL_ALLOWLIQUID: bm = WL_ALLOWLIQUID; break; + case O_WL_DESTROYALL: bm = WL_DESTROYALL; break; + case O_WL_ERASE: bm = WL_ERASE; break; + case O_WL_WALL: bm = WL_WALL; break; + case O_WL_ALLOWAIR: bm = WL_ALLOWAIR; break; + case O_WL_ALLOWSOLID: bm = WL_ALLOWPOWDER; break; + case O_WL_ALLOWALLELEC: bm = WL_ALLOWALLELEC; break; + case O_WL_EHOLE: bm = WL_EHOLE; break; + case O_WL_ALLOWGAS: bm = WL_ALLOWGAS; break; + case O_WL_GRAV: bm = WL_GRAV; break; + case O_WL_ALLOWENERGY: bm = WL_ALLOWENERGY; break; } + + if (bm == WL_FAN && fanData) + { + if(j+1 >= fanDataLen) + { + fprintf(stderr, "Not enough fan data\n"); + } + fanVelX[blockP + bpos] = (fanData[j++]-127.0f)/64.0f; + fanVelY[blockP + bpos] = (fanData[j++]-127.0f)/64.0f; + } + + if (bm >= UI_WALLCOUNT) + bm = 0; + blockMap[blockP + bpos] = bm; } } @@ -813,16 +788,13 @@ void GameSave::readOPS(const std::vector &data) { unsigned int j = 0; unsigned char i, i2; - if (blockW * blockH > pressDataLen) + if (blockS.X * blockS.Y > int(pressDataLen)) throw ParseException(ParseException::Corrupt, "Not enough pressure data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - i = pressData[j++]; - i2 = pressData[j++]; - pressure[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256; - } + i = pressData[j++]; + i2 = pressData[j++]; + pressure[blockP + bpos] = ((i+(i2<<8))/128.0f)-256; } hasPressure = true; } @@ -832,16 +804,13 @@ void GameSave::readOPS(const std::vector &data) { unsigned int j = 0; unsigned char i, i2; - if (blockW * blockH > vxDataLen) + if (blockS.X * blockS.Y > int(vxDataLen)) throw ParseException(ParseException::Corrupt, "Not enough vx data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - i = vxData[j++]; - i2 = vxData[j++]; - velocityX[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256; - } + i = vxData[j++]; + i2 = vxData[j++]; + velocityX[blockP + bpos] = ((i+(i2<<8))/128.0f)-256; } } @@ -850,16 +819,13 @@ void GameSave::readOPS(const std::vector &data) { unsigned int j = 0; unsigned char i, i2; - if (blockW * blockH > vyDataLen) + if (blockS.X * blockS.Y > int(vyDataLen)) throw ParseException(ParseException::Corrupt, "Not enough vy data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - i = vyData[j++]; - i2 = vyData[j++]; - velocityY[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256; - } + i = vyData[j++]; + i2 = vyData[j++]; + velocityY[blockP + bpos] = ((i+(i2<<8))/128.0f)-256; } } @@ -867,32 +833,28 @@ void GameSave::readOPS(const std::vector &data) if (ambientData) { unsigned int i = 0, tempTemp; - if (blockW * blockH > ambientDataLen) + if (blockS.X * blockS.Y > int(ambientDataLen)) throw ParseException(ParseException::Corrupt, "Not enough ambient heat data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - tempTemp = ambientData[i++]; - tempTemp |= (((unsigned)ambientData[i++]) << 8); - ambientHeat[blockY+y][blockX+x] = float(tempTemp); - } + tempTemp = ambientData[i++]; + tempTemp |= (((unsigned)ambientData[i++]) << 8); + ambientHeat[blockP + bpos] = float(tempTemp); } hasAmbientHeat = true; } if (blockAirData) { - if (blockW * blockH * 2 > blockAirDataLen) + if (blockS.X * blockS.Y * 2 > int(blockAirDataLen)) throw ParseException(ParseException::Corrupt, "Not enough block air data"); - auto blockAirhData = blockAirData + blockW * blockH; - for (unsigned int x = 0; x < blockW; x++) + // TODO: use PlaneAdapter> once we're C++20 + auto blockAirDataPlane = PlaneAdapter>(blockS, std::in_place, blockAirData, blockS.X * blockS.Y); + auto blockAirhDataPlane = PlaneAdapter>(blockS, std::in_place, blockAirData + blockS.X * blockS.Y, blockS.X * blockS.Y); + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - blockAir[blockY + y][blockX + x] = blockAirData[y * blockW + x]; - blockAirh[blockY + y][blockX + x] = blockAirhData[y * blockW + x]; - } + blockAir[blockP + bpos] = blockAirDataPlane[bpos]; + blockAirh[blockP + bpos] = blockAirhDataPlane[bpos]; } hasBlockAirMaps = true; } @@ -902,350 +864,342 @@ void GameSave::readOPS(const std::vector &data) { int newIndex = 0, tempTemp; int posCount, posTotal, partsPosDataIndex = 0; - if (fullW * fullH * 3 > partsPosDataLen) + if (partS.X * partS.Y * 3 > int(partsPosDataLen)) throw ParseException(ParseException::Corrupt, "Not enough particle position data"); partsCount = 0; unsigned int i = 0; - unsigned int saved_x, saved_y, x, y; newIndex = 0; - for (saved_y = 0; saved_y < fullH; saved_y++) + for (auto pos : RectSized(partP, partS).Range()) { - for (saved_x = 0; saved_x < fullW; saved_x++) + //Read total number of particles at this position + posTotal = 0; + posTotal |= partsPosData[partsPosDataIndex++]<<16; + posTotal |= partsPosData[partsPosDataIndex++]<<8; + posTotal |= partsPosData[partsPosDataIndex++]; + //Put the next posTotal particles at this position + for (posCount = 0; posCount < posTotal; posCount++) { - //Read total number of particles at this position - posTotal = 0; - posTotal |= partsPosData[partsPosDataIndex++]<<16; - posTotal |= partsPosData[partsPosDataIndex++]<<8; - posTotal |= partsPosData[partsPosDataIndex++]; - //Put the next posTotal particles at this position - for (posCount = 0; posCount < posTotal; posCount++) + particlesCount = newIndex+1; + //i+3 because we have 4 bytes of required fields (type (1), descriptor (2), temp (1)) + if (i+3 >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer"); + unsigned int fieldDescriptor = (unsigned int)(partsData[i+1]); + fieldDescriptor |= (unsigned int)(partsData[i+2]) << 8; + + if (newIndex < 0 || newIndex >= NPART) + throw ParseException(ParseException::Corrupt, "Too many particles"); + + //Clear the particle, ready for our new properties + memset(&(particles[newIndex]), 0, sizeof(Particle)); + + //Required fields + particles[newIndex].type = partsData[i]; + particles[newIndex].x = float(pos.X); + particles[newIndex].y = float(pos.Y); + i+=3; + + // Read type (2nd byte) + if (fieldDescriptor & 0x4000) + particles[newIndex].type |= (((unsigned)partsData[i++]) << 8); + + //Read temp + if(fieldDescriptor & 0x01) { - particlesCount = newIndex+1; - //i+3 because we have 4 bytes of required fields (type (1), descriptor (2), temp (1)) - if (i+3 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer"); - x = saved_x + fullX; - y = saved_y + fullY; - unsigned int fieldDescriptor = (unsigned int)(partsData[i+1]); - fieldDescriptor |= (unsigned int)(partsData[i+2]) << 8; - if (x >= fullW || y >= fullH) - throw ParseException(ParseException::Corrupt, "Particle out of range"); - - if (newIndex < 0 || newIndex >= NPART) - throw ParseException(ParseException::Corrupt, "Too many particles"); - - //Clear the particle, ready for our new properties - memset(&(particles[newIndex]), 0, sizeof(Particle)); - - //Required fields - particles[newIndex].type = partsData[i]; - particles[newIndex].x = float(x); - particles[newIndex].y = float(y); - i+=3; - - // Read type (2nd byte) - if (fieldDescriptor & 0x4000) - particles[newIndex].type |= (((unsigned)partsData[i++]) << 8); - - //Read temp - if(fieldDescriptor & 0x01) + //Full 16bit int + tempTemp = partsData[i++]; + tempTemp |= (((unsigned)partsData[i++]) << 8); + particles[newIndex].temp = float(tempTemp); + } + else + { + //1 Byte room temp offset + tempTemp = partsData[i++]; + if (tempTemp >= 0x80) { - //Full 16bit int - tempTemp = partsData[i++]; - tempTemp |= (((unsigned)partsData[i++]) << 8); - particles[newIndex].temp = float(tempTemp); - } - else - { - //1 Byte room temp offset - tempTemp = partsData[i++]; - if (tempTemp >= 0x80) - { - tempTemp -= 0x100; - } - particles[newIndex].temp = tempTemp+294.15f; + tempTemp -= 0x100; } + particles[newIndex].temp = tempTemp+294.15f; + } - // fieldDesc3 - if (fieldDescriptor & 0x8000) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading third byte of field descriptor"); - fieldDescriptor |= (unsigned int)(partsData[i++]) << 16; - } + // fieldDesc3 + if (fieldDescriptor & 0x8000) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading third byte of field descriptor"); + fieldDescriptor |= (unsigned int)(partsData[i++]) << 16; + } - //Read life - if(fieldDescriptor & 0x02) + //Read life + if(fieldDescriptor & 0x02) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading life"); + particles[newIndex].life = partsData[i++]; + //i++; + //Read 2nd byte + if(fieldDescriptor & 0x04) { if (i >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading life"); - particles[newIndex].life = partsData[i++]; - //i++; - //Read 2nd byte - if(fieldDescriptor & 0x04) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading life"); - particles[newIndex].life |= (((unsigned)partsData[i++]) << 8); - } + particles[newIndex].life |= (((unsigned)partsData[i++]) << 8); } + } - //Read tmp - if(fieldDescriptor & 0x08) + //Read tmp + if(fieldDescriptor & 0x08) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp"); + particles[newIndex].tmp = partsData[i++]; + //Read 2nd byte + if(fieldDescriptor & 0x10) { if (i >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp"); - particles[newIndex].tmp = partsData[i++]; - //Read 2nd byte - if(fieldDescriptor & 0x10) + particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 8); + //Read 3rd and 4th bytes + if(fieldDescriptor & 0x1000) { - if (i >= partsDataLen) + if (i+1 >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp"); - particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 8); - //Read 3rd and 4th bytes - if(fieldDescriptor & 0x1000) - { - if (i+1 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp"); - particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 24); - particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 16); - } + particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 24); + particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 16); } } + } - //Read ctype - if(fieldDescriptor & 0x20) + //Read ctype + if(fieldDescriptor & 0x20) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading ctype"); + particles[newIndex].ctype = partsData[i++]; + //Read additional bytes + if(fieldDescriptor & 0x200) { - if (i >= partsDataLen) + if (i+2 >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading ctype"); - particles[newIndex].ctype = partsData[i++]; - //Read additional bytes - if(fieldDescriptor & 0x200) - { - if (i+2 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading ctype"); - particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 24); - particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 16); - particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 8); - } + particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 24); + particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 16); + particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 8); } + } - //Read dcolour - if(fieldDescriptor & 0x40) - { - if (i+3 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading deco"); - particles[newIndex].dcolour = (((unsigned)partsData[i++]) << 24); - particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 16); - particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 8); - particles[newIndex].dcolour |= ((unsigned)partsData[i++]); - } + //Read dcolour + if(fieldDescriptor & 0x40) + { + if (i+3 >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading deco"); + particles[newIndex].dcolour = (((unsigned)partsData[i++]) << 24); + particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 16); + particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 8); + particles[newIndex].dcolour |= ((unsigned)partsData[i++]); + } - //Read vx - if(fieldDescriptor & 0x80) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vx"); - particles[newIndex].vx = (partsData[i++]-127.0f)/16.0f; - } + //Read vx + if(fieldDescriptor & 0x80) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vx"); + particles[newIndex].vx = (partsData[i++]-127.0f)/16.0f; + } - //Read vy - if(fieldDescriptor & 0x100) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vy"); - particles[newIndex].vy = (partsData[i++]-127.0f)/16.0f; - } + //Read vy + if(fieldDescriptor & 0x100) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vy"); + particles[newIndex].vy = (partsData[i++]-127.0f)/16.0f; + } - //Read tmp2 - if(fieldDescriptor & 0x400) + //Read tmp2 + if(fieldDescriptor & 0x400) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp2"); + particles[newIndex].tmp2 = partsData[i++]; + if(fieldDescriptor & 0x800) { if (i >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp2"); - particles[newIndex].tmp2 = partsData[i++]; - if(fieldDescriptor & 0x800) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp2"); - particles[newIndex].tmp2 |= (((unsigned)partsData[i++]) << 8); - } + particles[newIndex].tmp2 |= (((unsigned)partsData[i++]) << 8); } + } - //Read tmp3 and tmp4 - if(fieldDescriptor & 0x2000) + //Read tmp3 and tmp4 + if(fieldDescriptor & 0x2000) + { + if (i+3 >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp3 and tmp4"); + if (fieldDescriptor & 0x10000 && i+7 >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading high halves of tmp3 and tmp4"); + unsigned int tmp34; + tmp34 = (unsigned int)partsData[i + 0]; + tmp34 |= (unsigned int)partsData[i + 1] << 8; + if (fieldDescriptor & 0x10000) { - if (i+3 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp3 and tmp4"); - if (fieldDescriptor & 0x10000 && i+7 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading high halves of tmp3 and tmp4"); - unsigned int tmp34; - tmp34 = (unsigned int)partsData[i + 0]; - tmp34 |= (unsigned int)partsData[i + 1] << 8; - if (fieldDescriptor & 0x10000) - { - tmp34 |= (unsigned int)partsData[i + 4] << 16; - tmp34 |= (unsigned int)partsData[i + 5] << 24; - } - particles[newIndex].tmp3 = int(tmp34); - tmp34 = (unsigned int)partsData[i + 2]; - tmp34 |= (unsigned int)partsData[i + 3] << 8; - if (fieldDescriptor & 0x10000) - { - tmp34 |= (unsigned int)partsData[i + 6] << 16; - tmp34 |= (unsigned int)partsData[i + 7] << 24; - } - particles[newIndex].tmp4 = int(tmp34); + tmp34 |= (unsigned int)partsData[i + 4] << 16; + tmp34 |= (unsigned int)partsData[i + 5] << 24; + } + particles[newIndex].tmp3 = int(tmp34); + tmp34 = (unsigned int)partsData[i + 2]; + tmp34 |= (unsigned int)partsData[i + 3] << 8; + if (fieldDescriptor & 0x10000) + { + tmp34 |= (unsigned int)partsData[i + 6] << 16; + tmp34 |= (unsigned int)partsData[i + 7] << 24; + } + particles[newIndex].tmp4 = int(tmp34); + i += 4; + if (fieldDescriptor & 0x10000) i += 4; - if (fieldDescriptor & 0x10000) - i += 4; - } + } - //Particle specific parsing: - switch(particles[newIndex].type) + //Particle specific parsing: + switch(particles[newIndex].type) + { + case PT_SOAP: + //Clear soap links, links will be added back in if soapLinkData is present + particles[newIndex].ctype &= ~6; + break; + case PT_BOMB: + if (particles[newIndex].tmp!=0 && savedVersion < 81) { - case PT_SOAP: - //Clear soap links, links will be added back in if soapLinkData is present - particles[newIndex].ctype &= ~6; - break; - case PT_BOMB: - if (particles[newIndex].tmp!=0 && savedVersion < 81) + particles[newIndex].type = PT_EMBR; + particles[newIndex].ctype = 0; + if (particles[newIndex].tmp==1) + particles[newIndex].tmp = 0; + } + break; + case PT_DUST: + if (particles[newIndex].life>0 && savedVersion < 81) + { + particles[newIndex].type = PT_EMBR; + particles[newIndex].ctype = (particles[newIndex].tmp2<<16) | (particles[newIndex].tmp<<8) | particles[newIndex].ctype; + particles[newIndex].tmp = 1; + } + break; + case PT_FIRW: + if (particles[newIndex].tmp>=2 && savedVersion < 81) + { + particles[newIndex].type = PT_EMBR; + particles[newIndex].ctype = Renderer::firwTableAt(particles[newIndex].tmp - 4).Pack(); + particles[newIndex].tmp = 1; + } + break; + case PT_PSTN: + if (savedVersion < 87 && particles[newIndex].ctype) + particles[newIndex].life = 1; + if (savedVersion < 91) + particles[newIndex].temp = 283.15f; + break; + case PT_FILT: + if (savedVersion < 89) + { + if (particles[newIndex].tmp<0 || particles[newIndex].tmp>3) + particles[newIndex].tmp = 6; + particles[newIndex].ctype = 0; + } + break; + case PT_QRTZ: + case PT_PQRT: + if (savedVersion < 89) + { + particles[newIndex].tmp2 = particles[newIndex].tmp; + particles[newIndex].tmp = particles[newIndex].ctype; + particles[newIndex].ctype = 0; + } + break; + case PT_PHOT: + if (savedVersion < 90) + { + particles[newIndex].flags |= FLAG_PHOTDECO; + } + break; + case PT_VINE: + if (savedVersion < 91) + { + particles[newIndex].tmp = 1; + } + break; + case PT_DLAY: + // correct DLAY temperature in older saves + // due to either the +.5f now done in DLAY (higher temps), or rounding errors in the old DLAY code (room temperature temps), + // the delay in all DLAY from older versions will always be one greater than it should + if (savedVersion < 91) + { + particles[newIndex].temp = particles[newIndex].temp - 1.0f; + } + break; + case PT_CRAY: + if (savedVersion < 91) + { + if (particles[newIndex].tmp2) { - particles[newIndex].type = PT_EMBR; - particles[newIndex].ctype = 0; - if (particles[newIndex].tmp==1) - particles[newIndex].tmp = 0; + particles[newIndex].ctype |= particles[newIndex].tmp2<<8; + particles[newIndex].tmp2 = 0; } - break; - case PT_DUST: - if (particles[newIndex].life>0 && savedVersion < 81) - { - particles[newIndex].type = PT_EMBR; - particles[newIndex].ctype = (particles[newIndex].tmp2<<16) | (particles[newIndex].tmp<<8) | particles[newIndex].ctype; - particles[newIndex].tmp = 1; - } - break; - case PT_FIRW: - if (particles[newIndex].tmp>=2 && savedVersion < 81) - { - particles[newIndex].type = PT_EMBR; - particles[newIndex].ctype = Renderer::firwTableAt(particles[newIndex].tmp - 4).Pack(); - particles[newIndex].tmp = 1; - } - break; - case PT_PSTN: - if (savedVersion < 87 && particles[newIndex].ctype) - particles[newIndex].life = 1; - if (savedVersion < 91) - particles[newIndex].temp = 283.15f; - break; - case PT_FILT: - if (savedVersion < 89) - { - if (particles[newIndex].tmp<0 || particles[newIndex].tmp>3) - particles[newIndex].tmp = 6; - particles[newIndex].ctype = 0; - } - break; - case PT_QRTZ: - case PT_PQRT: - if (savedVersion < 89) - { - particles[newIndex].tmp2 = particles[newIndex].tmp; - particles[newIndex].tmp = particles[newIndex].ctype; - particles[newIndex].ctype = 0; - } - break; - case PT_PHOT: - if (savedVersion < 90) - { - particles[newIndex].flags |= FLAG_PHOTDECO; - } - break; - case PT_VINE: - if (savedVersion < 91) - { - particles[newIndex].tmp = 1; - } - break; - case PT_DLAY: - // correct DLAY temperature in older saves - // due to either the +.5f now done in DLAY (higher temps), or rounding errors in the old DLAY code (room temperature temps), - // the delay in all DLAY from older versions will always be one greater than it should - if (savedVersion < 91) - { - particles[newIndex].temp = particles[newIndex].temp - 1.0f; - } - break; - case PT_CRAY: - if (savedVersion < 91) - { - if (particles[newIndex].tmp2) - { - particles[newIndex].ctype |= particles[newIndex].tmp2<<8; - particles[newIndex].tmp2 = 0; - } - } - break; - case PT_CONV: - if (savedVersion < 91) - { - if (particles[newIndex].tmp) - { - particles[newIndex].ctype |= particles[newIndex].tmp<<8; - particles[newIndex].tmp = 0; - } - } - break; - case PT_PIPE: - case PT_PPIP: - if (savedVersion < 93 && !fakeNewerVersion) - { - if (particles[newIndex].ctype == 1) - particles[newIndex].tmp |= 0x00020000; //PFLAG_INITIALIZING - particles[newIndex].tmp |= (particles[newIndex].ctype-1)<<18; - particles[newIndex].ctype = particles[newIndex].tmp&0xFF; - } - break; - case PT_TSNS: - case PT_HSWC: - case PT_PSNS: - case PT_PUMP: - if (savedVersion < 93 && !fakeNewerVersion) + } + break; + case PT_CONV: + if (savedVersion < 91) + { + if (particles[newIndex].tmp) { + particles[newIndex].ctype |= particles[newIndex].tmp<<8; particles[newIndex].tmp = 0; } - break; - case PT_LIFE: - if (savedVersion < 96 && !fakeNewerVersion) - { - if (particles[newIndex].ctype >= 0 && particles[newIndex].ctype < NGOL) - { - particles[newIndex].tmp2 = particles[newIndex].tmp; - if (!particles[newIndex].dcolour) - particles[newIndex].dcolour = builtinGol[particles[newIndex].ctype].colour.Pack(); - particles[newIndex].tmp = builtinGol[particles[newIndex].ctype].colour2.Pack(); - } - } } - if (PressureInTmp3(particles[newIndex].type)) + break; + case PT_PIPE: + case PT_PPIP: + if (savedVersion < 93 && !fakeNewerVersion) { - // pavg[1] used to be saved as a u16, which PressureInTmp3 elements then treated as - // an i16. tmp3 is now saved as a u32, or as a u16 if it's small enough. PressureInTmp3 - // elements will never use the upper 16 bits, and should still treat the lower 16 bits - // as an i16, so they need sign extension. - auto tmp3 = (unsigned int)(particles[newIndex].tmp3); - if (tmp3 & 0x8000U) + if (particles[newIndex].ctype == 1) + particles[newIndex].tmp |= 0x00020000; //PFLAG_INITIALIZING + particles[newIndex].tmp |= (particles[newIndex].ctype-1)<<18; + particles[newIndex].ctype = particles[newIndex].tmp&0xFF; + } + break; + case PT_TSNS: + case PT_HSWC: + case PT_PSNS: + case PT_PUMP: + if (savedVersion < 93 && !fakeNewerVersion) + { + particles[newIndex].tmp = 0; + } + break; + case PT_LIFE: + if (savedVersion < 96 && !fakeNewerVersion) + { + if (particles[newIndex].ctype >= 0 && particles[newIndex].ctype < NGOL) { - tmp3 |= 0xFFFF0000U; - particles[newIndex].tmp3 = int(tmp3); + particles[newIndex].tmp2 = particles[newIndex].tmp; + if (!particles[newIndex].dcolour) + particles[newIndex].dcolour = builtinGol[particles[newIndex].ctype].colour.Pack(); + particles[newIndex].tmp = builtinGol[particles[newIndex].ctype].colour2.Pack(); } } - //note: PSv was used in version 77.0 and every version before, add something in PSv too if the element is that old - newIndex++; - partsCount++; } + if (PressureInTmp3(particles[newIndex].type)) + { + // pavg[1] used to be saved as a u16, which PressureInTmp3 elements then treated as + // an i16. tmp3 is now saved as a u32, or as a u16 if it's small enough. PressureInTmp3 + // elements will never use the upper 16 bits, and should still treat the lower 16 bits + // as an i16, so they need sign extension. + auto tmp3 = (unsigned int)(particles[newIndex].tmp3); + if (tmp3 & 0x8000U) + { + tmp3 |= 0xFFFF0000U; + particles[newIndex].tmp3 = int(tmp3); + } + } + //note: PSv was used in version 77.0 and every version before, add something in PSv too if the element is that old + newIndex++; + partsCount++; } } @@ -1299,8 +1253,8 @@ void GameSave::readPSv(const std::vector &dataVec) 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 bx0=0, by0=0, bw, bh, w, h, y0 = 0, x0 = 0; + int q,p=0, ver, pty, ty, legacy_beta=0; + Vec2 blockP = { 0, 0 }; int new_format = 0, ttv = 0; std::vector tempSigns; @@ -1350,18 +1304,10 @@ void GameSave::readPSv(const std::vector &dataVec) } } - bw = saveData[6]; - bh = saveData[7]; - if (bx0+bw > XCELLS) - bx0 = XCELLS - bw; - if (by0+bh > YCELLS) - by0 = YCELLS - bh; - if (bx0 < 0) - bx0 = 0; - if (by0 < 0) - by0 = 0; + auto blockS = Vec2{ int(saveData[6]), int(saveData[7]) }; + blockP = blockP.Clamp(blockS.OriginRect()); - if (saveData[5]!=CELL || bx0+bw>XCELLS || by0+bh>YCELLS) + if (saveData[5]!=CELL || blockP.X+blockS.X>XCELLS || blockP.Y+blockS.Y>YCELLS) throw ParseException(ParseException::InvalidDimensions, "Save too large"); int size = (unsigned)saveData[8]; size |= ((unsigned)saveData[9])<<8; @@ -1379,7 +1325,7 @@ void GameSave::readPSv(const std::vector &dataVec) default: throw ParseException(ParseException::Corrupt, String::Build("Cannot decompress: status ", int(status))); } - setSize(bw, bh); + setSize(blockS); const auto *data = reinterpret_cast(&bsonData[0]); dataLength = bsonData.size(); @@ -1388,135 +1334,116 @@ void GameSave::readPSv(const std::vector &dataVec) std::cout << "Parsing " << dataLength << " bytes of data, version " << ver << std::endl; } - if (dataLength < bw*bh) + if (dataLength < blockS.X*blockS.Y) throw ParseException(ParseException::Corrupt, "Save data corrupt (missing data)"); // normalize coordinates - x0 = bx0*CELL; - y0 = by0*CELL; - w = bw *CELL; - h = bh *CELL; + auto partS = blockS * CELL; + auto partP = blockP * CELL; if (ver<46) { gravityMode = 0; airMode = 0; } - std::vector particleIDMap(XRES * YRES, 0); + PlaneAdapter> particleIDMap(RES, 0); // load the required air state - for (y=by0; y()) + { + if (data[p]) { - if (data[p]) + //In old saves, ignore walls created by sign tool bug + //Not ignoring other invalid walls or invalid walls in new saves, so that any other bugs causing them are easier to notice, find and fix + if (ver>=44 && ver<71 && data[p]==O_WL_SIGN) { - //In old saves, ignore walls created by sign tool bug - //Not ignoring other invalid walls or invalid walls in new saves, so that any other bugs causing them are easier to notice, find and fix - if (ver>=44 && ver<71 && data[p]==O_WL_SIGN) - { - p++; - continue; - } - blockMap[y][x] = data[p]; - if (blockMap[y][x]==1) - blockMap[y][x]=WL_WALL; - else if (blockMap[y][x]==2) - blockMap[y][x]=WL_DESTROYALL; - else if (blockMap[y][x]==3) - blockMap[y][x]=WL_ALLOWLIQUID; - else if (blockMap[y][x]==4) - blockMap[y][x]=WL_FAN; - else if (blockMap[y][x]==5) - blockMap[y][x]=WL_STREAM; - else if (blockMap[y][x]==6) - blockMap[y][x]=WL_DETECT; - else if (blockMap[y][x]==7) - blockMap[y][x]=WL_EWALL; - else if (blockMap[y][x]==8) - blockMap[y][x]=WL_WALLELEC; - else if (blockMap[y][x]==9) - blockMap[y][x]=WL_ALLOWAIR; - else if (blockMap[y][x]==10) - blockMap[y][x]=WL_ALLOWPOWDER; - else if (blockMap[y][x]==11) - blockMap[y][x]=WL_ALLOWALLELEC; - else if (blockMap[y][x]==12) - blockMap[y][x]=WL_EHOLE; - else if (blockMap[y][x]==13) - blockMap[y][x]=WL_ALLOWGAS; - - if (ver>=44) - { - /* The numbers used to save walls were changed, starting in v44. - * The new numbers are ignored for older versions due to some corruption of bmap in saves from older versions. - */ - if (blockMap[y][x]==O_WL_WALLELEC) - blockMap[y][x]=WL_WALLELEC; - else if (blockMap[y][x]==O_WL_EWALL) - blockMap[y][x]=WL_EWALL; - else if (blockMap[y][x]==O_WL_DETECT) - blockMap[y][x]=WL_DETECT; - else if (blockMap[y][x]==O_WL_STREAM) - blockMap[y][x]=WL_STREAM; - else if (blockMap[y][x]==O_WL_FAN||blockMap[y][x]==O_WL_FANHELPER) - blockMap[y][x]=WL_FAN; - else if (blockMap[y][x]==O_WL_ALLOWLIQUID) - blockMap[y][x]=WL_ALLOWLIQUID; - else if (blockMap[y][x]==O_WL_DESTROYALL) - blockMap[y][x]=WL_DESTROYALL; - else if (blockMap[y][x]==O_WL_ERASE) - blockMap[y][x]=WL_ERASE; - else if (blockMap[y][x]==O_WL_WALL) - blockMap[y][x]=WL_WALL; - else if (blockMap[y][x]==O_WL_ALLOWAIR) - blockMap[y][x]=WL_ALLOWAIR; - else if (blockMap[y][x]==O_WL_ALLOWSOLID) - blockMap[y][x]=WL_ALLOWPOWDER; - else if (blockMap[y][x]==O_WL_ALLOWALLELEC) - blockMap[y][x]=WL_ALLOWALLELEC; - else if (blockMap[y][x]==O_WL_EHOLE) - blockMap[y][x]=WL_EHOLE; - else if (blockMap[y][x]==O_WL_ALLOWGAS) - blockMap[y][x]=WL_ALLOWGAS; - else if (blockMap[y][x]==O_WL_GRAV) - blockMap[y][x]=WL_GRAV; - else if (blockMap[y][x]==O_WL_ALLOWENERGY) - blockMap[y][x]=WL_ALLOWENERGY; - } - - if (blockMap[y][x] >= UI_WALLCOUNT) - blockMap[y][x] = 0; + p++; + continue; + } + auto bm = data[p]; + switch (bm) + { + case 1: bm = WL_WALL ; break; + case 2: bm = WL_DESTROYALL ; break; + case 3: bm = WL_ALLOWLIQUID ; break; + case 4: bm = WL_FAN ; break; + case 5: bm = WL_STREAM ; break; + case 6: bm = WL_DETECT ; break; + case 7: bm = WL_EWALL ; break; + case 8: bm = WL_WALLELEC ; break; + case 9: bm = WL_ALLOWAIR ; break; + case 10: bm = WL_ALLOWPOWDER ; break; + case 11: bm = WL_ALLOWALLELEC; break; + case 12: bm = WL_EHOLE ; break; + case 13: bm = WL_ALLOWGAS ; break; } - p++; + if (ver>=44) + { + /* The numbers used to save walls were changed, starting in v44. + * The new numbers are ignored for older versions due to some corruption of bmap in saves from older versions. + */ + switch (bm) + { + case O_WL_WALLELEC: bm = WL_WALLELEC ; break; + case O_WL_EWALL: bm = WL_EWALL ; break; + case O_WL_DETECT: bm = WL_DETECT ; break; + case O_WL_STREAM: bm = WL_STREAM ; break; + case O_WL_FAN: + case O_WL_FANHELPER: bm = WL_FAN ; break; + case O_WL_ALLOWLIQUID: bm = WL_ALLOWLIQUID ; break; + case O_WL_DESTROYALL: bm = WL_DESTROYALL ; break; + case O_WL_ERASE: bm = WL_ERASE ; break; + case O_WL_WALL: bm = WL_WALL ; break; + case O_WL_ALLOWAIR: bm = WL_ALLOWAIR ; break; + case O_WL_ALLOWSOLID: bm = WL_ALLOWPOWDER ; break; + case O_WL_ALLOWALLELEC: bm = WL_ALLOWALLELEC; break; + case O_WL_EHOLE: bm = WL_EHOLE ; break; + case O_WL_ALLOWGAS: bm = WL_ALLOWGAS ; break; + case O_WL_GRAV: bm = WL_GRAV ; break; + case O_WL_ALLOWENERGY: bm = WL_ALLOWENERGY ; break; + } + } + + if (bm >= UI_WALLCOUNT) + bm = 0; + blockMap[bpos] = bm; } - for (y=by0; y=44 && data[(y-by0)*bw+(x-bx0)]==O_WL_FAN)) - { - if (p >= dataLength) - throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - fanVelX[y][x] = (data[p++]-127.0f)/64.0f; - } - for (y=by0; y=44 && data[(y-by0)*bw+(x-bx0)]==O_WL_FAN)) - { - if (p >= dataLength) - throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - fanVelY[y][x] = (data[p++]-127.0f)/64.0f; - } - // load the particle map - int i = 0; - k = 0; - pty = p; - for (y=y0; y()) + { + // TODO: use PlaneAdapter> once we're C++20 + auto dataPlane = PlaneAdapter>(blockS, std::in_place, data, blockS.X * blockS.Y); + if (dataPlane[bpos - blockP]==4||(ver>=44 && dataPlane[bpos - blockP]==O_WL_FAN)) { if (p >= dataLength) throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - j=data[p++]; + fanVelX[bpos] = (data[p++]-127.0f)/64.0f; + } + } + for (auto bpos : RectSized(blockP, blockS).Range()) + { + // TODO: use PlaneAdapter> once we're C++20 + auto dataPlane = PlaneAdapter>(blockS, std::in_place, data, blockS.X * blockS.Y); + if (dataPlane[bpos - blockP]==4||(ver>=44 && dataPlane[bpos - blockP]==O_WL_FAN)) + { + if (p >= dataLength) + throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); + fanVelY[bpos] = (data[p++]-127.0f)/64.0f; + } + } + + // load the particle map + { + auto k = 0; + pty = p; + for (auto pos : RectSized(partP, partS).Range()) + { + if (p >= dataLength) + throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); + auto j=data[p++]; if (j >= PT_NUM) { j = PT_DUST;//throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); } @@ -1534,17 +1461,18 @@ void GameSave::readPSv(const std::vector &dataVec) particles[k].ctype = 0; if (j==PT_BIZR || j==PT_BIZRG || j==PT_BIZRS) particles[k].ctype = 0x47FFFF; - particles[k].x = (float)x; - particles[k].y = (float)y; - particleIDMap[(x-x0)+(y-y0)*w] = k+1; + particles[k].x = (float)pos.X; + particles[k].y = (float)pos.Y; + particleIDMap[pos - partP] = k+1; particlesCount = ++k; } } + } // load particle properties - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { i--; @@ -1559,9 +1487,9 @@ void GameSave::readPSv(const std::vector &dataVec) p += 2; } } - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=44) { @@ -1586,9 +1514,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } if (ver>=44) { - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (p >= dataLength) { @@ -1614,11 +1542,13 @@ void GameSave::readPSv(const std::vector &dataVec) } } } + // TODO: use PlaneAdapter> once we're C++20 + auto dataPlanePty = PlaneAdapter>(partS, std::in_place, data + pty, partS.X * partS.Y); if (ver>=53) { - for (j=0; j()) { - i = particleIDMap[j]; - ty = data[pty+j]; + auto i = particleIDMap[pos]; + ty = dataPlanePty[pos]; if (i && (ty==PT_PBCN || (ty==PT_TRON && ver>=77))) { if (p >= dataLength) @@ -1631,9 +1561,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } //Read ALPHA component - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=49) { @@ -1649,9 +1579,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } //Read RED component - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=49) { @@ -1667,9 +1597,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } //Read GREEN component - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=49) { @@ -1685,9 +1615,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } //Read BLUE component - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=49) { @@ -1702,10 +1632,10 @@ void GameSave::readPSv(const std::vector &dataVec) } } } - for (j=0; j()) { - i = particleIDMap[j]; - ty = data[pty+j]; + auto i = particleIDMap[pos]; + ty = dataPlanePty[pos]; if (i) { if (ver>=34&&legacy_beta==0) @@ -1746,11 +1676,11 @@ void GameSave::readPSv(const std::vector &dataVec) } } } - for (j=0; j()) { + auto i = particleIDMap[pos]; int gnum = 0; - i = particleIDMap[j]; - ty = data[pty+j]; + ty = dataPlanePty[pos]; if (i && (ty==PT_CLNE || (ty==PT_PCLN && ver>=43) || (ty==PT_BCLN && ver>=44) || (ty==PT_SPRK && ver>=21) || (ty==PT_LAVA && ver>=34) || (ty==PT_PIPE && ver>=43) || (ty==PT_LIFE && ver>=51) || (ty==PT_PBCN && ver>=52) || (ty==PT_WIRE && ver>=55) || (ty==PT_STOR && ver>=59) || (ty==PT_CONV && ver>=60))) { if (p >= dataLength) @@ -1776,8 +1706,6 @@ void GameSave::readPSv(const std::vector &dataVec) if (ver<48 && (ty==OLD_PT_WIND || (ty==PT_BRAY&&particles[i-1].life==0))) { // Replace invisible particles with something sensible and add decoration to hide it - x = (int)(particles[i-1].x+0.5f); - y = (int)(particles[i-1].y+0.5f); particles[i-1].dcolour = 0xFF000000; particles[i-1].type = PT_DMND; } @@ -1908,26 +1836,35 @@ void GameSave::readPSv(const std::vector &dataVec) if (p == dataLength) // no sign data, "version 1" PSv return; - j = data[p++]; - for (i=0; i dataLength) throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - x = data[p++]; - x |= ((unsigned)data[p++])<<8; - tempSign.x = x+x0; - x = data[p++]; - x |= ((unsigned)data[p++])<<8; - tempSign.y = x+y0; - x = data[p++]; - tempSign.ju = (sign::Justification)x; - x = data[p++]; - if (p+x > dataLength) - throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - if(x>254) - x = 254; - memcpy(tempSignText, &data[0]+p, x); - tempSignText[x] = 0; + { + auto x = data[p++]; + x |= ((unsigned)data[p++])<<8; + tempSign.x = x+partP.X; + } + { + auto y = data[p++]; + y |= ((unsigned)data[p++])<<8; + tempSign.y = y+partP.Y; + } + { + auto ju = data[p++]; + tempSign.ju = (sign::Justification)ju; + } + { + auto l = data[p++]; + if (p+l > dataLength) + throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); + if(l>254) + l = 254; + memcpy(tempSignText, &data[0]+p, l); + tempSignText[l] = 0; + p += l; + } tempSign.text = format::CleanString(ByteString(tempSignText).FromUtf8(), true, true, true).Substr(0, 45); if (tempSign.text == "{t}") { @@ -1938,7 +1875,6 @@ void GameSave::readPSv(const std::vector &dataVec) tempSign.text = "Pressure: {p}"; } tempSigns.push_back(tempSign); - p += x; } for (size_t i = 0; i < tempSigns.size(); i++) @@ -1953,8 +1889,6 @@ void GameSave::readPSv(const std::vector &dataVec) std::pair> GameSave::serialiseOPS() const { - int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH; - int x, y, i; // minimum version this save is compatible with // when building, this number may be increased depending on what elements are used // or what properties are detected @@ -1969,132 +1903,121 @@ std::pair> GameSave::serialiseOPS() const }; //Get coords in blocks - blockX = 0;//orig_x0/CELL; - blockY = 0;//orig_y0/CELL; + auto blockP = Vec2{ 0, 0 }; //Snap full coords to block size - fullX = blockX*CELL; - fullY = blockY*CELL; + auto partP = blockP * CELL; //Original size + offset of original corner from snapped corner, rounded up by adding CELL-1 - blockW = blockWidth;//(blockWidth-fullX+CELL-1)/CELL; - blockH = blockHeight;//(blockHeight-fullY+CELL-1)/CELL; - fullW = blockW*CELL; - fullH = blockH*CELL; + auto blockS = blockSize; + auto partS = blockS * CELL; // Copy fan and wall data - std::vector wallData(blockWidth*blockHeight); + PlaneAdapter> wallData(blockSize); bool hasWallData = false; - std::vector fanData(blockWidth*blockHeight*2); - std::vector pressData(blockWidth*blockHeight*2); - std::vector vxData(blockWidth*blockHeight*2); - std::vector vyData(blockWidth*blockHeight*2); - std::vector ambientData(blockWidth*blockHeight*2, 0); - std::vector blockAirData(blockWidth * blockHeight * 2); - auto *blockAirhData = &blockAirData[blockWidth * blockHeight]; - unsigned int wallDataLen = blockWidth*blockHeight, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0; + std::vector fanData(blockSize.X*blockSize.Y*2); + std::vector pressData(blockSize.X*blockSize.Y*2); + std::vector vxData(blockSize.X*blockSize.Y*2); + std::vector vyData(blockSize.X*blockSize.Y*2); + std::vector ambientData(blockSize.X*blockSize.Y*2, 0); + // TODO: have a separate vector with two PlaneAdapter>s over it once we're C++20 + PlaneAdapter> blockAirData({ blockSize.X, blockSize.Y * 2 }); + unsigned int wallDataLen = blockSize.X*blockSize.Y, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0; - for (x = blockX; x < blockX+blockW; x++) + for (auto bpos : RectSized(blockP, blockS).Range()) { - for (y = blockY; y < blockY+blockH; y++) + wallData[bpos - blockP] = blockMap[bpos]; + if (blockMap[bpos]) + hasWallData = true; + + if (hasPressure) { - auto rowMajorIndex = (y - blockY) * blockW + (x - blockX); - wallData[rowMajorIndex] = blockMap[y][x]; - if (blockMap[y][x]) - hasWallData = true; + //save pressure and x/y velocity grids + float pres = std::max(-255.0f,std::min(255.0f,pressure[bpos]))+256.0f; + float velX = std::max(-255.0f,std::min(255.0f,velocityX[bpos]))+256.0f; + float velY = std::max(-255.0f,std::min(255.0f,velocityY[bpos]))+256.0f; + pressData[pressDataLen++] = (unsigned char)((int)(pres*128)&0xFF); + pressData[pressDataLen++] = (unsigned char)((int)(pres*128)>>8); - if (hasPressure) + vxData[vxDataLen++] = (unsigned char)((int)(velX*128)&0xFF); + vxData[vxDataLen++] = (unsigned char)((int)(velX*128)>>8); + + vyData[vyDataLen++] = (unsigned char)((int)(velY*128)&0xFF); + vyData[vyDataLen++] = (unsigned char)((int)(velY*128)>>8); + + blockAirData[bpos - blockP] = blockAir[bpos]; + blockAirData[(bpos - blockP) + Vec2{ 0, blockS.Y }] = blockAirh[bpos]; + } + + if (hasAmbientHeat) + { + int tempTemp = (int)(ambientHeat[bpos]+0.5f); + ambientData[ambientDataLen++] = tempTemp; + ambientData[ambientDataLen++] = tempTemp >> 8; + } + + if (blockMap[bpos] == WL_FAN) + { { - //save pressure and x/y velocity grids - float pres = std::max(-255.0f,std::min(255.0f,pressure[y][x]))+256.0f; - float velX = std::max(-255.0f,std::min(255.0f,velocityX[y][x]))+256.0f; - float velY = std::max(-255.0f,std::min(255.0f,velocityY[y][x]))+256.0f; - pressData[pressDataLen++] = (unsigned char)((int)(pres*128)&0xFF); - pressData[pressDataLen++] = (unsigned char)((int)(pres*128)>>8); - - vxData[vxDataLen++] = (unsigned char)((int)(velX*128)&0xFF); - vxData[vxDataLen++] = (unsigned char)((int)(velX*128)>>8); - - vyData[vyDataLen++] = (unsigned char)((int)(velY*128)&0xFF); - vyData[vyDataLen++] = (unsigned char)((int)(velY*128)>>8); - - blockAirData[rowMajorIndex] = blockAir[y][x]; - blockAirhData[rowMajorIndex] = blockAirh[y][x]; - } - - if (hasAmbientHeat) - { - int tempTemp = (int)(ambientHeat[y][x]+0.5f); - ambientData[ambientDataLen++] = tempTemp; - ambientData[ambientDataLen++] = tempTemp >> 8; - } - - if (blockMap[y][x] == WL_FAN) - { - i = (int)(fanVelX[y][x]*64.0f+127.5f); - if (i<0) i=0; - if (i>255) i=255; - fanData[fanDataLen++] = i; - i = (int)(fanVelY[y][x]*64.0f+127.5f); + auto i = (int)(fanVelX[bpos]*64.0f+127.5f); if (i<0) i=0; if (i>255) i=255; fanData[fanDataLen++] = i; } - else if (blockMap[y][x] == WL_STASIS) { - RESTRICTVERSION(94, 0); + auto i = (int)(fanVelY[bpos]*64.0f+127.5f); + if (i<0) i=0; + if (i>255) i=255; + fanData[fanDataLen++] = i; } } + else if (blockMap[bpos] == WL_STASIS) + { + RESTRICTVERSION(94, 0); + } } - auto blockAirDataLen = blockAirData.size(); //Index positions of all particles, using linked lists //partsPosFirstMap is pmap for the first particle in each position //partsPosLastMap is pmap for the last particle 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 - std::vector partsPosFirstMap(fullW*fullH, 0); - std::vector partsPosLastMap(fullW*fullH, 0); - std::vector partsPosCount(fullW*fullH, 0); + PlaneAdapter> partsPosFirstMap(partS, 0); + PlaneAdapter> partsPosLastMap(partS, 0); + PlaneAdapter> partsPosCount(partS, 0); std::vector partsPosLink(NPART, 0); unsigned int soapCount = 0; - for(i = 0; i < particlesCount; i++) + for(auto i = 0; i < particlesCount; i++) { if(particles[i].type) { - x = (int)(particles[i].x+0.5f); - y = (int)(particles[i].y+0.5f); + auto pos = Vec2{ (int)(particles[i].x+0.5f), (int)(particles[i].y+0.5f) }; //Coordinates relative to top left corner of saved area - x -= fullX; - y -= fullY; - if (!partsPosFirstMap[y*fullW + x]) + if (!partsPosFirstMap[pos - partP]) { //First entry in list - partsPosFirstMap[y*fullW + x] = (i<<8)|1; - partsPosLastMap[y*fullW + x] = (i<<8)|1; + partsPosFirstMap[pos - partP] = (i<<8)|1; + partsPosLastMap[pos - partP] = (i<<8)|1; } else { //Add to end of list - partsPosLink[partsPosLastMap[y*fullW + x]>>8] = (i<<8)|1;//link to current end of list - partsPosLastMap[y*fullW + x] = (i<<8)|1;//set as new end of list + partsPosLink[partsPosLastMap[pos - partP]>>8] = (i<<8)|1;//link to current end of list + partsPosLastMap[pos - partP] = (i<<8)|1;//set as new end of list } - partsPosCount[y*fullW + x]++; + partsPosCount[pos - partP]++; } } //Store number of particles in each position - std::vector partsPosData(fullW*fullH*3); + std::vector partsPosData(partS.X * partS.Y * 3); unsigned int partsPosDataLen = 0; - for (y=0;y()) { - for (x=0;x>16; - partsPosData[partsPosDataLen++] = (posCount&0x0000FF00)>>8; - partsPosData[partsPosDataLen++] = (posCount&0x000000FF); - } + unsigned int posCount = partsPosCount[pos]; + partsPosData[partsPosDataLen++] = (posCount&0x00FF0000)>>16; + partsPosData[partsPosDataLen++] = (posCount&0x0000FF00)>>8; + partsPosData[partsPosDataLen++] = (posCount&0x000000FF); } //Copy parts data @@ -2120,276 +2043,273 @@ std::pair> GameSave::serialiseOPS() const std::vector partsSaveIndex(NPART); unsigned int partsCount = 0; std::fill(&partsSaveIndex[0], &partsSaveIndex[0] + NPART, 0); - for (y=0;y()) { - for (x=0;x>8; + + //Store saved particle index+1 for this partsptr index (0 means not saved) + partsSaveIndex[i] = (partsCount++) + 1; + + //Type (required) + partsData[partsDataLen++] = particles[i].type; + + //Location of the field descriptor + int fieldDesc3Loc = 0; + int fieldDescLoc = partsDataLen++; + partsDataLen++; + + auto tmp3 = (unsigned int)(particles[i].tmp3); + auto tmp4 = (unsigned int)(particles[i].tmp4); + if ((tmp3 || tmp4) && (!PressureInTmp3(particles[i].type) || hasPressure)) { - unsigned int fieldDesc = 0; - int tempTemp, vTemp; - - //Turn pmap entry into a particles index - i = i>>8; - - //Store saved particle index+1 for this partsptr index (0 means not saved) - partsSaveIndex[i] = (partsCount++) + 1; - - //Type (required) - partsData[partsDataLen++] = particles[i].type; - - //Location of the field descriptor - int fieldDesc3Loc = 0; - int fieldDescLoc = partsDataLen++; - partsDataLen++; - - auto tmp3 = (unsigned int)(particles[i].tmp3); - auto tmp4 = (unsigned int)(particles[i].tmp4); - if ((tmp3 || tmp4) && (!PressureInTmp3(particles[i].type) || hasPressure)) + fieldDesc |= 1 << 13; + // The tmp3 of PressureInTmp3 elements is okay to truncate because the loading code + // sign extends it anyway, expecting the value to not be higher in magnitude than + // 256 (max pressure value) * 64 (tmp3 multiplicative bias). + if (((tmp3 >> 16) || (tmp4 >> 16)) && !PressureInTmp3(particles[i].type)) { - fieldDesc |= 1 << 13; - // The tmp3 of PressureInTmp3 elements is okay to truncate because the loading code - // sign extends it anyway, expecting the value to not be higher in magnitude than - // 256 (max pressure value) * 64 (tmp3 multiplicative bias). - if (((tmp3 >> 16) || (tmp4 >> 16)) && !PressureInTmp3(particles[i].type)) + fieldDesc |= 1 << 15; + fieldDesc |= 1 << 16; + RESTRICTVERSION(97, 0); + } + } + + // Extra type byte if necessary + if (particles[i].type & 0xFF00) + { + partsData[partsDataLen++] = particles[i].type >> 8; + fieldDesc |= 1 << 14; + RESTRICTVERSION(93, 0); + } + + //Extra Temperature (2nd byte optional, 1st required), 1 to 2 bytes + //Store temperature as an offset of 21C(294.15K) or go into a 16byte int and store the whole thing + if(fabs(particles[i].temp-294.15f)<127) + { + tempTemp = int(floor(particles[i].temp-294.15f+0.5f)); + partsData[partsDataLen++] = tempTemp; + } + else + { + fieldDesc |= 1; + tempTemp = (int)(particles[i].temp+0.5f); + partsData[partsDataLen++] = tempTemp; + partsData[partsDataLen++] = tempTemp >> 8; + } + + if (fieldDesc & (1 << 15)) + { + fieldDesc3Loc = partsDataLen++; + } + + //Life (optional), 1 to 2 bytes + if(particles[i].life) + { + int life = particles[i].life; + if (life > 0xFFFF) + life = 0xFFFF; + else if (life < 0) + life = 0; + fieldDesc |= 1 << 1; + partsData[partsDataLen++] = life; + if (life & 0xFF00) + { + fieldDesc |= 1 << 2; + partsData[partsDataLen++] = life >> 8; + } + } + + //Tmp (optional), 1, 2, or 4 bytes + if(particles[i].tmp) + { + fieldDesc |= 1 << 3; + partsData[partsDataLen++] = particles[i].tmp; + if(particles[i].tmp & 0xFFFFFF00) + { + fieldDesc |= 1 << 4; + partsData[partsDataLen++] = particles[i].tmp >> 8; + if(particles[i].tmp & 0xFFFF0000) { - fieldDesc |= 1 << 15; - fieldDesc |= 1 << 16; - RESTRICTVERSION(97, 0); + fieldDesc |= 1 << 12; + partsData[partsDataLen++] = (particles[i].tmp&0xFF000000)>>24; + partsData[partsDataLen++] = (particles[i].tmp&0x00FF0000)>>16; } } + } - // Extra type byte if necessary - if (particles[i].type & 0xFF00) + //Ctype (optional), 1 or 4 bytes + if(particles[i].ctype) + { + fieldDesc |= 1 << 5; + partsData[partsDataLen++] = particles[i].ctype; + if(particles[i].ctype & 0xFFFFFF00) { - partsData[partsDataLen++] = particles[i].type >> 8; - fieldDesc |= 1 << 14; - RESTRICTVERSION(93, 0); + fieldDesc |= 1 << 9; + partsData[partsDataLen++] = (particles[i].ctype&0xFF000000)>>24; + partsData[partsDataLen++] = (particles[i].ctype&0x00FF0000)>>16; + partsData[partsDataLen++] = (particles[i].ctype&0x0000FF00)>>8; } + } - //Extra Temperature (2nd byte optional, 1st required), 1 to 2 bytes - //Store temperature as an offset of 21C(294.15K) or go into a 16byte int and store the whole thing - if(fabs(particles[i].temp-294.15f)<127) - { - tempTemp = int(floor(particles[i].temp-294.15f+0.5f)); - partsData[partsDataLen++] = tempTemp; - } - else - { - fieldDesc |= 1; - tempTemp = (int)(particles[i].temp+0.5f); - partsData[partsDataLen++] = tempTemp; - partsData[partsDataLen++] = tempTemp >> 8; - } + //Dcolour (optional), 4 bytes + if(particles[i].dcolour && (particles[i].dcolour & 0xFF000000 || particles[i].type == PT_LIFE)) + { + fieldDesc |= 1 << 6; + partsData[partsDataLen++] = (particles[i].dcolour&0xFF000000)>>24; + partsData[partsDataLen++] = (particles[i].dcolour&0x00FF0000)>>16; + partsData[partsDataLen++] = (particles[i].dcolour&0x0000FF00)>>8; + partsData[partsDataLen++] = (particles[i].dcolour&0x000000FF); + } - if (fieldDesc & (1 << 15)) - { - fieldDesc3Loc = partsDataLen++; - } + //VX (optional), 1 byte + if(fabs(particles[i].vx) > 0.001f) + { + fieldDesc |= 1 << 7; + vTemp = (int)(particles[i].vx*16.0f+127.5f); + if (vTemp<0) vTemp=0; + if (vTemp>255) vTemp=255; + partsData[partsDataLen++] = vTemp; + } - //Life (optional), 1 to 2 bytes - if(particles[i].life) - { - int life = particles[i].life; - if (life > 0xFFFF) - life = 0xFFFF; - else if (life < 0) - life = 0; - fieldDesc |= 1 << 1; - partsData[partsDataLen++] = life; - if (life & 0xFF00) - { - fieldDesc |= 1 << 2; - partsData[partsDataLen++] = life >> 8; - } - } + //VY (optional), 1 byte + if(fabs(particles[i].vy) > 0.001f) + { + fieldDesc |= 1 << 8; + vTemp = (int)(particles[i].vy*16.0f+127.5f); + if (vTemp<0) vTemp=0; + if (vTemp>255) vTemp=255; + partsData[partsDataLen++] = vTemp; + } - //Tmp (optional), 1, 2, or 4 bytes - if(particles[i].tmp) + //Tmp2 (optional), 1 or 2 bytes + if(particles[i].tmp2) + { + fieldDesc |= 1 << 10; + partsData[partsDataLen++] = particles[i].tmp2; + if(particles[i].tmp2 & 0xFF00) { - fieldDesc |= 1 << 3; - partsData[partsDataLen++] = particles[i].tmp; - if(particles[i].tmp & 0xFFFFFF00) - { - fieldDesc |= 1 << 4; - partsData[partsDataLen++] = particles[i].tmp >> 8; - if(particles[i].tmp & 0xFFFF0000) - { - fieldDesc |= 1 << 12; - partsData[partsDataLen++] = (particles[i].tmp&0xFF000000)>>24; - partsData[partsDataLen++] = (particles[i].tmp&0x00FF0000)>>16; - } - } + fieldDesc |= 1 << 11; + partsData[partsDataLen++] = particles[i].tmp2 >> 8; } + } - //Ctype (optional), 1 or 4 bytes - if(particles[i].ctype) + //tmp3 and tmp4, 4 bytes + if (fieldDesc & (1 << 13)) + { + partsData[partsDataLen++] = tmp3 ; + partsData[partsDataLen++] = tmp3 >> 8; + partsData[partsDataLen++] = tmp4 ; + partsData[partsDataLen++] = tmp4 >> 8; + if (fieldDesc & (1 << 16)) { - fieldDesc |= 1 << 5; - partsData[partsDataLen++] = particles[i].ctype; - if(particles[i].ctype & 0xFFFFFF00) - { - fieldDesc |= 1 << 9; - partsData[partsDataLen++] = (particles[i].ctype&0xFF000000)>>24; - partsData[partsDataLen++] = (particles[i].ctype&0x00FF0000)>>16; - partsData[partsDataLen++] = (particles[i].ctype&0x0000FF00)>>8; - } + partsData[partsDataLen++] = tmp3 >> 16; + partsData[partsDataLen++] = tmp3 >> 24; + partsData[partsDataLen++] = tmp4 >> 16; + partsData[partsDataLen++] = tmp4 >> 24; } + } - //Dcolour (optional), 4 bytes - if(particles[i].dcolour && (particles[i].dcolour & 0xFF000000 || particles[i].type == PT_LIFE)) - { - fieldDesc |= 1 << 6; - partsData[partsDataLen++] = (particles[i].dcolour&0xFF000000)>>24; - partsData[partsDataLen++] = (particles[i].dcolour&0x00FF0000)>>16; - partsData[partsDataLen++] = (particles[i].dcolour&0x0000FF00)>>8; - partsData[partsDataLen++] = (particles[i].dcolour&0x000000FF); - } + //Write the field descriptor + partsData[fieldDescLoc] = fieldDesc; + partsData[fieldDescLoc+1] = fieldDesc>>8; + if (fieldDesc & (1 << 15)) + { + partsData[fieldDesc3Loc] = fieldDesc>>16; + } - //VX (optional), 1 byte - if(fabs(particles[i].vx) > 0.001f) - { - fieldDesc |= 1 << 7; - vTemp = (int)(particles[i].vx*16.0f+127.5f); - if (vTemp<0) vTemp=0; - if (vTemp>255) vTemp=255; - partsData[partsDataLen++] = vTemp; - } + if (particles[i].type == PT_SOAP) + soapCount++; - //VY (optional), 1 byte - if(fabs(particles[i].vy) > 0.001f) - { - fieldDesc |= 1 << 8; - vTemp = (int)(particles[i].vy*16.0f+127.5f); - if (vTemp<0) vTemp=0; - if (vTemp>255) vTemp=255; - partsData[partsDataLen++] = vTemp; - } - - //Tmp2 (optional), 1 or 2 bytes - if(particles[i].tmp2) - { - fieldDesc |= 1 << 10; - partsData[partsDataLen++] = particles[i].tmp2; - if(particles[i].tmp2 & 0xFF00) - { - fieldDesc |= 1 << 11; - partsData[partsDataLen++] = particles[i].tmp2 >> 8; - } - } - - //tmp3 and tmp4, 4 bytes - if (fieldDesc & (1 << 13)) - { - partsData[partsDataLen++] = tmp3 ; - partsData[partsDataLen++] = tmp3 >> 8; - partsData[partsDataLen++] = tmp4 ; - partsData[partsDataLen++] = tmp4 >> 8; - if (fieldDesc & (1 << 16)) - { - partsData[partsDataLen++] = tmp3 >> 16; - partsData[partsDataLen++] = tmp3 >> 24; - partsData[partsDataLen++] = tmp4 >> 16; - partsData[partsDataLen++] = tmp4 >> 24; - } - } - - //Write the field descriptor - partsData[fieldDescLoc] = fieldDesc; - partsData[fieldDescLoc+1] = fieldDesc>>8; - if (fieldDesc & (1 << 15)) - { - partsData[fieldDesc3Loc] = fieldDesc>>16; - } - - if (particles[i].type == PT_SOAP) - soapCount++; - - if (particles[i].type == PT_RPEL && particles[i].ctype) - { - RESTRICTVERSION(91, 4); - } - else if (particles[i].type == PT_NWHL && particles[i].tmp) - { - RESTRICTVERSION(91, 5); - } - if (particles[i].type == PT_HEAC || particles[i].type == PT_SAWD || particles[i].type == PT_POLO - || particles[i].type == PT_RFRG || particles[i].type == PT_RFGL || particles[i].type == PT_LSNS) - { - RESTRICTVERSION(92, 0); - } - else if ((particles[i].type == PT_FRAY || particles[i].type == PT_INVIS) && particles[i].tmp) - { - RESTRICTVERSION(92, 0); - } - else if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP) + if (particles[i].type == PT_RPEL && particles[i].ctype) + { + RESTRICTVERSION(91, 4); + } + else if (particles[i].type == PT_NWHL && particles[i].tmp) + { + RESTRICTVERSION(91, 5); + } + if (particles[i].type == PT_HEAC || particles[i].type == PT_SAWD || particles[i].type == PT_POLO + || particles[i].type == PT_RFRG || particles[i].type == PT_RFGL || particles[i].type == PT_LSNS) + { + RESTRICTVERSION(92, 0); + } + else if ((particles[i].type == PT_FRAY || particles[i].type == PT_INVIS) && particles[i].tmp) + { + RESTRICTVERSION(92, 0); + } + else if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP) + { + RESTRICTVERSION(93, 0); + } + if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS + || particles[i].type == PT_HSWC || particles[i].type == PT_PUMP) + { + if (particles[i].tmp == 1) { RESTRICTVERSION(93, 0); } - if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS - || particles[i].type == PT_HSWC || particles[i].type == PT_PUMP) + } + if (PMAPBITS > 8) + { + for (auto index : possiblyCarriesType) { - if (particles[i].tmp == 1) + if (elements[particles[i].type].CarriesTypeIn & (1U << index)) { - RESTRICTVERSION(93, 0); - } - } - if (PMAPBITS > 8) - { - for (auto index : possiblyCarriesType) - { - if (elements[particles[i].type].CarriesTypeIn & (1U << index)) + auto *prop = reinterpret_cast(reinterpret_cast(&particles[i]) + properties[index].Offset); + if (TYP(*prop) > 0xFF) { - auto *prop = reinterpret_cast(reinterpret_cast(&particles[i]) + properties[index].Offset); - if (TYP(*prop) > 0xFF) - { - RESTRICTVERSION(93, 0); - } + RESTRICTVERSION(93, 0); } } } - if (particles[i].type == PT_LDTC) + } + if (particles[i].type == PT_LDTC) + { + RESTRICTVERSION(94, 0); + } + if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS) + { + if (particles[i].tmp == 2) { RESTRICTVERSION(94, 0); } - if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS) - { - if (particles[i].tmp == 2) - { - RESTRICTVERSION(94, 0); - } - } - if (particles[i].type == PT_LSNS) - { - if (particles[i].tmp >= 1 || particles[i].tmp <= 3) - { - RESTRICTVERSION(95, 0); - } - } - if (particles[i].type == PT_LIFE) - { - RESTRICTVERSION(96, 0); - } - if (particles[i].type == PT_GLAS && particles[i].life > 0) - { - RESTRICTVERSION(97, 0); - } - if (PressureInTmp3(particles[i].type)) - { - RESTRICTVERSION(97, 0); - } - if (particles[i].type == PT_CONV && particles[i].tmp2 != 0) - { - RESTRICTVERSION(97, 0); - } - - //Get the pmap entry for the next particle in the same position - i = partsPosLink[i]; } + if (particles[i].type == PT_LSNS) + { + if (particles[i].tmp >= 1 || particles[i].tmp <= 3) + { + RESTRICTVERSION(95, 0); + } + } + if (particles[i].type == PT_LIFE) + { + RESTRICTVERSION(96, 0); + } + if (particles[i].type == PT_GLAS && particles[i].life > 0) + { + RESTRICTVERSION(97, 0); + } + if (PressureInTmp3(particles[i].type)) + { + RESTRICTVERSION(97, 0); + } + if (particles[i].type == PT_CONV && particles[i].tmp2 != 0) + { + RESTRICTVERSION(97, 0); + } + + //Get the pmap entry for the next particle in the same position + i = partsPosLink[i]; } } @@ -2399,44 +2319,41 @@ std::pair> GameSave::serialiseOPS() const { //Iterate through particles in the same order that they were saved - for (y=0;y()) { - for (x=0;x>8; - //Loop while there is a pmap entry - while (i) + if (particles[i].type==PT_SOAP) { - //Turn pmap entry into a partsptr index - i = i>>8; + //Only save forward link for each particle, back links can be deduced from other forward links + //linkedIndex is index within saved particles + 1, 0 means not saved or no link - if (particles[i].type==PT_SOAP) + unsigned linkedIndex = 0; + if ((particles[i].ctype&2) && particles[i].tmp>=0 && particles[i].tmp=0 && particles[i].tmp>16; - soapLinkData[soapLinkDataLen++] = (linkedIndex&0x00FF00)>>8; - soapLinkData[soapLinkDataLen++] = (linkedIndex&0x0000FF); + linkedIndex = partsSaveIndex[particles[i].tmp]; } - - //Get the pmap entry for the next particle in the same position - i = partsPosLink[i]; + soapLinkData[soapLinkDataLen++] = (linkedIndex&0xFF0000)>>16; + soapLinkData[soapLinkDataLen++] = (linkedIndex&0x00FF00)>>8; + soapLinkData[soapLinkDataLen++] = (linkedIndex&0x0000FF); } + + //Get the pmap entry for the next particle in the same position + i = partsPosLink[i]; } } } for (size_t i = 0; i < signs.size(); i++) { - if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH) + if(signs[i].text.length() && partS.OriginRect().Contains({ signs[i].x, signs[i].y })) { int x, y, w, h; bool v95 = false; @@ -2542,7 +2459,7 @@ std::pair> GameSave::serialiseOPS() const bson_append_binary(&b, "partsPos", (char)BSON_BIN_USER, (const char *)&partsPosData[0], partsPosDataLen); } if (hasWallData) - bson_append_binary(&b, "wallMap", (char)BSON_BIN_USER, (const char *)&wallData[0], wallDataLen); + bson_append_binary(&b, "wallMap", (char)BSON_BIN_USER, (const char *)wallData.data(), wallDataLen); if (fanDataLen) bson_append_binary(&b, "fanMap", (char)BSON_BIN_USER, (const char *)&fanData[0], fanDataLen); if (hasPressure && pressDataLen) @@ -2558,7 +2475,7 @@ std::pair> GameSave::serialiseOPS() const if (ensureDeterminism) { bson_append_bool(&b, "ensureDeterminism", ensureDeterminism); - bson_append_binary(&b, "blockAir", (char)BSON_BIN_USER, (const char *)&blockAirData[0], blockAirDataLen); + bson_append_binary(&b, "blockAir", (char)BSON_BIN_USER, (const char *)blockAirData.data(), blockAirData.Size().X * blockAirData.Size().Y); bson_append_long(&b, "frameCount", int64_t(frameCount)); bson_append_binary(&b, "rngState", (char)BSON_BIN_USER, (const char *)&rngState, sizeof(rngState)); RESTRICTVERSION(98, 0); @@ -2566,7 +2483,7 @@ std::pair> GameSave::serialiseOPS() const unsigned int signsCount = 0; for (size_t i = 0; i < signs.size(); i++) { - if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH) + if(signs[i].text.length() && partS.OriginRect().Contains({ signs[i].x, signs[i].y })) { signsCount++; } @@ -2576,7 +2493,7 @@ std::pair> GameSave::serialiseOPS() const bson_append_start_array(&b, "signs"); for (size_t i = 0; i < signs.size(); i++) { - if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH) + if(signs[i].text.length() && partS.OriginRect().Contains({ signs[i].x, signs[i].y })) { bson_append_start_object(&b, "sign"); bson_append_string(&b, "text", signs[i].text.ToUtf8().c_str()); @@ -2623,8 +2540,8 @@ std::pair> GameSave::serialiseOPS() const header[3] = '1'; header[4] = SAVE_VERSION; header[5] = CELL; - header[6] = blockW; - header[7] = blockH; + header[6] = blockS.X; + header[7] = blockS.Y; header[8] = finalDataLen; header[9] = finalDataLen >> 8; header[10] = finalDataLen >> 16; diff --git a/src/client/GameSave.h b/src/client/GameSave.h index 21335504c..cbe472081 100644 --- a/src/client/GameSave.h +++ b/src/client/GameSave.h @@ -54,31 +54,6 @@ public: } }; -template -struct [[deprecated("Use PlaneAdapter")]] Plane: PlaneAdapter> -{ - [[deprecated("Use operator[](Vec2)")]] - Item *operator [](int y) - { - return &*PlaneAdapter>::RowIterator(Vec2(0, y)); - } - - [[deprecated("Use operator[](Vec2)")]] - const Item *operator [](int y) const - { - return &*PlaneAdapter>::RowIterator(Vec2(0, y)); - } - - [[deprecated("Use PlaneAdapter")]] - Plane() = default; - - [[deprecated("Use PlaneAdapter")]] - Plane(int newWidth, int newHeight, Item defaultVal): - PlaneAdapter>(Vec2(newWidth, newHeight), defaultVal) - { - } -}; - class GameSave { // number of pixels translated. When translating CELL pixels, shift all CELL grids @@ -88,8 +63,7 @@ class GameSave std::pair> serialiseOPS() const; public: - int blockWidth = 0; - int blockHeight = 0; + Vec2 blockSize = { 0, 0 }; bool fromNewerVersion = false; int majorVersion = 0; int minorVersion = 0; @@ -104,15 +78,15 @@ public: //Simulation data int particlesCount = 0; std::vector particles; - Plane blockMap; - Plane fanVelX; - Plane fanVelY; - Plane pressure; - Plane velocityX; - Plane velocityY; - Plane ambientHeat; - Plane blockAir; - Plane blockAirh; + PlaneAdapter> blockMap; + PlaneAdapter> fanVelX; + PlaneAdapter> fanVelY; + PlaneAdapter> pressure; + PlaneAdapter> velocityX; + PlaneAdapter> velocityY; + PlaneAdapter> ambientHeat; + PlaneAdapter> blockAir; + PlaneAdapter> blockAirh; //Simulation Options bool waterEEnabled = false; @@ -141,14 +115,14 @@ public: int pmapbits = 8; // default to 8 bits for older saves - GameSave(int width, int height); + GameSave(Vec2 newBlockSize); GameSave(const std::vector &data, bool newWantAuthors = true); - void setSize(int width, int height); + void setSize(Vec2 newBlockSize); // return value is [ fakeFromNewerVersion, gameData ] std::pair> Serialise() const; vector2d Translate(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, Vec2 newPartSize); void Expand(const std::vector &data); diff --git a/src/gui/render/RenderView.cpp b/src/gui/render/RenderView.cpp index 63fe35ea3..4023ecdf1 100644 --- a/src/gui/render/RenderView.cpp +++ b/src/gui/render/RenderView.cpp @@ -155,7 +155,7 @@ void RenderView::NotifyColourChanged(RenderModel * sender) void RenderView::OnDraw() { Graphics * g = GetGraphics(); - g->DrawFilledRect(RectSized({ 0, 0 }, WINDOW), 0x000000_rgb); + g->DrawFilledRect(WINDOW.OriginRect(), 0x000000_rgb); if(ren) { ren->clearScreen(); diff --git a/src/simulation/SaveRenderer.cpp b/src/simulation/SaveRenderer.cpp index 9127fdcb8..2e43fb6d3 100644 --- a/src/simulation/SaveRenderer.cpp +++ b/src/simulation/SaveRenderer.cpp @@ -58,7 +58,7 @@ std::unique_ptr SaveRenderer::Render(const GameSave *save, bool dec ren->RenderBegin(); ren->RenderEnd(); - tempThumb = std::make_unique(Vec2(save->blockWidth, save->blockHeight) * CELL); + tempThumb = std::make_unique(save->blockSize * CELL); tempThumb->BlendImage(ren->Data(), 0xFF, ren->Size().OriginRect()); } diff --git a/src/simulation/Simulation.cpp b/src/simulation/Simulation.cpp index e6d5cdc14..c852a2dfd 100644 --- a/src/simulation/Simulation.cpp +++ b/src/simulation/Simulation.cpp @@ -31,10 +31,9 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu auto save = std::unique_ptr(new GameSave(*originalSave)); //Align to blockMap - int blockX = (fullX + CELL/2)/CELL; - int blockY = (fullY + CELL/2)/CELL; - fullX = blockX*CELL; - fullY = blockY*CELL; + auto blockP = Vec2{ (fullX + CELL / 2) / CELL, (fullY + CELL / 2) / CELL }; + fullX = blockP.X * CELL; + fullY = blockP.Y * CELL; unsigned int pmapmask = (1<pmapbits)-1; int partMap[PT_NUM]; @@ -305,31 +304,28 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu signs.push_back(tempSign); } } - for(int saveBlockX = 0; saveBlockX < save->blockWidth; saveBlockX++) + for (auto bpos : save->blockSize.OriginRect()) { - for(int saveBlockY = 0; saveBlockY < save->blockHeight; saveBlockY++) + if(save->blockMap[bpos]) { - if(save->blockMap[saveBlockY][saveBlockX]) + bmap[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->blockMap[bpos]; + fvx[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->fanVelX[bpos]; + fvy[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->fanVelY[bpos]; + } + if (includePressure) + { + if (save->hasPressure) { - bmap[saveBlockY+blockY][saveBlockX+blockX] = save->blockMap[saveBlockY][saveBlockX]; - fvx[saveBlockY+blockY][saveBlockX+blockX] = save->fanVelX[saveBlockY][saveBlockX]; - fvy[saveBlockY+blockY][saveBlockX+blockX] = save->fanVelY[saveBlockY][saveBlockX]; + pv[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->pressure[bpos]; + vx[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->velocityX[bpos]; + vy[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->velocityY[bpos]; } - if (includePressure) + if (save->hasAmbientHeat) + hv[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->ambientHeat[bpos]; + if (save->hasBlockAirMaps) { - if (save->hasPressure) - { - pv[saveBlockY+blockY][saveBlockX+blockX] = save->pressure[saveBlockY][saveBlockX]; - vx[saveBlockY+blockY][saveBlockX+blockX] = save->velocityX[saveBlockY][saveBlockX]; - vy[saveBlockY+blockY][saveBlockX+blockX] = save->velocityY[saveBlockY][saveBlockX]; - } - if (save->hasAmbientHeat) - hv[saveBlockY+blockY][saveBlockX+blockX] = save->ambientHeat[saveBlockY][saveBlockX]; - if (save->hasBlockAirMaps) - { - air->bmap_blockair[saveBlockY+blockY][saveBlockX+blockX] = save->blockAir[saveBlockY][saveBlockX]; - air->bmap_blockairh[saveBlockY+blockY][saveBlockX+blockX] = save->blockAirh[saveBlockY][saveBlockX]; - } + air->bmap_blockair[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->blockAir[bpos]; + air->bmap_blockairh[blockP.Y + bpos.Y][blockP.X + bpos.X] = save->blockAirh[bpos]; } } } @@ -350,7 +346,6 @@ std::unique_ptr Simulation::Save(bool includePressure) std::unique_ptr Simulation::Save(bool includePressure, int fullX, int fullY, int fullX2, int fullY2) { - int blockX, blockY, blockX2, blockY2, blockW, blockH; //Normalise incoming coords int swapTemp; if(fullY>fullY2) @@ -367,16 +362,11 @@ std::unique_ptr Simulation::Save(bool includePressure, int fullX, int } //Align coords to blockMap - blockX = fullX/CELL; - blockY = fullY/CELL; + auto blockP = Vec2{ fullX / CELL, fullY / CELL }; + auto blockP2 = Vec2{ (fullX2 + CELL) / CELL, (fullY2 + CELL) / CELL }; + auto blockS = blockP2 - blockP; - blockX2 = (fullX2+CELL)/CELL; - blockY2 = (fullY2+CELL)/CELL; - - blockW = blockX2-blockX; - blockH = blockY2-blockY; - - auto newSave = std::make_unique(blockW, blockH); + auto newSave = std::make_unique(blockS); auto &possiblyCarriesType = Particle::PossiblyCarriesType(); auto &properties = Particle::GetProperties(); newSave->frameCount = frameCount; @@ -397,8 +387,8 @@ std::unique_ptr Simulation::Save(bool includePressure, int fullX, int if (parts[i].type && x >= fullX && y >= fullY && x <= fullX2 && y <= fullY2) { Particle tempPart = parts[i]; - tempPart.x -= blockX*CELL; - tempPart.y -= blockY*CELL; + tempPart.x -= blockP.X * CELL; + tempPart.y -= blockP.Y * CELL; if (elements[tempPart.type].Enabled) { particleMap.insert(std::pair(i, storedParts)); @@ -461,31 +451,28 @@ std::unique_ptr Simulation::Save(bool includePressure, int fullX, int if(signs[i].text.length() && signs[i].x >= fullX && signs[i].y >= fullY && signs[i].x <= fullX2 && signs[i].y <= fullY2) { sign tempSign = signs[i]; - tempSign.x -= blockX*CELL; - tempSign.y -= blockY*CELL; + tempSign.x -= blockP.X * CELL; + tempSign.y -= blockP.Y * CELL; *newSave << tempSign; } } - for(int saveBlockX = 0; saveBlockX < newSave->blockWidth; saveBlockX++) + for (auto bpos : newSave->blockSize.OriginRect()) { - for(int saveBlockY = 0; saveBlockY < newSave->blockHeight; saveBlockY++) + if(bmap[bpos.Y + blockP.Y][bpos.X + blockP.X]) { - if(bmap[saveBlockY+blockY][saveBlockX+blockX]) - { - newSave->blockMap[saveBlockY][saveBlockX] = bmap[saveBlockY+blockY][saveBlockX+blockX]; - newSave->fanVelX[saveBlockY][saveBlockX] = fvx[saveBlockY+blockY][saveBlockX+blockX]; - newSave->fanVelY[saveBlockY][saveBlockX] = fvy[saveBlockY+blockY][saveBlockX+blockX]; - } - if (includePressure) - { - newSave->pressure[saveBlockY][saveBlockX] = pv[saveBlockY+blockY][saveBlockX+blockX]; - newSave->velocityX[saveBlockY][saveBlockX] = vx[saveBlockY+blockY][saveBlockX+blockX]; - newSave->velocityY[saveBlockY][saveBlockX] = vy[saveBlockY+blockY][saveBlockX+blockX]; - newSave->ambientHeat[saveBlockY][saveBlockX] = hv[saveBlockY+blockY][saveBlockX+blockX]; - newSave->blockAir[saveBlockY][saveBlockX] = air->bmap_blockair[saveBlockY+blockY][saveBlockX+blockX]; - newSave->blockAirh[saveBlockY][saveBlockX] = air->bmap_blockairh[saveBlockY+blockY][saveBlockX+blockX]; - } + newSave->blockMap[bpos] = bmap[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->fanVelX[bpos] = fvx[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->fanVelY[bpos] = fvy[bpos.Y + blockP.Y][bpos.X + blockP.X]; + } + if (includePressure) + { + newSave->pressure[bpos] = pv[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->velocityX[bpos] = vx[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->velocityY[bpos] = vy[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->ambientHeat[bpos] = hv[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->blockAir[bpos] = air->bmap_blockair[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->blockAirh[bpos] = air->bmap_blockairh[bpos.Y + blockP.Y][bpos.X + blockP.X]; } } if (includePressure || ensureDeterminism)