From 75663d0601e4920b8164e810f76679d27e894867 Mon Sep 17 00:00:00 2001
From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>
Date: Sun, 21 Jan 2024 22:27:04 +0200
Subject: [PATCH] More app folder utilities

---
 include/emulator.hpp         |  2 ++
 src/emulator.cpp             | 45 ++++++++++++++++++++----------------
 src/panda_qt/main_window.cpp | 11 +++++++--
 3 files changed, 36 insertions(+), 22 deletions(-)

diff --git a/include/emulator.hpp b/include/emulator.hpp
index f4537425..da12c1bd 100644
--- a/include/emulator.hpp
+++ b/include/emulator.hpp
@@ -128,6 +128,8 @@ class Emulator {
 
 	std::filesystem::path getConfigPath();
 	std::filesystem::path getAndroidAppPath();
+	// Get the root path for the emulator's app data
+	std::filesystem::path getAppDataRoot();
 
 	std::span<u8> getSMDH();
 };
diff --git a/src/emulator.cpp b/src/emulator.cpp
index e94170a2..673e0ebc 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -117,6 +117,30 @@ void Emulator::runFrame() {
 	}
 }
 
+// Get path for saving files (AppData on Windows, /home/user/.local/share/ApplicationName on Linux, etc)
+// Inside that path, we be use a game-specific folder as well. Eg if we were loading a ROM called PenguinDemo.3ds, the savedata would be in
+// %APPDATA%/Alber/PenguinDemo/SaveData on Windows, and so on. We do this because games save data in their own filesystem on the cart.
+// If the portable build setting is enabled, then those saves go in the executable directory instead
+std::filesystem::path Emulator::getAppDataRoot() {
+	std::filesystem::path appDataPath;
+
+#ifdef __ANDROID__
+	appDataPath = getAndroidAppPath();
+#else
+	char* appData;
+	if (!config.usePortableBuild) {
+		appData = SDL_GetPrefPath(nullptr, "Alber");
+		appDataPath = std::filesystem::path(appData);
+	} else {
+		appData = SDL_GetBasePath();
+		appDataPath = std::filesystem::path(appData) / "Emulator Files";
+	}
+	SDL_free(appData);
+#endif
+
+	return appDataPath;
+}
+
 bool Emulator::loadROM(const std::filesystem::path& path) {
 	// Reset the emulator if we've already loaded a ROM
 	if (romType != ROMType::None) {
@@ -127,26 +151,7 @@ bool Emulator::loadROM(const std::filesystem::path& path) {
 	memory.loadedCXI = std::nullopt;
 	memory.loaded3DSX = std::nullopt;
 
-	// Get path for saving files (AppData on Windows, /home/user/.local/share/ApplicationName on Linux, etc)
-	// Inside that path, we be use a game-specific folder as well. Eg if we were loading a ROM called PenguinDemo.3ds, the savedata would be in
-	// %APPDATA%/Alber/PenguinDemo/SaveData on Windows, and so on. We do this because games save data in their own filesystem on the cart.
-	// If the portable build setting is enabled, then those saves go in the executable directory instead
-	std::filesystem::path appDataPath;
-
-	#ifdef __ANDROID__
-	appDataPath = getAndroidAppPath();
-	#else
-	char* appData;
-	if (!config.usePortableBuild) {
-		appData = SDL_GetPrefPath(nullptr, "Alber");
-		appDataPath = std::filesystem::path(appData);
-	} else {
-		appData = SDL_GetBasePath();
-		appDataPath = std::filesystem::path(appData) / "Emulator Files";
-	}
-	SDL_free(appData);
-	#endif
-
+	const std::filesystem::path appDataPath = getAppDataRoot();
 	const std::filesystem::path dataPath = appDataPath / path.filename().stem();
 	const std::filesystem::path aesKeysPath = appDataPath / "sysdata" / "aes_keys.txt";
 	IOFile::setAppDataDir(dataPath);
diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp
index e390aa44..5c661119 100644
--- a/src/panda_qt/main_window.cpp
+++ b/src/panda_qt/main_window.cpp
@@ -1,6 +1,8 @@
 #include "panda_qt/main_window.hpp"
 
+#include <QDesktopServices>
 #include <QFileDialog>
+#include <QString>
 #include <cstdio>
 #include <fstream>
 
@@ -26,8 +28,14 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
 	// Create and bind actions for them
 	auto loadGameAction = fileMenu->addAction(tr("Load game"));
 	auto loadLuaAction = fileMenu->addAction(tr("Load Lua script"));
+	auto openAppFolderAction = fileMenu->addAction(tr("Open Panda3DS folder"));
+
 	connect(loadGameAction, &QAction::triggered, this, &MainWindow::selectROM);
 	connect(loadLuaAction, &QAction::triggered, this, &MainWindow::selectLuaFile);
+	connect(openAppFolderAction, &QAction::triggered, this, [this]() {
+		QString path = QString::fromStdU16String(emu->getAppDataRoot().u16string());
+		QDesktopServices::openUrl(QUrl::fromLocalFile(path));
+	});
 
 	auto pauseAction = emulationMenu->addAction(tr("Pause"));
 	auto resumeAction = emulationMenu->addAction(tr("Resume"));
@@ -194,8 +202,7 @@ void MainWindow::dumpRomFS() {
 		return;
 	}
 	std::filesystem::path path(folder.toStdU16String());
-	
-	// TODO: This might break if the game accesses RomFS while we're dumping, we should move it to the emulator thread when we've got a message queue going
+
 	messageQueueMutex.lock();
 	RomFS::DumpingResult res = emu->dumpRomFS(path);
 	messageQueueMutex.unlock();