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:
parent
fb507ce676
commit
1612c6a2b8
|
@ -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
|
||||||
|
|
|
@ -38,28 +38,43 @@ 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);
|
||||||
|
|
||||||
FrameSkipping();
|
if(g_framesToSkip)
|
||||||
|
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()) {
|
||||||
} else if(IsPlayingInput()) {
|
fread(g_padStates, sizeof(ControllerState), g_numPads, g_recordfd);
|
||||||
// TODO
|
|
||||||
|
// 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) {
|
void SetFrameSkipping(unsigned int framesToSkip) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,8 +509,29 @@ 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();
|
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))
|
void CFrame::OnPlay(wxCommandEvent& WXUNUSED (event))
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue