CDVD: Synchronise access from different threads
Some checks failed
🐧 Linux Builds / AppImage (push) Has been cancelled
🐧 Linux Builds / Flatpak (push) Has been cancelled
🍎 MacOS Builds / Defaults (push) Has been cancelled
🖥️ Windows Builds / Lint VS Project Files (push) Has been cancelled
🖥️ Windows Builds / CMake (push) Has been cancelled
🖥️ Windows Builds / SSE4 (push) Has been cancelled
🖥️ Windows Builds / AVX2 (push) Has been cancelled
🏭 Update Controller Database / update-controller-db (push) Has been cancelled

This commit is contained in:
chaoticgd 2025-11-06 14:36:16 +00:00 committed by Ty
parent d02f30ee62
commit a98cfcf28c
16 changed files with 129 additions and 57 deletions

View File

@ -4,14 +4,17 @@
#include "GameListRefreshThread.h" #include "GameListRefreshThread.h"
#include "pcsx2/GameList.h" #include "pcsx2/GameList.h"
#include "pcsx2/Host.h"
#include "common/Assertions.h"
#include "common/Console.h"
#include "common/ProgressCallback.h" #include "common/ProgressCallback.h"
#include "common/Timer.h"
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
AsyncRefreshProgressCallback::AsyncRefreshProgressCallback(GameListRefreshThread* parent) AsyncRefreshProgressCallback::AsyncRefreshProgressCallback(bool popup_on_error, GameListRefreshThread* parent)
: m_parent(parent) : m_parent(parent)
, m_popup_on_error(popup_on_error)
{ {
} }
@ -57,17 +60,20 @@ void AsyncRefreshProgressCallback::SetTitle(const char* title)
void AsyncRefreshProgressCallback::DisplayError(const char* message) void AsyncRefreshProgressCallback::DisplayError(const char* message)
{ {
QMessageBox::critical(nullptr, QStringLiteral("Error"), QString::fromUtf8(message)); if (m_popup_on_error)
Host::ReportErrorAsync(TRANSLATE_SV("GameListRefreshThread", "Error"), message);
else
ERROR_LOG("{}", message);
} }
void AsyncRefreshProgressCallback::DisplayWarning(const char* message) void AsyncRefreshProgressCallback::DisplayWarning(const char* message)
{ {
QMessageBox::warning(nullptr, QStringLiteral("Warning"), QString::fromUtf8(message)); pxFailRel("Not implemented.");
} }
void AsyncRefreshProgressCallback::DisplayInformation(const char* message) void AsyncRefreshProgressCallback::DisplayInformation(const char* message)
{ {
QMessageBox::information(nullptr, QStringLiteral("Information"), QString::fromUtf8(message)); pxFailRel("Not implemented.");
} }
void AsyncRefreshProgressCallback::DisplayDebugMessage(const char* message) void AsyncRefreshProgressCallback::DisplayDebugMessage(const char* message)
@ -77,17 +83,18 @@ void AsyncRefreshProgressCallback::DisplayDebugMessage(const char* message)
void AsyncRefreshProgressCallback::ModalError(const char* message) void AsyncRefreshProgressCallback::ModalError(const char* message)
{ {
QMessageBox::critical(nullptr, QStringLiteral("Error"), QString::fromUtf8(message)); pxFailRel("Not implemented.");
} }
bool AsyncRefreshProgressCallback::ModalConfirmation(const char* message) bool AsyncRefreshProgressCallback::ModalConfirmation(const char* message)
{ {
return QMessageBox::question(nullptr, QStringLiteral("Question"), QString::fromUtf8(message)) == QMessageBox::Yes; pxFailRel("Not implemented.");
return false;
} }
void AsyncRefreshProgressCallback::ModalInformation(const char* message) void AsyncRefreshProgressCallback::ModalInformation(const char* message)
{ {
QMessageBox::information(nullptr, QStringLiteral("Information"), QString::fromUtf8(message)); pxFailRel("Not implemented.");
} }
void AsyncRefreshProgressCallback::fireUpdate() void AsyncRefreshProgressCallback::fireUpdate()
@ -95,9 +102,9 @@ void AsyncRefreshProgressCallback::fireUpdate()
m_parent->refreshProgress(m_status_text, m_last_value, m_last_range); m_parent->refreshProgress(m_status_text, m_last_value, m_last_range);
} }
GameListRefreshThread::GameListRefreshThread(bool invalidate_cache) GameListRefreshThread::GameListRefreshThread(bool invalidate_cache, bool popup_on_error)
: QThread() : QThread()
, m_progress(this) , m_progress(popup_on_error, this)
, m_invalidate_cache(invalidate_cache) , m_invalidate_cache(invalidate_cache)
{ {
} }

View File

@ -14,7 +14,7 @@ class GameListRefreshThread;
class AsyncRefreshProgressCallback : public BaseProgressCallback class AsyncRefreshProgressCallback : public BaseProgressCallback
{ {
public: public:
AsyncRefreshProgressCallback(GameListRefreshThread* parent); AsyncRefreshProgressCallback(bool popup_on_error, GameListRefreshThread* parent);
void Cancel(); void Cancel();
@ -38,6 +38,7 @@ private:
QString m_status_text; QString m_status_text;
int m_last_range = 1; int m_last_range = 1;
int m_last_value = 0; int m_last_value = 0;
bool m_popup_on_error = false;
}; };
class GameListRefreshThread final : public QThread class GameListRefreshThread final : public QThread
@ -45,7 +46,7 @@ class GameListRefreshThread final : public QThread
Q_OBJECT Q_OBJECT
public: public:
GameListRefreshThread(bool invalidate_cache); GameListRefreshThread(bool invalidate_cache, bool popup_on_error);
~GameListRefreshThread(); ~GameListRefreshThread();
void cancel(); void cancel();
@ -60,4 +61,5 @@ protected:
private: private:
AsyncRefreshProgressCallback m_progress; AsyncRefreshProgressCallback m_progress;
bool m_invalidate_cache; bool m_invalidate_cache;
bool m_popup_on_error;
}; };

View File

