Mercurial > hg > sidinfo
view sidlib.c @ 340:6f8c431a3040
Rename some variables and function arguments.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 13 Jan 2020 16:24:57 +0200 |
parents | 629876cc0540 |
children | c2ebcb0f0d62 |
line wrap: on
line source
/* * SIDInfoLib - Way too simplistic PSID/RSID file library * Programmed and designed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> * (C) Copyright 2014-2020 Tecnic Software productions (TNSP) */ #include "sidlib.h" #include "th_endian.h" #include "th_string.h" static char *sidlib_strdup_convert(SIDLibChConvCtx *chconv, const char *str) { if (chconv != NULL) return chconv->convert(chconv, str); else return th_strdup(str); } static BOOL sidlib_fread_str(th_ioctx *ctx, SIDLibChConvCtx *chconv, char **str, const size_t len) { char *tmp; if ((*str = tmp = th_malloc(len + 1)) == NULL) { th_io_error(ctx, THERR_MALLOC, "Could not allocate %" PRIu_SIZE_T " bytes for a string.", len); goto err; } if (!thfread_str(ctx, tmp, len)) { th_io_error(ctx, THERR_FREAD, "Could not read %" PRIu_SIZE_T " bytes from file.", len); goto err; } tmp[len] = 0; if (chconv != NULL) { *str = chconv->convert(chconv, tmp); th_free(tmp); } return TRUE; err: th_free(tmp); return FALSE; } static int sidlib_read_hash_data(th_ioctx *ctx, SIDLibPSIDHeader *psid, th_md5state_t *state, const BOOL newSLDB) { int ret = THERR_OK; uint8_t *data = NULL; BOOL first = TRUE; size_t read; if ((data = (uint8_t *) th_malloc(SIDLIB_BUFFER_SIZE)) == NULL) { ret = th_io_error(ctx, THERR_MALLOC, "Error allocating temporary data buffer of %d bytes.", SIDLIB_BUFFER_SIZE); goto exit; } psid->dataSize = 0; do { read = thfread(data, sizeof(uint8_t), SIDLIB_BUFFER_SIZE, ctx); psid->dataSize += read; // If load address is 0 in header and we have the first block, grab it if (first && psid->loadAddress == 0) { if (read < 4) { ret = th_io_error(ctx, THERR_FREAD, "Error reading song data, unexpectedly small file."); goto exit; } // Grab the load address psid->loadAddress = TH_LE16_TO_NATIVE(*(uint16_t *) data); // .. do not include the load address to the hash if NEW SLDB format if (newSLDB) th_md5_append(state, &data[2], read - 2); else th_md5_append(state, data, read); first = FALSE; } else if (read > 0) { // Append data "as is" th_md5_append(state, data, read); } } while (read > 0 && !thfeof(ctx)); exit: th_free(data); return ret; } int sidlib_read_sid_file(th_ioctx *ctx, SIDLibPSIDHeader *psid, const BOOL newSLDB, SIDLibChConvCtx *chconv) { int ret = THERR_OK; th_md5state_t state; off_t hdrStart, hdrEnd; hdrStart = thftell(ctx); // Read PSID header in if (!thfread_str(ctx, (uint8_t *) psid->magic, SIDLIB_PSID_MAGIC_LEN) || !thfread_be16(ctx, &psid->version) || !thfread_be16(ctx, &psid->dataOffset) || !thfread_be16(ctx, &psid->loadAddress) || !thfread_be16(ctx, &psid->initAddress) || !thfread_be16(ctx, &psid->playAddress) || !thfread_be16(ctx, &psid->nSongs) || !thfread_be16(ctx, &psid->startSong) || !thfread_be32(ctx, &psid->speed)) { ret = th_io_error(ctx, ctx->status, "Could not read PSID/RSID header."); goto exit; } psid->magic[SIDLIB_PSID_MAGIC_LEN] = 0; if ((psid->magic[0] != 'R' && psid->magic[0] != 'P') || psid->magic[1] != 'S' || psid->magic[2] != 'I' || psid->magic[3] != 'D' || psid->version < 1 || psid->version > 4) { ret = th_io_error(ctx, THERR_NOT_SUPPORTED, "Not a supported PSID or RSID file."); goto exit; } psid->isRSID = psid->magic[0] == 'R'; if (!sidlib_fread_str(ctx, chconv, &psid->sidName, SIDLIB_PSID_STR_LEN) || !sidlib_fread_str(ctx, chconv, &psid->sidAuthor, SIDLIB_PSID_STR_LEN) || !sidlib_fread_str(ctx, chconv, &psid->sidCopyright, SIDLIB_PSID_STR_LEN)) { ret = th_io_error(ctx, ctx->status, "Error reading SID file header data."); goto exit; } // Check if we need to load PSIDv2NG header ... if (psid->version >= 2) { // Yes, we need to if (!thfread_be16(ctx, &psid->flags) || !thfread_u8(ctx, &psid->startPage) || !thfread_u8(ctx, &psid->pageLength) || !thfread_u8(ctx, &psid->sid2Addr) || !thfread_u8(ctx, &psid->sid3Addr)) { ret = th_io_error(ctx, ctx->status, "Error reading PSID/RSID v2+ extra header data."); goto exit; } } hdrEnd = thftell(ctx); // Initialize MD5-hash calculation th_md5_init(&state); if (newSLDB) { // New Songlengths.md5 style hash calculation: // We just hash the whole file, so seek back to beginning .. thfseek(ctx, hdrStart, SEEK_SET); if ((ret = sidlib_read_hash_data(ctx, psid, &state, FALSE)) != THERR_OK) goto exit; psid->dataSize -= hdrEnd - hdrStart; } else { // "Old" Songlengths.txt style MD5 hash calculation // We need to separately hash data etc. if ((ret = sidlib_read_hash_data(ctx, psid, &state, TRUE)) != THERR_OK) goto exit; // Append header data to hash th_md5_append_le16(&state, psid->initAddress); th_md5_append_le16(&state, psid->playAddress); th_md5_append_le16(&state, psid->nSongs); // Append song speed data to hash uint8_t tmp8 = psid->isRSID ? 60 : 0; for (int index = 0; index < psid->nSongs && index < 32; index++) { if (psid->isRSID) tmp8 = 60; else tmp8 = (psid->speed & (1 << index)) ? 60 : 0; th_md5_append(&state, &tmp8, sizeof(tmp8)); } // Rest of songs (more than 32) for (int index = 32; index < psid->nSongs; index++) th_md5_append(&state, &tmp8, sizeof(tmp8)); // PSIDv2NG specific if (psid->version >= 2) { // REFER TO SIDPLAY HEADERS FOR MORE INFORMATION tmp8 = (psid->flags >> 2) & 3; if (tmp8 == 2) th_md5_append(&state, &tmp8, sizeof(tmp8)); } } // Calculate the hash th_md5_finish(&state, psid->hash); exit: return ret; } int sidlib_read_sid_file_alloc(th_ioctx *ctx, SIDLibPSIDHeader **ppsid, const BOOL newSLDB, SIDLibChConvCtx *chconv) { if ((*ppsid = th_malloc0(sizeof(SIDLibPSIDHeader))) == NULL) return THERR_MALLOC; (*ppsid)->allocated = TRUE; return sidlib_read_sid_file(ctx, *ppsid, newSLDB, chconv); } void sidlib_free_sid_file(SIDLibPSIDHeader *psid) { if (psid != NULL) { th_free(psid->sidName); th_free(psid->sidAuthor); th_free(psid->sidCopyright); if (psid->allocated) th_free(psid); } } const char *sidlib_get_sid_clock_str(const int flags) { switch (flags) { case PSF_CLOCK_UNKNOWN : return "Unknown"; case PSF_CLOCK_PAL : return "PAL 50Hz"; case PSF_CLOCK_NTSC : return "NTSC 60Hz"; case PSF_CLOCK_ANY : return "PAL / NTSC"; default : return "?"; } } const char *sidlib_get_sid_model_str(const int flags) { switch (flags) { case PSF_MODEL_UNKNOWN : return "Unknown"; case PSF_MODEL_MOS6581 : return "MOS6581"; case PSF_MODEL_MOS8580 : return "MOS8580"; case PSF_MODEL_ANY : return "MOS6581 / MOS8580"; default : return "?"; } } // Free memory allocated for given SLDB node // static void sidlib_sldb_node_free(SIDLibSLDBNode *node) { if (node != NULL) { th_free(node->lengths); th_free(node); } } // Parse a time-entry in SLDB format // static int sidlib_sldb_get_value(const char *str, size_t *pos) { int result = 0; while (isdigit(str[*pos])) result = (result * 10) + (str[(*pos)++] - '0'); return result; } static int sidlib_sldb_gettime(const char *str, size_t *pos) { int result; // Check if it starts with a digit if (th_isdigit(str[*pos])) { // Get minutes-field result = sidlib_sldb_get_value(str, pos) * 60; // Check the field separator char if (str[*pos] == ':') { // Get seconds-field (*pos)++; result += sidlib_sldb_get_value(str, pos); } else result = -2; } else result = -1; // Ignore and skip the possible attributes while (str[*pos] && !th_isspace(str[*pos])) (*pos)++; return result; } // Parse one SLDB definition line, return SLDB node // static int sidlib_sldb_parse_entry(th_ioctx *ctx, SIDLibSLDBNode **pnode, const char *line) { int ret = THERR_OK; SIDLibSLDBNode *node; size_t pos, tmpLen, savePos; BOOL isOK; int i; // Allocate new node if ((*pnode = node = (SIDLibSLDBNode *) th_malloc0(sizeof(SIDLibSLDBNode))) == NULL) { ret = th_io_error(ctx, THERR_MALLOC, "Error allocating new SLDB node."); goto exit; } // Get hash value for (pos = 0, i = 0; i < TH_MD5HASH_LENGTH; i++, pos += 2) { unsigned int tmpu; sscanf(&line[pos], "%2x", &tmpu); node->hash[i] = tmpu; } // Get playtimes th_findnext(line, &pos); if (line[pos] != '=') { ret = th_io_error(ctx, THERR_INVALID_DATA, "'=' expected on column #%" PRIu_SIZE_T ".", pos); goto exit; } // First playtime is after '=' savePos = ++pos; tmpLen = strlen(line); // Get number of sub-tune lengths isOK = TRUE; while (pos < tmpLen && isOK) { th_findnext(line, &pos); if (sidlib_sldb_gettime(line, &pos) >= 0) node->nlengths++; else isOK = FALSE; } // Allocate memory for lengths if (node->nlengths == 0) { ret = THERR_OK; goto exit; } if ((node->lengths = (int *) th_calloc(node->nlengths, sizeof(int))) == NULL) { ret = th_io_error(ctx, THERR_MALLOC, "Error allocating SLDB entry length data."); goto exit; } // Read lengths in for (i = 0, pos = savePos, isOK = TRUE; pos < tmpLen && i < node->nlengths && isOK; i++) { int l; th_findnext(line, &pos); l = sidlib_sldb_gettime(line, &pos); if (l >= 0) node->lengths[i] = l; else isOK = FALSE; } return ret; exit: sidlib_sldb_node_free(node); return ret; } int sidlib_sldb_new(SIDLibSLDB **pdbh) { SIDLibSLDB *dbh; if ((dbh = *pdbh = (SIDLibSLDB *) th_malloc0(sizeof(SIDLibSLDB))) == NULL) return THERR_MALLOC; return THERR_OK; } // Read SLDB database to memory // int sidlib_sldb_read(th_ioctx *ctx, SIDLibSLDB *dbh) { int ret = THERR_OK; char *line = NULL; if ((line = th_malloc(SIDLIB_BUFFER_SIZE)) == NULL) { ret = th_io_error(ctx, THERR_MALLOC, "Error allocating temporary data buffer of %d bytes.", SIDLIB_BUFFER_SIZE); goto exit; } while (thfgets(line, SIDLIB_BUFFER_SIZE, ctx) != NULL) { SIDLibSLDBNode *tmnode; size_t pos = 0; ctx->line++; th_findnext(line, &pos); // Check if it is datafield if (th_isxdigit(line[pos])) { // Check the length of the hash int hashLen; for (hashLen = 0; line[pos] && th_isxdigit(line[pos]); hashLen++, pos++); if (hashLen != TH_MD5HASH_LENGTH_CH) { ret = th_io_error(ctx, THERR_INVALID_DATA, "Invalid MD5-hash."); goto exit; } else { // Parse and add node to db if ((ret = sidlib_sldb_parse_entry(ctx, &tmnode, line)) != THERR_OK) goto exit; th_llist_append_node((th_llist_t **) &dbh->nodes, (th_llist_t *) tmnode); } } else if (line[pos] != ';' && line[pos] != '[' && line[pos] != 0) { ret = th_io_error(ctx, THERR_INVALID_DATA, "Invalid line / unexpected data at column #%" PRIu_SIZE_T ".", pos); goto exit; } } exit: th_free(line); return ret; } // Compare two given MD5-hashes. // Return: 0 if equal // negative if hash1 < hash2 // positive if hash1 > hash2 // static int sidlib_sldb_compare_hash(th_md5hash_t hash1, th_md5hash_t hash2) { int i, delta; for (i = delta = 0; i < TH_MD5HASH_LENGTH && !delta; i++) delta = hash1[i] - hash2[i]; return delta; } // Compare two nodes. // We assume here that we never ever get NULL-pointers. static int sidlib_sldb_compare_nodes(const void *node1, const void *node2) { return sidlib_sldb_compare_hash( (*(SIDLibSLDBNode **) node1)->hash, (*(SIDLibSLDBNode **) node2)->hash); } // (Re)create index // int sidlib_sldb_build_index(SIDLibSLDB *dbh) { if (dbh == NULL) return THERR_NULLPTR; // Free old index th_free_r(&(dbh->pindex)); // Get size of db dbh->nnodes = th_llist_length((th_llist_t *) dbh->nodes); // Check number of nodes if (dbh->nnodes > 0) { SIDLibSLDBNode *node; size_t i; // Check number of nodes against overflow if (dbh->nnodes > UINTPTR_MAX / sizeof(SIDLibSTILNode *)) return THERR_BOUNDS; // Allocate memory for index-table dbh->pindex = (SIDLibSLDBNode **) th_malloc(sizeof(SIDLibSLDBNode *) * dbh->nnodes); if (dbh->pindex == NULL) return THERR_MALLOC; // Get node-pointers to table for (i = 0, node = dbh->nodes; node != NULL && i < dbh->nnodes; node = (SIDLibSLDBNode *) node->node.next) dbh->pindex[i++] = node; // Sort the indexes qsort(dbh->pindex, dbh->nnodes, sizeof(SIDLibSLDBNode *), sidlib_sldb_compare_nodes); } return THERR_OK; } // Free a given song-length database // void sidlib_sldb_free(SIDLibSLDB *dbh) { if (dbh != NULL) { th_llist_free_func_node((th_llist_t *) dbh->nodes, (void (*)(th_llist_t *)) sidlib_sldb_node_free); dbh->nodes = NULL; dbh->nnodes = 0; th_free_r(&dbh->pindex); th_free(dbh); } } SIDLibSLDBNode *sidlib_sldb_get_by_hash(SIDLibSLDB *dbh, th_md5hash_t hash) { SIDLibSLDBNode keyItem, *key, **item; memcpy(&keyItem.hash, hash, sizeof(th_md5hash_t)); key = &keyItem; item = bsearch(&key, dbh->pindex, dbh->nnodes, sizeof(dbh->pindex[0]), sidlib_sldb_compare_nodes); return (item != NULL) ? *item : NULL; } /* STIL database handling functions */ int sidlib_stildb_new(SIDLibSTILDB **pdbh) { SIDLibSTILDB *dbh; if ((dbh = *pdbh = (SIDLibSTILDB *) th_malloc0(sizeof(SIDLibSTILDB))) == NULL) return THERR_MALLOC; return THERR_OK; } SIDLibSTILSubTune *sidlib_stildb_find_subtune(SIDLibSTILNode *node, const int nsubtune) { for (size_t n = 0; n < node->nsubtunes; n++) { SIDLibSTILSubTune *subtune = &node->subtunes[n]; if (subtune->tune == nsubtune) return subtune; } return NULL; } static int sidlib_stildb_entry_realloc( SIDLibSTILNode *node, const int nsubtune, const BOOL alloc, const int nfield, const char *fdata, SIDLibChConvCtx *chconv) { SIDLibSTILSubTune *subtune; if (node == NULL) return THERR_NULLPTR; // Get subtune or allocate new if ((subtune = sidlib_stildb_find_subtune(node, nsubtune)) == NULL) { node->subtunes = (SIDLibSTILSubTune *) th_realloc( node->subtunes, (node->nsubtunes + 1) * sizeof(SIDLibSTILSubTune)); if (node->subtunes == NULL) return THERR_MALLOC; subtune = &node->subtunes[node->nsubtunes]; memset(subtune, 0, sizeof(SIDLibSTILSubTune)); subtune->tune = nsubtune; node->nsubtunes++; } // Allocate field space, if needed if (alloc && nfield >= 0 && nfield < STF_LAST) { SIDLibSTILField *field = &subtune->fields[nfield]; if ((field->data = th_realloc(field->data, (field->ndata + 1) * sizeof(char *))) == NULL) return THERR_MALLOC; field->data[field->ndata] = sidlib_strdup_convert(chconv, fdata); field->ndata++; } return THERR_OK; } static void sidlib_stildb_node_free(SIDLibSTILNode *node) { if (node != NULL) { for (size_t nsubtune = 0; nsubtune < node->nsubtunes; nsubtune++) { SIDLibSTILSubTune *subtune = &node->subtunes[nsubtune]; for (int nfield = 0; nfield < STF_LAST; nfield++) { SIDLibSTILField *field = &subtune->fields[nfield]; for (int n = 0; n < field->ndata; n++) th_free(field->data[n]); th_free(field->data); } } th_free(node->subtunes); th_free(node->filename); th_free(node); } } static int sidlib_stildb_node_new(SIDLibSTILNode **pnode, const char *filename, SIDLibChConvCtx *chconv) { // Allocate memory for new node SIDLibSTILNode *node; int res; if ((node = *pnode = (SIDLibSTILNode *) th_malloc0(sizeof(SIDLibSTILNode))) == NULL) return THERR_MALLOC; // Allocate filename and initial space for one subtune // NOTE! Filename is not converted even if chconv != NULL if ((node->filename = th_strdup(filename)) == NULL) return THERR_MALLOC; if ((res = sidlib_stildb_entry_realloc(node, 1, FALSE, -1, NULL, chconv)) != THERR_OK) { sidlib_stildb_node_free(node); return res; } return res; } /* Free a given STIL database */ void sidlib_stildb_free(SIDLibSTILDB *dbh) { if (dbh != NULL) { th_llist_free_func_node((th_llist_t *) dbh->nodes, (void (*)(th_llist_t *)) sidlib_stildb_node_free); dbh->nodes = NULL; dbh->nnodes = 0; th_free_r(&dbh->pindex); th_free(dbh); } } /* Read database (additively) to given db-structure */ enum { PM_EOF, PM_ERROR, PM_IDLE, PM_COMMENT, PM_NEXT, PM_ENTRY, PM_FIELD_NAME, PM_FIELD_DATA, PM_SUBTUNE, PM_LAST }; const char *sidlib_stil_fields[STF_LAST] = { "NAME", "AUTHOR", "TITLE", "INFO", "ARTIST", "COMMENT", // STF_LAST }; typedef struct { size_t lineNum; ssize_t linePos; int ch, prevMode, nextMode, parseMode; BOOL lineStart; } SIDLibSTILParserCtx; static int sidlib_stildb_get_field(const char *name) { for (int n = 0; n < STF_LAST; n++) { if (strcmp(sidlib_stil_fields[n], name) == 0) return n; } return -1; } static void sidlib_stildb_set_parsemode(SIDLibSTILParserCtx *ctx, const int mode) { ctx->prevMode = ctx->parseMode; ctx->parseMode = mode; } static void sidlib_stildb_set_next_parsemode(SIDLibSTILParserCtx *ctx, const int mode) { sidlib_stildb_set_parsemode(ctx, PM_NEXT); ctx->nextMode = mode; } #define VADDCH(ch) if (strPos < SIDLIB_BUFFER_SIZE) { tmpStr[strPos++] = ch; } int sidlib_stildb_read(th_ioctx *fh, SIDLibSTILDB *dbh, SIDLibChConvCtx *chconv) { int ret = THERR_OK, field = -1; SIDLibSTILParserCtx ctx; char *tmpStr = NULL, *fieldName = NULL; size_t strPos; SIDLibSTILNode *entry = NULL; int subtune; if (fh == NULL || dbh == NULL) return THERR_NULLPTR; if ((tmpStr = th_malloc(SIDLIB_BUFFER_SIZE + 1)) == NULL) return THERR_MALLOC; // Initialize values memset(&ctx, 0, sizeof(ctx)); ctx.nextMode = ctx.prevMode = ctx.parseMode = PM_IDLE; ctx.ch = -1; ctx.lineStart = TRUE; strPos = 0; // Parse the STIL database while (ctx.parseMode != PM_EOF && ctx.parseMode != PM_ERROR) { if (ctx.ch == -1) { // Get next character switch (ctx.ch = thfgetc(fh)) { case '\n': fh->line++; ctx.linePos = -1; break; default: if (ctx.linePos < 0) { ctx.lineStart = TRUE; ctx.linePos = 0; } else ctx.linePos++; } } switch (ctx.parseMode) { case PM_COMMENT: // Comment parsing mode if (ctx.ch == '\n') { // End of line, end of comment sidlib_stildb_set_parsemode(&ctx, ctx.prevMode); } ctx.ch = -1; break; case PM_NEXT: case PM_IDLE: // Normal parsing mode if (ctx.ch == EOF) { sidlib_stildb_set_parsemode(&ctx, PM_EOF); } else if (ctx.ch == '#') { sidlib_stildb_set_parsemode(&ctx, PM_COMMENT); ctx.ch = -1; } else if (th_iscrlf(ctx.ch) || th_isspace(ctx.ch)) { ctx.ch = -1; } else if (ctx.parseMode == PM_IDLE) { // PM_IDLE strPos = 0; if (ctx.ch == '/') { sidlib_stildb_set_parsemode(&ctx, PM_ENTRY); } else if (ctx.ch == '(') { sidlib_stildb_set_parsemode(&ctx, PM_SUBTUNE); ctx.ch = -1; ctx.lineStart = TRUE; } else if (th_isalpha(ctx.ch)) { sidlib_stildb_set_parsemode(&ctx, PM_FIELD_NAME); } else { // Error! Invalid character found ret = th_io_error(fh, THERR_INVALID_DATA, "Unexpected character '%c'.", ctx.ch); goto out; } } else { // PM_NEXT - Next item found sidlib_stildb_set_parsemode(&ctx, ctx.nextMode); } break; case PM_ENTRY: if (ctx.ch == EOF || th_iscrlf(ctx.ch) || th_isspace(ctx.ch)) { // A new file / entry, allocate and append tmpStr[strPos] = 0; if ((ret = sidlib_stildb_node_new(&entry, tmpStr, chconv)) != THERR_OK) goto out; th_llist_append_node((th_llist_t **) &dbh->nodes, (th_llist_t *) entry); // Default subtune is 0, for "main tune" information subtune = 0; sidlib_stildb_set_parsemode(&ctx, PM_IDLE); } else { VADDCH(ctx.ch) else { ret = th_io_error(fh, THERR_INVALID_DATA, "STIL entry filename too long."); goto out; } ctx.ch = -1; } break; case PM_SUBTUNE: if (ctx.ch == EOF || ctx.ch == ')') { BOOL neg = FALSE; // Subtune indicator end tmpStr[strPos] = 0; if (!th_get_int(tmpStr, (unsigned int *) &subtune, &neg) || neg) { ret = th_io_error(fh, THERR_INVALID_DATA, "Entry '%s' subtune indicator '%s' is not a valid integer.", entry->filename, tmpStr); goto out; } if (subtune <= 0 || subtune > 128) { th_io_error(fh, THERR_INVALID_DATA, "Entry '%s' subtune number #%d is out of range/invalid.", entry->filename, subtune); subtune = -1; } sidlib_stildb_set_parsemode(&ctx, PM_IDLE); } else if (th_isdigit(ctx.ch)) { if (ctx.lineStart) goto sub_unexpected; VADDCH(ctx.ch) else { ret = th_io_error(fh, THERR_INVALID_DATA, "Subtune indicator too long."); goto out; } } else if ((ctx.ch == '#' && strPos > 0) || (ctx.ch != '#' && !th_isspace(ctx.ch))) { sub_unexpected: ret = th_io_error(fh, THERR_INVALID_DATA, "Unexpected character '%c' in subtune indicator.", ctx.ch); goto out; } ctx.ch = -1; ctx.lineStart = FALSE; break; case PM_FIELD_NAME: if (ctx.ch == EOF || ctx.ch == ':' || th_iscrlf(ctx.ch) || th_isspace(ctx.ch)) { tmpStr[strPos] = 0; th_pstr_cpy(&fieldName, tmpStr); field = sidlib_stildb_get_field(tmpStr); if (entry == NULL) { ret = th_io_error(fh, THERR_INVALID_DATA, "No STIL entry allocated, but field '%s' found.", fieldName); goto out; } sidlib_stildb_set_next_parsemode(&ctx, PM_FIELD_DATA); ctx.lineStart = FALSE; strPos = 0; } else { VADDCH(ctx.ch) else { ret = th_io_error(fh, THERR_INVALID_DATA, "Field name too long."); goto out; } } ctx.ch = -1; break; case PM_FIELD_DATA: if (ctx.lineStart && th_isspace(ctx.ch) && ctx.linePos < 8) { ctx.ch = -1; } else if (th_iscrlf(ctx.ch)) { ctx.lineStart = TRUE; ctx.ch = -1; } else if (ctx.ch == EOF || (ctx.lineStart && ctx.linePos < 8)) { // Field done tmpStr[strPos] = 0; if (field >= 0 && subtune >= 0) { // Supported field, add it if ((ret = sidlib_stildb_entry_realloc( entry, subtune, TRUE, field, tmpStr, chconv)) != THERR_OK) { ret = th_io_error(fh, THERR_MALLOC, "Could not allocate memory for field '%s'.", fieldName); goto out; } } sidlib_stildb_set_parsemode(&ctx, PM_IDLE); } else { ctx.lineStart = FALSE; VADDCH(ctx.ch) else { ret = th_io_error(fh, THERR_INVALID_DATA, "Field '%s' data too long.", fieldName); goto out; } ctx.ch = - 1; } break; } } out: th_free(tmpStr); th_free(fieldName); if (ret == THERR_OK && ctx.parseMode == PM_ERROR) ret = THERR_INVALID_DATA; return ret; } /* Compare two nodes */ static int sidlib_stildb_compare_subtunes(const void *node1, const void *node2) { return ((SIDLibSTILSubTune *) node1)->tune - ((SIDLibSTILSubTune *) node2)->tune; } static int sidlib_stildb_compare_nodes(const void *node1, const void *node2) { // We assume here that we never ever get NULL-pointers or similar return strcmp( (*(SIDLibSTILNode * *) node1)->filename, (*(SIDLibSTILNode * *) node2)->filename); } /* (Re)create index */ int sidlib_stildb_build_index(SIDLibSTILDB *dbh) { if (dbh == NULL) return THERR_NULLPTR; // Free old index th_free_r(&(dbh->pindex)); // Get size of db dbh->nnodes = th_llist_length((th_llist_t *) dbh->nodes); // Check number of nodes if (dbh->nnodes > 0) { SIDLibSTILNode *node; size_t i; // Check number of nodes against overflow if (dbh->nnodes > UINTPTR_MAX / sizeof(SIDLibSTILNode *)) return THERR_BOUNDS; // Allocate memory for index-table dbh->pindex = (SIDLibSTILNode **) th_malloc(sizeof(SIDLibSTILNode *) * dbh->nnodes); if (dbh->pindex == NULL) return THERR_MALLOC; // Get node-pointers to table for (i = 0, node = dbh->nodes; node != NULL && i < dbh->nnodes; node = (SIDLibSTILNode *) node->node.next) { dbh->pindex[i++] = node; // Sort the subtunes qsort(node->subtunes, node->nsubtunes, sizeof(SIDLibSTILSubTune), sidlib_stildb_compare_subtunes); } // Sort the indexes qsort(dbh->pindex, dbh->nnodes, sizeof(SIDLibSTILNode *), sidlib_stildb_compare_nodes); } return THERR_OK; } /* Get STIL information node from database */ SIDLibSTILNode *sidlib_stildb_get_node(SIDLibSTILDB *dbh, const char *filename) { SIDLibSTILNode keyItem, *key, **item; keyItem.filename = (char *) filename; key = &keyItem; item = bsearch(&key, dbh->pindex, dbh->nnodes, sizeof(SIDLibSTILNode *), sidlib_stildb_compare_nodes); return item != NULL ? *item : NULL; }