From fb4c82fb48b756b348140993c36d3177a869aa52 Mon Sep 17 00:00:00 2001 From: skidau Date: Tue, 15 Feb 2011 09:07:55 +0000 Subject: [PATCH] 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 --- Source/Core/Core/Src/Core.cpp | 27 +++++++++++- Source/Core/Core/Src/Core.h | 7 +++- Source/Core/Core/Src/CoreTiming.cpp | 4 +- Source/Core/Core/Src/HW/DSPLLE/DSPLLE.cpp | 7 ++-- Source/Core/Core/Src/OnFrame.cpp | 46 +++++++++++++++++---- Source/Core/Core/Src/OnFrame.h | 1 + Source/Core/Core/Src/State.cpp | 4 +- Source/Core/DolphinWX/Src/FrameTools.cpp | 2 +- Source/Core/DolphinWX/Src/Main.cpp | 1 + Source/Core/InputCommon/Src/GCPadStatus.h | 19 +++++++++ Source/Core/VideoCommon/Src/PixelEngine.cpp | 3 +- 11 files changed, 100 insertions(+), 21 deletions(-) diff --git a/Source/Core/Core/Src/Core.cpp b/Source/Core/Core/Src/Core.cpp index b0c10a57ab..3baafdec5f 100644 --- a/Source/Core/Core/Src/Core.cpp +++ b/Source/Core/Core/Src/Core.cpp @@ -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(); diff --git a/Source/Core/Core/Src/Core.h b/Source/Core/Core/Src/Core.h index e708ca367d..83a05b9c77 100644 --- a/Source/Core/Core/Src/Core.h +++ b/Source/Core/Core/Src/Core.h @@ -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 diff --git a/Source/Core/Core/Src/CoreTiming.cpp b/Source/Core/Core/Src/CoreTiming.cpp index c1b6b2225f..1bed6fbb22 100644 --- a/Source/Core/Core/Src/CoreTiming.cpp +++ b/Source/Core/Core/Src/CoreTiming.cpp @@ -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); diff --git a/Source/Core/Core/Src/HW/DSPLLE/DSPLLE.cpp b/Source/Core/Core/Src/HW/DSPLLE/DSPLLE.cpp index 41a25dea53..fa1e2d4b38 100644 --- a/Source/Core/Core/Src/HW/DSPLLE/DSPLLE.cpp +++ b/Source/Core/Core/Src/HW/DSPLLE/DSPLLE.cpp @@ -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); diff --git a/Source/Core/Core/Src/OnFrame.cpp b/Source/Core/Core/Src/OnFrame.cpp index 311912649c..0b84e9cb3e 100644 --- a/Source/Core/Core/Src/OnFrame.cpp +++ b/Source/Core/Core/Src/OnFrame.cpp @@ -34,6 +34,7 @@ #else #include //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 diff --git a/Source/Core/Core/Src/OnFrame.h b/Source/Core/Core/Src/OnFrame.h index ddb7755179..28d3d07255 100644 --- a/Source/Core/Core/Src/OnFrame.h +++ b/Source/Core/Core/Src/OnFrame.h @@ -97,6 +97,7 @@ void SetPolledDevice(); bool IsAutoFiring(); bool IsRecordingInput(); +bool IsRecordingInputFromSaveState(); bool IsPlayingInput(); bool IsUsingPad(int controller); diff --git a/Source/Core/Core/Src/State.cpp b/Source/Core/Core/Src/State.cpp index 6bba6072f4..3fddd7c536 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() || 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; diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp index 518edffcfc..73ac176a28 100644 --- a/Source/Core/DolphinWX/Src/FrameTools.cpp +++ b/Source/Core/DolphinWX/Src/FrameTools.cpp @@ -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); diff --git a/Source/Core/DolphinWX/Src/Main.cpp b/Source/Core/DolphinWX/Src/Main.cpp index 93356d2e21..d05840d941 100644 --- a/Source/Core/DolphinWX/Src/Main.cpp +++ b/Source/Core/DolphinWX/Src/Main.cpp @@ -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() diff --git a/Source/Core/InputCommon/Src/GCPadStatus.h b/Source/Core/InputCommon/Src/GCPadStatus.h index 138a79e1ba..72b790bbc6 100644 --- a/Source/Core/InputCommon/Src/GCPadStatus.h +++ b/Source/Core/InputCommon/Src/GCPadStatus.h @@ -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 diff --git a/Source/Core/VideoCommon/Src/PixelEngine.cpp b/Source/Core/VideoCommon/Src/PixelEngine.cpp index 9aff12a780..b7b95df8dd 100644 --- a/Source/Core/VideoCommon/Src/PixelEngine.cpp +++ b/Source/Core/VideoCommon/Src/PixelEngine.cpp @@ -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);