all repos — mgba @ 764acb7d6385fc9bf078f14effd9fc198943bb49

mGBA Game Boy Advance Emulator

Core: Add autosave/-load cheats
Vicki Pfau vi@endrift.com
Sat, 11 Nov 2017 12:30:04 -0800
commit

764acb7d6385fc9bf078f14effd9fc198943bb49

parent

fe354097f2596764d7072af8358586345a932291

M CHANGESCHANGES

@@ -8,6 +8,7 @@ - Super Game Boy support

- Customizable autofire speed - Ability to set default Game Boy model - Map viewer + - Automatic cheat loading and saving Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Serialize: Fix audio state loading
M include/mgba/core/cheats.hinclude/mgba/core/cheats.h

@@ -79,6 +79,7 @@

struct mCheatSet* (*createSet)(struct mCheatDevice*, const char* name); struct mCheatSets cheats; + bool autosave; }; struct VFile;

@@ -98,6 +99,7 @@ void mCheatRemoveSet(struct mCheatDevice*, struct mCheatSet*);

bool mCheatParseFile(struct mCheatDevice*, struct VFile*); bool mCheatSaveFile(struct mCheatDevice*, struct VFile*); +void mCheatAutosave(struct mCheatDevice*); void mCheatRefresh(struct mCheatDevice*, struct mCheatSet*);
M include/mgba/core/config.hinclude/mgba/core/config.h

@@ -51,6 +51,7 @@ char* savegamePath;

char* savestatePath; char* screenshotPath; char* patchPath; + char* cheatsPath; int volume; bool mute;
M include/mgba/core/core.hinclude/mgba/core/core.h

@@ -168,6 +168,7 @@ bool mCorePreloadFile(struct mCore* core, const char* path);

bool mCoreAutoloadSave(struct mCore* core); bool mCoreAutoloadPatch(struct mCore* core); +bool mCoreAutoloadCheats(struct mCore* core); bool mCoreSaveState(struct mCore* core, int slot, int flags); bool mCoreLoadState(struct mCore* core, int slot, int flags);
M include/mgba/core/directories.hinclude/mgba/core/directories.h

@@ -21,6 +21,7 @@ struct VDir* save;

struct VDir* patch; struct VDir* state; struct VDir* screenshot; + struct VDir* cheats; }; void mDirectorySetInit(struct mDirectorySet* dirs);
M src/core/cheats.csrc/core/cheats.c

@@ -51,6 +51,7 @@ void mCheatDeviceCreate(struct mCheatDevice* device) {

device->d.id = M_CHEAT_DEVICE_ID; device->d.init = mCheatDeviceInit; device->d.deinit = mCheatDeviceDeinit; + device->autosave = false; mCheatSetsInit(&device->cheats, 4); }

@@ -250,6 +251,15 @@ }

StringListClear(&directives); StringListDeinit(&directives); return true; +} + +void mCheatAutosave(struct mCheatDevice* device) { + if (!device->autosave) { + return; + } + struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC); + mCheatSaveFile(device, vf); + vf->close(vf); } void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) {
M src/core/config.csrc/core/config.c

@@ -379,6 +379,7 @@ _lookupCharValue(config, "savegamePath", &opts->savegamePath);

_lookupCharValue(config, "savestatePath", &opts->savestatePath); _lookupCharValue(config, "screenshotPath", &opts->screenshotPath); _lookupCharValue(config, "patchPath", &opts->patchPath); + _lookupCharValue(config, "cheatsPath", &opts->cheatsPath); } void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptions* opts) {

@@ -443,10 +444,12 @@ free(opts->savegamePath);

free(opts->savestatePath); free(opts->screenshotPath); free(opts->patchPath); + free(opts->cheatsPath); opts->bios = 0; opts->shader = 0; opts->savegamePath = 0; opts->savestatePath = 0; opts->screenshotPath = 0; opts->patchPath = 0; + opts->cheatsPath = 0; }
M src/core/core.csrc/core/core.c

