# HG changeset patch # User Matti Hamalainen # Date 1455225686 -7200 # Node ID e1ff9cd27a840006891519a9d57d26b40aa3470b # Parent 4c0ecb078591fe04125fc59bf0f556abcc881b70 Initial port of songlength database (SLDB) handling code from XMMS-SID to here. Needs refactoring. diff -r 4c0ecb078591 -r e1ff9cd27a84 sidlib.c --- a/sidlib.c Thu Feb 11 23:18:58 2016 +0200 +++ b/sidlib.c Thu Feb 11 23:21:26 2016 +0200 @@ -195,3 +195,337 @@ default : return "?"; } } + + +// Free memory allocated for given SLDB node +// +static void si_sldb_node_free(SIDLibSLDBNode *node) +{ + if (node) + { + th_free_r(&node->lengths); + th_free_r(&node); + } +} + + +// Insert given node to db linked list +// +static void si_sldb_node_insert(SIDLibSLDB *dbh, SIDLibSLDBNode *node) +{ + if (dbh->nodes) + { + node->prev = dbh->nodes->prev; + dbh->nodes->prev->next = node; + dbh->nodes->prev = node; + } + else + { + dbh->nodes = node; + node->prev = node; + } + node->next = NULL; +} + + +// Parse a time-entry in SLDB format +// +static int si_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 si_sldb_get_hex_value(const char *str, size_t *pos) +{ + int result = 0; + + do + { + char ch = str[*pos]; + result <<= 4; + if (ch >= 'A' && ch <= 'F') + result |= ch - 'A'; + else + if (ch >= 'a' && ch <= 'f') + result |= ch - 'a'; + else + if (ch >= '0' && ch <= '9') + result |= ch - '0'; + else + break; + } + while (1); + + return result; +} + + +static int si_sldb_gettime(const char *str, size_t *pos) +{ + int result; + + // Check if it starts with a digit + if (isdigit(str[*pos])) + { + // Get minutes-field + result = si_sldb_get_value(str, pos) * 60; + + // Check the field separator char + if (str[*pos] == ':') + { + // Get seconds-field + (*pos)++; + result += si_sldb_get_value(str, pos); + } + else + result = -2; + } + else + result = -1; + + // Ignore and skip the possible attributes + while (str[*pos] && !isspace(str[*pos])) + (*pos)++; + + return result; +} + + +// Parse one SLDB definition line, return SLDB node +// +SIDLibSLDBNode *si_sldb_parse_entry(th_ioctx *ctx, const char *line) +{ + SIDLibSLDBNode *node = NULL; + size_t pos, tmpLen, savePos; + BOOL isOK; + int i; + + // Allocate new node + node = (SIDLibSLDBNode *) th_malloc0(sizeof(SIDLibSLDBNode)); + if (node == NULL) + { + th_io_error(ctx, THERR_MALLOC, + "Error allocating new node.\n"); + return NULL; + } + + // Get hash value + pos = 0; + for (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] != '=') + { + th_io_error(ctx, THERR_INVALID_DATA, + "'=' expected on column #%d.\n", pos); + goto error; + } + + // 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 (si_sldb_gettime(line, &pos) >= 0) + node->nlengths++; + else + isOK = FALSE; + } + + // Allocate memory for lengths + if (node->nlengths == 0) + goto error; + + node->lengths = (int *) th_malloc0(node->nlengths * sizeof(int)); + if (node->lengths == NULL) + { + th_io_error(ctx, THERR_MALLOC, + "Could not allocate memory for node.\n"); + goto error; + } + + // Read lengths in + for (i = 0, pos = savePos, isOK = TRUE; + pos < tmpLen && i < node->nlengths && isOK; i++) + { + int l; + th_findnext(line, &pos); + + l = si_sldb_gettime(line, &pos); + if (l >= 0) + node->lengths[i] = l; + else + isOK = FALSE; + } + + return node; + +error: + si_sldb_node_free(node); + return NULL; +} + + +// Read SLDB database to memory +// +int si_sldb_read(th_ioctx *ctx, SIDLibSLDB *dbh) +{ + char line[PSID_BUFFER2_SIZE]; + + while (thfgets(line, PSID_BUFFER2_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) + { + th_io_error(ctx, THERR_INVALID_DATA, + "Invalid MD5-hash in SongLengthDB file '%s' line #%d:\n%s\n", + ctx->filename, ctx->line, line); + } + else + { + // Parse and add node to db + if ((tmnode = si_sldb_parse_entry(ctx, line)) != NULL) + { + si_sldb_node_insert(dbh, tmnode); + } + else + { + th_io_error(ctx, THERR_INVALID_DATA, + "Invalid entry in SongLengthDB file '%s' line #%d:\n%s\n", + ctx->filename, ctx->line, line); + } + } + } + else + if (line[pos] != ';' && line[pos] != '[' && line[pos] != 0) + { + th_io_error(ctx, THERR_INVALID_DATA, + "Invalid line in SongLengthDB file '%s' line #%d:\n%s\n", + ctx->filename, ctx->line, line); + } + } + + return THERR_OK; +} + + +// Compare two given MD5-hashes. +// Return: 0 if equal +// negative if testHash1 < testHash2 +// positive if testHash1 > testHash2 +// +static int si_sldb_compare_hash(th_md5hash_t testHash1, th_md5hash_t testHash2) +{ + int i, delta; + + for (i = delta = 0; i < TH_MD5HASH_LENGTH && !delta; i++) + delta = testHash1[i] - testHash2[i]; + + return delta; +} + + +// Compare two nodes. +// We assume here that we never ever get NULL-pointers. +static int si_sldb_compare_nodes(const void *node1, const void *node2) +{ + return si_sldb_compare_hash( + (*(SIDLibSLDBNode **) node1)->hash, + (*(SIDLibSLDBNode **) node2)->hash); +} + + +// (Re)create index +// +int si_sldb_build_index(SIDLibSLDB * dbh) +{ + SIDLibSLDBNode *node; + + // Free old index + th_free_r(&dbh->pindex); + + // Get size of db + for (node = dbh->nodes, dbh->n = 0; node != NULL; node = node->next) + dbh->n++; + + // Check number of nodes + if (dbh->n > 0) + { + size_t i; + + // Allocate memory for index-table + dbh->pindex = (SIDLibSLDBNode * *) th_malloc(sizeof(SIDLibSLDBNode *) * dbh->n); + if (dbh->pindex == NULL) + return -1; + + // Get node-pointers to table + for (i = 0, node = dbh->nodes; node && i < dbh->n; node = node->next) + dbh->pindex[i++] = node; + + // Sort the indexes + qsort(dbh->pindex, dbh->n, sizeof(SIDLibSLDBNode *), si_sldb_compare_nodes); + } + + return 0; +} + + +// Free a given song-length database +// +void si_sldb_free(SIDLibSLDB *dbh) +{ + SIDLibSLDBNode *node = dbh->nodes; + while (node != NULL) + { + SIDLibSLDBNode *next = node->next; + si_sldb_node_free(node); + node = next; + } + + dbh->nodes = NULL; + dbh->n = 0; + + th_free_r(&dbh->pindex); + th_free(dbh); +} + + +SIDLibSLDBNode *si_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->n, sizeof(dbh->pindex[0]), si_sldb_compare_nodes); + + return (item != NULL) ? *item : NULL; +} diff -r 4c0ecb078591 -r e1ff9cd27a84 sidlib.h --- a/sidlib.h Thu Feb 11 23:18:58 2016 +0200 +++ b/sidlib.h Thu Feb 11 23:21:26 2016 +0200 @@ -20,6 +20,24 @@ #define PSID_MAGIC_LEN 4 #define PSID_STR_LEN 32 #define PSID_BUFFER_SIZE (1024 * 16) +#define PSID_BUFFER2_SIZE (1024) + + +typedef struct _SIDLibSLDBNode +{ + th_md5hash_t hash; // MD5 hash-digest + int nlengths; // Number of lengths + int *lengths; // Lengths in seconds + struct _SIDLibSLDBNode *prev, *next; +} SIDLibSLDBNode; + + +typedef struct +{ + SIDLibSLDBNode *nodes, + **pindex; + size_t n; +} SIDLibSLDB; typedef struct @@ -78,6 +96,11 @@ const char * si_get_sid_clock_str(const int flags); const char * si_get_sid_model_str(const int flags); +int si_sldb_read(th_ioctx *ctx, SIDLibSLDB *dbh); +int si_sldb_build_index(SIDLibSLDB *dbh); +void si_sldb_free(SIDLibSLDB *dbh); +SIDLibSLDBNode *si_sldb_get_by_hash(SIDLibSLDB *dbh, th_md5hash_t hash); + #ifdef __cplusplus }