diff --git a/apps/SOURCES b/apps/SOURCES
index 181c780..d99d608 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -95,12 +95,12 @@ 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/gui/skin_engine/skin_display.c b/apps/gui/skin_engine/skin_display.c
index 3d3a654..1f1c279 100644
--- a/apps/gui/skin_engine/skin_display.c
+++ b/apps/gui/skin_engine/skin_display.c
@@ -124,8 +124,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, struct progressbar *pb)
 {
     struct screen *display = gwps->display;
     struct viewport *vp = pb->vp;
@@ -208,7 +207,7 @@ static void draw_progressbar(struct gui_wps *gwps,
     }
 }
 
-static void draw_playlist_viewer_list(struct gui_wps *gwps,
+void draw_playlist_viewer_list(struct gui_wps *gwps,
                                       struct playlistviewer *viewer)
 {
     struct wps_state *state = gwps->state;
@@ -386,7 +385,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 +394,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)
@@ -422,53 +421,6 @@ 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)
-{
-    if(!gwps || !gwps->data || !gwps->display)
-        return;
-
-    struct wps_data *data = gwps->data;
-    struct screen *display = gwps->display;
-    struct skin_token_list *list = data->images;
-
-    while (list)
-    {
-        struct gui_img *img = (struct gui_img*)list->token->value.data;
-        if (img->loaded)
-        {
-            if (img->display >= 0)
-            {
-                wps_draw_image(gwps, img, img->display);
-            }
-            else if (img->always_display && img->vp == vp)
-            {
-                wps_draw_image(gwps, img, 0);
-            }
-        }
-        list = list->next;
-    }
-#ifdef HAVE_ALBUMART
-    /* now draw the AA */
-    if (data->albumart && data->albumart->vp == vp
-        && data->albumart->draw)
-    {
-        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;
-    }
-#endif
-
-    display->set_drawmode(DRMODE_SOLID);
-}
-
 #else /* HAVE_LCD_CHARCELL */
 
 static bool draw_player_progress(struct gui_wps *gwps)
@@ -613,44 +565,28 @@ 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;
+    int prev_val = conditional->last_value;
 
-    /* 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,
@@ -660,333 +596,17 @@ static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
     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;
+    conditional->last_value = intval -1;
+    return intval -1;
 }
 
 
-/* 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;
-        }
-    }
-}
-
-/* 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)
@@ -1145,6 +765,7 @@ static void write_line(struct screen *display,
 
 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
 {
+#if 0
     struct wps_data *data = gwps->data;
     struct screen *display = gwps->display;
 
@@ -1176,6 +797,7 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
             display->clear_viewport();
         }
 
+        /* FIXME: set each subline to the beginingg
         for (viewport_list = data->viewports;
              viewport_list; viewport_list = viewport_list->next)
         {
@@ -1186,6 +808,7 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
                 line->curr_subline = NULL;
             }
         }
+        * */
     }
 
 #ifdef HAVE_LCD_CHARCELLS
@@ -1379,7 +1002,8 @@ static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
     display->set_viewport(NULL);
 
     display->update();
-
+#endif
+    skin_render(gwps, refresh_mode);
     return true;
 }
 
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c
index b9254d9..4342bda 100644
--- a/apps/gui/skin_engine/skin_parser.c
+++ b/apps/gui/skin_engine/skin_parser.c
@@ -28,6 +28,10 @@
 #include "plugin.h"
 #include "viewport.h"
 
+#include "skin_parser.h"
+#include "skin_buffer.h"
+#include "tag_table.h"
+
 #ifdef __PCTOOL__
 #ifdef WPSEDITOR
 #include "proxy.h"
@@ -71,30 +75,20 @@
 
 #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];
+static bool isdefault(struct skin_tag_parameter *param)
+{
+    return param->type == DEFAULT;
+}
 
-/* number of condtional options in current level */
-static int numoptions[WPS_MAX_COND_LEVEL];
 
