? cuesheet.patch
? h300
? h300-sim
? uisimulator
? apps/plugins/screenlock.c
? tools/codepages
? tools/mkboot
? tools/rdf2binary
Index: apps/action.c
===================================================================
RCS file: /cvsroot/rockbox/apps/action.c,v
retrieving revision 1.10
diff -u -r1.10 action.c
--- apps/action.c	12 Dec 2006 07:55:15 -0000	1.10
+++ apps/action.c	17 Dec 2006 13:46:14 -0000
@@ -31,6 +31,7 @@
 
 static bool ignore_until_release = false;
 static int last_button = BUTTON_NONE;
+static tsr_button_hook tsr_hook = NULL;
 
 /* software keylock stuff */
 #ifndef HAS_BUTTON_HOLD
@@ -99,6 +101,7 @@
     int button;
     int i=0;
     int ret = ACTION_UNKNOWN;
+    int start_context = context;
     
     if (timeout == TIMEOUT_NOBLOCK)
         button = button_get(false);
@@ -107,9 +110,10 @@
     else
         button = button_get_w_tmo(timeout);
 
-    
     if (button == BUTTON_NONE || button&SYS_EVENT)
     {
+        if (tsr_hook && ((button&SYS_EVENT) == 0))
+            return tsr_hook(button,start_context);
         return button;
     }
     
@@ -186,6 +190,8 @@
     }
 #endif
     last_button = button;
+    if (tsr_hook)
+        ret = tsr_hook(ret,start_context);
     return ret;
 }
 
@@ -224,3 +230,8 @@
     return (screen_has_lock && (keys_locked == true));
 }
 #endif
+
+void action_register_tsrbuttonhook(tsr_button_hook hook)
+{
+    tsr_hook = hook;
+}
Index: apps/action.h
===================================================================
RCS file: /cvsroot/rockbox/apps/action.h,v
retrieving revision 1.16
diff -u -r1.16 action.h
--- apps/action.h	16 Nov 2006 02:53:40 -0000	1.16
+++ apps/action.h	17 Dec 2006 13:46:14 -0000
@@ -237,4 +237,7 @@
 #ifndef HAS_BUTTON_HOLD
 bool is_keys_locked(void);
 #endif
+
+typedef int (*tsr_button_hook)(int action, int context);
+void action_register_tsrbuttonhook(tsr_button_hook hook);
 #endif
Index: apps/filetypes.c
===================================================================
RCS file: /cvsroot/rockbox/apps/filetypes.c,v
retrieving revision 1.28
diff -u -r1.28 filetypes.c
--- apps/filetypes.c	12 Dec 2006 22:22:20 -0000	1.28
+++ apps/filetypes.c	17 Dec 2006 13:46:15 -0000
@@ -56,7 +56,7 @@
 #endif
 
 /* string buffer length */
-#define STRING_BUFFER_SIZE 548
+#define STRING_BUFFER_SIZE 576
 
 /* number of bytes for the binary icon */
 #define ICON_LENGTH 6
Index: apps/onplay.c
===================================================================
RCS file: /cvsroot/rockbox/apps/onplay.c,v
retrieving revision 1.93
diff -u -r1.93 onplay.c
--- apps/onplay.c	11 Dec 2006 20:21:29 -0000	1.93
+++ apps/onplay.c	17 Dec 2006 13:46:17 -0000
@@ -125,7 +125,7 @@
 
 static bool list_viewers(void)
 {
-    struct menu_item menu[16];
+    struct menu_item menu[32];
     int m, i, result;
     int ret = 0;
 
Index: apps/plugin.c
===================================================================
RCS file: /cvsroot/rockbox/apps/plugin.c,v
retrieving revision 1.204
diff -u -r1.204 plugin.c
--- apps/plugin.c	7 Dec 2006 17:23:52 -0000	1.204
+++ apps/plugin.c	17 Dec 2006 13:46:19 -0000
@@ -486,6 +486,13 @@
     sound_default,
     pcm_record_more,
 #endif
+    action_register_tsrbuttonhook,
+    playlist_create,
+    playlist_start,
+    playlist_insert_track,
+#if CONFIG_CODEC == SWCODEC
+    audio_pre_ff_rewind,
+#endif
 };
 
 int plugin_load(const char* plugin, void* parameter)
