From 4a04da22d9faf432962e98dbc8dae2e3df9b16c3 Mon Sep 17 00:00:00 2001 From: waker Date: Sun, 5 Dec 2010 16:06:29 +0100 Subject: added UADE2 plugin --- .../uade-2.13/src/frontends/common/amifilemagic.c | 1168 ++++++++++++++++++++ .../uade-2.13/src/frontends/common/amifilemagic.h | 9 + .../uade-2.13/src/frontends/common/eagleplayer.c | 502 +++++++++ .../uade-2.13/src/frontends/common/eagleplayer.h | 127 +++ .../uade-2.13/src/frontends/common/effects.c | 490 ++++++++ .../uade-2.13/src/frontends/common/effects.h | 42 + .../uade-2.13/src/frontends/common/md5.c | 247 +++++ .../uade-2.13/src/frontends/common/md5.copyright | 8 + .../uade-2.13/src/frontends/common/md5.h | 21 + .../uade-2.13/src/frontends/common/songdb.c | 798 +++++++++++++ .../uade-2.13/src/frontends/common/songdb.h | 24 + .../uade-2.13/src/frontends/common/songinfo.c | 721 ++++++++++++ .../uade-2.13/src/frontends/common/songinfo.h | 19 + .../uade-2.13/src/frontends/common/support.c | 183 +++ .../uade-2.13/src/frontends/common/support.h | 31 + .../uade-2.13/src/frontends/common/uadeconf.c | 888 +++++++++++++++ .../uade-2.13/src/frontends/common/uadeconf.h | 27 + .../src/frontends/common/uadeconfstructure.h | 139 +++ .../uade-2.13/src/frontends/common/uadecontrol.c | 249 +++++ .../uade-2.13/src/frontends/common/uadecontrol.h | 24 + .../uade-2.13/src/frontends/common/uadestate.h | 25 + .../uade-2.13/src/frontends/common/unixwalkdir.c | 69 ++ .../uade-2.13/src/frontends/common/unixwalkdir.h | 16 + .../uade-2.13/src/frontends/common/vplist.c | 115 ++ .../uade-2.13/src/frontends/common/vplist.h | 44 + 25 files changed, 5986 insertions(+) create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/amifilemagic.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/effects.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.copyright create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/md5.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songdb.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/songinfo.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/support.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconf.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadeconfstructure.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadecontrol.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/uadestate.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/unixwalkdir.h create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.c create mode 100644 plugins/ddb_input_uade2/uade-2.13/src/frontends/common/vplist.h (limited to 'plugins/ddb_input_uade2/uade-2.13/src/frontends/common') 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 +#include +#include +#include + +#include +#include + +#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 + +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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 +#include +#include + +#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 + + This module is licensed under the GNU LGPL. +*/ + +#include +#include +#include +#include +#include + +#include + +#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 + +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 /* 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<>(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 +#include + +#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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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("", 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 + +#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 +#include +#include +#include +#include + +#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 + +#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 + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +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 + +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 + + 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 +#include +#include +#include +#include +#include +#include +#include + +#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 + +#include + +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 +#include + +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include + +#include + +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 +#include + +#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 +#include + + +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 -- cgit v1.2.3