finish COMPATIBLE_SAVES and FIX_INCOMPATIBLE_SAVES #1149

Merged
withmorten merged 1 commits from saves into master 3 years ago

@ -50,41 +50,41 @@ void CAutoPilot::RemoveOnePathNode()
#ifdef COMPATIBLE_SAVES #ifdef COMPATIBLE_SAVES
void CAutoPilot::Save(uint8*& buf) void CAutoPilot::Save(uint8*& buf)
{ {
WriteSaveBuf<int32>(buf, m_nCurrentRouteNode); WriteSaveBuf(buf, m_nCurrentRouteNode);
WriteSaveBuf<int32>(buf, m_nNextRouteNode); WriteSaveBuf(buf, m_nNextRouteNode);
WriteSaveBuf<int32>(buf, m_nPrevRouteNode); WriteSaveBuf(buf, m_nPrevRouteNode);
WriteSaveBuf<int32>(buf, m_nTimeEnteredCurve); WriteSaveBuf(buf, m_nTimeEnteredCurve);
WriteSaveBuf<int32>(buf, m_nTimeToSpendOnCurrentCurve); WriteSaveBuf(buf, m_nTimeToSpendOnCurrentCurve);
WriteSaveBuf<uint32>(buf, m_nCurrentPathNodeInfo); WriteSaveBuf(buf, m_nCurrentPathNodeInfo);
WriteSaveBuf<uint32>(buf, m_nNextPathNodeInfo); WriteSaveBuf(buf, m_nNextPathNodeInfo);
WriteSaveBuf<uint32>(buf, m_nPreviousPathNodeInfo); WriteSaveBuf(buf, m_nPreviousPathNodeInfo);
WriteSaveBuf<uint32>(buf, m_nAntiReverseTimer); WriteSaveBuf(buf, m_nAntiReverseTimer);
WriteSaveBuf<uint32>(buf, m_nTimeToStartMission); WriteSaveBuf(buf, m_nTimeToStartMission);
WriteSaveBuf<int8>(buf, m_nPreviousDirection); WriteSaveBuf(buf, m_nPreviousDirection);
WriteSaveBuf<int8>(buf, m_nCurrentDirection); WriteSaveBuf(buf, m_nCurrentDirection);
WriteSaveBuf<int8>(buf, m_nNextDirection); WriteSaveBuf(buf, m_nNextDirection);
WriteSaveBuf<int8>(buf, m_nCurrentLane); WriteSaveBuf(buf, m_nCurrentLane);
WriteSaveBuf<int8>(buf, m_nNextLane); WriteSaveBuf(buf, m_nNextLane);
WriteSaveBuf<uint8>(buf, m_nDrivingStyle); WriteSaveBuf(buf, m_nDrivingStyle);
WriteSaveBuf<uint8>(buf, m_nCarMission); WriteSaveBuf(buf, m_nCarMission);
WriteSaveBuf<uint8>(buf, m_nTempAction); WriteSaveBuf(buf, m_nTempAction);
WriteSaveBuf<uint32>(buf, m_nTimeTempAction); WriteSaveBuf(buf, m_nTimeTempAction);
WriteSaveBuf<float>(buf, m_fMaxTrafficSpeed); WriteSaveBuf(buf, m_fMaxTrafficSpeed);
WriteSaveBuf<uint8>(buf, m_nCruiseSpeed); WriteSaveBuf(buf, m_nCruiseSpeed);
uint8 flags = 0; uint8 flags = 0;
if (m_bSlowedDownBecauseOfCars) flags |= BIT(0); if (m_bSlowedDownBecauseOfCars) flags |= BIT(0);
if (m_bSlowedDownBecauseOfPeds) flags |= BIT(1); if (m_bSlowedDownBecauseOfPeds) flags |= BIT(1);
if (m_bStayInCurrentLevel) flags |= BIT(2); if (m_bStayInCurrentLevel) flags |= BIT(2);
if (m_bStayInFastLane) flags |= BIT(3); if (m_bStayInFastLane) flags |= BIT(3);
if (m_bIgnorePathfinding) flags |= BIT(4); if (m_bIgnorePathfinding) flags |= BIT(4);
WriteSaveBuf<uint8>(buf, flags); WriteSaveBuf(buf, flags);
SkipSaveBuf(buf, 2); ZeroSaveBuf(buf, 2);
WriteSaveBuf<float>(buf, m_vecDestinationCoors.x); WriteSaveBuf(buf, m_vecDestinationCoors.x);
WriteSaveBuf<float>(buf, m_vecDestinationCoors.y); WriteSaveBuf(buf, m_vecDestinationCoors.y);
WriteSaveBuf<float>(buf, m_vecDestinationCoors.z); WriteSaveBuf(buf, m_vecDestinationCoors.z);
SkipSaveBuf(buf, 32); ZeroSaveBuf(buf, 32);
WriteSaveBuf<int16>(buf, m_nPathFindNodesCount); WriteSaveBuf(buf, m_nPathFindNodesCount);
SkipSaveBuf(buf, 6); ZeroSaveBuf(buf, 6);
} }
void CAutoPilot::Load(uint8*& buf) void CAutoPilot::Load(uint8*& buf)

