add a bit more debug info for skins... show the subimage id in the display image...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
bloba6d595db53d1052449ce54eb02dc86151386fd37
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 "config.h"
26 #include "file.h"
27 #include "misc.h"
28 #include "plugin.h"
29 #include "viewport.h"
31 #ifdef __PCTOOL__
32 #ifdef WPSEDITOR
33 #include "proxy.h"
34 #include "sysfont.h"
35 #else
36 #include "action.h"
37 #include "checkwps.h"
38 #include "audio.h"
39 #define DEBUGF printf
40 #endif /*WPSEDITOR*/
41 #else
42 #include "debug.h"
43 #endif /*__PCTOOL__*/
45 #include <ctype.h>
46 #include <stdbool.h>
47 #include "font.h"
49 #include "wps_internals.h"
50 #include "skin_engine.h"
51 #include "settings.h"
52 #include "settings_list.h"
54 #ifdef HAVE_LCD_BITMAP
55 #include "bmp.h"
56 #endif
58 #include "backdrop.h"
60 #define WPS_ERROR_INVALID_PARAM -1
62 /* level of current conditional.
63 -1 means we're not in a conditional. */
64 static int level = -1;
66 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
67 or WPS_TOKEN_CONDITIONAL_START in current level */
68 static int lastcond[WPS_MAX_COND_LEVEL];
70 /* index of the WPS_TOKEN_CONDITIONAL in current level */
71 static int condindex[WPS_MAX_COND_LEVEL];
73 /* number of condtional options in current level */
74 static int numoptions[WPS_MAX_COND_LEVEL];
76 /* line number, debug only */
77 static int line_number;
79 /* the current viewport */
80 static struct skin_viewport *curr_vp;
81 /* the current line, linked to the above viewport */
82 static struct skin_line *curr_line;
84 #ifdef HAVE_LCD_BITMAP
86 #if LCD_DEPTH > 1
87 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
88 #else
89 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
90 #endif
92 #define PROGRESSBAR_BMP MAX_IMAGES
93 #define BACKDROP_BMP (MAX_BITMAPS-1)
95 /* pointers to the bitmap filenames in the WPS source */
96 static const char *bmp_names[MAX_BITMAPS];
98 #endif /* HAVE_LCD_BITMAP */
100 #if defined(DEBUG) || defined(SIMULATOR)
101 /* debugging function */
102 extern void print_debug_info(struct wps_data *data, int fail, int line);
103 extern void debug_skin_usage(void);
104 #endif
106 static void wps_reset(struct wps_data *data);
108 /* Function for parsing of details for a token. At the moment the
109 function is called, the token type has already been set. The
110 function must fill in the details and possibly add more tokens
111 to the token array. It should return the number of chars that
112 has been consumed.
114 wps_bufptr points to the char following the tag (i.e. where
115 details begin).
116 token is the pointer to the 'main' token being parsed
118 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
119 struct wps_token *token, struct wps_data *wps_data);
121 struct wps_tag {
122 enum wps_token_type type;
123 const char name[3];
124 unsigned char refresh_type;
125 const wps_tag_parse_func parse_func;
127 static int skip_end_of_line(const char *wps_bufptr);
128 /* prototypes of all special parse functions : */
129 static int parse_timeout(const char *wps_bufptr,
130 struct wps_token *token, struct wps_data *wps_data);
131 static int parse_progressbar(const char *wps_bufptr,
132 struct wps_token *token, struct wps_data *wps_data);
133 static int parse_dir_level(const char *wps_bufptr,
134 struct wps_token *token, struct wps_data *wps_data);
135 static int parse_setting(const char *wps_bufptr,
136 struct wps_token *token, struct wps_data *wps_data);
138 #ifdef HAVE_LCD_BITMAP
139 static int parse_viewport_display(const char *wps_bufptr,
140 struct wps_token *token, struct wps_data *wps_data);
141 static int parse_viewport(const char *wps_bufptr,
142 struct wps_token *token, struct wps_data *wps_data);
143 static int parse_statusbar_enable(const char *wps_bufptr,
144 struct wps_token *token, struct wps_data *wps_data);
145 static int parse_statusbar_disable(const char *wps_bufptr,
146 struct wps_token *token, struct wps_data *wps_data);
147 static int parse_image_display(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 static int parse_image_load(const char *wps_bufptr,
150 struct wps_token *token, struct wps_data *wps_data);
151 #endif /*HAVE_LCD_BITMAP */
152 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
153 static int parse_image_special(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 #endif
156 #ifdef HAVE_ALBUMART
157 static int parse_albumart_load(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_albumart_display(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 #endif /* HAVE_ALBUMART */
162 #ifdef HAVE_TOUCHSCREEN
163 static int parse_touchregion(const char *wps_bufptr,
164 struct wps_token *token, struct wps_data *wps_data);
165 #else
166 static int fulline_tag_not_supported(const char *wps_bufptr,
167 struct wps_token *token, struct wps_data *wps_data)
169 (void)token; (void)wps_data;
170 return skip_end_of_line(wps_bufptr);
172 #define parse_touchregion fulline_tag_not_supported
173 #endif
174 #ifdef CONFIG_RTC
175 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
176 #else
177 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
178 #endif
180 /* array of available tags - those with more characters have to go first
181 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
182 static const struct wps_tag all_tags[] = {
184 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
185 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
186 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
188 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
189 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
190 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
191 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
192 #if CONFIG_CHARGING >= CHARGING_MONITOR
193 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
194 #endif
195 #if CONFIG_CHARGING
196 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
197 #endif
199 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
200 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
216 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
217 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
219 /* current file */
220 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
230 parse_dir_level },
232 /* next file */
233 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
243 parse_dir_level },
245 /* current metadata */
246 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
259 /* next metadata */
260 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
273 #if (CONFIG_CODEC != MAS3507D)
274 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
275 #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, parse_albumart_display },
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_number++;
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 bool skin_start_new_subline(struct skin_line *line, int curr_token)
411 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
412 if (!subline)
413 return false;
415 subline->first_token_idx = curr_token;
416 subline->next = NULL;
418 subline->line_type = 0;
419 subline->time_mult = 0;
421 line->curr_subline->last_token_idx = curr_token-1;
422 line->curr_subline->next = subline;
423 line->curr_subline = subline;
424 return true;
427 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
429 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
430 struct skin_subline *subline = NULL;
431 if (!line)
432 return false;
434 /* init the subline */
435 subline = &line->sublines;
436 subline->first_token_idx = curr_token;
437 subline->next = NULL;
438 subline->line_type = 0;
439 subline->time_mult = 0;
441 /* init the new line */
442 line->curr_subline = &line->sublines;
443 line->next = NULL;
444 line->subline_expire_time = 0;
446 /* connect to curr_line and vp pointers.
447 * 1) close the previous lines subline
448 * 2) connect to vp pointer
449 * 3) connect to curr_line global pointer
451 if (curr_line)
453 curr_line->curr_subline->last_token_idx = curr_token - 1;
454 curr_line->next = line;
455 curr_line->curr_subline = NULL;
457 curr_line = line;
458 if (!vp->lines)
459 vp->lines = line;
460 line_number++;
461 return true;
464 #ifdef HAVE_LCD_BITMAP
466 static int parse_statusbar_enable(const char *wps_bufptr,
467 struct wps_token *token,
468 struct wps_data *wps_data)
470 (void)token; /* Kill warnings */
471 wps_data->wps_sb_tag = true;
472 wps_data->show_sb_on_wps = true;
473 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
474 if (default_vp->vp.y == 0)
476 default_vp->vp.y = STATUSBAR_HEIGHT;
477 default_vp->vp.height -= STATUSBAR_HEIGHT;
479 return skip_end_of_line(wps_bufptr);
482 static int parse_statusbar_disable(const char *wps_bufptr,
483 struct wps_token *token,
484 struct wps_data *wps_data)
486 (void)token; /* Kill warnings */
487 wps_data->wps_sb_tag = true;
488 wps_data->show_sb_on_wps = false;
489 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
490 if (default_vp->vp.y == STATUSBAR_HEIGHT)
492 default_vp->vp.y = 0;
493 default_vp->vp.height += STATUSBAR_HEIGHT;
495 return skip_end_of_line(wps_bufptr);
498 static int get_image_id(int c)
500 if(c >= 'a' && c <= 'z')
501 return c - 'a';
502 else if(c >= 'A' && c <= 'Z')
503 return c - 'A' + 26;
504 else
505 return -1;
508 static char *get_image_filename(const char *start, const char* bmpdir,
509 char *buf, int buf_size)
511 const char *end = strchr(start, '|');
513 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
515 buf = "\0";
516 return NULL;
519 int bmpdirlen = strlen(bmpdir);
521 strcpy(buf, bmpdir);
522 buf[bmpdirlen] = '/';
523 memcpy( &buf[bmpdirlen + 1], start, end - start);
524 buf[bmpdirlen + 1 + end - start] = 0;
526 return buf;
529 static int parse_image_display(const char *wps_bufptr,
530 struct wps_token *token,
531 struct wps_data *wps_data)
533 char label = wps_bufptr[0];
534 int subimage;
535 struct gui_img *img;;
537 /* sanity check */
538 img = find_image(label, wps_data);
539 if (!img)
541 token->value.i = label; /* so debug works */
542 return WPS_ERROR_INVALID_PARAM;
545 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
547 if (subimage >= img->num_subimages)
548 return WPS_ERROR_INVALID_PARAM;
550 /* Store sub-image number to display in high bits */
551 token->value.i = label | (subimage << 8);
552 return 2; /* We have consumed 2 bytes */
553 } else {
554 token->value.i = label;
555 return 1; /* We have consumed 1 byte */
559 static int parse_image_load(const char *wps_bufptr,
560 struct wps_token *token,
561 struct wps_data *wps_data)
563 const char *ptr = wps_bufptr;
564 const char *pos;
565 const char* filename;
566 const char* id;
567 const char *newline;
568 int x,y;
569 struct gui_img *img;
571 /* format: %x|n|filename.bmp|x|y|
572 or %xl|n|filename.bmp|x|y|
573 or %xl|n|filename.bmp|x|y|num_subimages|
576 if (*ptr != '|')
577 return WPS_ERROR_INVALID_PARAM;
579 ptr++;
581 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
582 return WPS_ERROR_INVALID_PARAM;
584 /* Check there is a terminating | */
585 if (*ptr != '|')
586 return WPS_ERROR_INVALID_PARAM;
588 /* check the image number and load state */
589 if(find_image(*id, wps_data))
591 /* Invalid image ID */
592 return WPS_ERROR_INVALID_PARAM;
594 img = skin_buffer_alloc(sizeof(struct gui_img));
595 if (!img)
596 return WPS_ERROR_INVALID_PARAM;
597 /* save a pointer to the filename */
598 img->bm.data = (char*)filename;
599 img->label = *id;
600 img->x = x;
601 img->y = y;
602 img->num_subimages = 1;
603 img->always_display = false;
605 /* save current viewport */
606 img->vp = &curr_vp->vp;
608 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
610 img->always_display = true;
612 else
614 /* Parse the (optional) number of sub-images */
615 ptr++;
616 newline = strchr(ptr, '\n');
617 pos = strchr(ptr, '|');
618 if (pos && pos < newline)
619 img->num_subimages = atoi(ptr);
621 if (img->num_subimages <= 0)
622 return WPS_ERROR_INVALID_PARAM;
624 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
625 if (!item)
626 return WPS_ERROR_INVALID_PARAM;
627 add_to_ll_chain(&wps_data->images, item);
629 /* Skip the rest of the line */
630 return skip_end_of_line(wps_bufptr);
633 static int parse_viewport_display(const char *wps_bufptr,
634 struct wps_token *token,
635 struct wps_data *wps_data)
637 (void)wps_data;
638 char letter = wps_bufptr[0];
640 if (letter < 'a' || letter > 'z')
642 /* invalid viewport tag */
643 return WPS_ERROR_INVALID_PARAM;
645 token->value.i = letter;
646 return 1;
649 static int parse_viewport(const char *wps_bufptr,
650 struct wps_token *token,
651 struct wps_data *wps_data)
653 (void)token; /* Kill warnings */
654 const char *ptr = wps_bufptr;
656 const int screen =
657 #ifdef HAVE_REMOTE_LCD
658 wps_data->remote_wps ? SCREEN_REMOTE :
659 #endif
660 SCREEN_MAIN;
662 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
664 /* check for the optional letter to signify its a hideable viewport */
665 /* %Vl|<label>|<rest of tags>| */
666 skin_vp->hidden_flags = 0;
667 skin_vp->label = VP_NO_LABEL;
668 skin_vp->pb = NULL;
669 skin_vp->lines = NULL;
670 if (curr_line)
671 curr_line->curr_subline->last_token_idx = wps_data->num_tokens;
672 curr_line = NULL;
673 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
674 return WPS_ERROR_INVALID_PARAM;
677 if (*ptr == 'l')
679 if (*(ptr+1) == '|')
681 char label = *(ptr+2);
682 if (label >= 'a' && label <= 'z')
684 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
685 skin_vp->label = label;
687 else
688 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
689 ptr += 3;
692 if (*ptr != '|')
693 return WPS_ERROR_INVALID_PARAM;
695 ptr++;
696 struct viewport *vp = &skin_vp->vp;
697 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
699 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
700 return WPS_ERROR_INVALID_PARAM;
702 /* Check for trailing | */
703 if (*ptr != '|')
704 return WPS_ERROR_INVALID_PARAM;
707 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
708 if (!list)
709 return WPS_ERROR_INVALID_PARAM;
710 add_to_ll_chain(&wps_data->viewports, list);
711 curr_vp = skin_vp;
712 /* Skip the rest of the line */
713 return skip_end_of_line(wps_bufptr);
716 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
717 static int parse_image_special(const char *wps_bufptr,
718 struct wps_token *token,
719 struct wps_data *wps_data)
721 (void)wps_data; /* kill warning */
722 (void)token;
723 const char *pos = NULL;
724 const char *newline;
726 pos = strchr(wps_bufptr + 1, '|');
727 newline = strchr(wps_bufptr, '\n');
729 if (pos > newline)
730 return WPS_ERROR_INVALID_PARAM;
731 #if LCD_DEPTH > 1
732 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
734 /* format: %X|filename.bmp| */
735 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
737 #endif
739 /* Skip the rest of the line */
740 return skip_end_of_line(wps_bufptr);
742 #endif
744 #endif /* HAVE_LCD_BITMAP */
746 static int parse_setting(const char *wps_bufptr,
747 struct wps_token *token,
748 struct wps_data *wps_data)
750 (void)wps_data;
751 const char *ptr = wps_bufptr;
752 const char *end;
753 int i;
755 /* Find the setting's cfg_name */
756 if (*ptr != '|')
757 return WPS_ERROR_INVALID_PARAM;
758 ptr++;
759 end = strchr(ptr,'|');
760 if (!end)
761 return WPS_ERROR_INVALID_PARAM;
763 /* Find the setting */
764 for (i=0; i<nb_settings; i++)
765 if (settings[i].cfg_name &&
766 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
767 /* prevent matches on cfg_name prefixes */
768 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
769 break;
770 if (i == nb_settings)
771 return WPS_ERROR_INVALID_PARAM;
773 /* Store the setting number */
774 token->value.i = i;
776 /* Skip the rest of the line */
777 return end-ptr+2;
781 static int parse_dir_level(const char *wps_bufptr,
782 struct wps_token *token,
783 struct wps_data *wps_data)
785 char val[] = { *wps_bufptr, '\0' };
786 token->value.i = atoi(val);
787 (void)wps_data; /* Kill warnings */
788 return 1;
791 static int parse_timeout(const char *wps_bufptr,
792 struct wps_token *token,
793 struct wps_data *wps_data)
795 int skip = 0;
796 int val = 0;
797 bool have_point = false;
798 bool have_tenth = false;
800 (void)wps_data; /* Kill the warning */
802 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
804 if (*wps_bufptr != '.')
806 val *= 10;
807 val += *wps_bufptr - '0';
808 if (have_point)
810 have_tenth = true;
811 wps_bufptr++;
812 skip++;
813 break;
816 else
817 have_point = true;
819 wps_bufptr++;
820 skip++;
823 if (have_tenth == false)
824 val *= 10;
826 if (val == 0 && skip == 0)
828 /* decide what to do if no value was specified */
829 switch (token->type)
831 case WPS_TOKEN_SUBLINE_TIMEOUT:
832 return -1;
833 case WPS_TOKEN_BUTTON_VOLUME:
834 val = 10;
835 break;
838 token->value.i = val;
840 return skip;
843 static int parse_progressbar(const char *wps_bufptr,
844 struct wps_token *token,
845 struct wps_data *wps_data)
847 /* %pb or %pb|filename|x|y|width|height|
848 using - for any of the params uses "sane" values */
849 #ifdef HAVE_LCD_BITMAP
850 enum {
851 PB_FILENAME = 0,
852 PB_X,
853 PB_Y,
854 PB_WIDTH,
855 PB_HEIGHT
857 const char *filename;
858 int x, y, height, width;
859 uint32_t set = 0;
860 const char *ptr = wps_bufptr;
861 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
862 struct skin_token_list *item = new_skin_token_list_item(token, pb);
864 if (!pb || !item)
865 return WPS_ERROR_INVALID_PARAM;
867 struct viewport *vp = &curr_vp->vp;
868 #ifndef __PCTOOL__
869 int font_height = font_get(vp->font)->height;
870 #else
871 int font_height = 8;
872 #endif
873 /* we need to know what line number (viewport relative) this pb is,
874 * so count them... */
875 int line_num = -1;
876 struct skin_line *line = curr_vp->lines;
877 while (line)
879 line_num++;
880 line = line->next;
882 pb->have_bitmap_pb = false;
883 pb->bm.data = NULL; /* no bitmap specified */
885 if (*wps_bufptr != '|') /* regular old style */
887 pb->x = 0;
888 pb->width = vp->width;
889 pb->height = SYSFONT_HEIGHT-2;
890 pb->y = -line_num - 1; /* Will be computed during the rendering */
892 curr_vp->pb = pb;
893 add_to_ll_chain(&wps_data->progressbars, item);
894 return 0;
896 ptr = wps_bufptr + 1;
898 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
899 &x, &y, &width, &height)))
900 return WPS_ERROR_INVALID_PARAM;
902 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
903 pb->bm.data = (char*)filename;
905 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
906 pb->x = x;
907 else
908 pb->x = vp->x;
910 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
912 /* A zero width causes a divide-by-zero error later, so reject it */
913 if (width == 0)
914 return WPS_ERROR_INVALID_PARAM;
916 pb->width = width;
918 else
919 pb->width = vp->width - pb->x;
921 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
923 /* A zero height makes no sense - reject it */
924 if (height == 0)
925 return WPS_ERROR_INVALID_PARAM;
927 pb->height = height;
929 else
930 pb->height = font_height;
932 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
933 pb->y = y;
934 else
935 pb->y = -line_num - 1; /* Will be computed during the rendering */
937 curr_vp->pb = pb;
938 add_to_ll_chain(&wps_data->progressbars, item);
940 /* Skip the rest of the line */
941 return skip_end_of_line(wps_bufptr)-1;
942 #else
943 (void)token;
945 if (*(wps_bufptr-1) == 'f')
946 wps_data->full_line_progressbar = true;
947 else
948 wps_data->full_line_progressbar = false;
950 return 0;
952 #endif
955 #ifdef HAVE_ALBUMART
956 static int parse_albumart_load(const char *wps_bufptr,
957 struct wps_token *token,
958 struct wps_data *wps_data)
960 const char *_pos, *newline;
961 bool parsing;
962 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
963 (void)token; /* silence warning */
964 if (!aa)
965 return skip_end_of_line(wps_bufptr);
967 /* reset albumart info in wps */
968 aa->albumart_max_width = -1;
969 aa->albumart_max_height = -1;
970 aa->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
971 aa->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
972 aa->vp = &curr_vp->vp;
974 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
976 newline = strchr(wps_bufptr, '\n');
978 /* initial validation and parsing of x and y components */
979 if (*wps_bufptr != '|')
980 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
982 _pos = wps_bufptr + 1;
983 if (!isdigit(*_pos))
984 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
985 aa->albumart_x = atoi(_pos);
987 _pos = strchr(_pos, '|');
988 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
989 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
991 aa->albumart_y = atoi(_pos);
993 _pos = strchr(_pos, '|');
994 if (!_pos || _pos > newline)
995 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
996 e.g. %Cl|7|59\n */
998 /* parsing width field */
999 parsing = true;
1000 while (parsing)
1002 /* apply each modifier in turn */
1003 ++_pos;
1004 switch (*_pos)
1006 case 'l':
1007 case 'L':
1008 case '+':
1009 aa->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
1010 break;
1011 case 'c':
1012 case 'C':
1013 aa->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
1014 break;
1015 case 'r':
1016 case 'R':
1017 case '-':
1018 aa->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
1019 break;
1020 case 'd':
1021 case 'D':
1022 case 'i':
1023 case 'I':
1024 case 's':
1025 case 'S':
1026 /* simply ignored */
1027 break;
1028 default:
1029 parsing = false;
1030 break;
1033 /* extract max width data */
1034 if (*_pos != '|')
1036 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1037 return WPS_ERROR_INVALID_PARAM;
1039 aa->albumart_max_width = atoi(_pos);
1041 _pos = strchr(_pos, '|');
1042 if (!_pos || _pos > newline)
1043 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1044 e.g. %Cl|7|59|200\n */
1047 /* parsing height field */
1048 parsing = true;
1049 while (parsing)
1051 /* apply each modifier in turn */
1052 ++_pos;
1053 switch (*_pos)
1055 case 't':
1056 case 'T':
1057 case '-':
1058 aa->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1059 break;
1060 case 'c':
1061 case 'C':
1062 aa->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1063 break;
1064 case 'b':
1065 case 'B':
1066 case '+':
1067 aa->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1068 break;
1069 case 'd':
1070 case 'D':
1071 case 'i':
1072 case 'I':
1073 case 's':
1074 case 'S':
1075 /* simply ignored */
1076 break;
1077 default:
1078 parsing = false;
1079 break;
1082 /* extract max height data */
1083 if (*_pos != '|')
1085 if (!isdigit(*_pos))
1086 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1088 aa->albumart_max_height = atoi(_pos);
1090 _pos = strchr(_pos, '|');
1091 if (!_pos || _pos > newline)
1092 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1093 e.g. %Cl|7|59|200|200\n */
1096 /* if we got here, we parsed everything ok .. ! */
1097 if (aa->albumart_max_width < 0)
1098 aa->albumart_max_width = 0;
1099 else if (aa->albumart_max_width > LCD_WIDTH)
1100 aa->albumart_max_width = LCD_WIDTH;
1102 if (aa->albumart_max_height < 0)
1103 aa->albumart_max_height = 0;
1104 else if (aa->albumart_max_height > LCD_HEIGHT)
1105 aa->albumart_max_height = LCD_HEIGHT;
1107 aa->wps_uses_albumart = WPS_ALBUMART_LOAD;
1108 aa->draw = false;
1109 wps_data->albumart = aa;
1111 /* Skip the rest of the line */
1112 return skip_end_of_line(wps_bufptr);
1115 static int parse_albumart_display(const char *wps_bufptr,
1116 struct wps_token *token,
1117 struct wps_data *wps_data)
1119 (void)wps_bufptr;
1120 (void)token;
1121 if (wps_data->albumart)
1123 wps_data->albumart->vp = &curr_vp->vp;
1125 #if 0
1126 /* the old code did this so keep it here for now...
1127 * this is to allow the posibility to showing the next tracks AA! */
1128 if (wps_bufptr+1 == 'n')
1129 return 1;
1130 #endif
1131 return 0;
1133 #endif /* HAVE_ALBUMART */
1135 #ifdef HAVE_TOUCHSCREEN
1137 struct touchaction {char* s; int action;};
1138 static struct touchaction touchactions[] = {
1139 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1140 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1141 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1142 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1143 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1144 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1145 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1147 static int parse_touchregion(const char *wps_bufptr,
1148 struct wps_token *token, struct wps_data *wps_data)
1150 (void)token;
1151 unsigned i, imax;
1152 struct touchregion *region = NULL;
1153 const char *ptr = wps_bufptr;
1154 const char *action;
1155 const char pb_string[] = "progressbar";
1156 const char vol_string[] = "volume";
1157 int x,y,w,h;
1159 /* format: %T|x|y|width|height|action|
1160 * if action starts with & the area must be held to happen
1161 * action is one of:
1162 * play - play/pause playback
1163 * stop - stop playback, exit the wps
1164 * prev - prev track
1165 * next - next track
1166 * ffwd - seek forward
1167 * rwd - seek backwards
1168 * menu - go back to the main menu
1169 * browse - go back to the file/db browser
1170 * shuffle - toggle shuffle mode
1171 * repmode - cycle the repeat mode
1172 * quickscreen - go into the quickscreen
1173 * contextmenu - open the context menu
1174 * playlist - go into the playlist
1175 * pitch - go into the pitchscreen
1179 if (*ptr != '|')
1180 return WPS_ERROR_INVALID_PARAM;
1181 ptr++;
1183 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1184 return WPS_ERROR_INVALID_PARAM;
1186 /* Check there is a terminating | */
1187 if (*ptr != '|')
1188 return WPS_ERROR_INVALID_PARAM;
1190 region = skin_buffer_alloc(sizeof(struct touchregion));
1191 if (!region)
1192 return WPS_ERROR_INVALID_PARAM;
1194 /* should probably do some bounds checking here with the viewport... but later */
1195 region->action = ACTION_NONE;
1196 region->x = x;
1197 region->y = y;
1198 region->width = w;
1199 region->height = h;
1200 region->wvp = curr_vp;
1202 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1203 && *(action + sizeof(pb_string)-1) == '|')
1204 region->type = WPS_TOUCHREGION_SCROLLBAR;
1205 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1206 && *(action + sizeof(vol_string)-1) == '|')
1207 region->type = WPS_TOUCHREGION_VOLUME;
1208 else
1210 region->type = WPS_TOUCHREGION_ACTION;
1212 if (*action == '&')
1214 action++;
1215 region->repeat = true;
1217 else
1218 region->repeat = false;
1220 i = 0;
1221 imax = ARRAYLEN(touchactions);
1222 while ((region->action == ACTION_NONE) &&
1223 (i < imax))
1225 /* try to match with one of our touchregion screens */
1226 int len = strlen(touchactions[i].s);
1227 if (!strncmp(touchactions[i].s, action, len)
1228 && *(action+len) == '|')
1229 region->action = touchactions[i].action;
1230 i++;
1232 if (region->action == ACTION_NONE)
1233 return WPS_ERROR_INVALID_PARAM;
1235 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1236 if (!item)
1237 return WPS_ERROR_INVALID_PARAM;
1238 add_to_ll_chain(&wps_data->touchregions, item);
1239 return skip_end_of_line(wps_bufptr);
1241 #endif
1243 /* Parse a generic token from the given string. Return the length read */
1244 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1246 int skip = 0, taglen = 0, ret;
1247 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1248 const struct wps_tag *tag;
1250 switch(*wps_bufptr)
1253 case '%':
1254 case '<':
1255 case '|':
1256 case '>':
1257 case ';':
1258 case '#':
1259 /* escaped characters */
1260 token->type = WPS_TOKEN_CHARACTER;
1261 token->value.c = *wps_bufptr;
1262 taglen = 1;
1263 wps_data->num_tokens++;
1264 break;
1266 case '?':
1267 /* conditional tag */
1268 token->type = WPS_TOKEN_CONDITIONAL;
1269 level++;
1270 condindex[level] = wps_data->num_tokens;
1271 numoptions[level] = 1;
1272 wps_data->num_tokens++;
1273 ret = parse_token(wps_bufptr + 1, wps_data);
1274 if (ret < 0) return ret;
1275 taglen = 1 + ret;
1276 break;
1278 default:
1279 /* find what tag we have */
1280 for (tag = all_tags;
1281 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1282 tag++) ;
1284 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1285 token->type = tag->type;
1286 curr_line->curr_subline->line_type |= tag->refresh_type;
1288 /* if the tag has a special parsing function, we call it */
1289 if (tag->parse_func)
1291 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1292 if (ret < 0) return ret;
1293 skip += ret;
1296 /* Some tags we don't want to save as tokens */
1297 if (tag->type == WPS_NO_TOKEN)
1298 break;
1300 /* tags that start with 'F', 'I' or 'D' are for the next file */
1301 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1302 *(tag->name) == 'D')
1303 token->next = true;
1305 wps_data->num_tokens++;
1306 break;
1309 skip += taglen;
1310 return skip;
1313 /* Parses the WPS.
1314 data is the pointer to the structure where the parsed WPS should be stored.
1315 It is initialised.
1316 wps_bufptr points to the string containing the WPS tags */
1317 #define TOKEN_BLOCK_SIZE 128
1318 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1320 if (!data || !wps_bufptr || !*wps_bufptr)
1321 return false;
1322 enum wps_parse_error fail = PARSE_OK;
1323 int ret;
1324 int max_tokens = TOKEN_BLOCK_SIZE;
1325 size_t buf_free = 0;
1326 line_number = 1;
1327 level = -1;
1329 /* allocate enough RAM for a reasonable skin, grow as needed.
1330 * Free any used RAM before loading the images to be 100% RAM efficient */
1331 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1332 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1333 return false;
1334 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1335 data->num_tokens = 0;
1337 while (*wps_bufptr && !fail)
1339 /* first make sure there is enough room for tokens */
1340 if (max_tokens <= data->num_tokens + 5)
1342 int extra_tokens = TOKEN_BLOCK_SIZE;
1343 size_t needed = extra_tokens * sizeof(struct wps_token);
1344 /* do some smarts here to grow the array a bit */
1345 if (skin_buffer_freespace() < needed)
1347 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1348 break;
1350 skin_buffer_increment(needed, false);
1351 max_tokens += extra_tokens;
1354 switch(*wps_bufptr++)
1357 /* Regular tag */
1358 case '%':
1359 if ((ret = parse_token(wps_bufptr, data)) < 0)
1361 fail = PARSE_FAIL_COND_INVALID_PARAM;
1362 break;
1364 else if (level >= WPS_MAX_COND_LEVEL - 1)
1366 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1367 break;
1369 wps_bufptr += ret;
1370 break;
1372 /* Alternating sublines separator */
1373 case ';':
1374 if (level >= 0) /* there are unclosed conditionals */
1376 fail = PARSE_FAIL_UNCLOSED_COND;
1377 break;
1380 if (!skin_start_new_subline(curr_line, data->num_tokens))
1381 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1383 break;
1385 /* Conditional list start */
1386 case '<':
1387 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1389 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1390 break;
1393 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1394 lastcond[level] = data->num_tokens++;
1395 break;
1397 /* Conditional list end */
1398 case '>':
1399 if (level < 0) /* not in a conditional, invalid char */
1401 fail = PARSE_FAIL_INVALID_CHAR;
1402 break;
1405 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1406 if (lastcond[level])
1407 data->tokens[lastcond[level]].value.i = data->num_tokens;
1408 else
1410 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1411 break;
1414 lastcond[level] = 0;
1415 data->num_tokens++;
1416 data->tokens[condindex[level]].value.i = numoptions[level];
1417 level--;
1418 break;
1420 /* Conditional list option */
1421 case '|':
1422 if (level < 0) /* not in a conditional, invalid char */
1424 fail = PARSE_FAIL_INVALID_CHAR;
1425 break;
1428 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1429 if (lastcond[level])
1430 data->tokens[lastcond[level]].value.i = data->num_tokens;
1431 else
1433 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1434 break;
1437 lastcond[level] = data->num_tokens;
1438 numoptions[level]++;
1439 data->num_tokens++;
1440 break;
1442 /* Comment */
1443 case '#':
1444 if (level >= 0) /* there are unclosed conditionals */
1446 fail = PARSE_FAIL_UNCLOSED_COND;
1447 break;
1450 wps_bufptr += skip_end_of_line(wps_bufptr);
1451 break;
1453 /* End of this line */
1454 case '\n':
1455 if (level >= 0) /* there are unclosed conditionals */
1457 fail = PARSE_FAIL_UNCLOSED_COND;
1458 break;
1460 /* add a new token for the \n so empty lines are correct */
1461 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1462 data->tokens[data->num_tokens].value.c = '\n';
1463 data->tokens[data->num_tokens].next = false;
1464 data->num_tokens++;
1466 if (!skin_start_new_line(curr_vp, data->num_tokens))
1468 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1469 break;
1472 break;
1474 /* String */
1475 default:
1477 unsigned int len = 1;
1478 const char *string_start = wps_bufptr - 1;
1480 /* find the length of the string */
1481 while (*wps_bufptr && *wps_bufptr != '#' &&
1482 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1483 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1484 *wps_bufptr != '|' && *wps_bufptr != '\n')
1486 wps_bufptr++;
1487 len++;
1490 /* look if we already have that string */
1491 char *str;
1492 bool found = false;
1493 struct skin_token_list *list = data->strings;
1494 while (list)
1496 str = (char*)list->token->value.data;
1497 found = (strlen(str) == len &&
1498 strncmp(string_start, str, len) == 0);
1499 if (found)
1500 break; /* break here because the list item is
1501 used if its found */
1502 list = list->next;
1504 /* If a matching string is found, found is true and i is
1505 the index of the string. If not, found is false */
1507 if (!found)
1509 /* new string */
1510 str = (char*)skin_buffer_alloc(len+1);
1511 if (!str)
1513 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1514 break;
1516 strlcpy(str, string_start, len+1);
1517 struct skin_token_list *item =
1518 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1519 if(!item)
1521 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1522 break;
1524 add_to_ll_chain(&data->strings, item);
1526 else
1528 /* another occurrence of an existing string */
1529 data->tokens[data->num_tokens].value.data = list->token->value.data;
1531 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1532 data->num_tokens++;
1534 break;
1538 if (!fail && level >= 0) /* there are unclosed conditionals */
1539 fail = PARSE_FAIL_UNCLOSED_COND;
1541 if (*wps_bufptr && !fail)
1542 /* one of the limits of the while loop was exceeded */
1543 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1545 /* Success! */
1546 curr_line->curr_subline->last_token_idx = data->num_tokens;
1547 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1548 /* freeup unused tokens */
1549 skin_buffer_free_from_front(sizeof(struct wps_token)
1550 * (max_tokens - data->num_tokens));
1552 #if defined(DEBUG) || defined(SIMULATOR)
1553 if (debug)
1554 print_debug_info(data, fail, line_number);
1555 #else
1556 (void)debug;
1557 #endif
1559 return (fail == 0);
1562 static void wps_reset(struct wps_data *data)
1564 #ifdef HAVE_REMOTE_LCD
1565 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1566 #endif
1567 memset(data, 0, sizeof(*data));
1568 skin_data_init(data);
1569 #ifdef HAVE_REMOTE_LCD
1570 data->remote_wps = rwps;
1571 #endif
1574 #ifdef HAVE_LCD_BITMAP
1575 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1577 (void)wps_data; /* only needed for remote targets */
1578 bool loaded = false;
1579 char img_path[MAX_PATH];
1580 get_image_filename(bitmap->data, bmpdir,
1581 img_path, sizeof(img_path));
1583 /* load the image */
1584 int format;
1585 #ifdef HAVE_REMOTE_LCD
1586 if (wps_data->remote_wps)
1587 format = FORMAT_ANY|FORMAT_REMOTE;
1588 else
1589 #endif
1590 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1592 size_t max_buf;
1593 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1594 bitmap->data = imgbuf;
1595 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1597 if (ret > 0)
1599 skin_buffer_increment(ret, true);
1600 loaded = true;
1602 else
1604 /* Abort if we can't load an image */
1605 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1606 loaded = false;
1608 return loaded;
1611 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1613 struct skin_token_list *list;
1614 /* do the progressbars */
1615 list = wps_data->progressbars;
1616 while (list)
1618 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1619 if (pb->bm.data)
1621 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1623 list = list->next;
1625 /* regular images */
1626 list = wps_data->images;
1627 while (list)
1629 struct gui_img *img = (struct gui_img*)list->token->value.data;
1630 if (img->bm.data)
1632 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1633 if (img->loaded)
1634 img->subimage_height = img->bm.height / img->num_subimages;
1636 list = list->next;
1639 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1640 if (bmp_names[BACKDROP_BMP])
1642 int screen = SCREEN_MAIN;
1643 char img_path[MAX_PATH];
1644 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1645 img_path, sizeof(img_path));
1646 #if defined(HAVE_REMOTE_LCD)
1647 /* We only need to check LCD type if there is a remote LCD */
1648 if (wps_data->remote_wps)
1649 screen = SCREEN_REMOTE;
1650 #endif
1651 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1653 #endif /* has backdrop support */
1655 /* If we got here, everything was OK */
1656 return true;
1659 #endif /* HAVE_LCD_BITMAP */
1661 /* to setup up the wps-data from a format-buffer (isfile = false)
1662 from a (wps-)file (isfile = true)*/
1663 bool skin_data_load(struct wps_data *wps_data,
1664 struct screen *display,
1665 const char *buf,
1666 bool isfile)
1669 if (!wps_data || !buf)
1670 return false;
1671 #ifdef HAVE_ALBUMART
1672 int status;
1673 struct mp3entry *curtrack;
1674 long offset;
1675 struct skin_albumart old_aa = {.wps_uses_albumart = WPS_ALBUMART_NONE};
1676 if (wps_data->albumart)
1678 old_aa.wps_uses_albumart = wps_data->albumart->wps_uses_albumart;
1679 old_aa.albumart_max_height = wps_data->albumart->albumart_max_height;
1680 old_aa.albumart_max_width = wps_data->albumart->albumart_max_width;
1682 #endif
1684 wps_reset(wps_data);
1686 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1687 if (!curr_vp)
1688 return false;
1689 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1690 if (!list)
1691 return false;
1692 add_to_ll_chain(&wps_data->viewports, list);
1695 /* Initialise the first (default) viewport */
1696 curr_vp->label = VP_DEFAULT_LABEL;
1697 curr_vp->vp.x = 0;
1698 curr_vp->vp.width = display->getwidth();
1699 curr_vp->vp.height = display->getheight();
1700 curr_vp->pb = NULL;
1701 curr_vp->hidden_flags = 0;
1702 curr_vp->lines = NULL;
1704 curr_line = NULL;
1705 if (!skin_start_new_line(curr_vp, 0))
1706 return false;
1708 switch (statusbar_position(display->screen_type))
1710 case STATUSBAR_OFF:
1711 curr_vp->vp.y = 0;
1712 break;
1713 case STATUSBAR_TOP:
1714 curr_vp->vp.y = STATUSBAR_HEIGHT;
1715 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1716 break;
1717 case STATUSBAR_BOTTOM:
1718 curr_vp->vp.y = 0;
1719 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1720 break;
1722 #ifdef HAVE_LCD_BITMAP
1723 curr_vp->vp.font = FONT_UI;
1724 curr_vp->vp.drawmode = DRMODE_SOLID;
1725 #endif
1726 #if LCD_DEPTH > 1
1727 if (display->depth > 1)
1729 curr_vp->vp.fg_pattern = display->get_foreground();
1730 curr_vp->vp.bg_pattern = display->get_background();
1732 #endif
1733 if (!isfile)
1735 return wps_parse(wps_data, buf, false);
1737 else
1739 int fd = open_utf8(buf, O_RDONLY);
1741 if (fd < 0)
1742 return false;
1744 /* get buffer space from the plugin buffer */
1745 size_t buffersize = 0;
1746 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1748 if (!wps_buffer)
1749 return false;
1751 /* copy the file's content to the buffer for parsing,
1752 ensuring that every line ends with a newline char. */
1753 unsigned int start = 0;
1754 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1756 start += strlen(wps_buffer + start);
1757 if (start < buffersize - 1)
1759 wps_buffer[start++] = '\n';
1760 wps_buffer[start] = 0;
1764 close(fd);
1766 if (start <= 0)
1767 return false;
1769 #ifdef HAVE_LCD_BITMAP
1770 /* Set all filename pointers to NULL */
1771 memset(bmp_names, 0, sizeof(bmp_names));
1772 #endif
1774 /* parse the WPS source */
1775 if (!wps_parse(wps_data, wps_buffer, true)) {
1776 wps_reset(wps_data);
1777 return false;
1780 wps_data->wps_loaded = true;
1782 #ifdef HAVE_LCD_BITMAP
1783 /* get the bitmap dir */
1784 char bmpdir[MAX_PATH];
1785 char *dot = strrchr(buf, '.');
1787 strlcpy(bmpdir, buf, dot - buf + 1);
1789 /* load the bitmaps that were found by the parsing */
1790 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1791 wps_reset(wps_data);
1792 return false;
1794 #endif
1795 #ifdef HAVE_ALBUMART
1796 status = audio_status();
1797 if (status & AUDIO_STATUS_PLAY)
1799 struct skin_albumart *aa = wps_data->albumart;
1800 if (aa && ((aa->wps_uses_albumart && !old_aa.wps_uses_albumart) ||
1801 (aa->wps_uses_albumart &&
1802 (((old_aa.albumart_max_height != aa->albumart_max_height) ||
1803 (old_aa.albumart_max_width != aa->albumart_max_width))))))
1805 curtrack = audio_current_track();
1806 offset = curtrack->offset;
1807 audio_stop();
1808 if (!(status & AUDIO_STATUS_PAUSE))
1809 audio_play(offset);
1812 #endif
1813 #if defined(DEBUG) || defined(SIMULATOR)
1814 debug_skin_usage();
1815 #endif
1816 return true;