diff --git a/CHANGES b/CHANGES index 206c2256b..dc49564ea 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Features: - Implemented cycle counting for sprite rendering - Cleaner, unified settings window - Added a setting for pausing when the emulator is not in focus + - Customizable paths for save games, save states, screenshots and patches Bugfixes: - Util: Fix PowerPC PNG read/write pixel order - VFS: Fix VFileReadline and remove _vfdReadline diff --git a/src/gba/context/config.c b/src/gba/context/config.c index f092cda1e..e9c68cd8d 100644 --- a/src/gba/context/config.c +++ b/src/gba/context/config.c @@ -347,6 +347,11 @@ void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) { _lookupIntValue(config, "width", &opts->width); _lookupIntValue(config, "height", &opts->height); + _lookupCharValue(config, "savegamePath", &opts->savegamePath); + _lookupCharValue(config, "savestatePath", &opts->savestatePath); + _lookupCharValue(config, "screenshotPath", &opts->screenshotPath); + _lookupCharValue(config, "patchPath", &opts->patchPath); + char* idleOptimization = 0; if (_lookupCharValue(config, "idleOptimization", &idleOptimization)) { if (strcasecmp(idleOptimization, "ignore") == 0) { @@ -409,6 +414,14 @@ struct Configuration* GBAConfigGetOverrides(struct GBAConfig* config) { void GBAConfigFreeOpts(struct GBAOptions* opts) { free(opts->bios); free(opts->shader); + free(opts->savegamePath); + free(opts->savestatePath); + free(opts->screenshotPath); + free(opts->patchPath); opts->bios = 0; opts->shader = 0; + opts->savegamePath = 0; + opts->savestatePath = 0; + opts->screenshotPath = 0; + opts->patchPath = 0; } diff --git a/src/gba/context/config.h b/src/gba/context/config.h index 2ff950c7d..ce35bf614 100644 --- a/src/gba/context/config.h +++ b/src/gba/context/config.h @@ -40,6 +40,11 @@ struct GBAOptions { bool suspendScreensaver; char* shader; + char* savegamePath; + char* savestatePath; + char* screenshotPath; + char* patchPath; + int volume; bool mute; diff --git a/src/gba/context/directories.c b/src/gba/context/directories.c index d18fde029..b8bfb889f 100644 --- a/src/gba/context/directories.c +++ b/src/gba/context/directories.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "directories.h" +#include "gba/context/config.h" #include "util/vfs.h" void GBADirectorySetInit(struct GBADirectorySet* dirs) { @@ -99,3 +100,33 @@ struct VFile* GBADirectorySetOpenPath(struct GBADirectorySet* dirs, const char* } return file; } + +void GBADirectorySetMapOptions(struct GBADirectorySet* dirs, const struct GBAOptions* opts) { + if (opts->savegamePath) { + if (dirs->save && dirs->save != dirs->base) { + dirs->save->close(dirs->save); + } + dirs->save = VDirOpen(opts->savegamePath); + } + + if (opts->savestatePath) { + if (dirs->state && dirs->state != dirs->base) { + dirs->state->close(dirs->state); + } + dirs->state = VDirOpen(opts->savestatePath); + } + + if (opts->screenshotPath) { + if (dirs->screenshot && dirs->screenshot != dirs->base) { + dirs->screenshot->close(dirs->screenshot); + } + dirs->screenshot = VDirOpen(opts->screenshotPath); + } + + if (opts->patchPath) { + if (dirs->patch && dirs->patch != dirs->base) { + dirs->patch->close(dirs->patch); + } + dirs->patch = VDirOpen(opts->patchPath); + } +} diff --git a/src/gba/context/directories.h b/src/gba/context/directories.h index bf906c4ab..7292c59ae 100644 --- a/src/gba/context/directories.h +++ b/src/gba/context/directories.h @@ -27,4 +27,7 @@ void GBADirectorySetDetachBase(struct GBADirectorySet* dirs); struct VFile* GBADirectorySetOpenPath(struct GBADirectorySet* dirs, const char* path, bool (*filter)(struct VFile*)); +struct GBAOptions; +void GBADirectorySetMapOptions(struct GBADirectorySet* dirs, const struct GBAOptions* opts); + #endif diff --git a/src/gba/supervisor/thread.c b/src/gba/supervisor/thread.c index 8265b1775..40db25f1f 100644 --- a/src/gba/supervisor/thread.c +++ b/src/gba/supervisor/thread.c @@ -401,6 +401,8 @@ void GBAMapOptionsToContext(const struct GBAOptions* opts, struct GBAThread* thr } threadContext->idleOptimization = opts->idleOptimization; + + GBADirectorySetMapOptions(&threadContext->dirs, opts); } void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread* threadContext) { @@ -441,7 +443,6 @@ bool GBAThreadStart(struct GBAThread* threadContext) { return false; } - GBADirectorySetInit(&threadContext->dirs); _reloadDirectories(threadContext); MutexInit(&threadContext->stateMutex); @@ -571,8 +572,6 @@ void GBAThreadJoin(struct GBAThread* threadContext) { threadContext->patch->close(threadContext->patch); threadContext->patch = 0; } - - GBADirectorySetDeinit(&threadContext->dirs); } bool GBAThreadIsActive(struct GBAThread* threadContext) { diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 6ab9d39d5..a2383333d 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -128,6 +128,7 @@ ConfigController::~ConfigController() { bool ConfigController::parseArguments(GBAArguments* args, int argc, char* argv[], SubParser* subparser) { if (::parseArguments(args, &m_config, argc, argv, subparser)) { + GBAConfigFreeOpts(&m_opts); GBAConfigMap(&m_config, &m_opts); return true; } @@ -262,6 +263,9 @@ void ConfigController::setMRU(const QList& mru) { void ConfigController::write() { GBAConfigSave(&m_config); m_settings->sync(); + + GBAConfigFreeOpts(&m_opts); + GBAConfigMap(&m_config, &m_opts); } void ConfigController::makePortable() { diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 9fb96b303..ee35f1808 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -162,6 +162,16 @@ QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QStr return filename; } +QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) { + interruptAll(); + QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getQtOption("lastDirectory").toString()); + continueAll(); + if (!filename.isEmpty()) { + m_configController.setQtOption("lastDirectory", QFileInfo(filename).dir().path()); + } + return filename; +} + QFileDialog* GBAApp::getOpenFileDialog(QWidget* owner, const QString& title, const QString& filter) { FileDialog* dialog = new FileDialog(this, owner, title, filter); dialog->setAcceptMode(QFileDialog::AcceptOpen); diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index e84f8f422..c110a51c3 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -36,6 +36,7 @@ public: QString getOpenFileName(QWidget* owner, const QString& title, const QString& filter = QString()); QString getSaveFileName(QWidget* owner, const QString& title, const QString& filter = QString()); + QString getOpenDirectoryName(QWidget* owner, const QString& title); QFileDialog* getOpenFileDialog(QWidget* owner, const QString& title, const QString& filter = QString()); QFileDialog* getSaveFileDialog(QWidget* owner, const QString& title, const QString& filter = QString()); diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 17cde61eb..65ad7528f 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -19,6 +19,7 @@ extern "C" { #include "gba/audio.h" #include "gba/context/config.h" +#include "gba/context/directories.h" #include "gba/gba.h" #include "gba/serialize.h" #include "gba/sharkport.h" @@ -216,6 +217,7 @@ GameController::~GameController() { clearMultiplayerController(); closeGame(); GBACheatDeviceDestroy(&m_cheatDevice); + GBADirectorySetDeinit(&m_threadContext.dirs); delete m_renderer; delete[] m_drawContext; delete[] m_frontBuffer; @@ -255,6 +257,7 @@ void GameController::setOptions(const GBAOptions* opts) { setMute(opts->mute); threadInterrupt(); + GBADirectorySetMapOptions(&m_threadContext.dirs, opts); m_threadContext.idleOptimization = opts->idleOptimization; threadContinue(); } diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index bc2f66183..810079459 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -40,6 +40,74 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections); loadSetting("suspendScreensaver", m_ui.suspendScreensaver); loadSetting("pauseOnFocusLost", m_ui.pauseOnFocusLost); + loadSetting("savegamePath", m_ui.savegamePath); + loadSetting("savestatePath", m_ui.savestatePath); + loadSetting("screenshotPath", m_ui.screenshotPath); + loadSetting("patchPath", m_ui.patchPath); + + if (m_ui.savegamePath->text().isEmpty()) { + m_ui.savegameSameDir->setChecked(true); + } + connect(m_ui.savegameSameDir, &QAbstractButton::toggled, [this] (bool e) { + if (e) { + m_ui.savegamePath->clear(); + } + }); + connect(m_ui.savegameBrowse, &QAbstractButton::pressed, [this] () { + QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); + if (!path.isNull()) { + m_ui.savegameSameDir->setChecked(false); + m_ui.savegamePath->setText(path); + } + }); + + if (m_ui.savestatePath->text().isEmpty()) { + m_ui.savestateSameDir->setChecked(true); + } + connect(m_ui.savestateSameDir, &QAbstractButton::toggled, [this] (bool e) { + if (e) { + m_ui.savestatePath->clear(); + } + }); + connect(m_ui.savestateBrowse, &QAbstractButton::pressed, [this] () { + QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); + if (!path.isNull()) { + m_ui.savestateSameDir->setChecked(false); + m_ui.savestatePath->setText(path); + } + }); + + if (m_ui.screenshotPath->text().isEmpty()) { + m_ui.screenshotSameDir->setChecked(true); + } + connect(m_ui.screenshotSameDir, &QAbstractButton::toggled, [this] (bool e) { + if (e) { + m_ui.screenshotPath->clear(); + } + }); + connect(m_ui.screenshotBrowse, &QAbstractButton::pressed, [this] () { + QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); + if (!path.isNull()) { + m_ui.screenshotSameDir->setChecked(false); + m_ui.screenshotPath->setText(path); + } + }); + + if (m_ui.patchPath->text().isEmpty()) { + m_ui.patchSameDir->setChecked(true); + } + connect(m_ui.patchSameDir, &QAbstractButton::toggled, [this] (bool e) { + if (e) { + m_ui.patchPath->clear(); + } + }); + connect(m_ui.patchBrowse, &QAbstractButton::pressed, [this] () { + QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); + if (!path.isNull()) { + m_ui.patchSameDir->setChecked(false); + m_ui.patchPath->setText(path); + } + }); double fastForwardRatio = loadSetting("fastForwardRatio").toDouble(); if (fastForwardRatio <= 0) { @@ -147,6 +215,10 @@ void SettingsView::updateConfig() { saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections); saveSetting("suspendScreensaver", m_ui.suspendScreensaver); saveSetting("pauseOnFocusLost", m_ui.pauseOnFocusLost); + saveSetting("savegamePath", m_ui.savegamePath); + saveSetting("savestatePath", m_ui.savestatePath); + saveSetting("screenshotPath", m_ui.screenshotPath); + saveSetting("patchPath", m_ui.patchPath); if (m_ui.fastForwardUnbounded->isChecked()) { saveSetting("fastForwardRatio", "-1"); @@ -182,6 +254,7 @@ void SettingsView::updateConfig() { m_controller->write(); + emit pathsChanged(); emit biosLoaded(m_ui.bios->text()); } diff --git a/src/platform/qt/SettingsView.h b/src/platform/qt/SettingsView.h index 3947416f3..4ccdabf0e 100644 --- a/src/platform/qt/SettingsView.h +++ b/src/platform/qt/SettingsView.h @@ -26,6 +26,7 @@ signals: void biosLoaded(const QString&); void audioDriverChanged(); void displayDriverChanged(); + void pathsChanged(); private slots: void selectBios(); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index e0903e73b..6a2cca0bb 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -6,8 +6,8 @@ 0 0 - 661 - 459 + 568 + 451 @@ -25,6 +25,9 @@ + + 0 + @@ -553,6 +556,198 @@ + + + + QFormLayout::FieldsStayAtSizeHint + + + + + Save games + + + + + + + + + + 0 + 0 + + + + + 170 + 0 + + + + + + + + Browse + + + + + + + + + Same directory as the ROM + + + + + + + Qt::Horizontal + + + + + + + Save states + + + + + + + + + + 0 + 0 + + + + + 170 + 0 + + + + + + + + Browse + + + + + + + + + Same directory as the ROM + + + + + + + Qt::Horizontal + + + + + + + Screenshots + + + + + + + + + + 0 + 0 + + + + + 170 + 0 + + + + + + + + Browse + + + + + + + + + Same directory as the ROM + + + + + + + Qt::Horizontal + + + + + + + Patches + + + + + + + + + + 0 + 0 + + + + + 170 + 0 + + + + + + + + Browse + + + + + + + + + Same directory as the ROM + + + + + @@ -576,7 +771,12 @@ - Running + Emulation + + + + + Paths @@ -640,5 +840,69 @@ + + savegameSameDir + toggled(bool) + savegamePath + setDisabled(bool) + + + 392 + 82 + + + 366 + 48 + + + + + savestateSameDir + toggled(bool) + savestatePath + setDisabled(bool) + + + 392 + 161 + + + 366 + 127 + + + + + screenshotSameDir + toggled(bool) + screenshotPath + setDisabled(bool) + + + 392 + 240 + + + 366 + 206 + + + + + patchSameDir + toggled(bool) + patchPath + setDisabled(bool) + + + 345 + 319 + + + 340 + 285 + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 0bdfbeb21..e27609a62 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -198,16 +198,7 @@ void Window::setConfig(ConfigController* config) { void Window::loadConfig() { const GBAOptions* opts = m_config->options(); - - m_log.setLevels(opts->logLevel); - - m_controller->setOptions(opts); - m_display->lockAspectRatio(opts->lockAspectRatio); - m_display->filter(opts->resampleVideo); - - if (opts->bios) { - m_controller->loadBIOS(opts->bios); - } + reloadConfig(); // TODO: Move these to ConfigController if (opts->fpsTarget) { @@ -239,14 +230,28 @@ void Window::loadConfig() { } } - m_inputController.setScreensaverSuspendable(opts->suspendScreensaver); - m_mruFiles = m_config->getMRU(); updateMRU(); m_inputController.setConfiguration(m_config); } +void Window::reloadConfig() { + const GBAOptions* opts = m_config->options(); + + m_log.setLevels(opts->logLevel); + + m_controller->setOptions(opts); + m_display->lockAspectRatio(opts->lockAspectRatio); + m_display->filter(opts->resampleVideo); + + if (opts->bios) { + m_controller->loadBIOS(opts->bios); + } + + m_inputController.setScreensaverSuspendable(opts->suspendScreensaver); +} + void Window::saveConfig() { m_inputController.saveConfiguration(); m_config->write(); @@ -350,6 +355,7 @@ void Window::openSettingsWindow() { connect(settingsWindow, SIGNAL(biosLoaded(const QString&)), m_controller, SLOT(loadBIOS(const QString&))); connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver())); connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart())); + connect(settingsWindow, SIGNAL(pathsChanged()), this, SLOT(reloadConfig())); openView(settingsWindow); } diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index c13c8d941..4dc927da6 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -66,6 +66,7 @@ public slots: void exitFullScreen(); void toggleFullScreen(); void loadConfig(); + void reloadConfig(); void saveConfig(); void replaceROM(); diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index d1c883707..ffc801413 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -164,6 +164,7 @@ int main(int argc, char** argv) { GBAConfigFreeOpts(&opts); GBAConfigDeinit(&config); free(context.debugger); + GBADirectorySetDeinit(&context.dirs); GBASDLDetachPlayer(&renderer.events, &renderer.player); GBAInputMapDeinit(&inputMap);