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
This commit is contained in:
XTra.KrazzY 2009-08-21 19:55:03 +00:00
parent fb507ce676
commit 1612c6a2b8
7 changed files with 163 additions and 26 deletions

View File

@ -148,6 +148,8 @@ CSIDevice_GCController::GetData(u32& _Hi, u32& _Low)
} }
#endif #endif
Frame::SetPolledDevice();
if(Frame::IsPlayingInput()) if(Frame::IsPlayingInput())
Frame::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber); Frame::PlayController(&PadStatus, ISIDevice::m_iDeviceNumber);
else else

View File

@ -38,29 +38,44 @@ int g_numPads = 0;
ControllerState *g_padStates; ControllerState *g_padStates;
FILE *g_recordfd = NULL; FILE *g_recordfd = NULL;
u64 g_frameCounter = 0; u64 g_frameCounter = 0, g_lagCounter = 0;
bool g_bPolled = false;
void FrameUpdate() { void FrameUpdate() {
g_frameCounter++; g_frameCounter++;
if(!g_bPolled)
g_lagCounter++;
if (g_bFrameStep) if (g_bFrameStep)
Core::SetState(Core::CORE_PAUSE); Core::SetState(Core::CORE_PAUSE);
if(g_framesToSkip)
FrameSkipping(); FrameSkipping();
if (g_bAutoFire) if (g_bAutoFire)
g_bFirstKey = !g_bFirstKey; g_bFirstKey = !g_bFirstKey;
if(IsRecordingInput()) { // Dump/Read all controllers' states for this frame
if(g_bPolled) {
// Dump all controllers' states for this frame if(IsRecordingInput())
fwrite(g_padStates, sizeof(ControllerState), g_numPads, g_recordfd); fwrite(g_padStates, sizeof(ControllerState), g_numPads, g_recordfd);
else if(IsPlayingInput()) {
fread(g_padStates, sizeof(ControllerState), g_numPads, g_recordfd);
} else if(IsPlayingInput()) { // End of recording
// TODO 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) { void SetFrameSkipping(unsigned int framesToSkip) {
cs_frameSkip.Enter(); cs_frameSkip.Enter();
@ -75,6 +90,10 @@ int FrameSkippingFactor() {
return g_framesToSkip; return g_framesToSkip;
} }
void SetPolledDevice() {
g_bPolled = true;
}
void SetAutoHold(bool bEnabled, u32 keyToHold) void SetAutoHold(bool bEnabled, u32 keyToHold)
{ {
g_bAutoFire = bEnabled; g_bAutoFire = bEnabled;
@ -161,18 +180,18 @@ bool IsPlayingInput()
} }
// TODO: Add BeginRecordingFromSavestate // 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) if(!filename || g_playMode != MODE_NONE || g_recordfd)
return; return false;
if(File::Exists(filename)) if(File::Exists(filename))
File::Delete(filename); File::Delete(filename);
g_recordfd = fopen(filename, "wb+"); g_recordfd = fopen(filename, "wb");
if(!g_recordfd) { if(!g_recordfd) {
PanicAlert("Error opening file %s for recording", filename); PanicAlert("Error opening file %s for recording", filename);
return; return false;
} }
// Write initial empty header // Write initial empty header
@ -183,8 +202,11 @@ void BeginRecordingInput(const char *filename, int controllers)
g_padStates = new ControllerState[controllers]; g_padStates = new ControllerState[controllers];
g_frameCounter = 0; g_frameCounter = 0;
g_lagCounter = 0;
g_playMode = MODE_RECORDING; g_playMode = MODE_RECORDING;
return true;
} }
void EndRecordingInput() void EndRecordingInput()
@ -201,9 +223,9 @@ void EndRecordingInput()
header.bFromSaveState = false; // TODO: add the case where it's true header.bFromSaveState = false; // TODO: add the case where it's true
header.frameCount = g_frameCounter; header.frameCount = g_frameCounter;
header.lagCount = g_lagCounter;
// TODO // TODO
header.lagCount = 0;
header.uniqueID = 0; header.uniqueID = 0;
header.numRerecords = 0; header.numRerecords = 0;
header.author; header.author;
@ -249,18 +271,100 @@ void RecordInput(SPADStatus *PadStatus, int controllerID)
g_padStates[controllerID].CStickY = PadStatus->substickY; g_padStates[controllerID].CStickY = PadStatus->substickY;
} }
void PlayInput(const char *filename) bool PlayInput(const char *filename)
{ {
// TODO: Implement if(!filename || g_playMode != MODE_NONE || g_recordfd)
// TODO: Add the play from savestate case 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) void PlayController(SPADStatus *PadStatus, int controllerID)
{ {
// TODO: Implement
if(!IsPlayingInput() || controllerID >= g_numPads || controllerID < 0) if(!IsPlayingInput() || controllerID >= g_numPads || controllerID < 0)
return; 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;
} }
}; };

