Core: Savestates now contain any RTC override data

This commit is contained in:
Vicki Pfau 2017-02-15 10:53:37 -08:00
parent a5b81ae9bf
commit ba3b068df4
15 changed files with 150 additions and 39 deletions

View File

@ -11,6 +11,7 @@ Features:
- 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

View File

@ -37,7 +37,6 @@ enum mCoreChecksumType {
CHECKSUM_CRC32,
};
struct mRTCSource;
struct mCoreConfig;
struct mCoreSync;
struct mStateExtdata;
@ -55,6 +54,8 @@ struct mCore {
struct mCoreConfig config;
struct mCoreOptions opts;
struct mRTCGenericSource rtc;
bool (*init)(struct mCore*);
void (*deinit)(struct mCore*);
@ -109,7 +110,6 @@ struct mCore {
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*);
@ -168,6 +168,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
#endif

View File

@ -13,6 +13,7 @@ CXX_GUARD_START
#include <mgba-util/vector.h>
struct mCore;
struct mStateExtdataItem;
#ifdef COLOR_16_BIT
typedef uint16_t color_t;
@ -69,12 +70,16 @@ 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 {
@ -82,6 +87,13 @@ struct mRTCGenericSource {
struct mCore* p;
enum mRTCGenericType override;
int64_t value;
struct mRTCSource* custom;
};
struct mRTCGenericState {
int32_t type;
int32_t padding;
int64_t value;
};
void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core);

View File

@ -15,12 +15,14 @@ enum mStateExtdataTag {
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;

View File

@ -233,3 +233,8 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config
}
core->loadConfig(core, config);
}
void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) {
core->rtc.custom = rtc;
core->rtc.override = RTC_CUSTOM_START;
}

View File

@ -6,26 +6,103 @@
#include <mgba/core/interface.h>
#include <mgba/core/core.h>
#include <mgba/core/serialize.h>
DEFINE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks);
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) {
case RTC_NO_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;
}

View File

@ -7,6 +7,7 @@
#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 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
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
@ -425,6 +434,12 @@ bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
}
}
}
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);
return success;
}

View File

@ -434,7 +434,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
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);

View File

@ -50,6 +50,8 @@ static bool _GBCoreInit(struct mCore* core) {
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 @@ struct mCore* GBCoreCreate(void) {
core->frequency = _GBCoreFrequency;
core->getGameTitle = _GBCoreGetGameTitle;
core->getGameCode = _GBCoreGetGameCode;
core->setRTC = _GBCoreSetRTC;
core->setRotation = _GBCoreSetRotation;
core->setRumble = _GBCoreSetRumble;
core->busRead8 = _GBCoreBusRead8;

View File

@ -101,7 +101,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
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 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system;
mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT);
mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
}

View File

@ -58,6 +58,8 @@ static bool _GBACoreInit(struct mCore* core) {
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 @@ struct mCore* GBACoreCreate(void) {
core->frequency = _GBACoreFrequency;
core->getGameTitle = _GBACoreGetGameTitle;
core->getGameCode = _GBACoreGetGameCode;
core->setRTC = _GBACoreSetRTC;
core->setRotation = _GBACoreSetRotation;
core->setRumble = _GBACoreSetRumble;
core->busRead8 = _GBACoreBusRead8;

View File

@ -100,7 +100,7 @@ static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
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 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT);
mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
}

View File

@ -70,8 +70,8 @@ GameController::GameController(QObject* parent)
, 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 @@ GameController::GameController(QObject* parent)
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 @@ void GameController::setLuminanceLevel(int level) {
}
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() {

View File

@ -245,8 +245,6 @@ private:
} m_lux;
uint8_t m_luxValue;
int m_luxLevel;
mRTCGenericSource m_rtc;
};
}

View File

@ -223,13 +223,13 @@ void SettingsView::updateConfig() {
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 @@ void SettingsView::reloadConfig() {
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 @@ void SettingsView::reloadConfig() {
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);