mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-29 19:15:38 +00:00
Qt: Implement context menu in game list
This commit is contained in:
parent
0c40903f74
commit
69f03959aa
|
@ -77,7 +77,7 @@ void HostInterface::CreateAudioStream()
|
||||||
m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4);
|
m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HostInterface::BootSystemFromFile(const char* filename)
|
bool HostInterface::BootSystem(const SystemBootParameters& parameters)
|
||||||
{
|
{
|
||||||
if (!AcquireHostDisplay())
|
if (!AcquireHostDisplay())
|
||||||
{
|
{
|
||||||
|
@ -92,7 +92,7 @@ bool HostInterface::BootSystemFromFile(const char* filename)
|
||||||
CreateAudioStream();
|
CreateAudioStream();
|
||||||
|
|
||||||
m_system = System::Create(this);
|
m_system = System::Create(this);
|
||||||
if (!m_system->Boot(filename))
|
if (!m_system->Boot(parameters))
|
||||||
{
|
{
|
||||||
ReportFormattedError("System failed to boot. The log may contain more information.");
|
ReportFormattedError("System failed to boot. The log may contain more information.");
|
||||||
DestroySystem();
|
DestroySystem();
|
||||||
|
@ -111,11 +111,6 @@ bool HostInterface::BootSystemFromFile(const char* filename)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HostInterface::BootSystemFromBIOS()
|
|
||||||
{
|
|
||||||
return BootSystemFromFile(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HostInterface::PauseSystem(bool paused)
|
void HostInterface::PauseSystem(bool paused)
|
||||||
{
|
{
|
||||||
if (paused == m_paused)
|
if (paused == m_paused)
|
||||||
|
@ -439,7 +434,8 @@ bool HostInterface::LoadState(const char* filename)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!BootSystemFromFile(nullptr))
|
SystemBootParameters boot_params;
|
||||||
|
if (!BootSystem(boot_params))
|
||||||
{
|
{
|
||||||
ReportFormattedError("Failed to boot system to load state from '%s'.", filename);
|
ReportFormattedError("Failed to boot system to load state from '%s'.", filename);
|
||||||
return false;
|
return false;
|
||||||
|
@ -512,7 +508,9 @@ bool HostInterface::SaveState(bool global, s32 slot)
|
||||||
|
|
||||||
bool HostInterface::ResumeSystemFromState(const char* filename, bool boot_on_failure)
|
bool HostInterface::ResumeSystemFromState(const char* filename, bool boot_on_failure)
|
||||||
{
|
{
|
||||||
if (!BootSystemFromFile(filename))
|
SystemBootParameters boot_params;
|
||||||
|
boot_params.filename = filename;
|
||||||
|
if (!BootSystem(boot_params))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const bool global = m_system->GetRunningCode().empty();
|
const bool global = m_system->GetRunningCode().empty();
|
||||||
|
@ -762,6 +760,20 @@ std::vector<HostInterface::SaveStateInfo> HostInterface::GetAvailableSaveStates(
|
||||||
return si;
|
return si;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HostInterface::DeleteSaveStates(const char* game_code, bool resume)
|
||||||
|
{
|
||||||
|
const std::vector<SaveStateInfo> states(GetAvailableSaveStates(game_code));
|
||||||
|
for (const SaveStateInfo& si : states)
|
||||||
|
{
|
||||||
|
if (si.global || (!resume && si.slot < 0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Log_InfoPrintf("Removing save state at '%s'", si.path.c_str());
|
||||||
|
if (!FileSystem::DeleteFile(si.path.c_str()))
|
||||||
|
Log_ErrorPrintf("Failed to delete save state file '%s'", si.path.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string HostInterface::GetMostRecentResumeSaveStatePath() const
|
std::string HostInterface::GetMostRecentResumeSaveStatePath() const
|
||||||
{
|
{
|
||||||
std::vector<FILESYSTEM_FIND_DATA> files;
|
std::vector<FILESYSTEM_FIND_DATA> files;
|
||||||
|
@ -970,7 +982,9 @@ void HostInterface::RecreateSystem()
|
||||||
}
|
}
|
||||||
|
|
||||||
DestroySystem();
|
DestroySystem();
|
||||||
if (!BootSystemFromFile(nullptr))
|
|
||||||
|
SystemBootParameters boot_params;
|
||||||
|
if (!BootSystem(boot_params))
|
||||||
{
|
{
|
||||||
ReportError("Failed to boot system after recreation.");
|
ReportError("Failed to boot system after recreation.");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -18,6 +18,7 @@ class HostDisplay;
|
||||||
class GameList;
|
class GameList;
|
||||||
|
|
||||||
class System;
|
class System;
|
||||||
|
struct SystemBootParameters;
|
||||||
|
|
||||||
class HostInterface
|
class HostInterface
|
||||||
{
|
{
|
||||||
|
@ -42,8 +43,7 @@ public:
|
||||||
/// Access to emulated system.
|
/// Access to emulated system.
|
||||||
ALWAYS_INLINE System* GetSystem() const { return m_system.get(); }
|
ALWAYS_INLINE System* GetSystem() const { return m_system.get(); }
|
||||||
|
|
||||||
bool BootSystemFromFile(const char* filename);
|
bool BootSystem(const SystemBootParameters& parameters);
|
||||||
bool BootSystemFromBIOS();
|
|
||||||
void PauseSystem(bool paused);
|
void PauseSystem(bool paused);
|
||||||
void ResetSystem();
|
void ResetSystem();
|
||||||
void PowerOffSystem();
|
void PowerOffSystem();
|
||||||
|
@ -90,6 +90,9 @@ public:
|
||||||
/// such as compiling shaders when starting up.
|
/// such as compiling shaders when starting up.
|
||||||
void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1, int progress_value = -1);
|
void DisplayLoadingScreen(const char* message, int progress_min = -1, int progress_max = -1, int progress_value = -1);
|
||||||
|
|
||||||
|
/// Deletes save states for the specified game code. If resume is set, the resume state is deleted too.
|
||||||
|
void DeleteSaveStates(const char* game_code, bool resume);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
enum : u32
|
enum : u32
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,6 +30,12 @@ Log_SetChannel(System);
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
SystemBootParameters::SystemBootParameters() = default;
|
||||||
|
|
||||||
|
SystemBootParameters::SystemBootParameters(std::string filename_) : filename(filename_) {}
|
||||||
|
|
||||||
|
SystemBootParameters::~SystemBootParameters() = default;
|
||||||
|
|
||||||
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
||||||
{
|
{
|
||||||
m_cpu = std::make_unique<CPU::Core>();
|
m_cpu = std::make_unique<CPU::Core>();
|
||||||
|
@ -102,14 +108,14 @@ void System::SetCPUExecutionMode(CPUExecutionMode mode)
|
||||||
m_cpu_code_cache->SetUseRecompiler(mode == CPUExecutionMode::Recompiler);
|
m_cpu_code_cache->SetUseRecompiler(mode == CPUExecutionMode::Recompiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::Boot(const char* filename)
|
bool System::Boot(const SystemBootParameters& params)
|
||||||
{
|
{
|
||||||
// Load CD image up and detect region.
|
// Load CD image up and detect region.
|
||||||
std::unique_ptr<CDImage> media;
|
std::unique_ptr<CDImage> media;
|
||||||
bool exe_boot = false;
|
bool exe_boot = false;
|
||||||
if (filename)
|
if (!params.filename.empty())
|
||||||
{
|
{
|
||||||
exe_boot = GameList::IsExeFileName(filename);
|
exe_boot = GameList::IsExeFileName(params.filename.c_str());
|
||||||
if (exe_boot)
|
if (exe_boot)
|
||||||
{
|
{
|
||||||
if (m_region == ConsoleRegion::Auto)
|
if (m_region == ConsoleRegion::Auto)
|
||||||
|
@ -120,11 +126,11 @@ bool System::Boot(const char* filename)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log_InfoPrintf("Loading CD image '%s'...", filename);
|
Log_InfoPrintf("Loading CD image '%s'...", params.filename.c_str());
|
||||||
media = CDImage::Open(filename);
|
media = CDImage::Open(params.filename.c_str());
|
||||||
if (!media)
|
if (!media)
|
||||||
{
|
{
|
||||||
m_host_interface->ReportFormattedError("Failed to load CD image '%s'", filename);
|
m_host_interface->ReportFormattedError("Failed to load CD image '%s'", params.filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +140,8 @@ bool System::Boot(const char* filename)
|
||||||
if (detected_region)
|
if (detected_region)
|
||||||
{
|
{
|
||||||
m_region = detected_region.value();
|
m_region = detected_region.value();
|
||||||
Log_InfoPrintf("Auto-detected %s region for '%s'", Settings::GetConsoleRegionName(m_region), filename);
|
Log_InfoPrintf("Auto-detected %s region for '%s'", Settings::GetConsoleRegionName(m_region),
|
||||||
|
params.filename.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -172,19 +179,22 @@ bool System::Boot(const char* filename)
|
||||||
BIOS::PatchBIOSEnableTTY(*bios_image, bios_hash);
|
BIOS::PatchBIOSEnableTTY(*bios_image, bios_hash);
|
||||||
|
|
||||||
// Load EXE late after BIOS.
|
// Load EXE late after BIOS.
|
||||||
if (exe_boot && !LoadEXE(filename, *bios_image))
|
if (exe_boot && !LoadEXE(params.filename.c_str(), *bios_image))
|
||||||
{
|
{
|
||||||
m_host_interface->ReportFormattedError("Failed to load EXE file '%s'", filename);
|
m_host_interface->ReportFormattedError("Failed to load EXE file '%s'", params.filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify change of disc.
|
// Notify change of disc.
|
||||||
UpdateRunningGame(filename, media.get());
|
UpdateRunningGame(params.filename.c_str(), media.get());
|
||||||
|
|
||||||
// Insert CD, and apply fastboot patch if enabled.
|
// Insert CD, and apply fastboot patch if enabled.
|
||||||
m_cdrom->InsertMedia(std::move(media));
|
m_cdrom->InsertMedia(std::move(media));
|
||||||
if (m_cdrom->HasMedia() && GetSettings().bios_patch_fast_boot)
|
if (m_cdrom->HasMedia() &&
|
||||||
|
(params.override_fast_boot.has_value() ? params.override_fast_boot.value() : GetSettings().bios_patch_fast_boot))
|
||||||
|
{
|
||||||
BIOS::PatchBIOSFastBoot(*bios_image, bios_hash);
|
BIOS::PatchBIOSFastBoot(*bios_image, bios_hash);
|
||||||
|
}
|
||||||
|
|
||||||
// Load the patched BIOS up.
|
// Load the patched BIOS up.
|
||||||
m_bus->SetBIOS(*bios_image);
|
m_bus->SetBIOS(*bios_image);
|
||||||
|
|
|
@ -28,11 +28,21 @@ class SPU;
|
||||||
class MDEC;
|
class MDEC;
|
||||||
class SIO;
|
class SIO;
|
||||||
|
|
||||||
|
struct SystemBootParameters
|
||||||
|
{
|
||||||
|
SystemBootParameters();
|
||||||
|
SystemBootParameters(std::string filename_);
|
||||||
|
~SystemBootParameters();
|
||||||
|
|
||||||
|
std::string filename;
|
||||||
|
std::optional<bool> override_fast_boot;
|
||||||
|
};
|
||||||
|
|
||||||
class System
|
class System
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
friend TimingEvent;
|
friend TimingEvent;
|
||||||
|
|
||||||
public:
|
|
||||||
~System();
|
~System();
|
||||||
|
|
||||||
/// Creates a new System.
|
/// Creates a new System.
|
||||||
|
@ -75,7 +85,7 @@ public:
|
||||||
float GetAverageFrameTime() const { return m_average_frame_time; }
|
float GetAverageFrameTime() const { return m_average_frame_time; }
|
||||||
float GetWorstFrameTime() const { return m_worst_frame_time; }
|
float GetWorstFrameTime() const { return m_worst_frame_time; }
|
||||||
|
|
||||||
bool Boot(const char* filename);
|
bool Boot(const SystemBootParameters& params);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
bool LoadState(ByteStream* state);
|
bool LoadState(ByteStream* state);
|
||||||
|
|
|
@ -261,6 +261,7 @@ void GameListWidget::initialize(QtHostInterface* host_interface)
|
||||||
m_table_view->setSortingEnabled(true);
|
m_table_view->setSortingEnabled(true);
|
||||||
m_table_view->setSelectionMode(QAbstractItemView::SingleSelection);
|
m_table_view->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
m_table_view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
m_table_view->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
m_table_view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
m_table_view->setAlternatingRowColors(true);
|
m_table_view->setAlternatingRowColors(true);
|
||||||
m_table_view->setShowGrid(false);
|
m_table_view->setShowGrid(false);
|
||||||
m_table_view->setCurrentIndex({});
|
m_table_view->setCurrentIndex({});
|
||||||
|
@ -269,9 +270,11 @@ void GameListWidget::initialize(QtHostInterface* host_interface)
|
||||||
m_table_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
m_table_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||||
m_table_view->resizeColumnsToContents();
|
m_table_view->resizeColumnsToContents();
|
||||||
|
|
||||||
connect(m_table_view, &QTableView::doubleClicked, this, &GameListWidget::onTableViewItemDoubleClicked);
|
|
||||||
connect(m_table_view->selectionModel(), &QItemSelectionModel::currentChanged, this,
|
connect(m_table_view->selectionModel(), &QItemSelectionModel::currentChanged, this,
|
||||||
&GameListWidget::onSelectionModelCurrentChanged);
|
&GameListWidget::onSelectionModelCurrentChanged);
|
||||||
|
connect(m_table_view, &QTableView::doubleClicked, this, &GameListWidget::onTableViewItemDoubleClicked);
|
||||||
|
connect(m_table_view, &QTableView::customContextMenuRequested, this,
|
||||||
|
&GameListWidget::onTableViewContextMenuRequested);
|
||||||
|
|
||||||
insertWidget(0, m_table_view);
|
insertWidget(0, m_table_view);
|
||||||
setCurrentIndex(0);
|
setCurrentIndex(0);
|
||||||
|
@ -282,16 +285,6 @@ void GameListWidget::onGameListRefreshed()
|
||||||
m_table_model->refresh();
|
m_table_model->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListWidget::onTableViewItemDoubleClicked(const QModelIndex& index)
|
|
||||||
{
|
|
||||||
const QModelIndex source_index = m_table_sort_model->mapToSource(index);
|
|
||||||
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const GameListEntry& entry = m_game_list->GetEntries().at(source_index.row());
|
|
||||||
emit bootEntryRequested(&entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameListWidget::onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous)
|
void GameListWidget::onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous)
|
||||||
{
|
{
|
||||||
const QModelIndex source_index = m_table_sort_model->mapToSource(current);
|
const QModelIndex source_index = m_table_sort_model->mapToSource(current);
|
||||||
|
@ -305,9 +298,45 @@ void GameListWidget::onSelectionModelCurrentChanged(const QModelIndex& current,
|
||||||
emit entrySelected(&entry);
|
emit entrySelected(&entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameListWidget::onTableViewItemDoubleClicked(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
const QModelIndex source_index = m_table_sort_model->mapToSource(index);
|
||||||
|
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const GameListEntry& entry = m_game_list->GetEntries().at(source_index.row());
|
||||||
|
emit entryDoubleClicked(&entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListWidget::onTableViewContextMenuRequested(const QPoint& point)
|
||||||
|
{
|
||||||
|
const GameListEntry* entry = getSelectedEntry();
|
||||||
|
if (!entry)
|
||||||
|
return;
|
||||||
|
|
||||||
|
emit entryContextMenuRequested(m_table_view->mapToGlobal(point), entry);
|
||||||
|
}
|
||||||
|
|
||||||
void GameListWidget::resizeEvent(QResizeEvent* event)
|
void GameListWidget::resizeEvent(QResizeEvent* event)
|
||||||
{
|
{
|
||||||
QStackedWidget::resizeEvent(event);
|
QStackedWidget::resizeEvent(event);
|
||||||
|
|
||||||
QtUtils::ResizeColumnsForTableView(m_table_view, {32, 80, -1, 60, 100});
|
QtUtils::ResizeColumnsForTableView(m_table_view, {32, 80, -1, 60, 100});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GameListEntry* GameListWidget::getSelectedEntry() const
|
||||||
|
{
|
||||||
|
const QItemSelectionModel* selection_model = m_table_view->selectionModel();
|
||||||
|
if (!selection_model->hasSelection())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const QModelIndexList selected_rows = selection_model->selectedRows();
|
||||||
|
if (selected_rows.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const QModelIndex source_index = m_table_sort_model->mapToSource(selected_rows[0]);
|
||||||
|
if (!source_index.isValid() || source_index.row() >= static_cast<int>(m_game_list->GetEntryCount()))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return &m_game_list->GetEntries().at(source_index.row());
|
||||||
|
}
|
||||||
|
|
|
@ -22,17 +22,21 @@ public:
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void entrySelected(const GameListEntry* entry);
|
void entrySelected(const GameListEntry* entry);
|
||||||
void bootEntryRequested(const GameListEntry* entry);
|
void entryDoubleClicked(const GameListEntry* entry);
|
||||||
|
void entryContextMenuRequested(const QPoint& point, const GameListEntry* entry);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onGameListRefreshed();
|
void onGameListRefreshed();
|
||||||
void onTableViewItemDoubleClicked(const QModelIndex& index);
|
|
||||||
void onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous);
|
void onSelectionModelCurrentChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||||
|
void onTableViewItemDoubleClicked(const QModelIndex& index);
|
||||||
|
void onTableViewContextMenuRequested(const QPoint& point);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void resizeEvent(QResizeEvent* event);
|
void resizeEvent(QResizeEvent* event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
const GameListEntry* getSelectedEntry() const;
|
||||||
|
|
||||||
QtHostInterface* m_host_interface = nullptr;
|
QtHostInterface* m_host_interface = nullptr;
|
||||||
GameList* m_game_list = nullptr;
|
GameList* m_game_list = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/game_list.h"
|
#include "core/game_list.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
#include "core/system.h"
|
||||||
#include "gamelistsettingswidget.h"
|
#include "gamelistsettingswidget.h"
|
||||||
#include "gamelistwidget.h"
|
#include "gamelistwidget.h"
|
||||||
#include "qtdisplaywindow.h"
|
#include "qtdisplaywindow.h"
|
||||||
|
@ -9,6 +10,7 @@
|
||||||
#include "qtsettingsinterface.h"
|
#include "qtsettingsinterface.h"
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
#include "settingwidgetbinder.h"
|
#include "settingwidgetbinder.h"
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
#include <QtGui/QDesktopServices>
|
#include <QtGui/QDesktopServices>
|
||||||
#include <QtWidgets/QFileDialog>
|
#include <QtWidgets/QFileDialog>
|
||||||
|
@ -47,7 +49,7 @@ bool MainWindow::confirmMessage(const QString& message)
|
||||||
{
|
{
|
||||||
const int result = QMessageBox::question(this, tr("DuckStation"), message);
|
const int result = QMessageBox::question(this, tr("DuckStation"), message);
|
||||||
focusDisplayWidget();
|
focusDisplayWidget();
|
||||||
|
|
||||||
return (result == QMessageBox::Yes);
|
return (result == QMessageBox::Yes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +176,15 @@ void MainWindow::onStartDiscActionTriggered()
|
||||||
if (filename.isEmpty())
|
if (filename.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_host_interface->bootSystemFromFile(std::move(filename));
|
SystemBootParameters boot_params;
|
||||||
|
boot_params.filename = filename.toStdString();
|
||||||
|
m_host_interface->bootSystem(boot_params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onStartBIOSActionTriggered()
|
||||||
|
{
|
||||||
|
SystemBootParameters boot_params;
|
||||||
|
m_host_interface->bootSystem(boot_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onChangeDiscFromFileActionTriggered()
|
void MainWindow::onChangeDiscFromFileActionTriggered()
|
||||||
|
@ -193,9 +203,8 @@ void MainWindow::onChangeDiscFromGameListActionTriggered()
|
||||||
switchToGameListView();
|
switchToGameListView();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OpenURL(QWidget* parent, const char* url)
|
static void OpenURL(QWidget* parent, const QUrl& qurl)
|
||||||
{
|
{
|
||||||
const QUrl qurl(QUrl::fromEncoded(QByteArray(url, static_cast<int>(std::strlen(url)))));
|
|
||||||
if (!QDesktopServices::openUrl(qurl))
|
if (!QDesktopServices::openUrl(qurl))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(parent, QObject::tr("Failed to open URL"),
|
QMessageBox::critical(parent, QObject::tr("Failed to open URL"),
|
||||||
|
@ -203,6 +212,11 @@ static void OpenURL(QWidget* parent, const char* url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void OpenURL(QWidget* parent, const char* url)
|
||||||
|
{
|
||||||
|
return OpenURL(parent, QUrl::fromEncoded(QByteArray(url, static_cast<int>(std::strlen(url)))));
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::onGitHubRepositoryActionTriggered()
|
void MainWindow::onGitHubRepositoryActionTriggered()
|
||||||
{
|
{
|
||||||
OpenURL(this, "https://github.com/stenzek/duckstation/");
|
OpenURL(this, "https://github.com/stenzek/duckstation/");
|
||||||
|
@ -215,6 +229,90 @@ void MainWindow::onIssueTrackerActionTriggered()
|
||||||
|
|
||||||
void MainWindow::onAboutActionTriggered() {}
|
void MainWindow::onAboutActionTriggered() {}
|
||||||
|
|
||||||
|
void MainWindow::onGameListEntrySelected(const GameListEntry* entry)
|
||||||
|
{
|
||||||
|
if (!entry)
|
||||||
|
{
|
||||||
|
m_ui.statusBar->clearMessage();
|
||||||
|
m_host_interface->populateSaveStateMenus("", m_ui.menuLoadState, m_ui.menuSaveState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ui.statusBar->showMessage(QString::fromStdString(entry->path));
|
||||||
|
m_host_interface->populateSaveStateMenus(entry->code.c_str(), m_ui.menuLoadState, m_ui.menuSaveState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onGameListEntryDoubleClicked(const GameListEntry* entry)
|
||||||
|
{
|
||||||
|
// if we're not running, boot the system, otherwise swap discs
|
||||||
|
QString path = QString::fromStdString(entry->path);
|
||||||
|
if (!m_emulation_running)
|
||||||
|
{
|
||||||
|
if (!entry->code.empty() && m_host_interface->getSettingValue("General/SaveStateOnExit", true).toBool())
|
||||||
|
{
|
||||||
|
m_host_interface->resumeSystemFromState(path, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SystemBootParameters boot_params;
|
||||||
|
boot_params.filename = path.toStdString();
|
||||||
|
m_host_interface->bootSystem(boot_params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_host_interface->changeDisc(path);
|
||||||
|
m_host_interface->pauseSystem(false);
|
||||||
|
switchToEmulationView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onGameListContextMenuRequested(const QPoint& point, const GameListEntry* entry)
|
||||||
|
{
|
||||||
|
QMenu menu;
|
||||||
|
|
||||||
|
// Hopefully this pointer doesn't disappear... it shouldn't.
|
||||||
|
if (entry)
|
||||||
|
{
|
||||||
|
connect(menu.addAction(tr("Properties...")), &QAction::triggered, [this]() { reportError(tr("TODO")); });
|
||||||
|
|
||||||
|
connect(menu.addAction(tr("Open Containing Directory...")), &QAction::triggered, [this, entry]() {
|
||||||
|
const QFileInfo fi(QString::fromStdString(entry->path));
|
||||||
|
OpenURL(this, QUrl::fromLocalFile(fi.absolutePath()));
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
if (!entry->code.empty())
|
||||||
|
{
|
||||||
|
m_host_interface->populateGameListContextMenu(entry->code.c_str(), this, &menu);
|
||||||
|
menu.addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(menu.addAction(tr("Default Boot")), &QAction::triggered,
|
||||||
|
[this, entry]() { m_host_interface->bootSystem(SystemBootParameters(entry->path)); });
|
||||||
|
|
||||||
|
connect(menu.addAction(tr("Fast Boot")), &QAction::triggered, [this, entry]() {
|
||||||
|
SystemBootParameters boot_params(entry->path);
|
||||||
|
boot_params.override_fast_boot = true;
|
||||||
|
m_host_interface->bootSystem(boot_params);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(menu.addAction(tr("Full Boot")), &QAction::triggered, [this, entry]() {
|
||||||
|
SystemBootParameters boot_params(entry->path);
|
||||||
|
boot_params.override_fast_boot = false;
|
||||||
|
m_host_interface->bootSystem(boot_params);
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(menu.addAction(tr("Add Search Directory...")), &QAction::triggered,
|
||||||
|
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
|
||||||
|
|
||||||
|
menu.exec(point);
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::setupAdditionalUi()
|
void MainWindow::setupAdditionalUi()
|
||||||
{
|
{
|
||||||
m_game_list_widget = new GameListWidget(m_ui.mainContainer);
|
m_game_list_widget = new GameListWidget(m_ui.mainContainer);
|
||||||
|
@ -320,7 +418,7 @@ void MainWindow::connectSignals()
|
||||||
onEmulationPaused(false);
|
onEmulationPaused(false);
|
||||||
|
|
||||||
connect(m_ui.actionStartDisc, &QAction::triggered, this, &MainWindow::onStartDiscActionTriggered);
|
connect(m_ui.actionStartDisc, &QAction::triggered, this, &MainWindow::onStartDiscActionTriggered);
|
||||||
connect(m_ui.actionStartBios, &QAction::triggered, m_host_interface, &QtHostInterface::bootSystemFromBIOS);
|
connect(m_ui.actionStartBios, &QAction::triggered, this, &MainWindow::onStartBIOSActionTriggered);
|
||||||
connect(m_ui.actionChangeDisc, &QAction::triggered, [this] { m_ui.menuChangeDisc->exec(QCursor::pos()); });
|
connect(m_ui.actionChangeDisc, &QAction::triggered, [this] { m_ui.menuChangeDisc->exec(QCursor::pos()); });
|
||||||
connect(m_ui.actionChangeDiscFromFile, &QAction::triggered, this, &MainWindow::onChangeDiscFromFileActionTriggered);
|
connect(m_ui.actionChangeDiscFromFile, &QAction::triggered, this, &MainWindow::onChangeDiscFromFileActionTriggered);
|
||||||
connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this,
|
connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this,
|
||||||
|
@ -369,34 +467,10 @@ void MainWindow::connectSignals()
|
||||||
&MainWindow::onSystemPerformanceCountersUpdated);
|
&MainWindow::onSystemPerformanceCountersUpdated);
|
||||||
connect(m_host_interface, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged);
|
connect(m_host_interface, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged);
|
||||||
|
|
||||||
connect(m_game_list_widget, &GameListWidget::bootEntryRequested, [this](const GameListEntry* entry) {
|
connect(m_game_list_widget, &GameListWidget::entrySelected, this, &MainWindow::onGameListEntrySelected);
|
||||||
// if we're not running, boot the system, otherwise swap discs
|
connect(m_game_list_widget, &GameListWidget::entryDoubleClicked, this, &MainWindow::onGameListEntryDoubleClicked);
|
||||||
QString path = QString::fromStdString(entry->path);
|
connect(m_game_list_widget, &GameListWidget::entryContextMenuRequested, this,
|
||||||
if (!m_emulation_running)
|
&MainWindow::onGameListContextMenuRequested);
|
||||||
{
|
|
||||||
if (!entry->code.empty() && m_host_interface->getSettingValue("General/SaveStateOnExit", true).toBool())
|
|
||||||
m_host_interface->resumeSystemFromState(path, true);
|
|
||||||
else
|
|
||||||
m_host_interface->bootSystemFromFile(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_host_interface->changeDisc(path);
|
|
||||||
m_host_interface->pauseSystem(false);
|
|
||||||
switchToEmulationView();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
connect(m_game_list_widget, &GameListWidget::entrySelected, [this](const GameListEntry* entry) {
|
|
||||||
if (!entry)
|
|
||||||
{
|
|
||||||
m_ui.statusBar->clearMessage();
|
|
||||||
m_host_interface->populateSaveStateMenus("", m_ui.menuLoadState, m_ui.menuSaveState);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ui.statusBar->showMessage(QString::fromStdString(entry->path));
|
|
||||||
m_host_interface->populateSaveStateMenus(entry->code.c_str(), m_ui.menuLoadState, m_ui.menuSaveState);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_host_interface->populateSaveStateMenus(nullptr, m_ui.menuLoadState, m_ui.menuSaveState);
|
m_host_interface->populateSaveStateMenus(nullptr, m_ui.menuLoadState, m_ui.menuSaveState);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ class QThread;
|
||||||
class GameListWidget;
|
class GameListWidget;
|
||||||
class QtHostInterface;
|
class QtHostInterface;
|
||||||
|
|
||||||
|
struct GameListEntry;
|
||||||
|
|
||||||
class MainWindow final : public QMainWindow
|
class MainWindow final : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -39,12 +41,17 @@ private Q_SLOTS:
|
||||||
void onRunningGameChanged(const QString& filename, const QString& game_code, const QString& game_title);
|
void onRunningGameChanged(const QString& filename, const QString& game_code, const QString& game_title);
|
||||||
|
|
||||||
void onStartDiscActionTriggered();
|
void onStartDiscActionTriggered();
|
||||||
|
void onStartBIOSActionTriggered();
|
||||||
void onChangeDiscFromFileActionTriggered();
|
void onChangeDiscFromFileActionTriggered();
|
||||||
void onChangeDiscFromGameListActionTriggered();
|
void onChangeDiscFromGameListActionTriggered();
|
||||||
void onGitHubRepositoryActionTriggered();
|
void onGitHubRepositoryActionTriggered();
|
||||||
void onIssueTrackerActionTriggered();
|
void onIssueTrackerActionTriggered();
|
||||||
void onAboutActionTriggered();
|
void onAboutActionTriggered();
|
||||||
|
|
||||||
|
void onGameListEntrySelected(const GameListEntry* entry);
|
||||||
|
void onGameListEntryDoubleClicked(const GameListEntry* entry);
|
||||||
|
void onGameListContextMenuRequested(const QPoint& point, const GameListEntry* entry);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,8 @@ QtHostInterface::QtHostInterface(QObject* parent)
|
||||||
: QObject(parent), CommonHostInterface(),
|
: QObject(parent), CommonHostInterface(),
|
||||||
m_qsettings(QString::fromStdString(GetSettingsFileName()), QSettings::IniFormat)
|
m_qsettings(QString::fromStdString(GetSettingsFileName()), QSettings::IniFormat)
|
||||||
{
|
{
|
||||||
|
qRegisterMetaType<SystemBootParameters>();
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
refreshGameList();
|
refreshGameList();
|
||||||
createThread();
|
createThread();
|
||||||
|
@ -149,15 +151,15 @@ QtDisplayWindow* QtHostInterface::createDisplayWindow()
|
||||||
return m_display_window;
|
return m_display_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::bootSystemFromFile(const QString& filename)
|
void QtHostInterface::bootSystem(const SystemBootParameters& params)
|
||||||
{
|
{
|
||||||
if (!isOnWorkerThread())
|
if (!isOnWorkerThread())
|
||||||
{
|
{
|
||||||
QMetaObject::invokeMethod(this, "bootSystemFromFile", Qt::QueuedConnection, Q_ARG(const QString&, filename));
|
QMetaObject::invokeMethod(this, "bootSystem", Qt::QueuedConnection, Q_ARG(const SystemBootParameters&, params));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HostInterface::BootSystemFromFile(filename.toStdString().c_str());
|
HostInterface::BootSystem(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::resumeSystemFromState(const QString& filename, bool boot_on_failure)
|
void QtHostInterface::resumeSystemFromState(const QString& filename, bool boot_on_failure)
|
||||||
|
@ -175,17 +177,6 @@ void QtHostInterface::resumeSystemFromState(const QString& filename, bool boot_o
|
||||||
HostInterface::ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure);
|
HostInterface::ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::bootSystemFromBIOS()
|
|
||||||
{
|
|
||||||
if (!isOnWorkerThread())
|
|
||||||
{
|
|
||||||
QMetaObject::invokeMethod(this, "bootSystemFromBIOS", Qt::QueuedConnection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HostInterface::BootSystemFromBIOS();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QtHostInterface::handleKeyEvent(int key, bool pressed)
|
void QtHostInterface::handleKeyEvent(int key, bool pressed)
|
||||||
{
|
{
|
||||||
if (!isOnWorkerThread())
|
if (!isOnWorkerThread())
|
||||||
|
@ -469,6 +460,62 @@ void QtHostInterface::populateSaveStateMenus(const char* game_code, QMenu* load_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QtHostInterface::populateGameListContextMenu(const char* game_code, QWidget* parent_window, QMenu* menu)
|
||||||
|
{
|
||||||
|
QAction* resume_action = menu->addAction(tr("Resume"));
|
||||||
|
resume_action->setEnabled(false);
|
||||||
|
|
||||||
|
QMenu* load_state_menu = menu->addMenu(tr("Load State"));
|
||||||
|
load_state_menu->setEnabled(false);
|
||||||
|
|
||||||
|
const std::vector<SaveStateInfo> available_states(GetAvailableSaveStates(game_code));
|
||||||
|
for (const SaveStateInfo& ssi : available_states)
|
||||||
|
{
|
||||||
|
if (ssi.global)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const s32 slot = ssi.slot;
|
||||||
|
const QDateTime timestamp(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(ssi.timestamp)));
|
||||||
|
const QString timestamp_str(timestamp.toString(Qt::SystemLocaleShortDate));
|
||||||
|
const QString path(QString::fromStdString(ssi.path));
|
||||||
|
|
||||||
|
QAction* action;
|
||||||
|
if (slot < 0)
|
||||||
|
{
|
||||||
|
resume_action->setText(tr("Resume (%1)").arg(timestamp_str));
|
||||||
|
resume_action->setEnabled(true);
|
||||||
|
action = resume_action;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
load_state_menu->setEnabled(true);
|
||||||
|
action = load_state_menu->addAction(tr("%1 Save %2 (%3)").arg(tr("Game")).arg(slot).arg(timestamp_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(action, &QAction::triggered, [this, path]() { loadState(path); });
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool has_any_states = resume_action->isEnabled() || load_state_menu->isEnabled();
|
||||||
|
QAction* delete_save_states_action = menu->addAction(tr("Delete Save States..."));
|
||||||
|
delete_save_states_action->setEnabled(has_any_states);
|
||||||
|
if (has_any_states)
|
||||||
|
{
|
||||||
|
const std::string game_code_copy(game_code);
|
||||||
|
connect(delete_save_states_action, &QAction::triggered, [this, parent_window, game_code_copy] {
|
||||||
|
if (QMessageBox::warning(
|
||||||
|
parent_window, tr("Confirm Save State Deletion"),
|
||||||
|
tr("Are you sure you want to delete all save states for %1?\n\nThe saves will not be recoverable.")
|
||||||
|
.arg(game_code_copy.c_str()),
|
||||||
|
QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteSaveStates(game_code_copy.c_str(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QtHostInterface::loadState(const QString& filename)
|
void QtHostInterface::loadState(const QString& filename)
|
||||||
{
|
{
|
||||||
if (!isOnWorkerThread())
|
if (!isOnWorkerThread())
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "core/host_interface.h"
|
#include "core/host_interface.h"
|
||||||
|
#include "core/system.h"
|
||||||
#include "frontend-common/common_host_interface.h"
|
#include "frontend-common/common_host_interface.h"
|
||||||
#include "opengldisplaywindow.h"
|
#include "opengldisplaywindow.h"
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
|
@ -23,6 +24,8 @@ class QTimer;
|
||||||
|
|
||||||
class GameList;
|
class GameList;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(SystemBootParameters);
|
||||||
|
|
||||||
class QtHostInterface : public QObject, private CommonHostInterface
|
class QtHostInterface : public QObject, private CommonHostInterface
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -52,6 +55,9 @@ public:
|
||||||
|
|
||||||
void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu);
|
void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu);
|
||||||
|
|
||||||
|
/// Fills menu with save state info and handlers.
|
||||||
|
void populateGameListContextMenu(const char* game_code, QWidget* parent_window, QMenu* menu);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void errorReported(const QString& message);
|
void errorReported(const QString& message);
|
||||||
void messageReported(const QString& message);
|
void messageReported(const QString& message);
|
||||||
|
@ -75,9 +81,8 @@ public Q_SLOTS:
|
||||||
void applySettings();
|
void applySettings();
|
||||||
void updateInputMap();
|
void updateInputMap();
|
||||||
void handleKeyEvent(int key, bool pressed);
|
void handleKeyEvent(int key, bool pressed);
|
||||||
void bootSystemFromFile(const QString& filename);
|
void bootSystem(const SystemBootParameters& params);
|
||||||
void resumeSystemFromState(const QString& filename, bool boot_on_failure);
|
void resumeSystemFromState(const QString& filename, bool boot_on_failure);
|
||||||
void bootSystemFromBIOS();
|
|
||||||
void powerOffSystem();
|
void powerOffSystem();
|
||||||
void synchronousPowerOffSystem();
|
void synchronousPowerOffSystem();
|
||||||
void resetSystem();
|
void resetSystem();
|
||||||
|
|
|
@ -47,7 +47,9 @@ static int Run(int argc, char* argv[])
|
||||||
// boot/load state
|
// boot/load state
|
||||||
if (boot_filename)
|
if (boot_filename)
|
||||||
{
|
{
|
||||||
if (host_interface->BootSystemFromFile(boot_filename) && state_index.has_value())
|
SystemBootParameters boot_params;
|
||||||
|
boot_params.filename = boot_filename;
|
||||||
|
if (host_interface->BootSystem(boot_params) && state_index.has_value())
|
||||||
host_interface->LoadState(state_is_global, state_index.value());
|
host_interface->LoadState(state_is_global, state_index.value());
|
||||||
}
|
}
|
||||||
else if (state_index.has_value())
|
else if (state_index.has_value())
|
||||||
|
|
|
@ -673,7 +673,10 @@ void SDLHostInterface::DrawMainMenuBar()
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("Start BIOS", nullptr, false, !system_enabled))
|
if (ImGui::MenuItem("Start BIOS", nullptr, false, !system_enabled))
|
||||||
{
|
{
|
||||||
RunLater([this]() { BootSystemFromBIOS(); });
|
RunLater([this]() {
|
||||||
|
SystemBootParameters boot_params;
|
||||||
|
BootSystem(boot_params);
|
||||||
|
});
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,7 +998,10 @@ void SDLHostInterface::DrawPoweredOffWindow()
|
||||||
ImGui::SetCursorPosX(button_left);
|
ImGui::SetCursorPosX(button_left);
|
||||||
if (ImGui::Button("Start BIOS", button_size))
|
if (ImGui::Button("Start BIOS", button_size))
|
||||||
{
|
{
|
||||||
RunLater([this]() { BootSystemFromFile(nullptr); });
|
RunLater([this]() {
|
||||||
|
SystemBootParameters boot_params;
|
||||||
|
BootSystem(boot_params);
|
||||||
|
});
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
|
@ -1372,7 +1378,10 @@ void SDLHostInterface::DoStartDisc()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
AddFormattedOSDMessage(2.0f, "Starting disc from '%s'...", path);
|
AddFormattedOSDMessage(2.0f, "Starting disc from '%s'...", path);
|
||||||
BootSystemFromFile(path);
|
|
||||||
|
SystemBootParameters boot_params;
|
||||||
|
boot_params.filename = path;
|
||||||
|
BootSystem(boot_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLHostInterface::DoChangeDisc()
|
void SDLHostInterface::DoChangeDisc()
|
||||||
|
|
Loading…
Reference in a new issue