Index: apps/SOURCES
===================================================================
RCS file: /cvsroot/rockbox/apps/SOURCES,v
retrieving revision 1.58
diff -u -b -B -p -r1.58 SOURCES
--- apps/SOURCES	19 Nov 2006 14:11:40 -0000	1.58
+++ apps/SOURCES	3 Jan 2007 21:02:28 -0000
@@ -23,6 +23,7 @@ settings.c
 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	3 Jan 2007 21:02:28 -0000
@@ -0,0 +1,416 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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 "settings.h"
+#include "buffer.h"
+#include "playback.h"
+
+#include "cuesheet.h"
+
+void cuesheet_init(void)
+{
+    if (!global_settings.max_cuesheet_tracks)
+        return;
+
+    curr_cue.max_tracks = global_settings.max_cuesheet_tracks;
+    temp_cue.max_tracks = global_settings.max_cuesheet_tracks;
+
+    curr_cue.tracks = (struct cue_track_info *)
+        buffer_alloc(curr_cue.max_tracks * sizeof(struct cue_track_info));
+    temp_cue.tracks = (struct cue_track_info *)
+        buffer_alloc(temp_cue.max_tracks * sizeof(struct cue_track_info));
+}
+
+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;
+
+    DEBUGF("get_trackname_from_cuesheet\n");
+
+    char line[MAX_PATH], audio_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(audio_filename,start,MAX_PATH);
+
+            strncpy(buf,filename,MAX_PATH);
+            slash = strrchr(buf,'/');
+            strncpy(slash+1,audio_filename,MAX_NAME);
+        }
+    }
+
+    close(fd);
+    return true;
+}
+
+/* parse cuesheet "file" and store the information in "cue" */
+bool parse_cuesheet(char *file, struct cuesheet *cue)
+{
+    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->curr_track_idx = 0;
+
+    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->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->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->audio_filename,file,MAX_PATH);
+            slash = strrchr(cue->audio_filename,'/');
+            strncpy(slash+1,start,MAX_NAME);
+        }
+        else if (MATCH(s, "TRACK"))
+        {
+            if (cue->track_count >= cue->max_tracks)
+                break; /* out of memeory! stop parsing */
+            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; */
+
+    DEBUGF("Parsed cuesheet %s\n", file);
+    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.
+ * allows for parsing the cuesheet at the same time, storing its information
+ * in the gloabl cuesheet struct.
+ */
+bool cue_spoof_track(char *trackname, int *current_fd, struct cuesheet *cue)
+{
+    if (cue) {
+        parse_cuesheet(trackname, cue);
+        strncpy(trackname,cue->audio_filename,MAX_PATH);
+    }
+    else {
+        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 %s\n", trackname);
+    return true;
+}
+
+/* takes care of seeking to a track in a playlist
+ * returns false if audio  isn't playing */
+bool seek(unsigned long pos)
+{
+    if (!(audio_status() & AUDIO_STATUS_PLAY))
+    {
+        return false;
+    }
+    else
+    {
+#if (CONFIG_CODEC == SWCODEC)
+        audio_pre_ff_rewind();
+#else
+        audio_pause();
+#endif
+        audio_ff_rewind(pos);
+        return true;
+    }
+}
+
+/* returns the index of the track currently being played. */
+int cue_find_current_track(struct cuesheet *cue, unsigned long curpos)
+{
+    int i=0;
+    while (cue->tracks[i+1].offset < curpos && i < cue->track_count)
+    {
+        i++;
+    }
+    DEBUGF("find_current_track : %d\n",i);
+    cue->curr_track_idx = i;
+    return i;
+}
+
+char *list_get_name_cb(int selected_item,
+                             void *data,
+                             char *buffer)
+{
+    struct cuesheet *cue = (struct cuesheet *)data;
+
+    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(struct cuesheet *cue)
+{
+    struct gui_synclist lists;
+    int action;
+    bool done = false;
+    int sel;
+    char title[MAX_PATH];
+    struct mp3entry *id3 = audio_current_track();
+
+    snprintf(title,MAX_PATH,"%s: %s",cue->performer, cue->title);
+    gui_synclist_init(&lists,list_get_name_cb,cue,false,2);
+    gui_synclist_set_nb_items(&lists,2*(cue->track_count+1));
+    gui_synclist_set_title(&lists,title,0);
+
+    if (id3->has_cuesheet && !strcmp(id3->path,cue->audio_filename))
+    {
+        gui_synclist_select_item(&lists,
+                                 2*cue_find_current_track(cue,id3->elapsed));
+    }
+    else
+    {
+        gui_syncsplash(HZ/2, true, "Read-only mode");
+    }
+
+    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();
+                if (id3->has_cuesheet && !strcmp(id3->path,cue->audio_filename))
+                {
+                    sel = gui_synclist_get_sel_pos(&lists);
+                    /*gui_synclist_select_item(&lists,2*find_current_track(cue,id3));*/
+                    seek(cue->tracks[sel/2].offset);
+                }
+                break;
+            case ACTION_STD_CANCEL:
+                done = true;
+        }
+    }
+}
+
+bool display_cuesheet_content(char* filename)
+{
+    struct cuesheet cue;
+    struct cue_track_info tracks[MAX_TRACKS];
+    cue.tracks = tracks;
+    cue.max_tracks = MAX_TRACKS;
+
+    if (!parse_cuesheet(filename, &cue))
+        return false;
+
+    browse(&cue);
+    return true;
+}
+
+/* skips backwards or forward in the current cuesheet
+ * the return value indicates whether we're still in a cusheet after skipping
+ * it also returns false if we weren't in a cuesheet.
+ */
+bool curr_cuesheet_skip(int direction, unsigned long curr_pos)
+{
+    /* struct mp3entry *id3 = audio_current_track();
+    if (strcmp(id3->path,curr_cue.audio_filename))
+        return false;
+    int track = cue_find_current_track(&curr_cue,id3->elapsed); */
+
+    int track = cue_find_current_track(&curr_cue, curr_pos);
+    if (direction >= 0)
+    {
+        if (track == curr_cue.track_count)
+        {
+            /* we want to get out of the cuesheet */
+            DEBUGF("Getting out of the cuesheet\n");
+            /* strcpy(curr_cue.audio_filename,"\0"); */
+            return false;
+        }
+        track++;
+    }
+    else
+    {
+        track--;
+    }
+
+    DEBUGF("skip to track index %d out of %d\n",
+           track, curr_cue.track_count);
+
+    seek(curr_cue.tracks[track].offset);
+    return true;
+}
+
+void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3)
+{
+    int i = cue->curr_track_idx;
+
+    id3->title = cue->tracks[i].title;
+    id3->artist = cue->tracks[i].performer;
+    id3->tracknum = i+1;
+    id3->album = cue->title;
+    id3->composer = cue->performer;
+    if (id3->track_string)
+        snprintf(id3->track_string,10,"%d/%d",i+1,cue->track_count+1);
+}
+
+static inline void draw_veritcal_line_mark(struct screen * screen,
+                                           int x, int y, int h)
+{
+    screen->set_drawmode(DRMODE_COMPLEMENT);
+    screen->vline(x, y, y+h-1);
+}
+
+/* draw the cuesheet markers for a track of length "tracklen",
+   between (x1,y) and (x2,y) */
+void cue_draw_markers(struct screen *screen, unsigned long tracklen,
+                      int x1, int x2, int y, int h)
+{
+    int i,xi;
+    int w = x2 - x1;
+    for (i=1; i<=curr_cue.track_count; i++)
+    {
+        xi = x1 + (w * curr_cue.tracks[i].offset)/tracklen;
+        draw_veritcal_line_mark(screen, xi, y, h);
+    }
+}
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	3 Jan 2007 21:02:28 -0000
@@ -0,0 +1,86 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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
+
+#define MATCH(l,s) !strncmp(l,s,strlen(s))
+
+struct cue_track_info {
+    char title[MAX_NAME];
+    char performer[MAX_NAME];
+    unsigned long offset; /* ms from start of track */
+};
+
+struct cuesheet {
+    char audio_filename[MAX_PATH];
+
+    char title[MAX_NAME];
+    char performer[MAX_NAME];
+
+    int track_count;
+
+    struct cue_track_info *tracks;
+    int max_tracks;
+
+    int curr_track_idx;
+};
+
+struct cuesheet curr_cue; /* the currently loaded cuesheet */
+struct cuesheet temp_cue;
+
+void cue_track_change_event(struct mp3entry *id);
+
+/* alllocates the cuesheet buffer */
+void cuesheet_init(void);
+
+bool parse_cuesheet(char *file, struct cuesheet *cue);
+
+/* tell wether a file is a cuesheet by looking at its extension.
+ * true if is "filename" a cuesheet. */
+bool is_cuesheet(char *filename);
+
+/* reads a cuesheet to find the audio track associated to it */
+bool get_trackname_from_cuesheet(char *filename, char *buf);
+
+/* if "trackname" is a cuesheet, open the associated audio
+ * file instead. This is used in the playback code.
+ * Also loads the cuesheet in curr_cue if "parse" is true. */
+bool cue_spoof_track(char *trackname, int *current_fd, struct cuesheet *cue);
+
+/* displays a cuesheet to the screen without loading it to curr_cue */
+bool display_cuesheet_content(char* filename);
+
+/* finds the current track played within a cuesheet */
+int cue_find_current_track(struct cuesheet *cue, unsigned long curpos);
+
+/* void update_curr_track(struct cuesheet *cue, unsigned long curpos); */
+void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3);
+
+/* skip to next track in the cuesheet towards "direction" */
+bool curr_cuesheet_skip(int direction, unsigned long curr_pos);
+
+void cue_draw_markers(struct screen *screen, unsigned long tracklen,
+                      int x1, int x2, int y, int h);
+
+#endif
Index: apps/main.c
===================================================================
RCS file: /cvsroot/rockbox/apps/main.c,v
retrieving revision 1.204
diff -u -b -B -p -r1.204 main.c
--- apps/main.c	31 Dec 2006 02:02:03 -0000	1.204
+++ apps/main.c	3 Jan 2007 21:02:28 -0000
@@ -103,6 +103,8 @@
 #include "m5636.h"
 #endif
 
