From 4e62b32d60cd32001907b9408059571b02f5eccc Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 16 Aug 2020 23:20:36 +1000 Subject: [PATCH] Add option to dump the contents of RAM to a file --- src/core/system.cpp | 12 +++++++++++- src/core/system.h | 3 +++ src/duckstation-qt/mainwindow.cpp | 7 +++++++ src/duckstation-qt/mainwindow.ui | 8 +++++++- src/duckstation-qt/qthostinterface.cpp | 18 ++++++++++++++++++ src/duckstation-qt/qthostinterface.h | 1 + src/duckstation-sdl/sdl_host_interface.cpp | 20 ++++++++++++++++++++ src/duckstation-sdl/sdl_host_interface.h | 1 + 8 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index 27f2b0161..0420a482a 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1228,6 +1228,15 @@ void UpdateMemoryCards() } } +bool DumpRAM(const char* filename) +{ + auto fp = FileSystem::OpenManagedCFile(filename, "wb"); + if (!fp) + return false; + + return std::fwrite(Bus::g_ram, Bus::RAM_SIZE, 1, fp.get()) == 1; +} + bool HasMedia() { return g_cdrom.HasMedia(); @@ -1333,7 +1342,8 @@ bool RemoveMediaPathFromPlaylist(u32 index) if (GetMediaPlaylistIndex() == index) { - g_host_interface->AddFormattedOSDMessage(10.0f, "Removing current media from playlist, removing media from CD-ROM."); + g_host_interface->AddFormattedOSDMessage(10.0f, + "Removing current media from playlist, removing media from CD-ROM."); g_cdrom.RemoveMedia(); } diff --git a/src/core/system.h b/src/core/system.h index 551247f6f..eee1f6973 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -104,6 +104,9 @@ void UpdateControllerSettings(); void ResetControllers(); void UpdateMemoryCards(); +/// Dumps RAM to a file. +bool DumpRAM(const char* filename); + bool HasMedia(); bool InsertMedia(const char* path); void RemoveMedia(); diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index bc28a4fb3..f32f55cc8 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -662,6 +662,13 @@ void MainWindow::connectSignals() else m_host_interface->stopDumpingAudio(); }); + connect(m_ui.actionDumpRAM, &QAction::triggered, [this]() { + const QString filename = QFileDialog::getSaveFileName(this, tr("Destination File")); + if (filename.isEmpty()) + return; + + m_host_interface->dumpRAM(filename); + }); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowVRAM, "Debug", "ShowVRAM"); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowGPUState, "Debug", "ShowGPUState"); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowCDROMState, "Debug", diff --git a/src/duckstation-qt/mainwindow.ui b/src/duckstation-qt/mainwindow.ui index 458d04621..03dafc216 100644 --- a/src/duckstation-qt/mainwindow.ui +++ b/src/duckstation-qt/mainwindow.ui @@ -149,9 +149,10 @@ - + + @@ -484,6 +485,11 @@ Dump Audio + + + Dump RAM... + + true diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 716ed9fca..39b6f6fa1 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -1026,6 +1026,24 @@ void QtHostInterface::stopDumpingAudio() StopDumpingAudio(); } +void QtHostInterface::dumpRAM(const QString& filename) +{ + if (!isOnWorkerThread()) + { + QMetaObject::invokeMethod(this, "dumpRAM", Q_ARG(const QString&, filename)); + return; + } + + if (System::IsShutdown()) + return; + + const std::string filename_str = filename.toStdString(); + if (System::DumpRAM(filename_str.c_str())) + ReportFormattedMessage("RAM dumped to '%s'", filename_str.c_str()); + else + ReportFormattedMessage("Failed to dump RAM to '%s'", filename_str.c_str()); +} + void QtHostInterface::saveScreenshot() { if (!isOnWorkerThread()) diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index f6476aa4a..5f843364c 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -151,6 +151,7 @@ public Q_SLOTS: void setAudioOutputMuted(bool muted); void startDumpingAudio(); void stopDumpingAudio(); + void dumpRAM(const QString& filename); void saveScreenshot(); void redrawDisplayWindow(); void toggleFullscreen(); diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp index 7a874465d..06dcb5bd2 100644 --- a/src/duckstation-sdl/sdl_host_interface.cpp +++ b/src/duckstation-sdl/sdl_host_interface.cpp @@ -904,6 +904,7 @@ void SDLHostInterface::DrawQuickSettingsMenu() void SDLHostInterface::DrawDebugMenu() { + const bool system_valid = System::IsValid(); Settings::DebugSettings& debug_settings = g_settings.debugging; bool settings_changed = false; @@ -931,6 +932,9 @@ void SDLHostInterface::DrawDebugMenu() settings_changed |= ImGui::MenuItem("Dump CPU to VRAM Copies", nullptr, &debug_settings.dump_cpu_to_vram_copies); settings_changed |= ImGui::MenuItem("Dump VRAM to CPU Copies", nullptr, &debug_settings.dump_vram_to_cpu_copies); + if (ImGui::MenuItem("Dump RAM...", nullptr, nullptr, system_valid)) + DoDumpRAM(); + ImGui::Separator(); settings_changed |= ImGui::MenuItem("Show VRAM", nullptr, &debug_settings.show_vram); @@ -1511,6 +1515,22 @@ void SDLHostInterface::DoChangeDisc() System::ResetPerformanceCounters(); } +void SDLHostInterface::DoDumpRAM() +{ + Assert(!System::IsShutdown()); + + nfdchar_t* path = nullptr; + if (!NFD_SaveDialog("bin", nullptr, &path) || !path || std::strlen(path) == 0) + return; + + if (System::DumpRAM(path)) + AddFormattedOSDMessage(5.0f, "Dumped RAM to '%s'", path); + else + AddFormattedOSDMessage(10.0f, "Failed to dump RAM to '%s'", path); + + System::ResetPerformanceCounters(); +} + void SDLHostInterface::Run() { while (!m_quit_request) diff --git a/src/duckstation-sdl/sdl_host_interface.h b/src/duckstation-sdl/sdl_host_interface.h index de98db5bd..ac0eae485 100644 --- a/src/duckstation-sdl/sdl_host_interface.h +++ b/src/duckstation-sdl/sdl_host_interface.h @@ -79,6 +79,7 @@ private: void DrawImGuiWindows() override; void DoStartDisc(); void DoChangeDisc(); + void DoDumpRAM(); void HandleSDLEvent(const SDL_Event* event); void ProcessEvents();