mirror of https://github.com/mgba-emu/mgba.git
Util: No-Intro database parser
This commit is contained in:
parent
cd6b6862ff
commit
746af3ff82
|
@ -0,0 +1,238 @@
|
||||||
|
/* Copyright (c) 2013-2015 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 "nointro.h"
|
||||||
|
|
||||||
|
#include "util/crc32.h"
|
||||||
|
#include "util/table.h"
|
||||||
|
#include "util/vector.h"
|
||||||
|
#include "util/vfs.h"
|
||||||
|
|
||||||
|
#define KEY_STACK_SIZE 8
|
||||||
|
|
||||||
|
struct NoIntroDB {
|
||||||
|
struct Table categories;
|
||||||
|
struct Table gameCrc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NoIntroItem {
|
||||||
|
union {
|
||||||
|
struct Table hash;
|
||||||
|
char* string;
|
||||||
|
};
|
||||||
|
enum NoIntroItemType {
|
||||||
|
NI_HASH,
|
||||||
|
NI_STRING
|
||||||
|
} type;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_VECTOR(NoIntroCategory, struct NoIntroItem*);
|
||||||
|
DEFINE_VECTOR(NoIntroCategory, struct NoIntroItem*);
|
||||||
|
|
||||||
|
static void _indexU32x(struct NoIntroDB* db, struct Table* table, const char* categoryKey, const char* key) {
|
||||||
|
struct NoIntroCategory* category = HashTableLookup(&db->categories, categoryKey);
|
||||||
|
if (!category) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TableInit(table, 256, 0);
|
||||||
|
char* tmpKey = strdup(key);
|
||||||
|
const char* keyStack[KEY_STACK_SIZE] = { tmpKey };
|
||||||
|
size_t i;
|
||||||
|
for (i = 1; i < KEY_STACK_SIZE; ++i) {
|
||||||
|
char* next = strchr(keyStack[i - 1], '.');
|
||||||
|
if (!next) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next[0] = '\0';
|
||||||
|
keyStack[i] = next + 1;
|
||||||
|
}
|
||||||
|
for (i = 0; i < NoIntroCategorySize(category); ++i) {
|
||||||
|
struct NoIntroItem* item = *NoIntroCategoryGetPointer(category, i);
|
||||||
|
if (!item) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct NoIntroItem* keyloc = item;
|
||||||
|
size_t s;
|
||||||
|
for (s = 0; s < KEY_STACK_SIZE && keyStack[s]; ++s) {
|
||||||
|
if (keyloc->type != NI_HASH) {
|
||||||
|
keyloc = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
keyloc = HashTableLookup(&keyloc->hash, keyStack[s]);
|
||||||
|
if (!keyloc) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!keyloc || keyloc->type != NI_STRING) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char* end;
|
||||||
|
uint32_t key = strtoul(keyloc->string, &end, 16);
|
||||||
|
if (!end || *end) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TableInsert(table, key, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _itemDeinit(void* value) {
|
||||||
|
struct NoIntroItem* item = value;
|
||||||
|
switch (item->type) {
|
||||||
|
case NI_STRING:
|
||||||
|
free(item->string);
|
||||||
|
break;
|
||||||
|
case NI_HASH:
|
||||||
|
HashTableDeinit(&item->hash);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _dbDeinit(void* value) {
|
||||||
|
struct NoIntroCategory* category = value;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < NoIntroCategorySize(category); ++i) {
|
||||||
|
struct NoIntroItem* item = *NoIntroCategoryGetPointer(category, i);
|
||||||
|
switch (item->type) {
|
||||||
|
case NI_STRING:
|
||||||
|
free(item->string);
|
||||||
|
break;
|
||||||
|
case NI_HASH:
|
||||||
|
HashTableDeinit(&item->hash);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
free(item);
|
||||||
|
}
|
||||||
|
NoIntroCategoryDeinit(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NoIntroDB* NoIntroDBLoad(struct VFile* vf) {
|
||||||
|
struct NoIntroDB* db = malloc(sizeof(*db));
|
||||||
|
HashTableInit(&db->categories, 0, _dbDeinit);
|
||||||
|
char line[512];
|
||||||
|
struct {
|
||||||
|
char* key;
|
||||||
|
struct NoIntroItem* item;
|
||||||
|
} keyStack[KEY_STACK_SIZE];
|
||||||
|
struct Table* parent = 0;
|
||||||
|
|
||||||
|
size_t stackDepth = 0;
|
||||||
|
while (true) {
|
||||||
|
ssize_t bytesRead = vf->readline(vf, line, sizeof(line));
|
||||||
|
if (!bytesRead) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ssize_t i;
|
||||||
|
const char* token;
|
||||||
|
for (i = 0; i < bytesRead; ++i) {
|
||||||
|
while (isspace((int) line[i]) && i < bytesRead) {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if (i >= bytesRead) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
token = &line[i];
|
||||||
|
while (!isspace((int) line[i]) && i < bytesRead) {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
if (i >= bytesRead) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (token[0]) {
|
||||||
|
case '(':
|
||||||
|
if (!keyStack[stackDepth].key) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
keyStack[stackDepth].item = malloc(sizeof(*keyStack[stackDepth].item));
|
||||||
|
keyStack[stackDepth].item->type = NI_HASH;
|
||||||
|
HashTableInit(&keyStack[stackDepth].item->hash, 8, _itemDeinit);
|
||||||
|
if (parent) {
|
||||||
|
HashTableInsert(parent, keyStack[stackDepth].key, keyStack[stackDepth].item);
|
||||||
|
} else {
|
||||||
|
struct NoIntroCategory* category = HashTableLookup(&db->categories, keyStack[stackDepth].key);
|
||||||
|
if (!category) {
|
||||||
|
category = malloc(sizeof(*category));
|
||||||
|
NoIntroCategoryInit(category, 0);
|
||||||
|
HashTableInsert(&db->categories, keyStack[stackDepth].key, category);
|
||||||
|
}
|
||||||
|
*NoIntroCategoryAppend(category) = keyStack[stackDepth].item;
|
||||||
|
}
|
||||||
|
parent = &keyStack[stackDepth].item->hash;
|
||||||
|
++stackDepth;
|
||||||
|
if (stackDepth >= KEY_STACK_SIZE) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
keyStack[stackDepth].key = 0;
|
||||||
|
break;
|
||||||
|
case ')':
|
||||||
|
if (keyStack[stackDepth].key || !stackDepth) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
--stackDepth;
|
||||||
|
if (stackDepth) {
|
||||||
|
parent = &keyStack[stackDepth - 1].item->hash;
|
||||||
|
} else {
|
||||||
|
parent = 0;
|
||||||
|
}
|
||||||
|
free(keyStack[stackDepth].key);
|
||||||
|
keyStack[stackDepth].key = 0;
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
++token;
|
||||||
|
for (; line[i] != '"' && i < bytesRead; ++i);
|
||||||
|
// Fall through
|
||||||
|
default:
|
||||||
|
line[i] = '\0';
|
||||||
|
if (!keyStack[stackDepth].key) {
|
||||||
|
keyStack[stackDepth].key = strdup(token);
|
||||||
|
} else {
|
||||||
|
struct NoIntroItem* item = malloc(sizeof(*keyStack[stackDepth].item));
|
||||||
|
item->type = NI_STRING;
|
||||||
|
item->string = strdup(token);
|
||||||
|
if (parent) {
|
||||||
|
HashTableInsert(parent, keyStack[stackDepth].key, item);
|
||||||
|
} else {
|
||||||
|
struct NoIntroCategory* category = HashTableLookup(&db->categories, keyStack[stackDepth].key);
|
||||||
|
if (!category) {
|
||||||
|
category = malloc(sizeof(*category));
|
||||||
|
NoIntroCategoryInit(category, 0);
|
||||||
|
HashTableInsert(&db->categories, keyStack[stackDepth].key, category);
|
||||||
|
}
|
||||||
|
*NoIntroCategoryAppend(category) = item;
|
||||||
|
}
|
||||||
|
free(keyStack[stackDepth].key);
|
||||||
|
keyStack[stackDepth].key = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_indexU32x(db, &db->gameCrc, "game", "rom.crc");
|
||||||
|
|
||||||
|
return db;
|
||||||
|
|
||||||
|
error:
|
||||||
|
HashTableDeinit(&db->categories);
|
||||||
|
free(db);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoIntroDBDestroy(struct NoIntroDB* db) {
|
||||||
|
HashTableDeinit(&db->categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NoIntroDBLookupGame(const struct NoIntroDB* db, const void* data, size_t len, struct NoIntroGame* info) {
|
||||||
|
if (!db) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t crc = doCrc32(data, len);
|
||||||
|
struct NoIntroItem* item = TableLookup(&db->gameCrc, crc);
|
||||||
|
if (item) {
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* Copyright (c) 2013-2015 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/. */
|
||||||
|
#ifndef NOINTRO_H
|
||||||
|
#define NOINTRO_H
|
||||||
|
|
||||||
|
#include "util/common.h"
|
||||||
|
|
||||||
|
struct NoIntroGame {
|
||||||
|
const char* name;
|
||||||
|
const char* romName;
|
||||||
|
const char* description;
|
||||||
|
size_t size;
|
||||||
|
uint32_t crc32;
|
||||||
|
uint8_t md5[16];
|
||||||
|
uint8_t sha1[20];
|
||||||
|
bool verified;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NoIntroDB;
|
||||||
|
struct VFile;
|
||||||
|
|
||||||
|
struct NoIntroDB* NoIntroDBLoad(struct VFile* vf);
|
||||||
|
void NoIntroDBDestroy(struct NoIntroDB* db);
|
||||||
|
bool NoIntroDBLookupGame(const struct NoIntroDB* db, const void* data, size_t len, struct NoIntroGame* info);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue