get the skin debug output working again, add a line to say how much of the buffer...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob892bb3d933c6fa6fa8dd2b6f5ed1f3129c1f1d28
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 extern void debug_skin_usage(void);
103 #endif
105 static void wps_reset(struct wps_data *data);
107 /* Function for parsing of details for a token. At the moment the
108 function is called, the token type has already been set. The
109 function must fill in the details and possibly add more tokens
110 to the token array. It should return the number of chars that
111 has been consumed.
113 wps_bufptr points to the char following the tag (i.e. where
114 details begin).
115 token is the pointer to the 'main' token being parsed
117 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
118 struct wps_token *token, struct wps_data *wps_data);
120 struct wps_tag {
121 enum wps_token_type type;
122 const char name[3];
123 unsigned char refresh_type;
124 const wps_tag_parse_func parse_func;
126 static int skip_end_of_line(const char *wps_bufptr);
127 /* prototypes of all special parse functions : */
128 static int parse_timeout(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_progressbar(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
132 static int parse_dir_level(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_setting(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
137 #ifdef HAVE_LCD_BITMAP
138 static int parse_viewport_display(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_viewport(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_statusbar_enable(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_statusbar_disable(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 static int parse_image_display(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 static int parse_image_load(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 #endif /*HAVE_LCD_BITMAP */
151 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
152 static int parse_image_special(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 #endif
155 #ifdef HAVE_ALBUMART
156 static int parse_albumart_load(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 static int parse_albumart_conditional(const char *wps_bufptr,
159 struct wps_token *token, struct wps_data *wps_data);
160 #endif /* HAVE_ALBUMART */
161 #ifdef HAVE_TOUCHSCREEN
162 static int parse_touchregion(const char *wps_bufptr,
163 struct wps_token *token, struct wps_data *wps_data);
164 #else
165 static int fulline_tag_not_supported(const char *wps_bufptr,
166 struct wps_token *token, struct wps_data *wps_data)
168 (void)token; (void)wps_data;
169 return skip_end_of_line(wps_bufptr);
171 #define parse_touchregion fulline_tag_not_supported
172 #endif
173 #ifdef CONFIG_RTC
174 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
175 #else
176 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
177 #endif
179 /* array of available tags - those with more characters have to go first
180 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
181 static const struct wps_tag all_tags[] = {
183 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
184 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
185 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
187 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
188 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
189 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
190 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
191 #if CONFIG_CHARGING >= CHARGING_MONITOR
192 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
193 #endif
194 #if CONFIG_CHARGING
195 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
196 #endif
198 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
199 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
216 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
218 /* current file */
219 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
229 parse_dir_level },
231 /* next file */
232 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
242 parse_dir_level },
244 /* current metadata */
245 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
258 /* next metadata */
259 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
272 #if (CONFIG_CODEC != MAS3507D)
273 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
274 #endif
276 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
277 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
278 #endif
280 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
282 #ifdef HAS_REMOTE_BUTTON_HOLD
283 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
284 #else
285 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
286 #endif
288 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
289 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
290 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
291 parse_timeout },
293 #ifdef HAVE_LCD_BITMAP
294 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
295 #else
296 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
297 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
298 #endif
299 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
300 parse_progressbar },
302 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
304 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
305 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
306 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
307 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
309 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
310 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
311 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
312 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
314 #ifdef HAVE_TAGCACHE
315 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
316 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
317 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
318 #endif
320 #if CONFIG_CODEC == SWCODEC
321 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
322 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
323 #endif
325 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
326 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
328 #ifdef HAVE_LCD_BITMAP
329 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
330 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
332 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
334 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
335 parse_image_display },
337 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
338 #ifdef HAVE_ALBUMART
339 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
340 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
341 parse_albumart_conditional },
342 #endif
344 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
345 parse_viewport_display },
346 { WPS_NO_TOKEN, "V", 0, parse_viewport },
348 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
349 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
350 #endif
351 #endif
353 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
355 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
356 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
358 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
359 /* the array MUST end with an empty string (first char is \0) */
363 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
364 * chains require the order to be kept.
366 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
368 if (*list == NULL)
369 *list = item;
370 else
372 struct skin_token_list *t = *list;
373 while (t->next)
374 t = t->next;
375 t->next = item;
378 /* create and init a new wpsll item.
379 * passing NULL to token will alloc a new one.
380 * You should only pass NULL for the token when the token type (table above)
381 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
383 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
384 void* token_data)
386 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
387 if (!token)
388 token = skin_buffer_alloc(sizeof(struct wps_token));
389 if (!llitem || !token)
390 return NULL;
391 llitem->next = NULL;
392 llitem->token = token;
393 if (token_data)
394 llitem->token->value.data = token_data;
395 return llitem;
398 /* Returns the number of chars that should be skipped to jump
399 immediately after the first eol, i.e. to the start of the next line */
400 static int skip_end_of_line(const char *wps_bufptr)
402 line++;
403 int skip = 0;
404 while(*(wps_bufptr + skip) != '\n')
405 skip++;
406 return ++skip;
409 /* Starts a new subline in the current line during parsing */
410 static void wps_start_new_subline(struct wps_data *data)
412 data->num_sublines++;
413 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
414 data->lines[data->num_lines].num_sublines++;
417 #ifdef HAVE_LCD_BITMAP
419 static int parse_statusbar_enable(const char *wps_bufptr,
420 struct wps_token *token,
421 struct wps_data *wps_data)
423 (void)token; /* Kill warnings */
424 wps_data->wps_sb_tag = true;
425 wps_data->show_sb_on_wps = true;
426 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
427 if (default_vp->vp.y == 0)
429 default_vp->vp.y = STATUSBAR_HEIGHT;
430 default_vp->vp.height -= STATUSBAR_HEIGHT;
432 return skip_end_of_line(wps_bufptr);
435 static int parse_statusbar_disable(const char *wps_bufptr,
436 struct wps_token *token,
437 struct wps_data *wps_data)
439 (void)token; /* Kill warnings */
440 wps_data->wps_sb_tag = true;
441 wps_data->show_sb_on_wps = false;
442 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
443 if (default_vp->vp.y == STATUSBAR_HEIGHT)
445 default_vp->vp.y = 0;
446 default_vp->vp.height += STATUSBAR_HEIGHT;
448 return skip_end_of_line(wps_bufptr);
451 static int get_image_id(int c)
453 if(c >= 'a' && c <= 'z')
454 return c - 'a';
455 else if(c >= 'A' && c <= 'Z')
456 return c - 'A' + 26;
457 else
458 return -1;
461 static char *get_image_filename(const char *start, const char* bmpdir,
462 char *buf, int buf_size)
464 const char *end = strchr(start, '|');
466 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
468 buf = "\0";
469 return NULL;
472 int bmpdirlen = strlen(bmpdir);
474 strcpy(buf, bmpdir);
475 buf[bmpdirlen] = '/';
476 memcpy( &buf[bmpdirlen + 1], start, end - start);
477 buf[bmpdirlen + 1 + end - start] = 0;
479 return buf;
482 static int parse_image_display(const char *wps_bufptr,
483 struct wps_token *token,
484 struct wps_data *wps_data)
486 char label = wps_bufptr[0];
487 int subimage;
488 struct gui_img *img;;
490 /* sanity check */
491 img = find_image(label, wps_data);
492 if (!img)
494 token->value.i = label; /* do debug works */
495 return WPS_ERROR_INVALID_PARAM;
498 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
500 if (subimage >= img->num_subimages)
501 return WPS_ERROR_INVALID_PARAM;
503 /* Store sub-image number to display in high bits */
504 token->value.i = label | (subimage << 8);
505 return 2; /* We have consumed 2 bytes */
506 } else {
507 token->value.i = label;
508 return 1; /* We have consumed 1 byte */
512 static int parse_image_load(const char *wps_bufptr,
513 struct wps_token *token,
514 struct wps_data *wps_data)
516 const char *ptr = wps_bufptr;
517 const char *pos;
518 const char* filename;
519 const char* id;
520 const char *newline;
521 int x,y;
522 struct gui_img *img;
524 /* format: %x|n|filename.bmp|x|y|
525 or %xl|n|filename.bmp|x|y|
526 or %xl|n|filename.bmp|x|y|num_subimages|
529 if (*ptr != '|')
530 return WPS_ERROR_INVALID_PARAM;
532 ptr++;
534 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
535 return WPS_ERROR_INVALID_PARAM;
537 /* Check there is a terminating | */
538 if (*ptr != '|')
539 return WPS_ERROR_INVALID_PARAM;
541 /* check the image number and load state */
542 if(find_image(*id, wps_data))
544 /* Invalid image ID */
545 return WPS_ERROR_INVALID_PARAM;
547 img = skin_buffer_alloc(sizeof(struct gui_img));
548 if (!img)
549 return WPS_ERROR_INVALID_PARAM;
550 /* save a pointer to the filename */
551 img->bm.data = (char*)filename;
552 img->label = *id;
553 img->x = x;
554 img->y = y;
555 img->num_subimages = 1;
556 img->always_display = false;
558 /* save current viewport */
559 img->vp = &curr_vp->vp;
561 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
563 img->always_display = true;
565 else
567 /* Parse the (optional) number of sub-images */
568 ptr++;
569 newline = strchr(ptr, '\n');
570 pos = strchr(ptr, '|');
571 if (pos && pos < newline)
572 img->num_subimages = atoi(ptr);
574 if (img->num_subimages <= 0)
575 return WPS_ERROR_INVALID_PARAM;
577 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
578 if (!item)
579 return WPS_ERROR_INVALID_PARAM;
580 add_to_ll_chain(&wps_data->images, item);
582 /* Skip the rest of the line */
583 return skip_end_of_line(wps_bufptr);
586 static int parse_viewport_display(const char *wps_bufptr,
587 struct wps_token *token,
588 struct wps_data *wps_data)
590 (void)wps_data;
591 char letter = wps_bufptr[0];
593 if (letter < 'a' || letter > 'z')
595 /* invalid viewport tag */
596 return WPS_ERROR_INVALID_PARAM;
598 token->value.i = letter;
599 return 1;
602 static int parse_viewport(const char *wps_bufptr,
603 struct wps_token *token,
604 struct wps_data *wps_data)
606 (void)token; /* Kill warnings */
607 const char *ptr = wps_bufptr;
609 const int screen =
610 #ifdef HAVE_REMOTE_LCD
611 wps_data->remote_wps ? SCREEN_REMOTE :
612 #endif
613 SCREEN_MAIN;
615 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
617 /* check for the optional letter to signify its a hideable viewport */
618 /* %Vl|<label>|<rest of tags>| */
619 skin_vp->hidden_flags = 0;
620 skin_vp->label = VP_NO_LABEL;
621 skin_vp->pb = NULL;
623 if (*ptr == 'l')
625 if (*(ptr+1) == '|')
627 char label = *(ptr+2);
628 if (label >= 'a' && label <= 'z')
630 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
631 skin_vp->label = label;
633 else
634 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
635 ptr += 3;
638 if (*ptr != '|')
639 return WPS_ERROR_INVALID_PARAM;
641 ptr++;
642 struct viewport *vp = &skin_vp->vp;
643 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
645 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
646 return WPS_ERROR_INVALID_PARAM;
648 /* Check for trailing | */
649 if (*ptr != '|')
650 return WPS_ERROR_INVALID_PARAM;
652 curr_vp->last_line = wps_data->num_lines - 1;
654 skin_vp->first_line = wps_data->num_lines;
656 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
658 wps_data->lines[wps_data->num_lines].first_subline_idx =
659 wps_data->num_sublines;
661 wps_data->sublines[wps_data->num_sublines].first_token_idx =
662 wps_data->num_tokens;
665 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
666 if (!list)
667 return WPS_ERROR_INVALID_PARAM;
668 add_to_ll_chain(&wps_data->viewports, list);
669 curr_vp = skin_vp;
670 /* Skip the rest of the line */
671 return skip_end_of_line(wps_bufptr);
674 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
675 static int parse_image_special(const char *wps_bufptr,
676 struct wps_token *token,
677 struct wps_data *wps_data)
679 (void)wps_data; /* kill warning */
680 (void)token;
681 const char *pos = NULL;
682 const char *newline;
684 pos = strchr(wps_bufptr + 1, '|');
685 newline = strchr(wps_bufptr, '\n');
687 if (pos > newline)
688 return WPS_ERROR_INVALID_PARAM;
689 #if LCD_DEPTH > 1
690 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
692 /* format: %X|filename.bmp| */
693 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
695 #endif
697 /* Skip the rest of the line */
698 return skip_end_of_line(wps_bufptr);
700 #endif
702 #endif /* HAVE_LCD_BITMAP */
704 static int parse_setting(const char *wps_bufptr,
705 struct wps_token *token,
706 struct wps_data *wps_data)
708 (void)wps_data;
709 const char *ptr = wps_bufptr;
710 const char *end;
711 int i;
713 /* Find the setting's cfg_name */
714 if (*ptr != '|')
715 return WPS_ERROR_INVALID_PARAM;
716 ptr++;
717 end = strchr(ptr,'|');
718 if (!end)
719 return WPS_ERROR_INVALID_PARAM;
721 /* Find the setting */
722 for (i=0; i<nb_settings; i++)
723 if (settings[i].cfg_name &&
724 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
725 /* prevent matches on cfg_name prefixes */
726 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
727 break;
728 if (i == nb_settings)
729 return WPS_ERROR_INVALID_PARAM;
731 /* Store the setting number */
732 token->value.i = i;
734 /* Skip the rest of the line */
735 return end-ptr+2;
739 static int parse_dir_level(const char *wps_bufptr,
740 struct wps_token *token,
741 struct wps_data *wps_data)
743 char val[] = { *wps_bufptr, '\0' };
744 token->value.i = atoi(val);
745 (void)wps_data; /* Kill warnings */
746 return 1;
749 static int parse_timeout(const char *wps_bufptr,
750 struct wps_token *token,
751 struct wps_data *wps_data)
753 int skip = 0;
754 int val = 0;
755 bool have_point = false;
756 bool have_tenth = false;
758 (void)wps_data; /* Kill the warning */
760 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
762 if (*wps_bufptr != '.')
764 val *= 10;
765 val += *wps_bufptr - '0';
766 if (have_point)
768 have_tenth = true;
769 wps_bufptr++;
770 skip++;
771 break;
774 else
775 have_point = true;
777 wps_bufptr++;
778 skip++;
781 if (have_tenth == false)
782 val *= 10;
784 if (val == 0 && skip == 0)
786 /* decide what to do if no value was specified */
787 switch (token->type)
789 case WPS_TOKEN_SUBLINE_TIMEOUT:
790 return -1;
791 case WPS_TOKEN_BUTTON_VOLUME:
792 val = 10;
793 break;
796 token->value.i = val;
798 return skip;
801 static int parse_progressbar(const char *wps_bufptr,
802 struct wps_token *token,
803 struct wps_data *wps_data)
805 /* %pb or %pb|filename|x|y|width|height|
806 using - for any of the params uses "sane" values */
807 #ifdef HAVE_LCD_BITMAP
808 enum {
809 PB_FILENAME = 0,
810 PB_X,
811 PB_Y,
812 PB_WIDTH,
813 PB_HEIGHT
815 const char *filename;
816 int x, y, height, width;
817 uint32_t set = 0;
818 const char *ptr = wps_bufptr;
819 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
820 struct skin_token_list *item = new_skin_token_list_item(token, pb);
822 if (!pb || !item)
823 return WPS_ERROR_INVALID_PARAM;
825 struct viewport *vp = &curr_vp->vp;
826 #ifndef __PCTOOL__
827 int font_height = font_get(vp->font)->height;
828 #else
829 int font_height = 8;
830 #endif
831 int line_num = wps_data->num_lines - curr_vp->first_line;
833 pb->have_bitmap_pb = false;
834 pb->bm.data = NULL; /* no bitmap specified */
836 if (*wps_bufptr != '|') /* regular old style */
838 pb->x = 0;
839 pb->width = vp->width;
840 pb->height = SYSFONT_HEIGHT-2;
841 pb->y = -line_num - 1; /* Will be computed during the rendering */
843 curr_vp->pb = pb;
844 add_to_ll_chain(&wps_data->progressbars, item);
845 return 0;
847 ptr = wps_bufptr + 1;
849 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
850 &x, &y, &width, &height)))
851 return WPS_ERROR_INVALID_PARAM;
853 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
854 pb->bm.data = (char*)filename;
856 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
857 pb->x = x;
858 else
859 pb->x = vp->x;
861 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
863 /* A zero width causes a divide-by-zero error later, so reject it */
864 if (width == 0)
865 return WPS_ERROR_INVALID_PARAM;
867 pb->width = width;
869 else
870 pb->width = vp->width - pb->x;
872 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
874 /* A zero height makes no sense - reject it */
875 if (height == 0)
876 return WPS_ERROR_INVALID_PARAM;
878 pb->height = height;
880 else
881 pb->height = font_height;
883 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
884 pb->y = y;
885 else
886 pb->y = -line_num - 1; /* Will be computed during the rendering */
888 curr_vp->pb = pb;
889 add_to_ll_chain(&wps_data->progressbars, item);
891 /* Skip the rest of the line */
892 return skip_end_of_line(wps_bufptr)-1;
893 #else
894 (void)token;
896 if (*(wps_bufptr-1) == 'f')
897 wps_data->full_line_progressbar = true;
898 else
899 wps_data->full_line_progressbar = false;
901 return 0;
903 #endif
906 #ifdef HAVE_ALBUMART
907 static int parse_albumart_load(const char *wps_bufptr,
908 struct wps_token *token,
909 struct wps_data *wps_data)
911 const char *_pos, *newline;
912 bool parsing;
913 (void)token; /* silence warning */
915 /* reset albumart info in wps */
916 wps_data->albumart_max_width = -1;
917 wps_data->albumart_max_height = -1;
918 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
919 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
921 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
923 newline = strchr(wps_bufptr, '\n');
925 /* initial validation and parsing of x and y components */
926 if (*wps_bufptr != '|')
927 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
929 _pos = wps_bufptr + 1;
930 if (!isdigit(*_pos))
931 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
932 wps_data->albumart_x = atoi(_pos);
934 _pos = strchr(_pos, '|');
935 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
936 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
938 wps_data->albumart_y = atoi(_pos);
940 _pos = strchr(_pos, '|');
941 if (!_pos || _pos > newline)
942 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
943 e.g. %Cl|7|59\n */
945 /* parsing width field */
946 parsing = true;
947 while (parsing)
949 /* apply each modifier in turn */
950 ++_pos;
951 switch (*_pos)
953 case 'l':
954 case 'L':
955 case '+':
956 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
957 break;
958 case 'c':
959 case 'C':
960 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
961 break;
962 case 'r':
963 case 'R':
964 case '-':
965 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
966 break;
967 case 'd':
968 case 'D':
969 case 'i':
970 case 'I':
971 case 's':
972 case 'S':
973 /* simply ignored */
974 break;
975 default:
976 parsing = false;
977 break;
980 /* extract max width data */
981 if (*_pos != '|')
983 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
984 return WPS_ERROR_INVALID_PARAM;
986 wps_data->albumart_max_width = atoi(_pos);
988 _pos = strchr(_pos, '|');
989 if (!_pos || _pos > newline)
990 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
991 e.g. %Cl|7|59|200\n */
994 /* parsing height field */
995 parsing = true;
996 while (parsing)
998 /* apply each modifier in turn */
999 ++_pos;
1000 switch (*_pos)
1002 case 't':
1003 case 'T':
1004 case '-':
1005 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1006 break;
1007 case 'c':
1008 case 'C':
1009 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1010 break;
1011 case 'b':
1012 case 'B':
1013 case '+':
1014 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1015 break;
1016 case 'd':
1017 case 'D':
1018 case 'i':
1019 case 'I':
1020 case 's':
1021 case 'S':
1022 /* simply ignored */
1023 break;
1024 default:
1025 parsing = false;
1026 break;
1029 /* extract max height data */
1030 if (*_pos != '|')
1032 if (!isdigit(*_pos))
1033 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1035 wps_data->albumart_max_height = atoi(_pos);
1037 _pos = strchr(_pos, '|');
1038 if (!_pos || _pos > newline)
1039 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1040 e.g. %Cl|7|59|200|200\n */
1043 /* if we got here, we parsed everything ok .. ! */
1044 if (wps_data->albumart_max_width < 0)
1045 wps_data->albumart_max_width = 0;
1046 else if (wps_data->albumart_max_width > LCD_WIDTH)
1047 wps_data->albumart_max_width = LCD_WIDTH;
1049 if (wps_data->albumart_max_height < 0)
1050 wps_data->albumart_max_height = 0;
1051 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1052 wps_data->albumart_max_height = LCD_HEIGHT;
1054 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1056 /* Skip the rest of the line */
1057 return skip_end_of_line(wps_bufptr);
1060 static int parse_albumart_conditional(const char *wps_bufptr,
1061 struct wps_token *token,
1062 struct wps_data *wps_data)
1064 struct wps_token *prevtoken = token;
1065 --prevtoken;
1066 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1068 /* This %C is part of a %?C construct.
1069 It's either %?C<blah> or %?Cn<blah> */
1070 token->type = WPS_TOKEN_ALBUMART_FOUND;
1071 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1073 token->next = true;
1074 return 1;
1076 else if (*wps_bufptr == '<')
1078 return 0;
1080 else
1082 token->type = WPS_NO_TOKEN;
1083 return 0;
1086 else
1088 /* This %C tag is in a conditional construct. */
1089 wps_data->albumart_cond_index = condindex[level];
1090 return 0;
1093 #endif /* HAVE_ALBUMART */
1095 #ifdef HAVE_TOUCHSCREEN
1097 struct touchaction {char* s; int action;};
1098 static struct touchaction touchactions[] = {
1099 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1100 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1101 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1102 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1103 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1104 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1105 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1107 static int parse_touchregion(const char *wps_bufptr,
1108 struct wps_token *token, struct wps_data *wps_data)
1110 (void)token;
1111 unsigned i, imax;
1112 struct touchregion *region = NULL;
1113 const char *ptr = wps_bufptr;
1114 const char *action;
1115 const char pb_string[] = "progressbar";
1116 const char vol_string[] = "volume";
1117 int x,y,w,h;
1119 /* format: %T|x|y|width|height|action|
1120 * if action starts with & the area must be held to happen
1121 * action is one of:
1122 * play - play/pause playback
1123 * stop - stop playback, exit the wps
1124 * prev - prev track
1125 * next - next track
1126 * ffwd - seek forward
1127 * rwd - seek backwards
1128 * menu - go back to the main menu
1129 * browse - go back to the file/db browser
1130 * shuffle - toggle shuffle mode
1131 * repmode - cycle the repeat mode
1132 * quickscreen - go into the quickscreen
1133 * contextmenu - open the context menu
1134 * playlist - go into the playlist
1135 * pitch - go into the pitchscreen
1139 if (*ptr != '|')
1140 return WPS_ERROR_INVALID_PARAM;
1141 ptr++;
1143 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1144 return WPS_ERROR_INVALID_PARAM;
1146 /* Check there is a terminating | */
1147 if (*ptr != '|')
1148 return WPS_ERROR_INVALID_PARAM;
1150 region = skin_buffer_alloc(sizeof(struct touchregion));
1151 if (!region)
1152 return WPS_ERROR_INVALID_PARAM;
1154 /* should probably do some bounds checking here with the viewport... but later */
1155 region->action = ACTION_NONE;
1156 region->x = x;
1157 region->y = y;
1158 region->width = w;
1159 region->height = h;
1160 region->wvp = curr_vp;
1162 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1163 && *(action + sizeof(pb_string)-1) == '|')
1164 region->type = WPS_TOUCHREGION_SCROLLBAR;
1165 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1166 && *(action + sizeof(vol_string)-1) == '|')
1167 region->type = WPS_TOUCHREGION_VOLUME;
1168 else
1170 region->type = WPS_TOUCHREGION_ACTION;
1172 if (*action == '&')
1174 action++;
1175 region->repeat = true;
1177 else
1178 region->repeat = false;
1180 i = 0;
1181 imax = ARRAYLEN(touchactions);
1182 while ((region->action == ACTION_NONE) &&
1183 (i < imax))
1185 /* try to match with one of our touchregion screens */
1186 int len = strlen(touchactions[i].s);
1187 if (!strncmp(touchactions[i].s, action, len)
1188 && *(action+len) == '|')
1189 region->action = touchactions[i].action;
1190 i++;
1192 if (region->action == ACTION_NONE)
1193 return WPS_ERROR_INVALID_PARAM;
1195 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1196 if (!item)
1197 return WPS_ERROR_INVALID_PARAM;
1198 add_to_ll_chain(&wps_data->touchregions, item);
1199 return skip_end_of_line(wps_bufptr);
1201 #endif
1203 /* Parse a generic token from the given string. Return the length read */
1204 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1206 int skip = 0, taglen = 0, ret;
1207 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1208 const struct wps_tag *tag;
1210 switch(*wps_bufptr)
1213 case '%':
1214 case '<':
1215 case '|':
1216 case '>':
1217 case ';':
1218 case '#':
1219 /* escaped characters */
1220 token->type = WPS_TOKEN_CHARACTER;
1221 token->value.c = *wps_bufptr;
1222 taglen = 1;
1223 wps_data->num_tokens++;
1224 break;
1226 case '?':
1227 /* conditional tag */
1228 token->type = WPS_TOKEN_CONDITIONAL;
1229 level++;
1230 condindex[level] = wps_data->num_tokens;
1231 numoptions[level] = 1;
1232 wps_data->num_tokens++;
1233 ret = parse_token(wps_bufptr + 1, wps_data);
1234 if (ret < 0) return ret;
1235 taglen = 1 + ret;
1236 break;
1238 default:
1239 /* find what tag we have */
1240 for (tag = all_tags;
1241 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1242 tag++) ;
1244 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1245 token->type = tag->type;
1246 wps_data->sublines[wps_data->num_sublines].line_type |=
1247 tag->refresh_type;
1249 /* if the tag has a special parsing function, we call it */
1250 if (tag->parse_func)
1252 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1253 if (ret < 0) return ret;
1254 skip += ret;
1257 /* Some tags we don't want to save as tokens */
1258 if (tag->type == WPS_NO_TOKEN)
1259 break;
1261 /* tags that start with 'F', 'I' or 'D' are for the next file */
1262 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1263 *(tag->name) == 'D')
1264 token->next = true;
1266 wps_data->num_tokens++;
1267 break;
1270 skip += taglen;
1271 return skip;
1274 /* Parses the WPS.
1275 data is the pointer to the structure where the parsed WPS should be stored.
1276 It is initialised.
1277 wps_bufptr points to the string containing the WPS tags */
1278 #define TOKEN_BLOCK_SIZE 128
1279 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1281 if (!data || !wps_bufptr || !*wps_bufptr)
1282 return false;
1283 enum wps_parse_error fail = PARSE_OK;
1284 int ret;
1285 int max_tokens = TOKEN_BLOCK_SIZE;
1286 size_t buf_free = 0;
1287 line = 1;
1288 level = -1;
1290 /* allocate enough RAM for a reasonable skin, grow as needed.
1291 * Free any used RAM before loading the images to be 100% RAM efficient */
1292 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1293 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1294 return false;
1295 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1296 data->num_tokens = 0;
1298 while(*wps_bufptr && !fail
1299 && data->num_lines < WPS_MAX_LINES)
1301 /* first make sure there is enough room for tokens */
1302 if (max_tokens -1 == data->num_tokens)
1304 int extra_tokens = TOKEN_BLOCK_SIZE;
1305 size_t needed = extra_tokens * sizeof(struct wps_token);
1306 /* do some smarts here to grow the array a bit */
1307 if (skin_buffer_freespace() < needed)
1309 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1310 break;
1312 skin_buffer_increment(needed, false);
1313 max_tokens += extra_tokens;
1316 switch(*wps_bufptr++)
1319 /* Regular tag */
1320 case '%':
1321 if ((ret = parse_token(wps_bufptr, data)) < 0)
1323 fail = PARSE_FAIL_COND_INVALID_PARAM;
1324 break;
1326 else if (level >= WPS_MAX_COND_LEVEL - 1)
1328 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1329 break;
1331 wps_bufptr += ret;
1332 break;
1334 /* Alternating sublines separator */
1335 case ';':
1336 if (level >= 0) /* there are unclosed conditionals */
1338 fail = PARSE_FAIL_UNCLOSED_COND;
1339 break;
1342 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1343 wps_start_new_subline(data);
1344 else
1345 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1347 break;
1349 /* Conditional list start */
1350 case '<':
1351 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1353 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1354 break;
1357 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1358 lastcond[level] = data->num_tokens++;
1359 break;
1361 /* Conditional list end */
1362 case '>':
1363 if (level < 0) /* not in a conditional, invalid char */
1365 fail = PARSE_FAIL_INVALID_CHAR;
1366 break;
1369 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1370 if (lastcond[level])
1371 data->tokens[lastcond[level]].value.i = data->num_tokens;
1372 else
1374 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1375 break;
1378 lastcond[level] = 0;
1379 data->num_tokens++;
1380 data->tokens[condindex[level]].value.i = numoptions[level];
1381 level--;
1382 break;
1384 /* Conditional list option */
1385 case '|':
1386 if (level < 0) /* not in a conditional, invalid char */
1388 fail = PARSE_FAIL_INVALID_CHAR;
1389 break;
1392 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1393 if (lastcond[level])
1394 data->tokens[lastcond[level]].value.i = data->num_tokens;
1395 else
1397 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1398 break;
1401 lastcond[level] = data->num_tokens;
1402 numoptions[level]++;
1403 data->num_tokens++;
1404 break;
1406 /* Comment */
1407 case '#':
1408 if (level >= 0) /* there are unclosed conditionals */
1410 fail = PARSE_FAIL_UNCLOSED_COND;
1411 break;
1414 wps_bufptr += skip_end_of_line(wps_bufptr);
1415 break;
1417 /* End of this line */
1418 case '\n':
1419 if (level >= 0) /* there are unclosed conditionals */
1421 fail = PARSE_FAIL_UNCLOSED_COND;
1422 break;
1425 line++;
1426 wps_start_new_subline(data);
1427 data->num_lines++; /* Start a new line */
1429 if ((data->num_lines < WPS_MAX_LINES) &&
1430 (data->num_sublines < WPS_MAX_SUBLINES))
1432 data->lines[data->num_lines].first_subline_idx =
1433 data->num_sublines;
1435 data->sublines[data->num_sublines].first_token_idx =
1436 data->num_tokens;
1439 break;
1441 /* String */
1442 default:
1444 unsigned int len = 1;
1445 const char *string_start = wps_bufptr - 1;
1447 /* find the length of the string */
1448 while (*wps_bufptr && *wps_bufptr != '#' &&
1449 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1450 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1451 *wps_bufptr != '|' && *wps_bufptr != '\n')
1453 wps_bufptr++;
1454 len++;
1457 /* look if we already have that string */
1458 char *str;
1459 bool found = false;
1460 struct skin_token_list *list = data->strings;
1461 while (list)
1463 str = (char*)list->token->value.data;
1464 found = (strlen(str) == len &&
1465 strncmp(string_start, str, len) == 0);
1466 if (found)
1467 break; /* break here because the list item is
1468 used if its found */
1469 list = list->next;
1471 /* If a matching string is found, found is true and i is
1472 the index of the string. If not, found is false */
1474 if (!found)
1476 /* new string */
1477 str = (char*)skin_buffer_alloc(len+1);
1478 if (!str)
1480 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1481 break;
1483 strlcpy(str, string_start, len+1);
1484 struct skin_token_list *item =
1485 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1486 if(!item)
1488 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1489 break;
1491 add_to_ll_chain(&data->strings, item);
1493 else
1495 /* another occurrence of an existing string */
1496 data->tokens[data->num_tokens].value.data = list->token->value.data;
1498 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1499 data->num_tokens++;
1501 break;
1505 if (!fail && level >= 0) /* there are unclosed conditionals */
1506 fail = PARSE_FAIL_UNCLOSED_COND;
1508 if (*wps_bufptr && !fail)
1509 /* one of the limits of the while loop was exceeded */
1510 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1512 /* Success! */
1513 /* freeup unused tokens */
1514 skin_buffer_free_from_front(sizeof(struct wps_token)
1515 * (max_tokens - data->num_tokens));
1516 curr_vp->last_line = data->num_lines - 1;
1518 #if defined(DEBUG) || defined(SIMULATOR)
1519 if (debug)
1520 print_debug_info(data, fail, line);
1521 #else
1522 (void)debug;
1523 #endif
1525 return (fail == 0);
1528 static void wps_reset(struct wps_data *data)
1530 #ifdef HAVE_REMOTE_LCD
1531 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1532 #endif
1533 memset(data, 0, sizeof(*data));
1534 skin_data_init(data);
1535 #ifdef HAVE_REMOTE_LCD
1536 data->remote_wps = rwps;
1537 #endif
1540 #ifdef HAVE_LCD_BITMAP
1541 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1543 (void)wps_data; /* only needed for remote targets */
1544 bool loaded = false;
1545 char img_path[MAX_PATH];
1546 get_image_filename(bitmap->data, bmpdir,
1547 img_path, sizeof(img_path));
1549 /* load the image */
1550 int format;
1551 #ifdef HAVE_REMOTE_LCD
1552 if (wps_data->remote_wps)
1553 format = FORMAT_ANY|FORMAT_REMOTE;
1554 else
1555 #endif
1556 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1558 size_t max_buf;
1559 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1560 bitmap->data = imgbuf;
1561 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1563 if (ret > 0)
1565 skin_buffer_increment(ret, true);
1566 loaded = true;
1568 else
1570 /* Abort if we can't load an image */
1571 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1572 loaded = false;
1574 return loaded;
1577 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1579 struct skin_token_list *list;
1580 /* do the progressbars */
1581 list = wps_data->progressbars;
1582 while (list)
1584 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1585 if (pb->bm.data)
1587 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1589 list = list->next;
1591 /* regular images */
1592 list = wps_data->images;
1593 while (list)
1595 struct gui_img *img = (struct gui_img*)list->token->value.data;
1596 if (img->bm.data)
1598 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1599 if (img->loaded)
1600 img->subimage_height = img->bm.height / img->num_subimages;
1602 list = list->next;
1605 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1606 if (bmp_names[BACKDROP_BMP])
1608 int screen = SCREEN_MAIN;
1609 char img_path[MAX_PATH];
1610 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1611 img_path, sizeof(img_path));
1612 #if defined(HAVE_REMOTE_LCD)
1613 /* We only need to check LCD type if there is a remote LCD */
1614 if (wps_data->remote_wps)
1615 screen = SCREEN_REMOTE;
1616 #endif
1617 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1619 #endif /* has backdrop support */
1621 /* If we got here, everything was OK */
1622 return true;
1625 #endif /* HAVE_LCD_BITMAP */
1627 /* to setup up the wps-data from a format-buffer (isfile = false)
1628 from a (wps-)file (isfile = true)*/
1629 bool skin_data_load(struct wps_data *wps_data,
1630 struct screen *display,
1631 const char *buf,
1632 bool isfile)
1634 #ifdef HAVE_ALBUMART
1635 struct mp3entry *curtrack;
1636 long offset;
1637 int status;
1638 int wps_uses_albumart = wps_data->wps_uses_albumart;
1639 int albumart_max_height = wps_data->albumart_max_height;
1640 int albumart_max_width = wps_data->albumart_max_width;
1641 #endif
1642 if (!wps_data || !buf)
1643 return false;
1645 wps_reset(wps_data);
1647 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1648 if (!curr_vp)
1649 return false;
1650 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1651 if (!list)
1652 return false;
1653 add_to_ll_chain(&wps_data->viewports, list);
1656 /* Initialise the first (default) viewport */
1657 curr_vp->label = VP_DEFAULT_LABEL;
1658 curr_vp->vp.x = 0;
1659 curr_vp->vp.width = display->getwidth();
1660 curr_vp->vp.height = display->getheight();
1661 curr_vp->pb = NULL;
1662 curr_vp->hidden_flags = 0;
1663 switch (statusbar_position(display->screen_type))
1665 case STATUSBAR_OFF:
1666 curr_vp->vp.y = 0;
1667 break;
1668 case STATUSBAR_TOP:
1669 curr_vp->vp.y = STATUSBAR_HEIGHT;
1670 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1671 break;
1672 case STATUSBAR_BOTTOM:
1673 curr_vp->vp.y = 0;
1674 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1675 break;
1677 #ifdef HAVE_LCD_BITMAP
1678 curr_vp->vp.font = FONT_UI;
1679 curr_vp->vp.drawmode = DRMODE_SOLID;
1680 #endif
1681 #if LCD_DEPTH > 1
1682 if (display->depth > 1)
1684 curr_vp->vp.fg_pattern = display->get_foreground();
1685 curr_vp->vp.bg_pattern = display->get_background();
1687 #endif
1688 if (!isfile)
1690 return wps_parse(wps_data, buf, false);
1692 else
1695 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1696 * wants to be a virtual file. Feel free to modify dirbrowse()
1697 * if you're feeling brave.
1699 #ifndef __PCTOOL__
1700 if (! strcmp(buf, WPS_DEFAULTCFG) )
1702 global_settings.wps_file[0] = 0;
1703 return false;
1706 #ifdef HAVE_REMOTE_LCD
1707 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1709 global_settings.rwps_file[0] = 0;
1710 return false;
1712 #endif
1713 #endif /* __PCTOOL__ */
1715 int fd = open_utf8(buf, O_RDONLY);
1717 if (fd < 0)
1718 return false;
1720 /* get buffer space from the plugin buffer */
1721 size_t buffersize = 0;
1722 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1724 if (!wps_buffer)
1725 return false;
1727 /* copy the file's content to the buffer for parsing,
1728 ensuring that every line ends with a newline char. */
1729 unsigned int start = 0;
1730 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1732 start += strlen(wps_buffer + start);
1733 if (start < buffersize - 1)
1735 wps_buffer[start++] = '\n';
1736 wps_buffer[start] = 0;
1740 close(fd);
1742 if (start <= 0)
1743 return false;
1745 #ifdef HAVE_LCD_BITMAP
1746 /* Set all filename pointers to NULL */
1747 memset(bmp_names, 0, sizeof(bmp_names));
1748 #endif
1750 /* parse the WPS source */
1751 if (!wps_parse(wps_data, wps_buffer, true)) {
1752 wps_reset(wps_data);
1753 return false;
1756 wps_data->wps_loaded = true;
1758 #ifdef HAVE_LCD_BITMAP
1759 /* get the bitmap dir */
1760 char bmpdir[MAX_PATH];
1761 char *dot = strrchr(buf, '.');
1763 strlcpy(bmpdir, buf, dot - buf + 1);
1765 /* load the bitmaps that were found by the parsing */
1766 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1767 wps_reset(wps_data);
1768 return false;
1770 #endif
1771 #ifdef HAVE_ALBUMART
1772 status = audio_status();
1773 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1774 (wps_data->wps_uses_albumart &&
1775 (albumart_max_height != wps_data->albumart_max_height ||
1776 albumart_max_width != wps_data->albumart_max_width))) &&
1777 status & AUDIO_STATUS_PLAY)
1779 curtrack = audio_current_track();
1780 offset = curtrack->offset;
1781 audio_stop();
1782 if (!(status & AUDIO_STATUS_PAUSE))
1783 audio_play(offset);
1785 #endif
1786 #if defined(DEBUG) || defined(SIMULATOR)
1787 debug_skin_usage();
1788 #endif
1789 return true;