mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-12 09:09:47 +12:00
Decouple data save paths from Emulator
This change allows the frontend to select the appdata and configuration file locations when instantiating the emulator, allowing for more flexibility in placing the files. This also fixes configuration not working in MacOS app bundles, as it will now look for config.toml in the user's "Application Support" directory and default there.
This commit is contained in:
parent
8cc9bfbb36
commit
80d8337592
8 changed files with 50 additions and 57 deletions
|
@ -28,6 +28,8 @@
|
|||
#include "gl/context.h"
|
||||
#endif
|
||||
|
||||
static const std::string EmulatorConfigFilename = "config.toml";
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
enum class ROMType {
|
||||
|
@ -51,6 +53,8 @@ class Emulator {
|
|||
MiniAudioDevice audioDevice;
|
||||
Cheats cheats;
|
||||
|
||||
std::filesystem::path appDataPath;
|
||||
|
||||
public:
|
||||
static constexpr u32 width = 400;
|
||||
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
|
||||
|
@ -85,7 +89,7 @@ class Emulator {
|
|||
// Used in CPU::runFrame
|
||||
bool frameDone = false;
|
||||
|
||||
Emulator();
|
||||
Emulator(std::vector<std::filesystem::path> configSearchPaths, std::filesystem::path appDataPath);
|
||||
~Emulator();
|
||||
|
||||
void step();
|
||||
|
|
|
@ -27,6 +27,7 @@ void EmulatorConfig::load() {
|
|||
return;
|
||||
}
|
||||
|
||||
printf("Loading existing configuration file %s\n", path.string().c_str());
|
||||
toml::value data;
|
||||
|
||||
try {
|
||||
|
|
|
@ -18,14 +18,29 @@ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
|
|||
}
|
||||
#endif
|
||||
|
||||
Emulator::Emulator()
|
||||
: config(getConfigPath()), kernel(cpu, memory, gpu, config), cpu(memory, kernel, *this), gpu(memory, config), memory(cpu.getTicksRef(), config),
|
||||
cheats(memory, kernel.getServiceManager().getHID()), audioDevice(config.audioDeviceConfig), lua(*this), running(false)
|
||||
std::filesystem::path findConfig(std::vector<std::filesystem::path> paths) {
|
||||
for (std::filesystem::path p: paths) {
|
||||
if (std::filesystem::exists(p)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return paths.back();
|
||||
}
|
||||
|
||||
Emulator::Emulator(std::vector<std::filesystem::path> configSearchPaths, std::filesystem::path appDataPath)
|
||||
: config(findConfig(configSearchPaths)), kernel(cpu, memory, gpu, config), cpu(memory, kernel, *this), gpu(memory, config), memory(cpu.getTicksRef(), config),
|
||||
cheats(memory, kernel.getServiceManager().getHID()), audioDevice(config.audioDeviceConfig), lua(*this), running(false), appDataPath(appDataPath)
|
||||
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
|
||||
,
|
||||
httpServer(this)
|
||||
#endif
|
||||
{
|
||||
if (config.usePortableBuild) {
|
||||
auto appData = SDL_GetBasePath();
|
||||
appDataPath = std::filesystem::path(appData) / "Emulator Files";
|
||||
SDL_free(appData);
|
||||
}
|
||||
|
||||
DSPService& dspService = kernel.getServiceManager().getDSP();
|
||||
|
||||
dsp = Audio::makeDSPCore(config, memory, scheduler, dspService);
|
||||
|
@ -91,25 +106,6 @@ void Emulator::reset(ReloadOption reload) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef __LIBRETRO__
|
||||
std::filesystem::path Emulator::getAndroidAppPath() {
|
||||
// SDL_GetPrefPath fails to get the path due to no JNI environment
|
||||
std::ifstream cmdline("/proc/self/cmdline");
|
||||
std::string applicationName;
|
||||
std::getline(cmdline, applicationName, '\0');
|
||||
|
||||
return std::filesystem::path("/data") / "data" / applicationName / "files";
|
||||
}
|
||||
|
||||
std::filesystem::path Emulator::getConfigPath() {
|
||||
if constexpr (Helpers::isAndroid()) {
|
||||
return getAndroidAppPath() / "config.toml";
|
||||
} else {
|
||||
return std::filesystem::current_path() / "config.toml";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void Emulator::step() {}
|
||||
void Emulator::render() {}
|
||||
|
||||
|
@ -188,31 +184,13 @@ void Emulator::pollScheduler() {
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef __LIBRETRO__
|
||||
// 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;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Emulator::loadROM(const std::filesystem::path& path) {
|
||||
// Reset the emulator if we've already loaded a ROM
|
||||
|
|
|
@ -50,7 +50,7 @@ class HC_GLOBAL HydraCore final : public hydra::IBase,
|
|||
void* getProcAddress = nullptr;
|
||||
};
|
||||
|
||||
HydraCore::HydraCore() : emulator(new Emulator) {
|
||||
HydraCore::HydraCore() : emulator(new Emulator({ std::filesystem::current_path() / EmulatorConfigFilename }, std::filesystem::current_path())) {
|
||||
if (emulator->getRendererType() != RendererType::OpenGL) {
|
||||
throw std::runtime_error("HydraCore: Renderer is not OpenGL");
|
||||
}
|
||||
|
|
|
@ -38,6 +38,15 @@ JNIEnv* jniEnv() {
|
|||
return env;
|
||||
}
|
||||
|
||||
std::filesystem::path getAndroidAppPath() {
|
||||
// SDL_GetPrefPath fails to get the path due to no JNI environment
|
||||
std::ifstream cmdline("/proc/self/cmdline");
|
||||
std::string applicationName;
|
||||
std::getline(cmdline, applicationName, '\0');
|
||||
|
||||
return std::filesystem::path("/data") / "data" / applicationName / "files";
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
#define MAKE_SETTING(functionName, type, settingName) \
|
||||
|
@ -64,7 +73,8 @@ AlberFunction(void, Pause)(JNIEnv* env, jobject obj) { emulator->pause(); }
|
|||
AlberFunction(void, Resume)(JNIEnv* env, jobject obj) { emulator->resume(); }
|
||||
|
||||
AlberFunction(void, Initialize)(JNIEnv* env, jobject obj) {
|
||||
emulator = std::make_unique<Emulator>();
|
||||
auto appPath = getAndroidAppPath();
|
||||
emulator = std::make_unique<Emulator>({ appPath / EmulatorConfigFilename }, appPath);
|
||||
|
||||
if (emulator->getRendererType() != RendererType::OpenGL) {
|
||||
return throwException(env, "Renderer type is not OpenGL");
|
||||
|
|
|
@ -15,21 +15,12 @@ static retro_input_poll_t inputPollCallback;
|
|||
static retro_input_state_t inputStateCallback;
|
||||
|
||||
static retro_hw_render_callback hwRender;
|
||||
static std::filesystem::path savePath;
|
||||
|
||||
static bool screenTouched;
|
||||
|
||||
std::unique_ptr<Emulator> emulator;
|
||||
RendererGL* renderer;
|
||||
|
||||
std::filesystem::path Emulator::getConfigPath() {
|
||||
return std::filesystem::path(savePath / "config.toml");
|
||||
}
|
||||
|
||||
std::filesystem::path Emulator::getAppDataRoot() {
|
||||
return std::filesystem::path(savePath / "Emulator Files");
|
||||
}
|
||||
|
||||
static void* getGLProcAddress(const char* name) {
|
||||
return (void*)hwRender.get_proc_address(name);
|
||||
}
|
||||
|
@ -276,15 +267,14 @@ void retro_init() {
|
|||
envCallback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &xrgb888);
|
||||
|
||||
char* saveDir = nullptr;
|
||||
std::filesystem::path savePath = std::filesystem::path(saveDir);
|
||||
|
||||
if (!envCallback(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &saveDir) || saveDir == nullptr) {
|
||||
Helpers::warn("No save directory provided by LibRetro.");
|
||||
savePath = std::filesystem::current_path();
|
||||
} else {
|
||||
savePath = std::filesystem::path(saveDir);
|
||||
}
|
||||
|
||||
emulator = std::make_unique<Emulator>();
|
||||
emulator = std::make_unique<Emulator>(({ std::filesystem::path(savePath / EmulatorConfigFilename) }, std::filesystem::path(savePath / "Emulator Files")));
|
||||
}
|
||||
|
||||
void retro_deinit() {
|
||||
|
|
|
@ -14,7 +14,10 @@
|
|||
#include "version.hpp"
|
||||
|
||||
MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), keyboardMappings(InputMappings::defaultKeyboardMappings()) {
|
||||
emu = new Emulator();
|
||||
QCoreApplication::setApplicationName("Alber");
|
||||
|
||||
const std::filesystem::path appData(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation).toStdString());
|
||||
emu = new Emulator({ std::filesystem::current_path() / EmulatorConfigFilename, appData / EmulatorConfigFilename }, appData);
|
||||
|
||||
loadTranslation();
|
||||
setWindowTitle(tr("Alber"));
|
||||
|
|
|
@ -6,7 +6,14 @@
|
|||
#include "sdl_sensors.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
FrontendSDL::FrontendSDL() : keyboardMappings(InputMappings::defaultKeyboardMappings()) {
|
||||
std::filesystem::path getAppDataPath() {
|
||||
auto appData = SDL_GetPrefPath(nullptr, "Alber");
|
||||
auto appDataPath = std::filesystem::path(appData);
|
||||
SDL_free(appData);
|
||||
return appDataPath;
|
||||
}
|
||||
|
||||
FrontendSDL::FrontendSDL() : emu({ std::filesystem::current_path() / EmulatorConfigFilename, getAppDataPath() / EmulatorConfigFilename }, getAppDataPath()), keyboardMappings(InputMappings::defaultKeyboardMappings()) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
||||
Helpers::panic("Failed to initialize SDL2");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue