1 /*
2     SID plugin for DeaDBeeF Player
3     Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net>
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <sys/stat.h>
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26 #include "sidplay/sidplay2.h"
27 #include "sidplay/builders/resid.h"
28 //#include "md5.h"
29 // #include "sidplay/sidendian.h"
30 
31 #include "../../deadbeef.h"
32 #include "csid.h"
33 
34 extern DB_decoder_t sid_plugin;
35 
36 //#define trace(...) { fprintf(stderr, __VA_ARGS__); }
37 #define trace(fmt,...)
38 
39 DB_functions_t *deadbeef;
40 
41 #define min(x,y) ((x)<(y)?(x):(y))
42 #define max(x,y) ((x)>(y)?(x):(y))
43 
44 typedef struct {
45     DB_fileinfo_t info;
46     sidplay2 *sidplay;
47     ReSIDBuilder *resid;
48     SidTune *tune;
49     float duration; // of the current song
50 } sid_info_t;
51 
52 static inline void
le_int16(int16_t in,unsigned char * out)53 le_int16 (int16_t in, unsigned char *out) {
54     char *pin = (char *)&in;
55 #if !WORDS_BIGENDIAN
56     out[0] = pin[0];
57     out[1] = pin[1];
58 #else
59     out[1] = pin[0];
60     out[0] = pin[1];
61 #endif
62 }
63 
64 // SLDB support costs ~1M!!!
65 // current hvsc sldb size is ~35k songs
66 #define SLDB_MAX_SONGS 40000
67 // ~50k subsongs in current sldb
68 #define SLDB_POOL_SIZE 55000
69 typedef struct {
70     uint8_t sldb_digests[SLDB_MAX_SONGS][16];
71     int16_t sldb_pool[SLDB_POOL_SIZE];
72     int sldb_poolmark;
73     int16_t *sldb_lengths[SLDB_MAX_SONGS];
74     int sldb_size;
75 } sldb_t;
76 static int sldb_loaded;
77 static sldb_t *sldb;
78 static int sldb_disable;
79 
80 static int chip_voices = 0xff;
81 static int chip_voices_changed = 0;
82 
83 static int conf_hvsc_enable = 0;
84 
85 static void
sldb_load()86 sldb_load()
87 {
88     if (sldb_disable) {
89         return;
90     }
91     trace ("sldb_load\n");
92     if (sldb_loaded || !conf_hvsc_enable) {
93         sldb_disable = 1;
94         return;
95     }
96     char conf_hvsc_path[1000];
97     deadbeef->conf_get_str ("hvsc_path", "", conf_hvsc_path, sizeof (conf_hvsc_path));
98     if (!conf_hvsc_path[0]) {
99         sldb_disable = 1;
100         return;
101     }
102     sldb_loaded = 1;
103     const char *fname = conf_hvsc_path;
104     FILE *fp = fopen (fname, "r");
105     if (!fp) {
106         trace ("sid: failed to open file %s\n", fname);
107         sldb_disable = 1;
108         return;
109     }
110     char str[1024];
111 
112     int line = 1;
113     if (fgets (str, 1024, fp) != str) {
114         goto fail; // eof
115     }
116     if (strncmp (str, "[Database]", 10)) {
117         goto fail; // bad format
118     }
119 
120     if (!sldb) {
121         sldb = (sldb_t *)malloc (sizeof (sldb_t));
122         memset (sldb, 0, sizeof (sldb_t));
123     }
124     while (fgets (str, 1024, fp) == str) {
125         if (sldb->sldb_size >= SLDB_MAX_SONGS) {
126             trace ("sldb loader ran out of memory.\n");
127             break;
128         }
129         line++;
130         if (str[0] == ';') {
131 //            trace ("reading songlength for %s", str);
132             continue; // comment
133         }
134         // read/validate md5
135         const char *p = str;
136         uint8_t digest[16];
137         int sz = 0;
138         char byte[3];
139         while (*p && sz < 16) {
140             byte[0] = tolower (*p);
141             if (!((byte[0] >= '0' && byte[0] <= '9') || (byte[0] >= 'a' && byte[0] <= 'f'))) {
142                 trace ("invalid byte 0 in md5: %c\n", byte[0]);
143                 break;
144             }
145             p++;
146             if (!(*p)) {
147                 break;
148             }
149             byte[1] = tolower (*p);
150             if (!((byte[1] >= '0' && byte[1] <= '9') || (byte[1] >= 'a' && byte[1] <= 'f'))) {
151                 trace ("invalid byte 1 in md5: %c\n", byte[1]);
152                 break;
153             }
154             byte[2] = 0;
155             p++;
156 
157             // convert from ascii hex to uint8_t
158             if (byte[1] < 'a') {
159                 digest[sz] = byte[1] - '0';
160             }
161             else {
162                 digest[sz] = byte[1] - 'a' + 10;
163             }
164             if (byte[0] < 'a') {
165                 digest[sz] |= (byte[0] - '0') << 4;
166             }
167             else {
168                 digest[sz] |= (byte[0] - 'a' + 10) << 4;
169             }
170             sz++;
171         }
172         if (sz < 16) {
173             trace ("bad md5 (sz=%d, line=%d)\n", sz, line);
174             continue; // bad song md5
175         }
176 //        else {
177 //            trace ("digest: ");
178 //            for (int j = 0; j < 16; j++) {
179 //                trace ("%02x", (int)digest[j]);
180 //            }
181 //            trace ("\n");
182 //            exit (0);
183 //        }
184         memcpy (sldb->sldb_digests[sldb->sldb_size], digest, 16);
185         sldb->sldb_lengths[sldb->sldb_size] = &sldb->sldb_pool[sldb->sldb_poolmark];
186         sldb->sldb_size++;
187         // check '=' sign
188         if (*p != '=') {
189             continue; // no '=' sign
190         }
191         p++;
192         if (!(*p)) {
193             continue; // unexpected eol
194         }
195         int subsong = 0;
196         while (*p >= ' ') {
197             // read subsong lengths until eol
198             char timestamp[7]; // up to MMM:SS
199             sz = 0;
200             while (*p > ' ' && *p != '(' && sz < 7) {
201                 timestamp[sz++] = *p;
202                 p++;
203             }
204             if (sz < 4 || sz == 6 && *p > ' ' && *p != '(') {
205                 break; // bad timestamp
206             }
207             timestamp[sz] = 0;
208             // check for unknown time
209             int16_t time = -1;
210             if (!strcmp (timestamp, "-:--")) {
211                 time = -1;
212             }
213             else {
214                 // parse timestamp
215                 const char *colon = strchr (timestamp, ':');
216                 if (!colon) {
217                     break; // bad timestamp
218                 }
219                 // minute
220                 char minute[4];
221                 strncpy (minute, timestamp, colon-timestamp);
222                 minute[colon-timestamp] = 0;
223                 // second
224                 char second[3];
225                 strncpy (second, colon+1, 3);
226                 //trace ("subsong %d, time %s:%s\n", subsong, minute, second);
227                 time = atoi (minute) * 60 + atoi (second);
228             }
229             if (sldb->sldb_poolmark >= SLDB_POOL_SIZE) {
230                 trace ("sldb ran out of memory\n");
231                 goto fail;
232             }
233 
234             sldb->sldb_lengths[sldb->sldb_size-1][subsong] = time;
235             sldb->sldb_poolmark++;
236             subsong++;
237 
238             // prepare for next timestamp
239             if (*p == '(') {
240                 // skip until next whitespace
241                 while (*p > ' ') {
242                     p++;
243                 }
244             }
245             if (*p < ' ') {
246                 break; // eol
247             }
248             // skip white spaces
249             while (*p == ' ') {
250                 p++;
251             }
252             if (*p < ' ') {
253                 break; // eol
254             }
255         }
256     }
257 
258 fail:
259     sldb_disable = 1;
260     fclose (fp);
261     trace ("HVSC sldb loaded %d songs, %d subsongs total\n", sldb->sldb_size, sldb->sldb_poolmark);
262 }
263 
264 static int
sldb_find(const uint8_t * digest)265 sldb_find (const uint8_t *digest) {
266     if (!sldb) {
267         trace ("sldb not loaded\n");
268         return -1;
269     }
270     for (int i = 0; i < sldb->sldb_size; i++) {
271         if (!memcmp (digest, sldb->sldb_digests[i], 16)) {
272             return i;
273         }
274     }
275     return -1;
276 }
277 
278 DB_fileinfo_t *
csid_open(uint32_t hints)279 csid_open (uint32_t hints) {
280     DB_fileinfo_t *_info = (DB_fileinfo_t *)malloc (sizeof (sid_info_t));
281     memset (_info, 0, sizeof (sid_info_t));
282     return _info;
283 }
284 
285 static void
csid_mute_voices(sid_info_t * info,int chip_voices)286 csid_mute_voices (sid_info_t *info, int chip_voices) {
287     int maxsids = info->sidplay->info ().maxsids;
288     for (int k = 0; k < maxsids; k++) {
289         sidemu *emu = info->resid->getsidemu (k);
290         if (emu) {
291             for (int i = 0; i < 3; i++) {
292                 bool mute = chip_voices & (1 << i) ? false : true;
293                 emu->voice (i, mute ? 0x00 : 0xff, mute);
294             }
295         }
296     }
297 }
298 
299 int
csid_init(DB_fileinfo_t * _info,DB_playItem_t * it)300 csid_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
301     sid_info_t *info = (sid_info_t *)_info;
302 
303     // libsidplay crashes if file doesn't exist
304     // so i have to check it here
305     deadbeef->pl_lock ();
306     DB_FILE *fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI"));
307     deadbeef->pl_unlock ();
308     if (!fp ){
309         return -1;
310     }
311     deadbeef->fclose (fp);
312 
313     info->sidplay = new sidplay2;
314     info->resid = new ReSIDBuilder ("wtf");
315     info->resid->create (info->sidplay->info ().maxsids);
316     info->resid->filter (true);
317 
318     int samplerate = deadbeef->conf_get_int ("sid.samplerate", 44100);
319     int bps = deadbeef->conf_get_int ("sid.bps", 16);
320     if (bps != 16 && bps != 8) {
321         bps = 16;
322     }
323 
324     info->resid->sampling (samplerate);
325     info->duration = deadbeef->pl_get_item_duration (it);
326     deadbeef->pl_lock ();
327     info->tune = new SidTune (deadbeef->pl_find_meta (it, ":URI"));
328     deadbeef->pl_unlock ();
329 
330     info->tune->selectSong (deadbeef->pl_find_meta_int (it, ":TRACKNUM", 0)+1);
331     sid2_config_t conf;
332     conf = info->sidplay->config ();
333     conf.frequency = samplerate;
334     conf.precision = bps;
335 
336     conf.playback = deadbeef->conf_get_int ("sid.mono", 0) ? sid2_mono : sid2_stereo;
337     conf.sidEmulation = info->resid;
338     conf.optimisation = 0;
339     info->sidplay->config (conf);
340     info->sidplay->load (info->tune);
341 
342     _info->plugin = &sid_plugin;
343     _info->fmt.channels = conf.playback == sid2_stereo ? 2 : 1;
344     _info->fmt.bps = bps;
345     _info->fmt.samplerate = conf.frequency;
346     _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT);
347     _info->readpos = 0;
348 
349     chip_voices = deadbeef->conf_get_int ("chip.voices", 0xff);
350     csid_mute_voices (info, chip_voices);
351     return 0;
352 }
353 
354 void
csid_free(DB_fileinfo_t * _info)355 csid_free (DB_fileinfo_t *_info) {
356     sid_info_t *info = (sid_info_t *)_info;
357     if (info) {
358         delete info->sidplay;
359         delete info->resid;
360         delete info->tune;
361         free (info);
362     }
363 }
364 
365 int
csid_read(DB_fileinfo_t * _info,char * bytes,int size)366 csid_read (DB_fileinfo_t *_info, char *bytes, int size) {
367     sid_info_t *info = (sid_info_t *)_info;
368     if (_info->readpos > info->duration) {
369         return 0;
370     }
371 
372     if (chip_voices_changed) {
373         chip_voices = deadbeef->conf_get_int ("chip.voices", 0xff);
374         chip_voices_changed = 0;
375         csid_mute_voices (info, chip_voices);
376     }
377 
378     int rd = info->sidplay->play (bytes, size);
379 
380     int samplesize = (_info->fmt.bps>>3) * _info->fmt.channels;
381 
382     _info->readpos += rd / samplesize / (float)_info->fmt.samplerate;
383 
384     return size;
385 
386 }
387 
388 int
csid_seek(DB_fileinfo_t * _info,float time)389 csid_seek (DB_fileinfo_t *_info, float time) {
390     sid_info_t *info = (sid_info_t *)_info;
391     float t = time;
392     if (t < _info->readpos) {
393         // reinit
394         info->sidplay->load (info->tune);
395         csid_mute_voices (info, chip_voices);
396     }
397     else {
398         t -= _info->readpos;
399     }
400     info->resid->filter (false);
401     int samples = t * _info->fmt.samplerate;
402     samples *= (_info->fmt.bps>>3) * _info->fmt.channels;
403     uint16_t buffer[2048 * _info->fmt.channels];
404     while (samples > 0) {
405         int n = min (samples, 2048) * _info->fmt.channels;
406         int done = info->sidplay->play (buffer, n);
407         if (done < n) {
408             trace ("sid seek failure\n");
409             return -1;
410         }
411         samples -= done;
412     }
413     info->resid->filter (true);
414     _info->readpos = time;
415 
416     return 0;
417 }
418 
419 static const char *
convstr(const char * str,int sz,char * out,int out_sz)420 convstr (const char* str, int sz, char *out, int out_sz) {
421     const char *cs = deadbeef->junk_detect_charset (str);
422     if (!cs) {
423         return str;
424     }
425     else {
426         if (deadbeef->junk_iconv (str, sz, out, out_sz, cs, "utf-8") >= 0) {
427             return out;
428         }
429     }
430     return NULL;
431 }
432 
433 static void
find_hvsc_path_from_fname(const char * fname)434 find_hvsc_path_from_fname (const char *fname) {
435     if (conf_hvsc_enable && !sldb_loaded && !sldb_disable) {
436         char conf_hvsc_path[1000];
437         deadbeef->conf_get_str ("hvsc_path", "", conf_hvsc_path, sizeof (conf_hvsc_path));
438         if (!conf_hvsc_path[0]) {
439             strcpy (conf_hvsc_path, fname);
440             char *p;
441             while ((p = strrchr (conf_hvsc_path, '/'))) {
442                 strcpy (p, "/DOCUMENTS/Songlengths.txt");
443                 struct stat st;
444                 int err = stat (conf_hvsc_path, &st);
445                 if (!err && (st.st_mode & S_IFREG)) {
446                     deadbeef->conf_set_str ("hvsc_path", conf_hvsc_path);
447                     deadbeef->conf_save ();
448                     break;
449                 }
450                 *p = 0;
451             }
452         }
453     }
454 }
455 
456 extern "C" DB_playItem_t *
csid_insert(ddb_playlist_t * plt,DB_playItem_t * after,const char * fname)457 csid_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) {
458     trace ("inserting %s\n", fname);
459 
460     find_hvsc_path_from_fname (fname);
461 
462     sldb_load ();
463     SidTune *tune;
464     trace ("new SidTune\n");
465     tune = new SidTune (fname);
466     int tunes = tune->getInfo ().songs;
467     trace ("subtunes: %d\n", tunes);
468     uint8_t sig[16];
469     unsigned char tmp[2];
470 #if 1
471     trace ("calculating md5\n");
472     DB_md5_t md5;
473     deadbeef->md5_init (&md5);
474     deadbeef->md5_append (&md5, (const uint8_t *)tune->cache.get () + tune->fileOffset, tune->getInfo ().c64dataLen);
475     le_int16 (tune->getInfo ().initAddr, tmp);
476     deadbeef->md5_append (&md5, tmp, 2);
477     le_int16 (tune->getInfo ().playAddr, tmp);
478     deadbeef->md5_append (&md5, tmp, 2);
479     le_int16 (tune->getInfo ().songs, tmp);
480     deadbeef->md5_append (&md5, tmp, 2);
481     for (int s = 1; s <= tunes; s++)
482     {
483         tune->selectSong (s);
484         // songspeed is uint8_t, so no need for byteswap
485         deadbeef->md5_append (&md5, &tune->getInfo ().songSpeed, 1);
486     }
487     if (tune->getInfo ().clockSpeed == SIDTUNE_CLOCK_NTSC) {
488         deadbeef->md5_append (&md5, &tune->getInfo ().clockSpeed, sizeof (tune->getInfo ().clockSpeed));
489     }
490     deadbeef->md5_finish (&md5, sig);
491 #else
492     // md5 calc from libsidplay2
493     MD5 myMD5;
494     myMD5.append ((const char *)tune->cache.get() + tune->fileOffset, tune->getInfo ().c64dataLen);
495     // Include INIT and PLAY address.
496     endian_little16 (tmp,tune->getInfo ().initAddr);
497     myMD5.append    (tmp,sizeof(tmp));
498     endian_little16 (tmp,tune->getInfo ().playAddr);
499     myMD5.append    (tmp,sizeof(tmp));
500     // Include number of songs.
501     endian_little16 (tmp,tune->getInfo ().songs);
502     myMD5.append    (tmp,sizeof(tmp));
503     {
504         // Include song speed for each song.
505         for (uint_least16_t s = 1; s <= tune->getInfo ().songs; s++)
506         {
507             tune->selectSong (s);
508             myMD5.append (&tune->getInfo ().songSpeed,1);
509         }
510     }
511     // Deal with PSID v2NG clock speed flags: Let only NTSC
512     // clock speed change the MD5 fingerprint. That way the
513     // fingerprint of a PAL-speed sidtune in PSID v1, v2, and
514     // PSID v2NG format is the same.
515     if (tune->getInfo ().clockSpeed == SIDTUNE_CLOCK_NTSC) {
516         myMD5.append (&tune->getInfo ().clockSpeed,sizeof(tune->getInfo ().clockSpeed));
517     }
518     myMD5.finish ();
519     memcpy (sig, myMD5.getDigest (), 16);
520 #endif
521 
522     int song = -1;
523     if (sldb_loaded) {
524         song = sldb_find (sig);
525     }
526 
527     trace ("inserting tunes...\n");
528     for (int s = 0; s < tunes; s++) {
529         trace ("select %d...\n", s);
530         if (tune->selectSong (s+1)) {
531             DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, sid_plugin.plugin.id);
532             deadbeef->pl_set_meta_int (it, ":TRACKNUM", s);
533             SidTuneInfo sidinfo;
534             tune->getInfo (sidinfo);
535             int i = sidinfo.numberOfInfoStrings;
536             int title_added = 0;
537             trace ("set %d metainfo...\n", s);
538             char temp[2048];
539             if (i >= 1 && sidinfo.infoString[0] && sidinfo.infoString[0][0]) {
540                 const char *meta;
541                 if (sidinfo.songs > 1) {
542                     meta = "album";
543                 }
544                 else {
545                     meta = "title";
546                     title_added = 1;
547                 }
548                 deadbeef->pl_add_meta (it, meta, convstr (sidinfo.infoString[0], strlen (sidinfo.infoString[0]), temp, sizeof (temp)));
549             }
550             if (i >= 2 && sidinfo.infoString[1] && sidinfo.infoString[1][0]) {
551                 deadbeef->pl_add_meta (it, "artist", convstr (sidinfo.infoString[1], strlen (sidinfo.infoString[1]), temp, sizeof (temp)));
552             }
553             if (i >= 3 && sidinfo.infoString[2] && sidinfo.infoString[2][0]) {
554                 deadbeef->pl_add_meta (it, "copyright", convstr (sidinfo.infoString[2], strlen (sidinfo.infoString[2]), temp, sizeof (temp)));
555             }
556 
557             for (int j = 3; j < i; j++)
558             {
559                 if (sidinfo.infoString[j] && sidinfo.infoString[j][0]) {
560                     deadbeef->pl_add_meta (it, "info", convstr (sidinfo.infoString[j], strlen (sidinfo.infoString[j]), temp, sizeof (temp)));
561                 }
562             }
563             char trk[10];
564             snprintf (trk, 10, "%d", s+1);
565             deadbeef->pl_add_meta (it, "track", trk);
566             if (!title_added) {
567                 deadbeef->pl_add_meta (it, "title", NULL);
568             }
569 
570             float length = deadbeef->conf_get_float ("sid.defaultlength", 180);
571             if (sldb_loaded) {
572                 if (song >= 0 && sldb->sldb_lengths[song][s] >= 0) {
573                     length = sldb->sldb_lengths[song][s];
574                 }
575                 //        if (song < 0) {
576                 //            trace ("song %s not found in db, md5: ", fname);
577                 //            for (int j = 0; j < 16; j++) {
578                 //                trace ("%02x", (int)sig[j]);
579                 //            }
580                 //            trace ("\n");
581                 //        }
582             }
583             deadbeef->plt_set_item_duration (plt, it, length);
584             deadbeef->pl_add_meta (it, ":FILETYPE", "SID");
585 
586             after = deadbeef->plt_insert_item (plt, after, it);
587             deadbeef->pl_item_unref (it);
588         }
589     }
590     trace ("delete sidtune\n");
591     delete tune;
592     return after;
593 }
594 
595 static int
sid_configchanged(void)596 sid_configchanged (void) {
597     conf_hvsc_enable = deadbeef->conf_get_int ("hvsc_enable", 0);
598     int disable = !conf_hvsc_enable;
599     if (disable != sldb_disable) {
600         sldb_disable = disable;
601     }
602 
603     // pick up new sldb filename in case it was changed
604     if (sldb) {
605         free (sldb);
606         sldb = NULL;
607         sldb_loaded = 0;
608     }
609 
610     if (chip_voices != deadbeef->conf_get_int ("chip.voices", 0xff)) {
611         chip_voices_changed = 1;
612     }
613 
614     return 0;
615 }
616 
617 int
sid_message(uint32_t id,uintptr_t ctx,uint32_t p1,uint32_t p2)618 sid_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) {
619     switch (id) {
620     case DB_EV_CONFIGCHANGED:
621         sid_configchanged ();
622         break;
623     }
624     return 0;
625 }
626 
627 int
csid_start(void)628 csid_start (void) {
629     sid_configchanged ();
630     return 0;
631 }
632 
633 int
csid_stop(void)634 csid_stop (void) {
635     if (sldb) {
636         free (sldb);
637         sldb = NULL;
638     }
639     sldb_loaded = 0;
640     return 0;
641 }
642 
643 extern "C" DB_plugin_t *
sid_load(DB_functions_t * api)644 sid_load (DB_functions_t *api) {
645     deadbeef = api;
646     return DB_PLUGIN (&sid_plugin);
647 }
648 
649