REDRIVER2/src_rebuild/Game/C/replays.c

625 lines
13 KiB
C

#include "driver2.h"
#include "replays.h"
#include "map.h"
#include "spool.h"
#include "system.h"
#include "cutscene.h"
#include "players.h"
#include "glaunch.h"
#include "mission.h"
#include "director.h"
#include "camera.h"
#include "civ_ai.h"
#include "cutrecorder.h"
char AnalogueUnpack[16] = {
0, -51, -63, -75, -87, -99, -111, -123,
0, 51, 63, 75, 87, 99, 111, 123
};
int gOutOfTape = 0;
REPLAY_PARAMETER_BLOCK *ReplayParameterPtr = NULL;
REPLAY_STREAM ReplayStreams[MAX_REPLAY_STREAMS];
int NumReplayStreams = 1;
char *ReplayStart;
char *replayptr = NULL;
int ReplaySize = 0;
int PingBufferPos = 0;
PING_PACKET *PingBuffer = NULL;
SXYPAIR *PlayerWayRecordPtr = NULL;
PLAYBACKCAMERA *PlaybackCamera = NULL;
int TimeToWay = 0;
short PlayerWaypoints = 0;
int way_distance = 0;
int gUseStoredPings = 1;
char ReplayMode = 0;
#define REPLAY_IDENT (('D' << 24) | ('2' << 16) | ('R' << 8) | 'P' )
// [D] [T]
void InitPadRecording(void)
{
char *bufferEnd;
int remain, cutsSize;
int i;
// initialize chases
cutsSize = CalcInGameCutsceneSize();
gOutOfTape = 0;
if (gLoadedReplay == 0 &&
CurrentGameMode != GAMEMODE_REPLAY &&
CurrentGameMode != GAMEMODE_DIRECTOR)
{
NumReplayStreams = 0;
ReplayStart = (char*)_replay_buffer;
ReplayParameterPtr = (REPLAY_PARAMETER_BLOCK *)ReplayStart;
PlayerWayRecordPtr = (SXYPAIR *)(ReplayParameterPtr + 1);
PlaybackCamera = (PLAYBACKCAMERA *)(PlayerWayRecordPtr + MAX_REPLAY_WAYPOINTS);
PingBuffer = (PING_PACKET *)(PlaybackCamera + MAX_REPLAY_CAMERAS);
setMem8((u_char*)PingBuffer, -1, sizeof(PING_PACKET) * MAX_REPLAY_PINGS);
replayptr = (char*)(PingBuffer + MAX_REPLAY_PINGS);
// FIXME: is that correct?
bufferEnd = replayptr-13380;
remain = (u_int)ReplayStart - (u_int)bufferEnd - cutsSize;
for (i = 0; i < NumPlayers; i++)
{
AllocateReplayStream(&ReplayStreams[i], remain / sizeof(PADRECORD) / NumPlayers);
NumReplayStreams++;
}
ReplaySize = (replayptr - ReplayStart);
}
else
{
// reset stream count as cutscene/chase can increase it
NumReplayStreams = NumPlayers;
for (i = 0; i < NumReplayStreams; i++)
{
ReplayStreams[i].playbackrun = 0;
ReplayStreams[i].PadRecordBuffer = ReplayStreams[i].InitialPadRecordBuffer;
}
}
TimeToWay = 150;
PlayerWaypoints = 0;
way_distance = 100;
InitDirectorVariables();
InitInGameCutsceneVariables();
}
// [D] [T]
int 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 < NumPlayers; 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); // srcStream->PadRecordBufferEnd - srcStream->InitialPadRecordBuffer;
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*)ReplayParameterPtr, sizeof(REPLAY_PARAMETER_BLOCK));
pt += sizeof(REPLAY_PARAMETER_BLOCK);
memcpy((u_char*)pt, (u_char*)PlayerWayRecordPtr, sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS);
pt += sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS;
memcpy((u_char*)pt, (u_char*)PlaybackCamera, sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS);
pt += sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS;
memcpy((u_char*)pt, (u_char*)PingBuffer, 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));
}
#ifdef PSX
return 0x3644; // size?
#else
return pt - buffer;
#endif
}
// [D] [T]
int LoadReplayFromBuffer(char *buffer)
{
REPLAY_SAVE_HEADER *header;
REPLAY_STREAM_HEADER *sheader;
char* pt = buffer;
header = (REPLAY_SAVE_HEADER*)pt;
if (header->magic != DRIVER2_REPLAY_MAGIC)
return 0;
ReplayStart = replayptr = (char*)_replay_buffer;
GameLevel = header->GameLevel;
GameType = (GAMETYPE)header->GameType;
gCurrentMissionNumber = header->MissionNumber;
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
destStream->InitialPadRecordBuffer = (PADRECORD*)replayptr;
destStream->PadRecordBuffer = (PADRECORD*)replayptr;
destStream->PadRecordBufferEnd = (PADRECORD*)(replayptr + size);
destStream->playbackrun = 0;
destStream->padCount = sheader->Size / sizeof(PADRECORD);
// copy pad data and advance buffer
memcpy((u_char*)replayptr, (u_char*)pt, size);
replayptr += size;
pt += size;
destStream->length = sheader->Length;
if (sheader->Length > maxLength)
maxLength = sheader->Length;
}
ReplayParameterPtr = (REPLAY_PARAMETER_BLOCK *)replayptr;
memcpy((u_char*)ReplayParameterPtr, (u_char*)pt, sizeof(REPLAY_PARAMETER_BLOCK));
pt += sizeof(REPLAY_PARAMETER_BLOCK);
PlayerWayRecordPtr = (SXYPAIR *)(ReplayParameterPtr + 1);
memcpy((u_char*)PlayerWayRecordPtr, (u_char*)pt, sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS);
pt += 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;
}
return 1;
}
#ifndef PSX
int LoadUserAttractReplay(int mission, int userId)
{
char customFilename[64];
if (userId >= 0 && userId < gNumUserChases)
{
sprintf(customFilename, "REPLAYS\\User\\%s\\ATTRACT.%d", gUserReplayFolderList[userId], mission);
if (FileExists(customFilename))
{
if (Loadfile(customFilename, (char*)_other_buffer))
return LoadReplayFromBuffer((char*)_other_buffer);
}
}
return 0;
}
#endif
// [D] [T]
int LoadAttractReplay(int mission)
{
char filename[32];
#ifndef PSX
int userId = -1;
// [A] REDRIVER2 PC - custom attract replays
if (gNumUserChases)
{
userId = rand() % (gNumUserChases + 1);
if (userId == gNumUserChases)
userId = -1;
}
if (LoadUserAttractReplay(mission, userId))
{
printInfo("Loaded custom attract replay (%d) by %s\n", mission, gUserReplayFolderList[userId]);
return 1;
}
#endif
sprintf(filename, "REPLAYS\\ATTRACT.%d", mission);
if (!FileExists(filename))
return 0;
if (!Loadfile(filename, (char*)_other_buffer))
return 0;
return LoadReplayFromBuffer((char*)_other_buffer);
}
// [D] [T]
char GetPingInfo(char *cookieCount)
{
char retCarId;
PING_PACKET *pp;
retCarId = -1;
if (PingBuffer && PingBufferPos < MAX_REPLAY_PINGS)
{
pp = &PingBuffer[PingBufferPos];
// accept only valid car pings
if (pp->frame != 0xFFFF)
{
if (CameraCnt - frameStart < pp->frame)
return -1;
retCarId = pp->carId;
*cookieCount = pp->cookieCount;
}
PingBufferPos++;
}
return retCarId;
}
// [A] returns 1 if can use ping buffer
int IsPingInfoAvailable()
{
if (!_CutRec_IsPlaying() && (gUseStoredPings == 0 || gInGameChaseActive == 0))// && gLoadedReplay == 0)
return 0;
return PingBuffer != NULL && PingBufferPos < MAX_REPLAY_PINGS;
}
// [D] [T]
int valid_region(int x, int z)
{
XYPAIR region_coords;
int region;
region_coords.x = (x >> 16) + regions_across / 2;
region_coords.y = (z >> 16) + regions_down / 2;
if (region_coords.x >= 0 && region_coords.x <= regions_across &&
region_coords.y >= 0 && region_coords.y <= regions_down)
{
region = region_coords.x + region_coords.y * regions_across;
if (region != regions_unpacked[0])
{
if (region == regions_unpacked[1])
return region + 1;
if (region == regions_unpacked[2])
return region + 1;
if (region != regions_unpacked[3])
return 0;
}
return region + 1;
}
return 0;
}
// [D] [T]
int Get(int stream, u_int *pt0)
{
REPLAY_STREAM* rstream;
if (stream < NumReplayStreams)
{
rstream = &ReplayStreams[stream];
if (rstream->PadRecordBuffer + 1 <= rstream->PadRecordBufferEnd)
{
u_int t0 = (rstream->PadRecordBuffer->pad << 8) | rstream->PadRecordBuffer->analogue;
*pt0 = t0;
if (rstream->playbackrun < rstream->PadRecordBuffer->run)
{
rstream->playbackrun++;
}
else
{
rstream->PadRecordBuffer++;
rstream->playbackrun = 0;
}
return 1;
}
}
*pt0 = 0x10;
return 0;
}
// [D] [T]
int Put(int stream, u_int*pt0)
{
REPLAY_STREAM *rstream;
u_int t0;
PADRECORD *padbuf;
rstream = &ReplayStreams[stream];
if (rstream->PadRecordBuffer + 1 >= rstream->PadRecordBufferEnd)
return 0;
padbuf = rstream->PadRecordBuffer;
t0 = *pt0;
if (CameraCnt != 0 && padbuf->run != 0xEE)
{
if (padbuf->pad == ((t0 >> 8) & 0xff) &&
padbuf->analogue == (t0 & 0xff) &&
padbuf->run != 143)
{
padbuf->run++;
return 1;
}
padbuf++;
padbuf->pad = (t0 >> 8) & 0xFF;
padbuf->analogue = t0 & 0xFF;
padbuf->run = 0;
rstream->PadRecordBuffer = padbuf;
rstream->padCount++;
return 1;
}
padbuf->pad = (t0 >> 8) & 0xFF;
padbuf->analogue = t0 & 0xFF;
padbuf->run = 0;
return 1;
}
// [D] [T]
int cjpPlay(int stream, u_int *ppad, char *psteer, char *ptype)
{
int ret;
int t1;
u_int t0;
#ifdef CUTSCENE_RECORDER
if (stream < 0)
stream = -stream;
#endif
ret = Get(stream, &t0);
t1 = (t0 >> 8) & 0xF;
*ppad = t0 & 0xF0FC;
if (t1 == 0)
{
*psteer = 0;
*ptype = 0;
}
else
{
*psteer = AnalogueUnpack[t1];
*ptype = 4;
}
return ret;
}
// [D] [T]
void cjpRecord(int stream, u_int *ppad, char *psteer, char *ptype)
{
int tmp;
int t1;
u_int t0;
if (stream > -1 && stream < NumReplayStreams)
{
RecordWaypoint();
if ((*ptype & 4U) == 0)
{
t1 = 0;
}
else
{
if (*psteer < -45)
{
tmp = -45 - *psteer >> 0x1f; // [A] still need to figure out this
t1 = (((-45 - *psteer) / 6 + tmp >> 1) - tmp) + 1;
}
else if (*psteer < 46)
{
t1 = 8;
}
else
{
tmp = *psteer - 45 >> 0x1f; // [A] still need to figure out this
t1 = (((*psteer - 45) / 6 + tmp >> 1) - tmp) + 9;
}
}
t0 = (t1 & 0xF) << 8 | *ppad & 0xF0FC;
if (Put(stream, &t0) == 0)
{
gOutOfTape = 1;
}
else if(NoPlayerControl == 0)
{
ClearCameras = 1;
if (ReplayMode != 3 && ReplayMode != 8)
{
ReplayStreams[stream].length = CameraCnt;
ReplayParameterPtr->RecordingEnd = CameraCnt;
}
}
t1 = (t0 >> 8) & 0xF;
if (t1 == 0)
{
*psteer = 0;
*ptype = 0;
}
else
{
*psteer = AnalogueUnpack[t1];
*ptype = 4;
}
*ppad = t0 & 0xF0FC;
}
}
// [D] [T]
void AllocateReplayStream(REPLAY_STREAM *stream, int maxpad)
{
stream->playbackrun = 0;
stream->length = 0;
stream->padCount = 0;
stream->InitialPadRecordBuffer = (PADRECORD*)replayptr;
stream->PadRecordBuffer = (PADRECORD*)replayptr;
stream->PadRecordBufferEnd = (PADRECORD *)(replayptr + maxpad * sizeof(PADRECORD));
if (NoPlayerControl == 0)
{
*replayptr = 0;
stream->InitialPadRecordBuffer->analogue = 0;
stream->InitialPadRecordBuffer->run = 238;
}
replayptr = (char *)(((u_int)replayptr + (maxpad+1) * sizeof(PADRECORD)) & -4);
}
// [D] [T]
void RecordWaypoint(void)
{
if (TimeToWay > 0)
{
TimeToWay--;
return;
}
if (PlayerWaypoints < MAX_REPLAY_WAYPOINTS)
{
TimeToWay = way_distance;
PlayerWayRecordPtr->x = (player[0].pos[0] >> 10);
PlayerWayRecordPtr->y = (player[0].pos[2] >> 10);
PlayerWayRecordPtr++;
PlayerWaypoints++;
}
}