Util: No-Intro database parser

This commit is contained in:
Jeffrey Pfau 2015-10-01 22:00:12 -07:00
parent cd6b6862ff
commit 746af3ff82
2 changed files with 267 additions and 0 deletions

238
src/util/nointro.c Normal file
View File

@ -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;
}

29
src/util/nointro.h Normal file
View File

@ -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