@ -294,7 +294,7 @@ void GameListWidget::initialize()
m_empty_ui.setupUi(m_empty_widget); m_empty_ui.setupUi(m_empty_widget);
m_empty_ui.supportedFormats->setText(qApp->translate("GameListWidget", SUPPORTED_FORMATS_STRING)); m_empty_ui.supportedFormats->setText(qApp->translate("GameListWidget", SUPPORTED_FORMATS_STRING));
connect(m_empty_ui.addGameDirectory, &QPushButton::clicked, this, [this]() { emit addGameDirectoryRequested(); }); connect(m_empty_ui.addGameDirectory, &QPushButton::clicked, this, [this]() { emit addGameDirectoryRequested(); });
connect(m_empty_ui.scanForNewGames, &QPushButton::clicked, this, [this]() { refresh(false); }); connect(m_empty_ui.scanForNewGames, &QPushButton::clicked, this, [this]() { refresh(false, true); });
connect(qApp, &QGuiApplication::applicationStateChanged, this, [this]() { GameListWidget::updateCustomBackgroundState(); }); connect(qApp, &QGuiApplication::applicationStateChanged, this, [this]() { GameListWidget::updateCustomBackgroundState(); });
m_ui.stack->insertWidget(2, m_empty_widget); m_ui.stack->insertWidget(2, m_empty_widget);
@ -453,11 +453,11 @@ bool GameListWidget::getShowGridCoverTitles() const
return m_model->getShowCoverTitles(); return m_model->getShowCoverTitles();
} }
void GameListWidget::refresh(bool invalidate_cache) void GameListWidget::refresh(bool invalidate_cache, bool popup_on_error)
{ {
cancelRefresh(); cancelRefresh();
m_refresh_thread = new GameListRefreshThread(invalidate_cache); m_refresh_thread = new GameListRefreshThread(invalidate_cache, popup_on_error);
connect(m_refresh_thread, &GameListRefreshThread::refreshProgress, this, &GameListWidget::onRefreshProgress, connect(m_refresh_thread, &GameListRefreshThread::refreshProgress, this, &GameListWidget::onRefreshProgress,
Qt::QueuedConnection); Qt::QueuedConnection);
connect(m_refresh_thread, &GameListRefreshThread::refreshComplete, this, &GameListWidget::onRefreshComplete, connect(m_refresh_thread, &GameListRefreshThread::refreshComplete, this, &GameListWidget::onRefreshComplete,

View File

@ -46,7 +46,7 @@ public:
void initialize(); void initialize();
void resizeTableViewColumnsToFit(); void resizeTableViewColumnsToFit();
void refresh(bool invalidate_cache); void refresh(bool invalidate_cache, bool popup_on_error);
void cancelRefresh(); void cancelRefresh();
void reloadThemeSpecificImages(); void reloadThemeSpecificImages();
void setCustomBackground(bool force = false); void setCustomBackground(bool force = false);

View File

@ -357,8 +357,8 @@ void MainWindow::connectSignals()
[this]() { doControllerSettings(ControllerSettingsWindow::Category::HotkeySettings); }); [this]() { doControllerSettings(ControllerSettingsWindow::Category::HotkeySettings); });
connect(m_ui.actionAddGameDirectory, &QAction::triggered, connect(m_ui.actionAddGameDirectory, &QAction::triggered,
[this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); }); [this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); });
connect(m_ui.actionScanForNewGames, &QAction::triggered, [this]() { refreshGameList(false); }); connect(m_ui.actionScanForNewGames, &QAction::triggered, [this]() { refreshGameList(false, true); });
connect(m_ui.actionRescanAllGames, &QAction::triggered, [this]() { refreshGameList(true); }); connect(m_ui.actionRescanAllGames, &QAction::triggered, [this]() { refreshGameList(true, true); });
connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled); connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled);
connect(m_ui.actionViewLockToolbar, &QAction::toggled, this, &MainWindow::onViewLockToolbarActionToggled); connect(m_ui.actionViewLockToolbar, &QAction::toggled, this, &MainWindow::onViewLockToolbarActionToggled);
connect(m_ui.actionViewStatusBar, &QAction::toggled, this, &MainWindow::onViewStatusBarActionToggled); connect(m_ui.actionViewStatusBar, &QAction::toggled, this, &MainWindow::onViewStatusBarActionToggled);
@ -547,7 +547,7 @@ void MainWindow::recreate()
MainWindow* new_main_window = new MainWindow(); MainWindow* new_main_window = new MainWindow();
pxAssert(g_main_window == new_main_window); pxAssert(g_main_window == new_main_window);
new_main_window->initialize(); new_main_window->initialize();
new_main_window->refreshGameList(false); new_main_window->refreshGameList(false, false);
new_main_window->show(); new_main_window->show();
deleteLater(); deleteLater();
@ -1137,7 +1137,7 @@ bool MainWindow::shouldMouseLock() const
if (!Host::GetBoolSettingValue("EmuCore", "EnableMouseLock", false)) if (!Host::GetBoolSettingValue("EmuCore", "EnableMouseLock", false))
return false; return false;
if(m_display_created == false || m_display_widget == nullptr && !isRenderingToMain()) if (m_display_created == false || m_display_widget == nullptr && !isRenderingToMain())
return false; return false;
bool windowsHidden = (!g_debugger_window || g_debugger_window->isHidden()) && bool windowsHidden = (!g_debugger_window || g_debugger_window->isHidden()) &&
@ -1146,7 +1146,7 @@ bool MainWindow::shouldMouseLock() const
auto* displayWindow = isRenderingToMain() ? window() : m_display_widget->window(); auto* displayWindow = isRenderingToMain() ? window() : m_display_widget->window();
if(displayWindow == nullptr) if (displayWindow == nullptr)
return false; return false;
return windowsHidden && (displayWindow->isActiveWindow() || displayWindow->isFullScreen()); return windowsHidden && (displayWindow->isActiveWindow() || displayWindow->isFullScreen());
@ -1215,13 +1215,9 @@ void MainWindow::switchToEmulationView()
m_display_widget->setFocus(); m_display_widget->setFocus();
} }
void MainWindow::refreshGameList(bool invalidate_cache) void MainWindow::refreshGameList(bool invalidate_cache, bool popup_on_error)
{ {
// can't do this while the VM is running because of CDVD m_game_list_widget->refresh(invalidate_cache, popup_on_error);
if (s_vm_valid)
return;
m_game_list_widget->refresh(invalidate_cache);
} }
void MainWindow::cancelGameListRefresh() void MainWindow::cancelGameListRefresh()
@ -1466,8 +1462,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
const time_t entry_played_time = GameList::GetCachedPlayedTimeForSerial(entry->serial); const time_t entry_played_time = GameList::GetCachedPlayedTimeForSerial(entry->serial);
// Best two options given zero play time are to grey this out or to not show it at all. // Best two options given zero play time are to grey this out or to not show it at all.
if (entry_played_time) if (entry_played_time)
connect(menu.addAction(tr("Reset Play Time")), &QAction::triggered, [this, entry, entry_played_time]() connect(menu.addAction(tr("Reset Play Time")), &QAction::triggered, [this, entry, entry_played_time]() { clearGameListEntryPlayTime(entry, entry_played_time); });
{ clearGameListEntryPlayTime(entry, entry_played_time); });
// Check Wiki Page functionality is based on a serial redirect. // Check Wiki Page functionality is based on a serial redirect.
if (!entry->serial.empty()) if (!entry->serial.empty())
@ -2102,7 +2097,7 @@ void MainWindow::onVMStopped()
// reload played time // reload played time
if (m_game_list_widget->isShowingGameList()) if (m_game_list_widget->isShowingGameList())
m_game_list_widget->refresh(false); m_game_list_widget->refresh(false, false);
} }
void MainWindow::onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path, void MainWindow::onGameChanged(const QString& title, const QString& elf_override, const QString& disc_path,
@ -2932,12 +2927,12 @@ void MainWindow::clearGameListEntryPlayTime(const GameList::Entry* entry, const
if (QMessageBox::question(this, tr("Confirm Reset"), if (QMessageBox::question(this, tr("Confirm Reset"),
tr("Are you sure you want to reset the play time for '%1' (%2)?\n\nYour current play time is %3.\n\nThis action cannot be undone.") tr("Are you sure you want to reset the play time for '%1' (%2)?\n\nYour current play time is %3.\n\nThis action cannot be undone.")
.arg(entry->title.empty() ? tr("empty title") : QString::fromStdString(entry->title), .arg(entry->title.empty() ? tr("empty title") : QString::fromStdString(entry->title),
entry->serial.empty() ? tr("no serial") : QString::fromStdString(entry->serial), entry->serial.empty() ? tr("no serial") : QString::fromStdString(entry->serial),
QString::fromStdString(GameList::FormatTimespan(entry_played_time, true))), QString::fromStdString(GameList::FormatTimespan(entry_played_time, true))),
(QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes) (QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes)
{ {
GameList::ClearPlayedTimeForSerial(entry->serial); GameList::ClearPlayedTimeForSerial(entry->serial);
m_game_list_widget->refresh(false); m_game_list_widget->refresh(false, false);
} }
} }

View File

@ -113,7 +113,7 @@ public:
void checkMousePosition(int x, int y); void checkMousePosition(int x, int y);
public Q_SLOTS: public Q_SLOTS:
void checkForUpdates(bool display_message, bool force_check); void checkForUpdates(bool display_message, bool force_check);
void refreshGameList(bool invalidate_cache); void refreshGameList(bool invalidate_cache, bool popup_on_error);
void cancelGameListRefresh(); void cancelGameListRefresh();
void reportInfo(const QString& title, const QString& message); void reportInfo(const QString& title, const QString& message);
void reportError(const QString& title, const QString& message); void reportError(const QString& title, const QString& message);

View File

@ -1152,7 +1152,7 @@ void Host::OpenHostFileSelectorAsync(std::string_view title, bool select_directo
if (!filters.empty()) if (!filters.empty())
{ {
filters_str.append(QStringLiteral("All File Types (%1)") filters_str.append(QStringLiteral("All File Types (%1)")
.arg(QString::fromStdString(StringUtil::JoinString(filters.begin(), filters.end(), " ")))); .arg(QString::fromStdString(StringUtil::JoinString(filters.begin(), filters.end(), " "))));
for (const std::string& filter : filters) for (const std::string& filter : filters)
{ {
filters_str.append( filters_str.append(
@ -2385,7 +2385,7 @@ int main(int argc, char* argv[])
// When running in batch mode, ensure game list is loaded, but don't scan for any new files. // When running in batch mode, ensure game list is loaded, but don't scan for any new files.
if (!s_batch_mode) if (!s_batch_mode)
g_main_window->refreshGameList(false); g_main_window->refreshGameList(false, false);
else else
GameList::Refresh(false, true); GameList::Refresh(false, true);

View File

@ -63,7 +63,7 @@ bool GameListSettingsWidget::addExcludedPath(const std::string& path)
Host::CommitBaseSettingChanges(); Host::CommitBaseSettingChanges();
m_ui.excludedPaths->addItem(QString::fromStdString(path)); m_ui.excludedPaths->addItem(QString::fromStdString(path));
g_main_window->refreshGameList(false); g_main_window->refreshGameList(false, true);
return true; return true;
} }
@ -152,7 +152,7 @@ void GameListSettingsWidget::addSearchDirectory(const QString& path, bool recurs
Host::AddBaseValueToStringList("GameList", recursive ? "RecursivePaths" : "Paths", spath.c_str()); Host::AddBaseValueToStringList("GameList", recursive ? "RecursivePaths" : "Paths", spath.c_str());
Host::CommitBaseSettingChanges(); Host::CommitBaseSettingChanges();
refreshDirectoryList(); refreshDirectoryList();
g_main_window->refreshGameList(false); g_main_window->refreshGameList(false, true);
} }
void GameListSettingsWidget::removeSearchDirectory(const QString& path) void GameListSettingsWidget::removeSearchDirectory(const QString& path)
@ -166,7 +166,7 @@ void GameListSettingsWidget::removeSearchDirectory(const QString& path)
Host::CommitBaseSettingChanges(); Host::CommitBaseSettingChanges();
refreshDirectoryList(); refreshDirectoryList();
g_main_window->refreshGameList(false); g_main_window->refreshGameList(false, true);
} }
void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& point) void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& point)
@ -261,7 +261,7 @@ void GameListSettingsWidget::onRemoveExcludedPathButtonClicked()
delete item; delete item;
g_main_window->refreshGameList(false); g_main_window->refreshGameList(false, true);
} }
void GameListSettingsWidget::onExcludedPathsSelectionChanged() void GameListSettingsWidget::onExcludedPathsSelectionChanged()
@ -271,10 +271,10 @@ void GameListSettingsWidget::onExcludedPathsSelectionChanged()
void GameListSettingsWidget::onRescanAllGamesClicked() void GameListSettingsWidget::onRescanAllGamesClicked()
{ {
g_main_window->refreshGameList(true); g_main_window->refreshGameList(true, true);
} }
void GameListSettingsWidget::onScanForNewGamesClicked() void GameListSettingsWidget::onScanForNewGamesClicked()
{ {
g_main_window->refreshGameList(false); g_main_window->refreshGameList(false, true);
} }

View File

@ -280,7 +280,8 @@ void GameSummaryWidget::onVerifyClicked()
Error error; Error error;
if (!hasher.Open(m_entry_path, &error)) if (!hasher.Open(m_entry_path, &error))
{ {
setVerifyResult(QString::fromStdString(error.GetDescription())); QString message(QString::fromStdString(error.GetDescription()));
QMessageBox::critical(QtUtils::GetRootWidget(this), tr("Error"), message);
return; return;
} }

View File

@ -21,6 +21,7 @@
#include <ctype.h> #include <ctype.h>
#include <exception> #include <exception>
#include <memory> #include <memory>
#include <mutex>
#include <time.h> #include <time.h>
#include "fmt/format.h" #include "fmt/format.h"
@ -270,6 +271,23 @@ static void DetectDiskType()
static std::string m_SourceFilename[3]; static std::string m_SourceFilename[3];
static CDVD_SourceType m_CurrentSourceType = CDVD_SourceType::NoDisc; static CDVD_SourceType m_CurrentSourceType = CDVD_SourceType::NoDisc;
static std::mutex s_cdvd_lock;
bool cdvdLock(Error* error)
{
if (!s_cdvd_lock.try_lock())
{
Error::SetString(error, TRANSLATE_STR("CDVD", "The CDVD system is currently in use."));
return false;
}
return true;
}
void cdvdUnlock()
{
s_cdvd_lock.unlock();
}
void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile) void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile)
{ {
@ -579,7 +597,7 @@ static s32 NODISCdummyS32()
return 0; return 0;
} }
static void NODISCnewDiskCB(void (*/* callback */)()) static void NODISCnewDiskCB(void (* /* callback */)())
{ {
} }

