2019-05-30 21:24:47 +02:00
|
|
|
#include "common.h"
|
|
|
|
#include "patcher.h"
|
2019-06-23 12:58:14 +02:00
|
|
|
#include "AnimBlendAssociation.h"
|
2019-06-22 20:16:29 +02:00
|
|
|
#include "BulletTraces.h"
|
|
|
|
#include "Clock.h"
|
|
|
|
#include "Draw.h"
|
|
|
|
#include "math/Matrix.h"
|
|
|
|
#include "ModelIndices.h"
|
2019-05-30 21:24:47 +02:00
|
|
|
#include "Replay.h"
|
2019-06-23 12:58:14 +02:00
|
|
|
#include "RpAnimBlend.h"
|
2019-06-22 20:16:29 +02:00
|
|
|
#include "Pad.h"
|
|
|
|
#include "Pools.h"
|
|
|
|
#include "CutsceneMgr.h"
|
|
|
|
#include "Timer.h"
|
|
|
|
#include "Weather.h"
|
2019-05-30 21:24:47 +02:00
|
|
|
|
2019-06-07 22:31:03 +02:00
|
|
|
uint8 &CReplay::Mode = *(uint8*)0x95CD5B;
|
2019-06-22 20:16:29 +02:00
|
|
|
CAddressInReplayBuffer &CReplay::Record = *(CAddressInReplayBuffer*)0x942F7C;
|
|
|
|
CAddressInReplayBuffer &CReplay::Playback = *(CAddressInReplayBuffer*)0x8F5F48;
|
|
|
|
uint8 *&CReplay::pBuf0 = *(uint8**)0x8E2C64;
|
|
|
|
CAutomobile *&CReplay::pBuf1 = *(CAutomobile**)0x8E2C68;
|
|
|
|
uint8 *&CReplay::pBuf2 = *(uint8**)0x8E2C6C;
|
|
|
|
CPlayerPed *&CReplay::pBuf3 = *(CPlayerPed**)0x8E2C70;
|
|
|
|
uint8 *&CReplay::pBuf4 = *(uint8**)0x8E2C74;
|
|
|
|
CCutsceneHead *&CReplay::pBuf5 = *(CCutsceneHead**)0x8E2C78;
|
|
|
|
uint8 *&CReplay::pBuf6 = *(uint8**)0x8E2C80;
|
|
|
|
CPtrNode *&CReplay::pBuf7 = *(CPtrNode**)0x8E2C84;
|
|
|
|
uint8 *&CReplay::pBuf8 = *(uint8**)0x8E2C54;
|
|
|
|
CEntryInfoNode *&CReplay::pBuf9 = *(CEntryInfoNode**)0x8E2C58;
|
|
|
|
uint8 *&CReplay::pBuf10 = *(uint8**)0x8F2C28;
|
|
|
|
CDummyPed *&CReplay::pBuf11 = *(CDummyPed**)0x8F2C2C;
|
|
|
|
CBlip *&CReplay::pRadarBlips = *(CBlip**)0x8F29F8;
|
|
|
|
CCamera *&CReplay::pStoredCam = *(CCamera**)0x8F2C34;
|
|
|
|
CSector *&CReplay::pWorld1 = *(CSector**)0x8E29C4;
|
|
|
|
CReference *&CReplay::pEmptyReferences = *(CReference**)0x8F256C;
|
|
|
|
CStoredDetailedAnimationState *&CReplay::pPedAnims = *(CStoredDetailedAnimationState**)0x8F6260;
|
|
|
|
CPickup *&CReplay::pPickups = *(CPickup**)0x8F1A48;
|
|
|
|
CReference *&CReplay::pReferences = *(CReference**)0x880FAC;
|
2019-06-22 21:23:26 +02:00
|
|
|
uint8(&CReplay::BufferStatus)[8] = *(uint8(*)[8])*(uintptr*)0x8804D8;
|
|
|
|
uint8(&CReplay::Buffers)[8][100000] = *(uint8(*)[8][100000])*(uintptr*)0x779958;
|
2019-06-17 00:16:38 +02:00
|
|
|
bool &CReplay::bPlayingBackFromFile = *(bool*)0x95CD58;
|
2019-06-22 20:16:29 +02:00
|
|
|
bool &CReplay::bReplayEnabled = *(bool*)0x617CAC;
|
|
|
|
uint32 &CReplay::SlowMotion = *(uint32*)0x9414D4;
|
|
|
|
uint32 &CReplay::FramesActiveLookAroundCam = *(uint32*)0x880F84;
|
|
|
|
bool &CReplay::bDoLoadSceneWhenDone = *(bool*)0x95CD76;
|
2019-06-07 22:31:03 +02:00
|
|
|
|
2019-06-22 20:16:29 +02:00
|
|
|
void PrintElementsInPtrList(void)
|
|
|
|
{
|
|
|
|
for (CPtrNode* node = CWorld::GetBigBuildingList(LEVEL_NONE).first; node; node = node->next) {
|
2019-06-23 12:58:14 +02:00
|
|
|
/* Most likely debug print was present here */
|
2019-06-22 20:16:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CReplay::Init(void)
|
|
|
|
{
|
|
|
|
pBuf0 = nil;
|
|
|
|
pBuf1 = nil;
|
|
|
|
pBuf2 = nil;
|
|
|
|
pBuf3 = nil;
|
|
|
|
pBuf4 = nil;
|
|
|
|
pBuf5 = nil;
|
|
|
|
pBuf6 = nil;
|
|
|
|
pBuf7 = nil;
|
|
|
|
pBuf8 = nil;
|
|
|
|
pBuf9 = nil;
|
|
|
|
pBuf10 = nil;
|
|
|
|
pBuf11 = nil;
|
|
|
|
pRadarBlips = nil;
|
|
|
|
pStoredCam = nil;
|
|
|
|
pWorld1 = nil;
|
|
|
|
pEmptyReferences = nil;
|
|
|
|
pPedAnims = nil;
|
|
|
|
pPickups = nil;
|
|
|
|
pReferences = nil;
|
|
|
|
Mode = MODE_RECORD;
|
|
|
|
Playback.m_nOffset = 0;
|
|
|
|
Playback.m_pBase = nil;
|
|
|
|
Playback.m_bSlot = 0;
|
|
|
|
Record.m_nOffset = 0;
|
|
|
|
Record.m_pBase = nil;
|
|
|
|
Record.m_bSlot = 0;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
BufferStatus[i] = REPLAYBUFFER_UNUSED;
|
|
|
|
Record.m_bSlot = 0;
|
|
|
|
Record.m_pBase = Buffers[0];
|
|
|
|
BufferStatus[0] = REPLAYBUFFER_RECORD;
|
|
|
|
Buffers[0][Record.m_nOffset] = REPLAYPACKET_END;
|
|
|
|
bPlayingBackFromFile = false;
|
|
|
|
bReplayEnabled = true;
|
|
|
|
SlowMotion = 1;
|
|
|
|
FramesActiveLookAroundCam = 0;
|
|
|
|
bDoLoadSceneWhenDone = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CReplay::DisableReplays(void)
|
|
|
|
{
|
|
|
|
bReplayEnabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CReplay::EnableReplays(void)
|
|
|
|
{
|
|
|
|
bReplayEnabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayReplayFromHD(void);
|
|
|
|
void CReplay::Update(void)
|
|
|
|
{
|
|
|
|
if (CCutsceneMgr::IsCutsceneProcessing() || CTimer::GetIsPaused())
|
|
|
|
return;
|
|
|
|
switch (Mode){
|
|
|
|
case MODE_RECORD:
|
|
|
|
RecordThisFrame();
|
|
|
|
break;
|
|
|
|
case MODE_PLAYBACK:
|
|
|
|
PlaybackThisFrame();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (CDraw::FadeValue || !bReplayEnabled)
|
|
|
|
return;
|
|
|
|
if (Mode == MODE_PLAYBACK){
|
|
|
|
if (CPad::NewKeyState.F[0] && !CPad::OldKeyState.F[0])
|
|
|
|
FinishPlayback();
|
|
|
|
}
|
|
|
|
else if (Mode == MODE_RECORD){
|
|
|
|
if (CPad::NewKeyState.F[0] && !CPad::OldKeyState.F[0])
|
|
|
|
TriggerPlayback(REPLAYCAMMODE_ASSTORED, 0.0f, 0.0f, 0.0f, false);
|
|
|
|
if (CPad::NewKeyState.F[1] && !CPad::OldKeyState.F[1])
|
|
|
|
SaveReplayToHD();
|
|
|
|
if (CPad::NewKeyState.F[2] && !CPad::OldKeyState.F[2])
|
|
|
|
PlayReplayFromHD();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
WRAPPER void CReplay::RecordThisFrame(void) { EAXJMP(0x5932B0); }
|
|
|
|
#else
|
|
|
|
void CReplay::RecordThisFrame(void)
|
|
|
|
{
|
|
|
|
tGeneralPacket* general = (tGeneralPacket*)&Record.m_pBase[Record.m_nOffset];
|
|
|
|
general->type = REPLAYPACKET_GENERAL;
|
|
|
|
general->camera_pos.CopyOnlyMatrix(&TheCamera.GetMatrix());
|
|
|
|
FindPlayerCoors(general->player_pos);
|
|
|
|
general->in_rcvehicle = CWorld::Players[CWorld::PlayerInFocus].m_pRemoteVehicle ? true : false;
|
|
|
|
Record.m_nOffset += sizeof(*general);
|
|
|
|
tClockPacket* clock = (tClockPacket*)&Record.m_pBase[Record.m_nOffset];
|
|
|
|
clock->type = REPLAYPACKET_CLOCK;
|
|
|
|
clock->hours = CClock::GetHours();
|
|
|
|
clock->minutes = CClock::GetMinutes();
|
|
|
|
Record.m_nOffset += sizeof(*clock);
|
|
|
|
tWeatherPacket* weather = (tWeatherPacket*)&Record.m_pBase[Record.m_nOffset];
|
|
|
|
weather->type = REPLAYPACKET_WEATHER;
|
|
|
|
weather->old_weather = CWeather::OldWeatherType;
|
|
|
|
weather->new_weather = CWeather::NewWeatherType;
|
|
|
|
weather->interpolation = CWeather::InterpolationValue;
|
|
|
|
Record.m_nOffset += sizeof(*weather);
|
|
|
|
tTimerPacket* timer = (tTimerPacket*)&Record.m_pBase[Record.m_nOffset];
|
|
|
|
timer->type = REPLAYPACKET_TIMER;
|
|
|
|
timer->timer = CTimer::GetTimeInMilliseconds();
|
|
|
|
Record.m_nOffset += sizeof(*timer);
|
|
|
|
CVehiclePool* vehicles = CPools::GetVehiclePool();
|
|
|
|
for (int i = 0; i < vehicles->GetSize(); i++){
|
|
|
|
CVehicle* v = vehicles->GetSlot(i);
|
|
|
|
if (v && v->m_rwObject && v->GetModelIndex() != MI_AIRTRAIN && v->GetModelIndex() != MI_TRAIN)
|
|
|
|
StoreCarUpdate(v, i);
|
|
|
|
}
|
|
|
|
CPedPool* peds = CPools::GetPedPool();
|
|
|
|
for (int i = 0; i < peds->GetSize(); i++) {
|
|
|
|
CPed* p = peds->GetSlot(i);
|
|
|
|
if (!p || !p->m_rwObject)
|
|
|
|
continue;
|
|
|
|
if (!p->bRecordedForReplay){
|
|
|
|
tPedHeaderPacket* ph = (tPedHeaderPacket*)&Record.m_pBase[Record.m_nOffset];
|
|
|
|
ph->type = REPLAYPACKET_PED_HEADER;
|
|
|
|
ph->index = i;
|
|
|
|
ph->mi = p->GetModelIndex();
|
|
|
|
ph->pedtype = p->m_nPedType;
|
|
|
|
Record.m_nOffset += sizeof(*ph);
|
|
|
|
p->bRecordedForReplay = true;
|
|
|
|
}
|
|
|
|
StorePedUpdate(p, i);
|
|
|
|
}
|
|
|
|
for (uint8 i = 0; i < 16; i++){
|
|
|
|
if (!CBulletTraces::aTraces[i].m_bInUse)
|
|
|
|
continue;
|
|
|
|
tBulletTracePacket* bt = (tBulletTracePacket*)&Record.m_pBase[Record.m_nOffset];
|
2019-06-23 00:34:11 +02:00
|
|
|
bt->type = REPLAYPACKET_BULLET_TRACES;
|
2019-06-22 20:16:29 +02:00
|
|
|
bt->index = i;
|
|
|
|
bt->frames = CBulletTraces::aTraces[i].m_bFramesInUse;
|
|
|
|
bt->lifetime = CBulletTraces::aTraces[i].m_bLifeTime;
|
|
|
|
bt->inf = CBulletTraces::aTraces[i].m_vecInf;
|
|
|
|
bt->sup = CBulletTraces::aTraces[i].m_vecSup;
|
|
|
|
Record.m_nOffset += sizeof(*bt);
|
|
|
|
}
|
|
|
|
tEndOfFramePacket* eof = (tEndOfFramePacket*)&Record.m_pBase[Record.m_nOffset];
|
|
|
|
eof->type = REPLAYPACKET_ENDOFFRAME;
|
|
|
|
Record.m_nOffset += sizeof(*eof);
|
|
|
|
if (Record.m_nOffset <= 97000){
|
|
|
|
/* Unsafe assumption which can cause buffer overflow
|
|
|
|
* if size of next frame exceeds 3000 bytes.
|
|
|
|
* Most notably it causes various timecyc errors. */
|
|
|
|
Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Record.m_pBase[Record.m_nOffset] = REPLAYPACKET_END;
|
|
|
|
BufferStatus[Record.m_bSlot] = REPLAYBUFFER_PLAYBACK;
|
|
|
|
Record.m_bSlot = (Record.m_bSlot + 1) % 8;
|
|
|
|
BufferStatus[Record.m_bSlot] = REPLAYBUFFER_RECORD;
|
|
|
|
Record.m_pBase = Buffers[Record.m_bSlot];
|
|
|
|
Record.m_nOffset = 0;
|
|
|
|
*Record.m_pBase = REPLAYPACKET_END;
|
|
|
|
MarkEverythingAsNew();
|
|
|
|
}
|
|
|
|
#endif
|
2019-06-23 00:34:11 +02:00
|
|
|
|
|
|
|
#if 0
|
2019-06-22 20:16:29 +02:00
|
|
|
WRAPPER void CReplay::StorePedUpdate(CPed *ped, int id) { EAXJMP(0x5935B0); }
|
2019-06-23 00:34:11 +02:00
|
|
|
#else
|
|
|
|
void CReplay::StorePedUpdate(CPed *ped, int id)
|
|
|
|
{
|
|
|
|
tPedUpdatePacket* pp = (tPedUpdatePacket*)&Record.m_pBase[Record.m_nOffset];
|
|
|
|
pp->type = REPLAYPACKET_PED_UPDATE;
|
|
|
|
pp->index = id;
|
|
|
|
pp->heading = 128.0f / M_PI * ped->m_fRotationCur;
|
|
|
|
pp->matrix.CompressFromFullMatrix(ped->GetMatrix());
|
|
|
|
pp->assoc_group_id = ped->m_animGroup;
|
|
|
|
/* Would be more sane to use GetJustIndex(ped->m_pMyVehicle) in following assignment */
|
|
|
|
if (ped->bInVehicle && ped->m_pMyVehicle)
|
|
|
|
pp->vehicle_index = (CPools::GetVehiclePool()->GetIndex(ped->m_pMyVehicle) >> 8) + 1;
|
|
|
|
else
|
|
|
|
pp->vehicle_index = 0;
|
|
|
|
pp->weapon_model = ped->m_wepModelID;
|
|
|
|
StorePedAnimation(ped, &pp->anim_state);
|
|
|
|
Record.m_nOffset += sizeof(tPedUpdatePacket);
|
|
|
|
}
|
|
|
|
#endif
|
2019-06-23 12:58:14 +02:00
|
|
|
|
|
|
|
#if 0
|
2019-06-22 20:16:29 +02:00
|
|
|
WRAPPER void CReplay::StorePedAnimation(CPed *ped, CStoredAnimationState *state) { EAXJMP(0x593670); }
|
2019-06-23 12:58:14 +02:00
|
|
|
#else
|
|
|
|
void CReplay::StorePedAnimation(CPed *ped, CStoredAnimationState *state)
|
|
|
|
{
|
|
|
|
CAnimBlendAssociation* second;
|
|
|
|
float blend_amount;
|
|
|
|
CAnimBlendAssociation* main = RpAnimBlendClumpGetMainAssociation((RpClump*)ped->m_rwObject, &second, &blend_amount);
|
|
|
|
if (main){
|
|
|
|
state->animId = main->animId;
|
|
|
|
state->time = 255.0f / 4.0f * max(0.0f, min(4.0f, main->currentTime));
|
|
|
|
state->speed = 255.0f / 3.0f * max(0.0f, min(3.0f, main->speed));
|
|
|
|
}else{
|
|
|
|
state->animId = 3;
|
|
|
|
state->time = 0;
|
|
|
|
state->speed = 85;
|
|
|
|
}
|
|
|
|
if (second) {
|
|
|
|
state->secAnimId = second->animId;
|
|
|
|
state->secTime = 255.0f / 4.0f * max(0.0f, min(4.0f, second->currentTime));
|
|
|
|
state->secSpeed = 255.0f / 3.0f * max(0.0f, min(3.0f, second->speed));
|
|
|
|
state->blendAmount = 255.0f / 2.0f * max(0.0f, min(2.0f, blend_amount));
|
|
|
|
}else{
|
|
|
|
state->secAnimId = 0;
|
|
|
|
state->secTime = 0;
|
|
|
|
state->secSpeed = 0;
|
|
|
|
state->blendAmount = 0;
|
|
|
|
}
|
|
|
|
CAnimBlendAssociation* partial = RpAnimBlendClumpGetMainPartialAssociation((RpClump*)ped->m_rwObject);
|
|
|
|
if (partial) {
|
|
|
|
state->partAnimId = partial->animId;
|
|
|
|
state->partAnimTime = 255.0f / 4.0f * max(0.0f, min(4.0f, partial->currentTime));
|
|
|
|
state->partAnimSpeed = 255.0f / 3.0f * max(0.0f, min(3.0f, partial->speed));
|
|
|
|
state->partBlendAmount = 255.0f / 2.0f * max(0.0f, min(2.0f, partial->blendAmount));
|
|
|
|
}else{
|
|
|
|
state->partAnimId = 0;
|
|
|
|
state->partAnimTime = 0;
|
|
|
|
state->partAnimSpeed = 0;
|
|
|
|
state->partBlendAmount = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2019-06-22 20:16:29 +02:00
|
|
|
WRAPPER void CReplay::StoreDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state) { EAXJMP(0x593BB0); }
|
|
|
|
WRAPPER void CReplay::ProcessPedUpdate(CPed *ped, float interpolation, CAddressInReplayBuffer *buffer) { EAXJMP(0x594050); }
|
|
|
|
WRAPPER void CReplay::RetrievePedAnimation(CPed *ped, CStoredAnimationState *state) { EAXJMP(0x5942A0); }
|
|
|
|
WRAPPER void CReplay::RetrieveDetailedPedAnimation(CPed *ped, CStoredDetailedAnimationState *state) { EAXJMP(0x5944B0); }
|
|
|
|
WRAPPER void CReplay::PlaybackThisFrame(void) { EAXJMP(0x5946B0); }
|
|
|
|
WRAPPER void CReplay::StoreCarUpdate(CVehicle *vehicle, int id) { EAXJMP(0x5947F0); }
|
|
|
|
WRAPPER void CReplay::ProcessCarUpdate(CVehicle *vehicle, float interpolation, CAddressInReplayBuffer *buffer) { EAXJMP(0x594D10); }
|
|
|
|
WRAPPER bool CReplay::PlayBackThisFrameInterpolation(CAddressInReplayBuffer *buffer, float interpolation, uint32 *pTimer) { EAXJMP(0x595240); }
|
|
|
|
WRAPPER void CReplay::FinishPlayback(void) { EAXJMP(0x595B20); }
|
|
|
|
WRAPPER void CReplay::Shutdown(void) { EAXJMP(0x595BD0); }
|
|
|
|
WRAPPER void CReplay::ProcessReplayCamera(void) { EAXJMP(0x595C40); }
|
2019-06-07 22:31:03 +02:00
|
|
|
WRAPPER void CReplay::Display(void) { EAXJMP(0x595EE0); }
|
2019-06-22 20:16:29 +02:00
|
|
|
WRAPPER void CReplay::TriggerPlayback(uint8 cam_mode, float cam_x, float cam_y, float cam_z, bool load_scene) { EAXJMP(0x596030); }
|
|
|
|
WRAPPER void CReplay::StoreStuffInMem(void) { EAXJMP(0x5961F0); }
|
|
|
|
WRAPPER void CReplay::RestoreStuffFromMem(void) { EAXJMP(0x5966E0); }
|
|
|
|
WRAPPER void CReplay::EmptyPedsAndVehiclePools(void) { EAXJMP(0x5970E0); }
|
|
|
|
WRAPPER void CReplay::EmptyAllPools(void) { EAXJMP(0x5971B0); }
|
|
|
|
WRAPPER void CReplay::MarkEverythingAsNew(void) { EAXJMP(0x597280); }
|
|
|
|
WRAPPER void CReplay::SaveReplayToHD(void) { EAXJMP(0x597330); }
|
|
|
|
WRAPPER void PlayReplayFromHD(void) { EAXJMP(0x597420); }
|
|
|
|
WRAPPER void CReplay::StreamAllNecessaryCarsAndPeds(void) { EAXJMP(0x597560); }
|
|
|
|
WRAPPER void CReplay::FindFirstFocusCoordinate(CVector *coord) { EAXJMP(0x5975E00); }
|
|
|
|
WRAPPER bool CReplay::ShouldStandardCameraBeProcessed(void) { EAXJMP(0x597680); }
|
|
|
|
WRAPPER void CReplay::ProcessLookAroundCam(void) { EAXJMP(0x5976C0); }
|
|
|
|
WRAPPER size_t CReplay::FindSizeOfPacket(uint8 type) { EAXJMP(0x597CC0); }
|
|
|
|
|
|
|
|
STARTPATCHES
|
|
|
|
InjectHook(0x592FC0, PrintElementsInPtrList, PATCH_JUMP);
|
|
|
|
InjectHook(0x592FE0, CReplay::Init, PATCH_JUMP);
|
|
|
|
InjectHook(0x593150, CReplay::DisableReplays, PATCH_JUMP);
|
|
|
|
InjectHook(0x593150, CReplay::EnableReplays, PATCH_JUMP);
|
|
|
|
InjectHook(0x593170, CReplay::Update, PATCH_JUMP);
|
2019-06-22 20:25:14 +02:00
|
|
|
ENDPATCHES
|