+#include "cuesheet.h"
+
 /*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */
 
 const char appsversion[]=APPSVERSION;
@@ -275,6 +277,7 @@ static void init(void)
               global_settings.superbass);
 
     scrobbler_init();
+    cuesheet_init();
     
     /* audio_init must to know the size of voice buffer so init voice first */
 #if CONFIG_CODEC == SWCODEC
@@ -487,6 +490,7 @@ static void init(void)
     playlist_init();
     tree_init();
     scrobbler_init();
+    cuesheet_init();
 
     /* No buffer allocation (see buffer.c) may take place after the call to
        audio_init() since the mpeg thread takes the rest of the buffer space */
Index: apps/onplay.c
===================================================================
RCS file: /cvsroot/rockbox/apps/onplay.c,v
retrieving revision 1.97
diff -u -b -B -p -r1.97 onplay.c
--- apps/onplay.c	27 Dec 2006 02:37:12 -0000	1.97
+++ apps/onplay.c	3 Jan 2007 21:02:29 -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
@@ -533,6 +534,11 @@ static bool set_backdrop(void)
 }
 #endif
 
+static bool view_cuesheet(void)
+{
+    return display_cuesheet_content(selected_file);
+}
+
 static bool rename_file(void)
 {
     char newname[MAX_PATH];
@@ -964,6 +970,12 @@ int onplay(char* file, int attr, int fro
                     }
                 }
 #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.409
