mirror of https://github.com/mgba-emu/mgba.git
GUI: Make autosave portable
This commit is contained in:
parent
0e9ba00dbf
commit
45c2fdf7ed
|
@ -23,6 +23,7 @@
|
|||
mLOG_DECLARE_CATEGORY(GUI_RUNNER);
|
||||
mLOG_DEFINE_CATEGORY(GUI_RUNNER, "GUI Runner", "gui.runner");
|
||||
|
||||
#define AUTOSAVE_GRANULARITY 600
|
||||
#define FPS_GRANULARITY 120
|
||||
#define FPS_BUFFER_SIZE 3
|
||||
|
||||
|
@ -34,19 +35,11 @@ enum {
|
|||
RUNNER_SCREENSHOT,
|
||||
RUNNER_CONFIG,
|
||||
RUNNER_RESET,
|
||||
RUNNER_COMMAND_MASK = 0xFFFF,
|
||||
|
||||
RUNNER_STATE_1 = 0x10000,
|
||||
RUNNER_STATE_2 = 0x20000,
|
||||
RUNNER_STATE_3 = 0x30000,
|
||||
RUNNER_STATE_4 = 0x40000,
|
||||
RUNNER_STATE_5 = 0x50000,
|
||||
RUNNER_STATE_6 = 0x60000,
|
||||
RUNNER_STATE_7 = 0x70000,
|
||||
RUNNER_STATE_8 = 0x80000,
|
||||
RUNNER_STATE_9 = 0x90000,
|
||||
RUNNER_COMMAND_MASK = 0xFFFF
|
||||
};
|
||||
|
||||
#define RUNNER_STATE(X) ((X) << 16)
|
||||
|
||||
static const struct mInputPlatformInfo _mGUIKeyInfo = {
|
||||
.platformName = "gui",
|
||||
.keyId = (const char*[GUI_INPUT_MAX]) {
|
||||
|
@ -141,6 +134,21 @@ static uint8_t _readLux(struct GBALuminanceSource* lux) {
|
|||
return 0xFF - value;
|
||||
}
|
||||
|
||||
static void _tryAutosave(struct mGUIRunner* runner) {
|
||||
#ifdef DISABLE_THREADING
|
||||
mCoreSaveState(runner->core, 0, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
|
||||
#else
|
||||
if (!runner->autosave.buffer) {
|
||||
runner->autosave.buffer = VFileMemChunk(NULL, 0);
|
||||
}
|
||||
MutexLock(&runner->autosave.mutex);
|
||||
runner->autosave.core = runner->core;
|
||||
mCoreSaveStateNamed(runner->core, runner->autosave.buffer, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
|
||||
ConditionWake(&runner->autosave.cond);
|
||||
MutexUnlock(&runner->autosave.mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
void mGUIInit(struct mGUIRunner* runner, const char* port) {
|
||||
GUIInit(&runner->params);
|
||||
runner->port = port;
|
||||
|
@ -174,9 +182,34 @@ void mGUIInit(struct mGUIRunner* runner, const char* port) {
|
|||
strncpy(runner->params.currentPath, lastPath, PATH_MAX - 1);
|
||||
runner->params.currentPath[PATH_MAX - 1] = '\0';
|
||||
}
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
if (!runner->autosave.running) {
|
||||
runner->autosave.running = true;
|
||||
MutexInit(&runner->autosave.mutex);
|
||||
ConditionInit(&runner->autosave.cond);
|
||||
ThreadCreate(&runner->autosave.thread, mGUIAutosaveThread, &runner->autosave);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void mGUIDeinit(struct mGUIRunner* runner) {
|
||||
#ifndef DISABLE_THREADING
|
||||
MutexLock(&runner->autosave.mutex);
|
||||
runner->autosave.running = false;
|
||||
ConditionWake(&runner->autosave.cond);
|
||||
MutexUnlock(&runner->autosave.mutex);
|
||||
|
||||
ThreadJoin(runner->autosave.thread);
|
||||
|
||||
ConditionDeinit(&runner->autosave.cond);
|
||||
MutexDeinit(&runner->autosave.mutex);
|
||||
|
||||
if (runner->autosave.buffer) {
|
||||
runner->autosave.buffer->close(runner->autosave.buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (runner->teardown) {
|
||||
runner->teardown(runner);
|
||||
}
|
||||
|
@ -239,25 +272,26 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Save state", .submenu = &stateSaveMenu };
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Load state", .submenu = &stateLoadMenu };
|
||||
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_1) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_2) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_3) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_4) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_5) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_6) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_7) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_8) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_9) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(1)) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(2)) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(3)) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(4)) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(5)) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(6)) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(7)) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(8)) };
|
||||
*GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(9)) };
|
||||
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_1) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_2) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_3) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_4) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_5) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_6) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_7) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_8) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_9) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "Autosave", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(0)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(1)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(2)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(3)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(4)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(5)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(6)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(7)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(8)) };
|
||||
*GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(9)) };
|
||||
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Take screenshot", .data = (void*) RUNNER_SCREENSHOT };
|
||||
*GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Configure", .data = (void*) RUNNER_CONFIG };
|
||||
|
@ -325,6 +359,13 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
}
|
||||
|
||||
bool running = true;
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
MutexLock(&runner->autosave.mutex);
|
||||
runner->autosave.core = runner->core;
|
||||
MutexUnlock(&runner->autosave.mutex);
|
||||
#endif
|
||||
|
||||
if (runner->gameLoaded) {
|
||||
runner->gameLoaded(runner);
|
||||
}
|
||||
|
@ -415,6 +456,9 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
runner->fps = (CircleBufferSize(&runner->fpsBuffer) * FPS_GRANULARITY * 1000000.0f) / (runner->totalDelta * sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
if (runner->core->frameCounter(runner->core) % AUTOSAVE_GRANULARITY == 0) {
|
||||
_tryAutosave(runner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -467,6 +511,11 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
if (runner->gameUnloaded) {
|
||||
runner->gameUnloaded(runner);
|
||||
}
|
||||
#ifndef DISABLE_THREADING
|
||||
MutexLock(&runner->autosave.mutex);
|
||||
runner->autosave.core = NULL;
|
||||
MutexUnlock(&runner->autosave.mutex);
|
||||
#endif
|
||||
mLOG(GUI_RUNNER, DEBUG, "Unloading game...");
|
||||
runner->core->unloadROM(runner->core);
|
||||
drawState.screenshotId = 0;
|
||||
|
@ -524,3 +573,21 @@ void mGUIRunloop(struct mGUIRunner* runner) {
|
|||
mGUIRun(runner, path);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
THREAD_ENTRY mGUIAutosaveThread(void* context) {
|
||||
struct mGUIAutosaveContext* autosave = context;
|
||||
MutexLock(&autosave->mutex);
|
||||
while (autosave->running) {
|
||||
ConditionWait(&autosave->cond, &autosave->mutex);
|
||||
if (autosave->running && autosave->core) {
|
||||
struct VFile* vf = mCoreGetState(autosave->core, 0, true);
|
||||
void* mem = autosave->buffer->map(autosave->buffer, autosave->buffer->size(autosave->buffer), MAP_READ);
|
||||
vf->write(vf, mem, autosave->buffer->size(autosave->buffer));
|
||||
autosave->buffer->unmap(autosave->buffer, mem, autosave->buffer->size(autosave->buffer));
|
||||
vf->close(vf);
|
||||
}
|
||||
}
|
||||
MutexUnlock(&autosave->mutex);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -15,6 +15,7 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/gba/hardware.h>
|
||||
#include <mgba-util/circle-buffer.h>
|
||||
#include <mgba-util/gui.h>
|
||||
#include <mgba-util/threading.h>
|
||||
|
||||
enum mGUIInput {
|
||||
mGUI_INPUT_INCREASE_BRIGHTNESS = GUI_INPUT_USER_START,
|
||||
|
@ -38,12 +39,27 @@ struct mGUIRunnerLux {
|
|||
int luxLevel;
|
||||
};
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
struct VFile;
|
||||
struct mGUIAutosaveContext {
|
||||
struct VFile* buffer;
|
||||
struct mCore* core;
|
||||
Thread thread;
|
||||
Mutex mutex;
|
||||
Condition cond;
|
||||
bool running;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct mGUIRunner {
|
||||
struct mCore* core;
|
||||
struct GUIParams params;
|
||||
|
||||
struct mGUIBackground background;
|
||||
struct mGUIRunnerLux luminanceSource;
|
||||
#ifndef DISABLE_THREADING
|
||||
struct mGUIAutosaveContext autosave;
|
||||
#endif
|
||||
|
||||
struct mInputMap guiKeys;
|
||||
struct mCoreConfig config;
|
||||
|
@ -78,6 +94,10 @@ void mGUIDeinit(struct mGUIRunner*);
|
|||
void mGUIRun(struct mGUIRunner*, const char* path);
|
||||
void mGUIRunloop(struct mGUIRunner*);
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
THREAD_ENTRY mGUIAutosaveThread(void* context);
|
||||
#endif
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -106,12 +106,6 @@ 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) {
|
||||
|
@ -201,33 +195,6 @@ static void _aptHook(APT_HookType hook, void* user) {
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
mInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key);
|
||||
}
|
||||
|
@ -460,10 +427,6 @@ static void _gameLoaded(struct mGUIRunner* runner) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
MutexLock(&autosaveMutex);
|
||||
autosaveCore = runner->core;
|
||||
MutexUnlock(&autosaveMutex);
|
||||
}
|
||||
|
||||
static void _gameUnloaded(struct mGUIRunner* runner) {
|
||||
|
@ -497,10 +460,6 @@ static void _gameUnloaded(struct mGUIRunner* runner) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
MutexLock(&autosaveMutex);
|
||||
autosaveCore = NULL;
|
||||
MutexUnlock(&autosaveMutex);
|
||||
}
|
||||
|
||||
static void _drawTex(struct mCore* core, bool faded) {
|
||||
|
@ -722,16 +681,7 @@ static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
|
|||
}
|
||||
|
||||
static bool _running(struct mGUIRunner* runner) {
|
||||
static int frame = 0;
|
||||
if (autosaveCore) {
|
||||
++frame;
|
||||
if (frame == 300) {
|
||||
_tryAutosave(autosaveCore);
|
||||
frame = 0;
|
||||
}
|
||||
} else {
|
||||
frame = 0;
|
||||
}
|
||||
UNUSED(runner);
|
||||
return aptMainLoop();
|
||||
}
|
||||
|
||||
|
@ -1081,6 +1031,13 @@ int main() {
|
|||
.running = _running
|
||||
};
|
||||
|
||||
runner.autosave.running = true;
|
||||
MutexInit(&runner.autosave.mutex);
|
||||
ConditionInit(&runner.autosave.cond);
|
||||
|
||||
APT_SetAppCpuTimeLimit(20);
|
||||
runner.autosave.thread = threadCreate(mGUIAutosaveThread, &runner.autosave, 0x4000, 0x1F, 1, true);
|
||||
|
||||
mGUIInit(&runner, "3ds");
|
||||
|
||||
_map3DSKey(&runner.params.keyMap, KEY_X, GUI_INPUT_CANCEL);
|
||||
|
@ -1094,29 +1051,7 @@ int main() {
|
|||
_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();
|
||||
|
|
Loading…
Reference in New Issue