View File

@ -20,7 +20,6 @@ struct cdvdTrackIndex
u8 discM; // current minute location on the disc (BCD encoded) u8 discM; // current minute location on the disc (BCD encoded)
u8 discS; // current sector location on the disc (BCD encoded) u8 discS; // current sector location on the disc (BCD encoded)
u8 discF; // current frame location on the disc (BCD encoded) u8 discF; // current frame location on the disc (BCD encoded)
}; };
struct cdvdTrack struct cdvdTrack
@ -188,6 +187,13 @@ extern u8 strack;
extern u8 etrack; extern u8 etrack;
extern std::array<cdvdTrack, 100> tracks; extern std::array<cdvdTrack, 100> tracks;
/// Try to take the CDVD lock, return false if it's already in use.
/// Must be called before your first CDVD call.
extern bool cdvdLock(Error* error = nullptr);
/// Release the CDVD lock. Must be called after you're done with CDVD.
extern void cdvdUnlock();
extern void CDVDsys_ChangeSource(CDVD_SourceType type); extern void CDVDsys_ChangeSource(CDVD_SourceType type);
extern void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile); extern void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile);
extern const std::string& CDVDsys_GetFile(CDVD_SourceType srctype); extern const std::string& CDVDsys_GetFile(CDVD_SourceType srctype);

