|
|
@@ -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(&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(&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 = "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
|
0 comments on commit
45c2fdf