From 9be62bb7d5e4a00566fa8571e7365a047136f382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 11 Nov 2026 10:27:09 +0100 Subject: [PATCH] Frame dumps: Share them as zips where folders can't be shown Where the file can't be shown in its folder (Android, iOS), "Create frame dump" in the dev menu now zips the dump and opens the share sheet. The frame dump hotkey does the same when its message is clicked, so the game isn't interrupted. Both go through OnFrameDumpCreated(), which moves the share to the main thread, since the recorder calls back on the emulation thread. Co-Authored-By: Claude Opus 5.5 --- UI/DevScreens.cpp | 40 ++++++++++++++++++++++++++++++++++------ UI/DevScreens.h | 4 ++++ UI/EmuScreen.cpp | 9 ++------- 3 files changed, 40 insertions(+), 13 deletions(-) diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 90a0377514..e0e45a7425 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -82,6 +82,7 @@ #include "UI/ControlMappingScreen.h" #include "UI/GameSettingsScreen.h" #include "UI/JitCompareScreen.h" +#include "UI/ShareFiles.h" #include "android/jni/app-android.h" @@ -121,17 +122,44 @@ void AddOverlayList(UI::ViewGroup *items, ScreenManager *screenManager) { } } +// Zipped, so apps that only take known file types accept it. The zip import recognizes it too. +static void ShareFrameDump(const Path &dumpPath) { + std::string name = dumpPath.GetFilename(); + if (endsWithNoCase(name, ".ppdmp")) { + name.resize(name.size() - strlen(".ppdmp")); + } + if (!ShareFilesAsZip(name, { { dumpPath, "" } })) { + auto sy = GetI18NCategory(I18NCat::SYSTEM); + g_OSD.Show(OSDType::MESSAGE_ERROR, sy->T("Failed")); + } +} + +void OnFrameDumpCreated(const Path &dumpPath, bool openNow) { + NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str()); + std::function open; + if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { + open = [dumpPath]() { System_ShowFileInFolder(dumpPath); }; + } else if (CanShareFiles()) { + open = [dumpPath]() { ShareFrameDump(dumpPath); }; + } + g_OSD.Show(OSDType::MESSAGE_SUCCESS, GetFriendlyPath(dumpPath), 7.0f, "framedump"); + if (!open) { + return; + } + if (openNow) { + // The recorder calls back on the emulation thread. + System_RunOnMainThread(open); + } else { + g_OSD.SetClickCallback("framedump", open); + } +} + void SaveFrameDump() { if (!gpu) { return; } gpu->GetRecorder()->RecordNextFrame([](const Path &dumpPath) { - NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str()); - if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { - System_ShowFileInFolder(dumpPath); - } else { - g_OSD.Show(OSDType::MESSAGE_SUCCESS, GetFriendlyPath(dumpPath), 7.0f); - } + OnFrameDumpCreated(dumpPath, true); }); } diff --git a/UI/DevScreens.h b/UI/DevScreens.h index fc4f2fae61..9240e12c04 100644 --- a/UI/DevScreens.h +++ b/UI/DevScreens.h @@ -213,3 +213,7 @@ public: void DrawProfile(UIContext &ui); void AddOverlayList(UI::ViewGroup *items, ScreenManager *screenManager); + +// Tells the user where the dump went. With openNow, shows it in its folder (or shares it, where +// folders can't be shown) right away, otherwise when the message is clicked. Callable from any thread. +void OnFrameDumpCreated(const Path &dumpPath, bool openNow); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 075d1bdfd1..35c0368260 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -771,14 +771,9 @@ void EmuScreen::ProcessVKey(VirtKey virtKey, bool down) { if (!gpu) { return; } + // Not opened right away, which would interrupt the game. gpu->GetRecorder()->RecordNextFrame([](const Path &dumpPath) { - NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str()); - g_OSD.Show(OSDType::MESSAGE_SUCCESS, GetFriendlyPath(dumpPath), 7.0f, "framedump"); - if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { - g_OSD.SetClickCallback("framedump", [dumpPath]() { - System_ShowFileInFolder(dumpPath); - }); - } + OnFrameDumpCreated(dumpPath, false); }); } break;