Index: apps/plugin.h
===================================================================
RCS file: /cvsroot/rockbox/apps/plugin.h,v
retrieving revision 1.208
diff -u -r1.208 plugin.h
--- apps/plugin.h	7 Dec 2006 17:23:52 -0000	1.208
+++ apps/plugin.h	17 Dec 2006 13:46:19 -0000
@@ -110,7 +110,7 @@
 #define PLUGIN_MAGIC 0x526F634B /* RocK */
 
 /* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 39
+#define PLUGIN_API_VERSION 40
 
 /* update this to latest version if a change to the api struct breaks
    backwards compatibility (and please take the opportunity to sort in any
@@ -602,6 +602,14 @@
     int (*sound_default)(int setting);
     void (*pcm_record_more)(void *start, size_t size);
 #endif
+    void (*action_register_tsrbuttonhook)(tsr_button_hook hook);
+    int (*playlist_create)(const char *dir, const char *file);
+    int (*playlist_start)(int start_index, int offset);
+    int (*playlist_insert_track)(struct playlist_info* playlist, const char *filename,
+                          int position, bool queue, bool sync);
+#if CONFIG_CODEC == SWCODEC
+    void (*audio_pre_ff_rewind)(void);
+#endif
 };
 
 /* plugin header */
Index: apps/plugins/SOURCES
===================================================================
RCS file: /cvsroot/rockbox/apps/plugins/SOURCES,v
retrieving revision 1.144
diff -u -r1.144 SOURCES
--- apps/plugins/SOURCES	30 Nov 2006 22:29:48 -0000	1.144
+++ apps/plugins/SOURCES	17 Dec 2006 13:46:20 -0000
@@ -3,6 +3,7 @@
 chessclock.c
 credits.c
 cube.c
+cuesheet.c
 dict.c
 firmware_flash.c
 logo.c
