mirror of
https://github.com/OpenDriver2/REDRIVER2.git
synced 2024-11-22 10:22:48 +01:00
840 lines
19 KiB
C
840 lines
19 KiB
C
#ifdef PSX
|
|
#error This file is not applicable for PSX build
|
|
#endif
|
|
|
|
#ifdef CUTSCENE_RECORDER
|
|
|
|
#include "driver2.h"
|
|
#include "cutrecorder.h"
|
|
|
|
#include "main.h"
|
|
#include "pad.h"
|
|
#include "cop_ai.h"
|
|
#include "spool.h"
|
|
#include "civ_ai.h"
|
|
#include "cutscene.h"
|
|
#include "cars.h"
|
|
#include "camera.h"
|
|
#include "glaunch.h"
|
|
#include "mission.h"
|
|
#include "replays.h"
|
|
#include "state.h"
|
|
#include "system.h"
|
|
#include "pause.h"
|
|
#include "pres.h"
|
|
#include "players.h"
|
|
|
|
#include "../utils/ini.h"
|
|
|
|
typedef struct AutoTestStats
|
|
{
|
|
int numHitCars;
|
|
int numHitWalls;
|
|
int stuck;
|
|
int saved;
|
|
};
|
|
|
|
PING_PACKET NewPingBuffer[MAX_REPLAY_PINGS];
|
|
AutoTestStats gAutoTestStats[16]; // 16th is dummy
|
|
|
|
int gCutsceneChaseAutoTest = 0;
|
|
int gChaseStuckTimer = 0;
|
|
|
|
int gCutsceneAsReplay_ReChaseLoaded = 0;
|
|
int gCutsceneAsReplay = 0;
|
|
int gCutsceneAsReplay_NumReplayStreams = 0;
|
|
int gCutsceneAsReplay_PlayerId = 0;
|
|
int gCutsceneAsReplay_PlayerChanged = 0;
|
|
int gCutsceneAsReplay_ReserveSlots = 2;
|
|
char gCutsceneRecorderPauseText[64] = { 0 };
|
|
char gCurrentChasePauseText[64] = { 0 };
|
|
|
|
extern int gDieWithFade;
|
|
|
|
int CutRec_LoadCutsceneAsReplayFromBuffer(char* buffer);
|
|
void InitCutsceneRecorder(char* configFilename);
|
|
int LoadCutsceneAsReplay(int subindex);
|
|
|
|
void CutRec_Reset()
|
|
{
|
|
if (gCutsceneChaseAutoTest != 0 && gCutsceneChaseAutoTest < 15)
|
|
{
|
|
gAutoTestStats[gCutsceneChaseAutoTest].numHitCars = 0;
|
|
gAutoTestStats[gCutsceneChaseAutoTest].numHitWalls = 0;
|
|
gAutoTestStats[gCutsceneChaseAutoTest].stuck = 0;
|
|
gAutoTestStats[gCutsceneChaseAutoTest].saved = 0;
|
|
gChaseStuckTimer = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
gCutsceneChaseAutoTest = 0;
|
|
gCutsceneAsReplay = 0;
|
|
|
|
gCutsceneAsReplay_PlayerId = 0;
|
|
gCutsceneAsReplay_PlayerChanged = 0;
|
|
gCutsceneAsReplay_ReserveSlots = 2;
|
|
}
|
|
|
|
void CutRec_NextChase(int dir)
|
|
{
|
|
if (dir > 0)
|
|
gChaseNumber++;
|
|
else if (dir < 0)
|
|
gChaseNumber--;
|
|
|
|
if (gChaseNumber < 0)
|
|
gChaseNumber = 0;
|
|
|
|
if (gChaseNumber > 15)
|
|
gChaseNumber = 15;
|
|
|
|
sprintf(gCurrentChasePauseText, "Chase ID: %d", gChaseNumber);
|
|
}
|
|
|
|
int CutRec_GotoChase(int number)
|
|
{
|
|
if (number < 2)
|
|
return 0;
|
|
|
|
if (number > 15)
|
|
return 0;
|
|
|
|
gCutsceneChaseAutoTest = number;
|
|
|
|
// load next replay and restart
|
|
if (LoadCutsceneAsReplay(gCutsceneChaseAutoTest))
|
|
{
|
|
State_GameComplete(NULL);
|
|
|
|
gDrawPauseMenus = 0;
|
|
gLoadedReplay = 1;
|
|
CurrentGameMode = GAMEMODE_REPLAY;
|
|
|
|
SetState(STATE_GAMELAUNCH);
|
|
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CutRec_Step()
|
|
{
|
|
if (gCutsceneChaseAutoTest != 0 && CutRec_IsPlaying())
|
|
{
|
|
// goto previous chase
|
|
if (Pads[0].dirnew & CAR_PAD_LEFT)
|
|
{
|
|
CutRec_GotoChase(gCutsceneChaseAutoTest - 1);
|
|
return;
|
|
}
|
|
|
|
// goto next chase
|
|
if (Pads[0].dirnew & CAR_PAD_RIGHT)
|
|
{
|
|
CutRec_GotoChase(gCutsceneChaseAutoTest + 1);
|
|
return;
|
|
}
|
|
|
|
if (!pauseflag)
|
|
{
|
|
int carId = player[gCutsceneAsReplay_PlayerId].playerCarId;
|
|
|
|
if (car_data[carId].hd.speed < 5)
|
|
gChaseStuckTimer++;
|
|
else
|
|
gChaseStuckTimer = 0;
|
|
|
|
if (gChaseStuckTimer > 45)
|
|
{
|
|
gAutoTestStats[gCutsceneChaseAutoTest].stuck = 1;
|
|
gChaseStuckTimer = 0;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (gCutsceneChaseAutoTest < 15 &&
|
|
(NoPlayerControl && ReplayParameterPtr->RecordingEnd - 2 < CameraCnt || gDieWithFade))
|
|
{
|
|
gCutsceneChaseAutoTest++;
|
|
|
|
// load next replay and restart
|
|
if (!CutRec_GotoChase(gCutsceneChaseAutoTest))
|
|
{
|
|
printInfo("------- AUTOTEST RESULTS -------\n");
|
|
for (int i = 0; i < 15; i++)
|
|
printInfo(" chase %d - hit cars: %d, stuck: %d\n", i, gAutoTestStats[i].numHitCars, gAutoTestStats[i].stuck);
|
|
printInfo("------- ---------------- -------\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CutRec_Draw()
|
|
{
|
|
char text[64];
|
|
|
|
if (gCutsceneAsReplay == 0)
|
|
return;
|
|
|
|
SetTextColour(128, 128, 128);
|
|
|
|
if(gCutsceneChaseAutoTest)
|
|
{
|
|
sprintf(text, "Auto-test - chase %d %s", gCutsceneChaseAutoTest, gAutoTestStats[gCutsceneChaseAutoTest].saved ? "-SAVED-" : "");
|
|
PrintString(text, 5, 10);
|
|
|
|
if(gCutsceneAsReplay_ReChaseLoaded)
|
|
{
|
|
SetTextColour(32, 128, 32);
|
|
PrintString("Re-recorded", 5, 20);
|
|
}
|
|
else
|
|
{
|
|
SetTextColour(128, 80, 32);
|
|
PrintString("Reflections", 5, 20);
|
|
}
|
|
|
|
SetTextColour(128, 128, 128);
|
|
|
|
sprintf(text, "Frame %d of %d", CameraCnt, ReplayParameterPtr->RecordingEnd);
|
|
PrintString(text, 5, 215);
|
|
}
|
|
|
|
if(PingBufferPos >= MAX_REPLAY_PINGS-1)
|
|
SetTextColour(128, 0, 0);
|
|
|
|
sprintf(text, "Pings: %d", PingBufferPos);
|
|
PrintString(text, 5, 205);
|
|
|
|
SetTextColour(128, 128, 128);
|
|
|
|
if (gAutoTestStats[gCutsceneChaseAutoTest].numHitCars > 0)
|
|
SetTextColour(128, 0, 0);
|
|
|
|
sprintf(text, "Hit cars: %d", gAutoTestStats[gCutsceneChaseAutoTest].numHitCars);
|
|
PrintString(text, 120, 205);
|
|
|
|
if(gAutoTestStats[gCutsceneChaseAutoTest].stuck)
|
|
{
|
|
SetTextColour(128, 0, 0);
|
|
|
|
sprintf(text, "Car is stuck!");
|
|
PrintString(text, 5, 60);
|
|
}
|
|
}
|
|
|
|
void CutRec_ReserveSlots()
|
|
{
|
|
if (gCutsceneAsReplay == 0)
|
|
return;
|
|
|
|
for (int i = 0; i < gCutsceneAsReplay_ReserveSlots; i++)
|
|
reservedSlots[i] = 1;
|
|
}
|
|
|
|
void CutRec_HandleCarRequest()
|
|
{
|
|
if (gCutsceneAsReplay == 0)
|
|
return;
|
|
|
|
requestCopCar = 0;
|
|
allowSpecSpooling = 0;
|
|
}
|
|
|
|
void CutRec_NextPlayer(int dir)
|
|
{
|
|
int old_player = gCutsceneAsReplay_PlayerId;
|
|
|
|
if (dir > 0)
|
|
gCutsceneAsReplay_PlayerId++;
|
|
else if (dir < 0)
|
|
gCutsceneAsReplay_PlayerId--;
|
|
|
|
if (gCutsceneAsReplay_PlayerId >= gCutsceneAsReplay_NumReplayStreams)
|
|
gCutsceneAsReplay_PlayerId -= gCutsceneAsReplay_NumReplayStreams;
|
|
else if (gCutsceneAsReplay_PlayerId < 0)
|
|
gCutsceneAsReplay_PlayerId += gCutsceneAsReplay_NumReplayStreams;
|
|
|
|
if (old_player != gCutsceneAsReplay_PlayerId)
|
|
gCutsceneAsReplay_PlayerChanged = 1;
|
|
|
|
sprintf(gCutsceneRecorderPauseText, "CUTSCENE PLAYER ID: %d", gCutsceneAsReplay_PlayerId);
|
|
}
|
|
|
|
int LoadCutsceneAsReplay(int subindex)
|
|
{
|
|
int offset;
|
|
int size;
|
|
|
|
if (gCutsceneAsReplay_PlayerId > 0)
|
|
gCutsceneAsReplay_PlayerChanged = 1;
|
|
|
|
CUTSCENE_HEADER header;
|
|
char filename[64];
|
|
|
|
// Try loading existing ReChase
|
|
sprintf(filename, "REPLAYS\\ReChases\\CUT%d_N.R", gCutsceneAsReplay);
|
|
|
|
if(!FileExists(filename))
|
|
{
|
|
gCutsceneAsReplay_ReChaseLoaded = 0;
|
|
printWarning("--- NO re-recorded chases available! ---\n");
|
|
|
|
if (gCutsceneAsReplay < 21)
|
|
sprintf(filename, "REPLAYS\\CUT%d.R", gCutsceneAsReplay);
|
|
else
|
|
sprintf(filename, "REPLAYS\\A\\CUT%d.R", gCutsceneAsReplay);
|
|
}
|
|
else
|
|
{
|
|
gCutsceneAsReplay_ReChaseLoaded = 1;
|
|
printInfo("--- Re-recorded chases loaded ---\n");
|
|
}
|
|
|
|
if (FileExists(filename))
|
|
{
|
|
LoadfileSeg(filename, (char*)&header, 0, sizeof(CUTSCENE_HEADER));
|
|
|
|
if (header.data[subindex].offset != 0xffff)
|
|
{
|
|
offset = header.data[subindex].offset * 4;
|
|
size = header.data[subindex].size;
|
|
|
|
LoadfileSeg(filename, (char*)_other_buffer, offset, size);
|
|
|
|
int result = CutRec_LoadCutsceneAsReplayFromBuffer((char*)_other_buffer);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
printError("Invalid cutscene subindex or mission!\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
void InitChaseAutoTest(char* configFilename)
|
|
{
|
|
gCutsceneChaseAutoTest = 2;
|
|
InitCutsceneRecorder(configFilename);
|
|
}
|
|
|
|
void InitCutsceneRecorder(char* configFilename)
|
|
{
|
|
ini_t* config;
|
|
int loadExistingCutscene;
|
|
int subindex;
|
|
|
|
config = ini_load(configFilename);
|
|
|
|
if (!config)
|
|
{
|
|
printError("Unable to open '%s'!\n", configFilename);
|
|
return;
|
|
}
|
|
|
|
//const char* stream = ini_get(config, "fs", "dataFolder");
|
|
|
|
loadExistingCutscene = 0;
|
|
ini_sget(config, "settings", "loadExistingCutscene", "%d", &loadExistingCutscene);
|
|
ini_sget(config, "settings", "mission", "%d", &gCutsceneAsReplay);
|
|
ini_sget(config, "settings", "baseMission", "%d", &gCurrentMissionNumber);
|
|
ini_sget(config, "settings", "player", "%d", &gCutsceneAsReplay_PlayerId);
|
|
ini_sget(config, "settings", "reserveSlots", "%d", &gCutsceneAsReplay_ReserveSlots);
|
|
ini_sget(config, "settings", "subindex", "%d", &subindex);
|
|
|
|
// totally limited by streams
|
|
if (gCutsceneAsReplay_ReserveSlots > 8)
|
|
gCutsceneAsReplay_ReserveSlots = 8;
|
|
|
|
if (loadExistingCutscene)
|
|
{
|
|
if(gCutsceneChaseAutoTest != 0)
|
|
{
|
|
subindex = gCutsceneChaseAutoTest;
|
|
}
|
|
|
|
if (!LoadCutsceneAsReplay(subindex))
|
|
{
|
|
ini_free(config);
|
|
|
|
gLoadedReplay = 0;
|
|
gCutsceneAsReplay = 0;
|
|
return;
|
|
}
|
|
|
|
gCutsceneAsReplay_NumReplayStreams = NumReplayStreams;
|
|
|
|
gLoadedReplay = 1;
|
|
CurrentGameMode = GAMEMODE_REPLAY;
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
char curStreamName[40];
|
|
STREAM_SOURCE* stream;
|
|
|
|
InitPadRecording();
|
|
|
|
ini_sget(config, "settings", "streams", "%d", &gCutsceneAsReplay_NumReplayStreams);
|
|
|
|
// initialize all streams
|
|
for (i = 0; i < gCutsceneAsReplay_NumReplayStreams; i++)
|
|
{
|
|
stream = &ReplayStreams[i].SourceType;
|
|
sprintf(curStreamName, "stream%d", i);
|
|
|
|
stream->position.vy = 0;
|
|
|
|
ini_sget(config, curStreamName, "type", "%hhd", &stream->type);
|
|
ini_sget(config, curStreamName, "model", "%hhd", &stream->model);
|
|
ini_sget(config, curStreamName, "palette", "%hhd", &stream->palette);
|
|
ini_sget(config, curStreamName, "controlType", "%hhd", &stream->controlType);
|
|
ini_sget(config, curStreamName, "startRot", "%d", &stream->rotation);
|
|
ini_sget(config, curStreamName, "startPosX", "%d", &stream->position.vx);
|
|
ini_sget(config, curStreamName, "startPosY", "%d", &stream->position.vy);
|
|
ini_sget(config, curStreamName, "startPosZ", "%d", &stream->position.vz);
|
|
}
|
|
}
|
|
|
|
GameType = GAME_TAKEADRIVE;
|
|
CameraCnt = 0;
|
|
ini_free(config);
|
|
|
|
CutRec_NextChase(0);
|
|
CutRec_NextPlayer(0);
|
|
|
|
SetState(STATE_GAMELAUNCH);
|
|
}
|
|
|
|
// [A] Stores ping info into replay buffer
|
|
int CutRec_StorePingInfo(int cookieCount, int carId)
|
|
{
|
|
PING_PACKET* buffer;
|
|
PING_PACKET* packet;
|
|
|
|
if (!CutRec_IsRecording())
|
|
return 0;
|
|
|
|
if (PingBufferPos < MAX_REPLAY_PINGS-1)
|
|
{
|
|
packet = &NewPingBuffer[PingBufferPos++];
|
|
packet->frame = CameraCnt - frameStart;
|
|
packet->carId = carId;
|
|
|
|
packet->cookieCount = cookieCount;
|
|
|
|
PingBuffer[PingBufferPos - 1] = *packet;
|
|
|
|
// always finalize last ping
|
|
packet = &NewPingBuffer[PingBufferPos];
|
|
packet->frame = 0xffff;
|
|
packet->carId = -1;
|
|
packet->cookieCount = -1;
|
|
|
|
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CutRec_CheckInvalidatePing(int carId, int howHard)
|
|
{
|
|
int pos;
|
|
|
|
if (gCutsceneAsReplay == 0)
|
|
return;
|
|
|
|
if (howHard < 60000)
|
|
return;
|
|
|
|
gAutoTestStats[gCutsceneChaseAutoTest].numHitCars++;
|
|
|
|
pos = PingBufferPos;
|
|
|
|
while (pos >= 0)
|
|
{
|
|
pos--;
|
|
if (PingBuffer[pos].carId == carId)
|
|
{
|
|
printWarning("Removing ping at %d\n", PingBuffer[pos].frame);
|
|
PingBuffer[pos].carId = -1;
|
|
NewPingBuffer[pos].carId = -1;
|
|
|
|
PingOutCar(&car_data[carId]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int CutRec_InitPlayers()
|
|
{
|
|
STREAM_SOURCE* temp;
|
|
if (gCutsceneAsReplay == 0)
|
|
return 0;
|
|
|
|
for (int i = 0; i < gCutsceneAsReplay_NumReplayStreams; i++)
|
|
{
|
|
PlayerStartInfo[i] = &ReplayStreams[i].SourceType;
|
|
PlayerStartInfo[i]->controlType = CONTROL_TYPE_CUTSCENE;
|
|
}
|
|
|
|
temp = PlayerStartInfo[0];
|
|
PlayerStartInfo[0] = PlayerStartInfo[gCutsceneAsReplay_PlayerId];
|
|
PlayerStartInfo[gCutsceneAsReplay_PlayerId] = temp;
|
|
|
|
NumReplayStreams = gCutsceneAsReplay_NumReplayStreams;
|
|
numPlayersToCreate = gCutsceneAsReplay_NumReplayStreams;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CutRec_InitMission(char* filename)
|
|
{
|
|
int header;
|
|
|
|
if (gCutsceneAsReplay == 0)
|
|
return 0;
|
|
|
|
LoadfileSeg(filename, (char*)&header, gCutsceneAsReplay * 4, 4);
|
|
|
|
if (header == 0)
|
|
return 0;
|
|
|
|
// load some data from target cutscene mission
|
|
MS_MISSION missionTempHeader;
|
|
|
|
LoadfileSeg(filename, (char*)&missionTempHeader, header & 0x7ffff, sizeof(MS_MISSION));
|
|
|
|
memcpy((u_char*)MissionHeader->residentModels, (u_char*)missionTempHeader.residentModels, sizeof(missionTempHeader.residentModels));
|
|
MissionHeader->time = missionTempHeader.time;
|
|
MissionHeader->weather = missionTempHeader.weather;
|
|
MissionHeader->cops = missionTempHeader.cops;
|
|
|
|
if(gCutsceneChaseAutoTest == 0)
|
|
ClearMem((char*)gAutoTestStats, sizeof(gAutoTestStats));
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Loads original cutscene replay inside CUT*.R
|
|
int CutRec_LoadCutsceneAsReplayFromBuffer(char* buffer)
|
|
{
|
|
REPLAY_SAVE_HEADER* header;
|
|
REPLAY_STREAM_HEADER* sheader;
|
|
|
|
char* pt = buffer;
|
|
|
|
header = (REPLAY_SAVE_HEADER*)pt;
|
|
|
|
if (header->magic != DRIVER2_REPLAY_MAGIC &&
|
|
header->magic != REDRIVER2_CHASE_MAGIC || // [A]
|
|
header->NumReplayStreams == 0)
|
|
return 0;
|
|
|
|
ReplayStart = replayptr = (char*)_replay_buffer;
|
|
|
|
GameLevel = header->GameLevel;
|
|
GameType = (GAMETYPE)header->GameType;
|
|
|
|
NumReplayStreams = header->NumReplayStreams;
|
|
NumPlayers = header->NumPlayers;
|
|
gRandomChase = header->RandomChase;
|
|
CutsceneEventTrigger = header->CutsceneEvent;
|
|
gCopDifficultyLevel = header->gCopDifficultyLevel;
|
|
ActiveCheats = header->ActiveCheats; // TODO: restore old value
|
|
|
|
wantedCar[0] = header->wantedCar[0];
|
|
wantedCar[1] = header->wantedCar[1];
|
|
|
|
memcpy((u_char*)&MissionEndData, (u_char*)&header->SavedData, sizeof(MISSION_DATA));
|
|
|
|
pt = (char*)(header + 1);
|
|
|
|
int maxLength = 0;
|
|
for (int i = 0; i < NumReplayStreams; i++)
|
|
{
|
|
sheader = (REPLAY_STREAM_HEADER*)pt;
|
|
pt += sizeof(REPLAY_STREAM_HEADER);
|
|
|
|
REPLAY_STREAM* destStream = &ReplayStreams[i];
|
|
|
|
// copy source type
|
|
memcpy((u_char*)&destStream->SourceType, (u_char*)&sheader->SourceType, sizeof(STREAM_SOURCE));
|
|
|
|
int size = (sheader->Size + sizeof(PADRECORD)) & -4;
|
|
|
|
// init buffers
|
|
AllocateReplayStream(destStream, 4000);
|
|
|
|
// copy pad data and advance buffer
|
|
memcpy((u_char*)destStream->PadRecordBuffer, pt, size);
|
|
|
|
pt += size;
|
|
|
|
destStream->padCount = size / sizeof(PADRECORD);
|
|
destStream->length = sheader->Length;
|
|
|
|
if (sheader->Length > maxLength)
|
|
maxLength = sheader->Length;
|
|
}
|
|
|
|
#if 0
|
|
ReplayParameterPtr = (REPLAY_PARAMETER_BLOCK*)replayptr;
|
|
memset((u_char*)ReplayParameterPtr, 0, sizeof(REPLAY_PARAMETER_BLOCK));
|
|
ReplayParameterPtr->RecordingEnd = maxLength;
|
|
|
|
PlayerWayRecordPtr = (SXYPAIR*)(ReplayParameterPtr + 1);
|
|
memset(PlayerWayRecordPtr, 0, sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS);
|
|
|
|
PlaybackCamera = (PLAYBACKCAMERA*)(PlayerWayRecordPtr + MAX_REPLAY_WAYPOINTS);
|
|
memcpy((u_char*)PlaybackCamera, (u_char*)pt, sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS);
|
|
pt += sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS;
|
|
|
|
PingBufferPos = 0;
|
|
PingBuffer = (PING_PACKET*)(PlaybackCamera + MAX_REPLAY_CAMERAS);
|
|
memcpy((u_char*)PingBuffer, (u_char*)pt, sizeof(PING_PACKET) * MAX_REPLAY_PINGS);
|
|
pt += sizeof(PING_PACKET) * MAX_REPLAY_PINGS;
|
|
|
|
replayptr = (char*)(PingBuffer + MAX_REPLAY_PINGS);
|
|
|
|
if (header->HaveStoredData == 0x91827364) // -0x6e7d8c9c
|
|
{
|
|
memcpy((u_char*)&MissionStartData, (u_char*)pt, sizeof(MISSION_DATA));
|
|
gHaveStoredData = 1;
|
|
}
|
|
#else
|
|
// [A] REDRIVER2 chase replays skip cameras
|
|
if (header->magic == REDRIVER2_CHASE_MAGIC)
|
|
{
|
|
ReplayParameterPtr = (REPLAY_PARAMETER_BLOCK*)replayptr;
|
|
memset((u_char*)ReplayParameterPtr, 0, sizeof(REPLAY_PARAMETER_BLOCK));
|
|
ReplayParameterPtr->RecordingEnd = maxLength;
|
|
|
|
PlayerWayRecordPtr = (SXYPAIR*)(ReplayParameterPtr + 1);
|
|
PlaybackCamera = (PLAYBACKCAMERA*)(PlayerWayRecordPtr + MAX_REPLAY_WAYPOINTS);
|
|
}
|
|
else
|
|
{
|
|
ReplayParameterPtr = (REPLAY_PARAMETER_BLOCK*)replayptr;
|
|
memset((u_char*)ReplayParameterPtr, 0, sizeof(REPLAY_PARAMETER_BLOCK));
|
|
ReplayParameterPtr->RecordingEnd = maxLength;
|
|
|
|
PlayerWayRecordPtr = (SXYPAIR*)(ReplayParameterPtr + 1);
|
|
memset(PlayerWayRecordPtr, 0, sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS);
|
|
|
|
PlaybackCamera = (PLAYBACKCAMERA*)(PlayerWayRecordPtr + MAX_REPLAY_WAYPOINTS);
|
|
memcpy((u_char*)PlaybackCamera, (u_char*)pt, sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS);
|
|
pt += sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS;
|
|
}
|
|
|
|
PingBufferPos = 0;
|
|
PingBuffer = (PING_PACKET*)(PlaybackCamera + MAX_REPLAY_CAMERAS);
|
|
memcpy((u_char*)PingBuffer, (u_char*)pt, sizeof(PING_PACKET) * MAX_REPLAY_PINGS);
|
|
memcpy((u_char*)NewPingBuffer, (u_char*)pt, sizeof(PING_PACKET) * MAX_REPLAY_PINGS);
|
|
pt += sizeof(PING_PACKET) * MAX_REPLAY_PINGS;
|
|
|
|
replayptr = (char*)(PingBuffer + MAX_REPLAY_PINGS);
|
|
|
|
if (header->HaveStoredData == 0x91827364) // -0x6e7d8c9c
|
|
{
|
|
memcpy((u_char*)&MissionStartData, (u_char*)pt, sizeof(MISSION_DATA));
|
|
gHaveStoredData = 1;
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
// [D] [T]
|
|
int CutRec_SaveReplayToBuffer(char* buffer)
|
|
{
|
|
REPLAY_STREAM_HEADER* sheader;
|
|
REPLAY_SAVE_HEADER* header;
|
|
|
|
if (buffer == NULL)
|
|
return 0x3644;
|
|
|
|
char* pt = buffer;
|
|
header = (REPLAY_SAVE_HEADER*)pt;
|
|
pt += sizeof(REPLAY_SAVE_HEADER);
|
|
|
|
header->magic = DRIVER2_REPLAY_MAGIC;
|
|
header->GameLevel = GameLevel;
|
|
header->GameType = GameType;
|
|
header->MissionNumber = gCurrentMissionNumber;
|
|
|
|
header->NumReplayStreams = NumReplayStreams - NumCutsceneStreams;
|
|
header->NumPlayers = NumPlayers;
|
|
header->CutsceneEvent = -1;
|
|
header->RandomChase = gRandomChase;
|
|
|
|
header->gCopDifficultyLevel = gCopDifficultyLevel;
|
|
header->ActiveCheats = ActiveCheats;
|
|
|
|
header->wantedCar[0] = wantedCar[0];
|
|
header->wantedCar[1] = wantedCar[1];
|
|
|
|
memcpy((u_char*)&header->SavedData, (u_char*)&MissionEndData, sizeof(MISSION_DATA));
|
|
|
|
// write each stream data
|
|
for (int i = 0; i < NumReplayStreams; i++)
|
|
{
|
|
sheader = (REPLAY_STREAM_HEADER*)pt;
|
|
pt += sizeof(REPLAY_STREAM_HEADER);
|
|
|
|
REPLAY_STREAM* srcStream = &ReplayStreams[i];
|
|
|
|
// copy source type
|
|
memcpy((u_char*)&sheader->SourceType, (u_char*)&srcStream->SourceType, sizeof(STREAM_SOURCE));
|
|
sheader->Size = srcStream->padCount * sizeof(PADRECORD);
|
|
sheader->Length = srcStream->length;
|
|
|
|
int size = (sheader->Size + sizeof(PADRECORD)) & -4;
|
|
|
|
// copy pad data to write buffer
|
|
memcpy((u_char*)pt, (u_char*)srcStream->InitialPadRecordBuffer, size);
|
|
|
|
pt += size;
|
|
}
|
|
|
|
memcpy((u_char*)pt, (u_char*)PlaybackCamera, sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS);
|
|
pt += sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS;
|
|
|
|
memcpy((u_char*)pt, (u_char*)NewPingBuffer, sizeof(PING_PACKET) * MAX_REPLAY_PINGS);
|
|
pt += sizeof(PING_PACKET) * MAX_REPLAY_PINGS;
|
|
|
|
// [A] is that ever valid?
|
|
if (gHaveStoredData)
|
|
{
|
|
header->HaveStoredData = 0x91827364; // -0x6e7d8c9c
|
|
memcpy((u_char*)pt, (u_char*)&MissionStartData, sizeof(MISSION_DATA));
|
|
}
|
|
|
|
return pt - buffer;
|
|
}
|
|
|
|
// saves chase to file
|
|
int CutRec_SaveReplayToFile(char* filename)
|
|
{
|
|
int size = CutRec_SaveReplayToBuffer((char*)_other_buffer);
|
|
|
|
FILE* fp = fopen(filename, "wb");
|
|
if (fp)
|
|
{
|
|
fwrite((char*)_other_buffer, 1, size, fp);
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CutRec_SaveChase()
|
|
{
|
|
char filename[64];
|
|
FILE* temp;
|
|
int cnt;
|
|
cnt = 2;
|
|
|
|
// put files to folder
|
|
sprintf(filename, "CUT%d", gCutsceneAsReplay);
|
|
_mkdir(filename);
|
|
|
|
// find suitable filename
|
|
while (cnt < 14)
|
|
{
|
|
sprintf(filename, "CUT%d/CUT%d_%d.D2RP", gCutsceneAsReplay, gCutsceneAsReplay, cnt);
|
|
|
|
if ((temp = fopen(filename, "rb")) != NULL)
|
|
{
|
|
fclose(temp);
|
|
cnt++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (cnt > 14)
|
|
return 0;
|
|
|
|
|
|
if(CutRec_SaveReplayToFile(filename))
|
|
{
|
|
printInfo("Chase replay '%s' saved\n", filename);
|
|
|
|
if (gCutsceneChaseAutoTest != 0)
|
|
gAutoTestStats[gCutsceneChaseAutoTest].saved = 1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CutRec_RecordCarPad(CAR_DATA* cp, uint* t0, char* t1, char* t2)
|
|
{
|
|
if (gCutsceneAsReplay == 0 || NoPlayerControl || (-*cp->ai.padid) != gCutsceneAsReplay_PlayerId)
|
|
return 0;
|
|
|
|
*t0 = Pads[0].mapped; // [A] padid might be wrong
|
|
*t1 = Pads[0].mapanalog[2];
|
|
*t2 = Pads[0].type & 4;
|
|
|
|
if (gStopPadReads != 0)
|
|
{
|
|
*t0 = CAR_PAD_BRAKE;
|
|
|
|
if (cp->hd.wheel_speed <= 0x9000)
|
|
*t0 = CAR_PAD_HANDBRAKE;
|
|
|
|
*t1 = 0;
|
|
*t2 = 1;
|
|
}
|
|
|
|
cjpRecord(-*cp->ai.padid, t0, t1, t2);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CutRec_RecordPad(PLAYER* pl, uint* t0, char* t1, char* t2)
|
|
{
|
|
if (gCutsceneAsReplay == 0 || NoPlayerControl || (-pl->padid) != gCutsceneAsReplay_PlayerId)
|
|
return 0;
|
|
|
|
*t0 = Pads[0].mapped;
|
|
*t1 = Pads[0].mapanalog[2];
|
|
*t2 = Pads[0].type & 4;
|
|
|
|
// [A] handle REDRIVER2 dedicated car entry button
|
|
if (*t0 & TANNER_PAD_ACTION_DED)
|
|
{
|
|
*t0 &= ~TANNER_PAD_ACTION_DED;
|
|
*t0 |= TANNER_PAD_ACTION;
|
|
}
|
|
|
|
cjpRecord(-pl->padid, t0, t1, t2);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int CutRec_IsRecording()
|
|
{
|
|
if (gCutsceneAsReplay == 0)
|
|
return 0;
|
|
|
|
return CurrentGameMode != GAMEMODE_REPLAY;
|
|
}
|
|
|
|
int CutRec_IsPlaying()
|
|
{
|
|
if (gCutsceneAsReplay == 0)
|
|
return 0;
|
|
|
|
return CurrentGameMode == GAMEMODE_REPLAY;
|
|
}
|
|
|
|
#endif // CUTSCENE_RECORDER
|