@@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <mgba/core/core.h> +#include <mgba/core/cheats.h> #include <mgba/core/log.h> #include <mgba/core/serialize.h> #include <mgba-util/vfs.h>

@@ -165,6 +166,24 @@ core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY)) ||

core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY)); } +bool mCoreAutoloadCheats(struct mCore* core) { + bool success = true; + int cheatAuto; + if (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto) { + struct VFile* vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.cheats, ".cheats", O_RDONLY); + if (vf) { + struct mCheatDevice* device = core->cheatDevice(core); + success = mCheatParseFile(device, vf); + vf->close(vf); + } + } + if (!mCoreConfigGetIntValue(&core->config, "cheatAutosave", &cheatAuto) || cheatAuto) { + struct mCheatDevice* device = core->cheatDevice(core); + device->autosave = true; + } + return success; +} + bool mCoreSaveState(struct mCore* core, int slot, int flags) { struct VFile* vf = mCoreGetState(core, slot, true); if (!vf) {

@@ -271,6 +290,10 @@ #endif

if (core->opts.audioBuffers) { core->setAudioBufferSize(core, core->opts.audioBuffers); } + + mCoreConfigCopyValue(&core->config, config, "cheatAutosave"); + mCoreConfigCopyValue(&core->config, config, "cheatAutoload"); + core->loadConfig(core, config); }
M src/core/directories.csrc/core/directories.c

@@ -16,6 +16,7 @@ dirs->save = 0;

dirs->patch = 0; dirs->state = 0; dirs->screenshot = 0; + dirs->cheats = 0; } void mDirectorySetDeinit(struct mDirectorySet* dirs) {

@@ -34,6 +35,9 @@ }

if (dirs->archive == dirs->screenshot) { dirs->screenshot = NULL; } + if (dirs->archive == dirs->cheats) { + dirs->cheats = NULL; + } dirs->archive->close(dirs->archive); dirs->archive = NULL; }

@@ -48,6 +52,9 @@ }

if (dirs->save == dirs->screenshot) { dirs->screenshot = NULL; } + if (dirs->save == dirs->cheats) { + dirs->cheats = NULL; + } dirs->save->close(dirs->save); dirs->save = NULL; }

@@ -58,6 +65,9 @@ dirs->state = NULL;

} if (dirs->patch == dirs->screenshot) { dirs->screenshot = NULL; + } + if (dirs->patch == dirs->cheats) { + dirs->cheats = NULL; } dirs->patch->close(dirs->patch); dirs->patch = NULL;

@@ -67,14 +77,25 @@ if (dirs->state) {

if (dirs->state == dirs->screenshot) { dirs->state = NULL; } + if (dirs->state == dirs->cheats) { + dirs->cheats = NULL; + } dirs->state->close(dirs->state); dirs->state = NULL; } if (dirs->screenshot) { + if (dirs->screenshot == dirs->cheats) { + dirs->cheats = NULL; + } dirs->screenshot->close(dirs->screenshot); dirs->screenshot = NULL; } + + if (dirs->cheats) { + dirs->cheats->close(dirs->cheats); + dirs->cheats = NULL; + } } void mDirectorySetAttachBase(struct mDirectorySet* dirs, struct VDir* base) {

@@ -91,6 +112,9 @@ }

if (!dirs->screenshot) { dirs->screenshot = dirs->base; } + if (!dirs->cheats) { + dirs->cheats = dirs->base; + } } void mDirectorySetDetachBase(struct mDirectorySet* dirs) {

@@ -105,6 +129,9 @@ dirs->state = NULL;

} if (dirs->screenshot == dirs->base) { dirs->screenshot = NULL; + } + if (dirs->cheats == dirs->base) { + dirs->cheats = NULL; } if (dirs->base) {

@@ -181,6 +208,16 @@ if (dirs->patch && dirs->patch != dirs->base) {

dirs->patch->close(dirs->patch); } dirs->patch = dir; + } + } + + if (opts->cheatsPath) { + struct VDir* dir = VDirOpen(opts->cheatsPath); + if (dir) { + if (dirs->cheats && dirs->cheats != dirs->base) { + dirs->cheats->close(dirs->cheats); + } + dirs->cheats = dir; } } }
M src/feature/gui/gui-runner.csrc/feature/gui/gui-runner.c