Index: apps/plugins/cuesheet.c
===================================================================
RCS file: apps/plugins/cuesheet.c
diff -N apps/plugins/cuesheet.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/plugins/cuesheet.c	17 Dec 2006 13:46:20 -0000
@@ -0,0 +1,350 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: $
+ *
+ * Copyright (C) 2006 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 "plugin.h"
+PLUGIN_HEADER
+
+/* seems to work with 1300, but who knows... */ 
+#define THREAD_STACK_SIZE DEFAULT_STACK_SIZE + 0x200
+
+bool exit_thread = false;
+bool thread_ended = false;
+struct thread_entry *thread_id;
+unsigned long thread_stack[THREAD_STACK_SIZE/sizeof(long)];
+
+static struct plugin_api* rb;
+int main(void);
+bool exit_tsr(bool);
+void thread(void);
+int button_hook(int action, int context);
+
+#define MAX_NAME 64
+#define MAX_TRACKS 99
+
+char cd_title[MAX_NAME];
+char cd_performer[MAX_NAME];
+char cd_filename[MAX_PATH];
+
+struct track_info {
+    char title[MAX_NAME];
+    char performer[MAX_NAME];
+    unsigned long offset; /* ms from start of track */
+} tracks[MAX_TRACKS];
+int track_count = 0;
+struct mp3entry* id3 = NULL;
+#define MATCH(l,s) !rb->strncmp(l,s,rb->strlen(s)) 
+
+char *skip_whitespace(char* buf)
+{
+    char *r = buf;
+    while (*r && (*r < 33))
+        r++;
+    return r;
+}
+
+bool parse_cuesheet(char *file)
+{
+    char line[MAX_PATH];
+    char *s, *start, *end;
+    int fd = rb->open(file,O_RDONLY);
+    if (fd<0)
+    {
+        rb->splash(HZ*2,true,"Couldnt open file: %s",file);
+        return false;
+    }
+    track_count = -1;
+    while (rb->read_line(fd,line,MAX_PATH))
+    {
+        s = skip_whitespace(line);
+        if (MATCH(s, "TITLE"))
+        {
+            start = rb->strchr(s,'"');
+            if (!start)
+                break;
+            end = rb->strchr(++start,'"');
+            if (!end)
+                break;
+            *end = '\0';
+            if (track_count < 0)
+                rb->strncpy(cd_title,start,MAX_NAME);
+            else rb->strncpy(tracks[track_count].title,
+                                start,MAX_NAME);
+        }
+        else if (MATCH(s, "PERFORMER"))
+        {
+            start = rb->strchr(s,'"');
+            if (!start)
+                break;
+            end = rb->strchr(++start,'"');
+            if (!end)
+                break;
+            *end = '\0';
+            if (track_count < 0)
+                rb->strncpy(cd_performer,start,MAX_NAME);
+            else rb->strncpy(tracks[track_count].performer,
+                                start,MAX_NAME);
+        }
+        else if (MATCH(s, "FILE"))
+        {
+            start = rb->strchr(s,'"');
+            if (!start)
+                break;
+            end = rb->strchr(++start,'"');
+            if (!end)
+                break;
+            *end = '\0';
+            rb->strncpy(cd_filename,start,MAX_PATH);
+        }
+        else if (MATCH(s, "TRACK"))
+            track_count++;
+        else if (MATCH(s, "INDEX"))
+        {
+            char min[3], sec[3], fr[3];
+            s = rb->strchr(s,' ');
+            s = skip_whitespace(s);
+            s = rb->strchr(s,' ');
+            s = skip_whitespace(s);
+            rb->strncpy(min,s,3);
+            s += 3;min[2] ='\0';
+            rb->strncpy(sec,s,3);
+            s += 3;sec[2] ='\0';
+            rb->strncpy(fr,s,3);fr[2] ='\0';
+            tracks[track_count].offset = (13*rb->atoi(fr))
+                                          + (1000*rb->atoi(sec))
+                                          + (1000*rb->atoi(min)*60);
+        }
+    }
+    return true;
+}
+
+enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
+{
+    char filename[MAX_PATH], *s;
+    rb = api;
+    if (!parameter)
+        return PLUGIN_ERROR;
+
+    rb->strcpy(filename,(char*)parameter);
+    if (!parse_cuesheet(filename))
+        return PLUGIN_ERROR;
+
+    s = rb->strrchr(filename,'/');
+    if (!s)
+    {
+        rb->splash(HZ,true,"Cuesheet Failed!");
+        return PLUGIN_ERROR;
+    }
+    rb->strcpy(s+1,cd_filename);
+    if (rb->playlist_create(NULL,NULL) != -1)
+    {
+        rb->playlist_insert_track(NULL,filename,0,0,1);
+        rb->playlist_start(0,0);
+
+        if((thread_id = rb->create_thread(thread, thread_stack,
+            sizeof(thread_stack), "Cuesheet" 
+            IF_PRIO(, PRIORITY_BUFFERING))) == NULL)
+        {
+            rb->splash(HZ,true,"Cannot create thread!");
+            return PLUGIN_ERROR;
+        }
+        rb->action_register_tsrbuttonhook(button_hook);
+        rb->plugin_tsr(exit_tsr);
+    }
+    else rb->splash(HZ,true,"Cannot create playlist!");
+    
+    return PLUGIN_OK;
+}
+
+
+bool exit_tsr(bool reenter)
+{
+    (void)reenter;
+    if (!thread_ended)
+    {
+        rb->action_register_tsrbuttonhook(NULL);
+        rb->remove_thread(thread_id);
+    }
+    return true;
+}
+int seek(int pos)
+{
+    if (rb->audio_status() & AUDIO_STATUS_PLAY)
+    {
+        rb->audio_pre_ff_rewind();
+        rb->audio_ff_rewind(pos);
+        return 1;
+    }
+    else
+    {
+        rb->splash(HZ, true, "Not playing");
+        return 0;
+    }
+}
+int find_current_track(void)
+{
+    int i=0;
+    unsigned long current;
+    current = id3->elapsed;
+    while ((i+1<track_count) && (tracks[i+1].offset < current ))
+    {
+        i++;
+    }
+    return i;
+}
+char * list_get_name_cb(int selected_item, void * data, char *buffer)
+{
+    (void)data;
+    if (selected_item%2)
+        rb->snprintf(buffer,MAX_PATH,"%s",
+                 tracks[selected_item/2].title);
+    else rb->snprintf(buffer,MAX_PATH,"%d %s",
+                 (selected_item/2)+1, 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];
+    rb->snprintf(title,MAX_PATH,"%s: %s",cd_performer, cd_title);
+    rb->gui_synclist_init(&lists,list_get_name_cb,0,true,2);
+    rb->gui_synclist_set_nb_items(&lists,track_count*2);
+    rb->gui_synclist_select_item(&lists,find_current_track());
+    rb->gui_synclist_set_title(&lists,title,0);
+
+    while (!done)
+    {
+        rb->gui_synclist_draw(&lists);
+        action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
+        if (rb->gui_synclist_do_button(&lists,action,LIST_WRAP_UNLESS_HELD))
+            continue;
+        switch (action)
+        {
+            case ACTION_STD_OK:
+                sel = rb->gui_synclist_get_sel_pos(&lists)/2;
+                seek(tracks[sel].offset);
+            case ACTION_STD_CANCEL:
+                done = true;
+        }
+    }
+    rb->action_signalscreenchange();
+}
+static bool need_redraw = false;
+int button_hook(int action, int context)
+{
+    int ret = action;
+    int current_track;
+    unsigned long seek_to;
+    static bool done_tree_return = false;
+    if (context == CONTEXT_TREE)
+    {
+        if (!done_tree_return)
+        {
+            done_tree_return = true;
+            ret = ACTION_TREE_WPS;
+        }
+        else 
+        {
+            exit_thread = true;
+            rb->yield();
+        }
+    }
+    else if (context == CONTEXT_WPS)
+    {
+        switch (action)
+        {
+            case ACTION_WPS_SKIPNEXT:
+                current_track = find_current_track();
+                if (current_track == (track_count-1)) 
+                {
+                    rb->splash(HZ,true, "Cuesheet Finished!");
+                    exit_thread = true;
+                    ret = ACTION_WPS_STOP;
+                    break;
+                }
+                else seek_to = tracks[current_track+1].offset;
+                seek(seek_to);
+                ret = ACTION_REDRAW;
+                break;
+            case ACTION_WPS_SKIPPREV:
+                current_track = find_current_track();
+                if (current_track == 0) seek_to = 0;
+                else seek_to = tracks[current_track-1].offset;
+                seek(seek_to);
+                ret = ACTION_REDRAW;
+                break;
+            case ACTION_WPS_MENU:
+            case ACTION_WPS_BROWSE:
+                browse();
+                ret = ACTION_REDRAW;
+                break;
+
+            /* cases which return the same action */
+            case ACTION_WPS_STOP:
+                rb->splash(HZ,true, "Cuesheet exiting!");
+                exit_thread = true;
+            case ACTION_WPS_PLAY:
+            case ACTION_REDRAW:
+            case ACTION_WPS_SEEKBACK:
+            case ACTION_WPS_SEEKFWD:
+            case ACTION_WPS_STOPSEEK:
+            case ACTION_WPS_VOLDOWN:
+            case ACTION_WPS_VOLUP:
+                ret = action;
+            break;
+            
+            default:
+                ret = need_redraw?ACTION_REDRAW:ACTION_NONE;
+        }
+    }
+    
+    return ret;
+}
+
+
+void thread(void)
+{
+    static int current_track = -1, last_check = -1;
+    while (1)
+    {
+        id3 = rb->audio_current_track();
+        current_track = find_current_track();
+        if (last_check != current_track)
+        {
+            last_check = current_track;
+            id3->title = tracks[current_track].title;
+            id3->artist = tracks[current_track].performer;
+            id3->tracknum = current_track+1;
+            need_redraw = true;
+        }
+        else need_redraw = false;
+        if (exit_thread)
+        {
+            rb->action_register_tsrbuttonhook(NULL);
+            rb->remove_thread(thread_id);
+            thread_ended = true;
+            rb->yield(); /* exit the thread, this yield() won't return */
+        }
+        rb->sleep(HZ/4);
+    }
+}
+
Index: apps/plugins/viewers.config
===================================================================
RCS file: /cvsroot/rockbox/apps/plugins/viewers.config,v
retrieving revision 1.37
diff -u -r1.37 viewers.config
--- apps/plugins/viewers.config	30 Nov 2006 22:29:48 -0000	1.37
+++ apps/plugins/viewers.config	17 Dec 2006 13:46:21 -0000
@@ -28,3 +28,4 @@
 tzx,viewers/zxbox,66 52 4A 66 52 4A
 z80,viewers/zxbox,66 52 4A 66 52 4A
 zzz,viewers/properties,00 00 00 00 00 00
+cue,viewers/cuesheet,55 00 55 55 55 55
