Index: apps/gui/gwps-common.c
===================================================================
--- apps/gui/gwps-common.c	(révision 12842)
+++ apps/gui/gwps-common.c	(copie de travail)
@@ -55,12 +55,6 @@
 #include "action.h"
 #include "cuesheet.h"
 
-#ifdef HAVE_LCD_CHARCELLS
-static bool draw_player_progress(struct gui_wps *gwps);
-static void draw_player_fullbar(struct gui_wps *gwps,
-                                char* buf, int buf_size);
-#endif
-
 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ 
                                 /* 3% of 30min file == 54s step size */
 #define MIN_FF_REWIND_STEP 500
@@ -354,7 +348,7 @@
 
 /* draws the statusbar on the given wps-screen */
 #ifdef HAVE_LCD_BITMAP
-static void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
+void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
 {
     bool draw = global_settings.statusbar;
     
@@ -371,1103 +365,6 @@
     gui_statusbar_draw((wps)->statusbar, (force))
 #endif
 
-/* Extract a part from a path.
- *
- * buf      - buffer extract part to.
- * buf_size - size of buffer.
- * path     - path to extract from.
- * level    - what to extract. 0 is file name, 1 is parent of file, 2 is
- *            parent of parent, etc.
- *
- * Returns buf if the desired level was found, NULL otherwise.
- */
-static char* get_dir(char* buf, int buf_size, const char* path, int level)
-{
-    const char* sep;
-    const char* last_sep;
-    int len;
-
-    sep = path + strlen(path);
-    last_sep = sep;
-
-    while (sep > path)
-    {
-        if ('/' == *(--sep))
-        {
-            if (!level)
-            {
-                break;
-            }
-
-            level--;
-            last_sep = sep - 1;
-        }
-    }
-
-    if (level || (last_sep <= sep))
-    {
-        return NULL;
-    }
-
-    len = MIN(last_sep - sep, buf_size - 1);
-    strncpy(buf, sep + 1, len);
-    buf[len] = 0;
-    return buf;
-}
-
-/* Get the tag specified by the two characters at fmt.
- *
- * cid3      - ID3 data to get tag values from.
- * nid3     - next-song ID3 data to get tag values from.
- * tag      - string (of two characters) specifying the tag to get.
- * buf      - buffer to certain tags, such as track number, play time or
- *           directory name.
- * buf_size - size of buffer.
- * flags    - returns the type of the line. See constants i wps-display.h
- *
- * Returns the tag. NULL indicates the tag wasn't available.
- */
-static char* get_tag(struct wps_data* wps_data,
-                     struct mp3entry* cid3,
-                     struct mp3entry* nid3,
-                     const char* tag,
-                     char* buf,
-                     int buf_size,
-                     unsigned char* tag_len,
-                     unsigned short* subline_time_mult,
-                     unsigned char* flags,
-                     int *intval)
-{
-    struct mp3entry *id3 = cid3; /* default to current song */
-    int limit = *intval;
-#ifndef HAVE_LCD_CHARCELLS
-    (void)wps_data;
-#endif
-    if ((0 == tag[0]) || (0 == tag[1]))
-    {
-        *tag_len = 0;
-        return NULL;
-    }
-
-    *tag_len = 2;
-
-    *intval = 0;
-    
-    switch (tag[0])
-    {
-        case 'I':  /* ID3 Information */
-            id3 = nid3; /* display next-song data */
-            *flags |= WPS_REFRESH_DYNAMIC;
-            if(!id3)
-                return NULL; /* no such info (yet) */
-            /* fall-through */
-        case 'i':  /* ID3 Information */
-            *flags |= WPS_REFRESH_STATIC;
-            switch (tag[1])
-            {
-                case 't':  /* ID3 Title */
-                    return id3->title;
-
-                case 'a':  /* ID3 Artist */
-                    return id3->artist;
-
-                case 'n':  /* ID3 Track Number */
-                    if (id3->track_string)
-                        return id3->track_string;
-
-                    if (id3->tracknum) {
-                        snprintf(buf, buf_size, "%d", id3->tracknum);
-                        return buf;
-                    }
-                    return NULL;
-
-                case 'd':  /* ID3 Album/Disc */
-                    return id3->album;
-
-                case 'c':  /* ID3 Composer */
-                    return id3->composer;
-
-                case 'C':  /* ID3 Comment */
-                    return id3->comment;
-
-                case 'A':  /* ID3 Albumartist */
-                    return id3->albumartist;
-
-                case 'y':  /* year */
-                    if( id3->year_string )
-                        return id3->year_string;
-
-                    if (id3->year) {
-                        snprintf(buf, buf_size, "%d", id3->year);
-                        return buf;
-                    }
-                    return NULL;
-
-                case 'g':  /* genre */
-                    return id3->genre_string;
-
-                case 'v': /* id3 version */
-                    switch (id3->id3version)
-                    {
-                        case ID3_VER_1_0:
-                            return "1";
-
-                        case ID3_VER_1_1:
-                            return "1.1";
-
-                        case ID3_VER_2_2:
-                            return "2.2";
-
-                        case ID3_VER_2_3:
-                            return "2.3";
-
-                        case ID3_VER_2_4:
-                            return "2.4";
-
-                        default:
-                            return NULL;
-                    }
-            }
-            break;
-
-        case 'F':  /* File Information */
-            id3 = nid3;
-            *flags |= WPS_REFRESH_DYNAMIC;
-            if(!id3)
-                return NULL; /* no such info (yet) */
-            /* fall-through */
-        case 'f':  /* File Information */
-            *flags |= WPS_REFRESH_STATIC;
-            switch(tag[1])
-            {
-                case 'v':  /* VBR file? */
-                    return id3->vbr ? "(avg)" : NULL;
-
-                case 'b':  /* File Bitrate */
-                    if(id3->bitrate)
-                        snprintf(buf, buf_size, "%d", id3->bitrate);
-                    else
-                        snprintf(buf, buf_size, "?");
-                    return buf;
-
-                case 'f':  /* File Frequency */
-                    snprintf(buf, buf_size, "%ld", id3->frequency);
-                    return buf;
-
-                case 'p':  /* File Path */
-                    return id3->path;
-
-                case 'm':  /* File Name - With Extension */
-                    return get_dir(buf, buf_size, id3->path, 0);
-
-                case 'n':  /* File Name */
-                    if (get_dir(buf, buf_size, id3->path, 0))
-                    {
-                        /* Remove extension */
-                        char* sep = strrchr(buf, '.');
-
-                        if (NULL != sep)
-                        {
-                            *sep = 0;
-                        }
-
-                        return buf;
-                    }
-                    else
-                    {
-                        return NULL;
-                    }
-
-                case 's':  /* File Size (in kilobytes) */
-                    snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
-                    return buf;
-
-                case 'c':  /* File Codec */
-                    if(id3->codectype == AFMT_UNKNOWN)
-                        *intval = AFMT_NUM_CODECS;
-                    else
-                        *intval = id3->codectype;
-                    return id3_get_codec(id3);
-            }
-            break;
-
-        case 'p':  /* Playlist/Song Information */
-            switch(tag[1])
-            {
-                case 'b':  /* progress bar */
-                    *flags |= WPS_REFRESH_PLAYER_PROGRESS;
-#ifdef HAVE_LCD_CHARCELLS
-                    snprintf(buf, buf_size, "%c",
-                             wps_data->wps_progress_pat[0]);
-                    wps_data->full_line_progressbar=0;
-                    return buf;
-#else
-                    /* default values : */
-                    wps_data->progress_top = -1;
-                    wps_data->progress_height = 6;
-                    wps_data->progress_start = 0;
-                    wps_data->progress_end = 0;
-
-                    char *prev=strchr(tag, '|');
-                    if (prev) {
-                        char *p=strchr(prev+1, '|');
-                        if (p) {
-                            wps_data->progress_height=atoi(++prev);
-                            prev=strchr(prev, '|');
-                            p=strchr(++p, '|');
-                            if (p) {
-                                wps_data->progress_start=atoi(++prev);
-                                prev=strchr(prev, '|');
-                                p=strchr(++p, '|');
-                                if (p) {
-                                    wps_data->progress_end=atoi(++prev);
-                                    prev=strchr(prev, '|');
-                                    p=strchr(++p, '|');
-                                    if(p)
-                                        wps_data->progress_top = atoi(++prev);
-                                }
-
-                            if (wps_data->progress_height<3)
-                                wps_data->progress_height=3;
-                            if (wps_data->progress_end<wps_data->progress_start+3)
-                                wps_data->progress_end=0;
-                            }
-                        }
-                    }
-                    return "\x01";
-#endif
-                case 'f':  /* full-line progress bar */
-#ifdef HAVE_LCD_CHARCELLS
-                    if(is_new_player()) {
-                        *flags |= WPS_REFRESH_PLAYER_PROGRESS;
-                        *flags |= WPS_REFRESH_DYNAMIC;
-                        wps_data->full_line_progressbar=1;
-                        /* we need 11 characters (full line) for
-                           progress-bar */
-                        snprintf(buf, buf_size, "           ");
-                    }
-                    else
-                    {
-                        /* Tell the user if we have an OldPlayer */
-                        snprintf(buf, buf_size, " <Old LCD> ");
-                    }
-                    return buf;
-#endif
-                case 'p':  /* Playlist Position */
-                    *flags |= WPS_REFRESH_STATIC;
-                    snprintf(buf, buf_size, "%d",
-                             playlist_get_display_index());
-                    return buf;
-
-                case 'n':  /* Playlist Name (without path) */
-                    *flags |= WPS_REFRESH_STATIC;
-                    return playlist_name(NULL, buf, buf_size);
-
-                case 'e':  /* Playlist Total Entries */
-                    *flags |= WPS_REFRESH_STATIC;
-                    snprintf(buf, buf_size, "%d", playlist_amount());
-                    return buf;
-
-                case 'c':  /* Current Time in Song */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    format_time(buf, buf_size,
-                                id3->elapsed + wps_state.ff_rewind_count);
-                    return buf;
-
-                case 'r': /* Remaining Time in Song */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    format_time(buf, buf_size,
-                                id3->length - id3->elapsed -
-                                wps_state.ff_rewind_count);
-                    return buf;
-
-                case 't':  /* Total Time */
-                    *flags |= WPS_REFRESH_STATIC;
-                    format_time(buf, buf_size, id3->length);
-                    return buf;
-
-#ifdef HAVE_LCD_BITMAP
-                case 'm': /* Peak Meter */
-                    *flags |= WPS_REFRESH_PEAK_METER;
-                    return "\x01";
-#endif
-                case 's': /* shuffle */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    if ( global_settings.playlist_shuffle )
-                        return "s";
-                    else
-                        return NULL;
-                    break;
-
-                case 'v': /* volume */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    snprintf(buf, buf_size, "%d", global_settings.volume);
-                    *intval = limit * (global_settings.volume 
-                                    - sound_min(SOUND_VOLUME))
-                                 / (sound_max(SOUND_VOLUME)
-                                    - sound_min(SOUND_VOLUME)) + 1;
-                    return buf;
-
-            }
-            break;
-
-#if (CONFIG_CODEC == SWCODEC)
-        case 'S': /* DSP/Equalizer/Sound settings */
-            switch (tag[1])
-            {
-                case 'p': /* pitch */
-                    *intval = sound_get_pitch();
-                    snprintf(buf, buf_size, "%d.%d",
-                            *intval / 10, *intval % 10);
-                    return buf;
-            }
-            break;
-#endif
-            
-        case 'm':
-            switch (tag[1])
-            {
-                case 'm': /* playback repeat mode */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    *intval = global_settings.repeat_mode + 1;
-                    snprintf(buf, buf_size, "%d", *intval);
-                    return buf;
-                    
-                /* playback status */
-                case 'p': /* play */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    int status = audio_status();
-                    *intval = 1;
-                    if (status == AUDIO_STATUS_PLAY && \
-                        !(status & AUDIO_STATUS_PAUSE))
-                        *intval = 2;
-                    if (audio_status() & AUDIO_STATUS_PAUSE && \
-                        (! status_get_ffmode()))
-                        *intval = 3;
-                    if (status_get_ffmode() == STATUS_FASTFORWARD)
-                        *intval = 4;
-                    if (status_get_ffmode() == STATUS_FASTBACKWARD)
-                        *intval = 5;
-                    snprintf(buf, buf_size, "%d", *intval);
-                    return buf;
-
-#ifdef HAS_BUTTON_HOLD
-                case 'h': /* hold */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    if (button_hold())
-                        return "h";
-                    else
-                        return NULL;
-#endif
-#ifdef HAS_REMOTE_BUTTON_HOLD
-                case 'r': /* remote hold */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    if (remote_button_hold())
-                        return "r";
-                    else
-                        return NULL;
-#endif
-            }
-            break;
-
-        case 'b': /* battery info */
-            *flags |= WPS_REFRESH_DYNAMIC;
-            switch (tag[1])
-            {
-                case 'l': /* battery level */
-                {
-                    int l = battery_level();
-                    limit = MAX(limit, 2);
-                    if (l > -1)
-                    {
-                        snprintf(buf, buf_size, "%d", l);
-                        /* First enum is used for "unknown level". */
-                        *intval = (limit - 1) * l / 100 + 1 + 1;
-                    }
-                    else
-                    {
-                        *intval = 1;
-                        return "?";
-                    }
-                    return buf;
-                }
-
-                case 'v': /* battery voltage */
-                {
-                    unsigned int v = battery_voltage();
-                    snprintf(buf, buf_size, "%d.%02d", v/100, v%100);
-                    return buf;
-                }
-
-                case 't': /* estimated battery time */
-                {
-                    int t = battery_time();
-                    if (t >= 0)
-                        snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
-                    else
-                        strncpy(buf, "?h ?m", buf_size);
-                    return buf;
-                }
-
-                case 's': /* sleep timer */
-                {
-                    if (get_sleep_timer() == 0)
-                    {
-                        return NULL;
-                    }
-                    else
-                    {
-                        format_time(buf, buf_size, \
-                                    get_sleep_timer() * 1000);
-                        return buf;
-                    }
-                }
-                
-#if CONFIG_CHARGING
-                case 'p': /* External power plugged in? */
-                {
-                    if(charger_input_state==CHARGER)
-                        return "p";
-                    else
-                        return NULL;
-                }
-#endif
-#if CONFIG_CHARGING >= CHARGING_MONITOR
-                case 'c': /* Charging */
-                {
-                    if (charge_state == CHARGING || charge_state == TOPOFF) {
-                        return "c";
-                    } else {
-                        return NULL;
-                    }
-                }
-#endif
-            }
-            break;
-
-#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
-        case 'l': /* VIRTUAL_LED */
-        {
-            switch(tag[1])
-            {
-                case 'h': /* Only one we have so far HDD LED */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    if(led_read(HZ/2))
-                        return "h";
-                    else
-                        return NULL;
-            }
-        }
-        break;
-#endif
-
-        case 'D': /* Directory path information */
-            id3 = nid3; /* next song please! */
-            *flags |= WPS_REFRESH_DYNAMIC;
-            if(!id3)
-                return NULL; /* no such info (yet) */
-            /* fall-through */
-        case 'd': /* Directory path information */
-            {
-                int level = tag[1] - '0';
-                *flags |= WPS_REFRESH_STATIC;
-                /* d1 through d9 */
-                if ((0 < level) && (9 > level))
-                {
-                    return get_dir(buf, buf_size, id3->path, level);
-                }
-            }
-            break;
-
-        case 't': /* set sub line time multiplier */
-            {
-                int d = 1;
-                int time_mult = 0;
-                bool have_point = false;
-                bool have_tenth = false;
-
-                while (((tag[d] >= '0') &&
-                        (tag[d] <= '9')) ||
-                       (tag[d] == '.'))
-                {
-                    if (tag[d] != '.')
-                    {
-                        time_mult = time_mult * 10;
-                        time_mult = time_mult + tag[d] - '0';
-                        if (have_point)
-                        {
-                            have_tenth = true;
-                            d++;
-                            break;
-                        }
-                    }
-                    else
-                    {
-                        have_point = true;
-                    }
-                    d++;
-                }
-
-                if (have_tenth == false)
-                    time_mult *= 10;
-
-                *subline_time_mult = time_mult;
-                *tag_len = d;
-
-                buf[0] = 0;
-                return buf;
-            }
-            break;
-       case 'r':  /* Runtime database Information and Replaygain */
-            switch(tag[1])
-            {
-                case 'p':  /* Playcount */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    *intval = cid3->playcount+1;
-                    snprintf(buf, buf_size, "%ld", cid3->playcount);
-                    return buf;
-                case 'r':  /* Rating */
-                    *flags |= WPS_REFRESH_DYNAMIC;
-                    *intval = cid3->rating+1;
-                    snprintf(buf, buf_size, "%d", cid3->rating);
-                    return buf;
-#if CONFIG_CODEC == SWCODEC
-                case 'g': /* ReplayGain */
-                    *flags |= WPS_REFRESH_STATIC;
-                    if (global_settings.replaygain == 0)
-                        *intval = 1; /* off */
-                    else
-                    {
-                        int type = get_replaygain_mode(
-                            id3->track_gain_string != NULL,
-                            id3->album_gain_string != NULL);
-                        
-                        if (type < 0)
-                            *intval = 6;    /* no tag */
-                        else
-                            *intval = type + 2;
-                            
-                        if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
-                            *intval += 2;
-                    }
-
-                    switch (*intval)
-                    {
-                        case 1:
-                        case 6:
-                            return "+0.00 dB";
-                            break;
-                        case 2:
-                        case 4:
-                            strncpy(buf, id3->track_gain_string, buf_size);
-                            break;
-                        case 3:
-                        case 5:
-                            strncpy(buf, id3->album_gain_string, buf_size);
-                            break;
-                    }
-                    return buf;
-#endif
-            }
-            break;
-#if CONFIG_RTC
-       case 'c':  /* Real Time Clock display */
-            *flags |= WPS_REFRESH_DYNAMIC;
-            {
-                int value;
-                char *format = 0;
-                char *bufptr = buf;
-                struct tm* tm = get_time();
-                int i;
-                for (i=1;/*break*/;i++) {
-                    switch(tag[i])
-                    {
-                        case 'a': /* abbreviated weekday name (Sun..Sat) */
-                            value = tm->tm_wday;
-                            if (value > 6 || value < 0) continue;
-                            value = snprintf(
-                                    bufptr,buf_size,"%s",str(dayname[value]));
-                            bufptr += value;
-                            buf_size -= value;
-                            continue;
-                        case 'b': /* abbreviated month name (Jan..Dec) */
-                            value = tm->tm_mon;
-                            if (value > 11 || value < 0) continue;
-                            value = snprintf(
-                                    bufptr,buf_size,"%s",str(monthname[value]));
-                            bufptr += value;
-                            buf_size -= value;
-                            continue;
-                        case 'd': /* day of month (01..31) */
-                            value = tm->tm_mday;
-                            if (value > 31 || value < 1) continue;
-                            format = "%02d";
-                            break;
-                        case 'e': /* day of month, blank padded ( 1..31) */
-                            value = tm->tm_mday;
-                            if (value > 31 || value < 1) continue;
-                            format = "%2d";
-                            break;
-                        case 'H': /* hour (00..23) */
-                            value = tm->tm_hour;
-                            if (value > 23) continue;
-                            format = "%02d";
-                            break;
-                        case 'k': /* hour ( 0..23) */
-                            value = tm->tm_hour;
-                            if (value > 23) continue;
-                            format = "%2d";
-                            break;
-                        case 'I': /* hour (01..12) */
-                            value = tm->tm_hour;
-                            if (value > 23) continue;
-                            value %= 12;
-                            if (value == 0) value = 12;
-                            format = "%02d";
-                            break;
-                        case 'l': /* hour ( 1..12) */
-                            value = tm->tm_hour;
-                            if (value > 23 || value < 0) continue;
-                            value %= 12;
-                            if (value == 0) value = 12;
-                            format = "%2d";
-                            break;
-                        case 'm': /* month (01..12) */
-                            value = tm->tm_mon;
-                            if (value > 11 || value < 0) continue;
-                            value++;
-                            format = "%02d";
-                            break;
-                        case 'M': /* minute (00..59) */
-                            value = tm->tm_min;
-                            if (value > 59 || value < 0) continue;
-                            format = "%02d";
-                            break;
-                        case 'S': /* second (00..59) */
-                            value = tm->tm_sec;
-                            if (value > 59 || value < 0) continue;
-                            format = "%02d";
-                            break;
-                        case 'y': /* last two digits of year (00..99) */
-                            value = tm->tm_year;
-                            value %= 100;
-                            format = "%02d";
-                            break;
-                        case 'Y': /* year (1970...) */
-                            value = tm->tm_year;
-                            if (value > 199 || value < 100) continue;
-                            value += 1900;
-                            format = "%04d";
-                            break;
-                        case 'p': /* upper case AM or PM indicator */
-                            if (tm->tm_hour/12 == 0) format = "AM";
-                            else format = "PM";
-                            snprintf(bufptr,buf_size,"%s",format);
-                            bufptr += 2;
-                            buf_size -= 2;
-                            continue;
-                        case 'P': /* lower case am or pm indicator */
-                            if (tm->tm_hour/12 == 0) format = "am";
-                            else format = "pm";
-                            snprintf(bufptr,buf_size,"%s",format);
-                            bufptr += 2;
-                            buf_size -= 2;
-                            continue;
-                        case 'u': /* day of week (1..7); 1 is Monday */
-                            value = tm->tm_wday;
-                            if (value < 0 || value > 6) continue;
-                            value++;
-                            format = "%1d";
-                            break;
-                        case 'w': /* day of week (0..6); 0 is Sunday */
-                            value = tm->tm_wday;
-                            if (value < 0 || value > 6) continue;
-                            format = "%1d";
-                            break;
-                        default:
-                            if (tag[i] == 'c') {
-                                i++;
-                                value = -1;
-                                break;
-                            } else if (tag[i] == '\n') {
-                                value = -1;
-                                break;
-                            }
-                            snprintf(bufptr,buf_size,"%c",tag[i]);
-                            bufptr++;
-                            buf_size--;
-                            continue;
-                    } /* switch */
-                    if (value < 0) break;
-
-                    value = snprintf(bufptr, buf_size, format, value);
-                    bufptr += value;
-                    buf_size -= value;
-                } /* while */
-                *tag_len = i;
-                return buf;
-            }
-#endif /* CONFIG_RTC */
-#if CONFIG_CODEC == SWCODEC
-        case 'x':
-            *flags |= WPS_REFRESH_DYNAMIC;
-            switch(tag[1])
-            {
-                case 'd': /* crossfeed */
-                    if(global_settings.crossfeed)
-                        return "d";
-                    else
-                        return NULL;
-                case 'f': /* crossfade */
-                     *intval = global_settings.crossfade+1;
-                     snprintf(buf, buf_size, "%d", global_settings.crossfade);
-                return buf;
-            }
-            break;
-#endif
-    }
-    return NULL;
-}
-
-#ifdef HAVE_LCD_BITMAP
-/* clears the area where the image was shown */
-static void clear_image_pos(struct gui_wps *gwps, int n)
-{
-    if(!gwps)
-        return;
-    struct wps_data *data = gwps->data;
-    gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
-    gwps->display->fillrect(data->img[n].x, data->img[n].y, 
-                            data->img[n].bm.width, data->img[n].bm.height);
-    gwps->display->set_drawmode(DRMODE_SOLID);
-}
-#endif
-
-/* Skip to the end of the current %? conditional.
- *
- * fmt     - string to skip it. Should point to somewhere after the leading
- *           "<" char (and before or at the last ">").
- * num     - number of |'s to skip, or 0 to skip to the end (the ">").
- * enums   - If not NULL, set to the number of |'s found in the current 
- *           conditional (sub-conditionals are ignored). num should be 0
- *           to find all |'s.
- *
- * Returns the new position in fmt.
- */
-static const char* skip_conditional(struct gui_wps *gwps, const char* fmt,
-                                    int num, int *enums)
-{
-    int level = 1;
-    int count = num;
-    const char *last_alternative = NULL;
-#ifdef HAVE_LCD_BITMAP
-    struct wps_data *data = NULL;
-    int last_x=-1, last_y=-1, last_w=-1, last_h=-1;
-    if(gwps)
-        data = gwps->data;
-    if (enums)
-        *enums = 0;
-#else
-    (void)gwps;
-#endif
-    while (*fmt)
-    {
-        switch (*fmt++)
-        {
-            case '%':
-#ifdef HAVE_LCD_BITMAP
-                if(data && *(fmt) == 'x' && *(fmt+1) == 'd' )
-                {
-                    fmt +=2;
-                    int n = *fmt;
-                    if(n >= 'a' && n <= 'z')
-                        n -= 'a';
-                    if(n >= 'A' && n <= 'Z')
-                        n = n - 'A' + 26;
-                    if(last_x != data->img[n].x || last_y != data->img[n].y
-                       || last_w != data->img[n].bm.width
-                       || last_h != data->img[n].bm.height)
-                    {
-                        last_x = data->img[n].x;
-                        last_y = data->img[n].y;
-                        last_w = data->img[n].bm.width;
-                        last_h = data->img[n].bm.height;
-                        clear_image_pos(gwps,n);
-                    }
-                }
-#endif
-                break;
-
-            case '|':
-                if(1 == level) {
-                    if (enums)
-                        (*enums)++;
-                    last_alternative = fmt;
-                    if(num) {
-                        count--;
-                        if(count == 0)
-                            return fmt;
-                        continue;
-                    }
-                }
-                continue;
-
-            case '>':
-                if (0 == --level)
-                {
-                    /* We're just skipping to the end */
-                    if(num == 0)
-                        return fmt;
-
-                    /* If we are parsing an enum, we'll return the selected
-                       item. If there weren't enough items in the enum, we'll
-                       return the last one found. */
-                    if(count && last_alternative)
-                    {
-                        return last_alternative;
-                    }
-                    return fmt - 1;
-                }
-                continue;
-
-            default:
-                continue;
-        }
-
-        switch (*fmt++)
-        {
-            case 0:
-            case '%':
-            case '|':
-            case '<':
-            case '>':
-                break;
-
-            case '?':
-                while (*fmt && ('<' != *fmt))
-                    fmt++;
-
-                if ('<' == *fmt)
-                    fmt++;
-
-                level++;
-                break;
-
-            default:
-                break;
-        }
-    }
-
-    return fmt;
-}
-
-/* Generate the display based on id3 information and format string.
- *
- * buf      - char buffer to write the display to.
- * buf_size - the size of buffer.
- * id3      - the ID3 data to format with.
- * nid3     - the ID3 data of the next song (might by NULL)
- * fmt      - format description.
- * flags    - returns the type of the line. See constants i wps-display.h
- */
-static void format_display(struct gui_wps *gwps, char* buf,
-                           int buf_size,
-                           struct mp3entry* id3,
-                           struct mp3entry* nid3, /* next song's id3 */
-                           const char* fmt,
-                           struct align_pos* align,
-                           unsigned short* subline_time_mult,
-                           unsigned char* flags)
-{
-    char temp_buf[128];
-    char* buf_start = buf;
-    char* buf_end = buf + buf_size - 1;   /* Leave room for end null */
-    char* value = NULL;
-    int level = 0;
-    unsigned char tag_length;
-    int intval;
-    int cur_align;
-    char* cur_align_start;
-#ifdef HAVE_LCD_BITMAP
-    struct gui_img *img = gwps->data->img;
-    int n;
-#endif
-    
-    cur_align_start = buf;
-    cur_align = WPS_ALIGN_LEFT;
-    *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
-
-    align->left = 0;
-    align->center = 0;
-    align->right = 0;
-
-    while (fmt && *fmt && buf < buf_end)
-    {
-        switch (*fmt)
-        {
-            case '%':
-                ++fmt;
-                break;
-
-            case '|':
-            case '>':
-                if (level > 0)
-                {
-                    fmt = skip_conditional(NULL, fmt, 0, NULL);
-                    level--;
-                    continue;
-                }
-                /* Else fall through */
-
-            default:
-                *buf++ = *fmt++;
-                continue;
-        }
-
-        switch (*fmt)
-        {
-            case 0:
-                *buf++ = '%';
-                break;
-            case 'a':
-                ++fmt;
-                /* remember where the current aligned text started */
-                switch (cur_align)
-                {
-                    case WPS_ALIGN_LEFT:
-                        align->left = cur_align_start;
-                        break;
-
-                    case WPS_ALIGN_CENTER:
-                        align->center = cur_align_start;
-                        break;
-
-                    case WPS_ALIGN_RIGHT:
-                        align->right = cur_align_start;
-                        break;
-                }
-                /* start a new alignment */
-                switch (*fmt)
-                {
-                    case 'l':
-                        cur_align = WPS_ALIGN_LEFT;
-                        break;
-                    case 'c':
-                        cur_align = WPS_ALIGN_CENTER;
-                        break;
-                    case 'r':
-                        cur_align = WPS_ALIGN_RIGHT;
-                        break;
-                } 
-                *buf++=0;
-                cur_align_start = buf;
-                ++fmt;
-                break;
-            case 's':
-                *flags |= WPS_REFRESH_SCROLL;
-                ++fmt;
-                break;
-
-            case 'x': /* image support */
-#ifdef HAVE_LCD_BITMAP
-                if ('d' == *(fmt+1) )
-                {
-                    fmt+=2;
-
-                    /* get the image ID */
-                    n = *fmt;
-                    if(n >= 'a' && n <= 'z')
-                        n -= 'a';
-                    if(n >= 'A' && n <= 'Z')
-                        n = n - 'A' + 26;
-                    if (n >= 0 && n < MAX_IMAGES && img[n].loaded) {
-                        img[n].display = true;
-                    }
-                }
-
-#endif
-                fmt++;
-                break;
-
-
-            case '%':
-            case '|':
-            case '<':
-            case '>':
-            case ';':
-                *buf++ = *fmt++;
-                break;
-
-            case '?':
-                fmt++;
-                /* Get number of "|" chars in the current conditional;
-                 * used by get_tag when calculating levels.
-                 */
-                skip_conditional(gwps, fmt, 0, &intval);
-                value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
-                                sizeof(temp_buf),&tag_length,
-                                subline_time_mult, flags, &intval);
-
-                while (*fmt && ('<' != *fmt))
-                    fmt++;
-
-                if ('<' == *fmt)
-                    fmt++;
-
-                /* No value, so skip to else part, using a sufficiently high
-                   value to "hit" the last part of the conditional */
-                if ((!value) || (!strlen(value)))
-                    fmt = skip_conditional(NULL, fmt, 1000, NULL);
-                else
-                    if(intval > 1) /* enum */
-                        fmt = skip_conditional(NULL, fmt, intval - 1, NULL);
-
-                level++;
-                break;
-
-            default:
-                intval = 1;
-                value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
-                                sizeof(temp_buf), &tag_length,
-                                subline_time_mult, flags,&intval);
-                fmt += tag_length;
-
-                if (value)
-                {
-                    while (*value && (buf < buf_end))
-                        *buf++ = *value++;
-                }
-        }
-    }
-
-    /* remember where the current aligned text started */
-    switch (cur_align)
-    {
-        case WPS_ALIGN_LEFT:
-            align->left = cur_align_start;
-            break;
-
-        case WPS_ALIGN_CENTER:
-            align->center = cur_align_start;
-            break;
-
-        case WPS_ALIGN_RIGHT:
-            align->right = cur_align_start;
-            break;
-    }
-
-    *buf = 0;
-
-    /* if resulting line is an empty line, set the subline time to 0 */
-    if (buf - buf_start == 0)
-        *subline_time_mult = 0;
-
-    /* If no flags have been set, the line didn't contain any format codes.
-       We still want to refresh it. */
-    if(*flags == 0)
-        *flags = WPS_REFRESH_STATIC;
-}
-
 /* fades the volume */
 void fade(bool fade_in)
 {
@@ -1513,810 +410,6 @@
     }
 }
 
