diff --git a/CHANGES b/CHANGES index 699967c61..e3feb1f10 100644 --- a/CHANGES +++ b/CHANGES @@ -126,6 +126,7 @@ Misc: - Qt: Relax hard dependency on OpenGL - GB Video: Improved video timings - Core: List memory segments in the core + - Core: Move savestate creation time to extdata 0.5.2: (2016-12-31) Bugfixes: diff --git a/include/mgba/core/serialize.h b/include/mgba/core/serialize.h index 0951fc4c3..afbf8de54 100644 --- a/include/mgba/core/serialize.h +++ b/include/mgba/core/serialize.h @@ -16,6 +16,7 @@ enum mStateExtdataTag { EXTDATA_SAVEDATA = 2, EXTDATA_CHEATS = 3, EXTDATA_RTC = 4, + EXTDATA_META_TIME = 0x101, EXTDATA_MAX }; @@ -23,6 +24,7 @@ enum mStateExtdataTag { #define SAVESTATE_SAVEDATA 2 #define SAVESTATE_CHEATS 4 #define SAVESTATE_RTC 8 +#define SAVESTATE_METADATA 16 struct mStateExtdataItem { int32_t size; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index e370c997b..c55f97600 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -154,8 +154,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | bit 4: Is HDMA active? * | bits 5 - 7: Active RTC register * | 0x00196 - 0x00197: Reserved (leave zero) - * 0x00198 - 0x0019F: Savestate creation time (usec since 1970) - * 0x001A0 - 0x0025F: Reserved (leave zero) + * 0x00198 - 0x0025F: Reserved (leave zero) * 0x00260 - 0x002FF: OAM * 0x00300 - 0x0037F: I/O memory * 0x00380 - 0x003FE: HRAM @@ -354,9 +353,7 @@ struct GBSerializedState { uint16_t reserved; } memory; - uint64_t creationUsec; - - uint32_t reserved[48]; + uint32_t reserved[50]; uint8_t oam[GB_SIZE_OAM]; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 14bddf6e2..4f24198dd 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -190,8 +190,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | 0x002F8 - 0x002FB: CPU prefecth (decode slot) * | 0x002FC - 0x002FF: CPU prefetch (fetch slot) * 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream) - * 0x00304 - 0x0030F: Reserved (leave zero) - * 0x00310 - 0x00317: Savestate creation time (usec since 1970) + * 0x00304 - 0x00317: Savestate creation time (usec since 1970) * 0x00318 - 0x0031B: Last prefetched program counter * 0x0031C - 0x0031F: Miscellaneous flags * | bit 0: Is CPU halted? @@ -312,9 +311,7 @@ struct GBASerializedState { uint32_t cpuPrefetch[2]; uint32_t associatedStreamId; - uint32_t reservedRr[3]; - - uint64_t creationUsec; + uint32_t reservedRr[5]; uint32_t lastPrefetchedPc; GBASerializedMiscFlags miscFlags; diff --git a/src/core/serialize.c b/src/core/serialize.c index bd6abd112..9efca53f7 100644 --- a/src/core/serialize.c +++ b/src/core/serialize.c @@ -303,6 +303,36 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) { struct mStateExtdata extdata; mStateExtdataInit(&extdata); size_t stateSize = core->stateSize(core); + + if (flags & SAVESTATE_METADATA) { + uint64_t creationUsec; +#ifndef _MSC_VER + struct timeval tv; + if (!gettimeofday(&tv, 0)) { + uint64_t usec = tv.tv_usec; + usec += tv.tv_sec * 1000000LL; + STORE_64LE(usec, 0, &creationUsec); + } +#else + struct timespec ts; + if (timespec_get(&ts, TIME_UTC)) { + uint64_t usec = ts.tv_nsec / 1000; + usec += ts.tv_sec * 1000000LL; + STORE_64LE(usec, 0, &creationUsec); + } +#endif + else { + creationUsec = 0; + } + + struct mStateExtdataItem item = { + .size = sizeof(creationUsec), + .data = &creationUsec, + .clean = NULL + }; + mStateExtdataPut(&extdata, EXTDATA_META_TIME, &item); + } + if (flags & SAVESTATE_SAVEDATA) { void* sram = NULL; size_t size = core->savedataClone(core, &sram); diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index ad59ae893..0f997f395 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -431,7 +431,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { runner->core->reset(runner->core); break; case RUNNER_SAVE_STATE: - mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA); + mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA); break; case RUNNER_LOAD_STATE: mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); diff --git a/src/gb/debugger/cli.c b/src/gb/debugger/cli.c index 809961290..2f59293d1 100644 --- a/src/gb/debugger/cli.c +++ b/src/gb/debugger/cli.c @@ -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 | SAVESTATE_RTC); + mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC | SAVESTATE_METADATA); } diff --git a/src/gb/serialize.c b/src/gb/serialize.c index 1cacc4224..9eab0fea2 100644 --- a/src/gb/serialize.c +++ b/src/gb/serialize.c @@ -59,25 +59,6 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) { GBVideoSerialize(&gb->video, state); GBTimerSerialize(&gb->timer, state); GBAudioSerialize(&gb->audio, state); - -#ifndef _MSC_VER - struct timeval tv; - if (!gettimeofday(&tv, 0)) { - uint64_t usec = tv.tv_usec; - usec += tv.tv_sec * 1000000LL; - STORE_64LE(usec, 0, &state->creationUsec); - } -#else - struct timespec ts; - if (timespec_get(&ts, TIME_UTC)) { - uint64_t usec = ts.tv_nsec / 1000; - usec += ts.tv_sec * 1000000LL; - STORE_64LE(usec, 0, &state->creationUsec); - } -#endif - else { - state->creationUsec = 0; - } } bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { diff --git a/src/gba/debugger/cli.c b/src/gba/debugger/cli.c index 44ae46917..4208d9021 100644 --- a/src/gba/debugger/cli.c +++ b/src/gba/debugger/cli.c @@ -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 | SAVESTATE_RTC); + mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC | SAVESTATE_METADATA); } diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 663fcc44c..5917fa5f1 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -69,25 +69,6 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { GBAAudioSerialize(&gba->audio, state); GBASavedataSerialize(&gba->memory.savedata, state); - -#ifndef _MSC_VER - struct timeval tv; - if (!gettimeofday(&tv, 0)) { - uint64_t usec = tv.tv_usec; - usec += tv.tv_sec * 1000000LL; - STORE_64(usec, 0, &state->creationUsec); - } -#else - struct timespec ts; - if (timespec_get(&ts, TIME_UTC)) { - uint64_t usec = ts.tv_nsec / 1000; - usec += ts.tv_sec * 1000000LL; - STORE_64(usec, 0, &state->creationUsec); - } -#endif - else { - state->creationUsec = 0; - } state->associatedStreamId = 0; if (gba->rr) { gba->rr->stateSaved(gba->rr, state); diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 0b48ac302..f42f24f19 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -40,7 +40,7 @@ using namespace std; GameController::GameController(QObject* parent) : QObject(parent) , m_audioProcessor(AudioProcessor::create()) - , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC) + , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC | SAVESTATE_METADATA) , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC) { #ifdef M_CORE_GBA diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index 40777000f..a381c3d22 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -188,7 +188,7 @@ void LoadSaveState::loadState(int slot) { return; } - QDateTime creation/*(QDateTime::fromMSecsSinceEpoch(state->creationUsec / 1000LL))*/; // TODO + QDateTime creation; QImage stateImage; unsigned width, height; @@ -198,6 +198,12 @@ void LoadSaveState::loadState(int slot) { stateImage = QImage((uchar*) item.data, width, height, QImage::Format_ARGB32).rgbSwapped(); } + if (mStateExtdataGet(&extdata, EXTDATA_META_TIME, &item) && item.size == sizeof(uint64_t)) { + uint64_t creationUsec; + LOAD_64LE(creationUsec, 0, item.data); + creation = QDateTime::fromMSecsSinceEpoch(creationUsec / 1000LL); + } + if (!stateImage.isNull()) { QPixmap statePixmap; statePixmap.convertFromImage(stateImage); diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 953be8d73..40afc6fc2 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -232,7 +232,7 @@ void SettingsView::updateConfig() { loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; saveSetting("loadStateExtdata", loadState); - int saveState = SAVESTATE_RTC; + int saveState = SAVESTATE_RTC | SAVESTATE_METADATA; saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0; saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0; saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; @@ -319,7 +319,7 @@ void SettingsView::reloadConfig() { int saveState = loadSetting("saveStateExtdata").toInt(&ok); if (!ok) { - saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC; + saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC | SAVESTATE_METADATA; } m_ui.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT); m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);