Start of some apps/ and wps cleanup work... Move everything related to the actual drawing of the wps into apps/gui/wps_engine, things related to the actual screen are in apps/gui/music_screen.c (names are temporary unless noone comes up with anything better)
No real code changes.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22062 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/SOURCES b/apps/SOURCES
index 8166dbe..e1d85a7 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -60,8 +60,6 @@
 #ifdef HAVE_BUTTONBAR
 gui/buttonbar.c
 #endif
-gui/gwps.c
-gui/gwps-common.c
 #ifdef HAVE_LCD_BITMAP
 gui/icon.c
 #endif
@@ -78,14 +76,19 @@
 #ifdef HAVE_QUICKSCREEN
 gui/quickscreen.c
 #endif
+
+gui/music_screen.c
 gui/scrollbar.c
 gui/splash.c
 gui/statusbar.c
 gui/yesno.c
-gui/wps_debug.c
-gui/wps_parser.c
 gui/viewport.c
 
+gui/wps_engine/wps_debug.c
+gui/wps_engine/wps_display.c
+gui/wps_engine/wps_parser.c
+gui/wps_engine/wps_tokens.c
+
 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
 gui/backdrop.c
 #endif
diff --git a/apps/filetree.c b/apps/filetree.c
index 87ac37c..458d553 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -33,7 +33,6 @@
 #include "filetypes.h"
 #include "talk.h"
 #include "playlist.h"
-#include "gwps.h"
 #include "lang.h"
 #include "language.h"
 #include "screens.h"
@@ -52,7 +51,7 @@
 #if CONFIG_TUNER
 #include "radio.h"
 #endif
