mirror of https://github.com/mgba-emu/mgba.git
Core: Savestates now contain any RTC override data
This commit is contained in:
parent
a5b81ae9bf
commit
ba3b068df4
1
CHANGES
1
CHANGES
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -245,8 +245,6 @@ private:
|
|||
} m_lux;
|
||||
uint8_t m_luxValue;
|
||||
int m_luxLevel;
|
||||
|
||||
mRTCGenericSource m_rtc;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue