Save higher halves of tmp3 and tmp4 if needed (fixes #822 with dbd971fb)

This commit is contained in:
Tamás Bálint Misius
2021-12-09 08:39:01 +01:00
parent dbd971fb05
commit c0e3818df3
2 changed files with 80 additions and 23 deletions

View File

@@ -45,8 +45,8 @@
#define BUILD_NUM 350 #define BUILD_NUM 350
#mesondefine SNAPSHOT_ID #mesondefine SNAPSHOT_ID
#mesondefine MOD_ID #mesondefine MOD_ID
#define FUTURE_SAVE_VERSION 96 #define FUTURE_SAVE_VERSION 97
#define FUTURE_MINOR_VERSION 2 #define FUTURE_MINOR_VERSION 0
#if !(defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0) #if !(defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0)
#undef FUTURE_SAVE_VERSION #undef FUTURE_SAVE_VERSION

View File

@@ -1024,7 +1024,7 @@ void GameSave::readOPS(char * data, int dataLength)
//Read particle data //Read particle data
if (partsData && partsPosData) if (partsData && partsPosData)
{ {
int newIndex = 0, fieldDescriptor, tempTemp; int newIndex = 0, tempTemp;
int posCount, posTotal, partsPosDataIndex = 0; int posCount, posTotal, partsPosDataIndex = 0;
if (fullW * fullH * 3 > partsPosDataLen) if (fullW * fullH * 3 > partsPosDataLen)
throw ParseException(ParseException::Corrupt, "Not enough particle position data"); throw ParseException(ParseException::Corrupt, "Not enough particle position data");
@@ -1052,8 +1052,8 @@ void GameSave::readOPS(char * data, int dataLength)
throw ParseException(ParseException::Corrupt, "Ran past particle data buffer"); throw ParseException(ParseException::Corrupt, "Ran past particle data buffer");
x = saved_x + fullX; x = saved_x + fullX;
y = saved_y + fullY; y = saved_y + fullY;
fieldDescriptor = partsData[i+1]; unsigned int fieldDescriptor = (unsigned int)(partsData[i+1]);
fieldDescriptor |= partsData[i+2] << 8; fieldDescriptor |= (unsigned int)(partsData[i+2]) << 8;
if (x >= fullW || y >= fullH) if (x >= fullW || y >= fullH)
throw ParseException(ParseException::Corrupt, "Particle out of range"); throw ParseException(ParseException::Corrupt, "Particle out of range");
@@ -1092,6 +1092,14 @@ void GameSave::readOPS(char * data, int dataLength)
particles[newIndex].temp = tempTemp+294.15f; 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;
}
//Read life //Read life
if(fieldDescriptor & 0x02) if(fieldDescriptor & 0x02)
{ {
@@ -1194,13 +1202,28 @@ void GameSave::readOPS(char * data, int dataLength)
{ {
if (i+3 >= partsDataLen) if (i+3 >= partsDataLen)
throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp3 and tmp4"); 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; unsigned int tmp34;
tmp34 = (unsigned int)partsData[i++]; tmp34 = (unsigned int)partsData[i + 0];
tmp34 |= (unsigned int)partsData[i++] << 8; tmp34 |= (unsigned int)partsData[i + 1] << 8;
particles[newIndex].tmp3 = tmp34; if (fieldDescriptor & 0x10000)
tmp34 = (unsigned int)partsData[i++]; {
tmp34 |= (unsigned int)partsData[i++] << 8; tmp34 |= (unsigned int)partsData[i + 4] << 16;
particles[newIndex].tmp4 = tmp34; 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;
} }
//Particle specific parsing: //Particle specific parsing:
@@ -2186,14 +2209,21 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
} }
//Copy parts data //Copy parts data
/* Field descriptor format: /* Field descriptor [1+2] format:
| 0 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| RESERVED | type[2] | tmp3 and tmp4 | tmp[3+4] | tmp2[2] | tmp2 | ctype[2] | vy | vx | decorations | ctype[1] | tmp[2] | tmp[1] | life[2] | life[1] | temp dbl len | | fieldDesc3 | type[2] | tmp3/4[1+2] | tmp[3+4] | tmp2[2] | tmp2 | ctype[2] | vy | vx | decorations | ctype[1] | tmp[2] | tmp[1] | life[2] | life[1] | temp dbl len |
life[2] means a second byte (for a 16 bit field) if life[1] is present life[2] means a second byte (for a 16 bit field) if life[1] is present
fieldDesc3 means Field descriptor [3] exists
Field descriptor [3] format:
| 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
| RESERVED | FREE | FREE | FREE | FREE | FREE | FREE | tmp3/4[3+4] |
last bit is reserved. If necessary, use it to signify that fieldDescriptor will have another byte last bit is reserved. If necessary, use it to signify that fieldDescriptor will have another byte
That way, if we ever need a 17th bit, we won't have to change the save format That way, if we ever need a 25th bit, we won't have to change the save format
*/ */
auto partsData = std::unique_ptr<unsigned char[]>(new unsigned char[NPART * (sizeof(Particle)+1)]);
// 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.
auto partsData = std::unique_ptr<unsigned char[]>(new unsigned char[NPART * (sizeof(Particle)+3)]);
unsigned int partsDataLen = 0; unsigned int partsDataLen = 0;
auto partsSaveIndex = std::unique_ptr<unsigned[]>(new unsigned[NPART]); auto partsSaveIndex = std::unique_ptr<unsigned[]>(new unsigned[NPART]);
unsigned int partsCount = 0; unsigned int partsCount = 0;
@@ -2210,8 +2240,8 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
//Loop while there is a pmap entry //Loop while there is a pmap entry
while (i) while (i)
{ {
unsigned short fieldDesc = 0; unsigned int fieldDesc = 0;
int fieldDescLoc = 0, tempTemp, vTemp; int tempTemp, vTemp;
//Turn pmap entry into a particles index //Turn pmap entry into a particles index
i = i>>8; i = i>>8;
@@ -2223,9 +2253,22 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
partsData[partsDataLen++] = particles[i].type; partsData[partsDataLen++] = particles[i].type;
//Location of the field descriptor //Location of the field descriptor
fieldDescLoc = partsDataLen++; int fieldDesc3Loc;
int fieldDescLoc = partsDataLen++;
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;
if ((tmp3 >> 16) || (tmp4 >> 16))
{
fieldDesc |= 1 << 15;
fieldDesc |= 1 << 16;
}
}
// Extra type byte if necessary // Extra type byte if necessary
if (particles[i].type & 0xFF00) if (particles[i].type & 0xFF00)
{ {
@@ -2249,6 +2292,12 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
partsData[partsDataLen++] = tempTemp >> 8; partsData[partsDataLen++] = tempTemp >> 8;
} }
if (fieldDesc & (1 << 15))
{
RESTRICTVERSION(97, 0);
fieldDesc3Loc = partsDataLen++;
}
//Life (optional), 1 to 2 bytes //Life (optional), 1 to 2 bytes
if(particles[i].life) if(particles[i].life)
{ {
@@ -2341,20 +2390,28 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
} }
//tmp3 and tmp4, 4 bytes //tmp3 and tmp4, 4 bytes
if ((particles[i].tmp3 || particles[i].tmp4) && (!PressureInTmp3(particles[i].type) || hasPressure)) if (fieldDesc & (1 << 13))
{ {
auto tmp3 = (unsigned int)(particles[i].tmp3);
auto tmp4 = (unsigned int)(particles[i].tmp4);
fieldDesc |= 1 << 13;
partsData[partsDataLen++] = tmp3 ; partsData[partsDataLen++] = tmp3 ;
partsData[partsDataLen++] = tmp3 >> 8; partsData[partsDataLen++] = tmp3 >> 8;
partsData[partsDataLen++] = tmp4 ; partsData[partsDataLen++] = tmp4 ;
partsData[partsDataLen++] = tmp4 >> 8; 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 //Write the field descriptor
partsData[fieldDescLoc] = fieldDesc; partsData[fieldDescLoc] = fieldDesc;
partsData[fieldDescLoc+1] = fieldDesc>>8; partsData[fieldDescLoc+1] = fieldDesc>>8;
if (fieldDesc & (1 << 15))
{
partsData[fieldDesc3Loc] = fieldDesc>>16;
}
if (particles[i].type == PT_SOAP) if (particles[i].type == PT_SOAP)
soapCount++; soapCount++;