Qt: Add CoreManager::getExtdata API

This commit is contained in:
Vicki Pfau 2020-08-25 20:22:41 -07:00
parent 6c805acab6
commit 3dcf879e94
4 changed files with 126 additions and 4 deletions

View File

@ -36,7 +36,7 @@ struct mStateExtdata {
struct mStateExtdataItem data[EXTDATA_MAX]; struct mStateExtdataItem data[EXTDATA_MAX];
}; };
bool mStateExtdataInit(struct mStateExtdata*); void mStateExtdataInit(struct mStateExtdata*);
void mStateExtdataDeinit(struct mStateExtdata*); void mStateExtdataDeinit(struct mStateExtdata*);
void mStateExtdataPut(struct mStateExtdata*, enum mStateExtdataTag, struct mStateExtdataItem*); void mStateExtdataPut(struct mStateExtdata*, enum mStateExtdataTag, struct mStateExtdataItem*);
bool mStateExtdataGet(struct mStateExtdata*, enum mStateExtdataTag, struct mStateExtdataItem*); bool mStateExtdataGet(struct mStateExtdata*, enum mStateExtdataTag, struct mStateExtdataItem*);
@ -49,6 +49,7 @@ struct mCore;
bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags); bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags);
bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags); bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags);
void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata); void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata);
bool mCoreExtractExtdata(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata);
CXX_GUARD_END CXX_GUARD_END

View File