@@ -306,6 +306,7 @@ logger.logLevel = runner->core->opts.logLevel;

mLOG(GUI_RUNNER, DEBUG, "Loading save..."); mCoreAutoloadSave(runner->core); + mCoreAutoloadCheats(runner->core); if (runner->setup) { mLOG(GUI_RUNNER, DEBUG, "Setting up runner..."); runner->setup(runner);
M src/platform/python/mgba/core.pysrc/platform/python/mgba/core.py

@@ -182,6 +182,9 @@

def autoloadPatch(self): return bool(lib.mCoreAutoloadPatch(self._core)) + def autoloadCheats(self): + return bool(lib.mCoreAutoloadCheats(self._core)) + def platform(self): return self._core.platform(self._core)
M src/platform/qt/CheatsModel.cppsrc/platform/qt/CheatsModel.cpp

@@ -70,10 +70,12 @@ switch (role) {

case Qt::DisplayRole: case Qt::EditRole: mCheatSetRename(cheats, value.toString().toUtf8().constData()); + mCheatAutosave(m_device); emit dataChanged(index, index); return true; case Qt::CheckStateRole: cheats->enabled = value == Qt::Checked; + mCheatAutosave(m_device); emit dataChanged(index, index); return true; default:

@@ -154,7 +156,8 @@ mCheatSet* set = *mCheatSetsGetPointer(&m_device->cheats, index.row());

beginRemoveRows(QModelIndex(), row, row); mCheatRemoveSet(m_device, set); mCheatSetDeinit(set); - endInsertRows(); + endRemoveRows(); + mCheatAutosave(m_device); } QString CheatsModel::toString(const QModelIndexList& indices) const {

@@ -201,6 +204,7 @@ }

void CheatsModel::endAppendRow() { endInsertRows(); + mCheatAutosave(m_device); } void CheatsModel::loadFile(const QString& path) {

@@ -232,6 +236,7 @@ set->copyProperties(set, *mCheatSetsGetPointer(&m_device->cheats, size - 1));

} mCheatAddSet(m_device, set); endInsertRows(); + mCheatAutosave(m_device); } void CheatsModel::invalidated() {
M src/platform/qt/CheatsView.cppsrc/platform/qt/CheatsView.cpp

@@ -109,14 +109,14 @@ return false;

} void CheatsView::load() { - QString filename = GBAApp::app()->getOpenFileName(this, tr("Select cheats file")); + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select cheats file"), tr(("Cheats file (*.cheats *.cht *.clt)"))); if (!filename.isEmpty()) { m_model.loadFile(filename); } } void CheatsView::save() { - QString filename = GBAApp::app()->getSaveFileName(this, tr("Select cheats file")); + QString filename = GBAApp::app()->getSaveFileName(this, tr("Select cheats file"), tr(("Cheats file (*.cheats *.cht *.clt)"))); if (!filename.isEmpty()) { m_model.saveFile(filename); }
M src/platform/qt/CoreManager.cppsrc/platform/qt/CoreManager.cpp

@@ -109,6 +109,7 @@ }

bytes = info.dir().canonicalPath().toUtf8(); mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData())); mCoreAutoloadSave(core); + mCoreAutoloadCheats(core); CoreController* cc = new CoreController(core); if (m_multiplayer) {
M src/platform/qt/SettingsView.cppsrc/platform/qt/SettingsView.cpp

@@ -106,6 +106,22 @@ m_ui.patchSameDir->setChecked(false);

m_ui.patchPath->setText(path); } }); + + if (m_ui.cheatsPath->text().isEmpty()) { + m_ui.cheatsSameDir->setChecked(true); + } + connect(m_ui.cheatsSameDir, &QAbstractButton::toggled, [this] (bool e) { + if (e) { + m_ui.cheatsPath->clear(); + } + }); + connect(m_ui.cheatsBrowse, &QAbstractButton::pressed, [this] () { + QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); + if (!path.isNull()) { + m_ui.cheatsSameDir->setChecked(false); + m_ui.cheatsPath->setText(path); + } + }); connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared); // TODO: Move to reloadConfig()

