Index: apps/misc.h =================================================================== --- apps/misc.h (revision 31228) +++ apps/misc.h (working copy) @@ -66,6 +66,9 @@ void car_adapter_mode_init(void); extern int show_logo(void); +#define BOM "\xef\xbb\xbf" +#define BOM_SIZE 3 + int open_utf8(const char* pathname, int flags); #ifdef BOOTFILE Index: apps/cuesheet.c =================================================================== --- apps/cuesheet.c (revision 31228) +++ apps/cuesheet.c (working copy) @@ -42,21 +42,28 @@ #define CUE_DIR ROCKBOX_DIR "/cue" -bool look_for_cuesheet_file(const char *trackpath, char *found_cue_path) +bool look_for_cuesheet_file(struct mp3entry *track_id3, struct cuesheet_file *cue_file) { /* DEBUGF("look for cue file\n"); */ char cuepath[MAX_PATH]; char *dot, *slash; - slash = strrchr(trackpath, '/'); - if (!slash) + if (track_id3->embed_cuesheet.present) { - found_cue_path = NULL; - return false; + cue_file->pos = track_id3->embed_cuesheet.pos; + cue_file->size = track_id3->embed_cuesheet.size; + strlcpy(cue_file->path, track_id3->path, MAX_PATH); + return true; } - strlcpy(cuepath, trackpath, MAX_PATH); + cue_file->pos = 0; + cue_file->size = 0; + cue_file->path[0] = '\0'; + slash = strrchr(track_id3->path, '/'); + if (!slash) + return false; + strlcpy(cuepath, track_id3->path, MAX_PATH); dot = strrchr(cuepath, '.'); strcpy(dot, ".cue"); @@ -67,15 +74,10 @@ char *dot = strrchr(cuepath, '.'); strcpy(dot, ".cue"); if (!file_exists(cuepath)) - { - if (found_cue_path) - found_cue_path = NULL; return false; - } } - if (found_cue_path) - strlcpy(found_cue_path, cuepath, MAX_PATH); + strlcpy(cue_file->path, cuepath, MAX_PATH); return true; } @@ -100,27 +102,44 @@ } /* parse cuesheet "file" and store the information in "cue" */ -bool parse_cuesheet(char *file, struct cuesheet *cue) +bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue) { char line[MAX_PATH]; char *s; bool utf8 = false; + bool is_embedded = false; + int line_len; + int bytes_left = 0; + int read_bytes = MAX_PATH; - int fd = open_utf8(file,O_RDONLY); - if (fd < 0) + int fd = open(cue_file->path, O_RDONLY, 0644); + if(fd < 0) + return false; + if (cue_file->pos > 0) { - /* couln't open the file */ - return false; + is_embedded = true; + lseek(fd, cue_file->pos, SEEK_SET); + bytes_left = cue_file->size; } - if(lseek(fd, 0, SEEK_CUR) > 0) + read(fd, line, BOM_SIZE); + if(memcmp(line, BOM, BOM_SIZE)) + lseek(fd, cue_file->pos, SEEK_SET); + else + { utf8 = true; + if (is_embedded) + bytes_left -= BOM_SIZE; + } + if (is_embedded && read_bytes > bytes_left) + read_bytes = bytes_left; /* Initialization */ memset(cue, 0, sizeof(struct cuesheet)); - strcpy(cue->path, file); + strcpy(cue->path, cue_file->path); cue->curr_track = cue->tracks; - while ( read_line(fd,line,MAX_PATH) && cue->track_count < MAX_TRACKS ) + while ((line_len = read_line(fd, line, read_bytes)) > 0 + && cue->track_count < MAX_TRACKS ) { s = skip_whitespace(line); @@ -180,6 +199,14 @@ } } } + if (is_embedded) + { + bytes_left -= line_len; + if (bytes_left <= 0) + break; + if (bytes_left < read_bytes) + read_bytes = bytes_left; + } } close(fd); @@ -256,7 +283,7 @@ bool done = false; int sel; char title[MAX_PATH]; - char cuepath[MAX_PATH]; + struct cuesheet_file cue_file; struct mp3entry *id3 = audio_current_track(); snprintf(title, MAX_PATH, "%s: %s", cue->performer, cue->title); @@ -283,8 +310,8 @@ id3 = audio_current_track(); if (id3 && *id3->path && strcmp(id3->path, "No file!")) { - look_for_cuesheet_file(id3->path, cuepath); - if (id3->cuesheet && !strcmp(cue->path, cuepath)) + look_for_cuesheet_file(id3, &cue_file); + if (id3->cuesheet && !strcmp(cue->path, cue_file.path)) { sel = gui_synclist_get_sel_pos(&lists); seek(cue->tracks[sel/2].offset); @@ -300,11 +327,16 @@ bool display_cuesheet_content(char* filename) { size_t bufsize = 0; + struct cuesheet_file cue_file; struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize); if (!cue || bufsize < sizeof(struct cuesheet)) return false; - if (!parse_cuesheet(filename, cue)) + strlcpy(cue_file.path, filename, MAX_PATH); + cue_file.pos = 0; + cue_file.size = 0; + + if (!parse_cuesheet(&cue_file, cue)) return false; browse_cuesheet(cue); Index: apps/cuesheet.h =================================================================== --- apps/cuesheet.h (revision 31228) +++ apps/cuesheet.h (working copy) @@ -51,11 +51,17 @@ struct cue_track_info *curr_track; }; +struct cuesheet_file { + char path[MAX_PATH]; + int size; + off_t pos; +}; + /* looks if there is a cuesheet file that has a name matching "trackpath" */ -bool look_for_cuesheet_file(const char *trackpath, char *found_cue_path); +bool look_for_cuesheet_file(struct mp3entry *track_id3, struct cuesheet_file *cue_file); -/* parse cuesheet "file" and store the information in "cue" */ -bool parse_cuesheet(char *file, struct cuesheet *cue); +/* parse cuesheet_file "path" and store the information in "cue" */ +bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue); /* reads a cuesheet to find the audio track associated to it */ bool get_trackname_from_cuesheet(char *filename, char *buf); Index: apps/metadata/vorbis.c =================================================================== --- apps/metadata/vorbis.c (revision 31228) +++ apps/metadata/vorbis.c (working copy) @@ -341,15 +341,26 @@ } len -= read_len; + read_len = file_read_string(&file, id3->path, sizeof(id3->path), -1, len); - if (file_read_string(&file, id3->path, sizeof(id3->path), -1, len) < 0) + if (read_len < 0) { return 0; } logf("Vorbis comment %d: %s=%s", i, name, id3->path); - len = parse_tag(name, id3->path, id3, buf, buf_remaining, - TAGTYPE_VORBIS); + if (!strcasecmp(name, "CUESHEET")) + { + id3->embed_cuesheet.present = true; + id3->embed_cuesheet.pos = lseek(file.fd, 0, SEEK_CUR) - read_len; + id3->embed_cuesheet.size = len; + } + else + { + len = parse_tag(name, id3->path, id3, buf, buf_remaining, + TAGTYPE_VORBIS); + } + buf += len; buf_remaining -= len; } Index: apps/metadata/id3tags.c =================================================================== --- apps/metadata/id3tags.c (revision 31228) +++ apps/metadata/id3tags.c (working copy) @@ -350,8 +350,8 @@ } #endif -/* parse user defined text, looking for album artist and replaygain - * information. +/* parse user defined text, looking for album artist, replaygain + * and embedded cuesheet information. */ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) { @@ -368,6 +368,8 @@ length = strlen(value) + 1; strlcpy(tag, value, length); entry->albumartist = tag; + } else if (!strcasecmp(tag, "CUESHEET")) { + entry->embed_cuesheet.present = true; #if CONFIG_CODEC == SWCODEC } else { /* Call parse_replaygain(). */ @@ -1044,6 +1046,13 @@ #endif if( tr->ppFunc ) bufferpos = tr->ppFunc(entry, tag, bufferpos); + if ( entry->embed_cuesheet.present + && entry->embed_cuesheet.pos == 0 ) + { + /* 0CUESHEET0 = 10 bytes */ + entry->embed_cuesheet.pos = lseek(fd, 0, SEEK_CUR) - framelen + 10; + entry->embed_cuesheet.size = totframelen - 10; + } break; } } Index: apps/metadata.c =================================================================== --- apps/metadata.c (revision 31228) +++ apps/metadata.c (working copy) @@ -436,6 +436,10 @@ /* Take our best guess at the codec type based on file extension */ id3->codectype = probe_file_format(trackname); + /* default values for embedded cuesheets */ + id3->embed_cuesheet.present = false; + id3->embed_cuesheet.pos = 0; + entry = &audio_formats[id3->codectype]; /* Load codec specific track tag information and confirm the codec type. */ Index: apps/metadata.h =================================================================== --- apps/metadata.h (revision 31228) +++ apps/metadata.h (working copy) @@ -217,6 +217,12 @@ }; #endif +struct embed_cuesheet { + bool present; + int size; + off_t pos; +}; + struct mp3entry { char path[MAX_PATH]; char* title; @@ -307,6 +313,7 @@ #endif /* Cuesheet support */ + struct embed_cuesheet embed_cuesheet; struct cuesheet *cuesheet; /* Musicbrainz Track ID */ Index: apps/mpeg.c =================================================================== --- apps/mpeg.c (revision 31228) +++ apps/mpeg.c (working copy) @@ -2170,10 +2170,10 @@ if (!checked_for_cuesheet && curr_cuesheet && id3->cuesheet == NULL) { checked_for_cuesheet = true; /* only check once per track */ - char cuepath[MAX_PATH]; + struct cuesheet_file cue_file; - if (look_for_cuesheet_file(id3->path, cuepath) && - parse_cuesheet(cuepath, curr_cuesheet)) + if (look_for_cuesheet_file(id3, &cue_file)) && + parse_cuesheet(&cue_file, curr_cuesheet)) { id3->cuesheet = curr_cuesheet; } Index: apps/playback.c =================================================================== --- apps/playback.c (revision 31228) +++ apps/playback.c (working copy) @@ -1485,12 +1485,12 @@ /* If error other than a full buffer, then mark it "unsupported" to avoid reloading attempt */ int hid = ERR_UNSUPPORTED_TYPE; - char cuepath[MAX_PATH]; + struct cuesheet_file cue_file; #ifdef HAVE_IO_PRIORITY buf_back_off_storage(true); #endif - if (look_for_cuesheet_file(track_id3->path, cuepath)) + if (look_for_cuesheet_file(track_id3, &cue_file)) { hid = bufalloc(NULL, sizeof (struct cuesheet), TYPE_CUESHEET); @@ -1499,7 +1499,7 @@ void *cuesheet = NULL; bufgetdata(hid, sizeof (struct cuesheet), &cuesheet); - if (parse_cuesheet(cuepath, (struct cuesheet *)cuesheet)) + if (parse_cuesheet(&cue_file, (struct cuesheet *)cuesheet)) { /* Indicate cuesheet is present (while track remains buffered) */ Index: apps/misc.c =================================================================== --- apps/misc.c (revision 31228) +++ apps/misc.c (working copy) @@ -1011,9 +1011,6 @@ * If the file is opened for writing and O_TRUNC is set, write a BOM to * the opened file and leave the file pointer set after the BOM. */ -#define BOM "\xef\xbb\xbf" -#define BOM_SIZE 3 - int open_utf8(const char* pathname, int flags) { int fd; Index: manual/configure_rockbox/playback_options.tex =================================================================== --- manual/configure_rockbox/playback_options.tex (revision 31228) +++ manual/configure_rockbox/playback_options.tex (working copy) @@ -269,10 +269,14 @@ effect. Cuesheet files should have the same file name as the audio file they - reference, except with the extension \fname{.cue}. This file can either reside in - the same directory as the audio file (checked first), or within the + reference, except with the extension \fname{.cue}. This file can either + reside in the same directory as the audio file (checked first), or within the \fname{.rockbox/cue} directory. + The contents of a cuesheet file can also be embedded within the metadata of + an audio file. There is currently support for the FLAC tag/ Vorbis comment + \emph{CUESHEET} or the ID3v2 \emph{TXXX CUESHEET} tag. + \section{Skip Length}\index{Skip Length} Designed to speed up navigation when listening to long audio tracks, \setting{Skip Length} changes the behaviour of