mirror of https://github.com/PCSX2/pcsx2.git
Memcard/Qt/Big Picture: Make shutdowns, resets, disc swaps, and savestates aware of memcard busy status
This commit is contained in:
parent
da22df5f5d
commit
feb9d7b2a9
|
@ -46,6 +46,7 @@
|
|||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
#include "pcsx2/Recording/InputRecordingControls.h"
|
||||
#include "pcsx2/SIO/Sio.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/CocoaTools.h"
|
||||
|
@ -433,10 +434,10 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
|
|||
connect(thread, &EmuThread::onAchievementsHardcoreModeChanged, this, &MainWindow::onAchievementsHardcoreModeChanged);
|
||||
connect(thread, &EmuThread::onCoverDownloaderOpenRequested, this, &MainWindow::onToolsCoverDownloaderTriggered);
|
||||
|
||||
connect(m_ui.actionReset, &QAction::triggered, thread, &EmuThread::resetVM);
|
||||
connect(m_ui.actionReset, &QAction::triggered, this, &MainWindow::requestReset);
|
||||
connect(m_ui.actionPause, &QAction::toggled, thread, &EmuThread::setVMPaused);
|
||||
connect(m_ui.actionFullscreen, &QAction::triggered, thread, &EmuThread::toggleFullscreen);
|
||||
connect(m_ui.actionToolbarReset, &QAction::triggered, thread, &EmuThread::resetVM);
|
||||
connect(m_ui.actionToolbarReset, &QAction::triggered, this, &MainWindow::requestReset);
|
||||
connect(m_ui.actionToolbarPause, &QAction::toggled, thread, &EmuThread::setVMPaused);
|
||||
connect(m_ui.actionToolbarFullscreen, &QAction::triggered, thread, &EmuThread::toggleFullscreen);
|
||||
connect(m_ui.actionToggleSoftwareRendering, &QAction::triggered, thread, &EmuThread::toggleSoftwareRendering);
|
||||
|
@ -977,6 +978,25 @@ bool MainWindow::shouldHideMainWindow() const
|
|||
QtHost::InNoGUIMode();
|
||||
}
|
||||
|
||||
bool MainWindow::shouldAbortForMemcardBusy(const VMLock& lock)
|
||||
{
|
||||
if (MemcardBusy::IsBusy() && !GSDumpReplayer::IsReplayingDump())
|
||||
{
|
||||
const QMessageBox::StandardButton res = QMessageBox::question(
|
||||
lock.getDialogParent(),
|
||||
tr("WARNING: Memory Card Busy"),
|
||||
tr("WARNING: Your memory card is still writing data. Shutting down now will IRREVERSIBLY DESTROY YOUR MEMORY CARD. It is strongly recommended to resume your game and let it finish writing to your memory card.\n\nDo you wish to shutdown anyways and IRREVERSIBLY DESTROY YOUR MEMORY CARD?")
|
||||
);
|
||||
|
||||
if (res != QMessageBox::Yes)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::switchToGameListView()
|
||||
{
|
||||
if (isShowingGameList())
|
||||
|
@ -1044,6 +1064,22 @@ void MainWindow::runOnUIThread(const std::function<void()>& func)
|
|||
func();
|
||||
}
|
||||
|
||||
void MainWindow::requestReset()
|
||||
{
|
||||
if (!s_vm_valid)
|
||||
return;
|
||||
|
||||
const auto lock = pauseAndLockVM();
|
||||
|
||||
// Check if memcard is busy, deny request if so
|
||||
if (shouldAbortForMemcardBusy(lock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_emu_thread->resetVM();
|
||||
}
|
||||
|
||||
bool MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, bool default_save_to_state)
|
||||
{
|
||||
if (!s_vm_valid)
|
||||
|
@ -1052,12 +1088,17 @@ bool MainWindow::requestShutdown(bool allow_confirm, bool allow_save_to_state, b
|
|||
// If we don't have a crc, we can't save state.
|
||||
allow_save_to_state &= (m_current_disc_crc != 0);
|
||||
bool save_state = allow_save_to_state && default_save_to_state;
|
||||
VMLock lock(pauseAndLockVM());
|
||||
|
||||
// Check if memcard is busy, deny request if so.
|
||||
if (shouldAbortForMemcardBusy(lock))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only confirm on UI thread because we need to display a msgbox.
|
||||
if (!m_is_closing && allow_confirm && !GSDumpReplayer::IsReplayingDump() && Host::GetBoolSettingValue("UI", "ConfirmShutdown", true))
|
||||
{
|
||||
VMLock lock(pauseAndLockVM());
|
||||
|
||||
QMessageBox msgbox(lock.getDialogParent());
|
||||
msgbox.setIcon(QMessageBox::Question);
|
||||
msgbox.setWindowTitle(tr("Confirm Shutdown"));
|
||||
|
@ -1899,6 +1940,14 @@ void MainWindow::dragEnterEvent(QDragEnterEvent* event)
|
|||
|
||||
void MainWindow::dropEvent(QDropEvent* event)
|
||||
{
|
||||
const auto mcLock = pauseAndLockVM();
|
||||
|
||||
// Check if memcard is busy, deny request if so
|
||||
if (shouldAbortForMemcardBusy(mcLock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QString filename(getFilenameFromMimeData(event->mimeData()));
|
||||
const std::string filename_str(filename.toStdString());
|
||||
if (VMManager::IsSaveStateFileName(filename_str))
|
||||
|
@ -2730,6 +2779,12 @@ void MainWindow::doDiscChange(CDVD_SourceType source, const QString& path)
|
|||
{
|
||||
const auto lock = pauseAndLockVM();
|
||||
|
||||
// Check if memcard is busy, deny request if so
|
||||
if (shouldAbortForMemcardBusy(lock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool reset_system = false;
|
||||
if (!m_was_disc_change_request)
|
||||
{
|
||||
|
|
|
@ -119,6 +119,7 @@ public Q_SLOTS:
|
|||
void reportError(const QString& title, const QString& message);
|
||||
bool confirmMessage(const QString& title, const QString& message);
|
||||
void runOnUIThread(const std::function<void()>& func);
|
||||
void requestReset();
|
||||
bool requestShutdown(bool allow_confirm = true, bool allow_save_to_state = true, bool default_save_to_state = true);
|
||||
void requestExit(bool allow_confirm = true);
|
||||
void checkForSettingChanges();
|
||||
|
@ -243,6 +244,8 @@ private:
|
|||
void switchToGameListView();
|
||||
void switchToEmulationView();
|
||||
|
||||
bool shouldAbortForMemcardBusy(const VMLock& lock);
|
||||
|
||||
QWidget* getContentParent();
|
||||
QWidget* getDisplayContainer() const;
|
||||
void saveDisplayWindowGeometryToConfig();
|
||||
|
|
|
@ -517,6 +517,9 @@ static __fi void VSyncStart(u32 sCycle)
|
|||
|
||||
// Memcard auto ejection - Uses a tick system timed off of real time, decrementing one tick per frame.
|
||||
AutoEject::CountDownTicks();
|
||||
// Memcard IO detection - Uses a tick system to determine when memcards are no longer being written.
|
||||
MemcardBusy::Decrement();
|
||||
|
||||
if (gates)
|
||||
rcntStartGate(true, sCycle); // Counters Start Gate code
|
||||
|
||||
|
|
|
@ -274,13 +274,19 @@ namespace FullscreenUI
|
|||
static void DoStartDisc();
|
||||
static void DoToggleFrameLimit();
|
||||
static void DoToggleSoftwareRenderer();
|
||||
static void RequestShutdown(bool save_state);
|
||||
static void DoShutdown(bool save_state);
|
||||
static void RequestReset();
|
||||
static void DoReset();
|
||||
static void RequestChangeDiscFromFile();
|
||||
static void DoChangeDiscFromFile();
|
||||
static void RequestChangeDisc();
|
||||
static void DoChangeDisc();
|
||||
static void DoRequestExit();
|
||||
static void DoToggleFullscreen();
|
||||
|
||||
static void ConfirmShutdownIfMemcardBusy(std::function<void(bool)> callback);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Settings
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -958,11 +964,31 @@ void FullscreenUI::DoToggleSoftwareRenderer()
|
|||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::RequestShutdown(bool save_state)
|
||||
{
|
||||
ConfirmShutdownIfMemcardBusy([save_state](bool result) {
|
||||
if (result)
|
||||
DoShutdown(save_state);
|
||||
|
||||
ClosePauseMenu();
|
||||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::DoShutdown(bool save_state)
|
||||
{
|
||||
Host::RunOnCPUThread([save_state]() { Host::RequestVMShutdown(false, save_state, save_state); });
|
||||
}
|
||||
|
||||
void FullscreenUI::RequestReset()
|
||||
{
|
||||
ConfirmShutdownIfMemcardBusy([](bool result) {
|
||||
if (result)
|
||||
DoReset();
|
||||
|
||||
ClosePauseMenu();
|
||||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::DoReset()
|
||||
{
|
||||
Host::RunOnCPUThread([]() {
|
||||
|
@ -973,6 +999,16 @@ void FullscreenUI::DoReset()
|
|||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::RequestChangeDiscFromFile()
|
||||
{
|
||||
ConfirmShutdownIfMemcardBusy([](bool result) {
|
||||
if (result)
|
||||
DoChangeDiscFromFile();
|
||||
else
|
||||
ClosePauseMenu();
|
||||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::DoChangeDiscFromFile()
|
||||
{
|
||||
auto callback = [](const std::string& path) {
|
||||
|
@ -991,12 +1027,23 @@ void FullscreenUI::DoChangeDiscFromFile()
|
|||
QueueResetFocus();
|
||||
CloseFileSelector();
|
||||
ReturnToPreviousWindow();
|
||||
ClosePauseMenu();
|
||||
};
|
||||
|
||||
OpenFileSelector(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), false, std::move(callback), GetDiscImageFilters(),
|
||||
std::string(Path::GetDirectory(s_current_disc_path)));
|
||||
}
|
||||
|
||||
void FullscreenUI::RequestChangeDisc()
|
||||
{
|
||||
ConfirmShutdownIfMemcardBusy([](bool result) {
|
||||
if (result)
|
||||
DoChangeDisc();
|
||||
else
|
||||
ClosePauseMenu();
|
||||
});
|
||||
}
|
||||
|
||||
void FullscreenUI::DoChangeDisc()
|
||||
{
|
||||
DoChangeDiscFromFile();
|
||||
|
@ -1012,6 +1059,20 @@ void FullscreenUI::DoToggleFullscreen()
|
|||
Host::RunOnCPUThread([]() { Host::SetFullscreen(!Host::IsFullscreen()); });
|
||||
}
|
||||
|
||||
void FullscreenUI::ConfirmShutdownIfMemcardBusy(std::function<void(bool)> callback)
|
||||
{
|
||||
if (!MemcardBusy::IsBusy())
|
||||
{
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
|
||||
OpenConfirmMessageDialog(FSUI_ICONSTR(ICON_FA_SD_CARD, "WARNING: Memory Card Busy"),
|
||||
FSUI_STR("WARNING: Your memory card is still writing data. Shutting down now will IRREVERSIBLY DESTROY YOUR MEMORY CARD. It is strongly recommended to resume your game and let it finish writing to your memory card.\n\nDo you wish to shutdown anyways and IRREVERSIBLY DESTROY YOUR MEMORY CARD?"),
|
||||
std::move(callback)
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Landing Window
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -4807,7 +4868,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
|||
if (ActiveButton(FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Change Disc"), false))
|
||||
{
|
||||
s_current_main_window = MainWindowType::None;
|
||||
DoChangeDisc();
|
||||
RequestChangeDisc();
|
||||
}
|
||||
|
||||
if (ActiveButton(FSUI_ICONSTR(ICON_FA_SLIDERS_H, "Settings"), false))
|
||||
|
@ -4817,7 +4878,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
|||
{
|
||||
// skip submenu when we can't save anyway
|
||||
if (!can_load_or_save_state)
|
||||
DoShutdown(false);
|
||||
RequestShutdown(false);
|
||||
else
|
||||
OpenPauseSubMenu(PauseSubMenu::Exit);
|
||||
}
|
||||
|
@ -4836,15 +4897,14 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
|
|||
|
||||
if (ActiveButton(FSUI_ICONSTR(ICON_FA_SYNC, "Reset System"), false))
|
||||
{
|
||||
ClosePauseMenu();
|
||||
DoReset();
|
||||
RequestReset();
|
||||
}
|
||||
|
||||
if (ActiveButton(FSUI_ICONSTR(ICON_FA_SAVE, "Exit And Save State"), false))
|
||||
DoShutdown(true);
|
||||
RequestShutdown(true);
|
||||
|
||||
if (ActiveButton(FSUI_ICONSTR(ICON_FA_POWER_OFF, "Exit Without Saving"), false))
|
||||
DoShutdown(false);
|
||||
RequestShutdown(false);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -6369,7 +6429,7 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock<std::mutex>& se
|
|||
return;
|
||||
|
||||
if (reset)
|
||||
DoReset();
|
||||
RequestReset();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,6 +234,8 @@ void MemoryCardProtocol::WriteData()
|
|||
g_Sio2FifoOut.push_back(mcd->term);
|
||||
|
||||
ReadWriteIncrement(writeLength);
|
||||
|
||||
MemcardBusy::SetBusy();
|
||||
}
|
||||
|
||||
void MemoryCardProtocol::ReadData()
|
||||
|
@ -396,8 +398,9 @@ u8 MemoryCardProtocol::PS1Write(u8 data)
|
|||
}
|
||||
|
||||
g_Sio0.SetAcknowledge(sendAck);
|
||||
|
||||
ps1McState.currentByte++;
|
||||
|
||||
MemcardBusy::SetBusy();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -421,6 +424,8 @@ void MemoryCardProtocol::EraseBlock()
|
|||
PS1_FAIL();
|
||||
mcd->EraseBlock();
|
||||
The2bTerminator(4);
|
||||
|
||||
MemcardBusy::SetBusy();
|
||||
}
|
||||
|
||||
void MemoryCardProtocol::UnknownBoot()
|
||||
|
|
|
@ -134,3 +134,31 @@ void AutoEject::ClearAll()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decremented once per frame if nonzero, indicates how many more frames must pass before
|
||||
// memcards are considered "no longer being written to". Used as a way to detect if it is
|
||||
// unsafe to shutdown the VM due to memcard access.
|
||||
static std::atomic_uint32_t currentBusyTicks = 0;
|
||||
|
||||
void MemcardBusy::Decrement()
|
||||
{
|
||||
if (currentBusyTicks.load(std::memory_order_relaxed) == 0)
|
||||
return;
|
||||
|
||||
currentBusyTicks.fetch_sub(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
void MemcardBusy::SetBusy()
|
||||
{
|
||||
currentBusyTicks.store(300, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool MemcardBusy::IsBusy()
|
||||
{
|
||||
return (currentBusyTicks.load(std::memory_order_acquire) > 0);
|
||||
}
|
||||
|
||||
void MemcardBusy::ClearBusy()
|
||||
{
|
||||
currentBusyTicks.store(0, std::memory_order_release);
|
||||
}
|
||||
|
|
|
@ -129,3 +129,11 @@ namespace AutoEject
|
|||
extern void SetAll();
|
||||
extern void ClearAll();
|
||||
} // namespace AutoEject
|
||||
|
||||
namespace MemcardBusy
|
||||
{
|
||||
extern void Decrement();
|
||||
extern void SetBusy();
|
||||
extern bool IsBusy();
|
||||
extern void ClearBusy();
|
||||
}
|
||||
|
|
|
@ -1421,6 +1421,7 @@ void VMManager::Shutdown(bool save_resume_state)
|
|||
Pad::Shutdown();
|
||||
g_Sio2.Shutdown();
|
||||
g_Sio0.Shutdown();
|
||||
MemcardBusy::ClearBusy();
|
||||
DEV9close();
|
||||
DoCDVDclose();
|
||||
FWclose();
|
||||
|
@ -1744,6 +1745,14 @@ bool VMManager::LoadState(const char* filename)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (MemcardBusy::IsBusy())
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to load state (Memory card is busy)")),
|
||||
Host::OSD_QUICK_DURATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Save the current state so we don't need to reset.
|
||||
if (DoLoadState(filename))
|
||||
return true;
|
||||
|
@ -1773,6 +1782,14 @@ bool VMManager::LoadStateFromSlot(s32 slot)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (MemcardBusy::IsBusy())
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to load state from slot {} (Memory card is busy)"), slot),
|
||||
Host::OSD_QUICK_DURATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_FOLDER_OPEN,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Loading state from slot {}..."), slot), Host::OSD_QUICK_DURATION);
|
||||
return DoLoadState(filename.c_str());
|
||||
|
@ -1780,6 +1797,14 @@ bool VMManager::LoadStateFromSlot(s32 slot)
|
|||
|
||||
bool VMManager::SaveState(const char* filename, bool zip_on_thread, bool backup_old_state)
|
||||
{
|
||||
if (MemcardBusy::IsBusy())
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state (Memory card is busy)")),
|
||||
Host::OSD_QUICK_DURATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
return DoSaveState(filename, -1, zip_on_thread, backup_old_state);
|
||||
}
|
||||
|
||||
|
@ -1789,6 +1814,14 @@ bool VMManager::SaveStateToSlot(s32 slot, bool zip_on_thread)
|
|||
if (filename.empty())
|
||||
return false;
|
||||
|
||||
if (MemcardBusy::IsBusy())
|
||||
{
|
||||
Host::AddIconOSDMessage("LoadStateFromSlot", ICON_FA_EXCLAMATION_TRIANGLE,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Failed to save state to slot {} (Memory card is busy)"), slot),
|
||||
Host::OSD_QUICK_DURATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
// if it takes more than a minute.. well.. wtf.
|
||||
Host::AddIconOSDMessage(fmt::format("SaveStateSlot{}", slot), ICON_FA_SAVE,
|
||||
fmt::format(TRANSLATE_FS("VMManager", "Saving state to slot {}..."), slot), 60.0f);
|
||||
|
|
Loading…
Reference in New Issue