all repos — mgba @ 63e1875f6bdae4254992b6bcf692532d479cbdfc

mGBA Game Boy Advance Emulator

GBA: Add savestte creation time to a savestate
Jeffrey Pfau jeffrey@endrift.com
Mon, 28 Dec 2015 04:27:30 -0500
commit

63e1875f6bdae4254992b6bcf692532d479cbdfc

parent

5c007289e40d7203c8bc5053de1f7a60090709c5

M src/gba/serialize.csrc/gba/serialize.c

@@ -15,6 +15,7 @@ #include "util/memory.h"

#include "util/vfs.h" #include <fcntl.h> +#include <sys/time.h> #ifdef USE_PNG #include "util/png-io.h"

@@ -23,10 +24,6 @@ #include <zlib.h>

#endif const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; - -struct GBAExtdata { - struct GBAExtdataItem data[EXTDATA_MAX]; -}; struct GBABundledState { struct GBASerializedState* state;

@@ -78,6 +75,14 @@ GBAVideoSerialize(&gba->video, state);

GBAAudioSerialize(&gba->audio, state); GBASavedataSerialize(&gba->memory.savedata, state); + 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 { + state->creationUsec = 0; + } state->associatedStreamId = 0; if (gba->rr) { gba->rr->stateSaved(gba->rr, state);

@@ -324,7 +329,7 @@ success = success && PNGReadPixels(png, info, pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS);

success = success && PNGReadFooter(png, end); PNGReadClose(png, info, end); - if (success && extdata) { + if (success) { struct GBAExtdataItem item = { .size = VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4, .data = pixels,
M src/gba/serialize.hsrc/gba/serialize.h

@@ -174,7 +174,9 @@ * | 0x002F4 - 0x002F7: GBA BIOS bus prefetch

* | 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 - 0x003FF: Reserved (leave zero) + * 0x00304 - 0x0030F: Reserved (leave zero) + * 0x00310 - 0x00317: Savestate creation time (usec since 1970) + * 0x00318 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM

@@ -319,8 +321,11 @@ uint32_t biosPrefetch;

uint32_t cpuPrefetch[2]; uint32_t associatedStreamId; + uint32_t reservedRr[3]; + + uint64_t creationUsec; - uint32_t reserved[63]; + uint32_t reserved[58]; uint16_t io[SIZE_IO >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1];

@@ -340,11 +345,14 @@

#define SAVESTATE_SCREENSHOT 1 #define SAVESTATE_SAVEDATA 2 -struct GBAExtdata; struct GBAExtdataItem { int32_t size; void* data; void (*clean)(void*); +}; + +struct GBAExtdata { + struct GBAExtdataItem data[EXTDATA_MAX]; }; struct VDir;
M src/platform/qt/LoadSaveState.cppsrc/platform/qt/LoadSaveState.cpp

@@ -10,6 +10,7 @@ #include "GamepadAxisEvent.h"

#include "GamepadButtonEvent.h" #include "VFileDevice.h" +#include <QDateTime> #include <QKeyEvent> #include <QPainter>

@@ -174,17 +175,39 @@ if (!vf) {

m_slots[slot - 1]->setText(tr("Empty")); return; } - VFileDevice vdev(vf); + + GBAExtdata extdata; + GBAExtdataInit(&extdata); + GBASerializedState* state = GBAExtractState(vf, &extdata); + vf->seek(vf, 0, SEEK_SET); + if (!state) { + m_slots[slot - 1]->setText(tr("Corrupted")); + GBAExtdataDeinit(&extdata); + return; + } + + QDateTime creation(QDateTime::fromMSecsSinceEpoch(state->creationUsec / 1000LL)); QImage stateImage; - stateImage.load(&vdev, "PNG"); + + GBAExtdataItem item; + if (GBAExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item) && item.size >= VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4) { + stateImage = QImage((uchar*) item.data, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, QImage::Format_ARGB32).rgbSwapped(); + } + if (!stateImage.isNull()) { QPixmap statePixmap; statePixmap.convertFromImage(stateImage); m_slots[slot - 1]->setIcon(statePixmap); - m_slots[slot - 1]->setText(QString()); + } + if (creation.toMSecsSinceEpoch()) { + m_slots[slot - 1]->setText(creation.toString(Qt::DefaultLocaleShortDate)); + } else if (stateImage.isNull()) { + m_slots[slot - 1]->setText(tr("Slot %1").arg(slot)); } else { - m_slots[slot - 1]->setText(tr("Slot %1").arg(slot)); + m_slots[slot - 1]->setText(QString()); } + vf->close(vf); + GBADeallocateState(state); } void LoadSaveState::triggerState(int slot) {
M src/platform/qt/SavestateButton.cppsrc/platform/qt/SavestateButton.cpp

@@ -42,5 +42,13 @@ highlight.setAlpha(128);

painter.fillRect(full, highlight); } painter.setPen(QPen(palette.text(), 0)); - painter.drawText(full, Qt::AlignCenter, text()); + if (icon().isNull()) { + painter.drawText(full, Qt::AlignCenter, text()); + } else { + if (!hasFocus()) { + painter.setPen(QPen(palette.light(), 0)); + painter.setCompositionMode(QPainter::CompositionMode_Exclusion); + } + painter.drawText(full, Qt::AlignHCenter | Qt::AlignBottom, text()); + } }
M src/util/png-io.csrc/util/png-io.c

@@ -186,10 +186,12 @@ #if __BIG_ENDIAN__

pixelData[stride * i * 4 + x * 4 + 3] = row[x * 3]; pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 1]; pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 2]; + pixelData[stride * i * 4 + x * 4] = 0xFF; #else pixelData[stride * i * 4 + x * 4] = row[x * 3]; pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1]; pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2]; + pixelData[stride * i * 4 + x * 4 + 3] = 0xFF; #endif } }