-/* line number, debug only */
-static int line_number;
+/* which screen are we parsing for? */
+static enum screen_type curr_screen;
 
 /* the current viewport */
 static struct skin_viewport *curr_vp;
-/* the current line, linked to the above viewport */
-static struct skin_line *curr_line;
+
+struct line *curr_line;
 
 static int follow_lang_direction = 0;
 
@@ -123,7 +117,7 @@ struct wps_tag {
     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);
@@ -152,8 +146,6 @@ 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,
@@ -176,8 +168,6 @@ static int parse_image_special(const char *wps_bufptr,
 #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,
@@ -187,7 +177,7 @@ 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);
+    return 0;
 }
 #define parse_touchregion fulline_tag_not_supported
 #endif
@@ -390,7 +380,7 @@ static const struct wps_tag all_tags[] = {
     { 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_DISPLAY,         "Cd",   WPS_REFRESH_STATIC,  NULL },
     { WPS_TOKEN_ALBUMART_FOUND,           "C", WPS_REFRESH_STATIC, NULL },
 #endif
 
@@ -407,7 +397,7 @@ static const struct wps_tag all_tags[] = {
     { 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      },
+    { WPS_NO_TOKEN,                       "V",   0,    NULL      },
 
 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
     { WPS_TOKEN_IMAGE_BACKDROP,           "X",   0,    parse_image_special },
@@ -478,10 +468,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;
@@ -510,72 +500,6 @@ 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;
-}
-
-static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
-{
-    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)
-    {
-        curr_line->curr_subline->last_token_idx = curr_token - 1;
-        curr_line->next = line;
-        curr_line->curr_subline = NULL;
-    }
-    curr_line = line;
-    if (!vp->lines)
-        vp->lines = line;
-    return true;
-}
-
 #ifdef HAVE_LCD_BITMAP
 
 static int parse_statusbar_enable(const char *wps_bufptr,
@@ -588,7 +512,7 @@ static int parse_statusbar_enable(const char *wps_bufptr,
     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);
+    return 0;
 }
 
 static int parse_statusbar_disable(const char *wps_bufptr,
@@ -601,7 +525,7 @@ static int parse_statusbar_disable(const char *wps_bufptr,
     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);
+    return 0;
 }
 
 static int parse_statusbar_inbuilt(const char *wps_bufptr,
@@ -609,7 +533,7 @@ static int parse_statusbar_inbuilt(const char *wps_bufptr,
 {
     (void)wps_data;
     token->value.data = (void*)&curr_vp->vp;
-    return skip_end_of_line(wps_bufptr);
+    return 0;
 }
 
 static int get_image_id(int c)
@@ -711,7 +635,8 @@ static int parse_image_load(const char *wps_bufptr,
     if (!img)
         return WPS_ERROR_INVALID_PARAM;
     /* save a pointer to the filename */
-    img->bm.data = (char*)filename;
+    img->bm.data = skin_buffer_alloc(strlen(filename));
+    strcpy(img->bm.data, filename);
     img->label = *id;
     img->x = x;
     img->y = y;
@@ -744,7 +669,7 @@ static int parse_image_load(const char *wps_bufptr,
     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 */
@@ -785,9 +710,9 @@ static int parse_font_load(const char *wps_bufptr,
     if (!ptr || strncmp(ptr, ".fnt)", 5))
         return WPS_ERROR_INVALID_PARAM;
     skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
-    skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
+    skinfonts[id-FONT_FIRSTUSERFONT].name = filename; /* FIXME */
 
-    return skip_end_of_line(wps_bufptr);
+    return 0;
 }
 
 
@@ -924,107 +849,10 @@ static int parse_playlistview(const char *wps_bufptr,
     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)
@@ -1049,7 +877,7 @@ static int parse_viewportcolour(const char *wps_bufptr,
     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 */
+     * 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)
@@ -1059,6 +887,9 @@ static int parse_viewportcolour(const char *wps_bufptr,
             wps_data->tokens[i++].type != WPS_TOKEN_VIEWPORT_BGCOLOUR )
             found_text = true;
     }
+    FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME 
+    */
+    found_text = false;
     if (!found_text)
     {
         if (token->type == WPS_TOKEN_VIEWPORT_FGCOLOUR)
@@ -1098,16 +929,20 @@ static int parse_image_special(const char *wps_bufptr,
         if (!strncmp(wps_bufptr, "(d)", 3))
         {
             wps_data->backdrop = NULL;
-            return skip_end_of_line(wps_bufptr);
+            return 0;
         }
         else if (!error)
-            wps_data->backdrop = (char*)wps_bufptr + 1;
+        {
+            char *filename = (char*)wps_bufptr + 1;
+            wps_data->backdrop = skin_buffer_alloc(strlen(filename));
+            strcpy(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 0;
 }
 #endif
 
@@ -1262,15 +1097,17 @@ static int parse_progressbar(const char *wps_bufptr,
         return WPS_ERROR_INVALID_PARAM;
 
     struct viewport *vp = &curr_vp->vp;
-    /* we need to know what line number (viewport relative) this pb is,
+    /* FIXME 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--;
     pb->vp = vp;
@@ -1298,7 +1135,10 @@ static int parse_progressbar(const char *wps_bufptr,
         return WPS_ERROR_INVALID_PARAM;
 
     if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
-        pb->bm.data = (char*)filename;
+    {
+        pb->bm.data = skin_buffer_alloc(strlen(filename));
+        strcpy(pb->bm.data, filename);
+    }
 
     if (LIST_VALUE_PARSED(set, PB_X)) /* x */
         pb->x = x;
@@ -1379,14 +1219,13 @@ static int parse_albumart_load(const char *wps_bufptr,
     struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
     (void)token; /* silence warning */
     if (!aa)
-        return skip_end_of_line(wps_bufptr);
+        return 0;
 
     /* 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; 
@@ -1410,7 +1249,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;
@@ -1470,27 +1308,9 @@ static int parse_albumart_load(const char *wps_bufptr,
     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
@@ -1625,15 +1445,15 @@ 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
-
+#if 0
 /* 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;
+    struct wps_token *token = skin_buffer_alloc(sizeof(*token));
     const struct wps_tag *tag;
     memset(token, 0, sizeof(*token));
 
@@ -1676,7 +1496,7 @@ static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
 
             taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
             token->type = tag->type;
-            curr_line->curr_subline->line_type |= tag->refresh_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)
@@ -1702,7 +1522,7 @@ static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
     skip += taglen;
     return skip;
 }
-
+#endif
 
 /*
  * Returns the number of bytes to skip the buf pointer to access the false
@@ -1763,32 +1583,31 @@ static int find_false_branch(const char *wps_bufptr)
  *    parse the true branch at all
  *
  * */
-static int check_feature_tag(const char *wps_bufptr, const int type)
+static bool check_feature_tag(const int type)
 {
-    (void)wps_bufptr;
     switch (type)
     {
         case WPS_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:
 #ifdef HAVE_RECORDING
-            return 0;
+            return true;
 #else
-            return find_false_branch(wps_bufptr);
+            return false;
 #endif
         case WPS_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;
+            return true;
     }
 }
 
@@ -1798,265 +1617,6 @@ static int check_feature_tag(const char *wps_bufptr, const int type)
         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));
-
-#if defined(DEBUG) || defined(SIMULATOR)
-    if (debug)
-    {
-        print_debug_info(data, fail, line_number);
-        debug_skin_usage();
-    }
-#else
-    (void)debug;
-#endif
-
-    return (fail == 0);
-}
-
 
 /*
  * initial setup of wps_data; does reset everything
@@ -2075,8 +1635,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)
@@ -2086,7 +1644,6 @@ static void skin_data_reset(struct wps_data *wps_data)
     }
 #endif
     wps_data->tokens = NULL;
-    wps_data->num_tokens = 0;
 
 #ifdef HAVE_LCD_BITMAP
     wps_data->peak_meter_enabled = false;
@@ -2194,14 +1751,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;
 
 
@@ -2248,6 +1805,221 @@ 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 = 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;
+    
+    viewport_set_defaults(&skin_vp->vp, curr_screen);
+    
+
+    struct skin_tag_parameter *param = element->params;
+    if (element->params_count == 0) /* default viewport */
+    {
+        if (!data->tree) /* FIXME */
+            data->tree = element;
+        skin_vp->label = VP_DEFAULT_LABEL;
+        curr_vp = skin_vp;
+        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;
+                skin_vp->label = param->data.text[0];
+        }
+        param++;
+    }
+    /* x */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.x = param->data.numeric;
+        if (param->data.numeric < 0)
+            skin_vp->vp.x += display->lcdwidth;
+    }
+    param++;
+    /* y */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.y = param->data.numeric;
+        if (param->data.numeric < 0)
+            skin_vp->vp.y += display->lcdheight;
+    }
+    param++;
+    /* width */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.width = param->data.numeric;
+        if (param->data.numeric < 0)
+            skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
+    }
+    param++;
+    /* height */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.height = param->data.numeric;
+        if (param->data.numeric < 0)
+            skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
+    }
+    param++;
+    /* font */
+    if (!isdefault(param))
+    {
+        skin_vp->vp.font = param->data.numeric;
+    }
+#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
+
+    curr_vp = skin_vp;
+    
+    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;
+    static char text[MAX_PATH];
+    int i;
+    
+    switch (element->type)
+    {
+        case TAG:
+        {
+            const struct wps_tag *tag;
+            token = skin_buffer_alloc(sizeof(struct wps_token));
+            memset(token, 0, sizeof(*token));
+            text[0] = '\0';
+            if (element->params_count > 0)
+            {
+                for(i=0;i<element->params_count;i++)
+                {
+                    struct skin_tag_parameter *p = &element->params[i];
+                    char temp[64]; 
+                    switch (p->type)
+                    {
+                        case NUMERIC:
+                            snprintf(temp, sizeof(temp), "%c%d", 
+                                    i==0?'(':',', p->data.numeric);
+                            break;
+                        case STRING:
+                            snprintf(temp, sizeof(temp), "%c%s", 
+                                    i==0?'(':',', p->data.text);
+                            break;
+                        case DEFAULT:
+                            snprintf(temp, sizeof(temp), "%c-", 
+                                    i==0?'(':',');
+                            break;
+                        case CODE:
+                            /* FIXME */
+                            break;
+                    }
+                    strlcat(text, temp, sizeof(text));
+                }
+                strlcat(text, ")\n", sizeof(text));
+            }
+            /* find what tag we have */
+            for (tag = all_tags;
+                 strncmp(element->tag->name, tag->name, strlen(tag->name)) != 0;
+                 tag++) ;
+
+            token->type = tag->type;
+            //FIXME: curr_line->curr_subline->line_type |= tag->refresh_type;
+            /* if the tag has a special parsing function, we call it */
+            if (tag->parse_func)
+            {
+                int retval = tag->parse_func(text, token, wps_data);
+                if (retval < 0)
+                {
+                    return CALLBACK_ERROR;
+                }
+            }
+            if (follow_lang_direction > 0 )
+                follow_lang_direction--;
+
+            /* 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;
+            
+            element->data = token;
+            break;
+        }
+        case VIEWPORT:
+            return convert_viewport(wps_data, element);
+        case LINE:
+        {
+            struct line *line = skin_buffer_alloc(sizeof(struct line));
+            line->update_mode = WPS_REFRESH_STATIC;
+            line->timeout = DEFAULT_SUBLINE_TIME_MULTIPLIER * TIMEOUT_UNIT;
+            curr_line = line;
+            element->data = line;
+        }
+        break;
+        case LINE_ALTERNATOR:
+        {
+            struct line_alternator *alternator = skin_buffer_alloc(sizeof(struct line_alternator));
+            alternator->current_line = 0;
+            alternator->last_change_tick = current_tick; /* probably wrong */
+            element->data = alternator;
+        }
+        break;
+        case CONDITIONAL:
+        {
+            struct conditional *conditional = skin_buffer_alloc(sizeof(struct conditional));
+            conditional->last_value = -1;
+            conditional->token = element->data;
+            element->data = conditional;
+            const struct wps_tag *tag;
+            for (tag = all_tags;
+                 strncmp(element->tag->name, tag->name, strlen(tag->name)) != 0;
+                 tag++) ;
+            if (tag->type != WPS_NO_TOKEN)
+            {
+                if (!check_feature_tag(tag->type))
+                {
+                    return FEATURE_NOT_AVAILABLE;
+                }
+            }
+            return CALLBACK_OK;
+        }
+        case TEXT:
+            /* nothing to do, the string is already in data */
+        default:
+            break;
+    }
+    return CALLBACK_OK;
+}
 
 /* to setup up the wps-data from a format-buffer (isfile = false)
    from a (wps-)file (isfile = true)*/
