Index: apps/SOURCES
===================================================================
RCS file: /cvsroot/rockbox/apps/SOURCES,v
retrieving revision 1.58
diff -u -r1.58 SOURCES
--- apps/SOURCES	19 Nov 2006 14:11:40 -0000	1.58
+++ apps/SOURCES	18 Dec 2006 00:58:09 -0000
@@ -23,6 +23,7 @@
 settings_menu.c
 sound_menu.c
 status.c
+cuesheet.c
 #if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC
 talk.c
 #endif
Index: apps/cuesheet.c
===================================================================
RCS file: apps/cuesheet.c
diff -N apps/cuesheet.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/cuesheet.c	18 Dec 2006 00:58:09 -0000
@@ -0,0 +1,318 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: cuesheet.c,v 1.17 2006-12-05 10:09:06 markun Exp $
+ *
+ *
+ * Copyright (C) 2006 Nicolas Pennequin, Jonathan Gordon
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <atoi.h>
+#include <string.h>
+#include "system.h"
+#include "audio.h"
+#include "kernel.h"
+#include "logf.h"
+#include "sprintf.h"
+#include "misc.h"
+#include "screens.h"
+#include "splash.h"
+#include "list.h"
+#include "action.h"
+#include "lang.h"
+#include "debug.h"
+
+#include "cuesheet.h"
+
+struct mp3entry* id3;
+
+bool is_cuesheet(char *filename)
+{
+    char *suffix;
+    suffix = strrchr(filename, '.');
+    if (suffix)
+        return (strcasecmp(suffix, ".cue") == 0);
+    else
+        return false;
+}
+
+char *skip_whitespace(char* buf)
+{
+    char *r = buf;
+    while (*r && (*r < 33))
+        r++;
+    return r;
+}
+
+/* Copy the name of the audio track associated to a cuesheet.
+ * The name is placed in "buf".
+ * Returns false if "filename" isn't a cuesheet or if there was an error.
+ */
+bool get_trackname_from_cuesheet(char *filename, char *buf)
+{
+    if (!is_cuesheet(filename))
+        return false;
+
+    char line[MAX_PATH], cd_filename[MAX_PATH];
+    char *s, *start, *end, *slash;
+    int fd = open(filename,O_RDONLY);
+    if (fd<0)
+    {
+        gui_syncsplash(HZ*2,true,"Couldnt open file: %s",filename);
+        return false;
+    }
+
+    while (read_line(fd,line,MAX_PATH))
+    {
+        s = skip_whitespace(line);
+        if (MATCH(s, "FILE"))
+        {
+            start = strchr(s,'"');
+            if (!start)
+                break;
+            end = strchr(++start,'"');
+            if (!end)
+                break;
+            *end = '\0';
+            strncpy(cd_filename,start,MAX_PATH);
+
+            strncpy(buf,filename,MAX_PATH);
+            slash = strrchr(buf,'/');
+            strncpy(slash+1,cd_filename,MAX_NAME);
+        }
+    }
+
+    close(fd);
+    return true;
+}
+
+/* Replace the filename of a cuesheet by the name of the audio track it plays
+ * and replace the file descriptor to that of the new opened file.
+ * Returns true if the file was a cuesheet and the filename was replaced,
+ * or false if the file wasn't a cuesheet or if there was an error.
+ */
+bool cue_spoof_track(char *trackname, int *current_fd)
+{
+    if (is_cuesheet(trackname))
+    {
+        char buf[MAX_PATH];
+        get_trackname_from_cuesheet(trackname,buf);
+        strncpy(trackname,buf,MAX_PATH);
+        close(*current_fd);
+        *current_fd = open(trackname, O_RDONLY);
+        DEBUGF("spoofed trackname and the file descriptor for a cuesheet\n");
+        return true;
+    }
+    else
+    {
+        /* the track wasn't a cuesheet or there was an error */
+        return false;
+    }
+}
+
+bool parse_cuesheet(char *file)
+{
+    char line[MAX_PATH];
+    char *s, *start, *end, *slash;
+    int fd = open(file,O_RDONLY);
+    if (fd<0)
+    {
+        gui_syncsplash(HZ*2,true,"Couldnt open file: %s",file);
+        return false;
+    }
+
+    strncpy(cue_current,file,MAX_PATH);
+
+    cue_track_count = -1;
+    while (read_line(fd,line,MAX_PATH))
+    {
+        s = skip_whitespace(line);
+        if (MATCH(s, "TITLE"))
+        {
+            start = strchr(s,'"');
+            if (!start)
+                break;
+            end = strchr(++start,'"');
+            if (!end)
+                break;
+            *end = '\0';
+            if (cue_track_count < 0)
+                strncpy(cue_cd_title,start,MAX_NAME);
+            else strncpy(cue_tracks[cue_track_count].title,
+                                start,MAX_NAME);
+        }
+        else if (MATCH(s, "PERFORMER"))
+        {
+            start = strchr(s,'"');
+            if (!start)
+                break;
+            end = strchr(++start,'"');
+            if (!end)
+                break;
+            *end = '\0';
+            if (cue_track_count < 0)
+                strncpy(cue_cd_performer,start,MAX_NAME);
+            else strncpy(cue_tracks[cue_track_count].performer,
+                                start,MAX_NAME);
+        }
+        else if (MATCH(s, "FILE"))
+        {
+            start = strchr(s,'"');
+            if (!start)
+                break;
+            end = strchr(++start,'"');
+            if (!end)
+                break;
+            *end = '\0';
+
+            strncpy(cue_cd_filename,file,MAX_PATH);
+            slash = strrchr(cue_cd_filename,'/');
+            strncpy(slash+1,start,MAX_NAME);
+        }
+        else if (MATCH(s, "TRACK"))
+            cue_track_count++;
+        else if (MATCH(s, "INDEX"))
+        {
+            char min[3], sec[3], fr[3];
+            s = strchr(s,' ');
+            s = skip_whitespace(s);
+            s = strchr(s,' ');
+            s = skip_whitespace(s);
+            strncpy(min,s,3);
+            s += 3;min[2] ='\0';
+            strncpy(sec,s,3);
+            s += 3;sec[2] ='\0';
+            strncpy(fr,s,3);fr[2] ='\0';
+            cue_tracks[cue_track_count].offset = (13*atoi(fr))
+                                          + (1000*atoi(sec))
+                                          + (1000*atoi(min)*60);
+            logf("%d %ld",cue_track_count,cue_tracks[cue_track_count].offset);
+        }
+    }
+    close(fd);
+    cue_loaded = true;
+    return true;
+}
+
+bool seek(unsigned int pos){
+    if (!(audio_status() & AUDIO_STATUS_PLAY) || pos > id3->length)
+    {
+        return false;
+    }
+    else
+    {
+        audio_pre_ff_rewind();
+        audio_ff_rewind(pos);
+        return true;
+    }
+}
+
+int find_current_track(void)
+{
+    int i=0;
+    while (cue_tracks[i+1].offset < id3->elapsed)
+    {
+        i++;
+    }
+    return i;
+}
+
+char *list_get_name_cb(int selected_item,
+                             void *data,
+                             char *buffer)
+{
+    if (selected_item % 2)
+    {
+        snprintf(buffer,MAX_PATH,(selected_item+1)/2>9 ? "   %s" : "  %s",
+                     cue_tracks[selected_item/2].title);
+    }
+    else
+    {
+        snprintf(buffer,MAX_PATH,"%d %s",
+                     selected_item/2+1,cue_tracks[selected_item/2].performer);
+    }
+    return buffer;
+}
+
+void browse(void)
+{
+    struct gui_synclist lists;
+    int action;
+    bool done = false;
+    int sel;
+    char title[MAX_PATH];
+    snprintf(title,MAX_PATH,"%s: %s",cue_cd_performer, cue_cd_title);
+    gui_synclist_init(&lists,list_get_name_cb,0,false,2);
+    gui_synclist_set_nb_items(&lists,2*cue_track_count);
+    gui_synclist_select_item(&lists,2*find_current_track());
+    gui_synclist_set_title(&lists,title,0);
+
+    while (!done)
+    {
+        gui_synclist_draw(&lists);
+        action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
+        if (gui_synclist_do_button(&lists,action,LIST_WRAP_UNLESS_HELD))
+            continue;
+        switch (action)
+        {
+            case ACTION_STD_OK:
+                id3 = audio_current_track();
+                sel = gui_synclist_get_sel_pos(&lists);
+                if (!seek(cue_tracks[sel/2].offset))
+                {
+                    gui_syncsplash(HZ, true, "Seeking there isn't allowed");
+                }
+                break;
+            case ACTION_STD_CANCEL:
+                done = true;
+        }
+    }
+}
+
+/* enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
+{
+    char filename[MAX_PATH], *s;
+    rb = api;
+    if (!parameter)
+        return PLUGIN_ERROR;
+
+    strcpy(filename,(char*)parameter);
+    if (!parse_cuesheet(filename))
+        return PLUGIN_ERROR;
+
+    s = strrchr(filename,'/');
+    if (!s)
+    {
+        gui_syncsplash(HZ,true,"Cuesheet Failed!");
+        return PLUGIN_ERROR;
+    }
+    strcpy(s+1,cd_filename);
+
+    id3 = audio_current_track();
+    browse();
+    return PLUGIN_OK;
+} */
+
+bool open_cuesheet(char* filename)
+{
+    if (!parse_cuesheet(filename))
+        return false;
+
+    id3 = audio_current_track();
+    browse();
+    return true;
+}
Index: apps/cuesheet.h
===================================================================
RCS file: apps/cuesheet.h
diff -N apps/cuesheet.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/cuesheet.h	18 Dec 2006 00:58:09 -0000
@@ -0,0 +1,50 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $$
+ *
+ * Copyright (C) 2005 Nicolas Pennequin, Jonathan Gordon
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _CUESHEET_H_
+#define _CUESHEET_H_
+
+#include <stdbool.h>
+
+#define MAX_NAME 64
+#define MAX_TRACKS 99
+
+char cue_cd_title[MAX_NAME];
+char cue_cd_performer[MAX_NAME];
+char cue_cd_filename[MAX_PATH]; /* complete path to the track associated */
+
+char cue_current[MAX_PATH];
+bool cue_loaded;
+
+struct cue_track_info {
+    char title[MAX_NAME];
+    char performer[MAX_NAME];
+    unsigned long offset; /* ms from start of track */
+} cue_tracks[MAX_TRACKS];
+int cue_track_count;
+
+#define MATCH(l,s) !strncmp(l,s,strlen(s)) 
+
+bool is_cuesheet(char *filename);
+bool get_trackname_from_cuesheet(char *filename, char *buf);
+bool cue_spoof_track(char *trackname, int *current_fd);
+bool open_cuesheet(char* filename);
+bool parse_cuesheet(char *file);
+int find_current_track(void);
+
+#endif
Index: apps/onplay.c
===================================================================
RCS file: /cvsroot/rockbox/apps/onplay.c,v
retrieving revision 1.94
diff -u -r1.94 onplay.c
--- apps/onplay.c	14 Dec 2006 22:43:16 -0000	1.94
+++ apps/onplay.c	18 Dec 2006 00:58:09 -0000
@@ -50,6 +50,7 @@
 #include "action.h"
 #include "splash.h"
 #include "yesno.h"
