diff --git a/apps/SOURCES b/apps/SOURCES
index 181c780..d15ba5b 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -94,13 +94,12 @@ gui/yesno.c
 gui/viewport.c
 
 gui/skin_engine/skin_backdrops.c
-gui/skin_engine/skin_buffer.c
-gui/skin_engine/wps_debug.c
 gui/skin_engine/skin_display.c
 #ifdef HAVE_LCD_BITMAP
 gui/skin_engine/skin_fonts.c
 #endif
 gui/skin_engine/skin_parser.c
+gui/skin_engine/skin_render.c
 gui/skin_engine/skin_tokens.c
 #ifdef HAVE_TOUCHSCREEN
 gui/skin_engine/skin_touchsupport.c
diff --git a/apps/cuesheet.c b/apps/cuesheet.c
index cadb2ab..a262b12 100644
--- a/apps/cuesheet.c
+++ b/apps/cuesheet.c
@@ -363,20 +363,22 @@ const char *get_cuesheetid3_token(struct wps_token *token, struct mp3entry *id3,
     }
     switch (token->type)
     {
-        case WPS_TOKEN_METADATA_ARTIST:
+        case SKIN_TOKEN_METADATA_ARTIST:
             return *track->performer ? track->performer : NULL;
-        case WPS_TOKEN_METADATA_COMPOSER:
+        case SKIN_TOKEN_METADATA_COMPOSER:
             return *track->songwriter ? track->songwriter : NULL;
-        case WPS_TOKEN_METADATA_ALBUM:
+        case SKIN_TOKEN_METADATA_ALBUM:
             return *cue->title ? cue->title : NULL;
-        case WPS_TOKEN_METADATA_ALBUM_ARTIST:
+        case SKIN_TOKEN_METADATA_ALBUM_ARTIST:
             return *cue->performer ? cue->performer : NULL;
-        case WPS_TOKEN_METADATA_TRACK_TITLE:
+        case SKIN_TOKEN_METADATA_TRACK_TITLE:
             return *track->title ? track->title : NULL;
-        case WPS_TOKEN_METADATA_TRACK_NUMBER:
+        case SKIN_TOKEN_METADATA_TRACK_NUMBER:
             snprintf(buf, buf_size, "%d/%d",  
                      cue->curr_track_idx+offset_tracks+1, cue->track_count);
             return buf;
+        default:
+            return NULL;
     }
     return NULL;
 }
diff --git a/apps/gui/skin_engine/skin_backdrops.c b/apps/gui/skin_engine/skin_backdrops.c
index a32bfbe..4288e0a 100644
--- a/apps/gui/skin_engine/skin_backdrops.c
+++ b/apps/gui/skin_engine/skin_backdrops.c
@@ -24,9 +24,9 @@
 #include <stdlib.h>
 #include "string-extra.h"
 #include "settings.h"
-#include "skin_buffer.h"
 #include "wps_internals.h"
 #include "skin_engine.h"
+#include "skin_buffer.h"
 
 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
 
@@ -100,7 +100,7 @@ char* skin_backdrop_load(char* backdrop, char *bmpdir, enum screen_type screen)
     if (!bdrop)
         return NULL; /* too many backdrops loaded */
     
-    bdrop->buffer = skin_buffer_alloc(buf_size);
+    bdrop->buffer = (char*)skin_buffer_alloc(buf_size);
     if (!bdrop->buffer)
         return NULL;
     loaded = screens[screen].backdrop_load(filename, bdrop->buffer);
diff --git a/apps/gui/skin_engine/skin_buffer.c b/apps/gui/skin_engine/skin_buffer.c
deleted file mode 100644
index d503b83..0000000
--- a/apps/gui/skin_engine/skin_buffer.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/***************************************************************************
- *             __________               __   ___.
- *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
- *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
- *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
- *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
- *                     \/            \/     \/    \/            \/
- * $Id$
- *
- * Copyright (C) 2002 by Linus Nielsen Feltzing
- * Copyright (C) 2009 Jonathan Gordon
- *
- * 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 <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include "config.h"
-#include "buffer.h"
-#include "settings.h"
-#include "screen_access.h"
-#include "skin_engine.h"
-#include "wps_internals.h"
-#include "skin_tokens.h"
-#include "skin_buffer.h"
-#include "skin_fonts.h"
-
-/* skin buffer management.
- * This module is used to allocate space in a single global skin buffer for
- * tokens for both/all screens.
- *
- * This is mostly just copy/paste from firmware/buffer.c
- *
- *
- * MAIN_ and REMOTE_BUFFER are just for reasonable size calibration,
- * both screens can use the whole buffer as they need; it's not split
- * between screens
- *
- * Buffer can be allocated from either "end" of the global buffer.
- * items with unknown sizes get allocated from the start (0->)      (data)
- * items with known sizes get allocated from the end (<-buf_size)   (tokens)
- * After loading 2 skins the buffer will look like this:
- *  |tokens skin1|images skin1|tokens s2|images s2|---SPACE---|data skin2|data skin1|
- * Make sure to never start allocating from the beginning before letting us know
- * how much was used. and RESPECT THE buf_free RETURN VALUES!
- *
- */
-
-
-#ifdef HAVE_LCD_BITMAP
-#define MAIN_BUFFER ((2*LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \
-                    + (SKINNABLE_SCREENS_COUNT * LCD_BACKDROP_BYTES))
-
-#if (NB_SCREENS > 1)
-#define REMOTE_BUFFER (2*(LCD_REMOTE_HEIGHT*LCD_REMOTE_WIDTH*LCD_REMOTE_DEPTH/8) \
-                      + (SKINNABLE_SCREENS_COUNT * REMOTE_LCD_BACKDROP_BYTES))
-#else
-#define REMOTE_BUFFER 0
-#endif
-
-
-#define SKIN_BUFFER_SIZE (MAIN_BUFFER + REMOTE_BUFFER + SKIN_FONT_SIZE) + \
-                         (WPS_MAX_TOKENS * sizeof(struct wps_token))
-#endif
-
-#ifdef HAVE_LCD_CHARCELLS
-#define SKIN_BUFFER_SIZE (LCD_HEIGHT * LCD_WIDTH) * 64 + \
-                         (WPS_MAX_TOKENS * sizeof(struct wps_token))
-#endif
-
-static unsigned char buffer[SKIN_BUFFER_SIZE];
-static unsigned char *buffer_front = NULL; /* start of the free space,
-                                              increases with allocation*/
-static unsigned char *buffer_back  = NULL; /* end of the free space
-                                              decreases with allocation */
-static size_t buf_size = SKIN_BUFFER_SIZE;
-
-void skin_buffer_init(void)
-{
-#if 0 /* this will go in again later probably */
-    if (buffer == NULL)
-    {
-        buf_size = SKIN_BUFFER_SIZE;/* global_settings.skin_buf_size */
-
-        buffer = buffer_alloc(buf_size);
-        buffer_front = buffer;
-        buffer_back = bufer + buf_size;
-    }
-    else
-#endif
-    {
-        /* reset the buffer.... */
-        buffer_front = buffer;
-        buffer_back = buffer + buf_size;
-    }
-}
-
-/* get the number of bytes currently being used */
-size_t skin_buffer_usage(void)
-{
-    return buf_size - (buffer_back-buffer_front);
-}
-
-size_t skin_buffer_freespace(void)
-{
-    return buffer_back-buffer_front;
-}
-
-/* Allocate size bytes from the buffer
- * allocates from the back end (data end)
- */
-void* skin_buffer_alloc(size_t size)
-{
-    if (skin_buffer_freespace() <= size)
-    {
-        return NULL;
-    }
-    buffer_back -= size;
-    /* 32-bit aligned */
-    buffer_back = (void *)(((unsigned long)buffer_back) & ~3);
-
-    memset(buffer_back, 0, size);
-    return buffer_back;
-}
-
-/* Get a pointer to the skin buffer and the count of how much is free
- * used to do your own buffer management.
- * Any memory used will be overwritten next time wps_buffer_alloc()
- * is called unless skin_buffer_increment() is called first
- *
- * This is from the start of the buffer, it is YOUR responsility to make
- * sure you dont ever use more then *freespace, and bear in mind this will only
- * be valid untill skin_buffer_alloc() is next called...
- * so call skin_buffer_increment() and skin_buffer_freespace() regularly
- */
-void* skin_buffer_grab(size_t *freespace)
-{
-    *freespace = buf_size - skin_buffer_usage();
-    return buffer_front;
-}
-
-/* Use after skin_buffer_grab() to specify how much buffer was used */
-void skin_buffer_increment(size_t used, bool align)
-{
-    buffer_front += used;
-    if (align)
-    {
-        /* 32-bit aligned */
-        buffer_front = (void *)(((unsigned long)buffer_front + 3) & ~3);
-    }
-}
-
-/* free previously skin_buffer_increment()'ed space. This just moves the pointer
- * back 'used' bytes so make sure you actually want to do this */
-void skin_buffer_free_from_front(size_t used)
-{
-    buffer_front -= used;
-    /* 32-bit aligned */
-    buffer_front = (void *)(((unsigned long)buffer_front + 3) & ~3);
-}
-
-
diff --git a/apps/gui/skin_engine/skin_buffer.h b/apps/gui/skin_engine/skin_buffer.h
deleted file mode 100644
index 521631f..0000000
--- a/apps/gui/skin_engine/skin_buffer.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/***************************************************************************
- *             __________               __   ___.
- *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
- *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
- *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
- *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
- *                     \/            \/     \/    \/            \/
- * $Id$
- *
- * Copyright (C) 2002 by Linus Nielsen Feltzing
- * Copyright (C) 2009 Jonathan Gordon
- *
- * 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 _SKIN_BUFFER_H_
-#define _SKIN_BUFFER_H_
-
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-/* int the global buffer */
-void skin_buffer_init(void);
-
-/* get the number of bytes currently being used */
-size_t skin_buffer_usage(void);
-size_t skin_buffer_freespace(void);
-
-/* Allocate size bytes from the buffer */
-void* skin_buffer_alloc(size_t size);
-
-
-/* Get a pointer to the skin buffer and the count of how much is free
- * used to do your own buffer management. 
- * Any memory used will be overwritten next time wps_buffer_alloc()
- * is called unless skin_buffer_increment() is called first
- * 
- * This is from the start of the buffer, it is YOUR responsility to make
- * sure you dont ever use more then *freespace, and bear in mind this will only
- * be valid untill skin_buffer_alloc() is next called...
- * so call skin_buffer_increment() and skin_buffer_freespace() regularly
- */
-void* skin_buffer_grab(size_t *freespace);
-
-/* Use after skin_buffer_grab() to specify how much buffer was used.
- * align should always be true unless there is a possibility that you will need
- * more space *immediatly* after the previous allocation. (i.e in an array).
- * NEVER leave the buffer unaligned */
-void skin_buffer_increment(size_t used, bool align);
-
-/* free previously skin_buffer_increment()'ed space. This just moves the pointer
- * back 'used' bytes so make sure you actually want to do this */
-void skin_buffer_free_from_front(size_t used);
-
-#endif /* _SKIN_BUFFER_H_ */
diff --git a/apps/gui/skin_engine/skin_display.c b/apps/gui/skin_engine/skin_display.c
index 3d3a654..18e96dd 100644
--- a/apps/gui/skin_engine/skin_display.c
+++ b/apps/gui/skin_engine/skin_display.c
@@ -69,25 +69,22 @@
 #include "skin_engine.h"
 #include "statusbar-skinned.h"
 
-static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
+void skin_render(struct gui_wps *gwps, unsigned refresh_mode);
 
 /* update a skinned screen, update_type is WPS_REFRESH_* values.
  * Usually it should only be WPS_REFRESH_NON_STATIC
  * A full update will be done if required (state.do_full_update == true)
  */
-bool skin_update(struct gui_wps *gwps, unsigned int update_type)
+void skin_update(struct gui_wps *gwps, unsigned int update_type)
 {
-    bool retval;
-    /* This maybe shouldnt be here, but while the skin is only used to
-     * display the music screen this is better than whereever we are being
-     * called from. This is also safe for skined screen which dont use the id3 */
+    /* This maybe shouldnt be here, 
+     * This is also safe for skined screen which dont use the id3 */
     struct mp3entry *id3 = gwps->state->id3;
     bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
     gwps->sync_data->do_full_update |= cuesheet_update;
  
-    retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
-                                        WPS_REFRESH_ALL : update_type);
-    return retval;
+    skin_render(gwps, gwps->sync_data->do_full_update ?
+                                        SKIN_REFRESH_ALL : update_type);
 }
 
 #ifdef HAVE_LCD_BITMAP
@@ -124,8 +121,7 @@ void skin_statusbar_changed(struct gui_wps *skin)
     }
 }
 
