3DS: Add experimental autosave
Vicki Pfau vi@endrift.com
Wed, 10 Jan 2018 00:42:09 -0800
2 files changed,
91 insertions(+),
0 deletions(-)
M
src/feature/gui/gui-runner.c
→
src/feature/gui/gui-runner.c
@@ -317,6 +317,13 @@ }
mLOG(GUI_RUNNER, DEBUG, "Reseting..."); runner->core->reset(runner->core); mLOG(GUI_RUNNER, DEBUG, "Reset!"); + + if (mCoreLoadState(runner->core, 0, SAVESTATE_SCREENSHOT | SAVESTATE_RTC)) { + struct VFile* autosave = mCoreGetState(runner->core, 0, true); + autosave->truncate(autosave, 0); + autosave->close(autosave); + } + bool running = true; if (runner->gameLoaded) { runner->gameLoaded(runner);@@ -469,6 +476,14 @@ runner->core->desiredVideoDimensions(runner->core, &w, &h);
mappedMemoryFree(drawState.screenshot, w * h * 4); } + struct VFile* autosave = mCoreGetState(runner->core, 0, false); + if (autosave) { + autosave->close(autosave); + autosave = mCoreGetState(runner->core, 0, true); + autosave->truncate(autosave, 0); + autosave->close(autosave); + } + if (runner->config.port) { mLOG(GUI_RUNNER, DEBUG, "Saving key sources..."); if (runner->keySources) {@@ -483,6 +498,7 @@ }
mInputMapDeinit(&runner->core->inputMap); mLOG(GUI_RUNNER, DEBUG, "Deinitializing core..."); runner->core->deinit(runner->core); + runner->core = NULL; GUIMenuItemListDeinit(&pauseMenu.items); GUIMenuItemListDeinit(&stateSaveMenu.items);
M
src/platform/3ds/main.c
→
src/platform/3ds/main.c
@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/blip_buf.h> #include <mgba/core/core.h> +#include <mgba/core/serialize.h> #ifdef M_CORE_GBA #include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/input.h>@@ -22,6 +23,7 @@ #include <mgba-util/gui/menu.h>
#include <mgba-util/memory.h> #include <mgba-util/platform/3ds/3ds-vfs.h> +#include <mgba-util/threading.h> #include "ctr-gpu.h" #include <3ds.h>@@ -104,6 +106,12 @@ static C3D_Tex upscaleBufferTex;
static aptHookCookie cookie; +static Thread autosave; +static struct VFile* autosaveBuffer = NULL; +static Mutex autosaveMutex; +static Condition autosaveCond; +static struct mCore* autosaveCore = NULL; + extern bool allocateRomBuffer(void); static bool _initGpu(void) {@@ -191,6 +199,33 @@ break;
default: break; } +} + +static void _autosaveThread(void* context) { + bool* running = context; + MutexLock(&autosaveMutex); + while (*running) { + ConditionWait(&autosaveCond, &autosaveMutex); + if (*running && autosaveCore) { + struct VFile* vf = mCoreGetState(autosaveCore, 0, true); + void* mem = autosaveBuffer->map(autosaveBuffer, autosaveBuffer->size(autosaveBuffer), MAP_READ); + vf->write(vf, mem, autosaveBuffer->size(autosaveBuffer)); + autosaveBuffer->unmap(autosaveBuffer, mem, autosaveBuffer->size(autosaveBuffer)); + vf->close(vf); + } + } + MutexUnlock(&autosaveMutex); +} + +static void _tryAutosave(struct mCore* core) { + if (!autosaveBuffer) { + autosaveBuffer = VFileMemChunk(NULL, 0); + } + MutexLock(&autosaveMutex); + autosaveCore = core; + mCoreSaveStateNamed(core, autosaveBuffer, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA); + ConditionWake(&autosaveCond); + MutexUnlock(&autosaveMutex); } static void _map3DSKey(struct mInputMap* map, int ctrKey, enum GBAKey key) {@@ -425,6 +460,10 @@ CAMU_Activate(camera.cam);
} } } + + MutexLock(&autosaveMutex); + autosaveCore = runner->core; + MutexUnlock(&autosaveMutex); } static void _gameUnloaded(struct mGUIRunner* runner) {@@ -458,6 +497,10 @@ #endif
default: break; } + + MutexLock(&autosaveMutex); + autosaveCore = NULL; + MutexUnlock(&autosaveMutex); } static void _drawTex(struct mCore* core, bool faded) {@@ -679,6 +722,16 @@ tickCounter = svcGetSystemTick();
} static bool _running(struct mGUIRunner* runner) { + static int frame = 0; + if (autosaveCore) { + ++frame; + if (frame == 300) { + _tryAutosave(autosaveCore); + frame = 0; + } + } else { + frame = 0; + } return aptMainLoop(); }@@ -1041,7 +1094,29 @@ _map3DSKey(&runner.params.keyMap, KEY_RIGHT, GUI_INPUT_RIGHT);
_map3DSKey(&runner.params.keyMap, KEY_CSTICK_UP, mGUI_INPUT_INCREASE_BRIGHTNESS); _map3DSKey(&runner.params.keyMap, KEY_CSTICK_DOWN, mGUI_INPUT_DECREASE_BRIGHTNESS); + bool autosaveActive = true; + MutexInit(&autosaveMutex); + ConditionInit(&autosaveCond); + + APT_SetAppCpuTimeLimit(20); + autosave = threadCreate(_autosaveThread, &autosaveActive, 0x4000, 0x1F, 1, true); + mGUIRunloop(&runner); + + MutexLock(&autosaveMutex); + autosaveActive = false; + ConditionWake(&autosaveCond); + MutexUnlock(&autosaveMutex); + + threadJoin(autosave, U64_MAX); + + if (autosaveBuffer) { + autosaveBuffer->close(autosaveBuffer); + } + + ConditionDeinit(&autosaveCond); + MutexDeinit(&autosaveMutex); + mGUIDeinit(&runner); _cleanup();