+#include "cuesheet.h"
 #if LCD_DEPTH > 1
 #include "backdrop.h"
 #endif
@@ -519,6 +520,11 @@
 }
 #endif
 
+static bool view_cuesheet(void)
+{
+    return open_cuesheet(selected_file);
+}
+
 static bool rename_file(void)
 {
     char newname[MAX_PATH];
@@ -950,6 +956,12 @@
                     }
                 }
 #endif
+                if (is_cuesheet(selected_file))
+                {
+                    items[i].desc = ID2P(LANG_VIEW_CUESHEET);
+                    items[i].function = view_cuesheet;
+                    i++;
+                }
             }
             else
             {
Index: apps/playback.c
===================================================================
RCS file: /cvsroot/rockbox/apps/playback.c,v
retrieving revision 1.406
diff -u -r1.406 playback.c
--- apps/playback.c	17 Dec 2006 14:47:12 -0000	1.406
+++ apps/playback.c	18 Dec 2006 00:58:10 -0000
@@ -57,6 +57,7 @@
 #include "buffer.h"
 #include "dsp.h"
 #include "abrepeat.h"
+#include "cuesheet.h"
 #ifdef HAVE_TAGCACHE
 #include "tagcache.h"
 #endif
@@ -2688,6 +2689,9 @@
         return false;
     }
 