@@ -334,10 +350,13 @@ saveSetting("savegamePath", m_ui.savegamePath);

saveSetting("savestatePath", m_ui.savestatePath); saveSetting("screenshotPath", m_ui.screenshotPath); saveSetting("patchPath", m_ui.patchPath); + saveSetting("cheatsPath", m_ui.cheatsPath); saveSetting("libraryStyle", m_ui.libraryStyle->currentIndex()); saveSetting("showLibrary", m_ui.showLibrary); saveSetting("preload", m_ui.preload); saveSetting("showFps", m_ui.showFps); + saveSetting("cheatAutoload", m_ui.cheatAutoload); + saveSetting("cheatAutosave", m_ui.cheatAutosave); if (m_ui.fastForwardUnbounded->isChecked()) { saveSetting("fastForwardRatio", "-1");

@@ -453,9 +472,12 @@ loadSetting("savegamePath", m_ui.savegamePath);

loadSetting("savestatePath", m_ui.savestatePath); loadSetting("screenshotPath", m_ui.screenshotPath); loadSetting("patchPath", m_ui.patchPath); + loadSetting("cheatsPath", m_ui.cheatsPath); loadSetting("showLibrary", m_ui.showLibrary); loadSetting("preload", m_ui.preload); loadSetting("showFps", m_ui.showFps, true); + loadSetting("cheatAutoload", m_ui.cheatAutoload, true); + loadSetting("cheatAutosave", m_ui.cheatAutosave, true); m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());
M src/platform/qt/SettingsView.uisrc/platform/qt/SettingsView.ui

@@ -419,6 +419,13 @@ </property>

</item> </widget> </item> + <item row="1" column="0" colspan="2"> + <widget class="Line" name="line_10"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> <item row="2" column="0"> <widget class="QLabel" name="label_6"> <property name="text">

@@ -488,17 +495,37 @@ <string>Pause when inactive</string>

</property> </widget> </item> - <item row="1" column="0" colspan="2"> - <widget class="Line" name="line_10"> + <item row="9" column="1"> + <widget class="QCheckBox" name="showFps"> + <property name="text"> + <string>Show FPS in title bar</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="10" column="0" colspan="2"> + <widget class="Line" name="line_13"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> </widget> </item> - <item row="9" column="1"> - <widget class="QCheckBox" name="showFps"> + <item row="11" column="1"> + <widget class="QCheckBox" name="cheatAutosave"> + <property name="text"> + <string>Automatically save cheats</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="12" column="1"> + <widget class="QCheckBox" name="cheatAutoload"> <property name="text"> - <string>Show FPS in title bar</string> + <string>Automatically load cheats</string> </property> <property name="checked"> <bool>true</bool>

@@ -1063,6 +1090,54 @@ </layout>

</item> <item row="11" column="1"> <widget class="QCheckBox" name="patchSameDir"> + <property name="text"> + <string>Same directory as the ROM</string> + </property> + </widget> + </item> + <item row="12" column="0" colspan="2"> + <widget class="Line" name="line_14"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="13" column="0"> + <widget class="QLabel" name="label_48"> + <property name="text"> + <string>Cheats</string> + </property> + </widget> + </item> + <item row="13" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_27"> + <item> + <widget class="QLineEdit" name="cheatsPath"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>170</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cheatsBrowse"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="14" column="1"> + <widget class="QCheckBox" name="cheatsSameDir"> <property name="text"> <string>Same directory as the ROM</string> </property>
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -181,6 +181,7 @@ if (!mCoreLoadFile(renderer->core, args->fname)) {

return 1; } mCoreAutoloadSave(renderer->core); + mCoreAutoloadCheats(renderer->core); #ifdef ENABLE_SCRIPTING struct mScriptBridge* bridge = mScriptBridgeCreate(); #ifdef ENABLE_PYTHON