GBA: Add savestte creation time to a savestate

This commit is contained in:
Jeffrey Pfau 2015-12-28 04:27:30 -05:00
parent 5c007289e4
commit 63e1875f6b
5 changed files with 60 additions and 14 deletions

View File

@ -15,6 +15,7 @@
#include "util/vfs.h" #include "util/vfs.h"
#include <fcntl.h> #include <fcntl.h>
#include <sys/time.h>
#ifdef USE_PNG #ifdef USE_PNG
#include "util/png-io.h" #include "util/png-io.h"
@ -24,10 +25,6 @@
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
struct GBAExtdata {
struct GBAExtdataItem data[EXTDATA_MAX];
};
struct GBABundledState { struct GBABundledState {
struct GBASerializedState* state; struct GBASerializedState* state;
struct GBAExtdata* extdata; struct GBAExtdata* extdata;
@ -78,6 +75,14 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
GBAAudioSerialize(&gba->audio, state); GBAAudioSerialize(&gba->audio, state);
GBASavedataSerialize(&gba->memory.savedata, 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; state->associatedStreamId = 0;
if (gba->rr) { if (gba->rr) {
gba->rr->stateSaved(gba->rr, state); gba->rr->stateSaved(gba->rr, state);
@ -324,7 +329,7 @@ static struct GBASerializedState* _loadPNGState(struct VFile* vf, struct GBAExtd
success = success && PNGReadFooter(png, end); success = success && PNGReadFooter(png, end);
PNGReadClose(png, info, end); PNGReadClose(png, info, end);
if (success && extdata) { if (success) {
struct GBAExtdataItem item = { struct GBAExtdataItem item = {
.size = VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4, .size = VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4,
.data = pixels, .data = pixels,

View File

@ -174,7 +174,9 @@ extern const uint32_t GBA_SAVESTATE_MAGIC;
* | 0x002F8 - 0x002FB: CPU prefecth (decode slot) * | 0x002F8 - 0x002FB: CPU prefecth (decode slot)
* | 0x002FC - 0x002FF: CPU prefetch (fetch slot) * | 0x002FC - 0x002FF: CPU prefetch (fetch slot)
* 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream) * 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 * 0x00400 - 0x007FF: I/O memory
* 0x00800 - 0x00BFF: Palette * 0x00800 - 0x00BFF: Palette
* 0x00C00 - 0x00FFF: OAM * 0x00C00 - 0x00FFF: OAM
@ -319,8 +321,11 @@ struct GBASerializedState {
uint32_t cpuPrefetch[2]; uint32_t cpuPrefetch[2];
uint32_t associatedStreamId; 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 io[SIZE_IO >> 1];
uint16_t pram[SIZE_PALETTE_RAM >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1];
@ -340,13 +345,16 @@ enum GBAExtdataTag {
#define SAVESTATE_SCREENSHOT 1 #define SAVESTATE_SCREENSHOT 1
#define SAVESTATE_SAVEDATA 2 #define SAVESTATE_SAVEDATA 2
struct GBAExtdata;
struct GBAExtdataItem { struct GBAExtdataItem {
int32_t size; int32_t size;
void* data; void* data;
void (*clean)(void*); void (*clean)(void*);
}; };
struct GBAExtdata {
struct GBAExtdataItem data[EXTDATA_MAX];
};
struct VDir; struct VDir;
struct GBAThread; struct GBAThread;

View File

@ -10,6 +10,7 @@
#include "GamepadButtonEvent.h" #include "GamepadButtonEvent.h"
#include "VFileDevice.h" #include "VFileDevice.h"
#include <QDateTime>
#include <QKeyEvent> #include <QKeyEvent>
#include <QPainter> #include <QPainter>
@ -174,17 +175,39 @@ void LoadSaveState::loadState(int slot) {
m_slots[slot - 1]->setText(tr("Empty")); m_slots[slot - 1]->setText(tr("Empty"));
return; 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; 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()) { if (!stateImage.isNull()) {
QPixmap statePixmap; QPixmap statePixmap;
statePixmap.convertFromImage(stateImage); statePixmap.convertFromImage(stateImage);
m_slots[slot - 1]->setIcon(statePixmap); 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) { void LoadSaveState::triggerState(int slot) {

View File

@ -42,5 +42,13 @@ void SavestateButton::paintEvent(QPaintEvent*) {
painter.fillRect(full, highlight); painter.fillRect(full, highlight);
} }
painter.setPen(QPen(palette.text(), 0)); 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());
}
} }

View File

@ -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 + 3] = row[x * 3];
pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 1]; 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 + 1] = row[x * 3 + 2];
pixelData[stride * i * 4 + x * 4] = 0xFF;
#else #else
pixelData[stride * i * 4 + x * 4] = row[x * 3]; 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 + 1] = row[x * 3 + 1];
pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2]; pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2];
pixelData[stride * i * 4 + x * 4 + 3] = 0xFF;
#endif #endif
} }
} }