/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2008 Dominik Wenger * * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include #include #include #include #include "metadata.h" #include "metadata_common.h" #include "metadata_parsers.h" #include "rbunicode.h" #include "debug.h" #include "platform.h" #define MAX_SONGS 32 static char readchar(int fd, char failchr) { char ch; if (read(fd,&ch,sizeof(ch)) == sizeof(ch)) return ch; return failchr; } static bool parse_dec(int *retval, const char *p, int minval, int maxval) { int r = 0; do { char c = *p; if (c >= '0' && c <= '9') r = 10 * r + c - '0'; else return false; if (r > maxval) return false; } while (*++p != '\0'); if (r < minval) return false; *retval = r; return true; } static bool parse_text(char *retval, const char *p) { int i; if (*p != '"') return false; p++; if (p[0] == '<' && p[1] == '?' && p[2] == '>' && p[3] == '"') return true; i = 0; while (*p != '"') { if (i >= 127) return false; if (*p == '\0') return false; retval[i++] = *p++; } retval[i] = '\0'; return true; } static int ASAP_ParseDuration(const char *s) { #define chk_digit(rtn) if (*s < '0' || *s > '9') {return (rtn);} #define set_digit(mult) r += (mult) * (*s++ - '0') #define parse_digit(m) chk_digit(r) set_digit(m) int r = 0; chk_digit(-1); set_digit(1); if (*s >= '0' && *s <= '9') r = 10 * r + *s++ - '0'; if (*s == ':') { s++; if (*s < '0' || *s > '5') return -1; r = 60 * r + (*s++ - '0') * 10; chk_digit(-1); set_digit(1); } r *= 1000; if (*s != '.') return r; s++; parse_digit(100); parse_digit(10); parse_digit(1); return r; } static bool read_asap_string(char* source, char** buf, char** buffer_end, char** dest) { if(parse_text(*buf,source) == false) return false; /* set dest pointer */ *dest = *buf; /* move buf ptr */ *buf += strlen(*buf)+1; /* check size */ if(*buf >= *buffer_end) { DEBUGF("Buffer full\n"); return false; } return true; } static bool parse_sap_header(int fd, struct mp3entry* id3, int file_len) { #define EOH_CHAR (0xFF) int module_index = 0; int sap_signature = -1; int duration_index = 0; unsigned char cur_char = 0; int i; /* set defaults */ int numSongs = 1; int defSong = 0; int durations[MAX_SONGS]; for (i = 0; i < MAX_SONGS; i++) durations[i] = -1; /* use id3v2 buffer for our strings */ char* buffer = id3->id3v2buf; char* buffer_end = id3->id3v2buf + ID3V2_BUF_SIZE; /* parse file */ while (1) { char line[256]; char *p; if (module_index + 8 >= file_len) return false; cur_char = readchar(fd, EOH_CHAR); /* end of header */ if (cur_char == EOH_CHAR) break; i = 0; while (cur_char != 0x0d) { line[i++] = cur_char; module_index++; if (module_index >= file_len || (unsigned)i >= sizeof(line) - 1) return false; cur_char = readchar(fd, 0x0d); } if (++module_index >= file_len ) return false; cur_char = readchar(fd, EOH_CHAR); if ( cur_char != 0x0a) return false; line[i] = '\0'; for (p = line; *p != '\0'; p++) { if (*p == ' ') { *p++ = '\0'; break; } } static const char *tg_options[] = {"SAP", "AUTHOR", "NAME", "DATE", "SONGS", "DEFSONG", "TIME", NULL}; /* parse tags */ int tg_op = string_option(line, tg_options, false); if (tg_op == 0) /*SAP*/ sap_signature = 1; if (sap_signature == -1) return false; if (tg_op == 1) /*AUTHOR*/ { if(read_asap_string(p, &buffer, &buffer_end, &id3->artist) == false) return false; } else if(tg_op == 2) /*NAME*/ { if(read_asap_string(p, &buffer, &buffer_end, &id3->title) == false) return false; } else if(tg_op == 3) /*DATE*/ { if(read_asap_string(p, &buffer, &buffer_end, &id3->year_string) == false) return false; } else if (tg_op == 4) /*SONGS*/ { if (parse_dec(&numSongs, p, 1, MAX_SONGS) == false ) return false; } else if (tg_op == 5) /*DEFSONG*/ { if (parse_dec(&defSong, p, 0, MAX_SONGS) == false) return false; } else if (tg_op == 6) /*TIME*/ { int durationTemp = ASAP_ParseDuration(p); if (durationTemp < 0 || duration_index >= MAX_SONGS) return false; durations[duration_index++] = durationTemp; } } /* set length: */ int length = durations[defSong]; if (length < 0) length = 180 * 1000; id3->length = length; lseek(fd, 0, SEEK_SET); return true; } bool get_asap_metadata(int fd, struct mp3entry* id3) { int filelength = filesize(fd); if(parse_sap_header(fd, id3, filelength) == false) { DEBUGF("parse sap header failed.\n"); return false; } id3->bitrate = 706; id3->frequency = 44100; id3->vbr = false; id3->filesize = filelength; id3->genre_string = id3_get_num_genre(36); return true; }