Qt: Add CoreManager::getExtdata API
Vicki Pfau vi@endrift.com
Tue, 25 Aug 2020 20:22:41 -0700
4 files changed,
126 insertions(+),
4 deletions(-)
M
include/mgba/core/serialize.h
→
include/mgba/core/serialize.h
@@ -36,7 +36,7 @@ struct mStateExtdata {
struct mStateExtdataItem data[EXTDATA_MAX]; }; -bool mStateExtdataInit(struct mStateExtdata*); +void mStateExtdataInit(struct mStateExtdata*); void mStateExtdataDeinit(struct mStateExtdata*); void mStateExtdataPut(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 mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags); 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
M
src/core/serialize.c
→
src/core/serialize.c
@@ -31,9 +31,8 @@ int32_t size;
int64_t offset; }; -bool mStateExtdataInit(struct mStateExtdata* extdata) { +void mStateExtdataInit(struct mStateExtdata* extdata) { memset(extdata->data, 0, sizeof(extdata->data)); - return true; } void mStateExtdataDeinit(struct mStateExtdata* extdata) {@@ -43,6 +42,7 @@ if (extdata->data[i].data && extdata->data[i].clean) {
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) {@@ -216,7 +216,7 @@ }
if (!strcmp((const char*) chunk->name, "gbAs")) { void* state = bundle->state; if (!state) { - return 0; + return 1; } uLongf len = bundle->stateSize; uncompress((Bytef*) state, &len, chunk->data, chunk->size);@@ -297,6 +297,54 @@ return 0;
} 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 bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {@@ -423,6 +471,20 @@ if (extdata) {
mStateExtdataDeserialize(extdata, vf); } 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) {
M
src/platform/qt/CoreManager.cpp
→
src/platform/qt/CoreManager.cpp
@@ -7,11 +7,15 @@ #include "CoreManager.h"
#include "CoreController.h" #include "LogController.h" +#include "VFileDevice.h" #include <QDir> #ifdef M_CORE_GBA #include <mgba/gba/core.h> +#endif +#ifdef M_CORE_GB +#include <mgba/gb/core.h> #endif #include <mgba/core/core.h>@@ -25,6 +29,57 @@ }
void CoreManager::setMultiplayerController(MultiplayerController* 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) {
M
src/platform/qt/CoreManager.h
→
src/platform/qt/CoreManager.h
@@ -9,6 +9,8 @@ #include <QFileInfo>
#include <QObject> #include <QString> +#include <mgba/core/serialize.h> + struct mCoreConfig; struct VFile;@@ -24,6 +26,8 @@ public:
void setConfig(const mCoreConfig*); void setMultiplayerController(MultiplayerController*); void setPreload(bool preload) { m_preload = preload; } + + static QByteArray getExtdata(const QString& filename, mStateExtdataTag extdataType); public slots: CoreController* loadGame(const QString& path);