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
This commit is contained in:
smelenchuk 2011-02-12 02:14:20 +00:00
parent 6912f0a18c
commit 0d110c41d9
7 changed files with 101 additions and 36 deletions

View File

@ -26,12 +26,19 @@
#include "IPC_HLE/WII_IPC_HLE_Device_usb.h" #include "IPC_HLE/WII_IPC_HLE_Device_usb.h"
#include "VideoBackendBase.h" #include "VideoBackendBase.h"
#ifdef WIN32
#include <io.h> //_chsize_s
#else
#include <unistd.h> //truncate
#endif
Common::CriticalSection cs_frameSkip; Common::CriticalSection cs_frameSkip;
namespace Frame { namespace Frame {
bool g_bFrameStep = false; bool g_bFrameStep = false;
bool g_bFrameStop = false; bool g_bFrameStop = false;
bool g_bReadOnly = true;
u32 g_rerecords = 0; u32 g_rerecords = 0;
PlayMode g_playMode = MODE_NONE; PlayMode g_playMode = MODE_NONE;
@ -39,6 +46,7 @@ unsigned int g_framesToSkip = 0, g_frameSkipCounter = 0;
int g_numPads = 0; int g_numPads = 0;
ControllerState g_padState; ControllerState g_padState;
char g_playingFile[256] = "\0";
FILE *g_recordfd = NULL; FILE *g_recordfd = NULL;
u64 g_frameCounter = 0, g_lagCounter = 0; u64 g_frameCounter = 0, g_lagCounter = 0;
@ -103,6 +111,11 @@ void SetFrameStopping(bool bEnabled)
g_bFrameStop = bEnabled; g_bFrameStop = bEnabled;
} }
void SetReadOnly(bool bEnabled)
{
g_bReadOnly = bEnabled;
}
void FrameSkipping() void FrameSkipping()
{ {
// Frameskipping will desync movie playback // Frameskipping will desync movie playback
@ -242,7 +255,10 @@ bool PlayInput(const char *filename)
DTMHeader header; 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) if(!g_recordfd)
return false; return false;
@ -278,6 +294,8 @@ bool PlayInput(const char *filename)
g_playMode = MODE_PLAYING; g_playMode = MODE_PLAYING;
strncpy(g_playingFile, filename, 256);
return true; return true;
cleanup: cleanup:
@ -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) { 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); PanicAlertT("Savestate movie %s is corrupted, movie recording stopping...", filename);
strncpy(g_playingFile, "\0", 256);
EndPlayInput(); EndPlayInput();
return; return;
} }
@ -304,6 +323,8 @@ void LoadInput(const char *filename)
if (g_rerecords == 0) if (g_rerecords == 0)
g_rerecords = header.numRerecords; g_rerecords = header.numRerecords;
g_frameCounter = header.frameCount;
g_numPads = header.numControllers; g_numPads = header.numControllers;
ChangePads(true); ChangePads(true);
@ -381,9 +402,7 @@ void PlayController(SPADStatus *PadStatus, int controllerID)
if(feof(g_recordfd)) if(feof(g_recordfd))
{ {
Core::DisplayMessage("Movie End", 2000); Core::DisplayMessage("Movie End", 2000);
// TODO: read-only mode EndPlayInput();
//EndPlayInput();
g_playMode = MODE_RECORDING;
} }
} }
@ -401,54 +420,88 @@ void PlayWiimote(u8 *data, s8 &size)
if(feof(g_recordfd)) if(feof(g_recordfd))
{ {
Core::DisplayMessage("Movie End", 2000); Core::DisplayMessage("Movie End", 2000);
// TODO: read-only mode EndPlayInput();
//EndPlayInput();
g_playMode = MODE_RECORDING;
} }
} }
void EndPlayInput() { void EndPlayInput() {
if (g_recordfd) if (g_recordfd)
fclose(g_recordfd); fclose(g_recordfd);
g_recordfd = NULL;
g_numPads = g_rerecords = 0; if (!g_bReadOnly && strncmp(g_playingFile, "\0", 1))
g_frameCounter = g_lagCounter = 0; {
g_playMode = MODE_NONE; 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) void SaveRecording(const char *filename)
{ {
rewind(g_recordfd); off_t size = ftello(g_recordfd);
// Create the real header now and write it // NOTE: Eventually this will not happen in
DTMHeader header; // read-only mode, but we need a way for the save state to
memset(&header, 0, sizeof(DTMHeader)); // 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; // Create the real header now and write it
strncpy((char *)header.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6); DTMHeader header;
header.bWii = Core::g_CoreStartupParameter.bWii; memset(&header, 0, sizeof(DTMHeader));
header.numControllers = g_numPads & (Core::g_CoreStartupParameter.bWii ? 0xFF : 0x0F);
header.bFromSaveState = false; // TODO: add the case where it's true header.filetype[0] = 'D'; header.filetype[1] = 'T'; header.filetype[2] = 'M'; header.filetype[3] = 0x1A;
header.frameCount = g_frameCounter; strncpy((char *)header.gameID, Core::g_CoreStartupParameter.GetUniqueID().c_str(), 6);
header.lagCount = g_lagCounter; header.bWii = Core::g_CoreStartupParameter.bWii;
header.numRerecords = g_rerecords; header.numControllers = g_numPads & (Core::g_CoreStartupParameter.bWii ? 0xFF : 0x0F);
// TODO header.bFromSaveState = false; // TODO: add the case where it's true
header.uniqueID = 0; header.frameCount = g_frameCounter;
// header.author; header.lagCount = g_lagCounter;
// header.videoPlugin; header.numRerecords = g_rerecords;
// header.audioPlugin;
fwrite(&header, sizeof(DTMHeader), 1, g_recordfd); // TODO
header.uniqueID = 0;
// header.author;
// header.videoPlugin;
// header.audioPlugin;
fwrite(&header, sizeof(DTMHeader), 1, g_recordfd);
}
bool success = false;
fclose(g_recordfd); fclose(g_recordfd);
File::Delete(filename);
success = File::Copy(g_recordFile.c_str(), filename);
if (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 (success)
Core::DisplayMessage(StringFromFormat("DTM %s saved", filename).c_str(), 2000); Core::DisplayMessage(StringFromFormat("DTM %s saved", filename).c_str(), 2000);
else else
Core::DisplayMessage(StringFromFormat("Failed to save %s", filename).c_str(), 2000); Core::DisplayMessage(StringFromFormat("Failed to save %s", filename).c_str(), 2000);
g_recordfd = fopen(g_recordFile.c_str(), "r+b"); g_recordfd = fopen(g_recordFile.c_str(), "r+b");
fseeko(g_recordfd, 0, SEEK_END); fseeko(g_recordfd, size, SEEK_SET);
} }
}; };