diff -u -b -B -p -r1.409 playback.c
--- apps/playback.c	25 Dec 2006 14:01:33 -0000	1.409
+++ apps/playback.c	3 Jan 2007 21:02:30 -0000
@@ -57,6 +57,7 @@
 #include "buffer.h"
 #include "dsp.h"
 #include "abrepeat.h"
+#include "cuesheet.h"
 #ifdef HAVE_TAGCACHE
 #include "tagcache.h"
 #endif
@@ -2703,6 +2704,14 @@ static bool audio_load_track(int offset,
         return false;
     }
 
+    if (is_cuesheet(trackname)) {
+        DEBUGF("audio_load_track\n");
+        strcpy(tracks[track_widx].id3.cuesheet_path, trackname);
+        tracks[track_widx].id3.has_cuesheet = true;
+        cue_spoof_track(trackname, &current_fd,
+                        start_play ? &curr_cue : &temp_cue);
+    }
+
     /* Initialize track entry. */
     size = filesize(current_fd);
     tracks[track_widx].filerem = size;
@@ -2751,6 +2760,9 @@ static bool audio_load_track(int offset,
 
     }
 
+    if (tracks[track_widx].id3.has_cuesheet)
+        cue_spoof_id3(&curr_cue, &tracks[track_widx].id3);
+
     /* Load the codec. */
     tracks[track_widx].codecbuf = &filebuf[buf_widx];
     if (!audio_loadcodec(start_play)) 
@@ -2844,6 +2856,13 @@ static bool audio_read_next_metadata(voi
     if (!trackname)
         return false;
 
+    if (is_cuesheet(trackname)) {
+        DEBUGF("audio_read_next_metatdata\n");
+        strcpy(tracks[next_idx].id3.cuesheet_path, trackname);
+        tracks[next_idx].id3.has_cuesheet = true;
+        cue_spoof_track(trackname,& current_fd, NULL);
+    }
+
     fd = open(trackname, O_RDONLY);
     if (fd < 0)
         return false;
@@ -3159,6 +3178,12 @@ static int audio_rebuffer_and_seek(size_
 
     /* (Re-)open current track's file handle. */
     trackname = playlist_peek(0);
+
+    if (is_cuesheet(trackname)) {
+        DEBUGF("audio_rebuffer_and_seek\n");
+        cue_spoof_track(trackname, &current_fd, NULL);
+    }
+
     fd = open(trackname, O_RDONLY);
     if (fd < 0) 
     {
Index: apps/settings.c
===================================================================
RCS file: /cvsroot/rockbox/apps/settings.c,v
retrieving revision 1.443
diff -u -b -B -p -r1.443 settings.c
--- apps/settings.c	31 Dec 2006 02:44:53 -0000	1.443
+++ apps/settings.c	3 Jan 2007 21:02:30 -0000
@@ -705,7 +705,7 @@ static const struct bit_entry hd_bits[] 
 #ifdef HAVE_RECORDING
     {2, S_O(rec_trigger_type), 0, "trigger type", "stop,pause,nf stp"},
 #endif
-
+    {7, S_O(max_cuesheet_tracks), 20, "max_cuesheet_tracks", NULL}, /* 0...127 (99 is .cue max) */
     /* Sum of all bit sizes must not grow beyond 0xB8*8 = 1472 */
 };
 
Index: apps/settings.h
===================================================================
RCS file: /cvsroot/rockbox/apps/settings.h,v
retrieving revision 1.256
diff -u -b -B -p -r1.256 settings.h
--- apps/settings.h	19 Dec 2006 01:26:37 -0000	1.256
+++ apps/settings.h	3 Jan 2007 21:02:31 -0000
@@ -536,6 +536,7 @@ struct user_settings
 #endif
     /* Encoder Settings End */
 #endif /* CONFIG_CODEC == SWCODEC */
+    int max_cuesheet_tracks;
 };
 
 enum optiontype { INT, BOOL };
Index: apps/tree.c
===================================================================
RCS file: /cvsroot/rockbox/apps/tree.c,v
retrieving revision 1.461
diff -u -b -B -p -r1.461 tree.c
--- apps/tree.c	25 Dec 2006 14:01:39 -0000	1.461
+++ apps/tree.c	3 Jan 2007 21:02:31 -0000
@@ -89,6 +89,8 @@ const struct filetype filetypes[] = {
     { "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/gui/gwps-common.c
===================================================================
RCS file: /cvsroot/rockbox/apps/gui/gwps-common.c,v
retrieving revision 1.69
diff -u -b -B -p -r1.69 gwps-common.c
--- apps/gui/gwps-common.c	29 Dec 2006 19:17:03 -0000	1.69
+++ apps/gui/gwps-common.c	3 Jan 2007 21:02:32 -0000
@@ -53,6 +53,7 @@
 #endif
 #include "dsp.h"
 #include "action.h"
+#include "cuesheet.h"
 
 #ifdef HAVE_LCD_CHARCELLS
 static bool draw_player_progress(struct gui_wps *gwps);
@@ -1844,6 +1845,14 @@ bool gui_wps_refresh(struct gui_wps *gwp
                             data->progress_start, data->progress_end, sb_y,
                             data->progress_height);
 #endif
+
+                if (state->id3->has_cuesheet)
+                {
+                    cue_draw_markers(display, state->id3->length,
+                                     data->progress_start, data->progress_end,
+                                     sb_y+1, data->progress_height-2);
+                }
+
                 update_line = true;
             }
             if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
@@ -2550,6 +2559,21 @@ bool update(struct gui_wps *gwps)
     bool track_changed = audio_has_changed_track();
     bool retcode = false;
 
+    if (gwps->state->id3->has_cuesheet) {
+        /* here we check wether we've changed tracks within the cuesheet */
+        int i = curr_cue.curr_track_idx;
+        if ((i < curr_cue.track_count
+             && gwps->state->id3->elapsed >= curr_cue.tracks[i+1].offset)
+            || gwps->state->id3->elapsed < curr_cue.tracks[i].offset)
+        {
+            track_changed = true;
+            i = cue_find_current_track(&curr_cue, gwps->state->id3->elapsed);
+            cue_spoof_id3(&curr_cue, gwps->state->id3);
+            DEBUGF("A - Now in track %d : %s (n° %d)\n", i,
+                   gwps->state->id3->title, gwps->state->id3->tracknum);
+        }
+    }
+
     gwps->state->nid3 = audio_next_track();
     if (track_changed)
     {
@@ -2564,6 +2588,31 @@ bool update(struct gui_wps *gwps)
         if (gwps->state->id3)
             memcpy(gwps->state->current_track_path, gwps->state->id3->path,
                    sizeof(gwps->state->current_track_path));
+
+        if (gwps->state->id3->has_cuesheet) {
+            /* here we update the current cuesheet if needed */
+            if (strcmp(gwps->state->id3->path, curr_cue.audio_filename)) {
+                if (!strcmp(gwps->state->id3->path, temp_cue.audio_filename)) {
+                    /* We have the new cuesheet in memory, make it the current one */
+                    memcpy(curr_cue.tracks, temp_cue.tracks,
+                        curr_cue.max_tracks * sizeof(struct cue_track_info));
+                    strcpy(curr_cue.title, temp_cue.title);
+                    strcpy(curr_cue.performer, temp_cue.performer);
+                    strcpy(curr_cue.audio_filename, temp_cue.audio_filename);
+                    curr_cue.track_count = temp_cue.track_count;
+                    DEBUGF("Copied temp_cue to curr_cue\n");
+                }
+                else {
+                    /* We need to parse the new cuesheet */
+                    parse_cuesheet(gwps->state->id3->cuesheet_path, &curr_cue);
+                }
+
+                int i = cue_find_current_track(&curr_cue, gwps->state->id3->elapsed);
+                cue_spoof_id3(&curr_cue, gwps->state->id3);
+                DEBUGF("A - Now in track %d : %s (n° %d)\n", i,
+                       gwps->state->id3->title, gwps->state->id3->tracknum);
+            }
+        }
     }
 
     if (gwps->state->id3)
Index: apps/gui/gwps.c
===================================================================
RCS file: /cvsroot/rockbox/apps/gui/gwps.c,v
retrieving revision 1.57
diff -u -b -B -p -r1.57 gwps.c
--- apps/gui/gwps.c	22 Dec 2006 05:19:55 -0000	1.57
+++ apps/gui/gwps.c	3 Jan 2007 21:02:33 -0000
@@ -54,6 +54,7 @@
 #include "abrepeat.h"
 #include "playback.h"
 #include "splash.h"
+#include "cuesheet.h"
 #if LCD_DEPTH > 1
 #include "backdrop.h"
 #endif
@@ -333,7 +334,12 @@ long gui_wps_show(void)
                 if (global_settings.party_mode)
                     break;
                 if (current_tick -last_right < HZ)
+                {
+                    if (wps_state.id3->has_cuesheet)
+                        audio_next();
+                    else
                    audio_next_dir();
+                }
                 else ffwd_rew(ACTION_WPS_SEEKFWD);
                 last_right = 0;
                 break;
@@ -343,7 +349,21 @@ long gui_wps_show(void)
                 if (global_settings.party_mode)
                     break;
                 if (current_tick -last_left < HZ)
+                {
+                    /* take care of if we're playing a cuesheet */
+                    if (wps_state.id3->has_cuesheet)
+                    {
+                        if (!wps_state.paused)
+#if (CONFIG_CODEC == SWCODEC)
+                            audio_pre_ff_rewind();
+#else
+                            audio_pause();
+#endif
+                        audio_ff_rewind(0);
+                    }
+                    else
                     audio_prev_dir();
+                }
                 else ffwd_rew(ACTION_WPS_SEEKBACK);
                 last_left = 0;
                 break;
@@ -377,6 +397,14 @@ long gui_wps_show(void)
                     audio_prev();
                 }
                 else {
+
+                    /* take care of if we're playing a cuesheet */
+                    if (wps_state.id3->has_cuesheet)
+                    {
+                        curr_cuesheet_skip(-1, wps_state.id3->elapsed);
+                        break;
+                    }
+
                     if (!wps_state.paused)
 #if (CONFIG_CODEC == SWCODEC)
                         audio_pre_ff_rewind();
@@ -417,6 +445,18 @@ long gui_wps_show(void)
                 }
                 /* ...otherwise, do it normally */
 #endif
+
+                /* take care of if we're playing a cuesheet */
+                if (wps_state.id3->has_cuesheet)
+                {
+                    if (curr_cuesheet_skip(1, wps_state.id3->elapsed))
+                    {
+                        /* if the result was false, then we really want
+                           to skip to the next track */
+                        break;
+                    }
+                }
+
                 audio_next();
                 break;
                 /* next / prev directories */
Index: apps/lang/english.lang
===================================================================
RCS file: /cvsroot/rockbox/apps/lang/english.lang,v
retrieving revision 1.309
diff -u -b -B -p -r1.309 english.lang
--- apps/lang/english.lang	30 Dec 2006 19:59:17 -0000	1.309
+++ apps/lang/english.lang	3 Jan 2007 21:02:34 -0000
@@ -10456,3 +10456,31 @@
     *: "Play Next"
   </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>
Index: firmware/export/id3.h
===================================================================
RCS file: /cvsroot/rockbox/firmware/export/id3.h,v
retrieving revision 1.36
diff -u -b -B -p -r1.36 id3.h
--- firmware/export/id3.h	24 Nov 2006 20:10:32 -0000	1.36
+++ firmware/export/id3.h	3 Jan 2007 21:02:35 -0000
@@ -205,6 +205,10 @@ struct mp3entry {
     long track_peak;    /* 7.24 signed fixed point. 0 for no peak. */
     long album_peak;
 #endif
+
+    /* Cuesheet support */
+    bool has_cuesheet; /* if true, the track has been added via a cuesheet */
+    char cuesheet_path[MAX_PATH];
 };
 
 enum {
