/*************************************************************************** * __________ __ ___. * 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 "system.h" #include "metadata.h" #include "metadata_common.h" #include "metadata_parsers.h" #include "rbunicode.h" #include "debug.h" #define MAX_SONGS 32 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) { int r; if (*s < '0' || *s > '9') return -1; r = *s++ - '0'; 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; if (*s < '0' || *s > '9') return -1; r += *s++ - '0'; } r *= 1000; if (*s != '.') return r; s++; if (*s < '0' || *s > '9') return r; r += 100 * (*s++ - '0'); if (*s < '0' || *s > '9') return r; r += 10 * (*s++ - '0'); if (*s < '0' || *s > '9') return r; r += *s - '0'; 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) { 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 numChannels = 1; int durations[MAX_SONGS]; int loops[MAX_SONGS]; for (i = 0; i < MAX_SONGS; i++) { durations[i] = -1; loops[i] = 0; } /* 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; /* read a char */ read(fd,&cur_char,1); /* end of header */ if (cur_char == 0xff) 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; /* read a char */ read(fd,&cur_char,1); } if (++module_index >= file_len ) return false; /* read a char */ read(fd,&cur_char,1); if ( cur_char != 0x0a) return false; line[i] = '\0'; for (p = line; *p != '\0'; p++) { if (*p == ' ') { *p++ = '\0'; break; } } /* parse tags */ if(strcmp(line, "SAP") == 0) sap_signature = 1; if (sap_signature == -1) return false; if (strcmp(line, "AUTHOR") == 0) { if(read_asap_string(p, &buffer, &buffer_end, &id3->artist) == false) return false; } else if(strcmp(line, "NAME") == 0) { if(read_asap_string(p, &buffer, &buffer_end, &id3->title) == false) return false; } else if(strcmp(line, "DATE") == 0) { if(read_asap_string(p, &buffer, &buffer_end, &id3->year_string) == false) return false; } else if (strcmp(line, "SONGS") == 0) { if (parse_dec(&numSongs, p, 1, MAX_SONGS) == false ) return false; } else if (strcmp(line, "DEFSONG") == 0) { if (parse_dec(&defSong, p, 0, MAX_SONGS) == false) return false; } else if (strcmp(line, "STEREO") == 0) { numChannels = 2; } else if (strcmp(line, "TIME") == 0) { int durationTemp = ASAP_ParseDuration(p); if (durationTemp < 0 || duration_index >= MAX_SONGS) return false; durations[duration_index] = durationTemp; if (strstr(p, "LOOP") != NULL) loops[duration_index] = 1; duration_index++; } } /* 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; return true; }