mirror of https://github.com/mgba-emu/mgba.git
GBA: Add savestte creation time to a savestate
This commit is contained in:
parent
5c007289e4
commit
63e1875f6b
|
@ -15,6 +15,7 @@
|
|||
#include "util/vfs.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef USE_PNG
|
||||
#include "util/png-io.h"
|
||||
|
@ -24,10 +25,6 @@
|
|||
|
||||
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
|
||||
|
||||
struct GBAExtdata {
|
||||
struct GBAExtdataItem data[EXTDATA_MAX];
|
||||
};
|
||||
|
||||
struct GBABundledState {
|
||||
struct GBASerializedState* state;
|
||||
struct GBAExtdata* extdata;
|
||||
|
@ -78,6 +75,14 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* 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 @@ static struct GBASerializedState* _loadPNGState(struct VFile* vf, struct GBAExtd
|
|||
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,
|
||||
|
|
|
@ -174,7 +174,9 @@ extern const uint32_t GBA_SAVESTATE_MAGIC;
|
|||
* | 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 @@ struct GBASerializedState {
|
|||
uint32_t cpuPrefetch[2];
|
||||
|
||||
uint32_t associatedStreamId;
|
||||
uint32_t reservedRr[3];
|
||||
|
||||
uint32_t reserved[63];
|
||||
uint64_t creationUsec;
|
||||
|
||||
uint32_t reserved[58];
|
||||
|
||||
uint16_t io[SIZE_IO >> 1];
|
||||
uint16_t pram[SIZE_PALETTE_RAM >> 1];
|
||||
|
@ -340,13 +345,16 @@ enum GBAExtdataTag {
|
|||
#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;
|
||||
struct GBAThread;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "GamepadButtonEvent.h"
|
||||
#include "VFileDevice.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QKeyEvent>
|
||||
#include <QPainter>
|
||||
|
||||
|
@ -174,17 +175,39 @@ void LoadSaveState::loadState(int slot) {
|
|||
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());
|
||||
} else {
|
||||
m_slots[slot - 1]->setText(tr("Slot %1").arg(slot));
|
||||
}
|
||||
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(QString());
|
||||
}
|
||||
vf->close(vf);
|
||||
GBADeallocateState(state);
|
||||
}
|
||||
|
||||
void LoadSaveState::triggerState(int slot) {
|
||||
|
|
|
@ -42,5 +42,13 @@ void SavestateButton::paintEvent(QPaintEvent*) {
|
|||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,10 +186,12 @@ bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width
|
|||
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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue