Index: apps/cuesheet.c
===================================================================
--- apps/cuesheet.c	(révision 0)
+++ apps/cuesheet.c	(révision 0)
@@ -0,0 +1,428 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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 "plugin.h"
+#include "playback.h"
+#include "cuesheet.h"
+
+
+void cuesheet_init(void)
+{
+    if (global_settings.cuesheet) {
+        curr_cue = (struct cuesheet *)buffer_alloc(MAX_TRACKS * sizeof(struct cuesheet));
+        temp_cue = (struct cuesheet *)buffer_alloc(MAX_TRACKS * sizeof(struct cuesheet));
+    } else {
+        curr_cue = NULL;
+        temp_cue = NULL;
+    }
+}
+
+bool cuesheet_is_enabled(void) {
+    return (curr_cue != NULL);
+}
+
+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 (!strncmp(s, "FILE", 4))
+        {
+            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;
+    }
+
+    memset(cue, 0, sizeof(struct cuesheet));
+
+    cue->curr_track_idx = 0;
+    cue->curr_track = cue->tracks;
+
+    cue->track_count = 0;
+    while (read_line(fd,line,MAX_PATH))
+    {
+        s = skip_whitespace(line);
+        if (!strncmp(s, "TITLE", 5))
+        {
+            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-1].title,
+                         start,MAX_NAME);
+        }
+        else if (!strncmp(s, "PERFORMER", 9))
+        {
+            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-1].performer,
+                         start,MAX_NAME);
+        }
+        else if (!strncmp(s, "FILE", 4))
+        {
+            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 (!strncmp(s, "TRACK", 5))
+        {
+            if (cue->track_count >= MAX_TRACKS)
+                break; /* out of memeory! stop parsing */
+            cue->track_count++;
+        }
+        else if (!strncmp(s, "INDEX", 5))
+        {
+            s = strchr(s,' ');
+            s = skip_whitespace(s);
+            s = strchr(s,' ');
+            s = skip_whitespace(s);
+            cue->tracks[cue->track_count-1].offset = 60*1000 * atoi(s);
+            s = strchr(s,':') + 1;
+            cue->tracks[cue->track_count-1].offset += 1000 * atoi(s);
+            s = strchr(s,':') + 1;
+            cue->tracks[cue->track_count-1].offset += 13 * atoi(s);
+            //DEBUGF("%d %ld\n",cue->track_count-1,cue->tracks[cue->track_count-1].offset);
+        }
+    }
+    close(fd);
+
+    int i;
+    for (i=0; i<cue->track_count; i++)
+    {
+        if (*(cue->tracks[i].performer) == '\0')
+        {
+            strncpy(cue->tracks[i].performer, cue->performer, MAX_NAME);
+        }
+    }
+
+    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 (i < cue->track_count-1 && cue->tracks[i+1].offset < curpos)
+    {
+        i++;
+    }
+    DEBUGF("find_current_track : %d\n",i);
+    cue->curr_track_idx = i;
+    cue->curr_track = cue->tracks + 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));
+    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)
+{
+    int bufsize = 0;
+    struct cuesheet *cue = (struct cuesheet *)plugin_get_buffer(&bufsize);
+    if (!cue)
+        return false;
+
+    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.
+ * direction should be 1 or -1.
+ */
+bool curr_cuesheet_skip(int direction, unsigned long curr_pos)
+{
+    int track = cue_find_current_track(curr_cue, curr_pos);
+
+    if (direction >= 0 && track == curr_cue->track_count - 1)
+    {
+        /* we want to get out of the cuesheet */
+        DEBUGF("Getting out of the cuesheet\n");
+        return false;
+    }
+    else
+    {
+        //if (!(direction <= 0 && track == 0))
+            track += direction;
+
+        if (track == 0) {track = -1;}
+        /* no idea why but skipping to track 0 goes to the previous file
+           in the playlist instead of goind back to the beginning
+           of the current file, whereas skipping to track -1
+           seems to skip back to the beginning of the file... */
+
+        DEBUGF("skip to track idx %d\n", track);
+
+        seek(curr_cue->tracks[track].offset);
+        return true;
+    }
+
+}
+
+void cue_spoof_id3(struct cuesheet *cue, struct mp3entry *id3)
+{
+    if (!cue)
+        return;
+
+    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);
+}
+
+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
===================================================================
--- apps/cuesheet.h	(révision 0)
+++ apps/cuesheet.h	(révision 0)
@@ -0,0 +1,84 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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    /* Max length of information strings */
+#define MAX_TRACKS 99  /* Max number of tracks in a cuesheet */
+
+
+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[MAX_TRACKS];
+
+    int curr_track_idx;
+    struct cue_track_info *curr_track;
+};
+
+struct cuesheet *curr_cue; /* the currently loaded cuesheet */
+struct cuesheet *temp_cue;
+
+bool cuesheet_is_enabled(void);
+
+/* 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/tree.c
===================================================================
--- apps/tree.c	(révision 12095)
+++ apps/tree.c	(copie de travail)
@@ -89,6 +89,7 @@
     { "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 },
 #if CONFIG_CODEC == SWCODEC
     /* Temporary hack to allow playlist creation */
     { "mp1", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
@@ -129,6 +130,7 @@
     { "kbd", TREE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD },
 #endif
     { "bmark",TREE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK },
+        //{ "cue", TREE_ATTR_CUE, Icon_Bookmark, LANG_CUESHEET },
 #ifdef BOOTFILE_EXT
     { BOOTFILE_EXT, TREE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
 #endif /* #ifndef SIMULATOR */
Index: apps/lang/english.lang
===================================================================
--- apps/lang/english.lang	(révision 12095)
+++ apps/lang/english.lang	(copie de travail)
@@ -10459,3 +10459,45 @@
     *: "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>
+<phrase>
+id: LANG_CUESHEET_ENABLE
+desc: cuesheet support option
+user:
+<source>
+*: "Cuesheet Support"
+</source>
+<dest>
+*: "Cuesheet Support"
+</dest>
+<voice>
+*: "Cuesheet Support"
+</voice>
+</phrase>
Index: apps/onplay.c
===================================================================
--- apps/onplay.c	(révision 12095)
+++ apps/onplay.c	(copie de travail)
@@ -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 @@
 }
 #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 @@
                     }
                 }
 #endif
