diff options
58 files changed, 2244 insertions, 730 deletions
diff --git a/.github/workflows/re3_msvc_amd64.yml b/.github/workflows/re3_msvc_amd64.yml index 428da540..014ac4f7 100644 --- a/.github/workflows/re3_msvc_amd64.yml +++ b/.github/workflows/re3_msvc_amd64.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: platform: [win-amd64-librw_d3d9-oal, win-amd64-librw_gl3_glfw-oal] - buildtype: [Debug, Release, Vanilla] + buildtype: [Debug, Release] steps: - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v1.0.2 diff --git a/gamefiles/TEXT/american.gxt b/gamefiles/TEXT/american.gxt Binary files differindex ebd1ac39..d4034411 100644 --- a/gamefiles/TEXT/american.gxt +++ b/gamefiles/TEXT/american.gxt diff --git a/gamefiles/TEXT/french.gxt b/gamefiles/TEXT/french.gxt Binary files differindex 5b4c9e05..16c7a716 100644 --- a/gamefiles/TEXT/french.gxt +++ b/gamefiles/TEXT/french.gxt diff --git a/gamefiles/TEXT/german.gxt b/gamefiles/TEXT/german.gxt Binary files differindex 1d6ec988..c3309d61 100644 --- a/gamefiles/TEXT/german.gxt +++ b/gamefiles/TEXT/german.gxt diff --git a/gamefiles/TEXT/italian.gxt b/gamefiles/TEXT/italian.gxt Binary files differindex 746f07da..b30b74f4 100644 --- a/gamefiles/TEXT/italian.gxt +++ b/gamefiles/TEXT/italian.gxt diff --git a/gamefiles/TEXT/polish.gxt b/gamefiles/TEXT/polish.gxt Binary files differindex 5519c290..d771427b 100755 --- a/gamefiles/TEXT/polish.gxt +++ b/gamefiles/TEXT/polish.gxt diff --git a/gamefiles/TEXT/russian.gxt b/gamefiles/TEXT/russian.gxt Binary files differindex 90c8b13d..0075c691 100644 --- a/gamefiles/TEXT/russian.gxt +++ b/gamefiles/TEXT/russian.gxt diff --git a/gamefiles/TEXT/spanish.gxt b/gamefiles/TEXT/spanish.gxt Binary files differindex be659528..8980eb4d 100644 --- a/gamefiles/TEXT/spanish.gxt +++ b/gamefiles/TEXT/spanish.gxt diff --git a/premake5.lua b/premake5.lua index 72ff4d09..0a8faa07 100644 --- a/premake5.lua +++ b/premake5.lua @@ -68,7 +68,7 @@ end workspace "re3"
language "C++"
- configurations { "Debug", "Release", "Vanilla" }
+ configurations { "Debug", "Release" }
startproject "re3"
location "build"
symbols "Full"
@@ -80,6 +80,7 @@ workspace "re3" end
filter { "system:windows" }
+ configurations { "Vanilla" }
platforms {
"win-x86-RW33_d3d8-mss",
"win-x86-librw_d3d9-mss",
@@ -123,9 +124,6 @@ workspace "re3" flags { "LinkTimeOptimization" }
end
- filter "configurations:Vanilla"
- defines { "VANILLA_DEFINES" }
-
filter { "platforms:win*" }
system "windows"
@@ -315,6 +313,9 @@ project "re3" includedirs { "vendor/opusfile/include" }
end
+ filter "configurations:Vanilla"
+ defines { "VANILLA_DEFINES" }
+
filter "platforms:*mss"
defines { "AUDIO_MSS" }
includedirs { "vendor/milessdk/include" }
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 35b7ec11..28090d7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,7 +39,7 @@ target_compile_definitions(${EXECUTABLE} PRIVATE $<IF:$<CONFIG:DEBUG>,DEBUG,NDEBUG> LIBRW - ${PROJECT}_NO_AUTOLINK + CMAKE_NO_AUTOLINK ) if(LIBRW_PLATFORM_D3D9) diff --git a/src/animation/AnimBlendAssociation.h b/src/animation/AnimBlendAssociation.h index 80927da2..45720b6f 100644 --- a/src/animation/AnimBlendAssociation.h +++ b/src/animation/AnimBlendAssociation.h @@ -35,7 +35,7 @@ public: CAnimBlendLink link; - int numNodes; // taken from CAnimBlendClumpData::numFrames + int32 numNodes; // taken from CAnimBlendClumpData::numFrames // NB: Order of these depends on order of nodes in Clump this was built from CAnimBlendNode *nodes; CAnimBlendHierarchy *hierarchy; diff --git a/src/animation/AnimBlendClumpData.cpp b/src/animation/AnimBlendClumpData.cpp index 702ee811..b333a449 100644 --- a/src/animation/AnimBlendClumpData.cpp +++ b/src/animation/AnimBlendClumpData.cpp @@ -3,7 +3,6 @@ #include "AnimBlendClumpData.h" #include "MemoryMgr.h" - CAnimBlendClumpData::CAnimBlendClumpData(void) { numFrames = 0; diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp index 2e391349..a113cc93 100644 --- a/src/audio/AudioManager.cpp +++ b/src/audio/AudioManager.cpp @@ -993,4 +993,4 @@ cAudioManager::ComputeEmittingVolume(uint8 emittingVolume, float intensity, floa return (quatIntensity - (dist - diffIntensity)) * (float)emittingVolume / quatIntensity; return emittingVolume; } -#endif
\ No newline at end of file +#endif diff --git a/src/audio/oal/stream.cpp b/src/audio/oal/stream.cpp index 0209202a..6afe8e30 100644 --- a/src/audio/oal/stream.cpp +++ b/src/audio/oal/stream.cpp @@ -2,7 +2,7 @@ #ifdef AUDIO_OAL -#if defined _MSC_VER && !defined RE3_NO_AUTOLINK +#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK #ifdef AUDIO_OAL_USE_SNDFILE #pragma comment( lib, "libsndfile-1.lib" ) #endif @@ -504,6 +504,7 @@ public: class CMP3File : public IDecoder { +protected: mpg123_handle *m_pMH; bool m_bOpened; uint32 m_nRate; 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/CarCtrl.cpp b/src/control/CarCtrl.cpp index 0516e214..35580053 100644 --- a/src/control/CarCtrl.cpp +++ b/src/control/CarCtrl.cpp @@ -77,7 +77,7 @@ int32 CCarCtrl::NumRandomCars; int32 CCarCtrl::NumParkedCars; int32 CCarCtrl::NumPermanentCars; int8 CCarCtrl::CountDownToCarsAtStart; -int32 CCarCtrl::MaxNumberOfCarsInUse = 12; +int32 CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS; uint32 CCarCtrl::LastTimeLawEnforcerCreated; uint32 CCarCtrl::LastTimeFireTruckCreated; uint32 CCarCtrl::LastTimeAmbulanceCreated; 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/Frontend.cpp b/src/core/Frontend.cpp index 7ff80697..ecb893b4 100644 --- a/src/core/Frontend.cpp +++ b/src/core/Frontend.cpp @@ -336,6 +336,7 @@ const char* MenuFilenames[][2] = { CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE), MENU_Y(SMALLESTTEXT_Y_SCALE)); \ CFont::SetFontStyle(FONT_LOCALE(FONT_BANK)); +// value must be between 0.0-1.0 #define ProcessSlider(value, increaseAction, decreaseAction, hoverStartX, hoverEndX) \ do { \ lastActiveBarX = DisplaySlider(MENU_X_RIGHT_ALIGNED(MENUSLIDER_X + columnWidth), MENU_Y(bitAboveNextItemY), MENU_Y(smallestSliderBar), MENU_Y(usableLineHeight), MENU_X(MENUSLIDER_UNK), value); \ @@ -489,7 +490,7 @@ CMenuManager::ThingsToDoBeforeGoingBack() option.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_FOCUSLOSS); if (option.m_Action == MENUACTION_CFO_SELECT && option.m_CFOSelect->onlyApplyOnEnter && option.m_CFOSelect->lastSavedValue != option.m_CFOSelect->displayedValue) - option.m_CFOSelect->displayedValue = *option.m_CFO->value = option.m_CFOSelect->lastSavedValue; + option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue; if (aScreens[m_nCurrScreen].returnPrevPageFunc) { aScreens[m_nCurrScreen].returnPrevPageFunc(); @@ -898,29 +899,29 @@ CMenuManager::CheckSliderMovement(int value) { switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) { case MENUACTION_BRIGHTNESS: - m_PrefsBrightness += value * (512/16); + m_PrefsBrightness += value * (512/MENUSLIDER_LOGICAL_BARS); m_PrefsBrightness = Clamp(m_PrefsBrightness, 0, 511); break; case MENUACTION_DRAWDIST: if(value > 0) - m_PrefsLOD += ((1.8f - 0.8f) / 16.0f); + m_PrefsLOD += ((1.8f - 0.8f) / MENUSLIDER_LOGICAL_BARS); else - m_PrefsLOD -= ((1.8f - 0.8f) / 16.0f); + m_PrefsLOD -= ((1.8f - 0.8f) / MENUSLIDER_LOGICAL_BARS); m_PrefsLOD = Clamp(m_PrefsLOD, 0.8f, 1.8f); CRenderer::ms_lodDistScale = m_PrefsLOD; break; case MENUACTION_MUSICVOLUME: - m_PrefsMusicVolume += value * (128/16); + m_PrefsMusicVolume += value * (128/MENUSLIDER_LOGICAL_BARS); m_PrefsMusicVolume = Clamp(m_PrefsMusicVolume, 0, 127); DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume); break; case MENUACTION_SFXVOLUME: - m_PrefsSfxVolume += value * (128/16); + m_PrefsSfxVolume += value * (128/MENUSLIDER_LOGICAL_BARS); m_PrefsSfxVolume = Clamp(m_PrefsSfxVolume, 0, 127); DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume); break; case MENUACTION_MOUSESENS: - TheCamera.m_fMouseAccelHorzntl += value * 1.0f/200.0f/15.0f; // ??? + TheCamera.m_fMouseAccelHorzntl += value * 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f/3200.0f, 1.0f/200.0f); #ifdef FIX_BUGS TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f; @@ -928,6 +929,20 @@ CMenuManager::CheckSliderMovement(int value) TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl; #endif break; +#ifdef CUSTOM_FRONTEND_OPTIONS + case MENUACTION_CFO_SLIDER: + { + CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption]; + float oldValue = *(float*)option.m_CFOSlider->value; + *(float*)option.m_CFOSlider->value += value * ((option.m_CFOSlider->max - option.m_CFOSlider->min) / MENUSLIDER_LOGICAL_BARS); + *(float*)option.m_CFOSlider->value = Clamp(*(float*)option.m_CFOSlider->value, option.m_CFOSlider->min, option.m_CFOSlider->max); + + if (*(float*)option.m_CFOSlider->value != oldValue && option.m_CFOSlider->changeFunc) + option.m_CFOSlider->changeFunc(oldValue, *(float*)option.m_CFOSlider->value); + + break; + } +#endif default: return; } @@ -1001,10 +1016,10 @@ CMenuManager::DisplaySlider(float x, float y, float mostLeftBarSize, float mostR int lastActiveBarX = 0; float curBarX = 0.0f; float spacing = SCREEN_SCALE_X(10.0f); - for (int i = 0; i < 16; i++) { - curBarX = i * rectSize/16.0f + x; + for (int i = 0; i < MENUSLIDER_BARS; i++) { + curBarX = i * rectSize/MENUSLIDER_BARS + x; - if (i / 16.0f + 1 / 32.0f < progress) { + if (i / (float)MENUSLIDER_BARS + 1 / (MENUSLIDER_BARS * 2.f) < progress) { color = CRGBA(SLIDERON_COLOR.r, SLIDERON_COLOR.g, SLIDERON_COLOR.b, FadeIn(255)); lastActiveBarX = curBarX; } else @@ -1012,7 +1027,7 @@ CMenuManager::DisplaySlider(float x, float y, float mostLeftBarSize, float mostR maxBarHeight = Max(mostLeftBarSize, mostRightBarSize); - float curBarFreeSpace = ((16 - i) * mostLeftBarSize + i * mostRightBarSize) / 16.0f; + float curBarFreeSpace = ((MENUSLIDER_BARS - i) * mostLeftBarSize + i * mostRightBarSize) / (float)MENUSLIDER_BARS; float left = curBarX; float top = y + maxBarHeight - curBarFreeSpace; float right = spacing + curBarX; @@ -1595,10 +1610,10 @@ CMenuManager::Draw() // If that was previously selected option, restore it to default value. // if (m_nCurrOption != lastSelectedOpt && lastSelectedOpt == i) - option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *option.m_CFO->value; + option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *(int8*)option.m_CFO->value; } else { - if (option.m_CFOSelect->displayedValue != *option.m_CFO->value) + if (option.m_CFOSelect->displayedValue != *(int8*)option.m_CFO->value) SetHelperText(1); // Enter to apply else if (m_nHelperTextMsgId == 1) ResetHelperText(); // Applied @@ -1606,8 +1621,8 @@ CMenuManager::Draw() } // To whom manipulate option.m_CFO->value of select options externally (like RestoreDef functions) - if (*option.m_CFO->value != option.m_CFOSelect->lastSavedValue) - option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *option.m_CFO->value; + if (*(int8*)option.m_CFO->value != option.m_CFOSelect->lastSavedValue) + option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *(int8*)option.m_CFO->value; if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0) option.m_CFOSelect->displayedValue = 0; @@ -1799,6 +1814,12 @@ CMenuManager::Draw() case MENUACTION_MOUSESENS: ProcessSlider(TheCamera.m_fMouseAccelHorzntl * 200.0f, HOVEROPTION_INCREASE_MOUSESENS, HOVEROPTION_DECREASE_MOUSESENS, MENU_X_LEFT_ALIGNED(200.0f), SCREEN_WIDTH); break; +#ifdef CUSTOM_FRONTEND_OPTIONS + case MENUACTION_CFO_SLIDER: + CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[i]; + ProcessSlider((*(float*)option.m_CFOSlider->value - option.m_CFOSlider->min) / (option.m_CFOSlider->max - option.m_CFOSlider->min), HOVEROPTION_INCREASE_CFO_SLIDER, HOVEROPTION_DECREASE_CFO_SLIDER, MENU_X_LEFT_ALIGNED(170.0f), SCREEN_WIDTH); + break; +#endif } // Needed after the bug fix in Font.cpp @@ -4477,7 +4498,7 @@ CMenuManager::ProcessButtonPresses(void) #ifndef TIDY_UP_PBP switch (m_nHoverOption) { case HOVEROPTION_INCREASE_BRIGHTNESS: - m_PrefsBrightness = m_PrefsBrightness + 32; + m_PrefsBrightness = m_PrefsBrightness + (512 / MENUSLIDER_LOGICAL_BARS); if (m_PrefsBrightness < 0) { m_PrefsBrightness = 0; } @@ -4487,7 +4508,7 @@ CMenuManager::ProcessButtonPresses(void) SaveSettings(); break; case HOVEROPTION_DECREASE_BRIGHTNESS: - m_PrefsBrightness = m_PrefsBrightness - 32; + m_PrefsBrightness = m_PrefsBrightness - (512 / MENUSLIDER_LOGICAL_BARS); if (m_PrefsBrightness < 0) { m_PrefsBrightness = 0; } @@ -4497,25 +4518,25 @@ CMenuManager::ProcessButtonPresses(void) SaveSettings(); break; case HOVEROPTION_INCREASE_DRAWDIST: - m_PrefsLOD = m_PrefsLOD + (1.0f / 16); + m_PrefsLOD = m_PrefsLOD + (1.0f / MENUSLIDER_LOGICAL_BARS); m_PrefsLOD = min(1.8f, m_PrefsLOD); CRenderer::ms_lodDistScale = m_PrefsLOD; SaveSettings(); break; case HOVEROPTION_DECREASE_DRAWDIST: - m_PrefsLOD = m_PrefsLOD - (1.0f / 16); + m_PrefsLOD = m_PrefsLOD - (1.0f / MENUSLIDER_LOGICAL_BARS); m_PrefsLOD = max(0.8f, m_PrefsLOD); CRenderer::ms_lodDistScale = m_PrefsLOD; SaveSettings(); break; case HOVEROPTION_INCREASE_MUSICVOLUME: - m_PrefsMusicVolume = m_PrefsMusicVolume + 8; + m_PrefsMusicVolume = m_PrefsMusicVolume + (128 / MENUSLIDER_LOGICAL_BARS); m_PrefsMusicVolume = Clamp(m_PrefsMusicVolume, 0, 127); DMAudio.SetMusicMasterVolume(uchar)(m_PrefsMusicVolume); SaveSettings(); break; case HOVEROPTION_DECREASE_MUSICVOLUME: - m_PrefsMusicVolume = m_PrefsMusicVolume - 8; + m_PrefsMusicVolume = m_PrefsMusicVolume - (128 / MENUSLIDER_LOGICAL_BARS); if (m_PrefsMusicVolume < 0) { m_PrefsMusicVolume = 0; } @@ -4526,7 +4547,7 @@ CMenuManager::ProcessButtonPresses(void) SaveSettings(); break; case HOVEROPTION_INCREASE_SFXVOLUME: - m_PrefsSFXVolume = m_PrefsSFXVolume + 8; + m_PrefsSFXVolume = m_PrefsSFXVolume + (128 / MENUSLIDER_LOGICAL_BARS); if (m_PrefsSFXVolume < 0) { m_PrefsSFXVolume = 0; } @@ -4537,7 +4558,7 @@ CMenuManager::ProcessButtonPresses(void) SaveSettings(); break; case HOVEROPTION_DECREASE_SFXVOLUME: - m_PrefsSFXVolume = m_PrefsSFXVolume - 8; + m_PrefsSFXVolume = m_PrefsSFXVolume - (128 / MENUSLIDER_LOGICAL_BARS); if (m_PrefsSFXVolume < 0) { m_PrefsSFXVolume = 0; } @@ -4548,7 +4569,7 @@ CMenuManager::ProcessButtonPresses(void) SaveSettings(); break; case HOVEROPTION_INCREASE_MOUSESENS: - TheCamera.m_fMouseAccelHorzntl += (1.0f / 3000); + TheCamera.m_fMouseAccelHorzntl += 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f / 3200, 1.0f / 200); #ifdef FIX_BUGS TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f; @@ -4558,7 +4579,7 @@ CMenuManager::ProcessButtonPresses(void) SaveSettings(); break; case HOVEROPTION_DECREASE_MOUSESENS: - TheCamera.m_fMouseAccelHorzntl -= (1.0f / 3000); + TheCamera.m_fMouseAccelHorzntl -= 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f / 3200, 1.0f / 200); #ifdef FIX_BUGS TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f; @@ -4575,6 +4596,9 @@ CMenuManager::ProcessButtonPresses(void) case HOVEROPTION_INCREASE_MUSICVOLUME: case HOVEROPTION_INCREASE_SFXVOLUME: case HOVEROPTION_INCREASE_MOUSESENS: +#ifdef CUSTOM_FRONTEND_OPTIONS + case HOVEROPTION_INCREASE_CFO_SLIDER: +#endif CheckSliderMovement(1); break; case HOVEROPTION_DECREASE_BRIGHTNESS: @@ -4582,6 +4606,9 @@ CMenuManager::ProcessButtonPresses(void) case HOVEROPTION_DECREASE_MUSICVOLUME: case HOVEROPTION_DECREASE_SFXVOLUME: case HOVEROPTION_DECREASE_MOUSESENS: +#ifdef CUSTOM_FRONTEND_OPTIONS + case HOVEROPTION_DECREASE_CFO_SLIDER: +#endif CheckSliderMovement(-1); break; } @@ -4612,7 +4639,11 @@ CMenuManager::ProcessButtonPresses(void) || CPad::GetPad(0)->GetAnaloguePadLeftJustUp() || CPad::GetPad(0)->GetAnaloguePadRightJustUp() || CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustDown()) { int option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action; - if (option == MENUACTION_BRIGHTNESS || option == MENUACTION_DRAWDIST) + if (option == MENUACTION_BRIGHTNESS || option == MENUACTION_DRAWDIST +#ifdef CUSTOM_FRONTEND_OPTIONS + || option == MENUACTION_CFO_SLIDER +#endif + ) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); else if (option == MENUACTION_SFXVOLUME) DMAudio.PlayFrontEndSound(SOUND_FRONTEND_AUDIO_TEST, 0); @@ -4775,7 +4806,12 @@ CMenuManager::ProcessButtonPresses(void) } else if (option != MENUACTION_CHANGEMENU && option != MENUACTION_BRIGHTNESS && option != MENUACTION_DRAWDIST && option != MENUACTION_MUSICVOLUME && option != MENUACTION_SFXVOLUME && option != MENUACTION_CHECKSAVE && option != MENUACTION_UNK24 - && option != MENUACTION_MOUSESENS && option != MENUACTION_SCREENRES) { + && option != MENUACTION_MOUSESENS && option != MENUACTION_SCREENRES +#ifdef CUSTOM_FRONTEND_OPTIONS + && option != MENUACTION_CFO_SLIDER +#endif + ) + { DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0); } @@ -5166,9 +5202,9 @@ CMenuManager::ProcessButtonPresses(void) if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0) option.m_CFOSelect->displayedValue = 0; } - int8 oldValue = *option.m_CFO->value; + int8 oldValue = *(int8*)option.m_CFO->value; - *option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue; + *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue; // Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO // if (option.m_CFOSelect->save) @@ -5412,9 +5448,9 @@ CMenuManager::ProcessButtonPresses(void) option.m_CFOSelect->displayedValue = option.m_CFOSelect->numRightTexts - 1; } if (!option.m_CFOSelect->onlyApplyOnEnter) { - int8 oldValue = *option.m_CFO->value; + int8 oldValue = *(int8*)option.m_CFO->value; - *option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue; + *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue; // Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO // if (option.m_CFOSelect->save) diff --git a/src/core/Frontend.h b/src/core/Frontend.h index 5c3523ab..32e5ef9d 100644 --- a/src/core/Frontend.h +++ b/src/core/Frontend.h @@ -25,6 +25,9 @@ #define MENUSLIDER_X 256.0f #define MENUSLIDER_UNK 256.0f +#define MENUSLIDER_BARS 16 +#define MENUSLIDER_LOGICAL_BARS MENUSLIDER_BARS + #define BIGTEXT_X_SCALE 0.75f // For FONT_HEADING #define BIGTEXT_Y_SCALE 0.9f #define MEDIUMTEXT_X_SCALE 0.55f // For FONT_HEADING @@ -256,6 +259,7 @@ enum eMenuScreen enum eMenuAction { #ifdef CUSTOM_FRONTEND_OPTIONS + MENUACTION_CFO_SLIDER = -3, MENUACTION_CFO_SELECT = -2, MENUACTION_CFO_DYNAMIC = -1, #endif @@ -424,6 +428,10 @@ enum eCheckHover HOVEROPTION_DECREASE_SFXVOLUME, HOVEROPTION_INCREASE_MOUSESENS, HOVEROPTION_DECREASE_MOUSESENS, +#ifdef CUSTOM_FRONTEND_OPTIONS + HOVEROPTION_INCREASE_CFO_SLIDER, + HOVEROPTION_DECREASE_CFO_SLIDER, +#endif HOVEROPTION_NOT_HOVERING, }; @@ -493,7 +501,7 @@ struct CCustomScreenLayout { struct CCFO { - int8 *value; + void *value; const char *saveCat; const char *save; }; @@ -524,6 +532,24 @@ struct CCFOSelect : CCFO } }; +// Value is float in here +struct CCFOSlider : CCFO +{ + ChangeFuncFloat changeFunc; + float min; + float max; + + CCFOSlider() {}; + CCFOSlider(float* value, const char* saveCat, const char* save, float min, float max, ChangeFuncFloat changeFunc = nil){ + this->value = value; + this->saveCat = saveCat; + this->save = save; + this->changeFunc = changeFunc; + this->min = min; + this->max = max; + } +}; + struct CCFODynamic : CCFO { DrawFunc drawFunc; @@ -555,6 +581,7 @@ struct CMenuScreenCustom CCFO *m_CFO; // for initializing CCFOSelect *m_CFOSelect; CCFODynamic *m_CFODynamic; + CCFOSlider *m_CFOSlider; }; int32 m_SaveSlot; // eSaveSlot int32 m_TargetMenu; // eMenuScreen diff --git a/src/core/Game.cpp b/src/core/Game.cpp index f6156a4c..b3dd1eda 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -409,7 +409,11 @@ bool CGame::Initialise(const char* datFile) #endif #ifndef GTA_PS2 - CIniFile::LoadIniFile(); +#ifdef PED_CAR_DENSITY_SLIDERS + // Load density values from gta3.ini only if our re3.ini have them 1.f + if (CIniFile::PedNumberMultiplier == 1.f && CIniFile::CarNumberMultiplier == 1.f) +#endif + CIniFile::LoadIniFile(); #endif currLevel = LEVEL_INDUSTRIAL; diff --git a/src/core/IniFile.cpp b/src/core/IniFile.cpp index df01b440..524632fe 100644 --- a/src/core/IniFile.cpp +++ b/src/core/IniFile.cpp @@ -23,6 +23,6 @@ void CIniFile::LoadIniFile() CarNumberMultiplier = Min(3.0f, Max(0.5f, CarNumberMultiplier)); CFileMgr::CloseFile(f); } - CPopulation::MaxNumberOfPedsInUse = 25.0f * PedNumberMultiplier; - CCarCtrl::MaxNumberOfCarsInUse = 12.0f * CarNumberMultiplier; + CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * PedNumberMultiplier; + CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CarNumberMultiplier; }
\ No newline at end of file diff --git a/src/core/IniFile.h b/src/core/IniFile.h index 1e30c4de..30dc8c21 100644 --- a/src/core/IniFile.h +++ b/src/core/IniFile.h @@ -1,5 +1,8 @@ #pragma once +#define DEFAULT_MAX_NUMBER_OF_PEDS 25.0f +#define DEFAULT_MAX_NUMBER_OF_CARS 12.0f + class CIniFile { public: diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp index 6e23f76a..033ed9b9 100644 --- a/src/core/MenuScreensCustom.cpp +++ b/src/core/MenuScreensCustom.cpp @@ -26,6 +26,9 @@ #include "ModelInfo.h" #include "Pad.h" #include "ControllerConfig.h" +#include "IniFile.h" +#include "CarCtrl.h" +#include "Population.h" // Menu screens array is at the bottom of the file. @@ -63,6 +66,15 @@ #define DUALPASS_SELECTOR #endif +#ifdef PED_CAR_DENSITY_SLIDERS + // 0.2f - 3.4f makes it possible to have 1.0f somewhere inbetween + #define DENSITY_SLIDERS \ + MENUACTION_CFO_SLIDER, "FEM_PED", { new CCFOSlider(&CIniFile::PedNumberMultiplier, "Display", "PedDensity", 0.2f, 3.4f, PedDensityChange) }, \ + MENUACTION_CFO_SLIDER, "FEM_CAR", { new CCFOSlider(&CIniFile::CarNumberMultiplier, "Display", "CarDensity", 0.2f, 3.4f, CarDensityChange) }, +#else + #define DENSITY_SLIDERS +#endif + #ifdef NO_ISLAND_LOADING #define ISLAND_LOADING_SELECTOR MENUACTION_CFO_SELECT, "FEM_ISL", { new CCFOSelect((int8*)&CMenuManager::m_PrefsIslandLoading, "Graphics", "IslandLoading", islandLoadingOpts, ARRAY_SIZE(islandLoadingOpts), true, IslandLoadingAfterChange) }, #else @@ -145,6 +157,9 @@ void RestoreDefDisplay(int8 action) { #ifdef FREE_CAM TheCamera.bFreeCam = false; #endif + #ifdef PED_CAR_DENSITY_SLIDERS + CIniFile::LoadIniFile(); + #endif #ifdef GRAPHICS_MENU_OPTIONS // otherwise Frontend will handle those CMenuManager::m_PrefsBrightness = 256; CMenuManager::m_PrefsLOD = 1.2f; @@ -195,6 +210,16 @@ void IslandLoadingAfterChange(int8 before, int8 after) { } #endif +#ifdef PED_CAR_DENSITY_SLIDERS +void PedDensityChange(float before, float after) { + CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * after; +} + +void CarDensityChange(float before, float after) { + CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * after; +} +#endif + #ifndef MULTISAMPLING void GraphicsGoBack() { } @@ -423,6 +448,7 @@ CMenuScreenCustom aScreens[MENUPAGES] = { { "FET_DIS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil, MENUACTION_BRIGHTNESS, "FED_BRI", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, MENUACTION_DRAWDIST, "FEM_LOD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, + DENSITY_SLIDERS MENUACTION_FRAMESYNC, "FEM_VSC", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, MENUACTION_FRAMELIMIT, "FEM_FRM", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, #ifndef EXTENDED_COLOURFILTER @@ -447,6 +473,7 @@ CMenuScreenCustom aScreens[MENUPAGES] = { { "FET_DIS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil, MENUACTION_BRIGHTNESS, "FED_BRI", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, MENUACTION_DRAWDIST, "FEM_LOD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, + DENSITY_SLIDERS CUTSCENE_BORDERS_TOGGLE FREE_CAM_TOGGLE MENUACTION_SUBTITLES, "FED_SUB", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS }, 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 298b2a1a..885f98b8 100644 --- a/src/core/config.h +++ b/src/core/config.h @@ -1,7 +1,9 @@ #pragma once -// disables (most) stuff that wasn't in original gta3.exe - check section at the bottom of this file -//#define VANILLA_DEFINES +// disables (most) stuff that wasn't in original gta3.exe +#ifdef __MWERKS__ +#define VANILLA_DEFINES +#endif enum Config { NUMPLAYERS = 1, // 4 on PS2 @@ -11,7 +13,7 @@ enum Config { MAX_CDCHANNELS = 5, MODELINFOSIZE = 5500, // 3150 on PS2 -#if defined __MWERKS__ || defined VANILLA_DEFINES +#ifdef VANILLA_DEFINES TXDSTORESIZE = 850, #else TXDSTORESIZE = 1024, // for Xbox map @@ -146,8 +148,30 @@ enum Config { //#define GTA_PS2 //#define GTA_XBOX -// This enables things from the PS2 version on PC -#define GTA_PS2_STUFF +// Version defines +#define GTA3_PS2_140 300 +#define GTA3_PS2_160 301 +#define GTA3_PC_10 310 +#define GTA3_PC_11 311 +#define GTA3_PC_STEAM 312 +// TODO? maybe something for xbox or android? + +#define GTA_VERSION GTA3_PC_11 + +#if defined GTA_PS2 +# define GTA_PS2_STUFF +# define RANDOMSPLASH +# define USE_CUSTOM_ALLOCATOR +# define VU_COLLISION +# define ANIM_COMPRESSION +# define PS2_MENU +#elif defined GTA_PC +# define PC_PLAYER_CONTROLS // mouse player/cam mode +# define GTA_REPLAY +# define GTA_SCENE_EDIT +# define PC_MENU +#elif defined GTA_XBOX +#endif // This is enabled for all released games. // any debug stuff that isn't left in any game is not in FINAL @@ -166,19 +190,29 @@ enum Config { #define FINAL #endif -// Version defines -#define GTA3_PS2_140 300 -#define GTA3_PS2_160 301 -#define GTA3_PC_10 310 -#define GTA3_PC_11 311 -#define GTA3_PC_STEAM 312 -// TODO? maybe something for xbox or android? +// these are placed here to work with VANILLA_DEFINES for compatibility +#define NO_CDCHECK // skip audio CD check +#define DEFAULT_NATIVE_RESOLUTION // Set default video mode to your native resolution (fixes Windows 10 launch) -#define GTA_VERSION GTA3_PC_11 +#ifdef VANILLA_DEFINES +#if !defined(_WIN32) || defined(__LP64__) || defined(_WIN64) +#error Vanilla can only be built for win-x86 +#endif + +#define FINAL +#define MASTER +//#define USE_MY_DOCUMENTS +#define THIS_IS_STUPID +#define PC_PARTICLE +#define DONT_FIX_REPLAY_BUGS +#define USE_TXD_CDIMAGE // generate and load textures from txd.img +//#define USE_TEXTURE_POOL // not possible because R* used custom RW33 +#else +// This enables things from the PS2 version on PC +#define GTA_PS2_STUFF // quality of life fixes that should also be in FINAL #define NASTY_GAME // nasty game for all languages -#define NO_CDCHECK // those infamous texts #define DRAW_GAME_VERSION_TEXT @@ -194,22 +228,10 @@ enum Config { //#define COMPRESSED_COL_VECTORS // use compressed vectors for collision vertices //#define ANIM_COMPRESSION // only keep most recently used anims uncompressed -#if defined GTA_PS2 -# define GTA_PS2_STUFF -# define RANDOMSPLASH -# define USE_CUSTOM_ALLOCATOR -# define VU_COLLISION -# define ANIM_COMPRESSION -#elif defined GTA_PC -# ifdef GTA_PS2_STUFF -# define USE_PS2_RAND -# define RANDOMSPLASH // use random splash as on PS2 -# define PS2_MATFX -# endif -# define PC_PLAYER_CONTROLS // mouse player/cam mode -# define GTA_REPLAY -# define GTA_SCENE_EDIT -#elif defined GTA_XBOX +#if defined GTA_PC && defined GTA_PS2_STUFF +# define USE_PS2_RAND +# define RANDOMSPLASH // use random splash as on PS2 +# define PS2_MATFX #endif #ifdef VU_COLLISION @@ -237,7 +259,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 @@ -248,7 +271,7 @@ enum Config { #define ASCII_STRCMP // use faster ascii str comparisons -#if !defined _WIN32 || defined __MWERKS__ || defined __MINGW32__ || defined VANILLA_DEFINES +#if !defined _WIN32 || defined __MINGW32__ #undef ASCII_STRCMP #endif @@ -340,6 +363,7 @@ enum Config { # define CUTSCENE_BORDERS_SWITCH # define MULTISAMPLING // adds MSAA option # define INVERT_LOOK_FOR_PAD // add bInvertLook4Pad from VC +# define PED_CAR_DENSITY_SLIDERS # endif #endif @@ -437,101 +461,4 @@ enum Config { #undef PEDS_REPORT_CRIMES_ON_PHONE #endif -// ------- - -#if defined __MWERKS__ || defined VANILLA_DEFINES -#define FINAL -#undef CHATTYSPLASH -#undef TIMEBARS -//#define USE_MY_DOCUMENTS - -#define MASTER -#undef VALIDATE_SAVE_SIZE -#undef NO_MOVIES -#undef DEBUGMENU - -//#undef NASTY_GAME -//#undef NO_CDCHECK - -#undef DRAW_GAME_VERSION_TEXT -#undef DRAW_MENU_VERSION_TEXT - -#undef GTA_PS2_STUFF -#undef USE_PS2_RAND -#undef RANDOMSPLASH -#undef PS2_MATFX - -#undef FIX_BUGS -#define THIS_IS_STUPID -#undef MORE_LANGUAGES -#undef COMPATIBLE_SAVES -#undef LOAD_INI_SETTINGS - -#undef ASPECT_RATIO_SCALE -#undef PROPER_SCALING -//#undef DEFAULT_NATIVE_RESOLUTION -#undef PS2_ALPHA_TEST -#undef IMPROVED_VIDEOMODE -#undef DISABLE_LOADING_SCREEN -#undef DISABLE_VSYNC_ON_TEXTURE_CONVERSION -#undef ANISOTROPIC_FILTERING -//#define USE_TEXTURE_POOL // not possible because R* used custom RW33 - -#undef EXTENDED_COLOURFILTER -#undef EXTENDED_PIPELINES -#undef SCREEN_DROPLETS -#undef NEW_RENDERER - -#undef FIX_SPRITES - -#define PC_PARTICLE - -#undef XINPUT -#undef DETECT_PAD_INPUT_SWITCH -#undef KANGAROO_CHEAT -#undef ALLCARSHELI_CHEAT -#undef ALT_DODO_CHEAT -#undef REGISTER_START_BUTTON -#undef BIND_VEHICLE_FIREWEAPON -#undef BUTTON_ICONS - -#undef HUD_ENHANCEMENTS -#undef TRIANGULAR_BLIPS -#undef FIX_RADAR -#undef RADIO_OFF_TEXT - -#undef MENU_MAP -#undef GAMEPAD_MENU -#undef SCROLLABLE_STATS_PAGE -#undef CUSTOM_FRONTEND_OPTIONS - -#undef GRAPHICS_MENU_OPTIONS -#undef NO_ISLAND_LOADING -#undef CUTSCENE_BORDERS_SWITCH -#undef MULTISAMPLING -#undef INVERT_LOOK_FOR_PAD - -#undef USE_DEBUG_SCRIPT_LOADER -#undef USE_MEASUREMENTS_IN_METERS -#undef USE_PRECISE_MEASUREMENT_CONVERTION -#undef MISSION_REPLAY -#undef USE_ADVANCED_SCRIPT_DEBUG_OUTPUT -#undef USE_BASIC_SCRIPT_DEBUG_OUTPUT - -#define DONT_FIX_REPLAY_BUGS - -#undef EXPLODING_AIRTRAIN -#undef CAMERA_PICKUP -#undef PED_SKIN -#undef ANIMATE_PED_COL_MODEL -#undef CANCELLABLE_CAR_ENTER -#undef IMPROVED_CAMERA -#undef FREE_CAM - -#undef RADIO_SCROLL_TO_PREV_STATION -#undef AUDIO_CACHE -#undef PS2_AUDIO_CHANNELS -#undef PAUSE_RADIO_IN_FRONTEND -#undef MULTITHREADED_AUDIO -#undef BIG_IMG -#endif +#endif // VANILLA_DEFINES diff --git a/src/core/re3.cpp b/src/core/re3.cpp index fe0347d9..b7d89363 100644 --- a/src/core/re3.cpp +++ b/src/core/re3.cpp @@ -41,6 +41,9 @@ #include "Camera.h" #include "MBlur.h" #include "ControllerConfig.h" +#include "CarCtrl.h" +#include "Population.h" +#include "IniFile.h" #ifdef DETECT_JOYSTICK_MENU #include "crossplatform.h" @@ -179,16 +182,29 @@ CustomFrontendOptionsPopulate(void) #endif #ifdef LOAD_INI_SETTINGS -#include "ini_parser.hpp" +#define MINI_CASE_SENSITIVE +#include "ini.h" + +mINI::INIFile ini("re3.ini"); +mINI::INIStructure cfg; -linb::ini cfg; bool ReadIniIfExists(const char *cat, const char *key, uint32 *out) { - std::string strval = cfg.get(cat, key, "\xBA"); - const char *value = strval.c_str(); - char *endPtr; - if (value && value[0] != '\xBA') { - *out = strtoul(value, &endPtr, 0); + mINI::INIMap<std::string> section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtoul(section.get(key).c_str(), &endPtr, 0); + return true; + } + return false; +} + +bool ReadIniIfExists(const char *cat, const char *key, uint8 *out) +{ + mINI::INIMap<std::string> section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtoul(section.get(key).c_str(), &endPtr, 0); return true; } return false; @@ -196,11 +212,10 @@ bool ReadIniIfExists(const char *cat, const char *key, uint32 *out) bool ReadIniIfExists(const char *cat, const char *key, bool *out) { - std::string strval = cfg.get(cat, key, "\xBA"); - const char *value = strval.c_str(); - char *endPtr; - if (value && value[0] != '\xBA') { - *out = strtoul(value, &endPtr, 0); + mINI::INIMap<std::string> section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtoul(section.get(key).c_str(), &endPtr, 0); return true; } return false; @@ -208,11 +223,10 @@ bool ReadIniIfExists(const char *cat, const char *key, bool *out) bool ReadIniIfExists(const char *cat, const char *key, int32 *out) { - std::string strval = cfg.get(cat, key, "\xBA"); - const char *value = strval.c_str(); - char *endPtr; - if (value && value[0] != '\xBA') { - *out = strtol(value, &endPtr, 0); + mINI::INIMap<std::string> section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtol(section.get(key).c_str(), &endPtr, 0); return true; } return false; @@ -220,11 +234,10 @@ bool ReadIniIfExists(const char *cat, const char *key, int32 *out) bool ReadIniIfExists(const char *cat, const char *key, int8 *out) { - std::string strval = cfg.get(cat, key, "\xBA"); - const char *value = strval.c_str(); - char *endPtr; - if (value && value[0] != '\xBA') { - *out = strtol(value, &endPtr, 0); + mINI::INIMap<std::string> section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtol(section.get(key).c_str(), &endPtr, 0); return true; } return false; @@ -232,10 +245,10 @@ bool ReadIniIfExists(const char *cat, const char *key, int8 *out) bool ReadIniIfExists(const char *cat, const char *key, float *out) { - std::string strval = cfg.get(cat, key, "\xBA"); - const char *value = strval.c_str(); - if (value && value[0] != '\xBA') { - *out = atof(value); + mINI::INIMap<std::string> section = cfg.get(cat); + if (section.has(key)) { + char *endPtr; + *out = strtof(section.get(key).c_str(), &endPtr); return true; } return false; @@ -243,10 +256,10 @@ bool ReadIniIfExists(const char *cat, const char *key, float *out) bool ReadIniIfExists(const char *cat, const char *key, char *out, int size) { - std::string strval = cfg.get(cat, key, "\xBA"); - const char *value = strval.c_str(); - if (value && value[0] != '\xBA') { - strncpy(out, value, size); + mINI::INIMap<std::string> section = cfg.get(cat); + if (section.has(key)) { + strncpy(out, section.get(key).c_str(), size - 1); + out[size - 1] = '\0'; return true; } return false; @@ -254,42 +267,42 @@ bool ReadIniIfExists(const char *cat, const char *key, char *out, int size) void StoreIni(const char *cat, const char *key, uint32 val) { - char temp[10]; + char temp[11]; sprintf(temp, "%u", val); - cfg.set(cat, key, temp); + cfg[cat][key] = temp; } void StoreIni(const char *cat, const char *key, uint8 val) { - char temp[10]; - sprintf(temp, "%u", (uint32)val); - cfg.set(cat, key, temp); + char temp[11]; + sprintf(temp, "%u", val); + cfg[cat][key] = temp; } void StoreIni(const char *cat, const char *key, int32 val) { - char temp[10]; + char temp[11]; sprintf(temp, "%d", val); - cfg.set(cat, key, temp); + cfg[cat][key] = temp; } void StoreIni(const char *cat, const char *key, int8 val) { - char temp[10]; - sprintf(temp, "%d", (int32)val); - cfg.set(cat, key, temp); + char temp[11]; + sprintf(temp, "%d", val); + cfg[cat][key] = temp; } void StoreIni(const char *cat, const char *key, float val) { - char temp[10]; + char temp[50]; sprintf(temp, "%f", val); - cfg.set(cat, key, temp); + cfg[cat][key] = temp; } void StoreIni(const char *cat, const char *key, char *val, int size) { - cfg.set(cat, key, val); + cfg[cat][key] = val; } const char *iniControllerActions[] = { "PED_FIREWEAPON", "PED_CYCLE_WEAPON_RIGHT", "PED_CYCLE_WEAPON_LEFT", "GO_FORWARD", "GO_BACK", "GO_LEFT", "GO_RIGHT", "PED_SNIPER_ZOOM_IN", @@ -351,7 +364,7 @@ void LoadINIControllerSettings() #endif // force to default GTA behaviour (never overwrite bindings on joy change/initialization) if user init'ed/set bindings before we introduced that if (!ReadIniIfExists("Controller", "PadButtonsInited", &ControlsManager.ms_padButtonsInited)) { - ControlsManager.ms_padButtonsInited = cfg.category_size("Bindings") != 0 ? 16 : 0; + ControlsManager.ms_padButtonsInited = cfg.get("Bindings").size() != 0 ? 16 : 0; } for (int32 i = 0; i < MAX_CONTROLLERACTIONS; i++) { @@ -453,12 +466,13 @@ void SaveINIControllerSettings() #endif #endif StoreIni("Controller", "PadButtonsInited", ControlsManager.ms_padButtonsInited); - cfg.write_file("re3.ini"); + + ini.write(cfg); } bool LoadINISettings() { - if (!cfg.load_file("re3.ini")) + if (!ini.read(cfg)) return false; #ifdef IMPROVED_VIDEOMODE @@ -524,7 +538,7 @@ bool LoadINISettings() #endif #ifdef CUSTOM_FRONTEND_OPTIONS - bool migrate = cfg.category_size("FrontendOptions") != 0; + bool migrate = cfg.get("FrontendOptions").size() != 0; for (int i = 0; i < MENUPAGES; i++) { for (int j = 0; j < NUM_MENUROWS; j++) { CMenuScreenCustom::CMenuEntry &option = aScreens[i].m_aEntries[j]; @@ -533,22 +547,29 @@ bool LoadINISettings() // CFO check if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) { - // CFO only supports saving uint8 right now - // Migrate from old .ini to new .ini - if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, option.m_CFO->value)) - cfg.remove("FrontendOptions", option.m_CFO->save); + // Old values can only be int8, new ones can contain float if it is slider + if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, (int8*)option.m_CFO->value)) + cfg["FrontendOptions"].remove(option.m_CFO->save); + else if (option.m_Action == MENUACTION_CFO_SLIDER) + ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (float*)option.m_CFO->value); else - ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, option.m_CFO->value); + ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (int8*)option.m_CFO->value); if (option.m_Action == MENUACTION_CFO_SELECT) { - option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *option.m_CFO->value; + option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value; } } } } #endif + // Fetched in above block, but needs evaluation +#ifdef PED_CAR_DENSITY_SLIDERS + CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * CIniFile::PedNumberMultiplier; + CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CIniFile::CarNumberMultiplier; +#endif + return true; } @@ -623,14 +644,16 @@ void SaveINISettings() break; if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) { - // Beware: CFO only supports saving uint8 right now - StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *option.m_CFO->value); + if (option.m_Action == MENUACTION_CFO_SLIDER) + StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(float*)option.m_CFO->value); + else + StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(int8*)option.m_CFO->value); } } } #endif - cfg.write_file("re3.ini"); + ini.write(cfg); } #endif 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/extras/frontendoption.h b/src/extras/frontendoption.h index 8b64335a..a571170f 100644 --- a/src/extras/frontendoption.h +++ b/src/extras/frontendoption.h @@ -40,6 +40,8 @@ typedef void (*ReturnPrevPageFunc)(); typedef void (*ChangeFunc)(int8 before, int8 after); // called after updating the value. // only called on enter if onlyApplyOnEnter set, otherwise called on every value change +typedef void (*ChangeFuncFloat)(float before, float after); // called after updating the value. + // for dynamic options typedef wchar* (*DrawFunc)(bool* disabled, bool userHovering); // you must return a pointer for right text. // you can also set *disabled if you want to gray it out. diff --git a/src/extras/ini.h b/src/extras/ini.h new file mode 100644 index 00000000..44dd3d57 --- /dev/null +++ b/src/extras/ini.h @@ -0,0 +1,761 @@ +/* + * The MIT License (MIT) + * Copyright (c) 2018 Danijel Durakovic + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/////////////////////////////////////////////////////////////////////////////// +// +// /mINI/ v0.9.10 +// An INI file reader and writer for the modern age. +// +/////////////////////////////////////////////////////////////////////////////// +// +// A tiny utility library for manipulating INI files with a straightforward +// API and a minimal footprint. It conforms to the (somewhat) standard INI +// format - sections and keys are case insensitive and all leading and +// trailing whitespace is ignored. Comments are lines that begin with a +// semicolon. Trailing comments are allowed on section lines. +// +// Files are read on demand, upon which data is kept in memory and the file +// is closed. This utility supports lazy writing, which only writes changes +// and updates to a file and preserves custom formatting and comments. A lazy +// write invoked by a write() call will read the output file, find what +// changes have been made and update the file accordingly. If you only need to +// generate files, use generate() instead. Section and key order is preserved +// on read, write and insert. +// +/////////////////////////////////////////////////////////////////////////////// +// +// /* BASIC USAGE EXAMPLE: */ +// +// /* read from file */ +// mINI::INIFile file("myfile.ini"); +// mINI::INIStructure ini; +// file.read(ini); +// +// /* read value; gets a reference to actual value in the structure. +// if key or section don't exist, a new empty value will be created */ +// std::string& value = ini["section"]["key"]; +// +// /* read value safely; gets a copy of value in the structure. +// does not alter the structure */ +// std::string value = ini.get("section").get("key"); +// +// /* set or update values */ +// ini["section"]["key"] = "value"; +// +// /* set multiple values */ +// ini["section2"].set({ +// {"key1", "value1"}, +// {"key2", "value2"} +// }); +// +// /* write updates back to file, preserving comments and formatting */ +// file.write(ini); +// +// /* or generate a file (overwrites the original) */ +// file.generate(ini); +// +/////////////////////////////////////////////////////////////////////////////// +// +// Long live the INI file!!! +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MINI_INI_H_ +#define MINI_INI_H_ + +#include <string> +#include <sstream> +#include <algorithm> +#include <utility> +#include <unordered_map> +#include <vector> +#include <memory> +#include <fstream> +#include <sys/stat.h> +#include <cctype> + +namespace mINI +{ + namespace INIStringUtil + { + const char* const whitespaceDelimiters = " \t\n\r\f\v"; + inline void trim(std::string& str) + { + str.erase(str.find_last_not_of(whitespaceDelimiters) + 1); + str.erase(0, str.find_first_not_of(whitespaceDelimiters)); + } +#ifndef MINI_CASE_SENSITIVE + inline void toLower(std::string& str) + { + std::transform(str.begin(), str.end(), str.begin(), [](const char c) { + return static_cast<const char>(std::tolower(c)); + }); + } +#endif + inline void replace(std::string& str, std::string const& a, std::string const& b) + { + if (!a.empty()) + { + std::size_t pos = 0; + while ((pos = str.find(a, pos)) != std::string::npos) + { + str.replace(pos, a.size(), b); + pos += b.size(); + } + } + } +#ifdef _WIN32 + const char* const endl = "\r\n"; +#else + const char* const endl = "\n"; +#endif + }; + + template<typename T> + class INIMap + { + private: + using T_DataIndexMap = std::unordered_map<std::string, std::size_t>; + using T_DataItem = std::pair<std::string, T>; + using T_DataContainer = std::vector<T_DataItem>; + using T_MultiArgs = typename std::vector<std::pair<std::string, T>>; + + T_DataIndexMap dataIndexMap; + T_DataContainer data; + + inline std::size_t setEmpty(std::string& key) + { + std::size_t index = data.size(); + dataIndexMap[key] = index; + data.emplace_back(key, T()); + return index; + } + + public: + using const_iterator = typename T_DataContainer::const_iterator; + + INIMap() { } + + INIMap(INIMap const& other) + { + std::size_t data_size = other.data.size(); + for (std::size_t i = 0; i < data_size; ++i) + { + auto const& key = other.data[i].first; + auto const& obj = other.data[i].second; + data.emplace_back(key, obj); + } + dataIndexMap = T_DataIndexMap(other.dataIndexMap); + } + + T& operator[](std::string key) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + bool hasIt = (it != dataIndexMap.end()); + std::size_t index = (hasIt) ? it->second : setEmpty(key); + return data[index].second; + } + T get(std::string key) const + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it == dataIndexMap.end()) + { + return T(); + } + return T(data[it->second].second); + } + bool has(std::string key) const + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + return (dataIndexMap.count(key) == 1); + } + void set(std::string key, T obj) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it != dataIndexMap.end()) + { + data[it->second].second = obj; + } + else + { + dataIndexMap[key] = data.size(); + data.emplace_back(key, obj); + } + } + void set(T_MultiArgs const& multiArgs) + { + for (auto const& it : multiArgs) + { + auto const& key = it.first; + auto const& obj = it.second; + set(key, obj); + } + } + bool remove(std::string key) + { + INIStringUtil::trim(key); +#ifndef MINI_CASE_SENSITIVE + INIStringUtil::toLower(key); +#endif + auto it = dataIndexMap.find(key); + if (it != dataIndexMap.end()) + { + std::size_t index = it->second; + data.erase(data.begin() + index); + dataIndexMap.erase(it); + for (auto& it2 : dataIndexMap) + { + auto& vi = it2.second; + if (vi > index) + { + vi--; + } + } + return true; + } + return false; + } + void clear() + { + data.clear(); + dataIndexMap.clear(); + } + std::size_t size() const + { + return data.size(); + } + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + }; + + using INIStructure = INIMap<INIMap<std::string>>; + + namespace INIParser + { + using T_ParseValues = std::pair<std::string, std::string>; + + enum class PDataType : char + { + PDATA_NONE, + PDATA_COMMENT, + PDATA_SECTION, + PDATA_KEYVALUE, + PDATA_UNKNOWN + }; + + inline PDataType parseLine(std::string line, T_ParseValues& parseData) + { + parseData.first.clear(); + parseData.second.clear(); + INIStringUtil::trim(line); + if (line.empty()) + { + return PDataType::PDATA_NONE; + } + char firstCharacter = line[0]; + if (firstCharacter == ';') + { + return PDataType::PDATA_COMMENT; + } + if (firstCharacter == '[') + { + auto commentAt = line.find_first_of(';'); + if (commentAt != std::string::npos) + { + line = line.substr(0, commentAt); + } + auto closingBracketAt = line.find_last_of(']'); + if (closingBracketAt != std::string::npos) + { + auto section = line.substr(1, closingBracketAt - 1); + INIStringUtil::trim(section); + parseData.first = section; + return PDataType::PDATA_SECTION; + } + } + auto lineNorm = line; + INIStringUtil::replace(lineNorm, "\\=", " "); + auto equalsAt = lineNorm.find_first_of('='); + if (equalsAt != std::string::npos) + { + auto key = line.substr(0, equalsAt); + INIStringUtil::trim(key); + INIStringUtil::replace(key, "\\=", "="); + auto value = line.substr(equalsAt + 1); + INIStringUtil::trim(value); + parseData.first = key; + parseData.second = value; + return PDataType::PDATA_KEYVALUE; + } + return PDataType::PDATA_UNKNOWN; + } + }; + + class INIReader + { + public: + using T_LineData = std::vector<std::string>; + using T_LineDataPtr = std::shared_ptr<T_LineData>; + + private: + std::ifstream fileReadStream; + T_LineDataPtr lineData; + + T_LineData readFile() + { + std::string fileContents; + fileReadStream.seekg(0, std::ios::end); + fileContents.resize(fileReadStream.tellg()); + fileReadStream.seekg(0, std::ios::beg); + std::size_t fileSize = fileContents.size(); + fileReadStream.read(&fileContents[0], fileSize); + fileReadStream.close(); + T_LineData output; + if (fileSize == 0) + { + return output; + } + std::string buffer; + buffer.reserve(50); + for (std::size_t i = 0; i < fileSize; ++i) + { + char& c = fileContents[i]; + if (c == '\n') + { + output.emplace_back(buffer); + buffer.clear(); + continue; + } + if (c != '\0' && c != '\r') + { + buffer += c; + } + } + output.emplace_back(buffer); + return output; + } + + public: + INIReader(std::string const& filename, bool keepLineData = false) + { + fileReadStream.open(filename, std::ios::in | std::ios::binary); + if (keepLineData) + { + lineData = std::make_shared<T_LineData>(); + } + } + ~INIReader() { } + + bool operator>>(INIStructure& data) + { + if (!fileReadStream.is_open()) + { + return false; + } + T_LineData fileLines = readFile(); + std::string section; + bool inSection = false; + INIParser::T_ParseValues parseData; + for (auto const& line : fileLines) + { + auto parseResult = INIParser::parseLine(line, parseData); + if (parseResult == INIParser::PDataType::PDATA_SECTION) + { + inSection = true; + data[section = parseData.first]; + } + else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE) + { + auto const& key = parseData.first; + auto const& value = parseData.second; + data[section][key] = value; + } + if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN) + { + if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection) + { + continue; + } + lineData->emplace_back(line); + } + } + return true; + } + T_LineDataPtr getLines() + { + return lineData; + } + }; + + class INIGenerator + { + private: + std::ofstream fileWriteStream; + + public: + bool prettyPrint = false; + + INIGenerator(std::string const& filename) + { + fileWriteStream.open(filename, std::ios::out | std::ios::binary); + } + ~INIGenerator() { } + + bool operator<<(INIStructure const& data) + { + if (!fileWriteStream.is_open()) + { + return false; + } + if (!data.size()) + { + return true; + } + auto it = data.begin(); + for (;;) + { + auto const& section = it->first; + auto const& collection = it->second; + fileWriteStream + << "[" + << section + << "]"; + if (collection.size()) + { + fileWriteStream << INIStringUtil::endl; + auto it2 = collection.begin(); + for (;;) + { + auto key = it2->first; + INIStringUtil::replace(key, "=", "\\="); + auto value = it2->second; + INIStringUtil::trim(value); + fileWriteStream + << key + << ((prettyPrint) ? " = " : "=") + << value; + if (++it2 == collection.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + } + } + if (++it == data.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + if (prettyPrint) + { + fileWriteStream << INIStringUtil::endl; + } + } + return true; + } + }; + + class INIWriter + { + private: + using T_LineData = std::vector<std::string>; + using T_LineDataPtr = std::shared_ptr<T_LineData>; + + std::string filename; + + T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original) + { + T_LineData output; + INIParser::T_ParseValues parseData; + std::string sectionCurrent; + bool parsingSection = false; + bool continueToNextSection = false; + bool discardNextEmpty = false; + bool writeNewKeys = false; + std::size_t lastKeyLine = 0; + for (auto line = lineData->begin(); line != lineData->end(); ++line) + { + if (!writeNewKeys) + { + auto parseResult = INIParser::parseLine(*line, parseData); + if (parseResult == INIParser::PDataType::PDATA_SECTION) + { + if (parsingSection) + { + writeNewKeys = true; + parsingSection = false; + --line; + continue; + } + sectionCurrent = parseData.first; + if (data.has(sectionCurrent)) + { + parsingSection = true; + continueToNextSection = false; + discardNextEmpty = false; + output.emplace_back(*line); + lastKeyLine = output.size(); + } + else + { + continueToNextSection = true; + discardNextEmpty = true; + continue; + } + } + else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE) + { + if (continueToNextSection) + { + continue; + } + if (data.has(sectionCurrent)) + { + auto& collection = data[sectionCurrent]; + auto const& key = parseData.first; + auto const& value = parseData.second; + if (collection.has(key)) + { + auto outputValue = collection[key]; + if (value == outputValue) + { + output.emplace_back(*line); + } + else + { + INIStringUtil::trim(outputValue); + auto lineNorm = *line; + INIStringUtil::replace(lineNorm, "\\=", " "); + auto equalsAt = lineNorm.find_first_of('='); + auto valueAt = lineNorm.find_first_not_of( + INIStringUtil::whitespaceDelimiters, + equalsAt + 1 + ); + std::string outputLine = line->substr(0, valueAt); + if (prettyPrint && equalsAt + 1 == valueAt) + { + outputLine += " "; + } + outputLine += outputValue; + output.emplace_back(outputLine); + } + lastKeyLine = output.size(); + } + } + } + else + { + if (discardNextEmpty && line->empty()) + { + discardNextEmpty = false; + } + else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN) + { + output.emplace_back(*line); + } + } + } + if (writeNewKeys || std::next(line) == lineData->end()) + { + T_LineData linesToAdd; + if (data.has(sectionCurrent) && original.has(sectionCurrent)) + { + auto const& collection = data[sectionCurrent]; + auto const& collectionOriginal = original[sectionCurrent]; + for (auto const& it : collection) + { + auto key = it.first; + if (collectionOriginal.has(key)) + { + continue; + } + auto value = it.second; + INIStringUtil::replace(key, "=", "\\="); + INIStringUtil::trim(value); + linesToAdd.emplace_back( + key + ((prettyPrint) ? " = " : "=") + value + ); + } + } + if (!linesToAdd.empty()) + { + output.insert( + output.begin() + lastKeyLine, + linesToAdd.begin(), + linesToAdd.end() + ); + } + if (writeNewKeys) + { + writeNewKeys = false; + --line; + } + } + } + for (auto const& it : data) + { + auto const& section = it.first; + if (original.has(section)) + { + continue; + } + if (prettyPrint && output.size() > 0 && !output.back().empty()) + { + output.emplace_back(); + } + output.emplace_back("[" + section + "]"); + auto const& collection = it.second; + for (auto const& it2 : collection) + { + auto key = it2.first; + auto value = it2.second; + INIStringUtil::replace(key, "=", "\\="); + INIStringUtil::trim(value); + output.emplace_back( + key + ((prettyPrint) ? " = " : "=") + value + ); + } + } + return output; + } + + public: + bool prettyPrint = false; + + INIWriter(std::string const& filename) + : filename(filename) + { + } + ~INIWriter() { } + + bool operator<<(INIStructure& data) + { + struct stat buf; + bool fileExists = (stat(filename.c_str(), &buf) == 0); + if (!fileExists) + { + INIGenerator generator(filename); + generator.prettyPrint = prettyPrint; + return generator << data; + } + INIStructure originalData; + T_LineDataPtr lineData; + bool readSuccess = false; + { + INIReader reader(filename, true); + if ((readSuccess = reader >> originalData)) + { + lineData = reader.getLines(); + } + } + if (!readSuccess) + { + return false; + } + T_LineData output = getLazyOutput(lineData, data, originalData); + std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary); + if (fileWriteStream.is_open()) + { + if (output.size()) + { + auto line = output.begin(); + for (;;) + { + fileWriteStream << *line; + if (++line == output.end()) + { + break; + } + fileWriteStream << INIStringUtil::endl; + } + } + return true; + } + return false; + } + }; + + class INIFile + { + private: + std::string filename; + + public: + INIFile(std::string const& filename) + : filename(filename) + { } + + ~INIFile() { } + + bool read(INIStructure& data) const + { + if (data.size()) + { + data.clear(); + } + if (filename.empty()) + { + return false; + } + INIReader reader(filename); + return reader >> data; + } + bool generate(INIStructure const& data, bool pretty = false) const + { + if (filename.empty()) + { + return false; + } + INIGenerator generator(filename); + generator.prettyPrint = pretty; + return generator << data; + } + bool write(INIStructure& data, bool pretty = false) const + { + if (filename.empty()) + { + return false; + } + INIWriter writer(filename); + writer.prettyPrint = pretty; + return writer << data; + } + }; +} + +#endif // MINI_INI_H_ diff --git a/src/extras/ini_parser.hpp b/src/extras/ini_parser.hpp deleted file mode 100644 index 7bea024c..00000000 --- a/src/extras/ini_parser.hpp +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) 2013-2015 Denilson das Mercês Amorim <dma_2012@hotmail.com> - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - * - */ -#ifndef LINB_INI_PARSER_HPP -#define LINB_INI_PARSER_HPP - -/* - * STL-like INI Container - */ - -#include <string> // for std::string -#include <map> // for std::map -#include <cstdio> // for std::FILE -#include <algorithm> // for std::find_if -#include <functional> // for std::function - -namespace linb -{ - template< - class CharT = char, /* Not compatible with other type here, since we're using C streams */ - class StringType = std::basic_string<CharT>, - class KeyContainer = std::map<StringType, StringType>, - class SectionContainer = std::map<StringType, KeyContainer> - > class basic_ini - { - public: - typedef CharT char_type; - typedef StringType string_type; - typedef KeyContainer key_container; - typedef SectionContainer section_container; - - // Typedef container values types - typedef typename section_container::value_type value_type; - typedef typename section_container::key_type key_type; - typedef typename section_container::mapped_type mapped_type; - - // Typedef common types - typedef typename section_container::size_type size_type; - typedef typename section_container::difference_type difference_type; - - // Typedef iterators - typedef typename section_container::iterator iterator; - typedef typename section_container::const_iterator const_iterator; - typedef typename section_container::reverse_iterator reverse_iterator; - typedef typename section_container::const_reverse_iterator const_reverse_iterator; - - // typedef References and pointers - typedef typename section_container::reference reference; - typedef typename section_container::const_reference const_reference; - typedef typename section_container::pointer pointer; - typedef typename section_container::const_pointer const_pointer; - - private: - section_container data; - - public: - - basic_ini() - { } - - basic_ini(const char_type* filename) - { this->read_file(filename); } - - /* Iterator methods */ - iterator begin() - { return data.begin(); } - const_iterator begin() const - { return data.begin(); } - iterator end() - { return data.end(); } - const_iterator end() const - { return data.end(); } - const_iterator cbegin() const - { return data.cbegin(); } - const_iterator cend() const - { return data.cend(); } - - /* Reverse iterator methods */ - reverse_iterator rbegin() - { return data.rbegin(); } - const_reverse_iterator rbegin() const - { return data.rbegin(); } - reverse_iterator rend() - { return data.rend(); } - const_reverse_iterator rend() const - { return data.rend(); } - const_reverse_iterator crbegin() const - { return data.crbegin(); } - const_reverse_iterator crend() const - { return data.crend(); } - - /* Acessing index methods */ - mapped_type& operator[](const string_type& sect) - { return data[sect]; } - mapped_type& operator[](string_type&& sect) - { return data[std::forward<string_type>(sect)]; } - mapped_type& at( const string_type& sect) - { return data.at(sect); } - const mapped_type& at(const string_type& sect) const - { return data.at(sect); } - - /* Capacity information */ - bool empty() const - { return data.empty(); } - size_type size() const - { return data.size(); } - size_type max_size() const - { return data.max_size(); } - - /* Modifiers */ - void clear() - { return data.clear(); } - - /* Lookup */ - size_type count(const string_type& sect) - { return data.count(sect); } - iterator find(const string_type& sect) - { return data.find(sect); } - - /* Gets a value from the specified section & key, default_value is returned if the sect & key doesn't exist */ - string_type get(const string_type& sect, const key_type& key, const string_type& default_value) - { - auto it = this->find(sect); - if(it != this->end()) - { - auto itv = it->second.find(key); - if(itv != it->second.end()) - return itv->second; - } - return default_value; - } - - /* Sets the value of a value in the ini */ - void set(const string_type& sect, const key_type& key, const string_type& value) - { - (*this)[sect][key] = value; // no emplace since overwrite! - } - - /* Too lazy to continue this container... If you need more methods, just add it */ - - // re3 - void remove(const string_type& sect, const key_type& key) - { - auto it = this->find(sect); - if(it != this->end()) - { - it->second.erase(key); - } - } - - int category_size(const string_type& sect) - { - auto it = this->find(sect); - if(it != this->end()) - { - return it->second.size(); - } - return 0; - } - -#if 1 - bool read_file(const char_type* filename) - { - /* Using C stream in a STL-like container, funny? - */ - if(FILE* f = fopen(filename, "r")) - { - key_container* keys = nullptr; - char_type buf[2048]; - string_type line; - string_type key; - string_type value; - string_type null_string; - size_type pos; - - // Trims an string - auto trim = [](string_type& s, bool trimLeft, bool trimRight) -> string_type& - { - if(s.size()) - { - // Ignore UTF-8 BOM - while(s.size() >= 3 && s[0] == (char)(0xEF) && s[1] == (char)(0xBB) && s[2] == (char)(0xBF)) - s.erase(s.begin(), s.begin() + 3); - - if(trimLeft) - s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::function<int(int)>(::isspace)))); - if(trimRight) - s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::function<int(int)>(::isspace))).base(), s.end()); - } - return s; - }; - - // Start parsing - while(fgets(buf, sizeof(buf), f)) - { - // What a thing, reading into a char buffer and then putting in the string... - line = buf; - - // Find comment and remove anything after it from the line - if((pos = line.find_first_of(';')) != line.npos) - line.erase(pos); - - // Trim the string, and if it gets empty, skip this line - if(trim(line, true, true).empty()) - continue; - - // Find section name - if(line.front() == '[' && line.back() == ']') - { - pos = line.length() - 1; //line.find_first_of(']'); - if(pos != line.npos) - { - trim(key.assign(line, 1, pos-1), true, true); - keys = &data[std::move(key)]; // Create section - } - else - keys = nullptr; - } - else - { - // Find key and value positions - pos = line.find_first_of('='); - if(pos == line.npos) - { - // There's only the key - key = line; // No need for trim, line is already trimmed - value.clear(); - } - else - { - // There's the key and the value - trim(key.assign(line, 0, pos), false, true); // trim the right - trim(value.assign(line, pos + 1, line.npos), true, false); // trim the left - } - - // Put the key/value into the current keys object, or into the section "" if no section has been found - #if __cplusplus >= 201103L || _MSC_VER >= 1800 - (keys ? *keys : data[null_string]).emplace(std::move(key), std::move(value)); - #else - (keys ? *keys : data[null_string])[key] = value; - key.clear(); value.clear(); - #endif - } - } - - fclose(f); - return true; - } - return false; - } - - /* - * Dumps the content of this container into an ini file - */ - bool write_file(const char_type* filename) - { - if(FILE* f = fopen(filename, "w")) - { - bool first = true; - for(auto& sec : this->data) - { - fprintf(f, first? "[%s]\n" : "\n[%s]\n", sec.first.c_str()); - first = false; - for(auto& kv : sec.second) - { - if(kv.second.empty()) - fprintf(f, "%s\n", kv.first.c_str()); - else - fprintf(f, "%s = %s\n", kv.first.c_str(), kv.second.c_str()); - } - } - fclose(f); - return true; - } - return false; - } - - - /* - */ - bool load_file(const char_type* filename) - { - return read_file(filename); - } - - bool load_file(const StringType& filename) - { - return load_file(filename.c_str()); - } - - bool write_file(const StringType& filename) - { - return write_file(filename.c_str()); - } -#endif - - - - }; - - - /* Use default basic_ini - * - * Limitations: - * * Not unicode aware - * * Case sensitive - * * Sections must have unique keys - */ - typedef basic_ini<> ini; -} - -#endif - 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 90aebf89..9be58d11 100644 --- a/src/peds/Ped.cpp +++ b/src/peds/Ped.cpp @@ -8497,21 +8497,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/peds/Population.cpp b/src/peds/Population.cpp index ace6d37c..1d2a5798 100644 --- a/src/peds/Population.cpp +++ b/src/peds/Population.cpp @@ -55,7 +55,7 @@ bool CPopulation::ms_bGivePedsWeapons; int32 CPopulation::m_AllRandomPedsThisType = -1; float CPopulation::PedDensityMultiplier = 1.0f; uint32 CPopulation::ms_nTotalMissionPeds; -int32 CPopulation::MaxNumberOfPedsInUse = 25; +int32 CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS; uint32 CPopulation::ms_nNumCivMale; uint32 CPopulation::ms_nNumCivFemale; uint32 CPopulation::ms_nNumCop; @@ -1122,12 +1122,6 @@ CPopulation::ManagePopulation(void) } float dist = (ped->GetPosition() - playerPos).Magnitude2D(); -#ifdef SQUEEZE_PERFORMANCE - if (dist > 50.f) - ped->bUsesCollision = false; - else - ped->bUsesCollision = true; -#endif bool pedIsFarAway = false; if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist 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 diff --git a/utils/gxt/american.txt b/utils/gxt/american.txt index cf71c958..d8f79f05 100644 --- a/utils/gxt/american.txt +++ b/utils/gxt/american.txt @@ -8094,6 +8094,12 @@ VIBRATION [FET_AGS] GAMEPAD SETTINGS +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + { end of file } [DUMMY] diff --git a/utils/gxt/french.txt b/utils/gxt/french.txt index 09a1f5c2..bd4acf96 100644 --- a/utils/gxt/french.txt +++ b/utils/gxt/french.txt @@ -8362,6 +8362,12 @@ VIBRATIONS [FET_AGS] GAMEPAD SETTINGS +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + { end of file } [DUMMY] diff --git a/utils/gxt/german.txt b/utils/gxt/german.txt index 4f7ee052..5f5c53c4 100644 --- a/utils/gxt/german.txt +++ b/utils/gxt/german.txt @@ -8179,6 +8179,12 @@ Vibration : [FET_AGS] KONTROLLEREINSTELLUNGEN +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + [DUMMY] THIS LABEL NEEDS TO BE HERE !!! AS THE LAST LABEL DOES NOT GET COMPILED diff --git a/utils/gxt/italian.txt b/utils/gxt/italian.txt index 54c9fee8..803b7fbf 100644 --- a/utils/gxt/italian.txt +++ b/utils/gxt/italian.txt @@ -8191,6 +8191,12 @@ VIBRAZIONE [FET_AGS] GAMEPAD SETTINGS +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + [DUMMY] THIS LABEL NEEDS TO BE HERE !!! AS THE LAST LABEL DOES NOT GET COMPILED
\ No newline at end of file diff --git a/utils/gxt/polish.txt b/utils/gxt/polish.txt index 39727554..33716291 100755 --- a/utils/gxt/polish.txt +++ b/utils/gxt/polish.txt @@ -8100,6 +8100,12 @@ WIBRACJA [FET_AGS]
GAMEPAD SETTINGS
+[FEM_PED]
+PED DENSITY
+
+[FEM_CAR]
+CAR DENSITY
+
{ end of file }
[DUMMY]
diff --git a/utils/gxt/russian.txt b/utils/gxt/russian.txt index 3a723aa2..221e59f5 100644 --- a/utils/gxt/russian.txt +++ b/utils/gxt/russian.txt @@ -8102,6 +8102,12 @@ DUALSHOCK 4 [FET_AGS] НАСТРОЙКИ ГЕЙМПАДА +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + { end of file } [DUMMY] diff --git a/utils/gxt/spanish.txt b/utils/gxt/spanish.txt index b2e418dd..5f108d3b 100644 --- a/utils/gxt/spanish.txt +++ b/utils/gxt/spanish.txt @@ -8161,6 +8161,12 @@ VIBRACIÓN [FET_AGS] AJUSTES DE MANDO +[FEM_PED] +PED DENSITY + +[FEM_CAR] +CAR DENSITY + { end of file } [DUMMY] |