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,