Core: Savestates now contain any RTC override data
jump to
@@ -11,6 +11,7 @@ - Configuration of gamepad hats
- Qt: Spanish translation (by Kevin López) - Add option for whether rewinding restores save games - Qt: German translation (by Lothar Serra Mari) + - Savestates now contain any RTC override data Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior
@@ -37,7 +37,6 @@ enum mCoreChecksumType {
CHECKSUM_CRC32, }; -struct mRTCSource; struct mCoreConfig; struct mCoreSync; struct mStateExtdata;@@ -54,6 +53,8 @@ struct mInputMap inputMap;
#endif struct mCoreConfig config; struct mCoreOptions opts; + + struct mRTCGenericSource rtc; bool (*init)(struct mCore*); void (*deinit)(struct mCore*);@@ -109,7 +110,6 @@
void (*getGameTitle)(const struct mCore*, char* title); void (*getGameCode)(const struct mCore*, char* title); - void (*setRTC)(struct mCore*, struct mRTCSource*); void (*setRotation)(struct mCore*, struct mRotationSource*); void (*setRumble)(struct mCore*, struct mRumble*);@@ -167,6 +167,8 @@
void mCoreInitConfig(struct mCore* core, const char* port); void mCoreLoadConfig(struct mCore* core); void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config); + +void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc); CXX_GUARD_END
@@ -13,6 +13,7 @@
#include <mgba-util/vector.h> struct mCore; +struct mStateExtdataItem; #ifdef COLOR_16_BIT typedef uint16_t color_t;@@ -69,18 +70,29 @@ struct mRTCSource {
void (*sample)(struct mRTCSource*); time_t (*unixTime)(struct mRTCSource*); + + void (*serialize)(struct mRTCSource*, struct mStateExtdataItem*); + bool (*deserialize)(struct mRTCSource*, const struct mStateExtdataItem*); }; enum mRTCGenericType { RTC_NO_OVERRIDE, RTC_FIXED, - RTC_FAKE_EPOCH + RTC_FAKE_EPOCH, + RTC_CUSTOM_START = 0x1000 }; struct mRTCGenericSource { struct mRTCSource d; struct mCore* p; enum mRTCGenericType override; + int64_t value; + struct mRTCSource* custom; +}; + +struct mRTCGenericState { + int32_t type; + int32_t padding; int64_t value; };
@@ -15,12 +15,14 @@ EXTDATA_NONE = 0,
EXTDATA_SCREENSHOT = 1, EXTDATA_SAVEDATA = 2, EXTDATA_CHEATS = 3, + EXTDATA_RTC = 4, EXTDATA_MAX }; #define SAVESTATE_SCREENSHOT 1 #define SAVESTATE_SAVEDATA 2 #define SAVESTATE_CHEATS 4 +#define SAVESTATE_RTC 8 struct mStateExtdataItem { int32_t size;
@@ -233,3 +233,8 @@ core->setAudioBufferSize(core, core->opts.audioBuffers);
} core->loadConfig(core, config); } + +void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) { + core->rtc.custom = rtc; + core->rtc.override = RTC_CUSTOM_START; +}
@@ -6,26 +6,103 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/interface.h> #include <mgba/core/core.h> +#include <mgba/core/serialize.h> DEFINE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks); -static time_t _rtcGenericCallback(struct mRTCSource* source) { +static void _rtcGenericSample(struct mRTCSource* source) { struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; switch (rtc->override) { + default: + if (rtc->custom->sample) { + return rtc->custom->sample(rtc->custom); + } + break; case RTC_NO_OVERRIDE: + case RTC_FIXED: + case RTC_FAKE_EPOCH: + break; + } +} + +static time_t _rtcGenericCallback(struct mRTCSource* source) { + struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; + switch (rtc->override) { default: + if (rtc->custom->unixTime) { + return rtc->custom->unixTime(rtc->custom); + } + // Fall through + case RTC_NO_OVERRIDE: return time(0); case RTC_FIXED: - return rtc->value; + return rtc->value / 1000LL; case RTC_FAKE_EPOCH: - return rtc->value + rtc->p->frameCounter(rtc->p) * (int64_t) rtc->p->frameCycles(rtc->p) / rtc->p->frequency(rtc->p); + return (rtc->value + rtc->p->frameCounter(rtc->p) * (rtc->p->frameCycles(rtc->p) * 1000LL) / rtc->p->frequency(rtc->p)) / 1000LL; + } +} + +static void _rtcGenericSerialize(struct mRTCSource* source, struct mStateExtdataItem* item) { + struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; + struct mRTCGenericState state = { + .type = rtc->override, + .padding = 0, + .value = rtc->value + }; + void* data; + if (rtc->override >= RTC_CUSTOM_START && rtc->custom->serialize) { + rtc->custom->serialize(rtc->custom, item); + data = malloc(item->size + sizeof(state)); + uint8_t* oldData = data; + oldData += sizeof(state); + memcpy(oldData, item->data, item->size); + item->size += sizeof(state); + if (item->clean) { + item->clean(item->data); + } + } else { + item->size = sizeof(state); + data = malloc(item->size); } + memcpy(data, &state, sizeof(state)); + item->data = data; + item->clean = free; +} + +static bool _rtcGenericDeserialize(struct mRTCSource* source, const struct mStateExtdataItem* item) { + struct mRTCGenericSource* rtc = (struct mRTCGenericSource*) source; + struct mRTCGenericState* state = item->data; + if (!state || item->size < (ssize_t) sizeof(*state)) { + return false; + } + if (state->type >= RTC_CUSTOM_START) { + if (!rtc->custom) { + return false; + } + if (rtc->custom->deserialize) { + uint8_t* oldData = item->data; + oldData += sizeof(state); + struct mStateExtdataItem fakeItem = { + .size = item->size - sizeof(*state), + .data = oldData, + .clean = NULL + }; + if (!rtc->custom->deserialize(rtc->custom, &fakeItem)) { + return false; + } + } + } + rtc->value = state->value; + rtc->override = state->type; + return true; } void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) { rtc->p = core; rtc->override = RTC_NO_OVERRIDE; rtc->value = 0; - rtc->d.sample = 0; + rtc->d.sample = _rtcGenericSample; rtc->d.unixTime = _rtcGenericCallback; + rtc->d.serialize = _rtcGenericSerialize; + rtc->d.deserialize = _rtcGenericDeserialize; }
@@ -7,6 +7,7 @@ #include <mgba/core/serialize.h>
#include <mgba/core/core.h> #include <mgba/core/cheats.h> +#include <mgba/core/interface.h> #include <mgba-util/memory.h> #include <mgba-util/vfs.h>@@ -328,6 +329,14 @@ };
mStateExtdataPut(&extdata, EXTDATA_CHEATS, &item); } } + if (flags & SAVESTATE_RTC) { + mLOG(SAVESTATE, INFO, "Loading RTC"); + struct mStateExtdataItem item; + if (core->rtc.d.serialize) { + core->rtc.d.serialize(&core->rtc.d, &item); + mStateExtdataPut(&extdata, EXTDATA_RTC, &item); + } + } #ifdef USE_PNG if (!(flags & SAVESTATE_SCREENSHOT)) { #else@@ -423,6 +432,12 @@ mCheatDeviceClear(device);
mCheatParseFile(device, svf); svf->close(svf); } + } + } + if (flags & SAVESTATE_RTC && mStateExtdataGet(&extdata, EXTDATA_RTC, &item)) { + mLOG(SAVESTATE, INFO, "Loading RTC"); + if (core->rtc.d.deserialize) { + core->rtc.d.deserialize(&core->rtc.d, &item); } } mStateExtdataDeinit(&extdata);
@@ -434,7 +434,7 @@ case RUNNER_SAVE_STATE:
mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA); break; case RUNNER_LOAD_STATE: - mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT); + mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); break; case RUNNER_SCREENSHOT: mCoreTakeScreenshot(runner->core);
@@ -50,6 +50,8 @@ GBCreate(gb);
memset(gbcore->components, 0, sizeof(gbcore->components)); LR35902SetComponents(cpu, &gb->d, CPU_COMPONENT_MAX, gbcore->components); LR35902Init(cpu); + mRTCGenericSourceInit(&core->rtc, core); + gb->memory.rtc = &core->rtc.d; GBVideoSoftwareRendererCreate(&gbcore->renderer); gbcore->renderer.outputBuffer = NULL;@@ -389,11 +391,6 @@ static void _GBCoreGetGameCode(const struct mCore* core, char* title) {
GBGetGameCode(core->board, title); } -static void _GBCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) { - struct GB* gb = core->board; - gb->memory.rtc = rtc; -} - static void _GBCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) { struct GB* gb = core->board; gb->memory.rotation = rotation;@@ -606,7 +603,6 @@ core->frameCycles = _GBCoreFrameCycles;
core->frequency = _GBCoreFrequency; core->getGameTitle = _GBCoreGetGameTitle; core->getGameCode = _GBCoreGetGameCode; - core->setRTC = _GBCoreSetRTC; core->setRotation = _GBCoreSetRotation; core->setRumble = _GBCoreSetRumble; core->busRead8 = _GBCoreBusRead8;
@@ -101,7 +101,7 @@ }
struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system; - mCoreLoadState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); + mCoreLoadState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); } static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {@@ -118,5 +118,5 @@ }
struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system; - mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); + mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); }
@@ -58,6 +58,8 @@ // TODO: Restore cheats
memset(gbacore->components, 0, sizeof(gbacore->components)); ARMSetComponents(cpu, &gba->d, CPU_COMPONENT_MAX, gbacore->components); ARMInit(cpu); + mRTCGenericSourceInit(&core->rtc, core); + gba->rtcSource = &core->rtc.d; GBAVideoSoftwareRendererCreate(&gbacore->renderer); gbacore->renderer.outputBuffer = NULL;@@ -401,11 +403,6 @@ static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
GBAGetGameCode(core->board, title); } -static void _GBACoreSetRTC(struct mCore* core, struct mRTCSource* rtc) { - struct GBA* gba = core->board; - gba->rtcSource = rtc; -} - static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) { struct GBA* gba = core->board; gba->rotationSource = rotation;@@ -620,7 +617,6 @@ core->frameCycles = _GBACoreFrameCycles;
core->frequency = _GBACoreFrequency; core->getGameTitle = _GBACoreGetGameTitle; core->getGameCode = _GBACoreGetGameCode; - core->setRTC = _GBACoreSetRTC; core->setRotation = _GBACoreSetRotation; core->setRumble = _GBACoreSetRumble; core->busRead8 = _GBACoreBusRead8;
@@ -100,7 +100,7 @@ }
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; - mCoreLoadState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); + mCoreLoadState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); } // TODO: Put back rewind@@ -119,5 +119,5 @@ }
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; - mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT); + mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); }
@@ -70,8 +70,8 @@ , m_stream(nullptr)
, m_stateSlot(1) , m_backupLoadState(nullptr) , m_backupSaveState(nullptr) - , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS) - , m_loadStateFlags(SAVESTATE_SCREENSHOT) + , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC) + , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC) , m_override(nullptr) { #ifdef M_CORE_GBA@@ -90,8 +90,6 @@ #endif
m_threadContext.startCallback = [](mCoreThread* context) { GameController* controller = static_cast<GameController*>(context->userData); - mRTCGenericSourceInit(&controller->m_rtc, context->core); - context->core->setRTC(context->core, &controller->m_rtc.d); context->core->setRotation(context->core, controller->m_inputController->rotationSource()); context->core->setRumble(context->core, controller->m_inputController->rumble());@@ -1182,17 +1180,26 @@ setLuminanceValue(value);
} void GameController::setRealTime() { - m_rtc.override = RTC_NO_OVERRIDE; + if (!isLoaded()) { + return; + } + m_threadContext.core->rtc.override = RTC_NO_OVERRIDE; } void GameController::setFixedTime(const QDateTime& time) { - m_rtc.override = RTC_FIXED; - m_rtc.value = time.toMSecsSinceEpoch() / 1000; + if (!isLoaded()) { + return; + } + m_threadContext.core->rtc.override = RTC_FIXED; + m_threadContext.core->rtc.value = time.toMSecsSinceEpoch(); } void GameController::setFakeEpoch(const QDateTime& time) { - m_rtc.override = RTC_FAKE_EPOCH; - m_rtc.value = time.toMSecsSinceEpoch() / 1000; + if (!isLoaded()) { + return; + } + m_threadContext.core->rtc.override = RTC_FAKE_EPOCH; + m_threadContext.core->rtc.value = time.toMSecsSinceEpoch(); } void GameController::updateKeys() {
@@ -245,8 +245,6 @@ uint8_t value;
} m_lux; uint8_t m_luxValue; int m_luxLevel; - - mRTCGenericSource m_rtc; }; }
@@ -223,13 +223,13 @@ saveSetting("idleOptimization", "detect");
break; } - int loadState = 0; + int loadState = SAVESTATE_RTC; loadState |= m_ui.loadStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0; loadState |= m_ui.loadStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0; loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; saveSetting("loadStateExtdata", loadState); - int saveState = 0; + int saveState = SAVESTATE_RTC; saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0; saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0; saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;@@ -306,7 +306,7 @@
bool ok; int loadState = loadSetting("loadStateExtdata").toInt(&ok); if (!ok) { - loadState = SAVESTATE_SCREENSHOT; + loadState = SAVESTATE_SCREENSHOT | SAVESTATE_RTC; } m_ui.loadStateScreenshot->setChecked(loadState & SAVESTATE_SCREENSHOT); m_ui.loadStateSave->setChecked(loadState & SAVESTATE_SAVEDATA);@@ -314,7 +314,7 @@ m_ui.loadStateCheats->setChecked(loadState & SAVESTATE_CHEATS);
int saveState = loadSetting("saveStateExtdata").toInt(&ok); if (!ok) { - saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS; + saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC; } m_ui.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT); m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);