summaryrefslogtreecommitdiff
path: root/plugins/ddb_input_uade2/uade-2.13/src/frontends/common
diff options
context:
space:
mode:
authorGravatar waker <wakeroid@gmail.com>2010-12-05 16:06:29 +0100
committerGravatar waker <wakeroid@gmail.com>2010-12-05 16:06:29 +0100
commit4a04da22d9faf432962e98dbc8dae2e3df9b16c3 (patch)
tree8a43fa66cace8de729d4c8bcaa95d23a684c6174 /plugins/ddb_input_uade2/uade-2.13/src/frontends/common
parentad085a1661d4a46eefb4a8d7359ea1b58bf8cc08 (diff)
added UADE2 plugin
Diffstat (limited to 'plugins/ddb_input_uade2/uade-2.13/src/frontends/common')
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.c1168
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.h9
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c502
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.h127
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.c490
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.h42
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.c247
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.copyright8
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.h21
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.c798
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.h24
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.c721
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.h19
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.c183
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.h31
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.c888
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.h27
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconfstructure.h139
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.c249
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.h24
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadestate.h25
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.c69
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.h16
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.c115
-rw-r--r--plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.h44
25 files changed, 5986 insertions, 0 deletions
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.c
new file mode 100644
index 00000000..9365436c
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.c
@@ -0,0 +1,1168 @@
+/*
+ Copyright (C) 2000-2005 Heikki Orsila
+ Copyright (C) 2000-2005 Michael Doering
+
+ This module is dual licensed under the GNU GPL and the Public Domain.
+ Hence you may use _this_ module (not another code module) in any way you
+ want in your projects.
+
+ About security:
+
+ This module tries to avoid any buffer overruns by not copying anything but
+ hard coded strings (such as "FC13"). This doesn't
+ copy any data from modules to program memory. Any memory writing with
+ non-hard-coded data is an error by assumption. This module will only
+ determine the format of a given module.
+
+ Occasional memory reads over buffer ranges can occur, but they will of course
+ be fixed when spotted :P The worst that can happen with reading over the
+ buffer range is a core dump :)
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <uadeutils.h>
+#include <amifilemagic.h>
+
+#define FILEMAGIC_DEBUG 0
+
+#if FILEMAGIC_DEBUG
+#define amifiledebug(fmt, args...) do { fprintf(stderr, "%s:%d: %s: " fmt, __FILE__, __LINE__, __func__, ## args); } while(0)
+#else
+#define amifiledebug(fmt, args...)
+#endif
+
+#define WAV_HEADER_LEN 44
+
+enum {
+ MOD_UNDEFINED = 0,
+ MOD_SOUNDTRACKER25_NOISETRACKER10,
+ MOD_NOISETRACKER12,
+ MOD_NOISETRACKER20,
+ MOD_STARTREKKER4,
+ MOD_STARTREKKER8,
+ MOD_AUDIOSCULPTURE4,
+ MOD_AUDIOSCULPTURE8,
+ MOD_PROTRACKER,
+ MOD_FASTTRACKER,
+ MOD_NOISETRACKER,
+ MOD_PTK_COMPATIBLE,
+ MOD_SOUNDTRACKER24
+};
+
+
+#define S15_HEADER_LENGTH 600
+#define S31_HEADER_LENGTH 1084
+
+
+static int chk_id_offset(unsigned char *buf, int bufsize,
+ const char *patterns[], int offset, char *pre);
+
+
+/* Do not use '\0'. They won't work in patterns */
+static const char *offset_0000_patterns[] = {
+ /* ID: Prefix: Desc: */
+ "DIGI Booster", "DIGI", /* Digibooster */
+ "OKTASONG", "OKT", /* Oktalyzer */
+ "SYNTRACKER", "SYNMOD", /* Syntracker */
+ "OBISYNTHPACK", "OSP", /* Synthpack */
+ "SOARV1.0", "SA", /* Sonic Arranger */
+ "AON4", "AON4", /* Art Of Noise (4ch) */
+ "AON8", "AON8", /* Art Of Noise (8ch) */
+ "ARP.", "MTP2", /* HolyNoise / Major Tom */
+ "AmBk", "ABK", /* Amos ABK */
+ "FUCO", "BSI", /* FutureComposer BSI */
+ "MMU2", "DSS", /* DSS */
+ "GLUE", "GLUE", /* GlueMon */
+ "ISM!", "IS", /* In Stereo */
+ "IS20", "IS20", /* In Stereo 2 */
+ "SMOD", "FC13", /* FC 1.3 */
+ "FC14", "FC14", /* FC 1.4 */
+ "MMDC", "MMDC", /* Med packer */
+ "MSOB", "MSO", /* Medley */
+ "MODU", "NTP", /* Novotrade */
+/* HIPPEL-ST CONFLICT: "COSO", "SOC",*/ /* Hippel Coso */
+ "BeEp", "JAM", /* Jamcracker */
+ "ALL ", "DM1", /* Deltamusic 1 */
+ "YMST", "YM", /* MYST ST-YM */
+ "AMC ", "AMC", /* AM-Composer */
+ "P40A", "P40A", /* The Player 4.0a */
+ "P40B", "P40B", /* The Player 4.0b */
+ "P41A", "P41A", /* The Player 4.1a */
+ "P50A", "P50A", /* The Player 5.0a */
+ "P60A", "P60A", /* The Player 6.0a */
+ "P61A", "P61A", /* The Player 6.1a */
+ "SNT!", "PRU2", /* Prorunner 2 */
+ "MEXX_TP2", "TP2", /* Tracker Packer 2 */
+ "CPLX_TP3", "TP3", /* Tracker Packer 3 */
+ "MEXX", "TP1", /* Tracker Packer 2 */
+ "PM40", "PM40", /* Promizer 4.0 */
+ "FC-M", "FC-M", /* FC-M */
+ "E.M.S. V6.", "EMSV6", /* EMS version 6 */
+ "MCMD", "MCMD_org", /* 0x00 MCMD format */
+ "STP3", "STP3", /* Soundtracker Pro 2 */
+ "MTM", "MTM", /* Multitracker */
+ "Extended Module:", "XM", /* Fasttracker2 */
+ "MLEDMODL", "ML", /* Musicline Editor */
+ "FTM", "FTM", /* Face The Music */
+ "MXTX", "MXTX", /* Maxtrax*/
+ "M1.0", "FUZZ", /* Fuzzac*/
+ "MSNG", "TPU", /* Dirk Bialluch*/
+ "YM!", "", /* stplay -- intentionally sabotaged */
+ "ST1.2 ModuleINFO", "", /* Startrekker AM .NT -- intentionally sabotaged */
+ "AudioSculpture10", "", /* Audiosculpture .AS -- intentionally sabotaged */
+ NULL, NULL
+};
+
+static const char *offset_0024_patterns[] = {
+ /* ID: Prefix: Desc: */
+ "UNCLEART", "DL", /* Dave Lowe WT */
+ "DAVELOWE", "DL_deli", /* Dave Lowe Deli */
+ "J.FLOGEL", "JMF", /* Janko Mrsic-Flogel */
+ "BEATHOVEN", "BSS", /* BSS */
+ "FREDGRAY", "GRAY", /* Fred Gray */
+ "H.DAVIES", "HD", /* Howie Davies */
+ "RIFFRAFF", "RIFF", /* Riff Raff */
+ "!SOPROL!", "SPL", /* Soprol */
+ "F.PLAYER", "FP", /* F.Player */
+ "S.PHIPPS", "CORE", /* Core Design */
+ "DAGLISH!", "BDS", /* Benn Daglish */
+ NULL, NULL
+};
+
+
+/* check for 'pattern' in 'buf'.
+ the 'pattern' must lie inside range [0, maxlen) in the buffer.
+ returns true if pattern is at buf[offset], otherwrise false
+ */
+static int patterntest(const unsigned char *buf, const char *pattern,
+ int offset, int bytes, int maxlen)
+{
+ if ((offset + bytes) <= maxlen)
+ return (memcmp(buf + offset, pattern, bytes) == 0) ? 1 : 0;
+ return 0;
+}
+
+
+static int tronictest(unsigned char *buf, size_t bufsize)
+{
+ size_t a = read_be_u16(&buf[0x02]) + read_be_u16(&buf[0x06]) +
+ read_be_u16(&buf[0x0a]) + read_be_u16(&buf[0x0e]) + 0x10;
+
+ if (((a + 2) >= bufsize) || (a & 1))
+ return 0; /* size & btst #0, d1; */
+
+ a = read_be_u16(&buf[a]) + a;
+ if (((a + 8) >= bufsize) || (a & 1))
+ return 0; /*size & btst #0,d1 */
+
+ if (read_be_u32(&buf[a + 4]) != 0x5800b0)
+ return 0;
+
+ amifiledebug("tronic recognized\n");
+
+ return 1;
+}
+
+static int tfmxtest(unsigned char *buf, size_t bufsize, char *pre)
+{
+ if (bufsize <= 0x208)
+ return 0;
+
+ if (strncmp((char *) buf, "TFHD", 4) == 0) {
+ if (buf[0x8] == 0x01) {
+ strcpy(pre, "TFHD1.5"); /* One File TFMX format by Alexis NASR */
+ return 1;
+ } else if (buf[0x8] == 0x02) {
+ strcpy(pre, "TFHDPro");
+ return 1;
+ } else if (buf[0x8] == 0x03) {
+ strcpy(pre, "TFHD7V");
+ return 1;
+ }
+ }
+
+ if (strncasecmp((char *) buf, "TFMX", 4) == 0) {
+ if (strncmp((char *) &buf[4], "-SONG", 5) == 0 ||
+ strncmp((char *) &buf[4], "_SONG ", 6) == 0 ||
+ strncasecmp((char *) &buf[4], "SONG", 4) == 0 ||
+ buf[4] == 0x20) {
+ strcpy(pre, "MDAT"); /*default TFMX: TFMX Pro */
+
+ if (strncmp((char *) &buf[10], "by", 2) == 0 ||
+ strncmp((char *) &buf[16], " ", 2) == 0 ||
+ strncmp((char *) &buf[16], "(Empty)", 7) == 0 ||
+ /* Lethal Zone */
+ (buf[16] == 0x30 && buf[17] == 0x3d) ||
+ (buf[4] == 0x20)){
+
+ if (read_be_u32(&buf[464]) == 0x00000000) {
+ uint16_t x = read_be_u16(&buf[14]);
+ if ((x != 0x0e60) || /* z-out title */
+ (x == 0x0860 && bufsize > 4645 && read_be_u16(&buf[4644]) != 0x090c) || /* metal law */
+ (x == 0x0b20 && bufsize > 5121 && read_be_u16(&buf[5120]) != 0x8c26) || /* bug bomber */
+ (x == 0x0920 && bufsize > 3977 && read_be_u16(&buf[3876]) != 0x9305)) { /* metal preview */
+ strcpy(pre, "TFMX1.5"); /*TFMX 1.0 - 1.6 */
+ }
+ }
+ return 1;
+
+ } else if (((buf[0x0e] == 0x08 && buf[0x0f] == 0xb0) && /* BMWi */
+ (buf[0x140] == 0x00 && buf[0x141] == 0x0b) && /*End tackstep 1st subsong */
+ (buf[0x1d2] == 0x02 && buf[0x1d3] == 0x00) && /*Trackstep datas */
+ (buf[0x200] == 0xff && buf[0x201] == 0x00 && /*First effect */
+ buf[0x202] == 0x00 && buf[0x203] == 0x00 &&
+ buf[0x204] == 0x01 && buf[0x205] == 0xf4 &&
+ buf[0x206] == 0xff && buf[0x207] == 0x00)) ||
+ ((buf[0x0e] == 0x0A && buf[0x0f] == 0xb0) && /* B.C Kid */
+ (buf[0x140] == 0x00 && buf[0x141] == 0x15) && /*End tackstep 1st subsong */
+ (buf[0x1d2] == 0x02 && buf[0x1d3] == 0x00) && /*Trackstep datas */
+ (buf[0x200] == 0xef && buf[0x201] == 0xfe && /*First effect */
+ buf[0x202] == 0x00 && buf[0x203] == 0x03 &&
+ buf[0x204] == 0x00 && buf[0x205] == 0x0d &&
+ buf[0x206] == 0x00 && buf[0x207] == 0x00))) {
+ strcpy(pre, "TFMX7V"); /* "special cases TFMX 7V */
+ return 1;
+
+ } else {
+ int e, i, s, t;
+
+ /* Trackstep datas offset */
+ s = read_be_u32(&buf[0x1d0]);
+ if (s == 0x00000000) {
+ /* unpacked */
+ s = 0x00000800;
+ }
+
+ for (i = 0; i < 0x3d; i += 2) {
+ if (read_be_u16(&buf[0x140 + i]) != 0x0000) { /* subsong */
+ /* Start of subsongs Trackstep data :) */
+ t = read_be_u16(&buf[0x100 + i]) * 16 + s;
+ /* End of subsongs Trackstep data :) */
+ e = read_be_u16(&buf[0x140 + i]) * 16 + s;
+ if (e < bufsize) {
+ for (; t < e && (t + 6) < bufsize; t += 2) {
+ if (read_be_u16(&buf[t]) == 0xeffe &&
+ read_be_u32(&buf[t + 2]) == 0x0003ff00 &&
+ buf[t + 6] == 0x00) {
+ strcpy(pre, "TFMX7V"); /*TFMX 7V */
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Calculate Module length: Just need at max 1084 */
+/* data in buf for a */
+/* succesful calculation */
+/* returns: */
+/* -1 for no mod */
+/* 1 for a mod with good length */
+static size_t modlentest(unsigned char *buf, size_t bufsize, size_t filesize,
+ int header)
+{
+ int i;
+ int no_of_instr;
+ int smpl = 0;
+ int plist;
+ int maxpattern = 0;
+
+ if (header > bufsize)
+ return -1; /* no mod */
+
+ if (header == S15_HEADER_LENGTH) {
+ no_of_instr = 15;
+ plist = header - 128;
+ } else if (header == S31_HEADER_LENGTH) {
+ no_of_instr = 31;
+ plist = header - 4 - 128;
+ } else {
+ return -1;
+ }
+
+ for (i = 0; i < 128; i++) {
+ if (buf[plist + i] > maxpattern)
+ maxpattern = buf[plist + i];
+ }
+
+ if (maxpattern > 100)
+ return -1;
+
+ for (i = 0; i < no_of_instr; i++)
+ smpl += 2 * read_be_u16(&buf[42 + i * 30]); /* add sample length in bytes*/
+
+ return header + (maxpattern + 1) * 1024 + smpl;
+}
+
+
+static void modparsing(unsigned char *buf, size_t bufsize, size_t header, int max_pattern, int pfx[], int pfxarg[])
+{
+ int offset;
+ int i, j, fx;
+ unsigned char fxarg;
+
+ for (i = 0; i < max_pattern; i++) {
+ for (j = 0; j < 256; j++) {
+ offset = header + i * 1024 + j * 4;
+
+ if ((offset + 4) > bufsize)
+ return;
+
+ fx = buf[offset + 2] & 0x0f;
+ fxarg = buf[offset + 3];
+
+ if (fx == 0) {
+ if (fxarg != 0 )
+ pfx[fx] += 1;
+ pfxarg[fx] = (pfxarg[fx] > fxarg) ? pfxarg[fx] : fxarg;
+
+ } else if (1 <= fx && fx <= 13) {
+ pfx[fx] +=1;
+ pfxarg[fx] = (pfxarg[fx] > fxarg) ? pfxarg[fx] : fxarg;
+
+ } else if (fx == 14) {
+ pfx[((fxarg >> 4) & 0x0f) + 16] +=1;
+
+ } else if (fx == 15) {
+ if (fxarg > 0x1f)
+ pfx[14] +=1;
+ else
+ pfx[15] +=1;
+ pfxarg[15] = (pfxarg[15] > fxarg) ? pfxarg[15] : fxarg;
+ }
+ }
+ }
+
+}
+
+
+static int mod32check(unsigned char *buf, size_t bufsize, size_t realfilesize,
+ const char *path, int verbose)
+{
+ /* mod patterns at file offset 0x438 */
+ char *mod_patterns[] = { "M.K.", ".M.K", NULL};
+ /* startrekker patterns at file offset 0x438 */
+ char *startrekker_patterns[] = { "FLT4", "FLT8", "EXO4", "EXO8", NULL};
+
+ int max_pattern = 0;
+ int i, j, t, ret;
+ int pfx[32];
+ int pfxarg[32];
+
+ /* instrument var */
+ int vol, slen, srep, sreplen;
+
+ int has_slen_sreplen_zero = 0; /* sreplen empty of non looping instrument */
+ int no_slen_sreplen_zero = 0; /* sreplen */
+
+ int has_slen_sreplen_one = 0;
+ int no_slen_sreplen_one = 0;
+
+ int no_slen_has_volume = 0;
+ int finetune_used = 0;
+
+ size_t calculated_size;
+
+ /* returns: 0 for undefined */
+ /* 1 for a Soundtracker2.5/Noisetracker 1.0 */
+ /* 2 for a Noisetracker 1.2 */
+ /* 3 for a Noisetracker 2.0 */
+ /* 4 for a Startrekker 4ch */
+ /* 5 for a Startrekker 8ch */
+ /* 6 for Audiosculpture 4 ch/fm */
+ /* 7 for Audiosculpture 8 ch/fm */
+ /* 8 for a Protracker */
+ /* 9 for a Fasttracker */
+ /* 10 for a Noisetracker (M&K!) */
+ /* 11 for a PTK Compatible */
+ /* 12 for a Soundtracker 31instr. with repl in bytes */
+
+ /* Special cases first */
+ if (patterntest(buf, "M&K!", (S31_HEADER_LENGTH - 4), 4, bufsize))
+ return MOD_NOISETRACKER; /* Noisetracker (M&K!) */
+
+ if (patterntest(buf, "M!K!", (S31_HEADER_LENGTH - 4), 4, bufsize))
+ return MOD_PROTRACKER; /* Protracker (100 patterns) */
+
+ if (patterntest(buf, "N.T.", (S31_HEADER_LENGTH - 4), 4, bufsize))
+ return MOD_NOISETRACKER20; /* Noisetracker2.x */
+
+ for (i = 0; startrekker_patterns[i]; i++) {
+ if (patterntest(buf, startrekker_patterns[i], (S31_HEADER_LENGTH - 4), 4, bufsize)) {
+ t = 0;
+ for (j = 0; j < 30 * 0x1e; j = j + 0x1e) {
+ if (buf[0x2a + j] == 0 && buf[0x2b + j] == 0 && buf[0x2d + j] != 0) {
+ t = t + 1; /* no of AM instr. */
+ }
+ }
+ if (t > 0) {
+ if (buf[0x43b] == '4'){
+ ret = MOD_AUDIOSCULPTURE4; /* Startrekker 4 AM / ADSC */
+ } else {
+ ret = MOD_AUDIOSCULPTURE8; /* Startrekker 8 AM / ADSC */
+ }
+ } else {
+ if (buf[0x43b] == '4'){
+ ret = MOD_STARTREKKER4; /* Startrekker 4ch */
+ } else {
+ ret = MOD_STARTREKKER8; /* Startrekker 8ch */
+ }
+ }
+ return ret;
+ }
+ }
+
+ calculated_size = modlentest(buf, bufsize, realfilesize, S31_HEADER_LENGTH);
+
+ if (calculated_size == -1)
+ return MOD_UNDEFINED;
+
+
+ for (i = 0; mod_patterns[i]; i++) {
+ if (patterntest(buf, mod_patterns[i], S31_HEADER_LENGTH - 4, 4, bufsize)) {
+ /* seems to be a generic M.K. MOD */
+ /* only spam filesize message when it's a tracker module */
+
+ if (calculated_size != realfilesize) {
+ fprintf(stderr, "uade: file size is %zd but calculated size for a mod file is %zd (%s).\n", realfilesize, calculated_size, path);
+ }
+
+ if (calculated_size > realfilesize) {
+ fprintf(stderr, "uade: file is truncated and won't get played (%s)\n", path);
+ return MOD_UNDEFINED;
+ }
+
+ if (calculated_size < realfilesize) {
+ fprintf(stderr, "uade: file has trailing garbage behind the actual module data. Please fix it. (%s)\n", path);
+ }
+
+ /* parse instruments */
+ for (i = 0; i < 31; i++) {
+ vol = buf[45 + i * 30];
+ slen = ((buf[42 + i * 30] << 8) + buf[43 + i * 30]) * 2;
+ srep = ((buf[46 + i * 30] << 8) + buf[47 + i * 30]) *2;
+ sreplen = ((buf[48 + i * 30] << 8) + buf[49 + i * 30]) * 2;
+ /* fprintf (stderr, "%d, slen: %d, %d (srep %d, sreplen %d), vol: %d\n",i, slen, srep+sreplen,srep, sreplen, vol); */
+
+ if (vol > 64)
+ return MOD_UNDEFINED;
+
+ if (buf[44 + i * 30] != 0) {
+ if (buf[44+i*30] > 15) {
+ return MOD_UNDEFINED;
+ } else {
+ finetune_used++;
+ }
+ }
+
+ if (slen > 0 && (srep + sreplen) > slen) {
+ /* Old Noisetracker /Soundtracker with repeat offset in bytes */
+ return MOD_SOUNDTRACKER24;
+ }
+
+ if (srep == 0) {
+ if (slen > 0) {
+ if (sreplen == 2){
+ has_slen_sreplen_one++;
+ }
+ if (sreplen == 0){
+ has_slen_sreplen_zero++;
+ }
+ } else {
+ if (sreplen > 0){
+ no_slen_sreplen_one++;
+ } else {
+ no_slen_sreplen_zero++;
+ }
+ if (vol > 0)
+ no_slen_has_volume++;
+ }
+ }
+ }
+
+ for (i = 0; i < 128; i++) {
+ if (buf[1080 - 130 + 2 + i] > max_pattern)
+ max_pattern = buf[1080 - 130 + 2 + i];
+ }
+
+ if (max_pattern > 100) {
+ /* pattern number can only be 0 <-> 100 for mod*/
+ return MOD_UNDEFINED;
+ }
+
+ memset (pfx, 0, sizeof (pfx));
+ memset (pfxarg, 0, sizeof (pfxarg));
+ modparsing(buf, bufsize, S31_HEADER_LENGTH-4, max_pattern, pfx, pfxarg);
+
+ /* and now for let's see if we can spot the mod */
+
+ /* FX used: */
+ /* DOC Soundtracker 2.x(2.5): 0,1,2(3,4) a,b,c,d,e,f */
+ /* Noisetracker 1.x: 0,1,2,3,4 a,b,c,d,e,f */
+ /* Noisetracker 2.x: 0,1,2,3,4 a,b,c,d,e,f */
+ /* Protracker: 0,1,2,3,4,5,6,7 9,a,b,c,d,e,f +e## */
+ /* PC tracker: 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f +e## */
+
+ for (j = 17; j <= 31; j++) {
+ if (pfx[j] != 0 || finetune_used >0) /* Extended fx used */ {
+ if (buf[0x3b7] != 0x7f && buf[0x3b7] != 0x78) {
+ return MOD_FASTTRACKER; /* Definetely Fasttracker*/
+ } else {
+ return MOD_PROTRACKER; /* Protracker*/
+ }
+ }
+ }
+
+ if ((buf[0x3b7] == 0x7f) &&
+ (has_slen_sreplen_zero <= has_slen_sreplen_one) &&
+ (no_slen_sreplen_zero <=no_slen_sreplen_one))
+ return MOD_PROTRACKER; /* Protracker */
+
+ if (buf[0x3b7] >0x7f)
+ return MOD_PTK_COMPATIBLE; /* Protracker compatible */
+
+ if ((buf[0x3b7] == 0) &&
+ (has_slen_sreplen_zero > has_slen_sreplen_one) &&
+ (no_slen_sreplen_zero > no_slen_sreplen_one)){
+ if (pfx[0x10] == 0) {
+ /* probl. Fastracker or Protracker compatible */
+ return MOD_PTK_COMPATIBLE;
+ }
+ /* FIXME: Investigate
+ else {
+ return MOD_PROTRACKER; // probl. Protracker
+ } */
+ }
+
+ if (pfx[0x05] != 0 || pfx[0x06] != 0 || pfx[0x07] != 0 ||
+ pfx[0x09] != 0) {
+ /* Protracker compatible */
+ return MOD_PTK_COMPATIBLE;
+ }
+
+ if ((buf[0x3b7] >0 && buf[0x3b7] <= buf[0x3b6]) &&
+ (has_slen_sreplen_zero <= has_slen_sreplen_one) &&
+ (no_slen_sreplen_zero == 1) &&
+ (no_slen_sreplen_zero <= no_slen_sreplen_one))
+ return MOD_NOISETRACKER12; // Noisetracker 1.2
+
+ if ((buf[0x3b7] <0x80) &&
+ (has_slen_sreplen_zero <= has_slen_sreplen_one) &&
+ (no_slen_sreplen_zero <=no_slen_sreplen_one))
+ return MOD_NOISETRACKER20; // Noisetracker 2.x
+
+ if ((buf[0x3b7] <0x80) &&
+ (pfx[0x0e] ==0) &&
+ (has_slen_sreplen_zero <= has_slen_sreplen_one) &&
+ (no_slen_sreplen_zero >=no_slen_sreplen_one))
+ return MOD_SOUNDTRACKER25_NOISETRACKER10; // Noisetracker 1.x
+
+ return MOD_PTK_COMPATIBLE; // Protracker compatible
+ }
+ }
+
+ return MOD_UNDEFINED;
+}
+
+
+static int mod15check(unsigned char *buf, size_t bufsize, size_t realfilesize,
+ const char *path)
+/* pattern parsing based on Sylvain 'Asle' Chipaux' */
+/* Modinfo-V2 */
+/* */
+/* returns: 0 for an undefined mod */
+/* 1 for a DOC Soundtracker mod */
+/* 2 for a Ultimate ST mod */
+/* 3 for a Mastersoundtracker */
+/* 4 for a SoundtrackerV2.0 -V4.0 */
+{
+ int i = 0, j = 0;
+ int slen = 0;
+ int srep = 0;
+ int sreplen = 0;
+ int vol = 0;
+
+ int noof_slen_zero_sreplen_zero = 0;
+ int noof_slen_zero_vol_zero = 0;
+ int srep_bigger_slen = 0;
+ int srep_bigger_ffff = 0;
+ int st_xy = 0;
+
+ int max_pattern = 1;
+ int pfx[32];
+ int pfxarg[32];
+
+ size_t calculated_size;
+
+ /* sanity checks */
+ if (bufsize < 0x1f3)
+ return 0; /* file too small */
+
+ if (bufsize < 2648+4 || realfilesize <2648+4) /* size 1 pattern + 1x 4 bytes Instrument :) */
+ return 0;
+
+ calculated_size = modlentest(buf, bufsize, realfilesize, S15_HEADER_LENGTH);
+ if (calculated_size == -1)
+ return 0; /* modlentest failed */
+
+ if (calculated_size != realfilesize) {
+ return 0 ;
+ }
+
+ if (calculated_size > realfilesize) {
+ fprintf(stderr, "uade: file is truncated and won't get played (%s)\n", path);
+ return 0 ;
+ }
+
+
+
+ /* check for 15 instruments */
+ if (buf[0x1d6] != 0x00 && buf[0x1d6] < 0x81 && buf[0x1f3] !=1) {
+ for (i = 0; i < 128; i++) { /* pattern list table: 128 posbl. entries */
+ max_pattern=(buf[600 - 130 + 2 + i] > max_pattern) ? buf[600 - 130 + 2 + i] : max_pattern;
+ }
+ if (max_pattern > 63)
+ return 0; /* pattern number can only be 0 <-> 63 for mod15 */
+ } else {
+ return 0;
+ }
+
+ /* parse instruments */
+ for (i = 0; i < 15; i++) {
+ vol = buf[45 + i * 30];
+ slen = ((buf[42 + i * 30] << 8) + buf[43 + i * 30]) * 2;
+ srep = ((buf[46 + i * 30] << 8) + buf[47 + i * 30]);
+ sreplen = ((buf[48 + i * 30] << 8) + buf[49 + i * 30]) * 2;
+ /* fprintf (stderr, "%d, slen: %d, %d (srep %d, sreplen %d), vol: %d\n",i, slen, srep+sreplen,srep, sreplen, vol); */
+
+ if (vol > 64 && buf[44+i*30] != 0) return 0; /* vol and finetune */
+
+ if (slen == 0) {
+
+ if (vol == 0)
+ noof_slen_zero_vol_zero++;
+
+ if (sreplen == 0 )
+ noof_slen_zero_sreplen_zero++;
+
+ } else {
+ if ((srep+sreplen) > slen)
+ srep_bigger_slen++;
+ }
+
+ /* slen < 9999 */
+ slen = (buf[42 + i * 30] << 8) + buf[43 + i * 30];
+ if (slen <= 9999) {
+ /* repeat offset + repeat size*2 < word size */
+ srep = ((buf[48 + i * 30] << 8) + buf[49 + i * 30]) * 2 +
+ ((buf[46 + i * 30] << 8) + buf[47 + i * 30]);
+ if (srep > 0xffff) srep_bigger_ffff++;
+ }
+
+ if (buf[25+i*30] ==':' && buf [22+i*30] == '-' &&
+ ((buf[20+i*30] =='S' && buf [21+i*30] == 'T') ||
+ (buf[20+i*30] =='s' && buf [21+i*30] == 't'))) st_xy++;
+ }
+
+ /* parse pattern data -> fill pfx[] with number of times fx being used*/
+ memset (pfx, 0, sizeof (pfx));
+ memset (pfxarg, 0, sizeof (pfxarg));
+
+ modparsing(buf, bufsize, S15_HEADER_LENGTH, max_pattern, pfx, pfxarg);
+
+ /* and now for let's see if we can spot the mod */
+
+/* FX used: */
+/* Ultimate ST: 0,1,2 */
+/* MasterSoundtracker: 0,1,2, c, e,f */
+/* DOC-Soundtracker V2.2: 0,1,2,a,b,c,d,e,f */
+/* Soundtracker I-VI 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f*/
+
+
+ /* Check for fx used between 0x3 <-> 0xb for some weird ST II-IV mods */
+ for (j = 0x5; j < 0xa; j++) {
+ if (pfx[j] != 0)
+ return 4; /* ST II-IV */
+ }
+
+ for (j = 0x0c; j < 0x11; j++) {
+ if (pfx[j] != 0) {
+
+ if (pfx[0x0d] != 0 && pfxarg[0x0d] != 0)
+ return 4; /* ST II-IV */
+
+ if (pfx[0x0b] != 0 || pfx[0x0d] != 0 || pfx[0x0a]!= 0 ) {
+ return 1; /* DOC ST */
+ } else {
+ if (pfxarg[1] > 0xe || pfxarg[2] > 0xe)
+ return 1; /* DOC ST */
+
+ return 3; /* Master ST */
+ }
+ }
+ }
+
+ /* pitchbend out of range ? */
+ if ((pfxarg[1] > 0 && pfxarg[1] <0x1f) ||
+ (pfxarg[2] > 0 && pfxarg [2] <0x1f) ||
+ pfx [0] >2) return 1; // ST style Arpeggio, Pitchbends ???
+
+ if (pfx[1] > 0 || pfx[2] > 0)
+ return 2; /* nope UST like fx */
+
+ /* the rest of the files has no fx. so check instruments */
+ if (st_xy!=0 && noof_slen_zero_vol_zero == 0 &&
+ noof_slen_zero_sreplen_zero == 0 && buf[0x1d7] == 120) {
+ return 3;
+ }
+
+ /* no fx, no loops... let's simply guess :)*/
+ if (srep_bigger_slen == 0 && srep_bigger_ffff == 0 &&
+ ((st_xy != 0 && buf[0x1d7] != 120 ) || st_xy==0))
+ return 2;
+
+ return 3; /* anything is played as normal soundtracker */
+}
+
+/* Reject WAV files so that uadefs doesn't cause bad behaviour */
+static int is_wav_file(unsigned char *buf, size_t size)
+{
+ if (size < WAV_HEADER_LEN)
+ return 0;
+
+ if (memcmp(buf, "RIFF", 4))
+ return 0;
+
+ if (memcmp(buf + 8, "WAVEfmt ", 8))
+ return 0;
+
+ if (memcmp(buf + 36, "data", 4))
+ return 0;
+
+ return 1;
+}
+
+void uade_filemagic(unsigned char *buf, size_t bufsize, char *pre,
+ size_t realfilesize, const char *path, int verbose)
+{
+ /* char filemagic():
+ detects formats like e.g.: tfmx1.5, hip, hipc, fc13, fc1.4
+ - tfmx 1.5 checking based on both tfmx DT and tfmxplay by jhp,
+ and the EP by Don Adan/WT.
+ - tfmx 7v checking based on info by don adan, the amore file
+ ripping description and jhp's desc of the tfmx format.
+ - other checks based on e.g. various player sources from Exotica
+ or by checking bytes with a hexeditor
+ by far not complete...
+
+ NOTE: Those Magic ID checks are quite lame compared to the checks the
+ amiga replayer do... well, after all we are not ripping. so they
+ have to do at the moment :)
+ */
+
+ int i, modtype, t;
+
+ struct modtype {
+ int e;
+ char *str;
+ };
+
+ struct modtype mod32types[] = {
+ {.e = MOD_SOUNDTRACKER25_NOISETRACKER10, .str = "MOD_NTK"},
+ {.e = MOD_NOISETRACKER12, .str = "MOD_NTK1"},
+ {.e = MOD_NOISETRACKER20, .str = "MOD_NTK2"},
+ {.e = MOD_STARTREKKER4, .str = "MOD_FLT4"},
+ {.e = MOD_STARTREKKER8, .str = "MOD_FLT8"},
+ {.e = MOD_AUDIOSCULPTURE4, .str = "MOD_ADSC4"},
+ {.e = MOD_AUDIOSCULPTURE8, .str = "MOD_ADSC8"},
+ {.e = MOD_PROTRACKER, .str = "MOD"},
+ {.e = MOD_FASTTRACKER, .str = "MOD_COMP"},
+ {.e = MOD_NOISETRACKER, .str = "MOD_NTKAMP"},
+ {.e = MOD_PTK_COMPATIBLE, .str = "MOD_COMP"},
+ {.e = MOD_SOUNDTRACKER24, .str = "MOD_DOC"},
+ {.str = NULL}
+ };
+
+ struct modtype mod15types[] = {
+ {.e = 1, .str = "MOD15"},
+ {.e = 2, .str = "MOD15_UST"},
+ {.e = 3, .str = "MOD15_MST"},
+ {.e = 4, .str = "MOD15_ST-IV"},
+ {.str = NULL}
+ };
+
+ /* Mark format unknown by default */
+ pre[0] = 0;
+
+ if (is_wav_file(buf, bufsize)) {
+ strcpy(pre, "reject");
+ return;
+ }
+
+ modtype = mod32check(buf, bufsize, realfilesize, path, verbose);
+ if (modtype != MOD_UNDEFINED) {
+ for (t = 0; mod32types[t].str != NULL; t++) {
+ if (modtype == mod32types[t].e) {
+ strcpy(pre, mod32types[t].str);
+ return;
+ }
+ }
+ }
+
+ /* 0x438 == S31_HEADER_LENGTH - 4 */
+ if (((buf[0x438] >= '1' && buf[0x438] <= '3')
+ && (buf[0x439] >= '0' && buf[0x439] <= '9') && buf[0x43a] == 'C'
+ && buf[0x43b] == 'H') || ((buf[0x438] >= '2' && buf[0x438] <= '8')
+ && buf[0x439] == 'C' && buf[0x43a] == 'H'
+ && buf[0x43b] == 'N')
+ || (buf[0x438] == 'T' && buf[0x439] == 'D' && buf[0x43a] == 'Z')
+ || (buf[0x438] == 'O' && buf[0x439] == 'C' && buf[0x43a] == 'T'
+ && buf[0x43b] == 'A') || (buf[0x438] == 'C' && buf[0x439] == 'D'
+ && buf[0x43a] == '8'
+ && buf[0x43b] == '1')) {
+ strcpy(pre, "MOD_PC"); /*Multichannel Tracker */
+
+ } else if (buf[0x2c] == 'S' && buf[0x2d] == 'C' && buf[0x2e] == 'R'
+ && buf[0x2f] == 'M') {
+ strcpy(pre, "S3M"); /*Scream Tracker */
+
+ } else if ((buf[0] == 0x60 && buf[2] == 0x60 && buf[4] == 0x48
+ && buf[5] == 0xe7) || (buf[0] == 0x60 && buf[2] == 0x60
+ && buf[4] == 0x41 && buf[5] == 0xfa)
+ || (buf[0] == 0x60 && buf[1] == 0x00 && buf[4] == 0x60
+ && buf[5] == 0x00 && buf[8] == 0x48 && buf[9] == 0xe7)
+ || (buf[0] == 0x60 && buf[1] == 0x00 && buf[4] == 0x60
+ && buf[5] == 0x00 && buf[8] == 0x60 && buf[9] == 0x00
+ && buf[12] == 0x60 && buf[13] == 0x00 && buf[16] == 0x48
+ && buf[17] == 0xe7)) {
+ strcpy(pre, "SOG"); /* Hippel */
+
+ } else if (buf[0x348] == '.' && buf[0x349] == 'Z' && buf[0x34A] == 'A'
+ && buf[0x34B] == 'D' && buf[0x34c] == 'S' && buf[0x34d] == '8'
+ && buf[0x34e] == '9' && buf[0x34f] == '.') {
+ strcpy(pre, "MKII"); /* Mark II */
+
+ } else if (read_be_u16(&buf[0x00]) == 0x2b7c &&
+ read_be_u16(&buf[0x08]) == 0x2b7c &&
+ read_be_u16(&buf[0x10]) == 0x2b7c &&
+ read_be_u16(&buf[0x18]) == 0x2b7c &&
+ read_be_u32(&buf[0x20]) == 0x303c00ff &&
+ read_be_u32(&buf[0x24]) == 0x32004eb9 &&
+ read_be_u16(&buf[0x2c]) == 0x4e75) {
+ strcpy(pre, "JPO"); /* Steve Turner*/
+
+ } else if (((buf[0] == 0x08 && buf[1] == 0xf9 && buf[2] == 0x00
+ && buf[3] == 0x01) && (buf[4] == 0x00 && buf[5] == 0xbb
+ && buf[6] == 0x41 && buf[7] == 0xfa)
+ && ((buf[0x25c] == 0x4e && buf[0x25d] == 0x75)
+ || (buf[0x25c] == 0x4e && buf[0x25d] == 0xf9)))
+ || ((buf[0] == 0x41 && buf[1] == 0xfa)
+ && (buf[4] == 0xd1 && buf[5] == 0xe8)
+ && (((buf[0x230] == 0x4e && buf[0x231] == 0x75)
+ || (buf[0x230] == 0x4e && buf[0x231] == 0xf9))
+ || ((buf[0x29c] == 0x4e && buf[0x29d] == 0x75)
+ || (buf[0x29c] == 0x4e && buf[0x29d] == 0xf9))
+ ))) {
+ strcpy(pre, "SID1"); /* SidMon1 */
+
+ } else if (buf[0] == 0x4e && buf[1] == 0xfa &&
+ buf[4] == 0x4e && buf[5] == 0xfa &&
+ buf[8] == 0x4e && buf[9] == 0xfa &&
+ buf[2] == 0x00 && buf[6] == 0x06 && buf[10] == 0x07) {
+ if (buf[3] == 0x2a && buf[7] == 0xfc && buf[11] == 0x7c) {
+ strcpy(pre, "SA_old");
+ } else if (buf[3] == 0x1a && buf[7] == 0xc6 && buf[11] == 0x3a) {
+ strcpy(pre, "SA");
+ }
+
+ } else if (buf[0] == 0x4e && buf[1] == 0xfa &&
+ buf[4] == 0x4e && buf[5] == 0xfa &&
+ buf[8] == 0x4e && buf[9] == 0xfa &&
+ buf[0xc] == 0x4e && buf[0xd] == 0xfa) {
+ for (i = 0x10; i < 256; i = i + 2) {
+ if (buf[i + 0] == 0x4e && buf[i + 1] == 0x75 && buf[i + 2] == 0x47
+ && buf[i + 3] == 0xfa && buf[i + 12] == 0x4e && buf[i + 13] == 0x75) {
+ strcpy(pre, "FRED"); /* FRED */
+ break;
+ }
+ }
+
+ } else if (buf[0] == 0x60 && buf[1] == 0x00 &&
+ buf[4] == 0x60 && buf[5] == 0x00 &&
+ buf[8] == 0x60 && buf[9] == 0x00 &&
+ buf[12] == 0x48 && buf[13] == 0xe7) {
+ strcpy(pre, "MA"); /*Music Assembler */
+
+ } else if (buf[0] == 0x00 && buf[1] == 0x00 &&
+ buf[2] == 0x00 && buf[3] == 0x28 &&
+ (buf[7] >= 0x34 && buf[7] <= 0x64) &&
+ buf[0x20] == 0x21 && (buf[0x21] == 0x54 || buf[0x21] == 0x44)
+ && buf[0x22] == 0xff && buf[0x23] == 0xff) {
+ strcpy(pre, "SA-P"); /*SonicArranger Packed */
+
+
+ } else if (buf[0] == 0x4e && buf[1] == 0xfa &&
+ buf[4] == 0x4e && buf[5] == 0xfa &&
+ buf[8] == 0x4e && buf[9] == 0xfa) {
+ t = ((buf[2] * 256) + buf[3]);
+ if (t < bufsize - 9) {
+ if (buf[2 + t] == 0x4b && buf[3 + t] == 0xfa &&
+ buf[6 + t] == 0x08 && buf[7 + t] == 0xad && buf[8 + t] == 0x00
+ && buf[9 + t] == 0x00) {
+ strcpy(pre, "MON"); /*M.O.N */
+ }
+ }
+
+ } else if (buf[0] == 0x02 && buf[1] == 0x39 &&
+ buf[2] == 0x00 && buf[3] == 0x01 &&
+ buf[8] == 0x66 && buf[9] == 0x02 &&
+ buf[10] == 0x4e && buf[11] == 0x75 &&
+ buf[12] == 0x78 && buf[13] == 0x00 &&
+ buf[14] == 0x18 && buf[15] == 0x39) {
+ strcpy(pre, "MON_old"); /*M.O.N_old */
+
+ } else if (buf[0] == 0x48 && buf[1] == 0xe7 && buf[2] == 0xf1
+ && buf[3] == 0xfe && buf[4] == 0x61 && buf[5] == 0x00) {
+ t = ((buf[6] * 256) + buf[7]);
+ if (t < (bufsize - 17)) {
+ for (i = 0; i < 10; i = i + 2) {
+ if (buf[6 + t + i] == 0x47 && buf[7 + t + i] == 0xfa) {
+ strcpy(pre, "DW"); /*Whittaker Type1... FIXME: incomplete */
+ }
+ }
+ }
+
+ } else if (buf[0] == 0x13 && buf[1] == 0xfc &&
+ buf[2] == 0x00 && buf[3] == 0x40 &&
+ buf[8] == 0x4e && buf[9] == 0x71 &&
+ buf[10] == 0x04 && buf[11] == 0x39 &&
+ buf[12] == 0x00 && buf[13] == 0x01 &&
+ buf[18] == 0x66 && buf[19] == 0xf4 &&
+ buf[20] == 0x4e && buf[21] == 0x75 &&
+ buf[22] == 0x48 && buf[23] == 0xe7 &&
+ buf[24] == 0xff && buf[25] == 0xfe) {
+ strcpy(pre, "EX"); /*Fashion Tracker */
+
+/* Magic ID */
+ } else if (buf[0x3a] == 'S' && buf[0x3b] == 'I' && buf[0x3c] == 'D' &&
+ buf[0x3d] == 'M' && buf[0x3e] == 'O' && buf[0x3f] == 'N' &&
+ buf[0x40] == ' ' && buf[0x41] == 'I' && buf[0x42] == 'I') {
+ strcpy(pre, "SID2"); /* SidMon II */
+
+ } else if (buf[0x28] == 'R' && buf[0x29] == 'O' && buf[0x2a] == 'N' &&
+ buf[0x2b] == '_' && buf[0x2c] == 'K' && buf[0x2d] == 'L' &&
+ buf[0x2e] == 'A' && buf[0x2f] == 'R' && buf[0x30] == 'E' &&
+ buf[0x31] == 'N') {
+ strcpy(pre, "CM"); /* Ron Klaren (CustomMade) */
+
+ } else if (buf[0x3e] == 'A' && buf[0x3f] == 'C' && buf[0x40] == 'T'
+ && buf[0x41] == 'I' && buf[0x42] == 'O' && buf[0x43] == 'N'
+ && buf[0x44] == 'A' && buf[0x45] == 'M') {
+ strcpy(pre, "AST"); /*Actionanamics */
+
+ } else if (buf[26] == 'V' && buf[27] == '.' && buf[28] == '2') {
+ strcpy(pre, "BP"); /* Soundmon V2 */
+
+ } else if (buf[26] == 'V' && buf[27] == '.' && buf[28] == '3') {
+ strcpy(pre, "BP3"); /* Soundmon V2.2 */
+
+ } else if (buf[60] == 'S' && buf[61] == 'O' && buf[62] == 'N'
+ && buf[63] == 'G') {
+ strcpy(pre, "SFX13"); /* Sfx 1.3-1.8 */
+
+ } else if (buf[124] == 'S' && buf[125] == 'O' && buf[126] == '3'
+ && buf[127] == '1') {
+ strcpy(pre, "SFX20"); /* Sfx 2.0 */
+
+ } else if (buf[0x1a] == 'E' && buf[0x1b] == 'X' && buf[0x1c] == 'I'
+ && buf[0x1d] == 'T') {
+ strcpy(pre, "AAM"); /*Audio Arts & Magic */
+ } else if (buf[8] == 'E' && buf[9] == 'M' && buf[10] == 'O'
+ && buf[11] == 'D' && buf[12] == 'E' && buf[13] == 'M'
+ && buf[14] == 'I' && buf[15] == 'C') {
+ strcpy(pre, "EMOD"); /* EMOD */
+
+ /* generic ID Check at offset 0x24 */
+
+ } else if (chk_id_offset(buf, bufsize, offset_0024_patterns, 0x24, pre)) {
+
+ /* HIP7 ID Check at offset 0x04 */
+ } else if (patterntest(buf, " **** Player by Jochen Hippel 1990 **** ",
+ 0x04, 40, bufsize)) {
+ strcpy(pre, "S7G"); /* HIP7 */
+
+ /* Magic ID at Offset 0x00 */
+ } else if (buf[0] == 'M' && buf[1] == 'M' && buf[2] == 'D') {
+ if (buf[0x3] >= '0' && buf[0x3] < '3') {
+ /*move.l mmd_songinfo(a0),a1 */
+ int s = (buf[8] << 24) + (buf[9] << 16) + (buf[0xa] << 8) + buf[0xb];
+ if (((int) buf[s + 767]) & (1 << 6)) { /* btst #6, msng_flags(a1); */
+ strcpy(pre, "OCTAMED");
+ /*OCTAMED*/} else {
+ strcpy(pre, "MED");
+ /*MED*/}
+ } else if (buf[0x3] != 'C') {
+ strcpy(pre, "MMD3"); /* mmd3 and above */
+ }
+
+ /* all TFMX format tests here */
+ } else if (tfmxtest(buf, bufsize, pre)) {
+ /* is TFMX, nothing to do here ('pre' set in tfmxtest() */
+
+ } else if (buf[0] == 'T' && buf[1] == 'H' && buf[2] == 'X') {
+ if ((buf[3] == 0x00) || (buf[3] == 0x01)) {
+ strcpy(pre, "AHX"); /* AHX */
+ }
+
+ } else if (buf[1] == 'M' && buf[2] == 'U' && buf[3] == 'G'
+ && buf[4] == 'I' && buf[5] == 'C' && buf[6] == 'I'
+ && buf[7] == 'A' && buf[8] == 'N') {
+ if (buf[9] == '2') {
+ strcpy(pre, "MUG2"); /* Digimugi2 */
+ } else {
+ strcpy(pre, "MUG"); /* Digimugi */
+ }
+
+ } else if (buf[0] == 'L' && buf[1] == 'M' && buf[2] == 'E' && buf[3] == 0x00) {
+ strcpy(pre, "LME"); /* LegLess */
+
+ } else if (buf[0] == 'P' && buf[1] == 'S' && buf[2] == 'A' && buf[3] == 0x00) {
+ strcpy(pre, "PSA"); /* PSA */
+
+ } else if ((buf[0] == 'S' && buf[1] == 'y' && buf[2] == 'n' && buf[3] == 't'
+ && buf[4] == 'h' && buf[6] == '.' && buf[8] == 0x00)
+ && (buf[5] > '1' && buf[5] < '4')) {
+ strcpy(pre, "SYN"); /* Synthesis */
+
+ } else if (buf[0xbc6] == '.' && buf[0xbc7] == 'F' && buf[0xbc8] == 'N'
+ && buf[0xbc9] == 'L') {
+ strcpy(pre, "DM2"); /* Delta 2.0 */
+
+ } else if (buf[0] == 'R' && buf[1] == 'J' && buf[2] == 'P') {
+
+ if (buf[4] == 'S' && buf[5] == 'M' && buf[6] == 'O' && buf[7] == 'D') {
+ strcpy(pre, "RJP"); /* Vectordean (Richard Joseph Player) */
+ } else {
+ strcpy(pre, ""); /* but don't play .ins files */
+ }
+ } else if (buf[0] == 'F' && buf[1] == 'O' && buf[2] == 'R' && buf[3] == 'M') {
+ if (buf[8] == 'S' && buf[9] == 'M' && buf[10] == 'U' && buf[11] == 'S') {
+ strcpy(pre, "SMUS"); /* Sonix */
+ }
+ // } else if (buf[0x00] == 0x00 && buf[0x01] == 0xfe &&
+ // buf[0x30] == 0x00 && buf[0x31] ==0x00 && buf[0x32] ==0x01 && buf[0x33] ==0x40 &&
+ // realfilesize > 332 ){
+ // }
+ // strcpy (pre, "SMUS"); /* Tiny Sonix*/
+
+ } else if (tronictest(buf, bufsize)) {
+ strcpy(pre, "TRONIC"); /* Tronic */
+
+ /* generic ID Check at offset 0x00 */
+ } else if (chk_id_offset(buf, bufsize, offset_0000_patterns, 0x00, pre)) {
+
+ /*magic ids of some modpackers */
+ } else if (buf[0x438] == 'P' && buf[0x439] == 'W' && buf[0x43a] == 'R'
+ && buf[0x43b] == 0x2e) {
+ strcpy(pre, "PPK"); /*Polkapacker */
+
+ } else if (buf[0x100] == 'S' && buf[0x101] == 'K' && buf[0x102] == 'Y'
+ && buf[0x103] == 'T') {
+ strcpy(pre, "SKT"); /*Skytpacker */
+
+ } else if ((buf[0x5b8] == 'I' && buf[0x5b9] == 'T' && buf[0x5ba] == '1'
+ && buf[0x5bb] == '0') || (buf[0x5b8] == 'M' && buf[0x5b9] == 'T'
+ && buf[0x5ba] == 'N'
+ && buf[0x5bb] == 0x00)) {
+ strcpy(pre, "ICE"); /*Ice/Soundtracker 2.6 */
+
+ } else if (buf[0x3b8] == 'K' && buf[0x3b9] == 'R' && buf[0x3ba] == 'I'
+ && buf[0x3bb] == 'S') {
+ strcpy(pre, "KRIS"); /*Kristracker */
+
+ } else if (buf[0] == 'X' && buf[1] == 'P' && buf[2] == 'K' && buf[3] == 'F'&&
+ read_be_u32(&buf[4]) + 8 == realfilesize &&
+ buf[8] == 'S' && buf[9] == 'Q' && buf[10] == 'S' && buf[11] == 'H') {
+ fprintf(stderr, "uade: The file is SQSH packed. Please depack first.\n");
+ strcpy(pre, "packed");
+
+ } else if ((modtype = mod15check(buf, bufsize, realfilesize, path)) != 0) {
+ for (t = 0; mod15types[t].str != NULL; t++) {
+ if (modtype == mod15types[t].e) {
+ strcpy(pre, mod15types[t].str);
+ return;
+ }
+ }
+
+ /* Custom file check */
+ } else if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x03
+ && buf[3] == 0xf3) {
+ /*CUSTOM*/ i = (buf[0x0b] * 4) + 0x1c; /* beginning of first chunk */
+
+ if (i < bufsize - 0x42) {
+
+ t = 0;
+ /* unfort. we can't always assume: moveq #-1,d0 rts before "delirium" */
+ /* search 0x40 bytes from here, (enough?) */
+ while ((buf[i + t + 0] != 'D' && buf[i + t + 1] != 'E'
+ && buf[i + t + 2] != 'L' && buf[i + t + 3] != 'I')
+ && (t < 0x40)) {
+ t++;
+ }
+
+ if (t < 0x40) {
+ /* longword after Delirium is rel. offset from first chunk
+ where "hopefully" the delitags are */
+ int s = (buf[i + t + 10] * 256) + buf[i + t + 11] + i; /* 64K */
+ if (s < bufsize - 0x33) {
+ for (i = 0; i < 0x30; i = i + 4) {
+ if (buf[i + s + 0] == 0x80 && buf[i + s + 1] == 0x00 &&
+ buf[i + s + 2] == 0x44 && buf[i + s + 3] == 0x55) {
+ strcpy(pre, "CUST"); /* CUSTOM */
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ } else if (buf[12] == 0x00) {
+ int s = (buf[12] * 256 + buf[13] + 1) * 14;
+ if (s < (bufsize - 91)) {
+ if (buf[80 + s] == 'p' && buf[81 + s] == 'a' && buf[82 + s] == 't'
+ && buf[83 + s] == 't' && buf[87 + s] == 32 && buf[88 + s] == 'p'
+ && buf[89 + s] == 'a' && buf[90 + s] == 't' && buf[91 + s] == 't') {
+ strcpy(pre, "PUMA"); /* Pumatracker */
+ }
+ }
+ }
+}
+
+
+/* We are currently stupid and check only for a few magic IDs at the offsets
+ * chk_id_offset returns 1 on success and sets the right prefix/extension
+ * in pre
+ * TODO: more and less easy check for the rest of the 52 trackerclones
+ */
+static int chk_id_offset(unsigned char *buf, int bufsize,
+ const char *patterns[], int offset, char *pre)
+{
+ int i;
+ for (i = 0; patterns[i]; i = i + 2) {
+ if (patterntest(buf, patterns[i], offset, strlen(patterns[i]), bufsize)) {
+ /* match found */
+ strcpy(pre, patterns[i + 1]);
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.h
new file mode 100644
index 00000000..45e5cd48
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.h
@@ -0,0 +1,9 @@
+#ifndef _UADE_AMIFILEMAGIC_H_
+#define _UADE_AMIFILEMAGIC_H_
+
+#include <stdio.h>
+
+void uade_filemagic(unsigned char *buf, size_t bufsize, char *pre,
+ size_t realfilesize, const char *path, int verbose);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c
new file mode 100644
index 00000000..7c8dfce2
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c
@@ -0,0 +1,502 @@
+/*
+ * Loads contents of 'eagleplayer.conf'. The file formats are
+ * specified in doc/uade123.1.
+ *
+ * Copyright 2005-2007 Heikki Orsila <heikki.orsila@iki.fi>
+ *
+ * This source code module is dual licensed under GPL and Public Domain.
+ * Hence you may use _this_ module (not another code module) in any you
+ * want in your projects.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdint.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "eagleplayer.h"
+#include "ossupport.h"
+#include "amifilemagic.h"
+#include "uadeconf.h"
+#include "unixatomic.h"
+#include "songdb.h"
+#include "support.h"
+#include "uadestate.h"
+
+
+#define OPTION_DELIMITER ","
+
+#define MAX_SUFFIX_LENGTH 16
+
+#define eperror(fmt, args...) do { uadeerror("Eagleplayer.conf error on line %zd: " fmt, lineno, ## args); } while (0)
+
+
+/* Table for associating eagleplayer.conf, song.conf and uade.conf options
+ * together.
+ */
+const struct epconfattr epconf[] = {
+ {.s = "a500", .e = ES_A500, .o = UC_FILTER_TYPE, .c = "a500"},
+ {.s = "a1200", .e = ES_A1200, .o = UC_FILTER_TYPE, .c = "a1200"},
+ {.s = "always_ends", .e = ES_ALWAYS_ENDS, .o = UC_DISABLE_TIMEOUTS},
+ {.s = "broken_song_end", .e = ES_BROKEN_SONG_END, .o = UC_NO_EP_END},
+ {.s = "detect_format_by_content", .e = ES_CONTENT_DETECTION, .o = UC_CONTENT_DETECTION},
+ {.s = "detect_format_by_name", .e = ES_NAME_DETECTION, .o = 0},
+ {.s = "ignore_player_check",.e = ES_IGNORE_PLAYER_CHECK, .o = UC_IGNORE_PLAYER_CHECK},
+ {.s = "led_off", .e = ES_LED_OFF, .o = UC_FORCE_LED_OFF},
+ {.s = "led_on", .e = ES_LED_ON, .o = UC_FORCE_LED_ON},
+ {.s = "never_ends", .e = ES_NEVER_ENDS, .o = 0},
+ {.s = "no_ep_end_detect", .e = ES_BROKEN_SONG_END, .o = UC_NO_EP_END},
+ {.s = "no_filter", .e = ES_NO_FILTER, .o = UC_NO_FILTER},
+ {.s = "no_headphones", .e = ES_NO_HEADPHONES, .o = UC_NO_HEADPHONES},
+ {.s = "no_panning", .e = ES_NO_PANNING, .o = UC_NO_PANNING},
+ {.s = "no_postprocessing", .e = ES_NO_POSTPROCESSING, .o = UC_NO_POSTPROCESSING},
+ {.s = "ntsc", .e = ES_NTSC, .o = UC_NTSC},
+ {.s = "one_subsong", .e = ES_ONE_SUBSONG, .o = UC_ONE_SUBSONG},
+ {.s = "pal", .e = ES_PAL, .o = UC_PAL},
+ {.s = "reject", .e = ES_REJECT, .o = 0},
+ {.s = "speed_hack", .e = ES_SPEED_HACK, .o = UC_SPEED_HACK},
+ {.s = NULL}
+};
+
+
+/* Variables for eagleplayer.conf and song.conf */
+static const struct epconfattr epconf_variables[] = {
+ {.s = "epopt", .t = UA_STRING, .e = ES_EP_OPTION},
+ {.s = "gain", .t = UA_STRING, .e = ES_GAIN},
+ {.s = "interpolator", .t = UA_STRING, .e = ES_RESAMPLER},
+ {.s = "panning", .t = UA_STRING, .e = ES_PANNING},
+ {.s = "player", .t = UA_STRING, .e = ES_PLAYER},
+ {.s = "resampler", .t = UA_STRING, .e = ES_RESAMPLER},
+ {.s = "silence_timeout", .t = UA_STRING, .e = ES_SILENCE_TIMEOUT},
+ {.s = "subsong_timeout", .t = UA_STRING, .e = ES_SUBSONG_TIMEOUT},
+ {.s = "subsongs", .t = UA_STRING, .e = ES_SUBSONGS},
+ {.s = "timeout", .t = UA_STRING, .e = ES_TIMEOUT},
+ {.s = NULL}
+};
+
+
+static int ufcompare(const void *a, const void *b);
+static struct eagleplayerstore *read_eagleplayer_conf(const char *filename);
+
+
+static struct eagleplayer *get_eagleplayer(const char *extension,
+ struct eagleplayerstore *playerstore);
+
+
+static int load_playerstore(struct uade_state *state)
+{
+ static int warnings = 1;
+ char formatsfile[PATH_MAX];
+
+ if (state->playerstore == NULL) {
+ snprintf(formatsfile, sizeof(formatsfile),
+ "%s/eagleplayer.conf", state->config.basedir.name);
+
+ state->playerstore = read_eagleplayer_conf(formatsfile);
+ if (state->playerstore == NULL) {
+ if (warnings) {
+ fprintf(stderr, "Tried to load eagleplayer.conf from %s, but failed\n", formatsfile);
+ }
+ warnings = 0;
+ return 0;
+ }
+
+ if (state->config.verbose)
+ fprintf(stderr, "Loaded eagleplayer.conf: %s\n",
+ formatsfile);
+ }
+
+ return 1;
+}
+
+
+static struct eagleplayer *analyze_file_format(int *content,
+ const char *modulename,
+ struct uade_state *state)
+{
+ struct stat st;
+ char ext[MAX_SUFFIX_LENGTH];
+ FILE *f;
+ struct eagleplayer *contentcandidate = NULL;
+ struct eagleplayer *namecandidate = NULL;
+ char *prefix, *postfix, *t;
+ size_t bufsize, bytesread;
+ uint8_t buf[8192];
+
+ *content = 0;
+
+ if ((f = fopen(modulename, "rb")) == NULL)
+ return NULL;
+
+ if (fstat(fileno(f), &st))
+ uadeerror("Very weird stat error: %s (%s)\n", modulename, strerror(errno));
+
+ bufsize = sizeof buf;
+ bytesread = atomic_fread(buf, 1, bufsize, f);
+ fclose(f);
+ if (bytesread == 0)
+ return NULL;
+ memset(&buf[bytesread], 0, bufsize - bytesread);
+
+ uade_filemagic(buf, bytesread, ext, st.st_size, modulename, state->config.verbose);
+
+ if (strcmp(ext, "reject") == 0)
+ return NULL;
+
+ if (ext[0] != 0 && state->config.verbose)
+ fprintf(stderr, "Content recognized: %s (%s)\n", ext, modulename);
+
+ if (strcmp(ext, "packed") == 0)
+ return NULL;
+
+ if (!load_playerstore(state))
+ return NULL;
+
+ /* First do filename detection (we'll later do content detection) */
+ t = xbasename(modulename);
+
+ if (strlcpy((char *) buf, t, sizeof buf) >= sizeof buf)
+ return NULL;
+
+ t = strchr((char *) buf, '.');
+ if (t == NULL)
+ return NULL;
+
+ *t = 0;
+ prefix = (char *) buf;
+
+ if (strlen(prefix) < MAX_SUFFIX_LENGTH)
+ namecandidate = get_eagleplayer(prefix, state->playerstore);
+
+ if (namecandidate == NULL) {
+ /* Try postfix */
+ t = xbasename(modulename);
+ strlcpy((char *) buf, t, sizeof buf);
+ postfix = strrchr((char *) buf, '.') + 1; /* postfix != NULL */
+
+ if (strlen(postfix) < MAX_SUFFIX_LENGTH)
+ namecandidate = get_eagleplayer(postfix, state->playerstore);
+ }
+
+ /* If filemagic found a match, we'll use player plugins associated with
+ that extension */
+ if (ext[0]) {
+ contentcandidate = get_eagleplayer(ext, state->playerstore);
+ if (contentcandidate != NULL) {
+ /* Do not recognize name detectable eagleplayers by
+ content */
+ if (namecandidate == NULL ||
+ (namecandidate->flags & ES_NAME_DETECTION) == 0) {
+ *content = 1;
+ return contentcandidate;
+ }
+ } else {
+ if (state->config.verbose)
+ fprintf(stderr, "%s not in eagleplayer.conf\n", ext);
+ }
+ }
+
+ if (state->config.verbose)
+ fprintf(stderr, "Format detection by filename\n");
+
+ return namecandidate;
+}
+
+
+static void handle_attribute(struct uade_attribute **attributelist,
+ const struct epconfattr *attr,
+ char *item, size_t len, size_t lineno)
+{
+ struct uade_attribute *a;
+ char *str, *endptr;
+ int success = 0;
+
+ if (item[len] != '=') {
+ fprintf(stderr, "Invalid song item: %s\n", item);
+ return;
+ }
+ str = item + len + 1;
+
+ if ((a = calloc(1, sizeof *a)) == NULL)
+ eperror("No memory for song attribute.\n");
+
+ switch (attr->t) {
+ case UA_DOUBLE:
+ a->d = strtod(str, &endptr);
+ if (*endptr == 0)
+ success = 1;
+ break;
+ case UA_INT:
+ a->i = strtol(str, &endptr, 10);
+ if (*endptr == 0)
+ success = 1;
+ break;
+ case UA_STRING:
+ a->s = strdup(str);
+ if (a->s == NULL)
+ eperror("Out of memory allocating string option for song\n");
+ success = 1;
+ break;
+ default:
+ fprintf(stderr, "Unknown song option: %s\n",
+ item);
+ break;
+ }
+
+ if (success) {
+ a->type = attr->e;
+ a->next = *attributelist;
+ *attributelist = a;
+ } else {
+ fprintf(stderr, "Invalid song option: %s\n", item);
+ free(a);
+ }
+}
+
+
+int uade_song_and_player_attribute(struct uade_attribute **attributelist,
+ int *flags, char *item, size_t lineno)
+{
+ size_t i, len;
+
+ for (i = 0; epconf[i].s != NULL; i++) {
+ if (strcasecmp(item, epconf[i].s) == 0) {
+ *flags |= epconf[i].e;
+ return 1;
+ }
+ }
+
+ for (i = 0; epconf_variables[i].s != NULL; i++) {
+ len = strlen(epconf_variables[i].s);
+ if (strncasecmp(item, epconf_variables[i].s, len) != 0)
+ continue;
+
+ handle_attribute(attributelist, &epconf_variables[i],
+ item, len, lineno);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Compare function for bsearch() and qsort() to sort eagleplayers with
+ respect to name extension. */
+static int ufcompare(const void *a, const void *b)
+{
+ const struct eagleplayermap *ua = a;
+ const struct eagleplayermap *ub = b;
+
+ return strcasecmp(ua->extension, ub->extension);
+}
+
+int uade_is_our_file(const char *modulename, int scanmode,
+ struct uade_state *state)
+{
+ int content;
+ struct eagleplayer *ep;
+
+ ep = analyze_file_format(&content, modulename, state);
+
+ if (!scanmode)
+ state->ep = ep;
+
+ if (ep == NULL)
+ return 0;
+
+ if (content)
+ return 1;
+
+ if (state->config.content_detection && content == 0)
+ return 0;
+
+ if ((ep->flags & ES_CONTENT_DETECTION) != 0)
+ return 0;
+
+ return 1;
+}
+
+static struct eagleplayer *get_eagleplayer(const char *extension,
+ struct eagleplayerstore *ps)
+{
+ struct eagleplayermap *uf = ps->map;
+ struct eagleplayermap *f;
+ struct eagleplayermap key = {.extension = (char *)extension };
+
+ f = bsearch(&key, uf, ps->nextensions, sizeof(uf[0]), ufcompare);
+ if (f == NULL)
+ return NULL;
+
+ return f->player;
+}
+
+/* Read eagleplayer.conf. */
+static struct eagleplayerstore *read_eagleplayer_conf(const char *filename)
+{
+ FILE *f;
+ struct eagleplayer *p;
+ size_t allocated;
+ size_t lineno = 0;
+ struct eagleplayerstore *ps = NULL;
+ size_t exti;
+ size_t i, j;
+ int epwarning;
+
+ f = fopen(filename, "r");
+ if (f == NULL)
+ goto error;
+
+ ps = calloc(1, sizeof ps[0]);
+ if (ps == NULL)
+ eperror("No memory for ps.");
+
+ allocated = 16;
+ if ((ps->players = malloc(allocated * sizeof(ps->players[0]))) == NULL)
+ eperror("No memory for eagleplayer.conf file.\n");
+
+ while (1) {
+ char **items;
+ size_t nitems;
+
+ items = read_and_split_lines(&nitems, &lineno, f, UADE_WS_DELIMITERS);
+ if (items == NULL)
+ break;
+
+ assert(nitems > 0);
+
+ if (ps->nplayers == allocated) {
+ allocated *= 2;
+ ps->players = realloc(ps->players, allocated * sizeof(ps->players[0]));
+ if (ps->players == NULL)
+ eperror("No memory for players.");
+ }
+
+ p = &ps->players[ps->nplayers];
+ ps->nplayers++;
+
+ memset(p, 0, sizeof p[0]);
+
+ p->playername = strdup(items[0]);
+ if (p->playername == NULL)
+ uadeerror("No memory for playername.\n");
+
+ for (i = 1; i < nitems; i++) {
+
+ if (strncasecmp(items[i], "prefixes=", 9) == 0) {
+ char prefixes[UADE_LINESIZE];
+ char *prefixstart = items[i] + 9;
+ char *sp, *s;
+ size_t pos;
+
+ assert(p->nextensions == 0 && p->extensions == NULL);
+
+ p->nextensions = 0;
+ strlcpy(prefixes, prefixstart,
+ sizeof(prefixes));
+ sp = prefixes;
+ while ((s = strsep(&sp, OPTION_DELIMITER)) != NULL) {
+ if (*s == 0)
+ continue;
+ p->nextensions++;
+ }
+
+ p->extensions =
+ malloc((p->nextensions +
+ 1) * sizeof(p->extensions[0]));
+ if (p->extensions == NULL)
+ eperror("No memory for extensions.");
+
+ pos = 0;
+ sp = prefixstart;
+ while ((s = strsep(&sp, OPTION_DELIMITER)) != NULL) {
+ if (*s == 0)
+ continue;
+
+ p->extensions[pos] = strdup(s);
+ if (s == NULL)
+ eperror("No memory for prefix.");
+ pos++;
+ }
+ p->extensions[pos] = NULL;
+ assert(pos == p->nextensions);
+
+ continue;
+ }
+
+ if (strncasecmp(items[i], "comment:", 7) == 0)
+ break;
+
+ if (uade_song_and_player_attribute(&p->attributelist, &p->flags, items[i], lineno))
+ continue;
+
+ fprintf(stderr, "Unrecognized option: %s\n", items[i]);
+ }
+
+ for (i = 0; items[i] != NULL; i++)
+ free(items[i]);
+
+ free(items);
+ }
+
+ fclose(f);
+
+ if (ps->nplayers == 0) {
+ free(ps->players);
+ free(ps);
+ return NULL;
+ }
+
+ for (i = 0; i < ps->nplayers; i++)
+ ps->nextensions += ps->players[i].nextensions;
+
+ ps->map = malloc(sizeof(ps->map[0]) * ps->nextensions);
+ if (ps->map == NULL)
+ eperror("No memory for extension map.");
+
+ exti = 0;
+ epwarning = 0;
+ for (i = 0; i < ps->nplayers; i++) {
+ p = &ps->players[i];
+ if (p->nextensions == 0) {
+ if (epwarning == 0) {
+ fprintf(stderr,
+ "uade warning: %s eagleplayer lacks prefixes in "
+ "eagleplayer.conf, which makes it unusable for any kind of "
+ "file type detection. If you don't want name based file type "
+ "detection for a particular format, use content_detection "
+ "option for the line in eagleplayer.conf.\n",
+ ps->players[i].playername);
+ epwarning = 1;
+ }
+ continue;
+ }
+ for (j = 0; j < p->nextensions; j++) {
+ assert(exti < ps->nextensions);
+ ps->map[exti].player = p;
+ ps->map[exti].extension = p->extensions[j];
+ exti++;
+ }
+ }
+
+ assert(exti == ps->nextensions);
+
+ /* Make the extension map bsearch() ready */
+ qsort(ps->map, ps->nextensions, sizeof(ps->map[0]), ufcompare);
+
+ return ps;
+
+ error:
+ if (ps)
+ free(ps->players);
+ free(ps);
+ if (f != NULL)
+ fclose(f);
+ return NULL;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.h
new file mode 100644
index 00000000..fc3497b5
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.h
@@ -0,0 +1,127 @@
+#ifndef _UADE_EAGLEPLAYER_H_
+#define _UADE_EAGLEPLAYER_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include "uadeconfstructure.h"
+
+/* We maintain alphabetical order even if that forces us to renumber bits
+ when a new option is added */
+#define ES_A1200 (1 << 0)
+#define ES_A500 (1 << 1)
+#define ES_ALWAYS_ENDS (1 << 2)
+#define ES_BROKEN_SONG_END (1 << 3)
+#define ES_CONTENT_DETECTION (1 << 4)
+#define ES_EP_OPTION (1 << 5)
+#define ES_GAIN (1 << 6)
+#define ES_IGNORE_PLAYER_CHECK (1 << 7)
+#define ES_LED_OFF (1 << 8)
+#define ES_LED_ON (1 << 9)
+#define ES_NAME_DETECTION (1 << 10)
+#define ES_NEVER_ENDS (1 << 11)
+#define ES_NO_FILTER (1 << 12)
+#define ES_NO_HEADPHONES (1 << 13)
+#define ES_NO_PANNING (1 << 14)
+#define ES_NO_POSTPROCESSING (1 << 15)
+#define ES_NTSC (1 << 16)
+#define ES_ONE_SUBSONG (1 << 17)
+#define ES_PAL (1 << 18)
+#define ES_PANNING (1 << 19)
+#define ES_PLAYER (1 << 20)
+#define ES_REJECT (1 << 21)
+#define ES_RESAMPLER (1 << 22)
+#define ES_SILENCE_TIMEOUT (1 << 23)
+#define ES_SPEED_HACK (1 << 24)
+#define ES_SUBSONGS (1 << 25)
+#define ES_SUBSONG_TIMEOUT (1 << 26)
+#define ES_TIMEOUT (1 << 27)
+
+#define UADE_WS_DELIMITERS " \t\n"
+
+struct eagleplayer {
+ char *playername;
+ size_t nextensions;
+ char **extensions;
+ int flags;
+ struct uade_attribute *attributelist;
+};
+
+struct eagleplayermap {
+ char *extension;
+ struct eagleplayer *player;
+};
+
+struct eagleplayerstore {
+ size_t nplayers;
+ struct eagleplayer *players;
+ size_t nextensions;
+ struct eagleplayermap *map;
+};
+
+enum uade_attribute_type {
+ UA_STRING = 1,
+ UA_INT,
+ UA_DOUBLE
+};
+
+struct uade_attribute;
+
+struct uade_attribute {
+ struct uade_attribute *next;
+ enum uade_attribute_type type;
+ char *s;
+ int i;
+ double d;
+};
+
+struct uade_song {
+ char md5[33];
+
+ char module_filename[PATH_MAX];
+
+ char playername[256]; /* Eagleplayer name in players directory */
+ char modulename[256]; /* From score */
+ char formatname[256];
+
+ uint8_t *buf;
+ size_t bufsize;
+
+ int min_subsong;
+ int max_subsong;
+ int cur_subsong;
+
+ int playtime;
+ int flags;
+ int nsubsongs;
+ uint8_t *subsongs;
+ struct uade_attribute *songattributes;
+ struct uade_ep_options ep_options;
+ char *normalisation;
+
+ int64_t out_bytes;
+
+ int64_t silence_count;
+};
+
+struct epconfattr {
+ char *s; /* config file directive/variable name */
+ int e; /* ES_* flags for eagleplayers and songs */
+ int o; /* UC_* flag for uade.conf option */
+ char *c; /* constant for an UC_* flag */
+ enum uade_attribute_type t; /* if variable, its special type */
+};
+
+
+extern const struct epconfattr epconf[];
+
+
+/* FIX: A forward declaration to avoid circular dependency */
+struct uade_state;
+
+int uade_is_our_file(const char *modulename, int scanmode, struct uade_state *state);
+int uade_song_and_player_attribute(struct uade_attribute **attributelist,
+ int *flags, char *item, size_t lineno);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.c
new file mode 100644
index 00000000..c75c859d
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.c
@@ -0,0 +1,490 @@
+/* Effect module for UADE2 frontends.
+
+ Copyright 2005 (C) Antti S. Lankila <alankila@bel.fi>
+
+ This module is licensed under the GNU LGPL.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+#include <compilersupport.h>
+
+#include "effects.h"
+
+/*** old headphone effect ***/
+#define UADE_EFFECT_HEADPHONES_DELAY_LENGTH 22
+#define UADE_EFFECT_HEADPHONES_DELAY_DIRECT 0.3
+#define UADE_EFFECT_HEADPHONES_CROSSMIX_VOL 0.80
+
+static float headphones_ap_l[UADE_EFFECT_HEADPHONES_DELAY_LENGTH];
+static float headphones_ap_r[UADE_EFFECT_HEADPHONES_DELAY_LENGTH];
+static float headphones_rc_l[4];
+static float headphones_rc_r[4];
+
+/*** new headphone effect ***/
+
+/* delay time defines the width of the head. 0.5 ms gives us 15 cm virtual distance
+ * between sound arriving to either ear. */
+#define HEADPHONE2_DELAY_TIME 0.49e-3
+#define HEADPHONE2_DELAY_K 0.15
+/* head shadow frequency cutoff */
+#define HEADPHONE2_SHADOW_FREQ 8000.0
+/* high shelve keeps frequencies below cutoff intact and attenuates
+ * the rest in an uniform way. The effect is to make bass more "mono" than "stereo". */
+#define HEADPHONE2_SHELVE_FREQ 100.0
+#define HEADPHONE2_SHELVE_LEVEL -2.0
+
+#define MAXIMUM_SAMPLING_RATE 96000
+#define HEADPHONE2_DELAY_MAX_LENGTH ((int)(MAXIMUM_SAMPLING_RATE*HEADPHONE2_DELAY_TIME+1))
+#define DENORMAL_OFFSET 1E-10
+
+#define NORMALISE_RESOLUTION 10 /* in bits */
+#define NORMALISE_DEFAULT_GAIN 8.0
+#define NORMALISE_MAXIMUM_GAIN 8.0
+
+/* Headphone variables */
+typedef struct {
+ float b0, b1, b2, a1, a2, x[2], y[2];
+} biquad_t;
+
+static float headphone2_ap_l[HEADPHONE2_DELAY_MAX_LENGTH];
+static float headphone2_ap_r[HEADPHONE2_DELAY_MAX_LENGTH];
+static int headphone2_delay_length;
+static biquad_t headphone2_shelve_l;
+static biquad_t headphone2_shelve_r;
+static biquad_t headphone2_rc_l;
+static biquad_t headphone2_rc_r;
+
+/* Normalise variables */
+static int normalise_peak_level;
+static int normalise_historic_maximum_peak;
+static int normalise_oldlevel;
+
+static void gain(int gain_amount, int16_t * sm, int frames);
+static void pan(int pan_amount, int16_t * sm, int frames);
+static void headphones(int16_t * sm, int frames);
+static void headphones2(int16_t * sm, int frames);
+static void normalise(int change_level, int16_t * sm, int frames);
+static int normalise_compute_gain(int peak);
+
+static inline int sampleclip(int x)
+{
+ if (unlikely(x > 32767 || x < -32768)) {
+ if (x > 32767)
+ x = 32767;
+ else
+ x = -32768;
+ }
+ return x;
+}
+
+/* calculate a high shelve filter */
+static void calculate_shelve(double fs, double fc, double g, biquad_t * bq)
+{
+ float A, omega, sn, cs, beta, b0, b1, b2, a0, a1, a2;
+
+ A = powf(10, g / 40);
+ omega = 2 * M_PI * fc / fs;
+ omega = tan(omega / 2) * 2;
+ sn = sin(omega);
+ cs = cos(omega);
+ beta = sqrt(A + A);
+
+ b0 = A * ((A + 1) + (A - 1) * cs + beta * sn);
+ b1 = -2 * A * ((A - 1) + (A + 1) * cs);
+ b2 = A * ((A + 1) + (A - 1) * cs - beta * sn);
+ a0 = (A + 1) - (A - 1) * cs + beta * sn;
+ a1 = 2 * ((A - 1) - (A + 1) * cs);
+ a2 = (A + 1) - (A - 1) * cs - beta * sn;
+
+ bq->b0 = b0 / a0;
+ bq->b1 = b1 / a0;
+ bq->b2 = b2 / a0;
+ bq->a1 = a1 / a0;
+ bq->a2 = a2 / a0;
+}
+
+/* calculate 1st order lowpass filter */
+static void calculate_rc(double fs, double fc, biquad_t * bq)
+{
+ float omega;
+
+ if (fc >= fs / 2) {
+ bq->b0 = 1.0;
+ bq->b1 = 0.0;
+ bq->b2 = 0.0;
+ bq->a1 = 0.0;
+ bq->a2 = 0.0;
+ return;
+ }
+ omega = 2 * M_PI * fc / fs;
+ omega = tan(omega / 2) * 2;
+
+ bq->b0 = 1 / (1 + 1 / omega);
+ bq->b1 = 0;
+ bq->b2 = 0;
+ bq->a1 = -1 + bq->b0;
+ bq->a2 = 0;
+}
+
+static inline float evaluate_biquad(float input, biquad_t * bq)
+{
+ float output = DENORMAL_OFFSET;
+
+ output += input * bq->b0 + bq->x[0] * bq->b1 + bq->x[1] * bq->b2;
+ output -= bq->y[0] * bq->a1 + bq->y[1] * bq->a2;
+
+ bq->x[1] = bq->x[0];
+ bq->x[0] = input;
+
+ bq->y[1] = bq->y[0];
+ bq->y[0] = output;
+
+ return output;
+}
+
+static void reset_biquad(biquad_t * bq)
+{
+ bq->x[0] = bq->x[1] = bq->y[0] = bq->y[1] = 0;
+}
+
+/* Reset effects' state variables.
+ * Call this method between before starting playback */
+void uade_effect_reset_internals(void)
+{
+ /* old headphones */
+ memset(headphones_ap_l, 0, sizeof(headphones_ap_l));
+ memset(headphones_ap_r, 0, sizeof(headphones_ap_r));
+ memset(headphones_rc_l, 0, sizeof(headphones_rc_l));
+ memset(headphones_rc_r, 0, sizeof(headphones_rc_r));
+
+ /* new headphones */
+ memset(headphone2_ap_l, 0, sizeof(headphone2_ap_l));
+ memset(headphone2_ap_r, 0, sizeof(headphone2_ap_r));
+ reset_biquad(&headphone2_shelve_l);
+ reset_biquad(&headphone2_shelve_r);
+ reset_biquad(&headphone2_rc_l);
+ reset_biquad(&headphone2_rc_r);
+
+ normalise_peak_level = 0;
+ normalise_historic_maximum_peak = 0;
+ normalise_oldlevel = 1 << NORMALISE_RESOLUTION;
+}
+
+void uade_effect_disable_all(struct uade_effect *ue)
+{
+ ue->enabled = 0;
+}
+
+void uade_effect_disable(struct uade_effect *ue, uade_effect_t effect)
+{
+ ue->enabled &= ~(1 << effect);
+}
+
+void uade_effect_enable(struct uade_effect *ue, uade_effect_t effect)
+{
+ ue->enabled |= 1 << effect;
+}
+
+/* Returns 1 if effect is enabled, and zero otherwise. Ignores
+ UADE_EFFECT_ALLOW. */
+int uade_effect_is_enabled(struct uade_effect *ue, uade_effect_t effect)
+{
+ return (ue->enabled & (1 << effect)) != 0;
+}
+
+void uade_effect_run(struct uade_effect *ue, int16_t * samples, int frames)
+{
+ if (ue->enabled & (1 << UADE_EFFECT_ALLOW)) {
+ normalise(ue->enabled & (1 << UADE_EFFECT_NORMALISE), samples,
+ frames);
+ if (ue->enabled & (1 << UADE_EFFECT_PAN))
+ pan(ue->pan, samples, frames);
+ if (ue->enabled & (1 << UADE_EFFECT_HEADPHONES))
+ headphones(samples, frames);
+ if (ue->enabled & (1 << UADE_EFFECT_HEADPHONES2) && ue->rate)
+ headphones2(samples, frames);
+ if (ue->enabled & (1 << UADE_EFFECT_GAIN))
+ gain(ue->gain, samples, frames);
+ }
+}
+
+void uade_effect_toggle(struct uade_effect *ue, uade_effect_t effect)
+{
+ ue->enabled ^= 1 << effect;
+}
+
+void uade_effect_set_defaults(struct uade_effect *ue)
+{
+ memset(ue, 0, sizeof(*ue));
+ uade_effect_disable_all(ue);
+ uade_effect_enable(ue, UADE_EFFECT_ALLOW);
+ uade_effect_gain_set_amount(ue, 1.0);
+ uade_effect_pan_set_amount(ue, 0.7);
+}
+
+/* Rate of 0 means undefined. Effects that depend on sample rate must
+ self-check against this because they can not implemented properly */
+void uade_effect_set_sample_rate(struct uade_effect *ue, int rate)
+{
+ assert(rate >= 0);
+ ue->rate = rate;
+
+ if (rate == 0)
+ return;
+
+ calculate_shelve(rate, HEADPHONE2_SHELVE_FREQ, HEADPHONE2_SHELVE_LEVEL,
+ &headphone2_shelve_l);
+ calculate_shelve(rate, HEADPHONE2_SHELVE_FREQ, HEADPHONE2_SHELVE_LEVEL,
+ &headphone2_shelve_r);
+ calculate_rc(rate, HEADPHONE2_SHADOW_FREQ, &headphone2_rc_l);
+ calculate_rc(rate, HEADPHONE2_SHADOW_FREQ, &headphone2_rc_r);
+ headphone2_delay_length = HEADPHONE2_DELAY_TIME * rate + 0.5;
+ if (headphone2_delay_length > HEADPHONE2_DELAY_MAX_LENGTH) {
+ fprintf(stderr, "effects.c: truncating headphone delay line due to samplerate exceeding 96 kHz.\n");
+ headphone2_delay_length = HEADPHONE2_DELAY_MAX_LENGTH;
+ }
+}
+
+void uade_effect_gain_set_amount(struct uade_effect *ue, float amount)
+{
+ assert(amount >= 0.0 && amount <= 128.0);
+ ue->gain = amount * 256.0;
+}
+
+void uade_effect_pan_set_amount(struct uade_effect *ue, float amount)
+{
+ assert(amount >= 0.0 && amount <= 2.0);
+ ue->pan = amount * 256.0 / 2.0;
+}
+
+static int normalise_compute_gain(int peak)
+{
+ if (normalise_historic_maximum_peak == 0) {
+ /* if the peak is not known, we cap gain in an attempt to avoid
+ * boosting silent intros too much. */
+ if (peak < 32768 / NORMALISE_DEFAULT_GAIN)
+ return NORMALISE_DEFAULT_GAIN *
+ (1 << NORMALISE_RESOLUTION);
+ else
+ return (32768 << NORMALISE_RESOLUTION) / peak;
+ } else {
+ int largerpeak;
+ if (peak < normalise_historic_maximum_peak)
+ largerpeak = normalise_historic_maximum_peak;
+ else
+ largerpeak = peak;
+ /* if the peak is known, we use the recorded value but adapt
+ if this rendition comes out louder for some reason (for
+ instance, updated UADE) */
+ if (largerpeak < 32768 / NORMALISE_MAXIMUM_GAIN)
+ return NORMALISE_MAXIMUM_GAIN *
+ (1 << NORMALISE_RESOLUTION);
+ else
+ return (32768 << NORMALISE_RESOLUTION) / largerpeak;
+ }
+}
+
+/* We save gain from maximum known level. This is an one-way street,
+ the gain can * only decrease with time. If the historic level is
+ known and larger, we prefer it. */
+void uade_effect_normalise_serialise(char *buf, size_t len)
+{
+ int peak = normalise_peak_level;
+
+ assert(len > 0);
+
+ if (normalise_historic_maximum_peak > normalise_peak_level)
+ peak = normalise_historic_maximum_peak;
+
+ if (snprintf(buf, len, "v=1,p=%d", peak) >= len) {
+ fprintf(stderr, "normalise effect: buffer too short, gain would be truncated. This is a bug in UADE.\n");
+ exit(-1);
+ }
+}
+
+/* similarly, this should only be called if gain has a positive value,
+ * but we try to recover from misuse. */
+void uade_effect_normalise_unserialise(const char *buf)
+{
+ int version, readcount;
+ float peak;
+
+ normalise_historic_maximum_peak = 0;
+
+ if (buf == NULL)
+ return;
+
+ readcount = sscanf(buf, "v=%d,p=%f", &version, &peak);
+
+ if (readcount == 0) {
+ fprintf(stderr, "normalise effect: gain string invalid: '%s'\n", buf);
+ exit(-1);
+ }
+
+ if (version != 1) {
+ fprintf(stderr, "normalise effect: unrecognized gain version: '%s'\n", buf);
+ exit(-1);
+ }
+
+ if (readcount != 2) {
+ fprintf(stderr, "Could not read peak value for version 1: '%s'\n", buf);
+ exit(-1);
+ }
+
+ if (peak >= 0.0 && peak <= 1.0) {
+ normalise_oldlevel = normalise_historic_maximum_peak =
+ 32768 * peak;
+ } else {
+ fprintf(stderr, "normalise effect: invalid peak level: '%s'\n", buf);
+ }
+}
+
+static void normalise(int change_level, int16_t * sm, int frames)
+{
+ int i;
+
+ /* Negative side is mirrored. but positive side gains by 1.
+ * This is to make both semiwaves have same max. */
+ for (i = 0; i < 2 * frames; i += 1) {
+ int tmp = sm[i];
+ tmp = (tmp >= 0) ? tmp + 1 : -tmp;
+ if (tmp > normalise_peak_level)
+ normalise_peak_level = tmp;
+ }
+
+ /* Slight clipping may result in first playback while the system
+ * adjusts. With a bit of "advance warning" of clipping about to
+ * occur, the level begins to adjust as soon as the buffer
+ * begins. Typical adjustment times are not large -- a few hundred
+ * samples are to be expected -- and the clipping should only
+ * occur on the first rendition of the song, if at all. */
+ if (change_level) {
+ int newlevel = normalise_compute_gain(normalise_peak_level);
+
+ for (i = 0; i < 2 * frames; i += 1) {
+ /* same gain for the frame */
+ if ((i & 1) == 0) {
+ if (normalise_oldlevel < newlevel)
+ normalise_oldlevel += 1;
+ if (normalise_oldlevel > newlevel)
+ normalise_oldlevel -= 1;
+ }
+ sm[i] =
+ sampleclip((sm[i] *
+ normalise_oldlevel) >>
+ NORMALISE_RESOLUTION);
+ }
+ }
+}
+
+static void gain(int gain_amount, int16_t * sm, int frames)
+{
+ int i;
+ for (i = 0; i < 2 * frames; i += 1)
+ sm[i] = sampleclip((sm[i] * gain_amount) >> 8);
+}
+
+/* Panning effect. Turns stereo into mono in a specific degree */
+static void pan(int pan_amount, int16_t * sm, int frames)
+{
+ int i, l, r, m;
+ for (i = 0; i < frames; i += 1) {
+ l = sm[0];
+ r = sm[1];
+ m = (r - l) * pan_amount;
+ sm[0] = ((l << 8) + m) >> 8;
+ sm[1] = ((r << 8) - m) >> 8;
+ sm += 2;
+ }
+}
+
+/* All-pass delay. Its purpose is to confuse the phase of the sound a bit
+ * and also provide some delay to locate the source outside the head. This
+ * seems to work better than a pure delay line. */
+static float headphones_allpass_delay(float in, float *state)
+{
+ int i;
+ float tmp, output;
+
+ tmp = in - UADE_EFFECT_HEADPHONES_DELAY_DIRECT * state[0];
+ output = state[0] + UADE_EFFECT_HEADPHONES_DELAY_DIRECT * tmp;
+
+ /* FIXME: use modulo and index */
+ for (i = 1; i < UADE_EFFECT_HEADPHONES_DELAY_LENGTH; i += 1)
+ state[i - 1] = state[i];
+ state[UADE_EFFECT_HEADPHONES_DELAY_LENGTH - 1] = tmp;
+
+ return output;
+}
+
+static float headphones_lpf(float in, float *state)
+{
+ float out = in * 0.53;
+ out += 0.47 * state[0];
+ state[0] = out;
+
+ return out;
+}
+
+/* A real implementation would simply perform FIR with recorded HRTF data. */
+static void headphones(int16_t * sm, int frames)
+{
+ int i;
+ float ld, rd;
+ int l_final, r_final;
+ for (i = 0; i < frames; i += 1) {
+ ld = headphones_allpass_delay(sm[0], headphones_ap_l);
+ rd = headphones_allpass_delay(sm[1], headphones_ap_r);
+ ld = headphones_lpf(ld, headphones_rc_l);
+ rd = headphones_lpf(rd, headphones_rc_r);
+
+ l_final =
+ (sm[0] + rd * UADE_EFFECT_HEADPHONES_CROSSMIX_VOL) / 2;
+ r_final =
+ (sm[1] + ld * UADE_EFFECT_HEADPHONES_CROSSMIX_VOL) / 2;
+ sm[0] = sampleclip(l_final);
+ sm[1] = sampleclip(r_final);
+
+ sm += 2;
+ }
+}
+
+static float headphone2_allpass_delay(float in, float *state)
+{
+ int i;
+ float tmp, output;
+
+ tmp = in - HEADPHONE2_DELAY_K * state[0];
+ output = state[0] + HEADPHONE2_DELAY_K * tmp;
+
+ /* FIXME: use modulo and index */
+ for (i = 1; i < headphone2_delay_length; i += 1)
+ state[i - 1] = state[i];
+ state[headphone2_delay_length - 1] = tmp;
+
+ return output;
+}
+
+static void headphones2(int16_t * sm, int frames)
+{
+ int i;
+ for (i = 0; i < frames; i += 1) {
+ float ld, rd;
+
+ ld = headphone2_allpass_delay(sm[0], headphone2_ap_l);
+ rd = headphone2_allpass_delay(sm[1], headphone2_ap_r);
+ ld = evaluate_biquad(ld, &headphone2_rc_l);
+ rd = evaluate_biquad(rd, &headphone2_rc_r);
+ ld = evaluate_biquad(ld, &headphone2_shelve_l);
+ rd = evaluate_biquad(rd, &headphone2_shelve_r);
+
+ sm[0] = sampleclip((sm[0] + rd) / 2);
+ sm[1] = sampleclip((sm[1] + ld) / 2);
+ sm += 2;
+ }
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.h
new file mode 100644
index 00000000..57a779df
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.h
@@ -0,0 +1,42 @@
+#ifndef _UADE2_EFFECTS_H_
+#define _UADE2_EFFECTS_H_
+
+#include <stdint.h>
+
+typedef enum {
+ UADE_EFFECT_ALLOW,
+ UADE_EFFECT_GAIN,
+ UADE_EFFECT_HEADPHONES,
+ UADE_EFFECT_HEADPHONES2,
+ UADE_EFFECT_PAN,
+ UADE_EFFECT_NORMALISE,
+} uade_effect_t;
+
+struct uade_effect {
+ uade_effect_t enabled;
+ int gain;
+ int pan;
+ int rate;
+};
+
+void uade_effect_disable(struct uade_effect *ue, uade_effect_t effect);
+void uade_effect_disable_all(struct uade_effect *ue);
+void uade_effect_enable(struct uade_effect *ue, uade_effect_t effect);
+int uade_effect_is_enabled(struct uade_effect *ue, uade_effect_t effect);
+void uade_effect_set_defaults(struct uade_effect *ue);
+void uade_effect_set_sample_rate(struct uade_effect *ue, int rate);
+void uade_effect_toggle(struct uade_effect *ue, uade_effect_t effect);
+
+/* effect-specific knobs */
+void uade_effect_gain_set_amount(struct uade_effect *ue, float amount);
+void uade_effect_normalise_unserialise(const char *buf);
+void uade_effect_normalise_serialise(char *buf, size_t len);
+void uade_effect_pan_set_amount(struct uade_effect *ue, float amount);
+
+/* reset state at start of song */
+void uade_effect_reset_internals(void);
+
+/* process n frames of sample buffer */
+void uade_effect_run(struct uade_effect *ue, int16_t * sample, int frames);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.c
new file mode 100644
index 00000000..7614ea48
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.c
@@ -0,0 +1,247 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include <string.h> /* for memcpy() */
+#include "md5.h"
+
+#if __BYTE_ORDER == 1234
+#define byteReverse(buf, len) /* Nothing */
+#else
+static void byteReverse(unsigned char *buf, unsigned longs);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32_t t;
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(MD5_CTX *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], MD5_CTX *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+ ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset((char *) ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.copyright b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.copyright
new file mode 100644
index 00000000..72b040b3
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.copyright
@@ -0,0 +1,8 @@
+The algorithm is due to Ron Rivest. This code was written by Colin
+Plumb in 1993, no copyright is claimed. This code is in the public
+domain; do with it what you wish.
+.Pp
+Equivalent code is available from RSA Data Security, Inc.
+This code has been tested against that, and is equivalent,
+except that you don't need to include two pages of legalese
+with every copy.
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.h
new file mode 100644
index 00000000..6f471e3b
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.h
@@ -0,0 +1,21 @@
+#ifndef _UADE_MD5_H_
+#define _UADE_MD5_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#define MD5_HASHBYTES 16
+
+typedef struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+} MD5_CTX;
+
+void MD5Init(MD5_CTX *context);
+void MD5Update(MD5_CTX *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[MD5_HASHBYTES], MD5_CTX *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+#endif /* !_UADE_MD5_H_ */
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.c
new file mode 100644
index 00000000..b7d79165
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.c
@@ -0,0 +1,798 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "songdb.h"
+#include "uadeconf.h"
+#include "md5.h"
+#include "unixatomic.h"
+#include "ossupport.h"
+#include "uadeconfig.h"
+#include "support.h"
+#include "uadeconstants.h"
+
+#define NORM_ID "n="
+#define NORM_ID_LENGTH 2
+
+#define eserror(fmt, args...) do { fprintf(stderr, "song.conf error on line %zd: " fmt "\n", lineno, ## args); exit(-1); } while (0)
+
+
+struct eaglesong {
+ int flags;
+ char md5[33];
+ struct uade_attribute *attributes;
+};
+
+struct persub {
+ int sub;
+ char *normalisation;
+};
+
+static struct uade_content *contentchecksums;
+static size_t nccused; /* number of valid entries in content db */
+static size_t nccalloc; /* number of allocated entries for content db */
+static int ccmodified;
+static int cccorrupted;
+
+static int nsongs;
+static struct eaglesong *songstore;
+
+static int escompare(const void *a, const void *b);
+static struct uade_content *get_content(const char *md5);
+
+
+static void add_sub_normalisation(struct uade_content *n, char *normalisation)
+{
+ struct persub *subinfo;
+ char *endptr;
+
+ subinfo = malloc(sizeof(*subinfo));
+ if (subinfo == NULL)
+ uadeerror("Can't allocate memory for normalisation entry\n");
+
+ subinfo->sub = strtol(normalisation, &endptr, 10);
+ if (*endptr != ',' || subinfo->sub < 0) {
+ fprintf(stderr, "Invalid normalisation entry: %s\n", normalisation);
+ return;
+ }
+
+ subinfo->normalisation = strdup(endptr + 1);
+ if (subinfo->normalisation == NULL)
+ uadeerror("Can't allocate memory for normalisation string\n");
+
+ vplist_append(n->subs, subinfo);
+}
+
+/* Compare function for bsearch() and qsort() to sort songs with respect
+ to their md5sums */
+static int contentcompare(const void *a, const void *b)
+{
+ return strcasecmp(((struct uade_content *)a)->md5,
+ ((struct uade_content *)b)->md5);
+}
+
+static int escompare(const void *a, const void *b)
+{
+ return strcasecmp(((struct eaglesong *)a)->md5,
+ ((struct eaglesong *)b)->md5);
+}
+
+static struct uade_content *get_content(const char *md5)
+{
+ struct uade_content key;
+
+ if (contentchecksums == NULL)
+ return NULL;
+
+ memset(&key, 0, sizeof key);
+ strlcpy(key.md5, md5, sizeof key.md5);
+
+ return bsearch(&key, contentchecksums, nccused,
+ sizeof contentchecksums[0], contentcompare);
+}
+
+static struct uade_content *create_content_checksum(const char *md5,
+ uint32_t playtime)
+{
+ struct uade_content *n;
+
+ if (nccused == nccalloc) {
+ nccalloc = MAX(nccalloc * 2, 16);
+ n = realloc(contentchecksums,
+ nccalloc * sizeof(struct uade_content));
+ if (n == NULL) {
+ fprintf(stderr,
+ "uade: No memory for new content checksums.\n");
+ return NULL;
+ }
+ contentchecksums = n;
+ }
+
+ n = &contentchecksums[nccused];
+
+ if (md5 == NULL)
+ return n;
+
+ nccused++;
+
+ ccmodified = 1;
+
+ memset(n, 0, sizeof(*n));
+ strlcpy(n->md5, md5, sizeof(n->md5));
+ n->playtime = playtime;
+
+ n->subs = vplist_create(1);
+
+ return n;
+}
+
+static void md5_from_buffer(char *dest, size_t destlen,
+ uint8_t * buf, size_t bufsize)
+{
+ uint8_t md5[16];
+ int ret;
+ MD5_CTX ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, buf, bufsize);
+ MD5Final(md5, &ctx);
+ ret =
+ snprintf(dest, destlen,
+ "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
+ md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6],
+ md5[7], md5[8], md5[9], md5[10], md5[11], md5[12], md5[13],
+ md5[14], md5[15]);
+ if (ret >= destlen || ret != 32) {
+ fprintf(stderr, "md5 buffer error (%d/%zd)\n", ret, destlen);
+ exit(1);
+ }
+}
+
+static void update_playtime(struct uade_content *n, uint32_t playtime)
+{
+ if (n->playtime != playtime) {
+ ccmodified = 1;
+ n->playtime = playtime;
+ }
+}
+
+static void sort_content_checksums(void)
+{
+ if (contentchecksums == NULL)
+ return;
+
+ qsort(contentchecksums, nccused, sizeof contentchecksums[0],
+ contentcompare);
+}
+
+/* replace must be zero if content db is unsorted */
+struct uade_content *uade_add_playtime(const char *md5, uint32_t playtime)
+{
+ struct uade_content *n;
+
+ /* If content db hasn't been read into memory already, it is not used */
+ if (contentchecksums == NULL)
+ return NULL;
+
+ /* Do not record song shorter than 3 secs */
+ if (playtime < 3000)
+ return NULL;
+
+ if (strlen(md5) != 32)
+ return NULL;
+
+ n = get_content(md5);
+ if (n != NULL) {
+ update_playtime(n, playtime);
+ return n;
+ }
+
+ n = create_content_checksum(md5, playtime);
+
+ sort_content_checksums();
+
+ return n;
+}
+
+void uade_lookup_volume_normalisation(struct uade_state *state)
+{
+ size_t i, nsubs;
+ struct uade_effect *ue = &state->effects;
+ struct uade_config *uc = &state->config;
+ struct uade_song *us = state->song;
+ struct uade_content *content = get_content(us->md5);
+
+ if (content != NULL) {
+
+ nsubs = vplist_len(content->subs);
+
+ for (i = 0; i < nsubs; i++) {
+
+ struct persub *subinfo = vplist_get(content->subs, i);
+
+ if (subinfo->sub == us->cur_subsong) {
+ uade_set_config_option(uc, UC_NORMALISE,
+ subinfo->normalisation);
+ uade_effect_normalise_unserialise(uc->
+ normalise_parameter);
+ uade_effect_enable(ue, UADE_EFFECT_NORMALISE);
+ break;
+ }
+ }
+ }
+}
+
+static void get_song_flags_and_attributes_from_songstore(struct uade_song *us)
+{
+ struct eaglesong key;
+ struct eaglesong *es;
+
+ if (songstore != NULL) {
+ /* Lookup md5 from the songdb */
+ strlcpy(key.md5, us->md5, sizeof key.md5);
+ es = bsearch(&key, songstore, nsongs, sizeof songstore[0], escompare);
+
+ if (es != NULL) {
+ /* Found -> copy flags and attributes from database */
+ us->flags |= es->flags;
+ us->songattributes = es->attributes;
+ }
+ }
+}
+
+int uade_alloc_song(struct uade_state *state, const char *filename)
+{
+ struct uade_song *us;
+ struct uade_content *content;
+
+ state->song = NULL;
+
+ us = calloc(1, sizeof *us);
+ if (us == NULL)
+ goto error;
+
+ strlcpy(us->module_filename, filename, sizeof us->module_filename);
+
+ us->buf = atomic_read_file(&us->bufsize, filename);
+ if (us->buf == NULL)
+ goto error;
+
+ /* Compute an md5sum of the song */
+ md5_from_buffer(us->md5, sizeof us->md5, us->buf, us->bufsize);
+
+ /* Needs us->md5 sum */
+ get_song_flags_and_attributes_from_songstore(us);
+
+ /* Lookup playtime from content database */
+ us->playtime = -1;
+ content = get_content(us->md5);
+ if (content != NULL && content->playtime > 0)
+ us->playtime = content->playtime;
+
+ /* We can't know subsong numbers yet. The eagleplayer will report them
+ * in the playback state */
+ us->min_subsong = us->max_subsong = us->cur_subsong = -1;
+
+ state->song = us;
+ return 1;
+
+ error:
+ if (us != NULL) {
+ free(us->buf);
+ free(us);
+ }
+ return 0;
+}
+
+static int uade_open_and_lock(const char *filename, int create)
+{
+ int fd, ret;
+ fd = open(filename, O_RDWR);
+ if (fd < 0) {
+ if (errno == ENOENT && create) {
+ fd = open(filename, O_RDWR | O_CREAT,
+ S_IRUSR | S_IWUSR);
+ if (fd < 0)
+ return -1;
+ } else {
+ return -1;
+ }
+ }
+#ifndef UADE_HAVE_CYGWIN
+ ret = lockf(fd, F_LOCK, 0);
+ if (ret) {
+ fprintf(stderr, "uade: Unable to lock song.conf: %s (%s)\n",
+ filename, strerror(errno));
+ atomic_close(fd);
+ return -1;
+ }
+#endif
+
+ return fd;
+}
+
+
+static struct uade_content *store_playtime(const char *md5, long playtime,
+ int *newccmodified,
+ size_t oldnccused)
+{
+ struct uade_content *n = NULL;
+
+ if (oldnccused > 0) {
+ struct uade_content key;
+ memset(&key, 0, sizeof key);
+ strlcpy(key.md5, md5, sizeof key.md5);
+
+ /* We use "oldnccused" here as the length, while new entries
+ are added in unsorted manner to the end of the array */
+ n = bsearch(&key, contentchecksums, oldnccused,
+ sizeof contentchecksums[0], contentcompare);
+ if (n == NULL)
+ /* new songs on disk db -> merge -> need saving */
+ *newccmodified = 1;
+ }
+
+ /* We value a playtime determined during run-time over
+ a database value */
+ if (n == NULL) {
+ /* Note, create_content_checksum() makes "ccmodified"
+ true, which we work-around later with the "newccmodified" */
+ n = create_content_checksum(md5, (uint32_t) playtime);
+ }
+
+ if (n == NULL) {
+ /* No memory, fuck. We shouldn't save anything to
+ avoid losing data. */
+ fprintf(stderr,
+ "uade: Warning, no memory for the song database\n");
+ cccorrupted = 1;
+ }
+
+ return n;
+}
+
+
+
+int uade_read_content_db(const char *filename)
+{
+ char line[1024];
+ FILE *f;
+ size_t lineno = 0;
+ long playtime;
+ int i, j, nexti;
+ char *id, *eptr;
+ char numberstr[1024];
+ char *md5;
+
+ /* We make backups of some variables because following loop will
+ make it always true, which is not what we want. The end result should
+ be that ccmodified is true in following cases only:
+ 1. the in-memory db is already dirty
+ 2. the in-memory db gets new data from disk db (merge operation)
+ Otherwise ccmodified should be false. */
+ int newccmodified = ccmodified;
+ size_t oldnccused = nccused;
+ int fd;
+ struct uade_content *n;
+
+ /* Try to create a database if it doesn't exist */
+ if (contentchecksums == NULL
+ && create_content_checksum(NULL, 0) == NULL)
+ return 0;
+
+ fd = uade_open_and_lock(filename, 0);
+ if (fd < 0) {
+ fprintf(stderr, "uade: Can not find %s\n", filename);
+ return 0;
+ }
+
+ f = fdopen(fd, "r");
+ if (f == NULL) {
+ fprintf(stderr, "uade: Can not create FILE structure for %s\n",
+ filename);
+ close(fd);
+ return 0;
+ }
+
+ while (xfgets(line, sizeof line, f) != NULL) {
+ lineno++;
+
+ if (line[0] == '#')
+ continue;
+
+ md5 = line;
+ i = skip_and_terminate_word(line, 0);
+ if (i < 0)
+ continue; /* playtime doesn't exist */
+
+ for (j = 0; isxdigit(line[j]); j++);
+
+ if (j != 32)
+ continue; /* is not a valid md5sum */
+
+ /* Grab and validate playtime (in milliseconds) */
+ nexti = skip_and_terminate_word(line, i);
+
+ playtime = strtol(&line[i], &eptr, 10);
+ if (*eptr != 0 || playtime < 0) {
+ fprintf(stderr, "Invalid playtime for md5 %s on contentdb line %zd: %s\n", md5, lineno, numberstr);
+ continue;
+ }
+
+ n = store_playtime(md5, playtime, &newccmodified, oldnccused);
+ if (n == NULL)
+ continue;
+
+ i = nexti; /* Note, it could be that i < 0 */
+
+ /* Get rest of the directives in a loop */
+ while (i >= 0) {
+ id = &line[i];
+ i = skip_and_terminate_word(line, i);
+
+ /* Subsong volume normalisation: n=sub1,XXX */
+ if (strncmp(id, NORM_ID, NORM_ID_LENGTH) == 0) {
+ id += NORM_ID_LENGTH;
+ add_sub_normalisation(n, id);
+ } else {
+ fprintf(stderr, "Unknown contentdb directive on line %zd: %s\n", lineno, id);
+ }
+ }
+ }
+ fclose(f);
+
+ ccmodified = newccmodified;
+
+ sort_content_checksums();
+
+ return 1;
+}
+
+int uade_read_song_conf(const char *filename)
+{
+ FILE *f = NULL;
+ struct eaglesong *s;
+ size_t allocated;
+ size_t lineno = 0;
+ size_t i;
+ int fd;
+
+ fd = uade_open_and_lock(filename, 1);
+ /* open_and_lock() may fail without harm (it's actually supposed to
+ fail if the process does not have lock (write) permissions to
+ the song.conf file */
+
+ f = fopen(filename, "r");
+ if (f == NULL)
+ goto error;
+
+ nsongs = 0;
+ allocated = 16;
+ songstore = calloc(allocated, sizeof songstore[0]);
+ if (songstore == NULL)
+ eserror("No memory for song store.");
+
+ while (1) {
+ char **items;
+ size_t nitems;
+
+ items = read_and_split_lines(&nitems, &lineno, f,
+ UADE_WS_DELIMITERS);
+ if (items == NULL)
+ break;
+
+ assert(nitems > 0);
+
+ if (nsongs == allocated) {
+ allocated *= 2;
+ songstore = realloc(songstore, allocated * sizeof(songstore[0]));
+ if (songstore == NULL)
+ eserror("No memory for players.");
+ }
+
+ s = &songstore[nsongs];
+ nsongs++;
+
+ memset(s, 0, sizeof s[0]);
+
+ if (strncasecmp(items[0], "md5=", 4) != 0) {
+ fprintf(stderr, "Line %zd must begin with md5= in %s\n",
+ lineno, filename);
+ free(items);
+ continue;
+ }
+ if (strlcpy(s->md5, items[0] + 4, sizeof s->md5) !=
+ ((sizeof s->md5) - 1)) {
+ fprintf(stderr,
+ "Line %zd in %s has too long an md5sum.\n",
+ lineno, filename);
+ free(items);
+ continue;
+ }
+
+ for (i = 1; i < nitems; i++) {
+ if (strncasecmp(items[i], "comment:", 7) == 0)
+ break;
+ if (uade_song_and_player_attribute(&s->attributes, &s->flags, items[i], lineno))
+ continue;
+ fprintf(stderr, "song option %s is invalid\n", items[i]);
+ }
+
+ for (i = 0; items[i] != NULL; i++)
+ free(items[i]);
+
+ free(items);
+ }
+
+ fclose(f);
+
+ /* we may not have the file locked */
+ if (fd >= 0)
+ atomic_close(fd); /* lock is closed too */
+
+ /* Sort MD5 sums for binary searching songs */
+ qsort(songstore, nsongs, sizeof songstore[0], escompare);
+ return 1;
+
+ error:
+ if (f)
+ fclose(f);
+ if (fd >= 0)
+ atomic_close(fd);
+ return 0;
+}
+
+void uade_save_content_db(const char *filename)
+{
+ int fd;
+ FILE *f;
+ size_t i;
+
+ if (ccmodified == 0 || cccorrupted)
+ return;
+
+ fd = uade_open_and_lock(filename, 1);
+ if (fd < 0) {
+ fprintf(stderr, "uade: Can not write content db: %s\n",
+ filename);
+ return;
+ }
+
+ f = fdopen(fd, "w");
+ if (f == NULL) {
+ fprintf(stderr,
+ "uade: Can not create a FILE structure for content db: %s\n",
+ filename);
+ close(fd);
+ return;
+ }
+
+ for (i = 0; i < nccused; i++) {
+ char str[1024];
+ size_t subi, nsubs;
+ size_t bindex, bleft;
+ struct uade_content *n = &contentchecksums[i];
+
+ str[0] = 0;
+
+ bindex = 0;
+ bleft = sizeof(str);
+
+ nsubs = vplist_len(n->subs);
+
+ for (subi = 0; subi < nsubs; subi++) {
+ struct persub *sub = vplist_get(n->subs, subi);
+ int ret;
+ ret =
+ snprintf(&str[bindex], bleft, NORM_ID "%s ",
+ sub->normalisation);
+ if (ret >= bleft) {
+ fprintf(stderr,
+ "Too much subsong infos for %s\n",
+ n->md5);
+ break;
+ }
+ bleft -= ret;
+ bindex += ret;
+ }
+
+ fprintf(f, "%s %u %s\n", n->md5, (unsigned int)n->playtime,
+ str);
+ }
+
+ ccmodified = 0;
+
+ fclose(f);
+ fprintf(stderr, "uade: Saved %zd entries into content db.\n", nccused);
+}
+
+int uade_test_silence(void *buf, size_t size, struct uade_state *state)
+{
+ int i, s, exceptioncount;
+ int16_t *sm;
+ int nsamples;
+ int64_t count = state->song->silence_count;
+ int end = 0;
+
+ if (state->config.silence_timeout < 0)
+ return 0;
+
+ exceptioncount = 0;
+ sm = buf;
+ nsamples = size / 2;
+
+ for (i = 0; i < nsamples; i++) {
+ s = (sm[i] >= 0) ? sm[i] : -sm[i];
+ if (s >= (32767 * 1 / 100)) {
+ exceptioncount++;
+ if (exceptioncount >= (size * 2 / 100)) {
+ count = 0;
+ break;
+ }
+ }
+ }
+
+ if (i == nsamples) {
+ count += size;
+ if (count / (UADE_BYTES_PER_FRAME * state->config.frequency) >= state->config.silence_timeout) {
+ count = 0;
+ end = 1;
+ }
+ }
+
+ state->song->silence_count = count;
+
+ return end;
+}
+
+void uade_unalloc_song(struct uade_state *state)
+{
+ free(state->song->buf);
+ state->song->buf = NULL;
+
+ free(state->song);
+ state->song = NULL;
+}
+
+int uade_update_song_conf(const char *songconfin, const char *songconfout,
+ const char *songname, const char *options)
+{
+ int ret;
+ int fd;
+ char md5[33];
+ void *mem = NULL;
+ size_t filesize, newsize;
+ int found = 0;
+ size_t inputsize;
+ char *input, *inputptr, *outputptr;
+ size_t inputoffs;
+ char newline[256];
+ size_t i;
+ int need_newline = 0;
+
+ if (strlen(options) > 128) {
+ fprintf(stderr, "Too long song.conf options.\n");
+ return 0;
+ }
+
+ fd = uade_open_and_lock(songconfout, 1);
+
+ input = atomic_read_file(&inputsize, songconfin);
+ if (input == NULL) {
+ fprintf(stderr, "Can not read song.conf: %s\n", songconfin);
+ atomic_close(fd); /* closes the lock too */
+ return 0;
+ }
+
+ newsize = inputsize + strlen(options) + strlen(songname) + 64;
+ mem = realloc(input, newsize);
+ if (mem == NULL) {
+ fprintf(stderr,
+ "Can not realloc the input file buffer for song.conf.\n");
+ free(input);
+ atomic_close(fd); /* closes the lock too */
+ return 0;
+ }
+ input = mem;
+
+ mem = atomic_read_file(&filesize, songname);
+ if (mem == NULL)
+ goto error;
+
+ md5_from_buffer(md5, sizeof md5, mem, filesize);
+
+ inputptr = outputptr = input;
+ inputoffs = 0;
+
+ while (inputoffs < inputsize) {
+ if (inputptr[0] == '#')
+ goto copyline;
+
+ if ((inputoffs + 37) >= inputsize)
+ goto copyline;
+
+ if (strncasecmp(inputptr, "md5=", 4) != 0)
+ goto copyline;
+
+ if (strncasecmp(inputptr + 4, md5, 32) == 0) {
+ if (found) {
+ fprintf(stderr,
+ "Warning: dupe entry in song.conf: %s (%s)\n"
+ "Need manual resolving.\n", songname,
+ md5);
+ goto copyline;
+ }
+ found = 1;
+ snprintf(newline, sizeof newline, "md5=%s\t%s\n", md5,
+ options);
+
+ /* Skip this line. It will be appended later to the end of the buffer */
+ for (i = inputoffs; i < inputsize; i++) {
+ if (input[i] == '\n') {
+ i = i + 1 - inputoffs;
+ break;
+ }
+ }
+ if (i == inputsize) {
+ i = inputsize - inputoffs;
+ found = 0;
+ need_newline = 1;
+ }
+ inputoffs += i;
+ inputptr += i;
+ continue;
+ }
+
+ copyline:
+ /* Copy the line */
+ for (i = inputoffs; i < inputsize; i++) {
+ if (input[i] == '\n') {
+ i = i + 1 - inputoffs;
+ break;
+ }
+ }
+ if (i == inputsize) {
+ i = inputsize - inputoffs;
+ need_newline = 1;
+ }
+ memmove(outputptr, inputptr, i);
+ inputoffs += i;
+ inputptr += i;
+ outputptr += i;
+ }
+
+ if (need_newline) {
+ snprintf(outputptr, 2, "\n");
+ outputptr += 1;
+ }
+
+ /* there is enough space */
+ ret = snprintf(outputptr, PATH_MAX + 256, "md5=%s\t%s\tcomment %s\n",
+ md5, options, songname);
+ outputptr += ret;
+
+ if (ftruncate(fd, 0)) {
+ fprintf(stderr, "Can not truncate the file.\n");
+ goto error;
+ }
+
+ /* Final file size */
+ i = (size_t) (outputptr - input);
+
+ if (atomic_write(fd, input, i) < i)
+ fprintf(stderr,
+ "Unable to write file contents back. Data loss happened. CRAP!\n");
+
+ error:
+ atomic_close(fd); /* Closes the lock too */
+ free(input);
+ free(mem);
+ return 1;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.h
new file mode 100644
index 00000000..8cbba160
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.h
@@ -0,0 +1,24 @@
+#ifndef _UADE_SONGDB_H_
+#define _UADE_SONGDB_H_
+
+#include "uadestate.h"
+#include "vplist.h"
+
+struct uade_content {
+ char md5[33];
+ uint32_t playtime; /* in milliseconds */
+ struct vplist *subs;
+};
+
+struct uade_content *uade_add_playtime(const char *md5, uint32_t playtime);
+int uade_alloc_song(struct uade_state *state, const char *filename);
+void uade_lookup_volume_normalisation(struct uade_state *state);
+int uade_read_content_db(const char *filename);
+int uade_read_song_conf(const char *filename);
+void uade_save_content_db(const char *filename);
+int uade_test_silence(void *buf, size_t size, struct uade_state *state);
+void uade_unalloc_song(struct uade_state *state);
+int uade_update_song_conf(const char *songconfin, const char *songconfout,
+ const char *songname, const char *options);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.c
new file mode 100644
index 00000000..5997b183
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.c
@@ -0,0 +1,721 @@
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "songinfo.h"
+#include "uadeutils.h"
+#include "ossupport.h"
+#include "amifilemagic.h"
+#include "support.h"
+
+
+static void asciiline(char *dst, unsigned char *buf)
+{
+ int i, c;
+ for (i = 0; i < 16; i++) {
+ c = buf[i];
+ if (isgraph(c) || c == ' ') {
+ dst[i] = c;
+ } else {
+ dst[i] = '.';
+ }
+ }
+ dst[i] = 0;
+}
+
+static int hexdump(char *info, size_t maxlen, char *filename, size_t toread)
+{
+ FILE *f;
+ size_t rb, ret;
+ uint8_t *buf;
+
+ assert(maxlen >= 8192);
+
+ f = fopen(filename, "rb");
+ if (f == NULL)
+ return 0;
+
+ buf = malloc(toread);
+ if (buf == NULL)
+ return 0;
+
+ rb = 0;
+ while (rb < toread) {
+ ret = fread(&buf[rb], 1, toread - rb, f);
+ if (ret == 0)
+ break;
+ rb += ret;
+ }
+
+ if (rb > 0) {
+ size_t roff = 0;
+ size_t woff = 0;
+
+ while (roff < rb) {
+ int iret;
+
+ if (woff >= maxlen)
+ break;
+
+ if (woff + 32 >= maxlen) {
+ strcpy(&info[woff], "\nbuffer overflow...\n");
+ woff += strlen(&info[woff]);
+ break;
+ }
+
+ iret = snprintf(&info[woff], maxlen - woff, "%.3zx: ",
+ roff);
+ assert(iret > 0);
+ woff += iret;
+
+ if (woff >= maxlen)
+ break;
+
+ if (roff + 16 > rb) {
+ iret = snprintf(&info[woff], maxlen - woff,
+ "Aligned line ");
+ assert(iret > 0);
+ woff += iret;
+
+ } else {
+ char dbuf[17];
+ asciiline(dbuf, &buf[roff]);
+ iret = snprintf(&info[woff], maxlen - woff,
+ "%.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x |%s|",
+ buf[roff + 0], buf[roff + 1],
+ buf[roff + 2], buf[roff + 3],
+ buf[roff + 4], buf[roff + 5],
+ buf[roff + 6], buf[roff + 7],
+ buf[roff + 8], buf[roff + 9],
+ buf[roff + 10], buf[roff + 11],
+ buf[roff + 12], buf[roff + 13],
+ buf[roff + 14], buf[roff + 15],
+ dbuf);
+ assert(iret > 0);
+ woff += iret;
+ }
+
+ if (woff >= maxlen)
+ break;
+
+ iret = snprintf(&info[woff], maxlen - woff, "\n");
+ woff += iret;
+
+ roff += 16;
+ }
+
+ if (woff >= maxlen)
+ woff = maxlen - 1;
+ info[woff] = 0;
+ }
+
+ fclose(f);
+ free(buf);
+ return rb == 0;
+}
+
+static size_t find_tag(uint8_t * buf, size_t startoffset, size_t buflen,
+ uint8_t * tag, size_t taglen)
+{
+ uint8_t *treasure;
+
+ if (startoffset >= buflen)
+ return -1;
+
+ treasure = memmem(buf + startoffset, buflen - startoffset, tag, taglen);
+ if (treasure == NULL)
+ return -1;
+
+ return (size_t) (treasure - buf);
+}
+
+static int string_checker(unsigned char *str, size_t off, size_t maxoff)
+{
+ assert(maxoff > 0);
+ while (off < maxoff) {
+ if (*str == 0)
+ return 1;
+ off++;
+ str++;
+ }
+ return 0;
+}
+
+/* Wanted Team's loadseg modules */
+static void process_WTWT_mod(char *credits, size_t credits_len,
+ unsigned char *buf, size_t len, char *lo,
+ char *hi, int rel)
+{
+ int offset, txt_offset, chunk;
+ char tmpstr[256];
+
+ /* check for Magic ID */
+ offset = find_tag((uint8_t *) buf, 0, len, (uint8_t *) lo, 4);
+ if (offset == -1)
+ return;
+
+ offset =
+ find_tag((uint8_t *) buf, offset + 4, offset + 8, (uint8_t *) hi,
+ 4);
+ if (offset == -1)
+ return;
+
+ chunk = offset - 8; /* here's where our first chunk should be */
+ offset = offset + rel; /* offset to our info pointers */
+
+ if (chunk < len && offset < len) {
+ txt_offset = read_be_u32(buf + offset) + chunk;
+ if (txt_offset < len && txt_offset != chunk) {
+ if (!string_checker(buf, txt_offset, len))
+ return;
+ snprintf(tmpstr, sizeof tmpstr, "\nMODULENAME:\n %s\n",
+ buf + txt_offset);
+ strlcat(credits, tmpstr, credits_len);
+
+ }
+ txt_offset = read_be_u32(buf + offset + 4) + chunk;
+ if (txt_offset < len && txt_offset != chunk) {
+ if (!string_checker(buf, txt_offset, len))
+ return;
+ snprintf(tmpstr, sizeof tmpstr, "\nAUTHORNAME:\n %s\n",
+ buf + txt_offset);
+ strlcat(credits, tmpstr, credits_len);
+ }
+
+ txt_offset = read_be_u32(buf + offset + 8) + chunk;
+ if (txt_offset < len && txt_offset != chunk) {
+ if (!string_checker(buf, txt_offset, len))
+ return;
+ snprintf(tmpstr, sizeof tmpstr, "\nSPECIALINFO:\n %s",
+ buf + txt_offset);
+ strlcat(credits, tmpstr, credits_len);
+ }
+ }
+}
+
+/* Get the info out of the AHX module data*/
+static void process_ahx_mod(char *credits, size_t credits_len,
+ unsigned char *buf, size_t len)
+{
+ int i;
+ size_t offset;
+ char tmpstr[256];
+
+ if (len < 13)
+ return;
+
+ offset = read_be_u16(buf + 4);
+
+ if (offset >= len)
+ return;
+
+ if (!string_checker(buf, offset, len))
+ return;
+
+ snprintf(tmpstr, sizeof tmpstr, "\nSong title: %s\n", buf + offset);
+ strlcat(credits, tmpstr, credits_len);
+
+ for (i = 0; i < buf[12]; i++) {
+ if (!string_checker(buf, offset, len))
+ break;
+ offset = offset + 1 + strlen((char *)buf + offset);
+ if (offset < len) {
+ snprintf(tmpstr, 256, "\n %s", buf + offset);
+ strlcat(credits, tmpstr, credits_len);
+ }
+ }
+}
+
+/* Get the info out of the protracker module data*/
+static void process_ptk_mod(char *credits, size_t credits_len, int inst,
+ uint8_t * buf, size_t len)
+{
+ int i;
+ char tmpstr[256];
+
+ if (!string_checker(buf, 0, len))
+ return;
+
+ snprintf(tmpstr, 35, "\nSong title: %s", buf);
+ strlcat(credits, tmpstr, credits_len);
+
+ if (inst == 31) {
+ if (len >= 0x43c) {
+ snprintf(tmpstr, sizeof tmpstr,
+ "\nmax positions: %d\n", buf[0x3b6]);
+ strlcat(credits, tmpstr, credits_len);
+ }
+ } else {
+ if (len >= 0x1da) {
+ snprintf(tmpstr, sizeof tmpstr,
+ "\nmax positions: %d\n", buf[0x1d6]);
+ strlcat(credits, tmpstr, credits_len);
+ }
+ }
+
+ snprintf(tmpstr, sizeof tmpstr, "\nINST - NAME SIZE VOL FINE LSTART LSIZE\n");
+ strlcat(credits, tmpstr, credits_len);
+ if (len >= (0x14 + inst * 0x1e)) {
+ for (i = 0; i < inst; i++) {
+ if (!string_checker(buf, 0x14 + i * 0x1e, len))
+ break;
+ snprintf(tmpstr, sizeof tmpstr, "[%2d] - ", i + 1);
+ strlcat(credits, tmpstr, credits_len);
+ snprintf(tmpstr, 23, "%-23s", buf + 0x14 + (i * 0x1e));
+ strlcat(credits, tmpstr, credits_len);
+ snprintf(tmpstr, sizeof tmpstr,
+ " %6d %2d %2d %6d %6d\n",
+ read_be_u16(buf + 42 + i * 0x1e) * 2,
+ buf[45 + i * 0x1e], buf[44 + i * 0x1e],
+ read_be_u16(buf + 46 + i * 0x1e) * 2,
+ read_be_u16(buf + 48 + i * 0x1e) * 2);
+ strlcat(credits, tmpstr, credits_len);
+ }
+ }
+}
+
+/* Get the info out of the digibooster module data*/
+static void process_digi_mod(char *credits, size_t credits_len,
+ uint8_t * buf, size_t len)
+{
+ int i;
+ char tmpstr[256];
+
+ if (len < (642 + 0x30 * 0x1e))
+ return;
+
+ if (!string_checker(buf, 610, len))
+ return;
+
+ snprintf(tmpstr, 0x2f, "\nSong title: %s \n", buf + 610);
+ strlcat(credits, tmpstr, credits_len);
+
+ snprintf(tmpstr, sizeof tmpstr, "max positions: %d\n", buf[47]);
+ strlcat(credits, tmpstr, credits_len);
+
+ snprintf(tmpstr, sizeof tmpstr, "\nINST - NAME SIZE VOL FINE LSTART LSIZE\n");
+ strlcat(credits, tmpstr, credits_len);
+ if (len >= (642 + 0x1f * 0x1e)) {
+ for (i = 0; i < 0x1f; i++) {
+ if (!string_checker(buf, 642 + i * 0x1e, len))
+ break;
+ snprintf(tmpstr, sizeof tmpstr, "[%2d] - ", i + 1);
+ strlcat(credits, tmpstr, credits_len);
+ snprintf(tmpstr, 30, "%-30s", buf + 642 + (i * 0x1e));
+ strlcat(credits, tmpstr, credits_len);
+ snprintf(tmpstr, sizeof tmpstr,
+ " %11d %2d %3d %11d %11d\n",
+ read_be_u32(buf + 176 + i * 4), buf[548 + i],
+ buf[579 + i], read_be_u32(buf + 300 + i * 4),
+ read_be_u32(buf + 424 + i * 4));
+ strlcat(credits, tmpstr, credits_len);
+ }
+ }
+}
+
+/* Get the info out of custom song. FIX ME, clean this function. */
+static void process_custom(char *credits, size_t credits_len,
+ unsigned char *buf, size_t len)
+{
+ char tmpstr[1024];
+ unsigned char *hunk;
+ unsigned char *tag_table;
+ int hunk_size;
+ int table_size;
+
+ int i;
+ int offset;
+ unsigned int x, y;
+ unsigned char startpattern[4] = { 0x70, 0xff, 0x4e, 0x75 };
+
+ if (len < 4)
+ return;
+
+ if (read_be_u32(buf) != 0x000003f3)
+ return;
+
+ i = find_tag(buf, 0, len, startpattern, sizeof startpattern);
+ if (i == -1 || (i + 12) >= len)
+ return;
+
+ if (strncmp((char *)buf + i + 4, "DELIRIUM", 8) != 0 &&
+ strncmp((char *)buf + i + 4, "EPPLAYER", 8) != 0) {
+ return;
+ }
+
+ /* Hunk found */
+ hunk = buf + i;
+ hunk_size = len - i;
+
+ if (16 + i + 5 >= hunk_size)
+ return;
+
+ /* Check if $VER is available */
+ if (!memcmp(&hunk[16], "$VER:", 5)) {
+ offset = 16 + 5;
+ while (offset < hunk_size) {
+ if (memcmp(&hunk[offset], " ", 1))
+ break;
+ offset++;
+ }
+ if (offset >= hunk_size)
+ return;
+
+ if ((offset + strlen((char *)hunk + offset) + 1) >
+ ((size_t) hunk_size))
+ return;
+
+ snprintf(tmpstr, sizeof tmpstr, "\nVERSION:\n%s\n\n",
+ hunk + offset);
+ strlcat(credits, tmpstr, credits_len);
+ }
+
+ offset = read_be_u32(hunk + 12);
+ if (offset < 0) {
+ return;
+ }
+
+ tag_table = hunk + offset;
+
+ if (tag_table >= &buf[len])
+ return;
+
+ table_size = ((int)(&buf[len] - tag_table)) / 8;
+
+ if (table_size <= 0)
+ return;
+
+ /* check all tags in this loop */
+ for (i = 0; i < table_size; i += 2) {
+ x = read_be_u32(tag_table + 4 * i);
+ y = read_be_u32(tag_table + 4 * (i + 1));
+
+ if (!x)
+ break;
+
+ switch (x) {
+ case 0x8000445a:
+ if (y >= ((unsigned int)hunk_size))
+ return;
+ if ((y + strlen((char *)hunk + y) + 1) >
+ ((size_t) hunk_size))
+ return;
+ snprintf(tmpstr, sizeof tmpstr, "\nCREDITS:\n%s\n\n",
+ hunk + y);
+ strlcat(credits, tmpstr, credits_len);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * Get the info out of the Deltamusic 2 module data
+ */
+static void process_dm2_mod(char *credits, size_t credits_len,
+ unsigned char *buf, size_t len)
+{
+ char tmpstr[256];
+ if (!string_checker(buf, 0x148, len))
+ return;
+ snprintf(tmpstr, sizeof tmpstr, "\nRemarks:\n%s", buf + 0x148);
+ strlcat(credits, tmpstr, credits_len);
+}
+
+static int process_module(char *credits, size_t credits_len, char *filename)
+{
+ FILE *modfile;
+ struct stat st;
+ size_t modfilelen;
+ unsigned char *buf;
+ char pre[11];
+ char tmpstr[256];
+ size_t rb;
+
+ if (!(modfile = fopen(filename, "rb")))
+ return 0;
+
+ if (fstat(fileno(modfile), &st))
+ return 0;
+
+ modfilelen = st.st_size;
+
+ if ((buf = malloc(modfilelen)) == NULL) {
+ fprintf(stderr, "uade: can't allocate mem in process_module()");
+ fclose(modfile);
+ return 0;
+ }
+
+ rb = 0;
+ while (rb < modfilelen) {
+ size_t ret = fread(&buf[rb], 1, modfilelen - rb, modfile);
+ if (ret == 0)
+ break;
+ rb += ret;
+ }
+
+ fclose(modfile);
+
+ if (rb < modfilelen) {
+ fprintf(stderr, "uade: song info could not read %s fully\n",
+ filename);
+ free(buf);
+ return 0;
+ }
+
+ snprintf(tmpstr, sizeof tmpstr, "UADE2 MODINFO:\n\nFile name: %s\nFile length: %zd bytes\n", filename, modfilelen);
+ strlcpy(credits, tmpstr, credits_len);
+
+ /* Get filetype in pre */
+ uade_filemagic(buf, modfilelen, pre, modfilelen, filename, 0);
+
+ snprintf(tmpstr, sizeof tmpstr, "File prefix: %s.*\n", pre);
+ strlcat(credits, tmpstr, credits_len);
+
+ if (strcasecmp(pre, "CUST") == 0) {
+ /* CUST */
+ process_custom(credits, credits_len, buf, modfilelen);
+
+ } else if (strcasecmp(pre, "DM2") == 0) {
+ /* DM2 */
+ process_dm2_mod(credits, credits_len, buf, modfilelen);
+
+ } else if (strcasecmp(pre, "DIGI") == 0) {
+ /* DIGIBooster */
+ process_digi_mod(credits, credits_len, buf, modfilelen);
+
+ } else if ((strcasecmp(pre, "AHX") == 0) ||
+ (strcasecmp(pre, "THX") == 0)) {
+ /* AHX */
+ process_ahx_mod(credits, credits_len, buf, modfilelen);
+
+ } else if ((strcasecmp(pre, "MOD15") == 0) ||
+ (strcasecmp(pre, "MOD15_UST") == 0) ||
+ (strcasecmp(pre, "MOD15_MST") == 0) ||
+ (strcasecmp(pre, "MOD15_ST-IV") == 0)) {
+ /*MOD15 */
+ process_ptk_mod(credits, credits_len, 15, buf, modfilelen);
+
+ } else if ((strcasecmp(pre, "MOD") == 0) ||
+ (strcasecmp(pre, "MOD_DOC") == 0) ||
+ (strcasecmp(pre, "MOD_NTK") == 0) ||
+ (strcasecmp(pre, "MOD_NTK1") == 0) ||
+ (strcasecmp(pre, "MOD_NTK2") == 0) ||
+ (strcasecmp(pre, "MOD_FLT4") == 0) ||
+ (strcasecmp(pre, "MOD_FLT8") == 0) ||
+ (strcasecmp(pre, "MOD_ADSC4") == 0) ||
+ (strcasecmp(pre, "MOD_ADSC8") == 0) ||
+ (strcasecmp(pre, "MOD_COMP") == 0) ||
+ (strcasecmp(pre, "MOD_NTKAMP") == 0) ||
+ (strcasecmp(pre, "PPK") == 0) ||
+ (strcasecmp(pre, "MOD_PC") == 0) ||
+ (strcasecmp(pre, "ICE") == 0) ||
+ (strcasecmp(pre, "ADSC") == 0)) {
+ /*MOD*/
+ process_ptk_mod(credits, credits_len, 31, buf, modfilelen);
+ } else if (strcasecmp(pre, "DL") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "UNCL",
+ "EART", 0x28);
+ } else if (strcasecmp(pre, "BSS") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "BEAT",
+ "HOVE", 0x1c);
+ } else if (strcasecmp(pre, "GRAY") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "FRED",
+ "GRAY", 0x10);
+ } else if (strcasecmp(pre, "JMF") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "J.FL",
+ "OGEL", 0x14);
+ } else if (strcasecmp(pre, "SPL") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "!SOP",
+ "ROL!", 0x10);
+ } else if (strcasecmp(pre, "HD") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "H.DA",
+ "VIES", 24);
+ } else if (strcasecmp(pre, "RIFF") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "RIFF",
+ "RAFF", 0x14);
+ } else if (strcasecmp(pre, "FP") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "F.PL",
+ "AYER", 0x8);
+ } else if (strcasecmp(pre, "CORE") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "S.PH",
+ "IPPS", 0x20);
+ } else if (strcasecmp(pre, "BDS") == 0) {
+ process_WTWT_mod(credits, credits_len, buf, modfilelen, "DAGL",
+ "ISH!", 0x14);
+ }
+
+ free(buf);
+
+ return 0;
+}
+
+int uade_generate_song_title(char *title, size_t dstlen,
+ struct uade_state *state)
+{
+ size_t srcoffs;
+ size_t dstoffs;
+ size_t srclen;
+ char *format;
+ char *bname;
+ char p[64];
+ char *default_format = "%F %X [%P]";
+ struct uade_song *us = state->song;
+ struct uade_config *uc = &state->config;
+
+ /* %A min subsong
+ %B cur subsong
+ %C max subsong
+ %F file base name (us->module_filename)
+ %P player name
+ %T title
+ %X print subsong info if more than one subsong exist
+ */
+
+ format = uc->song_title;
+
+ if (format == NULL)
+ format = default_format;
+
+ if (strcmp("default", format) == 0)
+ format = default_format;
+
+ if ((srclen = strlen(format)) == 0) {
+ fprintf(stderr, "Warning: empty song_title format string.\n");
+ return 1;
+ }
+
+ if (dstlen == 0)
+ return 1;
+
+ if (strlen(us->module_filename) == 0)
+ return 1;
+
+ bname = xbasename(us->module_filename);
+
+ p[0] = 0;
+ if (us->formatname[0] == 0) {
+ if (us->playername[0] == 0) {
+ strlcpy(p, "Custom", sizeof p);
+ } else {
+ strlcpy(p, us->playername, sizeof p);
+ }
+ } else {
+ if (strncmp(us->formatname, "type: ", 6) == 0) {
+ strlcpy(p, us->formatname + 6, sizeof p);
+ } else {
+ strlcpy(p, us->formatname, sizeof p);
+ }
+ }
+
+ srcoffs = dstoffs = 0;
+
+ title[0] = 0;
+
+ while (dstoffs < dstlen) {
+ char c;
+ if (srcoffs >= srclen)
+ break;
+
+ if ((c = format[srcoffs]) == 0)
+ break;
+
+ if (c != '%') {
+ title[dstoffs++] = format[srcoffs++];
+ } else {
+ size_t inc;
+ char *dat = NULL;
+ char tmp[32];
+
+ if ((srcoffs + 1) >= srclen) {
+ fprintf(stderr, "Error: no identifier given in song title format: %s\n", format);
+ title[dstoffs] = 0;
+ return 1;
+ }
+
+ c = format[srcoffs + 1];
+
+ switch (c) {
+ case 'A':
+ snprintf(tmp, sizeof tmp, "%d",
+ us->min_subsong);
+ dat = tmp;
+ break;
+ case 'B':
+ snprintf(tmp, sizeof tmp, "%d",
+ us->cur_subsong);
+ dat = tmp;
+ break;
+ case 'C':
+ snprintf(tmp, sizeof tmp, "%d",
+ us->max_subsong);
+ dat = tmp;
+ break;
+ case 'F':
+ dat = bname;
+ break;
+ case 'P':
+ dat = p;
+ break;
+ case 'T':
+ dat = us->modulename;
+ if (strcmp("<no songtitle>", dat) == 0)
+ dat[0] = 0;
+ if (dat[0] == 0)
+ dat = bname;
+ break;
+ case 'X':
+ if (us->min_subsong == us->max_subsong) {
+ tmp[0] = 0;
+ } else {
+ snprintf(tmp, sizeof tmp, "(%d/%d)",
+ us->cur_subsong,
+ us->max_subsong);
+ }
+ dat = tmp;
+ break;
+ default:
+ fprintf(stderr,
+ "Unknown identifier %%%c in song_title format: %s\n",
+ c, format);
+ title[dstoffs] = 0;
+ return 1;
+ }
+ inc = strlcpy(&title[dstoffs], dat, dstlen - dstoffs);
+ srcoffs += 2;
+ dstoffs += inc;
+ }
+ }
+
+ if (dstoffs < dstlen)
+ title[dstoffs] = 0;
+ else
+ title[dstlen - 1] = 0;
+
+ return 0;
+}
+
+/* Returns zero on success, non-zero otherwise. */
+int uade_song_info(char *info, size_t maxlen, char *filename,
+ enum song_info_type type)
+{
+ switch (type) {
+ case UADE_MODULE_INFO:
+ return process_module(info, maxlen, filename);
+ case UADE_HEX_DUMP_INFO:
+ return hexdump(info, maxlen, filename, 2048);
+ default:
+ fprintf(stderr, "Illegal info requested.\n");
+ exit(-1);
+ }
+ return 0;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.h
new file mode 100644
index 00000000..e0abd059
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.h
@@ -0,0 +1,19 @@
+#ifndef _UADE_SONG_INFO_
+#define _UADE_SONG_INFO_
+
+#include <stdio.h>
+
+#include "uadestate.h"
+
+enum song_info_type {
+ UADE_MODULE_INFO = 0,
+ UADE_HEX_DUMP_INFO,
+ UADE_NUMBER_OF_INFOS
+};
+
+int uade_generate_song_title(char *title, size_t dstlen,
+ struct uade_state *state);
+int uade_song_info(char *info, size_t maxlen, char *filename,
+ enum song_info_type type);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.c
new file mode 100644
index 00000000..852d4c0e
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.c
@@ -0,0 +1,183 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "support.h"
+#include "ossupport.h"
+
+/* Zero terminate the current word. Returns -1 is *s == 0 or the next word
+ does not exist. Otherwise returns offset to the beginning of next word. */
+int skip_and_terminate_word(char *s, int i)
+{
+ i = skipnws(s, i);
+ if (i < 0)
+ return -1;
+
+ /* Zero terminate word */
+ s[i] = 0;
+
+ i = skipws(s, i + 1);
+ if (i < 0)
+ return -1;
+
+ return i;
+}
+
+char *xbasename(const char *s)
+{
+ char *t = strrchr(s, (int) '/');
+ if (t == NULL) {
+ t = (char *) s;
+ } else {
+ t++;
+ }
+ return t;
+}
+
+/*
+ * Split a string into 2 whitespace separated fields returned in "key" and
+ * "value". If more than 2 fields are found, they are cut off by zero
+ * terminating "key" and "value" inside the string. If "value" is not found,
+ * *value is set to NULL. If "key" is not found, *key is set to NULL.
+ * If something is found, both *key and *value become pointers inside the
+ * string s.
+ *
+ * Return values:
+ * - 0 if neither "key" nor "value" is found
+ * - 1 if only "key" is found
+ * - 2 if both "key" and "value" are found
+ */
+int get_two_ws_separated_fields(char **key, char **value, char *s)
+{
+ int i;
+
+ *key = NULL;
+ *value = NULL;
+
+ i = skipws(s, 0); /* Skip initial whitespace */
+
+ if (i < 0)
+ return 0; /* We got nothing */
+
+ *key = s + i;
+
+ i = skip_and_terminate_word(s, i);
+
+ if (i < 0)
+ return 1; /* We got a "key", but not a "value" */
+
+ *value = s + i;
+
+ skip_and_terminate_word(s, i);
+
+ return 2; /* We got both a "key" and a "value" */
+}
+
+/*
+ * Skip whitespace characters in string starting from offset i. Returns offset
+ * j >= i as the next non-whitespace character offset, or -1 if non-whitespace
+ * are not found.
+ */
+int skipws(const char *s, int i)
+{
+ while (isspace(s[i]))
+ i++;
+
+ if (s[i] == 0)
+ return -1;
+
+ return i;
+}
+
+/*
+ * Skip non-whitespace characters in string starting from offset i. Returns
+ * offset j >= i as the next whitespace character offset, or -1 if no
+ * whitespace if found.
+ */
+int skipnws(const char *s, int i)
+{
+ while (!isspace(s[i]) && s[i] != 0)
+ i++;
+
+ if (s[i] == 0)
+ return -1;
+
+ return i;
+}
+
+
+/* Split line with respect to white space. */
+char **read_and_split_lines(size_t *nitems, size_t *lineno, FILE *f,
+ const char *delim)
+{
+ char line[UADE_LINESIZE], templine[UADE_LINESIZE];
+ char **items = NULL;
+ size_t pos;
+ char *sp, *s;
+
+ *nitems = 0;
+
+ while (xfgets(line, sizeof line, f) != NULL) {
+
+ if (lineno != NULL)
+ (*lineno)++;
+
+ /* Skip, if a comment line */
+ if (line[0] == '#')
+ continue;
+
+ /* strsep() modifies line that it touches, so we make a copy
+ of it, and then count the number of items on the line */
+ strlcpy(templine, line, sizeof(templine));
+ sp = templine;
+ while ((s = strsep(&sp, delim)) != NULL) {
+ if (*s == 0)
+ continue;
+ (*nitems)++;
+ }
+
+ if (*nitems > 0)
+ break;
+ }
+
+ if (*nitems == 0)
+ return NULL;
+
+ if ((items = malloc(sizeof(items[0]) * (*nitems + 1))) == NULL)
+ uadeerror("No memory for nws items.\n");
+
+ sp = line;
+ pos = 0;
+ while ((s = strsep(&sp, delim)) != NULL) {
+ if (*s == 0)
+ continue;
+
+ if ((items[pos] = strdup(s)) == NULL)
+ uadeerror("No memory for an nws item.\n");
+
+ pos++;
+ }
+ items[pos] = NULL;
+ assert(pos == *nitems);
+
+ return items;
+}
+
+
+char *xfgets(char *s, int size, FILE *stream)
+{
+ char *ret;
+
+ while (1) {
+ ret = fgets(s, size, stream);
+ if (ret != NULL)
+ break;
+
+ if (feof(stream))
+ break;
+ }
+
+ return ret;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.h
new file mode 100644
index 00000000..1b32fe92
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.h
@@ -0,0 +1,31 @@
+#ifndef _UADE_SUPPORT_H_
+#define _UADE_SUPPORT_H_
+
+#include <stdio.h>
+
+#define UADE_LINESIZE 1024
+
+#define uadeerror(fmt, args...) do { fprintf(stderr, "uade: " fmt, ## args); exit(1); } while (0)
+
+#define MAX(x, y) ((x) >= (y) ? (x) : (y))
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+
+char *xbasename(const char *path);
+
+int get_two_ws_separated_fields(char **key, char **value, char *s);
+
+int skipnws(const char *s, int i);
+
+int skip_and_terminate_word(char *s, int i);
+
+int skipws(const char *s, int i);
+
+char **read_and_split_lines(size_t *nitems, size_t *lineno, FILE *f,
+ const char *delim);
+
+/* Same as fgets(), but guarantees that feof() or ferror() have happened
+ when xfgets() returns NULL */
+char *xfgets(char *s, int size, FILE *stream);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.c
new file mode 100644
index 00000000..17300340
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.c
@@ -0,0 +1,888 @@
+/* Handle uade.conf file
+
+ Copyright (C) 2005 Heikki Orsila <heikki.orsila@iki.fi>
+
+ This source code module is dual licensed under GPL and Public Domain.
+ Hence you may use _this_ module (not another code module) in any way you
+ want in your projects.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "ossupport.h"
+#include "uadeconf.h"
+#include "uadeconfig.h"
+#include "amigafilter.h"
+#include "uadeconstants.h"
+#include "songdb.h"
+#include "uadeutils.h"
+#include "support.h"
+
+static int uade_set_silence_timeout(struct uade_config *uc, const char *value);
+static int uade_set_subsong_timeout(struct uade_config *uc, const char *value);
+static int uade_set_timeout(struct uade_config *uc, const char *value);
+
+
+struct uade_conf_opts {
+ char *str;
+ int l;
+ enum uade_option e;
+};
+
+/* List of uade.conf options. The list includes option name, minimum
+ string match length for the option name and its enum code. */
+static const struct uade_conf_opts uadeconfopts[] = {
+ {.str = "action_keys", .l = 2, .e = UC_ACTION_KEYS},
+ {.str = "ao_option", .l = 2, .e = UC_AO_OPTION},
+ {.str = "buffer_time", .l = 1, .e = UC_BUFFER_TIME},
+ {.str = "cygwin", .l = 1, .e = UC_CYGWIN_DRIVE_WORKAROUND},
+ {.str = "detect_format_by_detection", .l = 18, .e = UC_CONTENT_DETECTION},
+ {.str = "disable_timeout", .l = 1, .e = UC_DISABLE_TIMEOUTS},
+ {.str = "enable_timeout", .l = 2, .e = UC_ENABLE_TIMEOUTS},
+ {.str = "ep_option", .l = 2, .e = UC_EAGLEPLAYER_OPTION},
+ {.str = "filter_type", .l = 2, .e = UC_FILTER_TYPE},
+ {.str = "force_led_off", .l = 12, .e = UC_FORCE_LED_OFF},
+ {.str = "force_led_on", .l = 12, .e = UC_FORCE_LED_ON},
+ {.str = "force_led", .l = 9, .e = UC_FORCE_LED},
+ {.str = "frequency", .l = 2, .e = UC_FREQUENCY},
+ {.str = "gain", .l = 1, .e = UC_GAIN},
+ {.str = "headphones", .l = 11, .e = UC_HEADPHONES},
+ {.str = "headphones2", .l = 11, .e = UC_HEADPHONES2},
+ {.str = "headphone", .l = 11, .e = UC_HEADPHONES},
+ {.str = "ignore_player_check", .l = 2, .e = UC_IGNORE_PLAYER_CHECK},
+ {.str = "interpolator", .l = 2, .e = UC_RESAMPLER},
+ {.str = "magic_detection", .l = 1, .e = UC_CONTENT_DETECTION},
+ {.str = "no_ep_end_detect", .l = 4, .e = UC_NO_EP_END},
+ {.str = "no_filter", .l = 4, .e = UC_NO_FILTER},
+ {.str = "no_song_end", .l = 4, .e = UC_NO_EP_END},
+ {.str = "normalise", .l = 1, .e = UC_NORMALISE},
+ {.str = "ntsc", .l = 2, .e = UC_NTSC},
+ {.str = "one_subsong", .l = 1, .e = UC_ONE_SUBSONG},
+ {.str = "pal", .l = 3, .e = UC_PAL},
+ {.str = "panning_value", .l = 3, .e = UC_PANNING_VALUE},
+ {.str = "random_play", .l = 3, .e = UC_RANDOM_PLAY},
+ {.str = "recursive_mode", .l = 3, .e = UC_RECURSIVE_MODE},
+ {.str = "resampler", .l = 3, .e = UC_RESAMPLER},
+ {.str = "silence_timeout_value", .l = 2, .e = UC_SILENCE_TIMEOUT_VALUE},
+ {.str = "song_title", .l = 2, .e = UC_SONG_TITLE},
+ {.str = "speed_hack", .l = 2, .e = UC_SPEED_HACK},
+ {.str = "subsong_timeout_value", .l = 2, .e = UC_SUBSONG_TIMEOUT_VALUE},
+ {.str = "timeout_value", .l = 1, .e = UC_TIMEOUT_VALUE},
+ {.str = "verbose", .l = 1, .e = UC_VERBOSE},
+ {.str = NULL} /* END OF LIST */
+};
+
+
+/* Map an uade.conf option to an enum */
+static enum uade_option map_str_to_option(const char *key)
+{
+ size_t i;
+
+ for (i = 0; uadeconfopts[i].str != NULL; i++) {
+ if (strncmp(key, uadeconfopts[i].str, uadeconfopts[i].l) == 0)
+ return uadeconfopts[i].e;
+ }
+
+ return 0;
+}
+
+/* The function sets the default options. No *_set variables are set because
+ we don't want any option to become mergeable by default. See
+ uade_merge_configs(). */
+void uade_config_set_defaults(struct uade_config *uc)
+{
+ memset(uc, 0, sizeof(*uc));
+ uc->action_keys = 1;
+ strlcpy(uc->basedir.name, UADE_CONFIG_BASE_DIR, sizeof uc->basedir.name);
+ uade_set_filter_type(uc, NULL);
+ uc->frequency = UADE_DEFAULT_FREQUENCY;
+ uc->gain = 1.0;
+ uc->panning = 0.7;
+ uc->silence_timeout = 20;
+ uc->subsong_timeout = 512;
+ uc->timeout = -1;
+ uc->use_timeouts = 1;
+}
+
+double uade_convert_to_double(const char *value, double def, double low,
+ double high, const char *type)
+{
+ char *endptr, *newvalue;
+ char newseparator;
+ double v;
+
+ if (value == NULL)
+ return def;
+
+ v = strtod(value, &endptr);
+
+ /* Decimal separator conversion, if needed */
+ if (*endptr == ',' || *endptr == '.') {
+ newvalue = strdup(value);
+ if (newvalue == NULL)
+ uade_error("Out of memory\n");
+
+ newseparator = (*endptr == ',') ? '.' : ',';
+
+ newvalue[(intptr_t) endptr - (intptr_t) value] = newseparator;
+
+ v = strtod(newvalue, &endptr);
+ free(newvalue);
+ }
+
+ if (*endptr != 0 || v < low || v > high) {
+ fprintf(stderr, "Invalid %s value: %s\n", type, value);
+ v = def;
+ }
+
+ return v;
+}
+
+static void uade_add_ep_option(struct uade_ep_options *opts, const char *s)
+{
+ size_t freespace = sizeof(opts->o) - opts->s;
+
+ if (strlcpy(&opts->o[opts->s], s, freespace) >= freespace) {
+ fprintf(stderr, "Warning: uade eagleplayer option overflow: %s\n", s);
+ return;
+ }
+
+ opts->s += strlen(s) + 1;
+}
+
+static int handle_attributes(struct uade_config *uc, struct uade_song *us,
+ char *playername, size_t playernamelen,
+ int flags, struct uade_attribute *attributelist)
+{
+ struct uade_attribute *a;
+ size_t i;
+
+ for (i = 0; epconf[i].s != NULL; i++) {
+
+ if (epconf[i].o == 0)
+ continue;
+
+ if ((flags & epconf[i].e) == 0)
+ continue;
+
+ uade_set_config_option(uc, epconf[i].o, epconf[i].c);
+ }
+
+ if (flags & ES_NEVER_ENDS)
+ fprintf(stderr, "uade: ES_NEVER_ENDS is not implemented. What should it do?\n");
+
+ if (flags & ES_REJECT)
+ return -1;
+
+ a = attributelist;
+
+ while (a != NULL) {
+
+ switch (a->type) {
+ case ES_EP_OPTION:
+ if (uc->verbose)
+ fprintf(stderr, "Using eagleplayer option %s\n", a->s);
+ uade_add_ep_option(&us->ep_options, a->s);
+ break;
+
+ case ES_GAIN:
+ uade_set_config_option(uc, UC_GAIN, a->s);
+ break;
+
+ case ES_RESAMPLER:
+ uade_set_config_option(uc, UC_RESAMPLER, a->s);
+ break;
+
+ case ES_PANNING:
+ uade_set_config_option(uc, UC_PANNING_VALUE, a->s);
+ break;
+
+ case ES_PLAYER:
+ if (playername) {
+ snprintf(playername, playernamelen, "%s/players/%s", uc->basedir.name, a->s);
+ } else {
+ fprintf(stderr, "Error: attribute handling was given playername == NULL.\n");
+ }
+ break;
+
+ case ES_SILENCE_TIMEOUT:
+ uade_set_config_option(uc, UC_SILENCE_TIMEOUT_VALUE, a->s);
+ break;
+
+ case ES_SUBSONGS:
+ fprintf(stderr, "Subsongs not implemented.\n");
+ break;
+
+ case ES_SUBSONG_TIMEOUT:
+ uade_set_config_option(uc, UC_SUBSONG_TIMEOUT_VALUE, a->s);
+ break;
+
+ case ES_TIMEOUT:
+ uade_set_config_option(uc, UC_TIMEOUT_VALUE, a->s);
+ break;
+
+ default:
+ fprintf(stderr, "Unknown song attribute integer: 0x%x\n", a->type);
+ break;
+ }
+
+ a = a->next;
+ }
+
+ return 0;
+}
+
+int uade_set_song_attributes(struct uade_state *state,
+ char *playername, size_t playernamelen)
+{
+ struct uade_song *us = state->song;
+ struct uade_config *uc = &state->config;
+
+ if (us->normalisation)
+ uade_set_config_option(uc, UC_NORMALISE, us->normalisation);
+
+ return handle_attributes(uc, us, playername, playernamelen,
+ us->flags, us->songattributes);
+}
+
+int uade_load_config(struct uade_config *uc, const char *filename)
+{
+ char line[256];
+ FILE *f;
+ char *key, *value;
+ int linenumber = 0;
+ enum uade_option opt;
+
+ if ((f = fopen(filename, "r")) == NULL)
+ return 0;
+
+ uade_config_set_defaults(uc);
+
+ while (xfgets(line, sizeof(line), f) != NULL) {
+ linenumber++;
+
+ /* Skip comment lines */
+ if (line[0] == '#')
+ continue;
+
+ if (!get_two_ws_separated_fields(&key, &value, line))
+ continue; /* Skip an empty line */
+
+ opt = map_str_to_option(key);
+
+ if (opt) {
+ uade_set_config_option(uc, opt, value);
+ } else {
+ fprintf(stderr, "Unknown config key in %s on line %d: %s\n", filename, linenumber, key);
+ }
+ }
+
+ fclose(f);
+ return 1;
+}
+
+int uade_load_initial_config(char *uadeconfname, size_t maxlen,
+ struct uade_config *uc, struct uade_config *ucbase)
+{
+ int loaded;
+ char *home;
+
+ assert(maxlen > 0);
+ uadeconfname[0] = 0;
+
+ uade_config_set_defaults(uc);
+
+ loaded = 0;
+
+ /* First try to load from forced base dir (testing mode) */
+ if (ucbase != NULL && ucbase->basedir_set) {
+ snprintf(uadeconfname, maxlen, "%s/uade.conf",
+ ucbase->basedir.name);
+ loaded = uade_load_config(uc, uadeconfname);
+ }
+
+ home = uade_open_create_home();
+
+ /* Second, try to load config from ~/.uade2/uade.conf */
+ if (loaded == 0 && home != NULL) {
+ snprintf(uadeconfname, maxlen, "%s/.uade2/uade.conf", home);
+ loaded = uade_load_config(uc, uadeconfname);
+ }
+
+ /* Third, try to load from install path */
+ if (loaded == 0) {
+ snprintf(uadeconfname, maxlen, "%s/uade.conf",
+ uc->basedir.name);
+ loaded = uade_load_config(uc, uadeconfname);
+ }
+
+ return loaded;
+}
+
+int uade_load_initial_song_conf(char *songconfname, size_t maxlen,
+ struct uade_config *uc,
+ struct uade_config *ucbase)
+{
+ int loaded = 0;
+ char *home;
+
+ assert(maxlen > 0);
+ songconfname[0] = 0;
+
+ /* Used for testing */
+ if (ucbase != NULL && ucbase->basedir_set) {
+ snprintf(songconfname, maxlen, "%s/song.conf",
+ ucbase->basedir.name);
+ loaded = uade_read_song_conf(songconfname);
+ }
+
+ /* Avoid unwanted home directory creation for test mode */
+ if (loaded)
+ return loaded;
+
+ home = uade_open_create_home();
+
+ /* Try to load from home dir */
+ if (loaded == 0 && home != NULL) {
+ snprintf(songconfname, maxlen, "%s/.uade2/song.conf", home);
+ loaded = uade_read_song_conf(songconfname);
+ }
+
+ /* No? Try install path */
+ if (loaded == 0) {
+ snprintf(songconfname, maxlen, "%s/song.conf",
+ uc->basedir.name);
+ loaded = uade_read_song_conf(songconfname);
+ }
+
+ return loaded;
+}
+
+void uade_merge_configs(struct uade_config *ucd, const struct uade_config *ucs)
+{
+#define MERGE_OPTION(y) do { if (ucs->y##_set) ucd->y = ucs->y; } while (0)
+
+ MERGE_OPTION(action_keys);
+ MERGE_OPTION(ao_options);
+ MERGE_OPTION(basedir);
+ MERGE_OPTION(buffer_time);
+ MERGE_OPTION(content_detection);
+ MERGE_OPTION(cygwin_drive_workaround);
+ MERGE_OPTION(ep_options);
+ MERGE_OPTION(filter_type);
+ MERGE_OPTION(frequency);
+ MERGE_OPTION(gain);
+ MERGE_OPTION(gain_enable);
+ MERGE_OPTION(headphones);
+ MERGE_OPTION(headphones2);
+ MERGE_OPTION(ignore_player_check);
+ MERGE_OPTION(led_forced);
+ MERGE_OPTION(led_state);
+ MERGE_OPTION(no_ep_end);
+ MERGE_OPTION(no_filter);
+ MERGE_OPTION(no_postprocessing);
+
+ /* Special merge -> don't use MERGE_OPTION macro */
+ if (ucs->normalise_set && ucs->normalise) {
+ ucd->normalise = 1;
+ if (ucs->normalise_parameter != NULL)
+ ucd->normalise_parameter = ucs->normalise_parameter;
+ }
+
+ MERGE_OPTION(one_subsong);
+ MERGE_OPTION(panning);
+ MERGE_OPTION(panning_enable);
+ MERGE_OPTION(random_play);
+ MERGE_OPTION(recursive_mode);
+ MERGE_OPTION(resampler);
+ MERGE_OPTION(silence_timeout);
+ MERGE_OPTION(song_title);
+ MERGE_OPTION(speed_hack);
+ MERGE_OPTION(subsong_timeout);
+
+ MERGE_OPTION(timeout);
+ MERGE_OPTION(use_timeouts);
+ if (ucs->timeout_set) {
+ ucd->use_timeouts = 1;
+ ucd->use_timeouts_set = 1;
+ }
+
+ MERGE_OPTION(use_text_scope);
+ MERGE_OPTION(use_ntsc);
+ MERGE_OPTION(verbose);
+}
+
+char *uade_open_create_home(void)
+{
+ /* Create ~/.uade2 directory if it does not exist */
+ char *home = getenv("HOME");
+ if (home) {
+ char name[PATH_MAX];
+ struct stat st;
+ snprintf(name, sizeof name, "%s/.uade2", home);
+ if (stat(name, &st) != 0)
+ mkdir(name, S_IRUSR | S_IWUSR | S_IXUSR);
+ }
+
+ return home;
+}
+
+int uade_parse_subsongs(int **subsongs, char *option)
+{
+ char substr[256];
+ char *sp, *str;
+ size_t pos;
+ int nsubsongs;
+
+ nsubsongs = 0;
+ *subsongs = NULL;
+
+ if (strlcpy(substr, option, sizeof subsongs) >= sizeof subsongs) {
+ fprintf(stderr, "Too long a subsong option: %s\n", option);
+ return -1;
+ }
+
+ sp = substr;
+ while ((str = strsep(&sp, ",")) != NULL) {
+ if (*str == 0)
+ continue;
+ nsubsongs++;
+ }
+
+ *subsongs = malloc((nsubsongs + 1) * sizeof((*subsongs)[0]));
+ if (*subsongs == NULL) {
+ fprintf(stderr, "No memory for subsongs.\n");
+ return -1;
+ }
+
+ strlcpy(substr, option, sizeof subsongs);
+
+ pos = 0;
+ sp = substr;
+ while ((str = strsep(&sp, ",")) != NULL) {
+ if (*str == 0)
+ continue;
+ (*subsongs)[pos] = atoi(str);
+ pos++;
+ }
+
+ (*subsongs)[pos] = -1;
+ assert(pos == nsubsongs);
+
+ return nsubsongs;
+}
+
+void uade_set_effects(struct uade_state *state)
+{
+ struct uade_effect *effects = &state->effects;
+ struct uade_config *uc = &state->config;
+
+ uade_effect_set_defaults(effects);
+
+ if (uc->no_postprocessing)
+ uade_effect_disable(effects, UADE_EFFECT_ALLOW);
+
+ if (uc->gain_enable) {
+ uade_effect_gain_set_amount(effects, uc->gain);
+ uade_effect_enable(effects, UADE_EFFECT_GAIN);
+ }
+
+ if (uc->headphones)
+ uade_effect_enable(effects, UADE_EFFECT_HEADPHONES);
+
+ if (uc->headphones2)
+ uade_effect_enable(effects, UADE_EFFECT_HEADPHONES2);
+
+ if (uc->normalise) {
+ uade_effect_normalise_unserialise(uc->normalise_parameter);
+ uade_effect_enable(effects, UADE_EFFECT_NORMALISE);
+ }
+
+ if (uc->panning_enable) {
+ uade_effect_pan_set_amount(effects, uc->panning);
+ uade_effect_enable(effects, UADE_EFFECT_PAN);
+ }
+
+ uade_effect_set_sample_rate(effects, uc->frequency);
+}
+
+void uade_set_config_option(struct uade_config *uc, enum uade_option opt,
+ const char *value)
+{
+ char *endptr;
+ long x;
+
+#define SET_OPTION(opt, value) do { uc->opt = (value); uc->opt##_set = 1; } while (0)
+
+ switch (opt) {
+ case UC_ACTION_KEYS:
+ if (value != NULL) {
+ uc->action_keys_set = 1;
+ if (!strcasecmp(value, "on") || !strcmp(value, "1")) {
+ uc->action_keys = 1;
+ } else if (!strcasecmp(value, "off") ||
+ !strcmp(value, "0")) {
+ uc->action_keys = 0;
+ } else {
+ fprintf(stderr,
+ "uade.conf: Unknown setting for action keys: %s\n",
+ value);
+ }
+ }
+ break;
+
+ case UC_AO_OPTION:
+ strlcat(uc->ao_options.o, value, sizeof uc->ao_options.o);
+ strlcat(uc->ao_options.o, "\n", sizeof uc->ao_options.o);
+ uc->ao_options_set = 1;
+ break;
+
+ case UC_BASE_DIR:
+ if (value != NULL) {
+ strlcpy(uc->basedir.name, value,
+ sizeof uc->basedir.name);
+ uc->basedir_set = 1;
+ } else {
+ fprintf(stderr, "uade: Passed NULL to UC_BASE_DIR.\n");
+ }
+ break;
+
+ case UC_BUFFER_TIME:
+ if (value != NULL) {
+ uc->buffer_time_set = 1;
+ uc->buffer_time = strtol(value, &endptr, 10);
+ if (uc->buffer_time <= 0 || *endptr != 0) {
+ fprintf(stderr, "Invalid buffer_time: %s\n",
+ value);
+ uc->buffer_time = 0;
+ }
+ } else {
+ fprintf(stderr,
+ "uade: Passed NULL to UC_BUFFER_TIME.\n");
+ }
+ break;
+
+ case UC_CONTENT_DETECTION:
+ SET_OPTION(content_detection, 1);
+ break;
+
+ case UC_CYGWIN_DRIVE_WORKAROUND:
+ SET_OPTION(cygwin_drive_workaround, 1);
+ break;
+
+ case UC_DISABLE_TIMEOUTS:
+ SET_OPTION(use_timeouts, 0);
+ break;
+
+ case UC_ENABLE_TIMEOUTS:
+ SET_OPTION(use_timeouts, 1);
+ break;
+
+ case UC_EAGLEPLAYER_OPTION:
+ if (value != NULL) {
+ uade_add_ep_option(&uc->ep_options, value);
+ uc->ep_options_set = 1;
+ } else {
+ fprintf(stderr,
+ "uade: Passed NULL to UC_EAGLEPLAYER_OPTION.\n");
+ }
+ break;
+
+ case UC_FILTER_TYPE:
+ SET_OPTION(no_filter, 0);
+
+ if (value != NULL) {
+ if (strcasecmp(value, "none") != 0) {
+ /* Filter != NONE */
+ uade_set_filter_type(uc, value);
+ uc->filter_type_set = 1;
+ } else {
+ /* Filter == NONE */
+ uc->no_filter = 1;
+ }
+ }
+ break;
+
+ case UC_FORCE_LED:
+ if (value == NULL) {
+ fprintf(stderr, "uade: UC_FORCE_LED value is NULL\n");
+ break;
+ }
+ if (strcasecmp(value, "off") == 0 || strcmp(value, "0") == 0) {
+ uc->led_state = 0;
+ } else if (strcasecmp(value, "on") == 0
+ || strcmp(value, "1") == 0) {
+ uc->led_state = 1;
+ } else {
+ fprintf(stderr, "Unknown force led argument: %s\n",
+ value);
+ break;
+ }
+ uc->led_state_set = 1;
+
+ SET_OPTION(led_forced, 1);
+ break;
+
+ case UC_FORCE_LED_OFF:
+ SET_OPTION(led_forced, 1);
+ SET_OPTION(led_state, 0);
+ break;
+
+ case UC_FORCE_LED_ON:
+ SET_OPTION(led_forced, 1);
+ SET_OPTION(led_state, 1);
+ break;
+
+ case UC_FREQUENCY:
+ if (value == NULL) {
+ fprintf(stderr, "uade: UC_FREQUENCY value is NULL\n");
+ break;
+ }
+ x = strtol(value, &endptr, 10);
+ if (*endptr != 0) {
+ fprintf(stderr, "Invalid frequency number: %s\n",
+ value);
+ break;
+ }
+ /* The upper bound is NTSC Amigas bus freq */
+ if (x < 1 || x > 3579545) {
+ fprintf(stderr, "Frequency out of bounds: %ld\n", x);
+ x = UADE_DEFAULT_FREQUENCY;
+ }
+ SET_OPTION(frequency, x);
+ break;
+
+ case UC_GAIN:
+ if (value == NULL) {
+ fprintf(stderr, "uade: UC_GAIN value is NULL\n");
+ break;
+ }
+ SET_OPTION(gain_enable, 1);
+ SET_OPTION(gain, uade_convert_to_double(value, 1.0, 0.0, 128.0, "gain"));
+ break;
+
+ case UC_HEADPHONES:
+ SET_OPTION(headphones, 1);
+ break;
+
+ case UC_HEADPHONES2:
+ SET_OPTION(headphones2, 1);
+ break;
+
+ case UC_IGNORE_PLAYER_CHECK:
+ SET_OPTION(ignore_player_check, 1);
+ break;
+
+ case UC_RESAMPLER:
+ if (value == NULL) {
+ fprintf(stderr, "uade.conf: No resampler given.\n");
+ break;
+ }
+ uc->resampler = strdup(value);
+ if (uc->resampler != NULL) {
+ uc->resampler_set = 1;
+ } else {
+ fprintf(stderr, "uade.conf: no memory for resampler.\n");
+ }
+ break;
+
+ case UC_NO_EP_END:
+ SET_OPTION(no_ep_end, 1);
+ break;
+
+ case UC_NO_FILTER:
+ SET_OPTION(no_filter, 1);
+ break;
+
+ case UC_NO_HEADPHONES:
+ SET_OPTION(headphones, 0);
+ SET_OPTION(headphones2, 0);
+ break;
+
+ case UC_NO_PANNING:
+ SET_OPTION(panning_enable, 0);
+ break;
+
+ case UC_NO_POSTPROCESSING:
+ SET_OPTION(no_postprocessing, 1);
+ break;
+
+ case UC_NORMALISE:
+ if (value == NULL) {
+ fprintf(stderr, "uade: UC_NORMALISE is NULL\n");
+ break;
+ }
+ SET_OPTION(normalise, 1);
+ uc->normalise_parameter = (char *) value;
+ break;
+
+ case UC_NTSC:
+ SET_OPTION(use_ntsc, 1);
+ break;
+
+ case UC_ONE_SUBSONG:
+ SET_OPTION(one_subsong, 1);
+ break;
+
+ case UC_PAL:
+ SET_OPTION(use_ntsc, 0);
+ break;
+
+ case UC_PANNING_VALUE:
+ if (value == NULL) {
+ fprintf(stderr, "uade: UC_PANNING_VALUE is NULL\n");
+ break;
+ }
+ SET_OPTION(panning_enable, 1);
+ SET_OPTION(panning, uade_convert_to_double(value, 0.0, 0.0, 2.0, "panning"));
+ break;
+
+ case UC_RANDOM_PLAY:
+ SET_OPTION(random_play, 1);
+ break;
+
+ case UC_RECURSIVE_MODE:
+ SET_OPTION(recursive_mode, 1);
+ break;
+
+ case UC_SILENCE_TIMEOUT_VALUE:
+ if (value == NULL) {
+ fprintf(stderr,
+ "uade: UC_SILENCE_TIMEOUT_VALUE is NULL\n");
+ break;
+ }
+ uade_set_silence_timeout(uc, value);
+ break;
+
+ case UC_SONG_TITLE:
+ if (value == NULL) {
+ fprintf(stderr, "uade: No song_title format given.\n");
+ break;
+ }
+ if ((uc->song_title = strdup(value)) == NULL) {
+ fprintf(stderr, "No memory for song title format\n");
+ } else {
+ uc->song_title_set = 1;
+ }
+ break;
+
+ case UC_SPEED_HACK:
+ SET_OPTION(speed_hack, 1);
+ break;
+
+ case UC_SUBSONG_TIMEOUT_VALUE:
+ if (value == NULL) {
+ fprintf(stderr,
+ "uade: UC_SUBSONG_TIMEOUT_VALUE is NULL\n");
+ break;
+ }
+ uade_set_subsong_timeout(uc, value);
+ break;
+
+ case UC_TIMEOUT_VALUE:
+ if (value == NULL) {
+ fprintf(stderr, "uade: UC_TIMEOUT_VALUE is NULL\n");
+ break;
+ }
+ uade_set_timeout(uc, value);
+ break;
+
+ case UC_USE_TEXT_SCOPE:
+ SET_OPTION(use_text_scope, 1);
+ break;
+
+ case UC_VERBOSE:
+ SET_OPTION(verbose, 1);
+ break;
+
+ default:
+ fprintf(stderr, "uade_set_config_option(): unknown enum: %d\n",
+ opt);
+ exit(1);
+ }
+}
+
+void uade_set_ep_attributes(struct uade_state *state)
+{
+ handle_attributes(&state->config, state->song, NULL, 0, state->ep->flags, state->ep->attributelist);
+}
+
+void uade_set_filter_type(struct uade_config *uc, const char *model)
+{
+ uc->filter_type = FILTER_MODEL_A500;
+
+ if (model == NULL)
+ return;
+
+ /* a500 and a500e are the same */
+ if (strncasecmp(model, "a500", 4) == 0) {
+ uc->filter_type = FILTER_MODEL_A500;
+
+ /* a1200 and a1200e are the same */
+ } else if (strncasecmp(model, "a1200", 5) == 0) {
+ uc->filter_type = FILTER_MODEL_A1200;
+
+ } else {
+ fprintf(stderr, "Unknown filter model: %s\n", model);
+ }
+}
+
+static int uade_set_silence_timeout(struct uade_config *uc, const char *value)
+{
+ char *endptr;
+ int t;
+ if (value == NULL) {
+ return -1;
+ }
+ t = strtol(value, &endptr, 10);
+ if (*endptr != 0 || t < -1) {
+ fprintf(stderr, "Invalid silence timeout value: %s\n", value);
+ return -1;
+ }
+ uc->silence_timeout = t;
+ uc->silence_timeout_set = 1;
+ return 0;
+}
+
+static int uade_set_subsong_timeout(struct uade_config *uc, const char *value)
+{
+ char *endptr;
+ int t;
+ if (value == NULL) {
+ return -1;
+ }
+ t = strtol(value, &endptr, 10);
+ if (*endptr != 0 || t < -1) {
+ fprintf(stderr, "Invalid subsong timeout value: %s\n", value);
+ return -1;
+ }
+ uc->subsong_timeout = t;
+ uc->subsong_timeout_set = 1;
+ return 0;
+}
+
+static int uade_set_timeout(struct uade_config *uc, const char *value)
+{
+ char *endptr;
+ int t;
+ if (value == NULL) {
+ return -1;
+ }
+ t = strtol(value, &endptr, 10);
+ if (*endptr != 0 || t < -1) {
+ fprintf(stderr, "Invalid timeout value: %s\n", value);
+ return -1;
+ }
+ uc->timeout = t;
+ uc->timeout_set = 1;
+ return 0;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.h
new file mode 100644
index 00000000..62b11ef9
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.h
@@ -0,0 +1,27 @@
+#ifndef _UADE_FRONTEND_CONFIG_H_
+#define _UADE_FRONTEND_CONFIG_H_
+
+#include <uadestate.h>
+
+void uade_config_set_defaults(struct uade_config *uc);
+double uade_convert_to_double(const char *value, double def,
+ double low, double high, const char *type);
+int uade_load_config(struct uade_config *uc, const char *filename);
+int uade_load_initial_config(char *uadeconfname, size_t maxlen,
+ struct uade_config *uc,
+ struct uade_config *ucbase);
+int uade_load_initial_song_conf(char *songconfname, size_t maxlen,
+ struct uade_config *uc,
+ struct uade_config *ucbase);
+void uade_merge_configs(struct uade_config *ucd, const struct uade_config *ucs);
+char *uade_open_create_home(void);
+int uade_parse_subsongs(int **subsongs, char *option);
+void uade_set_config_option(struct uade_config *uc, enum uade_option opt,
+ const char *value);
+void uade_set_effects(struct uade_state *state);
+void uade_set_ep_attributes(struct uade_state *state);
+int uade_set_song_attributes(struct uade_state *state, char *playername,
+ size_t playernamelen);
+void uade_set_filter_type(struct uade_config *uc, const char *value);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconfstructure.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconfstructure.h
new file mode 100644
index 00000000..d6cff1e0
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconfstructure.h
@@ -0,0 +1,139 @@
+#ifndef _UADECONF_STRUCTURE_H_
+#define _UADECONF_STRUCTURE_H_
+
+#include <limits.h>
+
+enum uade_option {
+ UC_ACTION_KEYS = 0x1000,
+ UC_AO_OPTION,
+ UC_BASE_DIR,
+ UC_BUFFER_TIME,
+ UC_CONTENT_DETECTION,
+ UC_CYGWIN_DRIVE_WORKAROUND,
+ UC_DISABLE_TIMEOUTS,
+ UC_ENABLE_TIMEOUTS,
+ UC_EAGLEPLAYER_OPTION,
+ UC_FILTER_TYPE,
+ UC_FORCE_LED_OFF,
+ UC_FORCE_LED_ON,
+ UC_FORCE_LED,
+ UC_FREQUENCY,
+ UC_GAIN,
+ UC_HEADPHONES,
+ UC_HEADPHONES2,
+ UC_IGNORE_PLAYER_CHECK,
+ UC_NO_FILTER,
+ UC_NO_HEADPHONES,
+ UC_NO_PANNING,
+ UC_NO_POSTPROCESSING,
+ UC_NO_EP_END,
+ UC_NORMALISE,
+ UC_NTSC,
+ UC_ONE_SUBSONG,
+ UC_PAL,
+ UC_PANNING_VALUE,
+ UC_RANDOM_PLAY,
+ UC_RECURSIVE_MODE,
+ UC_RESAMPLER,
+ UC_SILENCE_TIMEOUT_VALUE,
+ UC_SONG_TITLE,
+ UC_SPEED_HACK,
+ UC_SUBSONG_TIMEOUT_VALUE,
+ UC_TIMEOUT_VALUE,
+ UC_USE_TEXT_SCOPE,
+ UC_VERBOSE
+};
+
+struct uade_dir {
+ char name[PATH_MAX];
+};
+
+struct uade_ep_options {
+ char o[256];
+ size_t s;
+};
+
+struct uade_ao_options {
+ char o[256];
+};
+
+#define UADE_CHAR_CONFIG(x) char x; char x##_set;
+#define UADE_FLOAT_CONFIG(x) float x; char x##_set;
+#define UADE_INT_CONFIG(x) int x; char x##_set;
+
+/* All the options are put into an instance of this structure.
+ * There can be many structures, one for uade.conf and the other for
+ * command line options. Then these structures are then merged together
+ * to know the complete behavior for each case. Note, these structures
+ * can be conflicting, so the options are merged in following order
+ * so that the last merge will determine true behavior:
+ *
+ * 1. set uade.conf options
+ * 2. set eagleplayer attributes
+ * 3. set song attributes
+ * 4. set command line options
+ *
+ * Merging works by looking at X_set members of this structure. X_set
+ * member indicates that feature X has explicitly been set, so the
+ * merge will notice the change in value.
+ */
+struct uade_config {
+ UADE_CHAR_CONFIG(action_keys);
+
+ struct uade_ao_options ao_options;
+ char ao_options_set;
+
+ struct uade_dir basedir;
+ char basedir_set;
+
+ UADE_INT_CONFIG(buffer_time);
+ UADE_CHAR_CONFIG(content_detection);
+ UADE_CHAR_CONFIG(cygwin_drive_workaround);
+
+ struct uade_ep_options ep_options;
+ char ep_options_set;
+
+ UADE_CHAR_CONFIG(filter_type);
+ UADE_INT_CONFIG(frequency);
+ UADE_CHAR_CONFIG(led_forced);
+ UADE_CHAR_CONFIG(led_state);
+
+ UADE_CHAR_CONFIG(gain_enable);
+ /* should be removed of uade_effect integrated */
+ UADE_FLOAT_CONFIG(gain);
+
+ UADE_CHAR_CONFIG(headphones);
+ UADE_CHAR_CONFIG(headphones2);
+ UADE_CHAR_CONFIG(ignore_player_check);
+
+ char *resampler;
+ char resampler_set;
+
+ UADE_CHAR_CONFIG(no_ep_end);
+ UADE_CHAR_CONFIG(no_filter);
+ UADE_CHAR_CONFIG(no_postprocessing);
+
+ UADE_CHAR_CONFIG(normalise);
+ /* no normalise_parameter_set entry, use manual merging code */
+ char *normalise_parameter;
+
+ UADE_CHAR_CONFIG(one_subsong);
+ UADE_FLOAT_CONFIG(panning); /* should be removed */
+ UADE_CHAR_CONFIG(panning_enable);
+ UADE_CHAR_CONFIG(random_play);
+ UADE_CHAR_CONFIG(recursive_mode);
+ UADE_INT_CONFIG(silence_timeout);
+
+ char *song_title;
+ char song_title_set;
+
+ UADE_CHAR_CONFIG(speed_hack);
+ UADE_INT_CONFIG(subsong_timeout);
+ UADE_INT_CONFIG(timeout);
+ UADE_CHAR_CONFIG(use_text_scope);
+ UADE_CHAR_CONFIG(use_timeouts);
+ UADE_CHAR_CONFIG(use_ntsc);
+ UADE_CHAR_CONFIG(verbose);
+};
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.c
new file mode 100644
index 00000000..66e3eac8
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.c
@@ -0,0 +1,249 @@
+/* uadecontrol.c is a helper module to control uade core through IPC:
+
+ Copyright (C) 2005 Heikki Orsila <heikki.orsila@iki.fi>
+
+ This source code module is dual licensed under GPL and Public Domain.
+ Hence you may use _this_ module (not another code module) in any way you
+ want in your projects.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#include "uadecontrol.h"
+#include "ossupport.h"
+#include "sysincludes.h"
+#include "uadeconstants.h"
+#include "songdb.h"
+
+static void subsong_control(int subsong, int command, struct uade_ipc *ipc);
+
+void uade_change_subsong(struct uade_state *state)
+{
+ state->song->silence_count = 0;
+
+ uade_lookup_volume_normalisation(state);
+
+ subsong_control(state->song->cur_subsong, UADE_COMMAND_CHANGE_SUBSONG, &state->ipc);
+}
+
+int uade_read_request(struct uade_ipc *ipc)
+{
+ uint32_t left = UADE_MAX_MESSAGE_SIZE - sizeof(struct uade_msg);
+ if (uade_send_u32(UADE_COMMAND_READ, left, ipc)) {
+ fprintf(stderr, "\ncan not send read command\n");
+ return 0;
+ }
+ return left;
+}
+
+static int send_ep_options(struct uade_ep_options *eo, struct uade_ipc *ipc)
+{
+ if (eo->s > 0) {
+ size_t i = 0;
+ while (i < eo->s) {
+ char *s = &eo->o[i];
+ size_t l = strlen(s) + 1;
+ assert((i + l) <= eo->s);
+ if (uade_send_string
+ (UADE_COMMAND_SET_PLAYER_OPTION, s, ipc)) {
+ fprintf(stderr,
+ "Can not send eagleplayer option.\n");
+ return -1;
+ }
+ i += l;
+ }
+ }
+ return 0;
+}
+
+void uade_send_filter_command(struct uade_state *state)
+{
+ struct uade_config *uadeconf = &state->config;
+ struct uade_ipc *ipc = &state->ipc;
+
+ int filter_type = uadeconf->filter_type;
+ int filter_state = uadeconf->led_state;
+ int force_filter = uadeconf->led_forced;
+
+ if (uadeconf->no_filter)
+ filter_type = 0;
+
+ /* Note that filter state is not normally forced */
+ filter_state = force_filter ? (2 + (filter_state & 1)) : 0;
+
+ if (uade_send_two_u32s
+ (UADE_COMMAND_FILTER, filter_type, filter_state, ipc)) {
+ fprintf(stderr, "Can not setup filters.\n");
+ exit(-1);
+ }
+}
+
+static void send_resampling_command(struct uade_ipc *ipc,
+ struct uade_config *uadeconf)
+{
+ char *mode = uadeconf->resampler;
+ if (mode != NULL) {
+ if (strlen(mode) == 0) {
+ fprintf(stderr, "Resampling mode may not be empty.\n");
+ exit(-1);
+ }
+ if (uade_send_string
+ (UADE_COMMAND_SET_RESAMPLING_MODE, mode, ipc)) {
+ fprintf(stderr, "Can not set resampling mode.\n");
+ exit(-1);
+ }
+ }
+}
+
+static void subsong_control(int subsong, int command, struct uade_ipc *ipc)
+{
+ assert(subsong >= 0 && subsong < 256);
+ if (uade_send_u32(command, (uint32_t) subsong, ipc) < 0) {
+ fprintf(stderr, "Could not changet subsong\n");
+ exit(-1);
+ }
+}
+
+void uade_set_subsong(int subsong, struct uade_ipc *ipc)
+{
+ subsong_control(subsong, UADE_COMMAND_SET_SUBSONG, ipc);
+}
+
+int uade_song_initialization(const char *scorename,
+ const char *playername,
+ const char *modulename,
+ struct uade_state *state)
+{
+ uint8_t space[UADE_MAX_MESSAGE_SIZE];
+ struct uade_msg *um = (struct uade_msg *)space;
+ struct uade_ipc *ipc = &state->ipc;
+ struct uade_config *uc = &state->config;
+ struct uade_song *us = state->song;
+
+ if (uade_send_string(UADE_COMMAND_SCORE, scorename, ipc)) {
+ fprintf(stderr, "Can not send score name.\n");
+ goto cleanup;
+ }
+
+ if (uade_send_string(UADE_COMMAND_PLAYER, playername, ipc)) {
+ fprintf(stderr, "Can not send player name.\n");
+ goto cleanup;
+ }
+
+ if (uade_send_string(UADE_COMMAND_MODULE, modulename, ipc)) {
+ fprintf(stderr, "Can not send module name.\n");
+ goto cleanup;
+ }
+
+ if (uade_send_short_message(UADE_COMMAND_TOKEN, ipc)) {
+ fprintf(stderr, "Can not send token after module.\n");
+ goto cleanup;
+ }
+
+ printf ("uade_song_initialization: receive message\n");
+ if (uade_receive_message(um, sizeof(space), ipc) <= 0) {
+ fprintf(stderr, "Can not receive acknowledgement.\n");
+ goto cleanup;
+ }
+
+ if (um->msgtype == UADE_REPLY_CANT_PLAY) {
+ if (uade_receive_short_message(UADE_COMMAND_TOKEN, ipc)) {
+ fprintf(stderr,
+ "Can not receive token in main loop.\n");
+ exit(-1);
+ }
+ return UADECORE_CANT_PLAY;
+ }
+
+ if (um->msgtype != UADE_REPLY_CAN_PLAY) {
+ fprintf(stderr, "Unexpected reply from uade: %u\n",
+ (unsigned int)um->msgtype);
+ goto cleanup;
+ }
+
+ if (uade_receive_short_message(UADE_COMMAND_TOKEN, ipc) < 0) {
+ fprintf(stderr, "Can not receive token after play ack.\n");
+ goto cleanup;
+ }
+
+ if (uc->ignore_player_check) {
+ if (uade_send_short_message(UADE_COMMAND_IGNORE_CHECK, ipc) < 0) {
+ fprintf(stderr, "Can not send ignore check message.\n");
+ goto cleanup;
+ }
+ }
+
+ if (uc->no_ep_end) {
+ if (uade_send_short_message
+ (UADE_COMMAND_SONG_END_NOT_POSSIBLE, ipc) < 0) {
+ fprintf(stderr,
+ "Can not send 'song end not possible'.\n");
+ goto cleanup;
+ }
+ }
+
+ uade_send_filter_command(state);
+
+ send_resampling_command(ipc, uc);
+
+ if (uc->speed_hack) {
+ if (uade_send_short_message(UADE_COMMAND_SPEED_HACK, ipc)) {
+ fprintf(stderr, "Can not send speed hack command.\n");
+ goto cleanup;
+ }
+ }
+
+ if (uc->use_ntsc) {
+ if (uade_send_short_message(UADE_COMMAND_SET_NTSC, ipc)) {
+ fprintf(stderr, "Can not send ntsc command.\n");
+ goto cleanup;
+ }
+ }
+
+ if (uc->frequency != UADE_DEFAULT_FREQUENCY) {
+ if (uade_send_u32
+ (UADE_COMMAND_SET_FREQUENCY, uc->frequency, ipc)) {
+ fprintf(stderr, "Can not send frequency.\n");
+ goto cleanup;
+ }
+ }
+
+ if (uc->use_text_scope) {
+ if (uade_send_short_message(UADE_COMMAND_USE_TEXT_SCOPE, ipc)) {
+ fprintf(stderr, "Can not send use text scope command.\n");
+ goto cleanup;
+ }
+ }
+
+ if (send_ep_options(&us->ep_options, ipc) ||
+ send_ep_options(&uc->ep_options, ipc))
+ goto cleanup;
+
+ printf ("uade_song_initialization: success\n");
+
+ return 0;
+
+ cleanup:
+ return UADECORE_INIT_ERROR;
+}
+
+void uade_spawn(struct uade_state *state, const char *uadename,
+ const char *configname)
+{
+ uade_arch_spawn(&state->ipc, &state->pid, uadename);
+
+ if (uade_send_string(UADE_COMMAND_CONFIG, configname, &state->ipc)) {
+ fprintf(stderr, "Can not send config name: %s\n",
+ strerror(errno));
+ kill(state->pid, SIGTERM);
+ state->pid = 0;
+ abort();
+ }
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.h
new file mode 100644
index 00000000..769521a9
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.h
@@ -0,0 +1,24 @@
+#ifndef _UADE_CONTROL_
+#define _UADE_CONTROL_
+
+#include <sys/types.h>
+
+#include <uadestate.h>
+
+enum {
+ UADECORE_INIT_OK = 0,
+ UADECORE_INIT_ERROR,
+ UADECORE_CANT_PLAY
+};
+
+void uade_change_subsong(struct uade_state *state);
+int uade_read_request(struct uade_ipc *ipc);
+void uade_send_filter_command(struct uade_state *state);
+void uade_set_subsong(int subsong, struct uade_ipc *ipc);
+int uade_song_initialization(const char *scorename, const char *playername,
+ const char *modulename,
+ struct uade_state *state);
+void uade_spawn(struct uade_state *state, const char *uadename,
+ const char *configname);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadestate.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadestate.h
new file mode 100644
index 00000000..7014a30a
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadestate.h
@@ -0,0 +1,25 @@
+#ifndef _UADE_STATE_H_
+#define _UADE_STATE_H_
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <eagleplayer.h>
+#include <effects.h>
+#include <uadeipc.h>
+
+struct uade_state {
+ /* Per song members */
+ struct uade_config config;
+ struct uade_song *song;
+ struct uade_effect effects;
+ struct eagleplayer *ep;
+
+ /* Permanent members */
+ int validconfig;
+ struct eagleplayerstore *playerstore;
+ struct uade_ipc ipc;
+ pid_t pid;
+};
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.c
new file mode 100644
index 00000000..292976d7
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.c
@@ -0,0 +1,69 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <unixwalkdir.h>
+
+void *uade_walk_directories(const char *dirname,
+ void *(*fn) (const char *file,
+ enum uade_wtype wtype, void *opaque),
+ void *opaque)
+{
+ char *dename;
+ DIR *dir;
+ size_t namelen;
+ struct dirent *de;
+ void *ret = NULL;
+ struct stat st;
+ enum uade_wtype wtype;
+
+ namelen = strlen(dirname) + 256 + 2;
+ if ((dename = malloc(namelen)) == NULL)
+ return NULL;
+
+ if ((dir = opendir(dirname)) == NULL)
+ return NULL;
+
+ while ((de = readdir(dir)) != NULL) {
+
+ if (strcmp(de->d_name, ".") == 0
+ || strcmp(de->d_name, "..") == 0)
+ continue;
+
+ if (snprintf(dename, namelen, "%s/%s", dirname, de->d_name) >=
+ namelen) {
+ fprintf(stderr, "interesting: too long a filename\n");
+ continue;
+ }
+
+ if (lstat(dename, &st))
+ continue;
+
+ if (S_ISREG(st.st_mode))
+ wtype = UADE_WALK_REGULAR_FILE;
+ else if (S_ISDIR(st.st_mode))
+ wtype = UADE_WALK_DIRECTORY;
+ else if (S_ISLNK(st.st_mode))
+ wtype = UADE_WALK_SYMLINK;
+ else
+ wtype = UADE_WALK_SPECIAL;
+
+ if ((ret = fn(dename, wtype, opaque)) != NULL)
+ break;
+
+ if (wtype == UADE_WALK_DIRECTORY) {
+ if ((ret =
+ uade_walk_directories(dename, fn, opaque)) != NULL)
+ break;
+ }
+ }
+
+ closedir(dir);
+ free(dename);
+
+ return ret;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.h
new file mode 100644
index 00000000..69844f47
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.h
@@ -0,0 +1,16 @@
+#ifndef _UADE_UNIXWALKDIR_H_
+#define _UADE_UNIXWALKDIR_H_
+
+enum uade_wtype {
+ UADE_WALK_REGULAR_FILE = 1,
+ UADE_WALK_DIRECTORY,
+ UADE_WALK_SYMLINK,
+ UADE_WALK_SPECIAL
+};
+
+void *uade_walk_directories(const char *dirname,
+ void *(*fn) (const char *file,
+ enum uade_wtype wtype, void *opaque),
+ void *opaque);
+
+#endif
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.c
new file mode 100644
index 00000000..5546f54d
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.c
@@ -0,0 +1,115 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "vplist.h"
+
+
+#define VPLIST_BASIC_LENGTH 5
+
+
+static void shrink_vplist(struct vplist *v, size_t newsize)
+{
+ size_t ncopied = v->tail - v->head;
+ void **newl;
+ if (newsize >= v->allocated) {
+ fprintf(stderr, "vplist not shrinked.\n");
+ return;
+ }
+ memmove(v->l, &v->l[v->head], ncopied * sizeof(v->l[0]));
+ v->head = 0;
+ v->tail = ncopied;
+ v->allocated = newsize;
+ if ((newl = realloc(v->l, v->allocated * sizeof(v->l[0]))) == NULL) {
+ fprintf(stderr, "Not enough memory for shrinking vplist.\n");
+ exit(-1);
+ }
+ v->l = newl;
+}
+
+
+void vplist_grow(struct vplist *v)
+{
+ size_t newsize = v->allocated * 2;
+ void **newl;
+ if (newsize == 0)
+ newsize = VPLIST_BASIC_LENGTH;
+ newl = realloc(v->l, newsize * sizeof(v->l[0]));
+ if (newl == NULL) {
+ fprintf(stderr, "Not enough memory for growing vplist.\n");
+ exit(-1);
+ }
+ v->l = newl;
+ v->allocated = newsize;
+}
+
+
+struct vplist *vplist_create(size_t initial_length)
+{
+ struct vplist *v;
+ if ((v = calloc(1, sizeof(*v))) == NULL) {
+ fprintf(stderr, "No memory for vplist.\n");
+ exit(-1);
+ }
+ if (initial_length == 0)
+ initial_length = VPLIST_BASIC_LENGTH;
+ v->allocated = initial_length;
+ if ((v->l = malloc(v->allocated * sizeof(v->l[0]))) == NULL) {
+ fprintf(stderr, "Can not create a vplist.\n");
+ exit(-1);
+ }
+ return v;
+}
+
+
+void vplist_flush(struct vplist *v)
+{
+ v->head = v->tail = 0;
+ if (v->allocated >= (2 * VPLIST_BASIC_LENGTH))
+ shrink_vplist(v, VPLIST_BASIC_LENGTH);
+}
+
+
+void vplist_free(struct vplist *v)
+{
+ free(v->l);
+ memset(v, 0, sizeof(*v));
+ free(v);
+}
+
+
+void *vplist_pop_head(struct vplist *v)
+{
+ void *item;
+
+ if (v->head == v->tail) {
+ fprintf(stderr, "Error: can not pop head from an empty vplist.\n");
+ exit(-1);
+ }
+
+ item = v->l[v->head++];
+
+ /* If 3/4 of a list is unused, free half the list */
+ if (v->allocated >= VPLIST_BASIC_LENGTH && v->head >= ((v->allocated / 4) * 3))
+ shrink_vplist(v, v->allocated / 2);
+
+ return item;
+}
+
+
+void *vplist_pop_tail(struct vplist *v)
+{
+ void *item;
+
+ if (v->head == v->tail) {
+ fprintf(stderr, "Error: can not pop tail from an empty vplist.\n");
+ exit(-1);
+ }
+
+ item = v->l[v->tail--];
+
+ /* If 3/4 of a list is unused, free half the list */
+ if (v->allocated >= VPLIST_BASIC_LENGTH && v->tail < (v->allocated / 4))
+ shrink_vplist(v, v->allocated / 2);
+
+ return item;
+}
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.h b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.h
new file mode 100644
index 00000000..ef79f5bd
--- /dev/null
+++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.h
@@ -0,0 +1,44 @@
+#ifndef _SHD_VPLIST_H_
+#define _SHD_VPLIST_H_
+
+#include <stdio.h>
+#include <assert.h>
+
+
+struct vplist {
+ size_t head;
+ size_t tail;
+ size_t allocated;
+ void **l;
+};
+
+
+struct vplist *vplist_create(size_t initial_length);
+void vplist_flush(struct vplist *v);
+void vplist_free(struct vplist *v);
+void vplist_grow(struct vplist *v);
+void *vplist_pop_head(struct vplist *v);
+void *vplist_pop_tail(struct vplist *v);
+
+
+static inline void vplist_append(struct vplist *v, void *item)
+{
+ if (v->tail == v->allocated)
+ vplist_grow(v);
+ v->l[v->tail++] = item;
+}
+
+
+static inline void *vplist_get(struct vplist *v, size_t i)
+{
+ assert(i < (v->tail - v->head));
+ return v->l[v->head + i];
+}
+
+
+static inline size_t vplist_len(struct vplist *v)
+{
+ return v->tail - v->head;
+}
+
+#endif