#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 "STRINGS.H" #include "RAND.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[8]; 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; int i; 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 = (uint)ReplayStart - (uint)bufferEnd - CalcInGameCutsceneSize(); 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 #ifdef CUTSCENE_RECORDER extern int gCutsceneAsReplay; int numStreams = gCutsceneAsReplay ? NumReplayStreams : NumPlayers; for (int i = 0; i < numStreams; i++) #else for (int i = 0; i < NumPlayers; i++) #endif { 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(pt, (u_char*)srcStream->InitialPadRecordBuffer, size); pt += size; } #ifdef CUTSCENE_RECORDER if (gCutsceneAsReplay == 0) #endif { memcpy(pt, (u_char*)ReplayParameterPtr, sizeof(REPLAY_PARAMETER_BLOCK)); pt += sizeof(REPLAY_PARAMETER_BLOCK); } #ifdef CUTSCENE_RECORDER if (gCutsceneAsReplay == 0) #endif { memcpy(pt, (u_char*)PlayerWayRecordPtr, sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS); pt += sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS; } memcpy(pt, (u_char*)PlaybackCamera, sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS); pt += sizeof(PLAYBACKCAMERA) * MAX_REPLAY_CAMERAS; memcpy(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(pt, (u_char*)&MissionStartData, sizeof(MISSION_DATA)); } #ifdef PSX return 0x3644; // size? #else return pt - buffer; #endif } #ifdef CUTSCENE_RECORDER #include "../utils/ini.h" int gCutsceneAsReplay = 0; int gCutsceneAsReplay_PlayerId = 0; int gCutsceneAsReplay_PlayerChanged = 0; int gCutsceneAsReplay_ReserveSlots = 2; char gCutsceneRecorderPauseText[64] = { 0 }; char gCurrentChasePauseText[64] = { 0 }; void 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); } void NextCutsceneRecorderPlayer(int dir) { int old_player = gCutsceneAsReplay_PlayerId; if(dir > 0) gCutsceneAsReplay_PlayerId++; else if(dir < 0) gCutsceneAsReplay_PlayerId--; if (gCutsceneAsReplay_PlayerId >= NumReplayStreams) gCutsceneAsReplay_PlayerId -= NumReplayStreams; else if (gCutsceneAsReplay_PlayerId < 0) gCutsceneAsReplay_PlayerId += 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; NextCutsceneRecorderPlayer(0); CUTSCENE_HEADER header; char filename[64]; if (gCutsceneAsReplay < 21) sprintf(filename, "REPLAYS\\CUT%d.R", gCutsceneAsReplay); else sprintf(filename, "REPLAYS\\A\\CUT%d.R", gCutsceneAsReplay); 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; printWarning("cutscene size: %d\n", size); LoadfileSeg(filename, (char*)_other_buffer, offset, size); int result = LoadReplayFromBuffer((char*)_other_buffer); return result; } } printError("Invalid cutscene subindex or mission!\n"); return 0; } void LoadCutsceneRecorder(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(!LoadCutsceneAsReplay(subindex)) { ini_free(config); gLoadedReplay = 0; gCutsceneAsReplay = 0; return; } gLoadedReplay = 1; CurrentGameMode = GAMEMODE_REPLAY; } else { int i; char curStreamName[40]; STREAM_SOURCE* stream; InitPadRecording(); ini_sget(config, "settings", "streams", "%d", &NumReplayStreams); // initialize all streams for(i = 0; i < 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); LaunchGame(); gLoadedReplay = 0; gCutsceneAsReplay = 0; } #endif // CUTSCENE_RECORDER // [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; #ifdef CUTSCENE_RECORDER if(gCutsceneAsReplay == 0) #endif 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 #ifdef CUTSCENE_RECORDER if (gCutsceneAsReplay) { AllocateReplayStream(destStream, 4000); // copy pad data and advance buffer memcpy((u_char*)destStream->PadRecordBuffer, pt, size); } else #endif { 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(replayptr, pt, size); replayptr += size; } pt += size; destStream->length = sheader->Length; if (sheader->Length > maxLength) maxLength = sheader->Length; } ReplayParameterPtr = (REPLAY_PARAMETER_BLOCK *)replayptr; #ifdef CUTSCENE_RECORDER if (gCutsceneAsReplay != 0) { memset(ReplayParameterPtr, 0, sizeof(REPLAY_PARAMETER_BLOCK)); ReplayParameterPtr->RecordingEnd = maxLength; } else #endif { memcpy((u_char*)ReplayParameterPtr, pt, sizeof(REPLAY_PARAMETER_BLOCK)); pt += sizeof(REPLAY_PARAMETER_BLOCK); } PlayerWayRecordPtr = (SXYPAIR *)(ReplayParameterPtr + 1); #ifdef CUTSCENE_RECORDER if (gCutsceneAsReplay != 0) { memset(PlayerWayRecordPtr, 0, sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS); } else #endif { memcpy((u_char*)PlayerWayRecordPtr, pt, sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS); pt += sizeof(SXYPAIR) * MAX_REPLAY_WAYPOINTS; } PlaybackCamera = (PLAYBACKCAMERA *)(PlayerWayRecordPtr + MAX_REPLAY_WAYPOINTS); memcpy((u_char*)PlaybackCamera, 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, 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, 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; pp = PingBuffer + PingBufferPos; if (PingBuffer != NULL && PingBufferPos < MAX_REPLAY_PINGS) { if (pp->frame != 0xffff) { if ((CameraCnt - frameStart & 0xffffU) < pp->frame) return -1; retCarId = pp->carId; *cookieCount = pp->cookieCount; PingBufferPos++; } else { printInfo("-1 frame!\n"); } return retCarId; } return -1; } // [A] Stores ping info into replay buffer int StorePingInfo(int cookieCount, int carId) { #ifdef CUTSCENE_RECORDER PING_PACKET* packet; //extern int gCutsceneAsReplay; if (gCutsceneAsReplay == 0) return 0; if (CurrentGameMode == GAMEMODE_REPLAY || gInGameChaseActive != 0) return 0; if(PingBuffer != NULL && PingBufferPos < MAX_REPLAY_PINGS) { packet = &PingBuffer[PingBufferPos++]; packet->frame = (CameraCnt - frameStart & 0xffffU); packet->carId = carId; packet->cookieCount = cookieCount; return 1; } #endif return 0; } // [A] returns 1 if can use ping buffer int IsPingInfoAvailable() { // [A] loaded replays pings temporarily disabled... if (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) { uint 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 *)(((uint)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++; } }