@ -31,9 +31,8 @@ struct mStateExtdataHeader {
int64_t offset; int64_t offset;
}; };
bool mStateExtdataInit(struct mStateExtdata* extdata) { void mStateExtdataInit(struct mStateExtdata* extdata) {
memset(extdata->data, 0, sizeof(extdata->data)); memset(extdata->data, 0, sizeof(extdata->data));
return true;
} }
void mStateExtdataDeinit(struct mStateExtdata* extdata) { void mStateExtdataDeinit(struct mStateExtdata* extdata) {
@ -43,6 +42,7 @@ void mStateExtdataDeinit(struct mStateExtdata* extdata) {
extdata->data[i].clean(extdata->data[i].data); extdata->data[i].clean(extdata->data[i].data);
} }
} }
memset(extdata->data, 0, sizeof(extdata->data));
} }
void mStateExtdataPut(struct mStateExtdata* extdata, enum mStateExtdataTag tag, struct mStateExtdataItem* item) { void mStateExtdataPut(struct mStateExtdata* extdata, enum mStateExtdataTag tag, struct mStateExtdataItem* item) {
@ -216,7 +216,7 @@ static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) {
if (!strcmp((const char*) chunk->name, "gbAs")) { if (!strcmp((const char*) chunk->name, "gbAs")) {
void* state = bundle->state; void* state = bundle->state;
if (!state) { if (!state) {
return 0; return 1;
} }
uLongf len = bundle->stateSize; uLongf len = bundle->stateSize;
uncompress((Bytef*) state, &len, chunk->data, chunk->size); uncompress((Bytef*) state, &len, chunk->data, chunk->size);
@ -297,6 +297,54 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx
} }
return state; return state;
} }
static bool _loadPNGExtadata(struct VFile* vf, struct mStateExtdata* extdata) {
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
png_infop info = png_create_info_struct(png);
png_infop end = png_create_info_struct(png);
if (!png || !info || !end) {
PNGReadClose(png, info, end);
return false;
}
struct mBundledState bundle = {
.stateSize = 0,
.state = NULL,
.extdata = extdata
};
PNGInstallChunkHandler(png, &bundle, _loadPNGChunkHandler, "gbAs gbAx");
bool success = PNGReadHeader(png, info);
if (!success) {
PNGReadClose(png, info, end);
return false;
}
unsigned width = png_get_image_width(png, info);
unsigned height = png_get_image_height(png, info);
uint32_t* pixels = NULL;
pixels = malloc(width * height * 4);
if (!pixels) {
PNGReadClose(png, info, end);
return false;
}
success = PNGReadPixels(png, info, pixels, width, height, width);
success = success && PNGReadFooter(png, end);
PNGReadClose(png, info, end);
if (success) {
struct mStateExtdataItem item = {
.size = width * height * 4,
.data = pixels,
.clean = free
};
mStateExtdataPut(extdata, EXTDATA_SCREENSHOT, &item);
} else {
free(pixels);
return false;
}
return true;
}
#endif #endif
bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) { bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
@ -425,6 +473,20 @@ void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtda
return state; return state;
} }
bool mCoreExtractExtdata(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata) {
#ifdef USE_PNG
if (isPNG(vf)) {
return _loadPNGExtadata(vf, extdata);
}
#endif
if (!core) {
return false;
}
ssize_t stateSize = core->stateSize(core);
vf->seek(vf, stateSize, SEEK_SET);
return mStateExtdataDeserialize(extdata, vf);
}
bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) { bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
struct mStateExtdata extdata; struct mStateExtdata extdata;
mStateExtdataInit(&extdata); mStateExtdataInit(&extdata);

View File

@ -7,12 +7,16 @@
#include "CoreController.h" #include "CoreController.h"
#include "LogController.h" #include "LogController.h"
#include "VFileDevice.h"
#include <QDir> #include <QDir>
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
#include <mgba/gba/core.h> #include <mgba/gba/core.h>
#endif #endif
#ifdef M_CORE_GB
#include <mgba/gb/core.h>
#endif
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -27,6 +31,57 @@ void CoreManager::setMultiplayerController(MultiplayerController* multiplayer) {
m_multiplayer = multiplayer; m_multiplayer = multiplayer;
} }
QByteArray CoreManager::getExtdata(const QString& filename, mStateExtdataTag extdataType) {
VFileDevice vf(filename, QIODevice::ReadOnly);
if (!vf.isOpen()) {
return {};
}
mStateExtdata extdata;
mStateExtdataInit(&extdata);
QByteArray bytes;
auto extract = [&bytes, &extdata, &vf, extdataType](mCore* core) -> bool {
if (mCoreExtractExtdata(core, vf, &extdata)) {
mStateExtdataItem extitem;
if (!mStateExtdataGet(&extdata, extdataType, &extitem)) {
return false;
}
if (extitem.size) {
bytes = QByteArray::fromRawData(static_cast<const char*>(extitem.data), extitem.size);
}
return true;
}
return false;
};
bool done = false;
struct mCore* core = nullptr;
#ifdef USE_PNG
done = extract(nullptr);
#endif
#ifdef M_CORE_GBA
if (!done) {
core = GBACoreCreate();
core->init(core);
done = extract(core);
core->deinit(core);
}
#endif
#ifdef M_CORE_GB
if (!done) {
core = GBCoreCreate();
core->init(core);
done = extract(core);
core->deinit(core);
}
#endif
mStateExtdataDeinit(&extdata);
return bytes;
}
CoreController* CoreManager::loadGame(const QString& path) { CoreController* CoreManager::loadGame(const QString& path) {
QFileInfo info(path); QFileInfo info(path);
if (!info.isReadable()) { if (!info.isReadable()) {

View File

@ -9,6 +9,8 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <mgba/core/serialize.h>
struct mCoreConfig; struct mCoreConfig;
struct VFile; struct VFile;
@ -25,6 +27,8 @@ public:
void setMultiplayerController(MultiplayerController*); void setMultiplayerController(MultiplayerController*);
void setPreload(bool preload) { m_preload = preload; } void setPreload(bool preload) { m_preload = preload; }
static QByteArray getExtdata(const QString& filename, mStateExtdataTag extdataType);
public slots: public slots:
CoreController* loadGame(const QString& path); CoreController* loadGame(const QString& path);
CoreController* loadGame(VFile* vf, const QString& path, const QString& base); CoreController* loadGame(VFile* vf, const QString& path, const QString& base);