/* Copyright (c) 2019, Anthony Latorre * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "db.h" #include /* for fopen(), etc. */ #include /* for strerror(), etc. */ #include /* for strncmp(), etc. */ #include /* for uint32_t */ #include /* for atoi() */ #include "dict.h" #include "util.h" char db_err[256]; static uint64_t dbHash(const void *key) { return dictGenHashFunction(key,8); } static int dbKeyCompare(void *privdata, const void *key1, const void *key2) { return memcmp(key1,key2,8) == 0; } static void dbFree(void *privdata, void *val) { free(val); } static dictType titleDB = { dbHash, NULL, NULL, dbKeyCompare, dbFree, dbFree }; /* Add a bank to the database. * * If a bank with the same name and id already exists, it will be replaced. */ void replace_bank(dict *db, const char name[4], uint32_t id, dbval *data) { uint32_t *buf = malloc(sizeof(uint32_t)*2); memcpy(buf,name,4); buf[1] = id; /* If the bank already exists, dictReplace will keep the old key, so we * have to free `buf`. */ if (!dictReplace(db, buf, data)) free(buf); } /* Add a bank to the database. * * Returns 0 on success, -1 if the key already exists. */ int add_bank(dict *db, const char name[4], uint32_t id, dbval *data) { uint32_t *buf = malloc(sizeof(uint32_t)*2); memcpy(buf,name,4); buf[1] = id; if (dictAdd(db, buf, data) != DICT_OK) { sprintf(db_err, "failed to add bank to database!\n"); goto err; } return 0; err: free(buf); return -1; } /* Get a bank from the database. * * Returns a pointer to the first value in the bank, or NULL if the bank * doesn't exist. */ dbval *get_bank(dict *db, const char name[4], uint32_t id) { uint32_t buf[2]; memcpy(buf,name,4); buf[1] = id; return dictFetchValue(db, buf); } /* Function to iterate over the fields in a title bank text file. Works sort of * like strtok(). Example: * * char *item = iter_field(line); * while (item) { * // do something with item * item = iter_field(NULL); * } */ static char *iter_field(char *str) { static char *ptr; static char buf[81]; if (!str) str = ptr; ptr = buf; while (*str != '\x0' && ptr < (buf + 81)) { if (ptr == (buf + 2) && buf[0] == '#' && buf[1] == '.') { ptr = buf; while (*str != '\x0' && *str++ != '#'); continue; } if (*str == ' ' || *str == '\n') { str++; if (ptr == buf) continue; break; } *ptr++ = *str++; } if (ptr == buf) return NULL; *ptr = '\x0'; ptr = str; return buf; } /* Create a new database. */ dict *db_init(void) { dict *db = dictCreate(&titleDB, NULL); return db; } /* Free a database. */ void db_free(dict *db) { dictRelease(db); } /* Load a title bank file into the database `db`. * * If `replace` is 0, then loading an already existing bank will cause an * error. Otherwise, the bank will be replaced. * * Returns 0 on success, -1 on error. */ int load_file(dict *db, const char *filename, int replace) { int i, index; char *item, *s; char line[256]; char idh[4]; uint32_t idn; int mul; int64_t value; double float_value; dbval *buf; int buf_size; buf_size = 10; buf = malloc(buf_size*sizeof(dbval)); if (!buf) { strcpy(db_err,strerror(errno)); return -1; } FILE *f = open_file(filename, "r"); if (!f) { sprintf(db_err, "unable to open file '%s': %s", filename, strerror(errno)); free(buf); return -1; } index = 0; while (fgets(line, sizeof(line), f)) { if (!strncmp(line,"*---",4)) { /* Comment. */ continue; } else if (!strncmp(line,"*.--",4)) { /* Comment. */ continue; } else if (!strncmp(line, "*LOG",4)) { /* Control line which we don't care about. */ continue; } else if (!strncmp(line, "*PRI",4)) { /* Control line which we don't care about. */ continue; } else if (!strncmp(line, "*US",3)) { /* Control line which we don't care about. */ continue; } else if (!strncmp(line, "*KI",3)) { /* Control line which we don't care about. */ continue; } else if (!strncmp(line, "*ANYWAY",7)) { /* Control line which we don't care about. */ continue; } else if (!strncmp(line, "*FIN",4)) { /* Control line which we don't care about. */ continue; } else if (!strncmp(line, "*DO",3)) { if (index > 0) { /* Save previous bank. */ buf = realloc(buf,sizeof(dbval)*index); if (replace) { replace_bank(db, idh, idn, buf); } else if (add_bank(db, idh, idn, buf) == DICT_ERR) { sprintf(db_err, "bank '%.4s' already exists in the database", idh); goto err; } buf_size = 10; buf = malloc(buf_size*sizeof(dbval)); index = 0; } item = iter_field(line); item = iter_field(NULL); strncpy(idh,item,4); item = iter_field(NULL); idn = atoi(item); continue; } item = iter_field(line); while (item) { mul = 1; if ((s = strchr(item,'*'))) { *s = '\0'; mul = atoi(item); item = s+1; } while (buf_size < (index+mul)*sizeof(dbval)) { buf_size *= 2; buf = realloc(buf, buf_size*sizeof(dbval)); } if (!strncmp(item,"#x",2)) { /* Hexadecimal input. */ value = strtol(item+2,NULL,16); for (i = 0; i < mul; i++) buf[index++].u32 = value; } else if (strchr(item,'E') || strchr(item,'.')) { /* Floating point input. */ float_value = strtod(item,NULL); for (i = 0; i < mul; i++) buf[index++].f = float_value; } else { /* Assume it's an integer. */ value = strtol(item,NULL,10); for (i = 0; i < mul; i++) buf[index++].u32 = value; } item = iter_field(NULL); } } buf = realloc(buf,sizeof(dbval)*index); if (replace) { replace_bank(db, idh, idn, buf); } else if (add_bank(db, idh, idn, buf) == DICT_ERR) { sprintf(db_err, "bank '%.4s' already exists in the database", idh); goto err; } fclose(f); return 0; err: if (buf) free(buf); fclose(f); return -1; }