From 0d110c41d9d39c33589d7ea441c34c9deed0a71b Mon Sep 17 00:00:00 2001 From: smelenchuk Date: Sat, 12 Feb 2011 02:14:20 +0000 Subject: [PATCH] Updates to rerecording behaviour: * Frame counter is restored properly post-load. * "Read-only mode" menu option added. Currently this either causes the movie file to be closed at the end of playback (if enabled) or continues recording past end of playback (if disabled). * Can now properly resume recording from a state saved during movie playback. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7142 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/Core/Src/OnFrame.cpp | 121 ++++++++++++++++------- Source/Core/Core/Src/OnFrame.h | 4 +- Source/Core/Core/Src/State.cpp | 2 +- Source/Core/DolphinWX/Src/Frame.cpp | 1 + Source/Core/DolphinWX/Src/Frame.h | 1 + Source/Core/DolphinWX/Src/FrameTools.cpp | 7 ++ Source/Core/DolphinWX/Src/Globals.h | 1 + 7 files changed, 101 insertions(+), 36 deletions(-) diff --git a/Source/Core/Core/Src/OnFrame.cpp b/Source/Core/Core/Src/OnFrame.cpp index 5cd83a1030..deb466d84f 100644 --- a/Source/Core/Core/Src/OnFrame.cpp +++ b/Source/Core/Core/Src/OnFrame.cpp @@ -26,12 +26,19 @@ #include "IPC_HLE/WII_IPC_HLE_Device_usb.h" #include "VideoBackendBase.h" +#ifdef WIN32 +#include //_chsize_s +#else +#include //truncate +#endif + Common::CriticalSection cs_frameSkip; namespace Frame { bool g_bFrameStep = false; bool g_bFrameStop = false; +bool g_bReadOnly = true; u32 g_rerecords = 0; PlayMode g_playMode = MODE_NONE; @@ -39,6 +46,7 @@ unsigned int g_framesToSkip = 0, g_frameSkipCounter = 0; int g_numPads = 0; ControllerState g_padState; +char g_playingFile[256] = "\0"; FILE *g_recordfd = NULL; u64 g_frameCounter = 0, g_lagCounter = 0; @@ -103,6 +111,11 @@ void SetFrameStopping(bool bEnabled) g_bFrameStop = bEnabled; } +void SetReadOnly(bool bEnabled) +{ + g_bReadOnly = bEnabled; +} + void FrameSkipping() { // Frameskipping will desync movie playback @@ -242,7 +255,10 @@ bool PlayInput(const char *filename) DTMHeader header; - g_recordfd = fopen(filename, "rb"); + File::Delete(g_recordFile.c_str()); + File::Copy(filename, g_recordFile.c_str()); + + g_recordfd = fopen(g_recordFile.c_str(), "r+b"); if(!g_recordfd) return false; @@ -277,6 +293,8 @@ bool PlayInput(const char *filename) ChangePads(); g_playMode = MODE_PLAYING; + + strncpy(g_playingFile, filename, 256); return true; @@ -297,6 +315,7 @@ void LoadInput(const char *filename) if(header.filetype[0] != 'D' || header.filetype[1] != 'T' || header.filetype[2] != 'M' || header.filetype[3] != 0x1A) { PanicAlertT("Savestate movie %s is corrupted, movie recording stopping...", filename); + strncpy(g_playingFile, "\0", 256); EndPlayInput(); return; } @@ -304,6 +323,8 @@ void LoadInput(const char *filename) if (g_rerecords == 0) g_rerecords = header.numRerecords; + g_frameCounter = header.frameCount; + g_numPads = header.numControllers; ChangePads(true); @@ -381,9 +402,7 @@ void PlayController(SPADStatus *PadStatus, int controllerID) if(feof(g_recordfd)) { Core::DisplayMessage("Movie End", 2000); - // TODO: read-only mode - //EndPlayInput(); - g_playMode = MODE_RECORDING; + EndPlayInput(); } } @@ -401,54 +420,88 @@ void PlayWiimote(u8 *data, s8 &size) if(feof(g_recordfd)) { Core::DisplayMessage("Movie End", 2000); - // TODO: read-only mode - //EndPlayInput(); - g_playMode = MODE_RECORDING; + EndPlayInput(); } } void EndPlayInput() { if (g_recordfd) fclose(g_recordfd); - g_recordfd = NULL; - g_numPads = g_rerecords = 0; - g_frameCounter = g_lagCounter = 0; - g_playMode = MODE_NONE; + + if (!g_bReadOnly && strncmp(g_playingFile, "\0", 1)) + { + File::Delete(g_recordFile.c_str()); + File::Copy(g_playingFile, g_recordFile.c_str()); + g_recordfd = fopen(g_recordFile.c_str(), "r+b"); + fseeko(g_recordfd, 0, SEEK_END); + g_playMode = MODE_RECORDING; + } + else + { + g_recordfd = NULL; + g_numPads = g_rerecords = 0; + g_frameCounter = g_lagCounter = 0; + g_playMode = MODE_NONE; + } } void SaveRecording(const char *filename) { - rewind(g_recordfd); + off_t size = ftello(g_recordfd); - // Create the real header now and write it - DTMHeader header; - memset(&header, 0, sizeof(DTMHeader)); + // NOTE: Eventually this will not happen in + // read-only mode, but we need a way for the save state to + // store the current point in the file first. + // if (!g_bReadOnly) + { + rewind(g_recordfd); - header.filetype[0] = 'D'; header.filetype[1] = 'T'; header.filetype[2] = 'M'; header.filetype[3] = 0x1A; - strncpy((char *)header.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6); - header.bWii = Core::g_CoreStartupParameter.bWii; - header.numControllers = g_numPads & (Core::g_CoreStartupParameter.bWii ? 0xFF : 0x0F); - - header.bFromSaveState = false; // TODO: add the case where it's true - header.frameCount = g_frameCounter; - header.lagCount = g_lagCounter; - header.numRerecords = g_rerecords; - - // TODO - header.uniqueID = 0; - // header.author; - // header.videoPlugin; - // header.audioPlugin; - - fwrite(&header, sizeof(DTMHeader), 1, g_recordfd); + // Create the real header now and write it + DTMHeader header; + memset(&header, 0, sizeof(DTMHeader)); + + header.filetype[0] = 'D'; header.filetype[1] = 'T'; header.filetype[2] = 'M'; header.filetype[3] = 0x1A; + strncpy((char *)header.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6); + header.bWii = Core::g_CoreStartupParameter.bWii; + header.numControllers = g_numPads & (Core::g_CoreStartupParameter.bWii ? 0xFF : 0x0F); + + header.bFromSaveState = false; // TODO: add the case where it's true + header.frameCount = g_frameCounter; + header.lagCount = g_lagCounter; + header.numRerecords = g_rerecords; + + // TODO + header.uniqueID = 0; + // header.author; + // header.videoPlugin; + // header.audioPlugin; + + fwrite(&header, sizeof(DTMHeader), 1, g_recordfd); + } + + bool success = false; fclose(g_recordfd); + File::Delete(filename); + success = File::Copy(g_recordFile.c_str(), filename); + + if (success /* && !g_bReadOnly*/) + { + success = +#ifdef WIN32 + (g_recordfd = fopen(filename, "r+b")) && + !(_chsize_s(g_recordfd, size) == 0) && + fclose(g_recordfd); +#else + !truncate(filename, size); +#endif + } - if (File::Copy(g_recordFile.c_str(), filename)) + if (success) Core::DisplayMessage(StringFromFormat("DTM %s saved", filename).c_str(), 2000); else Core::DisplayMessage(StringFromFormat("Failed to save %s", filename).c_str(), 2000); g_recordfd = fopen(g_recordFile.c_str(), "r+b"); - fseeko(g_recordfd, 0, SEEK_END); + fseeko(g_recordfd, size, SEEK_SET); } }; diff --git a/Source/Core/Core/Src/OnFrame.h b/Source/Core/Core/Src/OnFrame.h index 4d9ab488c0..3a5316955b 100644 --- a/Source/Core/Core/Src/OnFrame.h +++ b/Source/Core/Core/Src/OnFrame.h @@ -50,13 +50,14 @@ struct ControllerState { #pragma pack(pop) // Global declarations -extern bool g_bFrameStep, g_bPolled; +extern bool g_bFrameStep, g_bPolled, g_bReadOnly; extern PlayMode g_playMode; extern unsigned int g_framesToSkip, g_frameSkipCounter; extern int g_numPads; extern ControllerState *g_padStates; +extern char g_playingFile[256]; extern FILE *g_recordfd; extern std::string g_recordFile; @@ -105,6 +106,7 @@ void ChangeWiiPads(); void SetFrameStepping(bool bEnabled); void SetFrameStopping(bool bEnabled); +void SetReadOnly(bool bEnabled); void SetFrameSkipping(unsigned int framesToSkip); int FrameSkippingFactor(); diff --git a/Source/Core/Core/Src/State.cpp b/Source/Core/Core/Src/State.cpp index 79005eeca2..d65251fb7c 100644 --- a/Source/Core/Core/Src/State.cpp +++ b/Source/Core/Core/Src/State.cpp @@ -265,7 +265,7 @@ void SaveStateCallback(u64 userdata, int cyclesLate) saveData->buffer = buffer; saveData->size = sz; - if (Frame::IsRecordingInput()) + if (Frame::IsRecordingInput() || Frame::IsPlayingInput()) Frame::SaveRecording(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str()); Core::DisplayMessage("Saving State...", 1000); diff --git a/Source/Core/DolphinWX/Src/Frame.cpp b/Source/Core/DolphinWX/Src/Frame.cpp index dafd2f3f85..f5967e5f7c 100644 --- a/Source/Core/DolphinWX/Src/Frame.cpp +++ b/Source/Core/DolphinWX/Src/Frame.cpp @@ -250,6 +250,7 @@ EVT_MENU(IDM_RESET, CFrame::OnReset) EVT_MENU(IDM_RECORD, CFrame::OnRecord) EVT_MENU(IDM_PLAYRECORD, CFrame::OnPlayRecording) EVT_MENU(IDM_RECORDEXPORT, CFrame::OnRecordExport) +EVT_MENU(IDM_RECORDREADONLY, CFrame::OnRecordReadOnly) EVT_MENU(IDM_FRAMESTEP, CFrame::OnFrameStep) EVT_MENU(IDM_SCREENSHOT, CFrame::OnScreenshot) EVT_MENU(wxID_PREFERENCES, CFrame::OnConfigMain) diff --git a/Source/Core/DolphinWX/Src/Frame.h b/Source/Core/DolphinWX/Src/Frame.h index 9b817c6308..bd56511e38 100644 --- a/Source/Core/DolphinWX/Src/Frame.h +++ b/Source/Core/DolphinWX/Src/Frame.h @@ -279,6 +279,7 @@ class CFrame : public CRenderFrame void OnRecord(wxCommandEvent& event); void OnPlayRecording(wxCommandEvent& event); void OnRecordExport(wxCommandEvent& event); + void OnRecordReadOnly(wxCommandEvent& event); void OnChangeDisc(wxCommandEvent& event); void OnScreenshot(wxCommandEvent& event); void OnActive(wxActivateEvent& event); diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp index 4e2aa1b754..e4d71c8ae3 100644 --- a/Source/Core/DolphinWX/Src/FrameTools.cpp +++ b/Source/Core/DolphinWX/Src/FrameTools.cpp @@ -140,6 +140,8 @@ void CFrame::CreateMenu() emulationMenu->Append(IDM_RECORD, _("Start Re&cording")); emulationMenu->Append(IDM_PLAYRECORD, _("P&lay Recording...")); emulationMenu->Append(IDM_RECORDEXPORT, _("Export Recording...")); + emulationMenu->Append(IDM_RECORDREADONLY, _("&Read-only mode"), wxEmptyString, wxITEM_CHECK); + emulationMenu->Check(IDM_RECORDREADONLY, true); emulationMenu->AppendSeparator(); emulationMenu->Append(IDM_FRAMESTEP, _("&Frame Advance"), wxEmptyString, wxITEM_CHECK); @@ -633,6 +635,11 @@ void CFrame::DoOpen(bool Boot) } } +void CFrame::OnRecordReadOnly(wxCommandEvent& event) +{ + Frame::SetReadOnly(event.IsChecked()); +} + void CFrame::OnFrameStep(wxCommandEvent& event) { Frame::SetFrameStepping(event.IsChecked()); diff --git a/Source/Core/DolphinWX/Src/Globals.h b/Source/Core/DolphinWX/Src/Globals.h index 43932430fa..ef94021334 100644 --- a/Source/Core/DolphinWX/Src/Globals.h +++ b/Source/Core/DolphinWX/Src/Globals.h @@ -78,6 +78,7 @@ enum IDM_RECORD, IDM_PLAYRECORD, IDM_RECORDEXPORT, + IDM_RECORDREADONLY, IDM_FRAMESTEP, IDM_SCREENSHOT, IDM_BROWSE,