Merge pull request #6271 from JosJuice/load-state-at-boot

Redesign the ability to load state at boot
This commit is contained in:
Leo Lam 2017-12-26 20:56:06 +01:00 committed by GitHub
commit 3bc61ed363
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 93 additions and 81 deletions

View File

@ -48,11 +48,15 @@
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
BootParameters::BootParameters(Parameters&& parameters_) : parameters(std::move(parameters_))
BootParameters::BootParameters(Parameters&& parameters_,
const std::optional<std::string>& savestate_path_)
: parameters(std::move(parameters_)), savestate_path(savestate_path_)
{
}
std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(const std::string& path)
std::unique_ptr<BootParameters>
BootParameters::GenerateFromFile(const std::string& path,
const std::optional<std::string>& savestate_path)
{
const bool is_drive = cdio_is_cdrom(path);
// Check if the file exist, we may have gotten it from a --elf command line
@ -73,13 +77,19 @@ std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(const std::stri
{
std::unique_ptr<DiscIO::Volume> volume = DiscIO::CreateVolumeFromFilename(path);
if (volume)
return std::make_unique<BootParameters>(Disc{path, std::move(volume)});
return std::make_unique<BootParameters>(Disc{path, std::move(volume)}, savestate_path);
if (extension == ".elf")
return std::make_unique<BootParameters>(Executable{path, std::make_unique<ElfReader>(path)});
{
return std::make_unique<BootParameters>(Executable{path, std::make_unique<ElfReader>(path)},
savestate_path);
}
if (extension == ".dol")
return std::make_unique<BootParameters>(Executable{path, std::make_unique<DolReader>(path)});
{
return std::make_unique<BootParameters>(Executable{path, std::make_unique<DolReader>(path)},
savestate_path);
}
if (is_drive)
{
@ -97,10 +107,10 @@ std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(const std::stri
}
if (extension == ".dff")
return std::make_unique<BootParameters>(DFF{path});
return std::make_unique<BootParameters>(DFF{path}, savestate_path);
if (extension == ".wad")
return std::make_unique<BootParameters>(DiscIO::WiiWAD{path});
return std::make_unique<BootParameters>(DiscIO::WiiWAD{path}, savestate_path);
PanicAlertT("Could not recognize file %s", path.c_str());
return {};

View File

@ -67,12 +67,15 @@ struct BootParameters
std::string dff_path;
};
static std::unique_ptr<BootParameters> GenerateFromFile(const std::string& path);
static std::unique_ptr<BootParameters>
GenerateFromFile(const std::string& boot_path,
const std::optional<std::string>& savestate_path = {});
using Parameters = std::variant<Disc, Executable, DiscIO::WiiWAD, NANDTitle, IPL, DFF>;
BootParameters(Parameters&& parameters_);
BootParameters(Parameters&& parameters_, const std::optional<std::string>& savestate_path_ = {});
Parameters parameters;
std::optional<std::string> savestate_path;
};
class CBoot

View File

@ -382,8 +382,10 @@ bool BootCore(std::unique_ptr<BootParameters> boot)
std::holds_alternative<BootParameters::Disc>(boot->parameters);
if (load_ipl)
{
return Core::Init(std::make_unique<BootParameters>(BootParameters::IPL{
StartUp.m_region, std::move(std::get<BootParameters::Disc>(boot->parameters))}));
return Core::Init(std::make_unique<BootParameters>(
BootParameters::IPL{StartUp.m_region,
std::move(std::get<BootParameters::Disc>(boot->parameters))},
boot->savestate_path));
}
return Core::Init(std::move(boot));
}

View File

@ -96,7 +96,6 @@ static bool s_hardware_initialized = false;
static bool s_is_started = false;
static Common::Flag s_is_booting;
static void* s_window_handle = nullptr;
static std::string s_state_filename;
static std::thread s_emu_thread;
static StateChangedCallbackFunc s_on_state_changed_callback;
@ -136,15 +135,6 @@ void SetIsThrottlerTempDisabled(bool disable)
s_is_throttler_temp_disabled = disable;
}
std::string GetStateFileName()
{
return s_state_filename;
}
void SetStateFileName(const std::string& val)
{
s_state_filename = val;
}
void FrameUpdateOnCPUThread()
{
if (NetPlay::IsNetPlayRunning())
@ -328,7 +318,7 @@ static void CPUSetInitialExecutionState()
}
// Create the CPU thread, which is a CPU + Video thread in Single Core mode.
static void CpuThread()
static void CpuThread(const std::optional<std::string>& savestate_path)
{
DeclareAsCPUThread();
@ -351,17 +341,8 @@ static void CpuThread()
if (_CoreParameter.bFastmem)
EMM::InstallExceptionHandler(); // Let's run under memory watch
if (!s_state_filename.empty())
{
// Needs to PauseAndLock the Core
// NOTE: EmuThread should have left us in State::Stepping so nothing will happen
// until after the job is serviced.
QueueHostJob([] {
// Recheck in case Movie cleared it since.
if (!s_state_filename.empty())
::State::LoadAs(s_state_filename);
});
}
if (savestate_path)
QueueHostJob([&savestate_path] { ::State::LoadAs(*savestate_path); });
s_is_started = true;
CPUSetInitialExecutionState();
@ -399,7 +380,7 @@ static void CpuThread()
EMM::UninstallExceptionHandler();
}
static void FifoPlayerThread()
static void FifoPlayerThread(const std::optional<std::string>& savestate_path)
{
DeclareAsCPUThread();
const SConfig& _CoreParameter = SConfig::GetInstance();
@ -535,15 +516,20 @@ static void EmuThread(std::unique_ptr<BootParameters> boot)
Keyboard::LoadConfig();
}
const std::optional<std::string> savestate_path = boot->savestate_path;
// Load and Init Wiimotes - only if we are booting in Wii mode
if (core_parameter.bWii && !SConfig::GetInstance().m_bt_passthrough_enabled)
{
if (init_controllers)
Wiimote::Initialize(!s_state_filename.empty() ?
Wiimote::InitializeMode::DO_WAIT_FOR_WIIMOTES :
Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
{
Wiimote::Initialize(savestate_path ? Wiimote::InitializeMode::DO_WAIT_FOR_WIIMOTES :
Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
}
else
{
Wiimote::LoadConfig();
}
}
Common::ScopeGuard controller_guard{[init_controllers] {
@ -570,7 +556,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot)
PowerPC::SetMode(PowerPC::CoreMode::Interpreter);
// Determine the CPU thread function
void (*cpuThreadFunc)();
void (*cpuThreadFunc)(const std::optional<std::string>& savestate_path);
if (std::holds_alternative<BootParameters::DFF>(boot->parameters))
cpuThreadFunc = FifoPlayerThread;
else
@ -611,7 +597,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot)
Host_Message(WM_USER_CREATE);
// Spawn the CPU thread
s_cpu_thread = std::thread(cpuThreadFunc);
s_cpu_thread = std::thread(cpuThreadFunc, savestate_path);
// become the GPU thread
Fifo::RunGpuLoop();
@ -629,7 +615,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot)
Common::SetCurrentThreadName("Emuthread - Idle");
// Spawn the CPU+GPU thread
s_cpu_thread = std::thread(cpuThreadFunc);
s_cpu_thread = std::thread(cpuThreadFunc, savestate_path);
while (CPU::GetState() != CPU::State::PowerDown)
{

View File

@ -64,9 +64,6 @@ void Callback_WiimoteInterruptChannel(int _number, u16 _channelID, const void* _
// This displays messages in a user-visible way.
void DisplayMessage(const std::string& message, int time_in_ms);
std::string GetStateFileName();
void SetStateFileName(const std::string& val);
void FrameUpdateOnCPUThread();
bool ShouldSkipFrame(int skipped);

View File

@ -232,8 +232,6 @@ void Init(const BootParameters& boot)
}
memset(&s_padState, 0, sizeof(s_padState));
if (!tmpHeader.bFromSaveState || !IsPlayingInput())
Core::SetStateFileName("");
for (auto& disp : s_InputDisplay)
disp.clear();
@ -865,12 +863,12 @@ void ReadHeader()
}
// NOTE: Host Thread
bool PlayInput(const std::string& filename)
bool PlayInput(const std::string& movie_path, std::optional<std::string>* savestate_path)
{
if (s_playMode != MODE_NONE)
return false;
File::IOFile recording_file(filename, "rb");
File::IOFile recording_file(movie_path, "rb");
if (!recording_file.ReadArray(&tmpHeader, 1))
return false;
@ -902,13 +900,13 @@ bool PlayInput(const std::string& filename)
recording_file.Close();
// Load savestate (and skip to frame data)
if (tmpHeader.bFromSaveState)
if (tmpHeader.bFromSaveState && savestate_path)
{
const std::string stateFilename = filename + ".sav";
if (File::Exists(stateFilename))
Core::SetStateFileName(stateFilename);
const std::string savestate_path_temp = movie_path + ".sav";
if (File::Exists(savestate_path_temp))
*savestate_path = savestate_path_temp;
s_bRecordingFromSaveState = true;
Movie::LoadInput(filename);
Movie::LoadInput(movie_path);
}
return true;
@ -928,12 +926,12 @@ void DoState(PointerWrap& p)
}
// NOTE: Host Thread
void LoadInput(const std::string& filename)
void LoadInput(const std::string& movie_path)
{
File::IOFile t_record;
if (!t_record.Open(filename, "r+b"))
if (!t_record.Open(movie_path, "r+b"))
{
PanicAlertT("Failed to read %s", filename.c_str());
PanicAlertT("Failed to read %s", movie_path.c_str());
EndPlayInput(false);
return;
}
@ -942,7 +940,7 @@ void LoadInput(const std::string& filename)
if (!IsMovieHeader(tmpHeader.filetype))
{
PanicAlertT("Savestate movie %s is corrupted, movie recording stopping...", filename.c_str());
PanicAlertT("Savestate movie %s is corrupted, movie recording stopping...", movie_path.c_str());
EndPlayInput(false);
return;
}

View File

@ -5,6 +5,7 @@
#pragma once
#include <functional>
#include <optional>
#include <string>
#include "Common/CommonTypes.h"
@ -157,8 +158,8 @@ bool BeginRecordingInput(int controllers);
void RecordInput(GCPadStatus* PadStatus, int controllerID);
void RecordWiimote(int wiimote, u8* data, u8 size);
bool PlayInput(const std::string& filename);
void LoadInput(const std::string& filename);
bool PlayInput(const std::string& movie_path, std::optional<std::string>* savestate_path);
void LoadInput(const std::string& movie_path);
void ReadHeader();
void PlayController(GCPadStatus* PadStatus, int controllerID);
bool PlayWiimote(int wiimote, u8* data, const struct WiimoteEmu::ReportFeatures& rptf, int ext,

View File

@ -15,6 +15,7 @@
#include <QProgressDialog>
#include <future>
#include <optional>
#include "Common/Version.h"
@ -164,7 +165,7 @@ void MainWindow::CreateComponents()
m_fifo_window = new FIFOPlayerWindow(this);
connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this,
static_cast<void (MainWindow::*)(const QString&)>(&MainWindow::StartGame));
[this](const QString& path) { StartGame(path); });
#if defined(HAVE_XRANDR) && HAVE_XRANDR
m_graphics_window = new GraphicsWindow(
@ -192,7 +193,7 @@ void MainWindow::ConnectMenuBar()
// Emulation
connect(m_menu_bar, &MenuBar::Pause, this, &MainWindow::Pause);
connect(m_menu_bar, &MenuBar::Play, this, &MainWindow::Play);
connect(m_menu_bar, &MenuBar::Play, this, [this]() { Play(); });
connect(m_menu_bar, &MenuBar::Stop, this, &MainWindow::RequestStop);
connect(m_menu_bar, &MenuBar::Reset, this, &MainWindow::Reset);
connect(m_menu_bar, &MenuBar::Fullscreen, this, &MainWindow::FullScreen);
@ -278,7 +279,7 @@ void MainWindow::ConnectToolBar()
{
addToolBar(m_tool_bar);
connect(m_tool_bar, &ToolBar::OpenPressed, this, &MainWindow::Open);
connect(m_tool_bar, &ToolBar::PlayPressed, this, &MainWindow::Play);
connect(m_tool_bar, &ToolBar::PlayPressed, this, [this]() { Play(); });
connect(m_tool_bar, &ToolBar::PausePressed, this, &MainWindow::Pause);
connect(m_tool_bar, &ToolBar::StopPressed, this, &MainWindow::RequestStop);
connect(m_tool_bar, &ToolBar::FullScreenPressed, this, &MainWindow::FullScreen);
@ -290,7 +291,7 @@ void MainWindow::ConnectToolBar()
void MainWindow::ConnectGameList()
{
connect(m_game_list, &GameList::GameSelected, this, &MainWindow::Play);
connect(m_game_list, &GameList::GameSelected, this, [this]() { Play(); });
connect(m_game_list, &GameList::NetPlayHost, this, &MainWindow::NetPlayHost);
connect(m_game_list, &GameList::OpenGeneralSettings, this, &MainWindow::ShowGeneralWindow);
@ -327,7 +328,7 @@ void MainWindow::Open()
StartGame(file);
}
void MainWindow::Play()
void MainWindow::Play(const std::optional<std::string>& savestate_path)
{
// If we're in a paused game, start it up again.
// Otherwise, play the selected game, if there is one.
@ -343,14 +344,14 @@ void MainWindow::Play()
QSharedPointer<GameFile> selection = m_game_list->GetSelectedGame();
if (selection)
{
StartGame(selection->GetFilePath());
StartGame(selection->GetFilePath(), savestate_path);
}
else
{
auto default_path = QString::fromStdString(SConfig::GetInstance().m_strDefaultISO);
if (!default_path.isEmpty() && QFile::exists(default_path))
{
StartGame(default_path);
StartGame(default_path, savestate_path);
}
else
{
@ -472,9 +473,9 @@ void MainWindow::ScreenShot()
Core::SaveScreenShot();
}
void MainWindow::StartGame(const QString& path)
void MainWindow::StartGame(const QString& path, const std::optional<std::string>& savestate_path)
{
StartGame(BootParameters::GenerateFromFile(path.toStdString()));
StartGame(BootParameters::GenerateFromFile(path.toStdString(), savestate_path));
}
void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
@ -680,7 +681,7 @@ void MainWindow::NetPlayInit()
m_netplay_dialog = new NetPlayDialog(this);
connect(m_netplay_dialog, &NetPlayDialog::Boot, this,
static_cast<void (MainWindow::*)(const QString&)>(&MainWindow::StartGame));
[this](const QString& path) { StartGame(path); });
connect(m_netplay_dialog, &NetPlayDialog::Stop, this, &MainWindow::RequestStop);
connect(m_netplay_dialog, &NetPlayDialog::rejected, this, &MainWindow::NetPlayQuit);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Join, this, &MainWindow::NetPlayJoin);
@ -938,11 +939,12 @@ void MainWindow::OnPlayRecording()
emit ReadOnlyModeChanged(true);
}
if (Movie::PlayInput(dtm_file.toStdString()))
std::optional<std::string> savestate_path;
if (Movie::PlayInput(dtm_file.toStdString(), &savestate_path))
{
emit RecordingStatusChanged(true);
Play();
Play(savestate_path);
}
}

View File

@ -10,6 +10,7 @@
#include <QToolBar>
#include <memory>
#include <optional>
#include "DolphinQt2/GameList/GameList.h"
#include "DolphinQt2/MenuBar.h"
@ -47,7 +48,7 @@ signals:
private:
void Open();
void Play();
void Play(const std::optional<std::string>& savestate_path = {});
void Pause();
// May ask for confirmation. Returns whether or not it actually stopped.
@ -86,7 +87,7 @@ private:
void InitCoreCallbacks();
void StartGame(const QString& path);
void StartGame(const QString& path, const std::optional<std::string>& savestate_path = {});
void StartGame(std::unique_ptr<BootParameters>&& parameters);
void ShowRenderWidget();
void HideRenderWidget();

View File

@ -8,6 +8,7 @@
#include <atomic>
#include <cstddef>
#include <fstream>
#include <optional>
#include <string>
#include <utility>
#include <vector>
@ -130,8 +131,9 @@ void CRenderFrame::OnDropFiles(wxDropFilesEvent& event)
main_frame->GetMenuBar()->FindItem(IDM_RECORD_READ_ONLY)->Check(true);
}
if (Movie::PlayInput(filepath))
main_frame->BootGame("");
std::optional<std::string> savestate_path;
if (Movie::PlayInput(filepath, &savestate_path))
main_frame->BootGame("", savestate_path);
}
else if (!Core::IsRunning())
{

View File

@ -8,6 +8,7 @@
#include <cstddef>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
#include <wx/bitmap.h>
@ -105,7 +106,7 @@ public:
void ToggleLogConfigWindow(bool bShow);
void StatusBarMessage(const char* format, ...);
void ClearStatusBar();
void BootGame(const std::string& filename);
void BootGame(const std::string& filename, const std::optional<std::string>& savestate_path = {});
void StartGame(std::unique_ptr<BootParameters> boot);
bool RendererHasFocus();
bool RendererIsFullscreen();

View File

@ -9,6 +9,7 @@
#include <cstdio>
#include <future>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
#include <wx/app.h>
@ -301,7 +302,7 @@ void CFrame::OpenGeneralConfiguration(wxWindowID tab_id)
// 1. Show the game list and boot the selected game.
// 2. Default ISO
// 3. Boot last selected game
void CFrame::BootGame(const std::string& filename)
void CFrame::BootGame(const std::string& filename, const std::optional<std::string>& savestate_path)
{
std::string bootfile = filename;
SConfig& StartUp = SConfig::GetInstance();
@ -331,7 +332,7 @@ void CFrame::BootGame(const std::string& filename)
}
if (!bootfile.empty())
{
StartGame(BootParameters::GenerateFromFile(bootfile));
StartGame(BootParameters::GenerateFromFile(bootfile, savestate_path));
}
}
@ -513,8 +514,9 @@ void CFrame::OnPlayRecording(wxCommandEvent& WXUNUSED(event))
GetMenuBar()->FindItem(IDM_RECORD_READ_ONLY)->Check();
}
if (Movie::PlayInput(WxStrToStr(path)))
BootGame("");
std::optional<std::string> savestate_path;
if (Movie::PlayInput(WxStrToStr(path), &savestate_path))
BootGame("", savestate_path);
}
void CFrame::OnStopRecording(wxCommandEvent& WXUNUSED(event))

View File

@ -5,6 +5,7 @@
#include <OptionParser.h>
#include <cstdio>
#include <cstring>
#include <optional>
#include <string>
#include <utility>
#include <wx/app.h>
@ -256,12 +257,18 @@ void DolphinApp::AfterInit()
if (m_play_movie && !m_movie_file.empty())
{
if (Movie::PlayInput(WxStrToStr(m_movie_file)))
std::optional<std::string> savestate_path;
if (Movie::PlayInput(WxStrToStr(m_movie_file), &savestate_path))
{
if (m_boot)
{
m_boot->savestate_path = savestate_path;
main_frame->StartGame(std::move(m_boot));
}
else
main_frame->BootGame("");
{
main_frame->BootGame("", savestate_path);
}
}
}
// First check if we have an exec command line.