move the statically allocated tokens array into the skin buffer. this is done with...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob62ee3651284cbc448d1553281014fbe534d95efa
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "file.h"
26 #include "misc.h"
27 #include "plugin.h"
28 #include "viewport.h"
30 #ifdef __PCTOOL__
31 #ifdef WPSEDITOR
32 #include "proxy.h"
33 #include "sysfont.h"
34 #else
35 #include "checkwps.h"
36 #include "audio.h"
37 #define DEBUGF printf
38 #endif /*WPSEDITOR*/
39 #else
40 #include "debug.h"
41 #endif /*__PCTOOL__*/
43 #include <ctype.h>
44 #include <stdbool.h>
45 #include "font.h"
47 #include "wps_internals.h"
48 #include "skin_engine.h"
49 #include "settings.h"
50 #include "settings_list.h"
52 #ifdef HAVE_LCD_BITMAP
53 #include "bmp.h"
54 #endif
56 #include "backdrop.h"
58 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
59 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
61 #define WPS_ERROR_INVALID_PARAM -1
63 /* level of current conditional.
64 -1 means we're not in a conditional. */
65 static int level = -1;
67 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
68 or WPS_TOKEN_CONDITIONAL_START in current level */
69 static int lastcond[WPS_MAX_COND_LEVEL];
71 /* index of the WPS_TOKEN_CONDITIONAL in current level */
72 static int condindex[WPS_MAX_COND_LEVEL];
74 /* number of condtional options in current level */
75 static int numoptions[WPS_MAX_COND_LEVEL];
77 /* the current line in the file */
78 static int line;
80 /* the current viewport */
81 static struct skin_viewport *curr_vp;
83 #ifdef HAVE_LCD_BITMAP
85 #if LCD_DEPTH > 1
86 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
87 #else
88 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
89 #endif
91 #define PROGRESSBAR_BMP MAX_IMAGES
92 #define BACKDROP_BMP (MAX_BITMAPS-1)
94 /* pointers to the bitmap filenames in the WPS source */
95 static const char *bmp_names[MAX_BITMAPS];
97 #endif /* HAVE_LCD_BITMAP */
99 #if defined(DEBUG) || defined(SIMULATOR)
100 /* debugging function */
101 extern void print_debug_info(struct wps_data *data, int fail, int line);
102 #endif
104 static void wps_reset(struct wps_data *data);
106 /* Function for parsing of details for a token. At the moment the
107 function is called, the token type has already been set. The
108 function must fill in the details and possibly add more tokens
109 to the token array. It should return the number of chars that
110 has been consumed.
112 wps_bufptr points to the char following the tag (i.e. where
113 details begin).
114 token is the pointer to the 'main' token being parsed
116 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
117 struct wps_token *token, struct wps_data *wps_data);
119 struct wps_tag {
120 enum wps_token_type type;
121 const char name[3];
122 unsigned char refresh_type;
123 const wps_tag_parse_func parse_func;
125 static int skip_end_of_line(const char *wps_bufptr);
126 /* prototypes of all special parse functions : */
127 static int parse_timeout(const char *wps_bufptr,
128 struct wps_token *token, struct wps_data *wps_data);
129 static int parse_progressbar(const char *wps_bufptr,
130 struct wps_token *token, struct wps_data *wps_data);
131 static int parse_dir_level(const char *wps_bufptr,
132 struct wps_token *token, struct wps_data *wps_data);
133 static int parse_setting(const char *wps_bufptr,
134 struct wps_token *token, struct wps_data *wps_data);
136 #ifdef HAVE_LCD_BITMAP
137 static int parse_viewport_display(const char *wps_bufptr,
138 struct wps_token *token, struct wps_data *wps_data);
139 static int parse_viewport(const char *wps_bufptr,
140 struct wps_token *token, struct wps_data *wps_data);
141 static int parse_statusbar_enable(const char *wps_bufptr,
142 struct wps_token *token, struct wps_data *wps_data);
143 static int parse_statusbar_disable(const char *wps_bufptr,
144 struct wps_token *token, struct wps_data *wps_data);
145 static int parse_image_display(const char *wps_bufptr,
146 struct wps_token *token, struct wps_data *wps_data);
147 static int parse_image_load(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 #endif /*HAVE_LCD_BITMAP */
150 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
151 static int parse_image_special(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 #endif
154 #ifdef HAVE_ALBUMART
155 static int parse_albumart_load(const char *wps_bufptr,
156 struct wps_token *token, struct wps_data *wps_data);
157 static int parse_albumart_conditional(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 #endif /* HAVE_ALBUMART */
160 #ifdef HAVE_TOUCHSCREEN
161 static int parse_touchregion(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 #else
164 static int fulline_tag_not_supported(const char *wps_bufptr,
165 struct wps_token *token, struct wps_data *wps_data)
167 (void)token; (void)wps_data;
168 return skip_end_of_line(wps_bufptr);
170 #define parse_touchregion fulline_tag_not_supported
171 #endif
172 #ifdef CONFIG_RTC
173 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
174 #else
175 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
176 #endif
178 /* array of available tags - those with more characters have to go first
179 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
180 static const struct wps_tag all_tags[] = {
182 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
183 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
184 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
186 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
187 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
188 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
189 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
190 #if CONFIG_CHARGING >= CHARGING_MONITOR
191 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
192 #endif
193 #if CONFIG_CHARGING
194 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
195 #endif
197 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
198 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
217 /* current file */
218 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
228 parse_dir_level },
230 /* next file */
231 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
241 parse_dir_level },
243 /* current metadata */
244 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
257 /* next metadata */
258 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
271 #if (CONFIG_CODEC != MAS3507D)
272 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
273 #endif
275 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
276 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
277 #endif
279 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
281 #ifdef HAS_REMOTE_BUTTON_HOLD
282 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
283 #else
284 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
285 #endif
287 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
288 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
289 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
290 parse_timeout },
292 #ifdef HAVE_LCD_BITMAP
293 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
294 #else
295 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
296 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
297 #endif
298 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
299 parse_progressbar },
301 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
303 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
304 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
305 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
306 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
308 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
309 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
310 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
311 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
313 #ifdef HAVE_TAGCACHE
314 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
315 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
316 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
317 #endif
319 #if CONFIG_CODEC == SWCODEC
320 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
321 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
322 #endif
324 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
325 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
327 #ifdef HAVE_LCD_BITMAP
328 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
329 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
331 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
333 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
334 parse_image_display },
336 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
337 #ifdef HAVE_ALBUMART
338 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
339 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
340 parse_albumart_conditional },
341 #endif
343 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
344 parse_viewport_display },
345 { WPS_NO_TOKEN, "V", 0, parse_viewport },
347 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
348 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
349 #endif
350 #endif
352 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
354 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
355 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
357 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
358 /* the array MUST end with an empty string (first char is \0) */
362 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
363 * chains require the order to be kept.
365 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
367 if (*list == NULL)
368 *list = item;
369 else
371 struct skin_token_list *t = *list;
372 while (t->next)
373 t = t->next;
374 t->next = item;
377 /* create and init a new wpsll item.
378 * passing NULL to token will alloc a new one.
379 * You should only pass NULL for the token when the token type (table above)
380 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
382 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
383 void* token_data)
385 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
386 if (!token)
387 token = skin_buffer_alloc(sizeof(struct wps_token));
388 if (!llitem || !token)
389 return NULL;
390 llitem->next = NULL;
391 llitem->token = token;
392 if (token_data)
393 llitem->token->value.data = token_data;
394 return llitem;
397 /* Returns the number of chars that should be skipped to jump
398 immediately after the first eol, i.e. to the start of the next line */
399 static int skip_end_of_line(const char *wps_bufptr)
401 line++;
402 int skip = 0;
403 while(*(wps_bufptr + skip) != '\n')
404 skip++;
405 return ++skip;
408 /* Starts a new subline in the current line during parsing */
409 static void wps_start_new_subline(struct wps_data *data)
411 data->num_sublines++;
412 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
413 data->lines[data->num_lines].num_sublines++;
416 #ifdef HAVE_LCD_BITMAP
418 static int parse_statusbar_enable(const char *wps_bufptr,
419 struct wps_token *token,
420 struct wps_data *wps_data)
422 (void)token; /* Kill warnings */
423 wps_data->wps_sb_tag = true;
424 wps_data->show_sb_on_wps = true;
425 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
426 if (default_vp->vp.y == 0)
428 default_vp->vp.y = STATUSBAR_HEIGHT;
429 default_vp->vp.height -= STATUSBAR_HEIGHT;
431 return skip_end_of_line(wps_bufptr);
434 static int parse_statusbar_disable(const char *wps_bufptr,
435 struct wps_token *token,
436 struct wps_data *wps_data)
438 (void)token; /* Kill warnings */
439 wps_data->wps_sb_tag = true;
440 wps_data->show_sb_on_wps = false;
441 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
442 if (default_vp->vp.y == STATUSBAR_HEIGHT)
444 default_vp->vp.y = 0;
445 default_vp->vp.height += STATUSBAR_HEIGHT;
447 return skip_end_of_line(wps_bufptr);
450 static int get_image_id(int c)
452 if(c >= 'a' && c <= 'z')
453 return c - 'a';
454 else if(c >= 'A' && c <= 'Z')
455 return c - 'A' + 26;
456 else
457 return -1;
460 static char *get_image_filename(const char *start, const char* bmpdir,
461 char *buf, int buf_size)
463 const char *end = strchr(start, '|');
465 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
467 buf = "\0";
468 return NULL;
471 int bmpdirlen = strlen(bmpdir);
473 strcpy(buf, bmpdir);
474 buf[bmpdirlen] = '/';
475 memcpy( &buf[bmpdirlen + 1], start, end - start);
476 buf[bmpdirlen + 1 + end - start] = 0;
478 return buf;
481 static int parse_image_display(const char *wps_bufptr,
482 struct wps_token *token,
483 struct wps_data *wps_data)
485 char label = wps_bufptr[0];
486 int subimage;
487 struct gui_img *img;;
489 /* sanity check */
490 img = find_image(label, wps_data);
491 if (!img)
493 token->value.i = label; /* do debug works */
494 return WPS_ERROR_INVALID_PARAM;
497 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
499 if (subimage >= img->num_subimages)
500 return WPS_ERROR_INVALID_PARAM;
502 /* Store sub-image number to display in high bits */
503 token->value.i = label | (subimage << 8);
504 return 2; /* We have consumed 2 bytes */
505 } else {
506 token->value.i = label;
507 return 1; /* We have consumed 1 byte */
511 static int parse_image_load(const char *wps_bufptr,
512 struct wps_token *token,
513 struct wps_data *wps_data)
515 const char *ptr = wps_bufptr;
516 const char *pos;
517 const char* filename;
518 const char* id;
519 const char *newline;
520 int x,y;
521 struct gui_img *img;
523 /* format: %x|n|filename.bmp|x|y|
524 or %xl|n|filename.bmp|x|y|
525 or %xl|n|filename.bmp|x|y|num_subimages|
528 if (*ptr != '|')
529 return WPS_ERROR_INVALID_PARAM;
531 ptr++;
533 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
534 return WPS_ERROR_INVALID_PARAM;
536 /* Check there is a terminating | */
537 if (*ptr != '|')
538 return WPS_ERROR_INVALID_PARAM;
540 /* check the image number and load state */
541 if(find_image(*id, wps_data))
543 /* Invalid image ID */
544 return WPS_ERROR_INVALID_PARAM;
546 img = skin_buffer_alloc(sizeof(struct gui_img));
547 if (!img)
548 return WPS_ERROR_INVALID_PARAM;
549 /* save a pointer to the filename */
550 img->bm.data = (char*)filename;
551 img->label = *id;
552 img->x = x;
553 img->y = y;
554 img->num_subimages = 1;
555 img->always_display = false;
557 /* save current viewport */
558 img->vp = &curr_vp->vp;
560 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
562 img->always_display = true;
564 else
566 /* Parse the (optional) number of sub-images */
567 ptr++;
568 newline = strchr(ptr, '\n');
569 pos = strchr(ptr, '|');
570 if (pos && pos < newline)
571 img->num_subimages = atoi(ptr);
573 if (img->num_subimages <= 0)
574 return WPS_ERROR_INVALID_PARAM;
576 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
577 if (!item)
578 return WPS_ERROR_INVALID_PARAM;
579 add_to_ll_chain(&wps_data->images, item);
581 /* Skip the rest of the line */
582 return skip_end_of_line(wps_bufptr);
585 static int parse_viewport_display(const char *wps_bufptr,
586 struct wps_token *token,
587 struct wps_data *wps_data)
589 (void)wps_data;
590 char letter = wps_bufptr[0];
592 if (letter < 'a' || letter > 'z')
594 /* invalid viewport tag */
595 return WPS_ERROR_INVALID_PARAM;
597 token->value.i = letter;
598 return 1;
601 static int parse_viewport(const char *wps_bufptr,
602 struct wps_token *token,
603 struct wps_data *wps_data)
605 (void)token; /* Kill warnings */
606 const char *ptr = wps_bufptr;
608 const int screen =
609 #ifdef HAVE_REMOTE_LCD
610 wps_data->remote_wps ? SCREEN_REMOTE :
611 #endif
612 SCREEN_MAIN;
614 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
616 /* check for the optional letter to signify its a hideable viewport */
617 /* %Vl|<label>|<rest of tags>| */
618 skin_vp->hidden_flags = 0;
619 skin_vp->label = VP_NO_LABEL;
620 skin_vp->pb = NULL;
622 if (*ptr == 'l')
624 if (*(ptr+1) == '|')
626 char label = *(ptr+2);
627 if (label >= 'a' && label <= 'z')
629 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
630 skin_vp->label = label;
632 else
633 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
634 ptr += 3;
637 if (*ptr != '|')
638 return WPS_ERROR_INVALID_PARAM;
640 ptr++;
641 struct viewport *vp = &skin_vp->vp;
642 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
644 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
645 return WPS_ERROR_INVALID_PARAM;
647 /* Check for trailing | */
648 if (*ptr != '|')
649 return WPS_ERROR_INVALID_PARAM;
651 curr_vp->last_line = wps_data->num_lines - 1;
653 skin_vp->first_line = wps_data->num_lines;
655 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
657 wps_data->lines[wps_data->num_lines].first_subline_idx =
658 wps_data->num_sublines;
660 wps_data->sublines[wps_data->num_sublines].first_token_idx =
661 wps_data->num_tokens;
664 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
665 if (!list)
666 return WPS_ERROR_INVALID_PARAM;
667 add_to_ll_chain(&wps_data->viewports, list);
668 curr_vp = skin_vp;
669 /* Skip the rest of the line */
670 return skip_end_of_line(wps_bufptr);
673 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
674 static int parse_image_special(const char *wps_bufptr,
675 struct wps_token *token,
676 struct wps_data *wps_data)
678 (void)wps_data; /* kill warning */
679 (void)token;
680 const char *pos = NULL;
681 const char *newline;
683 pos = strchr(wps_bufptr + 1, '|');
684 newline = strchr(wps_bufptr, '\n');
686 if (pos > newline)
687 return WPS_ERROR_INVALID_PARAM;
688 #if LCD_DEPTH > 1
689 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
691 /* format: %X|filename.bmp| */
692 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
694 #endif
696 /* Skip the rest of the line */
697 return skip_end_of_line(wps_bufptr);
699 #endif
701 #endif /* HAVE_LCD_BITMAP */
703 static int parse_setting(const char *wps_bufptr,
704 struct wps_token *token,
705 struct wps_data *wps_data)
707 (void)wps_data;
708 const char *ptr = wps_bufptr;
709 const char *end;
710 int i;
712 /* Find the setting's cfg_name */
713 if (*ptr != '|')
714 return WPS_ERROR_INVALID_PARAM;
715 ptr++;
716 end = strchr(ptr,'|');
717 if (!end)
718 return WPS_ERROR_INVALID_PARAM;
720 /* Find the setting */
721 for (i=0; i<nb_settings; i++)
722 if (settings[i].cfg_name &&
723 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
724 /* prevent matches on cfg_name prefixes */
725 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
726 break;
727 if (i == nb_settings)
728 return WPS_ERROR_INVALID_PARAM;
730 /* Store the setting number */
731 token->value.i = i;
733 /* Skip the rest of the line */
734 return end-ptr+2;
738 static int parse_dir_level(const char *wps_bufptr,
739 struct wps_token *token,
740 struct wps_data *wps_data)
742 char val[] = { *wps_bufptr, '\0' };
743 token->value.i = atoi(val);
744 (void)wps_data; /* Kill warnings */
745 return 1;
748 static int parse_timeout(const char *wps_bufptr,
749 struct wps_token *token,
750 struct wps_data *wps_data)
752 int skip = 0;
753 int val = 0;
754 bool have_point = false;
755 bool have_tenth = false;
757 (void)wps_data; /* Kill the warning */
759 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
761 if (*wps_bufptr != '.')
763 val *= 10;
764 val += *wps_bufptr - '0';
765 if (have_point)
767 have_tenth = true;
768 wps_bufptr++;
769 skip++;
770 break;
773 else
774 have_point = true;
776 wps_bufptr++;
777 skip++;
780 if (have_tenth == false)
781 val *= 10;
783 if (val == 0 && skip == 0)
785 /* decide what to do if no value was specified */
786 switch (token->type)
788 case WPS_TOKEN_SUBLINE_TIMEOUT:
789 return -1;
790 case WPS_TOKEN_BUTTON_VOLUME:
791 val = 10;
792 break;
795 token->value.i = val;
797 return skip;
800 static int parse_progressbar(const char *wps_bufptr,
801 struct wps_token *token,
802 struct wps_data *wps_data)
804 /* %pb or %pb|filename|x|y|width|height|
805 using - for any of the params uses "sane" values */
806 #ifdef HAVE_LCD_BITMAP
807 enum {
808 PB_FILENAME = 0,
809 PB_X,
810 PB_Y,
811 PB_WIDTH,
812 PB_HEIGHT
814 const char *filename;
815 int x, y, height, width;
816 uint32_t set = 0;
817 const char *ptr = wps_bufptr;
818 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
819 struct skin_token_list *item = new_skin_token_list_item(token, pb);
821 if (!pb || !item)
822 return WPS_ERROR_INVALID_PARAM;
824 struct viewport *vp = &curr_vp->vp;
825 #ifndef __PCTOOL__
826 int font_height = font_get(vp->font)->height;
827 #else
828 int font_height = 8;
829 #endif
830 int line_num = wps_data->num_lines - curr_vp->first_line;
832 pb->have_bitmap_pb = false;
833 pb->bm.data = NULL; /* no bitmap specified */
835 if (*wps_bufptr != '|') /* regular old style */
837 pb->x = 0;
838 pb->width = vp->width;
839 pb->height = SYSFONT_HEIGHT-2;
840 pb->y = -line_num - 1; /* Will be computed during the rendering */
842 curr_vp->pb = pb;
843 add_to_ll_chain(&wps_data->progressbars, item);
844 return 0;
846 ptr = wps_bufptr + 1;
848 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
849 &x, &y, &width, &height)))
850 return WPS_ERROR_INVALID_PARAM;
852 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
853 pb->bm.data = (char*)filename;
855 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
856 pb->x = x;
857 else
858 pb->x = vp->x;
860 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
862 /* A zero width causes a divide-by-zero error later, so reject it */
863 if (width == 0)
864 return WPS_ERROR_INVALID_PARAM;
866 pb->width = width;
868 else
869 pb->width = vp->width - pb->x;
871 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
873 /* A zero height makes no sense - reject it */
874 if (height == 0)
875 return WPS_ERROR_INVALID_PARAM;
877 pb->height = height;
879 else
880 pb->height = font_height;
882 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
883 pb->y = y;
884 else
885 pb->y = -line_num - 1; /* Will be computed during the rendering */
887 curr_vp->pb = pb;
888 add_to_ll_chain(&wps_data->progressbars, item);
890 /* Skip the rest of the line */
891 return skip_end_of_line(wps_bufptr)-1;
892 #else
893 (void)token;
895 if (*(wps_bufptr-1) == 'f')
896 wps_data->full_line_progressbar = true;
897 else
898 wps_data->full_line_progressbar = false;
900 return 0;
902 #endif
905 #ifdef HAVE_ALBUMART
906 static int parse_albumart_load(const char *wps_bufptr,
907 struct wps_token *token,
908 struct wps_data *wps_data)
910 const char *_pos, *newline;
911 bool parsing;
912 (void)token; /* silence warning */
914 /* reset albumart info in wps */
915 wps_data->albumart_max_width = -1;
916 wps_data->albumart_max_height = -1;
917 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
918 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
920 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
922 newline = strchr(wps_bufptr, '\n');
924 /* initial validation and parsing of x and y components */
925 if (*wps_bufptr != '|')
926 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
928 _pos = wps_bufptr + 1;
929 if (!isdigit(*_pos))
930 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
931 wps_data->albumart_x = atoi(_pos);
933 _pos = strchr(_pos, '|');
934 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
935 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
937 wps_data->albumart_y = atoi(_pos);
939 _pos = strchr(_pos, '|');
940 if (!_pos || _pos > newline)
941 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
942 e.g. %Cl|7|59\n */
944 /* parsing width field */
945 parsing = true;
946 while (parsing)
948 /* apply each modifier in turn */
949 ++_pos;
950 switch (*_pos)
952 case 'l':
953 case 'L':
954 case '+':
955 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
956 break;
957 case 'c':
958 case 'C':
959 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
960 break;
961 case 'r':
962 case 'R':
963 case '-':
964 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
965 break;
966 case 'd':
967 case 'D':
968 case 'i':
969 case 'I':
970 case 's':
971 case 'S':
972 /* simply ignored */
973 break;
974 default:
975 parsing = false;
976 break;
979 /* extract max width data */
980 if (*_pos != '|')
982 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
983 return WPS_ERROR_INVALID_PARAM;
985 wps_data->albumart_max_width = atoi(_pos);
987 _pos = strchr(_pos, '|');
988 if (!_pos || _pos > newline)
989 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
990 e.g. %Cl|7|59|200\n */
993 /* parsing height field */
994 parsing = true;
995 while (parsing)
997 /* apply each modifier in turn */
998 ++_pos;
999 switch (*_pos)
1001 case 't':
1002 case 'T':
1003 case '-':
1004 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1005 break;
1006 case 'c':
1007 case 'C':
1008 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1009 break;
1010 case 'b':
1011 case 'B':
1012 case '+':
1013 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1014 break;
1015 case 'd':
1016 case 'D':
1017 case 'i':
1018 case 'I':
1019 case 's':
1020 case 'S':
1021 /* simply ignored */
1022 break;
1023 default:
1024 parsing = false;
1025 break;
1028 /* extract max height data */
1029 if (*_pos != '|')
1031 if (!isdigit(*_pos))
1032 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1034 wps_data->albumart_max_height = atoi(_pos);
1036 _pos = strchr(_pos, '|');
1037 if (!_pos || _pos > newline)
1038 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1039 e.g. %Cl|7|59|200|200\n */
1042 /* if we got here, we parsed everything ok .. ! */
1043 if (wps_data->albumart_max_width < 0)
1044 wps_data->albumart_max_width = 0;
1045 else if (wps_data->albumart_max_width > LCD_WIDTH)
1046 wps_data->albumart_max_width = LCD_WIDTH;
1048 if (wps_data->albumart_max_height < 0)
1049 wps_data->albumart_max_height = 0;
1050 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1051 wps_data->albumart_max_height = LCD_HEIGHT;
1053 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1055 /* Skip the rest of the line */
1056 return skip_end_of_line(wps_bufptr);
1059 static int parse_albumart_conditional(const char *wps_bufptr,
1060 struct wps_token *token,
1061 struct wps_data *wps_data)
1063 struct wps_token *prevtoken = token;
1064 --prevtoken;
1065 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1067 /* This %C is part of a %?C construct.
1068 It's either %?C<blah> or %?Cn<blah> */
1069 token->type = WPS_TOKEN_ALBUMART_FOUND;
1070 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1072 token->next = true;
1073 return 1;
1075 else if (*wps_bufptr == '<')
1077 return 0;
1079 else
1081 token->type = WPS_NO_TOKEN;
1082 return 0;
1085 else
1087 /* This %C tag is in a conditional construct. */
1088 wps_data->albumart_cond_index = condindex[level];
1089 return 0;
1092 #endif /* HAVE_ALBUMART */
1094 #ifdef HAVE_TOUCHSCREEN
1096 struct touchaction {char* s; int action;};
1097 static struct touchaction touchactions[] = {
1098 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1099 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1100 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1101 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1102 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1103 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1104 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1106 static int parse_touchregion(const char *wps_bufptr,
1107 struct wps_token *token, struct wps_data *wps_data)
1109 (void)token;
1110 unsigned i, imax;
1111 struct touchregion *region = NULL;
1112 const char *ptr = wps_bufptr;
1113 const char *action;
1114 const char pb_string[] = "progressbar";
1115 const char vol_string[] = "volume";
1116 int x,y,w,h;
1118 /* format: %T|x|y|width|height|action|
1119 * if action starts with & the area must be held to happen
1120 * action is one of:
1121 * play - play/pause playback
1122 * stop - stop playback, exit the wps
1123 * prev - prev track
1124 * next - next track
1125 * ffwd - seek forward
1126 * rwd - seek backwards
1127 * menu - go back to the main menu
1128 * browse - go back to the file/db browser
1129 * shuffle - toggle shuffle mode
1130 * repmode - cycle the repeat mode
1131 * quickscreen - go into the quickscreen
1132 * contextmenu - open the context menu
1133 * playlist - go into the playlist
1134 * pitch - go into the pitchscreen
1138 if (*ptr != '|')
1139 return WPS_ERROR_INVALID_PARAM;
1140 ptr++;
1142 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1143 return WPS_ERROR_INVALID_PARAM;
1145 /* Check there is a terminating | */
1146 if (*ptr != '|')
1147 return WPS_ERROR_INVALID_PARAM;
1149 region = skin_buffer_alloc(sizeof(struct touchregion));
1150 if (!region)
1151 return WPS_ERROR_INVALID_PARAM;
1153 /* should probably do some bounds checking here with the viewport... but later */
1154 region->action = ACTION_NONE;
1155 region->x = x;
1156 region->y = y;
1157 region->width = w;
1158 region->height = h;
1159 region->wvp = curr_vp;
1161 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1162 && *(action + sizeof(pb_string)-1) == '|')
1163 region->type = WPS_TOUCHREGION_SCROLLBAR;
1164 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1165 && *(action + sizeof(vol_string)-1) == '|')
1166 region->type = WPS_TOUCHREGION_VOLUME;
1167 else
1169 region->type = WPS_TOUCHREGION_ACTION;
1171 if (*action == '&')
1173 action++;
1174 region->repeat = true;
1176 else
1177 region->repeat = false;
1179 i = 0;
1180 imax = ARRAYLEN(touchactions);
1181 while ((region->action == ACTION_NONE) &&
1182 (i < imax))
1184 /* try to match with one of our touchregion screens */
1185 int len = strlen(touchactions[i].s);
1186 if (!strncmp(touchactions[i].s, action, len)
1187 && *(action+len) == '|')
1188 region->action = touchactions[i].action;
1189 i++;
1191 if (region->action == ACTION_NONE)
1192 return WPS_ERROR_INVALID_PARAM;
1194 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1195 if (!item)
1196 return WPS_ERROR_INVALID_PARAM;
1197 add_to_ll_chain(&wps_data->touchregions, item);
1198 return skip_end_of_line(wps_bufptr);
1200 #endif
1202 /* Parse a generic token from the given string. Return the length read */
1203 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1205 int skip = 0, taglen = 0, ret;
1206 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1207 const struct wps_tag *tag;
1209 switch(*wps_bufptr)
1212 case '%':
1213 case '<':
1214 case '|':
1215 case '>':
1216 case ';':
1217 case '#':
1218 /* escaped characters */
1219 token->type = WPS_TOKEN_CHARACTER;
1220 token->value.c = *wps_bufptr;
1221 taglen = 1;
1222 wps_data->num_tokens++;
1223 break;
1225 case '?':
1226 /* conditional tag */
1227 token->type = WPS_TOKEN_CONDITIONAL;
1228 level++;
1229 condindex[level] = wps_data->num_tokens;
1230 numoptions[level] = 1;
1231 wps_data->num_tokens++;
1232 ret = parse_token(wps_bufptr + 1, wps_data);
1233 if (ret < 0) return ret;
1234 taglen = 1 + ret;
1235 break;
1237 default:
1238 /* find what tag we have */
1239 for (tag = all_tags;
1240 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1241 tag++) ;
1243 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1244 token->type = tag->type;
1245 wps_data->sublines[wps_data->num_sublines].line_type |=
1246 tag->refresh_type;
1248 /* if the tag has a special parsing function, we call it */
1249 if (tag->parse_func)
1251 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1252 if (ret < 0) return ret;
1253 skip += ret;
1256 /* Some tags we don't want to save as tokens */
1257 if (tag->type == WPS_NO_TOKEN)
1258 break;
1260 /* tags that start with 'F', 'I' or 'D' are for the next file */
1261 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1262 *(tag->name) == 'D')
1263 token->next = true;
1265 wps_data->num_tokens++;
1266 break;
1269 skip += taglen;
1270 return skip;
1273 /* Parses the WPS.
1274 data is the pointer to the structure where the parsed WPS should be stored.
1275 It is initialised.
1276 wps_bufptr points to the string containing the WPS tags */
1277 #define TOKEN_BLOCK_SIZE 128
1278 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1280 if (!data || !wps_bufptr || !*wps_bufptr)
1281 return false;
1282 enum wps_parse_error fail = PARSE_OK;
1283 int ret;
1284 int max_tokens = TOKEN_BLOCK_SIZE;
1285 size_t buf_free = 0;
1286 line = 1;
1287 level = -1;
1289 /* allocate enough RAM for a reasonable skin, grow as needed.
1290 * Free any used RAM before loading the images to be 100% RAM efficient */
1291 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1292 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1293 return false;
1294 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1295 data->num_tokens = 0;
1297 while(*wps_bufptr && !fail
1298 && data->num_lines < WPS_MAX_LINES)
1300 /* first make sure there is enough room for tokens */
1301 if (max_tokens -1 == data->num_tokens)
1303 int extra_tokens = TOKEN_BLOCK_SIZE;
1304 size_t needed = extra_tokens * sizeof(struct wps_token);
1305 /* do some smarts here to grow the array a bit */
1306 if (skin_buffer_freespace() < needed)
1308 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1309 break;
1311 skin_buffer_increment(needed, false);
1312 max_tokens += extra_tokens;
1315 switch(*wps_bufptr++)
1318 /* Regular tag */
1319 case '%':
1320 if ((ret = parse_token(wps_bufptr, data)) < 0)
1322 fail = PARSE_FAIL_COND_INVALID_PARAM;
1323 break;
1325 else if (level >= WPS_MAX_COND_LEVEL - 1)
1327 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1328 break;
1330 wps_bufptr += ret;
1331 break;
1333 /* Alternating sublines separator */
1334 case ';':
1335 if (level >= 0) /* there are unclosed conditionals */
1337 fail = PARSE_FAIL_UNCLOSED_COND;
1338 break;
1341 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1342 wps_start_new_subline(data);
1343 else
1344 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1346 break;
1348 /* Conditional list start */
1349 case '<':
1350 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1352 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1353 break;
1356 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1357 lastcond[level] = data->num_tokens++;
1358 break;
1360 /* Conditional list end */
1361 case '>':
1362 if (level < 0) /* not in a conditional, invalid char */
1364 fail = PARSE_FAIL_INVALID_CHAR;
1365 break;
1368 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1369 if (lastcond[level])
1370 data->tokens[lastcond[level]].value.i = data->num_tokens;
1371 else
1373 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1374 break;
1377 lastcond[level] = 0;
1378 data->num_tokens++;
1379 data->tokens[condindex[level]].value.i = numoptions[level];
1380 level--;
1381 break;
1383 /* Conditional list option */
1384 case '|':
1385 if (level < 0) /* not in a conditional, invalid char */
1387 fail = PARSE_FAIL_INVALID_CHAR;
1388 break;
1391 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1392 if (lastcond[level])
1393 data->tokens[lastcond[level]].value.i = data->num_tokens;
1394 else
1396 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1397 break;
1400 lastcond[level] = data->num_tokens;
1401 numoptions[level]++;
1402 data->num_tokens++;
1403 break;
1405 /* Comment */
1406 case '#':
1407 if (level >= 0) /* there are unclosed conditionals */
1409 fail = PARSE_FAIL_UNCLOSED_COND;
1410 break;
1413 wps_bufptr += skip_end_of_line(wps_bufptr);
1414 break;
1416 /* End of this line */
1417 case '\n':
1418 if (level >= 0) /* there are unclosed conditionals */
1420 fail = PARSE_FAIL_UNCLOSED_COND;
1421 break;
1424 line++;
1425 wps_start_new_subline(data);
1426 data->num_lines++; /* Start a new line */
1428 if ((data->num_lines < WPS_MAX_LINES) &&
1429 (data->num_sublines < WPS_MAX_SUBLINES))
1431 data->lines[data->num_lines].first_subline_idx =
1432 data->num_sublines;
1434 data->sublines[data->num_sublines].first_token_idx =
1435 data->num_tokens;
1438 break;
1440 /* String */
1441 default:
1443 unsigned int len = 1;
1444 const char *string_start = wps_bufptr - 1;
1446 /* find the length of the string */
1447 while (*wps_bufptr && *wps_bufptr != '#' &&
1448 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1449 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1450 *wps_bufptr != '|' && *wps_bufptr != '\n')
1452 wps_bufptr++;
1453 len++;
1456 /* look if we already have that string */
1457 char *str;
1458 bool found = false;
1459 struct skin_token_list *list = data->strings;
1460 while (list)
1462 str = (char*)list->token->value.data;
1463 found = (strlen(str) == len &&
1464 strncmp(string_start, str, len) == 0);
1465 if (found)
1466 break; /* break here because the list item is
1467 used if its found */
1468 list = list->next;
1470 /* If a matching string is found, found is true and i is
1471 the index of the string. If not, found is false */
1473 if (!found)
1475 /* new string */
1476 str = (char*)skin_buffer_alloc(len+1);
1477 if (!str)
1479 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1480 break;
1482 strlcpy(str, string_start, len+1);
1483 struct skin_token_list *item =
1484 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1485 if(!item)
1487 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1488 break;
1490 add_to_ll_chain(&data->strings, item);
1492 else
1494 /* another occurrence of an existing string */
1495 data->tokens[data->num_tokens].value.data = list->token->value.data;
1497 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1498 data->num_tokens++;
1500 break;
1504 if (!fail && level >= 0) /* there are unclosed conditionals */
1505 fail = PARSE_FAIL_UNCLOSED_COND;
1507 if (*wps_bufptr && !fail)
1508 /* one of the limits of the while loop was exceeded */
1509 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1511 /* Success! */
1512 /* freeup unused tokens */
1513 skin_buffer_free_from_front(sizeof(struct wps_token)
1514 * (max_tokens - data->num_tokens));
1515 curr_vp->last_line = data->num_lines - 1;
1517 #if defined(DEBUG) || defined(SIMULATOR)
1518 print_debug_info(data, fail, line);
1519 #endif
1521 return (fail == 0);
1524 static void wps_reset(struct wps_data *data)
1526 #ifdef HAVE_REMOTE_LCD
1527 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1528 #endif
1529 memset(data, 0, sizeof(*data));
1530 skin_data_init(data);
1531 #ifdef HAVE_REMOTE_LCD
1532 data->remote_wps = rwps;
1533 #endif
1536 #ifdef HAVE_LCD_BITMAP
1537 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1539 (void)wps_data; /* only needed for remote targets */
1540 bool loaded = false;
1541 char img_path[MAX_PATH];
1542 get_image_filename(bitmap->data, bmpdir,
1543 img_path, sizeof(img_path));
1545 /* load the image */
1546 int format;
1547 #ifdef HAVE_REMOTE_LCD
1548 if (wps_data->remote_wps)
1549 format = FORMAT_ANY|FORMAT_REMOTE;
1550 else
1551 #endif
1552 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1554 size_t max_buf;
1555 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1556 bitmap->data = imgbuf;
1557 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1559 if (ret > 0)
1561 skin_buffer_increment(ret, true);
1562 loaded = true;
1564 else
1566 /* Abort if we can't load an image */
1567 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1568 loaded = false;
1570 return loaded;
1573 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1575 struct skin_token_list *list;
1576 /* do the progressbars */
1577 list = wps_data->progressbars;
1578 while (list)
1580 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1581 if (pb->bm.data)
1583 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1585 list = list->next;
1587 /* regular images */
1588 list = wps_data->images;
1589 while (list)
1591 struct gui_img *img = (struct gui_img*)list->token->value.data;
1592 if (img->bm.data)
1594 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1595 if (img->loaded)
1596 img->subimage_height = img->bm.height / img->num_subimages;
1598 list = list->next;
1601 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1602 if (bmp_names[BACKDROP_BMP])
1604 int screen = SCREEN_MAIN;
1605 char img_path[MAX_PATH];
1606 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1607 img_path, sizeof(img_path));
1608 #if defined(HAVE_REMOTE_LCD)
1609 /* We only need to check LCD type if there is a remote LCD */
1610 if (wps_data->remote_wps)
1611 screen = SCREEN_REMOTE;
1612 #endif
1613 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1615 #endif /* has backdrop support */
1617 /* If we got here, everything was OK */
1618 return true;
1621 #endif /* HAVE_LCD_BITMAP */
1623 /* to setup up the wps-data from a format-buffer (isfile = false)
1624 from a (wps-)file (isfile = true)*/
1625 bool skin_data_load(struct wps_data *wps_data,
1626 struct screen *display,
1627 const char *buf,
1628 bool isfile)
1630 #ifdef HAVE_ALBUMART
1631 struct mp3entry *curtrack;
1632 long offset;
1633 int status;
1634 int wps_uses_albumart = wps_data->wps_uses_albumart;
1635 int albumart_max_height = wps_data->albumart_max_height;
1636 int albumart_max_width = wps_data->albumart_max_width;
1637 #endif
1638 if (!wps_data || !buf)
1639 return false;
1641 wps_reset(wps_data);
1643 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1644 if (!curr_vp)
1645 return false;
1646 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1647 if (!list)
1648 return false;
1649 add_to_ll_chain(&wps_data->viewports, list);
1652 /* Initialise the first (default) viewport */
1653 curr_vp->label = VP_DEFAULT_LABEL;
1654 curr_vp->vp.x = 0;
1655 curr_vp->vp.width = display->getwidth();
1656 curr_vp->vp.height = display->getheight();
1657 curr_vp->pb = NULL;
1658 curr_vp->hidden_flags = 0;
1659 switch (statusbar_position(display->screen_type))
1661 case STATUSBAR_OFF:
1662 curr_vp->vp.y = 0;
1663 break;
1664 case STATUSBAR_TOP:
1665 curr_vp->vp.y = STATUSBAR_HEIGHT;
1666 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1667 break;
1668 case STATUSBAR_BOTTOM:
1669 curr_vp->vp.y = 0;
1670 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1671 break;
1673 #ifdef HAVE_LCD_BITMAP
1674 curr_vp->vp.font = FONT_UI;
1675 curr_vp->vp.drawmode = DRMODE_SOLID;
1676 #endif
1677 #if LCD_DEPTH > 1
1678 if (display->depth > 1)
1680 curr_vp->vp.fg_pattern = display->get_foreground();
1681 curr_vp->vp.bg_pattern = display->get_background();
1683 #endif
1684 if (!isfile)
1686 return wps_parse(wps_data, buf);
1688 else
1691 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1692 * wants to be a virtual file. Feel free to modify dirbrowse()
1693 * if you're feeling brave.
1695 #ifndef __PCTOOL__
1696 if (! strcmp(buf, WPS_DEFAULTCFG) )
1698 global_settings.wps_file[0] = 0;
1699 return false;
1702 #ifdef HAVE_REMOTE_LCD
1703 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1705 global_settings.rwps_file[0] = 0;
1706 return false;
1708 #endif
1709 #endif /* __PCTOOL__ */
1711 int fd = open_utf8(buf, O_RDONLY);
1713 if (fd < 0)
1714 return false;
1716 /* get buffer space from the plugin buffer */
1717 size_t buffersize = 0;
1718 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1720 if (!wps_buffer)
1721 return false;
1723 /* copy the file's content to the buffer for parsing,
1724 ensuring that every line ends with a newline char. */
1725 unsigned int start = 0;
1726 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1728 start += strlen(wps_buffer + start);
1729 if (start < buffersize - 1)
1731 wps_buffer[start++] = '\n';
1732 wps_buffer[start] = 0;
1736 close(fd);
1738 if (start <= 0)
1739 return false;
1741 #ifdef HAVE_LCD_BITMAP
1742 /* Set all filename pointers to NULL */
1743 memset(bmp_names, 0, sizeof(bmp_names));
1744 #endif
1746 /* parse the WPS source */
1747 if (!wps_parse(wps_data, wps_buffer)) {
1748 wps_reset(wps_data);
1749 return false;
1752 wps_data->wps_loaded = true;
1754 #ifdef HAVE_LCD_BITMAP
1755 /* get the bitmap dir */
1756 char bmpdir[MAX_PATH];
1757 char *dot = strrchr(buf, '.');
1759 strlcpy(bmpdir, buf, dot - buf + 1);
1761 /* load the bitmaps that were found by the parsing */
1762 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1763 wps_reset(wps_data);
1764 return false;
1766 #endif
1767 #ifdef HAVE_ALBUMART
1768 status = audio_status();
1769 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1770 (wps_data->wps_uses_albumart &&
1771 (albumart_max_height != wps_data->albumart_max_height ||
1772 albumart_max_width != wps_data->albumart_max_width))) &&
1773 status & AUDIO_STATUS_PLAY)
1775 curtrack = audio_current_track();
1776 offset = curtrack->offset;
1777 audio_stop();
1778 if (!(status & AUDIO_STATUS_PAUSE))
1779 audio_play(offset);
1781 #endif
1782 return true;