@@ -2287,34 +2059,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
     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;
 
     if (isfile)
     {
@@ -2350,8 +2095,11 @@ 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)) {
+    
+    wps_data->backdrop = "-";
+    /* parse the skin source */
+    wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
+    if (!wps_data->tree) {
         skin_data_reset(wps_data);
         return false;
     }
@@ -2396,8 +2144,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..e76c21b
--- /dev/null
+++ b/apps/gui/skin_engine/skin_render.c
@@ -0,0 +1,480 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   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 "skin_engine.h"
+#include "skin_parser.h"
+#include "tag_table.h"
+#include "skin_scan.h"
+#include "radio.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;
+    
+    char *buf;
+    size_t buf_size;
+};
+
+typedef void (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
+void skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
+
+void wps_display_images(struct gui_wps *gwps, struct viewport* vp);
+int evaluate_conditional(struct gui_wps *gwps, struct conditional *conditional, int num_options);
+void draw_progressbar(struct gui_wps *gwps, struct progressbar *pb);
+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);
+
+static bool do_non_text_tags(struct gui_wps *gwps, struct wps_token *token, struct viewport* vp)
+{
+    struct wps_data *data = gwps->data;
+    struct screen *display = gwps->display;
+    switch (token->type)
+    {   
+#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
+        case WPS_TOKEN_VIEWPORT_FGCOLOUR:
+        {
+            struct viewport_colour *col = token->value.data;
+            col->vp->fg_pattern = col->colour;
+        }
+        break;
+        case WPS_TOKEN_VIEWPORT_BGCOLOUR:
+        {
+            struct viewport_colour *col = token->value.data;
+            col->vp->bg_pattern = col->colour;
+        }
+        break;
+#endif
+        case WPS_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 WPS_TOKEN_UIVIEWPORT_ENABLE:
+            sb_set_info_vp(gwps->display->screen_type, 
+                           token->value.i|VP_INFO_LABEL);
+            break;
+        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*)token->value.data;
+            draw_progressbar(gwps, bar);
+        }
+        break;
+        case WPS_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)
+            {
+                if (subimage >= 0)
+                {
+                    wps_draw_image(gwps, img, subimage);
+                }
+                else if (img->always_display && img->vp == vp)
+                {
+                    wps_draw_image(gwps, img, 0);
+                }
+            }
+            break;
+        }
+#ifdef HAVE_ALBUMART
+        case WPS_TOKEN_ALBUMART_DISPLAY:
+            /* now draw the AA */
+            if (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
+                draw_album_art(gwps, handle, false);
+            }
+            break;
+#endif
+        case WPS_TOKEN_DRAW_INBUILTBAR:
+            gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
+                               /*refresh_mode == WPS_REFRESH_ALL*/true,
+                               token->value.data);
+            break;
+        case WPS_VIEWPORT_CUSTOMLIST:
+            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 != 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 == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
+            {
+                clear_image_pos(gwps, find_image(token->value.i&0xFF, data));
+            }
+            else if (token->type == WPS_TOKEN_VOLUMEBAR   ||
+                     token->type == WPS_TOKEN_PROGRESSBAR ||
+                     token->type == WPS_TOKEN_BATTERY_PERCENTBAR )
+            {
+                struct progressbar *bar = (struct progressbar*)token->value.data;
+                bar->draw = false;
+            }
+            else if (token->type == WPS_TOKEN_PEAKMETER)
+            {
+                data->peak_meter_enabled = false;
+            }
+#endif
+#ifdef HAVE_ALBUMART
+            if (data->albumart && token->type == WPS_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 */
+void skin_render_line(struct skin_element* line, struct skin_draw_info *info)
+{
+    int last_value, value;
+    if (line->children_count == 0)
+        return; /* empty line, do nothing */
+    struct skin_element *child = line->children[0];
+    skin_render_func func = skin_render_line;
+    char tempbuf[128];
+    while (child)
+    {
+        tempbuf[0] = '\0';
+        switch (child->type)
+        {
+            case CONDITIONAL:
+                last_value = ((struct conditional*)(child->data))->last_value;
+                value = evaluate_conditional(info->gwps, (struct conditional*)child->data, child->children_count);
+             //   if (last_value != value)
+             //       printf("%s, %d %d\n", child->tag->name, last_value, value);
+                if (value >= child->children_count)
+                    value = child->children_count-1;
+                    
+                /* some tags need handling if they are being disabled.
+                 * %?aa<true> and %?<true|false> need special handlng here */
+                if (value == 0 && last_value == 1 && last_value == child->children_count)
+                {
+                    /* we are in a false branch of a %?aa<true> conditional */
+                    do_tags_in_hidden_conditional(child->children[0], info->gwps);
+                    break;
+                }
+                else if (last_value >= 0 && value != last_value && last_value < child->children_count)
+                    do_tags_in_hidden_conditional(child->children[last_value], info->gwps);
+                last_value = value;
+                
+                if (child->children[value]->type == LINE_ALTERNATOR)
+                    func = skin_render_alternator;
+                else if (child->children[value]->type == LINE)
+                    func = skin_render_line;
+                func(child->children[value], info);
+                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, child->data, &info->skin_vp->vp))
+                {
+                    const char *value = get_token_value(info->gwps, child->data,
+                                              tempbuf, sizeof(tempbuf), NULL);
+                    if (value)
+                    {
+                        strlcat(info->cur_align_start, value, 
+                                info->buf_size - (info->cur_align_start-info->buf));
+                        tempbuf[0] = '\0';
+                    }
+                }
+                break;
+            case TEXT:
+                strlcat(info->cur_align_start, child->data, 
+                        info->buf_size - (info->cur_align_start-info->buf));
+                break;
+            case COMMENT:
+            default:
+                break;
+        }
+
+        child = child->next;
+    }
+}
+
+void skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
+{
+    struct line_alternator *alternator = (struct line_alternator*)element->data;
+    if (info->refresh_type == WPS_REFRESH_ALL)
+    {
+        alternator->current_line = 0;
+        alternator->last_change_tick = current_tick;
+    }
+    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(next_change, current_tick))
+        {
+            alternator->current_line++;
+            if (alternator->current_line >= element->children_count)
+                alternator->current_line = 0;
+            alternator->last_change_tick = current_tick;
+        }
+    }
+    skin_render_line(element->children[alternator->current_line], info);
+}
+
+void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
+                          struct skin_viewport* skin_viewport, unsigned long refresh_type)
+{
+    struct wps_data *data = gwps->data;
+    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;
+    
+    while (line)
+    {
+        linebuf[0] = '\0';
+        info.no_line_break = false;
+        info.line_scrolls = 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;
+        
+        func(line, &info);
+        
+        if (refresh_type)
+        {
+            write_line(display, align, info.line_number, info.line_scrolls);
+        }
+        if (!info.no_line_break)
+            info.line_number++;
+        line = line->next;
+    }
+}
+
+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;
+    
+    refresh_mode = viewport->next? 0 : old_refresh_mode;
+    
+    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 = WPS_REFRESH_ALL;
+            skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
+        }
+
+        if (vp_refresh_mode == WPS_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..c280687 100644
--- a/apps/gui/skin_engine/skin_tokens.c
+++ b/apps/gui/skin_engine/skin_tokens.c
@@ -609,13 +609,6 @@ 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:
diff --git a/apps/gui/skin_engine/wps_internals.h b/apps/gui/skin_engine/wps_internals.h
index 4ef860a..71d39e3 100644
--- a/apps/gui/skin_engine/wps_internals.h
+++ b/apps/gui/skin_engine/wps_internals.h
@@ -82,7 +82,6 @@ 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 */
@@ -157,45 +156,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 +166,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;
@@ -262,24 +221,40 @@ struct playlistviewer {
 #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 */
 };
 #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 +266,11 @@ 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..6b11911 100644
--- a/apps/gui/statusbar-skinned.c
+++ b/apps/gui/statusbar-skinned.c
@@ -27,6 +27,7 @@
 #include "appevents.h"
 #include "screens.h"
 #include "screen_access.h"
+#include "skin_parser.h"
 #include "skin_engine/skin_engine.h"
 #include "skin_engine/wps_internals.h"
 #include "viewport.h"
@@ -50,7 +51,7 @@ bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type s
 {
     int i;
     bool retval = false;
-    for(i=0; i<sb_skin_data[screen].num_tokens; i++)
+    for(i=0; i<0/*FIXME sb_skin_data[screen].num_tokens*/; i++)
     {
         if (sb_skin_data[screen].tokens[i].type == WPS_TOKEN_LIST_TITLE_TEXT)
         {
@@ -75,18 +76,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);
     }
 
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/lib/skin_parser/skin_parser.c b/lib/skin_parser/skin_parser.c
index 2ce41c6..0c09ac6 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>
 
@@ -35,6 +36,11 @@
 int skin_line = 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);
@@ -53,10 +59,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;
 
@@ -126,6 +145,13 @@ 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);
@@ -196,7 +222,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;
@@ -303,9 +328,22 @@ static struct skin_element* skin_parse_line_optional(char** document,
         }
     }
 