+                if (is_cuesheet(selected_file))
+                {
+                    items[i].desc = ID2P(LANG_VIEW_CUESHEET);
+                    items[i].function = view_cuesheet;
+                    i++;
+                }
             }
             else
             {
Index: apps/gui/gwps-common.c
===================================================================
--- apps/gui/gwps-common.c	(révision 12095)
+++ apps/gui/gwps-common.c	(copie de travail)
@@ -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 @@
                             data->progress_start, data->progress_end, sb_y,
                             data->progress_height);
 #endif
+
+                if (cuesheet_is_enabled() && 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) {
@@ -2555,6 +2564,29 @@
     {
         gwps->display->stop_scroll();
         gwps->state->id3 = audio_current_track();
+
+        if (cuesheet_is_enabled() && gwps->state->id3->has_cuesheet
+            && strcmp(gwps->state->id3->path, curr_cue->audio_filename))
+        {
+            /* the current cuesheet isn't the right one any more */
+
+            if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) {
+                /* We have the new cuesheet in memory (temp_cue),
+                   let's make it the current one ! */
+                memcpy(curr_cue, temp_cue, sizeof(struct cuesheet));
+                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 (gui_wps_display())
             retcode = true;
         else{
@@ -2566,12 +2598,39 @@
                    sizeof(gwps->state->current_track_path));
     }
 
+    if (cuesheet_is_enabled() && gwps->state->id3->has_cuesheet
+        && (gwps->state->id3->elapsed < curr_cue->curr_track->offset
+            || (curr_cue->curr_track_idx < curr_cue->track_count - 1
+                && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset)))
+    {
+        /* We've changed tracks within the cuesheet :
+           we need to update the ID3 info and refresh the WPS */
+
+        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);
+
+        gwps->display->stop_scroll();
+
+        if (gui_wps_display())
+            retcode = true;
+        else{
+            gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
+        }
+
+        gui_wps_statusbar_draw(gwps, false);
+
+        return retcode;
+    }
+
     if (gwps->state->id3)
         gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
 
     gui_wps_statusbar_draw(gwps, false);
-    
-    return retcode;           
+
+    return retcode;
 }
 
 
Index: apps/gui/gwps.c
===================================================================
--- apps/gui/gwps.c	(révision 12095)
+++ apps/gui/gwps.c	(copie de travail)
@@ -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 @@
                 if (global_settings.party_mode)
                     break;
                 if (current_tick -last_right < HZ)
