fix red :)
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob59499d7c98f91fc701e462ef36490bae4a9ab55f
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_conditional(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,
341 parse_albumart_conditional },
342 #endif
344 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
345 parse_viewport_display },
346 { WPS_NO_TOKEN, "V", 0, parse_viewport },
348 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
349 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
350 #endif
351 #endif
353 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
355 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
356 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
358 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
359 /* the array MUST end with an empty string (first char is \0) */
363 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
364 * chains require the order to be kept.
366 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
368 if (*list == NULL)
369 *list = item;
370 else
372 struct skin_token_list *t = *list;
373 while (t->next)
374 t = t->next;
375 t->next = item;
378 /* create and init a new wpsll item.
379 * passing NULL to token will alloc a new one.
380 * You should only pass NULL for the token when the token type (table above)
381 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
383 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
384 void* token_data)
386 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
387 if (!token)
388 token = skin_buffer_alloc(sizeof(struct wps_token));
389 if (!llitem || !token)
390 return NULL;
391 llitem->next = NULL;
392 llitem->token = token;
393 if (token_data)
394 llitem->token->value.data = token_data;
395 return llitem;
398 /* Returns the number of chars that should be skipped to jump
399 immediately after the first eol, i.e. to the start of the next line */
400 static int skip_end_of_line(const char *wps_bufptr)
402 line_number++;
403 int skip = 0;
404 while(*(wps_bufptr + skip) != '\n')
405 skip++;
406 return ++skip;
409 /* Starts a new subline in the current line during parsing */
410 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
412 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
413 if (!subline)
414 return false;
416 subline->first_token_idx = curr_token;
417 subline->next = NULL;
419 subline->line_type = 0;
420 subline->time_mult = 0;
422 line->curr_subline->last_token_idx = curr_token-1;
423 line->curr_subline->next = subline;
424 line->curr_subline = subline;
425 return true;
428 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
430 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
431 struct skin_subline *subline = NULL;
432 if (!line)
433 return false;
435 /* init the subline */
436 subline = &line->sublines;
437 subline->first_token_idx = curr_token;
438 subline->next = NULL;
439 subline->line_type = 0;
440 subline->time_mult = 0;
442 /* init the new line */
443 line->curr_subline = &line->sublines;
444 line->next = NULL;
445 line->subline_expire_time = 0;
447 /* connect to curr_line and vp pointers.
448 * 1) close the previous lines subline
449 * 2) connect to vp pointer
450 * 3) connect to curr_line global pointer
452 if (curr_line)
454 curr_line->curr_subline->last_token_idx = curr_token - 1;
455 curr_line->next = line;
456 curr_line->curr_subline = NULL;
458 curr_line = line;
459 if (!vp->lines)
460 vp->lines = line;
461 line_number++;
462 return true;
465 #ifdef HAVE_LCD_BITMAP
467 static int parse_statusbar_enable(const char *wps_bufptr,
468 struct wps_token *token,
469 struct wps_data *wps_data)
471 (void)token; /* Kill warnings */
472 wps_data->wps_sb_tag = true;
473 wps_data->show_sb_on_wps = true;
474 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
475 if (default_vp->vp.y == 0)
477 default_vp->vp.y = STATUSBAR_HEIGHT;
478 default_vp->vp.height -= STATUSBAR_HEIGHT;
480 return skip_end_of_line(wps_bufptr);
483 static int parse_statusbar_disable(const char *wps_bufptr,
484 struct wps_token *token,
485 struct wps_data *wps_data)
487 (void)token; /* Kill warnings */
488 wps_data->wps_sb_tag = true;
489 wps_data->show_sb_on_wps = false;
490 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
491 if (default_vp->vp.y == STATUSBAR_HEIGHT)
493 default_vp->vp.y = 0;
494 default_vp->vp.height += STATUSBAR_HEIGHT;
496 return skip_end_of_line(wps_bufptr);
499 static int get_image_id(int c)
501 if(c >= 'a' && c <= 'z')
502 return c - 'a';
503 else if(c >= 'A' && c <= 'Z')
504 return c - 'A' + 26;
505 else
506 return -1;
509 static char *get_image_filename(const char *start, const char* bmpdir,
510 char *buf, int buf_size)
512 const char *end = strchr(start, '|');
514 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
516 buf = "\0";
517 return NULL;
520 int bmpdirlen = strlen(bmpdir);
522 strcpy(buf, bmpdir);
523 buf[bmpdirlen] = '/';
524 memcpy( &buf[bmpdirlen + 1], start, end - start);
525 buf[bmpdirlen + 1 + end - start] = 0;
527 return buf;
530 static int parse_image_display(const char *wps_bufptr,
531 struct wps_token *token,
532 struct wps_data *wps_data)
534 char label = wps_bufptr[0];
535 int subimage;
536 struct gui_img *img;;
538 /* sanity check */
539 img = find_image(label, wps_data);
540 if (!img)
542 token->value.i = label; /* do debug works */
543 return WPS_ERROR_INVALID_PARAM;
546 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
548 if (subimage >= img->num_subimages)
549 return WPS_ERROR_INVALID_PARAM;
551 /* Store sub-image number to display in high bits */
552 token->value.i = label | (subimage << 8);
553 return 2; /* We have consumed 2 bytes */
554 } else {
555 token->value.i = label;
556 return 1; /* We have consumed 1 byte */
560 static int parse_image_load(const char *wps_bufptr,
561 struct wps_token *token,
562 struct wps_data *wps_data)
564 const char *ptr = wps_bufptr;
565 const char *pos;
566 const char* filename;
567 const char* id;
568 const char *newline;
569 int x,y;
570 struct gui_img *img;
572 /* format: %x|n|filename.bmp|x|y|
573 or %xl|n|filename.bmp|x|y|
574 or %xl|n|filename.bmp|x|y|num_subimages|
577 if (*ptr != '|')
578 return WPS_ERROR_INVALID_PARAM;
580 ptr++;
582 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
583 return WPS_ERROR_INVALID_PARAM;
585 /* Check there is a terminating | */
586 if (*ptr != '|')
587 return WPS_ERROR_INVALID_PARAM;
589 /* check the image number and load state */
590 if(find_image(*id, wps_data))
592 /* Invalid image ID */
593 return WPS_ERROR_INVALID_PARAM;
595 img = skin_buffer_alloc(sizeof(struct gui_img));
596 if (!img)
597 return WPS_ERROR_INVALID_PARAM;
598 /* save a pointer to the filename */
599 img->bm.data = (char*)filename;
600 img->label = *id;
601 img->x = x;
602 img->y = y;
603 img->num_subimages = 1;
604 img->always_display = false;
606 /* save current viewport */
607 img->vp = &curr_vp->vp;
609 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
611 img->always_display = true;
613 else
615 /* Parse the (optional) number of sub-images */
616 ptr++;
617 newline = strchr(ptr, '\n');
618 pos = strchr(ptr, '|');
619 if (pos && pos < newline)
620 img->num_subimages = atoi(ptr);
622 if (img->num_subimages <= 0)
623 return WPS_ERROR_INVALID_PARAM;
625 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
626 if (!item)
627 return WPS_ERROR_INVALID_PARAM;
628 add_to_ll_chain(&wps_data->images, item);
630 /* Skip the rest of the line */
631 return skip_end_of_line(wps_bufptr);
634 static int parse_viewport_display(const char *wps_bufptr,
635 struct wps_token *token,
636 struct wps_data *wps_data)
638 (void)wps_data;
639 char letter = wps_bufptr[0];
641 if (letter < 'a' || letter > 'z')
643 /* invalid viewport tag */
644 return WPS_ERROR_INVALID_PARAM;
646 token->value.i = letter;
647 return 1;
650 static int parse_viewport(const char *wps_bufptr,
651 struct wps_token *token,
652 struct wps_data *wps_data)
654 (void)token; /* Kill warnings */
655 const char *ptr = wps_bufptr;
657 const int screen =
658 #ifdef HAVE_REMOTE_LCD
659 wps_data->remote_wps ? SCREEN_REMOTE :
660 #endif
661 SCREEN_MAIN;
663 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
665 /* check for the optional letter to signify its a hideable viewport */
666 /* %Vl|<label>|<rest of tags>| */
667 skin_vp->hidden_flags = 0;
668 skin_vp->label = VP_NO_LABEL;
669 skin_vp->pb = NULL;
670 skin_vp->lines = NULL;
671 if (curr_line)
672 curr_line->curr_subline->last_token_idx = wps_data->num_tokens;
673 curr_line = NULL;
674 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
675 return WPS_ERROR_INVALID_PARAM;
678 if (*ptr == 'l')
680 if (*(ptr+1) == '|')
682 char label = *(ptr+2);
683 if (label >= 'a' && label <= 'z')
685 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
686 skin_vp->label = label;
688 else
689 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
690 ptr += 3;
693 if (*ptr != '|')
694 return WPS_ERROR_INVALID_PARAM;
696 ptr++;
697 struct viewport *vp = &skin_vp->vp;
698 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
700 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
701 return WPS_ERROR_INVALID_PARAM;
703 /* Check for trailing | */
704 if (*ptr != '|')
705 return WPS_ERROR_INVALID_PARAM;
708 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
709 if (!list)
710 return WPS_ERROR_INVALID_PARAM;
711 add_to_ll_chain(&wps_data->viewports, list);
712 curr_vp = skin_vp;
713 /* Skip the rest of the line */
714 return skip_end_of_line(wps_bufptr);
717 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
718 static int parse_image_special(const char *wps_bufptr,
719 struct wps_token *token,
720 struct wps_data *wps_data)
722 (void)wps_data; /* kill warning */
723 (void)token;
724 const char *pos = NULL;
725 const char *newline;
727 pos = strchr(wps_bufptr + 1, '|');
728 newline = strchr(wps_bufptr, '\n');
730 if (pos > newline)
731 return WPS_ERROR_INVALID_PARAM;
732 #if LCD_DEPTH > 1
733 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
735 /* format: %X|filename.bmp| */
736 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
738 #endif
740 /* Skip the rest of the line */
741 return skip_end_of_line(wps_bufptr);
743 #endif
745 #endif /* HAVE_LCD_BITMAP */
747 static int parse_setting(const char *wps_bufptr,
748 struct wps_token *token,
749 struct wps_data *wps_data)
751 (void)wps_data;
752 const char *ptr = wps_bufptr;
753 const char *end;
754 int i;
756 /* Find the setting's cfg_name */
757 if (*ptr != '|')
758 return WPS_ERROR_INVALID_PARAM;
759 ptr++;
760 end = strchr(ptr,'|');
761 if (!end)
762 return WPS_ERROR_INVALID_PARAM;
764 /* Find the setting */
765 for (i=0; i<nb_settings; i++)
766 if (settings[i].cfg_name &&
767 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
768 /* prevent matches on cfg_name prefixes */
769 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
770 break;
771 if (i == nb_settings)
772 return WPS_ERROR_INVALID_PARAM;
774 /* Store the setting number */
775 token->value.i = i;
777 /* Skip the rest of the line */
778 return end-ptr+2;
782 static int parse_dir_level(const char *wps_bufptr,
783 struct wps_token *token,
784 struct wps_data *wps_data)
786 char val[] = { *wps_bufptr, '\0' };
787 token->value.i = atoi(val);
788 (void)wps_data; /* Kill warnings */
789 return 1;
792 static int parse_timeout(const char *wps_bufptr,
793 struct wps_token *token,
794 struct wps_data *wps_data)
796 int skip = 0;
797 int val = 0;
798 bool have_point = false;
799 bool have_tenth = false;
801 (void)wps_data; /* Kill the warning */
803 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
805 if (*wps_bufptr != '.')
807 val *= 10;
808 val += *wps_bufptr - '0';
809 if (have_point)
811 have_tenth = true;
812 wps_bufptr++;
813 skip++;
814 break;
817 else
818 have_point = true;
820 wps_bufptr++;
821 skip++;
824 if (have_tenth == false)
825 val *= 10;
827 if (val == 0 && skip == 0)
829 /* decide what to do if no value was specified */
830 switch (token->type)
832 case WPS_TOKEN_SUBLINE_TIMEOUT:
833 return -1;
834 case WPS_TOKEN_BUTTON_VOLUME:
835 val = 10;
836 break;
839 token->value.i = val;
841 return skip;
844 static int parse_progressbar(const char *wps_bufptr,
845 struct wps_token *token,
846 struct wps_data *wps_data)
848 /* %pb or %pb|filename|x|y|width|height|
849 using - for any of the params uses "sane" values */
850 #ifdef HAVE_LCD_BITMAP
851 enum {
852 PB_FILENAME = 0,
853 PB_X,
854 PB_Y,
855 PB_WIDTH,
856 PB_HEIGHT
858 const char *filename;
859 int x, y, height, width;
860 uint32_t set = 0;
861 const char *ptr = wps_bufptr;
862 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
863 struct skin_token_list *item = new_skin_token_list_item(token, pb);
865 if (!pb || !item)
866 return WPS_ERROR_INVALID_PARAM;
868 struct viewport *vp = &curr_vp->vp;
869 #ifndef __PCTOOL__
870 int font_height = font_get(vp->font)->height;
871 #else
872 int font_height = 8;
873 #endif
874 /* we need to know what line number (viewport relative) this pb is,
875 * so count them... */
876 int line_num = -1;
877 struct skin_line *line = curr_vp->lines;
878 while (line)
880 line_num++;
881 line = line->next;
883 pb->have_bitmap_pb = false;
884 pb->bm.data = NULL; /* no bitmap specified */
886 if (*wps_bufptr != '|') /* regular old style */
888 pb->x = 0;
889 pb->width = vp->width;
890 pb->height = SYSFONT_HEIGHT-2;
891 pb->y = -line_num - 1; /* Will be computed during the rendering */
893 curr_vp->pb = pb;
894 add_to_ll_chain(&wps_data->progressbars, item);
895 return 0;
897 ptr = wps_bufptr + 1;
899 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
900 &x, &y, &width, &height)))
901 return WPS_ERROR_INVALID_PARAM;
903 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
904 pb->bm.data = (char*)filename;
906 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
907 pb->x = x;
908 else
909 pb->x = vp->x;
911 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
913 /* A zero width causes a divide-by-zero error later, so reject it */
914 if (width == 0)
915 return WPS_ERROR_INVALID_PARAM;
917 pb->width = width;
919 else
920 pb->width = vp->width - pb->x;
922 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
924 /* A zero height makes no sense - reject it */
925 if (height == 0)
926 return WPS_ERROR_INVALID_PARAM;
928 pb->height = height;
930 else
931 pb->height = font_height;
933 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
934 pb->y = y;
935 else
936 pb->y = -line_num - 1; /* Will be computed during the rendering */
938 curr_vp->pb = pb;
939 add_to_ll_chain(&wps_data->progressbars, item);
941 /* Skip the rest of the line */
942 return skip_end_of_line(wps_bufptr)-1;
943 #else
944 (void)token;
946 if (*(wps_bufptr-1) == 'f')
947 wps_data->full_line_progressbar = true;
948 else
949 wps_data->full_line_progressbar = false;
951 return 0;
953 #endif
956 #ifdef HAVE_ALBUMART
957 static int parse_albumart_load(const char *wps_bufptr,
958 struct wps_token *token,
959 struct wps_data *wps_data)
961 const char *_pos, *newline;
962 bool parsing;
963 (void)token; /* silence warning */
965 /* reset albumart info in wps */
966 wps_data->albumart_max_width = -1;
967 wps_data->albumart_max_height = -1;
968 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
969 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
971 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
973 newline = strchr(wps_bufptr, '\n');
975 /* initial validation and parsing of x and y components */
976 if (*wps_bufptr != '|')
977 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
979 _pos = wps_bufptr + 1;
980 if (!isdigit(*_pos))
981 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
982 wps_data->albumart_x = atoi(_pos);
984 _pos = strchr(_pos, '|');
985 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
986 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
988 wps_data->albumart_y = atoi(_pos);
990 _pos = strchr(_pos, '|');
991 if (!_pos || _pos > newline)
992 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
993 e.g. %Cl|7|59\n */
995 /* parsing width field */
996 parsing = true;
997 while (parsing)
999 /* apply each modifier in turn */
1000 ++_pos;
1001 switch (*_pos)
1003 case 'l':
1004 case 'L':
1005 case '+':
1006 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
1007 break;
1008 case 'c':
1009 case 'C':
1010 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
1011 break;
1012 case 'r':
1013 case 'R':
1014 case '-':
1015 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
1016 break;
1017 case 'd':
1018 case 'D':
1019 case 'i':
1020 case 'I':
1021 case 's':
1022 case 'S':
1023 /* simply ignored */
1024 break;
1025 default:
1026 parsing = false;
1027 break;
1030 /* extract max width data */
1031 if (*_pos != '|')
1033 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1034 return WPS_ERROR_INVALID_PARAM;
1036 wps_data->albumart_max_width = atoi(_pos);
1038 _pos = strchr(_pos, '|');
1039 if (!_pos || _pos > newline)
1040 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1041 e.g. %Cl|7|59|200\n */
1044 /* parsing height field */
1045 parsing = true;
1046 while (parsing)
1048 /* apply each modifier in turn */
1049 ++_pos;
1050 switch (*_pos)
1052 case 't':
1053 case 'T':
1054 case '-':
1055 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1056 break;
1057 case 'c':
1058 case 'C':
1059 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1060 break;
1061 case 'b':
1062 case 'B':
1063 case '+':
1064 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1065 break;
1066 case 'd':
1067 case 'D':
1068 case 'i':
1069 case 'I':
1070 case 's':
1071 case 'S':
1072 /* simply ignored */
1073 break;
1074 default:
1075 parsing = false;
1076 break;
1079 /* extract max height data */
1080 if (*_pos != '|')
1082 if (!isdigit(*_pos))
1083 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1085 wps_data->albumart_max_height = atoi(_pos);
1087 _pos = strchr(_pos, '|');
1088 if (!_pos || _pos > newline)
1089 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1090 e.g. %Cl|7|59|200|200\n */
1093 /* if we got here, we parsed everything ok .. ! */
1094 if (wps_data->albumart_max_width < 0)
1095 wps_data->albumart_max_width = 0;
1096 else if (wps_data->albumart_max_width > LCD_WIDTH)
1097 wps_data->albumart_max_width = LCD_WIDTH;
1099 if (wps_data->albumart_max_height < 0)
1100 wps_data->albumart_max_height = 0;
1101 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1102 wps_data->albumart_max_height = LCD_HEIGHT;
1104 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1106 /* Skip the rest of the line */
1107 return skip_end_of_line(wps_bufptr);
1110 static int parse_albumart_conditional(const char *wps_bufptr,
1111 struct wps_token *token,
1112 struct wps_data *wps_data)
1114 struct wps_token *prevtoken = token;
1115 --prevtoken;
1116 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1118 /* This %C is part of a %?C construct.
1119 It's either %?C<blah> or %?Cn<blah> */
1120 token->type = WPS_TOKEN_ALBUMART_FOUND;
1121 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1123 token->next = true;
1124 return 1;
1126 else if (*wps_bufptr == '<')
1128 return 0;
1130 else
1132 token->type = WPS_NO_TOKEN;
1133 return 0;
1136 else
1138 /* This %C tag is in a conditional construct. */
1139 wps_data->albumart_cond_index = condindex[level];
1140 return 0;
1143 #endif /* HAVE_ALBUMART */
1145 #ifdef HAVE_TOUCHSCREEN
1147 struct touchaction {char* s; int action;};
1148 static struct touchaction touchactions[] = {
1149 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1150 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1151 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1152 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1153 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1154 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1155 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1157 static int parse_touchregion(const char *wps_bufptr,
1158 struct wps_token *token, struct wps_data *wps_data)
1160 (void)token;
1161 unsigned i, imax;
1162 struct touchregion *region = NULL;
1163 const char *ptr = wps_bufptr;
1164 const char *action;
1165 const char pb_string[] = "progressbar";
1166 const char vol_string[] = "volume";
1167 int x,y,w,h;
1169 /* format: %T|x|y|width|height|action|
1170 * if action starts with & the area must be held to happen
1171 * action is one of:
1172 * play - play/pause playback
1173 * stop - stop playback, exit the wps
1174 * prev - prev track
1175 * next - next track
1176 * ffwd - seek forward
1177 * rwd - seek backwards
1178 * menu - go back to the main menu
1179 * browse - go back to the file/db browser
1180 * shuffle - toggle shuffle mode
1181 * repmode - cycle the repeat mode
1182 * quickscreen - go into the quickscreen
1183 * contextmenu - open the context menu
1184 * playlist - go into the playlist
1185 * pitch - go into the pitchscreen
1189 if (*ptr != '|')
1190 return WPS_ERROR_INVALID_PARAM;
1191 ptr++;
1193 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1194 return WPS_ERROR_INVALID_PARAM;
1196 /* Check there is a terminating | */
1197 if (*ptr != '|')
1198 return WPS_ERROR_INVALID_PARAM;
1200 region = skin_buffer_alloc(sizeof(struct touchregion));
1201 if (!region)
1202 return WPS_ERROR_INVALID_PARAM;
1204 /* should probably do some bounds checking here with the viewport... but later */
1205 region->action = ACTION_NONE;
1206 region->x = x;
1207 region->y = y;
1208 region->width = w;
1209 region->height = h;
1210 region->wvp = curr_vp;
1212 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1213 && *(action + sizeof(pb_string)-1) == '|')
1214 region->type = WPS_TOUCHREGION_SCROLLBAR;
1215 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1216 && *(action + sizeof(vol_string)-1) == '|')
1217 region->type = WPS_TOUCHREGION_VOLUME;
1218 else
1220 region->type = WPS_TOUCHREGION_ACTION;
1222 if (*action == '&')
1224 action++;
1225 region->repeat = true;
1227 else
1228 region->repeat = false;
1230 i = 0;
1231 imax = ARRAYLEN(touchactions);
1232 while ((region->action == ACTION_NONE) &&
1233 (i < imax))
1235 /* try to match with one of our touchregion screens */
1236 int len = strlen(touchactions[i].s);
1237 if (!strncmp(touchactions[i].s, action, len)
1238 && *(action+len) == '|')
1239 region->action = touchactions[i].action;
1240 i++;
1242 if (region->action == ACTION_NONE)
1243 return WPS_ERROR_INVALID_PARAM;
1245 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1246 if (!item)
1247 return WPS_ERROR_INVALID_PARAM;
1248 add_to_ll_chain(&wps_data->touchregions, item);
1249 return skip_end_of_line(wps_bufptr);
1251 #endif
1253 /* Parse a generic token from the given string. Return the length read */
1254 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1256 int skip = 0, taglen = 0, ret;
1257 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1258 const struct wps_tag *tag;
1260 switch(*wps_bufptr)
1263 case '%':
1264 case '<':
1265 case '|':
1266 case '>':
1267 case ';':
1268 case '#':
1269 /* escaped characters */
1270 token->type = WPS_TOKEN_CHARACTER;
1271 token->value.c = *wps_bufptr;
1272 taglen = 1;
1273 wps_data->num_tokens++;
1274 break;
1276 case '?':
1277 /* conditional tag */
1278 token->type = WPS_TOKEN_CONDITIONAL;
1279 level++;
1280 condindex[level] = wps_data->num_tokens;
1281 numoptions[level] = 1;
1282 wps_data->num_tokens++;
1283 ret = parse_token(wps_bufptr + 1, wps_data);
1284 if (ret < 0) return ret;
1285 taglen = 1 + ret;
1286 break;
1288 default:
1289 /* find what tag we have */
1290 for (tag = all_tags;
1291 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1292 tag++) ;
1294 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1295 token->type = tag->type;
1296 curr_line->curr_subline->line_type |= tag->refresh_type;
1298 /* if the tag has a special parsing function, we call it */
1299 if (tag->parse_func)
1301 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1302 if (ret < 0) return ret;
1303 skip += ret;
1306 /* Some tags we don't want to save as tokens */
1307 if (tag->type == WPS_NO_TOKEN)
1308 break;
1310 /* tags that start with 'F', 'I' or 'D' are for the next file */
1311 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1312 *(tag->name) == 'D')
1313 token->next = true;
1315 wps_data->num_tokens++;
1316 break;
1319 skip += taglen;
1320 return skip;
1323 /* Parses the WPS.
1324 data is the pointer to the structure where the parsed WPS should be stored.
1325 It is initialised.
1326 wps_bufptr points to the string containing the WPS tags */
1327 #define TOKEN_BLOCK_SIZE 128
1328 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1330 if (!data || !wps_bufptr || !*wps_bufptr)
1331 return false;
1332 enum wps_parse_error fail = PARSE_OK;
1333 int ret;
1334 int max_tokens = TOKEN_BLOCK_SIZE;
1335 size_t buf_free = 0;
1336 line_number = 1;
1337 level = -1;
1339 /* allocate enough RAM for a reasonable skin, grow as needed.
1340 * Free any used RAM before loading the images to be 100% RAM efficient */
1341 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1342 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1343 return false;
1344 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1345 data->num_tokens = 0;
1347 while (*wps_bufptr && !fail)
1349 /* first make sure there is enough room for tokens */
1350 if (max_tokens <= data->num_tokens + 5)
1352 int extra_tokens = TOKEN_BLOCK_SIZE;
1353 size_t needed = extra_tokens * sizeof(struct wps_token);
1354 /* do some smarts here to grow the array a bit */
1355 if (skin_buffer_freespace() < needed)
1357 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1358 break;
1360 skin_buffer_increment(needed, false);
1361 max_tokens += extra_tokens;
1364 switch(*wps_bufptr++)
1367 /* Regular tag */
1368 case '%':
1369 if ((ret = parse_token(wps_bufptr, data)) < 0)
1371 fail = PARSE_FAIL_COND_INVALID_PARAM;
1372 break;
1374 else if (level >= WPS_MAX_COND_LEVEL - 1)
1376 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1377 break;
1379 wps_bufptr += ret;
1380 break;
1382 /* Alternating sublines separator */
1383 case ';':
1384 if (level >= 0) /* there are unclosed conditionals */
1386 fail = PARSE_FAIL_UNCLOSED_COND;
1387 break;
1390 if (!skin_start_new_subline(curr_line, data->num_tokens))
1391 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1393 break;
1395 /* Conditional list start */
1396 case '<':
1397 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1399 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1400 break;
1403 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1404 lastcond[level] = data->num_tokens++;
1405 break;
1407 /* Conditional list end */
1408 case '>':
1409 if (level < 0) /* not in a conditional, invalid char */
1411 fail = PARSE_FAIL_INVALID_CHAR;
1412 break;
1415 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1416 if (lastcond[level])
1417 data->tokens[lastcond[level]].value.i = data->num_tokens;
1418 else
1420 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1421 break;
1424 lastcond[level] = 0;
1425 data->num_tokens++;
1426 data->tokens[condindex[level]].value.i = numoptions[level];
1427 level--;
1428 break;
1430 /* Conditional list option */
1431 case '|':
1432 if (level < 0) /* not in a conditional, invalid char */
1434 fail = PARSE_FAIL_INVALID_CHAR;
1435 break;
1438 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1439 if (lastcond[level])
1440 data->tokens[lastcond[level]].value.i = data->num_tokens;
1441 else
1443 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1444 break;
1447 lastcond[level] = data->num_tokens;
1448 numoptions[level]++;
1449 data->num_tokens++;
1450 break;
1452 /* Comment */
1453 case '#':
1454 if (level >= 0) /* there are unclosed conditionals */
1456 fail = PARSE_FAIL_UNCLOSED_COND;
1457 break;
1460 wps_bufptr += skip_end_of_line(wps_bufptr);
1461 break;
1463 /* End of this line */
1464 case '\n':
1465 if (level >= 0) /* there are unclosed conditionals */
1467 fail = PARSE_FAIL_UNCLOSED_COND;
1468 break;
1470 /* add a new token for the \n so empty lines are correct */
1471 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1472 data->tokens[data->num_tokens].value.c = '\n';
1473 data->tokens[data->num_tokens].next = false;
1474 data->num_tokens++;
1476 if (!skin_start_new_line(curr_vp, data->num_tokens))
1478 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1479 break;
1482 break;
1484 /* String */
1485 default:
1487 unsigned int len = 1;
1488 const char *string_start = wps_bufptr - 1;
1490 /* find the length of the string */
1491 while (*wps_bufptr && *wps_bufptr != '#' &&
1492 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1493 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1494 *wps_bufptr != '|' && *wps_bufptr != '\n')
1496 wps_bufptr++;
1497 len++;
1500 /* look if we already have that string */
1501 char *str;
1502 bool found = false;
1503 struct skin_token_list *list = data->strings;
1504 while (list)
1506 str = (char*)list->token->value.data;
1507 found = (strlen(str) == len &&
1508 strncmp(string_start, str, len) == 0);
1509 if (found)
1510 break; /* break here because the list item is
1511 used if its found */
1512 list = list->next;
1514 /* If a matching string is found, found is true and i is
1515 the index of the string. If not, found is false */
1517 if (!found)
1519 /* new string */
1520 str = (char*)skin_buffer_alloc(len+1);
1521 if (!str)
1523 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1524 break;
1526 strlcpy(str, string_start, len+1);
1527 struct skin_token_list *item =
1528 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1529 if(!item)
1531 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1532 break;
1534 add_to_ll_chain(&data->strings, item);
1536 else
1538 /* another occurrence of an existing string */
1539 data->tokens[data->num_tokens].value.data = list->token->value.data;
1541 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1542 data->num_tokens++;
1544 break;
1548 if (!fail && level >= 0) /* there are unclosed conditionals */
1549 fail = PARSE_FAIL_UNCLOSED_COND;
1551 if (*wps_bufptr && !fail)
1552 /* one of the limits of the while loop was exceeded */
1553 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1555 /* Success! */
1556 curr_line->curr_subline->last_token_idx = data->num_tokens;
1557 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1558 /* freeup unused tokens */
1559 skin_buffer_free_from_front(sizeof(struct wps_token)
1560 * (max_tokens - data->num_tokens));
1562 #if defined(DEBUG) || defined(SIMULATOR)
1563 if (debug)
1564 print_debug_info(data, fail, line_number);
1565 #else
1566 (void)debug;
1567 #endif
1569 return (fail == 0);
1572 static void wps_reset(struct wps_data *data)
1574 #ifdef HAVE_REMOTE_LCD
1575 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1576 #endif
1577 memset(data, 0, sizeof(*data));
1578 skin_data_init(data);
1579 #ifdef HAVE_REMOTE_LCD
1580 data->remote_wps = rwps;
1581 #endif
1584 #ifdef HAVE_LCD_BITMAP
1585 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1587 (void)wps_data; /* only needed for remote targets */
1588 bool loaded = false;
1589 char img_path[MAX_PATH];
1590 get_image_filename(bitmap->data, bmpdir,
1591 img_path, sizeof(img_path));
1593 /* load the image */
1594 int format;
1595 #ifdef HAVE_REMOTE_LCD
1596 if (wps_data->remote_wps)
1597 format = FORMAT_ANY|FORMAT_REMOTE;
1598 else
1599 #endif
1600 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1602 size_t max_buf;
1603 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1604 bitmap->data = imgbuf;
1605 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1607 if (ret > 0)
1609 skin_buffer_increment(ret, true);
1610 loaded = true;
1612 else
1614 /* Abort if we can't load an image */
1615 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1616 loaded = false;
1618 return loaded;
1621 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1623 struct skin_token_list *list;
1624 /* do the progressbars */
1625 list = wps_data->progressbars;
1626 while (list)
1628 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1629 if (pb->bm.data)
1631 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1633 list = list->next;
1635 /* regular images */
1636 list = wps_data->images;
1637 while (list)
1639 struct gui_img *img = (struct gui_img*)list->token->value.data;
1640 if (img->bm.data)
1642 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1643 if (img->loaded)
1644 img->subimage_height = img->bm.height / img->num_subimages;
1646 list = list->next;
1649 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1650 if (bmp_names[BACKDROP_BMP])
1652 int screen = SCREEN_MAIN;
1653 char img_path[MAX_PATH];
1654 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1655 img_path, sizeof(img_path));
1656 #if defined(HAVE_REMOTE_LCD)
1657 /* We only need to check LCD type if there is a remote LCD */
1658 if (wps_data->remote_wps)
1659 screen = SCREEN_REMOTE;
1660 #endif
1661 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1663 #endif /* has backdrop support */
1665 /* If we got here, everything was OK */
1666 return true;
1669 #endif /* HAVE_LCD_BITMAP */
1671 /* to setup up the wps-data from a format-buffer (isfile = false)
1672 from a (wps-)file (isfile = true)*/
1673 bool skin_data_load(struct wps_data *wps_data,
1674 struct screen *display,
1675 const char *buf,
1676 bool isfile)
1678 #ifdef HAVE_ALBUMART
1679 struct mp3entry *curtrack;
1680 long offset;
1681 int status;
1682 int wps_uses_albumart = wps_data->wps_uses_albumart;
1683 int albumart_max_height = wps_data->albumart_max_height;
1684 int albumart_max_width = wps_data->albumart_max_width;
1685 #endif
1686 if (!wps_data || !buf)
1687 return false;
1689 wps_reset(wps_data);
1691 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1692 if (!curr_vp)
1693 return false;
1694 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1695 if (!list)
1696 return false;
1697 add_to_ll_chain(&wps_data->viewports, list);
1700 /* Initialise the first (default) viewport */
1701 curr_vp->label = VP_DEFAULT_LABEL;
1702 curr_vp->vp.x = 0;
1703 curr_vp->vp.width = display->getwidth();
1704 curr_vp->vp.height = display->getheight();
1705 curr_vp->pb = NULL;
1706 curr_vp->hidden_flags = 0;
1707 curr_vp->lines = NULL;
1709 curr_line = NULL;
1710 if (!skin_start_new_line(curr_vp, 0))
1711 return false;
1713 switch (statusbar_position(display->screen_type))
1715 case STATUSBAR_OFF:
1716 curr_vp->vp.y = 0;
1717 break;
1718 case STATUSBAR_TOP:
1719 curr_vp->vp.y = STATUSBAR_HEIGHT;
1720 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1721 break;
1722 case STATUSBAR_BOTTOM:
1723 curr_vp->vp.y = 0;
1724 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1725 break;
1727 #ifdef HAVE_LCD_BITMAP
1728 curr_vp->vp.font = FONT_UI;
1729 curr_vp->vp.drawmode = DRMODE_SOLID;
1730 #endif
1731 #if LCD_DEPTH > 1
1732 if (display->depth > 1)
1734 curr_vp->vp.fg_pattern = display->get_foreground();
1735 curr_vp->vp.bg_pattern = display->get_background();
1737 #endif
1738 if (!isfile)
1740 return wps_parse(wps_data, buf, false);
1742 else
1744 int fd = open_utf8(buf, O_RDONLY);
1746 if (fd < 0)
1747 return false;
1749 /* get buffer space from the plugin buffer */
1750 size_t buffersize = 0;
1751 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1753 if (!wps_buffer)
1754 return false;
1756 /* copy the file's content to the buffer for parsing,
1757 ensuring that every line ends with a newline char. */
1758 unsigned int start = 0;
1759 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1761 start += strlen(wps_buffer + start);
1762 if (start < buffersize - 1)
1764 wps_buffer[start++] = '\n';
1765 wps_buffer[start] = 0;
1769 close(fd);
1771 if (start <= 0)
1772 return false;
1774 #ifdef HAVE_LCD_BITMAP
1775 /* Set all filename pointers to NULL */
1776 memset(bmp_names, 0, sizeof(bmp_names));
1777 #endif
1779 /* parse the WPS source */
1780 if (!wps_parse(wps_data, wps_buffer, true)) {
1781 wps_reset(wps_data);
1782 return false;
1785 wps_data->wps_loaded = true;
1787 #ifdef HAVE_LCD_BITMAP
1788 /* get the bitmap dir */
1789 char bmpdir[MAX_PATH];
1790 char *dot = strrchr(buf, '.');
1792 strlcpy(bmpdir, buf, dot - buf + 1);
1794 /* load the bitmaps that were found by the parsing */
1795 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1796 wps_reset(wps_data);
1797 return false;
1799 #endif
1800 #ifdef HAVE_ALBUMART
1801 status = audio_status();
1802 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1803 (wps_data->wps_uses_albumart &&
1804 (albumart_max_height != wps_data->albumart_max_height ||
1805 albumart_max_width != wps_data->albumart_max_width))) &&
1806 status & AUDIO_STATUS_PLAY)
1808 curtrack = audio_current_track();
1809 offset = curtrack->offset;
1810 audio_stop();
1811 if (!(status & AUDIO_STATUS_PAUSE))
1812 audio_play(offset);
1814 #endif
1815 #if defined(DEBUG) || defined(SIMULATOR)
1816 debug_skin_usage();
1817 #endif
1818 return true;