FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob68bddf620580f0e22273b6df64e51ca7a53f8e5d
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 "action.h"
36 #include "checkwps.h"
37 #include "audio.h"
38 #define DEBUGF printf
39 #endif /*WPSEDITOR*/
40 #else
41 #include "debug.h"
42 #endif /*__PCTOOL__*/
44 #include <ctype.h>
45 #include <stdbool.h>
46 #include "font.h"
48 #include "wps_internals.h"
49 #include "skin_engine.h"
50 #include "settings.h"
51 #include "settings_list.h"
53 #ifdef HAVE_LCD_BITMAP
54 #include "bmp.h"
55 #endif
57 #include "backdrop.h"
59 #define WPS_ERROR_INVALID_PARAM -1
61 /* level of current conditional.
62 -1 means we're not in a conditional. */
63 static int level = -1;
65 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
66 or WPS_TOKEN_CONDITIONAL_START in current level */
67 static int lastcond[WPS_MAX_COND_LEVEL];
69 /* index of the WPS_TOKEN_CONDITIONAL in current level */
70 static int condindex[WPS_MAX_COND_LEVEL];
72 /* number of condtional options in current level */
73 static int numoptions[WPS_MAX_COND_LEVEL];
75 /* the current line in the file */
76 static int line;
78 /* the current viewport */
79 static struct skin_viewport *curr_vp;
81 #ifdef HAVE_LCD_BITMAP
83 #if LCD_DEPTH > 1
84 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
85 #else
86 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
87 #endif
89 #define PROGRESSBAR_BMP MAX_IMAGES
90 #define BACKDROP_BMP (MAX_BITMAPS-1)
92 /* pointers to the bitmap filenames in the WPS source */
93 static const char *bmp_names[MAX_BITMAPS];
95 #endif /* HAVE_LCD_BITMAP */
97 #if defined(DEBUG) || defined(SIMULATOR)
98 /* debugging function */
99 extern void print_debug_info(struct wps_data *data, int fail, int line);
100 extern void debug_skin_usage(void);
101 #endif
103 static void wps_reset(struct wps_data *data);
105 /* Function for parsing of details for a token. At the moment the
106 function is called, the token type has already been set. The
107 function must fill in the details and possibly add more tokens
108 to the token array. It should return the number of chars that
109 has been consumed.
111 wps_bufptr points to the char following the tag (i.e. where
112 details begin).
113 token is the pointer to the 'main' token being parsed
115 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
116 struct wps_token *token, struct wps_data *wps_data);
118 struct wps_tag {
119 enum wps_token_type type;
120 const char name[3];
121 unsigned char refresh_type;
122 const wps_tag_parse_func parse_func;
124 static int skip_end_of_line(const char *wps_bufptr);
125 /* prototypes of all special parse functions : */
126 static int parse_timeout(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_progressbar(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_dir_level(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
132 static int parse_setting(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
135 #ifdef HAVE_LCD_BITMAP
136 static int parse_viewport_display(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_viewport(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_statusbar_enable(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_statusbar_disable(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_image_display(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 static int parse_image_load(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 #endif /*HAVE_LCD_BITMAP */
149 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
150 static int parse_image_special(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 #endif
153 #ifdef HAVE_ALBUMART
154 static int parse_albumart_load(const char *wps_bufptr,
155 struct wps_token *token, struct wps_data *wps_data);
156 static int parse_albumart_conditional(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 #endif /* HAVE_ALBUMART */
159 #ifdef HAVE_TOUCHSCREEN
160 static int parse_touchregion(const char *wps_bufptr,
161 struct wps_token *token, struct wps_data *wps_data);
162 #else
163 static int fulline_tag_not_supported(const char *wps_bufptr,
164 struct wps_token *token, struct wps_data *wps_data)
166 (void)token; (void)wps_data;
167 return skip_end_of_line(wps_bufptr);
169 #define parse_touchregion fulline_tag_not_supported
170 #endif
171 #ifdef CONFIG_RTC
172 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
173 #else
174 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
175 #endif
177 /* array of available tags - those with more characters have to go first
178 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
179 static const struct wps_tag all_tags[] = {
181 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
182 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
183 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
185 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
186 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
187 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
188 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
189 #if CONFIG_CHARGING >= CHARGING_MONITOR
190 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
191 #endif
192 #if CONFIG_CHARGING
193 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
194 #endif
196 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
197 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
216 /* current file */
217 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
227 parse_dir_level },
229 /* next file */
230 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
240 parse_dir_level },
242 /* current metadata */
243 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
256 /* next metadata */
257 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
270 #if (CONFIG_CODEC != MAS3507D)
271 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
272 #endif
274 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
275 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
276 #endif
278 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
280 #ifdef HAS_REMOTE_BUTTON_HOLD
281 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
282 #else
283 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
284 #endif
286 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
287 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
288 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
289 parse_timeout },
291 #ifdef HAVE_LCD_BITMAP
292 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
293 #else
294 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
295 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
296 #endif
297 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
298 parse_progressbar },
300 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
302 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
303 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
304 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
305 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
307 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
308 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
309 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
310 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
312 #ifdef HAVE_TAGCACHE
313 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
314 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
315 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
316 #endif
318 #if CONFIG_CODEC == SWCODEC
319 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
320 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
321 #endif
323 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
324 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
326 #ifdef HAVE_LCD_BITMAP
327 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
328 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
330 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
332 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
333 parse_image_display },
335 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
336 #ifdef HAVE_ALBUMART
337 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
338 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
339 parse_albumart_conditional },
340 #endif
342 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
343 parse_viewport_display },
344 { WPS_NO_TOKEN, "V", 0, parse_viewport },
346 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
347 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
348 #endif
349 #endif
351 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
353 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
354 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
356 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
357 /* the array MUST end with an empty string (first char is \0) */
361 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
362 * chains require the order to be kept.
364 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
366 if (*list == NULL)
367 *list = item;
368 else
370 struct skin_token_list *t = *list;
371 while (t->next)
372 t = t->next;
373 t->next = item;
376 /* create and init a new wpsll item.
377 * passing NULL to token will alloc a new one.
378 * You should only pass NULL for the token when the token type (table above)
379 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
381 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
382 void* token_data)
384 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
385 if (!token)
386 token = skin_buffer_alloc(sizeof(struct wps_token));
387 if (!llitem || !token)
388 return NULL;
389 llitem->next = NULL;
390 llitem->token = token;
391 if (token_data)
392 llitem->token->value.data = token_data;
393 return llitem;
396 /* Returns the number of chars that should be skipped to jump
397 immediately after the first eol, i.e. to the start of the next line */
398 static int skip_end_of_line(const char *wps_bufptr)
400 line++;
401 int skip = 0;
402 while(*(wps_bufptr + skip) != '\n')
403 skip++;
404 return ++skip;
407 /* Starts a new subline in the current line during parsing */
408 static void wps_start_new_subline(struct wps_data *data)
410 data->num_sublines++;
411 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
412 data->lines[data->num_lines].num_sublines++;
415 #ifdef HAVE_LCD_BITMAP
417 static int parse_statusbar_enable(const char *wps_bufptr,
418 struct wps_token *token,
419 struct wps_data *wps_data)
421 (void)token; /* Kill warnings */
422 wps_data->wps_sb_tag = true;
423 wps_data->show_sb_on_wps = true;
424 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
425 if (default_vp->vp.y == 0)
427 default_vp->vp.y = STATUSBAR_HEIGHT;
428 default_vp->vp.height -= STATUSBAR_HEIGHT;
430 return skip_end_of_line(wps_bufptr);
433 static int parse_statusbar_disable(const char *wps_bufptr,
434 struct wps_token *token,
435 struct wps_data *wps_data)
437 (void)token; /* Kill warnings */
438 wps_data->wps_sb_tag = true;
439 wps_data->show_sb_on_wps = false;
440 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
441 if (default_vp->vp.y == STATUSBAR_HEIGHT)
443 default_vp->vp.y = 0;
444 default_vp->vp.height += STATUSBAR_HEIGHT;
446 return skip_end_of_line(wps_bufptr);
449 static int get_image_id(int c)
451 if(c >= 'a' && c <= 'z')
452 return c - 'a';
453 else if(c >= 'A' && c <= 'Z')
454 return c - 'A' + 26;
455 else
456 return -1;
459 static char *get_image_filename(const char *start, const char* bmpdir,
460 char *buf, int buf_size)
462 const char *end = strchr(start, '|');
464 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
466 buf = "\0";
467 return NULL;
470 int bmpdirlen = strlen(bmpdir);
472 strcpy(buf, bmpdir);
473 buf[bmpdirlen] = '/';
474 memcpy( &buf[bmpdirlen + 1], start, end - start);
475 buf[bmpdirlen + 1 + end - start] = 0;
477 return buf;
480 static int parse_image_display(const char *wps_bufptr,
481 struct wps_token *token,
482 struct wps_data *wps_data)
484 char label = wps_bufptr[0];
485 int subimage;
486 struct gui_img *img;;
488 /* sanity check */
489 img = find_image(label, wps_data);
490 if (!img)
492 token->value.i = label; /* do debug works */
493 return WPS_ERROR_INVALID_PARAM;
496 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
498 if (subimage >= img->num_subimages)
499 return WPS_ERROR_INVALID_PARAM;
501 /* Store sub-image number to display in high bits */
502 token->value.i = label | (subimage << 8);
503 return 2; /* We have consumed 2 bytes */
504 } else {
505 token->value.i = label;
506 return 1; /* We have consumed 1 byte */
510 static int parse_image_load(const char *wps_bufptr,
511 struct wps_token *token,
512 struct wps_data *wps_data)
514 const char *ptr = wps_bufptr;
515 const char *pos;
516 const char* filename;
517 const char* id;
518 const char *newline;
519 int x,y;
520 struct gui_img *img;
522 /* format: %x|n|filename.bmp|x|y|
523 or %xl|n|filename.bmp|x|y|
524 or %xl|n|filename.bmp|x|y|num_subimages|
527 if (*ptr != '|')
528 return WPS_ERROR_INVALID_PARAM;
530 ptr++;
532 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
533 return WPS_ERROR_INVALID_PARAM;
535 /* Check there is a terminating | */
536 if (*ptr != '|')
537 return WPS_ERROR_INVALID_PARAM;
539 /* check the image number and load state */
540 if(find_image(*id, wps_data))
542 /* Invalid image ID */
543 return WPS_ERROR_INVALID_PARAM;
545 img = skin_buffer_alloc(sizeof(struct gui_img));
546 if (!img)
547 return WPS_ERROR_INVALID_PARAM;
548 /* save a pointer to the filename */
549 img->bm.data = (char*)filename;
550 img->label = *id;
551 img->x = x;
552 img->y = y;
553 img->num_subimages = 1;
554 img->always_display = false;
556 /* save current viewport */
557 img->vp = &curr_vp->vp;
559 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
561 img->always_display = true;
563 else
565 /* Parse the (optional) number of sub-images */
566 ptr++;
567 newline = strchr(ptr, '\n');
568 pos = strchr(ptr, '|');
569 if (pos && pos < newline)
570 img->num_subimages = atoi(ptr);
572 if (img->num_subimages <= 0)
573 return WPS_ERROR_INVALID_PARAM;
575 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
576 if (!item)
577 return WPS_ERROR_INVALID_PARAM;
578 add_to_ll_chain(&wps_data->images, item);
580 /* Skip the rest of the line */
581 return skip_end_of_line(wps_bufptr);
584 static int parse_viewport_display(const char *wps_bufptr,
585 struct wps_token *token,
586 struct wps_data *wps_data)
588 (void)wps_data;
589 char letter = wps_bufptr[0];
591 if (letter < 'a' || letter > 'z')
593 /* invalid viewport tag */
594 return WPS_ERROR_INVALID_PARAM;
596 token->value.i = letter;
597 return 1;
600 static int parse_viewport(const char *wps_bufptr,
601 struct wps_token *token,
602 struct wps_data *wps_data)
604 (void)token; /* Kill warnings */
605 const char *ptr = wps_bufptr;
607 const int screen =
608 #ifdef HAVE_REMOTE_LCD
609 wps_data->remote_wps ? SCREEN_REMOTE :
610 #endif
611 SCREEN_MAIN;
613 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
615 /* check for the optional letter to signify its a hideable viewport */
616 /* %Vl|<label>|<rest of tags>| */
617 skin_vp->hidden_flags = 0;
618 skin_vp->label = VP_NO_LABEL;
619 skin_vp->pb = NULL;
621 if (*ptr == 'l')
623 if (*(ptr+1) == '|')
625 char label = *(ptr+2);
626 if (label >= 'a' && label <= 'z')
628 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
629 skin_vp->label = label;
631 else
632 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
633 ptr += 3;
636 if (*ptr != '|')
637 return WPS_ERROR_INVALID_PARAM;
639 ptr++;
640 struct viewport *vp = &skin_vp->vp;
641 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
643 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
644 return WPS_ERROR_INVALID_PARAM;
646 /* Check for trailing | */
647 if (*ptr != '|')
648 return WPS_ERROR_INVALID_PARAM;
650 curr_vp->last_line = wps_data->num_lines - 1;
652 skin_vp->first_line = wps_data->num_lines;
654 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
656 wps_data->lines[wps_data->num_lines].first_subline_idx =
657 wps_data->num_sublines;
659 wps_data->sublines[wps_data->num_sublines].first_token_idx =
660 wps_data->num_tokens;
663 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
664 if (!list)
665 return WPS_ERROR_INVALID_PARAM;
666 add_to_ll_chain(&wps_data->viewports, list);
667 curr_vp = skin_vp;
668 /* Skip the rest of the line */
669 return skip_end_of_line(wps_bufptr);
672 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
673 static int parse_image_special(const char *wps_bufptr,
674 struct wps_token *token,
675 struct wps_data *wps_data)
677 (void)wps_data; /* kill warning */
678 (void)token;
679 const char *pos = NULL;
680 const char *newline;
682 pos = strchr(wps_bufptr + 1, '|');
683 newline = strchr(wps_bufptr, '\n');
685 if (pos > newline)
686 return WPS_ERROR_INVALID_PARAM;
687 #if LCD_DEPTH > 1
688 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
690 /* format: %X|filename.bmp| */
691 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
693 #endif
695 /* Skip the rest of the line */
696 return skip_end_of_line(wps_bufptr);
698 #endif
700 #endif /* HAVE_LCD_BITMAP */
702 static int parse_setting(const char *wps_bufptr,
703 struct wps_token *token,
704 struct wps_data *wps_data)
706 (void)wps_data;
707 const char *ptr = wps_bufptr;
708 const char *end;
709 int i;
711 /* Find the setting's cfg_name */
712 if (*ptr != '|')
713 return WPS_ERROR_INVALID_PARAM;
714 ptr++;
715 end = strchr(ptr,'|');
716 if (!end)
717 return WPS_ERROR_INVALID_PARAM;
719 /* Find the setting */
720 for (i=0; i<nb_settings; i++)
721 if (settings[i].cfg_name &&
722 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
723 /* prevent matches on cfg_name prefixes */
724 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
725 break;
726 if (i == nb_settings)
727 return WPS_ERROR_INVALID_PARAM;
729 /* Store the setting number */
730 token->value.i = i;
732 /* Skip the rest of the line */
733 return end-ptr+2;
737 static int parse_dir_level(const char *wps_bufptr,
738 struct wps_token *token,
739 struct wps_data *wps_data)
741 char val[] = { *wps_bufptr, '\0' };
742 token->value.i = atoi(val);
743 (void)wps_data; /* Kill warnings */
744 return 1;
747 static int parse_timeout(const char *wps_bufptr,
748 struct wps_token *token,
749 struct wps_data *wps_data)
751 int skip = 0;
752 int val = 0;
753 bool have_point = false;
754 bool have_tenth = false;
756 (void)wps_data; /* Kill the warning */
758 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
760 if (*wps_bufptr != '.')
762 val *= 10;
763 val += *wps_bufptr - '0';
764 if (have_point)
766 have_tenth = true;
767 wps_bufptr++;
768 skip++;
769 break;
772 else
773 have_point = true;
775 wps_bufptr++;
776 skip++;
779 if (have_tenth == false)
780 val *= 10;
782 if (val == 0 && skip == 0)
784 /* decide what to do if no value was specified */
785 switch (token->type)
787 case WPS_TOKEN_SUBLINE_TIMEOUT:
788 return -1;
789 case WPS_TOKEN_BUTTON_VOLUME:
790 val = 10;
791 break;
794 token->value.i = val;
796 return skip;
799 static int parse_progressbar(const char *wps_bufptr,
800 struct wps_token *token,
801 struct wps_data *wps_data)
803 /* %pb or %pb|filename|x|y|width|height|
804 using - for any of the params uses "sane" values */
805 #ifdef HAVE_LCD_BITMAP
806 enum {
807 PB_FILENAME = 0,
808 PB_X,
809 PB_Y,
810 PB_WIDTH,
811 PB_HEIGHT
813 const char *filename;
814 int x, y, height, width;
815 uint32_t set = 0;
816 const char *ptr = wps_bufptr;
817 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
818 struct skin_token_list *item = new_skin_token_list_item(token, pb);
820 if (!pb || !item)
821 return WPS_ERROR_INVALID_PARAM;
823 struct viewport *vp = &curr_vp->vp;
824 #ifndef __PCTOOL__
825 int font_height = font_get(vp->font)->height;
826 #else
827 int font_height = 8;
828 #endif
829 int line_num = wps_data->num_lines - curr_vp->first_line;
831 pb->have_bitmap_pb = false;
832 pb->bm.data = NULL; /* no bitmap specified */
834 if (*wps_bufptr != '|') /* regular old style */
836 pb->x = 0;
837 pb->width = vp->width;
838 pb->height = SYSFONT_HEIGHT-2;
839 pb->y = -line_num - 1; /* Will be computed during the rendering */
841 curr_vp->pb = pb;
842 add_to_ll_chain(&wps_data->progressbars, item);
843 return 0;
845 ptr = wps_bufptr + 1;
847 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
848 &x, &y, &width, &height)))
849 return WPS_ERROR_INVALID_PARAM;
851 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
852 pb->bm.data = (char*)filename;
854 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
855 pb->x = x;
856 else
857 pb->x = vp->x;
859 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
861 /* A zero width causes a divide-by-zero error later, so reject it */
862 if (width == 0)
863 return WPS_ERROR_INVALID_PARAM;
865 pb->width = width;
867 else
868 pb->width = vp->width - pb->x;
870 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
872 /* A zero height makes no sense - reject it */
873 if (height == 0)
874 return WPS_ERROR_INVALID_PARAM;
876 pb->height = height;
878 else
879 pb->height = font_height;
881 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
882 pb->y = y;
883 else
884 pb->y = -line_num - 1; /* Will be computed during the rendering */
886 curr_vp->pb = pb;
887 add_to_ll_chain(&wps_data->progressbars, item);
889 /* Skip the rest of the line */
890 return skip_end_of_line(wps_bufptr)-1;
891 #else
892 (void)token;
894 if (*(wps_bufptr-1) == 'f')
895 wps_data->full_line_progressbar = true;
896 else
897 wps_data->full_line_progressbar = false;
899 return 0;
901 #endif
904 #ifdef HAVE_ALBUMART
905 static int parse_albumart_load(const char *wps_bufptr,
906 struct wps_token *token,
907 struct wps_data *wps_data)
909 const char *_pos, *newline;
910 bool parsing;
911 (void)token; /* silence warning */
913 /* reset albumart info in wps */
914 wps_data->albumart_max_width = -1;
915 wps_data->albumart_max_height = -1;
916 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
917 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
919 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
921 newline = strchr(wps_bufptr, '\n');
923 /* initial validation and parsing of x and y components */
924 if (*wps_bufptr != '|')
925 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
927 _pos = wps_bufptr + 1;
928 if (!isdigit(*_pos))
929 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
930 wps_data->albumart_x = atoi(_pos);
932 _pos = strchr(_pos, '|');
933 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
934 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
936 wps_data->albumart_y = atoi(_pos);
938 _pos = strchr(_pos, '|');
939 if (!_pos || _pos > newline)
940 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
941 e.g. %Cl|7|59\n */
943 /* parsing width field */
944 parsing = true;
945 while (parsing)
947 /* apply each modifier in turn */
948 ++_pos;
949 switch (*_pos)
951 case 'l':
952 case 'L':
953 case '+':
954 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
955 break;
956 case 'c':
957 case 'C':
958 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
959 break;
960 case 'r':
961 case 'R':
962 case '-':
963 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
964 break;
965 case 'd':
966 case 'D':
967 case 'i':
968 case 'I':
969 case 's':
970 case 'S':
971 /* simply ignored */
972 break;
973 default:
974 parsing = false;
975 break;
978 /* extract max width data */
979 if (*_pos != '|')
981 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
982 return WPS_ERROR_INVALID_PARAM;
984 wps_data->albumart_max_width = atoi(_pos);
986 _pos = strchr(_pos, '|');
987 if (!_pos || _pos > newline)
988 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
989 e.g. %Cl|7|59|200\n */
992 /* parsing height field */
993 parsing = true;
994 while (parsing)
996 /* apply each modifier in turn */
997 ++_pos;
998 switch (*_pos)
1000 case 't':
1001 case 'T':
1002 case '-':
1003 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1004 break;
1005 case 'c':
1006 case 'C':
1007 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1008 break;
1009 case 'b':
1010 case 'B':
1011 case '+':
1012 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1013 break;
1014 case 'd':
1015 case 'D':
1016 case 'i':
1017 case 'I':
1018 case 's':
1019 case 'S':
1020 /* simply ignored */
1021 break;
1022 default:
1023 parsing = false;
1024 break;
1027 /* extract max height data */
1028 if (*_pos != '|')
1030 if (!isdigit(*_pos))
1031 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1033 wps_data->albumart_max_height = atoi(_pos);
1035 _pos = strchr(_pos, '|');
1036 if (!_pos || _pos > newline)
1037 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1038 e.g. %Cl|7|59|200|200\n */
1041 /* if we got here, we parsed everything ok .. ! */
1042 if (wps_data->albumart_max_width < 0)
1043 wps_data->albumart_max_width = 0;
1044 else if (wps_data->albumart_max_width > LCD_WIDTH)
1045 wps_data->albumart_max_width = LCD_WIDTH;
1047 if (wps_data->albumart_max_height < 0)
1048 wps_data->albumart_max_height = 0;
1049 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1050 wps_data->albumart_max_height = LCD_HEIGHT;
1052 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1054 /* Skip the rest of the line */
1055 return skip_end_of_line(wps_bufptr);
1058 static int parse_albumart_conditional(const char *wps_bufptr,
1059 struct wps_token *token,
1060 struct wps_data *wps_data)
1062 struct wps_token *prevtoken = token;
1063 --prevtoken;
1064 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1066 /* This %C is part of a %?C construct.
1067 It's either %?C<blah> or %?Cn<blah> */
1068 token->type = WPS_TOKEN_ALBUMART_FOUND;
1069 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1071 token->next = true;
1072 return 1;
1074 else if (*wps_bufptr == '<')
1076 return 0;
1078 else
1080 token->type = WPS_NO_TOKEN;
1081 return 0;
1084 else
1086 /* This %C tag is in a conditional construct. */
1087 wps_data->albumart_cond_index = condindex[level];
1088 return 0;
1091 #endif /* HAVE_ALBUMART */
1093 #ifdef HAVE_TOUCHSCREEN
1095 struct touchaction {char* s; int action;};
1096 static struct touchaction touchactions[] = {
1097 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1098 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1099 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1100 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1101 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1102 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1103 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1105 static int parse_touchregion(const char *wps_bufptr,
1106 struct wps_token *token, struct wps_data *wps_data)
1108 (void)token;
1109 unsigned i, imax;
1110 struct touchregion *region = NULL;
1111 const char *ptr = wps_bufptr;
1112 const char *action;
1113 const char pb_string[] = "progressbar";
1114 const char vol_string[] = "volume";
1115 int x,y,w,h;
1117 /* format: %T|x|y|width|height|action|
1118 * if action starts with & the area must be held to happen
1119 * action is one of:
1120 * play - play/pause playback
1121 * stop - stop playback, exit the wps
1122 * prev - prev track
1123 * next - next track
1124 * ffwd - seek forward
1125 * rwd - seek backwards
1126 * menu - go back to the main menu
1127 * browse - go back to the file/db browser
1128 * shuffle - toggle shuffle mode
1129 * repmode - cycle the repeat mode
1130 * quickscreen - go into the quickscreen
1131 * contextmenu - open the context menu
1132 * playlist - go into the playlist
1133 * pitch - go into the pitchscreen
1137 if (*ptr != '|')
1138 return WPS_ERROR_INVALID_PARAM;
1139 ptr++;
1141 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1142 return WPS_ERROR_INVALID_PARAM;
1144 /* Check there is a terminating | */
1145 if (*ptr != '|')
1146 return WPS_ERROR_INVALID_PARAM;
1148 region = skin_buffer_alloc(sizeof(struct touchregion));
1149 if (!region)
1150 return WPS_ERROR_INVALID_PARAM;
1152 /* should probably do some bounds checking here with the viewport... but later */
1153 region->action = ACTION_NONE;
1154 region->x = x;
1155 region->y = y;
1156 region->width = w;
1157 region->height = h;
1158 region->wvp = curr_vp;
1160 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1161 && *(action + sizeof(pb_string)-1) == '|')
1162 region->type = WPS_TOUCHREGION_SCROLLBAR;
1163 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1164 && *(action + sizeof(vol_string)-1) == '|')
1165 region->type = WPS_TOUCHREGION_VOLUME;
1166 else
1168 region->type = WPS_TOUCHREGION_ACTION;
1170 if (*action == '&')
1172 action++;
1173 region->repeat = true;
1175 else
1176 region->repeat = false;
1178 i = 0;
1179 imax = ARRAYLEN(touchactions);
1180 while ((region->action == ACTION_NONE) &&
1181 (i < imax))
1183 /* try to match with one of our touchregion screens */
1184 int len = strlen(touchactions[i].s);
1185 if (!strncmp(touchactions[i].s, action, len)
1186 && *(action+len) == '|')
1187 region->action = touchactions[i].action;
1188 i++;
1190 if (region->action == ACTION_NONE)
1191 return WPS_ERROR_INVALID_PARAM;
1193 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1194 if (!item)
1195 return WPS_ERROR_INVALID_PARAM;
1196 add_to_ll_chain(&wps_data->touchregions, item);
1197 return skip_end_of_line(wps_bufptr);
1199 #endif
1201 /* Parse a generic token from the given string. Return the length read */
1202 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1204 int skip = 0, taglen = 0, ret;
1205 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1206 const struct wps_tag *tag;
1208 switch(*wps_bufptr)
1211 case '%':
1212 case '<':
1213 case '|':
1214 case '>':
1215 case ';':
1216 case '#':
1217 /* escaped characters */
1218 token->type = WPS_TOKEN_CHARACTER;
1219 token->value.c = *wps_bufptr;
1220 taglen = 1;
1221 wps_data->num_tokens++;
1222 break;
1224 case '?':
1225 /* conditional tag */
1226 token->type = WPS_TOKEN_CONDITIONAL;
1227 level++;
1228 condindex[level] = wps_data->num_tokens;
1229 numoptions[level] = 1;
1230 wps_data->num_tokens++;
1231 ret = parse_token(wps_bufptr + 1, wps_data);
1232 if (ret < 0) return ret;
1233 taglen = 1 + ret;
1234 break;
1236 default:
1237 /* find what tag we have */
1238 for (tag = all_tags;
1239 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1240 tag++) ;
1242 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1243 token->type = tag->type;
1244 wps_data->sublines[wps_data->num_sublines].line_type |=
1245 tag->refresh_type;
1247 /* if the tag has a special parsing function, we call it */
1248 if (tag->parse_func)
1250 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1251 if (ret < 0) return ret;
1252 skip += ret;
1255 /* Some tags we don't want to save as tokens */
1256 if (tag->type == WPS_NO_TOKEN)
1257 break;
1259 /* tags that start with 'F', 'I' or 'D' are for the next file */
1260 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1261 *(tag->name) == 'D')
1262 token->next = true;
1264 wps_data->num_tokens++;
1265 break;
1268 skip += taglen;
1269 return skip;
1272 /* Parses the WPS.
1273 data is the pointer to the structure where the parsed WPS should be stored.
1274 It is initialised.
1275 wps_bufptr points to the string containing the WPS tags */
1276 #define TOKEN_BLOCK_SIZE 128
1277 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1279 if (!data || !wps_bufptr || !*wps_bufptr)
1280 return false;
1281 enum wps_parse_error fail = PARSE_OK;
1282 int ret;
1283 int max_tokens = TOKEN_BLOCK_SIZE;
1284 size_t buf_free = 0;
1285 line = 1;
1286 level = -1;
1288 /* allocate enough RAM for a reasonable skin, grow as needed.
1289 * Free any used RAM before loading the images to be 100% RAM efficient */
1290 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1291 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1292 return false;
1293 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1294 data->num_tokens = 0;
1296 while(*wps_bufptr && !fail
1297 && data->num_lines < WPS_MAX_LINES)
1299 /* first make sure there is enough room for tokens */
1300 if (max_tokens -1 == data->num_tokens)
1302 int extra_tokens = TOKEN_BLOCK_SIZE;
1303 size_t needed = extra_tokens * sizeof(struct wps_token);
1304 /* do some smarts here to grow the array a bit */
1305 if (skin_buffer_freespace() < needed)
1307 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1308 break;
1310 skin_buffer_increment(needed, false);
1311 max_tokens += extra_tokens;
1314 switch(*wps_bufptr++)
1317 /* Regular tag */
1318 case '%':
1319 if ((ret = parse_token(wps_bufptr, data)) < 0)
1321 fail = PARSE_FAIL_COND_INVALID_PARAM;
1322 break;
1324 else if (level >= WPS_MAX_COND_LEVEL - 1)
1326 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1327 break;
1329 wps_bufptr += ret;
1330 break;
1332 /* Alternating sublines separator */
1333 case ';':
1334 if (level >= 0) /* there are unclosed conditionals */
1336 fail = PARSE_FAIL_UNCLOSED_COND;
1337 break;
1340 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1341 wps_start_new_subline(data);
1342 else
1343 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1345 break;
1347 /* Conditional list start */
1348 case '<':
1349 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1351 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1352 break;
1355 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1356 lastcond[level] = data->num_tokens++;
1357 break;
1359 /* Conditional list end */
1360 case '>':
1361 if (level < 0) /* not in a conditional, invalid char */
1363 fail = PARSE_FAIL_INVALID_CHAR;
1364 break;
1367 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1368 if (lastcond[level])
1369 data->tokens[lastcond[level]].value.i = data->num_tokens;
1370 else
1372 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1373 break;
1376 lastcond[level] = 0;
1377 data->num_tokens++;
1378 data->tokens[condindex[level]].value.i = numoptions[level];
1379 level--;
1380 break;
1382 /* Conditional list option */
1383 case '|':
1384 if (level < 0) /* not in a conditional, invalid char */
1386 fail = PARSE_FAIL_INVALID_CHAR;
1387 break;
1390 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1391 if (lastcond[level])
1392 data->tokens[lastcond[level]].value.i = data->num_tokens;
1393 else
1395 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1396 break;
1399 lastcond[level] = data->num_tokens;
1400 numoptions[level]++;
1401 data->num_tokens++;
1402 break;
1404 /* Comment */
1405 case '#':
1406 if (level >= 0) /* there are unclosed conditionals */
1408 fail = PARSE_FAIL_UNCLOSED_COND;
1409 break;
1412 wps_bufptr += skip_end_of_line(wps_bufptr);
1413 break;
1415 /* End of this line */
1416 case '\n':
1417 if (level >= 0) /* there are unclosed conditionals */
1419 fail = PARSE_FAIL_UNCLOSED_COND;
1420 break;
1423 line++;
1424 wps_start_new_subline(data);
1425 data->num_lines++; /* Start a new line */
1427 if ((data->num_lines < WPS_MAX_LINES) &&
1428 (data->num_sublines < WPS_MAX_SUBLINES))
1430 data->lines[data->num_lines].first_subline_idx =
1431 data->num_sublines;
1433 data->sublines[data->num_sublines].first_token_idx =
1434 data->num_tokens;
1437 break;
1439 /* String */
1440 default:
1442 unsigned int len = 1;
1443 const char *string_start = wps_bufptr - 1;
1445 /* find the length of the string */
1446 while (*wps_bufptr && *wps_bufptr != '#' &&
1447 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1448 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1449 *wps_bufptr != '|' && *wps_bufptr != '\n')
1451 wps_bufptr++;
1452 len++;
1455 /* look if we already have that string */
1456 char *str;
1457 bool found = false;
1458 struct skin_token_list *list = data->strings;
1459 while (list)
1461 str = (char*)list->token->value.data;
1462 found = (strlen(str) == len &&
1463 strncmp(string_start, str, len) == 0);
1464 if (found)
1465 break; /* break here because the list item is
1466 used if its found */
1467 list = list->next;
1469 /* If a matching string is found, found is true and i is
1470 the index of the string. If not, found is false */
1472 if (!found)
1474 /* new string */
1475 str = (char*)skin_buffer_alloc(len+1);
1476 if (!str)
1478 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1479 break;
1481 strlcpy(str, string_start, len+1);
1482 struct skin_token_list *item =
1483 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1484 if(!item)
1486 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1487 break;
1489 add_to_ll_chain(&data->strings, item);
1491 else
1493 /* another occurrence of an existing string */
1494 data->tokens[data->num_tokens].value.data = list->token->value.data;
1496 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1497 data->num_tokens++;
1499 break;
1503 if (!fail && level >= 0) /* there are unclosed conditionals */
1504 fail = PARSE_FAIL_UNCLOSED_COND;
1506 if (*wps_bufptr && !fail)
1507 /* one of the limits of the while loop was exceeded */
1508 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1510 /* Success! */
1511 /* freeup unused tokens */
1512 skin_buffer_free_from_front(sizeof(struct wps_token)
1513 * (max_tokens - data->num_tokens));
1514 curr_vp->last_line = data->num_lines - 1;
1516 #if defined(DEBUG) || defined(SIMULATOR)
1517 if (debug)
1518 print_debug_info(data, fail, line);
1519 #else
1520 (void)debug;
1521 #endif
1523 return (fail == 0);
1526 static void wps_reset(struct wps_data *data)
1528 #ifdef HAVE_REMOTE_LCD
1529 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1530 #endif
1531 memset(data, 0, sizeof(*data));
1532 skin_data_init(data);
1533 #ifdef HAVE_REMOTE_LCD
1534 data->remote_wps = rwps;
1535 #endif
1538 #ifdef HAVE_LCD_BITMAP
1539 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1541 (void)wps_data; /* only needed for remote targets */
1542 bool loaded = false;
1543 char img_path[MAX_PATH];
1544 get_image_filename(bitmap->data, bmpdir,
1545 img_path, sizeof(img_path));
1547 /* load the image */
1548 int format;
1549 #ifdef HAVE_REMOTE_LCD
1550 if (wps_data->remote_wps)
1551 format = FORMAT_ANY|FORMAT_REMOTE;
1552 else
1553 #endif
1554 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1556 size_t max_buf;
1557 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1558 bitmap->data = imgbuf;
1559 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1561 if (ret > 0)
1563 skin_buffer_increment(ret, true);
1564 loaded = true;
1566 else
1568 /* Abort if we can't load an image */
1569 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1570 loaded = false;
1572 return loaded;
1575 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1577 struct skin_token_list *list;
1578 /* do the progressbars */
1579 list = wps_data->progressbars;
1580 while (list)
1582 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1583 if (pb->bm.data)
1585 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1587 list = list->next;
1589 /* regular images */
1590 list = wps_data->images;
1591 while (list)
1593 struct gui_img *img = (struct gui_img*)list->token->value.data;
1594 if (img->bm.data)
1596 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1597 if (img->loaded)
1598 img->subimage_height = img->bm.height / img->num_subimages;
1600 list = list->next;
1603 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1604 if (bmp_names[BACKDROP_BMP])
1606 int screen = SCREEN_MAIN;
1607 char img_path[MAX_PATH];
1608 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1609 img_path, sizeof(img_path));
1610 #if defined(HAVE_REMOTE_LCD)
1611 /* We only need to check LCD type if there is a remote LCD */
1612 if (wps_data->remote_wps)
1613 screen = SCREEN_REMOTE;
1614 #endif
1615 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1617 #endif /* has backdrop support */
1619 /* If we got here, everything was OK */
1620 return true;
1623 #endif /* HAVE_LCD_BITMAP */
1625 /* to setup up the wps-data from a format-buffer (isfile = false)
1626 from a (wps-)file (isfile = true)*/
1627 bool skin_data_load(struct wps_data *wps_data,
1628 struct screen *display,
1629 const char *buf,
1630 bool isfile)
1632 #ifdef HAVE_ALBUMART
1633 struct mp3entry *curtrack;
1634 long offset;
1635 int status;
1636 int wps_uses_albumart = wps_data->wps_uses_albumart;
1637 int albumart_max_height = wps_data->albumart_max_height;
1638 int albumart_max_width = wps_data->albumart_max_width;
1639 #endif
1640 if (!wps_data || !buf)
1641 return false;
1643 wps_reset(wps_data);
1645 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1646 if (!curr_vp)
1647 return false;
1648 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1649 if (!list)
1650 return false;
1651 add_to_ll_chain(&wps_data->viewports, list);
1654 /* Initialise the first (default) viewport */
1655 curr_vp->label = VP_DEFAULT_LABEL;
1656 curr_vp->vp.x = 0;
1657 curr_vp->vp.width = display->getwidth();
1658 curr_vp->vp.height = display->getheight();
1659 curr_vp->pb = NULL;
1660 curr_vp->hidden_flags = 0;
1661 switch (statusbar_position(display->screen_type))
1663 case STATUSBAR_OFF:
1664 curr_vp->vp.y = 0;
1665 break;
1666 case STATUSBAR_TOP:
1667 curr_vp->vp.y = STATUSBAR_HEIGHT;
1668 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1669 break;
1670 case STATUSBAR_BOTTOM:
1671 curr_vp->vp.y = 0;
1672 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1673 break;
1675 #ifdef HAVE_LCD_BITMAP
1676 curr_vp->vp.font = FONT_UI;
1677 curr_vp->vp.drawmode = DRMODE_SOLID;
1678 #endif
1679 #if LCD_DEPTH > 1
1680 if (display->depth > 1)
1682 curr_vp->vp.fg_pattern = display->get_foreground();
1683 curr_vp->vp.bg_pattern = display->get_background();
1685 #endif
1686 if (!isfile)
1688 return wps_parse(wps_data, buf, false);
1690 else
1692 int fd = open_utf8(buf, O_RDONLY);
1694 if (fd < 0)
1695 return false;
1697 /* get buffer space from the plugin buffer */
1698 size_t buffersize = 0;
1699 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1701 if (!wps_buffer)
1702 return false;
1704 /* copy the file's content to the buffer for parsing,
1705 ensuring that every line ends with a newline char. */
1706 unsigned int start = 0;
1707 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1709 start += strlen(wps_buffer + start);
1710 if (start < buffersize - 1)
1712 wps_buffer[start++] = '\n';
1713 wps_buffer[start] = 0;
1717 close(fd);
1719 if (start <= 0)
1720 return false;
1722 #ifdef HAVE_LCD_BITMAP
1723 /* Set all filename pointers to NULL */
1724 memset(bmp_names, 0, sizeof(bmp_names));
1725 #endif
1727 /* parse the WPS source */
1728 if (!wps_parse(wps_data, wps_buffer, true)) {
1729 wps_reset(wps_data);
1730 return false;
1733 wps_data->wps_loaded = true;
1735 #ifdef HAVE_LCD_BITMAP
1736 /* get the bitmap dir */
1737 char bmpdir[MAX_PATH];
1738 char *dot = strrchr(buf, '.');
1740 strlcpy(bmpdir, buf, dot - buf + 1);
1742 /* load the bitmaps that were found by the parsing */
1743 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1744 wps_reset(wps_data);
1745 return false;
1747 #endif
1748 #ifdef HAVE_ALBUMART
1749 status = audio_status();
1750 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1751 (wps_data->wps_uses_albumart &&
1752 (albumart_max_height != wps_data->albumart_max_height ||
1753 albumart_max_width != wps_data->albumart_max_width))) &&
1754 status & AUDIO_STATUS_PLAY)
1756 curtrack = audio_current_track();
1757 offset = curtrack->offset;
1758 audio_stop();
1759 if (!(status & AUDIO_STATUS_PAUSE))
1760 audio_play(offset);
1762 #endif
1763 #if defined(DEBUG) || defined(SIMULATOR)
1764 debug_skin_usage();
1765 #endif
1766 return true;