-
+#include "wps_engine/wps_internals.h" /* FIXME: REMOVE ME */
 #include "backdrop.h"
 
 static int compare_sort_dir; /* qsort key for sorting directories */
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c
deleted file mode 100644
index 721682f..0000000
--- a/apps/gui/gwps-common.c
+++ /dev/null
@@ -1,2071 +0,0 @@
-/***************************************************************************
- *             __________               __   ___.
- *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
- *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
- *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
- *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
- *                     \/            \/     \/    \/            \/
- * $Id$
- *
- * Copyright (C) 2002-2007 Björn Stenberg
- * Copyright (C) 2007-2008 Nicolas Pennequin
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#include "gwps-common.h"
-#include "font.h"
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include "system.h"
-#include "settings.h"
-#include "settings_list.h"
-#include "rbunicode.h"
-#include "rtc.h"
-#include "audio.h"
-#include "status.h"
-#include "power.h"
-#include "powermgmt.h"
-#include "sound.h"
-#include "debug.h"
-#ifdef HAVE_LCD_CHARCELLS
-#include "hwcompat.h"
-#endif
-#include "abrepeat.h"
-#include "mp3_playback.h"
-#include "lang.h"
-#include "misc.h"
-#include "splash.h"
-#include "scrollbar.h"
-#include "led.h"
-#include "lcd.h"
-#ifdef HAVE_LCD_BITMAP
-#include "peakmeter.h"
-/* Image stuff */
-#include "bmp.h"
-#include "albumart.h"
-#endif
-#include "dsp.h"
-#include "action.h"
-#include "cuesheet.h"
-#include "playlist.h"
-#if CONFIG_CODEC == SWCODEC
-#include "playback.h"
-#endif
-#include "backdrop.h"
-#include "viewport.h"
-
-#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
-
-/* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds
-   (possibly with a decimal fraction) but stored as integer values.
-   E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units.
-*/
-#define TIMEOUT_UNIT (HZ/10) /* I.e. 0.1 sec */
-#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* In TIMEOUT_UNIT's */
-
-bool wps_fading_out = false;
-void fade(bool fade_in, bool updatewps)
-{
-    int fp_global_vol = global_settings.volume << 8;
-    int fp_min_vol = sound_min(SOUND_VOLUME) << 8;
-    int fp_step = (fp_global_vol - fp_min_vol) / 30;
-    int i;
-    wps_fading_out = !fade_in;
-    if (fade_in) {
-        /* fade in */
-        int fp_volume = fp_min_vol;
-
-        /* zero out the sound */
-        sound_set_volume(fp_min_vol >> 8);
-
-        sleep(HZ/10); /* let audio thread run */
-        audio_resume();
-        
-        while (fp_volume < fp_global_vol - fp_step) {
-            fp_volume += fp_step;
-            sound_set_volume(fp_volume >> 8);
-            if (updatewps)
-            {
-                FOR_NB_SCREENS(i)
-                    gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
-            }
-            sleep(1);
-        }
-        sound_set_volume(global_settings.volume);
-    }
-    else {
-        /* fade out */
-        int fp_volume = fp_global_vol;
-
-        while (fp_volume > fp_min_vol + fp_step) {
-            fp_volume -= fp_step;
-            sound_set_volume(fp_volume >> 8);
-            if (updatewps)
-            {
-                FOR_NB_SCREENS(i)
-                    gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
-            }
-            sleep(1);
-        }
-        audio_pause();
-        wps_fading_out = false;
-#if CONFIG_CODEC != SWCODEC
-#ifndef SIMULATOR
-        /* let audio thread run and wait for the mas to run out of data */
-        while (!mp3_pause_done())
-#endif
-            sleep(HZ/10);
-#endif
-
-        /* reset volume to what it was before the fade */
-        sound_set_volume(global_settings.volume);
-    }
-}
-
-bool update_onvol_change(struct gui_wps * gwps)
-{
-    gui_wps_redraw(gwps, 0, WPS_REFRESH_NON_STATIC);
-
-#ifdef HAVE_LCD_CHARCELLS
-    splashf(0, "Vol: %3d dB",
-               sound_val2phys(SOUND_VOLUME, global_settings.volume));
-    return true;
-#endif
-    return false;
-}
-
-bool ffwd_rew(int button)
-{
-    unsigned int step = 0;     /* current ff/rewind step */ 
-    unsigned int max_step = 0; /* maximum ff/rewind step */ 
-    int ff_rewind_count = 0;   /* current ff/rewind count (in ticks) */
-    int direction = -1;         /* forward=1 or backward=-1 */
-    bool exit = false;
-    bool usb = false;
-    int i = 0;
-    const long ff_rw_accel = (global_settings.ff_rewind_accel + 3);
-
-    if (button == ACTION_NONE)
-    {
-        status_set_ffmode(0);
-        return usb;
-    }
-    while (!exit)
-    {
-        switch ( button )
-        {
-            case ACTION_WPS_SEEKFWD:
-                 direction = 1;
-            case ACTION_WPS_SEEKBACK:
-                if (wps_state.ff_rewind)
-                {
-                    if (direction == 1)
-                    {
-                        /* fast forwarding, calc max step relative to end */
-                        max_step = (wps_state.id3->length - 
-                                    (wps_state.id3->elapsed +
-                                     ff_rewind_count)) *
-                                     FF_REWIND_MAX_PERCENT / 100;
-                    }
-                    else
-                    {
-                        /* rewinding, calc max step relative to start */
-                        max_step = (wps_state.id3->elapsed + ff_rewind_count) *
-                                    FF_REWIND_MAX_PERCENT / 100;
-                    }
-
-                    max_step = MAX(max_step, MIN_FF_REWIND_STEP);
-
-                    if (step > max_step)
-                        step = max_step;
-
-                    ff_rewind_count += step * direction;
-
-                    /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
-                    step += step >> ff_rw_accel;
-                }
-                else
-                {
-                    if ( (audio_status() & AUDIO_STATUS_PLAY) &&
-                          wps_state.id3 && wps_state.id3->length )
-                    {
-                        if (!wps_state.paused)
-#if (CONFIG_CODEC == SWCODEC)
-                            audio_pre_ff_rewind();
-#else
-                            audio_pause();
-#endif
-#if CONFIG_KEYPAD == PLAYER_PAD
-                        FOR_NB_SCREENS(i)
-                            gui_wps[i].display->stop_scroll();
-#endif
-                        if (direction > 0) 
-                            status_set_ffmode(STATUS_FASTFORWARD);
-                        else
-                            status_set_ffmode(STATUS_FASTBACKWARD);
-
-                        wps_state.ff_rewind = true;
-
-                        step = 1000 * global_settings.ff_rewind_min_step;
-                    }
-                    else
-                        break;
-                }
-
-                if (direction > 0) {
-                    if ((wps_state.id3->elapsed + ff_rewind_count) >
-                        wps_state.id3->length)
-                        ff_rewind_count = wps_state.id3->length -
-                            wps_state.id3->elapsed;
-                }
-                else {
-                    if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0)
-                        ff_rewind_count = -wps_state.id3->elapsed;
-                }
-
-                FOR_NB_SCREENS(i)
-                    gui_wps_redraw(&gui_wps[i],
-                                    (wps_state.wps_time_countup == false)?
-                                    ff_rewind_count:-ff_rewind_count,
-                                    WPS_REFRESH_PLAYER_PROGRESS |
-                                    WPS_REFRESH_DYNAMIC);
-
-                break;
-
-            case ACTION_WPS_STOPSEEK:
-                wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count;
-                audio_ff_rewind(wps_state.id3->elapsed);
-                ff_rewind_count = 0;
-                wps_state.ff_rewind = false;
-                status_set_ffmode(0);
-#if (CONFIG_CODEC != SWCODEC)
-                if (!wps_state.paused)
-                    audio_resume();
-#endif
-#ifdef HAVE_LCD_CHARCELLS
-                FOR_NB_SCREENS(i)
-                    gui_wps_redraw(&gui_wps[i],0, WPS_REFRESH_ALL);
-#endif
-                exit = true;
-                break;
-
-            default:
-                if(default_event_handler(button) == SYS_USB_CONNECTED) {
-                    status_set_ffmode(0);
-                    usb = true;
-                    exit = true;
-                }
-                break;
-        }
-        if (!exit)
-        {
-            button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK);
-#ifdef HAVE_TOUCHSCREEN
-            if (button == ACTION_TOUCHSCREEN)
-                button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data);
-#endif
-        }        
-    }
-    return usb;
-}
-
-bool gui_wps_display(struct gui_wps *gwps)
-{
-    struct screen *display = gwps->display;
-    struct wps_data *data = gwps->data;
-    int screen = display->screen_type;
-
-    /* Update the values in the first (default) viewport - in case the user
-       has modified the statusbar or colour settings */
-#if LCD_DEPTH > 1
-    if (display->depth > 1)
-    {
-        data->viewports[0].vp.fg_pattern = display->get_foreground();
-        data->viewports[0].vp.bg_pattern = display->get_background();
-    }
-#endif
-    display->clear_display();
-    if (!data->wps_loaded) {
-        if ( !data->num_tokens ) {
-            /* set the default wps for the main-screen */
-            if(screen == SCREEN_MAIN)
-            {
-#if LCD_DEPTH > 1
-                unload_wps_backdrop();
-#endif
-                wps_data_load(data,
-                              display,
-#ifdef HAVE_LCD_BITMAP
-                              "%s%?it<%?in<%in. |>%it|%fn>\n"
-                              "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
-                              "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
-                              "\n"
-                              "%al%pc/%pt%ar[%pp:%pe]\n"
-                              "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
-                              "%pb\n"
-                              "%pm\n", false);
-#else
-                              "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
-                              "%pc%?ps<*|/>%pt\n", false);
-#endif
-            }
-#ifdef HAVE_REMOTE_LCD
-             /* set the default wps for the remote-screen */
-             else if(screen == SCREEN_REMOTE)
-             {
-#if LCD_REMOTE_DEPTH > 1
-                 unload_remote_wps_backdrop();
-#endif
-                 wps_data_load(data,
-                               display,
-                               "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
-                               "%s%?it<%?in<%in. |>%it|%fn>\n"
-                               "%al%pc/%pt%ar[%pp:%pe]\n"
-                               "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
-                               "%pb\n", false);
-             }
-#endif
-        }
-    }
-    else
-    {
-#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
-        if (screen == SCREEN_REMOTE)
-            show_remote_wps_backdrop();
-        else if (screen == SCREEN_MAIN)
-#endif
-#if LCD_DEPTH > 1
-            show_wps_backdrop();
-#endif
-    }
-    return gui_wps_redraw(gwps, 0, WPS_REFRESH_ALL);
-}
-
-bool gui_wps_update(struct gui_wps *gwps)
-{
-    struct mp3entry *id3 = gwps->state->id3;
-    bool retval;
-    bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
-    gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update;
-    retval = gui_wps_redraw(gwps, 0,
-                            gwps->state->do_full_update ? 
-                                        WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC);
-    return retval;
-}
-
-
-void display_keylock_text(bool locked)
-{
-    int i;
-    FOR_NB_SCREENS(i)
-        gui_wps[i].display->stop_scroll();
-
-    splash(HZ, locked ? ID2P(LANG_KEYLOCK_ON) : ID2P(LANG_KEYLOCK_OFF));
-}
-
-#ifdef HAVE_LCD_BITMAP
-
-static void draw_progressbar(struct gui_wps *gwps,
-                             struct wps_viewport *wps_vp)
- {
-    struct screen *display = gwps->display;
-    struct wps_state *state = gwps->state;
-    struct progressbar *pb = wps_vp->pb;
-    int y = pb->y;
-
-    if (y < 0)
-    {
-        int line_height = font_get(wps_vp->vp.font)->height;
-        /* center the pb in the line, but only if the line is higher than the pb */
-        int center = (line_height-pb->height)/2;
-        /* if Y was not set calculate by font height,Y is -line_number-1 */
-        y = (-y -1)*line_height + (0 > center ? 0 : center);
-    }
-
-    if (pb->have_bitmap_pb)
-        gui_bitmap_scrollbar_draw(display, pb->bm,
-                                  pb->x, y, pb->width, pb->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, pb->x, y, pb->width, pb->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() && state->id3->length != 0 )
-        ab_draw_markers(display, state->id3->length,
-                        pb->x, pb->x + pb->width, y, pb->height);
-#endif
-
-    if (state->id3->cuesheet)
-        cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
-                         pb->x, pb->x + pb->width, y+1, pb->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].subimage_height);
-    gwps->display->set_drawmode(DRMODE_SOLID);
-}
-
-static void wps_draw_image(struct gui_wps *gwps, int n, int subimage)
-{
-    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_part(data->img[n].bm.data, 
-                                  0, data->img[n].subimage_height * subimage,
-                                  data->img[n].bm.width, data->img[n].x,
-                                  data->img[n].y, data->img[n].bm.width,
-                                  data->img[n].subimage_height);
-#if LCD_DEPTH > 1
-    } else {
-        display->transparent_bitmap_part((fb_data *)data->img[n].bm.data,
-                                         0, data->img[n].subimage_height * subimage,
-                                         data->img[n].bm.width, data->img[n].x,
-                                         data->img[n].y, data->img[n].bm.width,
-                                         data->img[n].subimage_height);
-    }
-#endif
-}
-
-static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
-{
-    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 (data->img[n].display >= 0)
-            {
-                wps_draw_image(gwps, n, data->img[n].display);
-            } else if (data->img[n].always_display && data->img[n].vp == vp)
-            {
-                wps_draw_image(gwps, n, 0);
-            }
-        }
-    }
-    display->set_drawmode(DRMODE_SOLID);
-}
-
-#else /* HAVE_LCD_CHARCELL */
-
-static bool draw_player_progress(struct gui_wps *gwps)
-{
-    struct wps_state *state = gwps->state;
-    struct screen *display = gwps->display;
-    unsigned char progress_pattern[7];
-    int pos = 0;
-    int i;
-
-    if (!state->id3)
-        return false;
-
-    if (state->id3->length)
-        pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
-              / state->id3->length;
-
-    for (i = 0; i < 7; i++, pos -= 5)
-    {
-        if (pos <= 0)
-            progress_pattern[i] = 0x1fu;
-        else if (pos >= 5)
-            progress_pattern[i] = 0x00u;
-        else
-            progress_pattern[i] = 0x1fu >> pos;
-    }
-
-    display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
-    return true;
-}
-
-static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
-{
-    static const unsigned char numbers[10][4] = {
-        {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
-        {0x04, 0x0c, 0x04, 0x04}, /* 1 */
-        {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
-        {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
-        {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
-        {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
-        {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
-        {0x0e, 0x02, 0x04, 0x08}, /* 7 */
-        {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
-        {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
-    };
-
-    struct wps_state *state = gwps->state;
-    struct screen *display = gwps->display;
-    struct wps_data *data = gwps->data;
-    unsigned char progress_pattern[7];
-    char timestr[10];
-    int time;
-    int time_idx = 0;
-    int pos = 0;
-    int pat_idx = 1;
-    int digit, i, j;
-    bool softchar;
-
-    if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
-        return;
-
-    time = state->id3->elapsed + state->ff_rewind_count;
-    if (state->id3->length)
-        pos = 55 * time / state->id3->length;
-
-    memset(timestr, 0, sizeof(timestr));
-    format_time(timestr, sizeof(timestr)-2, time);
-    timestr[strlen(timestr)] = ':';   /* always safe */
-
-    for (i = 0; i < 11; i++, pos -= 5)
-    {
-        softchar = false;
-        memset(progress_pattern, 0, sizeof(progress_pattern));
-        
-        if ((digit = timestr[time_idx]))
-        {
-            softchar = true;
-            digit -= '0';
-
-            if (timestr[time_idx + 1] == ':')  /* ones, left aligned */
-            {   
-                memcpy(progress_pattern, numbers[digit], 4);
-                time_idx += 2;
-            }
-            else  /* tens, shifted right */
-            {
-                for (j = 0; j < 4; j++)
-                    progress_pattern[j] = numbers[digit][j] >> 1;
-
-                if (time_idx > 0)  /* not the first group, add colon in front */
-                {
-                    progress_pattern[1] |= 0x10u;
-                    progress_pattern[3] |= 0x10u;
-                }
-                time_idx++;
-            }
-
-            if (pos >= 5)
-                progress_pattern[5] = progress_pattern[6] = 0x1fu;
-        }
-
-        if (pos > 0 && pos < 5)
-        {
-            softchar = true;
-            progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
-        }
-
-        if (softchar && pat_idx < 8)
-        {
-            display->define_pattern(data->wps_progress_pat[pat_idx],
-                                    progress_pattern);
-            buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
-            pat_idx++;
-        }
-        else if (pos <= 0)
-            buf = utf8encode(' ', buf);
-        else
-            buf = utf8encode(0xe115, buf); /* 2/7 _ */
-    }
-    *buf = '\0';
-}
-
-#endif /* HAVE_LCD_CHARCELL */
-
-static char* get_codectype(const struct mp3entry* id3)
-{
-    if (id3->codectype < AFMT_NUM_CODECS) {
-        return (char*)audio_formats[id3->codectype].label;
-    } else {
-        return NULL;
-    }
-}
-
-/* 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);
-    strlcpy(buf, sep + 1, len + 1);
-    return buf;
-}
-
-/* Return the tag found at index i and write its value in buf.
-   The return value is buf if the tag had a value, or NULL if not.
-
-   intval is used with conditionals/enums: when this function is called,
-   intval should contain the number of options in the conditional/enum.
-   When this function returns, intval is -1 if the tag is non numeric or,
-   if the tag is numeric, *intval is the enum case we want to go to (between 1
-   and the original value of *intval, inclusive).
-   When not treating a conditional/enum, intval should be NULL.
-*/
-static const char *get_token_value(struct gui_wps *gwps,
-                                   struct wps_token *token,
-                                   char *buf, int buf_size,
-                                   int *intval)
-{
-    if (!gwps)
-        return NULL;
-
-    struct wps_data *data = gwps->data;
-    struct wps_state *state = gwps->state;
-
-    if (!data || !state)
-        return NULL;
-
-    struct mp3entry *id3;
-
-    if (token->next)
-        id3 = state->nid3;
-    else
-        id3 = state->id3;
-
-    if (!id3)
-        return NULL;
-
-#if CONFIG_RTC
-    struct tm* tm = NULL;
-
-    /* if the token is an RTC one, update the time
-       and do the necessary checks */
-    if (token->type >= WPS_TOKENS_RTC_BEGIN
-        && token->type <= WPS_TOKENS_RTC_END)
-    {
-        tm = get_time();
-
-        if (!valid_time(tm))
-            return NULL;
-    }
-#endif
-
-    int limit = 1;
-    if (intval)
-    {
-        limit = *intval;
-        *intval = -1;
-    }
-
-    switch (token->type)
-    {
-        case WPS_TOKEN_CHARACTER:
-            return &(token->value.c);
-
-        case WPS_TOKEN_STRING:
-            return data->strings[token->value.i];
-
-        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)
-            {
-                if (global_settings.volume == sound_min(SOUND_VOLUME))
-                {
-                    *intval = 1;
-                }
-                else if (global_settings.volume == 0)
-                {
-                    *intval = limit - 1;
-                }
-                else if (global_settings.volume > 0)
-                {
-                    *intval = limit;
-                }
-                else
-                {
-                    *intval = (limit - 3) * (global_settings.volume
-                                             - sound_min(SOUND_VOLUME) - 1)
-                              / (-1 - sound_min(SOUND_VOLUME)) + 2;
-                }
-            }
-            return buf;
-
-        case WPS_TOKEN_TRACK_ELAPSED_PERCENT:
-            if (id3->length <= 0)
-                return NULL;
-
-            if (intval)
-            {
-                *intval = limit * (id3->elapsed + state->ff_rewind_count)
-                          / id3->length + 1;
-            }
-            snprintf(buf, buf_size, "%d",
-                     100*(id3->elapsed + state->ff_rewind_count) / id3->length);
-            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_GROUPING:
-            return id3->grouping;
-
-        case WPS_TOKEN_METADATA_GENRE:
-            return id3->genre_string;
-
-        case WPS_TOKEN_METADATA_DISC_NUMBER:
-            if (id3->disc_string)
-                return id3->disc_string;
-            if (id3->discnum) {
-                snprintf(buf, buf_size, "%d", id3->discnum);
-                return buf;
-            }
-            return NULL;
-
-        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;
-
-#ifdef HAVE_ALBUMART
-        case WPS_TOKEN_ALBUMART_DISPLAY:
-            draw_album_art(gwps, audio_current_aa_hid(), false);
-            return NULL;
-
-        case WPS_TOKEN_ALBUMART_FOUND:
-            if (audio_current_aa_hid() >= 0) {
-                return "C";
-            }
-            return NULL;
-#endif
-
-        case WPS_TOKEN_FILE_BITRATE:
-            if(id3->bitrate)
-                snprintf(buf, buf_size, "%d", id3->bitrate);
-            else
-                return "?";
-            return buf;
-
-        case WPS_TOKEN_FILE_CODEC:
-            if (intval)
-            {
-                if(id3->codectype == AFMT_UNKNOWN)
-                    *intval = AFMT_NUM_CODECS;
-                else
-                    *intval = id3->codectype;
-            }
-            return get_codectype(id3);
-
-        case WPS_TOKEN_FILE_FREQUENCY:
-            snprintf(buf, buf_size, "%ld", id3->frequency);
-            return buf;
-
-        case WPS_TOKEN_FILE_FREQUENCY_KHZ:
-            /* ignore remainders < 100, so 22050 Hz becomes just 22k */
-            if ((id3->frequency % 1000) < 100)
-                snprintf(buf, buf_size, "%ld", id3->frequency / 1000);
-            else
-                snprintf(buf, buf_size, "%ld.%d",
-                        id3->frequency / 1000,
-                        (id3->frequency % 1000) / 100);
-            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, token->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 / 1000, (v % 1000) / 10);
-            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
-                return "?h ?m";
-            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_BATTERY_SLEEPTIME:
-        {
-            if (get_sleep_timer() == 0)
-                return NULL;
-            else
-            {
-                format_time(buf, buf_size, get_sleep_timer() * 1000);
-                return buf;
-            }
-        }
-
-        case WPS_TOKEN_PLAYBACK_STATUS:
-        {
-            int status = audio_status();
-            int mode = 1;
-            if (status == AUDIO_STATUS_PLAY)
-                mode = 2;
-            if (wps_fading_out || 
-               (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-1);
-            return buf;
-        }
-
-        case WPS_TOKEN_REPEAT_MODE:
-            if (intval)
-                *intval = global_settings.repeat_mode + 1;
-            snprintf(buf, buf_size, "%d", global_settings.repeat_mode);
-            return buf;
-            
-        case WPS_TOKEN_RTC_PRESENT:
-#if CONFIG_RTC
-                return "c";
-#else
-                return NULL;
-#endif
-
-#if CONFIG_RTC
-        case WPS_TOKEN_RTC_12HOUR_CFG:
-            if (intval)
-                *intval = global_settings.timeformat + 1;
-            snprintf(buf, buf_size, "%d", global_settings.timeformat);
-            return buf;
-
-        case WPS_TOKEN_RTC_DAY_OF_MONTH:
-            /* d: day of month (01..31) */
-            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) */
-            snprintf(buf, buf_size, "%2d", tm->tm_mday);
-            return buf;
-
-        case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
-            /* H: hour (00..23) */
-            snprintf(buf, buf_size, "%02d", tm->tm_hour);
-            return buf;
-
-        case WPS_TOKEN_RTC_HOUR_24:
-            /* k: hour ( 0..23) */
-            snprintf(buf, buf_size, "%2d", tm->tm_hour);
-            return buf;
-
-        case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
-            /* I: hour (01..12) */
-            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) */
-            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 (intval)
-                *intval = tm->tm_mon + 1;
-            snprintf(buf, buf_size, "%02d", tm->tm_mon + 1);
-            return buf;
-
-        case WPS_TOKEN_RTC_MINUTE:
-            /* M: minute (00..59) */
-            snprintf(buf, buf_size, "%02d", tm->tm_min);
-            return buf;
-
-        case WPS_TOKEN_RTC_SECOND:
-            /* S: second (00..59) */
-            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...) */
-            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 */
-            return tm->tm_hour/12 == 0 ? "AM" : "PM";
-
-        case WPS_TOKEN_RTC_AM_PM_LOWER:
-            /* P: lower case am or pm indicator */
-            return tm->tm_hour/12 == 0 ? "am" : "pm";
-
-        case WPS_TOKEN_RTC_WEEKDAY_NAME:
-            /* a: abbreviated weekday name (Sun..Sat) */
-            return str(LANG_WEEKDAY_SUNDAY + tm->tm_wday);
-
-        case WPS_TOKEN_RTC_MONTH_NAME:
-            /* b: abbreviated month name (Jan..Dec) */
-            return str(LANG_MONTH_JANUARY + tm->tm_mon);
-
-        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
-            /* u: day of week (1..7); 1 is Monday */
-            if (intval)
-                *intval = (tm->tm_wday == 0) ? 7 : tm->tm_wday;
-            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 (intval)
-                *intval = tm->tm_wday + 1;
-            snprintf(buf, buf_size, "%1d", tm->tm_wday);
-            return buf;
-#else
-        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_AM_PM_UPPER:
-        case WPS_TOKEN_RTC_AM_PM_LOWER:
-        case WPS_TOKEN_RTC_YEAR_2_DIGITS:
-            return "--";
-        case WPS_TOKEN_RTC_YEAR_4_DIGITS:
-            return "----";
-        case WPS_TOKEN_RTC_WEEKDAY_NAME:
-        case WPS_TOKEN_RTC_MONTH_NAME:
-            return "---";
-        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
-        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
-            return "-";
-#endif
-
-#ifdef HAVE_LCD_CHARCELLS
-        case WPS_TOKEN_PROGRESSBAR:
-        {
-            char *end = utf8encode(data->wps_progress_pat[0], buf);
-            *end = '\0';
-            return buf;
-        }
-
-        case WPS_TOKEN_PLAYER_PROGRESSBAR:
-            if(is_new_player())
-            {
-                /* we need 11 characters (full line) for
-                    progress-bar */
-                strlcpy(buf, "           ", buf_size);
-            }
-            else
-            {
-                /* Tell the user if we have an OldPlayer */
-                strlcpy(buf, " <Old LCD> ", buf_size);
-            }
-            return buf;
-#endif
-
-#ifdef HAVE_TAGCACHE
-        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;
-
-        case WPS_TOKEN_DATABASE_AUTOSCORE:
-            if (intval)
-                *intval = id3->score + 1;
-
-            snprintf(buf, buf_size, "%d", id3->score);
-            return buf;
-#endif
-
-#if (CONFIG_CODEC == SWCODEC)
-        case WPS_TOKEN_CROSSFADE:
-            if (intval)
-                *intval = global_settings.crossfade + 1;
-            snprintf(buf, buf_size, "%d", global_settings.crossfade);
-            return buf;
-
-        case WPS_TOKEN_REPLAYGAIN:
-        {
-            int val;
-
-            if (global_settings.replaygain_type == REPLAYGAIN_OFF)
-                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:
-                    strlcpy(buf, id3->track_gain_string, buf_size);
-                    break;
-                case 3:
-                case 5:
-                    strlcpy(buf, id3->album_gain_string, buf_size);
-                    break;
-            }
-            return buf;
-        }
-#endif  /* (CONFIG_CODEC == SWCODEC) */
-
-#if (CONFIG_CODEC != MAS3507D)
-        case WPS_TOKEN_SOUND_PITCH:
-        {
-            int val = sound_get_pitch();
-            snprintf(buf, buf_size, "%d.%d",
-                    val / 10, val % 10);
-            return buf;
-        }
-#endif
-
-        case WPS_TOKEN_MAIN_HOLD:
-#ifdef HAS_BUTTON_HOLD
-            if (button_hold())
-#else
-            if (is_keys_locked())
-#endif /*hold switch or softlock*/
-                return "h";
-            else
-                return NULL;
-
-#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
-        case WPS_TOKEN_BUTTON_VOLUME:
-            if (data->button_time_volume && 
-                TIME_BEFORE(current_tick, data->button_time_volume +
-                                          token->value.i * TIMEOUT_UNIT))
-                return "v";
-            return NULL;
-        case WPS_TOKEN_LASTTOUCH:
-#ifdef HAVE_TOUCHSCREEN
-            if (TIME_BEFORE(current_tick, token->value.i * TIMEOUT_UNIT +
-                                          touchscreen_last_touch()))
-                return "t";
-#endif
-            return NULL;
-
-        case WPS_TOKEN_SETTING:
-        {
-            if (intval)
-            {
-                /* Handle contionals */
-                const struct settings_list *s = settings+token->value.i;
-                switch (s->flags&F_T_MASK)
-                {
-                    case F_T_INT:
-                    case F_T_UINT:
-                        if (s->flags&F_RGB)
-                            /* %?St|name|<#000000|#000001|...|#FFFFFF> */
-                            /* shouldn't overflow since colors are stored
-                             * on 16 bits ...
-                             * but this is pretty useless anyway */
-                            *intval = *(int*)s->setting + 1;
-                        else if (s->cfg_vals == NULL)
-                            /* %?St|name|<1st choice|2nd choice|...> */
-                            *intval = (*(int*)s->setting-s->int_setting->min)
-                                      /s->int_setting->step + 1;
-                        else
-                            /* %?St|name|<1st choice|2nd choice|...> */
-                            /* Not sure about this one. cfg_name/vals are
-                             * indexed from 0 right? */
-                            *intval = *(int*)s->setting + 1;
-                        break;
-                    case F_T_BOOL:
-                        /* %?St|name|<if true|if false> */
-                        *intval = *(bool*)s->setting?1:2;
-                        break;
-                    case F_T_CHARPTR:
-                        /* %?St|name|<if non empty string|if empty>
-                         * The string's emptyness discards the setting's
-                         * prefix and suffix */
-                        *intval = ((char*)s->setting)[0]?1:2;
-                        break;
-                    default:
-                        /* This shouldn't happen ... but you never know */
-                        *intval = -1;
-                        break;
-                }
-            }
-            cfg_to_string(token->value.i,buf,buf_size);
-            return buf;
-        }
-
-        default:
-            return NULL;
-    }
-}
-
-/* Return the index to the end token for the conditional token at index.
-   The conditional token can be either a start token or a separator
-   (i.e. option) token.
-*/
-static int find_conditional_end(struct wps_data *data, int index)
-{
-    int ret = index;
-    while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
-        ret = data->tokens[ret].value.i;
-
-    /* ret now is the index to the end token for the conditional. */
-    return ret;
-}
-
-/* Evaluate the conditional that is at *token_index and return whether a skip
-   has ocurred. *token_index is updated with the new position.
-*/
-static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
-{
-    if (!gwps)
-        return false;
-
-    struct wps_data *data = gwps->data;
-
-    int i, cond_end;
-    int cond_index = *token_index;
-    char result[128];
-    const char *value;
-    unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
-    unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
-
-    /* treat ?xx<true> constructs as if they had 2 options. */
-    if (num_options < 2)
-        num_options = 2;
-
-    int intval = num_options;
-    /* get_token_value needs to know the number of options in the enum */
-    value = get_token_value(gwps, &data->tokens[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 is -1, we check if value is empty. */
-    if (intval == -1)
-        intval = (value && *value) ? 1 : num_options;
-    else if (intval > num_options || intval < 1)
-        intval = num_options;
-
-    data->tokens[cond_index].value.i = (intval << 8) + num_options;
-
-    /* skip to the appropriate enum case */
-    int next = cond_index + 2;
-    for (i = 1; i < intval; i++)
-    {
-        next = data->tokens[next].value.i;
-    }
-    *token_index = next;
-
-    if (prev_val == intval)
-    {
-        /* Same conditional case as previously. Return without clearing the
-           pictures */
-        return false;
-    }
-
-    cond_end = find_conditional_end(data, cond_index + 2);
-    for (i = cond_index + 3; i < cond_end; i++)
-    {
-#ifdef HAVE_LCD_BITMAP
-        /* clear all pictures in the conditional and nested ones */
-        if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
-            clear_image_pos(gwps, data->tokens[i].value.i & 0xFF);
-#endif
-#ifdef HAVE_ALBUMART
-        if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
-            draw_album_art(gwps, audio_current_aa_hid(), true);
-#endif
-    }
-
-    return true;
-}
-
-/* Read a (sub)line to the given alignment format buffer.
-   linebuf is the buffer where the data is actually stored.
-   align is the alignment format that'll be used to display the text.
-   The return value indicates whether the line needs to be updated.
-*/
-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;
-
-    char temp_buf[128];
-    char *buf = linebuf;  /* will always point to the writing position */
-    char *linebuf_end = linebuf + linebuf_size - 1;
-    int i, last_token_idx;
-    bool update = false;
-
-    /* alignment-related variables */
-    int cur_align;
-    char* cur_align_start;
-    cur_align_start = buf;
-    cur_align = WPS_ALIGN_LEFT;
-    align->left = NULL;
-    align->center = NULL;
-    align->right = NULL;
-
-    /* Process all tokens of the desired subline */
-    last_token_idx = wps_last_token_index(data, line, subline);
-    for (i = wps_first_token_index(data, line, subline);
-         i <= last_token_idx; i++)
-    {
-        switch(data->tokens[i].type)
-        {
-            case WPS_TOKEN_CONDITIONAL:
-                /* place ourselves in the right conditional case */
-                update |= evaluate_conditional(gwps, &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 = data->tokens[i].value.i & 0xFF;
-                int subimage = data->tokens[i].value.i >> 8;
-
-                if (n >= 0 && n < MAX_IMAGES && img[n].loaded)
-                    img[n].display = subimage;
-                break;
-            }
-#endif
-
-            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;
-            case WPS_VIEWPORT_ENABLE:
-            {
-                char label = data->tokens[i].value.i;
-                int j;
-                char temp = VP_DRAW_HIDEABLE;
-                for(j=0;j<data->num_viewports;j++)
-                {
-                    temp = VP_DRAW_HIDEABLE;
-                    if ((data->viewports[j].hidden_flags&VP_DRAW_HIDEABLE) &&
-                        (data->viewports[j].label == label))
-                    {
-                        if (data->viewports[j].hidden_flags&VP_DRAW_WASHIDDEN)
-                            temp |= VP_DRAW_WASHIDDEN;
-                        data->viewports[j].hidden_flags = temp;
-                    }
-                }
-            }
-                break;
-            default:
-            {
-                /* get the value of the tag and copy it to the buffer */
-                const char *value = get_token_value(gwps, &data->tokens[i],
-                                              temp_buf, sizeof(temp_buf), NULL);
-                if (value)
-                {
-                    update = true;
-                    while (*value && (buf < linebuf_end))
-                        *buf++ = *value++;
-                }
-                break;
-            }
-        }
-    }
-
-    /* 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;
-    }
-
-    return update;
-}
-
-static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
-{
-    struct wps_data *data = gwps->data;
-    int i;
-    int subline_idx = wps_subline_index(data, line, subline);
-    int last_token_idx = wps_last_token_index(data, line, subline);
-
-    data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
-
-    for (i = wps_first_token_index(data, line, subline);
-         i <= last_token_idx; i++)
-    {
-        switch(data->tokens[i].type)
-        {
-            case WPS_TOKEN_CONDITIONAL:
-                /* place ourselves in the right conditional case */
-                evaluate_conditional(gwps, &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;
-
-            case WPS_TOKEN_SUBLINE_TIMEOUT:
-                data->sublines[subline_idx].time_mult = data->tokens[i].value.i;
-                break;
-
-            default:
-                break;
-        }
-    }
-}
-
-/* Calculates which subline should be displayed for the specified line
-   Returns true iff the subline must be refreshed */
-static bool update_curr_subline(struct gui_wps *gwps, int line)
-{
-    struct wps_data *data = gwps->data;
-
-    int search, search_start, num_sublines;
-    bool reset_subline;
-    bool new_subline_refresh;
-    bool only_one_subline;
-
-    num_sublines = data->lines[line].num_sublines;
-    reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET);
-    new_subline_refresh = false;
-    only_one_subline = false;
-
-    /* if time to advance to next sub-line  */
-    if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 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->lines[line].curr_subline;
-
-        for (search = 0; search < num_sublines; search++)
-        {
-            data->lines[line].curr_subline++;
-
-            /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
-            if (data->lines[line].curr_subline == num_sublines)
-            {
-                if (data->lines[line].curr_subline == 1)
-                    only_one_subline = true;
-                data->lines[line].curr_subline = 0;
-            }
-
-            /* if back where we started after search or
-                only one subline is defined on the line */
-            if (((search > 0) &&
-                 (data->lines[line].curr_subline == search_start)) ||
-                only_one_subline)
-            {
-                /* no other subline with a time > 0 exists */
-                data->lines[line].subline_expire_time = (reset_subline ?
-                    current_tick :
-                    data->lines[line].subline_expire_time) + 100 * HZ;
-                break;
-            }
-            else
-            {
-                /* get initial time multiplier for this subline */
-                get_subline_timeout(gwps, line, data->lines[line].curr_subline);
-
-                int subline_idx = wps_subline_index(data, line,
-                                               data->lines[line].curr_subline);
-
-                /* only use this subline if subline time > 0 */
-                if (data->sublines[subline_idx].time_mult > 0)
-                {
-                    new_subline_refresh = true;
-                    data->lines[line].subline_expire_time = (reset_subline ?
-                        current_tick : data->lines[line].subline_expire_time) +
-                        TIMEOUT_UNIT*data->sublines[subline_idx].time_mult;
-                    break;
-                }
-            }
-        }
-    }
-
-    return new_subline_refresh;
-}
-
-/* Display a line appropriately according to its alignment format.
-   format_align contains the text, separated between left, center and right.
-   line is the index of the line on the screen.
-   scroll indicates whether the line is a scrolling one or not.
-*/
-static void write_line(struct screen *display,
-                       struct align_pos *format_align,
-                       int line,
-                       bool scroll)
-{
-    int left_width = 0, left_xpos;
-    int center_width = 0, center_xpos;
-    int right_width = 0,  right_xpos;
-    int ypos;
-    int space_width;
-    int string_height;
-    int scroll_width;
-
-    /* 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);
-    }
-
-    if (format_align->right != 0) {
-        display->getstringsize((unsigned char *)format_align->right,
-                                &right_width, &string_height);
-    }
-
-    if (format_align->center != 0) {
-        display->getstringsize((unsigned char *)format_align->center,
-                                &center_width, &string_height);
-    }
-
-    left_xpos = 0;
-    right_xpos = (display->getwidth() - right_width);
-    center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
-
-    scroll_width = display->getwidth() - left_xpos;
-
-    /* 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;
-        /* 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;
-        /* 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->getwidth() - 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->getwidth() - 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;
-        /* 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_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;
-        /* there is no right string anymore */
-        right_width = 0;
-    }
-
-    ypos = (line * string_height);
-
-
-    if (scroll && ((left_width > scroll_width) || 
-                   (center_width > scroll_width) ||
-                   (right_width > scroll_width)))
-    {
-        display->puts_scroll(0, line,
-                             (unsigned char *)format_align->left);
-    }
-    else
-    {
-#ifdef HAVE_LCD_BITMAP
-        /* clear the line first */
-        display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
-        display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
-        display->set_drawmode(DRMODE_SOLID);
-#endif
-
-        /* 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);
-        }
-    }
-}
-
-bool gui_wps_redraw(struct gui_wps *gwps,
-                     int ffwd_offset,
-                     unsigned refresh_mode)
-{
-    struct wps_data *data = gwps->data;
-    struct screen *display = gwps->display;
-    struct wps_state *state = gwps->state;
-
-    if (!data || !state || !display)
-        return false;
-
-    struct mp3entry *id3 = state->id3;
-
-    if (!id3)
-        return false;
-
-    int v, line, i, subline_idx;
-    unsigned flags;
-    char linebuf[MAX_PATH];
-
-    struct align_pos align;
-    align.left = NULL;
-    align.center = NULL;
-    align.right = NULL;
-
-    bool update_line, new_subline_refresh;
-
-#ifdef HAVE_LCD_BITMAP
-
-    /* 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;
-
-#endif
-
-    /* reset to first subline if refresh all flag is set */
-    if (refresh_mode == WPS_REFRESH_ALL)
-    {
-        display->set_viewport(&data->viewports[0].vp);
-        display->clear_viewport();
-
-        for (i = 0; i <= data->num_lines; i++)
-        {
-            data->lines[i].curr_subline = 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
-
-    state->ff_rewind_count = ffwd_offset;
-
-    /* disable any viewports which are conditionally displayed */
-    for (v = 0; v < data->num_viewports; v++)
-    {
-        if (data->viewports[v].hidden_flags&VP_DRAW_HIDEABLE)
-        {
-            if (data->viewports[v].hidden_flags&VP_DRAW_HIDDEN)
-                data->viewports[v].hidden_flags |= VP_DRAW_WASHIDDEN;
-            else
-                data->viewports[v].hidden_flags |= VP_DRAW_HIDDEN;
-        }
-    }
-    for (v = 0; v < data->num_viewports; v++)
-    {
-        struct wps_viewport *wps_vp = &(data->viewports[v]);
-        unsigned vp_refresh_mode = refresh_mode;
-        display->set_viewport(&wps_vp->vp);
-
-#ifdef HAVE_LCD_BITMAP
-        /* Set images to not to be displayed */
-        for (i = 0; i < MAX_IMAGES; i++)
-        {
-            data->img[i].display = -1;
-        }
-#endif
-        /* dont redraw the viewport if its disabled */
-        if ((wps_vp->hidden_flags&VP_DRAW_HIDDEN))
-        {
-            if (!(wps_vp->hidden_flags&VP_DRAW_WASHIDDEN))
-                display->scroll_stop(&wps_vp->vp);
-            wps_vp->hidden_flags |= VP_DRAW_WASHIDDEN;
-            continue;
-        }
-        else if (((wps_vp->hidden_flags&
-                   (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
-                    == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
-        {
-            vp_refresh_mode = WPS_REFRESH_ALL;
-            wps_vp->hidden_flags = VP_DRAW_HIDEABLE;
-        }
-        if (vp_refresh_mode == WPS_REFRESH_ALL)
-        {
-            display->clear_viewport();
-        }
-            
-        for (line = wps_vp->first_line; 
-             line <= wps_vp->last_line; line++)
-        {
-            memset(linebuf, 0, sizeof(linebuf));
-            update_line = false;
-
-            /* get current subline for the line */
-            new_subline_refresh = update_curr_subline(gwps, line);
-
-            subline_idx = wps_subline_index(data, line,
-                                            data->lines[line].curr_subline);
-            flags = data->sublines[subline_idx].line_type;
-
-            if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
-                || new_subline_refresh)
-            {
-                /* get_line tells us if we need to update the line */
-                update_line = get_line(gwps, line, data->lines[line].curr_subline,
-                                       &align, linebuf, sizeof(linebuf));
-            }
-#ifdef HAVE_LCD_BITMAP
-            /* peakmeter */
-            if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
-            {
-                /* the peakmeter should be alone on its line */
-                update_line = false;
-
-                int h = font_get(wps_vp->vp.font)->height;
-                int peak_meter_y = (line - wps_vp->first_line)* h;
-
-                /* 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->getheight()) {
-                    /* found a line with a peak meter -> remember that we must
-                        enable it later */
-                    enable_pm = true;
-                    peak_meter_enabled = true;
-                    peak_meter_screen(gwps->display, 0, peak_meter_y,
-                                      MIN(h, display->getheight() - peak_meter_y));
-                }
-                else
-                {
-                    peak_meter_enabled = false;
-                }
-            }
-
-#else /* HAVE_LCD_CHARCELL */
-
-            /* progressbar */
-            if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
-            {
-                if (data->full_line_progressbar)
-                    draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
-                else
-                    draw_player_progress(gwps);
-            }
-#endif
-
-            if (update_line && 
-                /* conditionals clear the line which means if the %Vd is put into the default
-                   viewport there will be a blank line.
-                   To get around this we dont allow any actual drawing to happen in the
-                   deault vp if other vp's are defined */
-                ((data->num_viewports>1 && v!=0) || data->num_viewports == 1))
-            {
-                if (flags & WPS_REFRESH_SCROLL)
-                {
-                    /* if the line is a scrolling one we don't want to update
-                       too often, so that it has the time to scroll */
-                    if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
-                        write_line(display, &align, line - wps_vp->first_line, true);
-                }
-                else
-                    write_line(display, &align, line - wps_vp->first_line, false);
-            }
-        }
-
-#ifdef HAVE_LCD_BITMAP
-        /* progressbar */
-        if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
-        {
-            if (wps_vp->pb)
-            {
-                draw_progressbar(gwps, wps_vp);
-            }
-        }
-        /* Now display any images in this viewport */
-        wps_display_images(gwps, &wps_vp->vp);
-#endif
-    }
-
-#ifdef HAVE_LCD_BITMAP
-    data->peak_meter_enabled = enable_pm;
-#endif
-
-    if (refresh_mode & WPS_REFRESH_STATUSBAR)
-    {
-        gwps_draw_statusbars();
-    }
-    /* Restore the default viewport */
-    display->set_viewport(NULL);
-
-    display->update();
-
-    return true;
-}
diff --git a/apps/gui/gwps-common.h b/apps/gui/gwps-common.h
deleted file mode 100644
index 0d196b2..0000000
--- a/apps/gui/gwps-common.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/***************************************************************************
- *             __________               __   ___.
- *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
- *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
- *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
- *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
- *                     \/            \/     \/    \/            \/
- * $Id$
- *
- * Copyright (C) 2002 Björn Stenberg
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- ****************************************************************************/
-#ifndef _GWPS_COMMON_
-#define _GWPS_COMMON_
-#include <stdbool.h>
-#include <sys/types.h> /* for size_t */
-
-#include "gwps.h"
-
-/* fades the volume, e.g. on pause or stop */
-void fade(bool fade_in, bool updatewps);
-
-/* Initially display the wps, can fall back to the built-in wps
- * if the chosen wps is invalid.
- *
- * Return true on success, otherwise false */
-bool gui_wps_display(struct gui_wps *gui_wps);
-
-/* return true if screen restore is needed
-   return false otherwise */
-bool update_onvol_change(struct gui_wps * gwps);
-
-/* Update track info related stuff, handle cue sheets as well, and redraw */
-bool gui_wps_update(struct gui_wps *gwps);
-
-bool ffwd_rew(int button);
-void display_keylock_text(bool locked);
-
-/* Refresh the WPS according to refresh_mode.
- *
- * Return true on success, otherwise false */
-bool gui_wps_redraw(struct gui_wps *gwps,
-                     int ffwd_offset,
-                     unsigned refresh_mode);
-#endif
diff --git a/apps/gui/gwps.c b/apps/gui/music_screen.c
similarity index 80%
rename from apps/gui/gwps.c
rename to apps/gui/music_screen.c
index 10c2a68..39d5e0e 100644
--- a/apps/gui/gwps.c
+++ b/apps/gui/music_screen.c
@@ -34,8 +34,8 @@
 #include "debug.h"
 #include "sprintf.h"
 #include "settings.h"
-#include "gwps.h"
-#include "gwps-common.h"
+#include "wps_engine/wps_engine.h"
+#include "mp3_playback.h"
 #include "audio.h"
 #include "usb.h"
 #include "status.h"
@@ -66,6 +66,7 @@
 #include "option_select.h"
 #include "dsp.h"
 #include "playlist_viewer.h"
+#include "music_screen.h"
 
 #define RESTORE_WPS_INSTANTLY       0l
 #define RESTORE_WPS_NEXT_SECOND     ((long)(HZ+current_tick))
@@ -84,6 +85,234 @@
 static void nextid3available_callback(void* param);
 
 
+
+#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
+
+bool wps_fading_out = false;
+void fade(bool fade_in, bool updatewps)
+{
+    int fp_global_vol = global_settings.volume << 8;
+    int fp_min_vol = sound_min(SOUND_VOLUME) << 8;
+    int fp_step = (fp_global_vol - fp_min_vol) / 30;
+    int i;
+    wps_fading_out = !fade_in;
+    if (fade_in) {
+        /* fade in */
+        int fp_volume = fp_min_vol;
+
+        /* zero out the sound */
+        sound_set_volume(fp_min_vol >> 8);
+
+        sleep(HZ/10); /* let audio thread run */
+        audio_resume();
+        
+        while (fp_volume < fp_global_vol - fp_step) {
+            fp_volume += fp_step;
+            sound_set_volume(fp_volume >> 8);
+            if (updatewps)
+            {
+                FOR_NB_SCREENS(i)
+                    gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
+            }
+            sleep(1);
+        }
+        sound_set_volume(global_settings.volume);
+    }
+    else {
+        /* fade out */
+        int fp_volume = fp_global_vol;
+
+        while (fp_volume > fp_min_vol + fp_step) {
+            fp_volume -= fp_step;
+            sound_set_volume(fp_volume >> 8);
+            if (updatewps)
+            {
+                FOR_NB_SCREENS(i)
+                    gui_wps_redraw(&gui_wps[i], 0, WPS_REFRESH_NON_STATIC);
+            }
+            sleep(1);
+        }
+        audio_pause();
+        wps_fading_out = false;
+#if CONFIG_CODEC != SWCODEC
+#ifndef SIMULATOR
+        /* let audio thread run and wait for the mas to run out of data */
+        while (!mp3_pause_done())
+#endif
+            sleep(HZ/10);
+#endif
+
+        /* reset volume to what it was before the fade */
+        sound_set_volume(global_settings.volume);
+    }
+}
+bool is_wps_fading(void)
+{
+    return wps_fading_out;
+}
+
+bool update_onvol_change(struct gui_wps * gwps)
+{
+    gui_wps_redraw(gwps, 0, WPS_REFRESH_NON_STATIC);
+
+#ifdef HAVE_LCD_CHARCELLS
+    splashf(0, "Vol: %3d dB",
+               sound_val2phys(SOUND_VOLUME, global_settings.volume));
+    return true;
+#endif
+    return false;
+}
+
+bool ffwd_rew(int button)
+{
+    unsigned int step = 0;     /* current ff/rewind step */ 
+    unsigned int max_step = 0; /* maximum ff/rewind step */ 
+    int ff_rewind_count = 0;   /* current ff/rewind count (in ticks) */
+    int direction = -1;         /* forward=1 or backward=-1 */
+    bool exit = false;
+    bool usb = false;
+    int i = 0;
+    const long ff_rw_accel = (global_settings.ff_rewind_accel + 3);
+
+    if (button == ACTION_NONE)
+    {
+        status_set_ffmode(0);
+        return usb;
+    }
+    while (!exit)
+    {
+        switch ( button )
+        {
+            case ACTION_WPS_SEEKFWD:
+                 direction = 1;
+            case ACTION_WPS_SEEKBACK:
+                if (wps_state.ff_rewind)
+                {
+                    if (direction == 1)
+                    {
+                        /* fast forwarding, calc max step relative to end */
+                        max_step = (wps_state.id3->length - 
+                                    (wps_state.id3->elapsed +
+                                     ff_rewind_count)) *
+                                     FF_REWIND_MAX_PERCENT / 100;
+                    }
+                    else
+                    {
+                        /* rewinding, calc max step relative to start */
+                        max_step = (wps_state.id3->elapsed + ff_rewind_count) *
+                                    FF_REWIND_MAX_PERCENT / 100;
+                    }
+
+                    max_step = MAX(max_step, MIN_FF_REWIND_STEP);
+
+                    if (step > max_step)
+                        step = max_step;
+
+                    ff_rewind_count += step * direction;
+
+                    /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
+                    step += step >> ff_rw_accel;
+                }
+                else
+                {
+                    if ( (audio_status() & AUDIO_STATUS_PLAY) &&
+                          wps_state.id3 && wps_state.id3->length )
+                    {
+                        if (!wps_state.paused)
+#if (CONFIG_CODEC == SWCODEC)
+                            audio_pre_ff_rewind();
+#else
+                            audio_pause();
+#endif
+#if CONFIG_KEYPAD == PLAYER_PAD
+                        FOR_NB_SCREENS(i)
+                            gui_wps[i].display->stop_scroll();
+#endif
+                        if (direction > 0) 
+                            status_set_ffmode(STATUS_FASTFORWARD);
+                        else
+                            status_set_ffmode(STATUS_FASTBACKWARD);
+
+                        wps_state.ff_rewind = true;
+
+                        step = 1000 * global_settings.ff_rewind_min_step;
+                    }
+                    else
+                        break;
+                }
+
+                if (direction > 0) {
+                    if ((wps_state.id3->elapsed + ff_rewind_count) >
+                        wps_state.id3->length)
+                        ff_rewind_count = wps_state.id3->length -
+                            wps_state.id3->elapsed;
+                }
+                else {
+                    if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0)
+                        ff_rewind_count = -wps_state.id3->elapsed;
+                }
+
+                FOR_NB_SCREENS(i)
+                    gui_wps_redraw(&gui_wps[i],
+                                    (wps_state.wps_time_countup == false)?
+                                    ff_rewind_count:-ff_rewind_count,
+                                    WPS_REFRESH_PLAYER_PROGRESS |
+                                    WPS_REFRESH_DYNAMIC);
+
+                break;
+
+            case ACTION_WPS_STOPSEEK:
+                wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count;
+                audio_ff_rewind(wps_state.id3->elapsed);
+                ff_rewind_count = 0;
+                wps_state.ff_rewind = false;
+                status_set_ffmode(0);
+#if (CONFIG_CODEC != SWCODEC)
+                if (!wps_state.paused)
+                    audio_resume();
+#endif
+#ifdef HAVE_LCD_CHARCELLS
+                FOR_NB_SCREENS(i)
+                    gui_wps_redraw(&gui_wps[i],0, WPS_REFRESH_ALL);
+#endif
+                exit = true;
+                break;
+
+            default:
+                if(default_event_handler(button) == SYS_USB_CONNECTED) {
+                    status_set_ffmode(0);
+                    usb = true;
+                    exit = true;
+                }
+                break;
+        }
+        if (!exit)
+        {
+            button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK);
+#ifdef HAVE_TOUCHSCREEN
+            if (button == ACTION_TOUCHSCREEN)
+                button = wps_get_touchaction(gui_wps[SCREEN_MAIN].data);
+#endif
+        }        
+    }
+    return usb;
+}
+
+
+void display_keylock_text(bool locked)
+{
+    int i;
+    FOR_NB_SCREENS(i)
+        gui_wps[i].display->stop_scroll();
+
+    splash(HZ, locked ? ID2P(LANG_KEYLOCK_ON) : ID2P(LANG_KEYLOCK_OFF));
+}
+
+
+
+
 #if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD)
 static void gwps_caption_backlight(struct wps_state *state)
 {
diff --git a/apps/gui/music_screen.h b/apps/gui/music_screen.h
new file mode 100644
index 0000000..90614cb
--- /dev/null
+++ b/apps/gui/music_screen.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: gwps-common.h 20492 2009-03-23 17:19:48Z alle $
+ *
+ * Copyright (C) 2002 Björn Stenberg
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _MUSICSCREEN_H_
+#define _MUSICSCREEN_H_
+#include <stdbool.h>
+ 
+long gui_wps_show(void);
+ 
+ 
+void gui_sync_wps_init(void);
+
+/* fades the volume, e.g. on pause or stop */
+void fade(bool fade_in, bool updatewps);
+
+bool ffwd_rew(int button);
+void display_keylock_text(bool locked);
+
+bool is_wps_fading(void);
+#endif
diff --git a/apps/gui/gwps.h b/apps/gui/wps_engine/gwps.h
similarity index 100%
rename from apps/gui/gwps.h
rename to apps/gui/wps_engine/gwps.h
diff --git a/apps/gui/wps_debug.c b/apps/gui/wps_engine/wps_debug.c
similarity index 99%
rename from apps/gui/wps_debug.c
rename to apps/gui/wps_engine/wps_debug.c
index 727d907..a89f61a 100644
--- a/apps/gui/wps_debug.c
+++ b/apps/gui/wps_engine/wps_debug.c
@@ -23,7 +23,7 @@
 
 #include <stdio.h>
 #include <string.h>
-#include "gwps.h"
+#include "wps_internals.h"
 #ifdef __PCTOOL__
 #ifdef WPSEDITOR
 #include "proxy.h"
diff --git a/apps/gui/wps_engine/wps_display.c b/apps/gui/wps_engine/wps_display.c
new file mode 100644
index 0000000..c74e2ce
--- /dev/null
+++ b/apps/gui/wps_engine/wps_display.c
@@ -0,0 +1,1099 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2002-2007 Björn Stenberg
+ * Copyright (C) 2007-2008 Nicolas Pennequin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "font.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "system.h"
+#include "settings.h"
+#include "settings_list.h"
+#include "rbunicode.h"
+#include "rtc.h"
+#include "audio.h"
+#include "status.h"
+#include "power.h"
+#include "powermgmt.h"
+#include "sound.h"
+#include "debug.h"
+#ifdef HAVE_LCD_CHARCELLS
+#include "hwcompat.h"
+#endif
+#include "abrepeat.h"
+#include "mp3_playback.h"
+#include "lang.h"
+#include "misc.h"
+#include "splash.h"
+#include "scrollbar.h"
+#include "led.h"
+#include "lcd.h"
+#ifdef HAVE_LCD_BITMAP
+#include "peakmeter.h"
+/* Image stuff */
+#include "bmp.h"
+#include "albumart.h"
+#endif
+#include "dsp.h"
+#include "action.h"
+#include "cuesheet.h"
+#include "playlist.h"
+#if CONFIG_CODEC == SWCODEC
+#include "playback.h"
+#endif
+#include "backdrop.h"
+#include "viewport.h"
+
+
+#include "wps_internals.h"
+#include "wps_engine.h"
+
+bool gui_wps_display(struct gui_wps *gwps)
+{
+    struct screen *display = gwps->display;
+    struct wps_data *data = gwps->data;
+    int screen = display->screen_type;
+
+    /* Update the values in the first (default) viewport - in case the user
+       has modified the statusbar or colour settings */
+#if LCD_DEPTH > 1
+    if (display->depth > 1)
+    {
+        data->viewports[0].vp.fg_pattern = display->get_foreground();
+        data->viewports[0].vp.bg_pattern = display->get_background();
+    }
+#endif
+    display->clear_display();
+    if (!data->wps_loaded) {
+        if ( !data->num_tokens ) {
+            /* set the default wps for the main-screen */
+            if(screen == SCREEN_MAIN)
+            {
+#if LCD_DEPTH > 1
+                unload_wps_backdrop();
+#endif
+                wps_data_load(data,
+                              display,
+#ifdef HAVE_LCD_BITMAP
+                              "%s%?it<%?in<%in. |>%it|%fn>\n"
+                              "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
+                              "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
+                              "\n"
+                              "%al%pc/%pt%ar[%pp:%pe]\n"
+                              "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
+                              "%pb\n"
+                              "%pm\n", false);
+#else
+                              "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
+                              "%pc%?ps<*|/>%pt\n", false);
+#endif
+            }
+#ifdef HAVE_REMOTE_LCD
+             /* set the default wps for the remote-screen */
+             else if(screen == SCREEN_REMOTE)
+             {
+#if LCD_REMOTE_DEPTH > 1
+                 unload_remote_wps_backdrop();
+#endif
+                 wps_data_load(data,
+                               display,
+                               "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
+                               "%s%?it<%?in<%in. |>%it|%fn>\n"
+                               "%al%pc/%pt%ar[%pp:%pe]\n"
+                               "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
+                               "%pb\n", false);
+             }
+#endif
+        }
+    }
+    else
+    {
+#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
+        if (screen == SCREEN_REMOTE)
+            show_remote_wps_backdrop();
+        else if (screen == SCREEN_MAIN)
+#endif
+#if LCD_DEPTH > 1
+            show_wps_backdrop();
+#endif
+    }
+    return gui_wps_redraw(gwps, 0, WPS_REFRESH_ALL);
+}
+
+bool gui_wps_update(struct gui_wps *gwps)
+{
+    struct mp3entry *id3 = gwps->state->id3;
+    bool retval;
+    bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
+    gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update;
+    retval = gui_wps_redraw(gwps, 0,
+                            gwps->state->do_full_update ? 
+                                        WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC);
+    return retval;
+}
+
+
+#ifdef HAVE_LCD_BITMAP
+
+static void draw_progressbar(struct gui_wps *gwps,
+                             struct wps_viewport *wps_vp)
+ {
+    struct screen *display = gwps->display;
+    struct wps_state *state = gwps->state;
+    struct progressbar *pb = wps_vp->pb;
+    int y = pb->y;
+
+    if (y < 0)
+    {
+        int line_height = font_get(wps_vp->vp.font)->height;
+        /* center the pb in the line, but only if the line is higher than the pb */
+        int center = (line_height-pb->height)/2;
+        /* if Y was not set calculate by font height,Y is -line_number-1 */
+        y = (-y -1)*line_height + (0 > center ? 0 : center);
+    }
+
+    if (pb->have_bitmap_pb)
+        gui_bitmap_scrollbar_draw(display, pb->bm,
+                                  pb->x, y, pb->width, pb->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, pb->x, y, pb->width, pb->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() && state->id3->length != 0 )
+        ab_draw_markers(display, state->id3->length,
+                        pb->x, pb->x + pb->width, y, pb->height);
+#endif
+
+    if (state->id3->cuesheet)
+        cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
+                         pb->x, pb->x + pb->width, y+1, pb->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].subimage_height);
+    gwps->display->set_drawmode(DRMODE_SOLID);
+}
+
+static void wps_draw_image(struct gui_wps *gwps, int n, int subimage)
+{
+    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_part(data->img[n].bm.data, 
+                                  0, data->img[n].subimage_height * subimage,
+                                  data->img[n].bm.width, data->img[n].x,
+                                  data->img[n].y, data->img[n].bm.width,
+                                  data->img[n].subimage_height);
+#if LCD_DEPTH > 1
+    } else {
+        display->transparent_bitmap_part((fb_data *)data->img[n].bm.data,
+                                         0, data->img[n].subimage_height * subimage,
+                                         data->img[n].bm.width, data->img[n].x,
+                                         data->img[n].y, data->img[n].bm.width,
+                                         data->img[n].subimage_height);
+    }
+#endif
+}
+
+static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
+{
+    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 (data->img[n].display >= 0)
+            {
+                wps_draw_image(gwps, n, data->img[n].display);
+            } else if (data->img[n].always_display && data->img[n].vp == vp)
+            {
+                wps_draw_image(gwps, n, 0);
+            }
+        }
+    }
+    display->set_drawmode(DRMODE_SOLID);
+}
+
+#else /* HAVE_LCD_CHARCELL */
+
+static bool draw_player_progress(struct gui_wps *gwps)
+{
+    struct wps_state *state = gwps->state;
+    struct screen *display = gwps->display;
+    unsigned char progress_pattern[7];
+    int pos = 0;
+    int i;
+
+    if (!state->id3)
+        return false;
+
+    if (state->id3->length)
+        pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
+              / state->id3->length;
+
+    for (i = 0; i < 7; i++, pos -= 5)
+    {
+        if (pos <= 0)
+            progress_pattern[i] = 0x1fu;
+        else if (pos >= 5)
+            progress_pattern[i] = 0x00u;
+        else
+            progress_pattern[i] = 0x1fu >> pos;
+    }
+
+    display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
+    return true;
+}
+
+static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
+{
+    static const unsigned char numbers[10][4] = {
+        {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
+        {0x04, 0x0c, 0x04, 0x04}, /* 1 */
+        {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
+        {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
+        {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
+        {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
+        {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
+        {0x0e, 0x02, 0x04, 0x08}, /* 7 */
+        {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
+        {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
+    };
+
+    struct wps_state *state = gwps->state;
+    struct screen *display = gwps->display;
+    struct wps_data *data = gwps->data;
+    unsigned char progress_pattern[7];
+    char timestr[10];
+    int time;
+    int time_idx = 0;
+    int pos = 0;
+    int pat_idx = 1;
+    int digit, i, j;
+    bool softchar;
+
+    if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
+        return;
+
+    time = state->id3->elapsed + state->ff_rewind_count;
+    if (state->id3->length)
+        pos = 55 * time / state->id3->length;
+
+    memset(timestr, 0, sizeof(timestr));
+    format_time(timestr, sizeof(timestr)-2, time);
+    timestr[strlen(timestr)] = ':';   /* always safe */
+
+    for (i = 0; i < 11; i++, pos -= 5)
+    {
+        softchar = false;
+        memset(progress_pattern, 0, sizeof(progress_pattern));
+        
+        if ((digit = timestr[time_idx]))
+        {
+            softchar = true;
+            digit -= '0';
+
+            if (timestr[time_idx + 1] == ':')  /* ones, left aligned */
+            {   
+                memcpy(progress_pattern, numbers[digit], 4);
+                time_idx += 2;
+            }
+            else  /* tens, shifted right */
+            {
+                for (j = 0; j < 4; j++)
+                    progress_pattern[j] = numbers[digit][j] >> 1;
+
+                if (time_idx > 0)  /* not the first group, add colon in front */
+                {
+                    progress_pattern[1] |= 0x10u;
+                    progress_pattern[3] |= 0x10u;
+                }
+                time_idx++;
+            }
+
+            if (pos >= 5)
+                progress_pattern[5] = progress_pattern[6] = 0x1fu;
+        }
+
+        if (pos > 0 && pos < 5)
+        {
+            softchar = true;
+            progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
+        }
+
+        if (softchar && pat_idx < 8)
+        {
+            display->define_pattern(data->wps_progress_pat[pat_idx],
+                                    progress_pattern);
+            buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
+            pat_idx++;
+        }
+        else if (pos <= 0)
+            buf = utf8encode(' ', buf);
+        else
+            buf = utf8encode(0xe115, buf); /* 2/7 _ */
+    }
+    *buf = '\0';
+}
+
+#endif /* HAVE_LCD_CHARCELL */
+
+/* Return the index to the end token for the conditional token at index.
+   The conditional token can be either a start token or a separator
+   (i.e. option) token.
+*/
+static int find_conditional_end(struct wps_data *data, int index)
+{
+    int ret = index;
+    while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
+        ret = data->tokens[ret].value.i;
+
+    /* ret now is the index to the end token for the conditional. */
+    return ret;
+}
+
+/* Evaluate the conditional that is at *token_index and return whether a skip
+   has ocurred. *token_index is updated with the new position.
+*/
+static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
+{
+    if (!gwps)
+        return false;
+
+    struct wps_data *data = gwps->data;
+
+    int i, cond_end;
+    int cond_index = *token_index;
+    char result[128];
+    const char *value;
+    unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
+    unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
+
+    /* treat ?xx<true> constructs as if they had 2 options. */
+    if (num_options < 2)
+        num_options = 2;
+
+    int intval = num_options;
+    /* get_token_value needs to know the number of options in the enum */
+    value = get_token_value(gwps, &data->tokens[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 is -1, we check if value is empty. */
+    if (intval == -1)
+        intval = (value && *value) ? 1 : num_options;
+    else if (intval > num_options || intval < 1)
+        intval = num_options;
+
+    data->tokens[cond_index].value.i = (intval << 8) + num_options;
+
+    /* skip to the appropriate enum case */
+    int next = cond_index + 2;
+    for (i = 1; i < intval; i++)
+    {
+        next = data->tokens[next].value.i;
+    }
+    *token_index = next;
+
+    if (prev_val == intval)
+    {
+        /* Same conditional case as previously. Return without clearing the
+           pictures */
+        return false;
+    }
+
+    cond_end = find_conditional_end(data, cond_index + 2);
+    for (i = cond_index + 3; i < cond_end; i++)
+    {
+#ifdef HAVE_LCD_BITMAP
+        /* clear all pictures in the conditional and nested ones */
+        if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
+            clear_image_pos(gwps, data->tokens[i].value.i & 0xFF);
+#endif
+#ifdef HAVE_ALBUMART
+        if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
+            draw_album_art(gwps, audio_current_aa_hid(), true);
+#endif
+    }
+
+    return true;
+}
+
+/* Read a (sub)line to the given alignment format buffer.
+   linebuf is the buffer where the data is actually stored.
+   align is the alignment format that'll be used to display the text.
+   The return value indicates whether the line needs to be updated.
+*/
+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;
+
+    char temp_buf[128];
+    char *buf = linebuf;  /* will always point to the writing position */
+    char *linebuf_end = linebuf + linebuf_size - 1;
+    int i, last_token_idx;
+    bool update = false;
+
+    /* alignment-related variables */
+    int cur_align;
+    char* cur_align_start;
+    cur_align_start = buf;
+    cur_align = WPS_ALIGN_LEFT;
+    align->left = NULL;
+    align->center = NULL;
+    align->right = NULL;
+
+    /* Process all tokens of the desired subline */
+    last_token_idx = wps_last_token_index(data, line, subline);
+    for (i = wps_first_token_index(data, line, subline);
+         i <= last_token_idx; i++)
+    {
+        switch(data->tokens[i].type)
+        {
+            case WPS_TOKEN_CONDITIONAL:
+                /* place ourselves in the right conditional case */
+                update |= evaluate_conditional(gwps, &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 = data->tokens[i].value.i & 0xFF;
+                int subimage = data->tokens[i].value.i >> 8;
+
+                if (n >= 0 && n < MAX_IMAGES && img[n].loaded)
+                    img[n].display = subimage;
+                break;
+            }
+#endif
+
+            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;
+            case WPS_VIEWPORT_ENABLE:
+            {
+                char label = data->tokens[i].value.i;
+                int j;
+                char temp = VP_DRAW_HIDEABLE;
+                for(j=0;j<data->num_viewports;j++)
+                {
+                    temp = VP_DRAW_HIDEABLE;
+                    if ((data->viewports[j].hidden_flags&VP_DRAW_HIDEABLE) &&
+                        (data->viewports[j].label == label))
+                    {
+                        if (data->viewports[j].hidden_flags&VP_DRAW_WASHIDDEN)
+                            temp |= VP_DRAW_WASHIDDEN;
+                        data->viewports[j].hidden_flags = temp;
+                    }
+                }
+            }
+                break;
+            default:
+            {
+                /* get the value of the tag and copy it to the buffer */
+                const char *value = get_token_value(gwps, &data->tokens[i],
+                                              temp_buf, sizeof(temp_buf), NULL);
+                if (value)
+                {
+                    update = true;
+                    while (*value && (buf < linebuf_end))
+                        *buf++ = *value++;
+                }
+                break;
+            }
+        }
+    }
+
+    /* 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;
+    }
+
+    return update;
+}
+
+static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
+{
+    struct wps_data *data = gwps->data;
+    int i;
+    int subline_idx = wps_subline_index(data, line, subline);
+    int last_token_idx = wps_last_token_index(data, line, subline);
+
+    data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
+
+    for (i = wps_first_token_index(data, line, subline);
+         i <= last_token_idx; i++)
+    {
+        switch(data->tokens[i].type)
+        {
+            case WPS_TOKEN_CONDITIONAL:
+                /* place ourselves in the right conditional case */
+                evaluate_conditional(gwps, &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;
+
+            case WPS_TOKEN_SUBLINE_TIMEOUT:
+                data->sublines[subline_idx].time_mult = data->tokens[i].value.i;
+                break;
+
+            default:
+                break;
+        }
+    }
+}
+
+/* Calculates which subline should be displayed for the specified line
+   Returns true iff the subline must be refreshed */
+static bool update_curr_subline(struct gui_wps *gwps, int line)
+{
+    struct wps_data *data = gwps->data;
+
+    int search, search_start, num_sublines;
+    bool reset_subline;
+    bool new_subline_refresh;
+    bool only_one_subline;
+
+    num_sublines = data->lines[line].num_sublines;
+    reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET);
+    new_subline_refresh = false;
+    only_one_subline = false;
+
+    /* if time to advance to next sub-line  */
+    if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 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->lines[line].curr_subline;
+
+        for (search = 0; search < num_sublines; search++)
+        {
+            data->lines[line].curr_subline++;
+
+            /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
+            if (data->lines[line].curr_subline == num_sublines)
+            {
+                if (data->lines[line].curr_subline == 1)
+                    only_one_subline = true;
+                data->lines[line].curr_subline = 0;
+            }
+
+            /* if back where we started after search or
+                only one subline is defined on the line */
+            if (((search > 0) &&
+                 (data->lines[line].curr_subline == search_start)) ||
+                only_one_subline)
+            {
+                /* no other subline with a time > 0 exists */
+                data->lines[line].subline_expire_time = (reset_subline ?
+                    current_tick :
+                    data->lines[line].subline_expire_time) + 100 * HZ;
+                break;
+            }
+            else
+            {
+                /* get initial time multiplier for this subline */
+                get_subline_timeout(gwps, line, data->lines[line].curr_subline);
+
+                int subline_idx = wps_subline_index(data, line,
+                                               data->lines[line].curr_subline);
+
+                /* only use this subline if subline time > 0 */
+                if (data->sublines[subline_idx].time_mult > 0)
+                {
+                    new_subline_refresh = true;
+                    data->lines[line].subline_expire_time = (reset_subline ?
+                        current_tick : data->lines[line].subline_expire_time) +
+                        TIMEOUT_UNIT*data->sublines[subline_idx].time_mult;
+                    break;
+                }
+            }
+        }
+    }
+
+    return new_subline_refresh;
+}
+
+/* Display a line appropriately according to its alignment format.
+   format_align contains the text, separated between left, center and right.
+   line is the index of the line on the screen.
+   scroll indicates whether the line is a scrolling one or not.
+*/
+static void write_line(struct screen *display,
+                       struct align_pos *format_align,
+                       int line,
+                       bool scroll)
+{
+    int left_width = 0, left_xpos;
+    int center_width = 0, center_xpos;
+    int right_width = 0,  right_xpos;
+    int ypos;
+    int space_width;
+    int string_height;
+    int scroll_width;
+
+    /* 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);
+    }
+
+    if (format_align->right != 0) {
+        display->getstringsize((unsigned char *)format_align->right,
+                                &right_width, &string_height);
+    }
+
+    if (format_align->center != 0) {
+        display->getstringsize((unsigned char *)format_align->center,
+                                &center_width, &string_height);
+    }
+
+    left_xpos = 0;
+    right_xpos = (display->getwidth() - right_width);
+    center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
+
+    scroll_width = display->getwidth() - left_xpos;
+
+    /* 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;
+        /* 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;
+        /* 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->getwidth() - 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->getwidth() - 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;
+        /* 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_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;
+        /* there is no right string anymore */
+        right_width = 0;
+    }
+
+    ypos = (line * string_height);
+
+
+    if (scroll && ((left_width > scroll_width) || 
+                   (center_width > scroll_width) ||
+                   (right_width > scroll_width)))
+    {
+        display->puts_scroll(0, line,
+                             (unsigned char *)format_align->left);
+    }
+    else
+    {
+#ifdef HAVE_LCD_BITMAP
+        /* clear the line first */
+        display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+        display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
+        display->set_drawmode(DRMODE_SOLID);
+#endif
+
+        /* 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);
+        }
+    }
+}
+
+bool gui_wps_redraw(struct gui_wps *gwps,
+                     int ffwd_offset,
+                     unsigned refresh_mode)
+{
+    struct wps_data *data = gwps->data;
+    struct screen *display = gwps->display;
+    struct wps_state *state = gwps->state;
+
+    if (!data || !state || !display)
+        return false;
+
+    struct mp3entry *id3 = state->id3;
+
+    if (!id3)
+        return false;
+
+    int v, line, i, subline_idx;
+    unsigned flags;
+    char linebuf[MAX_PATH];
+
+    struct align_pos align;
+    align.left = NULL;
+    align.center = NULL;
+    align.right = NULL;
+
+    bool update_line, new_subline_refresh;
+
+#ifdef HAVE_LCD_BITMAP
+
+    /* 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;
+
+#endif
+
+    /* reset to first subline if refresh all flag is set */
+    if (refresh_mode == WPS_REFRESH_ALL)
+    {
+        display->set_viewport(&data->viewports[0].vp);
+        display->clear_viewport();
+
+        for (i = 0; i <= data->num_lines; i++)
+        {
+            data->lines[i].curr_subline = 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
+
+    state->ff_rewind_count = ffwd_offset;
+
+    /* disable any viewports which are conditionally displayed */
+    for (v = 0; v < data->num_viewports; v++)
+    {
+        if (data->viewports[v].hidden_flags&VP_DRAW_HIDEABLE)
+        {
+            if (data->viewports[v].hidden_flags&VP_DRAW_HIDDEN)
+                data->viewports[v].hidden_flags |= VP_DRAW_WASHIDDEN;
+            else
+                data->viewports[v].hidden_flags |= VP_DRAW_HIDDEN;
+        }
+    }
+    for (v = 0; v < data->num_viewports; v++)
+    {
+        struct wps_viewport *wps_vp = &(data->viewports[v]);
+        unsigned vp_refresh_mode = refresh_mode;
+        display->set_viewport(&wps_vp->vp);
+
+#ifdef HAVE_LCD_BITMAP
+        /* Set images to not to be displayed */
+        for (i = 0; i < MAX_IMAGES; i++)
+        {
+            data->img[i].display = -1;
+        }
+#endif
+        /* dont redraw the viewport if its disabled */
+        if ((wps_vp->hidden_flags&VP_DRAW_HIDDEN))
+        {
+            if (!(wps_vp->hidden_flags&VP_DRAW_WASHIDDEN))
+                display->scroll_stop(&wps_vp->vp);
+            wps_vp->hidden_flags |= VP_DRAW_WASHIDDEN;
+            continue;
+        }
+        else if (((wps_vp->hidden_flags&
+                   (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
+                    == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
+        {
+            vp_refresh_mode = WPS_REFRESH_ALL;
+            wps_vp->hidden_flags = VP_DRAW_HIDEABLE;
+        }
+        if (vp_refresh_mode == WPS_REFRESH_ALL)
+        {
+            display->clear_viewport();
+        }
+            
+        for (line = wps_vp->first_line; 
+             line <= wps_vp->last_line; line++)
+        {
+            memset(linebuf, 0, sizeof(linebuf));
+            update_line = false;
+
+            /* get current subline for the line */
+            new_subline_refresh = update_curr_subline(gwps, line);
+
+            subline_idx = wps_subline_index(data, line,
+                                            data->lines[line].curr_subline);
+            flags = data->sublines[subline_idx].line_type;
+
+            if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
+                || new_subline_refresh)
+            {
+                /* get_line tells us if we need to update the line */
+                update_line = get_line(gwps, line, data->lines[line].curr_subline,
+                                       &align, linebuf, sizeof(linebuf));
+            }
+#ifdef HAVE_LCD_BITMAP
+            /* peakmeter */
+            if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
+            {
+                /* the peakmeter should be alone on its line */
+                update_line = false;
+
+                int h = font_get(wps_vp->vp.font)->height;
+                int peak_meter_y = (line - wps_vp->first_line)* h;
+
+                /* 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->getheight()) {
+                    /* found a line with a peak meter -> remember that we must
+                        enable it later */
+                    enable_pm = true;
+                    peak_meter_enabled = true;
+                    peak_meter_screen(gwps->display, 0, peak_meter_y,
+                                      MIN(h, display->getheight() - peak_meter_y));
+                }
+                else
+                {
+                    peak_meter_enabled = false;
+                }
+            }
+
+#else /* HAVE_LCD_CHARCELL */
+
+            /* progressbar */
+            if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+            {
+                if (data->full_line_progressbar)
+                    draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
+                else
+                    draw_player_progress(gwps);
+            }
+#endif
+
+            if (update_line && 
+                /* conditionals clear the line which means if the %Vd is put into the default
+                   viewport there will be a blank line.
+                   To get around this we dont allow any actual drawing to happen in the
+                   deault vp if other vp's are defined */
+                ((data->num_viewports>1 && v!=0) || data->num_viewports == 1))
+            {
+                if (flags & WPS_REFRESH_SCROLL)
+                {
+                    /* if the line is a scrolling one we don't want to update
+                       too often, so that it has the time to scroll */
+                    if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
+                        write_line(display, &align, line - wps_vp->first_line, true);
+                }
+                else
+                    write_line(display, &align, line - wps_vp->first_line, false);
+            }
+        }
+
+#ifdef HAVE_LCD_BITMAP
+        /* progressbar */
+        if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
+        {
+            if (wps_vp->pb)
+            {
+                draw_progressbar(gwps, wps_vp);
+            }
+        }
+        /* Now display any images in this viewport */
+        wps_display_images(gwps, &wps_vp->vp);
+#endif
+    }
+
+#ifdef HAVE_LCD_BITMAP
+    data->peak_meter_enabled = enable_pm;
+#endif
+
+    if (refresh_mode & WPS_REFRESH_STATUSBAR)
+    {
+        gwps_draw_statusbars();
+    }
+    /* Restore the default viewport */
+    display->set_viewport(NULL);
+
+    display->update();
+
+    return true;
+}
diff --git a/apps/gui/wps_engine/wps_engine.h b/apps/gui/wps_engine/wps_engine.h
new file mode 100644
index 0000000..fe034f9
--- /dev/null
+++ b/apps/gui/wps_engine/wps_engine.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: gwps.h 22003 2009-07-22 22:10:25Z kugel $
+ *
+ * Copyright (C) 2007 Nicolas Pennequin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+ 
+ /** Use this for stuff which external code needs to include **/
+ 
+#ifndef _WPS_ENGINE_H
+#define _WPS_ENGINE_H
+#include <stdbool.h>
+#include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */
+
+
+#ifdef HAVE_TOUCHSCREEN
+int wps_get_touchaction(struct wps_data *data);
+#endif
+
+#ifdef HAVE_ALBUMART
+/* gives back if WPS contains an albumart tag */
+bool gui_sync_wps_uses_albumart(void);
+#endif
+
+/* setup and display a WPS for the first time */
+bool gui_wps_display(struct gui_wps *gwps);
+/* do a requested redraw */
+bool gui_wps_redraw(struct gui_wps *gwps,
+                     int ffwd_offset,
+                     unsigned refresh_mode);
+/* do a partial redraw, or full if required, also do any housekeeping
+ * which might be needed */
+bool gui_wps_update(struct gui_wps *gwps);
+ 
+#endif
diff --git a/apps/gui/wps_engine/wps_internals.h b/apps/gui/wps_engine/wps_internals.h
new file mode 100644
index 0000000..581763f
--- /dev/null
+++ b/apps/gui/wps_engine/wps_internals.h
@@ -0,0 +1,556 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2007 Nicolas Pennequin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+ /* This stuff is for the wps engine only.. anyone caught using this outside
+  * of apps/gui/wps_engine will be shot on site! */
+ 
+#ifndef _WPS_ENGINE_INTERNALS_
+#define _WPS_ENGINE_INTERNALS_
+/* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds
+   (possibly with a decimal fraction) but stored as integer values.
+   E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units.
+*/
+#define TIMEOUT_UNIT (HZ/10) /* I.e. 0.1 sec */
+#define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* In TIMEOUT_UNIT's */
+
+
+
+                           
+/* TODO: sort this mess out */
+
+#include "screen_access.h"
+#include "statusbar.h"
+#include "metadata.h"
+
+/* constants used in line_type and as refresh_mode for wps_refresh */
+#define WPS_REFRESH_STATIC          (1u<<0)  /* line doesn't change over time */
+#define WPS_REFRESH_DYNAMIC         (1u<<1)  /* line may change (e.g. time flag) */
+#define WPS_REFRESH_SCROLL          (1u<<2)  /* line scrolls */
+#define WPS_REFRESH_PLAYER_PROGRESS (1u<<3)  /* line contains a progress bar */
+#define WPS_REFRESH_PEAK_METER      (1u<<4)  /* line contains a peak meter */
+#define WPS_REFRESH_STATUSBAR       (1u<<5)  /* refresh statusbar */
+#define WPS_REFRESH_ALL       (0xffffffffu)   /* to refresh all line types */
+
+/* to refresh only those lines that change over time */
+#define WPS_REFRESH_NON_STATIC (WPS_REFRESH_DYNAMIC| \
+                                WPS_REFRESH_PLAYER_PROGRESS| \
+                                WPS_REFRESH_PEAK_METER)
+/* alignments */
+#define WPS_ALIGN_RIGHT 32
+#define WPS_ALIGN_CENTER 64
+#define WPS_ALIGN_LEFT 128
+
+#ifdef HAVE_ALBUMART
+
+/* albumart definitions */
+#define WPS_ALBUMART_NONE           0      /* WPS does not contain AA tag */
+#define WPS_ALBUMART_CHECK          1      /* WPS contains AA conditional tag */
+#define WPS_ALBUMART_LOAD           2      /* WPS contains AA tag */
+
+#define WPS_ALBUMART_ALIGN_RIGHT    1    /* x align:   right */
+#define WPS_ALBUMART_ALIGN_CENTER   2    /* x/y align: center */
+#define WPS_ALBUMART_ALIGN_LEFT     4    /* x align:   left */
+#define WPS_ALBUMART_ALIGN_TOP      1    /* y align:   top */
+#define WPS_ALBUMART_ALIGN_BOTTOM   4    /* y align:   bottom */
+
+#endif /* HAVE_ALBUMART */
+
+/* wps_data*/
+
+#ifdef HAVE_LCD_BITMAP
+struct gui_img {
+    struct bitmap bm;
+    struct viewport* vp;    /* The viewport to display this image in */
+    short int x;                  /* x-pos */
+    short int y;                  /* y-pos */
+    short int num_subimages;      /* number of sub-images */
+    short int subimage_height;    /* height of each sub-image */
+    short int display;            /* -1 for no display, 0..n to display a subimage */
+    bool loaded;            /* load state */
+    bool always_display;    /* not using the preload/display mechanism */
+};
+
+struct progressbar {
+    /* regular pb */
+    short x;
+    /* >=0: explicitly set in the tag -> y-coord within the viewport
+       <0 : not set in the tag -> negated 1-based line number within
+            the viewport. y-coord will be computed based on the font height */
+    short y;
+    short width;
+    short height;
+    /*progressbar image*/
+    struct bitmap bm;
+    bool have_bitmap_pb;
+};
+#endif
+
+
+
+struct align_pos {
+    char* left;
+    char* center;
+    char* right;
+};
+
+#ifdef HAVE_LCD_BITMAP
+
+#define MAX_IMAGES (26*2) /* a-z and A-Z */
+#define MAX_PROGRESSBARS 3
+
+/* The image buffer is big enough to store one full-screen native bitmap,
+   plus two full-screen mono bitmaps. */
+
+#define IMG_BUFSIZE ((LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \
+                   + (2*LCD_HEIGHT*LCD_WIDTH/8))
+
+#define WPS_MAX_VIEWPORTS   24
+#define WPS_MAX_LINES       ((LCD_HEIGHT/5+1) * 2)
+#define WPS_MAX_SUBLINES    (WPS_MAX_LINES*3)
+#define WPS_MAX_TOKENS      1024
+#define WPS_MAX_STRINGS     128
+#define STRING_BUFFER_SIZE  1024
+#define WPS_MAX_COND_LEVEL  10
+
+#else
+
+#define WPS_MAX_VIEWPORTS   2
+#define WPS_MAX_LINES       2
+#define WPS_MAX_SUBLINES    12
+#define WPS_MAX_TOKENS      64
+#define WPS_MAX_STRINGS     32
+#define STRING_BUFFER_SIZE  64
+#define WPS_MAX_COND_LEVEL  5
+
+#endif
+
+#define SUBLINE_RESET -1
+
+enum wps_parse_error {
+    PARSE_OK,
+    PARSE_FAIL_UNCLOSED_COND,
+    PARSE_FAIL_INVALID_CHAR,
+    PARSE_FAIL_COND_SYNTAX_ERROR,
+    PARSE_FAIL_COND_INVALID_PARAM,
+    PARSE_FAIL_LIMITS_EXCEEDED,
+};
+
+enum wps_token_type {
+    WPS_NO_TOKEN,   /* for WPS tags we don't want to save as tokens */
+    WPS_TOKEN_UNKNOWN,
+
+    /* Markers */
+    WPS_TOKEN_CHARACTER,
+    WPS_TOKEN_STRING,
+
+    /* Alignment */
+    WPS_TOKEN_ALIGN_LEFT,
+    WPS_TOKEN_ALIGN_CENTER,
+    WPS_TOKEN_ALIGN_RIGHT,
+
+    /* Sublines */
+    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,
+
+    /* Sound */
+#if (CONFIG_CODEC != MAS3507D)
+    WPS_TOKEN_SOUND_PITCH,
+#endif
+#if (CONFIG_CODEC == SWCODEC)
+    WPS_TOKEN_REPLAYGAIN,
+    WPS_TOKEN_CROSSFADE,
+#endif
+
+    /* Time */
+
+    WPS_TOKEN_RTC_PRESENT,
+
+    /* The begin/end values allow us to know if a token is an RTC one.
+       New RTC tokens should be added between the markers. */
+
+    WPS_TOKENS_RTC_BEGIN, /* just the start marker, not an actual token */
+
+    WPS_TOKEN_RTC_DAY_OF_MONTH,
+    WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,
+    WPS_TOKEN_RTC_12HOUR_CFG,
+    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,
+
+    WPS_TOKENS_RTC_END,     /* just the end marker, not an actual token */
+
+    /* Conditional */
+    WPS_TOKEN_CONDITIONAL,
+    WPS_TOKEN_CONDITIONAL_START,
+    WPS_TOKEN_CONDITIONAL_OPTION,
+    WPS_TOKEN_CONDITIONAL_END,
+
+    /* Database */
+#ifdef HAVE_TAGCACHE
+    WPS_TOKEN_DATABASE_PLAYCOUNT,
+    WPS_TOKEN_DATABASE_RATING,
+    WPS_TOKEN_DATABASE_AUTOSCORE,
+#endif
+
+    /* File */
+    WPS_TOKEN_FILE_BITRATE,
+    WPS_TOKEN_FILE_CODEC,
+    WPS_TOKEN_FILE_FREQUENCY,
+    WPS_TOKEN_FILE_FREQUENCY_KHZ,
+    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
+
+#ifdef HAVE_ALBUMART
+    /* Albumart */
+    WPS_TOKEN_ALBUMART_DISPLAY,
+    WPS_TOKEN_ALBUMART_FOUND,
+#endif
+
+    /* Metadata */
+    WPS_TOKEN_METADATA_ARTIST,
+    WPS_TOKEN_METADATA_COMPOSER,
+    WPS_TOKEN_METADATA_ALBUM_ARTIST,
+    WPS_TOKEN_METADATA_GROUPING,
+    WPS_TOKEN_METADATA_ALBUM,
+    WPS_TOKEN_METADATA_GENRE,
+    WPS_TOKEN_METADATA_DISC_NUMBER,
+    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,
+
+    WPS_TOKEN_MAIN_HOLD,
+
+#ifdef HAS_REMOTE_BUTTON_HOLD
+    WPS_TOKEN_REMOTE_HOLD,
+#endif
+
+    /* Progressbar */
+    WPS_TOKEN_PROGRESSBAR,
+#ifdef HAVE_LCD_CHARCELLS
+    WPS_TOKEN_PLAYER_PROGRESSBAR,
+#endif
+
+#ifdef HAVE_LCD_BITMAP
+    /* Peakmeter */
+    WPS_TOKEN_PEAKMETER,
+#endif
+
+    /* Volume level */
+    WPS_TOKEN_VOLUME,
+
+    /* Current track */
+    WPS_TOKEN_TRACK_ELAPSED_PERCENT,
+    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,
+
+#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
+    /* Virtual LED */
+    WPS_TOKEN_VLED_HDD,
+#endif
+
+    /* Viewport display */
+    WPS_VIEWPORT_ENABLE,
+
+    /* buttons */
+    WPS_TOKEN_BUTTON_VOLUME,
+    WPS_TOKEN_LASTTOUCH,
+
+    /* Setting option */
+    WPS_TOKEN_SETTING,
+};
+
+struct wps_token {
+    unsigned char type; /* enough to store the token type */
+
+    /* Whether the tag (e.g. track name or the album) refers the
+       current or the next song (false=current, true=next) */
+    bool next;
+
+    union {
+        char c;
+        unsigned short i;
+    } value;
+};
+
+/* Description of a subline on the WPS */
+struct wps_subline {
+
+    /* Index of the first token for this subline in the token array.
+       Tokens of this subline end where tokens for the next subline
+       begin. */
+    unsigned short first_token_idx;
+
+    /* Bit or'ed WPS_REFRESH_xxx */
+    unsigned char line_type;
+
+    /* How long the subline should be displayed, in 10ths of sec */
+    unsigned char time_mult;
+};
+
+/* Description of a line on the WPS. A line is a set of sublines.
+   A subline is displayed for a certain amount of time. After that,
+   the next subline of the line is displayed. And so on. */
+struct wps_line {
+
+    /* Number of sublines in this line */
+    signed char num_sublines;
+
+    /* Number (0-based) of the subline within this line currently being displayed */
+    signed char curr_subline;
+
+    /* Index of the first subline of this line in the subline array.
+       Sublines for this line end where sublines for the next line begin. */
+    unsigned short first_subline_idx;
+
+    /* When the next subline of this line should be displayed
+       (absolute time value in ticks) */
+    long subline_expire_time;
+};
+
+#define VP_DRAW_HIDEABLE 0x1
+#define VP_DRAW_HIDDEN   0x2
+#define VP_DRAW_WASHIDDEN  0x4
+struct wps_viewport {
+    struct viewport vp;   /* The LCD viewport struct */
+    struct progressbar *pb;
+    /* Indexes of the first and last lines belonging to this viewport in the 
+       lines[] array */
+    int first_line, last_line;
+    char hidden_flags;
+    char label;
+};
+
+#ifdef HAVE_TOUCHSCREEN
+struct touchregion {
+    struct wps_viewport* wvp;/* The viewport this region is in */
+    short int x;             /* x-pos */
+    short int y;             /* y-pos */
+    short int width;         /* width */
+    short int height;        /* height */
+    bool repeat;             /* requires the area be held for the action */
+    int action;              /* action this button will return */
+};
+#define MAX_TOUCHREGIONS    15
+#endif
+/* wps_data
+   this struct holds all necessary data which describes the
+   viewable content of a wps */
+struct wps_data
+{
+#ifdef HAVE_LCD_BITMAP
+    struct gui_img img[MAX_IMAGES];
+    unsigned char img_buf[IMG_BUFSIZE];
+    unsigned char* img_buf_ptr;
+    int img_buf_free;
+    bool wps_sb_tag;
+    bool show_sb_on_wps;
+
+    struct progressbar progressbar[MAX_PROGRESSBARS];
+    short progressbar_count;
+    
+    bool peak_meter_enabled;
+
+#ifdef HAVE_ALBUMART
+    /* Album art support */
+    unsigned char wps_uses_albumart; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */
+    short albumart_x;
+    short albumart_y;
+    unsigned char albumart_xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */
+    unsigned char albumart_yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */
+    short albumart_max_width;
+    short albumart_max_height;
+
+    int albumart_cond_index;
+#endif
+
+#else /*HAVE_LCD_CHARCELLS */
+    unsigned short wps_progress_pat[8];
+    bool full_line_progressbar;
+#endif
+
+#ifdef HAVE_TOUCHSCREEN
+    struct touchregion touchregion[MAX_TOUCHREGIONS];
+    short touchregion_count;
+#endif
+    
+#ifdef HAVE_REMOTE_LCD
+    bool remote_wps;
+#endif
+
+    /* Number of lines in the WPS. During WPS parsing, this is
+       the index of the line being parsed. */
+    int num_lines;
+
+    /* Number of viewports in the WPS */
+    int num_viewports;
+    struct wps_viewport viewports[WPS_MAX_VIEWPORTS];
+
+    struct wps_line lines[WPS_MAX_LINES];
+
+    /* Total number of sublines in the WPS. During WPS parsing, this is
+       the index of the subline where the parsed tokens are added to. */
+    int num_sublines;
+    struct wps_subline sublines[WPS_MAX_SUBLINES];
+
+    /* Total number of tokens in the WPS. During WPS parsing, this is
+       the index of the token being parsed. */
+    int num_tokens;
+    struct wps_token tokens[WPS_MAX_TOKENS];
+
+    char string_buffer[STRING_BUFFER_SIZE];
+    char *strings[WPS_MAX_STRINGS];
+    int num_strings;
+
+    bool wps_loaded;
+    
+    /* tick the volume button was last pressed */
+    unsigned int button_time_volume;
+};
+
+/* initial setup of wps_data */
+void wps_data_init(struct wps_data *wps_data);
+
+/* to setup up the wps-data from a format-buffer (isfile = false)
+   from a (wps-)file (isfile = true)*/
+bool wps_data_load(struct wps_data *wps_data,
+                   struct screen *display,
+                   const char *buf,
+                   bool isfile);
+
+/* Redraw statusbars if necessary */
+void gwps_draw_statusbars(void);
+
+/* Returns the index of the subline in the subline array
+   line - 0-based line number
+   subline - 0-based subline number within the line
+ */
+int wps_subline_index(struct wps_data *wps_data, int line, int subline);
+
+/* Returns the index of the first subline's token in the token array
+   line - 0-based line number
+   subline - 0-based subline number within the line
+ */
+int wps_first_token_index(struct wps_data *data, int line, int subline);
+
+/* Returns the index of the last subline's token in the token array.
+   line - 0-based line number
+   subline - 0-based subline number within the line
+ */
+int wps_last_token_index(struct wps_data *data, int line, int subline);
+
+/* wps_data end */
+
+/* wps_state
+   holds the data which belongs to the current played track,
+   the track which will be played afterwards, current path to the track
+   and some status infos */
+struct wps_state
+{
+    bool ff_rewind;
+    bool paused;
+    int ff_rewind_count;
+    bool wps_time_countup;
+    struct mp3entry* id3;
+    struct mp3entry* nid3;
+    bool do_full_update;
+};
+
+
+/* change the ff/rew-status
+   if ff_rew = true then we are in skipping mode
+   else we are in normal mode */
+/* void wps_state_update_ff_rew(bool ff_rew); Currently unused */
+
+/* change the tag-information of the current played track
+   and the following track */
+/* void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3); Currently unused */
+/* wps_state end*/
+
+/* gui_wps
+   defines a wps with its data, state,
+   and the screen on which the wps-content should be drawn */
+struct gui_wps
+{
+    struct screen *display;
+    struct wps_data *data;
+    struct wps_state *state;
+};
+
+/* gui_wps end */
+
+
+/* currently only on wps_state is needed */
+extern struct wps_state wps_state;
+extern struct gui_wps gui_wps[NB_SCREENS];
+
+/***** wps_tokens.c ******/
+
+const char *get_token_value(struct gui_wps *gwps,
+                           struct wps_token *token,
+                           char *buf, int buf_size,
+                           int *intval);
+
+#endif
diff --git a/apps/gui/wps_parser.c b/apps/gui/wps_engine/wps_parser.c
similarity index 99%
rename from apps/gui/wps_parser.c
rename to apps/gui/wps_engine/wps_parser.c
index fb0b3ac..15acc14 100644
--- a/apps/gui/wps_parser.c
+++ b/apps/gui/wps_engine/wps_parser.c
@@ -43,7 +43,7 @@
 #include <stdbool.h>
 #include "font.h"
 
-#include "gwps.h"
+#include "wps_internals.h"
 #include "settings.h"
 #include "settings_list.h"
 
diff --git a/apps/gui/wps_engine/wps_tokens.c b/apps/gui/wps_engine/wps_tokens.c
new file mode 100644
index 0000000..3852251
--- /dev/null
+++ b/apps/gui/wps_engine/wps_tokens.c
@@ -0,0 +1,807 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2002-2007 Björn Stenberg
+ * Copyright (C) 2007-2008 Nicolas Pennequin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "font.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "action.h"
+#include "system.h"
+#include "settings.h"
+#include "settings_list.h"
+#include "rbunicode.h"
+#include "timefuncs.h"
+#include "audio.h"
+#include "status.h"
+#include "power.h"
+#include "powermgmt.h"
+#include "sound.h"
+#include "debug.h"
+#ifdef HAVE_LCD_CHARCELLS
+#include "hwcompat.h"
+#endif
+#include "abrepeat.h"
+#include "mp3_playback.h"
+#include "lang.h"
+#include "misc.h"
+#include "led.h"
+#ifdef HAVE_LCD_BITMAP
+/* Image stuff */
+#include "albumart.h"
+#endif
+#include "dsp.h"
+#include "playlist.h"
+#if CONFIG_CODEC == SWCODEC
+#include "playback.h"
+#endif
+#include "viewport.h"
+
+#include "wps_internals.h"
+#include "music_screen.h"
+
+static char* get_codectype(const struct mp3entry* id3)
+{
+    if (id3->codectype < AFMT_NUM_CODECS) {
+        return (char*)audio_formats[id3->codectype].label;
+    } else {
+        return NULL;
+    }
+}
+
+/* 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);
+    strlcpy(buf, sep + 1, len + 1);
+    return buf;
+}
+
+/* Return the tag found at index i and write its value in buf.
+   The return value is buf if the tag had a value, or NULL if not.
+
+   intval is used with conditionals/enums: when this function is called,
+   intval should contain the number of options in the conditional/enum.
+   When this function returns, intval is -1 if the tag is non numeric or,
+   if the tag is numeric, *intval is the enum case we want to go to (between 1
+   and the original value of *intval, inclusive).
+   When not treating a conditional/enum, intval should be NULL.
+*/
+const char *get_token_value(struct gui_wps *gwps,
+                           struct wps_token *token,
+                           char *buf, int buf_size,
+                           int *intval)
+{
+    if (!gwps)
+        return NULL;
+
+    struct wps_data *data = gwps->data;
+    struct wps_state *state = gwps->state;
+
+    if (!data || !state)
+        return NULL;
+
+    struct mp3entry *id3;
+
+    if (token->next)
+        id3 = state->nid3;
+    else
+        id3 = state->id3;
+
+    if (!id3)
+        return NULL;
+
+#if CONFIG_RTC
+    struct tm* tm = NULL;
+
+    /* if the token is an RTC one, update the time
+       and do the necessary checks */
+    if (token->type >= WPS_TOKENS_RTC_BEGIN
+        && token->type <= WPS_TOKENS_RTC_END)
+    {
+        tm = get_time();
+
+        if (!valid_time(tm))
+            return NULL;
+    }
+#endif
+
+    int limit = 1;
+    if (intval)
+    {
+        limit = *intval;
+        *intval = -1;
+    }
+
+    switch (token->type)
+    {
+        case WPS_TOKEN_CHARACTER:
+            return &(token->value.c);
+
+        case WPS_TOKEN_STRING:
+            return data->strings[token->value.i];
+
+        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)
+            {
+                if (global_settings.volume == sound_min(SOUND_VOLUME))
+                {
+                    *intval = 1;
+                }
+                else if (global_settings.volume == 0)
+                {
+                    *intval = limit - 1;
+                }
+                else if (global_settings.volume > 0)
+                {
+                    *intval = limit;
+                }
+                else
+                {
+                    *intval = (limit - 3) * (global_settings.volume
+                                             - sound_min(SOUND_VOLUME) - 1)
+                              / (-1 - sound_min(SOUND_VOLUME)) + 2;
+                }
+            }
+            return buf;
+
+        case WPS_TOKEN_TRACK_ELAPSED_PERCENT:
+            if (id3->length <= 0)
+                return NULL;
+
+            if (intval)
+            {
+                *intval = limit * (id3->elapsed + state->ff_rewind_count)
+                          / id3->length + 1;
+            }
+            snprintf(buf, buf_size, "%d",
+                     100*(id3->elapsed + state->ff_rewind_count) / id3->length);
+            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_GROUPING:
+            return id3->grouping;
+
+        case WPS_TOKEN_METADATA_GENRE:
+            return id3->genre_string;
+
+        case WPS_TOKEN_METADATA_DISC_NUMBER:
+            if (id3->disc_string)
+                return id3->disc_string;
+            if (id3->discnum) {
+                snprintf(buf, buf_size, "%d", id3->discnum);
+                return buf;
+            }
+            return NULL;
+
+        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;
+
+#ifdef HAVE_ALBUMART
+        case WPS_TOKEN_ALBUMART_DISPLAY:
+            draw_album_art(gwps, audio_current_aa_hid(), false);
+            return NULL;
+
+        case WPS_TOKEN_ALBUMART_FOUND:
+            if (audio_current_aa_hid() >= 0) {
+                return "C";
+            }
+            return NULL;
+#endif
+
+        case WPS_TOKEN_FILE_BITRATE:
+            if(id3->bitrate)
+                snprintf(buf, buf_size, "%d", id3->bitrate);
+            else
+                return "?";
+            return buf;
+
+        case WPS_TOKEN_FILE_CODEC:
+            if (intval)
+            {
+                if(id3->codectype == AFMT_UNKNOWN)
+                    *intval = AFMT_NUM_CODECS;
+                else
+                    *intval = id3->codectype;
+            }
+            return get_codectype(id3);
+
+        case WPS_TOKEN_FILE_FREQUENCY:
+            snprintf(buf, buf_size, "%ld", id3->frequency);
+            return buf;
+
+        case WPS_TOKEN_FILE_FREQUENCY_KHZ:
+            /* ignore remainders < 100, so 22050 Hz becomes just 22k */
+            if ((id3->frequency % 1000) < 100)
+                snprintf(buf, buf_size, "%ld", id3->frequency / 1000);
+            else
+                snprintf(buf, buf_size, "%ld.%d",
+                        id3->frequency / 1000,
+                        (id3->frequency % 1000) / 100);
+            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, token->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 / 1000, (v % 1000) / 10);
+            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
+                return "?h ?m";
+            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_BATTERY_SLEEPTIME:
+        {
+            if (get_sleep_timer() == 0)
+                return NULL;
+            else
+            {
+                format_time(buf, buf_size, get_sleep_timer() * 1000);
+                return buf;
+            }
+        }
+
+        case WPS_TOKEN_PLAYBACK_STATUS:
+        {
+            int status = audio_status();
+            int mode = 1;
+            if (status == AUDIO_STATUS_PLAY)
+                mode = 2;
+            if (is_wps_fading() || 
+               (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-1);
+            return buf;
+        }
+
+        case WPS_TOKEN_REPEAT_MODE:
+            if (intval)
+                *intval = global_settings.repeat_mode + 1;
+            snprintf(buf, buf_size, "%d", global_settings.repeat_mode);
+            return buf;
+            
+        case WPS_TOKEN_RTC_PRESENT:
+#if CONFIG_RTC
+                return "c";
+#else
+                return NULL;
+#endif
+
+#if CONFIG_RTC
+        case WPS_TOKEN_RTC_12HOUR_CFG:
+            if (intval)
+                *intval = global_settings.timeformat + 1;
+            snprintf(buf, buf_size, "%d", global_settings.timeformat);
+            return buf;
+
+        case WPS_TOKEN_RTC_DAY_OF_MONTH:
+            /* d: day of month (01..31) */
+            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) */
+            snprintf(buf, buf_size, "%2d", tm->tm_mday);
+            return buf;
+
+        case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
+            /* H: hour (00..23) */
+            snprintf(buf, buf_size, "%02d", tm->tm_hour);
+            return buf;
+
+        case WPS_TOKEN_RTC_HOUR_24:
+            /* k: hour ( 0..23) */
+            snprintf(buf, buf_size, "%2d", tm->tm_hour);
+            return buf;
+
+        case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
+            /* I: hour (01..12) */
+            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) */
+            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 (intval)
+                *intval = tm->tm_mon + 1;
+            snprintf(buf, buf_size, "%02d", tm->tm_mon + 1);
+            return buf;
+
+        case WPS_TOKEN_RTC_MINUTE:
+            /* M: minute (00..59) */
+            snprintf(buf, buf_size, "%02d", tm->tm_min);
+            return buf;
+
+        case WPS_TOKEN_RTC_SECOND:
+            /* S: second (00..59) */
+            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...) */
+            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 */
+            return tm->tm_hour/12 == 0 ? "AM" : "PM";
+
+        case WPS_TOKEN_RTC_AM_PM_LOWER:
+            /* P: lower case am or pm indicator */
+            return tm->tm_hour/12 == 0 ? "am" : "pm";
+
+        case WPS_TOKEN_RTC_WEEKDAY_NAME:
+            /* a: abbreviated weekday name (Sun..Sat) */
+            return str(LANG_WEEKDAY_SUNDAY + tm->tm_wday);
+
+        case WPS_TOKEN_RTC_MONTH_NAME:
+            /* b: abbreviated month name (Jan..Dec) */
+            return str(LANG_MONTH_JANUARY + tm->tm_mon);
+
+        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
+            /* u: day of week (1..7); 1 is Monday */
+            if (intval)
+                *intval = (tm->tm_wday == 0) ? 7 : tm->tm_wday;
+            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 (intval)
+                *intval = tm->tm_wday + 1;
+            snprintf(buf, buf_size, "%1d", tm->tm_wday);
+            return buf;
+#else
+        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_AM_PM_UPPER:
+        case WPS_TOKEN_RTC_AM_PM_LOWER:
+        case WPS_TOKEN_RTC_YEAR_2_DIGITS:
+            return "--";
+        case WPS_TOKEN_RTC_YEAR_4_DIGITS:
+            return "----";
+        case WPS_TOKEN_RTC_WEEKDAY_NAME:
+        case WPS_TOKEN_RTC_MONTH_NAME:
+            return "---";
+        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
+        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
+            return "-";
+#endif
+
+#ifdef HAVE_LCD_CHARCELLS
+        case WPS_TOKEN_PROGRESSBAR:
+        {
+            char *end = utf8encode(data->wps_progress_pat[0], buf);
+            *end = '\0';
+            return buf;
+        }
+
+        case WPS_TOKEN_PLAYER_PROGRESSBAR:
+            if(is_new_player())
+            {
+                /* we need 11 characters (full line) for
+                    progress-bar */
+                strlcpy(buf, "           ", buf_size);
+            }
+            else
+            {
+                /* Tell the user if we have an OldPlayer */
+                strlcpy(buf, " <Old LCD> ", buf_size);
+            }
+            return buf;
+#endif
+
+#ifdef HAVE_TAGCACHE
+        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;
+
+        case WPS_TOKEN_DATABASE_AUTOSCORE:
+            if (intval)
+                *intval = id3->score + 1;
+
+            snprintf(buf, buf_size, "%d", id3->score);
+            return buf;
+#endif
+
+#if (CONFIG_CODEC == SWCODEC)
+        case WPS_TOKEN_CROSSFADE:
+            if (intval)
+                *intval = global_settings.crossfade + 1;
+            snprintf(buf, buf_size, "%d", global_settings.crossfade);
+            return buf;
+
+        case WPS_TOKEN_REPLAYGAIN:
+        {
+            int val;
+
+            if (global_settings.replaygain_type == REPLAYGAIN_OFF)
+                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:
+                    strlcpy(buf, id3->track_gain_string, buf_size);
+                    break;
+                case 3:
+                case 5:
+                    strlcpy(buf, id3->album_gain_string, buf_size);
+                    break;
+            }
+            return buf;
+        }
+#endif  /* (CONFIG_CODEC == SWCODEC) */
+
+#if (CONFIG_CODEC != MAS3507D)
+        case WPS_TOKEN_SOUND_PITCH:
+        {
+            int val = sound_get_pitch();
+            snprintf(buf, buf_size, "%d.%d",
+                    val / 10, val % 10);
+            return buf;
+        }
+#endif
+
+        case WPS_TOKEN_MAIN_HOLD:
+#ifdef HAS_BUTTON_HOLD
+            if (button_hold())
+#else
+            if (is_keys_locked())
+#endif /*hold switch or softlock*/
+                return "h";
+            else
+                return NULL;
+
+#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
+        case WPS_TOKEN_BUTTON_VOLUME:
+            if (data->button_time_volume && 
+                TIME_BEFORE(current_tick, data->button_time_volume +
+                                          token->value.i * TIMEOUT_UNIT))
+                return "v";
+            return NULL;
+        case WPS_TOKEN_LASTTOUCH:
+#ifdef HAVE_TOUCHSCREEN
+            if (TIME_BEFORE(current_tick, token->value.i * TIMEOUT_UNIT +
+                                          touchscreen_last_touch()))
+                return "t";
+#endif
+            return NULL;
+
+        case WPS_TOKEN_SETTING:
+        {
+            if (intval)
+            {
+                /* Handle contionals */
+                const struct settings_list *s = settings+token->value.i;
+                switch (s->flags&F_T_MASK)
+                {
+                    case F_T_INT:
+                    case F_T_UINT:
+                        if (s->flags&F_RGB)
+                            /* %?St|name|<#000000|#000001|...|#FFFFFF> */
+                            /* shouldn't overflow since colors are stored
+                             * on 16 bits ...
+                             * but this is pretty useless anyway */
+                            *intval = *(int*)s->setting + 1;
+                        else if (s->cfg_vals == NULL)
+                            /* %?St|name|<1st choice|2nd choice|...> */
+                            *intval = (*(int*)s->setting-s->int_setting->min)
+                                      /s->int_setting->step + 1;
+                        else
+                            /* %?St|name|<1st choice|2nd choice|...> */
+                            /* Not sure about this one. cfg_name/vals are
+                             * indexed from 0 right? */
+                            *intval = *(int*)s->setting + 1;
+                        break;
+                    case F_T_BOOL:
+                        /* %?St|name|<if true|if false> */
+                        *intval = *(bool*)s->setting?1:2;
+                        break;
+                    case F_T_CHARPTR:
+                        /* %?St|name|<if non empty string|if empty>
+                         * The string's emptyness discards the setting's
+                         * prefix and suffix */
+                        *intval = ((char*)s->setting)[0]?1:2;
+                        break;
+                    default:
+                        /* This shouldn't happen ... but you never know */
+                        *intval = -1;
+                        break;
+                }
+            }
+            cfg_to_string(token->value.i,buf,buf_size);
+            return buf;
+        }
+
+        default:
+            return NULL;
+    }
+}
diff --git a/apps/iap.c b/apps/iap.c
index 592bbdb..f7a808d 100644
--- a/apps/iap.c
+++ b/apps/iap.c
@@ -36,7 +36,7 @@
 #include "audio.h"
 #include "settings.h"
 #include "metadata.h"
-#include "gwps.h"
+#include "wps_engine/wps_engine.h"
 
 #include "action.h"
 
diff --git a/apps/main.c b/apps/main.c
index 929c946..2cf9ce7 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -52,7 +52,7 @@
 #include "sprintf.h"
 #include "font.h"
 #include "language.h"
-#include "gwps.h"
+#include "music_screen.h"
 #include "playlist.h"
 #include "buffer.h"
 #include "rolo.h"
diff --git a/apps/menu.c b/apps/menu.c
index f6d0ef9..6690972 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -48,7 +48,6 @@
 #include "string.h"
 #include "root_menu.h"
 #include "bookmark.h"
-#include "gwps-common.h" /* for fade() */
 #include "audio.h"
 #include "viewport.h"
 #include "quickscreen.h"
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
index 76ed01d..4009566 100644
--- a/apps/menus/main_menu.c
+++ b/apps/menus/main_menu.c
@@ -50,7 +50,7 @@
 #endif
 #include "version.h"
 #include "time.h"
-#include "gwps.h"
+#include "gui/wps_engine/wps_engine.h"
 
 static const struct browse_folder_info config = {ROCKBOX_DIR, SHOW_CFG};
 
diff --git a/apps/misc.c b/apps/misc.c
index e6f8ca8..2e7051c 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -75,9 +75,8 @@
 #include "bmp.h"
 #include "icons.h"
 #endif /* End HAVE_LCD_BITMAP */
-#include "gui/gwps-common.h"
 #include "bookmark.h"
-
+#include "music_screen.h"
 #include "playback.h"
 
 #ifdef BOOTFILE
diff --git a/apps/recorder/albumart.c b/apps/recorder/albumart.c
index a378c33..f1315da 100644
--- a/apps/recorder/albumart.c
+++ b/apps/recorder/albumart.c
@@ -24,7 +24,6 @@
 #include "system.h"
 #include "albumart.h"
 #include "metadata.h"
-#include "gwps.h"
 #include "buffering.h"
 #include "dircache.h"
 #include "misc.h"
diff --git a/apps/recorder/albumart.h b/apps/recorder/albumart.h
index edf464f..82e0b79 100644
--- a/apps/recorder/albumart.h
+++ b/apps/recorder/albumart.h
@@ -26,7 +26,7 @@
 
 #include <stdbool.h>
 #include "metadata.h"
-#include "gwps.h"
+#include "wps_engine/wps_engine.h"
 
 /* Look for albumart bitmap in the same dir as the track and in its parent dir.
  * Stores the found filename in the buf parameter.
diff --git a/apps/root_menu.c b/apps/root_menu.c
index 15bc524..37fda98 100644
--- a/apps/root_menu.c
+++ b/apps/root_menu.c
@@ -53,7 +53,7 @@
 #ifdef HAVE_RECORDING
 #include "recording.h"
 #endif
-#include "gwps-common.h"
+#include "music_screen.h"
 #include "bookmark.h"
 #include "playlist.h"
 #include "tagtree.h"
diff --git a/apps/settings.c b/apps/settings.c
index 29360f1..800cc59 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -52,7 +52,6 @@
 #endif
 #include "lang.h"
 #include "language.h"
-#include "gwps.h"
 #include "powermgmt.h"
 #include "sprintf.h"
 #include "keyboard.h"
@@ -70,6 +69,7 @@
 #if CONFIG_TUNER
 #include "radio.h"
 #endif
+#include "wps_engine/wps_engine.h"
 
 #if CONFIG_CODEC == MAS3507D
 void dac_line_in(bool enable);
diff --git a/apps/tree.c b/apps/tree.c
index 576453a..2c6c85f 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -36,7 +36,7 @@
 #include "audio.h"
 #include "playlist.h"
 #include "menu.h"
-#include "gwps.h"
+#include "wps_engine/wps_engine.h"
 #include "settings.h"
 #include "debug.h"
 #include "storage.h"
@@ -64,7 +64,6 @@
 #include "tagcache.h"
 #endif
 #include "yesno.h"
-#include "gwps-common.h"
 #include "eeprom_settings.h"
 #include "playlist_catalog.h"