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:
parent
6912f0a18c
commit
0d110c41d9
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue