mirror of https://github.com/mgba-emu/mgba.git
Core: Back mLibraries with a shared database
This commit is contained in:
parent
c11551a1f7
commit
ac2097f0b6
|
@ -14,27 +14,34 @@ CXX_GUARD_START
|
|||
#include <mgba-util/vector.h>
|
||||
|
||||
struct mLibraryEntry {
|
||||
char* filename;
|
||||
char* title;
|
||||
const char* base;
|
||||
const char* filename;
|
||||
const char* title;
|
||||
char internalTitle[17];
|
||||
char internalCode[9];
|
||||
size_t filesize;
|
||||
enum mPlatform platform;
|
||||
size_t filesize;
|
||||
uint32_t crc32;
|
||||
};
|
||||
|
||||
#ifdef USE_SQLITE3
|
||||
|
||||
DECLARE_VECTOR(mLibraryListing, struct mLibraryEntry);
|
||||
|
||||
struct mLibrary {
|
||||
struct mLibraryListing listing;
|
||||
};
|
||||
|
||||
void mLibraryInit(struct mLibrary*);
|
||||
void mLibraryDeinit(struct mLibrary*);
|
||||
struct mLibrary;
|
||||
struct mLibrary* mLibraryCreateEmpty(void);
|
||||
struct mLibrary* mLibraryLoad(const char* filename);
|
||||
void mLibraryDestroy(struct mLibrary*);
|
||||
|
||||
struct VDir;
|
||||
struct VFile;
|
||||
void mLibraryLoadDirectory(struct mLibrary* library, struct VDir* dir);
|
||||
void mLibraryAddEntry(struct mLibrary* library, const char* filename, struct VFile* vf);
|
||||
void mLibraryLoadDirectory(struct mLibrary* library, const char* base);
|
||||
|
||||
size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints);
|
||||
size_t mLibraryGetEntries(struct mLibrary* library, struct mLibraryListing* out, size_t numEntries, size_t offset, const struct mLibraryEntry* constraints);
|
||||
struct VFile* mLibraryOpenVFile(struct mLibrary* library, const struct mLibraryEntry* entry);
|
||||
|
||||
#endif
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -1,29 +1,205 @@
|
|||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/core/library.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#ifdef USE_SQLITE3
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
DEFINE_VECTOR(mLibraryListing, struct mLibraryEntry);
|
||||
|
||||
void mLibraryInit(struct mLibrary* library) {
|
||||
mLibraryListingInit(&library->listing, 0);
|
||||
}
|
||||
struct mLibrary {
|
||||
sqlite3* db;
|
||||
sqlite3_stmt* insertPath;
|
||||
sqlite3_stmt* insertRom;
|
||||
sqlite3_stmt* insertRoot;
|
||||
sqlite3_stmt* selectRom;
|
||||
sqlite3_stmt* selectRoot;
|
||||
sqlite3_stmt* count;
|
||||
sqlite3_stmt* select;
|
||||
};
|
||||
|
||||
void mLibraryDeinit(struct mLibrary* library) {
|
||||
size_t i;
|
||||
for (i = 0; i < mLibraryListingSize(&library->listing); ++i) {
|
||||
struct mLibraryEntry* entry = mLibraryListingGetPointer(&library->listing, i);
|
||||
free(entry->filename);
|
||||
free(entry->title);
|
||||
#define CONSTRAINTS_ROMONLY \
|
||||
"CASE WHEN :useSize THEN roms.size = :size ELSE 1 END AND " \
|
||||
"CASE WHEN :usePlatform THEN roms.platform = :platform ELSE 1 END AND " \
|
||||
"CASE WHEN :useCrc32 THEN roms.crc32 = :crc32 ELSE 1 END AND " \
|
||||
"CASE WHEN :useInternalCode THEN roms.internalCode = :internalCode ELSE 1 END"
|
||||
|
||||
#define CONSTRAINTS \
|
||||
CONSTRAINTS_ROMONLY " AND " \
|
||||
"CASE WHEN :useFilename THEN paths.path = :path ELSE 1 END AND " \
|
||||
"CASE WHEN :useRoot THEN roots.path = :root ELSE 1 END"
|
||||
|
||||
static void _mLibraryInsertEntry(struct mLibrary* library, struct mLibraryEntry* entry);
|
||||
static void _mLibraryAddEntry(struct mLibrary* library, const char* filename, const char* base, struct VFile* vf);
|
||||
|
||||
static void _bindConstraints(sqlite3_stmt* statement, const struct mLibraryEntry* constraints) {
|
||||
if (!constraints) {
|
||||
return;
|
||||
}
|
||||
|
||||
int useIndex, index;
|
||||
if (constraints->crc32) {
|
||||
useIndex = sqlite3_bind_parameter_index(statement, ":useCrc32");
|
||||
index = sqlite3_bind_parameter_index(statement, ":crc32");
|
||||
sqlite3_bind_int(statement, useIndex, 1);
|
||||
sqlite3_bind_int(statement, index, constraints->crc32);
|
||||
}
|
||||
|
||||
if (constraints->filesize) {
|
||||
useIndex = sqlite3_bind_parameter_index(statement, ":useSize");
|
||||
index = sqlite3_bind_parameter_index(statement, ":size");
|
||||
sqlite3_bind_int(statement, useIndex, 1);
|
||||
sqlite3_bind_int64(statement, index, constraints->filesize);
|
||||
}
|
||||
|
||||
if (constraints->filename) {
|
||||
useIndex = sqlite3_bind_parameter_index(statement, ":useFilename");
|
||||
index = sqlite3_bind_parameter_index(statement, ":path");
|
||||
sqlite3_bind_int(statement, useIndex, 1);
|
||||
sqlite3_bind_text(statement, index, constraints->filename, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
if (constraints->base) {
|
||||
useIndex = sqlite3_bind_parameter_index(statement, ":useRoot");
|
||||
index = sqlite3_bind_parameter_index(statement, ":root");
|
||||
sqlite3_bind_int(statement, useIndex, 1);
|
||||
sqlite3_bind_text(statement, index, constraints->base, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
if (constraints->internalCode[0]) {
|
||||
useIndex = sqlite3_bind_parameter_index(statement, ":useInternalCode");
|
||||
index = sqlite3_bind_parameter_index(statement, ":internalCode");
|
||||
sqlite3_bind_int(statement, useIndex, 1);
|
||||
sqlite3_bind_text(statement, index, constraints->internalCode, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
if (constraints->platform != PLATFORM_NONE) {
|
||||
useIndex = sqlite3_bind_parameter_index(statement, ":usePlatform");
|
||||
index = sqlite3_bind_parameter_index(statement, ":platform");
|
||||
sqlite3_bind_int(statement, useIndex, 1);
|
||||
sqlite3_bind_int(statement, index, constraints->platform);
|
||||
}
|
||||
mLibraryListingDeinit(&library->listing);
|
||||
}
|
||||
|
||||
void mLibraryLoadDirectory(struct mLibrary* library, struct VDir* dir) {
|
||||
struct mLibrary* mLibraryCreateEmpty(void) {
|
||||
return mLibraryLoad(":memory:");
|
||||
}
|
||||
|
||||
struct mLibrary* mLibraryLoad(const char* path) {
|
||||
struct mLibrary* library = malloc(sizeof(*library));
|
||||
memset(library, 0, sizeof(*library));
|
||||
|
||||
if (sqlite3_open_v2(path, &library->db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
static const char createTables[] =
|
||||
" PRAGMA foreign_keys = ON;"
|
||||
"\n CREATE TABLE IF NOT EXISTS version ("
|
||||
"\n tname TEXT NOT NULL PRIMARY KEY,"
|
||||
"\n version INTEGER NOT NULL DEFAULT 1"
|
||||
"\n );"
|
||||
"\n CREATE TABLE IF NOT EXISTS roots ("
|
||||
"\n rootid INTEGER NOT NULL PRIMARY KEY ASC,"
|
||||
"\n path TEXT NOT NULL UNIQUE,"
|
||||
"\n mtime INTEGER NOT NULL DEFAULT 0"
|
||||
"\n );"
|
||||
"\n CREATE TABLE IF NOT EXISTS roms ("
|
||||
"\n romid INTEGER NOT NULL PRIMARY KEY ASC,"
|
||||
"\n internalTitle TEXT,"
|
||||
"\n internalCode TEXT,"
|
||||
"\n platform INTEGER NOT NULL DEFAULT -1,"
|
||||
"\n size INTEGER,"
|
||||
"\n crc32 INTEGER,"
|
||||
"\n md5 BLOB,"
|
||||
"\n sha1 BLOB"
|
||||
"\n );"
|
||||
"\n CREATE TABLE IF NOT EXISTS paths ("
|
||||
"\n pathid INTEGER NOT NULL PRIMARY KEY ASC,"
|
||||
"\n romid INTEGER NOT NULL REFERENCES roms(romid) ON DELETE CASCADE,"
|
||||
"\n path TEXT NOT NULL,"
|
||||
"\n mtime INTEGER NOT NULL DEFAULT 0,"
|
||||
"\n rootid INTEGER REFERENCES roots(rootid) ON DELETE CASCADE,"
|
||||
"\n customTitle TEXT,"
|
||||
"\n CONSTRAINT location UNIQUE (path, rootid)"
|
||||
"\n );"
|
||||
"\n CREATE INDEX IF NOT EXISTS crc32 ON roms (crc32);"
|
||||
"\n INSERT OR IGNORE INTO version (tname, version) VALUES ('version', 1);"
|
||||
"\n INSERT OR IGNORE INTO version (tname, version) VALUES ('roots', 1);"
|
||||
"\n INSERT OR IGNORE INTO version (tname, version) VALUES ('roms', 1);"
|
||||
"\n INSERT OR IGNORE INTO version (tname, version) VALUES ('paths', 1);";
|
||||
if (sqlite3_exec(library->db, createTables, NULL, NULL, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
static const char insertPath[] = "INSERT INTO paths (romid, path, customTitle, rootid) VALUES (?, ?, ?, ?);";
|
||||
if (sqlite3_prepare_v2(library->db, insertPath, -1, &library->insertPath, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
static const char insertRom[] = "INSERT INTO roms (crc32, size, internalCode, platform) VALUES (:crc32, :size, :internalCode, :platform);";
|
||||
if (sqlite3_prepare_v2(library->db, insertRom, -1, &library->insertRom, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
static const char insertRoot[] = "INSERT INTO roots (path) VALUES (?);";
|
||||
if (sqlite3_prepare_v2(library->db, insertRoot, -1, &library->insertRoot, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
static const char selectRom[] = "SELECT romid FROM roms WHERE " CONSTRAINTS_ROMONLY ";";
|
||||
if (sqlite3_prepare_v2(library->db, selectRom, -1, &library->selectRom, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
static const char selectRoot[] = "SELECT rootid FROM roots WHERE path = ?;";
|
||||
if (sqlite3_prepare_v2(library->db, selectRoot, -1, &library->selectRoot, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
static const char count[] = "SELECT count(pathid) FROM paths JOIN roots USING (rootid) JOIN roms USING (romid) WHERE " CONSTRAINTS ";";
|
||||
if (sqlite3_prepare_v2(library->db, count, -1, &library->count, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
static const char select[] = "SELECT *, paths.path AS filename, roots.path AS base FROM paths JOIN roots USING (rootid) JOIN roms USING (romid) WHERE " CONSTRAINTS " LIMIT :count OFFSET :offset;";
|
||||
if (sqlite3_prepare_v2(library->db, select, -1, &library->select, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return library;
|
||||
|
||||
error:
|
||||
mLibraryDestroy(library);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void mLibraryDestroy(struct mLibrary* library) {
|
||||
sqlite3_finalize(library->insertPath);
|
||||
sqlite3_finalize(library->insertRom);
|
||||
sqlite3_finalize(library->insertRoot);
|
||||
sqlite3_finalize(library->selectRom);
|
||||
sqlite3_finalize(library->selectRoot);
|
||||
sqlite3_finalize(library->select);
|
||||
sqlite3_finalize(library->count);
|
||||
sqlite3_close(library->db);
|
||||
}
|
||||
|
||||
void mLibraryLoadDirectory(struct mLibrary* library, const char* base) {
|
||||
struct VDir* dir = VDirOpenArchive(base);
|
||||
if (!dir) {
|
||||
dir = VDirOpen(base);
|
||||
}
|
||||
if (!dir) {
|
||||
return;
|
||||
}
|
||||
struct VDirEntry* dirent = dir->listNext(dir);
|
||||
while (dirent) {
|
||||
struct VFile* vf = dir->openFile(dir, dirent->name(dirent), O_RDONLY);
|
||||
|
@ -31,33 +207,174 @@ void mLibraryLoadDirectory(struct mLibrary* library, struct VDir* dir) {
|
|||
dirent = dir->listNext(dir);
|
||||
continue;
|
||||
}
|
||||
mLibraryAddEntry(library, dirent->name(dirent), vf);
|
||||
_mLibraryAddEntry(library, dirent->name(dirent), base, vf);
|
||||
dirent = dir->listNext(dir);
|
||||
}
|
||||
dir->close(dir);
|
||||
}
|
||||
|
||||
void mLibraryAddEntry(struct mLibrary* library, const char* filename, struct VFile* vf) {
|
||||
void _mLibraryAddEntry(struct mLibrary* library, const char* filename, const char* base, struct VFile* vf) {
|
||||
struct mCore* core;
|
||||
if (!vf) {
|
||||
vf = VFileOpen(filename, O_RDONLY);
|
||||
}
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
core = mCoreFindVF(vf);
|
||||
if (core) {
|
||||
struct mLibraryEntry entry;
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
core->init(core);
|
||||
core->loadROM(core, vf);
|
||||
|
||||
struct mLibraryEntry* entry = mLibraryListingAppend(&library->listing);
|
||||
core->getGameTitle(core, entry->internalTitle);
|
||||
core->getGameCode(core, entry->internalCode);
|
||||
entry->title = NULL;
|
||||
entry->filename = strdup(filename);
|
||||
entry->filesize = vf->size(vf);
|
||||
core->getGameTitle(core, entry.internalTitle);
|
||||
core->getGameCode(core, entry.internalCode);
|
||||
core->checksum(core, &entry.crc32, CHECKSUM_CRC32);
|
||||
entry.platform = core->platform(core);
|
||||
entry.title = NULL;
|
||||
entry.base = base;
|
||||
entry.filename = filename;
|
||||
entry.filesize = vf->size(vf);
|
||||
_mLibraryInsertEntry(library, &entry);
|
||||
// Note: this destroys the VFile
|
||||
core->deinit(core);
|
||||
} else {
|
||||
vf->close(vf);
|
||||
}
|
||||
}
|
||||
|
||||
static void _mLibraryInsertEntry(struct mLibrary* library, struct mLibraryEntry* entry) {
|
||||
sqlite3_exec(library->db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
||||
|
||||
sqlite3_clear_bindings(library->selectRom);
|
||||
sqlite3_reset(library->selectRom);
|
||||
struct mLibraryEntry constraints = *entry;
|
||||
constraints.filename = NULL;
|
||||
constraints.base = NULL;
|
||||
_bindConstraints(library->selectRom, &constraints);
|
||||
sqlite3_int64 romId;
|
||||
if (sqlite3_step(library->selectRom) == SQLITE_DONE) {
|
||||
sqlite3_clear_bindings(library->insertRom);
|
||||
sqlite3_reset(library->insertRom);
|
||||
_bindConstraints(library->insertRom, entry);
|
||||
sqlite3_step(library->insertRom);
|
||||
romId = sqlite3_last_insert_rowid(library->db);
|
||||
} else {
|
||||
romId = sqlite3_column_int64(library->selectRom, 0);
|
||||
}
|
||||
|
||||
sqlite3_int64 rootId = 0;
|
||||
if (entry->base) {
|
||||
sqlite3_clear_bindings(library->selectRoot);
|
||||
sqlite3_reset(library->selectRoot);
|
||||
sqlite3_bind_text(library->selectRoot, 1, entry->base, -1, SQLITE_TRANSIENT);
|
||||
if (sqlite3_step(library->selectRoot) == SQLITE_DONE) {
|
||||
sqlite3_clear_bindings(library->insertRoot);
|
||||
sqlite3_reset(library->insertRoot);
|
||||
sqlite3_bind_text(library->insertRoot, 1, entry->base, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_step(library->insertRoot);
|
||||
rootId = sqlite3_last_insert_rowid(library->db);
|
||||
} else {
|
||||
rootId = sqlite3_column_int64(library->selectRoot, 0);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_clear_bindings(library->insertPath);
|
||||
sqlite3_reset(library->insertPath);
|
||||
sqlite3_bind_int64(library->insertPath, 1, romId);
|
||||
sqlite3_bind_text(library->insertPath, 2, entry->filename, -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(library->insertPath, 3, entry->title, -1, SQLITE_TRANSIENT);
|
||||
if (rootId > 0) {
|
||||
sqlite3_bind_int64(library->insertPath, 4, rootId);
|
||||
}
|
||||
sqlite3_step(library->insertPath);
|
||||
sqlite3_exec(library->db, "COMMIT;", NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints) {
|
||||
sqlite3_clear_bindings(library->count);
|
||||
sqlite3_reset(library->count);
|
||||
_bindConstraints(library->count, constraints);
|
||||
if (sqlite3_step(library->count) != SQLITE_ROW) {
|
||||
return 0;
|
||||
}
|
||||
return sqlite3_column_int64(library->count, 0);
|
||||
}
|
||||
|
||||
size_t mLibraryGetEntries(struct mLibrary* library, struct mLibraryListing* out, size_t numEntries, size_t offset, const struct mLibraryEntry* constraints) {
|
||||
mLibraryListingClear(out); // TODO: Free memory
|
||||
sqlite3_clear_bindings(library->select);
|
||||
sqlite3_reset(library->select);
|
||||
_bindConstraints(library->select, constraints);
|
||||
|
||||
int countIndex = sqlite3_bind_parameter_index(library->select, ":count");
|
||||
int offsetIndex = sqlite3_bind_parameter_index(library->select, ":offset");
|
||||
sqlite3_bind_int64(library->select, countIndex, numEntries ? numEntries : -1);
|
||||
sqlite3_bind_int64(library->select, offsetIndex, offset);
|
||||
|
||||
size_t entryIndex;
|
||||
for (entryIndex = 0; (!numEntries || entryIndex < numEntries) && sqlite3_step(library->select) == SQLITE_ROW; ++entryIndex) {
|
||||
struct mLibraryEntry* entry = mLibraryListingAppend(out);
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
int nCols = sqlite3_column_count(library->select);
|
||||
int i;
|
||||
for (i = 0; i < nCols; ++i) {
|
||||
const char* colName = sqlite3_column_name(library->select, i);
|
||||
if (strcmp(colName, "crc32") == 0) {
|
||||
entry->crc32 = sqlite3_column_int(library->select, i);
|
||||
} else if (strcmp(colName, "platform") == 0) {
|
||||
entry->platform = sqlite3_column_int(library->select, i);
|
||||
} else if (strcmp(colName, "size") == 0) {
|
||||
entry->filesize = sqlite3_column_int64(library->select, i);
|
||||
} else if (strcmp(colName, "internalCode") == 0 && sqlite3_column_type(library->select, i) == SQLITE_TEXT) {
|
||||
strncpy(entry->internalCode, (const char*) sqlite3_column_text(library->select, i), sizeof(entry->internalCode) - 1);
|
||||
} else if (strcmp(colName, "internalTitle") == 0 && sqlite3_column_type(library->select, i) == SQLITE_TEXT) {
|
||||
strncpy(entry->internalTitle, (const char*) sqlite3_column_text(library->select, i), sizeof(entry->internalTitle) - 1);
|
||||
} else if (strcmp(colName, "filename") == 0) {
|
||||
entry->filename = strdup((const char*) sqlite3_column_text(library->select, i));
|
||||
} else if (strcmp(colName, "base") == 0) {
|
||||
entry->base = strdup((const char*) sqlite3_column_text(library->select, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return mLibraryListingSize(out);
|
||||
}
|
||||
|
||||
struct VFile* mLibraryOpenVFile(struct mLibrary* library, const struct mLibraryEntry* entry) {
|
||||
struct mLibraryListing entries;
|
||||
mLibraryListingInit(&entries, 0);
|
||||
if (!mLibraryGetEntries(library, &entries, 0, 0, entry)) {
|
||||
mLibraryListingDeinit(&entries);
|
||||
return NULL;
|
||||
}
|
||||
struct VFile* vf = NULL;
|
||||
size_t i;
|
||||
for (i = 0; i < mLibraryListingSize(&entries); ++i) {
|
||||
struct mLibraryEntry* e = mLibraryListingGetPointer(&entries, i);
|
||||
struct VDir* dir = VDirOpenArchive(e->base);
|
||||
bool isArchive = true;
|
||||
if (!dir) {
|
||||
dir = VDirOpen(e->base);
|
||||
isArchive = false;
|
||||
}
|
||||
if (!dir) {
|
||||
continue;
|
||||
}
|
||||
vf = dir->openFile(dir, e->filename, O_RDONLY);
|
||||
if (vf && isArchive) {
|
||||
struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf));
|
||||
uint8_t buffer[2048];
|
||||
ssize_t read;
|
||||
while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
|
||||
vfclone->write(vfclone, buffer, read);
|
||||
}
|
||||
vf->close(vf);
|
||||
vf = vfclone;
|
||||
}
|
||||
dir->close(dir);
|
||||
if (vf) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return vf;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,29 +7,24 @@
|
|||
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#include "ConfigController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
ArchiveInspector::ArchiveInspector(const QString& filename, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_model(ConfigController::configDir() + "/library.sqlite3")
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
m_dir = VDirOpenArchive(filename.toUtf8().constData());
|
||||
if (m_dir) {
|
||||
m_model.loadDirectory(m_dir);
|
||||
}
|
||||
m_model.loadDirectory(filename);
|
||||
m_model.constrainBase(filename);
|
||||
m_ui.archiveListing->setModel(&m_model);
|
||||
}
|
||||
|
||||
ArchiveInspector::~ArchiveInspector() {
|
||||
if (m_dir) {
|
||||
m_dir->close(m_dir);
|
||||
}
|
||||
}
|
||||
|
||||
VFile* ArchiveInspector::selectedVFile() const {
|
||||
QModelIndex index = m_ui.archiveListing->selectionModel()->currentIndex();
|
||||
if (!index.isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
return m_dir->openFile(m_dir, m_model.entryAt(index.row())->filename, O_RDONLY);
|
||||
return m_model.openVFile(index);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "ui_ArchiveInspector.h"
|
||||
|
||||
struct VDir;
|
||||
struct VFile;
|
||||
|
||||
namespace QGBA {
|
||||
|
@ -20,7 +19,6 @@ Q_OBJECT
|
|||
|
||||
public:
|
||||
ArchiveInspector(const QString& filename, QWidget* parent = nullptr);
|
||||
virtual ~ArchiveInspector();
|
||||
|
||||
VFile* selectedVFile() const;
|
||||
|
||||
|
@ -28,7 +26,6 @@ private:
|
|||
Ui::ArchiveInspector m_ui;
|
||||
|
||||
LibraryModel m_model;
|
||||
VDir* m_dir;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -9,40 +9,70 @@
|
|||
|
||||
using namespace QGBA;
|
||||
|
||||
LibraryModel::LibraryModel(QObject* parent)
|
||||
Q_DECLARE_METATYPE(mLibraryEntry);
|
||||
|
||||
LibraryModel::LibraryModel(const QString& path, QObject* parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
mLibraryInit(&m_library);
|
||||
if (!path.isNull()) {
|
||||
m_library = mLibraryLoad(path.toUtf8().constData());
|
||||
} else {
|
||||
m_library = mLibraryCreateEmpty();
|
||||
}
|
||||
memset(&m_constraints, 0, sizeof(m_constraints));
|
||||
m_constraints.platform = PLATFORM_NONE;
|
||||
}
|
||||
|
||||
LibraryModel::~LibraryModel() {
|
||||
mLibraryDeinit(&m_library);
|
||||
clearConstraints();
|
||||
mLibraryDestroy(m_library);
|
||||
}
|
||||
|
||||
void LibraryModel::loadDirectory(VDir* dir) {
|
||||
mLibraryLoadDirectory(&m_library, dir);
|
||||
void LibraryModel::loadDirectory(const QString& path) {
|
||||
beginResetModel();
|
||||
mLibraryLoadDirectory(m_library, path.toUtf8().constData());
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
const mLibraryEntry* LibraryModel::entryAt(int row) const {
|
||||
if ((unsigned) row < mLibraryListingSize(&m_library.listing)) {
|
||||
return mLibraryListingGetConstPointer(&m_library.listing, row);
|
||||
bool LibraryModel::entryAt(int row, mLibraryEntry* out) const {
|
||||
mLibraryListing entries;
|
||||
mLibraryListingInit(&entries, 0);
|
||||
if (!mLibraryGetEntries(m_library, &entries, 1, row, &m_constraints)) {
|
||||
mLibraryListingDeinit(&entries);
|
||||
return false;
|
||||
}
|
||||
return nullptr;
|
||||
*out = *mLibraryListingGetPointer(&entries, 0);
|
||||
mLibraryListingDeinit(&entries);
|
||||
return true;
|
||||
}
|
||||
|
||||
VFile* LibraryModel::openVFile(const QModelIndex& index) const {
|
||||
mLibraryEntry entry;
|
||||
if (!entryAt(index.row(), &entry)) {
|
||||
return nullptr;
|
||||
}
|
||||
return mLibraryOpenVFile(m_library, &entry);
|
||||
}
|
||||
|
||||
QVariant LibraryModel::data(const QModelIndex& index, int role) const {
|
||||
if (!index.isValid()) {
|
||||
return QVariant();
|
||||
}
|
||||
mLibraryEntry entry;
|
||||
if (!entryAt(index.row(), &entry)) {
|
||||
return QVariant();
|
||||
}
|
||||
if (role == Qt::UserRole) {
|
||||
return QVariant::fromValue(entry);
|
||||
}
|
||||
if (role != Qt::DisplayRole) {
|
||||
return QVariant();
|
||||
}
|
||||
const mLibraryEntry* entry = mLibraryListingGetConstPointer(&m_library.listing, index.row());
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
return entry->filename;
|
||||
return entry.filename;
|
||||
case 1:
|
||||
return (unsigned long long) entry->filesize;
|
||||
return (unsigned long long) entry.filesize;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
@ -84,5 +114,25 @@ int LibraryModel::rowCount(const QModelIndex& parent) const {
|
|||
if (parent.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return mLibraryListingSize(&m_library.listing);
|
||||
return mLibraryCount(m_library, &m_constraints);
|
||||
}
|
||||
|
||||
void LibraryModel::constrainBase(const QString& path) {
|
||||
if (m_constraints.base) {
|
||||
free(const_cast<char*>(m_constraints.base));
|
||||
}
|
||||
m_constraints.base = strdup(path.toUtf8().constData());
|
||||
}
|
||||
|
||||
void LibraryModel::clearConstraints() {
|
||||
if (m_constraints.base) {
|
||||
free(const_cast<char*>(m_constraints.base));
|
||||
}
|
||||
if (m_constraints.filename) {
|
||||
free(const_cast<char*>(m_constraints.filename));
|
||||
}
|
||||
if (m_constraints.title) {
|
||||
free(const_cast<char*>(m_constraints.title));
|
||||
}
|
||||
memset(&m_constraints, 0, sizeof(m_constraints));
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <mgba/core/library.h>
|
||||
|
||||
struct VDir;
|
||||
struct VFile;
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
|
@ -18,12 +19,13 @@ class LibraryModel : public QAbstractItemModel {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LibraryModel(QObject* parent = nullptr);
|
||||
LibraryModel(const QString& path, QObject* parent = nullptr);
|
||||
virtual ~LibraryModel();
|
||||
|
||||
void loadDirectory(VDir* dir);
|
||||
void loadDirectory(const QString& path);
|
||||
|
||||
const mLibraryEntry* entryAt(int row) const;
|
||||
bool entryAt(int row, mLibraryEntry* out) const;
|
||||
VFile* openVFile(const QModelIndex& index) const;
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
|
@ -34,9 +36,13 @@ public:
|
|||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
|
||||
private:
|
||||
mLibrary m_library;
|
||||
public slots:
|
||||
void constrainBase(const QString& path);
|
||||
void clearConstraints();
|
||||
|
||||
private:
|
||||
mLibrary* m_library;
|
||||
mLibraryEntry m_constraints;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue