From 1612c6a2b8386f874af320620213aae501c6521c Mon Sep 17 00:00:00 2001 From: "XTra.KrazzY" Date: Fri, 21 Aug 2009 19:55:03 +0000 Subject: [PATCH] TAS GC Recording/Playing with GUI! Yay! (Bonus: Lag Counter) Works with frameskipping/throttling and so forth. Only one bug: Every subsequent play plays the same, regardless of framerate, frameskipping and throttling. The only problem is that the recording and the plays act differently, with analog sticks ONLY. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4028 8ced0084-cf51-0410-be5f-012b33b47a6e --- .../Core/Src/HW/SI_DeviceGCController.cpp | 2 + Source/Core/Core/Src/OnFrame.cpp | 142 +++++++++++++++--- Source/Core/Core/Src/OnFrame.h | 6 +- Source/Core/DolphinWX/Src/Frame.cpp | 1 + Source/Core/DolphinWX/Src/Frame.h | 1 + Source/Core/DolphinWX/Src/FrameTools.cpp | 36 ++++- Source/Core/DolphinWX/Src/Globals.h | 1 + 7 files changed, 163 insertions(+), 26 deletions(-) diff --git a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp index 32be6e6532..76dbea22f0 100644 --- a/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp +++ b/Source/Core/Core/Src/HW/SI_DeviceGCController.cpp @@ -148,6 +148,8 @@ CSIDevice_GCController::GetData(u32& _Hi, u32& _Low) } #endif + Frame::SetPolledDevice(); + if(Frame::IsPlayingInput()) Frame::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber); else diff --git a/Source/Core/Core/Src/OnFrame.cpp b/Source/Core/Core/Src/OnFrame.cpp index 56928a38d9..a6a39aaba4 100644 --- a/Source/Core/Core/Src/OnFrame.cpp +++ b/Source/Core/Core/Src/OnFrame.cpp @@ -38,28 +38,43 @@ int g_numPads = 0; ControllerState *g_padStates; FILE *g_recordfd = NULL; -u64 g_frameCounter = 0; +u64 g_frameCounter = 0, g_lagCounter = 0; +bool g_bPolled = false; void FrameUpdate() { - g_frameCounter++; + if(!g_bPolled) + g_lagCounter++; + if (g_bFrameStep) Core::SetState(Core::CORE_PAUSE); - FrameSkipping(); + if(g_framesToSkip) + FrameSkipping(); if (g_bAutoFire) g_bFirstKey = !g_bFirstKey; - if(IsRecordingInput()) { - - // Dump all controllers' states for this frame - fwrite(g_padStates, sizeof(ControllerState), g_numPads, g_recordfd); - - } else if(IsPlayingInput()) { - // TODO + // Dump/Read all controllers' states for this frame + if(g_bPolled) { + if(IsRecordingInput()) + fwrite(g_padStates, sizeof(ControllerState), g_numPads, g_recordfd); + else if(IsPlayingInput()) { + fread(g_padStates, sizeof(ControllerState), g_numPads, g_recordfd); + + // End of recording + if(feof(g_recordfd)) { + fclose(g_recordfd); + g_recordfd = NULL; + g_numPads = 0; + delete[] g_padStates; + g_playMode = MODE_NONE; + } + } } + + g_bPolled = false; } void SetFrameSkipping(unsigned int framesToSkip) { @@ -75,6 +90,10 @@ int FrameSkippingFactor() { return g_framesToSkip; } +void SetPolledDevice() { + g_bPolled = true; +} + void SetAutoHold(bool bEnabled, u32 keyToHold) { g_bAutoFire = bEnabled; @@ -161,18 +180,18 @@ bool IsPlayingInput() } // TODO: Add BeginRecordingFromSavestate -void BeginRecordingInput(const char *filename, int controllers) +bool BeginRecordingInput(const char *filename, int controllers) { if(!filename || g_playMode != MODE_NONE || g_recordfd) - return; + return false; if(File::Exists(filename)) File::Delete(filename); - g_recordfd = fopen(filename, "wb+"); + g_recordfd = fopen(filename, "wb"); if(!g_recordfd) { PanicAlert("Error opening file %s for recording", filename); - return; + return false; } // Write initial empty header @@ -183,8 +202,11 @@ void BeginRecordingInput(const char *filename, int controllers) g_padStates = new ControllerState[controllers]; g_frameCounter = 0; + g_lagCounter = 0; g_playMode = MODE_RECORDING; + + return true; } void EndRecordingInput() @@ -201,9 +223,9 @@ void EndRecordingInput() header.bFromSaveState = false; // TODO: add the case where it's true header.frameCount = g_frameCounter; + header.lagCount = g_lagCounter; // TODO - header.lagCount = 0; header.uniqueID = 0; header.numRerecords = 0; header.author; @@ -249,18 +271,100 @@ void RecordInput(SPADStatus *PadStatus, int controllerID) g_padStates[controllerID].CStickY = PadStatus->substickY; } -void PlayInput(const char *filename) +bool PlayInput(const char *filename) { - // TODO: Implement - // TODO: Add the play from savestate case + if(!filename || g_playMode != MODE_NONE || g_recordfd) + return false; + + if(!File::Exists(filename)) + return false; + + DTMHeader header; + + g_recordfd = fopen(filename, "rb"); + if(!g_recordfd) + return false; + + fread(&header, sizeof(DTMHeader), 1, g_recordfd); + + // Load savestate (and skip to frame data) + if(header.bFromSaveState) { + // TODO + } + + /* TODO: Put this verification somewhere we have the gameID of the played game + // TODO: Replace with Unique ID + if(header.uniqueID != 0) { + PanicAlert("Recording Unique ID Verification Failed"); + goto cleanup; + } + + if(strncmp((char *)header.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6)) { + PanicAlert("The recorded game (%s) is not the same as the selected game (%s)", header.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str()); + goto cleanup; + } + */ + + g_numPads = header.numControllers; + g_padStates = new ControllerState[g_numPads]; + + g_playMode = MODE_PLAYING; + + return true; + +cleanup: + fclose(g_recordfd); + g_recordfd = NULL; + return false; } void PlayController(SPADStatus *PadStatus, int controllerID) { - // TODO: Implement if(!IsPlayingInput() || controllerID >= g_numPads || controllerID < 0) return; + memset(PadStatus, 0, sizeof(SPADStatus)); + + PadStatus->button |= PAD_USE_ORIGIN; + + if(g_padStates[controllerID].A) { + PadStatus->button |= PAD_BUTTON_A; + PadStatus->analogA = 0xFF; + } + if(g_padStates[controllerID].B) { + PadStatus->button |= PAD_BUTTON_B; + PadStatus->analogB = 0xFF; + } + if(g_padStates[controllerID].X) + PadStatus->button |= PAD_BUTTON_X; + if(g_padStates[controllerID].Y) + PadStatus->button |= PAD_BUTTON_Y; + if(g_padStates[controllerID].Z) + PadStatus->button |= PAD_TRIGGER_Z; + if(g_padStates[controllerID].Start) + PadStatus->button |= PAD_BUTTON_START; + + if(g_padStates[controllerID].DPadUp) + PadStatus->button |= PAD_BUTTON_UP; + if(g_padStates[controllerID].DPadDown) + PadStatus->button |= PAD_BUTTON_DOWN; + if(g_padStates[controllerID].DPadLeft) + PadStatus->button |= PAD_BUTTON_LEFT; + if(g_padStates[controllerID].DPadRight) + PadStatus->button |= PAD_BUTTON_RIGHT; + + PadStatus->triggerLeft = g_padStates[controllerID].L; + if(PadStatus->triggerLeft > 230) + PadStatus->button |= PAD_TRIGGER_L; + PadStatus->triggerRight = g_padStates[controllerID].R; + if(PadStatus->triggerRight > 230) + PadStatus->button |= PAD_TRIGGER_R; + + PadStatus->stickX = g_padStates[controllerID].AnalogStickX; + PadStatus->stickY = g_padStates[controllerID].AnalogStickY; + + PadStatus->substickX = g_padStates[controllerID].CStickX; + PadStatus->substickY = g_padStates[controllerID].CStickY; } }; diff --git a/Source/Core/Core/Src/OnFrame.h b/Source/Core/Core/Src/OnFrame.h index 72bc3108b2..f2bd9eac53 100644 --- a/Source/Core/Core/Src/OnFrame.h +++ b/Source/Core/Core/Src/OnFrame.h @@ -69,6 +69,8 @@ typedef struct { void FrameUpdate(); +void SetPolledDevice(); + bool IsAutoFiring(); bool IsRecordingInput(); bool IsPlayingInput(); @@ -84,11 +86,11 @@ void FrameSkipping(); void ModifyController(SPADStatus *PadStatus, int controllerID); -void BeginRecordingInput(const char *filename, int controllers); +bool BeginRecordingInput(const char *filename, int controllers); void RecordInput(SPADStatus *PadStatus, int controllerID); void EndRecordingInput(); -void PlayInput(const char *filename); +bool PlayInput(const char *filename); void PlayController(SPADStatus *PadStatus, int controllerID); }; diff --git a/Source/Core/DolphinWX/Src/Frame.cpp b/Source/Core/DolphinWX/Src/Frame.cpp index f1d420fe07..d119c0001f 100644 --- a/Source/Core/DolphinWX/Src/Frame.cpp +++ b/Source/Core/DolphinWX/Src/Frame.cpp @@ -236,6 +236,7 @@ EVT_MENU(IDM_HELPABOUT, CFrame::OnHelp) EVT_MENU(wxID_REFRESH, CFrame::OnRefresh) EVT_MENU(IDM_PLAY, CFrame::OnPlay) EVT_MENU(IDM_RECORD, CFrame::OnRecord) +EVT_MENU(IDM_PLAYRECORD, CFrame::OnPlayRecording) EVT_MENU(IDM_STOP, CFrame::OnStop) EVT_MENU(IDM_SCREENSHOT, CFrame::OnScreenshot) EVT_MENU(IDM_CONFIG_MAIN, CFrame::OnConfigMain) diff --git a/Source/Core/DolphinWX/Src/Frame.h b/Source/Core/DolphinWX/Src/Frame.h index b6bf177556..856dcf1f24 100644 --- a/Source/Core/DolphinWX/Src/Frame.h +++ b/Source/Core/DolphinWX/Src/Frame.h @@ -159,6 +159,7 @@ class CFrame : public wxFrame void OnPlay(wxCommandEvent& event); // Emulation void OnRecord(wxCommandEvent& event); + void OnPlayRecording(wxCommandEvent& event); void OnChangeDisc(wxCommandEvent& event); void OnStop(wxCommandEvent& event); void OnScreenshot(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp index 603d8afd90..7533b5a11b 100644 --- a/Source/Core/DolphinWX/Src/FrameTools.cpp +++ b/Source/Core/DolphinWX/Src/FrameTools.cpp @@ -129,9 +129,13 @@ void CFrame::CreateMenu() // Emulation menu wxMenu* emulationMenu = new wxMenu; emulationMenu->Append(IDM_PLAY, _T("&Play\tF10")); - emulationMenu->Append(IDM_RECORD, _T("&Start Recording")); - emulationMenu->Append(IDM_CHANGEDISC, _T("Change &Disc")); emulationMenu->Append(IDM_STOP, _T("&Stop")); + emulationMenu->AppendSeparator(); + emulationMenu->Append(IDM_RECORD, _T("Start &Recording...")); + emulationMenu->Append(IDM_PLAYRECORD, _T("P&lay Recording...")); + emulationMenu->AppendSeparator(); + emulationMenu->Append(IDM_CHANGEDISC, _T("Change &Disc")); + wxMenu *skippingMenu = new wxMenu; m_pSubMenuFrameSkipping = emulationMenu->AppendSubMenu(skippingMenu, _T("&Frame Skipping")); @@ -505,8 +509,29 @@ void CFrame::OnRecord(wxCommandEvent& WXUNUSED (event)) return; // TODO: Take controller settings from Gamecube Configuration menu - Frame::BeginRecordingInput(path.mb_str(), 1); - BootGame(); + if(Frame::BeginRecordingInput(path.mb_str(), 1)) + BootGame(); +} + +void CFrame::OnPlayRecording(wxCommandEvent& WXUNUSED (event)) +{ + wxString path = wxFileSelector( + _T("Select The Recording File"), + wxEmptyString, wxEmptyString, wxEmptyString, + wxString::Format + ( + _T("Dolphin TAS Movies (*.dtm)|*.dtm|All files (%s)|%s"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, + this); + + if(path.IsEmpty()) + return; + + if(Frame::PlayInput(path.mb_str())) + BootGame(); } void CFrame::OnPlay(wxCommandEvent& WXUNUSED (event)) @@ -858,7 +883,8 @@ void CFrame::UpdateGUI() // Emulation GetMenuBar()->FindItem(IDM_STOP)->Enable(running || paused); - GetMenuBar()->FindItem(IDM_RECORD)->Enable(!running && !paused); + GetMenuBar()->FindItem(IDM_RECORD)->Enable(!initialized); + GetMenuBar()->FindItem(IDM_PLAYRECORD)->Enable(!initialized); GetMenuBar()->FindItem(IDM_SCREENSHOT)->Enable(running || paused); m_pSubMenuLoad->Enable(initialized); m_pSubMenuSave->Enable(initialized); diff --git a/Source/Core/DolphinWX/Src/Globals.h b/Source/Core/DolphinWX/Src/Globals.h index ea1a4dbcad..a1fec1f9f5 100644 --- a/Source/Core/DolphinWX/Src/Globals.h +++ b/Source/Core/DolphinWX/Src/Globals.h @@ -63,6 +63,7 @@ enum IDM_FRAMESKIP9, IDM_PLAY, IDM_RECORD, + IDM_PLAYRECORD, IDM_STOP, IDM_SCREENSHOT, IDM_BROWSE,