-static void draw_progressbar(struct gui_wps *gwps,
-                             struct progressbar *pb)
+void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb)
 {
     struct screen *display = gwps->display;
     struct viewport *vp = pb->vp;
@@ -143,17 +139,17 @@ static void draw_progressbar(struct gui_wps *gwps,
         /* center the pb in the line, but only if the line is higher than the pb */
         int center = (line_height-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);
+        y = line*line_height + (0 > center ? 0 : center);
     }
 
-    if (pb->type == WPS_TOKEN_VOLUMEBAR)
+    if (pb->type == SKIN_TOKEN_VOLUMEBAR)
     {
         int minvol = sound_min(SOUND_VOLUME);
         int maxvol = sound_max(SOUND_VOLUME);
         length = maxvol-minvol;
         elapsed = global_settings.volume-minvol;
     }
-    else if (pb->type == WPS_TOKEN_BATTERY_PERCENTBAR)
+    else if (pb->type == SKIN_TOKEN_BATTERY_PERCENTBAR)
     {
         length = 100;
         elapsed = battery_level();
@@ -185,7 +181,7 @@ static void draw_progressbar(struct gui_wps *gwps,
         gui_scrollbar_draw(display, pb->x, y, pb->width, height,
                            length, 0, elapsed, HORIZONTAL);
 
-    if (pb->type == WPS_TOKEN_PROGRESSBAR)
+    if (pb->type == SKIN_TOKEN_PROGRESSBAR)
     {
         if (id3 && id3->length)
         {
@@ -208,8 +204,7 @@ static void draw_progressbar(struct gui_wps *gwps,
     }
 }
 
-static void draw_playlist_viewer_list(struct gui_wps *gwps,
-                                      struct playlistviewer *viewer)
+void draw_playlist_viewer_list(struct gui_wps *gwps, struct playlistviewer *viewer)
 {
     struct wps_state *state = gwps->state;
     int lines = viewport_get_nb_lines(viewer->vp);
@@ -217,8 +212,9 @@ static void draw_playlist_viewer_list(struct gui_wps *gwps,
     int cur_pos, max;
     int start_item;
     int i;
-    struct wps_token token;
-    int x, length, alignment = WPS_TOKEN_ALIGN_LEFT;
+    bool scroll = false;
+    struct wps_token *token;
+    int x, length, alignment = SKIN_TOKEN_ALIGN_LEFT;
     
     struct mp3entry *pid3;
     char buf[MAX_PATH*2], tempbuf[MAX_PATH];
@@ -281,51 +277,58 @@ static void draw_playlist_viewer_list(struct gui_wps *gwps,
             }
             line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
         }
-        int j = 0, cur_string = 0;
         unsigned int line_len = 0;
+        if (viewer->lines[line]->children_count == 0)
+            return;
+        struct skin_element *element = viewer->lines[line]->children[0];
         buf[0] = '\0';
-        while (j < viewer->lines[line].count && line_len < sizeof(buf))
+        while (element && line_len < sizeof(buf))
         {
             const char *out = NULL;
-            token.type = viewer->lines[line].tokens[j];
-            token.value.i = 0;
-            token.next = false;
-            out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
+            if (element->type == TEXT)
+            {
+                line_len = strlcat(buf, (char*)element->data, sizeof(buf));
+                element = element->next;
+                continue;
+            }
+            if (element->type != TAG)
+            {
+                element = element->next;
+                continue;
+            }
+            if (element->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
+                scroll = true;
+            token = (struct wps_token*)element->data;
+            out = get_id3_token(token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
 #if CONFIG_TUNER
             if (!out)
-                out = get_radio_token(&token, i-cur_pos,
+                out = get_radio_token(token, i-cur_pos,
                                       tempbuf, sizeof(tempbuf), -1, NULL);
 #endif
             if (out)
             {
                 line_len = strlcat(buf, out, sizeof(buf));
-                j++;
+                element = element->next;
                 continue;
             }
             
-            switch (viewer->lines[line].tokens[j])
+            switch (token->type)
             {
-                case WPS_TOKEN_ALIGN_CENTER:
-                case WPS_TOKEN_ALIGN_LEFT:
-                case WPS_TOKEN_ALIGN_LEFT_RTL:
-                case WPS_TOKEN_ALIGN_RIGHT:
-                case WPS_TOKEN_ALIGN_RIGHT_RTL:
-                    alignment = viewer->lines[line].tokens[j];
+                case SKIN_TOKEN_ALIGN_CENTER:
+                case SKIN_TOKEN_ALIGN_LEFT:
+                case SKIN_TOKEN_ALIGN_LEFT_RTL:
+                case SKIN_TOKEN_ALIGN_RIGHT:
+                case SKIN_TOKEN_ALIGN_RIGHT_RTL:
+                    alignment = token->type;
                     tempbuf[0] = '\0';
                     break;
-                case WPS_TOKEN_STRING:
-                case WPS_TOKEN_CHARACTER:
-                    snprintf(tempbuf, sizeof(tempbuf), "%s",
-                             viewer->lines[line].strings[cur_string]);
-                    cur_string++;
-                    break;
-                case WPS_TOKEN_PLAYLIST_POSITION:
+                case SKIN_TOKEN_PLAYLIST_POSITION:
                     snprintf(tempbuf, sizeof(tempbuf), "%d", i);
                     break;
-                case WPS_TOKEN_FILE_NAME:
+                case SKIN_TOKEN_FILE_NAME:
                     get_dir(tempbuf, sizeof(tempbuf), filename, 0);
                     break;
-                case WPS_TOKEN_FILE_PATH:
+                case SKIN_TOKEN_FILE_PATH:
                     snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
                     break;
                 default:
@@ -336,12 +339,12 @@ static void draw_playlist_viewer_list(struct gui_wps *gwps,
             {
                 line_len = strlcat(buf, tempbuf, sizeof(buf));
             }
-            j++;
+            element = element->next;
         }
 
         int vpwidth = viewer->vp->width;
         length = gwps->display->getstringsize(buf, NULL, NULL);
-        if (viewer->lines[line].scroll && length >= vpwidth)
+        if (scroll && length >= vpwidth)
         {
             gwps->display->puts_scroll(0, (i-start_item), buf );
         }
@@ -353,25 +356,25 @@ static void draw_playlist_viewer_list(struct gui_wps *gwps,
             {
                 switch (alignment)
                 {
-                    case WPS_TOKEN_ALIGN_CENTER:
+                    case SKIN_TOKEN_ALIGN_CENTER:
                         x = (vpwidth-length)/2;
                         break;
-                    case WPS_TOKEN_ALIGN_LEFT_RTL:
+                    case SKIN_TOKEN_ALIGN_LEFT_RTL:
                         if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
                         {
                             x = vpwidth - length;
                             break;
                         }
-                    case WPS_TOKEN_ALIGN_LEFT:
+                    case SKIN_TOKEN_ALIGN_LEFT:
                         x = 0;
                         break;
-                    case WPS_TOKEN_ALIGN_RIGHT_RTL:
+                    case SKIN_TOKEN_ALIGN_RIGHT_RTL:
                         if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
                         {
                             x = 0;
                             break;
                         }
-                    case WPS_TOKEN_ALIGN_RIGHT:
+                    case SKIN_TOKEN_ALIGN_RIGHT:
                         x = vpwidth - length;
                         break;
                     default:
@@ -386,7 +389,7 @@ static void draw_playlist_viewer_list(struct gui_wps *gwps,
 
 
 /* clears the area where the image was shown */
-static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
+void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
 {
     if(!gwps)
         return;
@@ -395,7 +398,7 @@ static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
     gwps->display->set_drawmode(DRMODE_SOLID);
 }
 
-static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
+void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
 {
     struct screen *display = gwps->display;
     if(img->always_display)
@@ -423,7 +426,8 @@ static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subima
 #endif
 }
 
-static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
+
+void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
 {
     if(!gwps || !gwps->data || !gwps->display)
         return;
@@ -451,18 +455,10 @@ static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
 #ifdef HAVE_ALBUMART
     /* now draw the AA */
     if (data->albumart && data->albumart->vp == vp
-        && data->albumart->draw)
+	    && data->albumart->draw_handle >= 0)
     {
-        int handle = playback_current_aa_hid(data->playback_aa_slot);
-#if CONFIG_TUNER
-        if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
-        {
-            struct dim dim = {data->albumart->width, data->albumart->height};
-            handle = radio_get_art_hid(&dim);
-        }
-#endif
-        draw_album_art(gwps, handle, false);
-        data->albumart->draw = false;
+        draw_album_art(gwps, data->albumart->draw_handle, false);
+		data->albumart->draw_handle = -1;
     }
 #endif
 
@@ -613,44 +609,25 @@ static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
 
 #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)
+int evaluate_conditional(struct gui_wps *gwps, struct conditional *conditional, int num_options)
 {
     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. */
+    /* treat ?xx<true> constructs as if they had 2 options. 
+     * (i.e ?xx<true|false>) */
     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],
+    value = get_token_value(gwps, conditional->token,
                             result, sizeof(result), &intval);
 
     /* intval is now the number of the enum option we want to read,
@@ -659,334 +636,18 @@ static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
         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, find_image(data->tokens[i].value.i&0xFF, data));
-        else if (data->tokens[i].type == WPS_TOKEN_VOLUMEBAR   ||
-                 data->tokens[i].type == WPS_TOKEN_PROGRESSBAR ||
-                 data->tokens[i].type == WPS_TOKEN_BATTERY_PERCENTBAR )
-        {
-            struct progressbar *bar = (struct progressbar*)data->tokens[i].value.data;
-            bar->draw = false;
-        }
-        else if (data->tokens[i].type == WPS_TOKEN_PEAKMETER)
-        {
-            data->peak_meter_enabled = false;
-        }
-#endif
-#ifdef HAVE_ALBUMART
-        if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
-        {
-            draw_album_art(gwps,
-                    playback_current_aa_hid(data->playback_aa_slot), true);
-            data->albumart->draw = false;
-        }
-#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,
-                     struct skin_subline *subline,
-                     struct align_pos *align,
-                     char *linebuf,
-                     int linebuf_size,
-                     unsigned refresh_mode)
-{
-    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;
-    bool update = false;
-    int i;
-    (void)refresh_mode; /* silence warning on charcell */
-
-    /* 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 */
-    for (i = subline->first_token_idx;
-         i <= subline->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;
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
-            case WPS_TOKEN_VIEWPORT_FGCOLOUR:
-            {
-                struct viewport_colour *col = data->tokens[i].value.data;
-                col->vp->fg_pattern = col->colour;
-            }
-            break;
-            case WPS_TOKEN_VIEWPORT_BGCOLOUR:
-            {
-                struct viewport_colour *col = data->tokens[i].value.data;
-                col->vp->bg_pattern = col->colour;
-            }
-            break;
-#endif
-#ifdef HAVE_LCD_BITMAP
-            case WPS_TOKEN_PEAKMETER:
-                data->peak_meter_enabled = true;
-                break;
-            case WPS_TOKEN_VOLUMEBAR:
-            case WPS_TOKEN_BATTERY_PERCENTBAR:
-            case WPS_TOKEN_PROGRESSBAR:
-            {
-                struct progressbar *bar = (struct progressbar*)data->tokens[i].value.data;
-                bar->draw = true;
-            }
-            break;
-            case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
-            {
-                char n = data->tokens[i].value.i & 0xFF;
-                int subimage = data->tokens[i].value.i >> 8;
-                struct gui_img *img = find_image(n, data);
-
-                if (img && img->loaded)
-                    img->display = subimage;
-                break;
-            }
-            case WPS_TOKEN_DRAW_INBUILTBAR:
-                gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
-                                   refresh_mode == WPS_REFRESH_ALL,
-                                   data->tokens[i].value.data);
-                break;
-#endif
-
-            case WPS_TOKEN_ALIGN_LEFT:
-            case WPS_TOKEN_ALIGN_LEFT_RTL:
-            case WPS_TOKEN_ALIGN_CENTER:
-            case WPS_TOKEN_ALIGN_RIGHT:
-            case WPS_TOKEN_ALIGN_RIGHT_RTL:
-                /* 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_LEFT_RTL:
-                        cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
-                            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;
-                    case WPS_TOKEN_ALIGN_RIGHT_RTL:
-                        cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
-                            WPS_ALIGN_RIGHT;
-                        break;
-                    default:
-                        break;
-                }
-                *buf++ = 0;
-                cur_align_start = buf;
-                break;
-            case WPS_VIEWPORT_ENABLE:
-            {
-                char label = data->tokens[i].value.i;
-                char temp = VP_DRAW_HIDEABLE;
-                /* viewports are allowed to share id's so find and enable
-                 * all of them */
-                struct skin_token_list *list = data->viewports;
-                while (list)
-                {
-                    struct skin_viewport *vp =
-                                (struct skin_viewport *)list->token->value.data;
-                    if (vp->label == label)
-                    {
-                        if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
-                            temp |= VP_DRAW_WASHIDDEN;
-                        vp->hidden_flags = temp;
-                    }
-                    list = list->next;
-                }
-            }
-                break;
-#ifdef HAVE_LCD_BITMAP
-            case WPS_TOKEN_UIVIEWPORT_ENABLE:
-                    sb_set_info_vp(gwps->display->screen_type, 
-                                   data->tokens[i].value.i|VP_INFO_LABEL);
-                break;
-            case WPS_VIEWPORT_CUSTOMLIST:
-                draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
-                break;
-#endif
-            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, struct skin_subline *subline)
-{
-    struct wps_data *data = gwps->data;
-    int i;
-    subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
-
-    for (i = subline->first_token_idx;
-         i <= subline->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:
-                subline->time_mult = data->tokens[i].value.i;
-                break;
-
-            default:
-                break;
-        }
-    }
+        
+    conditional->last_value = intval -1;
+    return intval -1;
 }
 
-/* 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, struct skin_line *line)
-{
-    /* shortcut this whole thing if we need to reset the line completly */
-    if (line->curr_subline == NULL)
-    {
-        line->subline_expire_time = current_tick;
-        line->curr_subline = &line->sublines;
-        if (!line->curr_subline->next)
-        {
-            line->subline_expire_time += 100*HZ;
-        }
-        else
-        {
-            get_subline_timeout(gwps, line->curr_subline);
-            line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
-        }
-        return true;
-    }
-    /* if time to advance to next sub-line  */
-    if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
-    {
-        /* if there is only one subline, there is no need to search for a new one */
-        if (&line->sublines == line->curr_subline &&
-             line->curr_subline->next == NULL)
-        {
-            line->subline_expire_time += 100 * HZ;
-            return false;
-        }
-        if (line->curr_subline->next)
-            line->curr_subline = line->curr_subline->next;
-        else
-            line->curr_subline = &line->sublines;
-        get_subline_timeout(gwps, line->curr_subline);
-        line->subline_expire_time = current_tick + TIMEOUT_UNIT*line->curr_subline->time_mult;
-        return true;
-    }
-    return false;
-}
 
 /* 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,
+void write_line(struct screen *display,
                        struct align_pos *format_align,
                        int line,
                        bool scroll)
@@ -1143,244 +804,29 @@ static void write_line(struct screen *display,
     }
 }
 
-static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
+void draw_peakmeters(struct gui_wps *gwps, int line_number,
+                     struct viewport *viewport)
 {
     struct wps_data *data = gwps->data;
-    struct screen *display = gwps->display;
-
-    if (!data || !display || !gwps->state)
-        return false;
-
-    unsigned flags;
-    char linebuf[MAX_PATH];
-
-    struct align_pos align;
-    align.left = NULL;
-    align.center = NULL;
-    align.right = NULL;
-
-
-    struct skin_token_list *viewport_list;
-
-    bool update_line, new_subline_refresh;
-
-    /* reset to first subline if refresh all flag is set */
-    if (refresh_mode == WPS_REFRESH_ALL)
-    {
-        struct skin_line *line;
-        struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
-        
-        if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
-        {
-            display->set_viewport(&skin_viewport->vp);
-            display->clear_viewport();
-        }
-
-        for (viewport_list = data->viewports;
-             viewport_list; viewport_list = viewport_list->next)
-        {
-            skin_viewport =
-                  (struct skin_viewport *)viewport_list->token->value.data;
-            for(line = skin_viewport->lines; line; line = line->next)
-            {
-                line->curr_subline = NULL;
-            }
-        }
-    }
-
-#ifdef HAVE_LCD_CHARCELLS
-    int i;
-    for (i = 0; i < 8; i++)
-    {
-        if (data->wps_progress_pat[i] == 0)
-            data->wps_progress_pat[i] = display->get_locked_pattern();
-    }
-#endif
-
-    /* disable any viewports which are conditionally displayed.
-     * If we are only refreshing the peak meter then don't change the viewport 
-     * enabled flags as this will stop scrolling. viewports cant be 
-     * toggled in this refresh mode anyway (FS#10215)*/
-    if (refresh_mode != WPS_REFRESH_PEAK_METER)
+    if (!data->peak_meter_enabled)
     {
-        for (viewport_list = data->viewports;
-             viewport_list; viewport_list = viewport_list->next)
-        {
-            struct skin_viewport *skin_viewport =
-                            (struct skin_viewport *)viewport_list->token->value.data;
-            if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
-            {
-                continue;
-            }
-            if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
-            {
-                if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
-                    skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
-                else
-                    skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
-            }
-        }
+        peak_meter_enable(false);
     }
-    for (viewport_list = data->viewports;
-         viewport_list; viewport_list = viewport_list->next)
+    else
     {
-        struct skin_viewport *skin_viewport =
-                        (struct skin_viewport *)viewport_list->token->value.data;
-        unsigned vp_refresh_mode = refresh_mode;
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
-        skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
-        skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
-#endif
-        display->set_viewport(&skin_viewport->vp);
+        int h = font_get(viewport->font)->height;
+        int peak_meter_y = line_number * h;
 
-        int hidden_vp = 0;
-
-#ifdef HAVE_LCD_BITMAP
-        /* Set images to not to be displayed */
-        struct skin_token_list *imglist = data->images;
-        while (imglist)
-        {
-            struct gui_img *img = (struct gui_img *)imglist->token->value.data;
-            img->display = -1;
-            imglist = imglist->next;
+        /* 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 <= viewport->y+viewport->height) {
+            peak_meter_enable(true);
+            peak_meter_screen(gwps->display, 0, peak_meter_y,
+                              MIN(h, viewport->y+viewport->height - peak_meter_y));
         }
-#endif
-        /* dont redraw the viewport if its disabled */
-        if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
-        {   /* don't draw anything into this one */
-            vp_refresh_mode = 0; hidden_vp = true;
-        }
-        else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
-        {
-            if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
-                display->scroll_stop(&skin_viewport->vp);
-            skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
-            continue;
-        }
-        else if (((skin_viewport->hidden_flags&
-                   (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
-                    == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
-        {
-            vp_refresh_mode = WPS_REFRESH_ALL;
-            skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
-        }
-
-        if (vp_refresh_mode == WPS_REFRESH_ALL)
-        {
-            display->clear_viewport();
-        }
-
-        /* loop over the lines for this viewport */
-        struct skin_line *line;
-        /* %V() doesnt eat the \n which means the first line of text
-         * is actually going to be one line down. so set line_count to -1 
-         * unless we are using the default viewport which doesnt have this problem */
-        int line_count = skin_viewport->label==VP_DEFAULT_LABEL?0:-1;
-
-        for (line = skin_viewport->lines; line; line = line->next, line_count++)
-        {
-            struct skin_subline *subline;
-            memset(linebuf, 0, sizeof(linebuf));
-            update_line = false;
-
-            /* get current subline for the line */
-            new_subline_refresh = update_curr_subline(gwps, line);
-            subline = line->curr_subline;
-            flags = line->curr_subline->line_type;
-
-            if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
-                || new_subline_refresh || hidden_vp)
-            {
-                /* get_line tells us if we need to update the line */
-                update_line = get_line(gwps, subline, &align,
-                                       linebuf, sizeof(linebuf), vp_refresh_mode);
-            }
-#ifdef HAVE_LCD_BITMAP
-            /* peakmeter */
-            if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
-            {
-                if (!data->peak_meter_enabled)
-                {
-                    peak_meter_enable(false);
-                }
-                else
-                {
-                    /* the peakmeter should be alone on its line */
-                    update_line = false;
-
-                    int h = font_get(skin_viewport->vp.font)->height;
-                    int peak_meter_y = line_count* 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 <= skin_viewport->vp.y+skin_viewport->vp.height) {
-                        peak_meter_enable(true);
-                        peak_meter_screen(gwps->display, 0, peak_meter_y,
-                                          MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
-                    }
-                }
-            }
-
-#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 (line_count>= 0 && update_line && !hidden_vp &&
-                /* 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 */
-                ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
-                 !viewport_list->next))
-            {
-                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_count, true);
-                }
-                else
-                    write_line(display, &align, line_count, false);
-            }
-        }
-#ifdef HAVE_LCD_BITMAP
-        /* progressbar */
-        if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
-        {
-            struct skin_token_list *bar = gwps->data->progressbars;
-            while (bar)
-            {
-                struct progressbar *thisbar = (struct progressbar*)bar->token->value.data;
-                if (thisbar->vp == &skin_viewport->vp && thisbar->draw)
-                {
-                    draw_progressbar(gwps, thisbar);
-                }
-                bar = bar->next;
-            }
-        }
-        /* Now display any images in this viewport */
-        if (!hidden_vp)
-            wps_display_images(gwps, &skin_viewport->vp);
-#endif
     }
-
-    /* Restore the default viewport */
-    display->set_viewport(NULL);
-
-    display->update();
-
-    return true;
 }
 
 bool skin_has_sbs(enum screen_type screen, struct wps_data *data)
@@ -1434,7 +880,7 @@ int skin_wait_for_action(struct gui_wps *gwps, int context, int timeout)
                 FOR_NB_SCREENS(i)
                 {
                     if(gwps[i].data->peak_meter_enabled)
-                        skin_update(&gwps[i], WPS_REFRESH_PEAK_METER);
+                        skin_update(&gwps[i], SKIN_REFRESH_PEAK_METER);
                     next_refresh += HZ / PEAK_METER_FPS;
                 }
             }
diff --git a/apps/gui/skin_engine/skin_display.h b/apps/gui/skin_engine/skin_display.h
new file mode 100644
index 0000000..958eace
--- /dev/null
+++ b/apps/gui/skin_engine/skin_display.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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 "config.h"
+#include <stdio.h>
+#include "wps_internals.h"
+#include "skin_engine.h"
+#include "statusbar-skinned.h"
+
+#ifndef _SKIN_DISPLAY_H_
+#define _SKIN_DISPLAY_H_
+
+void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb);
+void draw_playlist_viewer_list(struct gui_wps *gwps, struct playlistviewer *viewer);
+/* clears the area where the image was shown */
+void clear_image_pos(struct gui_wps *gwps, struct gui_img *img);
+void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage);
+void wps_display_images(struct gui_wps *gwps, struct viewport* vp);
+
+
+/* Evaluate the conditional that is at *token_index and return whether a skip
+   has ocurred. *token_index is updated with the new position.
+*/
+int evaluate_conditional(struct gui_wps *gwps, struct conditional *conditional, int num_options);
+/* 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.
+*/
+void write_line(struct screen *display,
+                       struct align_pos *format_align,
+                       int line,
+                       bool scroll);
+void draw_peakmeters(struct gui_wps *gwps, int line_number,
+                     struct viewport *viewport);
+#endif
diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h
index 69991ab..9845d8c 100644
--- a/apps/gui/skin_engine/skin_engine.h
+++ b/apps/gui/skin_engine/skin_engine.h
@@ -23,7 +23,10 @@
 #ifndef _SKIN_ENGINE_H
 #define _SKIN_ENGINE_H
 
-#include "skin_buffer.h"
+#ifndef PLUGIN
+
+#include "skin_fonts.h"
+#include "tag_table.h"
 
 #include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */
 
@@ -39,13 +42,37 @@ enum skinnable_screens {
 };
 
 
+#ifdef HAVE_LCD_BITMAP
+#define MAIN_BUFFER ((2*LCD_HEIGHT*LCD_WIDTH*LCD_DEPTH/8) \
+                    + (SKINNABLE_SCREENS_COUNT * LCD_BACKDROP_BYTES))
+
+#if (NB_SCREENS > 1)
+#define REMOTE_BUFFER (2*(LCD_REMOTE_HEIGHT*LCD_REMOTE_WIDTH*LCD_REMOTE_DEPTH/8) \
+                      + (SKINNABLE_SCREENS_COUNT * REMOTE_LCD_BACKDROP_BYTES))
+#else
+#define REMOTE_BUFFER 0
+#endif
+
+
+#define SKIN_BUFFER_SIZE (MAIN_BUFFER + REMOTE_BUFFER + SKIN_FONT_SIZE) + \
+                         (WPS_MAX_TOKENS * \
+                         (sizeof(struct wps_token) + (sizeof(struct skin_element))))
+#endif
+
+#ifdef HAVE_LCD_CHARCELLS
+#define SKIN_BUFFER_SIZE (LCD_HEIGHT * LCD_WIDTH) * 64 + \
+                         (WPS_MAX_TOKENS * \
+                         (sizeof(struct wps_token) + (sizeof(struct skin_element))))
+#endif
+
+
 #ifdef HAVE_TOUCHSCREEN
 int skin_get_touchaction(struct wps_data *data, int* edge_offset);
 void skin_disarm_touchregions(struct wps_data *data);
 #endif
 
 /* Do a update_type update of the skinned screen */
-bool skin_update(struct gui_wps *gwps, unsigned int update_type);
+void skin_update(struct gui_wps *gwps, unsigned int update_type);
 
 /*
  * setup up the skin-data from a format-buffer (isfile = false)
@@ -72,3 +99,5 @@ void skin_backdrop_init(void);
  */
 int skin_wait_for_action(struct gui_wps *gwps, int context, int timeout);
 #endif
+
+#endif
diff --git a/apps/gui/skin_engine/skin_fonts.c b/apps/gui/skin_engine/skin_fonts.c
index f446a99..49b4292 100644
--- a/apps/gui/skin_engine/skin_fonts.c
+++ b/apps/gui/skin_engine/skin_fonts.c
@@ -27,7 +27,6 @@
 #include "file.h"
 #include "settings.h"
 #include "font.h"
-#include "skin_buffer.h"
 #include "skin_fonts.h"
 
 static struct skin_font_info {
@@ -88,7 +87,7 @@ int skin_font_load(char* font_name)
     pf = &font->font;
     if (!font->buffer)
     {
-        pf->buffer_start = skin_buffer_alloc(SKIN_FONT_SIZE);
+        pf->buffer_start = (char*)skin_buffer_alloc(SKIN_FONT_SIZE);
         if (!pf->buffer_start)
             return -1;
         font->buffer = pf->buffer_start;
diff --git a/apps/gui/skin_engine/skin_fonts.h b/apps/gui/skin_engine/skin_fonts.h
index 6e3634e..2988b43 100644
--- a/apps/gui/skin_engine/skin_fonts.h
+++ b/apps/gui/skin_engine/skin_fonts.h
@@ -27,7 +27,6 @@
 #include "file.h"
 #include "settings.h"
 #include "font.h"
-#include "skin_buffer.h"
 
 #ifndef _SKINFONTS_H_
 #define _SKINFONTS_H_
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c
index 886aeae..5341847 100644
--- a/apps/gui/skin_engine/skin_parser.c
+++ b/apps/gui/skin_engine/skin_parser.c
@@ -8,6 +8,7 @@
  * $Id$
  *
  * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
+ *               2010 Jonathan Gordon
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -28,6 +29,10 @@
 #include "plugin.h"
 #include "viewport.h"
 
+#include "skin_buffer.h"
+#include "skin_parser.h"
+#include "tag_table.h"
+
 #ifdef __PCTOOL__
 #ifdef WPSEDITOR
 #include "proxy.h"
@@ -71,376 +76,27 @@
 
 #define WPS_ERROR_INVALID_PARAM         -1
 
-/* which screen are we parsing for? */
-static enum screen_type curr_screen;
-
-/* level of current conditional.
-   -1 means we're not in a conditional. */
-static int level = -1;
-
-/* index of the last WPS_TOKEN_CONDITIONAL_OPTION
-    or WPS_TOKEN_CONDITIONAL_START in current level */
-static int lastcond[WPS_MAX_COND_LEVEL];
-
-/* index of the WPS_TOKEN_CONDITIONAL in current level */
-static int condindex[WPS_MAX_COND_LEVEL];
-
-/* number of condtional options in current level */
-static int numoptions[WPS_MAX_COND_LEVEL];
-
-/* line number, debug only */
-static int line_number;
-
-/* the current viewport */
-static struct skin_viewport *curr_vp;
-/* the current line, linked to the above viewport */
-static struct skin_line *curr_line;
-
-static int follow_lang_direction = 0;
-
-#if defined(DEBUG) || defined(SIMULATOR)
-/* debugging function */
-extern void print_debug_info(struct wps_data *data, int fail, int line);
-extern void debug_skin_usage(void);
-#endif
 
-/* Function for parsing of details for a token. At the moment the
-   function is called, the token type has already been set. The
-   function must fill in the details and possibly add more tokens
-   to the token array. It should return the number of chars that
-   has been consumed.
-
-   wps_bufptr points to the char following the tag (i.e. where
-   details begin).
-   token is the pointer to the 'main' token being parsed
-   */
-typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
-                struct wps_token *token, struct wps_data *wps_data);
-
-struct wps_tag {
-    enum wps_token_type type;
-    const char name[3];
-    unsigned char refresh_type;
-    const wps_tag_parse_func parse_func;
-};
-static int skip_end_of_line(const char *wps_bufptr);
-/* prototypes of all special parse functions : */
-static int parse_timeout(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_progressbar(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_dir_level(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_setting_and_lang(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-
-
-static int parse_languagedirection(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data)
+static bool isdefault(struct skin_tag_parameter *param)
 {
-    (void)wps_bufptr;
-    (void)token;
-    (void)wps_data;
-    follow_lang_direction = 2; /* 2 because it is decremented immediatly after 
-                                  this token is parsed, after the next token it 
-                                  will be 0 again. */
-    return 0;
+    return param->type == DEFAULT;
 }
 
-#ifdef HAVE_LCD_BITMAP
-static int parse_viewport_display(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_playlistview(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_viewport(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_statusbar_enable(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_statusbar_disable(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_statusbar_inbuilt(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_image_display(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_image_load(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_font_load(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-#endif /*HAVE_LCD_BITMAP */
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
-static int parse_viewportcolour(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_image_special(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-#endif
-#ifdef HAVE_ALBUMART
-static int parse_albumart_load(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-static int parse_albumart_display(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-#endif /* HAVE_ALBUMART */
-#ifdef HAVE_TOUCHSCREEN
-static int parse_touchregion(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data);
-#else
-static int fulline_tag_not_supported(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data)
-{
-    (void)token; (void)wps_data;
-    return skip_end_of_line(wps_bufptr);
-}
-#define parse_touchregion fulline_tag_not_supported
-#endif
-#ifdef CONFIG_RTC
-#define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
-#else
-#define WPS_RTC_REFRESH WPS_REFRESH_STATIC
-#endif
 
-/* array of available tags - those with more characters have to go first
-   (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
-static const struct wps_tag all_tags[] = {
-
-    { WPS_TOKEN_ALIGN_CENTER,             "ac",  0,                   NULL },
-    { WPS_TOKEN_ALIGN_LEFT,               "al",  0,                   NULL },
-    { WPS_TOKEN_ALIGN_LEFT_RTL,           "aL",  0,                   NULL },
-    { WPS_TOKEN_ALIGN_RIGHT,              "ar",  0,                   NULL },
-    { WPS_TOKEN_ALIGN_RIGHT_RTL,          "aR",  0,                   NULL },
-    { WPS_NO_TOKEN,                       "ax",  0,   parse_languagedirection },
-
-    { WPS_TOKEN_BATTERY_PERCENT,          "bl",  WPS_REFRESH_DYNAMIC, parse_progressbar },
-    { WPS_TOKEN_BATTERY_VOLTS,            "bv",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_BATTERY_TIME,             "bt",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_BATTERY_SLEEPTIME,        "bs",  WPS_REFRESH_DYNAMIC, NULL },
-#if CONFIG_CHARGING >= CHARGING_MONITOR
-    { WPS_TOKEN_BATTERY_CHARGING,         "bc",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-#if CONFIG_CHARGING
-    { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-#ifdef HAVE_USB_POWER
-    { WPS_TOKEN_USB_POWERED,              "bu",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-
-    { WPS_TOKEN_RTC_PRESENT     ,             "cc", WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_RTC_DAY_OF_MONTH,             "cd", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_12HOUR_CFG,               "cf", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED,      "cH", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_HOUR_24,                  "ck", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED,      "cI", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_HOUR_12,                  "cl", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_MONTH,                    "cm", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_MINUTE,                   "cM", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_SECOND,                   "cS", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_YEAR_2_DIGITS,            "cy", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_YEAR_4_DIGITS,            "cY", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_AM_PM_UPPER,              "cP", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_AM_PM_LOWER,              "cp", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_WEEKDAY_NAME,             "ca", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_MONTH_NAME,               "cb", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON,    "cu", WPS_RTC_REFRESH, NULL },
-    { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN,    "cw", WPS_RTC_REFRESH, NULL },
-
-    /* current file */
-    { WPS_TOKEN_FILE_BITRATE,             "fb",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_CODEC,               "fc",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_FREQUENCY,           "ff",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_FREQUENCY_KHZ,       "fk",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_NAME,                "fn",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_PATH,                "fp",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_SIZE,                "fs",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_VBR,                 "fv",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_FILE_DIRECTORY,           "d",   WPS_REFRESH_STATIC,
-                                                           parse_dir_level },
-
-    /* next file */
-    { WPS_TOKEN_FILE_BITRATE,             "Fb",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_CODEC,               "Fc",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_FREQUENCY,           "Ff",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_FREQUENCY_KHZ,       "Fk",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_NAME,                "Fn",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_PATH,                "Fp",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_SIZE,                "Fs",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_VBR,                 "Fv",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_FILE_DIRECTORY,           "D",   WPS_REFRESH_STATIC,
-                                                           parse_dir_level },
-
-    /* current metadata */
-    { WPS_TOKEN_METADATA_ARTIST,          "ia",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_COMPOSER,        "ic",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_ALBUM,           "id",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_ALBUM_ARTIST,    "iA",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_GROUPING,        "iG",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_GENRE,           "ig",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_DISC_NUMBER,     "ik",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_TRACK_NUMBER,    "in",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_TRACK_TITLE,     "it",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_VERSION,         "iv",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_YEAR,            "iy",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_METADATA_COMMENT,         "iC",  WPS_REFRESH_STATIC,  NULL },
-
-    /* next metadata */
-    { WPS_TOKEN_METADATA_ARTIST,          "Ia",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_COMPOSER,        "Ic",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_ALBUM,           "Id",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_ALBUM_ARTIST,    "IA",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_GROUPING,        "IG",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_GENRE,           "Ig",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_DISC_NUMBER,     "Ik",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_TRACK_NUMBER,    "In",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_TRACK_TITLE,     "It",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_VERSION,         "Iv",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_YEAR,            "Iy",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_METADATA_COMMENT,         "IC",  WPS_REFRESH_STATIC, NULL },
-
-#if (CONFIG_CODEC != MAS3507D)
-    { WPS_TOKEN_SOUND_PITCH,              "Sp",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-#if (CONFIG_CODEC == SWCODEC)
-    { WPS_TOKEN_SOUND_SPEED,              "Ss",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
-    { WPS_TOKEN_VLED_HDD,                 "lh",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-
-    { WPS_TOKEN_MAIN_HOLD,                "mh",  WPS_REFRESH_DYNAMIC, NULL },
-
-#ifdef HAS_REMOTE_BUTTON_HOLD
-    { WPS_TOKEN_REMOTE_HOLD,              "mr",  WPS_REFRESH_DYNAMIC, NULL },
-#else
-    { WPS_TOKEN_UNKNOWN,                  "mr",  0,                   NULL },
-#endif
-
-    { WPS_TOKEN_REPEAT_MODE,              "mm",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_PLAYBACK_STATUS,          "mp",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_BUTTON_VOLUME,            "mv",  WPS_REFRESH_DYNAMIC,
-                                                             parse_timeout },
-
-#ifdef HAVE_LCD_BITMAP
-    { WPS_TOKEN_PEAKMETER,                "pm", WPS_REFRESH_PEAK_METER, NULL },
-#else
-    { WPS_TOKEN_PLAYER_PROGRESSBAR,       "pf",
-      WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
-#endif
-    { WPS_TOKEN_PROGRESSBAR,              "pb",  WPS_REFRESH_PLAYER_PROGRESS,
-                                                         parse_progressbar },
-
-    { WPS_TOKEN_VOLUME,                   "pv",  WPS_REFRESH_DYNAMIC, 
-                                                         parse_progressbar },
-
-    { WPS_TOKEN_TRACK_ELAPSED_PERCENT,    "px",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_TRACK_TIME_ELAPSED,       "pc",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_TRACK_TIME_REMAINING,     "pr",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_TRACK_LENGTH,             "pt",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_TRACK_STARTING,           "pS",  WPS_REFRESH_DYNAMIC, parse_timeout },
-    { WPS_TOKEN_TRACK_ENDING,             "pE",  WPS_REFRESH_DYNAMIC, parse_timeout },
-
-    { WPS_TOKEN_PLAYLIST_POSITION,        "pp",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_PLAYLIST_ENTRIES,         "pe",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_PLAYLIST_NAME,            "pn",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_PLAYLIST_SHUFFLE,         "ps",  WPS_REFRESH_DYNAMIC, NULL },
-
-#ifdef HAVE_TAGCACHE
-    { WPS_TOKEN_DATABASE_PLAYCOUNT,       "rp",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_DATABASE_RATING,          "rr",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_DATABASE_AUTOSCORE,       "ra",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-
-#if CONFIG_CODEC == SWCODEC
-    { WPS_TOKEN_REPLAYGAIN,               "rg",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_CROSSFADE,                "xf",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-
-    { WPS_TOKEN_HAVE_TUNER,               "tp",  WPS_REFRESH_STATIC,  NULL },
-#if CONFIG_TUNER /* Re-uses the 't' and 'T' prefixes, be careful about doubleups */
-    { WPS_TOKEN_TUNER_TUNED,              "tt",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_TUNER_SCANMODE,           "tm",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_TUNER_STEREO,             "ts",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_TUNER_MINFREQ,            "ta",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_TUNER_MAXFREQ,            "tb",  WPS_REFRESH_STATIC,  NULL },
-    { WPS_TOKEN_TUNER_CURFREQ,            "tf",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_PRESET_ID,                "Ti",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_PRESET_NAME,              "Tn",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_PRESET_FREQ,              "Tf",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_PRESET_COUNT,             "Tc",  WPS_REFRESH_STATIC, NULL },
-    { WPS_TOKEN_HAVE_RDS,                 "tx",  WPS_REFRESH_STATIC, NULL },
-#ifdef HAVE_RDS_CAP
-    { WPS_TOKEN_RDS_NAME,                 "ty", WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_RDS_TEXT,                 "tz", WPS_REFRESH_DYNAMIC, NULL },
-#endif
-#endif /* CONFIG_TUNER */
-
-    { WPS_NO_TOKEN,                       "s",   WPS_REFRESH_SCROLL,  NULL },
-    { WPS_TOKEN_SUBLINE_TIMEOUT,          "t",   0,  parse_timeout },
-
-#ifdef HAVE_LCD_BITMAP
-    { WPS_NO_TOKEN,                       "we",  0, parse_statusbar_enable },
-    { WPS_NO_TOKEN,                       "wd",  0, parse_statusbar_disable },
-    { WPS_TOKEN_DRAW_INBUILTBAR,          "wi",  WPS_REFRESH_DYNAMIC, parse_statusbar_inbuilt },
-
-    { WPS_NO_TOKEN,                       "xl",  0,       parse_image_load },
-
-    { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY,    "xd",  WPS_REFRESH_STATIC,
-                                                       parse_image_display },
-
-    { WPS_TOKEN_IMAGE_DISPLAY,            "x",   0,       parse_image_load },
-    { WPS_NO_TOKEN,                       "Fl",  0,       parse_font_load },
-#ifdef HAVE_ALBUMART
-    { WPS_NO_TOKEN,                       "Cl",  0,    parse_albumart_load },
-    { WPS_TOKEN_ALBUMART_DISPLAY,         "Cd",   WPS_REFRESH_STATIC,  parse_albumart_display },
-    { WPS_TOKEN_ALBUMART_FOUND,           "C", WPS_REFRESH_STATIC, NULL },
-#endif
-
-    { WPS_VIEWPORT_ENABLE,                "Vd",  WPS_REFRESH_DYNAMIC,
-                                                    parse_viewport_display },
-    { WPS_TOKEN_UIVIEWPORT_ENABLE,        "VI",  WPS_REFRESH_STATIC,
-                                                    parse_viewport_display },
-#ifdef HAVE_LCD_BITMAP
-    { WPS_VIEWPORT_CUSTOMLIST,            "Vp",  WPS_REFRESH_STATIC, parse_playlistview },
-    { WPS_TOKEN_LIST_TITLE_TEXT,          "Lt",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_LIST_TITLE_ICON,          "Li",  WPS_REFRESH_DYNAMIC, NULL },
-#endif
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
-    { WPS_TOKEN_VIEWPORT_FGCOLOUR,        "Vf", WPS_REFRESH_STATIC, parse_viewportcolour },
-    { WPS_TOKEN_VIEWPORT_BGCOLOUR,        "Vb", WPS_REFRESH_STATIC, parse_viewportcolour },
-#endif
-    { WPS_NO_TOKEN,                       "V",   0,    parse_viewport      },
-
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
-    { WPS_TOKEN_IMAGE_BACKDROP,           "X",   0,    parse_image_special },
-#endif
-#endif
-
-    { WPS_TOKEN_SETTING,                  "St",  WPS_REFRESH_DYNAMIC,
-                                                    parse_setting_and_lang },
-    { WPS_TOKEN_TRANSLATEDSTRING,         "Sx",  WPS_REFRESH_STATIC,
-                                                    parse_setting_and_lang },
-    { WPS_TOKEN_LANG_IS_RTL ,             "Sr",  WPS_REFRESH_STATIC, NULL },
+/* which screen are we parsing for? */
+static enum screen_type curr_screen;
 
-    { WPS_TOKEN_LASTTOUCH,                "Tl",  WPS_REFRESH_DYNAMIC, parse_timeout },
-    { WPS_TOKEN_CURRENT_SCREEN,           "cs",  WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_NO_TOKEN,                       "T",   0,    parse_touchregion      },
+/* the current viewport */
+static struct skin_element *curr_viewport_element;
+static struct skin_viewport *curr_vp;
 
+struct line *curr_line;
 
-    /* Recording Tokens */
-    { WPS_TOKEN_HAVE_RECORDING,         "Rp", WPS_REFRESH_STATIC, NULL },
-#ifdef HAVE_RECORDING
-    { WPS_TOKEN_IS_RECORDING,           "Rr", WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_REC_FREQ,               "Rf", WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_REC_ENCODER,            "Re", WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_REC_BITRATE,            "Rb", WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_REC_MONO,               "Rm", WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_REC_SECONDS,            "Rs", WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_REC_MINUTES,            "Rn", WPS_REFRESH_DYNAMIC, NULL },
-    { WPS_TOKEN_REC_HOURS,              "Rh", WPS_REFRESH_DYNAMIC, NULL },
-#endif
-    { WPS_TOKEN_UNKNOWN,                  "",    0, NULL }
-    /* the array MUST end with an empty string (first char is \0) */
-};
+static int follow_lang_direction = 0;
 
+typedef int (*parse_function)(struct skin_element *element,
+                              struct wps_token *token,
+                              struct wps_data *wps_data);
 
 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
  * chains require the order to be kept.
@@ -478,10 +134,10 @@ struct gui_img* find_image(char label, struct wps_data *data)
 /* traverse the viewport linked list for a viewport */
 struct skin_viewport* find_viewport(char label, struct wps_data *data)
 {
-    struct skin_token_list *list = data->viewports;
+    struct skin_element *list = data->tree;
     while (list)
     {
-        struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
+        struct skin_viewport *vp = (struct skin_viewport *)list->data;
         if (vp->label == label)
             return vp;
         list = list->next;
@@ -498,9 +154,10 @@ struct skin_viewport* find_viewport(char label, struct wps_data *data)
 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
                                                         void* token_data)
 {
-    struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
+    struct skin_token_list *llitem = 
+        (struct skin_token_list *)skin_buffer_alloc(sizeof(struct skin_token_list));
     if (!token)
-        token = skin_buffer_alloc(sizeof(struct wps_token));
+        token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
     if (!llitem || !token)
         return NULL;
     llitem->next = NULL;
@@ -510,108 +167,38 @@ static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
     return llitem;
 }
 
-/* Returns the number of chars that should be skipped to jump
-   immediately after the first eol, i.e. to the start of the next line */
-static int skip_end_of_line(const char *wps_bufptr)
-{
-    line_number++;
-    int skip = 0;
-    while(*(wps_bufptr + skip) != '\n')
-        skip++;
-    return ++skip;
-}
-
-/* Starts a new subline in the current line during parsing */
-static bool skin_start_new_subline(struct skin_line *line, int curr_token)
-{
-    struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
-    if (!subline)
-        return false;
-
-    subline->first_token_idx = curr_token;
-    subline->next = NULL;
-
-    subline->line_type = 0;
-    subline->time_mult = 0;
-
-    line->curr_subline->last_token_idx = curr_token-1;
-    line->curr_subline->next = subline;
-    line->curr_subline = subline;
-    return true;
-}
+#ifdef HAVE_LCD_BITMAP
 
-static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
+static int parse_statusbar_tags(struct skin_element* element,
+                                struct wps_token *token,
+                                struct wps_data *wps_data)
 {
-    struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
-    struct skin_subline *subline = NULL;
-    if (!line)
-        return false;
-
-    /* init the subline */
-    subline = &line->sublines;
-    subline->first_token_idx = curr_token;
-    subline->next = NULL;
-    subline->line_type = 0;
-    subline->time_mult = 0;
-
-    /* init the new line */
-    line->curr_subline  = &line->sublines;
-    line->next          = NULL;
-    line->subline_expire_time = 0;
-
-    /* connect to curr_line and vp pointers.
-     * 1) close the previous lines subline
-     * 2) connect to vp pointer
-     * 3) connect to curr_line global pointer
-     */
-    if (curr_line)
+    (void)element;
+    if (token->type == SKIN_TOKEN_DRAW_INBUILTBAR)
     {
-        curr_line->curr_subline->last_token_idx = curr_token - 1;
-        curr_line->next = line;
-        curr_line->curr_subline = NULL;
+        token->value.data = (void*)&curr_vp->vp;
     }
-    curr_line = line;
-    if (!vp->lines)
-        vp->lines = line;
-    return true;
-}
-
-#ifdef HAVE_LCD_BITMAP
-
-static int parse_statusbar_enable(const char *wps_bufptr,
-                                  struct wps_token *token,
-                                  struct wps_data *wps_data)
-{
-    (void)token; /* Kill warnings */
-    wps_data->wps_sb_tag = true;
-    wps_data->show_sb_on_wps = true;
-    struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
-    viewport_set_defaults(&default_vp->vp, curr_screen);
-    default_vp->vp.font = FONT_UI;
-    return skip_end_of_line(wps_bufptr);
-}
-
-static int parse_statusbar_disable(const char *wps_bufptr,
-                                   struct wps_token *token,
-                                   struct wps_data *wps_data)
-{
-    (void)token; /* Kill warnings */
-    wps_data->wps_sb_tag = true;
-    wps_data->show_sb_on_wps = false;
-    struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
-    viewport_set_fullscreen(&default_vp->vp, curr_screen);
-    default_vp->vp.font = FONT_UI;
-    return skip_end_of_line(wps_bufptr);
-}
-
-static int parse_statusbar_inbuilt(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data)
-{
-    (void)wps_data;
-    token->value.data = (void*)&curr_vp->vp;
-    return skip_end_of_line(wps_bufptr);
+    else
+    {
+        struct skin_element *def_vp = wps_data->tree;
+        struct skin_viewport *default_vp = def_vp->data;
+        if (def_vp->params_count == 0)
+        {
+            wps_data->wps_sb_tag = true;
+            wps_data->show_sb_on_wps = (token->type == SKIN_TOKEN_ENABLE_THEME);
+        }
+        if (wps_data->show_sb_on_wps)
+        {
+            viewport_set_defaults(&default_vp->vp, curr_screen);
+        }
+        else
+        {
+            viewport_set_fullscreen(&default_vp->vp, curr_screen);
+        }
+    }
+    return 0;
 }
-
+            
 static int get_image_id(int c)
 {
     if(c >= 'a' && c <= 'z')
@@ -625,32 +212,20 @@ static int get_image_id(int c)
 char *get_image_filename(const char *start, const char* bmpdir,
                                 char *buf, int buf_size)
 {
-    const char *end = start;
-    int bmpdirlen = strlen(bmpdir);
-
-    while (*end && *end != ',' && *end != ')')
-        end++;
-    if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
-    {
-        buf[0] = '\0';
-        return NULL;
-    }
-
-    strcpy(buf, bmpdir);
-    buf[bmpdirlen] = '/';
-    memcpy( &buf[bmpdirlen + 1], start, end - start);
-    buf[bmpdirlen + 1 + end - start] = 0;
-
+    snprintf(buf, buf_size, "%s/%s", bmpdir, start);
+    
     return buf;
 }
 
-static int parse_image_display(const char *wps_bufptr,
+static int parse_image_display(struct skin_element *element,
                                struct wps_token *token,
                                struct wps_data *wps_data)
 {
-    char label = wps_bufptr[1];
+    char *text = element->params[0].data.text;
+    char label = text[0];
+    char sublabel = text[1];
     int subimage;
-    struct gui_img *img;;
+    struct gui_img *img;
 
     /* sanity check */
     img = find_image(label, wps_data);
@@ -660,7 +235,7 @@ static int parse_image_display(const char *wps_bufptr,
         return WPS_ERROR_INVALID_PARAM;
     }
 
-    if ((subimage = get_image_id(wps_bufptr[2])) != -1)
+    if ((subimage = get_image_id(sublabel)) != -1)
     {
         if (subimage >= img->num_subimages)
             return WPS_ERROR_INVALID_PARAM;
@@ -674,32 +249,24 @@ static int parse_image_display(const char *wps_bufptr,
     }
 }
 
-static int parse_image_load(const char *wps_bufptr,
+static int parse_image_load(struct skin_element *element,
                             struct wps_token *token,
                             struct wps_data *wps_data)
 {
-    const char *ptr = wps_bufptr;
     const char* filename;
     const char* id;
     int x,y;
     struct gui_img *img;
 
-    /* format: %x|n|filename.bmp|x|y|
-       or %xl|n|filename.bmp|x|y|
-       or %xl|n|filename.bmp|x|y|num_subimages|
+    /* format: %x(n,filename.bmp,x,y)
+       or %xl(n,filename.bmp,x,y)
+       or %xl(n,filename.bmp,x,y,num_subimages)
     */
 
-    if (*ptr != '(')
-        return WPS_ERROR_INVALID_PARAM;
-
-    ptr++;
-
-    if (!(ptr = parse_list("ssdd", NULL, ',', ptr, &id, &filename, &x, &y)))
-        return WPS_ERROR_INVALID_PARAM;
-
-    /* Check there is a terminating ) */
-    if (*ptr != ')' && *ptr != ',')
-        return WPS_ERROR_INVALID_PARAM;
+    id = element->params[0].data.text;
+    filename = element->params[1].data.text;
+    x = element->params[2].data.number;
+    y = element->params[3].data.number;
 
     /* check the image number and load state */
     if(find_image(*id, wps_data))
@@ -707,7 +274,7 @@ static int parse_image_load(const char *wps_bufptr,
         /* Invalid image ID */
         return WPS_ERROR_INVALID_PARAM;
     }
-    img = skin_buffer_alloc(sizeof(struct gui_img));
+    img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
     if (!img)
         return WPS_ERROR_INVALID_PARAM;
     /* save a pointer to the filename */
@@ -717,62 +284,44 @@ static int parse_image_load(const char *wps_bufptr,
     img->y = y;
     img->num_subimages = 1;
     img->always_display = false;
+ //   img->just_drawn = false;
+    img->display = -1;
 
     /* save current viewport */
     img->vp = &curr_vp->vp;
 
-    if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
+    if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
     {
         img->always_display = true;
     }
-    else if (*ptr == ',')
+    else if (element->params_count == 5)
     {
-        /* Parse the (optional) number of sub-images */
-        ptr++;
-        img->num_subimages = atoi(ptr);
+        img->num_subimages = element->params[4].data.number;
         if (img->num_subimages <= 0)
             return WPS_ERROR_INVALID_PARAM;
-        /* Check there is a terminating ) */
-        while(isdigit(*ptr))
-            ptr++;
-        if (*ptr != ')')
-            return WPS_ERROR_INVALID_PARAM;
     }
-    struct skin_token_list *item = new_skin_token_list_item(NULL, img);
+    struct skin_token_list *item = 
+            (struct skin_token_list *)new_skin_token_list_item(NULL, img);
     if (!item)
         return WPS_ERROR_INVALID_PARAM;
     add_to_ll_chain(&wps_data->images, item);
 
-    /* Skip the rest of the line */
-    return skip_end_of_line(wps_bufptr);
+    return 0;
 }
 struct skin_font {
     int id; /* the id from font_load */
     char *name;  /* filename without path and extension */
 };
 static struct skin_font skinfonts[MAXUSERFONTS];
-static int parse_font_load(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data)
+static int parse_font_load(struct skin_element *element,
+                           struct wps_token *token,
+                           struct wps_data *wps_data)
 {
     (void)wps_data; (void)token;
-    const char *ptr = wps_bufptr;
-    int id;
-    char *filename;
-
-    if (*ptr != '(')
-        return WPS_ERROR_INVALID_PARAM;
-
-    ptr++;
-
-    if (!(ptr = parse_list("ds", NULL, ',', ptr, &id, &filename)))
-        return WPS_ERROR_INVALID_PARAM;
-
-    /* Check there is a terminating ) */
-    if (*ptr != ')')
-        return WPS_ERROR_INVALID_PARAM;
-
-    if (id <= FONT_UI || id >= MAXFONTS-1)
-        return WPS_ERROR_INVALID_PARAM;
+    int id = element->params[0].data.number;
+    char *filename = element->params[1].data.text;
+    char *ptr;
+    
 #if defined(DEBUG) || defined(SIMULATOR)
     if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
     {
@@ -782,286 +331,65 @@ static int parse_font_load(const char *wps_bufptr,
     /* make sure the filename contains .fnt, 
      * we dont actually use it, but require it anyway */
     ptr = strchr(filename, '.');
-    if (!ptr || strncmp(ptr, ".fnt)", 5))
+    if (!ptr || strncmp(ptr, ".fnt", 4))
         return WPS_ERROR_INVALID_PARAM;
     skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
     skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
 
-    return skip_end_of_line(wps_bufptr);
+    return 0;
 }
 
 
-static int parse_viewport_display(const char *wps_bufptr,
-                                  struct wps_token *token,
-                                  struct wps_data *wps_data)
-{
-    (void)wps_data;
-    char letter = wps_bufptr[1];
-
-    if (letter < 'a' || letter > 'z')
-    {
-        /* invalid viewport tag */
-        return WPS_ERROR_INVALID_PARAM;
-    }
-    token->value.i = letter;
-    return 3;
-}
-
 #ifdef HAVE_LCD_BITMAP
-static int parse_playlistview_text(struct playlistviewer *viewer,
-                             enum info_line_type line,  char* text)
-{
-    int cur_string = 0;
-    const struct wps_tag *tag;
-    int taglen = 0;
-    const char *start = text;
-    if (*text != ',')
-        return -1;
-    text++;
-    viewer->lines[line].count = 0;
-    viewer->lines[line].scroll = false;
-    while (*text != ',' && *text != ')')
-    {
-        if (*text == '%') /* it is a token of some type */
-        {
-            text++;
-            taglen = 0;
-            switch(*text)
-            {
-                case '%':
-                case '<':
-                case '|':
-                case '>':
-                case ';':
-                case '#':
-                case '(':
-                case ')':
-                case ',':
-                    /* escaped characters */
-                    viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
-                    viewer->lines[line].strings[cur_string][0] = *text;
-                    viewer->lines[line].strings[cur_string++][1] = '\0';
-                    text++;
-                    break;
-                default:
-                for (tag = all_tags;
-                     strncmp(text, tag->name, strlen(tag->name)) != 0;
-                     tag++) ;
-                /* %s isnt stored as a tag so manually check for it */
-                if (tag->type == WPS_NO_TOKEN)
-                {
-                    if (!strncmp(tag->name, "s", 1))
-                    {
-                        viewer->lines[line].scroll = true;
-                        taglen = 1;
-                    }
-                }
-                else if (tag->type == WPS_TOKEN_UNKNOWN)
-                {
-                    int i = 0;
-                    /* just copy the string */
-                    viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
-                    while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != ',' && text[i] != ')' && text[i] != '%')
-                    {
-                        viewer->lines[line].strings[cur_string][i] = text[i];
-                        i++;
-                    }
-                    viewer->lines[line].strings[cur_string][i] = '\0';
-                    cur_string++;
-                    taglen = i;
-                }
-                else
-                {
-                    if (tag->parse_func)
-                    {
-                        /* unsupported tag, reject */
-                        return -1;
-                    }
-                    taglen = strlen(tag->name);
-                    viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
-                }
-                text += taglen;
-            }
-        }
-        else
-        {
-            /* regular string */
-            int i = 0;
-            /* just copy the string */
-            viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
-            while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != ',' && text[i] != ')' && text[i] != '%')
-            {
-                viewer->lines[line].strings[cur_string][i] = text[i];
-                i++;
-            }
-            viewer->lines[line].strings[cur_string][i] = '\0';
-            cur_string++;
-            text += i;
-        }
-    }
-    return text - start;
-}
 
-static int parse_playlistview(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data)
+static int parse_playlistview(struct skin_element *element,
+                              struct wps_token *token,
+                              struct wps_data *wps_data)
 {
     (void)wps_data;
-    /* %Vp|<use icons>|<start offset>|info line text|no info text| */
-    struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
-    char *ptr = strchr(wps_bufptr, '(');
-    int length;
-    if (!viewer || !ptr)
+    struct playlistviewer *viewer = 
+        (struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
+    if (!viewer)
         return WPS_ERROR_INVALID_PARAM;
     viewer->vp = &curr_vp->vp;
     viewer->show_icons = true;
-    viewer->start_offset = atoi(ptr+1);
+    viewer->start_offset = element->params[0].data.number;
+    viewer->lines[0] = element->params[1].data.code;
+    viewer->lines[1] = element->params[2].data.code;
+    
     token->value.data = (void*)viewer;
-    ptr = strchr(ptr+1, ',');
-    length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
-    if (length < 0)
-        return WPS_ERROR_INVALID_PARAM;
-    length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
-    if (length < 0)
-        return WPS_ERROR_INVALID_PARAM;
-
-    return skip_end_of_line(wps_bufptr);
+    
+    return 0;
 }
 #endif
 
-static int parse_viewport(const char *wps_bufptr,
-                          struct wps_token *token,
-                          struct wps_data *wps_data)
-{
-    (void)token; /* Kill warnings */
-    const char *ptr = wps_bufptr;
-
-    struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
-
-    /* check for the optional letter to signify its a hideable viewport */
-    /* %Vl|<label>|<rest of tags>| */
-    skin_vp->hidden_flags = 0;
-    skin_vp->label = VP_NO_LABEL;
-    skin_vp->lines  = NULL;
-    if (curr_line)
-    {
-        curr_line->curr_subline->last_token_idx = wps_data->num_tokens
-                        - (wps_data->num_tokens > 0 ? 1 : 0);
-    }
-
-    curr_line = NULL;
-    if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
-        return WPS_ERROR_INVALID_PARAM;
-
-    if (*ptr == 'i')
-    {
-        if (*(ptr+1) == '(')
-        {
-            char label = *(ptr+2);
-            if (label >= 'a' && label <= 'z')
-            {
-                skin_vp->hidden_flags = VP_NEVER_VISIBLE;
-                skin_vp->label = VP_INFO_LABEL|label;
-                ptr += 3;
-            }
-            else
-            {
-                if (label != '-')
-                    return WPS_ERROR_INVALID_PARAM;
-                skin_vp->label = VP_INFO_LABEL|VP_DEFAULT_LABEL;
-                skin_vp->hidden_flags = VP_NEVER_VISIBLE;
-                ptr += 3;
-            }
-        }
-        else
-            return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7  */
-    }
-    else if (*ptr == 'l')
-    {
-        if (*(ptr+1) == '(')
-        {
-            char label = *(ptr+2);
-            if (label >= 'a' && label <= 'z')
-            {
-                skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
-                skin_vp->label = label;
-            }
-            else
-                return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7  */
-            ptr += 3;
-        }
-    }
-    if (*ptr != ',' && *ptr != '(')
-        return WPS_ERROR_INVALID_PARAM;
-
-    ptr++;
-    struct viewport *vp = &skin_vp->vp;
-    /* format: %V|x|y|width|height|font| */
-    if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, ',')))
-        return WPS_ERROR_INVALID_PARAM;
-
-    /* Check for trailing ) */
-    if (*ptr != ')')
-        return WPS_ERROR_INVALID_PARAM;
-    ptr++;
-
-    if (follow_lang_direction && lang_is_rtl())
-    {
-        vp->flags |= VP_FLAG_ALIGN_RIGHT;
-        vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
-    }
-    else
-        vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
-
 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
-    skin_vp->start_fgcolour = vp->fg_pattern;
-    skin_vp->start_bgcolour = vp->bg_pattern;
-#endif
 
-    struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
-    if (!list)
-        return WPS_ERROR_INVALID_PARAM;
-    add_to_ll_chain(&wps_data->viewports, list);
-    curr_vp = skin_vp;
-    /* Skip the rest of the line */
-    return ptr-wps_bufptr;
-}
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
-static int parse_viewportcolour(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data)
+static int parse_viewportcolour(struct skin_element *element,
+                                struct wps_token *token,
+                                struct wps_data *wps_data)
 {
     (void)wps_data;
-    const char *ptr = wps_bufptr;
-    int i;
-    bool found_text;
-    struct viewport_colour *colour = skin_buffer_alloc(sizeof(struct viewport_colour));
-    uint32_t set;
-    if (*ptr != '(' || !colour)
-        return -1;
-    ptr++;
-    if (!(ptr = parse_list("c", &set, ',', ptr, &colour->colour)))
-        return -1;
-    if (*ptr != ')')
+    struct skin_tag_parameter *param = element->params;
+    struct viewport_colour *colour = 
+        (struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
+    if (!colour)
         return -1;
-    if (!set)
+    if (isdefault(param))
+    {
         colour->colour = get_viewport_default_colour(curr_screen,
-                                        token->type == WPS_TOKEN_VIEWPORT_FGCOLOUR);
-    colour->vp = &curr_vp->vp;
-    token->value.data = colour;
-    /* If there havnt been any text tags between the %V() line and here use
-     * the colour as the viewport colour. fixes scrolling lines not
-     * having the correct colour */
-    i = curr_vp->lines->sublines.first_token_idx;
-    found_text = false;
-    while (!found_text && i< curr_vp->lines->sublines.last_token_idx)
+                                   token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
+    }
+    else
     {
-        if (wps_data->tokens[i++].type != WPS_TOKEN_CHARACTER &&
-            wps_data->tokens[i++].type != WPS_TOKEN_VIEWPORT_FGCOLOUR && 
-            wps_data->tokens[i++].type != WPS_TOKEN_VIEWPORT_BGCOLOUR )
-            found_text = true;
+        if (!parse_color(param->data.text, &colour->colour))
+            return -1;
     }
-    if (!found_text)
+    colour->vp = &curr_vp->vp;
+    token->value.data = colour;
+    if (element->line == curr_viewport_element->line)
     {
-        if (token->type == WPS_TOKEN_VIEWPORT_FGCOLOUR)
+        if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
         {
             curr_vp->start_fgcolour = colour->colour;
             curr_vp->vp.fg_pattern = colour->colour;
@@ -1072,72 +400,54 @@ static int parse_viewportcolour(const char *wps_bufptr,
             curr_vp->vp.bg_pattern = colour->colour;
         }
     }
-    ptr++;
-    return ptr - wps_bufptr;
+    return 0;
 }
 
-static int parse_image_special(const char *wps_bufptr,
+static int parse_image_special(struct skin_element *element,
                                struct wps_token *token,
                                struct wps_data *wps_data)
 {
     (void)wps_data; /* kill warning */
     (void)token;
-    const char *pos = NULL;
-    const char *newline;
     bool error = false;
 
-    pos = strchr(wps_bufptr + 1, ')');
-    newline = strchr(wps_bufptr, '\n');
-
-    error = (pos > newline);
-
 #if LCD_DEPTH > 1
-    if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
+    if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
     {
+        char *filename = element->params[0].data.text;
         /* format: %X|filename.bmp| or %Xd */
-        if (!strncmp(wps_bufptr, "(d)", 3))
+        if (!strcmp(filename, "d"))
         {
             wps_data->backdrop = NULL;
-            return skip_end_of_line(wps_bufptr);
+            return 0;
         }
         else if (!error)
-            wps_data->backdrop = (char*)wps_bufptr + 1;
+        {
+            wps_data->backdrop = filename;
+        }
     }
 #endif
-    if (error)
-        return WPS_ERROR_INVALID_PARAM;
     /* Skip the rest of the line */
-    return skip_end_of_line(wps_bufptr);
+    return error ? WPS_ERROR_INVALID_PARAM : 0;
 }
 #endif
 
 #endif /* HAVE_LCD_BITMAP */
 
-static int parse_setting_and_lang(const char *wps_bufptr,
-                                 struct wps_token *token,
-                                 struct wps_data *wps_data)
+static int parse_setting_and_lang(struct skin_element *element,
+                                  struct wps_token *token,
+                                  struct wps_data *wps_data)
 {
     /* NOTE: both the string validations that happen in here will
      * automatically PASS on checkwps because its too hard to get
-     * settings_list.c and englinsh.lang built for it. 
+     * settings_list.c and english.lang built for it. 
      * If that ever changes remove the #ifndef __PCTOOL__'s here 
      */
     (void)wps_data;
-    const char *ptr = wps_bufptr;
-    const char *end;
-    int i = 0;
-    char temp[64];
-
-    /* Find the setting's cfg_name */
-    if (*ptr != '(')
-        return WPS_ERROR_INVALID_PARAM;
-    ptr++;
-    end = strchr(ptr,')');
-    if (!end || (size_t)(end-ptr+1) > sizeof temp)
-        return WPS_ERROR_INVALID_PARAM;
-    strlcpy(temp, ptr,end-ptr+1);
+    char *temp = element->params[0].data.text;
+    int i;
     
-    if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
+    if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
     {
 #ifndef __PCTOOL__
         i = lang_english_to_id(temp);
@@ -1159,170 +469,104 @@ static int parse_setting_and_lang(const char *wps_bufptr,
     }
     /* Store the setting number */
     token->value.i = i;
-
-    /* Skip the rest of the line */
-    return end-ptr+2;
-}
-
-
-static int parse_dir_level(const char *wps_bufptr,
-                           struct wps_token *token,
-                           struct wps_data *wps_data)
-{
-    char val[] = { wps_bufptr[1], '\0' };
-    if (wps_bufptr[0] != '(' || wps_bufptr[2] != ')')
-        return WPS_ERROR_INVALID_PARAM;
-    token->value.i = atoi(val);
-    (void)wps_data; /* Kill warnings */
-    return 3;
+    return 0;
 }
 
-static int parse_timeout(const char *wps_bufptr,
-                         struct wps_token *token,
-                         struct wps_data *wps_data)
+static int parse_timeout_tag(struct skin_element *element,
+                             struct wps_token *token,
+                             struct wps_data *wps_data)
 {
-    int skip = 0;
+    (void)wps_data;
     int val = 0;
-    bool have_point = false;
-    bool have_tenth = false;
-
-    (void)wps_data; /* Kill the warning */
-    if (*wps_bufptr == '(')
+    if (element->params_count == 0)
     {
-        wps_bufptr++;
-        skip++;
-        while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
-        {
-            if (*wps_bufptr != '.')
-            {
-                val *= 10;
-                val += *wps_bufptr - '0';
-                if (have_point)
-                {
-                    have_tenth = true;
-                    wps_bufptr++;
-                    skip++;
-                    break;
-                }
-            }
-            else
-                have_point = true;
-
-            wps_bufptr++;
-            skip++;
-        }
-        if (*wps_bufptr != ')')
-            return -1;
-        skip++;
-    }
-    if (have_tenth == false)
-        val *= 10;
-
-    if (val == 0 && skip == 0)
-    {
-        /* decide what to do if no value was specified */
         switch (token->type)
         {
-            case WPS_TOKEN_SUBLINE_TIMEOUT:
+            case SKIN_TOKEN_SUBLINE_TIMEOUT:
                 return -1;
-            case WPS_TOKEN_BUTTON_VOLUME:
-            case WPS_TOKEN_TRACK_STARTING:
-            case WPS_TOKEN_TRACK_ENDING:
+            case SKIN_TOKEN_BUTTON_VOLUME:
+            case SKIN_TOKEN_TRACK_STARTING:
+            case SKIN_TOKEN_TRACK_ENDING:
+            case SKIN_TOKEN_LASTTOUCH:
                 val = 10;
                 break;
+            default:
+                break;
         }
     }
+    else
+        val = element->params[0].data.number;
     token->value.i = val;
-
-    return skip;
+    if (token->type == SKIN_TOKEN_SUBLINE_TIMEOUT)
+        curr_line->timeout = val * TIMEOUT_UNIT;
+    return 0;
 }
 
-static int parse_progressbar(const char *wps_bufptr,
-                             struct wps_token *token,
-                             struct wps_data *wps_data)
+static int parse_progressbar_tag(struct skin_element* element,
+                                 struct wps_token *token,
+                                 struct wps_data *wps_data)
 {
-    /* %pb or %pb|filename|x|y|width|height|
-    using - for any of the params uses "sane" values */
 #ifdef HAVE_LCD_BITMAP
-    enum {
-        PB_X = 0,
-        PB_Y,
-        PB_WIDTH,
-        PB_HEIGHT,
-        PB_FILENAME,
-    };
-    const char *filename;
-    int x, y, height, width;
-    uint32_t set = 0;
-    const char *ptr = wps_bufptr;
-    struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
-    struct skin_token_list *item = new_skin_token_list_item(token, pb);
-
-    if (!pb || !item)
-        return WPS_ERROR_INVALID_PARAM;
-
+    struct progressbar *pb;
+    struct skin_token_list *item;
     struct viewport *vp = &curr_vp->vp;
-    /* we need to know what line number (viewport relative) this pb is,
-     * so count them... */
-    int line_num = -1;
-    struct skin_line *line = curr_vp->lines;
-    while (line)
-    {
-        line_num++;
-        line = line->next;
-    }
-    if (curr_vp->label != VP_DEFAULT_LABEL)
-        line_num--;
+    struct skin_tag_parameter *param = element->params;
+    
+    if (element->params_count == 0 && 
+        element->tag->type != SKIN_TOKEN_PROGRESSBAR)
+        return 0; /* nothing to do */
+    pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
+    
+    token->value.data = pb;
+    
+    if (!pb)
+        return WPS_ERROR_INVALID_PARAM;
     pb->vp = vp;
     pb->have_bitmap_pb = false;
     pb->bm.data = NULL; /* no bitmap specified */
     pb->follow_lang_direction = follow_lang_direction > 0;
-    pb->draw = false;
-
-    if (*wps_bufptr != '(') /* regular old style */
+    
+    if (element->params_count == 0)
     {
         pb->x = 0;
         pb->width = vp->width;
         pb->height = SYSFONT_HEIGHT-2;
-        pb->y = -line_num - 1; /* Will be computed during the rendering */
-        if (token->type == WPS_TOKEN_VOLUME || token->type == WPS_TOKEN_BATTERY_PERCENT)
-            return 0; /* dont add it, let the regular token handling do the work */
-        pb->type = token->type;
-        add_to_ll_chain(&wps_data->progressbars, item);
+        pb->y = -1; /* Will be computed during the rendering */
+        pb->type = element->tag->type;
         return 0;
     }
-    ptr = wps_bufptr + 1;
-
-    if (!(ptr = parse_list("dddds", &set, ',', ptr,
-                           &x, &y, &width, &height, &filename)))
-        return WPS_ERROR_INVALID_PARAM;
-
-    if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
-        pb->bm.data = (char*)filename;
-
-    if (LIST_VALUE_PARSED(set, PB_X)) /* x */
-        pb->x = x;
+    
+    item = new_skin_token_list_item(token, pb);
+    if (!item)
+        return -1;
+    add_to_ll_chain(&wps_data->progressbars, item);
+    
+    /* (x,y,width,height,filename) */
+    if (!isdefault(param))
+        pb->x = param->data.number;
     else
         pb->x = vp->x;
-
-    if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
-    {
-        /* A zero width causes a divide-by-zero error later, so reject it */
-        if (width == 0)
-            return WPS_ERROR_INVALID_PARAM;
-
-        pb->width = width;
-    }
+    param++;
+    
+    if (!isdefault(param))
+        pb->y = param->data.number;
+    else
+        pb->y = -1; /* computed at rendering */
+    param++;
+    
+    if (!isdefault(param))
+        pb->width = param->data.number;
     else
         pb->width = vp->width - pb->x;
-
-    if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
+    param++;
+    
+    if (!isdefault(param))
     {
         /* A zero height makes no sense - reject it */
-        if (height == 0)
+        if (param->data.number == 0)
             return WPS_ERROR_INVALID_PARAM;
 
-        pb->height = height;
+        pb->height = param->data.number;
     }
     else
     {
@@ -1337,30 +581,26 @@ static int parse_progressbar(const char *wps_bufptr,
 #endif
         }
     }
-
-    if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
-        pb->y = y;
-    else
-        pb->y = -line_num - 1; /* Will be computed during the rendering */
-
-    if (*ptr != ')')
-        return WPS_ERROR_INVALID_PARAM;
-
-    add_to_ll_chain(&wps_data->progressbars, item);
-    if (token->type == WPS_TOKEN_VOLUME)
-        token->type = WPS_TOKEN_VOLUMEBAR;
-    else if (token->type == WPS_TOKEN_BATTERY_PERCENT)
-        token->type = WPS_TOKEN_BATTERY_PERCENTBAR;
+    param++;
+    if (!isdefault(param))
+        pb->bm.data = param->data.text;
+        
+        
+    if (token->type == SKIN_TOKEN_VOLUME)
+        token->type = SKIN_TOKEN_VOLUMEBAR;
+    else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
+        token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
     pb->type = token->type;
-
-    return ptr+1-wps_bufptr;
+        
+    return 0;
+    
 #else
-    (void)wps_bufptr;
-    if (token->type != WPS_TOKEN_VOLUME &&
-        token->type != WPS_TOKEN_BATTERY_PERCENTBAR)
+    (void)element;
+    if (token->type == SKIN_TOKEN_PROGRESSBAR ||
+        token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
     {
         wps_data->full_line_progressbar = 
-                        token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
+                        token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
     }
     return 0;
 
@@ -1368,32 +608,32 @@ static int parse_progressbar(const char *wps_bufptr,
 }
 
 #ifdef HAVE_ALBUMART
-static int parse_albumart_load(const char *wps_bufptr,
+static int parse_albumart_load(struct skin_element* element,
                                struct wps_token *token,
                                struct wps_data *wps_data)
 {
-    const char *ptr = wps_bufptr;
     struct dim dimensions;
     int albumart_slot;
     bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
-    struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
+    struct skin_albumart *aa = 
+        (struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
     (void)token; /* silence warning */
     if (!aa)
-        return skip_end_of_line(wps_bufptr);
+        return -1;
 
     /* reset albumart info in wps */
     aa->width = -1;
     aa->height = -1;
     aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
     aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
-    aa->vp = &curr_vp->vp;
 
-    if (*ptr != '(')
-        return WPS_ERROR_INVALID_PARAM; 
-    ptr++;
-    /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
-    if (!(ptr = parse_list("dddd", NULL,',',ptr, &aa->x, &aa->y, &aa->width, &aa->height)))
-        return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7  */
+    aa->x = element->params[0].data.number;
+    aa->y = element->params[1].data.number;
+    aa->width = element->params[2].data.number;
+    aa->height = element->params[3].data.number;
+    
+    aa->vp = &curr_vp->vp;
+    aa->draw_handle = -1;
 
     /* if we got here, we parsed everything ok .. ! */
     if (aa->width < 0)
@@ -1410,7 +650,6 @@ static int parse_albumart_load(const char *wps_bufptr,
         aa->x = LCD_WIDTH - (aa->x + aa->width);
 
     aa->state = WPS_ALBUMART_LOAD;
-    aa->draw = false;
     wps_data->albumart = aa;
 
     dimensions.width = aa->width;
@@ -1420,11 +659,10 @@ static int parse_albumart_load(const char *wps_bufptr,
 
     if (0 <= albumart_slot)
         wps_data->playback_aa_slot = albumart_slot;
-
-    if (*ptr == ',')
+        
+    if (element->params_count > 4 && !isdefault(&element->params[4]))
     {
-        ptr++;
-        switch (*ptr)
+        switch (*element->params[4].data.text)
         {
             case 'l':
             case 'L':
@@ -1445,12 +683,10 @@ static int parse_albumart_load(const char *wps_bufptr,
                     aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
                 break;
         }
-        ptr++;
     }
-    if (*ptr == ',')
+    if (element->params_count > 5 && !isdefault(&element->params[5]))
     {
-        ptr++;
-        switch (*ptr)
+        switch (*element->params[5].data.text)
         {
             case 't':
             case 'T':
@@ -1465,32 +701,10 @@ static int parse_albumart_load(const char *wps_bufptr,
                 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
                 break;
         }
-        ptr++;
     }
-    if (*ptr != ')')
-        return WPS_ERROR_INVALID_PARAM;
-
-    return skip_end_of_line(wps_bufptr);
+    return 0;
 }
 
-static int parse_albumart_display(const char *wps_bufptr,
-                                      struct wps_token *token,
-                                      struct wps_data *wps_data)
-{
-    (void)wps_bufptr;
-    (void)token;
-    if (wps_data->albumart)
-    {
-        wps_data->albumart->vp = &curr_vp->vp;
-    }
-#if 0
-    /* the old code did this so keep it here for now...
-     * this is to allow the posibility to showing the next tracks AA! */
-    if (wps_bufptr+1 == 'n')
-        return 1;
-#endif
-    return 0;
-};
 #endif /* HAVE_ALBUMART */
 
 #ifdef HAVE_TOUCHSCREEN
@@ -1520,8 +734,9 @@ static const struct touchaction touchactions[] = {
 #endif
 };
 
-static int parse_touchregion(const char *wps_bufptr,
-        struct wps_token *token, struct wps_data *wps_data)
+static int parse_touchregion(struct skin_element *element,
+                             struct wps_token *token,
+                             struct wps_data *wps_data)
 {
     (void)token;
     unsigned i, imax;
@@ -1533,7 +748,7 @@ static int parse_touchregion(const char *wps_bufptr,
     int x,y,w,h;
     char temp[20];
 
-    /* format: %T|x|y|width|height|action|
+    /* format: %T(x,y,width,height,action)
      * if action starts with & the area must be held to happen
      * action is one of:
      * play  -  play/pause playback
@@ -1554,35 +769,22 @@ static int parse_touchregion(const char *wps_bufptr,
      * voldown - decrease volume by one step
     */
 
-
-    if (*ptr != '(')
-        return WPS_ERROR_INVALID_PARAM;
-    ptr++;
-
-    if (!(ptr = parse_list("dddds", NULL, ',', ptr, &x, &y, &w, &h, &action)))
-        return WPS_ERROR_INVALID_PARAM;
-
-    /* Check there is a terminating ) */
-    if (*ptr != ')')
-        return WPS_ERROR_INVALID_PARAM;
-
-    region = skin_buffer_alloc(sizeof(struct touchregion));
+    
+    region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
     if (!region)
         return WPS_ERROR_INVALID_PARAM;
 
     /* should probably do some bounds checking here with the viewport... but later */
     region->action = ACTION_NONE;
-    region->x = x;
-    region->y = y;
-    region->width = w;
-    region->height = h;
+    region->x = element->params[0].data.number;
+    region->y = element->params[1].data.number;
+    region->width = element->params[2].data.number;
+    region->height = element->params[3].data.number;
     region->wvp = curr_vp;
     region->armed = false;
     region->reverse_bar = false;
+    action = element->params[4].data.text;
 
-    end = strchr(action, ')');
-    if (!end || (size_t)(end-action+1) > sizeof temp)
-        return WPS_ERROR_INVALID_PARAM;
     strlcpy(temp, action, end-action+1);
     action = temp;
     
@@ -1625,439 +827,38 @@ static int parse_touchregion(const char *wps_bufptr,
     if (!item)
         return WPS_ERROR_INVALID_PARAM;
     add_to_ll_chain(&wps_data->touchregions, item);
-    return skip_end_of_line(wps_bufptr);
+    return 0;
 }
 #endif
 
-/* Parse a generic token from the given string. Return the length read */
-static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
-{
-    int skip = 0, taglen = 0, ret;
-    struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
-    const struct wps_tag *tag;
-    memset(token, 0, sizeof(*token));
-
-    switch(*wps_bufptr)
-    {
-
-        case '%':
-        case '<':
-        case '|':
-        case '>':
-        case ';':
-        case '#':
-        case ')':
-        case '(':
-        case ',':
-            /* escaped characters */
-            token->type = WPS_TOKEN_CHARACTER;
-            token->value.c = *wps_bufptr;
-            taglen = 1;
-            wps_data->num_tokens++;
-            break;
-
-        case '?':
-            /* conditional tag */
-            token->type = WPS_TOKEN_CONDITIONAL;
-            level++;
-            condindex[level] = wps_data->num_tokens;
-            numoptions[level] = 1;
-            wps_data->num_tokens++;
-            ret = parse_token(wps_bufptr + 1, wps_data);
-            if (ret < 0) return ret;
-            taglen = 1 + ret;
-            break;
-
-        default:
-            /* find what tag we have */
-            for (tag = all_tags;
-                 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
-                 tag++) ;
-
-            taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
-            token->type = tag->type;
-            curr_line->curr_subline->line_type |= tag->refresh_type;
-
-            /* if the tag has a special parsing function, we call it */
-            if (tag->parse_func)
-            {
-                ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
-                if (ret < 0) return ret;
-                skip += ret;
-            }
-
-            /* Some tags we don't want to save as tokens */
-            if (tag->type == WPS_NO_TOKEN)
-                break;
-
-            /* tags that start with 'F', 'I' or 'D' are for the next file */
-            if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
-                 *(tag->name) == 'D')
-                token->next = true;
-
-            wps_data->num_tokens++;
-            break;
-    }
-
-    skip += taglen;
-    return skip;
-}
-
-
-/*
- * Returns the number of bytes to skip the buf pointer to access the false
- * branch in a _binary_ conditional
- *
- * That is:
- *  - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
- *  - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
- *
- * depending on the features of a target it's not called from check_feature_tag,
- * hence the __attribute__ or it issues compiler warnings
- *
- **/
-
-static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
-static int find_false_branch(const char *wps_bufptr)
+static bool check_feature_tag(const int type)
 {
-    const char *buf = wps_bufptr;
-    /* wps_bufptr is after the opening '<', hence level = 1*/
-    int level = 1;
-    char ch;
-    do
-    {
-        ch = *buf;
-        if (ch == '%')
-        {   /* filter out the characters we check later if they're printed
-             * as literals */
-            ch = *(++buf);
-            if (ch == '<' || ch == '>' || ch == '|')
-                continue;
-            /* else: some tags/printed literals we skip over */
-        }
-        else if (ch == '<') /* nested conditional */
-            level++;
-        else if (ch == '>')
-        {   /* closed our or a nested conditional,
-             * do NOT skip over the '>' so that wps_parse() sees it for closing
-             * if it is the closing one for our conditional */
-            level--;
-        }
-        else if (ch == '|' && level == 1)
-        {   /* we found our separator, point before and get out */
-            break;
-        }
-    /* if level is 0, we don't have a false branch */
-    } while (level > 0 && *(++buf));
-
-    return buf - wps_bufptr;
-}
-
-/*
- * returns the number of bytes to get the appropriate branch of a binary
- * conditional
- *
- * That means:
- *  - if a feature is available, it returns 0 to not skip anything
- *  - if the feature is not available, skip to the false branch and don't
- *    parse the true branch at all
- *
- * */
-static int check_feature_tag(const char *wps_bufptr, const int type)
-{
-    (void)wps_bufptr;
     switch (type)
     {
-        case WPS_TOKEN_RTC_PRESENT:
+        case SKIN_TOKEN_RTC_PRESENT:
 #if CONFIG_RTC
-            return 0;
+            return true;
 #else
-            return find_false_branch(wps_bufptr);
+            return false;
 #endif
-        case WPS_TOKEN_HAVE_RECORDING:
+        case SKIN_TOKEN_HAVE_RECORDING:
 #ifdef HAVE_RECORDING
-            return 0;
+            return true;
 #else
-            return find_false_branch(wps_bufptr);
+            return false;
 #endif
-        case WPS_TOKEN_HAVE_TUNER:
+        case SKIN_TOKEN_HAVE_TUNER:
 #if CONFIG_TUNER
             if (radio_hardware_present())
-                return 0;
+                return true;
 #endif
-            return find_false_branch(wps_bufptr);
+            return false;
 
         default: /* not a tag we care about, just don't skip */
-            return 0;
-    }
-}
-
-
-/* Parses the WPS.
-   data is the pointer to the structure where the parsed WPS should be stored.
-        It is initialised.
-   wps_bufptr points to the string containing the WPS tags */
-#define TOKEN_BLOCK_SIZE 128
-static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
-{
-    if (!data || !wps_bufptr || !*wps_bufptr)
-        return false;
-    enum wps_parse_error fail = PARSE_OK;
-    int ret;
-    int max_tokens = TOKEN_BLOCK_SIZE;
-    size_t buf_free = 0;
-    line_number = 0;
-    level = -1;
-
-    /* allocate enough RAM for a reasonable skin, grow as needed.
-     * Free any used RAM before loading the images to be 100% RAM efficient */
-    data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
-    if (sizeof(struct wps_token)*max_tokens >= buf_free)
-        return false;
-    skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
-    data->num_tokens = 0;
-
-#if LCD_DEPTH > 1
-    /* Backdrop defaults to the setting unless %X is used, so set it now */
-    if (global_settings.backdrop_file[0])
-    {
-        data->backdrop = "-";
-    }
-#endif
-
-    while (*wps_bufptr && !fail)
-    {
-        if (follow_lang_direction)
-            follow_lang_direction--;
-        /* first make sure there is enough room for tokens */
-        if (max_tokens <= data->num_tokens + 5)
-        {
-            int extra_tokens = TOKEN_BLOCK_SIZE;
-            size_t needed = extra_tokens * sizeof(struct wps_token);
-            /* do some smarts here to grow the array a bit */
-            if (skin_buffer_freespace() < needed)
-            {
-                fail = PARSE_FAIL_LIMITS_EXCEEDED;
-                break;
-            }
-            skin_buffer_increment(needed, false);
-            max_tokens += extra_tokens;
-        }
-
-        switch(*wps_bufptr++)
-        {
-
-            /* Regular tag */
-            case '%':
-                if ((ret = parse_token(wps_bufptr, data)) < 0)
-                {
-                    fail = PARSE_FAIL_COND_INVALID_PARAM;
-                    break;
-                }
-                else if (level >= WPS_MAX_COND_LEVEL - 1)
-                {
-                    fail = PARSE_FAIL_LIMITS_EXCEEDED;
-                    break;
-                }
-                wps_bufptr += ret;
-                break;
-
-            /* Alternating sublines separator */
-            case ';':
-                if (level >= 0) /* there are unclosed conditionals */
-                {
-                    fail = PARSE_FAIL_UNCLOSED_COND;
-                    break;
-                }
-
-                if (!skin_start_new_subline(curr_line, data->num_tokens))
-                    fail = PARSE_FAIL_LIMITS_EXCEEDED;
-
-                break;
-
-            /* Conditional list start */
-            case '<':
-                if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
-                {
-                    fail = PARSE_FAIL_COND_SYNTAX_ERROR;
-                    break;
-                }
-                wps_bufptr += check_feature_tag(wps_bufptr,
-                                    data->tokens[data->num_tokens-1].type);
-                data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
-                lastcond[level] = data->num_tokens++;
-                break;
-
-            /* Conditional list end */
-            case '>':
-                if (level < 0) /* not in a conditional, invalid char */
-                {
-                    fail = PARSE_FAIL_INVALID_CHAR;
-                    break;
-                }
-
-                data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
-                if (lastcond[level])
-                    data->tokens[lastcond[level]].value.i = data->num_tokens;
-                else
-                {
-                    fail = PARSE_FAIL_COND_SYNTAX_ERROR;
-                    break;
-                }
-
-                lastcond[level] = 0;
-                data->num_tokens++;
-                data->tokens[condindex[level]].value.i = numoptions[level];
-                level--;
-                break;
-
-            /* Conditional list option */
-            case '|':
-                if (level < 0) /* not in a conditional, invalid char */
-                {
-                    fail = PARSE_FAIL_INVALID_CHAR;
-                    break;
-                }
-
-                data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
-                if (lastcond[level])
-                    data->tokens[lastcond[level]].value.i = data->num_tokens;
-                else
-                {
-                    fail = PARSE_FAIL_COND_SYNTAX_ERROR;
-                    break;
-                }
-
-                lastcond[level] = data->num_tokens;
-                numoptions[level]++;
-                data->num_tokens++;
-                break;
-
-            /* Comment */
-            case '#':
-                if (level >= 0) /* there are unclosed conditionals */
-                {
-                    fail = PARSE_FAIL_UNCLOSED_COND;
-                    break;
-                }
-
-                wps_bufptr += skip_end_of_line(wps_bufptr);
-                break;
-
-            /* End of this line */
-            case '\n':
-                if (level >= 0) /* there are unclosed conditionals */
-                {
-                    fail = PARSE_FAIL_UNCLOSED_COND;
-                    break;
-                }
-                /* add a new token for the \n so empty lines are correct */
-                data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
-                data->tokens[data->num_tokens].value.c = '\n';
-                data->tokens[data->num_tokens].next = false;
-                data->num_tokens++;
-
-                if (!skin_start_new_line(curr_vp, data->num_tokens))
-                {
-                    fail = PARSE_FAIL_LIMITS_EXCEEDED;
-                    break;
-                }
-                line_number++;
-
-                break;
-
-            /* String */
-            default:
-                {
-                    unsigned int len = 1;
-                    const char *string_start = wps_bufptr - 1;
-
-                    /* find the length of the string */
-                    while (*wps_bufptr && *wps_bufptr != '#' &&
-                           *wps_bufptr != '%' && *wps_bufptr != ';' &&
-                           *wps_bufptr != '<' && *wps_bufptr != '>' &&
-                           *wps_bufptr != '|' && *wps_bufptr != '\n')
-                    {
-                        wps_bufptr++;
-                        len++;
-                    }
-
-                    /* look if we already have that string */
-                    char *str;
-                    bool found = false;
-                    struct skin_token_list *list = data->strings;
-                    while (list)
-                    {
-                        str = (char*)list->token->value.data;
-                        found = (strlen(str) == len &&
-                                    strncmp(string_start, str, len) == 0);
-                        if (found)
-                            break; /* break here because the list item is
-                                      used if its found */
-                        list = list->next;
-                    }
-                    /* If a matching string is found, found is true and i is
-                       the index of the string. If not, found is false */
-
-                    if (!found)
-                    {
-                        /* new string */
-                        str = (char*)skin_buffer_alloc(len+1);
-                        if (!str)
-                        {
-                            fail = PARSE_FAIL_LIMITS_EXCEEDED;
-                            break;
-                        }
-                        strlcpy(str, string_start, len+1);
-                        struct skin_token_list *item =
-                            new_skin_token_list_item(&data->tokens[data->num_tokens], str);
-                        if(!item)
-                        {
-                            fail = PARSE_FAIL_LIMITS_EXCEEDED;
-                            break;
-                        }
-                        add_to_ll_chain(&data->strings, item);
-                    }
-                    else
-                    {
-                        /* another occurrence of an existing string */
-                        data->tokens[data->num_tokens].value.data = list->token->value.data;
-                    }
-                    data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
-                    data->num_tokens++;
-                }
-                break;
-        }
-    }
-
-    if (!fail && level >= 0) /* there are unclosed conditionals */
-        fail = PARSE_FAIL_UNCLOSED_COND;
-
-    if (*wps_bufptr && !fail)
-        /* one of the limits of the while loop was exceeded */
-        fail = PARSE_FAIL_LIMITS_EXCEEDED;
-
-    /* Success! */
-    curr_line->curr_subline->last_token_idx = data->num_tokens;
-    data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
-    /* freeup unused tokens */
-    skin_buffer_free_from_front(sizeof(struct wps_token)
-                                * (max_tokens - data->num_tokens));
-
-#ifdef DEBUG_SKIN_ENGINE
-    if (debug)
-    {
-        print_debug_info(data, fail, line_number);
-        debug_skin_usage();
+            return true;
     }
-#else
-    (void)debug;
-#endif
-
-    return (fail == 0);
 }
 
-
 /*
  * initial setup of wps_data; does reset everything
  * except fields which need to survive, i.e.
@@ -2065,6 +866,7 @@ static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
  **/
 static void skin_data_reset(struct wps_data *wps_data)
 {
+    wps_data->tree = NULL;
 #ifdef HAVE_LCD_BITMAP
     wps_data->images = NULL;
     wps_data->progressbars = NULL;
@@ -2075,8 +877,6 @@ static void skin_data_reset(struct wps_data *wps_data)
 #ifdef HAVE_TOUCHSCREEN
     wps_data->touchregions = NULL;
 #endif
-    wps_data->viewports = NULL;
-    wps_data->strings = NULL;
 #ifdef HAVE_ALBUMART
     wps_data->albumart = NULL;
     if (wps_data->playback_aa_slot >= 0)
@@ -2085,8 +885,6 @@ static void skin_data_reset(struct wps_data *wps_data)
         wps_data->playback_aa_slot = -1;
     }
 #endif
-    wps_data->tokens = NULL;
-    wps_data->num_tokens = 0;
 
 #ifdef HAVE_LCD_BITMAP
     wps_data->peak_meter_enabled = false;
@@ -2109,6 +907,7 @@ static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char
 {
     (void)wps_data; /* only needed for remote targets */
     char img_path[MAX_PATH];
+    int fd;
     get_image_filename(bitmap->data, bmpdir,
                        img_path, sizeof(img_path));
 
@@ -2121,14 +920,24 @@ static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char
 #endif
         format = FORMAT_ANY|FORMAT_TRANSPARENT;
 
-    size_t max_buf;
-    char* imgbuf = (char*)skin_buffer_grab(&max_buf);
+    fd = open(img_path, O_RDONLY);
+    if (fd < 0)
+        return false;
+    size_t buf_size = read_bmp_file(img_path, bitmap, 0, 
+                                    format|FORMAT_RETURN_SIZE, NULL);  
+    char* imgbuf = (char*)skin_buffer_alloc(buf_size);
+    if (!imgbuf)
+    {
+        close(fd);
+        return NULL;
+    }
+    lseek(fd, 0, SEEK_SET);
     bitmap->data = imgbuf;
-    int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
+    int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
 
+    close(fd);
     if (ret > 0)
     {
-        skin_buffer_increment(ret, true);
         return true;
     }
     else
@@ -2194,14 +1003,14 @@ static bool skin_load_fonts(struct wps_data *data)
 {
     /* don't spit out after the first failue to aid debugging */
     bool success = true;
-    struct skin_token_list *vp_list;
+    struct skin_element *vp_list;
     int font_id;
     /* walk though each viewport and assign its font */
-    for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
+    for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
     {
         /* first, find the viewports that have a non-sys/ui-font font */
         struct skin_viewport *skin_vp =
-                (struct skin_viewport*)vp_list->token->value.data;
+                (struct skin_viewport*)vp_list->data;
         struct viewport *vp = &skin_vp->vp;
 
 
@@ -2240,6 +1049,7 @@ static bool skin_load_fonts(struct wps_data *data)
         {
             DEBUGF("Unable to load font %d: '%s.fnt'\n",
                     font_id, font->name);
+            font->name = NULL; /* to stop trying to load it again if we fail */
             success = false;
             font->name = NULL;
             continue;
@@ -2252,6 +1062,272 @@ static bool skin_load_fonts(struct wps_data *data)
 }
 
 #endif /* HAVE_LCD_BITMAP */
+static int convert_viewport(struct wps_data *data, struct skin_element* element)
+{
+    struct skin_viewport *skin_vp = 
+        (struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
+    struct screen *display = &screens[curr_screen];
+    
+    if (!skin_vp)
+        return CALLBACK_ERROR;
+        
+    skin_vp->hidden_flags = 0;
+    skin_vp->label = VP_NO_LABEL;
+    element->data = skin_vp;
+    curr_vp = skin_vp;
+    curr_viewport_element = element;
+    
+    viewport_set_defaults(&skin_vp->vp, curr_screen);
+    
+#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
+    skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
+    skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
+#endif
+    
+
+    struct skin_tag_parameter *param = element->params;
+    if (element->params_count == 0) /* default viewport */
+    {
+        if (!data->tree) /* first viewport in the skin */
+            data->tree = element;
+        skin_vp->label = VP_DEFAULT_LABEL;
+        return CALLBACK_OK;
+    }
+    
+    if (element->params_count == 6)
+    {
+        if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
+        {
+            if (isdefault(param))
+            {
+                skin_vp->hidden_flags = VP_NEVER_VISIBLE;
+                skin_vp->label = VP_INFO_LABEL|VP_DEFAULT_LABEL;
+            }
+            else
+            {
+                skin_vp->hidden_flags = VP_NEVER_VISIBLE;
+                skin_vp->label = VP_INFO_LABEL|param->data.text[0];
+            }
+        }
+        else
+        {
+                skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
+                skin_vp->label = param->data.text[0];
+        }
+        param++;
+    }
+    /* x */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.x = param->data.number;
+        if (param->data.number < 0)
+            skin_vp->vp.x += display->lcdwidth;
+    }
+    param++;
+    /* y */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.y = param->data.number;
+        if (param->data.number < 0)
+            skin_vp->vp.y += display->lcdheight;
+    }
+    param++;
+    /* width */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.width = param->data.number;
+        if (param->data.number < 0)
+            skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
+    }
+    else
+    {
+        skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
+    }
+    param++;
+    /* height */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.height = param->data.number;
+        if (param->data.number < 0)
+            skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
+    }
+    else
+    {
+        skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
+    }
+    param++;
+    /* font */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.font = param->data.number;
+    }
+
+    return CALLBACK_OK;
+    
+}
+
+int skin_element_callback(struct skin_element* element, void* data)
+{
+    struct wps_data *wps_data = (struct wps_data *)data;
+    struct wps_token *token;
+    parse_function function = NULL;
+    
+    switch (element->type)
+    {
+        case TAG:
+        {
+            token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
+            memset(token, 0, sizeof(*token));
+            token->type = element->tag->type;
+            
+            if ((element->tag->flags&SKIN_REFRESH_ALL) == SKIN_RTC_REFRESH)
+            {
+#ifdef CONFIG_RTC
+                curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
+#else
+                curr_line->update_mode |= SKIN_REFRESH_STATIC;
+#endif
+            }
+            else
+                curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
+            
+            element->data = token;
+            
+            /* Some tags need special handling for the tag, so add them here */
+            switch (token->type)
+            {
+                case SKIN_TOKEN_ALIGN_LANGDIRECTION:
+                    follow_lang_direction = 2;
+                    break;
+                case SKIN_TOKEN_PROGRESSBAR:
+                case SKIN_TOKEN_VOLUME:
+                case SKIN_TOKEN_BATTERY_PERCENT:
+                case SKIN_TOKEN_PLAYER_PROGRESSBAR:
+                    function = parse_progressbar_tag;
+                    break;
+                case SKIN_TOKEN_SUBLINE_TIMEOUT:
+                case SKIN_TOKEN_BUTTON_VOLUME:
+                case SKIN_TOKEN_TRACK_STARTING:
+                case SKIN_TOKEN_TRACK_ENDING:
+                case SKIN_TOKEN_LASTTOUCH:
+                    function = parse_timeout_tag;
+                    break;
+                case SKIN_TOKEN_DISABLE_THEME:
+                case SKIN_TOKEN_ENABLE_THEME:
+                case SKIN_TOKEN_DRAW_INBUILTBAR:
+                    function = parse_statusbar_tags;
+                    break;
+                case SKIN_TOKEN_FILE_DIRECTORY:
+                    token->value.i = element->params[0].data.number;
+                    break;
+#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
+                case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
+                case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
+                    function = parse_viewportcolour;
+                    break;
+                case SKIN_TOKEN_IMAGE_BACKDROP:
+                    function = parse_image_special;
+                    break;
+#endif
+                case SKIN_TOKEN_TRANSLATEDSTRING:
+                case SKIN_TOKEN_SETTING:
+                    function = parse_setting_and_lang;
+                    break;
+                case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
+                    function = parse_playlistview;
+                    break;
+#ifdef HAVE_LCD_BITMAP
+                case SKIN_TOKEN_LOAD_FONT:
+                    function = parse_font_load;
+                    break;
+                case SKIN_TOKEN_VIEWPORT_ENABLE:
+                case SKIN_TOKEN_UIVIEWPORT_ENABLE:
+                    token->value.i = element->params[0].data.text[0];
+                    break;
+                case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
+                    function = parse_image_display;
+                    break;
+                case SKIN_TOKEN_IMAGE_PRELOAD:
+                case SKIN_TOKEN_IMAGE_DISPLAY:
+                    function = parse_image_load;
+                    break;
+#endif
+#ifdef HAVE_TOUCHSCREEN
+                case SKIN_TOKEN_TOUCHREGION:
+                    function = parse_touchregion;
+                    break;
+#endif
+#ifdef HAVE_ALBUMART
+                case SKIN_TOKEN_ALBUMART_DISPLAY:
+                    if (wps_data->albumart)
+                        wps_data->albumart->vp = &curr_vp->vp;
+                    break;
+                case SKIN_TOKEN_ALBUMART_LOAD:
+                    function = parse_albumart_load;
+                    break;
+#endif
+                default:
+                    break;
+            }
+            if (function)
+            {
+                if (function(element, token, wps_data) < 0)
+                    return CALLBACK_ERROR;
+            }
+            /* tags that start with 'F', 'I' or 'D' are for the next file */
+            if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
+                 *(element->tag->name) == 'D')
+                token->next = true;
+            if (follow_lang_direction > 0 )
+                follow_lang_direction--;
+
+            element->data = token;
+            break;
+        }
+        case VIEWPORT:
+            return convert_viewport(wps_data, element);
+        case LINE:
+        {
+            struct line *line = 
+                (struct line *)skin_buffer_alloc(sizeof(struct line));
+            line->update_mode = SKIN_REFRESH_STATIC;
+            line->timeout = DEFAULT_SUBLINE_TIME_MULTIPLIER * TIMEOUT_UNIT;
+            curr_line = line;
+            element->data = line;
+        }
+        break;
+        case LINE_ALTERNATOR:
+        {
+            struct line_alternator *alternator = 
+                (struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
+            alternator->current_line = 0;
+#ifndef __PCTOOL__
+            alternator->last_change_tick = current_tick;
+#endif
+            element->data = alternator;
+        }
+        break;
+        case CONDITIONAL:
+        {
+            struct conditional *conditional = 
+                (struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
+            conditional->last_value = -1;
+            conditional->token = element->data;
+            element->data = conditional;
+            if (!check_feature_tag(element->tag->type))
+            {
+                return FEATURE_NOT_AVAILABLE;
+            }
+            return CALLBACK_OK;
+        }
+        case TEXT:
+            curr_line->update_mode |= SKIN_REFRESH_STATIC;
+            break;
+        default:
+            break;
+    }
+    return CALLBACK_OK;
+}
 
 /* to setup up the wps-data from a format-buffer (isfile = false)
    from a (wps-)file (isfile = true)*/
@@ -2288,37 +1364,13 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
     }
 #endif
 
+
     skin_data_reset(wps_data);
     wps_data->wps_loaded = false;
     curr_screen = screen;
-
-    /* alloc default viewport, will be fixed up later */
-    curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
-    if (!curr_vp)
-        return false;
-    struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
-    if (!list)
-        return false;
-    add_to_ll_chain(&wps_data->viewports, list);
-
-
-    /* Initialise the first (default) viewport */
-    curr_vp->label         = VP_DEFAULT_LABEL;
-    curr_vp->hidden_flags  = 0;
-    curr_vp->lines         = NULL;
-
-    viewport_set_defaults(&curr_vp->vp, screen);
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
-    curr_vp->start_fgcolour = curr_vp->vp.fg_pattern;
-    curr_vp->start_bgcolour = curr_vp->vp.bg_pattern;
-#endif
-#ifdef HAVE_LCD_BITMAP
-    curr_vp->vp.font = FONT_UI;
-#endif
-
     curr_line = NULL;
-    if (!skin_start_new_line(curr_vp, 0))
-        return false;
+    curr_vp = NULL;
+    curr_viewport_element = NULL;
 
     if (isfile)
     {
@@ -2354,9 +1406,15 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
     {
         wps_buffer = (char*)buf;
     }
-    /* parse the WPS source */
-    if (!wps_parse(wps_data, wps_buffer, isfile)) {
+#if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
+    wps_data->backdrop = "-";
+#endif
+    /* parse the skin source */
+    skin_buffer_save_position();
+    wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
+    if (!wps_data->tree) {
         skin_data_reset(wps_data);
+        skin_buffer_restore_position();
         return false;
     }
 
@@ -2377,6 +1435,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
         !skin_load_fonts(wps_data)) 
     {
         skin_data_reset(wps_data);
+        skin_buffer_restore_position();
         return false;
     }
 #endif
@@ -2400,8 +1459,8 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
 #endif
     wps_data->wps_loaded = true;
 #ifdef DEBUG_SKIN_ENGINE
-    if (isfile && debug_wps)
-        debug_skin_usage();
+ //   if (isfile && debug_wps)
+ //       debug_skin_usage();
 #endif
     return true;
 }
diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c
new file mode 100644
index 0000000..a923f7d
--- /dev/null
+++ b/apps/gui/skin_engine/skin_render.c
@@ -0,0 +1,596 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $
+ *
+ * Copyright (C) 2010 Jonathan Gordon
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include "strlcat.h"
+
+#include "config.h"
+#include "kernel.h"
+#include "albumart.h"
+#include "skin_display.h"
+#include "skin_engine.h"
+#include "skin_parser.h"
+#include "tag_table.h"
+#include "skin_scan.h"
+#include "radio.h"
+#include "language.h"
+#include "playback.h"
+
+
+#define MAX_LINE 1024
+
+struct skin_draw_info {
+    struct gui_wps *gwps;
+    struct skin_viewport *skin_vp;
+    int line_number;
+    unsigned long refresh_type;
+    
+    char* cur_align_start;
+    struct align_pos align;
+    bool no_line_break;
+    bool line_scrolls;
+    bool force_redraw;
+    
+    char *buf;
+    size_t buf_size;
+};
+
+typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
+bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
+
+
+static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
+                             struct skin_element *element, struct viewport* vp)
+{
+    struct wps_token *token = (struct wps_token *)element->data;
+    struct wps_data *data = gwps->data;
+    bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
+    switch (token->type)
+    {   
+#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
+        case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
+        {
+            struct viewport_colour *col = token->value.data;
+            col->vp->fg_pattern = col->colour;
+        }
+        break;
+        case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
+        {
+            struct viewport_colour *col = token->value.data;
+            col->vp->bg_pattern = col->colour;
+        }
+        break;
+#endif
+        case SKIN_TOKEN_VIEWPORT_ENABLE:
+        {
+            char label = token->value.i;
+            char temp = VP_DRAW_HIDEABLE;
+            struct skin_element *viewport = gwps->data->tree;
+            while (viewport)
+            {
+                struct skin_viewport *skinvp = (struct skin_viewport*)viewport->data;
+                if (skinvp->label == label)
+                {
+                    if (!(skinvp->hidden_flags&VP_DRAW_WASHIDDEN))
+                        temp |= VP_DRAW_WASHIDDEN;
+                    skinvp->hidden_flags = temp;
+                }
+                viewport = viewport->next;
+            }
+        }
+        break;
+#ifdef HAVE_LCD_BITMAP
+        case SKIN_TOKEN_UIVIEWPORT_ENABLE:
+            sb_set_info_vp(gwps->display->screen_type, 
+                           token->value.i|VP_INFO_LABEL);
+            break;
+        case SKIN_TOKEN_PEAKMETER:
+            data->peak_meter_enabled = true;
+            if (do_refresh)
+                draw_peakmeters(gwps, info->line_number, vp);
+            break;
+        case SKIN_TOKEN_VOLUMEBAR:
+        case SKIN_TOKEN_BATTERY_PERCENTBAR:
+        case SKIN_TOKEN_PROGRESSBAR:
+        {
+#ifdef HAVE_LCD_BITMAP
+            struct progressbar *bar = (struct progressbar*)token->value.data;
+            if (do_refresh)
+                draw_progressbar(gwps, info->line_number, bar);
+#else /* HAVE_LCD_CHARCELL */
+            if (do_refresh)
+            {
+                if (data->full_line_progressbar)
+                    draw_player_fullbar(gwps, info->buf, info->buf_size);
+                else
+                    draw_player_progress(gwps);
+            }
+#endif
+        }
+        break;
+        case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
+        {
+            char n = token->value.i & 0xFF;
+            int subimage = token->value.i >> 8;
+            struct gui_img *img = find_image(n, data);
+            if (img && img->loaded)
+                img->display = subimage;
+            break;
+        }
+#ifdef HAVE_ALBUMART
+        case SKIN_TOKEN_ALBUMART_DISPLAY:
+            /* now draw the AA */
+            if (do_refresh && data->albumart)
+            {
+                int handle = playback_current_aa_hid(data->playback_aa_slot);
+#if CONFIG_TUNER
+                if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
+                {
+                    struct dim dim = {data->albumart->width, data->albumart->height};
+                    handle = radio_get_art_hid(&dim);
+                }
+#endif
+                data->albumart->draw_handle = handle;
+            }
+            break;
+#endif
+        case SKIN_TOKEN_DRAW_INBUILTBAR:
+            gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
+                               info->refresh_type == SKIN_REFRESH_ALL,
+                               token->value.data);
+            break;
+        case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
+            if (do_refresh)
+                draw_playlist_viewer_list(gwps, token->value.data);
+            break;
+        
+#endif /* HAVE_LCD_BITMAP */
+        default:
+            return false;
+    }
+    return true;
+}
+                
+
+
+static void do_tags_in_hidden_conditional(struct skin_element* branch, 
+                                          struct gui_wps *gwps)
+{
+    struct wps_data *data = gwps->data;
+    /* Tags here are ones which need to be "turned off" or cleared 
+     * if they are in a conditional branch which isnt being used */
+    if (branch->type == LINE_ALTERNATOR)
+    {
+        int i;
+        for (i=0; i<branch->children_count; i++)
+        {
+            do_tags_in_hidden_conditional(branch->children[i], gwps);
+        }
+    }
+    else if (branch->type == LINE && branch->children_count)
+    {
+        struct skin_element *child = branch->children[0];
+        struct wps_token *token;
+        while (child)
+        {
+            if (child->type == CONDITIONAL)
+            {
+                int i;
+                for (i=0; i<child->children_count; i++)
+                {
+                    do_tags_in_hidden_conditional(child->children[i], gwps);
+                }
+                child = child->next;
+                continue;
+            }
+            else if (child->type != TAG || !child->data)
+            {
+                child = child->next;
+                continue;
+            }
+            token = (struct wps_token *)child->data;
+#ifdef HAVE_LCD_BITMAP
+            /* clear all pictures in the conditional and nested ones */
+            if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
+            {
+                struct gui_img *img = find_image(token->value.i&0xFF, data);
+                clear_image_pos(gwps, img);
+            }
+            else if (token->type == SKIN_TOKEN_PEAKMETER)
+            {
+                data->peak_meter_enabled = false;
+            }
+#endif
+#ifdef HAVE_ALBUMART
+            else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
+            {
+                draw_album_art(gwps,
+                        playback_current_aa_hid(data->playback_aa_slot), true);
+            }
+#endif
+            child = child->next;
+        }
+    }
+}
+    
+static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
+{
+    struct align_pos *align = &info->align;
+    char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
+    switch (element->tag->type)
+    {
+        case SKIN_TOKEN_ALIGN_LEFT:
+            *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
+            align->left = cur_pos;
+            info->cur_align_start = cur_pos;
+            break;
+        case SKIN_TOKEN_ALIGN_LEFT_RTL:
+            *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
+            if (lang_is_rtl())
+                align->right = cur_pos;
+            else
+                align->left = cur_pos;
+            info->cur_align_start = cur_pos;
+            break;
+        case SKIN_TOKEN_ALIGN_CENTER:
+            *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
+            align->center = cur_pos;
+            info->cur_align_start = cur_pos;
+            break;
+        case SKIN_TOKEN_ALIGN_RIGHT:
+            *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
+            align->right = cur_pos;
+            info->cur_align_start = cur_pos;
+            break;
+        case SKIN_TOKEN_ALIGN_RIGHT_RTL:
+            *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
+            if (lang_is_rtl())
+                align->left = cur_pos;
+            else
+                align->right = cur_pos;
+            info->cur_align_start = cur_pos;
+            break;
+        default:
+            break;
+    }
+}
+    
+/* Draw a LINE element onto the display */
+bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
+{
+    bool needs_update = false;
+    int last_value, value;
+    
+    if (line->children_count == 0)
+        return false; /* empty line, do nothing */
+        
+    struct skin_element *child = line->children[0];
+    struct conditional *conditional;
+    skin_render_func func = skin_render_line;
+    char tempbuf[128];
+    int old_refresh_mode = info->refresh_type;
+    while (child)
+    {
+        tempbuf[0] = '\0';
+        switch (child->type)
+        {
+            case CONDITIONAL:
+                conditional = (struct conditional*)child->data;
+                last_value = conditional->last_value;
+                value = evaluate_conditional(info->gwps, conditional, child->children_count);
+                
+                if (value != 1 && value >= child->children_count)
+                    value = child->children_count-1;
+                if (child->children_count == 1)
+                {
+                    /* special handling so 
+                     * %?aa<true> and %?<true|false> need special handlng here */
+                    
+                    if (value == 1) /* tag is false */
+                    {
+                        /* we are in a false branch of a %?aa<true> conditional */
+                        if (last_value == 0)
+                            do_tags_in_hidden_conditional(child->children[0], info->gwps);
+                        break;
+                    }
+                    value = 0;
+                }
+                else
+                {
+                    if (last_value >= 0 && value != last_value && last_value < child->children_count)
+                        do_tags_in_hidden_conditional(child->children[last_value], info->gwps);
+                }
+                if (child->children[value]->type == LINE_ALTERNATOR)
+                {
+                    func = skin_render_alternator;
+                }
+                else if (child->children[value]->type == LINE)
+                    func = skin_render_line;
+                
+                if (value != last_value)
+                {
+                    info->refresh_type = SKIN_REFRESH_ALL;
+                    info->force_redraw = true;
+                }
+                    
+                if (func(child->children[value], info))
+                    needs_update = true;
+                else
+                    needs_update = needs_update || (last_value != value);
+                    
+                info->refresh_type = old_refresh_mode;
+                break;
+            case TAG:
+                if (child->tag->flags & NOBREAK)
+                    info->no_line_break = true;
+                if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
+                    info->line_scrolls = true;
+                
+                fix_line_alignment(info, child);
+                
+                if (!child->data)
+                {
+                    break;
+                }
+                if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
+                {
+                    const char *value = get_token_value(info->gwps, child->data,
+                                              tempbuf, sizeof(tempbuf), NULL);
+                    if (value)
+                    {
+                        needs_update = needs_update || 
+                                ((child->tag->flags&info->refresh_type)!=0);
+                        strlcat(info->cur_align_start, value, 
+                                info->buf_size - (info->cur_align_start-info->buf));
+                    }
+                }
+                break;
+            case TEXT:
+                strlcat(info->cur_align_start, child->data, 
+                        info->buf_size - (info->cur_align_start-info->buf));
+                needs_update = needs_update || 
+                                (info->refresh_type&SKIN_REFRESH_STATIC) != 0;
+                break;
+            case COMMENT:
+            default:
+                break;
+        }
+
+        child = child->next;
+    }
+    return needs_update;
+}
+
+bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
+{
+    bool changed_lines = false;
+    struct line_alternator *alternator = (struct line_alternator*)element->data;
+    unsigned old_refresh = info->refresh_type;
+    if (info->refresh_type == SKIN_REFRESH_ALL)
+    {
+        alternator->current_line = 0;
+        alternator->last_change_tick = current_tick;
+        changed_lines = true;
+    }
+    else
+    {
+        struct skin_element *current_line = element->children[alternator->current_line];
+        struct line *line = (struct line *)current_line->data;
+        int next_change = alternator->last_change_tick + line->timeout;
+        if (TIME_AFTER(current_tick, next_change))
+        {
+            alternator->current_line++;
+            if (alternator->current_line >= element->children_count)
+                alternator->current_line = 0;
+            alternator->last_change_tick = current_tick;
+            changed_lines = true;
+        }
+    }
+    if (element->children[alternator->current_line]->children_count == 0)
+    {
+        /* skip empty sublines */
+        alternator->current_line++;
+        if (alternator->current_line >= element->children_count)
+            alternator->current_line = 0;
+        changed_lines = true;
+    }
+    
+    if (changed_lines)
+    {
+        info->refresh_type = SKIN_REFRESH_ALL;
+        info->force_redraw = true;
+    }
+    bool ret = skin_render_line(element->children[alternator->current_line], info);
+    info->refresh_type = old_refresh;
+    return changed_lines || ret;
+}
+
+void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
+                          struct skin_viewport* skin_viewport, unsigned long refresh_type)
+{
+    struct screen *display = gwps->display;
+    char linebuf[MAX_LINE];
+    skin_render_func func = skin_render_line;
+    struct skin_element* line = viewport;
+    struct skin_draw_info info = {
+        .gwps = gwps,
+        .buf = linebuf,
+        .buf_size = sizeof(linebuf),
+        .line_number = 0,
+        .no_line_break = false,
+        .line_scrolls = false,
+        .refresh_type = refresh_type,
+        .skin_vp = skin_viewport
+    };
+    
+    struct align_pos * align = &info.align;
+    bool needs_update;
+#ifdef HAVE_LCD_BITMAP
+    /* Set images to not to be displayed */
+    struct skin_token_list *imglist = gwps->data->images;
+    while (imglist)
+    {
+        struct gui_img *img = (struct gui_img *)imglist->token->value.data;
+        img->display = -1;
+        imglist = imglist->next;
+    }
+#endif
+    
+    while (line)
+    {
+        linebuf[0] = '\0';
+        info.no_line_break = false;
+        info.line_scrolls = false;
+        info.force_redraw = false;
+    
+        info.cur_align_start = info.buf;
+        align->left = info.buf;
+        align->center = NULL;
+        align->right = NULL;
+        
+        
+        if (line->type == LINE_ALTERNATOR)
+            func = skin_render_alternator;
+        else if (line->type == LINE)
+            func = skin_render_line;
+        
+        needs_update = func(line, &info);
+        
+        /* only update if the line needs to be, and there is something to write */
+        if (refresh_type && needs_update)
+        {
+            if (info.line_scrolls)
+            {
+                /* if the line is a scrolling one we don't want to update
+                   too often, so that it has the time to scroll */
+                if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
+                    write_line(display, align, info.line_number, true);
+            }
+            else
+                write_line(display, align, info.line_number, false);
+        }
+        if (!info.no_line_break)
+            info.line_number++;
+        line = line->next;
+    }
+    wps_display_images(gwps, &skin_viewport->vp);
+}
+
+void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
+{
+    struct wps_data *data = gwps->data;
+    struct screen *display = gwps->display;
+    
+    struct skin_element* viewport = data->tree;
+    struct skin_viewport* skin_viewport;
+    
+    int old_refresh_mode = refresh_mode;
+    
+#ifdef HAVE_LCD_CHARCELLS
+    int i;
+    for (i = 0; i < 8; i++)
+    {
+        if (data->wps_progress_pat[i] == 0)
+            data->wps_progress_pat[i] = display->get_locked_pattern();
+    }
+#endif
+    /* disable any viewports which are conditionally displayed.
+     * If we are only refreshing the peak meter then don't change the viewport 
+     * enabled flags as this will stop scrolling. viewports cant be 
+     * toggled in this refresh mode anyway (FS#10215)*/
+    if (refresh_mode != SKIN_REFRESH_PEAK_METER)
+    {
+        for (viewport = data->tree;
+             viewport;
+             viewport = viewport->next)
+        {
+            struct skin_viewport *skin_viewport = (struct skin_viewport*)viewport->data;
+            if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
+            {
+                continue;
+            }
+            if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
+            {
+                if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
+                    skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
+                else
+                    skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
+            }
+        }
+    }
+    
+    viewport = data->tree;
+    skin_viewport = (struct skin_viewport *)viewport->data;
+    if (skin_viewport->label == VP_DEFAULT_LABEL && viewport->next)
+        refresh_mode = 0;
+    
+    for (viewport = data->tree;
+         viewport;
+         viewport = viewport->next)
+    {
+        /* SETUP */
+        skin_viewport = (struct skin_viewport*)viewport->data;
+        unsigned vp_refresh_mode = refresh_mode;
+#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
+        skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
+        skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
+#endif
+        display->set_viewport(&skin_viewport->vp);
+        
+        /* dont redraw the viewport if its disabled */
+        if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
+        {   /* don't draw anything into this one */
+            vp_refresh_mode = 0;
+        }
+        else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
+        {
+            if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
+                display->scroll_stop(&skin_viewport->vp);
+            skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
+            continue;
+        }
+        else if (((skin_viewport->hidden_flags&
+                   (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
+                    == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
+        {
+            vp_refresh_mode = SKIN_REFRESH_ALL;
+            skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
+        }
+        
+        if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
+        {
+            display->clear_viewport();
+        }
+        
+        /* render */
+        skin_render_viewport(viewport->children[0], gwps,
+                             skin_viewport, vp_refresh_mode);
+        refresh_mode = old_refresh_mode;
+    }
+    
+    /* Restore the default viewport */
+    display->set_viewport(NULL);
+    display->update();
+}
diff --git a/apps/gui/skin_engine/skin_tokens.c b/apps/gui/skin_engine/skin_tokens.c
index b0a55ca..c5e1ebc 100644
--- a/apps/gui/skin_engine/skin_tokens.c
+++ b/apps/gui/skin_engine/skin_tokens.c
@@ -167,19 +167,19 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
         unsigned long elapsed = id3->elapsed + state->ff_rewind_count;
         switch (token->type)
         {
-            case WPS_TOKEN_METADATA_ARTIST:
+            case SKIN_TOKEN_METADATA_ARTIST:
                 return id3->artist;
-            case WPS_TOKEN_METADATA_COMPOSER:
+            case SKIN_TOKEN_METADATA_COMPOSER:
                 return id3->composer;
-            case WPS_TOKEN_METADATA_ALBUM:
+            case SKIN_TOKEN_METADATA_ALBUM:
                 return id3->album;
-            case WPS_TOKEN_METADATA_ALBUM_ARTIST:
+            case SKIN_TOKEN_METADATA_ALBUM_ARTIST:
                 return id3->albumartist;
-            case WPS_TOKEN_METADATA_GROUPING:
+            case SKIN_TOKEN_METADATA_GROUPING:
                 return id3->grouping;
-            case WPS_TOKEN_METADATA_GENRE:
+            case SKIN_TOKEN_METADATA_GENRE:
                 return id3->genre_string;
-            case WPS_TOKEN_METADATA_DISC_NUMBER:
+            case SKIN_TOKEN_METADATA_DISC_NUMBER:
                 if (id3->disc_string)
                     return id3->disc_string;
                 if (id3->discnum) {
@@ -187,7 +187,7 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                     return buf;
                 }
                 return NULL;
-            case WPS_TOKEN_METADATA_TRACK_NUMBER:
+            case SKIN_TOKEN_METADATA_TRACK_NUMBER:
                 if (id3->track_string)
                     return id3->track_string;
                 if (id3->tracknum) {
@@ -195,9 +195,9 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                     return buf;
                 }
                 return NULL;
-            case WPS_TOKEN_METADATA_TRACK_TITLE:
+            case SKIN_TOKEN_METADATA_TRACK_TITLE:
                 return id3->title;
-            case WPS_TOKEN_METADATA_VERSION:
+            case SKIN_TOKEN_METADATA_VERSION:
                 switch (id3->id3version)
                 {
                     case ID3_VER_1_0:
@@ -214,7 +214,7 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                         break;
                 }
                 return NULL;
-            case WPS_TOKEN_METADATA_YEAR:
+            case SKIN_TOKEN_METADATA_YEAR:
                 if( id3->year_string )
                     return id3->year_string;
                 if (id3->year) {
@@ -222,29 +222,29 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                     return buf;
                 }
                 return NULL;
-            case WPS_TOKEN_METADATA_COMMENT:
+            case SKIN_TOKEN_METADATA_COMMENT:
                 return id3->comment;
-            case WPS_TOKEN_FILE_PATH:
+            case SKIN_TOKEN_FILE_PATH:
                 return id3->path;
-            case WPS_TOKEN_FILE_BITRATE:
+            case SKIN_TOKEN_FILE_BITRATE:
                 if(id3->bitrate)
                     snprintf(buf, buf_size, "%d", id3->bitrate);
                 else
                     return "?";
                 return buf;
-            case WPS_TOKEN_TRACK_TIME_ELAPSED:
+            case SKIN_TOKEN_TRACK_TIME_ELAPSED:
                 format_time(buf, buf_size, elapsed);
                 return buf;
 
-            case WPS_TOKEN_TRACK_TIME_REMAINING:
+            case SKIN_TOKEN_TRACK_TIME_REMAINING:
                 format_time(buf, buf_size, length - elapsed);
                 return buf;
 
-            case WPS_TOKEN_TRACK_LENGTH:
+            case SKIN_TOKEN_TRACK_LENGTH:
                 format_time(buf, buf_size, length);
                 return buf;
 
-            case WPS_TOKEN_TRACK_ELAPSED_PERCENT:
+            case SKIN_TOKEN_TRACK_ELAPSED_PERCENT:
                 if (length <= 0)
                     return NULL;
 
@@ -255,14 +255,14 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                 snprintf(buf, buf_size, "%lu", 100 * elapsed / length);
                 return buf;
 
-            case WPS_TOKEN_TRACK_STARTING:
+            case SKIN_TOKEN_TRACK_STARTING:
                 {
                     unsigned long time = token->value.i * 1000;
                     if (elapsed < time)
                         return "starting";
                 }
                 return NULL;
-            case WPS_TOKEN_TRACK_ENDING:
+            case SKIN_TOKEN_TRACK_ENDING:
                 {
                     unsigned long time = token->value.i * 1000;
                     if (length - elapsed < time)
@@ -270,7 +270,7 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                 }
                 return NULL;
 
-            case WPS_TOKEN_FILE_CODEC:
+            case SKIN_TOKEN_FILE_CODEC:
                 if (intval)
                 {
                     if(id3->codectype == AFMT_UNKNOWN)
@@ -280,10 +280,10 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                 }
                 return get_codectype(id3);
 
-            case WPS_TOKEN_FILE_FREQUENCY:
+            case SKIN_TOKEN_FILE_FREQUENCY:
                 snprintf(buf, buf_size, "%ld", id3->frequency);
                 return buf;
-            case WPS_TOKEN_FILE_FREQUENCY_KHZ:
+            case SKIN_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);
@@ -292,7 +292,7 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                             id3->frequency / 1000,
                             (id3->frequency % 1000) / 100);
                 return buf;
-            case WPS_TOKEN_FILE_NAME:
+            case SKIN_TOKEN_FILE_NAME:
                 if (get_dir(buf, buf_size, id3->path, 0)) {
                     /* Remove extension */
                     char* sep = strrchr(buf, '.');
@@ -302,28 +302,28 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
                     return buf;
                 }
                 return NULL;
-            case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
+            case SKIN_TOKEN_FILE_NAME_WITH_EXTENSION:
                 return get_dir(buf, buf_size, id3->path, 0);
-            case WPS_TOKEN_FILE_SIZE:
+            case SKIN_TOKEN_FILE_SIZE:
                 snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
                 return buf;
-            case WPS_TOKEN_FILE_VBR:
+            case SKIN_TOKEN_FILE_VBR:
                 return (id3->vbr) ? "(avg)" : NULL;
-            case WPS_TOKEN_FILE_DIRECTORY:
+            case SKIN_TOKEN_FILE_DIRECTORY:
                 return get_dir(buf, buf_size, id3->path, token->value.i);
 
 #ifdef HAVE_TAGCACHE
-        case WPS_TOKEN_DATABASE_PLAYCOUNT:
+        case SKIN_TOKEN_DATABASE_PLAYCOUNT:
             if (intval)
                 *intval = id3->playcount + 1;
             snprintf(buf, buf_size, "%ld", id3->playcount);
             return buf;
-        case WPS_TOKEN_DATABASE_RATING:
+        case SKIN_TOKEN_DATABASE_RATING:
             if (intval)
                 *intval = id3->rating + 1;
             snprintf(buf, buf_size, "%d", id3->rating);
             return buf;
-        case WPS_TOKEN_DATABASE_AUTOSCORE:
+        case SKIN_TOKEN_DATABASE_AUTOSCORE:
             if (intval)
                 *intval = id3->score + 1;
             snprintf(buf, buf_size, "%d", id3->score);
@@ -340,13 +340,13 @@ const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
         {
             /* Most tokens expect NULL on error so leave that for the default case,
              * The ones that expect "0" need to be handled */
-            case WPS_TOKEN_FILE_FREQUENCY:
-            case WPS_TOKEN_FILE_FREQUENCY_KHZ:
-            case WPS_TOKEN_FILE_SIZE:
+            case SKIN_TOKEN_FILE_FREQUENCY:
+            case SKIN_TOKEN_FILE_FREQUENCY_KHZ:
+            case SKIN_TOKEN_FILE_SIZE:
 #ifdef HAVE_TAGCACHE
-            case WPS_TOKEN_DATABASE_PLAYCOUNT:
-            case WPS_TOKEN_DATABASE_RATING:
-            case WPS_TOKEN_DATABASE_AUTOSCORE:
+            case SKIN_TOKEN_DATABASE_PLAYCOUNT:
+            case SKIN_TOKEN_DATABASE_RATING:
+            case SKIN_TOKEN_DATABASE_AUTOSCORE:
 #endif
                 if (intval)
                     *intval = 0;
@@ -397,30 +397,30 @@ const char *get_radio_token(struct wps_token *token, int preset_offset,
     switch (token->type)
     {
         /* Radio/tuner tokens */
-        case WPS_TOKEN_TUNER_TUNED:
+        case SKIN_TOKEN_TUNER_TUNED:
             if (tuner_get(RADIO_TUNED))
                 return "t";
             return NULL;
-        case WPS_TOKEN_TUNER_SCANMODE:
+        case SKIN_TOKEN_TUNER_SCANMODE:
             if (radio_scan_mode())
                 return "s";
             return NULL;
-        case WPS_TOKEN_TUNER_STEREO:
+        case SKIN_TOKEN_TUNER_STEREO:
             if (radio_is_stereo())
                 return "s";
             return NULL;
-        case WPS_TOKEN_TUNER_MINFREQ: /* changes based on "region" */
+        case SKIN_TOKEN_TUNER_MINFREQ: /* changes based on "region" */
             return format_freq_MHz(region_data->freq_min,
                             region_data->freq_step, buf, buf_size);
-        case WPS_TOKEN_TUNER_MAXFREQ: /* changes based on "region" */
+        case SKIN_TOKEN_TUNER_MAXFREQ: /* changes based on "region" */
             return format_freq_MHz(region_data->freq_max,
                             region_data->freq_step, buf, buf_size);
-        case WPS_TOKEN_TUNER_CURFREQ:
+        case SKIN_TOKEN_TUNER_CURFREQ:
             return format_freq_MHz(radio_current_frequency(),
                             region_data->freq_step, buf, buf_size);
-        case WPS_TOKEN_PRESET_NAME:
-        case WPS_TOKEN_PRESET_FREQ:
-        case WPS_TOKEN_PRESET_ID:
+        case SKIN_TOKEN_PRESET_NAME:
+        case SKIN_TOKEN_PRESET_FREQ:
+        case SKIN_TOKEN_PRESET_ID:
         {
             int preset_count = radio_preset_count();
             int cur_preset = radio_current_preset();
@@ -431,30 +431,32 @@ const char *get_radio_token(struct wps_token *token, int preset_offset,
             preset %= preset_count;
             if (preset < 0)
                 preset += preset_count;
-            if (token->type == WPS_TOKEN_PRESET_NAME)
+            if (token->type == SKIN_TOKEN_PRESET_NAME)
                 snprintf(buf, buf_size, "%s", radio_get_preset(preset)->name);
-            else if (token->type == WPS_TOKEN_PRESET_FREQ)
+            else if (token->type == SKIN_TOKEN_PRESET_FREQ)
                 format_freq_MHz(radio_get_preset(preset)->frequency,
                                 region_data->freq_step, buf, buf_size);
             else
                 snprintf(buf, buf_size, "%d", preset + 1);
             return buf;
         }
-        case WPS_TOKEN_PRESET_COUNT:
+        case SKIN_TOKEN_PRESET_COUNT:
             snprintf(buf, buf_size, "%d", radio_preset_count());        
             if (intval)
                 *intval = radio_preset_count();
             return buf;
-        case WPS_TOKEN_HAVE_RDS:
+        case SKIN_TOKEN_HAVE_RDS:
 #ifdef HAVE_RDS_CAP
             return "rds";
-        case WPS_TOKEN_RDS_NAME:
+        case SKIN_TOKEN_RDS_NAME:
             return tuner_get_rds_info(RADIO_RDS_NAME);
-        case WPS_TOKEN_RDS_TEXT:
+        case SKIN_TOKEN_RDS_TEXT:
             return tuner_get_rds_info(RADIO_RDS_TEXT);
 #else
-            return NULL; /* end of the WPS_TOKEN_HAVE_RDS case */
+            return NULL; /* end of the SKIN_TOKEN_HAVE_RDS case */
 #endif /* HAVE_RDS_CAP */
+        default:
+            return NULL;
     }
     return NULL;
 }
@@ -497,8 +499,8 @@ const char *get_token_value(struct gui_wps *gwps,
 
     /* 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)
+    if (token->type >= SKIN_TOKENS_RTC_BEGIN
+        && token->type <= SKIN_TOKENS_RTC_END)
     {
         tm = get_time();
 
@@ -531,44 +533,44 @@ const char *get_token_value(struct gui_wps *gwps,
 
     switch (token->type)
     {
-        case WPS_TOKEN_CHARACTER:
+        case SKIN_TOKEN_CHARACTER:
             if (token->value.c == '\n')
                 return NULL;
             return &(token->value.c);
 
-        case WPS_TOKEN_STRING:
+        case SKIN_TOKEN_STRING:
             return (char*)token->value.data;
             
-        case WPS_TOKEN_TRANSLATEDSTRING:
+        case SKIN_TOKEN_TRANSLATEDSTRING:
             return (char*)P2STR(ID2P(token->value.i));
 
-        case WPS_TOKEN_PLAYLIST_ENTRIES:
+        case SKIN_TOKEN_PLAYLIST_ENTRIES:
             snprintf(buf, buf_size, "%d", playlist_amount());
             return buf;
         
-        case WPS_TOKEN_LIST_TITLE_TEXT:
+        case SKIN_TOKEN_LIST_TITLE_TEXT:
             return (char*)token->value.data;
-        case WPS_TOKEN_LIST_TITLE_ICON:
+        case SKIN_TOKEN_LIST_TITLE_ICON:
             if (intval)
                 *intval = token->value.i;
             snprintf(buf, buf_size, "%d", token->value.i);
             return buf;
 
-        case WPS_TOKEN_PLAYLIST_NAME:
+        case SKIN_TOKEN_PLAYLIST_NAME:
             return playlist_name(NULL, buf, buf_size);
 
-        case WPS_TOKEN_PLAYLIST_POSITION:
+        case SKIN_TOKEN_PLAYLIST_POSITION:
             snprintf(buf, buf_size, "%d", playlist_get_display_index());
             return buf;
 
-        case WPS_TOKEN_PLAYLIST_SHUFFLE:
+        case SKIN_TOKEN_PLAYLIST_SHUFFLE:
             if ( global_settings.playlist_shuffle )
                 return "s";
             else
                 return NULL;
             break;
 
-        case WPS_TOKEN_VOLUME:
+        case SKIN_TOKEN_VOLUME:
             snprintf(buf, buf_size, "%d", global_settings.volume);
             if (intval)
             {
@@ -593,7 +595,7 @@ const char *get_token_value(struct gui_wps *gwps,
             }
             return buf;
 #ifdef HAVE_ALBUMART
-        case WPS_TOKEN_ALBUMART_FOUND:
+        case SKIN_TOKEN_ALBUMART_FOUND:
             if (data->albumart)
             {
                 int handle = -1;
@@ -609,16 +611,9 @@ const char *get_token_value(struct gui_wps *gwps,
                     return "C";
             }
             return NULL;
-            
-        case WPS_TOKEN_ALBUMART_DISPLAY:
-            if (!data->albumart)
-                return NULL;
-            if (!data->albumart->draw)
-                data->albumart->draw = true;
-            return NULL;
 #endif
 
-        case WPS_TOKEN_BATTERY_PERCENT:
+        case SKIN_TOKEN_BATTERY_PERCENT:
         {
             int l = battery_level();
 
@@ -641,14 +636,14 @@ const char *get_token_value(struct gui_wps *gwps,
             }
         }
 
-        case WPS_TOKEN_BATTERY_VOLTS:
+        case SKIN_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:
+        case SKIN_TOKEN_BATTERY_TIME:
         {
             int t = battery_time();
             if (t >= 0)
@@ -659,7 +654,7 @@ const char *get_token_value(struct gui_wps *gwps,
         }
 
 #if CONFIG_CHARGING
-        case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
+        case SKIN_TOKEN_BATTERY_CHARGER_CONNECTED:
         {
             if(charger_input_state==CHARGER)
                 return "p";
@@ -668,7 +663,7 @@ const char *get_token_value(struct gui_wps *gwps,
         }
 #endif
 #if CONFIG_CHARGING >= CHARGING_MONITOR
-        case WPS_TOKEN_BATTERY_CHARGING:
+        case SKIN_TOKEN_BATTERY_CHARGING:
         {
             if (charge_state == CHARGING || charge_state == TOPOFF) {
                 return "c";
@@ -678,12 +673,12 @@ const char *get_token_value(struct gui_wps *gwps,
         }
 #endif
 #ifdef HAVE_USB_POWER
-        case WPS_TOKEN_USB_POWERED:
+        case SKIN_TOKEN_USB_POWERED:
             if (usb_powered())
                 return "u";
             return NULL;
 #endif
-        case WPS_TOKEN_BATTERY_SLEEPTIME:
+        case SKIN_TOKEN_BATTERY_SLEEPTIME:
         {
             if (get_sleep_timer() == 0)
                 return NULL;
@@ -694,7 +689,7 @@ const char *get_token_value(struct gui_wps *gwps,
             }
         }
 
-        case WPS_TOKEN_PLAYBACK_STATUS:
+        case SKIN_TOKEN_PLAYBACK_STATUS:
         {
             int status = current_playmode();
             /* music */
@@ -734,13 +729,13 @@ const char *get_token_value(struct gui_wps *gwps,
             return buf;
         }
 
-        case WPS_TOKEN_REPEAT_MODE:
+        case SKIN_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:
+        case SKIN_TOKEN_RTC_PRESENT:
 #if CONFIG_RTC
                 return "c";
 #else
@@ -748,41 +743,41 @@ const char *get_token_value(struct gui_wps *gwps,
 #endif
 
 #if CONFIG_RTC
-        case WPS_TOKEN_RTC_12HOUR_CFG:
+        case SKIN_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:
+        case SKIN_TOKEN_RTC_DAY_OF_MONTH:
             /* d: day of month (01..31) */
             snprintf(buf, buf_size, "%02d", tm->tm_mday);
             if (intval)
                 *intval = tm->tm_mday - 1;
             return buf;
 
-        case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
+        case SKIN_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
             /* e: day of month, blank padded ( 1..31) */
             snprintf(buf, buf_size, "%2d", tm->tm_mday);
             if (intval)
                 *intval = tm->tm_mday - 1;
             return buf;
 
-        case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
+        case SKIN_TOKEN_RTC_HOUR_24_ZERO_PADDED:
             /* H: hour (00..23) */
             snprintf(buf, buf_size, "%02d", tm->tm_hour);
             if (intval)
                 *intval = tm->tm_hour;
             return buf;
 
-        case WPS_TOKEN_RTC_HOUR_24:
+        case SKIN_TOKEN_RTC_HOUR_24:
             /* k: hour ( 0..23) */
             snprintf(buf, buf_size, "%2d", tm->tm_hour);
             if (intval)
                 *intval = tm->tm_hour;
             return buf;
 
-        case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
+        case SKIN_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);
@@ -790,7 +785,7 @@ const char *get_token_value(struct gui_wps *gwps,
                 *intval = (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12;
             return buf;
 
-        case WPS_TOKEN_RTC_HOUR_12:
+        case SKIN_TOKEN_RTC_HOUR_12:
             /* l: hour ( 1..12) */
             snprintf(buf, buf_size, "%2d",
                      (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
@@ -798,107 +793,107 @@ const char *get_token_value(struct gui_wps *gwps,
                 *intval = (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12;
             return buf;
 
-        case WPS_TOKEN_RTC_MONTH:
+        case SKIN_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:
+        case SKIN_TOKEN_RTC_MINUTE:
             /* M: minute (00..59) */
             snprintf(buf, buf_size, "%02d", tm->tm_min);
             if (intval)
                 *intval = tm->tm_min;
             return buf;
 
-        case WPS_TOKEN_RTC_SECOND:
+        case SKIN_TOKEN_RTC_SECOND:
             /* S: second (00..59) */
             snprintf(buf, buf_size, "%02d", tm->tm_sec);
             if (intval)
                 *intval = tm->tm_sec;
             return buf;
 
-        case WPS_TOKEN_RTC_YEAR_2_DIGITS:
+        case SKIN_TOKEN_RTC_YEAR_2_DIGITS:
             /* y: last two digits of year (00..99) */
             snprintf(buf, buf_size, "%02d", tm->tm_year % 100);
             if (intval)
                 *intval = tm->tm_year % 100;
             return buf;
 
-        case WPS_TOKEN_RTC_YEAR_4_DIGITS:
+        case SKIN_TOKEN_RTC_YEAR_4_DIGITS:
             /* Y: year (1970...) */
             snprintf(buf, buf_size, "%04d", tm->tm_year + 1900);
             if (intval)
                 *intval = tm->tm_year + 1900;
             return buf;
 
-        case WPS_TOKEN_RTC_AM_PM_UPPER:
+        case SKIN_TOKEN_RTC_AM_PM_UPPER:
             /* p: upper case AM or PM indicator */
             if (intval)
                 *intval = tm->tm_hour/12 == 0 ? 0 : 1;
             return tm->tm_hour/12 == 0 ? "AM" : "PM";
 
-        case WPS_TOKEN_RTC_AM_PM_LOWER:
+        case SKIN_TOKEN_RTC_AM_PM_LOWER:
             /* P: lower case am or pm indicator */
             if (intval)
                 *intval = tm->tm_hour/12 == 0 ? 0 : 1;
             return tm->tm_hour/12 == 0 ? "am" : "pm";
 
-        case WPS_TOKEN_RTC_WEEKDAY_NAME:
+        case SKIN_TOKEN_RTC_WEEKDAY_NAME:
             /* a: abbreviated weekday name (Sun..Sat) */
             return str(LANG_WEEKDAY_SUNDAY + tm->tm_wday);
 
-        case WPS_TOKEN_RTC_MONTH_NAME:
+        case SKIN_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:
+        case SKIN_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:
+        case SKIN_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:
+        case SKIN_TOKEN_RTC_DAY_OF_MONTH:
+        case SKIN_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
+        case SKIN_TOKEN_RTC_HOUR_24_ZERO_PADDED:
+        case SKIN_TOKEN_RTC_HOUR_24:
+        case SKIN_TOKEN_RTC_HOUR_12_ZERO_PADDED:
+        case SKIN_TOKEN_RTC_HOUR_12:
+        case SKIN_TOKEN_RTC_MONTH:
+        case SKIN_TOKEN_RTC_MINUTE:
+        case SKIN_TOKEN_RTC_SECOND:
+        case SKIN_TOKEN_RTC_AM_PM_UPPER:
+        case SKIN_TOKEN_RTC_AM_PM_LOWER:
+        case SKIN_TOKEN_RTC_YEAR_2_DIGITS:
             return "--";
-        case WPS_TOKEN_RTC_YEAR_4_DIGITS:
+        case SKIN_TOKEN_RTC_YEAR_4_DIGITS:
             return "----";
-        case WPS_TOKEN_RTC_WEEKDAY_NAME:
-        case WPS_TOKEN_RTC_MONTH_NAME:
+        case SKIN_TOKEN_RTC_WEEKDAY_NAME:
+        case SKIN_TOKEN_RTC_MONTH_NAME:
             return "---";
-        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
-        case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
+        case SKIN_TOKEN_RTC_DAY_OF_WEEK_START_MON:
+        case SKIN_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
             return "-";
 #endif
 
 #ifdef HAVE_LCD_CHARCELLS
-        case WPS_TOKEN_PROGRESSBAR:
+        case SKIN_TOKEN_PROGRESSBAR:
         {
             char *end = utf8encode(data->wps_progress_pat[0], buf);
             *end = '\0';
             return buf;
         }
 
-        case WPS_TOKEN_PLAYER_PROGRESSBAR:
+        case SKIN_TOKEN_PLAYER_PROGRESSBAR:
             if(is_new_player())
             {
                 /* we need 11 characters (full line) for
@@ -916,7 +911,7 @@ const char *get_token_value(struct gui_wps *gwps,
 
 
 #if (CONFIG_CODEC == SWCODEC)
-        case WPS_TOKEN_CROSSFADE:
+        case SKIN_TOKEN_CROSSFADE:
 #ifdef HAVE_CROSSFADE
             if (intval)
                 *intval = global_settings.crossfade + 1;
@@ -926,7 +921,7 @@ const char *get_token_value(struct gui_wps *gwps,
 #endif
             return buf;
 
-        case WPS_TOKEN_REPLAYGAIN:
+        case SKIN_TOKEN_REPLAYGAIN:
         {
             int val;
 
@@ -974,7 +969,7 @@ const char *get_token_value(struct gui_wps *gwps,
 #endif  /* (CONFIG_CODEC == SWCODEC) */
 
 #if (CONFIG_CODEC != MAS3507D)
-        case WPS_TOKEN_SOUND_PITCH:
+        case SKIN_TOKEN_SOUND_PITCH:
         {
             int32_t pitch = sound_get_pitch();
             snprintf(buf, buf_size, "%ld.%ld",
@@ -989,7 +984,7 @@ const char *get_token_value(struct gui_wps *gwps,
 #endif
 
 #if CONFIG_CODEC == SWCODEC
-    case WPS_TOKEN_SOUND_SPEED:
+    case SKIN_TOKEN_SOUND_SPEED:
     {
         int32_t pitch = sound_get_pitch();
         int32_t speed;
@@ -1007,7 +1002,7 @@ const char *get_token_value(struct gui_wps *gwps,
     }
 #endif
 
-        case WPS_TOKEN_MAIN_HOLD:
+        case SKIN_TOKEN_MAIN_HOLD:
 #ifdef HAS_BUTTON_HOLD
             if (button_hold())
 #else
@@ -1018,7 +1013,7 @@ const char *get_token_value(struct gui_wps *gwps,
                 return NULL;
 
 #ifdef HAS_REMOTE_BUTTON_HOLD
-        case WPS_TOKEN_REMOTE_HOLD:
+        case SKIN_TOKEN_REMOTE_HOLD:
             if (remote_button_hold())
                 return "r";
             else
@@ -1026,20 +1021,20 @@ const char *get_token_value(struct gui_wps *gwps,
 #endif
 
 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
-        case WPS_TOKEN_VLED_HDD:
+        case SKIN_TOKEN_VLED_HDD:
             if(led_read(HZ/2))
                 return "h";
             else
                 return NULL;
 #endif
-        case WPS_TOKEN_BUTTON_VOLUME:
+        case SKIN_TOKEN_BUTTON_VOLUME:
             if (global_status.last_volume_change &&
                 TIME_BEFORE(current_tick, global_status.last_volume_change +
                                           token->value.i * TIMEOUT_UNIT))
                 return "v";
             return NULL;
             
-        case WPS_TOKEN_LASTTOUCH:
+        case SKIN_TOKEN_LASTTOUCH:
             {
 #ifdef HAVE_TOUCHSCREEN
             unsigned int last_touch = touchscreen_last_touch();
@@ -1051,7 +1046,7 @@ const char *get_token_value(struct gui_wps *gwps,
             }
             return NULL;
 
-        case WPS_TOKEN_SETTING:
+        case SKIN_TOKEN_SETTING:
         {
             const struct settings_list *s = settings+token->value.i;
             if (intval)
@@ -1120,14 +1115,14 @@ const char *get_token_value(struct gui_wps *gwps,
             cfg_to_string(token->value.i,buf,buf_size);
             return buf;
         }
-        case WPS_TOKEN_HAVE_TUNER:
+        case SKIN_TOKEN_HAVE_TUNER:
 #if CONFIG_TUNER
             if (radio_hardware_present())
                 return "r";
 #endif
             return NULL;
         /* Recording tokens */
-        case WPS_TOKEN_HAVE_RECORDING:
+        case SKIN_TOKEN_HAVE_RECORDING:
 #ifdef HAVE_RECORDING
             return "r";
 #else
@@ -1135,11 +1130,11 @@ const char *get_token_value(struct gui_wps *gwps,
 #endif
 
 #ifdef HAVE_RECORDING
-        case WPS_TOKEN_IS_RECORDING:
+        case SKIN_TOKEN_IS_RECORDING:
             if (audio_status() == AUDIO_STATUS_RECORD)
                 return "r";
             return NULL;
-        case WPS_TOKEN_REC_FREQ: /* order from REC_FREQ_CFG_VAL_LIST */
+        case SKIN_TOKEN_REC_FREQ: /* order from REC_FREQ_CFG_VAL_LIST */
         {
 #if CONFIG_CODEC == SWCODEC
             unsigned long samprk;
@@ -1227,7 +1222,7 @@ const char *get_token_value(struct gui_wps *gwps,
             return buf;
         }
 #if CONFIG_CODEC == SWCODEC
-        case WPS_TOKEN_REC_ENCODER:
+        case SKIN_TOKEN_REC_ENCODER:
         {
             int rec_format = global_settings.rec_format+1; /* WAV, AIFF, WV, MPEG */
             if (intval)
@@ -1248,7 +1243,7 @@ const char *get_token_value(struct gui_wps *gwps,
             break;
         }
 #endif
-        case WPS_TOKEN_REC_BITRATE:
+        case SKIN_TOKEN_REC_BITRATE:
 #if CONFIG_CODEC == SWCODEC
             if (global_settings.rec_format == REC_FORMAT_MPA_L3)
             {
@@ -1317,12 +1312,12 @@ const char *get_token_value(struct gui_wps *gwps,
             snprintf(buf, buf_size, "%d", global_settings.rec_quality);
             return buf;
 #endif
-        case WPS_TOKEN_REC_MONO:
+        case SKIN_TOKEN_REC_MONO:
             if (!global_settings.rec_channels)
                 return "m";
             return NULL;
             
-        case WPS_TOKEN_REC_SECONDS:
+        case SKIN_TOKEN_REC_SECONDS:
         {
             int time = (audio_recorded_time() / HZ) % 60;
             if (intval)
@@ -1330,7 +1325,7 @@ const char *get_token_value(struct gui_wps *gwps,
             snprintf(buf, buf_size, "%02d", time);
             return buf;
         }
-        case WPS_TOKEN_REC_MINUTES:
+        case SKIN_TOKEN_REC_MINUTES:
         {
             int time = (audio_recorded_time() / HZ) / 60;
             if (intval)
@@ -1338,7 +1333,7 @@ const char *get_token_value(struct gui_wps *gwps,
             snprintf(buf, buf_size, "%02d", time);
             return buf;
         }
-        case WPS_TOKEN_REC_HOURS:
+        case SKIN_TOKEN_REC_HOURS:
         {
             int time = (audio_recorded_time() / HZ) / 3600;
             if (intval)
@@ -1349,7 +1344,7 @@ const char *get_token_value(struct gui_wps *gwps,
 
 #endif /* HAVE_RECORDING */
 
-        case WPS_TOKEN_CURRENT_SCREEN:
+        case SKIN_TOKEN_CURRENT_SCREEN:
         {
             int curr_screen = current_screen();
 
@@ -1390,7 +1385,7 @@ const char *get_token_value(struct gui_wps *gwps,
             return buf;
         }
 
-        case WPS_TOKEN_LANG_IS_RTL:
+        case SKIN_TOKEN_LANG_IS_RTL:
             return lang_is_rtl() ? "r" : NULL;
 
         default:
diff --git a/apps/gui/skin_engine/skin_tokens.h b/apps/gui/skin_engine/skin_tokens.h
index 4731131..d259fe4 100644
--- a/apps/gui/skin_engine/skin_tokens.h
+++ b/apps/gui/skin_engine/skin_tokens.h
@@ -23,243 +23,11 @@
 #define _SKIN_TOKENS_H_
 
 #include <stdbool.h>
-
-
-enum wps_token_type {
-    
-  TOKEN_MARKER_CONTROL_TOKENS = -1,
-    WPS_NO_TOKEN = 0,   /* for WPS tags we don't want to save as tokens */
-    WPS_TOKEN_UNKNOWN,
-
-    /* Markers */
-    WPS_TOKEN_CHARACTER,
-    WPS_TOKEN_STRING,
-    WPS_TOKEN_TRANSLATEDSTRING,
-
-    /* Alignment */
-    WPS_TOKEN_ALIGN_LEFT,
-    WPS_TOKEN_ALIGN_LEFT_RTL,
-    WPS_TOKEN_ALIGN_CENTER,
-    WPS_TOKEN_ALIGN_RIGHT,
-    WPS_TOKEN_ALIGN_RIGHT_RTL,
-
-    /* Sublines */
-    WPS_TOKEN_SUBLINE_TIMEOUT,
-    
-    /* Conditional */
-    WPS_TOKEN_CONDITIONAL,
-    WPS_TOKEN_CONDITIONAL_START,
-    WPS_TOKEN_CONDITIONAL_OPTION,
-    WPS_TOKEN_CONDITIONAL_END,
-
-    /* Viewport display */
-    WPS_VIEWPORT_ENABLE,
-    WPS_VIEWPORT_CUSTOMLIST,
-    WPS_TOKEN_UIVIEWPORT_ENABLE,
-    WPS_TOKEN_VIEWPORT_FGCOLOUR,
-    WPS_TOKEN_VIEWPORT_BGCOLOUR,
-    
-    /* Battery */
-  TOKEN_MARKER_BATTERY,
-    WPS_TOKEN_BATTERY_PERCENT,
-    WPS_TOKEN_BATTERY_PERCENTBAR,
-    WPS_TOKEN_BATTERY_VOLTS,
-    WPS_TOKEN_BATTERY_TIME,
-    WPS_TOKEN_BATTERY_CHARGER_CONNECTED,
-    WPS_TOKEN_BATTERY_CHARGING,
-    WPS_TOKEN_BATTERY_SLEEPTIME,
-    WPS_TOKEN_USB_POWERED,
-
-    /* Sound */
-  TOKEN_MARKER_SOUND,
-#if (CONFIG_CODEC != MAS3507D)
-    WPS_TOKEN_SOUND_PITCH,
-#endif
-#if (CONFIG_CODEC == SWCODEC)
-    WPS_TOKEN_SOUND_SPEED,
-    WPS_TOKEN_REPLAYGAIN,
-    WPS_TOKEN_CROSSFADE,
-#endif
-
-    /* Time */
-  TOKEN_MARKER_RTC,
-    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 */
-
-    /* Database */
-  TOKEN_MARKER_DATABASE,
-#ifdef HAVE_TAGCACHE
-    WPS_TOKEN_DATABASE_PLAYCOUNT,
-    WPS_TOKEN_DATABASE_RATING,
-    WPS_TOKEN_DATABASE_AUTOSCORE,
-#endif
-
-    /* File */
-  TOKEN_MARKER_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,
-
-    /* Image */
-  TOKEN_MARKER_IMAGES,
-#ifdef HAVE_LCD_BITMAP
-    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 */
-  TOKEN_MARKER_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,
-
-  TOKEN_MARKER_PLAYBACK_INFO,
-    /* Mode */
-    WPS_TOKEN_REPEAT_MODE,
-    WPS_TOKEN_PLAYBACK_STATUS,
-    /* Progressbar */
-    WPS_TOKEN_PROGRESSBAR,
-#ifdef HAVE_LCD_CHARCELLS
-    WPS_TOKEN_PLAYER_PROGRESSBAR,
-#endif
-#ifdef HAVE_LCD_BITMAP
-    /* Peakmeter */
-    WPS_TOKEN_PEAKMETER,
-#endif
-
-    /* Current track */
-    WPS_TOKEN_TRACK_ELAPSED_PERCENT,
-    WPS_TOKEN_TRACK_TIME_ELAPSED,
-    WPS_TOKEN_TRACK_TIME_REMAINING,
-    WPS_TOKEN_TRACK_LENGTH,
-    WPS_TOKEN_TRACK_STARTING,
-    WPS_TOKEN_TRACK_ENDING,
-
-    /* Playlist */
-  TOKEN_MARKER_PLAYLIST,
-    WPS_TOKEN_PLAYLIST_ENTRIES,
-    WPS_TOKEN_PLAYLIST_NAME,
-    WPS_TOKEN_PLAYLIST_POSITION,
-    WPS_TOKEN_PLAYLIST_SHUFFLE,
-
-
-    /* buttons */
-  TOKEN_MARKER_MISC,
-    WPS_TOKEN_DRAW_INBUILTBAR,
-    WPS_TOKEN_LIST_TITLE_TEXT,
-    WPS_TOKEN_LIST_TITLE_ICON,
-    WPS_TOKEN_BUTTON_VOLUME,
-    WPS_TOKEN_LASTTOUCH,
-#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
-    /* Virtual LED */
-    WPS_TOKEN_VLED_HDD,
-#endif
-    /* Volume level */
-    WPS_TOKEN_VOLUME,
-    WPS_TOKEN_VOLUMEBAR,
-    /* hold */
-    WPS_TOKEN_MAIN_HOLD,
-#ifdef HAS_REMOTE_BUTTON_HOLD
-    WPS_TOKEN_REMOTE_HOLD,
-#endif
-
-    /* Setting option */
-    WPS_TOKEN_SETTING,
-    WPS_TOKEN_CURRENT_SCREEN,
-    WPS_TOKEN_LANG_IS_RTL,
-    
-    /* Recording Tokens */
-  TOKEN_MARKER_RECORDING,
-    WPS_TOKEN_HAVE_RECORDING,
-    WPS_TOKEN_IS_RECORDING,
-    WPS_TOKEN_REC_FREQ,
-    WPS_TOKEN_REC_ENCODER,
-    WPS_TOKEN_REC_BITRATE, /* SWCODEC: MP3 bitrate, HWCODEC: MP3 "quality" */
-    WPS_TOKEN_REC_MONO,
-    WPS_TOKEN_REC_SECONDS,
-    WPS_TOKEN_REC_MINUTES,
-    WPS_TOKEN_REC_HOURS,
-    
-    
-    /* Radio Tokens */
-  TOKEN_MARKER_TUNER,
-    WPS_TOKEN_HAVE_TUNER,
-#if CONFIG_TUNER
-    WPS_TOKEN_TUNER_TUNED,
-    WPS_TOKEN_TUNER_SCANMODE,
-    WPS_TOKEN_TUNER_STEREO,
-    WPS_TOKEN_TUNER_MINFREQ, /* changes based on "region" */
-    WPS_TOKEN_TUNER_MAXFREQ, /* changes based on "region" */
-    WPS_TOKEN_TUNER_CURFREQ,
-    WPS_TOKEN_PRESET_ID, /* "id" of this preset.. really the array element number */
-    WPS_TOKEN_PRESET_NAME,
-    WPS_TOKEN_PRESET_FREQ,
-    WPS_TOKEN_PRESET_COUNT,
-    /* RDS tokens */
-    WPS_TOKEN_HAVE_RDS,
-#ifdef HAVE_RDS_CAP
-    WPS_TOKEN_RDS_NAME,
-    WPS_TOKEN_RDS_TEXT,
-#endif
-#endif /* CONFIG_TUNER */
-    
-    
-  TOKEN_MARKER_END, /* this needs to be the last value in this enum */
-};
+#include "tag_table.h"
 
 struct wps_token {
-    unsigned char type; /* enough to store the token type */
-
+    enum skin_token_type 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;
diff --git a/apps/gui/skin_engine/wps_debug.c b/apps/gui/skin_engine/wps_debug.c
index 59e8542..2ab7e5a 100644
--- a/apps/gui/skin_engine/wps_debug.c
+++ b/apps/gui/skin_engine/wps_debug.c
@@ -26,7 +26,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#include "skin_buffer.h"
 #include "settings_list.h"
 #ifdef __PCTOOL__
 #ifdef WPSEDITOR
diff --git a/apps/gui/skin_engine/wps_internals.h b/apps/gui/skin_engine/wps_internals.h
index f3f8337..ec37560 100644
--- a/apps/gui/skin_engine/wps_internals.h
+++ b/apps/gui/skin_engine/wps_internals.h
@@ -24,6 +24,8 @@
  
 #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.
@@ -32,6 +34,8 @@
 #define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* In TIMEOUT_UNIT's */
 
 #include "skin_tokens.h"
+#include "tag_table.h"
+#include "skin_parser.h"
 
 
 /* TODO: sort this mess out */
@@ -40,19 +44,6 @@
 #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
@@ -82,16 +73,16 @@ struct gui_img {
     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 */
     struct bitmap bm;
     char label;
     bool loaded;            /* load state */
     bool always_display;    /* not using the preload/display mechanism */
+    int display;
 };
 
 
 struct progressbar {
-    enum wps_token_type type;
+    enum skin_token_type type;
     struct viewport *vp;
     /* regular pb */
     short x;
@@ -105,8 +96,6 @@ struct progressbar {
     /*progressbar image*/
     struct bitmap bm;
     bool have_bitmap_pb;
-    
-    bool draw;
 };
 #endif
 
@@ -157,45 +146,6 @@ enum wps_parse_error {
     PARSE_FAIL_LIMITS_EXCEEDED,
 };
 
-
-/* Description of a subline on the WPS */
-struct skin_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;
-    unsigned short last_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;
-    
-    /* pointer to the next subline in this line */
-    struct skin_subline *next;
-};
-
-/* 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 skin_line {
-
-    /* Linked list of all the sublines on this line,
-     * a line *must* have at least one subline so no need to add an extra pointer */
-    struct skin_subline sublines;
-    /* pointer to the current subline */
-    struct skin_subline *curr_subline;
-
-    /* When the next subline of this line should be displayed
-       (absolute time value in ticks) */
-    long subline_expire_time;
-    
-    /* pointer to the next line */
-    struct skin_line *next;
-};
-
 #define VP_DRAW_HIDEABLE    0x1
 #define VP_DRAW_HIDDEN      0x2
 #define VP_DRAW_WASHIDDEN   0x4
@@ -206,7 +156,6 @@ struct skin_line {
 #define VP_INFO_LABEL       0x80
 struct skin_viewport {
     struct viewport vp;   /* The LCD viewport struct */
-    struct skin_line *lines;
     char hidden_flags;
     char label;
     unsigned start_fgcolour;
@@ -236,9 +185,6 @@ struct touchregion {
 };
 #endif
 
-#define MAX_PLAYLISTLINE_TOKENS 16
-#define MAX_PLAYLISTLINE_STRINGS    8
-#define MAX_PLAYLISTLINE_STRLEN     8
 enum info_line_type {
     TRACK_HAS_INFO = 0,
     TRACK_HAS_NO_INFO
@@ -250,36 +196,50 @@ struct playlistviewer {
 #ifdef HAVE_TC_RAMCACHE
     struct mp3entry tempid3;
 #endif
-    struct {
-        enum wps_token_type tokens[MAX_PLAYLISTLINE_TOKENS];
-        char strings[MAX_PLAYLISTLINE_STRINGS][MAX_PLAYLISTLINE_STRLEN];
-        int count;
-        bool scroll;
-    } lines[2];
+    struct skin_element *lines[2];
 };
 
 
 #ifdef HAVE_ALBUMART
 struct skin_albumart {
     /* Album art support */
-    struct viewport *vp;/* The viewport this is in */
     int x;
     int y;
     int width;
     int height;
 
-    bool draw;
     unsigned char xalign; /* WPS_ALBUMART_ALIGN_LEFT, _CENTER, _RIGHT */
     unsigned char yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */
     unsigned char state; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */
+    
+    struct viewport *vp;
+    int draw_handle;
 };
 #endif
 
+
+struct line {
+    int timeout; /* if inside a line alternator */
+    unsigned update_mode;
+};
+
+struct line_alternator {
+    int current_line;
+    unsigned long last_change_tick;
+};
+
+struct conditional {
+    int last_value;
+    struct wps_token *token;
+};
+
+
 /* wps_data
    this struct holds all necessary data which describes the
    viewable content of a wps */
 struct wps_data
 {
+    struct skin_element *tree;
 #ifdef HAVE_LCD_BITMAP
     struct skin_token_list *images;
     struct skin_token_list *progressbars;
@@ -291,16 +251,10 @@ struct wps_data
 #ifdef HAVE_TOUCHSCREEN
     struct skin_token_list *touchregions;
 #endif
-    struct skin_token_list *viewports;
-    struct skin_token_list *strings;
 #ifdef HAVE_ALBUMART
     struct skin_albumart *albumart;
     int    playback_aa_slot;
 #endif
-    struct wps_token *tokens;
-    /* Total number of tokens in the WPS. During WPS parsing, this is
-       the index of the token being parsed. */
-    int num_tokens;
 
 #ifdef HAVE_LCD_BITMAP
     bool peak_meter_enabled;
diff --git a/apps/gui/statusbar-skinned.c b/apps/gui/statusbar-skinned.c
index 168b17f..0aadddc 100644
--- a/apps/gui/statusbar-skinned.c
+++ b/apps/gui/statusbar-skinned.c
@@ -27,6 +27,8 @@
 #include "appevents.h"
 #include "screens.h"
 #include "screen_access.h"
+#include "skin_parser.h"
+#include "skin_buffer.h"
 #include "skin_engine/skin_engine.h"
 #include "skin_engine/wps_internals.h"
 #include "viewport.h"
@@ -46,26 +48,45 @@ static struct wps_sync_data sb_skin_sync_data        = { .do_full_update = false
 /* initial setup of wps_data  */
 static int update_delay = DEFAULT_UPDATE_DELAY;
 
-bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type screen)
+static int set_title_worker(char* title, enum themable_icons icon, 
+                            struct wps_data *data, struct skin_element *root)
 {
-    int i;
-    bool retval = false;
-    for(i=0; i<sb_skin_data[screen].num_tokens; i++)
+    int retval = 0;
+    struct skin_element *element = root;
+    while (element)
     {
-        if (sb_skin_data[screen].tokens[i].type == WPS_TOKEN_LIST_TITLE_TEXT)
+        if (element->children_count)
         {
-            sb_skin_data[screen].tokens[i].value.data = title;
-            retval = true;
+            int i;
+            for (i=0; i<element->children_count; i++)
+                retval |= set_title_worker(title, icon, data, element->children[i]);
         }
-        else if (sb_skin_data[screen].tokens[i].type == WPS_TOKEN_LIST_TITLE_ICON)
+        if (element->type == TAG)
         {
-            /* Icon_NOICON == -1 which the skin engine wants at position 1, so + 2 */
-            sb_skin_data[screen].tokens[i].value.i = icon+2;
+            struct wps_token *token = (struct wps_token *)element->data;
+            if (token->type == SKIN_TOKEN_LIST_TITLE_TEXT)
+            {
+                token->value.data = title;
+                retval = 1;
+            }
+            else if (token->type == SKIN_TOKEN_LIST_TITLE_ICON)
+            {
+                /* Icon_NOICON == -1 which the skin engine wants at position 1, so + 2 */
+                token->value.i = icon+2;
+            }
         }
+        element = element->next;
     }
     return retval;
 }
     
+bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type screen)
+{
+    bool retval = set_title_worker(title, icon, &sb_skin_data[screen], 
+                                   sb_skin_data[screen].tree) > 0;
+    return retval;
+}
+    
 
 void sb_skin_data_load(enum screen_type screen, const char *buf, bool isfile)
 {
@@ -75,18 +96,22 @@ void sb_skin_data_load(enum screen_type screen, const char *buf, bool isfile)
     success = buf && skin_data_load(screen, data, buf, isfile);
 
     if (success)
-    {  /* hide the sb's default viewport because it has nasty effect with stuff
+    {  
+        /* hide the sb's default viewport because it has nasty effect with stuff
         * not part of the statusbar,
         * hence .sbs's without any other vps are unsupported*/
         struct skin_viewport *vp = find_viewport(VP_DEFAULT_LABEL, data);
-        struct skin_token_list *next_vp = data->viewports->next;
-
-        if (!next_vp)
-        {    /* no second viewport, let parsing fail */
-            success = false;
+        struct skin_element *next_vp = data->tree->next;
+        
+        if (vp)
+        {
+            if (!next_vp)
+            {    /* no second viewport, let parsing fail */
+                success = false;
+            }
+            /* hide this viewport, forever */
+            vp->hidden_flags = VP_NEVER_VISIBLE;
         }
-        /* hide this viewport, forever */
-        vp->hidden_flags = VP_NEVER_VISIBLE;
         sb_set_info_vp(screen, VP_INFO_LABEL|VP_DEFAULT_LABEL);
     }
 
@@ -135,7 +160,7 @@ bool sb_set_backdrop(enum screen_type screen, char* filename)
         else
 #endif
             buf_size = LCD_BACKDROP_BYTES;
-        sb_skin[screen].data->backdrop = skin_buffer_alloc(buf_size);
+        sb_skin[screen].data->backdrop = (char*)skin_buffer_alloc(buf_size);
         if (!sb_skin[screen].data->backdrop)
             return false;          
     }
@@ -150,6 +175,8 @@ void sb_skin_update(enum screen_type screen, bool force)
 {
     static long next_update[NB_SCREENS] = {0};
     int i = screen;
+    if (!sb_skin_data[screen].wps_loaded)
+        return;
     if (TIME_AFTER(current_tick, next_update[i]) || force)
     {
 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
@@ -158,7 +185,7 @@ void sb_skin_update(enum screen_type screen, bool force)
         if (lcd_active() || (i != SCREEN_MAIN))
 #endif
             skin_update(&sb_skin[i], force?
-                    WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC);
+                    SKIN_REFRESH_ALL : SKIN_REFRESH_NON_STATIC);
         next_update[i] = current_tick + update_delay; /* don't update too often */
         sb_skin[SCREEN_MAIN].sync_data->do_full_update = false;
     }
@@ -216,7 +243,30 @@ void sb_create_from_settings(enum screen_type screen)
     
     if (ptr2[0] && ptr2[0] != '-') /* from ui viewport setting */
     {
+        char *comma = ptr;
+        int param_count = 0;
         len = snprintf(ptr, remaining, "%%ax%%Vi(-,%s)\n", ptr2);
+        /* The config put the colours at the end of the viewport,
+         * they need to be stripped for the skin code though */
+        do {
+            param_count++;
+            comma = strchr(comma+1, ',');
+            
+        } while (comma && param_count < 6);
+        if (comma)
+        {
+            char *end = comma;
+            char fg[8], bg[8];
+            int i = 0;
+            comma++;
+            while (*comma != ',')
+                fg[i++] = *comma++;
+            fg[i] = '\0'; comma++; i=0;
+            while (*comma != ')')
+                bg[i++] = *comma++;
+            bg[i] = '\0'; 
+            len += snprintf(end, remaining-len, ") %%Vf(%s) %%Vb(%s)\n", fg, bg);
+        }       
     }
     else
     {
diff --git a/apps/gui/theme_settings.c b/apps/gui/theme_settings.c
index 7f4046c..c67555b 100644
--- a/apps/gui/theme_settings.c
+++ b/apps/gui/theme_settings.c
@@ -30,14 +30,21 @@
 #include "settings.h"
 #include "wps.h"
 #include "file.h"
+#include "buffer.h"
 #if CONFIG_TUNER
 #include "radio.h"
 #endif
 #include "skin_engine/skin_engine.h"
-#include "skin_engine/skin_fonts.h"
+#include "skin_buffer.h"
 #include "statusbar-skinned.h"
 #include "bootchart.h"
 
+static char *skin_buffer = NULL;
+void theme_init_buffer(void)
+{
+    skin_buffer = buffer_alloc(SKIN_BUFFER_SIZE);
+}
+
 
 /* call this after loading a .wps/.rwps or other skin files, so that the
  * skin buffer is reset properly
@@ -71,9 +78,10 @@ void settings_apply_skins(void)
 {
     char buf[MAX_PATH];
     /* re-initialize the skin buffer before we start reloading skins */
-    skin_buffer_init();
     enum screen_type screen = SCREEN_MAIN;
     unsigned int i;
+    
+    skin_buffer_init(skin_buffer, SKIN_BUFFER_SIZE);
 #ifdef HAVE_LCD_BITMAP
     skin_backdrop_init();
     skin_font_init();
diff --git a/apps/gui/viewport.c b/apps/gui/viewport.c
index 7d79e5f..2b1cc9e 100644
--- a/apps/gui/viewport.c
+++ b/apps/gui/viewport.c
@@ -310,13 +310,6 @@ static void set_default_align_flags(struct viewport *vp)
 #endif /* HAVE_LCD_BITMAP */
 #endif /* __PCTOOL__ */
 
-#ifdef HAVE_LCD_COLOR
-#define ARG_STRING(_depth) ((_depth) == 2 ? "dddddgg":"dddddcc")
-#else
-#define ARG_STRING(_depth) "dddddgg"
-#endif
-
-
 void viewport_set_fullscreen(struct viewport *vp,
                               const enum screen_type screen)
 {
@@ -416,81 +409,4 @@ int get_viewport_default_colour(enum screen_type screen, bool fgcolour)
 #endif /* LCD_DEPTH > 1 || LCD_REMOTE_DEPTH > 1 */
 }
 
-const char* viewport_parse_viewport(struct viewport *vp,
-                                    enum screen_type screen,
-                                    const char *bufptr,
-                                    const char separator)
-{
-    /* parse the list to the viewport struct */
-    const char *ptr = bufptr;
-    uint32_t set = 0;
-
-    enum {
-        PL_X = 0,
-        PL_Y,
-        PL_WIDTH,
-        PL_HEIGHT,
-        PL_FONT,
-    };
-    
-    if (!(ptr = parse_list("ddddd", &set, separator, ptr,
-                &vp->x, &vp->y, &vp->width, &vp->height, &vp->font)))
-        return NULL;
-
-    /* X and Y *must* be set */
-    if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
-        return NULL;
-    /* check for negative values */
-    if (vp->x < 0)
-        vp->x += screens[screen].lcdwidth;
-    if (vp->y < 0)
-        vp->y += screens[screen].lcdheight;
-        
-    /* fix defaults, 
-     * and negative width/height which means "extend to edge minus value */
-    if (!LIST_VALUE_PARSED(set, PL_WIDTH))
-        vp->width = screens[screen].lcdwidth - vp->x;
-    else if (vp->width < 0)
-        vp->width = (vp->width + screens[screen].lcdwidth) - vp->x;
-    if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
-        vp->height = screens[screen].lcdheight - vp->y;
-    else if (vp->height < 0)
-        vp->height = (vp->height + screens[screen].lcdheight) - vp->y;
-
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
-    vp->fg_pattern = get_viewport_default_colour(screen, true);
-    vp->bg_pattern = get_viewport_default_colour(screen, false);
-#endif /* LCD_DEPTH > 1 || LCD_REMOTE_DEPTH > 1 */
-
-#ifdef HAVE_LCD_COLOR
-    vp->lss_pattern = global_settings.lss_color;
-    vp->lse_pattern = global_settings.lse_color;
-    vp->lst_pattern = global_settings.lst_color;
-#endif
-
-    /* Validate the viewport dimensions - we know that the numbers are
-       non-negative integers, ignore bars and assume the viewport takes them
-       * into account */
-    if ((vp->x >= screens[screen].lcdwidth) ||
-        ((vp->x + vp->width) > screens[screen].lcdwidth) ||
-        (vp->y >= screens[screen].lcdheight) ||
-        ((vp->y + vp->height) > screens[screen].lcdheight))
-    {
-        return NULL;
-    }
-
-    /* Default to using the user font if the font was an invalid number or '-'
-     * font 1 is *always* the UI font for the current screen
-     * 2 is always the first extra font    */
-    if (!LIST_VALUE_PARSED(set, PL_FONT))
-        vp->font = FONT_UI;
-
-    /* Set the defaults for fields not user-specified */
-    vp->drawmode = DRMODE_SOLID;
-#ifndef __PCTOOL__
-    set_default_align_flags(vp);
-#endif
-
-    return ptr;
-}
 #endif
diff --git a/apps/gui/viewport.h b/apps/gui/viewport.h
index 943cac2..51ab35e 100644
--- a/apps/gui/viewport.h
+++ b/apps/gui/viewport.h
@@ -74,25 +74,4 @@ bool viewport_point_within_vp(const struct viewport *vp,
 
 #endif /* __PCTOOL__ */
 
-#ifdef HAVE_LCD_BITMAP
-
-/*
- * Parse a viewport definition (vp_def), which looks like:
- *
- * Screens with depth > 1:
- *   X|Y|width|height|font|foregorund color|background color
- * Screens with depth = 1:
- *   X|Y|width|height|font
- *
- * | is a separator and can be specified via the parameter
- *
- * Returns the pointer to the char after the last character parsed
- * if everything went OK or NULL if an error happened (some values
- * not specified in the definition)
- */
-const char* viewport_parse_viewport(struct viewport *vp,
-                                    enum screen_type screen,
-                                    const char *vp_def,
-                                    const char separator);
-#endif /* HAVE_LCD_BITMAP */
 #endif /* __VIEWPORT_H__ */
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index a582afa..7dca645 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -175,7 +175,7 @@ void fade(bool fade_in, bool updatewps)
             if (updatewps)
             {
                 FOR_NB_SCREENS(i)
-                    skin_update(&gui_wps[i], WPS_REFRESH_NON_STATIC);
+                    skin_update(&gui_wps[i], SKIN_REFRESH_NON_STATIC);
             }
             sleep(1);
         }
@@ -191,7 +191,7 @@ void fade(bool fade_in, bool updatewps)
             if (updatewps)
             {
                 FOR_NB_SCREENS(i)
-                    skin_update(&gui_wps[i], WPS_REFRESH_NON_STATIC);
+                    skin_update(&gui_wps[i], SKIN_REFRESH_NON_STATIC);
             }
             sleep(1);
         }
@@ -212,7 +212,7 @@ void fade(bool fade_in, bool updatewps)
 
 static bool update_onvol_change(struct gui_wps * gwps)
 {
-    skin_update(gwps, WPS_REFRESH_NON_STATIC);
+    skin_update(gwps, SKIN_REFRESH_NON_STATIC);
 
 #ifdef HAVE_LCD_CHARCELLS
     splashf(0, "Vol: %3d dB",
@@ -372,8 +372,8 @@ bool ffwd_rew(int button)
                 FOR_NB_SCREENS(i)
                 {
                     skin_update(&gui_wps[i],
-                                WPS_REFRESH_PLAYER_PROGRESS |
-                                WPS_REFRESH_DYNAMIC);
+                                SKIN_REFRESH_PLAYER_PROGRESS |
+                                SKIN_REFRESH_DYNAMIC);
                 }
 
                 break;
@@ -390,7 +390,7 @@ bool ffwd_rew(int button)
 #endif
 #ifdef HAVE_LCD_CHARCELLS
                 FOR_NB_SCREENS(i)
-                    skin_update(&gui_wps[i], WPS_REFRESH_ALL);
+                    skin_update(&gui_wps[i], SKIN_REFRESH_ALL);
 #endif
                 exit = true;
                 break;
@@ -662,7 +662,7 @@ static void gwps_enter_wps(void)
         display->backdrop_show(gwps->data->backdrop);
 #endif
         display->clear_display();
-        skin_update(gwps, WPS_REFRESH_ALL);
+        skin_update(gwps, SKIN_REFRESH_ALL);
 
 #ifdef HAVE_TOUCHSCREEN
         skin_disarm_touchregions(gui_wps[i].data);
@@ -1116,7 +1116,7 @@ long gui_wps_show(void)
 #endif
                 {
                     skin_update(&gui_wps[i], wps_sync_data.do_full_update ?
-                                            WPS_REFRESH_ALL : WPS_REFRESH_NON_STATIC);
+                                            SKIN_REFRESH_ALL : SKIN_REFRESH_NON_STATIC);
                 }
             }
             wps_sync_data.do_full_update = false;
diff --git a/apps/main.c b/apps/main.c
index b3252d8..f473a7c 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -356,6 +356,7 @@ static void init(void)
     tree_mem_init();
     filetype_init();
     playlist_init();
+    theme_init_buffer();
 
 #if CONFIG_CODEC != SWCODEC
     mp3_init( global_settings.volume,
@@ -638,6 +639,7 @@ static void init(void)
 #if CONFIG_CODEC == SWCODEC
     tdspeed_init();
 #endif /* CONFIG_CODEC == SWCODEC */
+    theme_init_buffer();
 
 #if CONFIG_CODEC != SWCODEC
     /* No buffer allocation (see buffer.c) may take place after the call to
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
index 32164ff..199f421 100644
--- a/apps/menus/main_menu.c
+++ b/apps/menus/main_menu.c
@@ -51,7 +51,7 @@
 #include "version.h"
 #include "time.h"
 #include "wps.h"
-#include "skin_engine/skin_buffer.h"
+#include "skin_buffer.h"
 
 static const struct browse_folder_info config = {ROCKBOX_DIR, SHOW_CFG};
 
diff --git a/apps/misc.c b/apps/misc.c
index bae8dfb..c0d07d6 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -937,142 +937,22 @@ int hex_to_rgb(const char* hex, int* color)
 #endif /* HAVE_LCD_COLOR */
 
 #ifdef HAVE_LCD_BITMAP
-/* A simplified scanf - used (at time of writing) by wps parsing functions.
-
-   fmt - char array specifying the format of each list option.  Valid values
-         are:  d - int
-               s - string (sets pointer to string, without copying)
-               c - hex colour (RGB888 - e.g. ff00ff)
-               g - greyscale "colour" (0-3)
-   set_vals - if not NULL 1 is set in the bitplace if the item was read OK
-                0 if not read.
-                first item is LSB, (max 32 items! )
-                Stops parseing if an item is invalid unless the item == '-'
-   sep - list separator (e.g. ',' or '|')
-   str  - string to parse, must be terminated by 0 or sep
-   ... - pointers to store the parsed values
-
-   return value - pointer to char after parsed data, 0 if there was an error.
-
-*/
-
 /* '0'-'3' are ASCII 0x30 to 0x33 */
 #define is0123(x) (((x) & 0xfc) == 0x30)
 
-const char* parse_list(const char *fmt, uint32_t *set_vals,
-                       const char sep, const char* str, ...)
+bool parse_color(char *text, int *value)
 {
-    va_list ap;
-    const char* p = str, *f = fmt;
-    const char** s;
-    int* d;
-    bool set, is_negative;
-    int i=0;
-
-    va_start(ap, str);
-    if (set_vals)
-        *set_vals = 0;
-    while (*fmt)
-    {
-        /* Check for separator, if we're not at the start */
-        if (f != fmt)
-        {
-            if (*p != sep)
-                goto err;
-            p++;
-        }
-        set = false;
-        switch (*fmt++) 
-        {
-            case 's': /* string - return a pointer to it (not a copy) */
-                s = va_arg(ap, const char **);
-
-                *s = p;
-                while (*p && *p != sep && *p != ')')
-                    p++;
-                set = (s[0][0]!='-') && (s[0][1]!=sep && s[0][1]!=')') ;
-                break;
-
-            case 'd': /* int */
-                is_negative = false;
-                d = va_arg(ap, int*);
-
-                if (*p == '-' && isdigit(*(p+1)))
-                {
-                    is_negative = true;
-                    p++;
-                }
-                if (!isdigit(*p))
-                {
-                    if (!set_vals || *p != '-')
-                        goto err;
-                    p++;
-                }
-                else
-                {
-                    *d = *p++ - '0';
-                    while (isdigit(*p))
-                        *d = (*d * 10) + (*p++ - '0');
-                    set = true;
-                    if (is_negative)
-                        *d *= -1;
-                }
-
-                break;
-
 #ifdef HAVE_LCD_COLOR
-            case 'c': /* colour (rrggbb - e.g. f3c1a8) */
-                d = va_arg(ap, int*);
-
-                if (hex_to_rgb(p, d) < 0)
-                {
-                    if (!set_vals || *p != '-')
-                        goto err;
-                    p++;
-                }
-                else
-                {
-                    p += 6;
-                    set = true;
-                }
-
-                break;
+    if (hex_to_rgb(text, value) < 0)
+        return false;
 #endif
 
 #if LCD_DEPTH == 2 || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
-            case 'g': /* greyscale colour (0-3) */
-                d = va_arg(ap, int*);
-
-                if (!is0123(*p))
-                {
-                    if (!set_vals || *p != '-')
-                        goto err;
-                    p++;
-                }
-                else
-                {
-                    *d = *p++ - '0';
-                    set = true;
-                }
-
-                break;
+    if (!is0123(*text))
+        return false;
+    *value = *text - '0';
 #endif
-
-            default:  /* Unknown format type */
-                goto err;
-                break;
-        }
-        if (set_vals && set)
-            *set_vals |= BIT_N(i);
-        i++;
-    }
-
-    va_end(ap);
-    return p;
-
-err:
-    va_end(ap);
-    return NULL;
+    return true;
 }
 
 /* only used in USB HID and set_time screen */
diff --git a/apps/misc.h b/apps/misc.h
index 34f754b..58a9085 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -94,16 +94,7 @@ bool dir_exists(const char *path);
 char *strip_extension(char* buffer, int buffer_size, const char *filename);
 
 #ifdef HAVE_LCD_BITMAP
-/* A simplified scanf */
-/*
- * Checks whether the value at position 'position' was really read
- * during a call to 'parse_list'
- *   - position: 0-based number of the value
- *   - valid_vals: value after the call to 'parse_list'
- */
-#define LIST_VALUE_PARSED(setvals, position) ((setvals) & BIT_N(position))
-const char* parse_list(const char *fmt, uint32_t *set_vals,
-                       const char sep, const char* str, ...);
+bool parse_color(char *text, int *value);
 
 /* only used in USB HID and set_time screen */
 #if defined(USB_ENABLE_HID) || (CONFIG_RTC != 0)
diff --git a/apps/radio/radio.c b/apps/radio/radio.c
index b675062..49fd9e7 100644
--- a/apps/radio/radio.c
+++ b/apps/radio/radio.c
@@ -799,8 +799,8 @@ int radio_screen(void)
                 if (restore)
                     fms_fix_displays(FMS_ENTER);
                 FOR_NB_SCREENS(i)
-                    skin_update(fms_get(i), restore ? WPS_REFRESH_ALL :
-                                                      WPS_REFRESH_NON_STATIC);
+                    skin_update(fms_get(i), restore ? SKIN_REFRESH_ALL :
+                                                      SKIN_REFRESH_NON_STATIC);
                 restore = false; 
             }
         }
diff --git a/apps/recorder/albumart.h b/apps/recorder/albumart.h
index 51f456d..0f50979 100644
--- a/apps/recorder/albumart.h
+++ b/apps/recorder/albumart.h
@@ -35,9 +35,11 @@
 bool find_albumart(const struct mp3entry *id3, char *buf, int buflen,
                     struct dim *dim);
 
-/* Draw the album art bitmap from the given handle ID onto the given WPS.
+#ifndef PLUGIN
+/* Draw the album art bitmap from the given handle ID onto the given Skin.
    Call with clear = true to clear the bitmap instead of drawing it. */
 void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear);
+#endif
 
 bool search_albumart_files(const struct mp3entry *id3, const char *size_string,
                            char *buf, int buflen);
diff --git a/apps/settings.h b/apps/settings.h
index 75eddb4..855498b 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -260,6 +260,7 @@ void sound_settings_apply(void);
  * skin buffer is reset properly
  */
 void settings_apply_skins(void);
+void theme_init_buffer(void);
 
 void settings_apply(bool read_disk);
 void settings_apply_pm_range(void);
diff --git a/lib/skin_parser/SOURCES b/lib/skin_parser/SOURCES
index 3024797..37a6e9a 100644
--- a/lib/skin_parser/SOURCES
+++ b/lib/skin_parser/SOURCES
@@ -1,6 +1,6 @@
 skin_buffer.c
 skin_parser.c
-#ifndef ROCKBOX
+#if !defined(ROCKBOX) || defined(__PCTOOL__)
 skin_debug.c
 #endif
 skin_scan.c
diff --git a/lib/skin_parser/skin_buffer.c b/lib/skin_parser/skin_buffer.c
index 05cdc0c..10d580d 100644
--- a/lib/skin_parser/skin_buffer.c
+++ b/lib/skin_parser/skin_buffer.c
@@ -24,21 +24,19 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include "skin_buffer.h"
+
 #ifdef ROCKBOX
-#define SKIN_BUFFER_SIZE (400*1024) /* Excessivly large for now */
-static unsigned char buffer[SKIN_BUFFER_SIZE];
-static unsigned char *buffer_front = NULL; /* start of the free space,
-                                              increases with allocation*/
+static size_t buf_size;
+static unsigned char *buffer_start = NULL;
+static unsigned char *buffer_front = NULL;
 #endif
 
-void skin_buffer_init(void)
+void skin_buffer_init(char* buffer, size_t size)
 {
 #if defined(ROCKBOX)
-    {
-        /* reset the buffer.... */
-        buffer_front = buffer;
-        //TODO: buf_size = size;
-    }
+    buffer_start = buffer_front = buffer;
+    buf_size = size;
 #endif
 }
 
@@ -46,7 +44,9 @@ void skin_buffer_init(void)
 void* skin_buffer_alloc(size_t size)
 {
     void *retval = NULL;
-#ifdef ROCKBOX    
+#ifdef ROCKBOX
+    if (size > skin_buffer_freespace())
+        return NULL;
     retval = buffer_front;
     buffer_front += size;
     /* 32-bit aligned */
@@ -62,10 +62,22 @@ void* skin_buffer_alloc(size_t size)
 /* get the number of bytes currently being used */
 size_t skin_buffer_usage(void)
 {
-    return buffer_front - buffer;
+    return buffer_front - buffer_start;
 }
 size_t skin_buffer_freespace(void)
 {
-    return SKIN_BUFFER_SIZE - skin_buffer_usage();
+    return buf_size - skin_buffer_usage();
 }
 #endif
+
+static unsigned char *saved_buffer_pos = NULL;
+void skin_buffer_save_position(void)
+{
+    saved_buffer_pos = buffer_front;
+}
+    
+void skin_buffer_restore_position(void)
+{
+    if (saved_buffer_pos)
+        buffer_front = saved_buffer_pos;
+}
diff --git a/lib/skin_parser/skin_buffer.h b/lib/skin_parser/skin_buffer.h
index ff477da..1698b8a 100644
--- a/lib/skin_parser/skin_buffer.h
+++ b/lib/skin_parser/skin_buffer.h
@@ -25,11 +25,15 @@
 #include <stdlib.h>
 #ifndef _SKIN_BUFFFER_H_
 #define _SKIN_BUFFFER_H_
-void skin_buffer_init(size_t size);
+void skin_buffer_init(char* buffer, size_t size);
 /* Allocate size bytes from the buffer */
 void* skin_buffer_alloc(size_t size);
 
 /* get the number of bytes currently being used */
 size_t skin_buffer_usage(void);
 size_t skin_buffer_freespace(void);
+
+/* save and restore a buffer position incase a skin fails to load */
+void skin_buffer_save_position(void);
+void skin_buffer_restore_position(void);
 #endif
diff --git a/lib/skin_parser/skin_debug.c b/lib/skin_parser/skin_debug.c
index c03b32e..88ad209 100644
--- a/lib/skin_parser/skin_debug.c
+++ b/lib/skin_parser/skin_debug.c
@@ -35,6 +35,7 @@ extern char* skin_start;
 /* Global error variables */
 int error_line;
 int error_col;
+char *error_line_start;
 char* error_message;
 
 /* Debugging functions */
@@ -48,6 +49,7 @@ void skin_error(enum skin_errorcode error, char* cursor)
         cursor--;
         error_col++;
     }
+    error_line_start = cursor+1;
 
     error_line = skin_line;
 
@@ -285,4 +287,42 @@ void skin_debug_indent()
     for(i = 0; i < debug_indent_level; i++)
         printf("    ");
 }
+
 #endif
+
+#define MIN(a,b) ((a<b)?(a):(b))
+void skin_error_format_message()
+{
+    int i;
+    char text[128];
+    char* line_end = strchr(error_line_start, '\n');
+    int len = MIN(line_end - error_line_start, 80);
+    if (!line_end)
+        len = strlen(error_line_start);
+    printf("Error on line %d.\n", error_line);
+    error_col--;
+    if (error_col <= 10)
+    {
+        strncpy(text, error_line_start, len);
+        text[len] = '\0';
+    }
+    else
+    {
+        int j;
+        /* make it fit nicely.. "<start few chars>...<10 chars><error>" */
+        strncpy(text, error_line_start, 6);
+        i = 5;
+        text[i++] = '.';
+        text[i++] = '.';
+        text[i++] = '.';
+        for (j=error_col-10; error_line_start[j] && error_line_start[j] != '\n'; j++)
+            text[i++] = error_line_start[j];
+        text[i] = '\0';
+        error_col = 18;
+    }
+    printf("%s\n", text);
+    for (i=0; i<error_col; i++)
+        text[i] = ' ';
+    snprintf(&text[i],64, "^ \'%s\' Here", error_message);
+    printf("%s\n", text);
+}
diff --git a/lib/skin_parser/skin_debug.h b/lib/skin_parser/skin_debug.h
index fbff5cb..c353808 100644
--- a/lib/skin_parser/skin_debug.h
+++ b/lib/skin_parser/skin_debug.h
@@ -28,8 +28,12 @@ extern "C"
 {
 #endif
 
+#if !defined(ROCKBOX) || defined(__PCTOOL__)
+#define SKINPARSER_DEBUG
+#endif
+
 #include "skin_parser.h"
-#ifndef ROCKBOX
+#ifdef SKINPARSER_DEBUG
 /* Debugging functions */
 void skin_error(enum skin_errorcode error, char* cursor);
 int skin_error_line(void);
@@ -37,6 +41,7 @@ int skin_error_col(void);
 char* skin_error_message(void);
 void skin_clear_errors(void);
 void skin_debug_tree(struct skin_element* root);
+void skin_error_format_message();
 
 /* Auxiliary debug functions */
 void skin_debug_params(int count, struct skin_tag_parameter params[]);
@@ -46,7 +51,7 @@ void skin_debug_indent(void);
 #define skin_error(...)
 #define skin_clear_errors()
 
-#endif /* !ROCKBOX */
+#endif /* SKINPARSER_DEBUG */
 
 
 #ifdef __cplusplus
diff --git a/lib/skin_parser/skin_parser.c b/lib/skin_parser/skin_parser.c
index 3e23067..b742316 100644
--- a/lib/skin_parser/skin_parser.c
+++ b/lib/skin_parser/skin_parser.c
@@ -21,6 +21,7 @@
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdbool.h>
 #include <string.h>
 #include <ctype.h>
 #include <stdbool.h>
@@ -37,6 +38,11 @@ int skin_line = 0;
 char* skin_start = 0;
 int viewport_line = 0;
 
+#ifdef ROCKBOX
+static skin_callback callback = NULL;
+static void* callback_data;
+#endif
+
 /* Auxiliary parsing functions (not visible at global scope) */
 static struct skin_element* skin_parse_viewport(char** document);
 static struct skin_element* skin_parse_line(char** document);
@@ -55,10 +61,23 @@ static int skin_parse_comment(struct skin_element* element, char** document);
 static struct skin_element* skin_parse_code_as_arg(char** document);
 
 
+static void skip_whitespace(char** document)
+{
+    while(**document == ' ' || **document == '\t')
+        (*document)++;
+}
 
+#ifdef ROCKBOX
+struct skin_element* skin_parse(const char* document, 
+                                skin_callback cb, void* cb_data)
+                                
+{
+    callback = cb;
+    callback_data = cb_data;
+#else
 struct skin_element* skin_parse(const char* document)
 {
-
+#endif
     struct skin_element* root = NULL;
     struct skin_element* last = NULL;
 
@@ -107,6 +126,8 @@ static struct skin_element* skin_parse_viewport(char** document)
     struct skin_element* retval = NULL;
 
     retval = skin_alloc_element();
+    if (!retval)
+        return NULL;
     retval->type = VIEWPORT;
     retval->children_count = 1;
     retval->line = skin_line;
@@ -129,11 +150,18 @@ static struct skin_element* skin_parse_viewport(char** document)
             skin_line++;
         }
     }
+#ifdef ROCKBOX
+    else if (callback)
+    {
+        if (callback(retval, callback_data) == CALLBACK_ERROR)
+            return NULL;
+    }
+#endif
 
     retval->children_count = 1;
     retval->children = skin_alloc_children(1);
-
-
+    if (!retval->children)
+        return NULL;
     do
     {
 
@@ -199,7 +227,6 @@ static struct skin_element* skin_parse_viewport(char** document)
                 return NULL;
 
         }
-
         /* Making sure last is at the end */
         while(last->next)
             last = last->next;
@@ -245,6 +272,8 @@ static struct skin_element* skin_parse_line_optional(char** document,
 
     /* A wrapper for the line */
     retval = skin_alloc_element();
+    if (!retval)
+        return NULL;
     retval->type = LINE;
     retval->line = skin_line;
     if(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
@@ -261,7 +290,24 @@ static struct skin_element* skin_parse_line_optional(char** document,
     }
 
     if(retval->children_count > 0)
+    {
         retval->children = skin_alloc_children(1);
+        if (!retval->children)
+            return NULL;
+    }
+
+#ifdef ROCKBOX
+    if (callback)
+    {
+        switch (callback(retval, callback_data))
+        {
+            case CALLBACK_ERROR:
+                return NULL;
+            default:
+                break;
+        }
+    }
+#endif
 
     while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
           && !((*cursor == ARGLISTSEPERATESYM
@@ -275,11 +321,15 @@ static struct skin_element* skin_parse_line_optional(char** document,
         if(root)
         {
             current->next = skin_alloc_element();
+            if (!current->next)
+                return NULL;
             current = current->next;
         }
         else
         {
             current = skin_alloc_element();
+            if (!current)
+                return NULL;
             root = current;
         }
 
@@ -306,9 +356,10 @@ static struct skin_element* skin_parse_line_optional(char** document,
         }
     }
 
+
     /* Moving up the calling function's pointer */
     *document = cursor;
-
+    
     if(root)
         retval->children[0] = root;
     return retval;
@@ -328,6 +379,8 @@ static struct skin_element* skin_parse_sublines_optional(char** document,
     int i;
 
     retval = skin_alloc_element();
+    if (!retval)
+        return NULL;
     retval->type = LINE_ALTERNATOR;
     retval->next = NULL;
     retval->line = skin_line;
@@ -374,6 +427,8 @@ static struct skin_element* skin_parse_sublines_optional(char** document,
     /* ...and then we parse them */
     retval->children_count = sublines;
     retval->children = skin_alloc_children(sublines);
+    if (!retval->children)
+        return NULL;
 
     cursor = *document;
     for(i = 0; i < sublines; i++)
@@ -392,6 +447,13 @@ static struct skin_element* skin_parse_sublines_optional(char** document,
         }
     }
 
+#ifdef ROCKBOX
+    if (callback)
+    {
+        if (callback(retval, callback_data) == CALLBACK_ERROR)
+            return NULL;
+    }
+#endif
     *document = cursor;
 
     return retval;
@@ -458,6 +520,14 @@ static int skin_parse_tag(struct skin_element* element, char** document)
        || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
        || (star && *cursor != ARGLISTOPENSYM))
     {
+        
+#ifdef ROCKBOX
+        if (callback)
+        {
+            if (callback(element, callback_data) == CALLBACK_ERROR)
+                return 0;
+        }
+#endif
         *document = cursor;
         return 1;
     }
@@ -507,6 +577,8 @@ static int skin_parse_tag(struct skin_element* element, char** document)
     cursor = bookmark; /* Restoring the cursor */
     element->params_count = num_args;
     element->params = skin_alloc_params(num_args);
+    if (!element->params)
+        return 0;
 
     /* Now we have to actually parse each argument */
     for(i = 0; i < num_args; i++)
@@ -587,7 +659,6 @@ static int skin_parse_tag(struct skin_element* element, char** document)
             }
             if (have_tenth == false)
                 val *= 10;
-
             element->params[i].type = DECIMAL;
             element->params[i].data.number = val;
         }
@@ -644,7 +715,13 @@ static int skin_parse_tag(struct skin_element* element, char** document)
         skin_error(INSUFFICIENT_ARGS, cursor);
         return 0;
     }
-    
+#ifdef ROCKBOX
+    if (callback)
+    {
+        if (callback(element, callback_data) == CALLBACK_ERROR)
+            return 0;
+    }
+#endif
     *document = cursor;
 
     return 1;
@@ -691,6 +768,8 @@ static int skin_parse_text(struct skin_element* element, char** document,
     element->line = skin_line;
     element->next = NULL;
     element->data = text = skin_alloc_string(length);
+    if (!element->data)
+        return 0;
     
     for(dest = 0; dest < length; dest++)
     {
@@ -702,6 +781,14 @@ static int skin_parse_text(struct skin_element* element, char** document,
         cursor++;
     }
     text[length] = '\0';
+    
+#ifdef ROCKBOX
+    if (callback)
+    {
+        if (callback(element, callback_data) == CALLBACK_ERROR)
+            return 0;
+    }
+#endif
 
     *document = cursor;
 
@@ -715,14 +802,40 @@ static int skin_parse_conditional(struct skin_element* element, char** document)
     char* bookmark;
     int children = 1;
     int i;
+    
+#ifdef ROCKBOX
+    bool feature_available = true;
+    char *false_branch = NULL;
+#endif
 
-    element->type = CONDITIONAL;
+    /* Some conditional tags allow for target feature checking,
+     * so to handle that call the callback as usual with type == TAG
+     * then call it a second time with type == CONDITIONAL and check the return
+     * value */
+    element->type = TAG;
     element->line = skin_line;
 
     /* Parsing the tag first */
     if(!skin_parse_tag(element, &cursor))
         return 0;
 
+    element->type = CONDITIONAL;
+#ifdef ROCKBOX
+    if (callback)
+    {
+        switch (callback(element, callback_data))
+        {
+            case FEATURE_NOT_AVAILABLE:
+                feature_available = false;
+                break;
+            case CALLBACK_ERROR:
+                return 0;
+            default:
+                break;
+        }
+    }
+#endif
+    
     /* Counting the children */
     if(*(cursor++) != ENUMLISTOPENSYM)
     {
@@ -751,16 +864,35 @@ static int skin_parse_conditional(struct skin_element* element, char** document)
         {
             children++;
             cursor++;
+#ifdef ROCKBOX
+            if (false_branch == NULL && !feature_available)
+            {
+                false_branch = cursor;
+                children--;
+            }
+#endif
         }
         else
         {
             cursor++;
         }
     }
+#ifdef ROCKBOX
+    if (*cursor == ENUMLISTCLOSESYM && 
+        false_branch == NULL && !feature_available)
+    {
+        false_branch = cursor+1;
+        children--;
+    }
+    /* if we are skipping the true branch fix that up */
+    cursor = false_branch ? false_branch : bookmark;
+#else
     cursor = bookmark;
-
+#endif
     /* Parsing the children */
     element->children = skin_alloc_children(children);
+    if (!element->children)
+        return 0;
     element->children_count = children;
 
     for(i = 0; i < children; i++)
@@ -809,6 +941,8 @@ static int skin_parse_comment(struct skin_element* element, char** document)
     element->data = NULL;
 #else    
     element->data = text = skin_alloc_string(length);
+    if (!element->data)
+        return 0;
     /* We copy from one char past cursor to leave out the # */
     memcpy((void*)text, (void*)(cursor + 1),
            sizeof(char) * (length-1));
@@ -877,6 +1011,8 @@ struct skin_element* skin_alloc_element()
 {
     struct skin_element* retval =  (struct skin_element*)
                                    skin_buffer_alloc(sizeof(struct skin_element));
+    if (!retval)
+        return NULL;
     retval->type = UNKNOWN;
     retval->next = NULL;
     retval->tag = NULL;
diff --git a/lib/skin_parser/skin_parser.h b/lib/skin_parser/skin_parser.h
index ad10f90..8514dfd 100644
--- a/lib/skin_parser/skin_parser.h
+++ b/lib/skin_parser/skin_parser.h
@@ -115,14 +115,27 @@ struct skin_element
     struct skin_element* next;
 };
 
+enum skin_cb_returnvalue
+{
+    CALLBACK_ERROR = -666,
+    FEATURE_NOT_AVAILABLE,
+    CALLBACK_OK = 0,
+    /* > 0 reserved for future use */
+};
+typedef int (*skin_callback)(struct skin_element* element, void* data);
+
 /***********************************************************************
  ***** Functions *******************************************************
  **********************************************************************/
 
 /* Parses a WPS document and returns a list of skin_element
    structures. */
+#ifdef ROCKBOX
+struct skin_element* skin_parse(const char* document, 
+                                skin_callback callback, void* callback_data);
+#else
 struct skin_element* skin_parse(const char* document);
-
+#endif
 /* Memory management functions */
 struct skin_element* skin_alloc_element(void);
 struct skin_element** skin_alloc_children(int count);
diff --git a/lib/skin_parser/skin_scan.c b/lib/skin_parser/skin_scan.c
index 6b5c189..d18f222 100644
--- a/lib/skin_parser/skin_scan.c
+++ b/lib/skin_parser/skin_scan.c
@@ -40,12 +40,6 @@ void skip_comment(char** document)
         (*document)++;
 }
 
-void skip_whitespace(char** document)
-{
-    while(**document == ' ' || **document == '\t')
-        (*document)++;
-}
-
 void skip_arglist(char** document)
 {
     if(**document == ARGLISTOPENSYM)
@@ -132,6 +126,8 @@ char* scan_string(char** document)
     /* Copying the string */
     cursor = *document;
     buffer = skin_alloc_string(length);
+    if (!buffer)
+        return NULL;
     buffer[length] = '\0';
     for(i = 0; i < length; i++)
     {
diff --git a/lib/skin_parser/skin_scan.h b/lib/skin_parser/skin_scan.h
index b1d04a6..72d4475 100644
--- a/lib/skin_parser/skin_scan.h
+++ b/lib/skin_parser/skin_scan.h
@@ -30,7 +30,6 @@ extern "C"
 
 /* Scanning functions */
 void skip_comment(char** document);
-void skip_whitespace(char** document);
 void skip_arglist(char** document);
 void skip_enumlist(char** document);
 char* scan_string(char** document);
diff --git a/lib/skin_parser/tag_table.c b/lib/skin_parser/tag_table.c
index dd8df63..a7e3378 100644
--- a/lib/skin_parser/tag_table.c
+++ b/lib/skin_parser/tag_table.c
@@ -33,182 +33,181 @@ struct tag_info legal_tags[] =
     { SKIN_TOKEN_ALIGN_RIGHT_RTL,       "aR", "", 0 },
     { SKIN_TOKEN_ALIGN_LANGDIRECTION,   "ax", "", 0 },
     
-    { SKIN_TOKEN_BATTERY_PERCENT,       "bl" , BAR_PARAMS, 0 },
-    { SKIN_TOKEN_BATTERY_VOLTS,         "bv", "", 0 },
-    { SKIN_TOKEN_BATTERY_TIME,          "bt", "", 0 },
-    { SKIN_TOKEN_BATTERY_SLEEPTIME,     "bs", "", 0 },
-    { SKIN_TOKEN_BATTERY_CHARGING,      "bc", "", 0 },
-    { SKIN_TOKEN_BATTERY_CHARGER_CONNECTED, "bp", "", 0 },
-    { SKIN_TOKEN_USB_POWERED,           "bu", "", 0 },
-    
-    
-    { SKIN_TOKEN_RTC_PRESENT,           "cc", "", 0 },
-    { SKIN_TOKEN_RTC_DAY_OF_MONTH,      "cd", "", 0 },
-    { SKIN_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED, "ce", "", 0 },
-    { SKIN_TOKEN_RTC_12HOUR_CFG,        "cf", "", 0 },
-    { SKIN_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", "", 0 },
-    { SKIN_TOKEN_RTC_HOUR_24,           "ck", "", 0 },
-    { SKIN_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", "", 0 },
-    { SKIN_TOKEN_RTC_HOUR_12,           "cl", "", 0 },
-    { SKIN_TOKEN_RTC_MONTH,             "cm", "", 0 },
-    { SKIN_TOKEN_RTC_MINUTE,            "cM", "", 0 },
-    { SKIN_TOKEN_RTC_SECOND,            "cS", "", 0 },
-    { SKIN_TOKEN_RTC_YEAR_2_DIGITS,     "cy", "", 0 },
-    { SKIN_TOKEN_RTC_YEAR_4_DIGITS,     "cY", "", 0 },
-    { SKIN_TOKEN_RTC_AM_PM_UPPER,       "cP", "", 0 },
-    { SKIN_TOKEN_RTC_AM_PM_LOWER,       "cp", "", 0 },
-    { SKIN_TOKEN_RTC_WEEKDAY_NAME,      "ca", "", 0 },
-    { SKIN_TOKEN_RTC_MONTH_NAME,        "cb", "", 0 },
-    { SKIN_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", "", 0 },
-    { SKIN_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", "", 0 },
+    { SKIN_TOKEN_BATTERY_PERCENT,       "bl" , BAR_PARAMS, SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_BATTERY_VOLTS,         "bv", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_BATTERY_TIME,          "bt", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_BATTERY_SLEEPTIME,     "bs", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_BATTERY_CHARGING,      "bc", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_BATTERY_CHARGER_CONNECTED, "bp", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_USB_POWERED,           "bu", "", SKIN_REFRESH_DYNAMIC },
+    
+    
+    { SKIN_TOKEN_RTC_PRESENT,           "cc", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_RTC_DAY_OF_MONTH,      "cd", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED, "ce", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_12HOUR_CFG,        "cf", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_HOUR_24,           "ck", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_HOUR_12,           "cl", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_MONTH,             "cm", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_MINUTE,            "cM", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_SECOND,            "cS", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_YEAR_2_DIGITS,     "cy", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_YEAR_4_DIGITS,     "cY", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_AM_PM_UPPER,       "cP", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_AM_PM_LOWER,       "cp", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_WEEKDAY_NAME,      "ca", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_MONTH_NAME,        "cb", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", "", SKIN_RTC_REFRESH },
+    { SKIN_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", "", SKIN_RTC_REFRESH },
         
-    { SKIN_TOKEN_FILE_BITRATE,          "fb", "", 0 },
-    { SKIN_TOKEN_FILE_CODEC,            "fc", "", 0 },
-    { SKIN_TOKEN_FILE_FREQUENCY,        "ff", "", 0 },
-    { SKIN_TOKEN_FILE_FREQUENCY_KHZ,    "fk", "", 0 },
-    { SKIN_TOKEN_FILE_NAME_WITH_EXTENSION,  "fm", "", 0 },
-    { SKIN_TOKEN_FILE_NAME,             "fn", "", 0 },
-    { SKIN_TOKEN_FILE_PATH,             "fp", "", 0 },
-    { SKIN_TOKEN_FILE_SIZE,             "fs", "", 0 },
-    { SKIN_TOKEN_FILE_VBR,              "fv", "", 0 },
-    { SKIN_TOKEN_FILE_DIRECTORY,        "d"  , "I", 0 },
-    
-    { SKIN_TOKEN_FILE_BITRATE,          "Fb", "", 0 },
-    { SKIN_TOKEN_FILE_CODEC,            "Fc", "", 0 },
-    { SKIN_TOKEN_FILE_FREQUENCY,        "Ff", "", 0 },
-    { SKIN_TOKEN_FILE_FREQUENCY_KHZ,    "Fk", "", 0 },
-    { SKIN_TOKEN_FILE_NAME_WITH_EXTENSION,  "Fm", "", 0 },
-    { SKIN_TOKEN_FILE_NAME,             "Fn", "", 0 },
-    { SKIN_TOKEN_FILE_PATH,             "Fp", "", 0 },
-    { SKIN_TOKEN_FILE_SIZE,             "Fs", "", 0 },
-    { SKIN_TOKEN_FILE_VBR,              "Fv", "", 0 },
-    { SKIN_TOKEN_FILE_DIRECTORY,        "D"  , "I", 0 },
-    
-    
-    { SKIN_TOKEN_METADATA_ARTIST,       "ia", "", 0 },
-    { SKIN_TOKEN_METADATA_COMPOSER,     "ic", "", 0 },
-    { SKIN_TOKEN_METADATA_ALBUM,        "id", "", 0 },
-    { SKIN_TOKEN_METADATA_ALBUM_ARTIST, "iA", "", 0 },
-    { SKIN_TOKEN_METADATA_GROUPING,     "iG", "", 0 },
-    { SKIN_TOKEN_METADATA_GENRE,        "ig", "", 0 },
-    { SKIN_TOKEN_METADATA_DISC_NUMBER,  "ik", "", 0 },
-    { SKIN_TOKEN_METADATA_TRACK_NUMBER, "in", "", 0 },
-    { SKIN_TOKEN_METADATA_TRACK_TITLE,  "it", "", 0 },
-    { SKIN_TOKEN_METADATA_VERSION,      "iv", "", 0 },
-    { SKIN_TOKEN_METADATA_YEAR,         "iy", "", 0 },
-    { SKIN_TOKEN_METADATA_COMMENT,      "iC", "", 0 },
-    
-    { SKIN_TOKEN_METADATA_ARTIST,       "Ia", "", 0 },
-    { SKIN_TOKEN_METADATA_COMPOSER,     "Ic", "", 0 },
-    { SKIN_TOKEN_METADATA_ALBUM,        "Id", "", 0 },
-    { SKIN_TOKEN_METADATA_ALBUM_ARTIST, "IA", "", 0 },
-    { SKIN_TOKEN_METADATA_GROUPING,     "IG", "", 0 },
-    { SKIN_TOKEN_METADATA_GENRE,        "Ig", "", 0 },
-    { SKIN_TOKEN_METADATA_DISC_NUMBER,  "Ik", "", 0 },
-    { SKIN_TOKEN_METADATA_TRACK_NUMBER, "In", "", 0 },
-    { SKIN_TOKEN_METADATA_TRACK_TITLE,  "It", "", 0 },
-    { SKIN_TOKEN_METADATA_VERSION,      "Iv", "", 0 },
-    { SKIN_TOKEN_METADATA_YEAR,         "Iy", "", 0 },
-    { SKIN_TOKEN_METADATA_COMMENT,      "IC", "", 0 },
-    
-    { SKIN_TOKEN_SOUND_PITCH,           "Sp", "", 0 },
-    { SKIN_TOKEN_SOUND_SPEED,           "Ss", "", 0 },
-    
-    { SKIN_TOKEN_VLED_HDD,              "lh", "", 0 },
-    
-    { SKIN_TOKEN_MAIN_HOLD,             "mh", "", 0 },
-    { SKIN_TOKEN_REMOTE_HOLD,           "mr", "", 0 },
-    { SKIN_TOKEN_REPEAT_MODE,           "mm", "", 0 },
-    { SKIN_TOKEN_PLAYBACK_STATUS,       "mp", "", 0 },
-    { SKIN_TOKEN_BUTTON_VOLUME,         "mv", "|D", 0 },
-    
-    { SKIN_TOKEN_PEAKMETER,             "pm", "", 0 },
-    { SKIN_TOKEN_PLAYER_PROGRESSBAR,    "pf", "", 0 },
-    { SKIN_TOKEN_PROGRESSBAR,           "pb" , BAR_PARAMS, 0 },
-    { SKIN_TOKEN_VOLUME,                "pv" , BAR_PARAMS, 0 },
-    
-    { SKIN_TOKEN_TRACK_ELAPSED_PERCENT, "px", "", 0 },
-    { SKIN_TOKEN_TRACK_TIME_ELAPSED,    "pc", "", 0 },
-    { SKIN_TOKEN_TRACK_TIME_REMAINING,  "pr", "", 0 },
-    { SKIN_TOKEN_TRACK_LENGTH,          "pt", "", 0 },
-    { SKIN_TOKEN_TRACK_STARTING,        "pS" , "|D", 0 },
-    { SKIN_TOKEN_TRACK_ENDING,          "pE" , "|D", 0 },
-    { SKIN_TOKEN_PLAYLIST_POSITION,     "pp", "", 0 },
-    { SKIN_TOKEN_PLAYLIST_ENTRIES,      "pe", "", 0 },
-    { SKIN_TOKEN_PLAYLIST_NAME,         "pn", "", 0 },
-    { SKIN_TOKEN_PLAYLIST_SHUFFLE,      "ps", "", 0 },
-    
-    { SKIN_TOKEN_DATABASE_PLAYCOUNT,    "rp", "", 0 },
-    { SKIN_TOKEN_DATABASE_RATING,       "rr", "", 0 },
-    { SKIN_TOKEN_DATABASE_AUTOSCORE,    "ra", "", 0 },
-    
-    { SKIN_TOKEN_REPLAYGAIN,            "rg", "", 0 },
-    { SKIN_TOKEN_CROSSFADE,             "xf", "", 0 },
-    
-    { SKIN_TOKEN_HAVE_TUNER,            "tp", "", 0 },
-    { SKIN_TOKEN_TUNER_TUNED,           "tt", "", 0 },
-    { SKIN_TOKEN_TUNER_SCANMODE,        "tm", "", 0 },
-    { SKIN_TOKEN_TUNER_STEREO,          "ts", "", 0 },
-    { SKIN_TOKEN_TUNER_MINFREQ,         "ta", "", 0 },
-    { SKIN_TOKEN_TUNER_MAXFREQ,         "tb", "", 0 },
-    { SKIN_TOKEN_TUNER_CURFREQ,         "tf", "", 0 },
-    { SKIN_TOKEN_PRESET_ID,             "Ti", "", 0 },
-    { SKIN_TOKEN_PRESET_NAME,           "Tn", "", 0 },
-    { SKIN_TOKEN_PRESET_FREQ,           "Tf", "", 0 },
-    { SKIN_TOKEN_PRESET_COUNT,          "Tc", "", 0 },
-    { SKIN_TOKEN_HAVE_RDS,              "tx", "", 0 },
-    { SKIN_TOKEN_RDS_NAME,              "ty", "", 0 },
-    { SKIN_TOKEN_RDS_TEXT,              "tz", "", 0 },
-    
-    { SKIN_TOKEN_SUBLINE_SCROLL,        "s", "", 0 },
+    { SKIN_TOKEN_FILE_BITRATE,          "fb", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_CODEC,            "fc", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_FREQUENCY,        "ff", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_FREQUENCY_KHZ,    "fk", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_NAME_WITH_EXTENSION,  "fm", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_NAME,             "fn", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_PATH,             "fp", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_SIZE,             "fs", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_VBR,              "fv", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_DIRECTORY,        "d"  , "I", SKIN_REFRESH_STATIC },
+    
+    { SKIN_TOKEN_FILE_BITRATE,          "Fb", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_CODEC,            "Fc", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_FREQUENCY,        "Ff", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_FREQUENCY_KHZ,    "Fk", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_NAME_WITH_EXTENSION,  "Fm", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_NAME,             "Fn", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_PATH,             "Fp", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_SIZE,             "Fs", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_VBR,              "Fv", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_FILE_DIRECTORY,        "D"  , "I", SKIN_REFRESH_STATIC },
+    
+    
+    { SKIN_TOKEN_METADATA_ARTIST,       "ia", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_COMPOSER,     "ic", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_ALBUM,        "id", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_ALBUM_ARTIST, "iA", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_GROUPING,     "iG", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_GENRE,        "ig", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_DISC_NUMBER,  "ik", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_TRACK_NUMBER, "in", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_TRACK_TITLE,  "it", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_VERSION,      "iv", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_YEAR,         "iy", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_COMMENT,      "iC", "", SKIN_REFRESH_STATIC },
+    
+    { SKIN_TOKEN_METADATA_ARTIST,       "Ia", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_COMPOSER,     "Ic", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_ALBUM,        "Id", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_ALBUM_ARTIST, "IA", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_GROUPING,     "IG", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_GENRE,        "Ig", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_DISC_NUMBER,  "Ik", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_TRACK_NUMBER, "In", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_TRACK_TITLE,  "It", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_VERSION,      "Iv", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_YEAR,         "Iy", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_METADATA_COMMENT,      "IC", "", SKIN_REFRESH_STATIC },
+    
+    { SKIN_TOKEN_SOUND_PITCH,           "Sp", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_SOUND_SPEED,           "Ss", "", SKIN_REFRESH_DYNAMIC },
+    
+    { SKIN_TOKEN_VLED_HDD,              "lh", "", SKIN_REFRESH_DYNAMIC },
+    
+    { SKIN_TOKEN_MAIN_HOLD,             "mh", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REMOTE_HOLD,           "mr", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REPEAT_MODE,           "mm", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_PLAYBACK_STATUS,       "mp", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_BUTTON_VOLUME,         "mv", "|D", SKIN_REFRESH_DYNAMIC },
+    
+    { SKIN_TOKEN_PEAKMETER,             "pm", "", SKIN_REFRESH_PEAK_METER },
+    { SKIN_TOKEN_PLAYER_PROGRESSBAR,    "pf", "", SKIN_REFRESH_DYNAMIC|SKIN_REFRESH_PLAYER_PROGRESS },
+    { SKIN_TOKEN_PROGRESSBAR,           "pb" , BAR_PARAMS, SKIN_REFRESH_PLAYER_PROGRESS },
+    { SKIN_TOKEN_VOLUME,                "pv" , BAR_PARAMS, SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TRACK_ELAPSED_PERCENT, "px", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TRACK_TIME_ELAPSED,    "pc", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TRACK_TIME_REMAINING,  "pr", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TRACK_LENGTH,          "pt", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_TRACK_STARTING,        "pS" , "|D", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TRACK_ENDING,          "pE" , "|D", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_PLAYLIST_POSITION,     "pp", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_PLAYLIST_ENTRIES,      "pe", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_PLAYLIST_NAME,         "pn", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_PLAYLIST_SHUFFLE,      "ps", "", SKIN_REFRESH_DYNAMIC },
+    
+    { SKIN_TOKEN_DATABASE_PLAYCOUNT,    "rp", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_DATABASE_RATING,       "rr", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_DATABASE_AUTOSCORE,    "ra", "", SKIN_REFRESH_DYNAMIC },
+    
+    { SKIN_TOKEN_REPLAYGAIN,            "rg", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_CROSSFADE,             "xf", "", SKIN_REFRESH_DYNAMIC },
+    
+    { SKIN_TOKEN_HAVE_TUNER,            "tp", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_TUNER_TUNED,           "tt", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TUNER_SCANMODE,        "tm", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TUNER_STEREO,          "ts", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TUNER_MINFREQ,         "ta", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_TUNER_MAXFREQ,         "tb", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_TUNER_CURFREQ,         "tf", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_PRESET_ID,             "Ti", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_PRESET_NAME,           "Tn", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_PRESET_FREQ,           "Tf", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_PRESET_COUNT,          "Tc", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_HAVE_RDS,              "tx", "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_RDS_NAME,              "ty", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_RDS_TEXT,              "tz", "", SKIN_REFRESH_DYNAMIC },
+
+    { SKIN_TOKEN_SUBLINE_SCROLL,        "s", "", SKIN_REFRESH_SCROLL },
     { SKIN_TOKEN_SUBLINE_TIMEOUT,       "t"  , "D", 0 },
+
+    { SKIN_TOKEN_ENABLE_THEME,          "we", "", 0|NOBREAK },
+    { SKIN_TOKEN_DISABLE_THEME,         "wd", "", 0|NOBREAK },
+    { SKIN_TOKEN_DRAW_INBUILTBAR,       "wi", "", SKIN_REFRESH_STATIC|NOBREAK },
     
-    { SKIN_TOKEN_ENABLE_THEME,          "we", "", NOBREAK },
-    { SKIN_TOKEN_DISABLE_THEME,         "wd", "", NOBREAK },
-    { SKIN_TOKEN_DRAW_INBUILTBAR,       "wi", "", NOBREAK },
-    
-    { SKIN_TOKEN_IMAGE_PRELOAD,         "xl", "SFII|I", NOBREAK },
+    { SKIN_TOKEN_IMAGE_PRELOAD,         "xl", "SFII|I", 0|NOBREAK },
     { SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", "S", 0 },
-    { SKIN_TOKEN_IMAGE_PRELOAD,         "x", "SFII", NOBREAK },
+    { SKIN_TOKEN_IMAGE_DISPLAY,         "x", "SFII", 0|NOBREAK },
     
-    { SKIN_TOKEN_LOAD_FONT,             "Fl" , "IF", NOBREAK },
-    { SKIN_TOKEN_ALBUMART_LOAD,         "Cl" , "IIII|ss", NOBREAK },
-    { SKIN_TOKEN_ALBUMART_DISPLAY,      "Cd" , "", 0 },
-    { SKIN_TOKEN_ALBUMART_FOUND,        "C" , "", 0 },
+    { SKIN_TOKEN_LOAD_FONT,             "Fl" , "IF", 0|NOBREAK },
+    { SKIN_TOKEN_ALBUMART_LOAD,         "Cl" , "IIII|ss", 0|NOBREAK },
+    { SKIN_TOKEN_ALBUMART_DISPLAY,      "Cd" , "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_ALBUMART_FOUND,        "C" , "", SKIN_REFRESH_STATIC },
     
-    { SKIN_TOKEN_VIEWPORT_ENABLE,       "Vd" , "S", 0 },
-    { SKIN_TOKEN_UIVIEWPORT_ENABLE,     "VI" , "S", 0 },
+    { SKIN_TOKEN_VIEWPORT_ENABLE,       "Vd" , "S", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_UIVIEWPORT_ENABLE,     "VI" , "S", SKIN_REFRESH_STATIC },
     
-    { SKIN_TOKEN_VIEWPORT_CUSTOMLIST,   "Vp" , "ICC", NOBREAK },
-    { SKIN_TOKEN_LIST_TITLE_TEXT,       "Lt" , "", 0 },
-    { SKIN_TOKEN_LIST_TITLE_ICON,       "Li" , "", 0 },
+    { SKIN_TOKEN_VIEWPORT_CUSTOMLIST,   "Vp" , "ICC", SKIN_REFRESH_DYNAMIC|NOBREAK },
+    { SKIN_TOKEN_LIST_TITLE_TEXT,       "Lt" , "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_LIST_TITLE_ICON,       "Li" , "", SKIN_REFRESH_DYNAMIC },
     
-    { SKIN_TOKEN_VIEWPORT_FGCOLOUR,       "Vf" , "S", NOBREAK },
-    { SKIN_TOKEN_VIEWPORT_BGCOLOUR,       "Vb" , "S", NOBREAK },
+    { SKIN_TOKEN_VIEWPORT_FGCOLOUR,       "Vf" , "s", SKIN_REFRESH_STATIC|NOBREAK },
+    { SKIN_TOKEN_VIEWPORT_BGCOLOUR,       "Vb" , "s", SKIN_REFRESH_STATIC|NOBREAK },
     
     { SKIN_TOKEN_VIEWPORT_CONDITIONAL,  "Vl" , "SIIiii", 0 },
     { SKIN_TOKEN_UIVIEWPORT_LOAD,       "Vi" , "sIIiii", 0 },
     { SKIN_TOKEN_VIEWPORT_LOAD,         "V"  , "IIiii", 0 },
     
-    { SKIN_TOKEN_IMAGE_BACKDROP,        "X"  , "f", NOBREAK },
-    
-    { SKIN_TOKEN_SETTING,               "St" , "S", 0 },
-    { SKIN_TOKEN_TRANSLATEDSTRING,      "Sx" , "S", 0 },
-    { SKIN_TOKEN_LANG_IS_RTL,           "Sr" , "", 0 },
-    
-    { SKIN_TOKEN_LASTTOUCH,             "Tl" , "|D", 0 },
-    { SKIN_TOKEN_CURRENT_SCREEN,        "cs", "", 0 },
-    { SKIN_TOKEN_TOUCHREGION,           "T"  , "IIiiS", NOBREAK },
-    
-    { SKIN_TOKEN_HAVE_RECORDING,        "Rp"   , "", 0 },
-    { SKIN_TOKEN_IS_RECORDING,          "Rr"   , "", 0 },
-    { SKIN_TOKEN_REC_FREQ,              "Rf"   , "", 0 },
-    { SKIN_TOKEN_REC_ENCODER,           "Re"   , "", 0 },
-    { SKIN_TOKEN_REC_BITRATE,           "Rb"   , "", 0 },
-    { SKIN_TOKEN_REC_MONO,              "Rm"   , "", 0 },
-    { SKIN_TOKEN_REC_SECONDS,           "Rs"   , "", 0 },
-    { SKIN_TOKEN_REC_MINUTES,           "Rn"   , "", 0 },
-    { SKIN_TOKEN_REC_HOURS,             "Rh"   , "", 0 },
+    { SKIN_TOKEN_IMAGE_BACKDROP,        "X"  , "f", SKIN_REFRESH_STATIC|NOBREAK },
+    
+    { SKIN_TOKEN_SETTING,               "St" , "S", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TRANSLATEDSTRING,      "Sx" , "S", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_LANG_IS_RTL,           "Sr" , "", SKIN_REFRESH_STATIC },
+    
+    { SKIN_TOKEN_LASTTOUCH,             "Tl" , "|D", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_CURRENT_SCREEN,        "cs", "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_TOUCHREGION,           "T"  , "IIiiS", 0|NOBREAK },
+    
+    { SKIN_TOKEN_HAVE_RECORDING,        "Rp"   , "", SKIN_REFRESH_STATIC },
+    { SKIN_TOKEN_IS_RECORDING,          "Rr"   , "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REC_FREQ,              "Rf"   , "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REC_ENCODER,           "Re"   , "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REC_BITRATE,           "Rb"   , "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REC_MONO,              "Rm"   , "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REC_SECONDS,           "Rs"   , "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REC_MINUTES,           "Rn"   , "", SKIN_REFRESH_DYNAMIC },
+    { SKIN_TOKEN_REC_HOURS,             "Rh"   , "", SKIN_REFRESH_DYNAMIC },
     
     { SKIN_TOKEN_UNKNOWN,                ""   , "", 0 }
     /* Keep this here to mark the end of the table */
diff --git a/lib/skin_parser/tag_table.h b/lib/skin_parser/tag_table.h
index f84d4ac..ae87a93 100644
--- a/lib/skin_parser/tag_table.h
+++ b/lib/skin_parser/tag_table.h
@@ -30,9 +30,25 @@ extern "C"
     /* Flag to tell the renderer not to insert a line break */
 #define NOBREAK 0x1
 
+/* constants used in line_type and as refresh_mode for wps_refresh */
+#define SKIN_REFRESH_SHIFT           16
+#define SKIN_REFRESH_STATIC          (1u<<SKIN_REFRESH_SHIFT)  /* line doesn't change over time */
+#define SKIN_REFRESH_DYNAMIC         (1u<<(SKIN_REFRESH_SHIFT+1))  /* line may change (e.g. time flag) */
+#define SKIN_REFRESH_SCROLL          (1u<<(SKIN_REFRESH_SHIFT+2))  /* line scrolls */
+#define SKIN_REFRESH_PLAYER_PROGRESS (1u<<(SKIN_REFRESH_SHIFT+3))  /* line contains a progress bar */
+#define SKIN_REFRESH_PEAK_METER      (1u<<(SKIN_REFRESH_SHIFT+4))  /* line contains a peak meter */
+#define SKIN_REFRESH_STATUSBAR       (1u<<(SKIN_REFRESH_SHIFT+5))  /* refresh statusbar */
+#define SKIN_RTC_REFRESH             (1u<<(SKIN_REFRESH_SHIFT+6))  /* refresh rtc, convert at parse time */
+#define SKIN_REFRESH_ALL             (0xffffu<<SKIN_REFRESH_SHIFT)   /* to refresh all line types */
+
+/* to refresh only those lines that change over time */
+#define SKIN_REFRESH_NON_STATIC (SKIN_REFRESH_DYNAMIC| \
+                                 SKIN_REFRESH_PLAYER_PROGRESS| \
+                                 SKIN_REFRESH_PEAK_METER)
 
 enum skin_token_type {
     
+    SKIN_TOKEN_NO_TOKEN,
     SKIN_TOKEN_UNKNOWN,
 
     /* Markers */
@@ -91,7 +107,7 @@ enum skin_token_type {
     /* The begin/end values allow us to know if a token is an RTC one.
        New RTC tokens should be added between the markers. */
 
-    SKIN_TOKENs_RTC_BEGIN, /* just the start marker, not an actual token */
+    SKIN_TOKENS_RTC_BEGIN, /* just the start marker, not an actual token */
 
     SKIN_TOKEN_RTC_DAY_OF_MONTH,
     SKIN_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,
diff --git a/tools/checkwps/SOURCES b/tools/checkwps/SOURCES
index 48ed53b..3e12d1f 100644
--- a/tools/checkwps/SOURCES
+++ b/tools/checkwps/SOURCES
@@ -1,7 +1,6 @@
 ../../apps/gui/skin_engine/wps_debug.c
 ../../apps/gui/skin_engine/skin_parser.c
 ../../apps/gui/skin_engine/skin_backdrops.c
-../../apps/gui/skin_engine/skin_buffer.c
 #ifdef HAVE_LCD_BITMAP
 ../../apps/gui/skin_engine/skin_fonts.c
 #endif
diff --git a/tools/checkwps/checkwps.c b/tools/checkwps/checkwps.c
index 94136e0..e00fb72 100644
--- a/tools/checkwps/checkwps.c
+++ b/tools/checkwps/checkwps.c
@@ -26,6 +26,8 @@
 #include "checkwps.h"
 #include "resize.h"
 #include "wps.h"
+#include "skin_buffer.h"
+#include "skin_debug.h"
 #include "skin_engine.h"
 #include "wps_internals.h"
 #include "settings.h"
@@ -237,6 +239,8 @@ int main(int argc, char **argv)
     struct wps_data wps;
     enum screen_type screen = SCREEN_MAIN;
     struct screen* wps_screen;
+    
+    char* buffer = NULL;
 
     /* No arguments -> print the help text
      * Also print the help text upon -h or --help */
@@ -261,13 +265,19 @@ int main(int argc, char **argv)
             wps_verbose_level++;
         }
     }
+    buffer = malloc(SKIN_BUFFER_SIZE);
+    if (!buffer)
+    {
+        printf("mallloc fail!\n");
+        return 1;
+    }
 
-    skin_buffer_init();
+    skin_buffer_init(buffer, SKIN_BUFFER_SIZE);
 #ifdef HAVE_LCD_BITMAP
     skin_font_init();
 #endif
 
-    /* Go through every wps that was thrown at us, error out at the first
+    /* Go through every skin that was thrown at us, error out at the first
      * flawed wps */
     while (argv[filearg]) {
         printf("Checking %s...\n", argv[filearg]);
@@ -285,6 +295,7 @@ int main(int argc, char **argv)
 
         if (!res) {
             printf("WPS parsing failure\n");
+            skin_error_format_message();
             return 3;
         }
 
diff --git a/tools/checkwps/checkwps.make b/tools/checkwps/checkwps.make
index 0db643b..e09ce25 100644
--- a/tools/checkwps/checkwps.make
+++ b/tools/checkwps/checkwps.make
@@ -26,6 +26,6 @@ GCCOPTS+=-D__PCTOOL__
 
 .SECONDEXPANSION: # $$(OBJ) is not populated until after this
 
-$(BUILDDIR)/$(BINARY): $$(OBJ)
+$(BUILDDIR)/$(BINARY): $$(OBJ) $$(SKINLIB)
 	@echo LD $(BINARY)
-	$(SILENT)$(HOSTCC) $(INCLUDE) $(FLAGS) -o $@ $+
+	$(SILENT)$(HOSTCC) $(INCLUDE) $(FLAGS) -L$(BUILDDIR)/lib -lskin_parser -o $@ $+
diff --git a/tools/root.make b/tools/root.make
index e53c452..4e970e1 100644
--- a/tools/root.make
+++ b/tools/root.make
@@ -70,6 +70,7 @@ else ifneq (,$(findstring bootbox,$(APPSDIR)))
   include $(APPSDIR)/bootbox.make
 else ifneq (,$(findstring checkwps,$(APPSDIR)))
   include $(APPSDIR)/checkwps.make
+  include $(ROOTDIR)/lib/skin_parser/skin_parser.make
 else ifneq (,$(findstring database,$(APPSDIR)))
   include $(APPSDIR)/database.make
 else
diff --git a/utils/newparser/skin_render.c b/utils/newparser/skin_render.c
index 1690455..09c9ca1 100644
--- a/utils/newparser/skin_render.c
+++ b/utils/newparser/skin_render.c
@@ -43,7 +43,7 @@ static void do_tags_in_hidden_conditional(struct skin_element* branch)
 {
     /* Tags here are ones which need to be "turned off" or cleared 
      * if they are in a conditional branch which isnt being used */
-    if (branch->type == SUBLINES)
+    if (branch->type == LINE_ALTERNATOR)
     {
         int i;
         for (i=0; i<branch->children_count; i++)
@@ -71,7 +71,6 @@ static void do_tags_in_hidden_conditional(struct skin_element* branch)
                     break;
                 case SKIN_TOKEN_IMAGE_DISPLAY:
                 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
-                    printf("disable image\n");
                     /* clear images */
                     break;
                 default:
@@ -109,7 +108,7 @@ void skin_render_line(struct skin_element* line,
                     do_tags_in_hidden_conditional(child->children[last_value]);
                 last_value = value;
                 
-                if (child->children[value]->type == SUBLINES)
+                if (child->children[value]->type == LINE_ALTERNATOR)
                     func = skin_render_alternator;
                 else if (child->children[value]->type == LINE)
                     func = skin_render_line;
