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
#mesondefine SNAPSHOT_ID
#mesondefine MOD_ID
#define FUTURE_SAVE_VERSION 96
#define FUTURE_MINOR_VERSION 2
#define FUTURE_SAVE_VERSION 97
#define FUTURE_MINOR_VERSION 0
#if !(defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0)
#undef FUTURE_SAVE_VERSION

View File

@@ -1024,7 +1024,7 @@ void GameSave::readOPS(char * data, int dataLength)
//Read particle data
if (partsData && partsPosData)
{
int newIndex = 0, fieldDescriptor, tempTemp;
int newIndex = 0, tempTemp;
int posCount, posTotal, partsPosDataIndex = 0;
if (fullW * fullH * 3 > partsPosDataLen)
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");
x = saved_x + fullX;
y = saved_y + fullY;
fieldDescriptor = partsData[i+1];
fieldDescriptor |= partsData[i+2] << 8;
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");
@@ -1092,6 +1092,14 @@ void GameSave::readOPS(char * data, int dataLength)
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
if(fieldDescriptor & 0x02)
{
@@ -1194,13 +1202,28 @@ void GameSave::readOPS(char * data, int dataLength)
{
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++];
tmp34 |= (unsigned int)partsData[i++] << 8;
particles[newIndex].tmp3 = tmp34;
tmp34 = (unsigned int)partsData[i++];
tmp34 |= (unsigned int)partsData[i++] << 8;
particles[newIndex].tmp4 = 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);
i += 4;
if (fieldDescriptor & 0x10000)
i += 4;
}
//Particle specific parsing:
@@ -2186,14 +2209,21 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
}
//Copy parts data
/* Field descriptor format:
| 0 | 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 |
/* Field descriptor [1+2] format:
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 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
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
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;
auto partsSaveIndex = std::unique_ptr<unsigned[]>(new unsigned[NPART]);
unsigned int partsCount = 0;
@@ -2210,8 +2240,8 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
//Loop while there is a pmap entry
while (i)
{
unsigned short fieldDesc = 0;
int fieldDescLoc = 0, tempTemp, vTemp;
unsigned int fieldDesc = 0;
int tempTemp, vTemp;
//Turn pmap entry into a particles index
i = i>>8;
@@ -2223,9 +2253,22 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
partsData[partsDataLen++] = particles[i].type;
//Location of the field descriptor
fieldDescLoc = partsDataLen++;
int fieldDesc3Loc;
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;
if ((tmp3 >> 16) || (tmp4 >> 16))
{
fieldDesc |= 1 << 15;
fieldDesc |= 1 << 16;
}
}
// Extra type byte if necessary
if (particles[i].type & 0xFF00)
{
@@ -2249,6 +2292,12 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
partsData[partsDataLen++] = tempTemp >> 8;
}
if (fieldDesc & (1 << 15))
{
RESTRICTVERSION(97, 0);
fieldDesc3Loc = partsDataLen++;
}
//Life (optional), 1 to 2 bytes
if(particles[i].life)
{
@@ -2341,20 +2390,28 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
}
//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 >> 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++;