/*\ XMMS - Cross-platform multimedia player |*| Copyright (C) 1998-1999 Peter Alm, Mikael Alm, Olle Hallnas, |*| Thomas Nilsson and 4Front Technologies |*| |*| CD audio data input plugin by Willem Monsuwe (willem@stack.nl) |*| |*| This program is free software; you can redistribute it and/or modify |*| it under the terms of the GNU General Public License as published by |*| the Free Software Foundation; either version 2 of the License, or |*| (at your option) any later version. |*| |*| This program is distributed in the hope that it will be useful, |*| but WITHOUT ANY WARRANTY; without even the implied warranty of |*| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |*| GNU General Public License for more details. |*| |*| You should have received a copy of the GNU General Public License |*| along with this program; if not, write to the Free Software |*| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. \*/ /*\ |*| CDDB (cddbslave) database code. \*/ #include "cdread.h" #include #include #include #include #include #include #include #include /*\ Utility function |*| add 'add' at end of 'str', using RENEW to make room |*| Returns: str, eventually realloc()ed. <- IMPORTANT \*/ gchar * my_strncat(gchar *str, gchar *add, gint n) { gint s = 0; if (str) s = strlen(str); RENEW(str, s + n + 1); strncpy(str + s, add, n); str[s + n] = 0; return str; } gchar * my_strcat(gchar *str, gchar *add) { return my_strncat(str, add, strlen(add)); } static guint32 cddb_discid(guint32 lba[], gint nt) { gint i; guint32 n = 0, t; for (i = nt; --i >= 0; ) { t = lba[i] / 75; while (t > 0) { n += (t % 10); t /= 10; } } /*\ Important: divide before subtracting! \*/ t = (lba[nt] / 75) - (lba[0] / 75); return (((n % 0xff) << 24) | (t << 8) | nt); } static gchar * get_value(gchar *l) { l = strchr(l, '='); if (l) { gchar *p, *v; *l++ = 0; p = v = l; while (*p) { if (*p == '\\') switch(*++p) { case 'n': *v++ = '\n'; break; case 't': *v++ = '\t'; break; case '\\': *v++ = '\\'; break; default: *v++ = *--p; } else if (isprint(*p)) *v++ = *p; p++; } *v = 0; } return l; } static void cddb_read_file(struct cd_struct *cd, gboolean svr) { gchar **tt = cd->ttitle + cd->first_trk; gchar **et = cd->extt + cd->first_trk; gint nt = (cd->last_trk - cd->first_trk) + 1; gchar *fn, l[100], *v; FILE *f; gint n; for (n = 100; --n >= 0; ) { if (cd->ttitle[n]) g_free(cd->ttitle[n]); cd->ttitle[n] = 0; if (cd->extt[n]) g_free(cd->extt[n]); cd->extt[n] = 0; } if (cd->dtitle) g_free(cd->dtitle); cd->dtitle = 0; if (cd->extd) g_free(cd->extd); cd->extd = 0; if (cd->playorder) g_free(cd->playorder); cd->playorder = 0; if (cd->discid) g_free(cd->discid); cd->discid = 0; if (!cd_cfg.cddb_dir || !cd_cfg.cddb_dir[0] || !cd->id || cd->datacd) return; fn = g_strdup_printf("%s/%08x", cd_cfg.cddb_dir, cd->id); f = fopen(fn, "r"); if (!f) { /*\ frees fn when its done \*/ if (svr) { mkdir(cd_cfg.cddb_dir, 0755); cddb_server_get(cd, fn, FALSE); } else g_free(fn); return; } g_free(fn); if (!fgets(l, sizeof(l), f) || memcmp(l, "# xmcd", 6)) { fclose(f); return; } while (fgets(l, sizeof(l), f)) { if (l[0] == '#') continue; v = get_value(l); if (!v) continue; if (sscanf(l, "TTITLE%d", &n) && (n < nt)) { STRCAT(tt[n], v); } else if (sscanf(l, "EXTT%d", &n) && (n < nt)) { STRCAT(et[n], v); } else if (!strcmp(l, "DISCID")) { if (cd->discid) STRCAT(cd->discid, ","); STRCAT(cd->discid, v); } else if (!strcmp(l, "DTITLE")) { STRCAT(cd->dtitle, v); } else if (!strcmp(l, "EXTD")) { STRCAT(cd->extd, v); } else if (!strcmp(l, "PLAYORDER")) { if (cd->playorder) STRCAT(cd->playorder, ","); STRCAT(cd->playorder, v); } } fclose(f); } static void put_value(FILE *f, gchar *k, gchar *v, gint sc) { gint ll = strlen(k); gchar *ns = v; fputs(k, f); if (v) while (*v) { if (v == ns) { while (isalnum(*(++ns))) ; if ((ll > 50) && ((ll + (ns - v)) > 78)) { putc('\n', f); fputs(k, f); ll = strlen(k); if (sc) { while (!isalnum(*v)) v++; } } } if (ll > 78) { putc('\n', f); fputs(k, f); ll = strlen(k); } ll++; switch (*v) { case '\n': fputs("\\n", f); ll++; break; case '\t': fputs("\\t", f); ll++; break; case '\\': fputs("\\\\", f); ll++; break; default: putc(*v, f); break; } v++; } putc('\n', f); } /*\ Set the current playorder, but only if it's not 1,2,3,...,N |*| (a bit hairy how that's done..) \*/ static void set_playorder(struct cd_struct *cd) { gint i = 0, x = 1; GList *node, *list = playlist_find(cd->device); gchar *tmp; if (cd->playorder) g_free(cd->playorder); cd->playorder = 0; if (!list) return; CNEW(tmp, 4 * g_list_length(list)); node = list; while (node) { gint v; if (sscanf(node->data, "/" TRACK_CDR, &v) > 0) { if ((x > 0) && (v == x)) { x++; /*\ Still as expected. \*/ } else { gint j; for (j = 1; j < x; j++) { gint k = 1; while (k <= j) k *= 10; while ((k /= 10) > 0) tmp[i++] = '0' + ((j / k) % 10); tmp[i++] = ','; } x = 0; j = 1; while (j <= v) j *= 10; while ((j /= 10) > 0) tmp[i++] = '0' + ((v / j) % 10); tmp[i++] = ','; } } g_free(node->data); node = g_list_next(node); } if (i > 0) { tmp[i - 1] = 0; cd->playorder = g_strdup(tmp); } g_free(tmp); g_list_free(list); } void cddb_write_file(struct cd_struct *cd) { gchar **tt = cd->ttitle + cd->first_trk; gchar **et = cd->extt + cd->first_trk; gint nt = (cd->last_trk - cd->first_trk) + 1; gchar *fn; FILE *f; fn = g_strdup_printf("%s/%08x", cd_cfg.cddb_dir, cd->id); mkdir(cd_cfg.cddb_dir, 0755); f = fopen(fn, "w"); g_free(fn); if (f) { gint i; fputs( "# xmcd CD Database Entry\n" "#\n" "# Track frame offsets:\n", f); for (i = 0; i < nt; i++) fprintf(f, "# %d\n", cd->lba[i + cd->first_trk]); /*\ TODO: Revision \*/ fprintf(f, "#\n" "# Disc length: %d seconds\n" "#\n" "# Revision: %d\n" "# Submitted via: " PACKAGE " " VERSION "\n" "#\n", (cd->lba[cd->last_trk + 1] / 75), 0); put_value(f, "DISCID=", cd->discid, TRUE); put_value(f, "DTITLE=", cd->dtitle, FALSE); for (i = 0; i < nt; i++) { gchar tmp[20]; sprintf(tmp, "TTITLE%d=", i); put_value(f, tmp, tt[i], FALSE); } put_value(f, "EXTD=", cd->extd, FALSE); for (i = 0; i < nt; i++) { gchar tmp[20]; sprintf(tmp, "EXTT%d=", i); put_value(f, tmp, et[i], FALSE); } if (cd_cfg.playorder) set_playorder(cd); put_value(f, "PLAYORDER=", cd->playorder, TRUE); } if (!f || ferror(f)) show_dialog("Could not write CD Database file:\n%s", strerror(errno)); if (f) fclose(f); } gint cd_read_cddb(struct cd_struct *cd, gboolean svr) { guint32 di; di = cddb_discid(cd->lba + cd->first_trk, (cd->last_trk - cd->first_trk) + 1); if (di != cd->id) { cd->cddb_pending = 0; TT_LOCK(); cd->id = di; cddb_read_file(cd, svr); if (!cd->discid) cd->discid = g_strdup_printf("%08x", di); TT_UNLOCK(); return 1; } return 0; /*\ Disc not changed \*/ } gchar * cd_strdup_title(struct cd_struct *cd, gint n) { gchar *ret; TT_LOCK(); if (n == 100) { if (cd->dtitle) ret = g_strdup(cd->dtitle); else if (cd->last_trk >= cd->first_trk) ret = g_strdup("Unknown / Audio CD"); else ret = g_strdup_printf("No CD in %s", cd->device); } else if (cd->ttitle[n]) { gchar *p, *q; ret = 0; p = cd_cfg.format; while (*p) { q = strchr(p, '%'); if (!q) q = p + strlen(p); STRNCAT(ret, p, q - p); p = q; if (*p) switch(*(++p)) { case 0: --p; /*\ FALLTHROUGH \*/ case '%': STRCAT(ret, "%"); break; case 'p': case 'P': if (cd->dtitle) { gchar *sl = strchr(cd->dtitle, '/'); if (!sl) sl = cd->dtitle + strlen(cd->dtitle); while (sl > cd->dtitle) if (!isspace(*--sl)) break; sl++; STRNCAT(ret, cd->dtitle, sl - cd->dtitle); } break; case 'a': case 'A': if (cd->dtitle) { gchar *sl = strchr(cd->dtitle, '/'); if (!sl) sl = cd->dtitle + strlen(cd->dtitle); while (isspace(*++sl)) ; STRCAT(ret, sl); } break; case 't': case 'T': if (cd->ttitle[n]) STRCAT(ret, cd->ttitle[n]); break; case 'n': case 'N': { gchar buf[10]; sprintf(buf, "%d", n); STRCAT(ret, buf); break; } default: STRNCAT(ret, p - 1, 2); break; } p++; } } else { ret = g_strdup_printf("CD Audio Track %2d", n); } TT_UNLOCK(); return ret; }