View File

@ -50,13 +50,14 @@ struct ControllerState {
#pragma pack(pop) #pragma pack(pop)
// Global declarations // Global declarations
extern bool g_bFrameStep, g_bPolled; extern bool g_bFrameStep, g_bPolled, g_bReadOnly;
extern PlayMode g_playMode; extern PlayMode g_playMode;
extern unsigned int g_framesToSkip, g_frameSkipCounter; extern unsigned int g_framesToSkip, g_frameSkipCounter;
extern int g_numPads; extern int g_numPads;
extern ControllerState *g_padStates; extern ControllerState *g_padStates;
extern char g_playingFile[256];
extern FILE *g_recordfd; extern FILE *g_recordfd;
extern std::string g_recordFile; extern std::string g_recordFile;
@ -105,6 +106,7 @@ void ChangeWiiPads();
void SetFrameStepping(bool bEnabled); void SetFrameStepping(bool bEnabled);
void SetFrameStopping(bool bEnabled); void SetFrameStopping(bool bEnabled);
void SetReadOnly(bool bEnabled);
void SetFrameSkipping(unsigned int framesToSkip); void SetFrameSkipping(unsigned int framesToSkip);
int FrameSkippingFactor(); int FrameSkippingFactor();

View File

@ -265,7 +265,7 @@ void SaveStateCallback(u64 userdata, int cyclesLate)
saveData->buffer = buffer; saveData->buffer = buffer;
saveData->size = sz; saveData->size = sz;
if (Frame::IsRecordingInput()) if (Frame::IsRecordingInput() || Frame::IsPlayingInput())
Frame::SaveRecording(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str()); Frame::SaveRecording(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str());
Core::DisplayMessage("Saving State...", 1000); Core::DisplayMessage("Saving State...", 1000);

View File

@ -250,6 +250,7 @@ EVT_MENU(IDM_RESET, CFrame::OnReset)
EVT_MENU(IDM_RECORD, CFrame::OnRecord) EVT_MENU(IDM_RECORD, CFrame::OnRecord)
EVT_MENU(IDM_PLAYRECORD, CFrame::OnPlayRecording) EVT_MENU(IDM_PLAYRECORD, CFrame::OnPlayRecording)
EVT_MENU(IDM_RECORDEXPORT, CFrame::OnRecordExport) EVT_MENU(IDM_RECORDEXPORT, CFrame::OnRecordExport)
EVT_MENU(IDM_RECORDREADONLY, CFrame::OnRecordReadOnly)
EVT_MENU(IDM_FRAMESTEP, CFrame::OnFrameStep) EVT_MENU(IDM_FRAMESTEP, CFrame::OnFrameStep)
EVT_MENU(IDM_SCREENSHOT, CFrame::OnScreenshot) EVT_MENU(IDM_SCREENSHOT, CFrame::OnScreenshot)
EVT_MENU(wxID_PREFERENCES, CFrame::OnConfigMain) EVT_MENU(wxID_PREFERENCES, CFrame::OnConfigMain)

View File

@ -279,6 +279,7 @@ class CFrame : public CRenderFrame
void OnRecord(wxCommandEvent& event); void OnRecord(wxCommandEvent& event);
void OnPlayRecording(wxCommandEvent& event); void OnPlayRecording(wxCommandEvent& event);
void OnRecordExport(wxCommandEvent& event); void OnRecordExport(wxCommandEvent& event);
void OnRecordReadOnly(wxCommandEvent& event);
void OnChangeDisc(wxCommandEvent& event); void OnChangeDisc(wxCommandEvent& event);
void OnScreenshot(wxCommandEvent& event); void OnScreenshot(wxCommandEvent& event);
void OnActive(wxActivateEvent& event); void OnActive(wxActivateEvent& event);

View File

@ -140,6 +140,8 @@ void CFrame::CreateMenu()
emulationMenu->Append(IDM_RECORD, _("Start Re&cording")); emulationMenu->Append(IDM_RECORD, _("Start Re&cording"));
emulationMenu->Append(IDM_PLAYRECORD, _("P&lay Recording...")); emulationMenu->Append(IDM_PLAYRECORD, _("P&lay Recording..."));
emulationMenu->Append(IDM_RECORDEXPORT, _("Export 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->AppendSeparator();
emulationMenu->Append(IDM_FRAMESTEP, _("&Frame Advance"), wxEmptyString, wxITEM_CHECK); 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) void CFrame::OnFrameStep(wxCommandEvent& event)
{ {
Frame::SetFrameStepping(event.IsChecked()); Frame::SetFrameStepping(event.IsChecked());

View File

@ -78,6 +78,7 @@ enum
IDM_RECORD, IDM_RECORD,
IDM_PLAYRECORD, IDM_PLAYRECORD,
IDM_RECORDEXPORT, IDM_RECORDEXPORT,
IDM_RECORDREADONLY,
IDM_FRAMESTEP, IDM_FRAMESTEP,
IDM_SCREENSHOT, IDM_SCREENSHOT,
IDM_BROWSE, IDM_BROWSE,