summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/control/AutoPilot.cpp58
-rw-r--r--src/control/Garages.cpp115
-rw-r--r--src/control/Garages.h22
-rw-r--r--src/control/Phones.cpp45
-rw-r--r--src/control/Pickups.cpp33
-rw-r--r--src/control/Script5.cpp36
-rw-r--r--src/core/Pools.cpp10
-rw-r--r--src/core/Zones.cpp78
-rw-r--r--src/core/config.h4
-rw-r--r--src/entities/Entity.cpp4
-rw-r--r--src/objects/ParticleObject.cpp109
-rw-r--r--src/peds/Ped.cpp12
-rw-r--r--src/peds/PlayerPed.cpp6
-rw-r--r--src/save/GenericGameStorage.cpp546
-rw-r--r--src/save/GenericGameStorage.h5
-rw-r--r--src/save/PCSave.cpp7
-rw-r--r--src/save/PCSave.h2
-rw-r--r--src/save/SaveBuf.h9
-rw-r--r--src/skel/crossplatform.cpp23
-rw-r--r--src/skel/crossplatform.h3
-rw-r--r--src/vehicles/Automobile.cpp4
-rw-r--r--src/vehicles/Boat.cpp2
-rw-r--r--src/vehicles/Cranes.cpp86
-rw-r--r--src/vehicles/Vehicle.cpp99
-rw-r--r--src/weapons/Weapon.cpp2
25 files changed, 1161 insertions, 159 deletions
diff --git a/src/control/AutoPilot.cpp b/src/control/AutoPilot.cpp
index 22a73179..5af4071a 100644
--- a/src/control/AutoPilot.cpp
+++ b/src/control/AutoPilot.cpp
@@ -50,41 +50,41 @@ void CAutoPilot::RemoveOnePathNode()
#ifdef COMPATIBLE_SAVES
void CAutoPilot::Save(uint8*& buf)
{
- WriteSaveBuf<int32>(buf, m_nCurrentRouteNode);
- WriteSaveBuf<int32>(buf, m_nNextRouteNode);
- WriteSaveBuf<int32>(buf, m_nPrevRouteNode);
- WriteSaveBuf<int32>(buf, m_nTimeEnteredCurve);
- WriteSaveBuf<int32>(buf, m_nTimeToSpendOnCurrentCurve);
- WriteSaveBuf<uint32>(buf, m_nCurrentPathNodeInfo);
- WriteSaveBuf<uint32>(buf, m_nNextPathNodeInfo);
- WriteSaveBuf<uint32>(buf, m_nPreviousPathNodeInfo);
- WriteSaveBuf<uint32>(buf, m_nAntiReverseTimer);
- WriteSaveBuf<uint32>(buf, m_nTimeToStartMission);
- WriteSaveBuf<int8>(buf, m_nPreviousDirection);
- WriteSaveBuf<int8>(buf, m_nCurrentDirection);
- WriteSaveBuf<int8>(buf, m_nNextDirection);
- WriteSaveBuf<int8>(buf, m_nCurrentLane);
- WriteSaveBuf<int8>(buf, m_nNextLane);
- WriteSaveBuf<uint8>(buf, m_nDrivingStyle);
- WriteSaveBuf<uint8>(buf, m_nCarMission);
- WriteSaveBuf<uint8>(buf, m_nTempAction);
- WriteSaveBuf<uint32>(buf, m_nTimeTempAction);
- WriteSaveBuf<float>(buf, m_fMaxTrafficSpeed);
- WriteSaveBuf<uint8>(buf, m_nCruiseSpeed);
+ WriteSaveBuf(buf, m_nCurrentRouteNode);
+ WriteSaveBuf(buf, m_nNextRouteNode);
+ WriteSaveBuf(buf, m_nPrevRouteNode);
+ WriteSaveBuf(buf, m_nTimeEnteredCurve);
+ WriteSaveBuf(buf, m_nTimeToSpendOnCurrentCurve);
+ WriteSaveBuf(buf, m_nCurrentPathNodeInfo);
+ WriteSaveBuf(buf, m_nNextPathNodeInfo);
+ WriteSaveBuf(buf, m_nPreviousPathNodeInfo);
+ WriteSaveBuf(buf, m_nAntiReverseTimer);
+ WriteSaveBuf(buf, m_nTimeToStartMission);
+ WriteSaveBuf(buf, m_nPreviousDirection);
+ WriteSaveBuf(buf, m_nCurrentDirection);
+ WriteSaveBuf(buf, m_nNextDirection);
+ WriteSaveBuf(buf, m_nCurrentLane);
+ WriteSaveBuf(buf, m_nNextLane);
+ WriteSaveBuf(buf, m_nDrivingStyle);
+ WriteSaveBuf(buf, m_nCarMission);
+ WriteSaveBuf(buf, m_nTempAction);
+ WriteSaveBuf(buf, m_nTimeTempAction);
+ WriteSaveBuf(buf, m_fMaxTrafficSpeed);
+ WriteSaveBuf(buf, m_nCruiseSpeed);
uint8 flags = 0;
if (m_bSlowedDownBecauseOfCars) flags |= BIT(0);
if (m_bSlowedDownBecauseOfPeds) flags |= BIT(1);
if (m_bStayInCurrentLevel) flags |= BIT(2);
if (m_bStayInFastLane) flags |= BIT(3);
if (m_bIgnorePathfinding) flags |= BIT(4);
- WriteSaveBuf<uint8>(buf, flags);
- SkipSaveBuf(buf, 2);
- WriteSaveBuf<float>(buf, m_vecDestinationCoors.x);
- WriteSaveBuf<float>(buf, m_vecDestinationCoors.y);
- WriteSaveBuf<float>(buf, m_vecDestinationCoors.z);
- SkipSaveBuf(buf, 32);
- WriteSaveBuf<int16>(buf, m_nPathFindNodesCount);
- SkipSaveBuf(buf, 6);
+ WriteSaveBuf(buf, flags);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_vecDestinationCoors.x);
+ WriteSaveBuf(buf, m_vecDestinationCoors.y);
+ WriteSaveBuf(buf, m_vecDestinationCoors.z);
+ ZeroSaveBuf(buf, 32);
+ WriteSaveBuf(buf, m_nPathFindNodesCount);
+ ZeroSaveBuf(buf, 6);
}
void CAutoPilot::Load(uint8*& buf)
diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp
index 3410c881..91971ae7 100644
--- a/src/control/Garages.cpp
+++ b/src/control/Garages.cpp
@@ -26,13 +26,6 @@
#include "World.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_CLOSE_SPEED (0.02f)
#define DEFAULT_DOOR_OPEN_SPEED (0.035f)
@@ -1883,11 +1876,12 @@ void CStoredCar::StoreCar(CVehicle* pVehicle)
m_nRadioStation = pVehicle->m_nRadioStation;
m_nVariationA = pVehicle->m_aExtras[0];
m_nVariationB = pVehicle->m_aExtras[1];
- m_bBulletproof = pVehicle->bBulletProof;
- m_bFireproof = pVehicle->bFireProof;
- m_bExplosionproof = pVehicle->bExplosionProof;
- m_bCollisionproof = pVehicle->bCollisionProof;
- m_bMeleeproof = pVehicle->bMeleeProof;
+ m_nFlags = 0;
+ if (pVehicle->bBulletProof) m_nFlags |= FLAG_BULLETPROOF;
+ if (pVehicle->bFireProof) m_nFlags |= FLAG_FIREPROOF;
+ if (pVehicle->bExplosionProof) m_nFlags |= FLAG_EXPLOSIONPROOF;
+ if (pVehicle->bCollisionProof) m_nFlags |= FLAG_COLLISIONPROOF;
+ if (pVehicle->bMeleeProof) m_nFlags |= FLAG_MELEEPROOF;
if (pVehicle->IsCar())
m_nCarBombType = ((CAutomobile*)pVehicle)->m_bombType;
}
@@ -1936,11 +1930,11 @@ CVehicle* CStoredCar::RestoreCar()
}
pVehicle->bHasBeenOwnedByPlayer = true;
pVehicle->m_nDoorLock = CARLOCK_UNLOCKED;
- pVehicle->bBulletProof = m_bBulletproof;
- pVehicle->bFireProof = m_bFireproof;
- pVehicle->bExplosionProof = m_bExplosionproof;
- pVehicle->bCollisionProof = m_bCollisionproof;
- pVehicle->bMeleeProof = m_bMeleeproof;
+ if (m_nFlags & FLAG_BULLETPROOF) pVehicle->bBulletProof = true;
+ if (m_nFlags & FLAG_FIREPROOF) pVehicle->bFireProof = true;
+ if (m_nFlags & FLAG_EXPLOSIONPROOF) pVehicle->bExplosionProof = true;
+ if (m_nFlags & FLAG_COLLISIONPROOF) pVehicle->bCollisionProof = true;
+ if (m_nFlags & FLAG_MELEEPROOF) pVehicle->bMeleeProof = true;
return pVehicle;
}
@@ -2327,8 +2321,47 @@ void CGarages::Save(uint8 * buf, uint32 * size)
WriteSaveBuf(buf, aCarsInSafeHouse2[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]);
+#endif
+ }
#ifdef FIX_GARAGE_SIZE
VALIDATESAVEBUF(*size);
#endif
@@ -2339,11 +2372,7 @@ const CStoredCar &CStoredCar::operator=(const CStoredCar & other)
m_nModelIndex = other.m_nModelIndex;
m_vecPos = other.m_vecPos;
m_vecAngle = other.m_vecAngle;
- m_bBulletproof = other.m_bBulletproof;
- m_bFireproof = other.m_bFireproof;
- m_bExplosionproof = other.m_bExplosionproof;
- m_bCollisionproof = other.m_bCollisionproof;
- m_bMeleeproof = other.m_bMeleeproof;
+ m_nFlags = other.m_nFlags;
m_nPrimaryColor = other.m_nPrimaryColor;
m_nSecondaryColor = other.m_nSecondaryColor;
m_nRadioStation = other.m_nRadioStation;
@@ -2357,7 +2386,7 @@ void CGarages::Load(uint8* buf, uint32 size)
{
#ifdef FIX_GARAGE_SIZE
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
assert(size == 5484);
#endif
@@ -2380,7 +2409,45 @@ void CGarages::Load(uint8* buf, uint32 size)
ReadSaveBuf(&aCarsInSafeHouse3[i], buf);
}
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);
+#endif
aGarages[i].m_pDoor1 = nil;
aGarages[i].m_pDoor2 = nil;
aGarages[i].m_pTarget = nil;
diff --git a/src/control/Garages.h b/src/control/Garages.h
index a7dfa462..8a9fd1b6 100644
--- a/src/control/Garages.h
+++ b/src/control/Garages.h
@@ -51,14 +51,17 @@ enum
class CStoredCar
{
+ enum {
+ FLAG_BULLETPROOF = 0x1,
+ FLAG_FIREPROOF = 0x2,
+ FLAG_EXPLOSIONPROOF = 0x4,
+ FLAG_COLLISIONPROOF = 0x8,
+ FLAG_MELEEPROOF = 0x10,
+ };
int32 m_nModelIndex;
CVector m_vecPos;
CVector m_vecAngle;
- int32 m_bBulletproof : 1;
- int32 m_bFireproof : 1;
- int32 m_bExplosionproof : 1;
- int32 m_bCollisionproof : 1;
- int32 m_bMeleeproof : 1;
+ int32 m_nFlags;
int8 m_nPrimaryColor;
int8 m_nSecondaryColor;
int8 m_nRadioStation;
@@ -78,6 +81,13 @@ VALIDATE_SIZE(CStoredCar, 0x28);
#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
{
public:
@@ -87,7 +97,7 @@ public:
bool m_bClosingWithoutTargetCar;
bool m_bDeactivated;
bool m_bResprayHappened;
- int m_nTargetModelIndex;
+ int32 m_nTargetModelIndex;
CEntity *m_pDoor1;
CEntity *m_pDoor2;
uint8 m_bDoor1PoolIndex;
diff --git a/src/control/Phones.cpp b/src/control/Phones.cpp
index f9cb1421..7632cfa3 100644
--- a/src/control/Phones.cpp
+++ b/src/control/Phones.cpp
@@ -18,6 +18,12 @@
#include "Replay.h"
#endif
+#ifdef COMPATIBLE_SAVES
+#define PHONEINFO_SAVE_SIZE 0xA30
+#else
+#define PHONEINFO_SAVE_SIZE sizeof(CPhoneInfo)
+#endif
+
CPhoneInfo gPhoneInfo;
bool CPhoneInfo::bDisplayingPhoneMessage; // is phone picked up
@@ -209,6 +215,22 @@ CPhoneInfo::IsMessageBeingDisplayed(int 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
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
for (int i = 0; i < 50; i++) {
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);
+#endif
if (ignoreOtherPhones)
continue;
@@ -252,7 +279,11 @@ INITSAVEBUF
m_nScriptPhonesMax = scriptPhonesMax;
for (int i = 0; i < NUMPHONES; i++) {
+#ifdef COMPATIBLE_SAVES
+ LoadPhone(m_aPhones[i], buf);
+#else
ReadSaveBuf(&m_aPhones[i], buf);
+#endif
// It's saved as building pool index in save file, convert it to true entity
if (m_aPhones[i].m_pEntity) {
m_aPhones[i].m_pEntity = CPools::GetBuildingPool()->GetSlot((uintptr)m_aPhones[i].m_pEntity - 1);
@@ -376,7 +407,7 @@ CPhoneInfo::Initialise(void)
void
CPhoneInfo::Save(uint8 *buf, uint32 *size)
{
- *size = sizeof(CPhoneInfo);
+ *size = PHONEINFO_SAVE_SIZE;
INITSAVEBUF
WriteSaveBuf(buf, m_nMax);
WriteSaveBuf(buf, m_nScriptPhonesMax);
@@ -385,12 +416,24 @@ INITSAVEBUF
#else
for (int phoneId = 0; phoneId < NUMPHONES; phoneId++) {
#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]);
// Convert entity pointer to building pool index while saving
if (phone->m_pEntity) {
phone->m_pEntity = (CEntity*) (CPools::GetBuildingPool()->GetJustIndex_NoFreeAssert((CBuilding*)phone->m_pEntity) + 1);
}
+#endif
}
VALIDATESAVEBUF(*size)
}
diff --git a/src/control/Pickups.cpp b/src/control/Pickups.cpp
index 10175fba..8d3472ea 100644
--- a/src/control/Pickups.cpp
+++ b/src/control/Pickups.cpp
@@ -32,6 +32,12 @@
#include "WaterLevel.h"
#include "World.h"
+#ifdef COMPATIBLE_SAVES
+#define PICKUPS_SAVE_SIZE 0x24C0
+#else
+#define PICKUPS_SAVE_SIZE sizeof(aPickUps)
+#endif
+
CPickup CPickups::aPickUps[NUMPICKUPS];
int16 CPickups::NumMessages;
int32 CPickups::aPickUpsCollected[NUMCOLLECTEDPICKUPS];
@@ -1000,10 +1006,23 @@ CPickups::Load(uint8 *buf, uint32 size)
INITSAVEBUF
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);
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);
+#endif
}
ReadSaveBuf(&CollectedPickUpIndex, buf);
@@ -1019,14 +1038,26 @@ VALIDATESAVEBUF(size)
void
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
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]);
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);
+#endif
}
WriteSaveBuf(buf, CollectedPickUpIndex);
diff --git a/src/control/Script5.cpp b/src/control/Script5.cpp
index a9aec18e..953a1f50 100644
--- a/src/control/Script5.cpp
+++ b/src/control/Script5.cpp
@@ -2089,33 +2089,33 @@ VALIDATESAVEBUF(size)
void CRunningScript::Save(uint8*& buf)
{
#ifdef COMPATIBLE_SAVES
- SkipSaveBuf(buf, 8);
+ ZeroSaveBuf(buf, 8);
for (int i = 0; i < 8; i++)
- WriteSaveBuf<char>(buf, m_abScriptName[i]);
- WriteSaveBuf<uint32>(buf, m_nIp);
+ WriteSaveBuf(buf, m_abScriptName[i]);
+ WriteSaveBuf(buf, m_nIp);
#ifdef CHECK_STRUCT_SIZES
static_assert(MAX_STACK_DEPTH == 6, "Compatibility loss: MAX_STACK_DEPTH != 6");
#endif
for (int i = 0; i < MAX_STACK_DEPTH; i++)
- WriteSaveBuf<uint32>(buf, m_anStack[i]);
- WriteSaveBuf<uint16>(buf, m_nStackPointer);
- SkipSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_anStack[i]);
+ WriteSaveBuf(buf, m_nStackPointer);
+ ZeroSaveBuf(buf, 2);
#ifdef CHECK_STRUCT_SIZES
static_assert(NUM_LOCAL_VARS + NUM_TIMERS == 18, "Compatibility loss: NUM_LOCAL_VARS + NUM_TIMERS != 18");
#endif
for (int i = 0; i < NUM_LOCAL_VARS + NUM_TIMERS; i++)
- WriteSaveBuf<int32>(buf, m_anLocalVariables[i]);
- WriteSaveBuf<bool>(buf, m_bCondResult);
- WriteSaveBuf<bool>(buf, m_bIsMissionScript);
- WriteSaveBuf<bool>(buf, m_bSkipWakeTime);
- SkipSaveBuf(buf, 1);
- WriteSaveBuf<uint32>(buf, m_nWakeTime);
- WriteSaveBuf<uint16>(buf, m_nAndOrState);
- WriteSaveBuf<bool>(buf, m_bNotFlag);
- WriteSaveBuf<bool>(buf, m_bDeatharrestEnabled);
- WriteSaveBuf<bool>(buf, m_bDeatharrestExecuted);
- WriteSaveBuf<bool>(buf, m_bMissionFlag);
- SkipSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_anLocalVariables[i]);
+ WriteSaveBuf(buf, m_bCondResult);
+ WriteSaveBuf(buf, m_bIsMissionScript);
+ WriteSaveBuf(buf, m_bSkipWakeTime);
+ ZeroSaveBuf(buf, 1);
+ WriteSaveBuf(buf, m_nWakeTime);
+ WriteSaveBuf(buf, m_nAndOrState);
+ WriteSaveBuf(buf, m_bNotFlag);
+ WriteSaveBuf(buf, m_bDeatharrestEnabled);
+ WriteSaveBuf(buf, m_bDeatharrestExecuted);
+ WriteSaveBuf(buf, m_bMissionFlag);
+ ZeroSaveBuf(buf, 2);
#else
WriteSaveBuf(buf, *this);
#endif
diff --git a/src/core/Pools.cpp b/src/core/Pools.cpp
index 5cffe9e4..b0248664 100644
--- a/src/core/Pools.cpp
+++ b/src/core/Pools.cpp
@@ -281,9 +281,9 @@ INITSAVEBUF
#else
if ((pVehicle->IsCar() || pVehicle->IsBoat()) && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif
- WriteSaveBuf<uint32>(buf, pVehicle->m_vehType);
- WriteSaveBuf<int16>(buf, pVehicle->GetModelIndex());
- WriteSaveBuf<int32>(buf, GetVehicleRef(pVehicle));
+ WriteSaveBuf(buf, pVehicle->m_vehType);
+ WriteSaveBuf(buf, pVehicle->GetModelIndex());
+ WriteSaveBuf(buf, GetVehicleRef(pVehicle));
pVehicle->Save(buf);
}
#else
@@ -292,7 +292,7 @@ INITSAVEBUF
#else
if (pVehicle->IsCar() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif
- WriteSaveBuf(buf, (uint32)pVehicle->m_vehType);
+ WriteSaveBuf(buf, pVehicle->m_vehType);
WriteSaveBuf(buf, pVehicle->GetModelIndex());
WriteSaveBuf(buf, GetVehicleRef(pVehicle));
memcpy(buf, pVehicle, sizeof(CAutomobile));
@@ -303,7 +303,7 @@ INITSAVEBUF
#else
if (pVehicle->IsBoat() && pVehicle->VehicleCreatedBy == MISSION_VEHICLE) {
#endif
- WriteSaveBuf(buf, (uint32)pVehicle->m_vehType);
+ WriteSaveBuf(buf, pVehicle->m_vehType);
WriteSaveBuf(buf, pVehicle->GetModelIndex());
WriteSaveBuf(buf, GetVehicleRef(pVehicle));
memcpy(buf, pVehicle, sizeof(CBoat));
diff --git a/src/core/Zones.cpp b/src/core/Zones.cpp
index 107b1db8..82fbc047 100644
--- a/src/core/Zones.cpp
+++ b/src/core/Zones.cpp
@@ -10,6 +10,14 @@
#include "Timer.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;
CZone *CTheZones::m_pPlayersZone;
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
CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
{
@@ -643,9 +673,9 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
+ sizeof(int32) // GetIndexForZonePointer
+ sizeof(m_CurrLevel) + sizeof(FindIndex)
+ sizeof(int16) // padding
- + sizeof(ZoneArray) + sizeof(ZoneInfoArray)
+ + ZONEARRAY_SAVE_SIZE + sizeof(ZoneInfoArray)
+ sizeof(TotalNumberOfZones) + sizeof(TotalNumberOfZoneInfos)
- + sizeof(MapZoneArray) + sizeof(AudioZoneArray)
+ + MAPZONEARRAY_SAVE_SIZE + sizeof(AudioZoneArray)
+ sizeof(TotalNumberOfMapZones) + sizeof(NumberOfAudioZones);
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
for(i = 0; i < ARRAY_SIZE(ZoneArray); i++){
+#ifdef COMPATIBLE_SAVES
+ SaveOneZone(ZoneArray[i], buffer);
+#else
CZone *zone = WriteSaveBuf(buffer, ZoneArray[i]);
zone->child = (CZone*)GetIndexForZonePointer(ZoneArray[i].child);
zone->parent = (CZone*)GetIndexForZonePointer(ZoneArray[i].parent);
zone->next = (CZone*)GetIndexForZonePointer(ZoneArray[i].next);
+#endif
}
for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++)
@@ -669,7 +703,9 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
WriteSaveBuf(buffer, TotalNumberOfZoneInfos);
for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) {
+#ifndef COMPATIBLE_SAVES
CZone* zone = WriteSaveBuf(buffer, MapZoneArray[i]);
+#endif
/*
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].parent == nil);
assert(MapZoneArray[i].next == nil);
+#ifndef COMPATIBLE_SAVES
zone->child = (CZone*)GetIndexForZonePointer(MapZoneArray[i].child);
zone->parent = (CZone*)GetIndexForZonePointer(MapZoneArray[i].parent);
zone->next = (CZone*)GetIndexForZonePointer(MapZoneArray[i].next);
+#else
+ SaveOneZone(MapZoneArray[i], buffer);
+#endif
}
for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++)
@@ -693,6 +733,32 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *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
CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
{
@@ -708,11 +774,15 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
SkipSaveBuf(buffer, 2);
for(i = 0; i < ARRAY_SIZE(ZoneArray); i++){
+#ifdef COMPATIBLE_SAVES
+ LoadOneZone(ZoneArray[i], buffer);
+#else
ReadSaveBuf(&ZoneArray[i], buffer);
ZoneArray[i].child = GetPointerForZoneIndex((uintptr)ZoneArray[i].child);
ZoneArray[i].parent = GetPointerForZoneIndex((uintptr)ZoneArray[i].parent);
ZoneArray[i].next = GetPointerForZoneIndex((uintptr)ZoneArray[i].next);
+#endif
}
for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++)
@@ -722,6 +792,9 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
ReadSaveBuf(&TotalNumberOfZoneInfos, buffer);
for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++){
+#ifdef COMPATIBLE_SAVES
+ LoadOneZone(MapZoneArray[i], buffer);
+#else
ReadSaveBuf(&MapZoneArray[i], buffer);
/*
@@ -732,6 +805,7 @@ CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
MapZoneArray[i].child = GetPointerForZoneIndex((uintptr)MapZoneArray[i].child);
MapZoneArray[i].parent = GetPointerForZoneIndex((uintptr)MapZoneArray[i].parent);
MapZoneArray[i].next = GetPointerForZoneIndex((uintptr)MapZoneArray[i].next);
+#endif
assert(MapZoneArray[i].child == nil);
assert(MapZoneArray[i].parent == nil);
assert(MapZoneArray[i].next == nil);
diff --git a/src/core/config.h b/src/core/config.h
index d7d43b62..a8b83577 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -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 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 NO_MOVIES // add option to disable intro videos
@@ -466,6 +467,7 @@ enum Config {
#define THIS_IS_STUPID
#undef MORE_LANGUAGES
#undef COMPATIBLE_SAVES
+#undef FIX_INCOMPATIBLE_SAVES
#undef LOAD_INI_SETTINGS
#undef ASPECT_RATIO_SCALE
diff --git a/src/entities/Entity.cpp b/src/entities/Entity.cpp
index a7f4bd45..c38f12c7 100644
--- a/src/entities/Entity.cpp
+++ b/src/entities/Entity.cpp
@@ -732,7 +732,7 @@ CEntity::SaveEntityFlags(uint8*& buf)
if (bZoneCulled) tmp |= BIT(30);
if (bZoneCulled2) tmp |= BIT(31);
- WriteSaveBuf<uint32>(buf, tmp);
+ WriteSaveBuf(buf, tmp);
tmp = 0;
@@ -748,7 +748,7 @@ CEntity::SaveEntityFlags(uint8*& buf)
if (bDistanceFade) tmp |= BIT(8);
if (m_flagE2) tmp |= BIT(9);
- WriteSaveBuf<uint32>(buf, tmp);
+ WriteSaveBuf(buf, tmp);
}
void
diff --git a/src/objects/ParticleObject.cpp b/src/objects/ParticleObject.cpp
index 211a568c..5d480ecc 100644
--- a/src/objects/ParticleObject.cpp
+++ b/src/objects/ParticleObject.cpp
@@ -10,6 +10,12 @@
#include "DMAudio.h"
#include "screendroplets.h"
+#ifdef COMPATIBLE_SAVES
+#define PARTICLE_OBJECT_SIZEOF 0x88
+#else
+#define PARTICLE_OBJECT_SIZEOF sizeof(CParticleObject)
+#endif
+
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
CParticleObject::SaveParticle(uint8 *buffer, uint32 *length)
{
@@ -1128,27 +1177,35 @@ CParticleObject::SaveParticle(uint8 *buffer, uint32 *length)
*(int32 *)buffer = numObjects;
buffer += sizeof(int32);
- int32 objectsLength = sizeof(CParticleObject) * (numObjects + 1);
+ int32 objectsLength = PARTICLE_OBJECT_SIZEOF * (numObjects + 1);
int32 dataLength = objectsLength + sizeof(int32);
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;
#else
memcpy(buffer, p, sizeof(CParticleObject));
#endif
buffer += sizeof(CParticleObject);
+#endif
}
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;
#else
memcpy(buffer, p, sizeof(CParticleObject));
#endif
buffer += sizeof(CParticleObject);
+#endif
}
*length = dataLength;
@@ -1166,7 +1223,7 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
int32 numObjects = *(int32 *)buffer;
buffer += sizeof(int32);
- if ( length != sizeof(CParticleObject) * (numObjects + 1) + sizeof(int32) )
+ if ( length != PARTICLE_OBJECT_SIZEOF * (numObjects + 1) + sizeof(int32) )
return false;
if ( numObjects == 0 )
@@ -1177,14 +1234,17 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
while ( i < numObjects )
{
CParticleObject *dst = pUnusedListHead;
+#ifndef COMPATIBLE_SAVES
CParticleObject *src = (CParticleObject *)buffer;
buffer += sizeof(CParticleObject);
+#endif
if ( dst == NULL )
return false;
MoveToList(&pUnusedListHead, &pCloseListHead, dst);
+#ifndef COMPATIBLE_SAVES
dst->m_nState = POBJECTSTATE_UPDATE_CLOSE;
dst->m_Type = src->m_Type;
dst->m_ParticleType = src->m_ParticleType;
@@ -1200,6 +1260,47 @@ CParticleObject::LoadParticle(uint8 *buffer, uint32 length)
dst->m_nNumEffectCycles = src->m_nNumEffectCycles;
dst->m_nSkipFrames = src->m_nSkipFrames;
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++;
}
diff --git a/src/peds/Ped.cpp b/src/peds/Ped.cpp
index 4d80cac2..5b52d021 100644
--- a/src/peds/Ped.cpp
+++ b/src/peds/Ped.cpp
@@ -8496,21 +8496,21 @@ CPed::renderLimb(int node)
void
CPed::Save(uint8*& buf)
{
- SkipSaveBuf(buf, 52);
+ ZeroSaveBuf(buf, 52);
CopyToBuf(buf, GetPosition().x);
CopyToBuf(buf, GetPosition().y);
CopyToBuf(buf, GetPosition().z);
- SkipSaveBuf(buf, 288);
+ ZeroSaveBuf(buf, 288);
CopyToBuf(buf, CharCreatedBy);
- SkipSaveBuf(buf, 351);
+ ZeroSaveBuf(buf, 351);
CopyToBuf(buf, m_fHealth);
CopyToBuf(buf, m_fArmour);
- SkipSaveBuf(buf, 148);
+ ZeroSaveBuf(buf, 148);
for (int i = 0; i < 13; i++) // has to be hardcoded
m_weapons[i].Save(buf);
- SkipSaveBuf(buf, 5);
+ ZeroSaveBuf(buf, 5);
CopyToBuf(buf, m_maxWeaponTypeAllowed);
- SkipSaveBuf(buf, 162);
+ ZeroSaveBuf(buf, 162);
}
void
diff --git a/src/peds/PlayerPed.cpp b/src/peds/PlayerPed.cpp
index 93a403bd..6d6fc714 100644
--- a/src/peds/PlayerPed.cpp
+++ b/src/peds/PlayerPed.cpp
@@ -1492,14 +1492,14 @@ void
CPlayerPed::Save(uint8*& buf)
{
CPed::Save(buf);
- SkipSaveBuf(buf, 16);
+ ZeroSaveBuf(buf, 16);
CopyToBuf(buf, m_fMaxStamina);
- SkipSaveBuf(buf, 28);
+ ZeroSaveBuf(buf, 28);
CopyToBuf(buf, m_nTargettableObjects[0]);
CopyToBuf(buf, m_nTargettableObjects[1]);
CopyToBuf(buf, m_nTargettableObjects[2]);
CopyToBuf(buf, m_nTargettableObjects[3]);
- SkipSaveBuf(buf, 116);
+ ZeroSaveBuf(buf, 116);
}
void
diff --git a/src/save/GenericGameStorage.cpp b/src/save/GenericGameStorage.cpp
index 23a8fd6a..f51f8233 100644
--- a/src/save/GenericGameStorage.cpp
+++ b/src/save/GenericGameStorage.cpp
@@ -600,6 +600,552 @@ align4bytes(int32 size)
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
void DisplaySaveResult(int unk, char* name)
diff --git a/src/save/GenericGameStorage.h b/src/save/GenericGameStorage.h
index 069ba7cd..b291ddf9 100644
--- a/src/save/GenericGameStorage.h
+++ b/src/save/GenericGameStorage.h
@@ -22,6 +22,11 @@ bool CheckDataNotCorrupt(int32 slot, char *name);
bool RestoreForStartLoad();
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 char DefaultPCSaveFileName[260];
diff --git a/src/save/PCSave.cpp b/src/save/PCSave.cpp
index a9df00af..0c228a6d 100644
--- a/src/save/PCSave.cpp
+++ b/src/save/PCSave.cpp
@@ -122,6 +122,13 @@ C_PcSave::PopulateSlotInfo()
}
if (Slots[i + 1] == SLOT_OK) {
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;
memcpy(&st, &header.SaveDateTime, sizeof(SYSTEMTIME));
const char *month;
diff --git a/src/save/PCSave.h b/src/save/PCSave.h
index 4a2d9a66..83471b5d 100644
--- a/src/save/PCSave.h
+++ b/src/save/PCSave.h
@@ -33,7 +33,7 @@ public:
void PopulateSlotInfo();
bool DeleteSlot(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);
};
diff --git a/src/save/SaveBuf.h b/src/save/SaveBuf.h
index 98fe888b..aad2e1a8 100644
--- a/src/save/SaveBuf.h
+++ b/src/save/SaveBuf.h
@@ -36,6 +36,15 @@ WriteSaveBuf(uint8 *&buf, const T &value)
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 WriteSaveHeader(buf, a, b, c, d, size) \
diff --git a/src/skel/crossplatform.cpp b/src/skel/crossplatform.cpp
index 1d49ebd2..577983b6 100644
--- a/src/skel/crossplatform.cpp
+++ b/src/skel/crossplatform.cpp
@@ -155,6 +155,29 @@ FILE* _fcaseopen(char const* filename, char const* mode)
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)
// Returned string should freed manually (if exists)
char* casepath(char const* path, bool checkPathFirst)
diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h
index 2dd9c162..aa90ce5a 100644
--- a/src/skel/crossplatform.h
+++ b/src/skel/crossplatform.h
@@ -29,6 +29,7 @@ enum eWinVersion
#endif
extern DWORD _dwOperatingSystemVersion;
#define fcaseopen fopen
+#define caserename rename
#else
char *strupr(char *str);
char *strlwr(char *str);
@@ -51,6 +52,8 @@ extern long _dwOperatingSystemVersion;
char *casepath(char const *path, bool checkPathFirst = true);
FILE *_fcaseopen(char const *filename, char const *mode);
#define fcaseopen _fcaseopen
+int _caserename(const char *old_filename, const char *new_filename);
+#define caserename _caserename
#endif
#ifdef RW_GL3
diff --git a/src/vehicles/Automobile.cpp b/src/vehicles/Automobile.cpp
index 3de3e12b..7d942dcd 100644
--- a/src/vehicles/Automobile.cpp
+++ b/src/vehicles/Automobile.cpp
@@ -4717,8 +4717,8 @@ void
CAutomobile::Save(uint8*& buf)
{
CVehicle::Save(buf);
- WriteSaveBuf<CDamageManager>(buf, Damage);
- SkipSaveBuf(buf, 800 - sizeof(CDamageManager));
+ WriteSaveBuf(buf, Damage);
+ ZeroSaveBuf(buf, 800 - sizeof(CDamageManager));
}
void
diff --git a/src/vehicles/Boat.cpp b/src/vehicles/Boat.cpp
index 88444e95..65cdd8c6 100644
--- a/src/vehicles/Boat.cpp
+++ b/src/vehicles/Boat.cpp
@@ -940,7 +940,7 @@ void
CBoat::Save(uint8*& buf)
{
CVehicle::Save(buf);
- SkipSaveBuf(buf, 1156 - 648);
+ ZeroSaveBuf(buf, 1156 - 648);
}
void
diff --git a/src/vehicles/Cranes.cpp b/src/vehicles/Cranes.cpp
index 0f1b8b4c..db9d2e00 100644
--- a/src/vehicles/Cranes.cpp
+++ b/src/vehicles/Cranes.cpp
@@ -37,6 +37,12 @@
#define MIN_VALID_POSITION (-10000.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 CCranes::CarsCollectedMilitaryCrane;
@@ -634,10 +640,46 @@ void CCranes::Save(uint8* buf, uint32* size)
{
INITSAVEBUF
- *size = 2 * sizeof(uint32) + sizeof(aCranes);
+ *size = 2 * sizeof(uint32) + CRANES_SAVE_SIZE;
WriteSaveBuf(buf, NumCranes);
WriteSaveBuf(buf, CarsCollectedMilitaryCrane);
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]);
if (pCrane->m_pCraneEntity != nil)
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);
if (pCrane->m_pVehiclePickedUp != nil)
pCrane->m_pVehiclePickedUp = (CVehicle*)(CPools::GetVehiclePool()->GetJustIndex_NoFreeAssert(pCrane->m_pVehiclePickedUp) + 1);
+#endif
}
VALIDATESAVEBUF(*size);
@@ -656,8 +699,46 @@ void CCranes::Load(uint8* buf, uint32 size)
ReadSaveBuf(&NumCranes, 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);
+ }
for (int i = 0; i < NUM_CRANES; i++) {
CCrane *pCrane = &aCranes[i];
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);
if (pCrane->m_pVehiclePickedUp != nil)
pCrane->m_pVehiclePickedUp = CPools::GetVehiclePool()->GetSlot((uintptr)pCrane->m_pVehiclePickedUp - 1);
+#endif
}
for (int i = 0; i < NUM_CRANES; i++) {
aCranes[i].m_nAudioEntity = DMAudio.CreateEntity(AUDIOTYPE_CRANE, &aCranes[i]);
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index 3d3ba8f2..4259f9d8 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -1262,42 +1262,42 @@ DestroyVehicleAndDriverAndPassengers(CVehicle* pVehicle)
void
CVehicle::Save(uint8*& buf)
{
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<float>(buf, GetRight().x);
- WriteSaveBuf<float>(buf, GetRight().y);
- WriteSaveBuf<float>(buf, GetRight().z);
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<float>(buf, GetForward().x);
- WriteSaveBuf<float>(buf, GetForward().y);
- WriteSaveBuf<float>(buf, GetForward().z);
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<float>(buf, GetUp().x);
- WriteSaveBuf<float>(buf, GetUp().y);
- WriteSaveBuf<float>(buf, GetUp().z);
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<float>(buf, GetPosition().x);
- WriteSaveBuf<float>(buf, GetPosition().y);
- WriteSaveBuf<float>(buf, GetPosition().z);
- SkipSaveBuf(buf, 16);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, GetRight().x);
+ WriteSaveBuf(buf, GetRight().y);
+ WriteSaveBuf(buf, GetRight().z);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, GetForward().x);
+ WriteSaveBuf(buf, GetForward().y);
+ WriteSaveBuf(buf, GetForward().z);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, GetUp().x);
+ WriteSaveBuf(buf, GetUp().y);
+ WriteSaveBuf(buf, GetUp().z);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, GetPosition().x);
+ WriteSaveBuf(buf, GetPosition().y);
+ WriteSaveBuf(buf, GetPosition().z);
+ ZeroSaveBuf(buf, 16);
SaveEntityFlags(buf);
- SkipSaveBuf(buf, 212);
+ ZeroSaveBuf(buf, 212);
AutoPilot.Save(buf);
- WriteSaveBuf<int8>(buf, m_currentColour1);
- WriteSaveBuf<int8>(buf, m_currentColour2);
- SkipSaveBuf(buf, 2);
- WriteSaveBuf<int16>(buf, m_nAlarmState);
- SkipSaveBuf(buf, 43);
- WriteSaveBuf<uint8>(buf, m_nNumMaxPassengers);
- SkipSaveBuf(buf, 2);
- WriteSaveBuf<float>(buf, field_1D0[0]);
- WriteSaveBuf<float>(buf, field_1D0[1]);
- WriteSaveBuf<float>(buf, field_1D0[2]);
- WriteSaveBuf<float>(buf, field_1D0[3]);
- SkipSaveBuf(buf, 8);
- WriteSaveBuf<float>(buf, m_fSteerAngle);
- WriteSaveBuf<float>(buf, m_fGasPedal);
- WriteSaveBuf<float>(buf, m_fBrakePedal);
- WriteSaveBuf<uint8>(buf, VehicleCreatedBy);
+ WriteSaveBuf(buf, m_currentColour1);
+ WriteSaveBuf(buf, m_currentColour2);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_nAlarmState);
+ ZeroSaveBuf(buf, 43);
+ WriteSaveBuf(buf, m_nNumMaxPassengers);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, field_1D0[0]);
+ WriteSaveBuf(buf, field_1D0[1]);
+ WriteSaveBuf(buf, field_1D0[2]);
+ WriteSaveBuf(buf, field_1D0[3]);
+ ZeroSaveBuf(buf, 8);
+ WriteSaveBuf(buf, m_fSteerAngle);
+ WriteSaveBuf(buf, m_fGasPedal);
+ WriteSaveBuf(buf, m_fBrakePedal);
+ WriteSaveBuf(buf, VehicleCreatedBy);
uint8 flags = 0;
if (bIsLawEnforcer) flags |= BIT(0);
if (bIsLocked) flags |= BIT(3);
@@ -1305,19 +1305,19 @@ CVehicle::Save(uint8*& buf)
if (bIsHandbrakeOn) flags |= BIT(5);
if (bLightsOn) flags |= BIT(6);
if (bFreebies) flags |= BIT(7);
- WriteSaveBuf<uint8>(buf, flags);
- SkipSaveBuf(buf, 10);
- WriteSaveBuf<float>(buf, m_fHealth);
- WriteSaveBuf<uint8>(buf, m_nCurrentGear);
- SkipSaveBuf(buf, 3);
- WriteSaveBuf<float>(buf, m_fChangeGearTime);
- SkipSaveBuf(buf, 4);
- WriteSaveBuf<uint32>(buf, m_nTimeOfDeath);
- SkipSaveBuf(buf, 2);
- WriteSaveBuf<int16>(buf, m_nBombTimer);
- SkipSaveBuf(buf, 12);
- WriteSaveBuf<int8>(buf, m_nDoorLock);
- SkipSaveBuf(buf, 99);
+ WriteSaveBuf(buf, flags);
+ ZeroSaveBuf(buf, 10);
+ WriteSaveBuf(buf, m_fHealth);
+ WriteSaveBuf(buf, m_nCurrentGear);
+ ZeroSaveBuf(buf, 3);
+ WriteSaveBuf(buf, m_fChangeGearTime);
+ ZeroSaveBuf(buf, 4);
+ WriteSaveBuf(buf, m_nTimeOfDeath);
+ ZeroSaveBuf(buf, 2);
+ WriteSaveBuf(buf, m_nBombTimer);
+ ZeroSaveBuf(buf, 12);
+ WriteSaveBuf(buf, m_nDoorLock);
+ ZeroSaveBuf(buf, 96);
}
void
@@ -1379,8 +1379,7 @@ CVehicle::Load(uint8*& buf)
SkipSaveBuf(buf, 2);
ReadSaveBuf(&m_nBombTimer, buf);
SkipSaveBuf(buf, 12);
- ReadSaveBuf(&flags, buf);
- m_nDoorLock = (eCarLock)flags;
- SkipSaveBuf(buf, 99);
+ ReadSaveBuf(&m_nDoorLock, buf);
+ SkipSaveBuf(buf, 96);
}
#endif
diff --git a/src/weapons/Weapon.cpp b/src/weapons/Weapon.cpp
index 43a85db8..6f0e9094 100644
--- a/src/weapons/Weapon.cpp
+++ b/src/weapons/Weapon.cpp
@@ -2337,7 +2337,7 @@ CWeapon::Save(uint8*& buf)
CopyToBuf(buf, m_nAmmoTotal);
CopyToBuf(buf, m_nTimer);
CopyToBuf(buf, m_bAddRotOffset);
- SkipSaveBuf(buf, 3);
+ ZeroSaveBuf(buf, 3);
}
void