diff options
Diffstat (limited to 'plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c')
-rw-r--r-- | plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c new file mode 100644 index 00000000..7c8dfce2 --- /dev/null +++ b/plugins/ddb_input_uade2/uade-2.13/src/frontends/common/eagleplayer.c @@ -0,0 +1,502 @@ +/* + * Loads contents of 'eagleplayer.conf'. The file formats are + * specified in doc/uade123.1. + * + * Copyright 2005-2007 Heikki Orsila <heikki.orsila@iki.fi> + * + * This source code module is dual licensed under GPL and Public Domain. + * Hence you may use _this_ module (not another code module) in any you + * want in your projects. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#include <stdint.h> + +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "eagleplayer.h" +#include "ossupport.h" +#include "amifilemagic.h" +#include "uadeconf.h" +#include "unixatomic.h" +#include "songdb.h" +#include "support.h" +#include "uadestate.h" + + +#define OPTION_DELIMITER "," + +#define MAX_SUFFIX_LENGTH 16 + +#define eperror(fmt, args...) do { uadeerror("Eagleplayer.conf error on line %zd: " fmt, lineno, ## args); } while (0) + + +/* Table for associating eagleplayer.conf, song.conf and uade.conf options + * together. + */ +const struct epconfattr epconf[] = { + {.s = "a500", .e = ES_A500, .o = UC_FILTER_TYPE, .c = "a500"}, + {.s = "a1200", .e = ES_A1200, .o = UC_FILTER_TYPE, .c = "a1200"}, + {.s = "always_ends", .e = ES_ALWAYS_ENDS, .o = UC_DISABLE_TIMEOUTS}, + {.s = "broken_song_end", .e = ES_BROKEN_SONG_END, .o = UC_NO_EP_END}, + {.s = "detect_format_by_content", .e = ES_CONTENT_DETECTION, .o = UC_CONTENT_DETECTION}, + {.s = "detect_format_by_name", .e = ES_NAME_DETECTION, .o = 0}, + {.s = "ignore_player_check",.e = ES_IGNORE_PLAYER_CHECK, .o = UC_IGNORE_PLAYER_CHECK}, + {.s = "led_off", .e = ES_LED_OFF, .o = UC_FORCE_LED_OFF}, + {.s = "led_on", .e = ES_LED_ON, .o = UC_FORCE_LED_ON}, + {.s = "never_ends", .e = ES_NEVER_ENDS, .o = 0}, + {.s = "no_ep_end_detect", .e = ES_BROKEN_SONG_END, .o = UC_NO_EP_END}, + {.s = "no_filter", .e = ES_NO_FILTER, .o = UC_NO_FILTER}, + {.s = "no_headphones", .e = ES_NO_HEADPHONES, .o = UC_NO_HEADPHONES}, + {.s = "no_panning", .e = ES_NO_PANNING, .o = UC_NO_PANNING}, + {.s = "no_postprocessing", .e = ES_NO_POSTPROCESSING, .o = UC_NO_POSTPROCESSING}, + {.s = "ntsc", .e = ES_NTSC, .o = UC_NTSC}, + {.s = "one_subsong", .e = ES_ONE_SUBSONG, .o = UC_ONE_SUBSONG}, + {.s = "pal", .e = ES_PAL, .o = UC_PAL}, + {.s = "reject", .e = ES_REJECT, .o = 0}, + {.s = "speed_hack", .e = ES_SPEED_HACK, .o = UC_SPEED_HACK}, + {.s = NULL} +}; + + +/* Variables for eagleplayer.conf and song.conf */ +static const struct epconfattr epconf_variables[] = { + {.s = "epopt", .t = UA_STRING, .e = ES_EP_OPTION}, + {.s = "gain", .t = UA_STRING, .e = ES_GAIN}, + {.s = "interpolator", .t = UA_STRING, .e = ES_RESAMPLER}, + {.s = "panning", .t = UA_STRING, .e = ES_PANNING}, + {.s = "player", .t = UA_STRING, .e = ES_PLAYER}, + {.s = "resampler", .t = UA_STRING, .e = ES_RESAMPLER}, + {.s = "silence_timeout", .t = UA_STRING, .e = ES_SILENCE_TIMEOUT}, + {.s = "subsong_timeout", .t = UA_STRING, .e = ES_SUBSONG_TIMEOUT}, + {.s = "subsongs", .t = UA_STRING, .e = ES_SUBSONGS}, + {.s = "timeout", .t = UA_STRING, .e = ES_TIMEOUT}, + {.s = NULL} +}; + + +static int ufcompare(const void *a, const void *b); +static struct eagleplayerstore *read_eagleplayer_conf(const char *filename); + + +static struct eagleplayer *get_eagleplayer(const char *extension, + struct eagleplayerstore *playerstore); + + +static int load_playerstore(struct uade_state *state) +{ + static int warnings = 1; + char formatsfile[PATH_MAX]; + + if (state->playerstore == NULL) { + snprintf(formatsfile, sizeof(formatsfile), + "%s/eagleplayer.conf", state->config.basedir.name); + + state->playerstore = read_eagleplayer_conf(formatsfile); + if (state->playerstore == NULL) { + if (warnings) { + fprintf(stderr, "Tried to load eagleplayer.conf from %s, but failed\n", formatsfile); + } + warnings = 0; + return 0; + } + + if (state->config.verbose) + fprintf(stderr, "Loaded eagleplayer.conf: %s\n", + formatsfile); + } + + return 1; +} + + +static struct eagleplayer *analyze_file_format(int *content, + const char *modulename, + struct uade_state *state) +{ + struct stat st; + char ext[MAX_SUFFIX_LENGTH]; + FILE *f; + struct eagleplayer *contentcandidate = NULL; + struct eagleplayer *namecandidate = NULL; + char *prefix, *postfix, *t; + size_t bufsize, bytesread; + uint8_t buf[8192]; + + *content = 0; + + if ((f = fopen(modulename, "rb")) == NULL) + return NULL; + + if (fstat(fileno(f), &st)) + uadeerror("Very weird stat error: %s (%s)\n", modulename, strerror(errno)); + + bufsize = sizeof buf; + bytesread = atomic_fread(buf, 1, bufsize, f); + fclose(f); + if (bytesread == 0) + return NULL; + memset(&buf[bytesread], 0, bufsize - bytesread); + + uade_filemagic(buf, bytesread, ext, st.st_size, modulename, state->config.verbose); + + if (strcmp(ext, "reject") == 0) + return NULL; + + if (ext[0] != 0 && state->config.verbose) + fprintf(stderr, "Content recognized: %s (%s)\n", ext, modulename); + + if (strcmp(ext, "packed") == 0) + return NULL; + + if (!load_playerstore(state)) + return NULL; + + /* First do filename detection (we'll later do content detection) */ + t = xbasename(modulename); + + if (strlcpy((char *) buf, t, sizeof buf) >= sizeof buf) + return NULL; + + t = strchr((char *) buf, '.'); + if (t == NULL) + return NULL; + + *t = 0; + prefix = (char *) buf; + + if (strlen(prefix) < MAX_SUFFIX_LENGTH) + namecandidate = get_eagleplayer(prefix, state->playerstore); + + if (namecandidate == NULL) { + /* Try postfix */ + t = xbasename(modulename); + strlcpy((char *) buf, t, sizeof buf); + postfix = strrchr((char *) buf, '.') + 1; /* postfix != NULL */ + + if (strlen(postfix) < MAX_SUFFIX_LENGTH) + namecandidate = get_eagleplayer(postfix, state->playerstore); + } + + /* If filemagic found a match, we'll use player plugins associated with + that extension */ + if (ext[0]) { + contentcandidate = get_eagleplayer(ext, state->playerstore); + if (contentcandidate != NULL) { + /* Do not recognize name detectable eagleplayers by + content */ + if (namecandidate == NULL || + (namecandidate->flags & ES_NAME_DETECTION) == 0) { + *content = 1; + return contentcandidate; + } + } else { + if (state->config.verbose) + fprintf(stderr, "%s not in eagleplayer.conf\n", ext); + } + } + + if (state->config.verbose) + fprintf(stderr, "Format detection by filename\n"); + + return namecandidate; +} + + +static void handle_attribute(struct uade_attribute **attributelist, + const struct epconfattr *attr, + char *item, size_t len, size_t lineno) +{ + struct uade_attribute *a; + char *str, *endptr; + int success = 0; + + if (item[len] != '=') { + fprintf(stderr, "Invalid song item: %s\n", item); + return; + } + str = item + len + 1; + + if ((a = calloc(1, sizeof *a)) == NULL) + eperror("No memory for song attribute.\n"); + + switch (attr->t) { + case UA_DOUBLE: + a->d = strtod(str, &endptr); + if (*endptr == 0) + success = 1; + break; + case UA_INT: + a->i = strtol(str, &endptr, 10); + if (*endptr == 0) + success = 1; + break; + case UA_STRING: + a->s = strdup(str); + if (a->s == NULL) + eperror("Out of memory allocating string option for song\n"); + success = 1; + break; + default: + fprintf(stderr, "Unknown song option: %s\n", + item); + break; + } + + if (success) { + a->type = attr->e; + a->next = *attributelist; + *attributelist = a; + } else { + fprintf(stderr, "Invalid song option: %s\n", item); + free(a); + } +} + + +int uade_song_and_player_attribute(struct uade_attribute **attributelist, + int *flags, char *item, size_t lineno) +{ + size_t i, len; + + for (i = 0; epconf[i].s != NULL; i++) { + if (strcasecmp(item, epconf[i].s) == 0) { + *flags |= epconf[i].e; + return 1; + } + } + + for (i = 0; epconf_variables[i].s != NULL; i++) { + len = strlen(epconf_variables[i].s); + if (strncasecmp(item, epconf_variables[i].s, len) != 0) + continue; + + handle_attribute(attributelist, &epconf_variables[i], + item, len, lineno); + return 1; + } + + return 0; +} + +/* Compare function for bsearch() and qsort() to sort eagleplayers with + respect to name extension. */ +static int ufcompare(const void *a, const void *b) +{ + const struct eagleplayermap *ua = a; + const struct eagleplayermap *ub = b; + + return strcasecmp(ua->extension, ub->extension); +} + +int uade_is_our_file(const char *modulename, int scanmode, + struct uade_state *state) +{ + int content; + struct eagleplayer *ep; + + ep = analyze_file_format(&content, modulename, state); + + if (!scanmode) + state->ep = ep; + + if (ep == NULL) + return 0; + + if (content) + return 1; + + if (state->config.content_detection && content == 0) + return 0; + + if ((ep->flags & ES_CONTENT_DETECTION) != 0) + return 0; + + return 1; +} + +static struct eagleplayer *get_eagleplayer(const char *extension, + struct eagleplayerstore *ps) +{ + struct eagleplayermap *uf = ps->map; + struct eagleplayermap *f; + struct eagleplayermap key = {.extension = (char *)extension }; + + f = bsearch(&key, uf, ps->nextensions, sizeof(uf[0]), ufcompare); + if (f == NULL) + return NULL; + + return f->player; +} + +/* Read eagleplayer.conf. */ +static struct eagleplayerstore *read_eagleplayer_conf(const char *filename) +{ + FILE *f; + struct eagleplayer *p; + size_t allocated; + size_t lineno = 0; + struct eagleplayerstore *ps = NULL; + size_t exti; + size_t i, j; + int epwarning; + + f = fopen(filename, "r"); + if (f == NULL) + goto error; + + ps = calloc(1, sizeof ps[0]); + if (ps == NULL) + eperror("No memory for ps."); + + allocated = 16; + if ((ps->players = malloc(allocated * sizeof(ps->players[0]))) == NULL) + eperror("No memory for eagleplayer.conf file.\n"); + + while (1) { + char **items; + size_t nitems; + + items = read_and_split_lines(&nitems, &lineno, f, UADE_WS_DELIMITERS); + if (items == NULL) + break; + + assert(nitems > 0); + + if (ps->nplayers == allocated) { + allocated *= 2; + ps->players = realloc(ps->players, allocated * sizeof(ps->players[0])); + if (ps->players == NULL) + eperror("No memory for players."); + } + + p = &ps->players[ps->nplayers]; + ps->nplayers++; + + memset(p, 0, sizeof p[0]); + + p->playername = strdup(items[0]); + if (p->playername == NULL) + uadeerror("No memory for playername.\n"); + + for (i = 1; i < nitems; i++) { + + if (strncasecmp(items[i], "prefixes=", 9) == 0) { + char prefixes[UADE_LINESIZE]; + char *prefixstart = items[i] + 9; + char *sp, *s; + size_t pos; + + assert(p->nextensions == 0 && p->extensions == NULL); + + p->nextensions = 0; + strlcpy(prefixes, prefixstart, + sizeof(prefixes)); + sp = prefixes; + while ((s = strsep(&sp, OPTION_DELIMITER)) != NULL) { + if (*s == 0) + continue; + p->nextensions++; + } + + p->extensions = + malloc((p->nextensions + + 1) * sizeof(p->extensions[0])); + if (p->extensions == NULL) + eperror("No memory for extensions."); + + pos = 0; + sp = prefixstart; + while ((s = strsep(&sp, OPTION_DELIMITER)) != NULL) { + if (*s == 0) + continue; + + p->extensions[pos] = strdup(s); + if (s == NULL) + eperror("No memory for prefix."); + pos++; + } + p->extensions[pos] = NULL; + assert(pos == p->nextensions); + + continue; + } + + if (strncasecmp(items[i], "comment:", 7) == 0) + break; + + if (uade_song_and_player_attribute(&p->attributelist, &p->flags, items[i], lineno)) + continue; + + fprintf(stderr, "Unrecognized option: %s\n", items[i]); + } + + for (i = 0; items[i] != NULL; i++) + free(items[i]); + + free(items); + } + + fclose(f); + + if (ps->nplayers == 0) { + free(ps->players); + free(ps); + return NULL; + } + + for (i = 0; i < ps->nplayers; i++) + ps->nextensions += ps->players[i].nextensions; + + ps->map = malloc(sizeof(ps->map[0]) * ps->nextensions); + if (ps->map == NULL) + eperror("No memory for extension map."); + + exti = 0; + epwarning = 0; + for (i = 0; i < ps->nplayers; i++) { + p = &ps->players[i]; + if (p->nextensions == 0) { + if (epwarning == 0) { + fprintf(stderr, + "uade warning: %s eagleplayer lacks prefixes in " + "eagleplayer.conf, which makes it unusable for any kind of " + "file type detection. If you don't want name based file type " + "detection for a particular format, use content_detection " + "option for the line in eagleplayer.conf.\n", + ps->players[i].playername); + epwarning = 1; + } + continue; + } + for (j = 0; j < p->nextensions; j++) { + assert(exti < ps->nextensions); + ps->map[exti].player = p; + ps->map[exti].extension = p->extensions[j]; + exti++; + } + } + + assert(exti == ps->nextensions); + + /* Make the extension map bsearch() ready */ + qsort(ps->map, ps->nextensions, sizeof(ps->map[0]), ufcompare); + + return ps; + + error: + if (ps) + free(ps->players); + free(ps); + if (f != NULL) + fclose(f); + return NULL; +} |