1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <assert.h>
6
7 #include <unistd.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <errno.h>
12
13 #include "songdb.h"
14 #include "uadeconf.h"
15 #include "md5.h"
16 #include "unixatomic.h"
17 #include "ossupport.h"
18 #include "uadeconfig.h"
19 #include "support.h"
20 #include "uadeconstants.h"
21
22 #define NORM_ID "n="
23 #define NORM_ID_LENGTH 2
24
25 #define eserror(fmt, args...) do { fprintf(stderr, "song.conf error on line %zd: " fmt "\n", lineno, ## args); exit(-1); } while (0)
26
27
28 struct eaglesong {
29 int flags;
30 char md5[33];
31 struct uade_attribute *attributes;
32 };
33
34 struct persub {
35 int sub;
36 char *normalisation;
37 };
38
39 static struct uade_content *contentchecksums;
40 static size_t nccused; /* number of valid entries in content db */
41 static size_t nccalloc; /* number of allocated entries for content db */
42 static int ccmodified;
43 static int cccorrupted;
44
45 static int nsongs;
46 static struct eaglesong *songstore;
47
48 static int escompare(const void *a, const void *b);
49 static struct uade_content *get_content(const char *md5);
50
51
add_sub_normalisation(struct uade_content * n,char * normalisation)52 static void add_sub_normalisation(struct uade_content *n, char *normalisation)
53 {
54 struct persub *subinfo;
55 char *endptr;
56
57 subinfo = malloc(sizeof(*subinfo));
58 if (subinfo == NULL)
59 uadeerror("Can't allocate memory for normalisation entry\n");
60
61 subinfo->sub = strtol(normalisation, &endptr, 10);
62 if (*endptr != ',' || subinfo->sub < 0) {
63 fprintf(stderr, "Invalid normalisation entry: %s\n", normalisation);
64 return;
65 }
66
67 subinfo->normalisation = strdup(endptr + 1);
68 if (subinfo->normalisation == NULL)
69 uadeerror("Can't allocate memory for normalisation string\n");
70
71 vplist_append(n->subs, subinfo);
72 }
73
74 /* Compare function for bsearch() and qsort() to sort songs with respect
75 to their md5sums */
contentcompare(const void * a,const void * b)76 static int contentcompare(const void *a, const void *b)
77 {
78 return strcasecmp(((struct uade_content *)a)->md5,
79 ((struct uade_content *)b)->md5);
80 }
81
escompare(const void * a,const void * b)82 static int escompare(const void *a, const void *b)
83 {
84 return strcasecmp(((struct eaglesong *)a)->md5,
85 ((struct eaglesong *)b)->md5);
86 }
87
get_content(const char * md5)88 static struct uade_content *get_content(const char *md5)
89 {
90 struct uade_content key;
91
92 if (contentchecksums == NULL)
93 return NULL;
94
95 memset(&key, 0, sizeof key);
96 strlcpy(key.md5, md5, sizeof key.md5);
97
98 return bsearch(&key, contentchecksums, nccused,
99 sizeof contentchecksums[0], contentcompare);
100 }
101
create_content_checksum(const char * md5,uint32_t playtime)102 static struct uade_content *create_content_checksum(const char *md5,
103 uint32_t playtime)
104 {
105 struct uade_content *n;
106
107 if (nccused == nccalloc) {
108 nccalloc = MAX(nccalloc * 2, 16);
109 n = realloc(contentchecksums,
110 nccalloc * sizeof(struct uade_content));
111 if (n == NULL) {
112 fprintf(stderr,
113 "uade: No memory for new content checksums.\n");
114 return NULL;
115 }
116 contentchecksums = n;
117 }
118
119 n = &contentchecksums[nccused];
120
121 if (md5 == NULL)
122 return n;
123
124 nccused++;
125
126 ccmodified = 1;
127
128 memset(n, 0, sizeof(*n));
129 strlcpy(n->md5, md5, sizeof(n->md5));
130 n->playtime = playtime;
131
132 n->subs = vplist_create(1);
133
134 return n;
135 }
136
md5_from_buffer(char * dest,size_t destlen,uint8_t * buf,size_t bufsize)137 static void md5_from_buffer(char *dest, size_t destlen,
138 uint8_t * buf, size_t bufsize)
139 {
140 uint8_t md5[16];
141 int ret;
142 MD5_CTX ctx;
143 MD5Init(&ctx);
144 MD5Update(&ctx, buf, bufsize);
145 MD5Final(md5, &ctx);
146 ret =
147 snprintf(dest, destlen,
148 "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
149 md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6],
150 md5[7], md5[8], md5[9], md5[10], md5[11], md5[12], md5[13],
151 md5[14], md5[15]);
152 if (ret >= destlen || ret != 32) {
153 fprintf(stderr, "md5 buffer error (%d/%zd)\n", ret, destlen);
154 exit(1);
155 }
156 }
157
update_playtime(struct uade_content * n,uint32_t playtime)158 static void update_playtime(struct uade_content *n, uint32_t playtime)
159 {
160 if (n->playtime != playtime) {
161 ccmodified = 1;
162 n->playtime = playtime;
163 }
164 }
165
sort_content_checksums(void)166 static void sort_content_checksums(void)
167 {
168 if (contentchecksums == NULL)
169 return;
170
171 qsort(contentchecksums, nccused, sizeof contentchecksums[0],
172 contentcompare);
173 }
174
175 /* replace must be zero if content db is unsorted */
uade_add_playtime(const char * md5,uint32_t playtime)176 struct uade_content *uade_add_playtime(const char *md5, uint32_t playtime)
177 {
178 struct uade_content *n;
179
180 /* If content db hasn't been read into memory already, it is not used */
181 if (contentchecksums == NULL)
182 return NULL;
183
184 /* Do not record song shorter than 3 secs */
185 if (playtime < 3000)
186 return NULL;
187
188 if (strlen(md5) != 32)
189 return NULL;
190
191 n = get_content(md5);
192 if (n != NULL) {
193 update_playtime(n, playtime);
194 return n;
195 }
196
197 n = create_content_checksum(md5, playtime);
198
199 sort_content_checksums();
200
201 return n;
202 }
203
uade_lookup_volume_normalisation(struct uade_state * state)204 void uade_lookup_volume_normalisation(struct uade_state *state)
205 {
206 size_t i, nsubs;
207 struct uade_effect *ue = &state->effects;
208 struct uade_config *uc = &state->config;
209 struct uade_song *us = state->song;
210 struct uade_content *content = get_content(us->md5);
211
212 if (content != NULL) {
213
214 nsubs = vplist_len(content->subs);
215
216 for (i = 0; i < nsubs; i++) {
217
218 struct persub *subinfo = vplist_get(content->subs, i);
219
220 if (subinfo->sub == us->cur_subsong) {
221 uade_set_config_option(uc, UC_NORMALISE,
222 subinfo->normalisation);
223 uade_effect_normalise_unserialise(uc->
224 normalise_parameter);
225 uade_effect_enable(ue, UADE_EFFECT_NORMALISE);
226 break;
227 }
228 }
229 }
230 }
231
get_song_flags_and_attributes_from_songstore(struct uade_song * us)232 static void get_song_flags_and_attributes_from_songstore(struct uade_song *us)
233 {
234 struct eaglesong key;
235 struct eaglesong *es;
236
237 if (songstore != NULL) {
238 /* Lookup md5 from the songdb */
239 strlcpy(key.md5, us->md5, sizeof key.md5);
240 es = bsearch(&key, songstore, nsongs, sizeof songstore[0], escompare);
241
242 if (es != NULL) {
243 /* Found -> copy flags and attributes from database */
244 us->flags |= es->flags;
245 us->songattributes = es->attributes;
246 }
247 }
248 }
249
uade_alloc_song(struct uade_state * state,const char * filename)250 int uade_alloc_song(struct uade_state *state, const char *filename)
251 {
252 struct uade_song *us;
253 struct uade_content *content;
254
255 state->song = NULL;
256
257 us = calloc(1, sizeof *us);
258 if (us == NULL)
259 goto error;
260
261 strlcpy(us->module_filename, filename, sizeof us->module_filename);
262
263 us->buf = atomic_read_file(&us->bufsize, filename);
264 if (us->buf == NULL)
265 goto error;
266
267 /* Compute an md5sum of the song */
268 md5_from_buffer(us->md5, sizeof us->md5, us->buf, us->bufsize);
269
270 /* Needs us->md5 sum */
271 get_song_flags_and_attributes_from_songstore(us);
272
273 /* Lookup playtime from content database */
274 us->playtime = -1;
275 content = get_content(us->md5);
276 if (content != NULL && content->playtime > 0)
277 us->playtime = content->playtime;
278
279 /* We can't know subsong numbers yet. The eagleplayer will report them
280 * in the playback state */
281 us->min_subsong = us->max_subsong = us->cur_subsong = -1;
282
283 state->song = us;
284 return 1;
285
286 error:
287 if (us != NULL) {
288 free(us->buf);
289 free(us);
290 }
291 return 0;
292 }
293
uade_open_and_lock(const char * filename,int create)294 static int uade_open_and_lock(const char *filename, int create)
295 {
296 int fd, ret;
297 fd = open(filename, O_RDWR);
298 if (fd < 0) {
299 if (errno == ENOENT && create) {
300 fd = open(filename, O_RDWR | O_CREAT,
301 S_IRUSR | S_IWUSR);
302 if (fd < 0)
303 return -1;
304 } else {
305 return -1;
306 }
307 }
308 #ifndef UADE_HAVE_CYGWIN
309 ret = lockf(fd, F_LOCK, 0);
310 if (ret) {
311 fprintf(stderr, "uade: Unable to lock song.conf: %s (%s)\n",
312 filename, strerror(errno));
313 atomic_close(fd);
314 return -1;
315 }
316 #endif
317
318 return fd;
319 }
320
321
store_playtime(const char * md5,long playtime,int * newccmodified,size_t oldnccused)322 static struct uade_content *store_playtime(const char *md5, long playtime,
323 int *newccmodified,
324 size_t oldnccused)
325 {
326 struct uade_content *n = NULL;
327
328 if (oldnccused > 0) {
329 struct uade_content key;
330 memset(&key, 0, sizeof key);
331 strlcpy(key.md5, md5, sizeof key.md5);
332
333 /* We use "oldnccused" here as the length, while new entries
334 are added in unsorted manner to the end of the array */
335 n = bsearch(&key, contentchecksums, oldnccused,
336 sizeof contentchecksums[0], contentcompare);
337 if (n == NULL)
338 /* new songs on disk db -> merge -> need saving */
339 *newccmodified = 1;
340 }
341
342 /* We value a playtime determined during run-time over
343 a database value */
344 if (n == NULL) {
345 /* Note, create_content_checksum() makes "ccmodified"
346 true, which we work-around later with the "newccmodified" */
347 n = create_content_checksum(md5, (uint32_t) playtime);
348 }
349
350 if (n == NULL) {
351 /* No memory, fuck. We shouldn't save anything to
352 avoid losing data. */
353 fprintf(stderr,
354 "uade: Warning, no memory for the song database\n");
355 cccorrupted = 1;
356 }
357
358 return n;
359 }
360
361
362
uade_read_content_db(const char * filename)363 int uade_read_content_db(const char *filename)
364 {
365 char line[1024];
366 FILE *f;
367 size_t lineno = 0;
368 long playtime;
369 int i, j, nexti;
370 char *id, *eptr;
371 char numberstr[1024];
372 char *md5;
373
374 /* We make backups of some variables because following loop will
375 make it always true, which is not what we want. The end result should
376 be that ccmodified is true in following cases only:
377 1. the in-memory db is already dirty
378 2. the in-memory db gets new data from disk db (merge operation)
379 Otherwise ccmodified should be false. */
380 int newccmodified = ccmodified;
381 size_t oldnccused = nccused;
382 int fd;
383 struct uade_content *n;
384
385 /* Try to create a database if it doesn't exist */
386 if (contentchecksums == NULL
387 && create_content_checksum(NULL, 0) == NULL)
388 return 0;
389
390 fd = uade_open_and_lock(filename, 0);
391 if (fd < 0) {
392 fprintf(stderr, "uade: Can not find %s\n", filename);
393 return 0;
394 }
395
396 f = fdopen(fd, "r");
397 if (f == NULL) {
398 fprintf(stderr, "uade: Can not create FILE structure for %s\n",
399 filename);
400 close(fd);
401 return 0;
402 }
403
404 while (xfgets(line, sizeof line, f) != NULL) {
405 lineno++;
406
407 if (line[0] == '#')
408 continue;
409
410 md5 = line;
411 i = skip_and_terminate_word(line, 0);
412 if (i < 0)
413 continue; /* playtime doesn't exist */
414
415 for (j = 0; isxdigit(line[j]); j++);
416
417 if (j != 32)
418 continue; /* is not a valid md5sum */
419
420 /* Grab and validate playtime (in milliseconds) */
421 nexti = skip_and_terminate_word(line, i);
422
423 playtime = strtol(&line[i], &eptr, 10);
424 if (*eptr != 0 || playtime < 0) {
425 fprintf(stderr, "Invalid playtime for md5 %s on contentdb line %zd: %s\n", md5, lineno, numberstr);
426 continue;
427 }
428
429 n = store_playtime(md5, playtime, &newccmodified, oldnccused);
430 if (n == NULL)
431 continue;
432
433 i = nexti; /* Note, it could be that i < 0 */
434
435 /* Get rest of the directives in a loop */
436 while (i >= 0) {
437 id = &line[i];
438 i = skip_and_terminate_word(line, i);
439
440 /* Subsong volume normalisation: n=sub1,XXX */
441 if (strncmp(id, NORM_ID, NORM_ID_LENGTH) == 0) {
442 id += NORM_ID_LENGTH;
443 add_sub_normalisation(n, id);
444 } else {
445 fprintf(stderr, "Unknown contentdb directive on line %zd: %s\n", lineno, id);
446 }
447 }
448 }
449 fclose(f);
450
451 ccmodified = newccmodified;
452
453 sort_content_checksums();
454
455 return 1;
456 }
457
uade_read_song_conf(const char * filename)458 int uade_read_song_conf(const char *filename)
459 {
460 FILE *f = NULL;
461 struct eaglesong *s;
462 size_t allocated;
463 size_t lineno = 0;
464 size_t i;
465 int fd;
466
467 fd = uade_open_and_lock(filename, 1);
468 /* open_and_lock() may fail without harm (it's actually supposed to
469 fail if the process does not have lock (write) permissions to
470 the song.conf file */
471
472 f = fopen(filename, "r");
473 if (f == NULL)
474 goto error;
475
476 nsongs = 0;
477 allocated = 16;
478 songstore = calloc(allocated, sizeof songstore[0]);
479 if (songstore == NULL)
480 eserror("No memory for song store.");
481
482 while (1) {
483 char **items;
484 size_t nitems;
485
486 items = read_and_split_lines(&nitems, &lineno, f,
487 UADE_WS_DELIMITERS);
488 if (items == NULL)
489 break;
490
491 assert(nitems > 0);
492
493 if (nsongs == allocated) {
494 allocated *= 2;
495 songstore = realloc(songstore, allocated * sizeof(songstore[0]));
496 if (songstore == NULL)
497 eserror("No memory for players.");
498 }
499
500 s = &songstore[nsongs];
501 nsongs++;
502
503 memset(s, 0, sizeof s[0]);
504
505 if (strncasecmp(items[0], "md5=", 4) != 0) {
506 fprintf(stderr, "Line %zd must begin with md5= in %s\n",
507 lineno, filename);
508 free(items);
509 continue;
510 }
511 if (strlcpy(s->md5, items[0] + 4, sizeof s->md5) !=
512 ((sizeof s->md5) - 1)) {
513 fprintf(stderr,
514 "Line %zd in %s has too long an md5sum.\n",
515 lineno, filename);
516 free(items);
517 continue;
518 }
519
520 for (i = 1; i < nitems; i++) {
521 if (strncasecmp(items[i], "comment:", 7) == 0)
522 break;
523 if (uade_song_and_player_attribute(&s->attributes, &s->flags, items[i], lineno))
524 continue;
525 fprintf(stderr, "song option %s is invalid\n", items[i]);
526 }
527
528 for (i = 0; items[i] != NULL; i++)
529 free(items[i]);
530
531 free(items);
532 }
533
534 fclose(f);
535
536 /* we may not have the file locked */
537 if (fd >= 0)
538 atomic_close(fd); /* lock is closed too */
539
540 /* Sort MD5 sums for binary searching songs */
541 qsort(songstore, nsongs, sizeof songstore[0], escompare);
542 return 1;
543
544 error:
545 if (f)
546 fclose(f);
547 if (fd >= 0)
548 atomic_close(fd);
549 return 0;
550 }
551
uade_save_content_db(const char * filename)552 void uade_save_content_db(const char *filename)
553 {
554 int fd;
555 FILE *f;
556 size_t i;
557
558 if (ccmodified == 0 || cccorrupted)
559 return;
560
561 fd = uade_open_and_lock(filename, 1);
562 if (fd < 0) {
563 fprintf(stderr, "uade: Can not write content db: %s\n",
564 filename);
565 return;
566 }
567
568 f = fdopen(fd, "w");
569 if (f == NULL) {
570 fprintf(stderr,
571 "uade: Can not create a FILE structure for content db: %s\n",
572 filename);
573 close(fd);
574 return;
575 }
576
577 for (i = 0; i < nccused; i++) {
578 char str[1024];
579 size_t subi, nsubs;
580 size_t bindex, bleft;
581 struct uade_content *n = &contentchecksums[i];
582
583 str[0] = 0;
584
585 bindex = 0;
586 bleft = sizeof(str);
587
588 nsubs = vplist_len(n->subs);
589
590 for (subi = 0; subi < nsubs; subi++) {
591 struct persub *sub = vplist_get(n->subs, subi);
592 int ret;
593 ret =
594 snprintf(&str[bindex], bleft, NORM_ID "%s ",
595 sub->normalisation);
596 if (ret >= bleft) {
597 fprintf(stderr,
598 "Too much subsong infos for %s\n",
599 n->md5);
600 break;
601 }
602 bleft -= ret;
603 bindex += ret;
604 }
605
606 fprintf(f, "%s %u %s\n", n->md5, (unsigned int)n->playtime,
607 str);
608 }
609
610 ccmodified = 0;
611
612 fclose(f);
613 fprintf(stderr, "uade: Saved %zd entries into content db.\n", nccused);
614 }
615
uade_test_silence(void * buf,size_t size,struct uade_state * state)616 int uade_test_silence(void *buf, size_t size, struct uade_state *state)
617 {
618 int i, s, exceptioncount;
619 int16_t *sm;
620 int nsamples;
621 int64_t count = state->song->silence_count;
622 int end = 0;
623
624 if (state->config.silence_timeout < 0)
625 return 0;
626
627 exceptioncount = 0;
628 sm = buf;
629 nsamples = size / 2;
630
631 for (i = 0; i < nsamples; i++) {
632 s = (sm[i] >= 0) ? sm[i] : -sm[i];
633 if (s >= (32767 * 1 / 100)) {
634 exceptioncount++;
635 if (exceptioncount >= (size * 2 / 100)) {
636 count = 0;
637 break;
638 }
639 }
640 }
641
642 if (i == nsamples) {
643 count += size;
644 if (count / (UADE_BYTES_PER_FRAME * state->config.frequency) >= state->config.silence_timeout) {
645 count = 0;
646 end = 1;
647 }
648 }
649
650 state->song->silence_count = count;
651
652 return end;
653 }
654
uade_unalloc_song(struct uade_state * state)655 void uade_unalloc_song(struct uade_state *state)
656 {
657 free(state->song->buf);
658 state->song->buf = NULL;
659
660 free(state->song);
661 state->song = NULL;
662 }
663
uade_update_song_conf(const char * songconfin,const char * songconfout,const char * songname,const char * options)664 int uade_update_song_conf(const char *songconfin, const char *songconfout,
665 const char *songname, const char *options)
666 {
667 int ret;
668 int fd;
669 char md5[33];
670 void *mem = NULL;
671 size_t filesize, newsize;
672 int found = 0;
673 size_t inputsize;
674 char *input, *inputptr, *outputptr;
675 size_t inputoffs;
676 char newline[256];
677 size_t i;
678 int need_newline = 0;
679
680 if (strlen(options) > 128) {
681 fprintf(stderr, "Too long song.conf options.\n");
682 return 0;
683 }
684
685 fd = uade_open_and_lock(songconfout, 1);
686
687 input = atomic_read_file(&inputsize, songconfin);
688 if (input == NULL) {
689 fprintf(stderr, "Can not read song.conf: %s\n", songconfin);
690 atomic_close(fd); /* closes the lock too */
691 return 0;
692 }
693
694 newsize = inputsize + strlen(options) + strlen(songname) + 64;
695 mem = realloc(input, newsize);
696 if (mem == NULL) {
697 fprintf(stderr,
698 "Can not realloc the input file buffer for song.conf.\n");
699 free(input);
700 atomic_close(fd); /* closes the lock too */
701 return 0;
702 }
703 input = mem;
704
705 mem = atomic_read_file(&filesize, songname);
706 if (mem == NULL)
707 goto error;
708
709 md5_from_buffer(md5, sizeof md5, mem, filesize);
710
711 inputptr = outputptr = input;
712 inputoffs = 0;
713
714 while (inputoffs < inputsize) {
715 if (inputptr[0] == '#')
716 goto copyline;
717
718 if ((inputoffs + 37) >= inputsize)
719 goto copyline;
720
721 if (strncasecmp(inputptr, "md5=", 4) != 0)
722 goto copyline;
723
724 if (strncasecmp(inputptr + 4, md5, 32) == 0) {
725 if (found) {
726 fprintf(stderr,
727 "Warning: dupe entry in song.conf: %s (%s)\n"
728 "Need manual resolving.\n", songname,
729 md5);
730 goto copyline;
731 }
732 found = 1;
733 snprintf(newline, sizeof newline, "md5=%s\t%s\n", md5,
734 options);
735
736 /* Skip this line. It will be appended later to the end of the buffer */
737 for (i = inputoffs; i < inputsize; i++) {
738 if (input[i] == '\n') {
739 i = i + 1 - inputoffs;
740 break;
741 }
742 }
743 if (i == inputsize) {
744 i = inputsize - inputoffs;
745 found = 0;
746 need_newline = 1;
747 }
748 inputoffs += i;
749 inputptr += i;
750 continue;
751 }
752
753 copyline:
754 /* Copy the line */
755 for (i = inputoffs; i < inputsize; i++) {
756 if (input[i] == '\n') {
757 i = i + 1 - inputoffs;
758 break;
759 }
760 }
761 if (i == inputsize) {
762 i = inputsize - inputoffs;
763 need_newline = 1;
764 }
765 memmove(outputptr, inputptr, i);
766 inputoffs += i;
767 inputptr += i;
768 outputptr += i;
769 }
770
771 if (need_newline) {
772 snprintf(outputptr, 2, "\n");
773 outputptr += 1;
774 }
775
776 /* there is enough space */
777 ret = snprintf(outputptr, PATH_MAX + 256, "md5=%s\t%s\tcomment %s\n",
778 md5, options, songname);
779 outputptr += ret;
780
781 if (ftruncate(fd, 0)) {
782 fprintf(stderr, "Can not truncate the file.\n");
783 goto error;
784 }
785
786 /* Final file size */
787 i = (size_t) (outputptr - input);
788
789 if (atomic_write(fd, input, i) < i)
790 fprintf(stderr,
791 "Unable to write file contents back. Data loss happened. CRAP!\n");
792
793 error:
794 atomic_close(fd); /* Closes the lock too */
795 free(input);
796 free(mem);
797 return 1;
798 }
799