Index: apps/cuesheet.c
===================================================================
--- apps/cuesheet.c	(révision 0)
+++ apps/cuesheet.c	(révision 0)
@@ -0,0 +1,367 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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;
+}
+
+bool look_for_cuesheet_file(const char *trackpath) {
+    char cuepath[MAX_PATH];
+    strncpy(cuepath, trackpath, MAX_PATH);
+    char *dot = strrchr(cuepath, '.');
+    strcpy(dot, ".cue\0");
+
+    int fd = open(cuepath,O_RDONLY);
+    if (fd<0)
+    {
+        return false;
+    }
+    else
+    {
+        close(fd);
+        return true;
+    }
+}
+
+char *skip_whitespace(char* buf)
+{
+    char *r = buf;
+    while (*r && (*r < 33))
+        r++;
+    return r;
+}
+
+/* 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;
+    int fd = open(file,O_RDONLY);
+    if (fd<0)
+    {
+        /* couln't open the file */
+        return false;
+    }
+
+    memset(cue, 0, sizeof(struct cuesheet));
+
+    strcpy(cue->path, file);
+
+    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, "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);
+        }
+    }
+    close(fd);
+
+    /* If some songs don't have performer info, we copy the cuesheet performer */
+    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);
+        }
+    }
+
+    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
+   and updates the information about the current track. */
+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++;
+    }
+    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];
+    char cuepath[MAX_PATH];
+    char *dot;
+    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 (strcmp(id3->path, "No file!"))
+    {
+        strncpy(cuepath, id3->path, MAX_PATH);
+        dot = strrchr(cuepath, '.');
+        strcpy(dot, ".cue\0");
+    }
+
+    if (id3->cuesheet_type && !strcmp(cue->path, cuepath))
+    {
+        gui_synclist_select_item(&lists,
+                                 2*cue_find_current_track(cue, id3->elapsed));
+    }
+
+    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 (strcmp(id3->path, "No file!"))
+                {
+                    strncpy(cuepath, id3->path, MAX_PATH);
+                    dot = strrchr(cuepath, '.');
+                    strcpy(dot, ".cue\0");
+                    if (id3->cuesheet_type && !strcmp(cue->path, cuepath))
+                    {
+                        sel = gui_synclist_get_sel_pos(&lists);
+                        //gui_synclist_select_item(&lists,2*cue_find_current_track(cue,id3->elapsed));
+                        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 */
+        return false;
+    }
+    else
+    {
+        if (!(direction <= 0 && track == 0))
+            track += direction;
+
+        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,89 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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>
+#include "screens.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 path[MAX_PATH];
+    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;
+struct cuesheet *temp_cue;
+
+bool cuesheet_is_enabled(void);
+
+/* alllocates the cuesheet buffer */
+void cuesheet_init(void);
+
+/* looks if there is a cuesheet file that has a name matching "trackpath" */
+bool look_for_cuesheet_file(const char *trackpath);
+
+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 12289)
+++ apps/tree.c	(copie de travail)
@@ -132,6 +132,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 12289)
+++ apps/lang/english.lang	(copie de travail)
@@ -10515,3 +10515,45 @@
     *: ""
   </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/gui/gwps-common.c
===================================================================
--- apps/gui/gwps-common.c	(révision 12289)
+++ 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);
@@ -1850,6 +1851,14 @@
                             data->progress_start, data->progress_end, sb_y,
                             data->progress_height);
 #endif
