Mercurial > hg > sidinfo
comparison sidlib.c @ 86:e1ff9cd27a84
Initial port of songlength database (SLDB) handling code from XMMS-SID to here.
Needs refactoring.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 11 Feb 2016 23:21:26 +0200 |
parents | 4c0ecb078591 |
children | c5ff71d64e53 |
comparison
equal
deleted
inserted
replaced
85:4c0ecb078591 | 86:e1ff9cd27a84 |
---|---|
193 case PSF_MODEL_MOS8580 : return "MOS8580"; | 193 case PSF_MODEL_MOS8580 : return "MOS8580"; |
194 case PSF_MODEL_ANY : return "MOS6581 / MOS8580"; | 194 case PSF_MODEL_ANY : return "MOS6581 / MOS8580"; |
195 default : return "?"; | 195 default : return "?"; |
196 } | 196 } |
197 } | 197 } |
198 | |
199 | |
200 // Free memory allocated for given SLDB node | |
201 // | |
202 static void si_sldb_node_free(SIDLibSLDBNode *node) | |
203 { | |
204 if (node) | |
205 { | |
206 th_free_r(&node->lengths); | |
207 th_free_r(&node); | |
208 } | |
209 } | |
210 | |
211 | |
212 // Insert given node to db linked list | |
213 // | |
214 static void si_sldb_node_insert(SIDLibSLDB *dbh, SIDLibSLDBNode *node) | |
215 { | |
216 if (dbh->nodes) | |
217 { | |
218 node->prev = dbh->nodes->prev; | |
219 dbh->nodes->prev->next = node; | |
220 dbh->nodes->prev = node; | |
221 } | |
222 else | |
223 { | |
224 dbh->nodes = node; | |
225 node->prev = node; | |
226 } | |
227 node->next = NULL; | |
228 } | |
229 | |
230 | |
231 // Parse a time-entry in SLDB format | |
232 // | |
233 static int si_sldb_get_value(const char *str, size_t *pos) | |
234 { | |
235 int result = 0; | |
236 | |
237 while (isdigit(str[*pos])) | |
238 result = (result * 10) + (str[(*pos)++] - '0'); | |
239 | |
240 return result; | |
241 } | |
242 | |
243 | |
244 static int si_sldb_get_hex_value(const char *str, size_t *pos) | |
245 { | |
246 int result = 0; | |
247 | |
248 do | |
249 { | |
250 char ch = str[*pos]; | |
251 result <<= 4; | |
252 if (ch >= 'A' && ch <= 'F') | |
253 result |= ch - 'A'; | |
254 else | |
255 if (ch >= 'a' && ch <= 'f') | |
256 result |= ch - 'a'; | |
257 else | |
258 if (ch >= '0' && ch <= '9') | |
259 result |= ch - '0'; | |
260 else | |
261 break; | |
262 } | |
263 while (1); | |
264 | |
265 return result; | |
266 } | |
267 | |
268 | |
269 static int si_sldb_gettime(const char *str, size_t *pos) | |
270 { | |
271 int result; | |
272 | |
273 // Check if it starts with a digit | |
274 if (isdigit(str[*pos])) | |
275 { | |
276 // Get minutes-field | |
277 result = si_sldb_get_value(str, pos) * 60; | |
278 | |
279 // Check the field separator char | |
280 if (str[*pos] == ':') | |
281 { | |
282 // Get seconds-field | |
283 (*pos)++; | |
284 result += si_sldb_get_value(str, pos); | |
285 } | |
286 else | |
287 result = -2; | |
288 } | |
289 else | |
290 result = -1; | |
291 | |
292 // Ignore and skip the possible attributes | |
293 while (str[*pos] && !isspace(str[*pos])) | |
294 (*pos)++; | |
295 | |
296 return result; | |
297 } | |
298 | |
299 | |
300 // Parse one SLDB definition line, return SLDB node | |
301 // | |
302 SIDLibSLDBNode *si_sldb_parse_entry(th_ioctx *ctx, const char *line) | |
303 { | |
304 SIDLibSLDBNode *node = NULL; | |
305 size_t pos, tmpLen, savePos; | |
306 BOOL isOK; | |
307 int i; | |
308 | |
309 // Allocate new node | |
310 node = (SIDLibSLDBNode *) th_malloc0(sizeof(SIDLibSLDBNode)); | |
311 if (node == NULL) | |
312 { | |
313 th_io_error(ctx, THERR_MALLOC, | |
314 "Error allocating new node.\n"); | |
315 return NULL; | |
316 } | |
317 | |
318 // Get hash value | |
319 pos = 0; | |
320 for (i = 0; i < TH_MD5HASH_LENGTH; i++, pos += 2) | |
321 { | |
322 unsigned int tmpu; | |
323 sscanf(&line[pos], "%2x", &tmpu); | |
324 node->hash[i] = tmpu; | |
325 } | |
326 | |
327 // Get playtimes | |
328 th_findnext(line, &pos); | |
329 if (line[pos] != '=') | |
330 { | |
331 th_io_error(ctx, THERR_INVALID_DATA, | |
332 "'=' expected on column #%d.\n", pos); | |
333 goto error; | |
334 } | |
335 | |
336 // First playtime is after '=' | |
337 savePos = ++pos; | |
338 tmpLen = strlen(line); | |
339 | |
340 // Get number of sub-tune lengths | |
341 isOK = TRUE; | |
342 while (pos < tmpLen && isOK) | |
343 { | |
344 th_findnext(line, &pos); | |
345 | |
346 if (si_sldb_gettime(line, &pos) >= 0) | |
347 node->nlengths++; | |
348 else | |
349 isOK = FALSE; | |
350 } | |
351 | |
352 // Allocate memory for lengths | |
353 if (node->nlengths == 0) | |
354 goto error; | |
355 | |
356 node->lengths = (int *) th_malloc0(node->nlengths * sizeof(int)); | |
357 if (node->lengths == NULL) | |
358 { | |
359 th_io_error(ctx, THERR_MALLOC, | |
360 "Could not allocate memory for node.\n"); | |
361 goto error; | |
362 } | |
363 | |
364 // Read lengths in | |
365 for (i = 0, pos = savePos, isOK = TRUE; | |
366 pos < tmpLen && i < node->nlengths && isOK; i++) | |
367 { | |
368 int l; | |
369 th_findnext(line, &pos); | |
370 | |
371 l = si_sldb_gettime(line, &pos); | |
372 if (l >= 0) | |
373 node->lengths[i] = l; | |
374 else | |
375 isOK = FALSE; | |
376 } | |
377 | |
378 return node; | |
379 | |
380 error: | |
381 si_sldb_node_free(node); | |
382 return NULL; | |
383 } | |
384 | |
385 | |
386 // Read SLDB database to memory | |
387 // | |
388 int si_sldb_read(th_ioctx *ctx, SIDLibSLDB *dbh) | |
389 { | |
390 char line[PSID_BUFFER2_SIZE]; | |
391 | |
392 while (thfgets(line, PSID_BUFFER2_SIZE, ctx) != NULL) | |
393 { | |
394 SIDLibSLDBNode *tmnode; | |
395 size_t pos = 0; | |
396 ctx->line++; | |
397 | |
398 th_findnext(line, &pos); | |
399 | |
400 // Check if it is datafield | |
401 if (th_isxdigit(line[pos])) | |
402 { | |
403 // Check the length of the hash | |
404 int hashLen; | |
405 for (hashLen = 0; line[pos] && th_isxdigit(line[pos]); hashLen++, pos++); | |
406 | |
407 if (hashLen != TH_MD5HASH_LENGTH_CH) | |
408 { | |
409 th_io_error(ctx, THERR_INVALID_DATA, | |
410 "Invalid MD5-hash in SongLengthDB file '%s' line #%d:\n%s\n", | |
411 ctx->filename, ctx->line, line); | |
412 } | |
413 else | |
414 { | |
415 // Parse and add node to db | |
416 if ((tmnode = si_sldb_parse_entry(ctx, line)) != NULL) | |
417 { | |
418 si_sldb_node_insert(dbh, tmnode); | |
419 } | |
420 else | |
421 { | |
422 th_io_error(ctx, THERR_INVALID_DATA, | |
423 "Invalid entry in SongLengthDB file '%s' line #%d:\n%s\n", | |
424 ctx->filename, ctx->line, line); | |
425 } | |
426 } | |
427 } | |
428 else | |
429 if (line[pos] != ';' && line[pos] != '[' && line[pos] != 0) | |
430 { | |
431 th_io_error(ctx, THERR_INVALID_DATA, | |
432 "Invalid line in SongLengthDB file '%s' line #%d:\n%s\n", | |
433 ctx->filename, ctx->line, line); | |
434 } | |
435 } | |
436 | |
437 return THERR_OK; | |
438 } | |
439 | |
440 | |
441 // Compare two given MD5-hashes. | |
442 // Return: 0 if equal | |
443 // negative if testHash1 < testHash2 | |
444 // positive if testHash1 > testHash2 | |
445 // | |
446 static int si_sldb_compare_hash(th_md5hash_t testHash1, th_md5hash_t testHash2) | |
447 { | |
448 int i, delta; | |
449 | |
450 for (i = delta = 0; i < TH_MD5HASH_LENGTH && !delta; i++) | |
451 delta = testHash1[i] - testHash2[i]; | |
452 | |
453 return delta; | |
454 } | |
455 | |
456 | |
457 // Compare two nodes. | |
458 // We assume here that we never ever get NULL-pointers. | |
459 static int si_sldb_compare_nodes(const void *node1, const void *node2) | |
460 { | |
461 return si_sldb_compare_hash( | |
462 (*(SIDLibSLDBNode **) node1)->hash, | |
463 (*(SIDLibSLDBNode **) node2)->hash); | |
464 } | |
465 | |
466 | |
467 // (Re)create index | |
468 // | |
469 int si_sldb_build_index(SIDLibSLDB * dbh) | |
470 { | |
471 SIDLibSLDBNode *node; | |
472 | |
473 // Free old index | |
474 th_free_r(&dbh->pindex); | |
475 | |
476 // Get size of db | |
477 for (node = dbh->nodes, dbh->n = 0; node != NULL; node = node->next) | |
478 dbh->n++; | |
479 | |
480 // Check number of nodes | |
481 if (dbh->n > 0) | |
482 { | |
483 size_t i; | |
484 | |
485 // Allocate memory for index-table | |
486 dbh->pindex = (SIDLibSLDBNode * *) th_malloc(sizeof(SIDLibSLDBNode *) * dbh->n); | |
487 if (dbh->pindex == NULL) | |
488 return -1; | |
489 | |
490 // Get node-pointers to table | |
491 for (i = 0, node = dbh->nodes; node && i < dbh->n; node = node->next) | |
492 dbh->pindex[i++] = node; | |
493 | |
494 // Sort the indexes | |
495 qsort(dbh->pindex, dbh->n, sizeof(SIDLibSLDBNode *), si_sldb_compare_nodes); | |
496 } | |
497 | |
498 return 0; | |
499 } | |
500 | |
501 | |
502 // Free a given song-length database | |
503 // | |
504 void si_sldb_free(SIDLibSLDB *dbh) | |
505 { | |
506 SIDLibSLDBNode *node = dbh->nodes; | |
507 while (node != NULL) | |
508 { | |
509 SIDLibSLDBNode *next = node->next; | |
510 si_sldb_node_free(node); | |
511 node = next; | |
512 } | |
513 | |
514 dbh->nodes = NULL; | |
515 dbh->n = 0; | |
516 | |
517 th_free_r(&dbh->pindex); | |
518 th_free(dbh); | |
519 } | |
520 | |
521 | |
522 SIDLibSLDBNode *si_sldb_get_by_hash(SIDLibSLDB *dbh, th_md5hash_t hash) | |
523 { | |
524 SIDLibSLDBNode keyItem, *key, **item; | |
525 | |
526 memcpy(&keyItem.hash, &hash, sizeof(th_md5hash_t)); | |
527 key = &keyItem; | |
528 item = bsearch(&key, dbh->pindex, dbh->n, sizeof(dbh->pindex[0]), si_sldb_compare_nodes); | |
529 | |
530 return (item != NULL) ? *item : NULL; | |
531 } |