View File

@ -7,7 +7,6 @@
#include "common/Error.h" #include "common/Error.h"
#include "common/MD5Digest.h" #include "common/MD5Digest.h"
#include "common/StringUtil.h"
#include "fmt/format.h" #include "fmt/format.h"
@ -39,6 +38,10 @@ bool IsoHasher::Open(std::string iso_path, Error* error)
{ {
Close(); Close();
m_is_locked = cdvdLock(error);
if (!m_is_locked)
return false;
CDVDsys_SetFile(CDVD_SourceType::Iso, std::move(iso_path)); CDVDsys_SetFile(CDVD_SourceType::Iso, std::move(iso_path));
CDVDsys_ChangeSource(CDVD_SourceType::Iso); CDVDsys_ChangeSource(CDVD_SourceType::Iso);
@ -103,6 +106,12 @@ bool IsoHasher::Open(std::string iso_path, Error* error)
void IsoHasher::Close() void IsoHasher::Close()
{ {
if (!m_is_locked)
return;
cdvdUnlock();
m_is_locked = false;
if (!m_is_open) if (!m_is_open)
return; return;
@ -151,7 +160,7 @@ bool IsoHasher::ComputeTrackHash(Track& track, ProgressCallback* callback)
const u32 update_interval = std::max<u32>(track.sectors / 100u, 1u); const u32 update_interval = std::max<u32>(track.sectors / 100u, 1u);
callback->SetStatusText( callback->SetStatusText(
fmt::format(TRANSLATE_FS("CDVD", "Calculating checksum for track {}..."), track.number).c_str()); fmt::format(TRANSLATE_FS("CDVD", "Calculating checksum for track {}..."), track.number).c_str());
callback->SetProgressRange(track.sectors); callback->SetProgressRange(track.sectors);
MD5Digest md5; MD5Digest md5;
for (u32 i = 0; i < track.sectors; i++) for (u32 i = 0; i < track.sectors; i++)

View File

@ -28,6 +28,9 @@ public:
IsoHasher(); IsoHasher();
~IsoHasher(); ~IsoHasher();
IsoHasher(const IsoHasher&) = delete;
IsoHasher& operator=(const IsoHasher&) = delete;
static std::string_view GetTrackTypeString(u32 type); static std::string_view GetTrackTypeString(u32 type);
u32 GetTrackCount() const { return static_cast<u32>(m_tracks.size()); } u32 GetTrackCount() const { return static_cast<u32>(m_tracks.size()); }
@ -44,6 +47,7 @@ private:
bool ComputeTrackHash(Track& track, ProgressCallback* callback); bool ComputeTrackHash(Track& track, ProgressCallback* callback);
std::vector<Track> m_tracks; std::vector<Track> m_tracks;
bool m_is_locked = false;
bool m_is_open = false; bool m_is_open = false;
bool m_is_cd = false; bool m_is_cd = false;
}; };

View File

@ -163,6 +163,9 @@ void SymbolImporter::Reset()
void SymbolImporter::LoadAndAnalyseElf(Pcsx2Config::DebugAnalysisOptions options) void SymbolImporter::LoadAndAnalyseElf(Pcsx2Config::DebugAnalysisOptions options)
{ {
if (!VMManager::HasValidVM())
return;
const std::string& elf_path = VMManager::GetCurrentELF(); const std::string& elf_path = VMManager::GetCurrentELF();
Error error; Error error;
@ -192,6 +195,9 @@ void SymbolImporter::AnalyseElf(
Pcsx2Config::DebugAnalysisOptions options, Pcsx2Config::DebugAnalysisOptions options,
bool wait_until_elf_is_loaded) bool wait_until_elf_is_loaded)
{ {
if (!VMManager::HasValidVM())
return;
// Search for a .sym file to load symbols from. // Search for a .sym file to load symbols from.
std::string nocash_path; std::string nocash_path;
CDVD_SourceType source_type = CDVDsys_GetSourceType(); CDVD_SourceType source_type = CDVDsys_GetSourceType();

View File

@ -16,6 +16,7 @@
#include "common/HeterogeneousContainers.h" #include "common/HeterogeneousContainers.h"
#include "common/Path.h" #include "common/Path.h"
#include "common/ProgressCallback.h" #include "common/ProgressCallback.h"
#include "common/ScopedGuard.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include <algorithm> #include <algorithm>
@ -826,6 +827,15 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback*
if (!progress) if (!progress)
progress = ProgressCallback::NullProgressCallback; progress = ProgressCallback::NullProgressCallback;
Error cdvd_lock_error;
if (!cdvdLock(&cdvd_lock_error))
{
progress->DisplayError(cdvd_lock_error.GetDescription().c_str());
return;
}
ScopedGuard unlock_cdvd = &cdvdUnlock;
if (invalidate_cache) if (invalidate_cache)
DeleteCacheFile(); DeleteCacheFile();
else else

View File

@ -485,7 +485,7 @@ void VMManager::UpdateLoggingSettings(SettingsInterface& si)
if (system_console_enabled != Log::IsConsoleOutputEnabled()) if (system_console_enabled != Log::IsConsoleOutputEnabled())
Log::SetConsoleOutputLevel(system_console_enabled ? level : LOGLEVEL_NONE); Log::SetConsoleOutputLevel(system_console_enabled ? level : LOGLEVEL_NONE);
// Debug console only exists on Windows. // Debug console only exists on Windows.
#ifdef _WIN32 #ifdef _WIN32
const bool debug_console_enabled = IsDebuggerPresent() && si.GetBoolValue("Logging", "EnableDebugConsole", false); const bool debug_console_enabled = IsDebuggerPresent() && si.GetBoolValue("Logging", "EnableDebugConsole", false);
Log::SetDebugOutputLevel(debug_console_enabled ? level : LOGLEVEL_NONE); Log::SetDebugOutputLevel(debug_console_enabled ? level : LOGLEVEL_NONE);
@ -776,8 +776,8 @@ std::string VMManager::GetGameSettingsPath(const std::string_view game_serial, u
std::string sanitized_serial(Path::SanitizeFileName(game_serial)); std::string sanitized_serial(Path::SanitizeFileName(game_serial));
return game_serial.empty() ? return game_serial.empty() ?
Path::Combine(EmuFolders::GameSettings, fmt::format("{:08X}.ini", game_crc)) : Path::Combine(EmuFolders::GameSettings, fmt::format("{:08X}.ini", game_crc)) :
Path::Combine(EmuFolders::GameSettings, fmt::format("{}_{:08X}.ini", sanitized_serial, game_crc)); Path::Combine(EmuFolders::GameSettings, fmt::format("{}_{:08X}.ini", sanitized_serial, game_crc));
} }
std::string VMManager::GetDiscOverrideFromGameSettings(const std::string& elf_path) std::string VMManager::GetDiscOverrideFromGameSettings(const std::string& elf_path)
@ -1324,6 +1324,15 @@ bool VMManager::Initialize(VMBootParameters boot_params)
} }
} }
Error cdvd_lock_error;
if (!cdvdLock(&cdvd_lock_error))
{
Host::ReportErrorAsync("Startup Error", cdvd_lock_error.GetDescription());
return false;
}
ScopedGuard unlock_cdvd = &cdvdUnlock;
// resolve source type // resolve source type
if (boot_params.source_type.has_value()) if (boot_params.source_type.has_value())
{ {
@ -1355,12 +1364,14 @@ bool VMManager::Initialize(VMBootParameters boot_params)
if (!LoadBIOS()) if (!LoadBIOS())
{ {
Host::ReportErrorAsync(TRANSLATE_SV("VMManager", "Error No BIOS Present"), Host::ReportErrorAsync(TRANSLATE_SV("VMManager", "Error No BIOS Present"),
fmt::format(TRANSLATE_FS("VMManager", fmt::format(
"PCSX2 requires a PlayStation 2 BIOS in order to run.\n\n" TRANSLATE_FS("VMManager",
"For legal reasons, you will need to obtain this BIOS from a PlayStation 2 unit which you own.\n\n" "PCSX2 requires a PlayStation 2 BIOS in order to run.\n\n"
"For step-by-step help with this process, please consult the setup guide at {}.\n\n" "For legal reasons, you will need to obtain this BIOS from a PlayStation 2 unit which you own.\n\n"
"PCSX2 will be able to run once you've placed your BIOS image inside the folder named \"bios\" within the data directory " "For step-by-step help with this process, please consult the setup guide at {}.\n\n"
"(Tools Menu -> Open Data Directory)."), PCSX2_DOCUMENTATION_BIOS_URL_SHORTENED)); "PCSX2 will be able to run once you've placed your BIOS image inside the folder named \"bios\" within the data directory "
"(Tools Menu -> Open Data Directory)."),
PCSX2_DOCUMENTATION_BIOS_URL_SHORTENED));
return false; return false;
} }
@ -1559,6 +1570,7 @@ bool VMManager::Initialize(VMBootParameters boot_params)
close_memcards.Cancel(); close_memcards.Cancel();
close_cdvd.Cancel(); close_cdvd.Cancel();
close_cdvd_files.Cancel(); close_cdvd_files.Cancel();
unlock_cdvd.Cancel();
close_state.Cancel(); close_state.Cancel();
if (EmuConfig.CdvdPrecache) if (EmuConfig.CdvdPrecache)
@ -1667,6 +1679,8 @@ void VMManager::Shutdown(bool save_resume_state)
else else
cdvdSaveNVRAM(); cdvdSaveNVRAM();
cdvdUnlock();
s_state.store(VMState::Shutdown, std::memory_order_release); s_state.store(VMState::Shutdown, std::memory_order_release);
FullscreenUI::OnVMDestroyed(); FullscreenUI::OnVMDestroyed();
SaveStateSelectorUI::Clear(); SaveStateSelectorUI::Clear();