+
+                if (cuesheet_is_enabled() && state->id3->cuesheet_type)
+                {
+                    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) {
@@ -2561,6 +2570,34 @@
     {
         gwps->display->stop_scroll();
         gwps->state->id3 = audio_current_track();
+
+        if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
+            && 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));
+            }
+            else {
+                /* We need to parse the new cuesheet */
+                char cuepath[MAX_PATH];
+                strncpy(cuepath, gwps->state->id3->path, MAX_PATH);
+                char *dot = strrchr(cuepath, '.');
+                strcpy(dot, ".cue\0");
+
+                if (parse_cuesheet(cuepath, curr_cue))
+                {
+                    gwps->state->id3->cuesheet_type = 1;
+                    strcpy(curr_cue->audio_filename, gwps->state->id3->path);
+                }
+            }
+
+            cue_spoof_id3(curr_cue, gwps->state->id3);
+        }
+
         if (gui_wps_display())
             retcode = true;
         else{
@@ -2572,12 +2609,34 @@
                    sizeof(gwps->state->current_track_path));
     }
 
+    if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
+        && (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 */
+
+        cue_find_current_track(curr_cue, gwps->state->id3->elapsed);
+        cue_spoof_id3(curr_cue, gwps->state->id3);
+
+        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 12289)
+++ 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,16 @@
                 if (global_settings.party_mode)
                     break;
                 if (current_tick -last_right < HZ)
-                   audio_next_dir();
+                {
+                    if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
+                    {
+                        audio_next();
+                    }
+                    else
+                    {
+                        audio_next_dir();
+                    }
+                }
                 else ffwd_rew(ACTION_WPS_SEEKFWD);
                 last_right = 0;
                 break;
@@ -343,7 +353,23 @@
                 if (global_settings.party_mode)
                     break;
                 if (current_tick -last_left < HZ)
-                    audio_prev_dir();
+                {
+                    /* take care of if we're playing a cuesheet */
+                    if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
+                    {
+                        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 +403,14 @@
                     audio_prev();
                 }
                 else {
+
+                    /* take care of if we're playing a cuesheet */
+                    if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
+                    {
+                        curr_cuesheet_skip(-1, wps_state.id3->elapsed);
+                        break;
+                    }
+
                     if (!wps_state.paused)
 #if (CONFIG_CODEC == SWCODEC)
                         audio_pre_ff_rewind();
@@ -417,6 +451,18 @@
                 }
                 /* ...otherwise, do it normally */
 #endif
+
+                /* take care of if we're playing a cuesheet */
+                if (cuesheet_is_enabled() && wps_state.id3->cuesheet_type)
+                {
+                    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 12289)
+++ apps/tree.h	(copie de travail)
@@ -94,6 +94,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 12289)
+++ apps/settings.h	(copie de travail)
@@ -681,6 +681,7 @@
 #endif
     /* Encoder Settings End */
 #endif /* CONFIG_CODEC == SWCODEC */
+    bool cuesheet;
 };
 
 /** global variables **/
Index: apps/menus/playback_menu.c
===================================================================
--- apps/menus/playback_menu.c	(révision 12289)
+++ apps/menus/playback_menu.c	(copie de travail)
@@ -33,6 +33,7 @@
 #include "dsp.h"
 #include "scrobbler.h"
 #include "audio.h"
+#include "cuesheet.h"
 
 #if CONFIG_CODEC == SWCODEC
 int setcrossfadeonexit_callback(int action,const struct menu_item_ex *this_item)
@@ -146,6 +147,21 @@
 }
 MENUITEM_SETTING(audioscrobbler, &global_settings.audioscrobbler, audioscrobbler_callback);
 
+
+int cuesheet_callback(int action,const struct menu_item_ex *this_item)
+{
+    (void)this_item;
+    switch (action)
+    {
+        case ACTION_EXIT_MENUITEM: /* on exit */
+            if (!cuesheet_is_enabled() && global_settings.cuesheet)
+                gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT));
+            break;
+    }
+    return action;
+}
+MENUITEM_SETTING(cuesheet, &global_settings.cuesheet, cuesheet_callback);
+
 #ifdef HAVE_HEADPHONE_DETECTION
 MENUITEM_SETTING(unplug_mode, &global_settings.unplug_mode, NULL);
 MENUITEM_SETTING(unplug_rw, &global_settings.unplug_rw, NULL);
