Added the feature to allow creating a TAS movie from a save state. To activate this function, start the game and use the "Start recording" command. A save state will be created at that point in time and the emulator will start recording. This results in two files, a .dtm containing the movie and a .dtm.sav which is the save state.
Changes: * Allow events to be scheduled when the emulator is not running. This allows the save state event to be added before the emulator starts. * Removed the Audio back-end init flag from the save state. This value should not be saved as it is not data relevant to guest machine. * Allow a recording to be started at any time (apart from when a recording is already being made). * Updated the status bar and title bar when an on-screen message is shown * Removed the saving of PEToken from the save state as the FIFO will save this information * Added a couple Pixel Engine interrupt states to the save state * Added the copyright notice to the GCPadStatus.h file. This function is preliminary. Let us know of any bugs you find or any UI quirks. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7175 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
cf21251802
commit
fb4c82fb48
|
@ -94,7 +94,7 @@ bool g_bStopping = false;
|
|||
bool g_bHwInit = false;
|
||||
bool g_bRealWiimote = false;
|
||||
void *g_pWindowHandle = NULL;
|
||||
|
||||
std::string g_stateFileName;
|
||||
std::thread g_EmuThread;
|
||||
|
||||
static std::thread cpuThread;
|
||||
|
@ -105,7 +105,8 @@ SCoreStartupParameter g_CoreStartupParameter;
|
|||
Common::Event emuThreadGoing;
|
||||
Common::Event cpuRunloopQuit;
|
||||
|
||||
|
||||
std::string GetStateFileName() { return g_stateFileName; }
|
||||
void SetStateFileName(std::string val) { g_stateFileName = val; }
|
||||
|
||||
// Display messages and return values
|
||||
|
||||
|
@ -125,12 +126,26 @@ bool PanicAlertToVideo(const char* text, bool yes_no)
|
|||
|
||||
void DisplayMessage(const std::string &message, int time_in_ms)
|
||||
{
|
||||
SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
|
||||
g_video_backend->Video_AddMessage(message.c_str(), time_in_ms);
|
||||
if (_CoreParameter.bRenderToMain &&
|
||||
SConfig::GetInstance().m_InterfaceStatusbar) {
|
||||
Host_UpdateStatusBar(message.c_str());
|
||||
} else
|
||||
Host_UpdateTitle(message.c_str());
|
||||
}
|
||||
|
||||
void DisplayMessage(const char *message, int time_in_ms)
|
||||
{
|
||||
SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
|
||||
|
||||
g_video_backend->Video_AddMessage(message, time_in_ms);
|
||||
if (_CoreParameter.bRenderToMain &&
|
||||
SConfig::GetInstance().m_InterfaceStatusbar) {
|
||||
Host_UpdateStatusBar(message);
|
||||
} else
|
||||
Host_UpdateTitle(message);
|
||||
}
|
||||
|
||||
void Callback_DebuggerBreak()
|
||||
|
@ -158,6 +173,11 @@ bool IsRunningInCurrentThread()
|
|||
return isRunning() && ((!cpuThread.joinable()) || cpuThread.get_id() == std::this_thread::get_id());
|
||||
}
|
||||
|
||||
bool IsCPUThread()
|
||||
{
|
||||
return ((!cpuThread.joinable()) || cpuThread.get_id() == std::this_thread::get_id());
|
||||
}
|
||||
|
||||
// This is called from the GUI thread. See the booting call schedule in
|
||||
// BootManager.cpp
|
||||
bool Init()
|
||||
|
@ -258,6 +278,9 @@ void CpuThread()
|
|||
#endif
|
||||
}
|
||||
|
||||
if (!g_stateFileName.empty())
|
||||
State_LoadAs(g_stateFileName);
|
||||
|
||||
// Enter CPU run loop. When we leave it - we are done.
|
||||
CCPU::Run();
|
||||
|
||||
|
|
|
@ -55,7 +55,8 @@ void Callback_CoreMessage(int Id);
|
|||
std::string StopMessage(bool, std::string);
|
||||
|
||||
bool isRunning();
|
||||
bool IsRunningInCurrentThread(); // this tells us whether we are in the cpu thread.
|
||||
bool IsRunningInCurrentThread(); // this tells us whether we are running in the cpu thread.
|
||||
bool IsCPUThread(); // this tells us whether we are the cpu thread.
|
||||
|
||||
void SetState(EState _State);
|
||||
EState GetState();
|
||||
|
@ -78,6 +79,9 @@ void Callback_CoreMessage(int Id);
|
|||
void StartTrace(bool write);
|
||||
void DisplayMessage(const std::string &message, int time_in_ms); // This displays messages in a user-visible way.
|
||||
void DisplayMessage(const char *message, int time_in_ms); // This displays messages in a user-visible way.
|
||||
|
||||
std::string GetStateFileName();
|
||||
void SetStateFileName(std::string val);
|
||||
|
||||
int SyncTrace();
|
||||
void SetBlockStart(u32 addr);
|
||||
|
@ -100,7 +104,6 @@ void Callback_CoreMessage(int Id);
|
|||
extern bool g_FrameStep;
|
||||
#endif
|
||||
// ---------------------------
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
|
|
@ -241,11 +241,11 @@ void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata
|
|||
externalEventSection.Leave();
|
||||
}
|
||||
|
||||
// Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the main thread
|
||||
// Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread
|
||||
// in which case the event will get handled immediately, before returning.
|
||||
void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata)
|
||||
{
|
||||
if(Core::IsRunningInCurrentThread())
|
||||
if(Core::IsCPUThread())
|
||||
{
|
||||
externalEventSection.Enter();
|
||||
event_types[event_type].callback(userdata, 0);
|
||||
|
|
|
@ -51,8 +51,6 @@ DSPLLE::DSPLLE() {
|
|||
|
||||
void DSPLLE::DoState(PointerWrap &p)
|
||||
{
|
||||
p.Do(m_InitMixer);
|
||||
|
||||
p.Do(g_dsp.r);
|
||||
p.Do(g_dsp.pc);
|
||||
#if PROFILE
|
||||
|
@ -96,7 +94,8 @@ void DSPLLE::dsp_thread(DSPLLE *lpParameter)
|
|||
}
|
||||
Common::AtomicStore(dsp_lle->m_cycle_count, 0);
|
||||
}
|
||||
Common::YieldCPU();
|
||||
else
|
||||
Common::YieldCPU();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,7 +163,7 @@ u16 DSPLLE::DSP_WriteControlRegister(u16 _uFlag)
|
|||
UDSPControl Temp(_uFlag);
|
||||
if (!m_InitMixer)
|
||||
{
|
||||
if (!Temp.DSPHalt && Temp.DSPInit)
|
||||
if (!Temp.DSPHalt)
|
||||
{
|
||||
unsigned int AISampleRate, DACSampleRate;
|
||||
AudioInterface::Callback_GetSampleRate(AISampleRate, DACSampleRate);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#else
|
||||
#include <unistd.h> //truncate
|
||||
#endif
|
||||
#include "State.h"
|
||||
|
||||
Common::CriticalSection cs_frameSkip;
|
||||
|
||||
|
@ -53,6 +54,7 @@ char g_playingFile[256] = "\0";
|
|||
FILE *g_recordfd = NULL;
|
||||
|
||||
u64 g_frameCounter = 0, g_lagCounter = 0;
|
||||
bool g_bRecordingFromSaveState = false;
|
||||
bool g_bPolled = false;
|
||||
|
||||
int g_numRerecords = 0;
|
||||
|
@ -141,6 +143,11 @@ bool IsRecordingInput()
|
|||
return (g_playMode == MODE_RECORDING);
|
||||
}
|
||||
|
||||
bool IsRecordingInputFromSaveState()
|
||||
{
|
||||
return g_bRecordingFromSaveState;
|
||||
}
|
||||
|
||||
bool IsPlayingInput()
|
||||
{
|
||||
return (g_playMode == MODE_PLAYING);
|
||||
|
@ -177,17 +184,27 @@ void ChangeWiiPads()
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Add BeginRecordingFromSavestate
|
||||
bool BeginRecordingInput(int controllers)
|
||||
{
|
||||
if(g_playMode != MODE_NONE || controllers == 0 || g_recordfd != NULL)
|
||||
return false;
|
||||
|
||||
|
||||
const char *filename = g_recordFile.c_str();
|
||||
|
||||
if(File::Exists(filename))
|
||||
File::Delete(filename);
|
||||
|
||||
|
||||
if (Core::isRunning())
|
||||
{
|
||||
std::string tmpStateFilename = g_recordFile;
|
||||
tmpStateFilename.append(".sav");
|
||||
const char *stateFilename = tmpStateFilename.c_str();
|
||||
if(File::Exists(stateFilename))
|
||||
File::Delete(stateFilename);
|
||||
State_SaveAs(stateFilename);
|
||||
g_bRecordingFromSaveState = true;
|
||||
}
|
||||
|
||||
g_recordfd = fopen(filename, "wb");
|
||||
if(!g_recordfd) {
|
||||
PanicAlertT("Error opening file %s for recording", filename);
|
||||
|
@ -273,8 +290,13 @@ bool PlayInput(const char *filename)
|
|||
}
|
||||
|
||||
// Load savestate (and skip to frame data)
|
||||
if(header.bFromSaveState) {
|
||||
// TODO
|
||||
if(header.bFromSaveState)
|
||||
{
|
||||
std::string stateFilename = filename;
|
||||
stateFilename.append(".sav");
|
||||
if(File::Exists(stateFilename.c_str()))
|
||||
Core::SetStateFileName(stateFilename);
|
||||
g_bRecordingFromSaveState = true;
|
||||
}
|
||||
|
||||
/* TODO: Put this verification somewhere we have the gameID of the played game
|
||||
|
@ -316,7 +338,8 @@ void LoadInput(const char *filename)
|
|||
fread(&header, sizeof(DTMHeader), 1, t_record);
|
||||
fclose(t_record);
|
||||
|
||||
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);
|
||||
strncpy(g_playingFile, "\0", 256);
|
||||
EndPlayInput();
|
||||
|
@ -469,7 +492,7 @@ void SaveRecording(const char *filename)
|
|||
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.bFromSaveState = g_bRecordingFromSaveState;
|
||||
header.frameCount = g_frameCounter;
|
||||
header.lagCount = g_lagCounter;
|
||||
header.numRerecords = g_rerecords;
|
||||
|
@ -488,6 +511,15 @@ void SaveRecording(const char *filename)
|
|||
File::Delete(filename);
|
||||
success = File::Copy(g_recordFile.c_str(), filename);
|
||||
|
||||
if (success && g_bRecordingFromSaveState)
|
||||
{
|
||||
std::string tmpStateFilename = g_recordFile;
|
||||
tmpStateFilename.append(".sav");
|
||||
std::string stateFilename = filename;
|
||||
stateFilename.append(".sav");
|
||||
success = File::Copy(tmpStateFilename.c_str(), stateFilename.c_str());
|
||||
}
|
||||
|
||||
if (success /* && !g_bReadOnly*/)
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
|
|
@ -97,6 +97,7 @@ void SetPolledDevice();
|
|||
|
||||
bool IsAutoFiring();
|
||||
bool IsRecordingInput();
|
||||
bool IsRecordingInputFromSaveState();
|
||||
bool IsPlayingInput();
|
||||
|
||||
bool IsUsingPad(int controller);
|
||||
|
|
|
@ -265,7 +265,7 @@ void SaveStateCallback(u64 userdata, int cyclesLate)
|
|||
saveData->buffer = buffer;
|
||||
saveData->size = sz;
|
||||
|
||||
if (Frame::IsRecordingInput() || Frame::IsPlayingInput())
|
||||
if ((Frame::IsRecordingInput() || Frame::IsPlayingInput()) && !Frame::IsRecordingInputFromSaveState())
|
||||
Frame::SaveRecording(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str());
|
||||
|
||||
Core::DisplayMessage("Saving State...", 1000);
|
||||
|
@ -390,7 +390,7 @@ void LoadStateCallback(u64 userdata, int cyclesLate)
|
|||
|
||||
if (File::Exists(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str()))
|
||||
Frame::LoadInput(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str());
|
||||
else
|
||||
else if (!Frame::IsRecordingInputFromSaveState())
|
||||
Frame::EndPlayInput();
|
||||
|
||||
state_op_in_progress = false;
|
||||
|
|
|
@ -1456,7 +1456,7 @@ void CFrame::UpdateGUI()
|
|||
// Emulation
|
||||
GetMenuBar()->FindItem(IDM_STOP)->Enable(Running || Paused);
|
||||
GetMenuBar()->FindItem(IDM_RESET)->Enable(Running || Paused);
|
||||
GetMenuBar()->FindItem(IDM_RECORD)->Enable(!Initialized);
|
||||
GetMenuBar()->FindItem(IDM_RECORD)->Enable(!Frame::IsRecordingInput());
|
||||
GetMenuBar()->FindItem(IDM_PLAYRECORD)->Enable(!Initialized);
|
||||
GetMenuBar()->FindItem(IDM_RECORDEXPORT)->Enable(Frame::IsRecordingInput());
|
||||
GetMenuBar()->FindItem(IDM_FRAMESTEP)->Enable(Running || Paused);
|
||||
|
|
|
@ -572,6 +572,7 @@ void Host_UpdateTitle(const char* title)
|
|||
wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATETITLE);
|
||||
event.SetString(wxString::FromAscii(title));
|
||||
main_frame->GetEventHandler()->AddPendingEvent(event);
|
||||
Host_UpdateMainFrame();
|
||||
}
|
||||
|
||||
void Host_UpdateBreakPointView()
|
||||
|
|
|
@ -1,6 +1,25 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _GCPAD_H_INCLUDED__
|
||||
#define _GCPAD_H_INCLUDED__
|
||||
|
||||
#include "CommonTypes.h"
|
||||
|
||||
#define PAD_ERR_NONE 0
|
||||
#define PAD_ERR_NO_CONTROLLER -1
|
||||
#define PAD_ERR_NOT_READY -2
|
||||
|
|
|
@ -135,10 +135,11 @@ void DoState(PointerWrap &p)
|
|||
p.Do(m_AlphaModeConf);
|
||||
p.Do(m_AlphaRead);
|
||||
p.Do(m_Control);
|
||||
p.Do(CommandProcessor::fifo.PEToken);
|
||||
|
||||
p.Do(g_bSignalTokenInterrupt);
|
||||
p.Do(g_bSignalFinishInterrupt);
|
||||
p.Do(interruptSetToken);
|
||||
p.Do(interruptSetFinish);
|
||||
|
||||
p.Do(bbox);
|
||||
p.Do(bbox_active);
|
||||
|
|
Loading…
Reference in New Issue