View File

@ -69,6 +69,8 @@ typedef struct {
void FrameUpdate(); void FrameUpdate();
void SetPolledDevice();
bool IsAutoFiring(); bool IsAutoFiring();
bool IsRecordingInput(); bool IsRecordingInput();
bool IsPlayingInput(); bool IsPlayingInput();
@ -84,11 +86,11 @@ void FrameSkipping();
void ModifyController(SPADStatus *PadStatus, int controllerID); 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 RecordInput(SPADStatus *PadStatus, int controllerID);
void EndRecordingInput(); void EndRecordingInput();
void PlayInput(const char *filename); bool PlayInput(const char *filename);
void PlayController(SPADStatus *PadStatus, int controllerID); void PlayController(SPADStatus *PadStatus, int controllerID);
}; };

View File

@ -236,6 +236,7 @@ EVT_MENU(IDM_HELPABOUT, CFrame::OnHelp)
EVT_MENU(wxID_REFRESH, CFrame::OnRefresh) EVT_MENU(wxID_REFRESH, CFrame::OnRefresh)
EVT_MENU(IDM_PLAY, CFrame::OnPlay) EVT_MENU(IDM_PLAY, CFrame::OnPlay)
EVT_MENU(IDM_RECORD, CFrame::OnRecord) EVT_MENU(IDM_RECORD, CFrame::OnRecord)
EVT_MENU(IDM_PLAYRECORD, CFrame::OnPlayRecording)
EVT_MENU(IDM_STOP, CFrame::OnStop) EVT_MENU(IDM_STOP, CFrame::OnStop)
EVT_MENU(IDM_SCREENSHOT, CFrame::OnScreenshot) EVT_MENU(IDM_SCREENSHOT, CFrame::OnScreenshot)
EVT_MENU(IDM_CONFIG_MAIN, CFrame::OnConfigMain) EVT_MENU(IDM_CONFIG_MAIN, CFrame::OnConfigMain)

View File

@ -159,6 +159,7 @@ class CFrame : public wxFrame
void OnPlay(wxCommandEvent& event); // Emulation void OnPlay(wxCommandEvent& event); // Emulation
void OnRecord(wxCommandEvent& event); void OnRecord(wxCommandEvent& event);
void OnPlayRecording(wxCommandEvent& event);
void OnChangeDisc(wxCommandEvent& event); void OnChangeDisc(wxCommandEvent& event);
void OnStop(wxCommandEvent& event); void OnStop(wxCommandEvent& event);
void OnScreenshot(wxCommandEvent& event); void OnScreenshot(wxCommandEvent& event);

View File

@ -129,9 +129,13 @@ void CFrame::CreateMenu()
// Emulation menu // Emulation menu
wxMenu* emulationMenu = new wxMenu; wxMenu* emulationMenu = new wxMenu;
emulationMenu->Append(IDM_PLAY, _T("&Play\tF10")); 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->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; wxMenu *skippingMenu = new wxMenu;
m_pSubMenuFrameSkipping = emulationMenu->AppendSubMenu(skippingMenu, _T("&Frame Skipping")); m_pSubMenuFrameSkipping = emulationMenu->AppendSubMenu(skippingMenu, _T("&Frame Skipping"));
@ -505,7 +509,28 @@ void CFrame::OnRecord(wxCommandEvent& WXUNUSED (event))
return; return;
// TODO: Take controller settings from Gamecube Configuration menu // TODO: Take controller settings from Gamecube Configuration menu
Frame::BeginRecordingInput(path.mb_str(), 1); 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(); BootGame();
} }
@ -858,7 +883,8 @@ void CFrame::UpdateGUI()
// Emulation // Emulation
GetMenuBar()->FindItem(IDM_STOP)->Enable(running || paused); 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); GetMenuBar()->FindItem(IDM_SCREENSHOT)->Enable(running || paused);
m_pSubMenuLoad->Enable(initialized); m_pSubMenuLoad->Enable(initialized);
m_pSubMenuSave->Enable(initialized); m_pSubMenuSave->Enable(initialized);

View File

@ -63,6 +63,7 @@ enum
IDM_FRAMESKIP9, IDM_FRAMESKIP9,
IDM_PLAY, IDM_PLAY,
IDM_RECORD, IDM_RECORD,
IDM_PLAYRECORD,
IDM_STOP, IDM_STOP,
IDM_SCREENSHOT, IDM_SCREENSHOT,
IDM_BROWSE, IDM_BROWSE,