@@ -166,7 +182,7 @@
 #ifdef HAVE_SPDIF_POWER
           &spdif_enable,
 #endif
-          &id3_v1_first, &next_folder, &audioscrobbler
+          &id3_v1_first, &next_folder, &audioscrobbler, &cuesheet
 #ifdef HAVE_HEADPHONE_DETECTION
          ,&unplug_menu
 #endif
Index: apps/metadata.c
===================================================================
--- apps/metadata.c	(révision 12289)
+++ apps/metadata.c	(copie de travail)
@@ -31,6 +31,7 @@
 #include "debug.h"
 #include "system.h"
 #include "gwps.h"
+#include "cuesheet.h"
 
 enum tagtype { TAGTYPE_APE = 1, TAGTYPE_VORBIS };
 
@@ -2270,6 +2271,11 @@
 
     /* We have successfully read the metadata from the file */
 
+    if (cuesheet_is_enabled() && look_for_cuesheet_file(trackname))
+    {
+        track->id3.cuesheet_type = 1;
+    }
+
     lseek(fd, 0, SEEK_SET);
     strncpy(track->id3.path, trackname, sizeof(track->id3.path));
     track->taginfo_ready = true;
Index: apps/settings_list.c
===================================================================
--- apps/settings_list.c	(révision 12289)
+++ apps/settings_list.c	(copie de travail)
@@ -869,6 +869,7 @@
     OFFON_SETTING(0,usb_charging,LANG_USB_CHARGING,false,"usb charging",NULL),
 #endif
 #endif
+    OFFON_SETTING(0,cuesheet,LANG_CUESHEET,false,"cuesheet support", NULL),
 };
 
 const int nb_settings = sizeof(settings)/sizeof(*settings);
Index: apps/filetree.c
===================================================================
--- apps/filetree.c	(révision 12289)
+++ apps/filetree.c	(copie de travail)
@@ -41,6 +41,7 @@
 #include "dircache.h"
 #include "splash.h"
 #include "yesno.h"
+#include "cuesheet.h"
 #ifdef HAVE_LCD_BITMAP
 #include "keyboard.h"
 #endif
@@ -550,6 +551,10 @@
                 }
                 break;
 
+            case TREE_ATTR_CUE:
+                display_cuesheet_content(buf);
+                break;
+
             default:
             {
                 char* plugin;
Index: apps/playback.c
===================================================================
--- apps/playback.c	(révision 12289)
+++ 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
@@ -2742,6 +2743,20 @@
 
     }
 
+    if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1)
+    {
+        char cuepath[MAX_PATH];
+        strncpy(cuepath, trackname, MAX_PATH);
+        char *dot = strrchr(cuepath, '.');
+        strcpy(dot, ".cue\0");
+
+        if (parse_cuesheet(cuepath, start_play ? curr_cue : temp_cue))
+        {
+            strcpy((start_play ? curr_cue : temp_cue)->audio_filename, trackname);
+            cue_spoof_id3(curr_cue, &tracks[track_widx].id3);
+        }
+    }
+
     /* Load the codec. */
     tracks[track_widx].codecbuf = &filebuf[buf_widx];
     if (!audio_loadcodec(start_play)) 
@@ -3151,6 +3166,7 @@
 
     /* (Re-)open current track's file handle. */
     trackname = playlist_peek(0);
+
     fd = open(trackname, O_RDONLY);
     if (fd < 0) 
     {
Index: apps/SOURCES
===================================================================
--- apps/SOURCES	(révision 12289)
+++ apps/SOURCES	(copie de travail)
@@ -32,6 +32,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 12289)
+++ 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 12289)
+++ firmware/export/id3.h	(copie de travail)
@@ -212,6 +212,9 @@
     long track_peak;    /* 7.24 signed fixed point. 0 for no peak. */
     long album_peak;
 #endif
+
+    /* Cuesheet support */
+    int cuesheet_type;      /* 0: none, 1: external, 2: embedded */
 };
 
 enum {