+#ifdef ROCKBOX
+    if (callback)
+    {
+        switch (callback(retval, callback_data))
+        {
+            case CALLBACK_ERROR:
+                return NULL;
+            default:
+                break;
+        }
+    }
+#endif
+
     /* Moving up the calling function's pointer */
     *document = cursor;
-
+    
     if(root)
         retval->children[0] = root;
     return retval;
@@ -389,6 +427,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;
@@ -455,6 +500,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;
     }
@@ -613,7 +666,13 @@ static int skin_parse_tag(struct skin_element* element, char** document)
         skin_error(INSUFFICIENT_ARGS);
         return 0;
     }
-    
+#ifdef ROCKBOX
+    if (callback)
+    {
+        if (callback(element, callback_data) == CALLBACK_ERROR)
+            return 0;
+    }
+#endif
     *document = cursor;
 
     return 1;
@@ -671,6 +730,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;
 
@@ -684,14 +751,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)
     {
@@ -720,14 +813,31 @@ 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);
     element->children_count = children;
diff --git a/lib/skin_parser/skin_parser.h b/lib/skin_parser/skin_parser.h
index 126a014..974cde2 100644
--- a/lib/skin_parser/skin_parser.h
+++ b/lib/skin_parser/skin_parser.h
@@ -113,14 +113,27 @@ struct skin_element
     struct skin_element* next;
 };
 
+enum skin_cb_returnvalue
+{
+    CALLBACK_ERROR = -666 /* \m/ o_0  \m/ */,
+    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 79f7162..81d0d25 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)
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/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;