+    /* if the file is a cuesheet, we need to replace trackname and current_fd */
+    cue_spoof_track(trackname, &current_fd);
+
     /* Initialize track entry. */
     size = filesize(current_fd);
     tracks[track_widx].filerem = size;
@@ -2829,6 +2833,9 @@
     if (!trackname)
         return false;
 
+    /* if the file is a cuesheet, we need to replace trackname and current_fd */
+    cue_spoof_track(trackname, &current_fd);
+
     fd = open(trackname, O_RDONLY);
     if (fd < 0)
         return false;
@@ -3144,6 +3151,10 @@
 
     /* (Re-)open current track's file handle. */
     trackname = playlist_peek(0);
+
+    /* if the file is a cuesheet, we need to replace trackname and current_fd */
+    cue_spoof_track(trackname, &current_fd);
+
     fd = open(trackname, O_RDONLY);
     if (fd < 0) 
     {
Index: apps/tree.c
===================================================================
RCS file: /cvsroot/rockbox/apps/tree.c,v
retrieving revision 1.458
diff -u -r1.458 tree.c
--- apps/tree.c	12 Dec 2006 10:45:34 -0000	1.458
+++ apps/tree.c	18 Dec 2006 00:58:11 -0000
@@ -89,6 +89,8 @@
     { "mp3", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
     { "mp2", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
     { "mpa", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
+    { "cue", TREE_ATTR_MPA, Icon_Bookmark, LANG_CUESHEET },
+                        /* TODO: find out why it get Icon_Audio */
 #if CONFIG_CODEC == SWCODEC
     /* Temporary hack to allow playlist creation */
     { "mp1", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
Index: apps/lang/english.lang
===================================================================
RCS file: /cvsroot/rockbox/apps/lang/english.lang,v
retrieving revision 1.304
diff -u -r1.304 english.lang
--- apps/lang/english.lang	10 Dec 2006 10:47:11 -0000	1.304
+++ apps/lang/english.lang	18 Dec 2006 00:58:12 -0000
@@ -10372,3 +10372,31 @@
     *: "Properties"
   </voice>
 </phrase>
+<phrase>
+  id: LANG_CUESHEET
+  desc: 
+  user:
+  <source>
+    *: "Cuesheet"
+  </source>
+  <dest>
+    *: "Cuesheet"
+  </dest>
+  <voice>
+    *: "Cuesheet"
+  </voice>
+</phrase>
+<phrase>
+  id: LANG_VIEW_CUESHEET
+  desc: cuesheet viewer
+  user:
+  <source>
+    *: "View cuesheet"
+  </source>
+  <dest>
+    *: "View cuesheet"
+  </dest>
+  <voice>
+    *: "View cuesheet"
+  </voice>
+</phrase>