@ -26,13 +26,6 @@
#include "World.h" #include "World.h"
#include "SaveBuf.h" #include "SaveBuf.h"
#define CRUSHER_GARAGE_X1 (1135.5f)
#define CRUSHER_GARAGE_Y1 (57.0f)
#define CRUSHER_GARAGE_Z1 (-1.0f)
#define CRUSHER_GARAGE_X2 (1149.5f)
#define CRUSHER_GARAGE_Y2 (63.7f)
#define CRUSHER_GARAGE_Z2 (3.5f)
#define ROTATED_DOOR_OPEN_SPEED (0.015f) #define ROTATED_DOOR_OPEN_SPEED (0.015f)
#define ROTATED_DOOR_CLOSE_SPEED (0.02f) #define ROTATED_DOOR_CLOSE_SPEED (0.02f)
#define DEFAULT_DOOR_OPEN_SPEED (0.035f) #define DEFAULT_DOOR_OPEN_SPEED (0.035f)
@ -1883,11 +1876,12 @@ void CStoredCar::StoreCar(CVehicle* pVehicle)
m_nRadioStation = pVehicle->m_nRadioStation; m_nRadioStation = pVehicle->m_nRadioStation;
m_nVariationA = pVehicle->m_aExtras[0]; m_nVariationA = pVehicle->m_aExtras[0];
m_nVariationB = pVehicle->m_aExtras[1]; m_nVariationB = pVehicle->m_aExtras[1];
m_bBulletproof = pVehicle->bBulletProof; m_nFlags = 0;
m_bFireproof = pVehicle->bFireProof; if (pVehicle->bBulletProof) m_nFlags |= FLAG_BULLETPROOF;
m_bExplosionproof = pVehicle->bExplosionProof; if (pVehicle->bFireProof) m_nFlags |= FLAG_FIREPROOF;
m_bCollisionproof = pVehicle->bCollisionProof; if (pVehicle->bExplosionProof) m_nFlags |= FLAG_EXPLOSIONPROOF;
m_bMeleeproof = pVehicle->bMeleeProof; if (pVehicle->bCollisionProof) m_nFlags |= FLAG_COLLISIONPROOF;
if (pVehicle->bMeleeProof) m_nFlags |= FLAG_MELEEPROOF;
if (pVehicle->IsCar()) if (pVehicle->IsCar())
m_nCarBombType = ((CAutomobile*)pVehicle)->m_bombType; m_nCarBombType = ((CAutomobile*)pVehicle)->m_bombType;
} }
@ -1936,11 +1930,11 @@ CVehicle* CStoredCar::RestoreCar()
} }
pVehicle->bHasBeenOwnedByPlayer = true; pVehicle->bHasBeenOwnedByPlayer = true;
pVehicle->m_nDoorLock = CARLOCK_UNLOCKED; pVehicle->m_nDoorLock = CARLOCK_UNLOCKED;
pVehicle->bBulletProof = m_bBulletproof; if (m_nFlags & FLAG_BULLETPROOF) pVehicle->bBulletProof = true;
pVehicle->bFireProof = m_bFireproof; if (m_nFlags & FLAG_FIREPROOF) pVehicle->bFireProof = true;
pVehicle->bExplosionProof = m_bExplosionproof; if (m_nFlags & FLAG_EXPLOSIONPROOF) pVehicle->bExplosionProof = true;
pVehicle->bCollisionProof = m_bCollisionproof; if (m_nFlags & FLAG_COLLISIONPROOF) pVehicle->bCollisionProof = true;
pVehicle->bMeleeProof = m_bMeleeproof; if (m_nFlags & FLAG_MELEEPROOF) pVehicle->bMeleeProof = true;
return pVehicle; return pVehicle;
} }
@ -2327,8 +2321,47 @@ void CGarages::Save(uint8 * buf, uint32 * size)
WriteSaveBuf(buf, aCarsInSafeHouse2[i]); WriteSaveBuf(buf, aCarsInSafeHouse2[i]);
WriteSaveBuf(buf, aCarsInSafeHouse3[i]); WriteSaveBuf(buf, aCarsInSafeHouse3[i]);
} }
for (int i = 0; i < NUM_GARAGES; i++) for (int i = 0; i < NUM_GARAGES; i++) {
#ifdef COMPATIBLE_SAVES
WriteSaveBuf(buf, aGarages[i].m_eGarageType);
WriteSaveBuf(buf, aGarages[i].m_eGarageState);
WriteSaveBuf(buf, aGarages[i].field_2);
WriteSaveBuf(buf, aGarages[i].m_bClosingWithoutTargetCar);
WriteSaveBuf(buf, aGarages[i].m_bDeactivated);
WriteSaveBuf(buf, aGarages[i].m_bResprayHappened);
ZeroSaveBuf(buf, 2);
WriteSaveBuf(buf, aGarages[i].m_nTargetModelIndex);
ZeroSaveBuf(buf, 4 + 4);
WriteSaveBuf(buf, aGarages[i].m_bDoor1PoolIndex);
WriteSaveBuf(buf, aGarages[i].m_bDoor2PoolIndex);
WriteSaveBuf(buf, aGarages[i].m_bDoor1IsDummy);
WriteSaveBuf(buf, aGarages[i].m_bDoor2IsDummy);
WriteSaveBuf(buf, aGarages[i].m_bRecreateDoorOnNextRefresh);
WriteSaveBuf(buf, aGarages[i].m_bRotatedDoor);
WriteSaveBuf(buf, aGarages[i].m_bCameraFollowsPlayer);
ZeroSaveBuf(buf, 1);
WriteSaveBuf(buf, aGarages[i].m_fX1);
WriteSaveBuf(buf, aGarages[i].m_fX2);
WriteSaveBuf(buf, aGarages[i].m_fY1);
WriteSaveBuf(buf, aGarages[i].m_fY2);
WriteSaveBuf(buf, aGarages[i].m_fZ1);
WriteSaveBuf(buf, aGarages[i].m_fZ2);
WriteSaveBuf(buf, aGarages[i].m_fDoorPos);
WriteSaveBuf(buf, aGarages[i].m_fDoorHeight);
WriteSaveBuf(buf, aGarages[i].m_fDoor1X);
WriteSaveBuf(buf, aGarages[i].m_fDoor1Y);
WriteSaveBuf(buf, aGarages[i].m_fDoor2X);
WriteSaveBuf(buf, aGarages[i].m_fDoor2Y);
WriteSaveBuf(buf, aGarages[i].m_fDoor1Z);
WriteSaveBuf(buf, aGarages[i].m_fDoor2Z);
WriteSaveBuf(buf, aGarages[i].m_nTimeToStartAction);
WriteSaveBuf(buf, aGarages[i].m_bCollectedCarsState);
ZeroSaveBuf(buf, 3 + 4 + 4);
ZeroSaveBuf(buf, sizeof(aGarages[i].m_sStoredCar));
#else
WriteSaveBuf(buf, aGarages[i]); WriteSaveBuf(buf, aGarages[i]);
#endif
}
#ifdef FIX_GARAGE_SIZE #ifdef FIX_GARAGE_SIZE
VALIDATESAVEBUF(*size); VALIDATESAVEBUF(*size);
#endif #endif
@ -2339,11 +2372,7 @@ const CStoredCar &CStoredCar::operator=(const CStoredCar & other)
m_nModelIndex = other.m_nModelIndex; m_nModelIndex = other.m_nModelIndex;
m_vecPos = other.m_vecPos; m_vecPos = other.m_vecPos;
m_vecAngle = other.m_vecAngle; m_vecAngle = other.m_vecAngle;
m_bBulletproof = other.m_bBulletproof; m_nFlags = other.m_nFlags;
m_bFireproof = other.m_bFireproof;
m_bExplosionproof = other.m_bExplosionproof;
m_bCollisionproof = other.m_bCollisionproof;
m_bMeleeproof = other.m_bMeleeproof;
m_nPrimaryColor = other.m_nPrimaryColor; m_nPrimaryColor = other.m_nPrimaryColor;
m_nSecondaryColor = other.m_nSecondaryColor; m_nSecondaryColor = other.m_nSecondaryColor;
m_nRadioStation = other.m_nRadioStation; m_nRadioStation = other.m_nRadioStation;
@ -2357,7 +2386,7 @@ void CGarages::Load(uint8* buf, uint32 size)
{ {
#ifdef FIX_GARAGE_SIZE #ifdef FIX_GARAGE_SIZE
INITSAVEBUF INITSAVEBUF
assert(size == (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + 3 * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage)); assert(size == (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CarTypesCollected) + sizeof(uint32) + 3 * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage)));
#else #else
assert(size == 5484); assert(size == 5484);
#endif #endif
@ -2380,7 +2409,45 @@ void CGarages::Load(uint8* buf, uint32 size)
ReadSaveBuf(&aCarsInSafeHouse3[i], buf); ReadSaveBuf(&aCarsInSafeHouse3[i], buf);
} }
for (int i = 0; i < NUM_GARAGES; i++) { for (int i = 0; i < NUM_GARAGES; i++) {
#ifdef COMPATIBLE_SAVES
ReadSaveBuf(&aGarages[i].m_eGarageType, buf);
ReadSaveBuf(&aGarages[i].m_eGarageState, buf);
ReadSaveBuf(&aGarages[i].field_2, buf);
ReadSaveBuf(&aGarages[i].m_bClosingWithoutTargetCar, buf);
ReadSaveBuf(&aGarages[i].m_bDeactivated, buf);
ReadSaveBuf(&aGarages[i].m_bResprayHappened, buf);
SkipSaveBuf(buf, 2);
ReadSaveBuf(&aGarages[i].m_nTargetModelIndex, buf);
SkipSaveBuf(buf, 4 + 4);
ReadSaveBuf(&aGarages[i].m_bDoor1PoolIndex, buf);
ReadSaveBuf(&aGarages[i].m_bDoor2PoolIndex, buf);
ReadSaveBuf(&aGarages[i].m_bDoor1IsDummy, buf);
ReadSaveBuf(&aGarages[i].m_bDoor2IsDummy, buf);
ReadSaveBuf(&aGarages[i].m_bRecreateDoorOnNextRefresh, buf);
ReadSaveBuf(&aGarages[i].m_bRotatedDoor, buf);
ReadSaveBuf(&aGarages[i].m_bCameraFollowsPlayer, buf);
SkipSaveBuf(buf, 1);
ReadSaveBuf(&aGarages[i].m_fX1, buf);
ReadSaveBuf(&aGarages[i].m_fX2, buf);
ReadSaveBuf(&aGarages[i].m_fY1, buf);
ReadSaveBuf(&aGarages[i].m_fY2, buf);
ReadSaveBuf(&aGarages[i].m_fZ1, buf);
ReadSaveBuf(&aGarages[i].m_fZ2, buf);
ReadSaveBuf(&aGarages[i].m_fDoorPos, buf);
ReadSaveBuf(&aGarages[i].m_fDoorHeight, buf);
ReadSaveBuf(&aGarages[i].m_fDoor1X, buf);
ReadSaveBuf(&aGarages[i].m_fDoor1Y, buf);
ReadSaveBuf(&aGarages[i].m_fDoor2X, buf);
ReadSaveBuf(&aGarages[i].m_fDoor2Y, buf);
ReadSaveBuf(&aGarages[i].m_fDoor1Z, buf);
ReadSaveBuf(&aGarages[i].m_fDoor2Z, buf);
ReadSaveBuf(&aGarages[i].m_nTimeToStartAction, buf);
ReadSaveBuf(&aGarages[i].m_bCollectedCarsState, buf);
SkipSaveBuf(buf, 3 + 4 + 4);
SkipSaveBuf(buf, sizeof(aGarages[i].m_sStoredCar));
#else
ReadSaveBuf(&aGarages[i], buf); ReadSaveBuf(&aGarages[i], buf);
#endif
aGarages[i].m_pDoor1 = nil; aGarages[i].m_pDoor1 = nil;
aGarages[i].m_pDoor2 = nil; aGarages[i].m_pDoor2 = nil;
aGarages[i].m_pTarget = nil; aGarages[i].m_pTarget = nil;

@ -51,14 +51,17 @@ enum
class CStoredCar class CStoredCar
{ {
enum {
FLAG_BULLETPROOF = 0x1,
FLAG_FIREPROOF = 0x2,
FLAG_EXPLOSIONPROOF = 0x4,
FLAG_COLLISIONPROOF = 0x8,
FLAG_MELEEPROOF = 0x10,
};
int32 m_nModelIndex; int32 m_nModelIndex;
CVector m_vecPos; CVector m_vecPos;
CVector m_vecAngle; CVector m_vecAngle;
int32 m_bBulletproof : 1; int32 m_nFlags;
int32 m_bFireproof : 1;
int32 m_bExplosionproof : 1;
int32 m_bCollisionproof : 1;
int32 m_bMeleeproof : 1;
int8 m_nPrimaryColor; int8 m_nPrimaryColor;
int8 m_nSecondaryColor; int8 m_nSecondaryColor;
int8 m_nRadioStation; int8 m_nRadioStation;
@ -78,6 +81,13 @@ VALIDATE_SIZE(CStoredCar, 0x28);
#define SWITCH_GARAGE_DISTANCE_CLOSE 40.0f #define SWITCH_GARAGE_DISTANCE_CLOSE 40.0f
#define CRUSHER_GARAGE_X1 (1135.5f)
#define CRUSHER_GARAGE_Y1 (57.0f)
#define CRUSHER_GARAGE_Z1 (-1.0f)
#define CRUSHER_GARAGE_X2 (1149.5f)
#define CRUSHER_GARAGE_Y2 (63.7f)
#define CRUSHER_GARAGE_Z2 (3.5f)
class CGarage class CGarage
{ {
public: public:
@ -87,7 +97,7 @@ public:
bool m_bClosingWithoutTargetCar; bool m_bClosingWithoutTargetCar;
bool m_bDeactivated; bool m_bDeactivated;
bool m_bResprayHappened; bool m_bResprayHappened;
int m_nTargetModelIndex; int32 m_nTargetModelIndex;
CEntity *m_pDoor1; CEntity *m_pDoor1;
CEntity *m_pDoor2; CEntity *m_pDoor2;
uint8 m_bDoor1PoolIndex; uint8 m_bDoor1PoolIndex;

@ -18,6 +18,12 @@
#include "Replay.h" #include "Replay.h"
#endif #endif
#ifdef COMPATIBLE_SAVES
#define PHONEINFO_SAVE_SIZE 0xA30
#else
#define PHONEINFO_SAVE_SIZE sizeof(CPhoneInfo)
#endif
CPhoneInfo gPhoneInfo; CPhoneInfo gPhoneInfo;
bool CPhoneInfo::bDisplayingPhoneMessage; // is phone picked up bool CPhoneInfo::bDisplayingPhoneMessage; // is phone picked up
@ -209,6 +215,22 @@ CPhoneInfo::IsMessageBeingDisplayed(int phoneId)
return pPhoneDisplayingMessages == &m_aPhones[phoneId]; return pPhoneDisplayingMessages == &m_aPhones[phoneId];
} }
#ifdef COMPATIBLE_SAVES
static inline void
LoadPhone(CPhone &phone, uint8 *&buf)
{
ReadSaveBuf(&phone.m_vecPos, buf);
SkipSaveBuf(buf, 6 * 4);
ReadSaveBuf<uint32>(&phone.m_repeatedMessagePickupStart, buf);
uint32 tmp;
ReadSaveBuf(&tmp, buf);
phone.m_pEntity = (CEntity*)(uintptr)tmp;
ReadSaveBuf<PhoneState>(&phone.m_nState, buf);
ReadSaveBuf<bool>(&phone.m_visibleToCam, buf);
SkipSaveBuf(buf, 3);
}
#endif
void void
CPhoneInfo::Load(uint8 *buf, uint32 size) CPhoneInfo::Load(uint8 *buf, uint32 size)
{ {
@ -226,7 +248,12 @@ INITSAVEBUF
// We can do it without touching saves. We'll only load script phones, others are already loaded in Initialise // We can do it without touching saves. We'll only load script phones, others are already loaded in Initialise
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
CPhone phoneToLoad; CPhone phoneToLoad;
#ifdef COMPATIBLE_SAVES
phoneToLoad.m_apMessages[0]=phoneToLoad.m_apMessages[1]=phoneToLoad.m_apMessages[2]=phoneToLoad.m_apMessages[3]=phoneToLoad.m_apMessages[4]=phoneToLoad.m_apMessages[5] = nil;
LoadPhone(phoneToLoad, buf);
#else
ReadSaveBuf(&phoneToLoad, buf); ReadSaveBuf(&phoneToLoad, buf);
#endif
if (ignoreOtherPhones) if (ignoreOtherPhones)
continue; continue;
@ -252,7 +279,11 @@ INITSAVEBUF
m_nScriptPhonesMax = scriptPhonesMax; m_nScriptPhonesMax = scriptPhonesMax;
for (int i = 0; i < NUMPHONES; i++) { for (int i = 0; i < NUMPHONES; i++) {
#ifdef COMPATIBLE_SAVES
LoadPhone(m_aPhones[i], buf);
#else
ReadSaveBuf(&m_aPhones[i], buf); ReadSaveBuf(&m_aPhones[i], buf);
#endif
// It's saved as building pool index in save file, convert it to true entity // It's saved as building pool index in save file, convert it to true entity
if (m_aPhones[i].m_pEntity) { if (m_aPhones[i].m_pEntity) {
m_aPhones[i].m_pEntity = CPools::GetBuildingPool()->GetSlot((uintptr)m_aPhones[i].m_pEntity - 1); m_aPhones[i].m_pEntity = CPools::GetBuildingPool()->GetSlot((uintptr)m_aPhones[i].m_pEntity - 1);
@ -376,7 +407,7 @@ CPhoneInfo::Initialise(void)
void void
CPhoneInfo::Save(uint8 *buf, uint32 *size) CPhoneInfo::Save(uint8 *buf, uint32 *size)
{ {
*size = sizeof(CPhoneInfo); *size = PHONEINFO_SAVE_SIZE;
INITSAVEBUF INITSAVEBUF
WriteSaveBuf(buf, m_nMax); WriteSaveBuf(buf, m_nMax);
WriteSaveBuf(buf, m_nScriptPhonesMax); WriteSaveBuf(buf, m_nScriptPhonesMax);
@ -385,12 +416,24 @@ INITSAVEBUF
#else #else
for (int phoneId = 0; phoneId < NUMPHONES; phoneId++) { for (int phoneId = 0; phoneId < NUMPHONES; phoneId++) {
#endif #endif
#ifdef COMPATIBLE_SAVES
WriteSaveBuf(buf, m_aPhones[phoneId].m_vecPos);
ZeroSaveBuf(buf, 6 * 4);
WriteSaveBuf(buf, m_aPhones[phoneId].m_repeatedMessagePickupStart);
// Convert entity pointer to building pool index while saving
int32 tmp = m_aPhones[phoneId].m_pEntity ? CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)m_aPhones[phoneId].m_pEntity) + 1 : 0;
WriteSaveBuf(buf, tmp);
WriteSaveBuf(buf, m_aPhones[phoneId].m_nState);
WriteSaveBuf(buf, m_aPhones[phoneId].m_visibleToCam);
ZeroSaveBuf(buf, 3);
#else
CPhone* phone = WriteSaveBuf(buf, m_aPhones[phoneId]); CPhone* phone = WriteSaveBuf(buf, m_aPhones[phoneId]);
// Convert entity pointer to building pool index while saving // Convert entity pointer to building pool index while saving
if (phone->m_pEntity) { if (phone->m_pEntity) {
phone->m_pEntity = (CEntity*) (CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)phone->m_pEntity) + 1); phone->m_pEntity = (CEntity*) (CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)phone->m_pEntity) + 1);
} }
#endif
} }
VALIDATESAVEBUF(*size) VALIDATESAVEBUF(*size)
} }

@ -32,6 +32,12 @@
#include "WaterLevel.h" #include "WaterLevel.h"
#include "World.h" #include "World.h"
#ifdef COMPATIBLE_SAVES
#define PICKUPS_SAVE_SIZE 0x24C0
#else
#define PICKUPS_SAVE_SIZE sizeof(aPickUps)
#endif
CPickup CPickups::aPickUps[NUMPICKUPS]; CPickup CPickups::aPickUps[NUMPICKUPS];
int16 CPickups::NumMessages; int16 CPickups::NumMessages;
int32 CPickups::aPickUpsCollected[NUMCOLLECTEDPICKUPS]; int32 CPickups::aPickUpsCollected[NUMCOLLECTEDPICKUPS];
@ -1000,10 +1006,23 @@ CPickups::Load(uint8 *buf, uint32 size)
INITSAVEBUF INITSAVEBUF
for (int32 i = 0; i < NUMPICKUPS; i++) { for (int32 i = 0; i < NUMPICKUPS; i++) {
#ifdef COMPATIBLE_SAVES
ReadSaveBuf(&aPickUps[i].m_eType, buf);
ReadSaveBuf(&aPickUps[i].m_bRemoved, buf);
ReadSaveBuf(&aPickUps[i].m_nQuantity, buf);
int32 tmp;
ReadSaveBuf(&tmp, buf);
aPickUps[i].m_pObject = aPickUps[i].m_eType != PICKUP_NONE && tmp != 0 ? CPools::GetObjectPool()->GetSlot(tmp - 1) : nil;
ReadSaveBuf(&aPickUps[i].m_nTimer, buf);
ReadSaveBuf(&aPickUps[i].m_eModelIndex, buf);
ReadSaveBuf(&aPickUps[i].m_nIndex, buf);
ReadSaveBuf(&aPickUps[i].m_vecPos, buf);
#else
ReadSaveBuf(&aPickUps[i], buf); ReadSaveBuf(&aPickUps[i], buf);
if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_pObject != nil) if (aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_pObject != nil)
aPickUps[i].m_pObject = CPools::GetObjectPool()->GetSlot((uintptr)aPickUps[i].m_pObject - 1); aPickUps[i].m_pObject = CPools::GetObjectPool()->GetSlot((uintptr)aPickUps[i].m_pObject - 1);
#endif
} }
ReadSaveBuf(&CollectedPickUpIndex, buf); ReadSaveBuf(&CollectedPickUpIndex, buf);
@ -1019,14 +1038,26 @@ VALIDATESAVEBUF(size)
void void
CPickups::Save(uint8 *buf, uint32 *size) CPickups::Save(uint8 *buf, uint32 *size)
{ {
*size = sizeof(aPickUps) + sizeof(uint16) + sizeof(uint16) + sizeof(aPickUpsCollected); *size = PICKUPS_SAVE_SIZE + sizeof(uint16) + sizeof(uint16) + sizeof(aPickUpsCollected);
INITSAVEBUF INITSAVEBUF
for (int32 i = 0; i < NUMPICKUPS; i++) { for (int32 i = 0; i < NUMPICKUPS; i++) {
#ifdef COMPATIBLE_SAVES
WriteSaveBuf(buf, aPickUps[i].m_eType);
WriteSaveBuf(buf, aPickUps[i].m_bRemoved);
WriteSaveBuf(buf, aPickUps[i].m_nQuantity);
int32 tmp = aPickUps[i].m_eType != PICKUP_NONE && aPickUps[i].m_pObject != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aPickUps[i].m_pObject) + 1 : 0;
WriteSaveBuf(buf, tmp);
WriteSaveBuf(buf, aPickUps[i].m_nTimer);
WriteSaveBuf(buf, aPickUps[i].m_eModelIndex);
WriteSaveBuf(buf, aPickUps[i].m_nIndex);
WriteSaveBuf(buf, aPickUps[i].m_vecPos);
#else
CPickup *buf_pickup = WriteSaveBuf(buf, aPickUps[i]); CPickup *buf_pickup = WriteSaveBuf(buf, aPickUps[i]);
if (buf_pickup->m_eType != PICKUP_NONE && buf_pickup->m_pObject != nil) if (buf_pickup->m_eType != PICKUP_NONE && buf_pickup->m_pObject != nil)
buf_pickup->m_pObject = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(buf_pickup->m_pObject) + 1); buf_pickup->m_pObject = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(buf_pickup->m_pObject) + 1);
#endif
} }
WriteSaveBuf(buf, CollectedPickUpIndex); WriteSaveBuf(buf, CollectedPickUpIndex);

@ -2089,33 +2089,33 @@ VALIDATESAVEBUF(size)
void CRunningScript::Save(uint8*& buf) void CRunningScript::Save(uint8*& buf)
{ {
#ifdef COMPATIBLE_SAVES #ifdef COMPATIBLE_SAVES
SkipSaveBuf(buf, 8); ZeroSaveBuf(buf, 8);
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
WriteSaveBuf<char>(buf, m_abScriptName[i]); WriteSaveBuf(buf, m_abScriptName[i]);
WriteSaveBuf<uint32>(buf, m_nIp); WriteSaveBuf(buf, m_nIp);
#ifdef CHECK_STRUCT_SIZES #ifdef CHECK_STRUCT_SIZES
static_assert(MAX_STACK_DEPTH == 6, "Compatibility loss: MAX_STACK_DEPTH != 6"); static_assert(MAX_STACK_DEPTH == 6, "Compatibility loss: MAX_STACK_DEPTH != 6");
#endif #endif
for (int i = 0; i < MAX_STACK_DEPTH; i++) for (int i = 0; i < MAX_STACK_DEPTH; i++)
WriteSaveBuf<uint32>(buf, m_anStack[i]); WriteSaveBuf(buf, m_anStack[i]);
WriteSaveBuf<uint16>(buf, m_nStackPointer); WriteSaveBuf(buf, m_nStackPointer);
SkipSaveBuf(buf, 2); ZeroSaveBuf(buf, 2);
#ifdef CHECK_STRUCT_SIZES #ifdef CHECK_STRUCT_SIZES
static_assert(NUM_LOCAL_VARS + NUM_TIMERS == 18, "Compatibility loss: NUM_LOCAL_VARS + NUM_TIMERS != 18"); static_assert(NUM_LOCAL_VARS + NUM_TIMERS == 18, "Compatibility loss: NUM_LOCAL_VARS + NUM_TIMERS != 18");
#endif #endif
for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++) for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++)
WriteSaveBuf<int32>(buf, m_anLocalVariables[i]); WriteSaveBuf(buf, m_anLocalVariables[i]);
WriteSaveBuf<bool>(buf, m_bCondResult); WriteSaveBuf(buf, m_bCondResult);
WriteSaveBuf<bool>(buf, m_bIsMissionScript); WriteSaveBuf(buf, m_bIsMissionScript);
WriteSaveBuf<bool>(buf, m_bSkipWakeTime); WriteSaveBuf(buf, m_bSkipWakeTime);
SkipSaveBuf(buf, 1); ZeroSaveBuf(buf, 1);
WriteSaveBuf<uint32>(buf, m_nWakeTime); WriteSaveBuf(buf, m_nWakeTime);
WriteSaveBuf<uint16>(buf, m_nAndOrState); WriteSaveBuf(buf, m_nAndOrState);
WriteSaveBuf<bool>(buf, m_bNotFlag); WriteSaveBuf(buf, m_bNotFlag);
WriteSaveBuf<bool>(buf, m_bDeatharrestEnabled); WriteSaveBuf(buf, m_bDeatharrestEnabled);
WriteSaveBuf<bool>(buf, m_bDeatharrestExecuted); WriteSaveBuf(buf, m_bDeatharrestExecuted);
WriteSaveBuf<bool>(buf, m_bMissionFlag); WriteSaveBuf(buf, m_bMissionFlag);
SkipSaveBuf(buf, 2); ZeroSaveBuf(buf, 2);
#else #else
WriteSaveBuf(buf, *this); WriteSaveBuf(buf, *this);
#endif #endif

@ -281,9 +281,9 @@ INITSAVEBUF
#else #else
if ((pVehicle->IsCar() || pVehicle->IsBoat()) && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { if ((pVehicle->IsCar() || pVehicle->IsBoat()) && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif #endif
WriteSaveBuf<uint32>(buf, pVehicle->m_vehType); WriteSaveBuf(buf, pVehicle->m_vehType);
WriteSaveBuf<int16>(buf, pVehicle->GetModelIndex()); WriteSaveBuf(buf, pVehicle->GetModelIndex());
WriteSaveBuf<int32>(buf, GetVehicleRef(pVehicle)); WriteSaveBuf(buf, GetVehicleRef(pVehicle));
pVehicle->Save(buf); pVehicle->Save(buf);
} }
#else #else
@ -292,7 +292,7 @@ INITSAVEBUF
#else #else
if (pVehicle->IsCar() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { if (pVehicle->IsCar() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif #endif
WriteSaveBuf(buf, (uint32)pVehicle->m_vehType); WriteSaveBuf(buf, pVehicle->m_vehType);
WriteSaveBuf(buf, pVehicle->GetModelIndex()); WriteSaveBuf(buf, pVehicle->GetModelIndex());
WriteSaveBuf(buf, GetVehicleRef(pVehicle)); WriteSaveBuf(buf, GetVehicleRef(pVehicle));
memcpy(buf, pVehicle, sizeof(CAutomobile)); memcpy(buf, pVehicle, sizeof(CAutomobile));
@ -303,7 +303,7 @@ INITSAVEBUF
#else #else
if (pVehicle->IsBoat() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) { if (pVehicle->IsBoat() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif #endif
WriteSaveBuf(buf, (uint32)pVehicle->m_vehType); WriteSaveBuf(buf, pVehicle->m_vehType);
WriteSaveBuf(buf, pVehicle->GetModelIndex()); WriteSaveBuf(buf, pVehicle->GetModelIndex());
WriteSaveBuf(buf, GetVehicleRef(pVehicle)); WriteSaveBuf(buf, GetVehicleRef(pVehicle));
memcpy(buf, pVehicle, sizeof(CBoat)); memcpy(buf, pVehicle, sizeof(CBoat));

@ -10,6 +10,14 @@
#include "Timer.h" #include "Timer.h"
#include "SaveBuf.h" #include "SaveBuf.h"
#ifdef COMPATIBLE_SAVES
#define ZONEARRAY_SAVE_SIZE 0xAF0
#define MAPZONEARRAY_SAVE_SIZE 0x578
#else
#define ZONEARRAY_SAVE_SIZE sizeof(ZoneArray)
#define MAPZONEARRAY_SAVE_SIZE sizeof(MapZoneArray)
#endif
eLevelName CTheZones::m_CurrLevel; eLevelName CTheZones::m_CurrLevel;
CZone *CTheZones::m_pPlayersZone; CZone *CTheZones::m_pPlayersZone;
int16 CTheZones::FindIndex; int16 CTheZones::FindIndex;
@ -633,6 +641,28 @@ CTheZones::InitialiseAudioZoneArray(void)
} }
} }
#ifdef COMPATIBLE_SAVES
static inline void
SaveOneZone(CZone &zone, uint8 *&buffer)
{
memcpy(buffer, zone.name, sizeof(zone.name));
SkipSaveBuf(buffer, sizeof(zone.name));
WriteSaveBuf(buffer, zone.minx);
WriteSaveBuf(buffer, zone.miny);
WriteSaveBuf(buffer, zone.minz);
WriteSaveBuf(buffer, zone.maxx);
WriteSaveBuf(buffer, zone.maxy);
WriteSaveBuf(buffer, zone.maxz);
WriteSaveBuf(buffer, zone.type);
WriteSaveBuf(buffer, zone.level);
WriteSaveBuf(buffer, zone.zoneinfoDay);
WriteSaveBuf(buffer, zone.zoneinfoNight);
WriteSaveBuf(buffer, (int32)CTheZones::GetIndexForZonePointer(zone.child));
WriteSaveBuf(buffer, (int32)CTheZones::GetIndexForZonePointer(zone.parent));
WriteSaveBuf(buffer, (int32)CTheZones::GetIndexForZonePointer(zone.next));
}
#endif
void void
CTheZones::SaveAllZones(uint8 *buffer, uint32 *size) CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
{ {
@ -643,9 +673,9 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
+ sizeof(int32) // GetIndexForZonePointer + sizeof(int32) // GetIndexForZonePointer
+ sizeof(m_CurrLevel) + sizeof(FindIndex) + sizeof(m_CurrLevel) + sizeof(FindIndex)
+ sizeof(int16) // padding + sizeof(int16) // padding
+ sizeof(ZoneArray) + sizeof(ZoneInfoArray) + ZONEARRAY_SAVE_SIZE + sizeof(ZoneInfoArray)
+ sizeof(TotalNumberOfZones) + sizeof(TotalNumberOfZoneInfos) + sizeof(TotalNumberOfZones) + sizeof(TotalNumberOfZoneInfos)
+ sizeof(MapZoneArray) + sizeof(AudioZoneArray) + MAPZONEARRAY_SAVE_SIZE + sizeof(AudioZoneArray)
+ sizeof(TotalNumberOfMapZones) + sizeof(NumberOfAudioZones); + sizeof(TotalNumberOfMapZones) + sizeof(NumberOfAudioZones);
WriteSaveHeader(buffer, 'Z', 'N', 'S', '\0', *size - SAVE_HEADER_SIZE); WriteSaveHeader(buffer, 'Z', 'N', 'S', '\0', *size - SAVE_HEADER_SIZE);
@ -656,10 +686,14 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
WriteSaveBuf(buffer, (int16)0); // padding WriteSaveBuf(buffer, (int16)0); // padding
for(i = 0; i < ARRAY_SIZE(ZoneArray); i++){ for(i = 0; i < ARRAY_SIZE(ZoneArray); i++){
#ifdef COMPATIBLE_SAVES
SaveOneZone(ZoneArray[i], buffer);
#else
CZone *zone = WriteSaveBuf(buffer, ZoneArray[i]); CZone *zone = WriteSaveBuf(buffer, ZoneArray[i]);
zone->child = (CZone*)GetIndexForZonePointer(ZoneArray[i].child); zone->child = (CZone*)GetIndexForZonePointer(ZoneArray[i].child);
zone->parent = (CZone*)GetIndexForZonePointer(ZoneArray[i].parent); zone->parent = (CZone*)GetIndexForZonePointer(ZoneArray[i].parent);
zone->next = (CZone*)GetIndexForZonePointer(ZoneArray[i].next); zone->next = (CZone*)GetIndexForZonePointer(ZoneArray[i].next);
#endif
} }
for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++) for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++)
@ -669,7 +703,9 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
WriteSaveBuf(buffer, TotalNumberOfZoneInfos); WriteSaveBuf(buffer, TotalNumberOfZoneInfos);
for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) { for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) {
#ifndef COMPATIBLE_SAVES
CZone* zone = WriteSaveBuf(buffer, MapZoneArray[i]); CZone* zone = WriteSaveBuf(buffer, MapZoneArray[i]);
#endif
/* /*
The call of GetIndexForZonePointer is wrong, as it is The call of GetIndexForZonePointer is wrong, as it is
@ -679,9 +715,13 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
assert(MapZoneArray[i].child == nil); assert(MapZoneArray[i].child == nil);
assert(MapZoneArray[i].parent == nil); assert(MapZoneArray[i].parent == nil);
assert(MapZoneArray[i].next == nil); assert(MapZoneArray[i].next == nil);
#ifndef COMPATIBLE_SAVES
zone->child = (CZone*)GetIndexForZonePointer(MapZoneArray[i].child); zone->child = (CZone*)GetIndexForZonePointer(MapZoneArray[i].child);
zone->parent = (CZone*)GetIndexForZonePointer(MapZoneArray[i].parent); zone->parent = (CZone*)GetIndexForZonePointer(MapZoneArray[i].parent);
zone->next = (CZone*)GetIndexForZonePointer(MapZoneArray[i].next); zone->next = (CZone*)GetIndexForZonePointer(MapZoneArray[i].next);
#else
SaveOneZone(MapZoneArray[i], buffer);
#endif
} }
for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++) for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++)
@ -693,6 +733,32 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
VALIDATESAVEBUF(*size) VALIDATESAVEBUF(*size)
} }
#ifdef COMPATIBLE_SAVES
static inline void
LoadOneZone(CZone &zone, uint8 *&buffer)
{
memcpy(zone.name, buffer, sizeof(zone.name));
SkipSaveBuf(buffer, sizeof(zone.name));
ReadSaveBuf(&zone.minx, buffer);
ReadSaveBuf(&zone.miny, buffer);
ReadSaveBuf(&zone.minz, buffer);
ReadSaveBuf(&zone.maxx, buffer);
ReadSaveBuf(&zone.maxy, buffer);
ReadSaveBuf(&zone.maxz, buffer);
ReadSaveBuf(&zone.type, buffer);
ReadSaveBuf(&zone.level, buffer);
ReadSaveBuf(&zone.zoneinfoDay, buffer);
ReadSaveBuf(&zone.zoneinfoNight, buffer);
int32 tmp;
ReadSaveBuf(&tmp, buffer);
zone.child = CTheZones::GetPointerForZoneIndex(tmp);
ReadSaveBuf(&tmp, buffer);
zone.parent = CTheZones::GetPointerForZoneIndex(tmp);
ReadSaveBuf(&tmp, buffer);
zone.next = CTheZones::GetPointerForZoneIndex(tmp);
}
#endif
void void
CTheZones::LoadAllZones(uint8 *buffer, uint32 size) CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
{ {
@ -708,11 +774,15 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
SkipSaveBuf(buffer, 2); SkipSaveBuf(buffer, 2);
for(i = 0; i < ARRAY_SIZE(ZoneArray); i++){ for(i = 0; i < ARRAY_SIZE(ZoneArray); i++){
#ifdef COMPATIBLE_SAVES
LoadOneZone(ZoneArray[i], buffer);
#else
ReadSaveBuf(&ZoneArray[i], buffer); ReadSaveBuf(&ZoneArray[i], buffer);
ZoneArray[i].child = GetPointerForZoneIndex((uintptr)ZoneArray[i].child); ZoneArray[i].child = GetPointerForZoneIndex((uintptr)ZoneArray[i].child);
ZoneArray[i].parent = GetPointerForZoneIndex((uintptr)ZoneArray[i].parent); ZoneArray[i].parent = GetPointerForZoneIndex((uintptr)ZoneArray[i].parent);
ZoneArray[i].next = GetPointerForZoneIndex((uintptr)ZoneArray[i].next); ZoneArray[i].next = GetPointerForZoneIndex((uintptr)ZoneArray[i].next);
#endif
} }
for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++) for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++)
@ -722,6 +792,9 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
ReadSaveBuf(&TotalNumberOfZoneInfos, buffer); ReadSaveBuf(&TotalNumberOfZoneInfos, buffer);
for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++){ for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++){
#ifdef COMPATIBLE_SAVES
LoadOneZone(MapZoneArray[i], buffer);
#else
ReadSaveBuf(&MapZoneArray[i], buffer); ReadSaveBuf(&MapZoneArray[i], buffer);
/* /*
@ -732,6 +805,7 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
MapZoneArray[i].child = GetPointerForZoneIndex((uintptr)MapZoneArray[i].child); MapZoneArray[i].child = GetPointerForZoneIndex((uintptr)MapZoneArray[i].child);
MapZoneArray[i].parent = GetPointerForZoneIndex((uintptr)MapZoneArray[i].parent); MapZoneArray[i].parent = GetPointerForZoneIndex((uintptr)MapZoneArray[i].parent);
MapZoneArray[i].next = GetPointerForZoneIndex((uintptr)MapZoneArray[i].next); MapZoneArray[i].next = GetPointerForZoneIndex((uintptr)MapZoneArray[i].next);
#endif
assert(MapZoneArray[i].child == nil); assert(MapZoneArray[i].child == nil);
assert(MapZoneArray[i].parent == nil); assert(MapZoneArray[i].parent == nil);
assert(MapZoneArray[i].next == nil); assert(MapZoneArray[i].next == nil);

@ -237,7 +237,8 @@ enum Config {
#define FIX_BUGS // fixes bugs that we've came across during reversing. You can undefine this only on release builds. #define FIX_BUGS // fixes bugs that we've came across during reversing. You can undefine this only on release builds.
#define MORE_LANGUAGES // Add more translations to the game #define MORE_LANGUAGES // Add more translations to the game
#define COMPATIBLE_SAVES // this allows changing structs while keeping saves compatible #define COMPATIBLE_SAVES // this allows changing structs while keeping saves compatible, and keeps saves compatible between platforms
#define FIX_INCOMPATIBLE_SAVES // try to fix incompatible saves, requires COMPATIBLE_SAVES
#define LOAD_INI_SETTINGS // as the name suggests. fundamental for CUSTOM_FRONTEND_OPTIONS #define LOAD_INI_SETTINGS // as the name suggests. fundamental for CUSTOM_FRONTEND_OPTIONS
#define NO_MOVIES // add option to disable intro videos #define NO_MOVIES // add option to disable intro videos
@ -464,6 +465,7 @@ enum Config {
#define THIS_IS_STUPID #define THIS_IS_STUPID
#undef MORE_LANGUAGES #undef MORE_LANGUAGES
#undef COMPATIBLE_SAVES #undef COMPATIBLE_SAVES
#undef FIX_INCOMPATIBLE_SAVES
#undef LOAD_INI_SETTINGS #undef LOAD_INI_SETTINGS
#undef ASPECT_RATIO_SCALE #undef ASPECT_RATIO_SCALE

@ -732,7 +732,7 @@ CEntity::SaveEntityFlags(uint8*& buf)
if (bZoneCulled) tmp |= BIT(30); if (bZoneCulled) tmp |= BIT(30);
if (bZoneCulled2) tmp |= BIT(31); if (bZoneCulled2) tmp |= BIT(31);
WriteSaveBuf<uint32>(buf, tmp); WriteSaveBuf(buf, tmp);
tmp = 0; tmp = 0;
@ -748,7 +748,7 @@ CEntity::SaveEntityFlags(uint8*& buf)
if (bDistanceFade) tmp |= BIT(8); if (bDistanceFade) tmp |= BIT(8);
if (m_flagE2) tmp |= BIT(9); if (m_flagE2) tmp |= BIT(9);
WriteSaveBuf<uint32>(buf, tmp); WriteSaveBuf(buf, tmp);
} }
void void

@ -10,6 +10,12 @@
#include "DMAudio.h" #include "DMAudio.h"
#include "screendroplets.h" #include "screendroplets.h"
#ifdef COMPATIBLE_SAVES
#define PARTICLE_OBJECT_SIZEOF 0x88
#else
#define PARTICLE_OBJECT_SIZEOF sizeof(CParticleObject)
#endif
CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS]; CParticleObject gPObjectArray[MAX_PARTICLEOBJECTS];
@ -1111,6 +1117,49 @@ CParticleObject::UpdateFar(void)
} }
} }
#ifdef COMPATIBLE_SAVES
static inline void
SaveOneParticle(CParticleObject *p, uint8 *&buffer)
{
#define SkipBuf(buf, num) buf += num
#define ZeroBuf(buf, num) memset(buf, 0, num); SkipBuf(buf, num)
#define CopyToBuf(buf, data) memcpy(buf, &data, sizeof(data)); SkipBuf(buf, sizeof(data))
// CPlaceable
{
ZeroBuf(buffer, 4);
CopyToBuf(buffer, p->GetMatrix().f);
ZeroBuf(buffer, 4);
CopyToBuf(buffer, p->GetMatrix().m_hasRwMatrix);
ZeroBuf(buffer, 3);
}
// CParticleObject
{
ZeroBuf(buffer, 4);
ZeroBuf(buffer, 4);
ZeroBuf(buffer, 4);
CopyToBuf(buffer, p->m_nRemoveTimer);
CopyToBuf(buffer, p->m_Type);
CopyToBuf(buffer, p->m_ParticleType);
CopyToBuf(buffer, p->m_nNumEffectCycles);
CopyToBuf(buffer, p->m_nSkipFrames);
CopyToBuf(buffer, p->m_nFrameCounter);
CopyToBuf(buffer, p->m_nState);
ZeroBuf(buffer, 2);
CopyToBuf(buffer, p->m_vecTarget);
CopyToBuf(buffer, p->m_fRandVal);
CopyToBuf(buffer, p->m_fSize);
CopyToBuf(buffer, p->m_Color);
CopyToBuf(buffer, p->m_bRemove);
CopyToBuf(buffer, p->m_nCreationChance);
ZeroBuf(buffer, 2);
}
#undef SkipBuf
#undef ZeroBuf
#undef CopyToBuf
}
#endif
bool bool
CParticleObject::SaveParticle(uint8 *buffer, uint32 *length) CParticleObject::SaveParticle(uint8 *buffer, uint32 *length)
{ {
@ -1128,27 +1177,35 @@ CParticleObject::SaveParticle(uint8 *buffer, uint32 *length)
*(int32 *)buffer = numObjects; *(int32 *)buffer = numObjects;
buffer += sizeof(int32); buffer += sizeof(int32);
int32 objectsLength = sizeof(CParticleObject) * (numObjects + 1); int32 objectsLength = PARTICLE_OBJECT_SIZEOF * (numObjects + 1);
int32 dataLength = objectsLength + sizeof(int32); int32 dataLength = objectsLength + sizeof(int32);
for ( CParticleObject *p = pCloseListHead; p != NULL; p = p->m_pNext ) for ( CParticleObject *p = pCloseListHead; p != NULL; p = p->m_pNext )
{ {
#if 0 // todo better #ifdef COMPATIBLE_SAVES
SaveOneParticle(p, buffer);
#else
#ifdef THIS_IS_STUPID
*(CParticleObject*)buffer = *p; *(CParticleObject*)buffer = *p;
#else #else
memcpy(buffer, p, sizeof(CParticleObject)); memcpy(buffer, p, sizeof(CParticleObject));
#endif #endif
buffer += sizeof(CParticleObject); buffer += sizeof(CParticleObject);
#endif
} }
for ( CParticleObject *p = pFarListHead; p != NULL; p = p->m_pNext ) for ( CParticleObject *p = pFarListHead; p != NULL; p = p->m_pNext )
{ {
#if 0 // todo better #ifdef COMPATIBLE_SAVES
SaveOneParticle(p, buffer);
#else
#ifdef THIS_IS_STUPID
*(CParticleObject*)buffer = *p; *(CParticleObject*)buffer = *p;
#else #else
memcpy(buffer, p, sizeof(CParticleObject)); memcpy(buffer, p, sizeof(CParticleObject));
#endif #endif
buffer += sizeof(CParticleObject); buffer += sizeof(CParticleObject);
#endif
} }
*length = dataLength; *length = dataLength;
@ -1166,7 +1223,7 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
int32 numObjects = *(int32 *)buffer; int32 numObjects = *(int32 *)buffer;
buffer += sizeof(int32); buffer += sizeof(int32);
if ( length != sizeof(CParticleObject) * (numObjects + 1) + sizeof(int32) ) if ( length != PARTICLE_OBJECT_SIZEOF * (numObjects + 1) + sizeof(int32) )
return false; return false;
if ( numObjects == 0 ) if ( numObjects == 0 )
@ -1177,14 +1234,17 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
while ( i < numObjects ) while ( i < numObjects )
{ {
CParticleObject *dst = pUnusedListHead; CParticleObject *dst = pUnusedListHead;
#ifndef COMPATIBLE_SAVES
CParticleObject *src = (CParticleObject *)buffer; CParticleObject *src = (CParticleObject *)buffer;
buffer += sizeof(CParticleObject); buffer += sizeof(CParticleObject);
#endif
if ( dst == NULL ) if ( dst == NULL )
return false; return false;
MoveToList(&pUnusedListHead, &pCloseListHead, dst); MoveToList(&pUnusedListHead, &pCloseListHead, dst);
#ifndef COMPATIBLE_SAVES
dst->m_nState = POBJECTSTATE_UPDATE_CLOSE; dst->m_nState = POBJECTSTATE_UPDATE_CLOSE;
dst->m_Type = src->m_Type; dst->m_Type = src->m_Type;
dst->m_ParticleType = src->m_ParticleType; dst->m_ParticleType = src->m_ParticleType;
@ -1200,6 +1260,47 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
dst->m_nNumEffectCycles = src->m_nNumEffectCycles; dst->m_nNumEffectCycles = src->m_nNumEffectCycles;
dst->m_nSkipFrames = src->m_nSkipFrames; dst->m_nSkipFrames = src->m_nSkipFrames;
dst->m_nCreationChance = src->m_nCreationChance; dst->m_nCreationChance = src->m_nCreationChance;
#else
dst->m_nState = POBJECTSTATE_UPDATE_CLOSE;
dst->m_pParticle = NULL;
#define SkipBuf(buf, num) buf += num
#define CopyFromBuf(buf, data) memcpy(&data, buf, sizeof(data)); SkipBuf(buf, sizeof(data))
// CPlaceable
{
SkipBuf(buffer, 4);
CMatrix matrix;
CopyFromBuf(buffer, matrix.f);
SkipBuf(buffer, 4);
CopyFromBuf(buffer, matrix.m_hasRwMatrix);
SkipBuf(buffer, 3);
dst->SetPosition(matrix.GetPosition());
}
// CParticleObject
{
SkipBuf(buffer, 4);
SkipBuf(buffer, 4);
SkipBuf(buffer, 4);
CopyFromBuf(buffer, dst->m_nRemoveTimer);
CopyFromBuf(buffer, dst->m_Type);
CopyFromBuf(buffer, dst->m_ParticleType);
CopyFromBuf(buffer, dst->m_nNumEffectCycles);
CopyFromBuf(buffer, dst->m_nSkipFrames);
CopyFromBuf(buffer, dst->m_nFrameCounter);
SkipBuf(buffer, 2);
SkipBuf(buffer, 2);
CopyFromBuf(buffer, dst->m_vecTarget);
CopyFromBuf(buffer, dst->m_fRandVal);
CopyFromBuf(buffer, dst->m_fSize);
CopyFromBuf(buffer, dst->m_Color);
CopyFromBuf(buffer, dst->m_bRemove);
CopyFromBuf(buffer, dst->m_nCreationChance);
SkipBuf(buffer, 2);
}
#undef CopyFromBuf
#undef SkipBuf
#endif
i++; i++;
} }

@ -8496,21 +8496,21 @@ CPed::renderLimb(int node)
void void
CPed::Save(uint8*& buf) CPed::Save(uint8*& buf)
{ {
SkipSaveBuf(buf, 52); ZeroSaveBuf(buf, 52);
CopyToBuf(buf, GetPosition().x); CopyToBuf(buf, GetPosition().x);
CopyToBuf(buf, GetPosition().y); CopyToBuf(buf, GetPosition().y);
CopyToBuf(buf, GetPosition().z); CopyToBuf(buf, GetPosition().z);
SkipSaveBuf(buf, 288); ZeroSaveBuf(buf, 288);
CopyToBuf(buf, CharCreatedBy); CopyToBuf(buf, CharCreatedBy);
SkipSaveBuf(buf, 351); ZeroSaveBuf(buf, 351);
CopyToBuf(buf, m_fHealth); CopyToBuf(buf, m_fHealth);
CopyToBuf(buf, m_fArmour); CopyToBuf(buf, m_fArmour);
SkipSaveBuf(buf, 148); ZeroSaveBuf(buf, 148);
for (int i = 0; i < 13; i++) // has to be hardcoded for (int i = 0; i < 13; i++) // has to be hardcoded
m_weapons[i].Save(buf); m_weapons[i].Save(buf);
SkipSaveBuf(buf, 5); ZeroSaveBuf(buf, 5);
CopyToBuf(buf, m_maxWeaponTypeAllowed); CopyToBuf(buf, m_maxWeaponTypeAllowed);
SkipSaveBuf(buf, 162); ZeroSaveBuf(buf, 162);
} }
void void

@ -1492,14 +1492,14 @@ void
CPlayerPed::Save(uint8*& buf) CPlayerPed::Save(uint8*& buf)
{ {
CPed::Save(buf); CPed::Save(buf);
SkipSaveBuf(buf, 16); ZeroSaveBuf(buf, 16);
CopyToBuf(buf, m_fMaxStamina); CopyToBuf(buf, m_fMaxStamina);
SkipSaveBuf(buf, 28); ZeroSaveBuf(buf, 28);
CopyToBuf(buf, m_nTargettableObjects[0]); CopyToBuf(buf, m_nTargettableObjects[0]);
CopyToBuf(buf, m_nTargettableObjects[1]); CopyToBuf(buf, m_nTargettableObjects[1]);
CopyToBuf(buf, m_nTargettableObjects[2]); CopyToBuf(buf, m_nTargettableObjects[2]);
CopyToBuf(buf, m_nTargettableObjects[3]); CopyToBuf(buf, m_nTargettableObjects[3]);
SkipSaveBuf(buf, 116); ZeroSaveBuf(buf, 116);
} }
void void

@ -600,6 +600,552 @@ align4bytes(int32 size)
return (size + 3) & 0xFFFFFFFC; return (size + 3) & 0xFFFFFFFC;
} }
#ifdef FIX_INCOMPATIBLE_SAVES
#define LoadSaveDataBlockNoCheck(buf, file, size) \
do { \
CFileMgr::Read(file, (const char *)&size, sizeof(size)); \
size = align4bytes(size); \
CFileMgr::Read(file, (const char *)work_buff, size); \
buf = work_buff; \
} while(0)
#define WriteSavaDataBlockNoFunc(buf, file, size) \
do { \
if (!PcSaveHelper.PcClassSaveRoutine(file, buf, size)) \
goto fail; \
totalSize += size; \
} while(0)
#define FixSaveDataBlock(fix_func, file, size) \
do { \
ReadDataFromBufferPointer(buf, size); \
memset(work_buff2, 0, sizeof(work_buff2)); \
buf2 = work_buff2; \
reserved = 0; \
MakeSpaceForSizeInBufferPointer(presize, buf2, postsize); \
fix_func(save_type, buf, buf2, &size); \
CopySizeAndPreparePointer(presize, buf2, postsize, reserved, size); \
if (!PcSaveHelper.PcClassSaveRoutine(file, work_buff2, buf2 - work_buff2)) \
goto fail; \
totalSize += buf2 - work_buff2; \
} while(0)
#define ReadDataFromBufferPointerWithSize(buf, to, size) memcpy(&to, buf, size); buf += align4bytes(size)
#define ReadBuf(buf, to) memcpy(&to, buf, sizeof(to)); buf += sizeof(to)
#define WriteBuf(buf, from) memcpy(buf, &from, sizeof(from)); buf += sizeof(from)
#define CopyBuf(from, to, size) memcpy(to, from, size); to += (size); from += (size)
#define CopyPtr(from, to) memcpy(to, from, 4); to += 4; from += 8
#define SkipBuf(buf, size) buf += (size)
#define SkipBoth(from, to, size) to += (size); from += (size)
#define SkipPtr(from, to) to += 4; from += 8
// unfortunately we need a 2nd buffer of the same size to store the fixed output ...
static uint8 work_buff2[sizeof(work_buff)];
enum
{
SAVE_TYPE_NONE = 0,
SAVE_TYPE_32_BIT = 1,
SAVE_TYPE_64_BIT = 2,
SAVE_TYPE_MSVC = 4,
SAVE_TYPE_GCC = 8,
};
uint8
GetSaveType(char *savename)
{
uint8 save_type = SAVE_TYPE_NONE;
int file = CFileMgr::OpenFile(savename, "rb");
uint32 size;
CFileMgr::Read(file, (const char *)&size, sizeof(size));
uint8 *buf = work_buff;
CFileMgr::Read(file, (const char *)work_buff, size); // simple vars + scripts
LoadSaveDataBlockNoCheck(buf, file, size); // ped pool
LoadSaveDataBlockNoCheck(buf, file, size); // garages
ReadDataFromBufferPointer(buf, size);
// store for later after we know how much data we need to skip
ReadDataFromBufferPointerWithSize(buf, work_buff2, size);
LoadSaveDataBlockNoCheck(buf, file, size); // vehicle pool
LoadSaveDataBlockNoCheck(buf, file, size); // object pool
LoadSaveDataBlockNoCheck(buf, file, size); // paths
LoadSaveDataBlockNoCheck(buf, file, size); // cranes
CFileMgr::CloseFile(file);
ReadDataFromBufferPointer(buf, size);
if (size == 1032)
save_type |= SAVE_TYPE_32_BIT;
else if (size == 1160)
save_type |= SAVE_TYPE_64_BIT;
else
assert(0); // this should never happen
buf = work_buff2;
buf += 760; // skip everything before the first garage
buf += save_type & SAVE_TYPE_32_BIT ? 28 : 40; // skip first garage up to m_fX1
// now the values we want to verify
float fX1, fX2, fY1, fY2, fZ1, fZ2;
ReadBuf(buf, fX1);
ReadBuf(buf, fX2);
ReadBuf(buf, fY1);
ReadBuf(buf, fY2);
ReadBuf(buf, fZ1);
ReadBuf(buf, fZ2);
if (fX1 == CRUSHER_GARAGE_X1 && fX2 == CRUSHER_GARAGE_X2 &&
fY1 == CRUSHER_GARAGE_Y1 && fY2 == CRUSHER_GARAGE_Y2 &&
fZ1 == CRUSHER_GARAGE_Z1 && fZ2 == CRUSHER_GARAGE_Z2)
save_type |= SAVE_TYPE_MSVC;
else
save_type |= SAVE_TYPE_GCC;
return save_type;
}
static void
FixGarages(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
{
// hardcoded: 5484
// x86 msvc: 5240
// x86 gcc: 5040
// amd64 msvc: 5880
// amd64 gcc: 5808
uint8 *buf_start = buf;
uint8 *buf2_start = buf2;
uint32 read;
uint32 written = 5240;
if (save_type & SAVE_TYPE_32_BIT && save_type & SAVE_TYPE_GCC)
read = 5040;
else if (save_type & SAVE_TYPE_64_BIT && save_type & SAVE_TYPE_GCC)
read = 5808;
else
read = 5880;
uint32 ptrsize = save_type & SAVE_TYPE_32_BIT ? 4 : 8;
CopyBuf(buf, buf2, 4 * 6);
CopyBuf(buf, buf2, 4 * TOTAL_COLLECTCARS_GARAGES);
CopyBuf(buf, buf2, 4);
if (save_type & SAVE_TYPE_GCC)
{
for (int32 i = 0; i < NUM_GARAGE_STORED_CARS; i++)
{
#define FixStoredCar(buf, buf2) \
do { \
CopyBuf(buf, buf2, 4 + sizeof(CVector) + sizeof(CVector)); \
uint8 nFlags8; \
ReadBuf(buf, nFlags8); \
int32 nFlags32 = nFlags8; \
WriteBuf(buf2, nFlags32); \
CopyBuf(buf, buf2, 1 * 6); \
SkipBuf(buf, 1); \
SkipBuf(buf2, 2); \
} while(0)
FixStoredCar(buf, buf2);
FixStoredCar(buf, buf2);
FixStoredCar(buf, buf2);
#undef FixStoredCar
}
}
else
{
CopyBuf(buf, buf2, sizeof(CStoredCar) * NUM_GARAGE_STORED_CARS);
CopyBuf(buf, buf2, sizeof(CStoredCar) * NUM_GARAGE_STORED_CARS);
CopyBuf(buf, buf2, sizeof(CStoredCar) * NUM_GARAGE_STORED_CARS);
}
for (int32 i = 0; i < NUM_GARAGES; i++)
{
// skip the last 5 garages in 64bit builds without FIX_GARAGE_SIZE since they weren't actually saved and are unused
if (save_type & SAVE_TYPE_64_BIT && *size == 5484 && i >= NUM_GARAGES - 5)
{
SkipBuf(buf, 160); // sizeof(CGarage) on x64
SkipBuf(buf2, 140); // sizeof(CGarage) on x86
}
else
{
CopyBuf(buf, buf2, 1 * 6);
SkipBoth(buf, buf2, 2);
CopyBuf(buf, buf2, 4);
SkipBuf(buf, ptrsize - 4); // write 4 bytes padding if 8 byte pointer, if not, write 0
SkipBuf(buf, ptrsize * 2);
SkipBuf(buf2, 4 * 2);
CopyBuf(buf, buf2, 1 * 7);
SkipBoth(buf, buf2, 1);
CopyBuf(buf, buf2, 4 * 15 + 1);
SkipBoth(buf, buf2, 3);
SkipBuf(buf, ptrsize * 2);
SkipBuf(buf2, 4 * 2);
if (save_type & SAVE_TYPE_GCC)
SkipBuf(buf, save_type & SAVE_TYPE_64_BIT ? 36 + 4 : 36); // sizeof(CStoredCar) on gcc 64/32 before fix
else
SkipBuf(buf, sizeof(CStoredCar));
SkipBuf(buf2, sizeof(CStoredCar));
}
}
*size = 0;
assert(buf - buf_start == read);
assert(buf2 - buf2_start == written);
#ifdef FIX_GARAGE_SIZE
*size = (6 * sizeof(uint32) + TOTAL_COLLECTCARS_GARAGES * sizeof(*CGarages::CarTypesCollected) + sizeof(uint32) + 3 * NUM_GARAGE_STORED_CARS * sizeof(CStoredCar) + NUM_GARAGES * sizeof(CGarage));
#else
*size = 5484;
#endif
}
static void
FixCranes(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
{
uint8 *buf_start = buf;
uint8 *buf2_start = buf2;
uint32 read = 2 * sizeof(uint32) + 0x480; // sizeof(aCranes)
uint32 written = 2 * sizeof(uint32) + 0x400; // see CRANES_SAVE_SIZE
CopyBuf(buf, buf2, 4 + 4);
for (int32 i = 0; i < NUM_CRANES; i++)
{
CopyPtr(buf, buf2);
CopyPtr(buf, buf2);
CopyBuf(buf, buf2, 15 * 4 + sizeof(CVector) * 3 + sizeof(CVector2D));
CopyPtr(buf, buf2);
CopyBuf(buf, buf2, 4 + 7 * 1);
SkipBuf(buf, 5);
SkipBuf(buf2, 1);
}
*size = 0;
assert(buf - buf_start == read);
assert(buf2 - buf2_start == written);
*size = written;
}
static void
FixPickups(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
{
uint8 *buf_start = buf;
uint8 *buf2_start = buf2;
uint32 read = 0x3480 + sizeof(uint16) + sizeof(uint16) + sizeof(int32) * NUMCOLLECTEDPICKUPS; // sizeof(aPickUps)
uint32 written = 0x24C0 + sizeof(uint16) + sizeof(uint16) + sizeof(int32) * NUMCOLLECTEDPICKUPS; // see PICKUPS_SAVE_SIZE
for (int32 i = 0; i < NUMPICKUPS; i++)
{
CopyBuf(buf, buf2, 1 + 1 + 2);
SkipBuf(buf, 4);
CopyPtr(buf, buf2);
CopyBuf(buf, buf2, 4 + 2 + 2 + sizeof(CVector));
SkipBuf(buf, 4);
}
CopyBuf(buf, buf2, 2);
SkipBoth(buf, buf2, 2);
CopyBuf(buf, buf2, NUMCOLLECTEDPICKUPS * 4);
*size = 0;
assert(buf - buf_start == read);
assert(buf2 - buf2_start == written);
*size = written;
}
static void
FixPhoneInfo(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
{
uint8 *buf_start = buf;
uint8 *buf2_start = buf2;
uint32 read = 0x1138; // sizeof(CPhoneInfo)
uint32 written = 0xA30; // see PHONEINFO_SAVE_SIZE
CopyBuf(buf, buf2, 4 + 4);
for (int32 i = 0; i < NUMPHONES; i++)
{
CopyBuf(buf, buf2, sizeof(CVector));
SkipBuf(buf, 4);
SkipPtr(buf, buf2);
SkipPtr(buf, buf2);
SkipPtr(buf, buf2);
SkipPtr(buf, buf2);
SkipPtr(buf, buf2);
SkipPtr(buf, buf2);
CopyBuf(buf, buf2, 4);
SkipBuf(buf, 4);
CopyPtr(buf, buf2);
CopyBuf(buf, buf2, 4 + 1);
SkipBoth(buf, buf2, 3);
}
*size = 0;
assert(buf - buf_start == read);
assert(buf2 - buf2_start == written);
*size = written;
}
static void
FixZones(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
{
uint8 *buf_start = buf;
uint8 *buf2_start = buf2;
uint32 read = 11300; // see SaveAllZones
uint32 written = 10100; // see SaveAllZones
CopyBuf(buf, buf2, 1 * 4);
SkipBuf(buf, 4);
uint32 hdr_size = 10100 - (1 * 4 + 4); // see SaveAllZones
WriteBuf(buf2, hdr_size);
CopyBuf(buf, buf2, 4 * 2 + 2);
SkipBoth(buf, buf2, 2);
#define FixOneZone(buf, buf2) \
do { \
CopyBuf(buf, buf2, 8 + 8 * 4 + 2 * 2); \
SkipBuf(buf, 4); \
CopyPtr(buf, buf2); \
CopyPtr(buf, buf2); \
CopyPtr(buf, buf2); \
} while(0)
for (int32 i = 0; i < NUMZONES; i++)
FixOneZone(buf, buf2);
CopyBuf(buf, buf2, sizeof(CZoneInfo) * NUMZONES * 2);
CopyBuf(buf, buf2, 2 + 2);
for (int32 i = 0; i < NUMMAPZONES; i++)
FixOneZone(buf, buf2);
CopyBuf(buf, buf2, 2 * NUMAUDIOZONES);
CopyBuf(buf, buf2, 2 + 2);
#undef FixOneZone
*size = 0;
assert(buf - buf_start == read);
assert(buf2 - buf2_start == written);
*size = written;
}
static void
FixParticles(uint8 save_type, uint8 *buf, uint8 *buf2, uint32 *size)
{
uint8 *buf_start = buf;
uint8 *buf2_start = buf2;
int32 numObjects;
ReadBuf(buf, numObjects);
WriteBuf(buf2, numObjects);
uint32 read = 0xA0 * (numObjects + 1) + 4; // sizeof(CParticleObject)
uint32 written = 0x88 * (numObjects + 1) + 4; // see PARTICLE_OBJECT_SIZEOF
for (int32 i = 0; i < numObjects; i++)
{
// CPlaceable
SkipPtr(buf, buf2);
CopyBuf(buf, buf2, 4 * 4 * 4);
SkipPtr(buf, buf2);
CopyBuf(buf, buf2, 1);
SkipBuf(buf, 7);
SkipBuf(buf2, 3);
// CParticleObject
SkipPtr(buf, buf2);
SkipPtr(buf, buf2);
SkipPtr(buf, buf2);
CopyBuf(buf, buf2, 4 * 3 + 2 * 1 + 2 * 2);
SkipBoth(buf, buf2, 2);
CopyBuf(buf, buf2, sizeof(CVector) + 2 * 4 + sizeof(CRGBA) + 2 * 1);
SkipBoth(buf, buf2, 2);
}
SkipBuf(buf, 0xA0); // sizeof(CParticleObject)
SkipBuf(buf2, 0x88); // see PARTICLE_OBJECT_SIZEOF
*size = 0;
assert(buf - buf_start == read);
assert(buf2 - buf2_start == written);
*size = written;
}
bool
FixSave(int32 slot, uint8 save_type)
{
if (save_type & SAVE_TYPE_32_BIT && save_type & SAVE_TYPE_MSVC)
return true;
bool success = false;
uint8 *buf, *presize, *postsize, *buf2;
uint32 size;
uint32 reserved;
uint32 totalSize;
char savename[MAX_PATH];
char savename_bak[MAX_PATH];
sprintf(savename, "%s%i%s", DefaultPCSaveFileName, slot + 1, ".b");
sprintf(savename_bak, "%s%i%s.%lld.bak", DefaultPCSaveFileName, slot + 1, ".b", time(nil));
assert(caserename(savename, savename_bak) == 0);
int file_in = CFileMgr::OpenFile(savename_bak, "rb");
int file_out = CFileMgr::OpenFileForWriting(savename);
CheckSum = 0;
totalSize = 0;
CFileMgr::Read(file_in, (const char *)&size, sizeof(size));
buf = work_buff;
CFileMgr::Read(file_in, (const char *)work_buff, size); // simple vars + scripts
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // ped pool
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // garages
FixSaveDataBlock(FixGarages, file_out, size); // garages need to be fixed in either case
LoadSaveDataBlockNoCheck(buf, file_in, size); // vehicle pool
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // object pool
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // paths
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // cranes
if (save_type & SAVE_TYPE_64_BIT)
FixSaveDataBlock(FixCranes, file_out, size);
else
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // pickups
if (save_type & SAVE_TYPE_64_BIT)
FixSaveDataBlock(FixPickups, file_out, size);
else
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // phoneinfo
if (save_type & SAVE_TYPE_64_BIT)
FixSaveDataBlock(FixPhoneInfo, file_out, size);
else
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // restart
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // radar blips
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // zones
if (save_type & SAVE_TYPE_64_BIT)
FixSaveDataBlock(FixZones, file_out, size);
else
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // gang data
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // car generators
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // particles
if (save_type & SAVE_TYPE_64_BIT)
FixSaveDataBlock(FixParticles, file_out, size);
else
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // audio script objects
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // player info
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // stats
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // streaming
WriteSavaDataBlockNoFunc(buf, file_out, size);
LoadSaveDataBlockNoCheck(buf, file_in, size); // ped type
WriteSavaDataBlockNoFunc(buf, file_out, size);
memset(work_buff, 0, sizeof(work_buff));
for (int i = 0; i < 4; i++) {
size = align4bytes(SIZE_OF_ONE_GAME_IN_BYTES - totalSize - 4);
if (size > sizeof(work_buff))
size = sizeof(work_buff);
if (size > 4) {
if (!PcSaveHelper.PcClassSaveRoutine(file_out, work_buff, size))
goto fail;
totalSize += size;
}
}
if (!CFileMgr::Write(file_out, (const char *)&CheckSum, sizeof(CheckSum)))
goto fail;
success = true;
fail:;
CFileMgr::CloseFile(file_in);
CFileMgr::CloseFile(file_out);
return success;
}
#undef LoadSaveDataBlockNoCheck
#undef WriteSavaDataBlockNoFunc
#undef FixSaveDataBlock
#undef ReadDataFromBufferPointerWithSize
#undef ReadBuf
#undef WriteBuf
#undef CopyBuf
#undef CopyPtr
#undef SkipBuf
#undef SkipBoth
#undef SkipPtr
#endif
#ifdef MISSION_REPLAY #ifdef MISSION_REPLAY
void DisplaySaveResult(int unk, char* name) void DisplaySaveResult(int unk, char* name)

@ -22,6 +22,11 @@ bool CheckDataNotCorrupt(int32 slot, char *name);
bool RestoreForStartLoad(); bool RestoreForStartLoad();
int align4bytes(int32 size); int align4bytes(int32 size);
#ifdef FIX_INCOMPATIBLE_SAVES
uint8 GetSaveType(char *savename);
bool FixSave(int32 slot, uint8 save_type);
#endif
extern class CDate CompileDateAndTime; extern class CDate CompileDateAndTime;
extern char DefaultPCSaveFileName[260]; extern char DefaultPCSaveFileName[260];

@ -122,6 +122,13 @@ C_PcSave::PopulateSlotInfo()
} }
if (Slots[i + 1] == SLOT_OK) { if (Slots[i + 1] == SLOT_OK) {
if (CheckDataNotCorrupt(i, savename)) { if (CheckDataNotCorrupt(i, savename)) {
#ifdef FIX_INCOMPATIBLE_SAVES
if (!FixSave(i, GetSaveType(savename))) {
CMessages::InsertNumberInString(TheText.Get("FEC_SLC"), i + 1, -1, -1, -1, -1, -1, SlotFileName[i]);
Slots[i + 1] = SLOT_CORRUPTED;
continue;
}
#endif
SYSTEMTIME st; SYSTEMTIME st;
memcpy(&st, &header.SaveDateTime, sizeof(SYSTEMTIME)); memcpy(&st, &header.SaveDateTime, sizeof(SYSTEMTIME));
const char *month; const char *month;

@ -33,7 +33,7 @@ public:
void PopulateSlotInfo(); void PopulateSlotInfo();
bool DeleteSlot(int32 slot); bool DeleteSlot(int32 slot);
bool SaveSlot(int32 slot); bool SaveSlot(int32 slot);
bool PcClassSaveRoutine(int32 a2, uint8 *data, uint32 size); bool PcClassSaveRoutine(int32 file, uint8 *data, uint32 size);
static void SetSaveDirectory(const char *path); static void SetSaveDirectory(const char *path);
}; };

@ -36,6 +36,15 @@ WriteSaveBuf(uint8 *&buf, const T &value)
return p; return p;
} }
#ifdef COMPATIBLE_SAVES
inline void
ZeroSaveBuf(uint8 *&buf, uint32 length)
{
memset(buf, 0, length);
SkipSaveBuf(buf, length);
}
#endif
#define SAVE_HEADER_SIZE (4 * sizeof(char) + sizeof(uint32)) #define SAVE_HEADER_SIZE (4 * sizeof(char) + sizeof(uint32))
#define WriteSaveHeader(buf, a, b, c, d, size) \ #define WriteSaveHeader(buf, a, b, c, d, size) \

@ -155,6 +155,29 @@ FILE* _fcaseopen(char const* filename, char const* mode)
return result; return result;
} }
int _caserename(const char *old_filename, const char *new_filename)
{
int result;
char *real_old = casepath(old_filename);
char *real_new = casepath(new_filename);
// hack so we don't even try to rename it to new_filename if it already exists
if (!real_new) {
free(real_old);
return -1;
}
if (!real_old)
result = rename(old_filename, real_new);
else
result = rename(real_old, real_new);
free(real_old);
free(real_new);
return result;
}
// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) // Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
// Returned string should freed manually (if exists) // Returned string should freed manually (if exists)
char* casepath(char const* path, bool checkPathFirst) char* casepath(char const* path, bool checkPathFirst)

@ -29,6 +29,7 @@ enum eWinVersion
#endif #endif
extern DWORD _dwOperatingSystemVersion; extern DWORD _dwOperatingSystemVersion;
#define fcaseopen fopen #define fcaseopen fopen
#define caserename rename
#else #else
char *strupr(char *str); char *strupr(char *str);
char *strlwr(char *str); char *strlwr(char *str);
@ -51,6 +52,8 @@ extern long _dwOperatingSystemVersion;
char *casepath(char const *path, bool checkPathFirst = true); char *casepath(char const *path, bool checkPathFirst = true);
FILE *_fcaseopen(char const *filename, char const *mode); FILE *_fcaseopen(char const *filename, char const *mode);
#define fcaseopen _fcaseopen #define fcaseopen _fcaseopen
int _caserename(const char *old_filename, const char *new_filename);
#define caserename _caserename
#endif #endif
#ifdef RW_GL3 #ifdef RW_GL3

@ -4717,8 +4717,8 @@ void
CAutomobile::Save(uint8*& buf) CAutomobile::Save(uint8*& buf)
{ {
CVehicle::Save(buf); CVehicle::Save(buf);
WriteSaveBuf<CDamageManager>(buf, Damage); WriteSaveBuf(buf, Damage);
SkipSaveBuf(buf, 800 - sizeof(CDamageManager)); ZeroSaveBuf(buf, 800 - sizeof(CDamageManager));
} }
void void

@ -940,7 +940,7 @@ void
CBoat::Save(uint8*& buf) CBoat::Save(uint8*& buf)
{ {
CVehicle::Save(buf); CVehicle::Save(buf);
SkipSaveBuf(buf, 1156 - 648); ZeroSaveBuf(buf, 1156 - 648);
} }
void void

@ -37,6 +37,12 @@
#define MIN_VALID_POSITION (-10000.0f) #define MIN_VALID_POSITION (-10000.0f)
#define DEFAULT_OFFSET (20.0f) #define DEFAULT_OFFSET (20.0f)
#ifdef COMPATIBLE_SAVES
#define CRANES_SAVE_SIZE 0x400
#else
#define CRANES_SAVE_SIZE sizeof(aCranes)
#endif
uint32 TimerForCamInterpolation; uint32 TimerForCamInterpolation;
uint32 CCranes::CarsCollectedMilitaryCrane; uint32 CCranes::CarsCollectedMilitaryCrane;
@ -634,10 +640,46 @@ void CCranes::Save(uint8* buf, uint32* size)
{ {
INITSAVEBUF INITSAVEBUF
*size = 2 * sizeof(uint32) + sizeof(aCranes); *size = 2 * sizeof(uint32) + CRANES_SAVE_SIZE;
WriteSaveBuf(buf, NumCranes); WriteSaveBuf(buf, NumCranes);
WriteSaveBuf(buf, CarsCollectedMilitaryCrane); WriteSaveBuf(buf, CarsCollectedMilitaryCrane);
for (int i = 0; i < NUM_CRANES; i++) { for (int i = 0; i < NUM_CRANES; i++) {
#ifdef COMPATIBLE_SAVES
int32 tmp = aCranes[i].m_pCraneEntity != nil ? CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pCraneEntity) + 1 : 0;
WriteSaveBuf(buf, tmp);
tmp = aCranes[i].m_pHook != nil ? CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pHook) + 1 : 0;
WriteSaveBuf(buf, tmp);
WriteSaveBuf(buf, aCranes[i].m_nAudioEntity);
WriteSaveBuf(buf, aCranes[i].m_fPickupX1);
WriteSaveBuf(buf, aCranes[i].m_fPickupX2);
WriteSaveBuf(buf, aCranes[i].m_fPickupY1);
WriteSaveBuf(buf, aCranes[i].m_fPickupY2);
WriteSaveBuf(buf, aCranes[i].m_vecDropoffTarget);
WriteSaveBuf(buf, aCranes[i].m_fDropoffHeading);
WriteSaveBuf(buf, aCranes[i].m_fPickupAngle);
WriteSaveBuf(buf, aCranes[i].m_fDropoffAngle);
WriteSaveBuf(buf, aCranes[i].m_fPickupDistance);
WriteSaveBuf(buf, aCranes[i].m_fDropoffDistance);
WriteSaveBuf(buf, aCranes[i].m_fPickupHeight);
WriteSaveBuf(buf, aCranes[i].m_fDropoffHeight);
WriteSaveBuf(buf, aCranes[i].m_fHookAngle);
WriteSaveBuf(buf, aCranes[i].m_fHookOffset);
WriteSaveBuf(buf, aCranes[i].m_fHookHeight);
WriteSaveBuf(buf, aCranes[i].m_vecHookInitPos);
WriteSaveBuf(buf, aCranes[i].m_vecHookCurPos);
WriteSaveBuf(buf, aCranes[i].m_vecHookVelocity);
tmp = aCranes[i].m_pVehiclePickedUp != nil ? CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(aCranes[i].m_pVehiclePickedUp) + 1 : 0;
WriteSaveBuf(buf, tmp);
WriteSaveBuf(buf, aCranes[i].m_nTimeForNextCheck);
WriteSaveBuf(buf, aCranes[i].m_nCraneStatus);
WriteSaveBuf(buf, aCranes[i].m_nCraneState);
WriteSaveBuf(buf, aCranes[i].m_nVehiclesCollected);
WriteSaveBuf(buf, aCranes[i].m_bIsCrusher);
WriteSaveBuf(buf, aCranes[i].m_bIsMilitaryCrane);
WriteSaveBuf(buf, aCranes[i].m_bWasMilitaryCrane);
WriteSaveBuf(buf, aCranes[i].m_bIsTop);
ZeroSaveBuf(buf, 1);
#else
CCrane *pCrane = WriteSaveBuf(buf, aCranes[i]); CCrane *pCrane = WriteSaveBuf(buf, aCranes[i]);
if (pCrane->m_pCraneEntity != nil) if (pCrane->m_pCraneEntity != nil)
pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pCrane->m_pCraneEntity) + 1); pCrane->m_pCraneEntity = (CBuilding*)(CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert(pCrane->m_pCraneEntity) + 1);
@ -645,6 +687,7 @@ void CCranes::Save(uint8* buf, uint32* size)
pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pCrane->m_pHook) + 1); pCrane->m_pHook = (CObject*)(CPools::GetObjectPool()->GetJustIndex_NoFreeAssert(pCrane->m_pHook) + 1);
if (pCrane->m_pVehiclePickedUp != nil) if (pCrane->m_pVehiclePickedUp != nil)
pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pCrane->m_pVehiclePickedUp) + 1); pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pCrane->m_pVehiclePickedUp) + 1);
#endif
} }
VALIDATESAVEBUF(*size); VALIDATESAVEBUF(*size);
@ -656,8 +699,46 @@ void CCranes::Load(uint8* buf, uint32 size)
ReadSaveBuf(&NumCranes, buf); ReadSaveBuf(&NumCranes, buf);
ReadSaveBuf(&CarsCollectedMilitaryCrane, buf); ReadSaveBuf(&CarsCollectedMilitaryCrane, buf);
for (int i = 0; i < NUM_CRANES; i++) for (int i = 0; i < NUM_CRANES; i++) {
#ifdef COMPATIBLE_SAVES
int32 tmp;
ReadSaveBuf(&tmp, buf);
aCranes[i].m_pCraneEntity = tmp != 0 ? CPools::GetBuildingPool()->GetSlot(tmp - 1) : nil;
ReadSaveBuf(&tmp, buf);
aCranes[i].m_pHook = tmp != 0 ? CPools::GetObjectPool()->GetSlot(tmp - 1) : nil;
ReadSaveBuf(&aCranes[i].m_nAudioEntity, buf);
ReadSaveBuf(&aCranes[i].m_fPickupX1, buf);
ReadSaveBuf(&aCranes[i].m_fPickupX2, buf);
ReadSaveBuf(&aCranes[i].m_fPickupY1, buf);
ReadSaveBuf(&aCranes[i].m_fPickupY2, buf);
ReadSaveBuf(&aCranes[i].m_vecDropoffTarget, buf);
ReadSaveBuf(&aCranes[i].m_fDropoffHeading, buf);
ReadSaveBuf(&aCranes[i].m_fPickupAngle, buf);
ReadSaveBuf(&aCranes[i].m_fDropoffAngle, buf);
ReadSaveBuf(&aCranes[i].m_fPickupDistance, buf);
ReadSaveBuf(&aCranes[i].m_fDropoffDistance, buf);
ReadSaveBuf(&aCranes[i].m_fPickupHeight, buf);
ReadSaveBuf(&aCranes[i].m_fDropoffHeight, buf);
ReadSaveBuf(&aCranes[i].m_fHookAngle, buf);
ReadSaveBuf(&aCranes[i].m_fHookOffset, buf);
ReadSaveBuf(&aCranes[i].m_fHookHeight, buf);
ReadSaveBuf(&aCranes[i].m_vecHookInitPos, buf);
ReadSaveBuf(&aCranes[i].m_vecHookCurPos, buf);
ReadSaveBuf(&aCranes[i].m_vecHookVelocity, buf);
ReadSaveBuf(&tmp, buf);
aCranes[i].m_pVehiclePickedUp = tmp != 0 ? CPools::GetVehiclePool()->GetSlot(tmp - 1) : nil;
ReadSaveBuf(&aCranes[i].m_nTimeForNextCheck, buf);
ReadSaveBuf(&aCranes[i].m_nCraneStatus, buf);
ReadSaveBuf(&aCranes[i].m_nCraneState, buf);
ReadSaveBuf(&aCranes[i].m_nVehiclesCollected, buf);
ReadSaveBuf(&aCranes[i].m_bIsCrusher, buf);
ReadSaveBuf(&aCranes[i].m_bIsMilitaryCrane, buf);
ReadSaveBuf(&aCranes[i].m_bWasMilitaryCrane, buf);
ReadSaveBuf(&aCranes[i].m_bIsTop, buf);
SkipSaveBuf(buf, 1);
#else
ReadSaveBuf(&aCranes[i], buf); ReadSaveBuf(&aCranes[i], buf);
}
for (int i = 0; i < NUM_CRANES; i++) { for (int i = 0; i < NUM_CRANES; i++) {
CCrane *pCrane = &aCranes[i]; CCrane *pCrane = &aCranes[i];
if (pCrane->m_pCraneEntity != nil) if (pCrane->m_pCraneEntity != nil)
@ -666,6 +747,7 @@ void CCranes::Load(uint8* buf, uint32 size)
pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uintptr)pCrane->m_pHook - 1); pCrane->m_pHook = CPools::GetObjectPool()->GetSlot((uintptr)pCrane->m_pHook - 1);
if (pCrane->m_pVehiclePickedUp != nil) if (pCrane->m_pVehiclePickedUp != nil)
pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uintptr)pCrane->m_pVehiclePickedUp - 1); pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uintptr)pCrane->m_pVehiclePickedUp - 1);
#endif
} }
for (int i = 0; i < NUM_CRANES; i++) { for (int i = 0; i < NUM_CRANES; i++) {
aCranes[i].m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[i]); aCranes[i].m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[i]);

@ -1262,42 +1262,42 @@ DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle)
void void
CVehicle::Save(uint8*& buf) CVehicle::Save(uint8*& buf)
{ {
SkipSaveBuf(buf, 4); ZeroSaveBuf(buf, 4);
WriteSaveBuf<float>(buf, GetRight().x); WriteSaveBuf(buf, GetRight().x);
WriteSaveBuf<float>(buf, GetRight().y); WriteSaveBuf(buf, GetRight().y);
WriteSaveBuf<float>(buf, GetRight().z); WriteSaveBuf(buf, GetRight().z);
SkipSaveBuf(buf, 4); ZeroSaveBuf(buf, 4);
WriteSaveBuf<float>(buf, GetForward().x); WriteSaveBuf(buf, GetForward().x);
WriteSaveBuf<float>(buf, GetForward().y); WriteSaveBuf(buf, GetForward().y);
WriteSaveBuf<float>(buf, GetForward().z); WriteSaveBuf(buf, GetForward().z);
SkipSaveBuf(buf, 4); ZeroSaveBuf(buf, 4);
WriteSaveBuf<float>(buf, GetUp().x); WriteSaveBuf(buf, GetUp().x);
WriteSaveBuf<float>(buf, GetUp().y); WriteSaveBuf(buf, GetUp().y);
WriteSaveBuf<float>(buf, GetUp().z); WriteSaveBuf(buf, GetUp().z);
SkipSaveBuf(buf, 4); ZeroSaveBuf(buf, 4);
WriteSaveBuf<float>(buf, GetPosition().x); WriteSaveBuf(buf, GetPosition().x);
WriteSaveBuf<float>(buf, GetPosition().y); WriteSaveBuf(buf, GetPosition().y);
WriteSaveBuf<float>(buf, GetPosition().z); WriteSaveBuf(buf, GetPosition().z);
SkipSaveBuf(buf, 16); ZeroSaveBuf(buf, 16);
SaveEntityFlags(buf); SaveEntityFlags(buf);
SkipSaveBuf(buf, 212); ZeroSaveBuf(buf, 212);
AutoPilot.Save(buf); AutoPilot.Save(buf);
WriteSaveBuf<int8>(buf, m_currentColour1); WriteSaveBuf(buf, m_currentColour1);
WriteSaveBuf<int8>(buf, m_currentColour2); WriteSaveBuf(buf, m_currentColour2);
SkipSaveBuf(buf, 2); ZeroSaveBuf(buf, 2);
WriteSaveBuf<int16>(buf, m_nAlarmState); WriteSaveBuf(buf, m_nAlarmState);
SkipSaveBuf(buf, 43); ZeroSaveBuf(buf, 43);
WriteSaveBuf<uint8>(buf, m_nNumMaxPassengers); WriteSaveBuf(buf, m_nNumMaxPassengers);
SkipSaveBuf(buf, 2); ZeroSaveBuf(buf, 2);
WriteSaveBuf<float>(buf, field_1D0[0]); WriteSaveBuf(buf, field_1D0[0]);
WriteSaveBuf<float>(buf, field_1D0[1]); WriteSaveBuf(buf, field_1D0[1]);
WriteSaveBuf<float>(buf, field_1D0[2]); WriteSaveBuf(buf, field_1D0[2]);
WriteSaveBuf<float>(buf, field_1D0[3]); WriteSaveBuf(buf, field_1D0[3]);
SkipSaveBuf(buf, 8); ZeroSaveBuf(buf, 8);
WriteSaveBuf<float>(buf, m_fSteerAngle); WriteSaveBuf(buf, m_fSteerAngle);
WriteSaveBuf<float>(buf, m_fGasPedal); WriteSaveBuf(buf, m_fGasPedal);
WriteSaveBuf<float>(buf, m_fBrakePedal); WriteSaveBuf(buf, m_fBrakePedal);
WriteSaveBuf<uint8>(buf, VehicleCreatedBy); WriteSaveBuf(buf, VehicleCreatedBy);
uint8 flags = 0; uint8 flags = 0;
if (bIsLawEnforcer) flags |= BIT(0); if (bIsLawEnforcer) flags |= BIT(0);
if (bIsLocked) flags |= BIT(3); if (bIsLocked) flags |= BIT(3);
@ -1305,19 +1305,19 @@ CVehicle::Save(uint8*& buf)
if (bIsHandbrakeOn) flags |= BIT(5); if (bIsHandbrakeOn) flags |= BIT(5);
if (bLightsOn) flags |= BIT(6); if (bLightsOn) flags |= BIT(6);
if (bFreebies) flags |= BIT(7); if (bFreebies) flags |= BIT(7);
WriteSaveBuf<uint8>(buf, flags); WriteSaveBuf(buf, flags);
SkipSaveBuf(buf, 10); ZeroSaveBuf(buf, 10);
WriteSaveBuf<float>(buf, m_fHealth); WriteSaveBuf(buf, m_fHealth);
WriteSaveBuf<uint8>(buf, m_nCurrentGear); WriteSaveBuf(buf, m_nCurrentGear);
SkipSaveBuf(buf, 3); ZeroSaveBuf(buf, 3);
WriteSaveBuf<float>(buf, m_fChangeGearTime); WriteSaveBuf(buf, m_fChangeGearTime);
SkipSaveBuf(buf, 4); ZeroSaveBuf(buf, 4);
WriteSaveBuf<uint32>(buf, m_nTimeOfDeath); WriteSaveBuf(buf, m_nTimeOfDeath);
SkipSaveBuf(buf, 2); ZeroSaveBuf(buf, 2);
WriteSaveBuf<int16>(buf, m_nBombTimer); WriteSaveBuf(buf, m_nBombTimer);
SkipSaveBuf(buf, 12); ZeroSaveBuf(buf, 12);
WriteSaveBuf<int8>(buf, m_nDoorLock); WriteSaveBuf(buf, m_nDoorLock);
SkipSaveBuf(buf, 99); ZeroSaveBuf(buf, 96);
} }
void void
@ -1379,8 +1379,7 @@ CVehicle::Load(uint8*& buf)
SkipSaveBuf(buf, 2); SkipSaveBuf(buf, 2);
ReadSaveBuf(&m_nBombTimer, buf); ReadSaveBuf(&m_nBombTimer, buf);
SkipSaveBuf(buf, 12); SkipSaveBuf(buf, 12);
ReadSaveBuf(&flags, buf); ReadSaveBuf(&m_nDoorLock, buf);
m_nDoorLock = (eCarLock)flags; SkipSaveBuf(buf, 96);
SkipSaveBuf(buf, 99);
} }
#endif #endif

@ -2337,7 +2337,7 @@ CWeapon::Save(uint8*& buf)
CopyToBuf(buf, m_nAmmoTotal); CopyToBuf(buf, m_nAmmoTotal);
CopyToBuf(buf, m_nTimer); CopyToBuf(buf, m_nTimer);
CopyToBuf(buf, m_bAddRotOffset); CopyToBuf(buf, m_bAddRotOffset);
SkipSaveBuf(buf, 3); ZeroSaveBuf(buf, 3);
} }
void void