-/* Set format string to use for WPS, splitting it into lines */
-void gui_wps_format(struct wps_data *data)
-{
-    char* buf = data->format_buffer;
-    char* start_of_line = data->format_buffer;
-    int line = 0;
-    int subline;
-    char c;
-    if(!data)
-        return;
-
-    for (line=0; line<WPS_MAX_LINES; line++)
-    {
-        for (subline=0; subline<WPS_MAX_SUBLINES; subline++)
-        {
-            data->format_lines[line][subline] = 0;
-            data->time_mult[line][subline] = 0;
-        }
-        data->subline_expire_time[line] = 0;
-        data->curr_subline[line] = SUBLINE_RESET;
-    }
-    
-    line = 0;
-    subline = 0;
-    buf = skip_utf8_bom(buf);
-    data->format_lines[line][subline] = buf;
-
-    while ((*buf) && (line < WPS_MAX_LINES))
-    {
-        c = *buf;
-        
-        switch (c)
-        {
-            /*
-             * skip % sequences so "%;" doesn't start a new subline
-             * don't skip %x lines (pre-load bitmaps)
-             */
-            case '%':
-                buf++;
-                break;
-
-            case '\r': /* CR */
-                *buf = 0;
-                break;
-
-            case '\n': /* LF */
-                *buf = 0;
-
-                if (*start_of_line != '#') /* A comment? */
-                    line++;
-
-                if (line < WPS_MAX_LINES)
-                {
-                    /* the next line starts on the next byte */
-                    subline = 0;
-                    data->format_lines[line][subline] = buf+1;
-                    start_of_line = data->format_lines[line][subline];
-                }
-                break;
-
-            case ';': /* start a new subline */
-                *buf = 0;
-                subline++;
-                if (subline < WPS_MAX_SUBLINES)
-                {
-                    data->format_lines[line][subline] = buf+1;
-                }
-                else /* exceeded max sublines, skip rest of line */
-                {
-                    while (*(++buf))
-                    {
-                        if  ((*buf == '\r') || (*buf == '\n'))
-                        {
-                            break;
-                        }
-                    }
-                    buf--;
-                    subline = 0;
-                }
-                break;
-        }
-        buf++;
-    }
-}
-
-#ifdef HAVE_LCD_BITMAP
-/* Display images */
-static void wps_draw_image(struct gui_wps *gwps, int n)
-{
-    struct screen *display = gwps->display;
-    struct wps_data *data = gwps->data;
-    if(data->img[n].always_display)
-        display->set_drawmode(DRMODE_FG);
-    else
-        display->set_drawmode(DRMODE_SOLID);
-
-#if LCD_DEPTH > 1
-    if(data->img[n].bm.format == FORMAT_MONO) {
-#endif
-        display->mono_bitmap(data->img[n].bm.data, data->img[n].x,
-                             data->img[n].y, data->img[n].bm.width,
-                             data->img[n].bm.height);
-#if LCD_DEPTH > 1
-    } else {
-        display->transparent_bitmap((fb_data *)data->img[n].bm.data,
-                                    data->img[n].x,
-                                    data->img[n].y, data->img[n].bm.width,
-                                    data->img[n].bm.height);
-    }
-#endif
-}
-static void wps_display_images(struct gui_wps *gwps, bool always)
-{
-    if(!gwps || !gwps->data || !gwps->display) return;
-    int n;
-    struct wps_data *data = gwps->data;
-    struct screen *display = gwps->display;
-    for (n = 0; n < MAX_IMAGES; n++) {
-        if (data->img[n].loaded) {
-            if( (!always && data->img[n].display)
-                || (always && data->img[n].always_display) )
-                wps_draw_image(gwps, n);
-        }
-    }
-    display->set_drawmode(DRMODE_SOLID);
-}
-#endif
-
-#if 0 /* currently unused */
-void gui_wps_reset(struct gui_wps *gui_wps)
-{
-    if(!gui_wps || !gui_wps->data)
-        return;
-    gui_wps->data->wps_loaded = false;
-    memset(&gui_wps->data->format_buffer, 0,
-           sizeof(gui_wps->data->format_buffer));
-}
-#endif
-
-bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset,
-                     unsigned char refresh_mode)
-{
-    char buf[MAX_PATH];
-    unsigned char flags;
-    int i;
-    bool update_line;
-    bool only_one_subline;
-    bool new_subline_refresh;
-    bool reset_subline;
-    int search;
-    int search_start;
-    struct align_pos format_align;
-    struct wps_data *data = gwps->data;
-    struct wps_state *state = gwps->state;
-    struct screen *display = gwps->display;
-    if(!gwps || !data || !state || !display)
-    {
-        return false;
-    }
-#ifdef HAVE_LCD_BITMAP
-    int h = font_get(FONT_UI)->height;
-    int offset = 0;
-    gui_wps_statusbar_draw(gwps, true);
-    if(data->wps_sb_tag && data->show_sb_on_wps)
-        offset = STATUSBAR_HEIGHT;
-    else if ( global_settings.statusbar && !data->wps_sb_tag)
-        offset = STATUSBAR_HEIGHT;
-    
-    /* to find out wether the peak meter is enabled we
-       assume it wasn't until we find a line that contains
-       the peak meter. We can't use peak_meter_enabled itself
-       because that would mean to turn off the meter thread
-       temporarily. (That shouldn't matter unless yield
-       or sleep is called but who knows...)
-    */
-    bool enable_pm = false;
-
-    /* Set images to not to be displayed */
-    for (i = 0; i < MAX_IMAGES; i++) {
-        data->img[i].display = false;
-    }
-#endif
-    /* reset to first subline if refresh all flag is set */
-    if (refresh_mode == WPS_REFRESH_ALL)
-    {
-        for (i=0; i<WPS_MAX_LINES; i++)
-        {
-            data->curr_subline[i] = SUBLINE_RESET;
-        }
-    }
-
-#ifdef HAVE_LCD_CHARCELLS
-    for (i=0; i<8; i++) {
-       if (data->wps_progress_pat[i]==0)
-           data->wps_progress_pat[i]=display->get_locked_pattern();
-    }
-#endif
-
-    if (!state->id3)
-    {
-        display->stop_scroll();
-        return false;
-    }
-
-    state->ff_rewind_count = ffwd_offset;
-
-    for (i = 0; i < WPS_MAX_LINES; i++)
-    {
-        reset_subline = (data->curr_subline[i] == SUBLINE_RESET);
-        new_subline_refresh = false;
-        only_one_subline = false;
-
-        /* if time to advance to next sub-line  */
-        if (TIME_AFTER(current_tick,  data->subline_expire_time[i] - 1) ||
-           reset_subline)
-        {
-            /* search all sublines until the next subline with time > 0
-               is found or we get back to the subline we started with */
-            if (reset_subline)
-                search_start = 0;
-            else
-                search_start = data->curr_subline[i];
-            for (search=0; search<WPS_MAX_SUBLINES; search++)
-            {
-                data->curr_subline[i]++;
-
-                /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
-                if ((!data->format_lines[i][data->curr_subline[i]]) ||
-                    (data->curr_subline[i] == WPS_MAX_SUBLINES))
-                {
-                    if (data->curr_subline[i] == 1)
-                        only_one_subline = true;
-                    data->curr_subline[i] = 0;
-                }
-
-                /* if back where we started after search or
-                   only one subline is defined on the line */
-                if (((search > 0) && (data->curr_subline[i] == search_start)) ||
-                    only_one_subline)
-                {
-                    /* no other subline with a time > 0 exists */
-                    data->subline_expire_time[i] = (reset_subline?
-                        current_tick : data->subline_expire_time[i]) + 100 * HZ;
-                    break;
-                }
-                else
-                {
-                    /* get initial time multiplier and
-                       line type flags for this subline */
-                    format_display(gwps, buf, sizeof(buf),
-                                   state->id3, state->nid3,
-                                   data->format_lines[i][data->curr_subline[i]],
-                                   &format_align,
-                                   &data->time_mult[i][data->curr_subline[i]],
-                                   &data->line_type[i][data->curr_subline[i]]);
-
-                    /* only use this subline if subline time > 0 */
-                    if (data->time_mult[i][data->curr_subline[i]] > 0)
-                    {
-                        new_subline_refresh = true;
-                        data->subline_expire_time[i] = (reset_subline?
-                            current_tick : data->subline_expire_time[i]) +
-                            BASE_SUBLINE_TIME * data->time_mult[i][data->curr_subline[i]];
-                        break;
-                    }
-                }
-            }
-
-        }
-
-        update_line = false;
-
-        if ( !data->format_lines[i][data->curr_subline[i]] )
-            break;
-
-        if ((data->line_type[i][data->curr_subline[i]] & refresh_mode) ||
-            (refresh_mode == WPS_REFRESH_ALL) ||
-            new_subline_refresh)
-        {
-            flags = 0;
-#ifdef HAVE_LCD_BITMAP
-            int left_width,   left_xpos;
-            int center_width, center_xpos;
-            int right_width,  right_xpos;
-            int space_width;
-            int string_height;
-            int ypos;
-#endif
-
-            format_display(gwps, buf, sizeof(buf),
-                           state->id3, state->nid3,
-                           data->format_lines[i][data->curr_subline[i]],
-                           &format_align,
-                           &data->time_mult[i][data->curr_subline[i]],
-                           &flags);
-            data->line_type[i][data->curr_subline[i]] = flags;
-
-#ifdef HAVE_LCD_BITMAP
-            /* progress */
-            if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) 
-            {
-                int sb_y;
-                if (data->progress_top == -1)
-                    sb_y = i*h + offset + ((h > data->progress_height + 1)
-                                           ? (h - data->progress_height) / 2 : 1);
-                else
-                    sb_y = data->progress_top;
-
-                if (!data->progress_end)
-                    data->progress_end=display->width;
-                
-                if (gwps->data->progressbar.have_bitmap_pb)
-                    gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
-                                        data->progress_start, sb_y, 
-                                        data->progress_end-data->progress_start,
-                                        data->progressbar.bm.height,
-                                        state->id3->length?state->id3->length:1, 0,
-                                        state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
-                                        HORIZONTAL);
-                else
-                    gui_scrollbar_draw(display, data->progress_start, sb_y, 
-                                        data->progress_end-data->progress_start,
-                                        data->progress_height,
-                                        state->id3->length?state->id3->length:1, 0,
-                                        state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
-                                        HORIZONTAL);
-#ifdef AB_REPEAT_ENABLE
-                if ( ab_repeat_mode_enabled() )
-                    ab_draw_markers(display, state->id3->length,
-                            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) {
-                /* peak meter */
-                int peak_meter_y;
-
-                update_line = true;
-                peak_meter_y = i * h + offset;
-
-                /* The user might decide to have the peak meter in the last
-                   line so that it is only displayed if no status bar is
-                   visible. If so we neither want do draw nor enable the
-                   peak meter. */
-                if (peak_meter_y + h <= display->height) {
-                    /* found a line with a peak meter -> remember that we must
-                       enable it later */
-                    enable_pm = true;
-                    peak_meter_screen(gwps->display, 0, peak_meter_y,
-                                    MIN(h, display->height - peak_meter_y));
-                }
-            }
-#else
-            /* progress */
-            if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
-                if (data->full_line_progressbar)
-                    draw_player_fullbar(gwps, buf, sizeof(buf));
-                else
-                    draw_player_progress(gwps);
-            }
-#endif
-#ifdef HAVE_LCD_BITMAP
-            /* calculate different string sizes and positions */
-            display->getstringsize((unsigned char *)" ", &space_width, &string_height);
-            if (format_align.left != 0) {
-                display->getstringsize((unsigned char *)format_align.left,
-                                       &left_width, &string_height);
-            }
-            else {
-                left_width = 0;
-            }
-            left_xpos = 0;
-
-            if (format_align.center != 0) {
-                display->getstringsize((unsigned char *)format_align.center,
-                                       &center_width, &string_height);
-            }
-            else {
-                center_width = 0;
-            }
-            center_xpos=(display->width - center_width) / 2;
-
-            if (format_align.right != 0) {
-                display->getstringsize((unsigned char *)format_align.right,
-                                       &right_width, &string_height);
-            }
-            else {
-                right_width = 0;
-            }
-            right_xpos = (display->width - right_width);
-
-            /* Checks for overlapping strings.
-               If needed the overlapping strings will be merged, separated by a
-               space */
-
-            /* CASE 1: left and centered string overlap */
-            /* there is a left string, need to merge left and center */
-            if ((left_width != 0 && center_width != 0) &&
-                (left_xpos + left_width + space_width > center_xpos)) {
-                /* replace the former separator '\0' of left and 
-                   center string with a space */
-                *(--format_align.center) = ' ';
-                /* calculate the new width and position of the merged string */
-                left_width = left_width + space_width + center_width;
-                left_xpos = 0;
-                /* there is no centered string anymore */
-                center_width = 0;
-            }
-            /* there is no left string, move center to left */
-            if ((left_width == 0 && center_width != 0) &&
-                (left_xpos + left_width > center_xpos)) {
-                /* move the center string to the left string */
-                format_align.left = format_align.center;
-                /* calculate the new width and position of the string */
-                left_width = center_width;
-                left_xpos = 0;
-                /* there is no centered string anymore */
-                center_width = 0;
-            }
-
-            /* CASE 2: centered and right string overlap */
-            /* there is a right string, need to merge center and right */
-            if ((center_width != 0 && right_width != 0) &&
-                (center_xpos + center_width + space_width > right_xpos)) {
-                /* replace the former separator '\0' of center and 
-                   right string with a space */
-                *(--format_align.right) = ' ';
-                /* move the center string to the right after merge */
-                format_align.right = format_align.center;
-                /* calculate the new width and position of the merged string */
-                right_width = center_width + space_width + right_width;
-                right_xpos = (display->width - right_width);
-                /* there is no centered string anymore */
-                center_width = 0;
-            }
-            /* there is no right string, move center to right */
-            if ((center_width != 0 && right_width == 0) &&
-                (center_xpos + center_width > right_xpos)) {
-                /* move the center string to the right string */
-                format_align.right = format_align.center;
-                /* calculate the new width and position of the string */
-                right_width = center_width;
-                right_xpos = (display->width - right_width);
-                /* there is no centered string anymore */
-                center_width = 0;
-            }
-
-            /* CASE 3: left and right overlap
-               There is no center string anymore, either there never
-               was one or it has been merged in case 1 or 2 */
-            /* there is a left string, need to merge left and right */
-            if ((left_width != 0 && center_width == 0 && right_width != 0) &&
-                (left_xpos + left_width + space_width > right_xpos)) {
-                /* replace the former separator '\0' of left and 
-                   right string with a space */
-                *(--format_align.right) = ' ';
-                /* calculate the new width and position of the string */
-                left_width = left_width + space_width + right_width;
-                left_xpos = 0;
-                /* there is no right string anymore */
-                right_width = 0;
-            }
-            /* there is no left string, move right to left */
-            if ((left_width == 0 && center_width == 0 && right_width != 0) &&
-                (left_xpos + left_width > right_xpos)) {
-                /* move the right string to the left string */
-                format_align.left = format_align.right;
-                /* calculate the new width and position of the string */
-                left_width = right_width;
-                left_xpos = 0;
-                /* there is no right string anymore */
-                right_width = 0;
-            }
-
-#endif
-
-            if (flags & WPS_REFRESH_SCROLL) {
-
-                /* scroll line */
-                if ((refresh_mode & WPS_REFRESH_SCROLL) ||
-                    new_subline_refresh) {
-#ifdef HAVE_LCD_BITMAP
-                    ypos = (i*string_height)+display->getymargin();
-                    update_line = true;
-
-                    if (left_width>display->width) {
-                        display->puts_scroll(0, i,
-                                             (unsigned char *)format_align.left);
-                    } else {
-                        /* clear the line first */
-                        display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
-                        display->fillrect(0, ypos, display->width, string_height);
-                        display->set_drawmode(DRMODE_SOLID);
-
-                        /* Nasty hack: we output an empty scrolling string,
-                           which will reset the scroller for that line */
-                        display->puts_scroll(0, i, (unsigned char *)"");
-                        
-                        /* print aligned strings */
-                        if (left_width != 0)
-                        {
-                            display->putsxy(left_xpos, ypos,
-                                            (unsigned char *)format_align.left);
-                        }
-                        if (center_width != 0)
-                        {
-                            display->putsxy(center_xpos, ypos, 
-                                            (unsigned char *)format_align.center);
-                        }
-                        if (right_width != 0)
-                        {
-                            display->putsxy(right_xpos, ypos,
-                                            (unsigned char *)format_align.right);
-                        }
-                    }
-#else
-                    display->puts_scroll(0, i, buf);
-                    update_line = true;
-#endif
-                }
-            }
-            else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
-            {
-                /* dynamic / static line */
-                if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
-                    new_subline_refresh)
-                {
-#ifdef HAVE_LCD_BITMAP
-                    ypos = (i*string_height)+display->getymargin();
-                    update_line = true;
-
-                    /* clear the line first */
-                    display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
-                    display->fillrect(0, ypos, display->width, string_height);
-                    display->set_drawmode(DRMODE_SOLID);
-
-                    /* Nasty hack: we output an empty scrolling string,
-                       which will reset the scroller for that line */
-                    display->puts_scroll(0, i, (unsigned char *)"");
-                        
-                    /* print aligned strings */
-                    if (left_width != 0)
-                    {
-                        display->putsxy(left_xpos, ypos,
-                                        (unsigned char *)format_align.left);
-                    }
-                    if (center_width != 0)
-                    {
-                        display->putsxy(center_xpos, ypos,
-                                        (unsigned char *)format_align.center);
-                    }
-                    if (right_width != 0)
-                    {
-                        display->putsxy(right_xpos, ypos,
-                                        (unsigned char *)format_align.right);
-                    }
-#else
-                    update_line = true;
-                    display->puts(0, i, buf);
-#endif
-                }
-            }
-        }
-#ifdef HAVE_LCD_BITMAP
-        if (update_line) {
-            wps_display_images(gwps,false);
-        }
-#endif
-    }
-
-#ifdef HAVE_LCD_BITMAP
-    /* Display all images */
-    wps_display_images(gwps,true);
-    display->update();
-    /* Now we know wether the peak meter is used.
-       So we can enable / disable the peak meter thread */
-    data->peak_meter_enabled = enable_pm;
-#endif
-
-#if CONFIG_BACKLIGHT
-    if (global_settings.caption_backlight && state->id3) {
-        /* turn on backlight n seconds before track ends, and turn it off n
-           seconds into the new track. n == backlight_timeout, or 5s */
-        int n = backlight_timeout_value[global_settings.backlight_timeout] 
-              * 1000;
-
-        if ( n < 1000 )
-            n = 5000; /* use 5s if backlight is always on or off */
-
-        if (((state->id3->elapsed < 1000) ||
-            ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
-            (state->paused == false))
-            backlight_on();
-    }
-#endif
-#ifdef HAVE_REMOTE_LCD
-    if (global_settings.remote_caption_backlight && state->id3) {
-        /* turn on remote backlight n seconds before track ends, and turn it
-           off n seconds into the new track. n == remote_backlight_timeout,
-           or 5s */
-        int n = backlight_timeout_value[global_settings.remote_backlight_timeout]
-              * 1000;
-
-        if ( n < 1000 )
-            n = 5000; /* use 5s if backlight is always on or off */
-
-        if (((state->id3->elapsed < 1000) ||
-            ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
-            (state->paused == false))
-            remote_backlight_on();
-    }
-#endif
-    return true;
-}
-
-#ifdef HAVE_LCD_CHARCELLS
-static bool draw_player_progress(struct gui_wps *gwps)
-{
-    char player_progressbar[7];
-    char binline[36];
-    int songpos = 0;
-    int i,j;
-    struct wps_state *state = gwps->state;
-    struct screen *display = gwps->display;
-    if (!state->id3)
-        return false;
-
-    memset(binline, 1, sizeof binline);
-    memset(player_progressbar, 1, sizeof player_progressbar);
-
-    if(state->id3->elapsed >= state->id3->length)
-        songpos = 0;
-    else
-    {
-        if(state->wps_time_countup == false)
-            songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) /
-                            state->id3->length;
-        else
-            songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) / 
-                            state->id3->length;
-    }
-    for (i=0; i < songpos; i++)
-        binline[i] = 0;
-
-    for (i=0; i<=6; i++) {
-        for (j=0;j<5;j++) {
-            player_progressbar[i] <<= 1;
-            player_progressbar[i] += binline[i*5+j];
-        }
-    }
-    display->define_pattern(gwps->data->wps_progress_pat[0], player_progressbar);
-    return true;
-}
-
-static char map_fullbar_char(char ascii_val)
-{
-    if (ascii_val >= '0' && ascii_val <= '9') {
-        return(ascii_val - '0');
-    }
-    else if (ascii_val == ':') {
-        return(10);
-    }
-    else
-        return(11); /* anything besides a number or ':' is mapped to <blank> */
-}
-
-static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
-{
-    int i,j,lcd_char_pos;
-
-    char player_progressbar[7];
-    char binline[36];
-    static const char numbers[12][4][3]={
-        {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
-        {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
-        {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
-        {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
-        {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
-        {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
-        {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
-        {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
-        {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
-        {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
-        {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
-        {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
-    };
-
-    int songpos = 0;
-    int digits[6];
-    int time;
-    char timestr[7];
-    
-    struct wps_state *state = gwps->state;
-    struct screen *display = gwps->display;
-    struct wps_data *data = gwps->data;
-
-    for (i=0; i < buf_size; i++)
-        buf[i] = ' ';
-
-    if(state->id3->elapsed >= state->id3->length)
-        songpos = 55;
-    else {
-        if(state->wps_time_countup == false)
-            songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) / 
-                                state->id3->length;
-        else
-            songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) / 
-                                state->id3->length;
-    }
-
-    time=(state->id3->elapsed + state->ff_rewind_count);
-
-    memset(timestr, 0, sizeof(timestr));
-    format_time(timestr, sizeof(timestr), time);
-    for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
-        digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]);
-    }
-
-    /* build the progressbar-icons */
-    for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
-        memset(binline, 0, sizeof binline);
-        memset(player_progressbar, 0, sizeof player_progressbar);
-
-        /* make the character (progressbar & digit)*/
-        for (i=0; i<7; i++) {
-            for (j=0;j<5;j++) {
-                /* make the progressbar */
-                if (lcd_char_pos==(songpos/5)) {
-                    /* partial */
-                    if ((j<(songpos%5))&&(i>4))
-                        binline[i*5+j] = 1;
-                    else
-                        binline[i*5+j] = 0;
-                }
-                else {
-                    if (lcd_char_pos<(songpos/5)) {
-                        /* full character */
-                        if (i>4)
-                            binline[i*5+j] = 1;
-                    }
-                }
-                /* insert the digit */
-                if ((j<3)&&(i<4)) {
-                    if (numbers[digits[lcd_char_pos]][i][j]==1)
-                        binline[i*5+j] = 1;
-                }
-            }
-        }
-
-        for (i=0; i<=6; i++) {
-            for (j=0;j<5;j++) {
-                player_progressbar[i] <<= 1;
-                player_progressbar[i] += binline[i*5+j];
-            }
-        }
-
-        display->define_pattern(data->wps_progress_pat[lcd_char_pos+1],
-                                player_progressbar);
-        buf[lcd_char_pos]=data->wps_progress_pat[lcd_char_pos+1];
-
-    }
-
-    /* make rest of the progressbar if necessary */
-    if (songpos/5>5) {
-
-        /* set the characters positions that use the full 5 pixel wide bar */
-        for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++)
-            buf[lcd_char_pos] = 0x86; /* '_' */
-
-        /* build the partial bar character for the tail character position */
-        memset(binline, 0, sizeof binline);
-        memset(player_progressbar, 0, sizeof player_progressbar);
-
-        for (i=5; i<7; i++) {
-            for (j=0;j<5;j++) {
-                if (j<(songpos%5)) {
-                    binline[i*5+j] = 1;
-                }
-            }
-        }
-
-        for (i=0; i<7; i++) {
-            for (j=0;j<5;j++) {
-                player_progressbar[i] <<= 1;
-                player_progressbar[i] += binline[i*5+j];
-            }
-        }
-
-        display->define_pattern(data->wps_progress_pat[7],player_progressbar);
-
-        buf[songpos/5]=data->wps_progress_pat[7];
-    }
-}
-#endif
-
 /* set volume */
 void setvol(void)
 {
Index: apps/gui/wps.c
===================================================================
--- apps/gui/wps.c	(révision 0)
+++ apps/gui/wps.c	(révision 0)
@@ -0,0 +1,2208 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
+ *
+ * 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 <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "system.h"
+#include "atoi.h"
+#include "gwps.h"
+#include "wps.h"
+#include "debug.h"
+#include "lang.h"
+#include "rtc.h"
+#include "audio.h"
+#include "font.h"
+#include "cuesheet.h"
+#include "backlight.h"
+#include "settings.h"
+#include "scrollbar.h"
+#include "misc.h"
+#include "sound.h"
+#include "powermgmt.h"
+#include "abrepeat.h"
+#include "led.h"
+
+#ifdef HAVE_LCD_BITMAP
+#include "peakmeter.h"
+#endif
+
+#ifdef HAVE_LCD_CHARCELLS
+#include "hwcompat.h"
+#endif
+
+
+void gui_wps_statusbar_draw(struct gui_wps *wps, bool force);
+
+/* Parse a token from the given string. Return the length read */
+static int parse_token(const char *wps_token, struct wps_data *wps_data)
+{
+    int skip = 0;
+    int i = 0;
+    int line = wps_data->num_lines;
+
+    switch(*wps_token)
+    {
+
+        case '%':
+        case '<':
+        case '|':
+        case '>':
+        case ';':
+            /* escaped characters */
+            wps_data->tokens[wps_data->num_tokens].type = WPS_TOKEN_CHARACTER;
+            wps_data->tokens[wps_data->num_tokens].value.c = *wps_token;
+            wps_data->num_tokens++;
+            skip++;
+            break;
+
+        case '?':
+            /* conditional tag */
+            wps_data->tokens[wps_data->num_tokens++].type = WPS_TOKEN_CONDITIONAL;
+            wps_token++;
+            skip++;
+            /* no break because a '?' is followed by a regular tag */
+
+        default:
+            /* find what tag we have */
+            while (all_tags[i].name &&
+                   strncmp(wps_token, all_tags[i].name, strlen(all_tags[i].name)))
+            {
+                i++;
+            }
+
+            /* tags that start with 'F' or 'I' are for the next file */
+            if (*(all_tags[i].name) == 'I' || *(all_tags[i].name) == 'F' ||
+                *(all_tags[i].name) == 'D')
+            {
+                wps_data->tokens[wps_data->num_tokens].next = true;
+            }
+
+            wps_data->tokens[wps_data->num_tokens].type = all_tags[i].type;
+            int subline = wps_data->num_sublines[line];
+            wps_data->line_type[line][subline] |= all_tags[i].refresh_type;
+            skip += strlen(all_tags[i].name);
+
+            if (all_tags[i].parse_func) {
+                skip += all_tags[i].parse_func(wps_token + strlen(all_tags[i].name), wps_data);
+            }
+
+            wps_data->num_tokens++;
+            break;
+    }
+
+    return skip;
+}
+
+#if CONFIG_RTC
+static int parse_rtc_format(const char *wps_token, struct wps_data *wps_data)
+{
+    int skip = 0, i;
+    static struct wps_tag rtc_tags[] = {
+        { "d", WPS_TOKEN_RTC_DAY_OF_MONTH,              0, NULL },
+        { "e", WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED, 0, NULL },
+        { "H", WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED,       0, NULL },
+        { "k", WPS_TOKEN_RTC_HOUR_24,                   0, NULL },
+        { "I", WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED,       0, NULL },
+        { "l", WPS_TOKEN_RTC_HOUR_12,                   0, NULL },
+        { "m", WPS_TOKEN_RTC_MONTH,                     0, NULL },
+        { "M", WPS_TOKEN_RTC_MINUTE,                    0, NULL },
+        { "S", WPS_TOKEN_RTC_SECOND,                    0, NULL },
+        { "y", WPS_TOKEN_RTC_YEAR_2_DIGITS,             0, NULL },
+        { "Y", WPS_TOKEN_RTC_YEAR_4_DIGITS,             0, NULL },
+        { "p", WPS_TOKEN_RTC_AM_PM_UPPER,               0, NULL },
+        { "P", WPS_TOKEN_RTC_AM_PM_LOWER,               0, NULL },
+        { "a", WPS_TOKEN_RTC_WEEKDAY_NAME,              0, NULL },
+        { "b", WPS_TOKEN_RTC_MONTH_NAME,                0, NULL },
+        { "u", WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON,     0, NULL },
+        { "w", WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN,     0, NULL },
+        { "\0",WPS_TOKEN_CHARACTER,                     0, NULL }
+    };
+
+    //wps_data->num_tokens--; /* eat the unneeded %c tag */
+
+    /* RTC tag format ends with a c or a newline */
+    while (wps_token && *wps_token != 'c' && *wps_token != '\n')
+    {
+        /* find what format char we have */
+        i = 0;
+        while (*(rtc_tags[i].name) && *wps_token != *(rtc_tags[i].name))
+        {
+            i++;
+        }
+
+        wps_data->num_tokens++;
+        wps_data->tokens[wps_data->num_tokens].type = rtc_tags[i].type;
+        wps_data->tokens[wps_data->num_tokens].value.c = *wps_token;
+        skip ++;
+        wps_token++;
+    }
+
+    /* eat the unwanted c at the end of the format */
+    if (*wps_token == 'c')
+        skip++;
+
+    return skip;
+}
+#endif
+
+
+
+static int parse_dir_level(const char *wps_token, struct wps_data *wps_data)
+{
+    char val[] = { *wps_token, '\0' };
+    wps_data->tokens[wps_data->num_tokens].value.i = atoi(val);
+    return 1;
+}
+
+static int parse_subline_timeout(const char *wps_token, struct wps_data *wps_data)
+{
+    int skip = 0;
+    int val = 0;
+    bool have_point = false;
+    bool have_tenth = false;
+
+    while (((*wps_token >= '0') && (*wps_token <= '9'))
+           || *wps_token == '.' )
+    {
+        if (*wps_token != '.')
+        {
+            val *= 10;
+            val += *wps_token - '0';
+            if (have_point)
+            {
+                have_tenth = true;
+                wps_token++;
+                skip++;
+                break;
+            }
+        }
+        else
+        {
+            have_point = true;
+        }
+        wps_token++;
+        skip++;
+    }
+
+    if (have_tenth == false)
+        val *= 10;
+
+    int line = wps_data->num_lines;
+    int subline = wps_data->num_sublines[line];
+    wps_data->time_mult[line][subline] = val;
+
+    wps_data->tokens[wps_data->num_tokens].value.i = val;
+    return skip;
+}
+
+#if 0
+static int parse_eat_eol(const char *wps_token, struct wps_data *wps_data)
+{
+    int skip = 0;
+    while(wps_token && *wps_token++ != '\n') {
+        skip++;
+    }
+    skip++; /* also skip newline */
+    return skip;
+}
+#endif
+
+#ifdef HAVE_LCD_BITMAP
+/*
+static int parse_image_preload(const char *wps_token, struct wps_data *wps_data)
+{
+    return parse_eat_eol(wps_token, wps_data);
+}*/
+
+static int parse_image_display(const char *wps_token, struct wps_data *wps_data)
+{
+    wps_data->tokens[wps_data->num_tokens].value.c = *wps_token;
+    return 1;
+}
+
+static int parse_peakmeter(const char *wps_token, struct wps_data *wps_data)
+{
+    wps_data->peak_meter_enabled = true;
+
+    if (wps_token)  /* just so it's used, to avoid a warning */
+        return 0;
+    else
+        return 0;
+}
+#endif
+
+static int parse_progressbar(const char *wps_token, struct wps_data *wps_data)
+{
+    int *vals[] = {
+        &wps_data->progress_height,
+        &wps_data->progress_start,
+        &wps_data->progress_end,
+        &wps_data->progress_top };
+
+    /* default values : */
+    wps_data->progress_height = 6;
+    wps_data->progress_start = 0;
+    wps_data->progress_end = 0;
+    wps_data->progress_top = -1;
+
+    int i = 0;
+    char *newline = strchr(wps_token, '\n');
+    char *prev = strchr(wps_token, '|');
+    if (prev && prev < newline) {
+        char *next = strchr(prev+1, '|');
+        while (i < 4 && next && next < newline)
+        {
+            *(vals[i++]) = atoi(++prev);
+            prev = strchr(prev, '|');
+            next = strchr(++next, '|');
+        }
+
+        if (wps_data->progress_height < 3)
+            wps_data->progress_height = 3;
+        if (wps_data->progress_end < wps_data->progress_start + 3)
+            wps_data->progress_end = 0;
+    }
+
+    int line = wps_data->num_lines;
+    int subline = wps_data->num_sublines[line];
+    wps_data->line_type[line][subline] |= WPS_REFRESH_PLAYER_PROGRESS;
+/*
+    DEBUGF("pogressbar height: %d\n", wps_data->progress_height);
+    DEBUGF("pogressbar start: %d\n", wps_data->progress_start);
+    DEBUGF("pogressbar end: %d\n", wps_data->progress_end);
+    DEBUGF("pogressbar top: %d\n", wps_data->progress_top);*/
+
+    return newline - wps_token;
+}
+
+static void wps_parse(struct wps_data *data)
+{
+    /* level of current conditional */
+    int level = -1;
+
+    /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
+       or WPS_TOKEN_CONDITIONAL_START in current level */
+    int lastcond[WPS_MAX_COND_LEVEL];
+
+    /* index of the WPS_TOKEN_CONDITIONAL in current level */
+    int condindex[WPS_MAX_COND_LEVEL];
+
+    /* number of condtional options in current level */
+    int numoptions[WPS_MAX_COND_LEVEL];
+
+    data->num_tokens = 0;
+
+    char *wps_string = data->format_buffer;
+    char *wps_current_string = data->wps_strings;
+
+    int subline;
+
+    while(wps_string && *wps_string && data->num_tokens < WPS_MAX_TOKENS) {
+
+        switch(*wps_string++) {
+
+            /* Regular tag */
+            case '%':
+                wps_string += parse_token(wps_string, data);
+                break;
+
+            /* Alternating sublines separator */
+            case ';':
+                data->tokens[data->num_tokens++].type = WPS_TOKEN_SUBLINE_SEPARATOR;
+                subline = ++(data->num_sublines[data->num_lines]);
+                data->format_lines[data->num_lines][subline] = data->num_tokens;
+                break;
+
+            /* Conditional list start */
+            case '<':
+                data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
+                lastcond[++level] = data->num_tokens++;
+                {
+                    /* find conditional token. Usually, it's 2 tokens before,
+                    but this can change if a conditional uses an unknown token,
+                    so we have to be sure. */
+                    int i = data->num_tokens;
+                    while (i > 0 && data->tokens[i].type != WPS_TOKEN_CONDITIONAL)
+                        i--;
+
+                    condindex[level] = i;
+                    numoptions[level] = 1;
+                }
+                break;
+
+            /* Conditional list end */
+            case '>':
+            condlistend:
+                data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
+                if (lastcond[level])
+                {
+                    data->tokens[lastcond[level]].value.i = data->num_tokens;
+                }
+                lastcond[level] = 0;
+                data->num_tokens++;
+                data->tokens[condindex[level]].value.i = numoptions[level];
+                level--;
+                break;
+
+            /* Conditional list option */
+            case '|':
+                if (level < 0)
+                {
+                    /* Not a conditional option, just a regular character */
+                    wps_string--;
+                    wps_string += parse_token(wps_string, data);
+                    break;
+                }
+                data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
+                if (lastcond[level])
+                {
+                    data->tokens[lastcond[level]].value.i = data->num_tokens;
+                }
+                lastcond[level] = data->num_tokens;
+                numoptions[level]++;
+                data->num_tokens++;
+                break;
+
+            /* Comment */
+            case '#':
+                while(*wps_string != '\n') {
+                    wps_string++;
+                }
+                wps_string++; /* Skip newline as well */
+                break;
+
+            /* End of this line */
+            case '\n':
+                if (level >= 0)
+                {
+                    /* We have unclosed conditionals, so we
+                       close them before adding the EOL token */
+                    wps_string--;
+                    goto condlistend;
+                    break;
+                }
+                data->tokens[data->num_tokens++].type = WPS_TOKEN_EOL;
+                (data->num_sublines[data->num_lines])++;
+                data->format_lines[++(data->num_lines)][0] = data->num_tokens;
+                break;
+
+            /* String */
+            default:
+            {
+                    /* this is just a normal string */
+                data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
+                data->tokens[data->num_tokens].value.s = wps_current_string;
+                data->num_tokens++;
+
+                    /* Copy the first byte */
+                *wps_current_string++ = *(wps_string - 1);
+
+                    /* Scan ahead until we hit something we shouldn't just read */
+                while(wps_string &&
+                      *wps_string != '%' && //*wps_string != '#' &&
+                      *wps_string != '<' && *wps_string != '>' &&
+                      *wps_string != '|' && *wps_string != '\n') {
+                          *wps_current_string++ = *wps_string++;
+                      }
+
+                    /* null terminate the string */
+                *wps_current_string++ = '\0';
+            }
+
+                break;
+        }
+    }
+}
+
+#ifdef DEBUG
+static void dump_wps_tokens(struct wps_data *wps_data)
+{
+    int i, j;
+    int indent = 0;
+    char buf[64];
+    bool next;
+
+    /* Dump parsed WPS */
+    for(i = 0; i < wps_data->num_tokens && i < WPS_MAX_TOKENS; i++) {
+
+        next = wps_data->tokens[i].next;
+
+        switch(wps_data->tokens[i].type) {
+            case WPS_TOKEN_UNKNOWN:
+                snprintf(buf, sizeof(buf), "Unknown token");
+                break;
+            case WPS_TOKEN_CHARACTER:
+                snprintf(buf, sizeof(buf), "Character '%c'",
+                         wps_data->tokens[i].value.c);
+                break;
+
+            case WPS_TOKEN_STRING:
+                snprintf(buf, sizeof(buf), "String '%s'",
+                         wps_data->tokens[i].value.s);
+                break;
+
+            case WPS_TOKEN_EOL:
+                snprintf(buf, sizeof(buf), "%s", "EOL");
+                break;
+
+#ifdef HAVE_LCD_BITMAP
+            case WPS_TOKEN_ALIGN_LEFT:
+                snprintf(buf, sizeof(buf), "%s", "align left");
+                break;
+
+            case WPS_TOKEN_ALIGN_CENTER:
+                snprintf(buf, sizeof(buf), "%s", "align center");
+                break;
+
+            case WPS_TOKEN_ALIGN_RIGHT:
+                snprintf(buf, sizeof(buf), "%s", "align right");
+                break;
+#endif
+
+            case WPS_TOKEN_CONDITIONAL:
+            snprintf(buf, sizeof(buf), "%s, %d options", "conditional",
+                     wps_data->tokens[i].value.i);
+                break;
+
+            case WPS_TOKEN_CONDITIONAL_START:
+                snprintf(buf, sizeof(buf), "%s, next cond: %d",
+                         "conditional start", wps_data->tokens[i].value.i);
+                indent++;
+                break;
+
+            case WPS_TOKEN_CONDITIONAL_OPTION:
+                snprintf(buf, sizeof(buf), "%s, next cond: %d",
+                         "conditional option", wps_data->tokens[i].value.i);
+                break;
+
+            case WPS_TOKEN_CONDITIONAL_END:
+                snprintf(buf, sizeof(buf), "%s", "conditional end");
+                indent--;
+                break;
+
+#ifdef HAVE_LCD_BITMAP
+            case WPS_TOKEN_IMAGE_PRELOAD:
+                snprintf(buf, sizeof(buf), "%s", "preload image");
+                break;
+
+            case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
+                snprintf(buf, sizeof(buf), "%s %c", "display preloaded image",
+                         wps_data->tokens[i].value.c);
+                break;
+
+            case WPS_TOKEN_IMAGE_DISPLAY:
+                snprintf(buf, sizeof(buf), "%s", "display image");
+                break;
+#endif
+
+#ifdef HAS_BUTTON_HOLD
+            case WPS_TOKEN_MAIN_HOLD:
+                snprintf(buf, sizeof(buf), "%s", "mode hold");
+                break;
+#endif
+
+#ifdef HAS_REMOTE_BUTTON_HOLD
+            case WPS_TOKEN_REMOTE_HOLD:
+                snprintf(buf, sizeof(buf), "%s", "mode remote hold");
+                break;
+#endif
+
+            case WPS_TOKEN_REPEAT_MODE:
+                snprintf(buf, sizeof(buf), "%s", "mode repeat");
+                break;
+
+            case WPS_TOKEN_PLAYBACK_STATUS:
+                snprintf(buf, sizeof(buf), "%s", "mode playback");
+                break;
+
+#if CONFIG_RTC
+            case WPS_TOKEN_RTC_DAY_OF_MONTH:
+            case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
+            case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
+            case WPS_TOKEN_RTC_HOUR_24:
+            case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
+            case WPS_TOKEN_RTC_HOUR_12:
+            case WPS_TOKEN_RTC_MONTH:
+            case WPS_TOKEN_RTC_MINUTE:
+            case WPS_TOKEN_RTC_SECOND:
+            case WPS_TOKEN_RTC_YEAR_2_DIGITS:
+            case WPS_TOKEN_RTC_YEAR_4_DIGITS:
+            case WPS_TOKEN_RTC_AM_PM_UPPER:
+            case WPS_TOKEN_RTC_AM_PM_LOWER:
+            case WPS_TOKEN_RTC_WEEKDAY_NAME:
+            case WPS_TOKEN_RTC_MONTH_NAME:
+            case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
+            case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
+            case WPS_TOKEN_RTC:
+            snprintf(buf, sizeof(buf), "%s %c", "real-time clock tag:",
+                     wps_data->tokens[i].value.c);
+                break;
+#endif
+
+#ifdef HAVE_LCD_BITMAP
+            case WPS_TOKEN_IMAGE_BACKDROP:
+                snprintf(buf, sizeof(buf), "%s", "backdrop image");
+                break;
+
+            case WPS_TOKEN_IMAGE_PROGRESS_BAR:
+                snprintf(buf, sizeof(buf), "%s", "progressbar bitmap");
+                break;
+
+
+            case WPS_TOKEN_STATUSBAR_ENABLED:
+                snprintf(buf, sizeof(buf), "%s", "statusbar enable");
+                break;
+
+            case WPS_TOKEN_STATUSBAR_DISABLED:
+                snprintf(buf, sizeof(buf), "%s", "statusbar disable");
+                break;
+
+            case WPS_TOKEN_PEAKMETER:
+                snprintf(buf, sizeof(buf), "%s", "peakmeter");
+                break;
+#endif
+
+            case WPS_TOKEN_PROGRESSBAR:
+                snprintf(buf, sizeof(buf), "%s", "progressbar");
+                break;
+
+#ifdef HAVE_LCD_CHARCELLS
+            case WPS_TOKEN_PLAYER_PROGRESSBAR:
+                snprintf(buf, sizeof(buf), "%s", "full line progressbar");
+                break;
+#endif
+
+            case WPS_TOKEN_TRACK_TIME_ELAPSED:
+                snprintf(buf, sizeof(buf), "%s", "time elapsed in track");
+                break;
+
+            case WPS_TOKEN_PLAYLIST_ENTRIES:
+                snprintf(buf, sizeof(buf), "%s", "number of entries in playlist");
+                break;
+
+            case WPS_TOKEN_PLAYLIST_NAME:
+                snprintf(buf, sizeof(buf), "%s", "playlist name");
+                break;
+
+            case WPS_TOKEN_PLAYLIST_POSITION:
+                snprintf(buf, sizeof(buf), "%s", "position in playlist");
+                break;
+
+            case WPS_TOKEN_TRACK_TIME_REMAINING:
+                snprintf(buf, sizeof(buf), "%s", "time remaining in track");
+                break;
+
+            case WPS_TOKEN_PLAYLIST_SHUFFLE:
+                snprintf(buf, sizeof(buf), "%s", "playlist shuffle mode");
+                break;
+
+            case WPS_TOKEN_TRACK_LENGTH:
+                snprintf(buf, sizeof(buf), "%s", "track length");
+                break;
+
+            case WPS_TOKEN_VOLUME:
+                snprintf(buf, sizeof(buf), "%s", "volume");
+                break;
+
+            case WPS_TOKEN_METADATA_ARTIST:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "track artist");
+                break;
+
+            case WPS_TOKEN_METADATA_COMPOSER:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "track composer");
+                break;
+
+            case WPS_TOKEN_METADATA_ALBUM:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "track album");
+                break;
+
+            case WPS_TOKEN_METADATA_GENRE:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "track genre");
+                break;
+
+            case WPS_TOKEN_METADATA_TRACK_NUMBER:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "track number");
+                break;
+
+            case WPS_TOKEN_METADATA_TRACK_TITLE:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "track title");
+                break;
+
+            case WPS_TOKEN_METADATA_VERSION:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "track ID3 version");
+                break;
+
+            case WPS_TOKEN_METADATA_YEAR:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "track year");
+                break;
+
+            case WPS_TOKEN_BATTERY_PERCENT:
+                snprintf(buf, sizeof(buf), "%s", "battery percentage");
+                break;
+
+            case WPS_TOKEN_BATTERY_VOLTS:
+                snprintf(buf, sizeof(buf), "%s", "battery voltage");
+                break;
+
+            case WPS_TOKEN_BATTERY_TIME:
+                snprintf(buf, sizeof(buf), "%s", "battery time left");
+                break;
+
+            case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
+                snprintf(buf, sizeof(buf), "%s", "battery charger connected");
+                break;
+
+            case WPS_TOKEN_BATTERY_CHARGING:
+                snprintf(buf, sizeof(buf), "%s", "battery charging");
+                break;
+
+            case WPS_TOKEN_FILE_BITRATE:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "file bitrate");
+                break;
+
+            case WPS_TOKEN_FILE_CODEC:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "file codec");
+                break;
+
+            case WPS_TOKEN_FILE_FREQUENCY:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "file audio frequency");
+                break;
+
+            case WPS_TOKEN_FILE_NAME:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "file name");
+                break;
+
+            case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "file name with extension");
+                break;
+
+            case WPS_TOKEN_FILE_PATH:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "file path");
+                break;
+
+            case WPS_TOKEN_FILE_SIZE:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "file size");
+                break;
+
+            case WPS_TOKEN_FILE_VBR:
+                snprintf(buf, sizeof(buf), "%s%s", next ? "next " : "",
+                         "file is vbr");
+                break;
+
+            case WPS_TOKEN_FILE_DIRECTORY:
+                snprintf(buf, sizeof(buf), "%s%s: %d", next ? "next " : "",
+                         "file directory, level",
+                         wps_data->tokens[i].value.i);
+                break;
+
+            case WPS_TOKEN_SCROLL:
+                snprintf(buf, sizeof(buf), "%s", "scrolling line");
+                break;
+
+            case WPS_TOKEN_SUBLINE_TIMEOUT:
+                snprintf(buf, sizeof(buf), "%s: %d", "subline timeout value",
+                         wps_data->tokens[i].value.i);
+                break;
+
+            case WPS_TOKEN_SUBLINE_SEPARATOR:
+                snprintf(buf, sizeof(buf), "%s", "subline separator");
+                break;
+
+            default:
+                snprintf(buf, sizeof(buf), "%s (code: %d)", "FIXME",
+                         wps_data->tokens[i].type);
+                break;
+        }
+
+        for(j = 0; j < indent; j++) {
+            DEBUGF("\t");
+        }
+
+        DEBUGF("[%03d] = %s\n", i, buf);
+    }
+}
+
+static void print_line_info(struct wps_data *data)
+{
+    int line, subline;
+
+    int max_num_sublines = data->num_sublines[0];
+    for (line = 1; line < data->num_lines; line++)
+    {
+        if (data->num_sublines[line] > max_num_sublines)
+            max_num_sublines = data->num_sublines[line];
+    }
+
+    DEBUGF("line/subline start indexes :\n");
+    for (line = 0; line < data->num_lines; line++)
+    {
+        DEBUGF("%2d. ", line);
+        for (subline = 0; subline < max_num_sublines; subline++)
+        {
+            DEBUGF("%3d ", data->format_lines[line][subline]);
+        }
+        DEBUGF("\n");
+    }
+
+    DEBUGF("\n");
+
+    DEBUGF("subline time multipliers :\n");
+    for (line = 0; line < data->num_lines; line++)
+    {
+        DEBUGF("%2d. ", line);
+        for (subline = 0; subline < max_num_sublines; subline++)
+        {
+            DEBUGF("%3d ", data->time_mult[line][subline]);
+        }
+        DEBUGF("\n");
+    }
+
+}
+#endif
+
+bool wps_load(struct wps_data *wps_data)
+{
+    wps_parse(wps_data);
+
+#ifdef DEBUG
+    if (true)
+    {
+        dump_wps_tokens(wps_data);
+        print_line_info(wps_data);
+    }
+#endif
+
+    return true;
+}
+
+/* Extract a part from a path.
+ *
+ * buf      - buffer extract part to.
+ * buf_size - size of buffer.
+ * path     - path to extract from.
+ * level    - what to extract. 0 is file name, 1 is parent of file, 2 is
+ *            parent of parent, etc.
+ *
+ * Returns buf if the desired level was found, NULL otherwise.
+ */
+static char* get_dir(char* buf, int buf_size, const char* path, int level)
+{
+    const char* sep;
+    const char* last_sep;
+    int len;
+
+    sep = path + strlen(path);
+    last_sep = sep;
+
+    while (sep > path)
+    {
+        if ('/' == *(--sep))
+        {
+            if (!level)
+            {
+                break;
+            }
+
+            level--;
+            last_sep = sep - 1;
+        }
+    }
+
+    if (level || (last_sep <= sep))
+    {
+        return NULL;
+    }
+
+    len = MIN(last_sep - sep, buf_size - 1);
+    strncpy(buf, sep + 1, len);
+    buf[len] = 0;
+    return buf;
+}
+
+/* return the tag found at index i */
+static char *get_tag(struct wps_data *data,
+                     struct wps_state *state,
+                     int i,
+                     char *buf,
+                     int buf_size,
+                     int *intval)
+{
+    if (!data || !state)
+        return NULL;
+
+    struct mp3entry *id3;
+
+    if (data->tokens[i].next) {
+        if (!state->nid3)
+            return NULL;
+        id3 = state->nid3;
+    } else {
+        id3 = state->id3;
+    }
+
+    int limit = 1;
+    if (intval) {
+        limit = *intval;
+    }
+
+#if CONFIG_RTC
+    static struct tm* tm;
+#endif
+
+    //DEBUGF("%d\n", i);
+
+    switch (data->tokens[i].type)
+    {
+        case WPS_TOKEN_CHARACTER:
+            return &(data->tokens[i].value.c);
+
+        case WPS_TOKEN_STRING:
+            return data->tokens[i].value.s;
+
+        case WPS_TOKEN_TRACK_TIME_ELAPSED:
+            format_time(buf, buf_size,
+                        id3->elapsed + state->ff_rewind_count);
+            return buf;
+
+        case WPS_TOKEN_TRACK_TIME_REMAINING:
+            format_time(buf, buf_size,
+                        id3->length - id3->elapsed -
+                        state->ff_rewind_count);
+            return buf;
+
+        case WPS_TOKEN_TRACK_LENGTH:
+            format_time(buf, buf_size, id3->length);
+            return buf;
+
+        case WPS_TOKEN_PLAYLIST_ENTRIES:
+            snprintf(buf, buf_size, "%d", playlist_amount());
+            return buf;
+
+        case WPS_TOKEN_PLAYLIST_NAME:
+            return playlist_name(NULL, buf, buf_size);
+
+        case WPS_TOKEN_PLAYLIST_POSITION:
+            snprintf(buf, buf_size, "%d",
+                        playlist_get_display_index());
+            return buf;
+
+        case WPS_TOKEN_PLAYLIST_SHUFFLE:
+            if ( global_settings.playlist_shuffle )
+                return "s";
+            else
+                return NULL;
+            break;
+
+        case WPS_TOKEN_VOLUME:
+            snprintf(buf, buf_size, "%d", global_settings.volume);
+            if (intval)
+            {
+                *intval = limit * (global_settings.volume
+                                    - sound_min(SOUND_VOLUME))
+                                    / (sound_max(SOUND_VOLUME)
+                                        - sound_min(SOUND_VOLUME)) + 1;
+            }
+            return buf;
+
+        case WPS_TOKEN_METADATA_ARTIST:
+            return id3->artist;
+
+        case WPS_TOKEN_METADATA_COMPOSER:
+            return id3->composer;
+
+        case WPS_TOKEN_METADATA_ALBUM:
+            return id3->album;
+
+        case WPS_TOKEN_METADATA_ALBUM_ARTIST:
+            return id3->albumartist;
+
+        case WPS_TOKEN_METADATA_GENRE:
+            return id3->genre_string;
+
+        case WPS_TOKEN_METADATA_TRACK_NUMBER:
+            if (id3->track_string)
+                return id3->track_string;
+
+            if (id3->tracknum) {
+                snprintf(buf, buf_size, "%d", id3->tracknum);
+                return buf;
+            }
+            return NULL;
+
+        case WPS_TOKEN_METADATA_TRACK_TITLE:
+            return id3->title;
+
+        case WPS_TOKEN_METADATA_VERSION:
+            switch (id3->id3version)
+            {
+                case ID3_VER_1_0:
+                    return "1";
+
+                case ID3_VER_1_1:
+                    return "1.1";
+
+                case ID3_VER_2_2:
+                    return "2.2";
+
+                case ID3_VER_2_3:
+                    return "2.3";
+
+                case ID3_VER_2_4:
+                    return "2.4";
+
+                default:
+                    return NULL;
+            }
+
+        case WPS_TOKEN_METADATA_YEAR:
+            if( id3->year_string )
+                return id3->year_string;
+
+            if (id3->year) {
+                snprintf(buf, buf_size, "%d", id3->year);
+                return buf;
+            }
+            return NULL;
+
+        case WPS_TOKEN_METADATA_COMMENT:
+            return id3->comment;
+
+        case WPS_TOKEN_FILE_BITRATE:
+            if(id3->bitrate)
+                snprintf(buf, buf_size, "%d", id3->bitrate);
+            else
+                snprintf(buf, buf_size, "?");
+            return buf;
+
+        case WPS_TOKEN_FILE_CODEC:
+            if (intval)
+            {
+                if(id3->codectype == AFMT_UNKNOWN)
+                    *intval = AFMT_NUM_CODECS;
+                else
+                    *intval = id3->codectype;
+            }
+            return id3_get_codec(id3);
+
+        case WPS_TOKEN_FILE_FREQUENCY:
+            snprintf(buf, buf_size, "%ld", id3->frequency);
+            return buf;
+
+        case WPS_TOKEN_FILE_NAME:
+            if (get_dir(buf, buf_size, id3->path, 0)) {
+                /* Remove extension */
+                char* sep = strrchr(buf, '.');
+                if (NULL != sep) {
+                    *sep = 0;
+                }
+                return buf;
+            }
+            else {
+                return NULL;
+            }
+
+        case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
+            return get_dir(buf, buf_size, id3->path, 0);
+
+        case WPS_TOKEN_FILE_PATH:
+            return id3->path;
+
+        case WPS_TOKEN_FILE_SIZE:
+            snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
+            return buf;
+
+        case WPS_TOKEN_FILE_VBR:
+            return id3->vbr ? "(avg)" : NULL;
+
+        case WPS_TOKEN_FILE_DIRECTORY:
+            return get_dir(buf, buf_size, id3->path, data->tokens[i].value.i);
+
+        case WPS_TOKEN_BATTERY_PERCENT:
+        {
+            int l = battery_level();
+
+            if (intval)
+            {
+                limit = MAX(limit, 2);
+                if (l > -1) {
+                    /* First enum is used for "unknown level". */
+                    *intval = (limit - 1) * l / 100 + 2;
+                } else {
+                    *intval = 1;
+                }
+            }
+
+            if (l > -1) {
+                snprintf(buf, buf_size, "%d", l);
+                return buf;
+            } else {
+                return "?";
+            }
+        }
+
+        case WPS_TOKEN_BATTERY_VOLTS:
+        {
+            unsigned int v = battery_voltage();
+            snprintf(buf, buf_size, "%d.%02d", v/100, v%100);
+            return buf;
+        }
+
+        case WPS_TOKEN_BATTERY_TIME:
+        {
+            int t = battery_time();
+            if (t >= 0)
+                snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
+            else
+                strncpy(buf, "?h ?m", buf_size);
+            return buf;
+        }
+
+#if CONFIG_CHARGING
+        case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
+        {
+            if(charger_input_state==CHARGER)
+                return "p";
+            else
+                return NULL;
+        }
+#endif
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+        case WPS_TOKEN_BATTERY_CHARGING:
+        {
+            if (charge_state == CHARGING || charge_state == TOPOFF) {
+                return "c";
+            } else {
+                return NULL;
+            }
+        }
+#endif
+
+        case WPS_TOKEN_PLAYBACK_STATUS:
+        {
+            int status = audio_status();
+            int mode = 1;
+            if (status == AUDIO_STATUS_PLAY && \
+                !(status & AUDIO_STATUS_PAUSE))
+                mode = 2;
+            if (audio_status() & AUDIO_STATUS_PAUSE && \
+                (! status_get_ffmode()))
+                mode = 3;
+            if (status_get_ffmode() == STATUS_FASTFORWARD)
+                mode = 4;
+            if (status_get_ffmode() == STATUS_FASTBACKWARD)
+                mode = 5;
+
+            if (intval) {
+                *intval = mode;
+            }
+
+            snprintf(buf, buf_size, "%d", mode);
+            return buf;
+        }
+
+        case WPS_TOKEN_REPEAT_MODE:
+            if (intval)
+                *intval = global_settings.repeat_mode + 1;
+            snprintf(buf, buf_size, "%d", *intval);
+            return buf;
+
+#if CONFIG_RTC
+        case WPS_TOKEN_RTC:
+            tm = get_time();
+            return NULL; //rtc_string(buf, buf_size, data->tokens[i].value.s);
+
+        case WPS_TOKEN_RTC_DAY_OF_MONTH:
+            /* d: day of month (01..31) */
+            if (tm->tm_mday > 31 || tm->tm_mday < 1) return NULL;
+            snprintf(buf, buf_size, "%02d", tm->tm_mday);
+            return buf;
+
+        case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
+            /* e: day of month, blank padded ( 1..31) */
+            if (tm->tm_mday > 31 || tm->tm_mday < 1) return NULL;
+            snprintf(buf, buf_size, "%2d", tm->tm_mday);
+            return buf;
+
+        case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
+            /* H: hour (00..23) */
+            if (tm->tm_hour > 23) return NULL;
+            snprintf(buf, buf_size, "%02d", tm->tm_hour);
+            return buf;
+
+        case WPS_TOKEN_RTC_HOUR_24:
+            /* k: hour ( 0..23) */
+            if (tm->tm_hour > 23) return NULL;
+            snprintf(buf, buf_size, "%2d", tm->tm_hour);
+            return buf;
+
+        case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
+            /* I: hour (01..12) */
+            if (tm->tm_hour > 23) return NULL;
+            snprintf(buf, buf_size, "%02d",
+                     (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
+            return buf;
+
+        case WPS_TOKEN_RTC_HOUR_12:
+            /* l: hour ( 1..12) */
+            if (tm->tm_hour > 23) return NULL;
+            snprintf(buf, buf_size, "%2d",
+                     (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
+            return buf;
+
+        case WPS_TOKEN_RTC_MONTH:
+            /* m: month (01..12) */
+            if (tm->tm_mon > 11 || tm->tm_mon < 0) return NULL;
+            snprintf(buf, buf_size, "%02d", tm->tm_mon + 1);
+            return buf;
+
+        case WPS_TOKEN_RTC_MINUTE:
+            /* M: minute (00..59) */
+            if (tm->tm_min > 59 || tm->tm_min < 0) return NULL;
+            snprintf(buf, buf_size, "%02d", tm->tm_min);
+            return buf;
+
+        case WPS_TOKEN_RTC_SECOND:
+            /* S: second (00..59) */
+            if (tm->tm_sec > 59 || tm->tm_sec < 0) return NULL;
+            snprintf(buf, buf_size, "%02d", tm->tm_sec);
+            return buf;
+
+        case WPS_TOKEN_RTC_YEAR_2_DIGITS:
+            /* y: last two digits of year (00..99) */
+            snprintf(buf, buf_size, "%02d", tm->tm_year % 100);
+            return buf;
+
+        case WPS_TOKEN_RTC_YEAR_4_DIGITS:
+            /* Y: year (1970...) */
+            if (tm->tm_year > 199 || tm->tm_year < 100) return NULL;
+            snprintf(buf, buf_size, "%04d", tm->tm_year + 1900);
+            return buf;
+
+        case WPS_TOKEN_RTC_AM_PM_UPPER:
+            /* p: upper case AM or PM indicator */
+            snprintf(buf, buf_size, (tm->tm_hour/12 == 0) ? "AM" : "PM");
+            return buf;
+
+        case WPS_TOKEN_RTC_AM_PM_LOWER:
+            /* P: lower case am or pm indicator */
+            snprintf(buf, buf_size, (tm->tm_hour/12 == 0) ? "am" : "pm");
+            return buf;
+
+        case WPS_TOKEN_RTC_WEEKDAY_NAME:
+            /* a: abbreviated weekday name (Sun..Sat) */
+            if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
+            snprintf(buf, buf_size, "%s",str(dayname[tm->tm_wday]));
+            return buf;
+
+        case WPS_TOKEN_RTC_MONTH_NAME:
+            /* b: abbreviated month name (Jan..Dec) */
+            if (tm->tm_mon > 11 || tm->tm_mon < 0) return NULL;
+            snprintf(buf, buf_size, "%s",str(monthname[tm->tm_mon]));
+            return buf;
+
+        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
+            /* u: day of week (1..7); 1 is Monday */
+            if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
+            snprintf(buf, buf_size, "%1d", tm->tm_wday + 1);
+            return buf;
+
+        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
+            /* w: day of week (0..6); 0 is Sunday */
+            if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
+            snprintf(buf, buf_size, "%1d", tm->tm_wday);
+            return buf;
+#endif
+
+#ifdef HAVE_LCD_CHARCELLS
+        case WPS_TOKEN_PROGRESSBAR:
+            snprintf(buf, buf_size, "%c",
+                        data->wps_progress_pat[0]);
+            data->full_line_progressbar = 0;
+            return buf;
+
+        case WPS_TOKEN_PLAYER_PROGRESSBAR:
+            if(is_new_player()) {
+                data->full_line_progressbar = 1;
+                /* we need 11 characters (full line) for
+                    progress-bar */
+                snprintf(buf, buf_size, "           ");
+            }
+            else
+            {
+                /* Tell the user if we have an OldPlayer */
+                snprintf(buf, buf_size, " <Old LCD> ");
+            }
+            return buf;
+#endif
+
+        case WPS_TOKEN_DATABASE_PLAYCOUNT:
+            if (intval) {
+                *intval = id3->playcount + 1;
+            }
+            snprintf(buf, buf_size, "%ld", id3->playcount);
+            return buf;
+
+        case WPS_TOKEN_DATABASE_RATING:
+            if (intval) {
+                *intval = id3->rating + 1;
+            }
+            snprintf(buf, buf_size, "%d", id3->rating);
+            return buf;
+
+#if (CONFIG_CODEC == SWCODEC)
+        case WPS_TOKEN_REPLAYGAIN:
+        {
+            int val;
+
+            if (global_settings.replaygain == 0)
+                val = 1; /* off */
+            else
+            {
+                int type =
+                    get_replaygain_mode(id3->track_gain_string != NULL,
+                                        id3->album_gain_string != NULL);
+                if (type < 0)
+                    val = 6;    /* no tag */
+                else
+                    val = type + 2;
+
+                if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
+                    val += 2;
+            }
+
+            if (intval)
+                *intval = val;
+
+            switch (val)
+            {
+                case 1:
+                case 6:
+                    return "+0.00 dB";
+                    break;
+                case 2:
+                case 4:
+                    strncpy(buf, id3->track_gain_string, buf_size);
+                    break;
+                case 3:
+                case 5:
+                    strncpy(buf, id3->album_gain_string, buf_size);
+                    break;
+            }
+            return buf;
+        }
+
+        case WPS_TOKEN_SOUND_PITCH:
+            snprintf(buf, buf_size, "%d.%d",
+                    *intval / 10, *intval % 10);
+            return buf;
+
+#endif
+
+#ifdef HAS_BUTTON_HOLD
+        case WPS_TOKEN_MAIN_HOLD:
+            if (button_hold())
+                return "h";
+            else
+                return NULL;
+#endif
+#ifdef HAS_REMOTE_BUTTON_HOLD
+        case WPS_TOKEN_REMOTE_HOLD:
+            if (remote_button_hold())
+                return "r";
+            else
+                return NULL;
+#endif
+
+#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
+        case WPS_TOKEN_VLED_HDD:
+            if(led_read(HZ/2))
+                return "h";
+            else
+                return NULL;
+#endif
+
+        default:
+            return NULL;
+    }
+}
+
+/* should only be used with "index" pointing to a WPS_TOKEN_CONDITIONAL_START
+   or a WPS_TOKEN_CONDITIONAL_OPTION */
+static int find_conditional_end(struct wps_data *data, int index)
+{
+    int ret = index;
+    do
+    {
+        ret = data->tokens[ret].value.i;
+    } while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END);
+    return ret;
+}
+
+static int evaluate_conditional(struct wps_data *data,
+                                struct wps_state *state,
+                                int cond_index)
+{
+    if (!data || !state)
+        return NULL;
+
+    int ret;
+    int num_options = data->tokens[cond_index].value.i;
+    char result[128], *value;
+
+    /* find conditional start.
+       usually, cond_start = cond_index + 2,
+       but this can change if a conditional uses an unknown token,
+       so we have to be sure. */
+    int cond_start = cond_index;
+    while (data->tokens[cond_start].type != WPS_TOKEN_CONDITIONAL_START
+           && cond_start < data->num_tokens)
+    {
+        cond_start++;
+    }
+
+    if (num_options > 2)  /* enum */
+    {
+        int intval = num_options;
+        /* get_tag needs to know the number of options in the enum */
+        get_tag(data, state, cond_index + 1, result, sizeof(result), &intval);
+        /* intval is now the number of the enum option we want to read,
+           starting from 1 */
+        if (intval > num_options || intval < 1)
+            intval = num_options;
+
+        int next = cond_start;
+        int i;
+        for (i = 1; i < intval; i++)
+        {
+            next = data->tokens[next].value.i;
+        }
+        ret = next;
+    }
+    else  /* %?xx<true|false> or %?<true> */
+    {
+        value = get_tag(data, state, cond_index + 1, result, sizeof(result), NULL);
+        ret = value ? cond_start : data->tokens[cond_start].value.i;
+    }
+
+    return ret;
+}
+
+#ifdef HAVE_LCD_BITMAP
+
+static void draw_progressbar(struct gui_wps *gwps, int line)
+{
+
+    struct wps_data *data = gwps->data;
+    struct screen *display = gwps->display;
+    struct wps_state *state = gwps->state;
+    int h = font_get(FONT_UI)->height;
+
+    int sb_y;
+    if (data->progress_top < 0)
+        sb_y = line*h + display->getymargin() +
+               ((h > data->progress_height + 1)
+                ? (h - data->progress_height) / 2 : 1);
+    else
+        sb_y = data->progress_top;
+
+    if (!data->progress_end)
+        data->progress_end=display->width;
+
+    if (gwps->data->progressbar.have_bitmap_pb)
+        gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
+                                  data->progress_start, sb_y,
+                                  data->progress_end-data->progress_start,
+                                  data->progressbar.bm.height,
+                                  state->id3->length?state->id3->length:1, 0,
+                                  state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
+                                  HORIZONTAL);
+    else
+        gui_scrollbar_draw(display, data->progress_start, sb_y,
+                           data->progress_end-data->progress_start,
+                           data->progress_height,
+                           state->id3->length?state->id3->length:1, 0,
+                           state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
+                           HORIZONTAL);
+#ifdef AB_REPEAT_ENABLE
+    if ( ab_repeat_mode_enabled() )
+        ab_draw_markers(display, state->id3->length,
+                        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);
+    }
+}
+
+/* clears the area where the image was shown */
+static void clear_image_pos(struct gui_wps *gwps, int n)
+{
+    if(!gwps)
+        return;
+    struct wps_data *data = gwps->data;
+    gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+    gwps->display->fillrect(data->img[n].x, data->img[n].y,
+                            data->img[n].bm.width, data->img[n].bm.height);
+    gwps->display->set_drawmode(DRMODE_SOLID);
+}
+
+static void wps_draw_image(struct gui_wps *gwps, int n)
+{
+    struct screen *display = gwps->display;
+    struct wps_data *data = gwps->data;
+    if(data->img[n].always_display)
+        display->set_drawmode(DRMODE_FG);
+    else
+        display->set_drawmode(DRMODE_SOLID);
+
+#if LCD_DEPTH > 1
+    if(data->img[n].bm.format == FORMAT_MONO) {
+#endif
+        display->mono_bitmap(data->img[n].bm.data, data->img[n].x,
+                             data->img[n].y, data->img[n].bm.width,
+                             data->img[n].bm.height);
+#if LCD_DEPTH > 1
+    } else {
+        display->transparent_bitmap((fb_data *)data->img[n].bm.data,
+                                    data->img[n].x,
+                                    data->img[n].y, data->img[n].bm.width,
+                                    data->img[n].bm.height);
+    }
+#endif
+}
+
+static void wps_display_images(struct gui_wps *gwps, bool always)
+{
+    if(!gwps || !gwps->data || !gwps->display) return;
+    int n;
+    struct wps_data *data = gwps->data;
+    struct screen *display = gwps->display;
+    for (n = 0; n < MAX_IMAGES; n++) {
+        if (data->img[n].loaded) {
+            if( (!always && data->img[n].display)
+                || (always && data->img[n].always_display) )
+                wps_draw_image(gwps, n);
+        }
+    }
+    display->set_drawmode(DRMODE_SOLID);
+}
+
+static void write_line(struct screen *display,
+                       struct align_pos *format_align,
+                       int line,
+                       bool scroll)
+{
+
+    int left_width,   left_xpos;
+    int center_width, center_xpos;
+    int right_width,  right_xpos;
+    int ypos;
+    int space_width;
+    int string_height;
+
+    /* calculate different string sizes and positions */
+    display->getstringsize((unsigned char *)" ", &space_width, &string_height);
+    if (format_align->left != 0) {
+        display->getstringsize((unsigned char *)format_align->left,
+                                &left_width, &string_height);
+    }
+    else {
+        left_width = 0;
+    }
+    left_xpos = 0;
+
+    if (format_align->center != 0) {
+        display->getstringsize((unsigned char *)format_align->center,
+                                &center_width, &string_height);
+    }
+    else {
+        center_width = 0;
+    }
+    center_xpos=(display->width - center_width) / 2;
+
+    if (format_align->right != 0) {
+        display->getstringsize((unsigned char *)format_align->right,
+                                &right_width, &string_height);
+    }
+    else {
+        right_width = 0;
+    }
+    right_xpos = (display->width - right_width);
+
+    /* Checks for overlapping strings.
+        If needed the overlapping strings will be merged, separated by a
+        space */
+
+    /* CASE 1: left and centered string overlap */
+    /* there is a left string, need to merge left and center */
+    if ((left_width != 0 && center_width != 0) &&
+        (left_xpos + left_width + space_width > center_xpos)) {
+        /* replace the former separator '\0' of left and
+            center string with a space */
+        *(--format_align->center) = ' ';
+        /* calculate the new width and position of the merged string */
+        left_width = left_width + space_width + center_width;
+        left_xpos = 0;
+        /* there is no centered string anymore */
+        center_width = 0;
+    }
+    /* there is no left string, move center to left */
+    if ((left_width == 0 && center_width != 0) &&
+        (left_xpos + left_width > center_xpos)) {
+        /* move the center string to the left string */
+        format_align->left = format_align->center;
+        /* calculate the new width and position of the string */
+        left_width = center_width;
+        left_xpos = 0;
+        /* there is no centered string anymore */
+        center_width = 0;
+    }
+
+    /* CASE 2: centered and right string overlap */
+    /* there is a right string, need to merge center and right */
+    if ((center_width != 0 && right_width != 0) &&
+        (center_xpos + center_width + space_width > right_xpos)) {
+        /* replace the former separator '\0' of center and
+            right string with a space */
+        *(--format_align->right) = ' ';
+        /* move the center string to the right after merge */
+        format_align->right = format_align->center;
+        /* calculate the new width and position of the merged string */
+        right_width = center_width + space_width + right_width;
+        right_xpos = (display->width - right_width);
+        /* there is no centered string anymore */
+        center_width = 0;
+    }
+    /* there is no right string, move center to right */
+    if ((center_width != 0 && right_width == 0) &&
+        (center_xpos + center_width > right_xpos)) {
+        /* move the center string to the right string */
+        format_align->right = format_align->center;
+        /* calculate the new width and position of the string */
+        right_width = center_width;
+        right_xpos = (display->width - right_width);
+        /* there is no centered string anymore */
+        center_width = 0;
+    }
+
+    /* CASE 3: left and right overlap
+        There is no center string anymore, either there never
+        was one or it has been merged in case 1 or 2 */
+    /* there is a left string, need to merge left and right */
+    if ((left_width != 0 && center_width == 0 && right_width != 0) &&
+        (left_xpos + left_width + space_width > right_xpos)) {
+        /* replace the former separator '\0' of left and
+            right string with a space */
+        *(--format_align->right) = ' ';
+        /* calculate the new width and position of the string */
+        left_width = left_width + space_width + right_width;
+        left_xpos = 0;
+        /* there is no right string anymore */
+        right_width = 0;
+    }
+    /* there is no left string, move right to left */
+    if ((left_width == 0 && center_width == 0 && right_width != 0) &&
+        (left_xpos + left_width > right_xpos)) {
+        /* move the right string to the left string */
+        format_align->left = format_align->right;
+        /* calculate the new width and position of the string */
+        left_width = right_width;
+        left_xpos = 0;
+        /* there is no right string anymore */
+        right_width = 0;
+    }
+
+    ypos = (line * string_height) + display->getymargin();
+
+
+    if (scroll && left_width > display->width)
+    {
+        display->puts_scroll(0, line,
+                             (unsigned char *)format_align->left);
+    }
+    else
+    {
+        /* clear the line first */
+        display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+        display->fillrect(0, ypos, display->width, string_height);
+        display->set_drawmode(DRMODE_SOLID);
+
+        /* Nasty hack: we output an empty scrolling string,
+        which will reset the scroller for that line */
+        //display->puts_scroll(0, line, (unsigned char *)"");
+
+        /* print aligned strings */
+        if (left_width != 0)
+        {
+            display->putsxy(left_xpos, ypos,
+                            (unsigned char *)format_align->left);
+        }
+        if (center_width != 0)
+        {
+            display->putsxy(center_xpos, ypos,
+                            (unsigned char *)format_align->center);
+        }
+        if (right_width != 0)
+        {
+            display->putsxy(right_xpos, ypos,
+                            (unsigned char *)format_align->right);
+        }
+    }
+}
+
+#else
+
+static bool draw_player_progress(struct gui_wps *gwps)
+{
+    char player_progressbar[7];
+    char binline[36];
+    int songpos = 0;
+    int i,j;
+    struct wps_state *state = gwps->state;
+    struct screen *display = gwps->display;
+    if (!state->id3)
+        return false;
+
+    memset(binline, 1, sizeof binline);
+    memset(player_progressbar, 1, sizeof player_progressbar);
+
+    if(state->id3->elapsed >= state->id3->length)
+        songpos = 0;
+    else
+    {
+        if(state->wps_time_countup == false)
+            songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) /
+                            state->id3->length;
+        else
+            songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) /
+                            state->id3->length;
+    }
+    for (i=0; i < songpos; i++)
+        binline[i] = 0;
+
+    for (i=0; i<=6; i++) {
+        for (j=0;j<5;j++) {
+            player_progressbar[i] <<= 1;
+            player_progressbar[i] += binline[i*5+j];
+        }
+    }
+    display->define_pattern(gwps->data->wps_progress_pat[0], player_progressbar);
+    return true;
+}
+
+static char map_fullbar_char(char ascii_val)
+{
+    if (ascii_val >= '0' && ascii_val <= '9') {
+        return(ascii_val - '0');
+    }
+    else if (ascii_val == ':') {
+        return(10);
+    }
+    else
+        return(11); /* anything besides a number or ':' is mapped to <blank> */
+}
+
+static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
+{
+    int i,j,lcd_char_pos;
+
+    char player_progressbar[7];
+    char binline[36];
+    static const char numbers[12][4][3]={
+        {{1,1,1},{1,0,1},{1,0,1},{1,1,1}},/*0*/
+        {{0,1,0},{1,1,0},{0,1,0},{0,1,0}},/*1*/
+        {{1,1,1},{0,0,1},{0,1,0},{1,1,1}},/*2*/
+        {{1,1,1},{0,0,1},{0,1,1},{1,1,1}},/*3*/
+        {{1,0,0},{1,1,0},{1,1,1},{0,1,0}},/*4*/
+        {{1,1,1},{1,1,0},{0,0,1},{1,1,0}},/*5*/
+        {{1,1,1},{1,0,0},{1,1,1},{1,1,1}},/*6*/
+        {{1,1,1},{0,0,1},{0,1,0},{1,0,0}},/*7*/
+        {{1,1,1},{1,1,1},{1,0,1},{1,1,1}},/*8*/
+        {{1,1,1},{1,1,1},{0,0,1},{1,1,1}},/*9*/
+        {{0,0,0},{0,1,0},{0,0,0},{0,1,0}},/*:*/
+        {{0,0,0},{0,0,0},{0,0,0},{0,0,0}} /*<blank>*/
+    };
+
+    int songpos = 0;
+    int digits[6];
+    int time;
+    char timestr[7];
+    
+    struct wps_state *state = gwps->state;
+    struct screen *display = gwps->display;
+    struct wps_data *data = gwps->data;
+
+    for (i=0; i < buf_size; i++)
+        buf[i] = ' ';
+
+    if(state->id3->elapsed >= state->id3->length)
+        songpos = 55;
+    else {
+        if(state->wps_time_countup == false)
+            songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) /
+                                state->id3->length;
+        else
+            songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) /
+                                state->id3->length;
+    }
+
+    time=(state->id3->elapsed + state->ff_rewind_count);
+
+    memset(timestr, 0, sizeof(timestr));
+    format_time(timestr, sizeof(timestr), time);
+    for(lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
+        digits[lcd_char_pos] = map_fullbar_char(timestr[lcd_char_pos]);
+    }
+
+    /* build the progressbar-icons */
+    for (lcd_char_pos=0; lcd_char_pos<6; lcd_char_pos++) {
+        memset(binline, 0, sizeof binline);
+        memset(player_progressbar, 0, sizeof player_progressbar);
+
+        /* make the character (progressbar & digit)*/
+        for (i=0; i<7; i++) {
+            for (j=0;j<5;j++) {
+                /* make the progressbar */
+                if (lcd_char_pos==(songpos/5)) {
+                    /* partial */
+                    if ((j<(songpos%5))&&(i>4))
+                        binline[i*5+j] = 1;
+                    else
+                        binline[i*5+j] = 0;
+                }
+                else {
+                    if (lcd_char_pos<(songpos/5)) {
+                        /* full character */
+                        if (i>4)
+                            binline[i*5+j] = 1;
+                    }
+                }
+                /* insert the digit */
+                if ((j<3)&&(i<4)) {
+                    if (numbers[digits[lcd_char_pos]][i][j]==1)
+                        binline[i*5+j] = 1;
+                }
+            }
+        }
+
+        for (i=0; i<=6; i++) {
+            for (j=0;j<5;j++) {
+                player_progressbar[i] <<= 1;
+                player_progressbar[i] += binline[i*5+j];
+            }
+        }
+
+        display->define_pattern(data->wps_progress_pat[lcd_char_pos+1],
+                                player_progressbar);
+        buf[lcd_char_pos]=data->wps_progress_pat[lcd_char_pos+1];
+
+    }
+
+    /* make rest of the progressbar if necessary */
+    if (songpos/5>5) {
+
+        /* set the characters positions that use the full 5 pixel wide bar */
+        for (lcd_char_pos=6; lcd_char_pos < (songpos/5); lcd_char_pos++)
+            buf[lcd_char_pos] = 0x86; /* '_' */
+
+        /* build the partial bar character for the tail character position */
+        memset(binline, 0, sizeof binline);
+        memset(player_progressbar, 0, sizeof player_progressbar);
+
+        for (i=5; i<7; i++) {
+            for (j=0;j<5;j++) {
+                if (j<(songpos%5)) {
+                    binline[i*5+j] = 1;
+                }
+            }
+        }
+
+        for (i=0; i<7; i++) {
+            for (j=0;j<5;j++) {
+                player_progressbar[i] <<= 1;
+                player_progressbar[i] += binline[i*5+j];
+            }
+        }
+
+        display->define_pattern(data->wps_progress_pat[7],player_progressbar);
+
+        buf[songpos/5]=data->wps_progress_pat[7];
+    }
+}
+#endif
+
+
+
+/* Get the current (sub)line and return whether it should be updated.
+   Cases were it shouldn't include lines with the peakmeter or
+   progressbar. Drawing them is handled by this function. */
+static bool get_line(struct gui_wps *gwps,
+                     int line,
+                     int subline,
+                     struct align_pos *align,
+                     char *linebuf,
+                     int linebuf_size)
+{
+    struct wps_data *data = gwps->data;
+    struct wps_state *state = gwps->state;
+
+    char temp_buf[128];
+    char *buf = linebuf;  /* will always point to the writing position */
+    char *linebuf_end = linebuf + linebuf_size - 1;
+
+    /* alignment-related variables */
+    int cur_align;
+    char* cur_align_start;
+    cur_align_start = buf;
+    cur_align = WPS_ALIGN_LEFT;
+    align->left = 0;
+    align->center = 0;
+    align->right = 0;
+
+    int i = data->format_lines[line][subline];
+
+    while (data->tokens[i].type != WPS_TOKEN_EOL
+           && data->tokens[i].type != WPS_TOKEN_SUBLINE_SEPARATOR
+           && i < data->num_tokens)
+    {
+        switch(data->tokens[i].type)
+        {
+            case WPS_TOKEN_CONDITIONAL:
+                /* place ourselves in the right conditional case */
+                i = evaluate_conditional(data, state, i);
+                break;
+
+            case WPS_TOKEN_CONDITIONAL_OPTION:
+                /* we've finished in the curent conditional case,
+                    skip to the end of the conditional structure */
+                i = find_conditional_end(data, i);
+                break;
+
+#ifdef HAVE_LCD_BITMAP
+            case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
+            {
+                struct gui_img *img = data->img;
+                int n;
+
+                /* get the image ID */
+                n = data->tokens[i].value.c;
+                if(n >= 'a' && n <= 'z')
+                    n -= 'a';
+                if(n >= 'A' && n <= 'Z')
+                    n = n - 'A' + 26;
+                if (n >= 0 && n < MAX_IMAGES && img[n].loaded) {
+                    img[n].display = true;
+                }
+                break;
+            }
+
+            case WPS_TOKEN_ALIGN_LEFT:
+            case WPS_TOKEN_ALIGN_CENTER:
+            case WPS_TOKEN_ALIGN_RIGHT:
+                /* remember where the current aligned text started */
+                switch (cur_align)
+                {
+                    case WPS_ALIGN_LEFT:
+                        align->left = cur_align_start;
+                        break;
+
+                    case WPS_ALIGN_CENTER:
+                        align->center = cur_align_start;
+                        break;
+
+                    case WPS_ALIGN_RIGHT:
+                        align->right = cur_align_start;
+                        break;
+                }
+                /* start a new alignment */
+                switch (data->tokens[i].type)
+                {
+                    case WPS_TOKEN_ALIGN_LEFT:
+                        cur_align = WPS_ALIGN_LEFT;
+                        break;
+                    case WPS_TOKEN_ALIGN_CENTER:
+                        cur_align = WPS_ALIGN_CENTER;
+                        break;
+                    case WPS_TOKEN_ALIGN_RIGHT:
+                        cur_align = WPS_ALIGN_RIGHT;
+                        break;
+                    default:
+                        break;
+                }
+                *buf++ = 0;
+                cur_align_start = buf;
+                break;
+#endif
+
+            default:
+            {
+                /* get the value of the tag and copy it to the buffer */
+                char *value = get_tag(data, state, i, temp_buf,
+                                        sizeof(temp_buf), NULL);
+                if (value)
+                {
+                    while (*value && (buf < linebuf_end))
+                        *buf++ = *value++;
+                }
+                break;
+            }
+        }
+        i++;
+    }
+
+#ifdef HAVE_LCD_BITMAP
+    /* close the current alignment */
+    switch (cur_align)
+    {
+        case WPS_ALIGN_LEFT:
+            align->left = cur_align_start;
+            break;
+
+        case WPS_ALIGN_CENTER:
+            align->center = cur_align_start;
+            break;
+
+        case WPS_ALIGN_RIGHT:
+            align->right = cur_align_start;
+            break;
+    }
+#endif
+
+    if (i == data->format_lines[line][subline]) {
+        return false;
+    } else {
+        return true;
+    }
+
+}
+
+static void get_curr_sublines(struct wps_data *data)
+{
+    int line, search, search_start;
+    bool reset_subline;
+    bool new_subline_refresh;
+    bool only_one_subline;
+
+    for (line = 0; line < data->num_lines; line++)
+    {
+        reset_subline = (data->curr_subline[line] == SUBLINE_RESET);
+        new_subline_refresh = false;
+        only_one_subline = false;
+
+        /* if time to advance to next sub-line  */
+        if (TIME_AFTER(current_tick, data->subline_expire_time[line] - 1) ||
+            reset_subline)
+        {
+            /* search all sublines until the next subline with time > 0
+                is found or we get back to the subline we started with */
+            if (reset_subline)
+                search_start = 0;
+            else
+                search_start = data->curr_subline[line];
+            for (search=0; search < WPS_MAX_SUBLINES; search++)
+            {
+                data->curr_subline[line]++;
+
+                /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
+                if ((!data->format_lines[line][data->curr_subline[line]]) ||
+                    (data->curr_subline[line] == WPS_MAX_SUBLINES))
+                {
+                    if (data->curr_subline[line] == 1)
+                        only_one_subline = true;
+                    data->curr_subline[line] = 0;
+                }
+
+                /* if back where we started after search or
+                    only one subline is defined on the line */
+                if (((search > 0) && (data->curr_subline[line] == search_start)) ||
+                    only_one_subline)
+                {
+                    /* no other subline with a time > 0 exists */
+                    data->subline_expire_time[line] = (reset_subline?
+                        current_tick : data->subline_expire_time[line]) + 100 * HZ;
+                    break;
+                }
+                else
+                {
+                    /* only use this subline if subline time > 0 */
+                    if (data->time_mult[line][data->curr_subline[line]] > 0)
+                    {
+                        new_subline_refresh = true;
+                        data->subline_expire_time[line] = (reset_subline ?
+                            current_tick : data->subline_expire_time[line]) +
+                            BASE_SUBLINE_TIME * data->time_mult[line][data->curr_subline[line]];
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+bool gui_wps_refresh(struct gui_wps *gwps,
+                     int ffwd_offset,
+                     unsigned char refresh_mode)
+{
+    struct wps_data *data = gwps->data;
+    struct screen *display = gwps->display;
+    struct wps_state *state = gwps->state;
+
+    int line;
+    unsigned char flags;
+    char linebuf[MAX_PATH];
+    struct align_pos align;
+
+    /* FF/RW offset */
+    state->ff_rewind_count = ffwd_offset;
+
+#ifdef HAVE_LCD_BITMAP
+    /* statusbar */
+    gui_wps_statusbar_draw(gwps, true);
+
+    /* Set images to not to be displayed.
+       The ones that need to be displayed will be activated
+       when encountered by get_line. */
+    int i;
+    for (i = 0; i < MAX_IMAGES; i++) {
+        data->img[i].display = false;
+    }
+#endif
+
+    /* reset to first subline if refresh all flag is set */
+    if (refresh_mode == WPS_REFRESH_ALL) {
+        for (line = 0; line < data->num_lines; line++) {
+            data->curr_subline[line] = SUBLINE_RESET;
+        }
+    }
+    get_curr_sublines(data);
+
+    for (line = 0; line < data->num_lines; line++)
+    {
+        memset(linebuf, 0, sizeof(linebuf));
+        flags = data->line_type[line][data->curr_subline[line]];
+
+        if (refresh_mode == WPS_REFRESH_ALL || flags & refresh_mode)
+        {
+            get_line(gwps, line, data->curr_subline[line],
+                     &align, linebuf, sizeof(linebuf));
+
+#ifdef HAVE_LCD_BITMAP
+            /* calculate alignment and draw the strings */
+            write_line(display, &align, line, flags & WPS_REFRESH_SCROLL);
+#else
+            if (flags & WPS_REFRESH_SCROLL)
+                display->puts_scroll(0, line, linebuf);
+            else
+                display->puts(0, line, linebuf);
+#endif
+        }
+
+#ifdef HAVE_LCD_BITMAP
+        /* progressbar */
+        if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
+            draw_progressbar(gwps, line);
+        }
+
+        /* peakmeter */
+        if (flags & refresh_mode & WPS_REFRESH_PEAK_METER)
+        {
+            int h = font_get(FONT_UI)->height;
+            if (data->peak_meter_y < 0) {
+                data->peak_meter_y = display->getymargin() + line * h;
+            }
+            peak_meter_screen(display, 0, data->peak_meter_y,
+                            MIN(h, display->height - data->peak_meter_y));
+        }
+#else
+        /* progressbar */
+        if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
+            if (data->full_line_progressbar)
+                draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
+            else
+                draw_player_progress(gwps);
+        }
+#endif
+
+    }
+
+#ifdef HAVE_LCD_BITMAP
+    wps_display_images(gwps, false);
+    wps_display_images(gwps, true);
+    display->update();
+#endif
+
+#if CONFIG_BACKLIGHT
+    if (global_settings.caption_backlight && state->id3) {
+        /* turn on backlight n seconds before track ends, and turn it off n
+           seconds into the new track. n == backlight_timeout, or 5s */
+        int n = backlight_timeout_value[global_settings.backlight_timeout] 
+              * 1000;
+
+        if ( n < 1000 )
+            n = 5000; /* use 5s if backlight is always on or off */
+
+        if (((state->id3->elapsed < 1000) ||
+            ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
+            (state->paused == false))
+            backlight_on();
+    }
+#endif
+#ifdef HAVE_REMOTE_LCD
+    if (global_settings.remote_caption_backlight && state->id3) {
+        /* turn on remote backlight n seconds before track ends, and turn it
+           off n seconds into the new track. n == remote_backlight_timeout,
+           or 5s */
+        int n = backlight_timeout_value[global_settings.remote_backlight_timeout]
+              * 1000;
+
+        if ( n < 1000 )
+            n = 5000; /* use 5s if backlight is always on or off */
+
+        if (((state->id3->elapsed < 1000) ||
+            ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
+            (state->paused == false))
+            remote_backlight_on();
+    }
+#endif
+
+    return true;
+}
Index: apps/gui/wps.h
===================================================================
--- apps/gui/wps.h	(révision 0)
+++ apps/gui/wps.h	(révision 0)
@@ -0,0 +1,185 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
+ *
+ * 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 _WPS_H_
+#define _WPS_H_
+
+#include <stdbool.h>
+#include "gwps.h"
+
+
+typedef int (*wps_tag_parse_func)(const char *wps_token, struct wps_data *wps_data);
+
+struct wps_tag {
+    const char name[3];
+    enum wps_token_type type;
+    unsigned char refresh_type;
+    wps_tag_parse_func parse_func;
+};
+
+/* prototypes of all special parse functions */
+static int parse_subline_timeout(const char *wps_token, struct wps_data *wps_data);
+static int parse_progressbar(const char *wps_token, struct wps_data *wps_data);
+static int parse_dir_level(const char *wps_token, struct wps_data *wps_data);
+#ifdef HAVE_LCD_BITMAP
+static int parse_peakmeter(const char *wps_token, struct wps_data *wps_data);
+static int parse_image_display(const char *wps_token, struct wps_data *wps_data);
+#endif
+#if CONFIG_RTC
+static int parse_rtc_format(const char *wps_token, struct wps_data *wps_data);
+#endif
+
+
+/* array of available tags - those with more characters have to go first
+ * (e.g. "xl" and "xd" before "x") */
+static struct wps_tag all_tags[] = {
+
+#ifdef HAVE_LCD_BITMAP
+    { "ac",  WPS_TOKEN_ALIGN_CENTER,             0,                   NULL },
+    { "al",  WPS_TOKEN_ALIGN_LEFT,               0,                   NULL },
+    { "ar",  WPS_TOKEN_ALIGN_RIGHT,              0,                   NULL },
+#endif
+
+    { "bl",  WPS_TOKEN_BATTERY_PERCENT,          WPS_REFRESH_DYNAMIC, NULL },
+    { "bv",  WPS_TOKEN_BATTERY_VOLTS,            WPS_REFRESH_DYNAMIC, NULL },
+    { "bt",  WPS_TOKEN_BATTERY_TIME,             WPS_REFRESH_DYNAMIC, NULL },
+    { "bs",  WPS_TOKEN_BATTERY_SLEEPTIME,        WPS_REFRESH_DYNAMIC, NULL },
+#if CONFIG_CHARGING >= CHARGING_MONITOR
+    { "bc",  WPS_TOKEN_BATTERY_CHARGING,         WPS_REFRESH_DYNAMIC, NULL },
+#endif
+#if CONFIG_CHARGING
+    { "bp",  WPS_TOKEN_BATTERY_CHARGER_CONNECTED,WPS_REFRESH_DYNAMIC, NULL },
+#endif
+
+#if CONFIG_RTC
+    { "c",   WPS_TOKEN_RTC,                      WPS_REFRESH_DYNAMIC, parse_rtc_format },
+#endif
+
+    /* current file */
+    { "fb",  WPS_TOKEN_FILE_BITRATE,             WPS_REFRESH_STATIC,  NULL },
+    { "fc",  WPS_TOKEN_FILE_CODEC,               WPS_REFRESH_STATIC,  NULL },
+    { "ff",  WPS_TOKEN_FILE_FREQUENCY,           WPS_REFRESH_STATIC,  NULL },
+    { "fm",  WPS_TOKEN_FILE_NAME_WITH_EXTENSION, WPS_REFRESH_STATIC,  NULL },
+    { "fn",  WPS_TOKEN_FILE_NAME,                WPS_REFRESH_STATIC,  NULL },
+    { "fp",  WPS_TOKEN_FILE_PATH,                WPS_REFRESH_STATIC,  NULL },
+    { "fs",  WPS_TOKEN_FILE_SIZE,                WPS_REFRESH_STATIC,  NULL },
+    { "fv",  WPS_TOKEN_FILE_VBR,                 WPS_REFRESH_STATIC,  NULL },
+    { "d",   WPS_TOKEN_FILE_DIRECTORY,           WPS_REFRESH_STATIC,  parse_dir_level },
+
+    /* next file */
+    { "Fb",  WPS_TOKEN_FILE_BITRATE,             WPS_REFRESH_DYNAMIC, NULL },
+    { "Fc",  WPS_TOKEN_FILE_CODEC,               WPS_REFRESH_DYNAMIC, NULL },
+    { "Ff",  WPS_TOKEN_FILE_FREQUENCY,           WPS_REFRESH_DYNAMIC, NULL },
+    { "Fm",  WPS_TOKEN_FILE_NAME_WITH_EXTENSION, WPS_REFRESH_DYNAMIC, NULL },
+    { "Fn",  WPS_TOKEN_FILE_NAME,                WPS_REFRESH_DYNAMIC, NULL },
+    { "Fp",  WPS_TOKEN_FILE_PATH,                WPS_REFRESH_DYNAMIC, NULL },
+    { "Fs",  WPS_TOKEN_FILE_SIZE,                WPS_REFRESH_DYNAMIC, NULL },
+    { "Fv",  WPS_TOKEN_FILE_VBR,                 WPS_REFRESH_DYNAMIC, NULL },
+    { "D",   WPS_TOKEN_FILE_DIRECTORY,           WPS_REFRESH_DYNAMIC, parse_dir_level },
+
+    /* current metadata */
+    { "ia",  WPS_TOKEN_METADATA_ARTIST,          WPS_REFRESH_STATIC,  NULL },
+    { "ic",  WPS_TOKEN_METADATA_COMPOSER,        WPS_REFRESH_STATIC,  NULL },
+    { "id",  WPS_TOKEN_METADATA_ALBUM,           WPS_REFRESH_STATIC,  NULL },
+    { "iA",  WPS_TOKEN_METADATA_ALBUM_ARTIST,    WPS_REFRESH_STATIC,  NULL },
+    { "ig",  WPS_TOKEN_METADATA_GENRE,           WPS_REFRESH_STATIC,  NULL },
+    { "in",  WPS_TOKEN_METADATA_TRACK_NUMBER,    WPS_REFRESH_STATIC,  NULL },
+    { "it",  WPS_TOKEN_METADATA_TRACK_TITLE,     WPS_REFRESH_STATIC,  NULL },
+    { "iv",  WPS_TOKEN_METADATA_VERSION,         WPS_REFRESH_STATIC,  NULL },
+    { "iy",  WPS_TOKEN_METADATA_YEAR,            WPS_REFRESH_STATIC,  NULL },
+    { "iC",  WPS_TOKEN_METADATA_COMMENT,         WPS_REFRESH_DYNAMIC, NULL },
+
+    /* next metadata */
+    { "Ia",  WPS_TOKEN_METADATA_ARTIST,          WPS_REFRESH_DYNAMIC, NULL },
+    { "Ic",  WPS_TOKEN_METADATA_COMPOSER,        WPS_REFRESH_DYNAMIC, NULL },
+    { "Id",  WPS_TOKEN_METADATA_ALBUM,           WPS_REFRESH_DYNAMIC, NULL },
+    { "IA",  WPS_TOKEN_METADATA_ALBUM_ARTIST,    WPS_REFRESH_STATIC,  NULL },
+    { "Ig",  WPS_TOKEN_METADATA_GENRE,           WPS_REFRESH_DYNAMIC, NULL },
+    { "In",  WPS_TOKEN_METADATA_TRACK_NUMBER,    WPS_REFRESH_DYNAMIC, NULL },
+    { "It",  WPS_TOKEN_METADATA_TRACK_TITLE,     WPS_REFRESH_DYNAMIC, NULL },
+    { "Iv",  WPS_TOKEN_METADATA_VERSION,         WPS_REFRESH_DYNAMIC, NULL },
+    { "Iy",  WPS_TOKEN_METADATA_YEAR,            WPS_REFRESH_DYNAMIC, NULL },
+    { "IC",  WPS_TOKEN_METADATA_COMMENT,         WPS_REFRESH_DYNAMIC, NULL },
+
+#if (CONFIG_CODEC == SWCODEC)
+    { "Sp",  WPS_TOKEN_SOUND_PITCH,              WPS_REFRESH_DYNAMIC, NULL },
+#endif
+
+#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
+    { "lh",  WPS_TOKEN_VLED_HDD,                 WPS_REFRESH_DYNAMIC, NULL },
+#endif
+
+#ifdef HAS_BUTTON_HOLD
+    { "mh",  WPS_TOKEN_MAIN_HOLD,                WPS_REFRESH_DYNAMIC, NULL },
+#endif
+#ifdef HAS_REMOTE_BUTTON_HOLD
+    { "mr",  WPS_TOKEN_REMOTE_HOLD,              WPS_REFRESH_DYNAMIC, NULL },
+#endif
+
+    { "mm",  WPS_TOKEN_REPEAT_MODE,              WPS_REFRESH_DYNAMIC, NULL },
+    { "mp",  WPS_TOKEN_PLAYBACK_STATUS,          WPS_REFRESH_DYNAMIC, NULL },
+
+#ifdef HAVE_LCD_BITMAP
+    { "pm",  WPS_TOKEN_PEAKMETER,                WPS_REFRESH_PEAK_METER,
+                                                           parse_peakmeter },
+#endif
+
+    { "pb",  WPS_TOKEN_PROGRESSBAR,              WPS_REFRESH_PLAYER_PROGRESS,
+                                                         parse_progressbar },
+
+    { "pf",  WPS_TOKEN_PLAYER_PROGRESSBAR,       WPS_REFRESH_PLAYER_PROGRESS,
+                                                                      NULL },
+
+    { "pv",  WPS_TOKEN_VOLUME,                   WPS_REFRESH_DYNAMIC, NULL },
+
+    { "pc",  WPS_TOKEN_TRACK_TIME_ELAPSED,       WPS_REFRESH_DYNAMIC, NULL },
+    { "pr",  WPS_TOKEN_TRACK_TIME_REMAINING,     WPS_REFRESH_DYNAMIC, NULL },
+    { "pt",  WPS_TOKEN_TRACK_LENGTH,             WPS_REFRESH_STATIC,  NULL },
+
+    { "pp",  WPS_TOKEN_PLAYLIST_POSITION,        WPS_REFRESH_STATIC,  NULL },
+    { "pe",  WPS_TOKEN_PLAYLIST_ENTRIES,         WPS_REFRESH_STATIC,  NULL },
+    { "pn",  WPS_TOKEN_PLAYLIST_NAME,            WPS_REFRESH_STATIC,  NULL },
+    { "ps",  WPS_TOKEN_PLAYLIST_SHUFFLE,         WPS_REFRESH_DYNAMIC, NULL },
+
+    { "rp",  WPS_TOKEN_DATABASE_PLAYCOUNT,       WPS_REFRESH_DYNAMIC, NULL },
+    { "rr",  WPS_TOKEN_DATABASE_RATING,          WPS_REFRESH_DYNAMIC, NULL },
+#if CONFIG_CODEC == SWCODEC
+    { "rg",  WPS_TOKEN_REPLAYGAIN,               WPS_REFRESH_STATIC,  NULL },
+#endif
+
+    { "s",   WPS_TOKEN_SCROLL,                   WPS_REFRESH_SCROLL,  NULL },
+    { "t",   WPS_TOKEN_SUBLINE_TIMEOUT,          0, parse_subline_timeout },
+
+#ifdef HAVE_LCD_BITMAP
+//     { "we",  WPS_TOKEN_STATUSBAR_ENABLED,         parse_eat_eol },
+//     { "wd",  WPS_TOKEN_STATUSBAR_DISABLED,        parse_eat_eol },
+
+    { "xd",  WPS_TOKEN_IMAGE_PRELOAD_DISPLAY,    0, parse_image_display },
+//     { "xl",  WPS_TOKEN_IMAGE_PRELOAD,             parse_image_preload },
+//     { "x",   WPS_TOKEN_IMAGE_DISPLAY,             parse_image_preload },
+//     { "P",   WPS_TOKEN_IMAGE_PROGRESS_BAR,        parse_image_preload },
+// #if LCD_DEPTH > 1
+//     { "X",   WPS_TOKEN_IMAGE_BACKDROP,            parse_image_preload },
+// #endif
+#endif
+
+    { "\0",  WPS_TOKEN_UNKNOWN, 0, NULL }
+};
+
+#endif
Index: apps/gui/gwps.c
===================================================================
--- apps/gui/gwps.c	(révision 12842)
+++ apps/gui/gwps.c	(copie de travail)
@@ -81,7 +81,6 @@
 /* connects a wps with a statusbar*/
 static void gui_wps_set_statusbar(struct gui_wps *gui_wps, struct gui_statusbar *statusbar);
 
-
 #ifdef HAVE_LCD_BITMAP
 static void gui_wps_set_margin(struct gui_wps *gwps)
 {
@@ -674,7 +673,7 @@
 /* wps_data*/
 #ifdef HAVE_LCD_BITMAP
 /* Clear the WPS image cache */
-static void wps_clear(struct wps_data *data )
+static void wps_images_clear(struct wps_data *data )
 {
     int i;
     /* set images to unloaded and not displayed */
@@ -688,14 +687,14 @@
     data->progressbar.have_bitmap_pb=false;
 }
 #else
-#define wps_clear(a)
+#define wps_images_clear(a)
 #endif
 
 /* initial setup of wps_data */
 void wps_data_init(struct wps_data *wps_data)
 {
 #ifdef HAVE_LCD_BITMAP
-    wps_clear(wps_data);
+    wps_images_clear(wps_data);
 #else /* HAVE_LCD_CHARCELLS */
     {
         int i;
@@ -707,12 +706,13 @@
     wps_data->format_buffer[0] = '\0';
     wps_data->wps_loaded = false;
     wps_data->peak_meter_enabled = false;
+    wps_data->peak_meter_y = -1;
 }
 
 static void wps_reset(struct wps_data *data)
 {
+    memset(data, 0, sizeof(*data));
     data->wps_loaded = false;
-    memset(&data->format_buffer, 0, sizeof data->format_buffer);
     wps_data_init(data);
 }
 
@@ -727,12 +727,13 @@
     if(!wps_data || !buf)
         return false;
 
+    wps_reset(wps_data);
+
     if(!isfile)
     {
-        wps_clear(wps_data);
         strncpy(wps_data->format_buffer, buf, sizeof(wps_data->format_buffer));
         wps_data->format_buffer[sizeof(wps_data->format_buffer) - 1] = 0;
-        gui_wps_format(wps_data);
+        wps_load(wps_data);
         return true;
     }
     else
@@ -744,7 +745,6 @@
          */
         if (! strcmp(buf, WPS_DEFAULTCFG) )
         {
-            wps_reset(wps_data);
             global_settings.wps_file[0] = 0;
             return false;
         }
@@ -752,7 +752,6 @@
 #ifdef HAVE_REMOTE_LCD
         if (! strcmp(buf, RWPS_DEFAULTCFG) )
         {
-            wps_reset(wps_data);
             global_settings.rwps_file[0] = 0;
             return false;
         }
@@ -768,7 +767,6 @@
         {
             unsigned int start = 0;
 
-            wps_reset(wps_data);
 #ifdef HAVE_LCD_BITMAP
             wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
 
@@ -793,7 +791,7 @@
 
             if (start > 0)
             {
-                gui_wps_format(wps_data);
+                wps_load(wps_data);
             }
 
             close(fd);
Index: apps/gui/gwps.h
===================================================================
--- apps/gui/gwps.h	(révision 12842)
+++ apps/gui/gwps.h	(copie de travail)
@@ -64,21 +64,191 @@
 };
 
 #ifdef HAVE_LCD_BITMAP
+
 #define MAX_IMAGES (26*2) /* a-z and A-Z */
 #define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \
                    + (2*LCD_HEIGHT*LCD_WIDTH/8))
 #define WPS_MAX_LINES (LCD_HEIGHT/5+1)
 #define FORMAT_BUFFER_SIZE 3072
+#define WPS_MAX_TOKENS 1024
+#define WPS_MAX_STRING 2058
+#define WPS_MAX_COND_LEVEL 10
+
 #else
+
 #define WPS_MAX_LINES 2
 #define FORMAT_BUFFER_SIZE 400
+#define WPS_MAX_TOKENS 256
+#define WPS_MAX_STRING 512
+#define WPS_MAX_COND_LEVEL 4
+
 #endif
+
 #define WPS_MAX_SUBLINES 12
 #define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* (10ths of sec) */
 #define BASE_SUBLINE_TIME 10 /* base time that multiplier is applied to
                                 (1/HZ sec, or 100ths of sec) */
 #define SUBLINE_RESET -1
 
+
+enum wps_token_type {
+    WPS_TOKEN_UNKNOWN,
+
+    /* Markers */
+    WPS_TOKEN_CHARACTER,
+    WPS_TOKEN_STRING,
+    WPS_TOKEN_EOL,
+
+#ifdef HAVE_LCD_BITMAP
+    /* Alignment */
+    WPS_TOKEN_ALIGN_LEFT,
+    WPS_TOKEN_ALIGN_CENTER,
+    WPS_TOKEN_ALIGN_RIGHT,
+#endif
+
+    /* Scrolling */
+    WPS_TOKEN_SCROLL,
+
+    /* Alternating sublines */
+    WPS_TOKEN_SUBLINE_SEPARATOR,
+    WPS_TOKEN_SUBLINE_TIMEOUT,
+
+    /* Battery */
+    WPS_TOKEN_BATTERY_PERCENT,
+    WPS_TOKEN_BATTERY_VOLTS,
+    WPS_TOKEN_BATTERY_TIME,
+    WPS_TOKEN_BATTERY_CHARGER_CONNECTED,
+    WPS_TOKEN_BATTERY_CHARGING,
+    WPS_TOKEN_BATTERY_SLEEPTIME,
+
+#if (CONFIG_CODEC == SWCODEC)
+    /* Sound */
+    WPS_TOKEN_SOUND_PITCH,
+    WPS_TOKEN_REPLAYGAIN,
+#endif
+
+#if CONFIG_RTC
+    /* Time */
+    WPS_TOKEN_RTC,
+    WPS_TOKEN_RTC_DAY_OF_MONTH,
+    WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,
+    WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED,
+    WPS_TOKEN_RTC_HOUR_24,
+    WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED,
+    WPS_TOKEN_RTC_HOUR_12,
+    WPS_TOKEN_RTC_MONTH,
+    WPS_TOKEN_RTC_MINUTE,
+    WPS_TOKEN_RTC_SECOND,
+    WPS_TOKEN_RTC_YEAR_2_DIGITS,
+    WPS_TOKEN_RTC_YEAR_4_DIGITS,
+    WPS_TOKEN_RTC_AM_PM_UPPER,
+    WPS_TOKEN_RTC_AM_PM_LOWER,
+    WPS_TOKEN_RTC_WEEKDAY_NAME,
+    WPS_TOKEN_RTC_MONTH_NAME,
+    WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON,
+    WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN,
+#endif
+
+    /* Conditional */
+    WPS_TOKEN_CONDITIONAL,
+    WPS_TOKEN_CONDITIONAL_START,
+    WPS_TOKEN_CONDITIONAL_OPTION,
+    WPS_TOKEN_CONDITIONAL_END,
+
+    /* Database */
+    WPS_TOKEN_DATABASE_PLAYCOUNT,
+    WPS_TOKEN_DATABASE_RATING,
+
+    /* File */
+    WPS_TOKEN_FILE_BITRATE,
+    WPS_TOKEN_FILE_CODEC,
+    WPS_TOKEN_FILE_FREQUENCY,
+    WPS_TOKEN_FILE_NAME,
+    WPS_TOKEN_FILE_NAME_WITH_EXTENSION,
+    WPS_TOKEN_FILE_PATH,
+    WPS_TOKEN_FILE_SIZE,
+    WPS_TOKEN_FILE_VBR,
+    WPS_TOKEN_FILE_DIRECTORY,
+
+#ifdef HAVE_LCD_BITMAP
+    /* Image */
+    WPS_TOKEN_IMAGE_BACKDROP,
+    WPS_TOKEN_IMAGE_PROGRESS_BAR,
+    WPS_TOKEN_IMAGE_PRELOAD,
+    WPS_TOKEN_IMAGE_PRELOAD_DISPLAY,
+    WPS_TOKEN_IMAGE_DISPLAY,
+#endif
+
+    /* Metadata */
+    WPS_TOKEN_METADATA_ARTIST,
+    WPS_TOKEN_METADATA_COMPOSER,
+    WPS_TOKEN_METADATA_ALBUM_ARTIST,
+    WPS_TOKEN_METADATA_ALBUM,
+    WPS_TOKEN_METADATA_GENRE,
+    WPS_TOKEN_METADATA_TRACK_NUMBER,
+    WPS_TOKEN_METADATA_TRACK_TITLE,
+    WPS_TOKEN_METADATA_VERSION,
+    WPS_TOKEN_METADATA_YEAR,
+    WPS_TOKEN_METADATA_COMMENT,
+
+    /* Mode */
+    WPS_TOKEN_REPEAT_MODE,
+    WPS_TOKEN_PLAYBACK_STATUS,
+
+#ifdef HAS_BUTTON_HOLD
+    WPS_TOKEN_MAIN_HOLD,
+#endif
+#ifdef HAS_REMOTE_BUTTON_HOLD
+    WPS_TOKEN_REMOTE_HOLD,
+#endif
+
+    /* Progressbar */
+    WPS_TOKEN_PROGRESSBAR,
+    WPS_TOKEN_PLAYER_PROGRESSBAR,
+
+#ifdef HAVE_LCD_BITMAP
+    /* Peakmeter */
+    WPS_TOKEN_PEAKMETER,
+#endif
+
+    /* Volume level */
+    WPS_TOKEN_VOLUME,
+
+    /* Current track */
+    WPS_TOKEN_TRACK_TIME_ELAPSED,
+    WPS_TOKEN_TRACK_TIME_REMAINING,
+    WPS_TOKEN_TRACK_LENGTH,
+
+    /* Playlist */
+    WPS_TOKEN_PLAYLIST_ENTRIES,
+    WPS_TOKEN_PLAYLIST_NAME,
+    WPS_TOKEN_PLAYLIST_POSITION,
+    WPS_TOKEN_PLAYLIST_SHUFFLE,
+
+#ifdef HAVE_LCD_BITMAP
+    /* Statusbar */
+    WPS_TOKEN_STATUSBAR_ENABLED,
+    WPS_TOKEN_STATUSBAR_DISABLED,
+#endif
+
+#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
+    /* Virtual LED */
+    WPS_TOKEN_VLED_HDD
+#endif
+};
+
+struct wps_token {
+    enum wps_token_type type;
+    bool next;
+    unsigned char flag;
+    union {
+        char *s;
+        char c;
+        int i;
+    } value;
+};
+
+
 /* wps_data
    this struct old all necessary data which describes the
    viewable content of a wps */
@@ -98,17 +268,26 @@
     bool full_line_progressbar;
 #endif
     char format_buffer[FORMAT_BUFFER_SIZE];
-    char* format_lines[WPS_MAX_LINES][WPS_MAX_SUBLINES];
+    int format_lines[WPS_MAX_LINES][WPS_MAX_SUBLINES];
     unsigned char line_type[WPS_MAX_LINES][WPS_MAX_SUBLINES];
     unsigned short time_mult[WPS_MAX_LINES][WPS_MAX_SUBLINES];
     long subline_expire_time[WPS_MAX_LINES];
     int curr_subline[WPS_MAX_LINES];
+    int num_lines;
+    int num_sublines[WPS_MAX_LINES];
+
+    bool line_scroll[WPS_MAX_LINES];
+    char wps_strings[WPS_MAX_STRING];
+    struct wps_token tokens[WPS_MAX_TOKENS];
+    int num_tokens;
+
     int progress_top;
     int progress_height;
     int progress_start;
     int progress_end;
     bool wps_loaded;
     bool peak_meter_enabled;
+    int peak_meter_y;
 };
 
 /* initial setup of wps_data */
@@ -120,6 +299,9 @@
                    const char *buf,
                    bool isfile);
 
+bool wps_load(struct wps_data *wps_data);
+bool trace;
+
 /* wps_data end */
 
 /* wps_state
Index: apps/SOURCES
===================================================================
--- apps/SOURCES	(révision 12842)
+++ apps/SOURCES	(copie de travail)
@@ -58,6 +58,7 @@
 gui/statusbar.c
 gui/textarea.c
 gui/yesno.c
+gui/wps.c
 
 #ifdef HAVE_LCD_CHARCELLS
 player/icons.c