+                {
+                    if (cuesheet_is_enabled() && 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 @@
                 if (global_settings.party_mode)
                     break;
                 if (current_tick -last_left < HZ)
+                {
+                    /* take care of if we're playing a cuesheet */
+                    if (cuesheet_is_enabled() && 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 @@
                     audio_prev();
                 }
                 else {
+
+                    /* take care of if we're playing a cuesheet */
+                    if (cuesheet_is_enabled() && 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 @@
                 }
                 /* ...otherwise, do it normally */
 #endif
+
+                /* take care of if we're playing a cuesheet */
+                if (cuesheet_is_enabled() && 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/tree.h
===================================================================
--- apps/tree.h	(révision 12095)
+++ apps/tree.h	(copie de travail)
@@ -92,6 +92,7 @@
 #define TREE_ATTR_BMP   0x1100 /* backdrop bmp file */
 #define TREE_ATTR_KBD   0x1200 /* keyboard file */
 #define TREE_ATTR_FMR   0x1300 /* preset file */
+//#define TREE_ATTR_CUE   0x1400 /* cuesheet file */
 #define TREE_ATTR_MASK  0xFF00 /* which bits tree.c uses for file types */
 
 void tree_get_filetypes(const struct filetype**, int*);
Index: apps/settings.h
===================================================================
--- apps/settings.h	(révision 12095)
+++ apps/settings.h	(copie de travail)
@@ -538,6 +538,7 @@
 #endif
     /* Encoder Settings End */
 #endif /* CONFIG_CODEC == SWCODEC */
+    bool cuesheet;
 };
 
 enum optiontype { INT, BOOL };
Index: apps/settings_menu.c
===================================================================
--- apps/settings_menu.c	(révision 12095)
+++ apps/settings_menu.c	(copie de travail)
@@ -48,6 +48,7 @@
 #include "power.h"
 #include "dir.h"
 #include "dircache.h"
+#include "cuesheet.h"
 #ifdef HAVE_TAGCACHE
 #include "tagcache.h"
 #include "tagtree.h"
@@ -1635,6 +1636,20 @@
 }
 #endif /* HAVE_DIRCACHE */
 
+static bool cuesheet(void)
+{
+    bool result = set_bool_options(str(LANG_CUESHEET_ENABLE),
+                                   &global_settings.cuesheet,
+                                   STR(LANG_ON),
+                                   STR(LANG_OFF),
+                                   NULL);
+
+    if (!cuesheet_is_enabled() && global_settings.cuesheet)
+        gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT));
+
+    return result;
+}
+
 #ifdef HAVE_TAGCACHE
 #ifdef HAVE_TC_RAMCACHE
 static bool tagcache_ram(void)
@@ -1772,7 +1787,8 @@
 #ifdef HAVE_HEADPHONE_DETECTION
         { ID2P(LANG_UNPLUG), unplug_menu },
 #endif
-        { ID2P(LANG_AUDIOSCROBBLER), audioscrobbler}
+        { ID2P(LANG_AUDIOSCROBBLER), audioscrobbler},
+        { ID2P(LANG_CUESHEET_ENABLE), cuesheet}
     };
 
     bool old_shuffle = global_settings.playlist_shuffle;
Index: apps/playback.c
===================================================================
--- apps/playback.c	(révision 12095)
+++ apps/playback.c	(copie de travail)
@@ -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 @@
         return false;
     }
 
+    if (cuesheet_is_enabled() && 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 @@
 
     }
 
+    if (cuesheet_is_enabled() && 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 @@
     if (!trackname)
         return false;
 
+    if (cuesheet_is_enabled() && 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 @@
 
     /* (Re-)open current track's file handle. */
     trackname = playlist_peek(0);
+
+    if (cuesheet_is_enabled() && 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/SOURCES
===================================================================
--- apps/SOURCES	(révision 12095)
+++ apps/SOURCES	(copie de travail)
@@ -24,6 +24,7 @@
 settings_menu.c
 sound_menu.c
 status.c
+cuesheet.c
 #if !defined(SIMULATOR) || CONFIG_CODEC == SWCODEC
 talk.c
 #endif
Index: apps/main.c
===================================================================
--- apps/main.c	(révision 12095)
+++ apps/main.c	(copie de travail)
@@ -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;
@@ -274,7 +276,8 @@
               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
     talk_init();
@@ -489,6 +492,7 @@
     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: firmware/export/id3.h
===================================================================
--- firmware/export/id3.h	(révision 12095)
+++ firmware/export/id3.h	(copie de travail)
@@ -205,6 +205,10 @@
     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 {
