diff --git a/pcsx2-qt/GameList/GameListRefreshThread.cpp b/pcsx2-qt/GameList/GameListRefreshThread.cpp index 5b3084389c..6fa5219e51 100644 --- a/pcsx2-qt/GameList/GameListRefreshThread.cpp +++ b/pcsx2-qt/GameList/GameListRefreshThread.cpp @@ -4,14 +4,17 @@ #include "GameListRefreshThread.h" #include "pcsx2/GameList.h" +#include "pcsx2/Host.h" +#include "common/Assertions.h" +#include "common/Console.h" #include "common/ProgressCallback.h" -#include "common/Timer.h" #include -AsyncRefreshProgressCallback::AsyncRefreshProgressCallback(GameListRefreshThread* parent) +AsyncRefreshProgressCallback::AsyncRefreshProgressCallback(bool popup_on_error, GameListRefreshThread* 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) { - 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) { - QMessageBox::warning(nullptr, QStringLiteral("Warning"), QString::fromUtf8(message)); + pxFailRel("Not implemented."); } void AsyncRefreshProgressCallback::DisplayInformation(const char* message) { - QMessageBox::information(nullptr, QStringLiteral("Information"), QString::fromUtf8(message)); + pxFailRel("Not implemented."); } void AsyncRefreshProgressCallback::DisplayDebugMessage(const char* message) @@ -77,17 +83,18 @@ void AsyncRefreshProgressCallback::DisplayDebugMessage(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) { - return QMessageBox::question(nullptr, QStringLiteral("Question"), QString::fromUtf8(message)) == QMessageBox::Yes; + pxFailRel("Not implemented."); + return false; } void AsyncRefreshProgressCallback::ModalInformation(const char* message) { - QMessageBox::information(nullptr, QStringLiteral("Information"), QString::fromUtf8(message)); + pxFailRel("Not implemented."); } void AsyncRefreshProgressCallback::fireUpdate() @@ -95,9 +102,9 @@ void AsyncRefreshProgressCallback::fireUpdate() 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() - , m_progress(this) + , m_progress(popup_on_error, this) , m_invalidate_cache(invalidate_cache) { } diff --git a/pcsx2-qt/GameList/GameListRefreshThread.h b/pcsx2-qt/GameList/GameListRefreshThread.h index 40efd74911..a686c3dcbd 100644 --- a/pcsx2-qt/GameList/GameListRefreshThread.h +++ b/pcsx2-qt/GameList/GameListRefreshThread.h @@ -14,7 +14,7 @@ class GameListRefreshThread; class AsyncRefreshProgressCallback : public BaseProgressCallback { public: - AsyncRefreshProgressCallback(GameListRefreshThread* parent); + AsyncRefreshProgressCallback(bool popup_on_error, GameListRefreshThread* parent); void Cancel(); @@ -38,6 +38,7 @@ private: QString m_status_text; int m_last_range = 1; int m_last_value = 0; + bool m_popup_on_error = false; }; class GameListRefreshThread final : public QThread @@ -45,7 +46,7 @@ class GameListRefreshThread final : public QThread Q_OBJECT public: - GameListRefreshThread(bool invalidate_cache); + GameListRefreshThread(bool invalidate_cache, bool popup_on_error); ~GameListRefreshThread(); void cancel(); @@ -60,4 +61,5 @@ protected: private: AsyncRefreshProgressCallback m_progress; bool m_invalidate_cache; + bool m_popup_on_error; }; diff --git a/pcsx2-qt/GameList/GameListWidget.cpp b/pcsx2-qt/GameList/GameListWidget.cpp index ccd80b7758..7c3b5b7d6b 100644 --- a/pcsx2-qt/GameList/GameListWidget.cpp +++ b/pcsx2-qt/GameList/GameListWidget.cpp @@ -294,7 +294,7 @@ void GameListWidget::initialize() m_empty_ui.setupUi(m_empty_widget); 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.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(); }); m_ui.stack->insertWidget(2, m_empty_widget); @@ -453,11 +453,11 @@ bool GameListWidget::getShowGridCoverTitles() const return m_model->getShowCoverTitles(); } -void GameListWidget::refresh(bool invalidate_cache) +void GameListWidget::refresh(bool invalidate_cache, bool popup_on_error) { 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, Qt::QueuedConnection); connect(m_refresh_thread, &GameListRefreshThread::refreshComplete, this, &GameListWidget::onRefreshComplete, diff --git a/pcsx2-qt/GameList/GameListWidget.h b/pcsx2-qt/GameList/GameListWidget.h index 7a061f8b07..7043fbba1e 100644 --- a/pcsx2-qt/GameList/GameListWidget.h +++ b/pcsx2-qt/GameList/GameListWidget.h @@ -46,7 +46,7 @@ public: void initialize(); void resizeTableViewColumnsToFit(); - void refresh(bool invalidate_cache); + void refresh(bool invalidate_cache, bool popup_on_error); void cancelRefresh(); void reloadThemeSpecificImages(); void setCustomBackground(bool force = false); diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 2070271b09..826a164d5d 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -357,8 +357,8 @@ void MainWindow::connectSignals() [this]() { doControllerSettings(ControllerSettingsWindow::Category::HotkeySettings); }); connect(m_ui.actionAddGameDirectory, &QAction::triggered, [this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); }); - connect(m_ui.actionScanForNewGames, &QAction::triggered, [this]() { refreshGameList(false); }); - connect(m_ui.actionRescanAllGames, &QAction::triggered, [this]() { refreshGameList(true); }); + connect(m_ui.actionScanForNewGames, &QAction::triggered, [this]() { refreshGameList(false, true); }); + connect(m_ui.actionRescanAllGames, &QAction::triggered, [this]() { refreshGameList(true, true); }); connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled); connect(m_ui.actionViewLockToolbar, &QAction::toggled, this, &MainWindow::onViewLockToolbarActionToggled); connect(m_ui.actionViewStatusBar, &QAction::toggled, this, &MainWindow::onViewStatusBarActionToggled); @@ -547,7 +547,7 @@ void MainWindow::recreate() MainWindow* new_main_window = new MainWindow(); pxAssert(g_main_window == new_main_window); new_main_window->initialize(); - new_main_window->refreshGameList(false); + new_main_window->refreshGameList(false, false); new_main_window->show(); deleteLater(); @@ -1137,7 +1137,7 @@ bool MainWindow::shouldMouseLock() const if (!Host::GetBoolSettingValue("EmuCore", "EnableMouseLock", 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; 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(); - if(displayWindow == nullptr) + if (displayWindow == nullptr) return false; return windowsHidden && (displayWindow->isActiveWindow() || displayWindow->isFullScreen()); @@ -1215,13 +1215,9 @@ void MainWindow::switchToEmulationView() 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 - if (s_vm_valid) - return; - - m_game_list_widget->refresh(invalidate_cache); + m_game_list_widget->refresh(invalidate_cache, popup_on_error); } void MainWindow::cancelGameListRefresh() @@ -1466,8 +1462,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point) 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. if (entry_played_time) - connect(menu.addAction(tr("Reset Play Time")), &QAction::triggered, [this, entry, entry_played_time]() - { clearGameListEntryPlayTime(entry, entry_played_time); }); + connect(menu.addAction(tr("Reset Play Time")), &QAction::triggered, [this, entry, entry_played_time]() { clearGameListEntryPlayTime(entry, entry_played_time); }); // Check Wiki Page functionality is based on a serial redirect. if (!entry->serial.empty()) @@ -2102,7 +2097,7 @@ void MainWindow::onVMStopped() // reload played time 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, @@ -2932,12 +2927,12 @@ void MainWindow::clearGameListEntryPlayTime(const GameList::Entry* entry, const 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.") .arg(entry->title.empty() ? tr("empty title") : QString::fromStdString(entry->title), - entry->serial.empty() ? tr("no serial") : QString::fromStdString(entry->serial), - QString::fromStdString(GameList::FormatTimespan(entry_played_time, true))), - (QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes) + entry->serial.empty() ? tr("no serial") : QString::fromStdString(entry->serial), + QString::fromStdString(GameList::FormatTimespan(entry_played_time, true))), + (QMessageBox::Yes | QMessageBox::No), QMessageBox::No) == QMessageBox::Yes) { GameList::ClearPlayedTimeForSerial(entry->serial); - m_game_list_widget->refresh(false); + m_game_list_widget->refresh(false, false); } } diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index f86240c3f7..b9f8e5c36d 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -113,7 +113,7 @@ public: void checkMousePosition(int x, int y); public Q_SLOTS: 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 reportInfo(const QString& title, const QString& message); void reportError(const QString& title, const QString& message); diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index a65fbd08d5..c3025f48c3 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -1152,7 +1152,7 @@ void Host::OpenHostFileSelectorAsync(std::string_view title, bool select_directo if (!filters.empty()) { 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) { 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. if (!s_batch_mode) - g_main_window->refreshGameList(false); + g_main_window->refreshGameList(false, false); else GameList::Refresh(false, true); diff --git a/pcsx2-qt/Settings/GameListSettingsWidget.cpp b/pcsx2-qt/Settings/GameListSettingsWidget.cpp index 63395e679a..378cc6e55c 100644 --- a/pcsx2-qt/Settings/GameListSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GameListSettingsWidget.cpp @@ -63,7 +63,7 @@ bool GameListSettingsWidget::addExcludedPath(const std::string& path) Host::CommitBaseSettingChanges(); m_ui.excludedPaths->addItem(QString::fromStdString(path)); - g_main_window->refreshGameList(false); + g_main_window->refreshGameList(false, 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::CommitBaseSettingChanges(); refreshDirectoryList(); - g_main_window->refreshGameList(false); + g_main_window->refreshGameList(false, true); } void GameListSettingsWidget::removeSearchDirectory(const QString& path) @@ -166,7 +166,7 @@ void GameListSettingsWidget::removeSearchDirectory(const QString& path) Host::CommitBaseSettingChanges(); refreshDirectoryList(); - g_main_window->refreshGameList(false); + g_main_window->refreshGameList(false, true); } void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& point) @@ -261,7 +261,7 @@ void GameListSettingsWidget::onRemoveExcludedPathButtonClicked() delete item; - g_main_window->refreshGameList(false); + g_main_window->refreshGameList(false, true); } void GameListSettingsWidget::onExcludedPathsSelectionChanged() @@ -271,10 +271,10 @@ void GameListSettingsWidget::onExcludedPathsSelectionChanged() void GameListSettingsWidget::onRescanAllGamesClicked() { - g_main_window->refreshGameList(true); + g_main_window->refreshGameList(true, true); } void GameListSettingsWidget::onScanForNewGamesClicked() { - g_main_window->refreshGameList(false); + g_main_window->refreshGameList(false, true); } diff --git a/pcsx2-qt/Settings/GameSummaryWidget.cpp b/pcsx2-qt/Settings/GameSummaryWidget.cpp index 2f6248426a..9312356d3b 100644 --- a/pcsx2-qt/Settings/GameSummaryWidget.cpp +++ b/pcsx2-qt/Settings/GameSummaryWidget.cpp @@ -280,7 +280,8 @@ void GameSummaryWidget::onVerifyClicked() Error 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; } diff --git a/pcsx2/CDVD/CDVDcommon.cpp b/pcsx2/CDVD/CDVDcommon.cpp index d5cd11ed44..a4a40e11bf 100644 --- a/pcsx2/CDVD/CDVDcommon.cpp +++ b/pcsx2/CDVD/CDVDcommon.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "fmt/format.h" @@ -270,6 +271,23 @@ static void DetectDiskType() static std::string m_SourceFilename[3]; 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) { @@ -579,7 +597,7 @@ static s32 NODISCdummyS32() return 0; } -static void NODISCnewDiskCB(void (*/* callback */)()) +static void NODISCnewDiskCB(void (* /* callback */)()) { } diff --git a/pcsx2/CDVD/CDVDcommon.h b/pcsx2/CDVD/CDVDcommon.h index 1e74232ad6..6bd9ead3ad 100644 --- a/pcsx2/CDVD/CDVDcommon.h +++ b/pcsx2/CDVD/CDVDcommon.h @@ -20,7 +20,6 @@ struct cdvdTrackIndex u8 discM; // current minute 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) - }; struct cdvdTrack @@ -188,6 +187,13 @@ extern u8 strack; extern u8 etrack; extern std::array 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_SetFile(CDVD_SourceType srctype, std::string newfile); extern const std::string& CDVDsys_GetFile(CDVD_SourceType srctype); diff --git a/pcsx2/CDVD/IsoHasher.cpp b/pcsx2/CDVD/IsoHasher.cpp index a66dcece1a..98428f3824 100644 --- a/pcsx2/CDVD/IsoHasher.cpp +++ b/pcsx2/CDVD/IsoHasher.cpp @@ -7,7 +7,6 @@ #include "common/Error.h" #include "common/MD5Digest.h" -#include "common/StringUtil.h" #include "fmt/format.h" @@ -39,6 +38,10 @@ bool IsoHasher::Open(std::string iso_path, Error* error) { Close(); + m_is_locked = cdvdLock(error); + if (!m_is_locked) + return false; + CDVDsys_SetFile(CDVD_SourceType::Iso, std::move(iso_path)); CDVDsys_ChangeSource(CDVD_SourceType::Iso); @@ -103,6 +106,12 @@ bool IsoHasher::Open(std::string iso_path, Error* error) void IsoHasher::Close() { + if (!m_is_locked) + return; + + cdvdUnlock(); + m_is_locked = false; + if (!m_is_open) return; @@ -151,7 +160,7 @@ bool IsoHasher::ComputeTrackHash(Track& track, ProgressCallback* callback) const u32 update_interval = std::max(track.sectors / 100u, 1u); callback->SetStatusText( fmt::format(TRANSLATE_FS("CDVD", "Calculating checksum for track {}..."), track.number).c_str()); - callback->SetProgressRange(track.sectors); + callback->SetProgressRange(track.sectors); MD5Digest md5; for (u32 i = 0; i < track.sectors; i++) diff --git a/pcsx2/CDVD/IsoHasher.h b/pcsx2/CDVD/IsoHasher.h index bb7f3dacec..991f26ac6c 100644 --- a/pcsx2/CDVD/IsoHasher.h +++ b/pcsx2/CDVD/IsoHasher.h @@ -28,6 +28,9 @@ public: IsoHasher(); ~IsoHasher(); + IsoHasher(const IsoHasher&) = delete; + IsoHasher& operator=(const IsoHasher&) = delete; + static std::string_view GetTrackTypeString(u32 type); u32 GetTrackCount() const { return static_cast(m_tracks.size()); } @@ -44,6 +47,7 @@ private: bool ComputeTrackHash(Track& track, ProgressCallback* callback); std::vector m_tracks; + bool m_is_locked = false; bool m_is_open = false; bool m_is_cd = false; }; diff --git a/pcsx2/DebugTools/SymbolImporter.cpp b/pcsx2/DebugTools/SymbolImporter.cpp index e054954c4c..e4785f09d8 100644 --- a/pcsx2/DebugTools/SymbolImporter.cpp +++ b/pcsx2/DebugTools/SymbolImporter.cpp @@ -163,6 +163,9 @@ void SymbolImporter::Reset() void SymbolImporter::LoadAndAnalyseElf(Pcsx2Config::DebugAnalysisOptions options) { + if (!VMManager::HasValidVM()) + return; + const std::string& elf_path = VMManager::GetCurrentELF(); Error error; @@ -192,6 +195,9 @@ void SymbolImporter::AnalyseElf( Pcsx2Config::DebugAnalysisOptions options, bool wait_until_elf_is_loaded) { + if (!VMManager::HasValidVM()) + return; + // Search for a .sym file to load symbols from. std::string nocash_path; CDVD_SourceType source_type = CDVDsys_GetSourceType(); diff --git a/pcsx2/GameList.cpp b/pcsx2/GameList.cpp index 1b5b7535cd..ab3f9ffe92 100644 --- a/pcsx2/GameList.cpp +++ b/pcsx2/GameList.cpp @@ -16,6 +16,7 @@ #include "common/HeterogeneousContainers.h" #include "common/Path.h" #include "common/ProgressCallback.h" +#include "common/ScopedGuard.h" #include "common/StringUtil.h" #include @@ -826,6 +827,15 @@ void GameList::Refresh(bool invalidate_cache, bool only_cache, ProgressCallback* if (!progress) 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) DeleteCacheFile(); else diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 12153807f0..f55cddcba4 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -485,7 +485,7 @@ void VMManager::UpdateLoggingSettings(SettingsInterface& si) if (system_console_enabled != Log::IsConsoleOutputEnabled()) Log::SetConsoleOutputLevel(system_console_enabled ? level : LOGLEVEL_NONE); - // Debug console only exists on Windows. + // Debug console only exists on Windows. #ifdef _WIN32 const bool debug_console_enabled = IsDebuggerPresent() && si.GetBoolValue("Logging", "EnableDebugConsole", false); 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)); return game_serial.empty() ? - 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", game_crc)) : + Path::Combine(EmuFolders::GameSettings, fmt::format("{}_{:08X}.ini", sanitized_serial, game_crc)); } 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 if (boot_params.source_type.has_value()) { @@ -1355,12 +1364,14 @@ bool VMManager::Initialize(VMBootParameters boot_params) if (!LoadBIOS()) { Host::ReportErrorAsync(TRANSLATE_SV("VMManager", "Error – No BIOS Present"), - fmt::format(TRANSLATE_FS("VMManager", - "PCSX2 requires a PlayStation 2 BIOS in order to run.\n\n" - "For legal reasons, you will need to obtain this BIOS from a PlayStation 2 unit which you own.\n\n" - "For step-by-step help with this process, please consult the setup guide at {}.\n\n" - "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)); + fmt::format( + TRANSLATE_FS("VMManager", + "PCSX2 requires a PlayStation 2 BIOS in order to run.\n\n" + "For legal reasons, you will need to obtain this BIOS from a PlayStation 2 unit which you own.\n\n" + "For step-by-step help with this process, please consult the setup guide at {}.\n\n" + "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; } @@ -1559,6 +1570,7 @@ bool VMManager::Initialize(VMBootParameters boot_params) close_memcards.Cancel(); close_cdvd.Cancel(); close_cdvd_files.Cancel(); + unlock_cdvd.Cancel(); close_state.Cancel(); if (EmuConfig.CdvdPrecache) @@ -1667,6 +1679,8 @@ void VMManager::Shutdown(bool save_resume_state) else cdvdSaveNVRAM(); + cdvdUnlock(); + s_state.store(VMState::Shutdown, std::memory_order_release); FullscreenUI::OnVMDestroyed(); SaveStateSelectorUI::Clear();