summaryrefslogtreecommitdiffstats
path: root/src/control
diff options
context:
space:
mode:
Diffstat (limited to 'src/control')
-rw-r--r--src/control/Record.cpp178
-rw-r--r--src/control/Record.h36
2 files changed, 210 insertions, 4 deletions
diff --git a/src/control/Record.cpp b/src/control/Record.cpp
index 7c330311..7a3763be 100644
--- a/src/control/Record.cpp
+++ b/src/control/Record.cpp
@@ -2,12 +2,184 @@
#include "patcher.h"
#include "Record.h"
+#include "FileMgr.h"
+#include "Pad.h"
+#include "Pools.h"
+#include "Timer.h"
+
uint16 &CRecordDataForGame::RecordingState = *(uint16*)0x95CC24;
+uint8*& CRecordDataForGame::pDataBuffer = *(uint8**)0x8F1B70;
+uint8*& CRecordDataForGame::pDataBufferPointer = *(uint8**)0x8F1AB0;
+int& CRecordDataForGame::FId = *(int*)0x885BA4;
+tGameBuffer& CRecordDataForGame::pDataBufferForFrame = *(tGameBuffer*)0x72CED0;
uint8 &CRecordDataForChase::Status = *(uint8*)0x95CDCE;
-WRAPPER void CRecordDataForGame::SaveOrRetrieveDataForThisFrame(void) { EAXJMP(0x4341F0); }
-WRAPPER void CRecordDataForGame::Init(void) { EAXJMP(0x4340F0); }
+#define MEMORY_FOR_GAME_RECORD (150000)
+
+void CRecordDataForGame::Init(void)
+{
+ RecordingState = STATE_NONE;
+ if (pDataBuffer)
+ delete[] pDataBuffer;
+ pDataBufferPointer = nil;
+ pDataBuffer = nil;
+#ifndef GTA_PS2 // this stuff is not present on PS2
+ FId = CFileMgr::OpenFile("playback.dat", "r");
+ if (FId <= 0) {
+ if ((FId = CFileMgr::OpenFile("record.dat", "r")) <= 0)
+ RecordingState = STATE_NONE;
+ else {
+ CFileMgr::CloseFile(FId);
+ FId = CFileMgr::OpenFileForWriting("record.dat");
+ RecordingState = STATE_RECORD;
+ }
+ }
+ else {
+ RecordingState = STATE_PLAYBACK;
+ }
+ if (RecordingState == STATE_PLAYBACK) {
+ pDataBufferPointer = (uint8*)malloc(MEMORY_FOR_GAME_RECORD);
+ pDataBuffer = pDataBufferPointer;
+ pDataBuffer[CFileMgr::Read(FId, (char*)pDataBufferPointer, MEMORY_FOR_GAME_RECORD) + 8] = -1;
+ CFileMgr::CloseFile(FId);
+ }
+#else
+ RecordingState = STATE_NONE; // second time to make sure
+#endif
+}
+
+void CRecordDataForGame::SaveOrRetrieveDataForThisFrame(void)
+{
+ switch (RecordingState) {
+ case STATE_RECORD:
+ {
+ pDataBufferForFrame.m_fTimeStep = CTimer::GetTimeStep();
+ pDataBufferForFrame.m_nTimeInMilliseconds = CTimer::GetTimeInMilliseconds();
+ pDataBufferForFrame.m_nSizeOfPads[0] = 0;
+ pDataBufferForFrame.m_nSizeOfPads[1] = 0;
+ pDataBufferForFrame.m_nChecksum = CalcGameChecksum();
+ uint8* pController1 = PackCurrentPadValues(pDataBufferForFrame.m_ControllerBuffer, &CPad::GetPad(0)->OldState, &CPad::GetPad(0)->NewState);
+ pDataBufferForFrame.m_nSizeOfPads[0] = (pController1 - pDataBufferForFrame.m_ControllerBuffer) / 2;
+ uint8* pController2 = PackCurrentPadValues(pController1, &CPad::GetPad(1)->OldState, &CPad::GetPad(1)->NewState);
+ pDataBufferForFrame.m_nSizeOfPads[1] = (pController2 - pController1) / 2;
+ uint8* pEndPtr = pController2;
+ if ((pDataBufferForFrame.m_nSizeOfPads[0] + pDataBufferForFrame.m_nSizeOfPads[1]) & 1)
+ pEndPtr += 2;
+ CFileMgr::Write(FId, (char*)&pDataBufferForFrame, pEndPtr - (uint8*)&pDataBufferForFrame);
+ break;
+ }
+ case STATE_PLAYBACK:
+ if (pDataBufferPointer[8] == -1)
+ CPad::GetPad(0)->NewState.Clear();
+ else {
+ tGameBuffer* pData = (tGameBuffer*)pDataBufferPointer;
+ CTimer::SetTimeInMilliseconds(pData->m_nTimeInMilliseconds);
+ CTimer::SetTimeStep(pData->m_fTimeStep);
+ uint8 size1 = pData->m_nSizeOfPads[0];
+ uint8 size2 = pData->m_nSizeOfPads[1];
+ pDataBufferPointer = (uint8*)&pData->m_ControllerBuffer;
+ pDataBufferPointer = UnPackCurrentPadValues(pDataBufferPointer, size1, &CPad::GetPad(0)->NewState);
+ pDataBufferPointer = UnPackCurrentPadValues(pDataBufferPointer, size2, &CPad::GetPad(1)->NewState);
+ if ((size1 + size2) & 1)
+ pDataBufferPointer += 2;
+ if (pData->m_nChecksum != CalcGameChecksum())
+ printf("Playback out of sync\n");
+ }
+ }
+}
+
+#define PROCESS_BUTTON_STATE_STORE(buf, os, ns, field, id) \
+ do { \
+ if (os->field != os->field){ \
+ *buf++ = id; \
+ *buf++ = ns->field; \
+ } \
+ } while (0);
+
+uint8* CRecordDataForGame::PackCurrentPadValues(uint8* buf, CControllerState* os, CControllerState* ns)
+{
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftStickX, 0);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftStickY, 1);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftShoulder1, 2);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightStickY, 3);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftShoulder1, 4);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftShoulder2, 5);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightShoulder1, 6);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightShoulder2, 7);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, DPadUp, 8);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, DPadDown, 9);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, DPadLeft, 10);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, DPadRight, 11);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Start, 12);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Select, 13);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Square, 14);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Triangle, 15);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Cross, 16);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, Circle, 17);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, LeftShock, 18);
+ PROCESS_BUTTON_STATE_STORE(buf, os, ns, RightShock, 19);
+ return buf;
+}
+#undef PROCESS_BUTTON_STATE_STORE
+
+#define PROCESS_BUTTON_STATE_RESTORE(buf, state, field, id) case id: state->field = *buf++; break;
+
+uint8* CRecordDataForGame::UnPackCurrentPadValues(uint8* buf, uint8 total, CControllerState* state)
+{
+ for (uint8 i = 0; i < total; i++) {
+ switch (*buf++) {
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftStickX, 0);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftStickY, 1);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftShoulder1, 2);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightStickY, 3);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftShoulder1, 4);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftShoulder2, 5);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightShoulder1, 6);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightShoulder2, 7);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, DPadUp, 8);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, DPadDown, 9);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, DPadLeft, 10);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, DPadRight, 11);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Start, 12);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Select, 13);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Square, 14);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Triangle, 15);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Cross, 16);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, Circle, 17);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, LeftShock, 18);
+ PROCESS_BUTTON_STATE_RESTORE(buf, state, RightShock, 19);
+ }
+ }
+ return buf;
+}
+
+#undef PROCESS_BUTTON_STATE_RESTORE
+
+uint16 CRecordDataForGame::CalcGameChecksum(void)
+{
+ uint32 checksum = 0;
+ int i = CPools::GetPedPool()->GetSize();
+ while (i--) {
+ CPed* pPed = CPools::GetPedPool()->GetSlot(i);
+ if (!pPed)
+ continue;
+ checksum ^= pPed->GetModelIndex() ^ *(uint32*)&pPed->GetPosition().z ^ *(uint32*)&pPed->GetPosition().y ^ *(uint32*)&pPed->GetPosition().x;
+ }
+ i = CPools::GetVehiclePool()->GetSize();
+ while (i--) {
+ CVehicle* pVehicle = CPools::GetVehiclePool()->GetSlot(i);
+ if (!pVehicle)
+ continue;
+ checksum ^= pVehicle->GetModelIndex() ^ *(uint32*)&pVehicle->GetPosition().z ^ *(uint32*)&pVehicle->GetPosition().y ^ *(uint32*)&pVehicle->GetPosition().x;
+ }
+ return checksum ^ checksum >> 16;
+}
+
+WRAPPER void CRecordDataForChase::Init(void)
+{
+ EAXJMP(0x434780);
+}
WRAPPER void CRecordDataForChase::SaveOrRetrieveDataForThisFrame(void) { EAXJMP(0x4347F0); }
WRAPPER void CRecordDataForChase::ProcessControlCars(void) { EAXJMP(0x435540); }
@@ -16,4 +188,4 @@ WRAPPER void CRecordDataForChase::StartChaseScene(float) { EAXJMP(0x435690); }
WRAPPER void CRecordDataForChase::CleanUpChaseScene() { EAXJMP(0x4357C0); }
WRAPPER void CRecordDataForChase::RemoveCarFromChase(int32) { EAXJMP(0x435BC0); }
WRAPPER CVehicle* CRecordDataForChase::TurnChaseCarIntoScriptCar(int32) { EAXJMP(0x435C00); }
-WRAPPER void CRecordDataForChase::Init(void) { EAXJMP(0x434780); }
+
diff --git a/src/control/Record.h b/src/control/Record.h
index e52a623e..3cb09318 100644
--- a/src/control/Record.h
+++ b/src/control/Record.h
@@ -1,6 +1,8 @@
#pragma once
+class CAutomobile;
class CVehicle;
+class CControllerState;
enum {
RECORDSTATE_0,
@@ -10,8 +12,15 @@ enum {
class CRecordDataForChase
{
+ enum {
+ NUM_CHASE_CARS = 20
+ };
public:
static uint8 &Status;
+ static int &PositionChanges;
+ static uint8 &CurrentCar;
+ static CAutomobile*(&pChaseCars)[NUM_CHASE_CARS];
+ static uint32 &AnimStartTime;
static void SaveOrRetrieveDataForThisFrame(void);
static void ProcessControlCars(void);
@@ -23,12 +32,37 @@ public:
static void Init(void);
};
+struct tGameBuffer
+{
+ float m_fTimeStep;
+ uint32 m_nTimeInMilliseconds;
+ uint8 m_nSizeOfPads[2];
+ uint16 m_nChecksum;
+ uint8 m_ControllerBuffer[116];
+};
class CRecordDataForGame
{
+ enum {
+ STATE_NONE = 0,
+ STATE_RECORD = 1,
+ STATE_PLAYBACK = 2,
+ };
+ static uint16& RecordingState;
+ static uint8* &pDataBuffer;
+ static uint8* &pDataBufferPointer;
+ static int &FId;
+ static tGameBuffer &pDataBufferForFrame;
+
public:
- static uint16 &RecordingState;
+ static bool IsRecording() { return RecordingState == STATE_RECORD; }
+ static bool IsPlayingBack() { return RecordingState == STATE_PLAYBACK; }
static void SaveOrRetrieveDataForThisFrame(void);
static void Init(void);
+
+private:
+ static uint16 CalcGameChecksum(void);
+ static uint8* PackCurrentPadValues(uint8*, CControllerState*, CControllerState*);
+ static uint8* UnPackCurrentPadValues(uint8*, uint8, CControllerState*);
};