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 "VideoBackendBase.h"
#ifdef WIN32
#include <io.h> //_chsize_s
#else
#include <unistd.h> //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);
}
};

View File

@ -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();

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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());

View File

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