new skin tag: %Sx|<english>| will display the current languages translation of the...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobe62d473405f8c236720f77d13102f1a7d931aa2c
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 #include "language.h"
44 #endif /*__PCTOOL__*/
46 #include <ctype.h>
47 #include <stdbool.h>
48 #include "font.h"
50 #include "wps_internals.h"
51 #include "skin_engine.h"
52 #include "settings.h"
53 #include "settings_list.h"
55 #ifdef HAVE_LCD_BITMAP
56 #include "bmp.h"
57 #endif
59 #include "backdrop.h"
61 #define WPS_ERROR_INVALID_PARAM -1
63 /* level of current conditional.
64 -1 means we're not in a conditional. */
65 static int level = -1;
67 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
68 or WPS_TOKEN_CONDITIONAL_START in current level */
69 static int lastcond[WPS_MAX_COND_LEVEL];
71 /* index of the WPS_TOKEN_CONDITIONAL in current level */
72 static int condindex[WPS_MAX_COND_LEVEL];
74 /* number of condtional options in current level */
75 static int numoptions[WPS_MAX_COND_LEVEL];
77 /* line number, debug only */
78 static int line_number;
80 /* the current viewport */
81 static struct skin_viewport *curr_vp;
82 /* the current line, linked to the above viewport */
83 static struct skin_line *curr_line;
85 #ifdef HAVE_LCD_BITMAP
87 #if LCD_DEPTH > 1
88 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
89 #else
90 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
91 #endif
93 #define PROGRESSBAR_BMP MAX_IMAGES
94 #define BACKDROP_BMP (MAX_BITMAPS-1)
96 /* pointers to the bitmap filenames in the WPS source */
97 static const char *bmp_names[MAX_BITMAPS];
99 #endif /* HAVE_LCD_BITMAP */
101 #if defined(DEBUG) || defined(SIMULATOR)
102 /* debugging function */
103 extern void print_debug_info(struct wps_data *data, int fail, int line);
104 extern void debug_skin_usage(void);
105 #endif
107 static void wps_reset(struct wps_data *data);
109 /* Function for parsing of details for a token. At the moment the
110 function is called, the token type has already been set. The
111 function must fill in the details and possibly add more tokens
112 to the token array. It should return the number of chars that
113 has been consumed.
115 wps_bufptr points to the char following the tag (i.e. where
116 details begin).
117 token is the pointer to the 'main' token being parsed
119 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
120 struct wps_token *token, struct wps_data *wps_data);
122 struct wps_tag {
123 enum wps_token_type type;
124 const char name[3];
125 unsigned char refresh_type;
126 const wps_tag_parse_func parse_func;
128 static int skip_end_of_line(const char *wps_bufptr);
129 /* prototypes of all special parse functions : */
130 static int parse_timeout(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
132 static int parse_progressbar(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_dir_level(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_setting_and_lang(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
139 #ifdef HAVE_LCD_BITMAP
140 static int parse_viewport_display(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_viewport(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_statusbar_enable(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 static int parse_statusbar_disable(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 static int parse_image_display(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 static int parse_image_load(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 #endif /*HAVE_LCD_BITMAP */
153 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
154 static int parse_image_special(const char *wps_bufptr,
155 struct wps_token *token, struct wps_data *wps_data);
156 #endif
157 #ifdef HAVE_ALBUMART
158 static int parse_albumart_load(const char *wps_bufptr,
159 struct wps_token *token, struct wps_data *wps_data);
160 static int parse_albumart_display(const char *wps_bufptr,
161 struct wps_token *token, struct wps_data *wps_data);
162 #endif /* HAVE_ALBUMART */
163 #ifdef HAVE_TOUCHSCREEN
164 static int parse_touchregion(const char *wps_bufptr,
165 struct wps_token *token, struct wps_data *wps_data);
166 #else
167 static int fulline_tag_not_supported(const char *wps_bufptr,
168 struct wps_token *token, struct wps_data *wps_data)
170 (void)token; (void)wps_data;
171 return skip_end_of_line(wps_bufptr);
173 #define parse_touchregion fulline_tag_not_supported
174 #endif
175 #ifdef CONFIG_RTC
176 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
177 #else
178 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
179 #endif
181 /* array of available tags - those with more characters have to go first
182 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
183 static const struct wps_tag all_tags[] = {
185 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
186 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
187 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
189 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
190 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
191 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
192 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
193 #if CONFIG_CHARGING >= CHARGING_MONITOR
194 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
195 #endif
196 #if CONFIG_CHARGING
197 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
198 #endif
200 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
201 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
216 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
217 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
218 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
220 /* current file */
221 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
231 parse_dir_level },
233 /* next file */
234 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
244 parse_dir_level },
246 /* current metadata */
247 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
260 /* next metadata */
261 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
274 #if (CONFIG_CODEC != MAS3507D)
275 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
276 #endif
277 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
278 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
279 #endif
281 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
283 #ifdef HAS_REMOTE_BUTTON_HOLD
284 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
285 #else
286 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
287 #endif
289 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
290 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
291 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
292 parse_timeout },
294 #ifdef HAVE_LCD_BITMAP
295 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
296 #else
297 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
298 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
299 #endif
300 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
301 parse_progressbar },
303 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
305 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
306 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
307 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
308 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
310 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
311 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
312 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
313 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
315 #ifdef HAVE_TAGCACHE
316 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
317 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
318 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
319 #endif
321 #if CONFIG_CODEC == SWCODEC
322 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
323 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
324 #endif
326 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
327 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
329 #ifdef HAVE_LCD_BITMAP
330 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
331 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
333 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
335 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
336 parse_image_display },
338 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
339 #ifdef HAVE_ALBUMART
340 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
341 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
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,
354 parse_setting_and_lang },
355 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
356 parse_setting_and_lang },
358 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
359 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
361 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
362 /* the array MUST end with an empty string (first char is \0) */
366 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
367 * chains require the order to be kept.
369 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
371 if (*list == NULL)
372 *list = item;
373 else
375 struct skin_token_list *t = *list;
376 while (t->next)
377 t = t->next;
378 t->next = item;
381 /* create and init a new wpsll item.
382 * passing NULL to token will alloc a new one.
383 * You should only pass NULL for the token when the token type (table above)
384 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
386 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
387 void* token_data)
389 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
390 if (!token)
391 token = skin_buffer_alloc(sizeof(struct wps_token));
392 if (!llitem || !token)
393 return NULL;
394 llitem->next = NULL;
395 llitem->token = token;
396 if (token_data)
397 llitem->token->value.data = token_data;
398 return llitem;
401 /* Returns the number of chars that should be skipped to jump
402 immediately after the first eol, i.e. to the start of the next line */
403 static int skip_end_of_line(const char *wps_bufptr)
405 line_number++;
406 int skip = 0;
407 while(*(wps_bufptr + skip) != '\n')
408 skip++;
409 return ++skip;
412 /* Starts a new subline in the current line during parsing */
413 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
415 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
416 if (!subline)
417 return false;
419 subline->first_token_idx = curr_token;
420 subline->next = NULL;
422 subline->line_type = 0;
423 subline->time_mult = 0;
425 line->curr_subline->last_token_idx = curr_token-1;
426 line->curr_subline->next = subline;
427 line->curr_subline = subline;
428 return true;
431 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
433 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
434 struct skin_subline *subline = NULL;
435 if (!line)
436 return false;
438 /* init the subline */
439 subline = &line->sublines;
440 subline->first_token_idx = curr_token;
441 subline->next = NULL;
442 subline->line_type = 0;
443 subline->time_mult = 0;
445 /* init the new line */
446 line->curr_subline = &line->sublines;
447 line->next = NULL;
448 line->subline_expire_time = 0;
450 /* connect to curr_line and vp pointers.
451 * 1) close the previous lines subline
452 * 2) connect to vp pointer
453 * 3) connect to curr_line global pointer
455 if (curr_line)
457 curr_line->curr_subline->last_token_idx = curr_token - 1;
458 curr_line->next = line;
459 curr_line->curr_subline = NULL;
461 curr_line = line;
462 if (!vp->lines)
463 vp->lines = line;
464 line_number++;
465 return true;
468 #ifdef HAVE_LCD_BITMAP
470 static int parse_statusbar_enable(const char *wps_bufptr,
471 struct wps_token *token,
472 struct wps_data *wps_data)
474 (void)token; /* Kill warnings */
475 wps_data->wps_sb_tag = true;
476 wps_data->show_sb_on_wps = true;
477 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
478 if (default_vp->vp.y == 0)
480 default_vp->vp.y = STATUSBAR_HEIGHT;
481 default_vp->vp.height -= STATUSBAR_HEIGHT;
483 return skip_end_of_line(wps_bufptr);
486 static int parse_statusbar_disable(const char *wps_bufptr,
487 struct wps_token *token,
488 struct wps_data *wps_data)
490 (void)token; /* Kill warnings */
491 wps_data->wps_sb_tag = true;
492 wps_data->show_sb_on_wps = false;
493 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
494 if (default_vp->vp.y == STATUSBAR_HEIGHT)
496 default_vp->vp.y = 0;
497 default_vp->vp.height += STATUSBAR_HEIGHT;
499 return skip_end_of_line(wps_bufptr);
502 static int get_image_id(int c)
504 if(c >= 'a' && c <= 'z')
505 return c - 'a';
506 else if(c >= 'A' && c <= 'Z')
507 return c - 'A' + 26;
508 else
509 return -1;
512 static char *get_image_filename(const char *start, const char* bmpdir,
513 char *buf, int buf_size)
515 const char *end = strchr(start, '|');
517 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
519 buf = "\0";
520 return NULL;
523 int bmpdirlen = strlen(bmpdir);
525 strcpy(buf, bmpdir);
526 buf[bmpdirlen] = '/';
527 memcpy( &buf[bmpdirlen + 1], start, end - start);
528 buf[bmpdirlen + 1 + end - start] = 0;
530 return buf;
533 static int parse_image_display(const char *wps_bufptr,
534 struct wps_token *token,
535 struct wps_data *wps_data)
537 char label = wps_bufptr[0];
538 int subimage;
539 struct gui_img *img;;
541 /* sanity check */
542 img = find_image(label, wps_data);
543 if (!img)
545 token->value.i = label; /* so debug works */
546 return WPS_ERROR_INVALID_PARAM;
549 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
551 if (subimage >= img->num_subimages)
552 return WPS_ERROR_INVALID_PARAM;
554 /* Store sub-image number to display in high bits */
555 token->value.i = label | (subimage << 8);
556 return 2; /* We have consumed 2 bytes */
557 } else {
558 token->value.i = label;
559 return 1; /* We have consumed 1 byte */
563 static int parse_image_load(const char *wps_bufptr,
564 struct wps_token *token,
565 struct wps_data *wps_data)
567 const char *ptr = wps_bufptr;
568 const char *pos;
569 const char* filename;
570 const char* id;
571 const char *newline;
572 int x,y;
573 struct gui_img *img;
575 /* format: %x|n|filename.bmp|x|y|
576 or %xl|n|filename.bmp|x|y|
577 or %xl|n|filename.bmp|x|y|num_subimages|
580 if (*ptr != '|')
581 return WPS_ERROR_INVALID_PARAM;
583 ptr++;
585 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
586 return WPS_ERROR_INVALID_PARAM;
588 /* Check there is a terminating | */
589 if (*ptr != '|')
590 return WPS_ERROR_INVALID_PARAM;
592 /* check the image number and load state */
593 if(find_image(*id, wps_data))
595 /* Invalid image ID */
596 return WPS_ERROR_INVALID_PARAM;
598 img = skin_buffer_alloc(sizeof(struct gui_img));
599 if (!img)
600 return WPS_ERROR_INVALID_PARAM;
601 /* save a pointer to the filename */
602 img->bm.data = (char*)filename;
603 img->label = *id;
604 img->x = x;
605 img->y = y;
606 img->num_subimages = 1;
607 img->always_display = false;
609 /* save current viewport */
610 img->vp = &curr_vp->vp;
612 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
614 img->always_display = true;
616 else
618 /* Parse the (optional) number of sub-images */
619 ptr++;
620 newline = strchr(ptr, '\n');
621 pos = strchr(ptr, '|');
622 if (pos && pos < newline)
623 img->num_subimages = atoi(ptr);
625 if (img->num_subimages <= 0)
626 return WPS_ERROR_INVALID_PARAM;
628 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
629 if (!item)
630 return WPS_ERROR_INVALID_PARAM;
631 add_to_ll_chain(&wps_data->images, item);
633 /* Skip the rest of the line */
634 return skip_end_of_line(wps_bufptr);
637 static int parse_viewport_display(const char *wps_bufptr,
638 struct wps_token *token,
639 struct wps_data *wps_data)
641 (void)wps_data;
642 char letter = wps_bufptr[0];
644 if (letter < 'a' || letter > 'z')
646 /* invalid viewport tag */
647 return WPS_ERROR_INVALID_PARAM;
649 token->value.i = letter;
650 return 1;
653 static int parse_viewport(const char *wps_bufptr,
654 struct wps_token *token,
655 struct wps_data *wps_data)
657 (void)token; /* Kill warnings */
658 const char *ptr = wps_bufptr;
660 const int screen =
661 #ifdef HAVE_REMOTE_LCD
662 wps_data->remote_wps ? SCREEN_REMOTE :
663 #endif
664 SCREEN_MAIN;
666 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
668 /* check for the optional letter to signify its a hideable viewport */
669 /* %Vl|<label>|<rest of tags>| */
670 skin_vp->hidden_flags = 0;
671 skin_vp->label = VP_NO_LABEL;
672 skin_vp->pb = NULL;
673 skin_vp->lines = NULL;
674 if (curr_line)
676 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
677 - (wps_data->num_tokens > 0 ? 1 : 0);
680 curr_line = NULL;
681 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
682 return WPS_ERROR_INVALID_PARAM;
684 if (*ptr == 'l')
686 if (*(ptr+1) == '|')
688 char label = *(ptr+2);
689 if (label >= 'a' && label <= 'z')
691 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
692 skin_vp->label = label;
694 else
695 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
696 ptr += 3;
699 if (*ptr != '|')
700 return WPS_ERROR_INVALID_PARAM;
702 ptr++;
703 struct viewport *vp = &skin_vp->vp;
704 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
706 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
707 return WPS_ERROR_INVALID_PARAM;
709 /* Check for trailing | */
710 if (*ptr != '|')
711 return WPS_ERROR_INVALID_PARAM;
714 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
715 if (!list)
716 return WPS_ERROR_INVALID_PARAM;
717 add_to_ll_chain(&wps_data->viewports, list);
718 curr_vp = skin_vp;
719 /* Skip the rest of the line */
720 return skip_end_of_line(wps_bufptr);
723 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
724 static int parse_image_special(const char *wps_bufptr,
725 struct wps_token *token,
726 struct wps_data *wps_data)
728 (void)wps_data; /* kill warning */
729 (void)token;
730 const char *pos = NULL;
731 const char *newline;
733 pos = strchr(wps_bufptr + 1, '|');
734 newline = strchr(wps_bufptr, '\n');
736 if (pos > newline)
737 return WPS_ERROR_INVALID_PARAM;
738 #if LCD_DEPTH > 1
739 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
741 /* format: %X|filename.bmp| */
742 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
744 #endif
746 /* Skip the rest of the line */
747 return skip_end_of_line(wps_bufptr);
749 #endif
751 #endif /* HAVE_LCD_BITMAP */
753 static int parse_setting_and_lang(const char *wps_bufptr,
754 struct wps_token *token,
755 struct wps_data *wps_data)
757 /* NOTE: both the string validations that happen in here will
758 * automatically PASS on checkwps because its too hard to get
759 * settings_list.c and englinsh.lang built for it.
760 * If that ever changes remove the #ifndef __PCTOOL__'s here
762 (void)wps_data;
763 const char *ptr = wps_bufptr;
764 const char *end;
765 int i = 0;
766 char temp[64];
768 /* Find the setting's cfg_name */
769 if (*ptr != '|')
770 return WPS_ERROR_INVALID_PARAM;
771 ptr++;
772 end = strchr(ptr,'|');
773 if (!end)
774 return WPS_ERROR_INVALID_PARAM;
775 strlcpy(temp, ptr,end-ptr+1);
777 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
779 #ifndef __PCTOOL__
780 i = lang_english_to_id(temp);
781 if (i < 0)
782 return WPS_ERROR_INVALID_PARAM;
783 #endif
785 else
787 /* Find the setting */
788 for (i=0; i<nb_settings; i++)
789 if (settings[i].cfg_name &&
790 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
791 /* prevent matches on cfg_name prefixes */
792 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
793 break;
794 #ifndef __PCTOOL__
795 if (i == nb_settings)
796 return WPS_ERROR_INVALID_PARAM;
797 #endif
799 /* Store the setting number */
800 token->value.i = i;
802 /* Skip the rest of the line */
803 return end-ptr+2;
807 static int parse_dir_level(const char *wps_bufptr,
808 struct wps_token *token,
809 struct wps_data *wps_data)
811 char val[] = { *wps_bufptr, '\0' };
812 token->value.i = atoi(val);
813 (void)wps_data; /* Kill warnings */
814 return 1;
817 static int parse_timeout(const char *wps_bufptr,
818 struct wps_token *token,
819 struct wps_data *wps_data)
821 int skip = 0;
822 int val = 0;
823 bool have_point = false;
824 bool have_tenth = false;
826 (void)wps_data; /* Kill the warning */
828 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
830 if (*wps_bufptr != '.')
832 val *= 10;
833 val += *wps_bufptr - '0';
834 if (have_point)
836 have_tenth = true;
837 wps_bufptr++;
838 skip++;
839 break;
842 else
843 have_point = true;
845 wps_bufptr++;
846 skip++;
849 if (have_tenth == false)
850 val *= 10;
852 if (val == 0 && skip == 0)
854 /* decide what to do if no value was specified */
855 switch (token->type)
857 case WPS_TOKEN_SUBLINE_TIMEOUT:
858 return -1;
859 case WPS_TOKEN_BUTTON_VOLUME:
860 val = 10;
861 break;
864 token->value.i = val;
866 return skip;
869 static int parse_progressbar(const char *wps_bufptr,
870 struct wps_token *token,
871 struct wps_data *wps_data)
873 /* %pb or %pb|filename|x|y|width|height|
874 using - for any of the params uses "sane" values */
875 #ifdef HAVE_LCD_BITMAP
876 enum {
877 PB_FILENAME = 0,
878 PB_X,
879 PB_Y,
880 PB_WIDTH,
881 PB_HEIGHT
883 const char *filename;
884 int x, y, height, width;
885 uint32_t set = 0;
886 const char *ptr = wps_bufptr;
887 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
888 struct skin_token_list *item = new_skin_token_list_item(token, pb);
890 if (!pb || !item)
891 return WPS_ERROR_INVALID_PARAM;
893 struct viewport *vp = &curr_vp->vp;
894 #ifndef __PCTOOL__
895 int font_height = font_get(vp->font)->height;
896 #else
897 int font_height = 8;
898 #endif
899 /* we need to know what line number (viewport relative) this pb is,
900 * so count them... */
901 int line_num = -1;
902 struct skin_line *line = curr_vp->lines;
903 while (line)
905 line_num++;
906 line = line->next;
908 pb->have_bitmap_pb = false;
909 pb->bm.data = NULL; /* no bitmap specified */
911 if (*wps_bufptr != '|') /* regular old style */
913 pb->x = 0;
914 pb->width = vp->width;
915 pb->height = SYSFONT_HEIGHT-2;
916 pb->y = -line_num - 1; /* Will be computed during the rendering */
918 curr_vp->pb = pb;
919 add_to_ll_chain(&wps_data->progressbars, item);
920 return 0;
922 ptr = wps_bufptr + 1;
924 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
925 &x, &y, &width, &height)))
926 return WPS_ERROR_INVALID_PARAM;
928 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
929 pb->bm.data = (char*)filename;
931 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
932 pb->x = x;
933 else
934 pb->x = vp->x;
936 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
938 /* A zero width causes a divide-by-zero error later, so reject it */
939 if (width == 0)
940 return WPS_ERROR_INVALID_PARAM;
942 pb->width = width;
944 else
945 pb->width = vp->width - pb->x;
947 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
949 /* A zero height makes no sense - reject it */
950 if (height == 0)
951 return WPS_ERROR_INVALID_PARAM;
953 pb->height = height;
955 else
956 pb->height = font_height;
958 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
959 pb->y = y;
960 else
961 pb->y = -line_num - 1; /* Will be computed during the rendering */
963 curr_vp->pb = pb;
964 add_to_ll_chain(&wps_data->progressbars, item);
966 /* Skip the rest of the line */
967 return skip_end_of_line(wps_bufptr)-1;
968 #else
969 (void)token;
971 if (*(wps_bufptr-1) == 'f')
972 wps_data->full_line_progressbar = true;
973 else
974 wps_data->full_line_progressbar = false;
976 return 0;
978 #endif
981 #ifdef HAVE_ALBUMART
982 static int parse_albumart_load(const char *wps_bufptr,
983 struct wps_token *token,
984 struct wps_data *wps_data)
986 const char *_pos, *newline;
987 bool parsing;
988 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
989 (void)token; /* silence warning */
990 if (!aa)
991 return skip_end_of_line(wps_bufptr);
993 /* reset albumart info in wps */
994 aa->width = -1;
995 aa->height = -1;
996 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
997 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
998 aa->vp = &curr_vp->vp;
1000 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1002 newline = strchr(wps_bufptr, '\n');
1004 /* initial validation and parsing of x and y components */
1005 if (*wps_bufptr != '|')
1006 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1008 _pos = wps_bufptr + 1;
1009 _pos = parse_list("dd", NULL, '|', _pos, &aa->x, &aa->y);
1011 if (!_pos || _pos > newline || *_pos != '|')
1012 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1013 e.g. %Cl|7|59\n */
1015 /* parsing width field */
1016 parsing = true;
1017 while (parsing)
1019 /* apply each modifier in turn */
1020 ++_pos;
1021 switch (*_pos)
1023 case 'l':
1024 case 'L':
1025 case '+':
1026 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1027 break;
1028 case 'c':
1029 case 'C':
1030 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1031 break;
1032 case 'r':
1033 case 'R':
1034 case '-':
1035 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1036 break;
1037 case 'd':
1038 case 'D':
1039 case 'i':
1040 case 'I':
1041 case 's':
1042 case 'S':
1043 /* simply ignored */
1044 break;
1045 default:
1046 parsing = false;
1047 break;
1050 /* extract max width data */
1051 if (*_pos != '|')
1053 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1054 return WPS_ERROR_INVALID_PARAM;
1056 aa->width = atoi(_pos);
1058 _pos = strchr(_pos, '|');
1059 if (!_pos || _pos > newline)
1060 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1061 e.g. %Cl|7|59|200\n */
1064 /* parsing height field */
1065 parsing = true;
1066 while (parsing)
1068 /* apply each modifier in turn */
1069 ++_pos;
1070 switch (*_pos)
1072 case 't':
1073 case 'T':
1074 case '-':
1075 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1076 break;
1077 case 'c':
1078 case 'C':
1079 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1080 break;
1081 case 'b':
1082 case 'B':
1083 case '+':
1084 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1085 break;
1086 case 'd':
1087 case 'D':
1088 case 'i':
1089 case 'I':
1090 case 's':
1091 case 'S':
1092 /* simply ignored */
1093 break;
1094 default:
1095 parsing = false;
1096 break;
1099 /* extract max height data */
1100 if (*_pos != '|')
1102 if (!isdigit(*_pos))
1103 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1105 aa->height = atoi(_pos);
1107 _pos = strchr(_pos, '|');
1108 if (!_pos || _pos > newline)
1109 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1110 e.g. %Cl|7|59|200|200\n */
1113 /* if we got here, we parsed everything ok .. ! */
1114 if (aa->width < 0)
1115 aa->width = 0;
1116 else if (aa->width > LCD_WIDTH)
1117 aa->width = LCD_WIDTH;
1119 if (aa->height < 0)
1120 aa->height = 0;
1121 else if (aa->height > LCD_HEIGHT)
1122 aa->height = LCD_HEIGHT;
1124 aa->state = WPS_ALBUMART_LOAD;
1125 aa->draw = false;
1126 wps_data->albumart = aa;
1128 /* Skip the rest of the line */
1129 return skip_end_of_line(wps_bufptr);
1132 static int parse_albumart_display(const char *wps_bufptr,
1133 struct wps_token *token,
1134 struct wps_data *wps_data)
1136 (void)wps_bufptr;
1137 struct wps_token *prev = token-1;
1138 if ((wps_data->num_tokens > 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1140 token->type = WPS_TOKEN_ALBUMART_FOUND;
1142 else if (wps_data->albumart)
1144 wps_data->albumart->vp = &curr_vp->vp;
1146 #if 0
1147 /* the old code did this so keep it here for now...
1148 * this is to allow the posibility to showing the next tracks AA! */
1149 if (wps_bufptr+1 == 'n')
1150 return 1;
1151 #endif
1152 return 0;
1154 #endif /* HAVE_ALBUMART */
1156 #ifdef HAVE_TOUCHSCREEN
1158 struct touchaction {char* s; int action;};
1159 static struct touchaction touchactions[] = {
1160 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1161 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1162 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1163 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1164 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1165 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1166 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1168 static int parse_touchregion(const char *wps_bufptr,
1169 struct wps_token *token, struct wps_data *wps_data)
1171 (void)token;
1172 unsigned i, imax;
1173 struct touchregion *region = NULL;
1174 const char *ptr = wps_bufptr;
1175 const char *action;
1176 const char pb_string[] = "progressbar";
1177 const char vol_string[] = "volume";
1178 int x,y,w,h;
1180 /* format: %T|x|y|width|height|action|
1181 * if action starts with & the area must be held to happen
1182 * action is one of:
1183 * play - play/pause playback
1184 * stop - stop playback, exit the wps
1185 * prev - prev track
1186 * next - next track
1187 * ffwd - seek forward
1188 * rwd - seek backwards
1189 * menu - go back to the main menu
1190 * browse - go back to the file/db browser
1191 * shuffle - toggle shuffle mode
1192 * repmode - cycle the repeat mode
1193 * quickscreen - go into the quickscreen
1194 * contextmenu - open the context menu
1195 * playlist - go into the playlist
1196 * pitch - go into the pitchscreen
1200 if (*ptr != '|')
1201 return WPS_ERROR_INVALID_PARAM;
1202 ptr++;
1204 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1205 return WPS_ERROR_INVALID_PARAM;
1207 /* Check there is a terminating | */
1208 if (*ptr != '|')
1209 return WPS_ERROR_INVALID_PARAM;
1211 region = skin_buffer_alloc(sizeof(struct touchregion));
1212 if (!region)
1213 return WPS_ERROR_INVALID_PARAM;
1215 /* should probably do some bounds checking here with the viewport... but later */
1216 region->action = ACTION_NONE;
1217 region->x = x;
1218 region->y = y;
1219 region->width = w;
1220 region->height = h;
1221 region->wvp = curr_vp;
1223 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1224 && *(action + sizeof(pb_string)-1) == '|')
1225 region->type = WPS_TOUCHREGION_SCROLLBAR;
1226 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1227 && *(action + sizeof(vol_string)-1) == '|')
1228 region->type = WPS_TOUCHREGION_VOLUME;
1229 else
1231 region->type = WPS_TOUCHREGION_ACTION;
1233 if (*action == '&')
1235 action++;
1236 region->repeat = true;
1238 else
1239 region->repeat = false;
1241 i = 0;
1242 imax = ARRAYLEN(touchactions);
1243 while ((region->action == ACTION_NONE) &&
1244 (i < imax))
1246 /* try to match with one of our touchregion screens */
1247 int len = strlen(touchactions[i].s);
1248 if (!strncmp(touchactions[i].s, action, len)
1249 && *(action+len) == '|')
1250 region->action = touchactions[i].action;
1251 i++;
1253 if (region->action == ACTION_NONE)
1254 return WPS_ERROR_INVALID_PARAM;
1256 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1257 if (!item)
1258 return WPS_ERROR_INVALID_PARAM;
1259 add_to_ll_chain(&wps_data->touchregions, item);
1260 return skip_end_of_line(wps_bufptr);
1262 #endif
1264 /* Parse a generic token from the given string. Return the length read */
1265 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1267 int skip = 0, taglen = 0, ret;
1268 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1269 const struct wps_tag *tag;
1270 memset(token, 0, sizeof(*token));
1272 switch(*wps_bufptr)
1275 case '%':
1276 case '<':
1277 case '|':
1278 case '>':
1279 case ';':
1280 case '#':
1281 /* escaped characters */
1282 token->type = WPS_TOKEN_CHARACTER;
1283 token->value.c = *wps_bufptr;
1284 taglen = 1;
1285 wps_data->num_tokens++;
1286 break;
1288 case '?':
1289 /* conditional tag */
1290 token->type = WPS_TOKEN_CONDITIONAL;
1291 level++;
1292 condindex[level] = wps_data->num_tokens;
1293 numoptions[level] = 1;
1294 wps_data->num_tokens++;
1295 ret = parse_token(wps_bufptr + 1, wps_data);
1296 if (ret < 0) return ret;
1297 taglen = 1 + ret;
1298 break;
1300 default:
1301 /* find what tag we have */
1302 for (tag = all_tags;
1303 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1304 tag++) ;
1306 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1307 token->type = tag->type;
1308 curr_line->curr_subline->line_type |= tag->refresh_type;
1310 /* if the tag has a special parsing function, we call it */
1311 if (tag->parse_func)
1313 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1314 if (ret < 0) return ret;
1315 skip += ret;
1318 /* Some tags we don't want to save as tokens */
1319 if (tag->type == WPS_NO_TOKEN)
1320 break;
1322 /* tags that start with 'F', 'I' or 'D' are for the next file */
1323 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1324 *(tag->name) == 'D')
1325 token->next = true;
1327 wps_data->num_tokens++;
1328 break;
1331 skip += taglen;
1332 return skip;
1335 /* Parses the WPS.
1336 data is the pointer to the structure where the parsed WPS should be stored.
1337 It is initialised.
1338 wps_bufptr points to the string containing the WPS tags */
1339 #define TOKEN_BLOCK_SIZE 128
1340 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1342 if (!data || !wps_bufptr || !*wps_bufptr)
1343 return false;
1344 enum wps_parse_error fail = PARSE_OK;
1345 int ret;
1346 int max_tokens = TOKEN_BLOCK_SIZE;
1347 size_t buf_free = 0;
1348 line_number = 1;
1349 level = -1;
1351 /* allocate enough RAM for a reasonable skin, grow as needed.
1352 * Free any used RAM before loading the images to be 100% RAM efficient */
1353 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1354 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1355 return false;
1356 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1357 data->num_tokens = 0;
1359 while (*wps_bufptr && !fail)
1361 /* first make sure there is enough room for tokens */
1362 if (max_tokens <= data->num_tokens + 5)
1364 int extra_tokens = TOKEN_BLOCK_SIZE;
1365 size_t needed = extra_tokens * sizeof(struct wps_token);
1366 /* do some smarts here to grow the array a bit */
1367 if (skin_buffer_freespace() < needed)
1369 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1370 break;
1372 skin_buffer_increment(needed, false);
1373 max_tokens += extra_tokens;
1376 switch(*wps_bufptr++)
1379 /* Regular tag */
1380 case '%':
1381 if ((ret = parse_token(wps_bufptr, data)) < 0)
1383 fail = PARSE_FAIL_COND_INVALID_PARAM;
1384 break;
1386 else if (level >= WPS_MAX_COND_LEVEL - 1)
1388 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1389 break;
1391 wps_bufptr += ret;
1392 break;
1394 /* Alternating sublines separator */
1395 case ';':
1396 if (level >= 0) /* there are unclosed conditionals */
1398 fail = PARSE_FAIL_UNCLOSED_COND;
1399 break;
1402 if (!skin_start_new_subline(curr_line, data->num_tokens))
1403 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1405 break;
1407 /* Conditional list start */
1408 case '<':
1409 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1411 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1412 break;
1415 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1416 lastcond[level] = data->num_tokens++;
1417 break;
1419 /* Conditional list end */
1420 case '>':
1421 if (level < 0) /* not in a conditional, invalid char */
1423 fail = PARSE_FAIL_INVALID_CHAR;
1424 break;
1427 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1428 if (lastcond[level])
1429 data->tokens[lastcond[level]].value.i = data->num_tokens;
1430 else
1432 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1433 break;
1436 lastcond[level] = 0;
1437 data->num_tokens++;
1438 data->tokens[condindex[level]].value.i = numoptions[level];
1439 level--;
1440 break;
1442 /* Conditional list option */
1443 case '|':
1444 if (level < 0) /* not in a conditional, invalid char */
1446 fail = PARSE_FAIL_INVALID_CHAR;
1447 break;
1450 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1451 if (lastcond[level])
1452 data->tokens[lastcond[level]].value.i = data->num_tokens;
1453 else
1455 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1456 break;
1459 lastcond[level] = data->num_tokens;
1460 numoptions[level]++;
1461 data->num_tokens++;
1462 break;
1464 /* Comment */
1465 case '#':
1466 if (level >= 0) /* there are unclosed conditionals */
1468 fail = PARSE_FAIL_UNCLOSED_COND;
1469 break;
1472 wps_bufptr += skip_end_of_line(wps_bufptr);
1473 break;
1475 /* End of this line */
1476 case '\n':
1477 if (level >= 0) /* there are unclosed conditionals */
1479 fail = PARSE_FAIL_UNCLOSED_COND;
1480 break;
1482 /* add a new token for the \n so empty lines are correct */
1483 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1484 data->tokens[data->num_tokens].value.c = '\n';
1485 data->tokens[data->num_tokens].next = false;
1486 data->num_tokens++;
1488 if (!skin_start_new_line(curr_vp, data->num_tokens))
1490 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1491 break;
1494 break;
1496 /* String */
1497 default:
1499 unsigned int len = 1;
1500 const char *string_start = wps_bufptr - 1;
1502 /* find the length of the string */
1503 while (*wps_bufptr && *wps_bufptr != '#' &&
1504 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1505 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1506 *wps_bufptr != '|' && *wps_bufptr != '\n')
1508 wps_bufptr++;
1509 len++;
1512 /* look if we already have that string */
1513 char *str;
1514 bool found = false;
1515 struct skin_token_list *list = data->strings;
1516 while (list)
1518 str = (char*)list->token->value.data;
1519 found = (strlen(str) == len &&
1520 strncmp(string_start, str, len) == 0);
1521 if (found)
1522 break; /* break here because the list item is
1523 used if its found */
1524 list = list->next;
1526 /* If a matching string is found, found is true and i is
1527 the index of the string. If not, found is false */
1529 if (!found)
1531 /* new string */
1532 str = (char*)skin_buffer_alloc(len+1);
1533 if (!str)
1535 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1536 break;
1538 strlcpy(str, string_start, len+1);
1539 struct skin_token_list *item =
1540 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1541 if(!item)
1543 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1544 break;
1546 add_to_ll_chain(&data->strings, item);
1548 else
1550 /* another occurrence of an existing string */
1551 data->tokens[data->num_tokens].value.data = list->token->value.data;
1553 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1554 data->num_tokens++;
1556 break;
1560 if (!fail && level >= 0) /* there are unclosed conditionals */
1561 fail = PARSE_FAIL_UNCLOSED_COND;
1563 if (*wps_bufptr && !fail)
1564 /* one of the limits of the while loop was exceeded */
1565 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1567 /* Success! */
1568 curr_line->curr_subline->last_token_idx = data->num_tokens;
1569 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1570 /* freeup unused tokens */
1571 skin_buffer_free_from_front(sizeof(struct wps_token)
1572 * (max_tokens - data->num_tokens));
1574 #if defined(DEBUG) || defined(SIMULATOR)
1575 if (debug)
1576 print_debug_info(data, fail, line_number);
1577 #else
1578 (void)debug;
1579 #endif
1581 return (fail == 0);
1584 static void wps_reset(struct wps_data *data)
1586 #ifdef HAVE_REMOTE_LCD
1587 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1588 #endif
1589 memset(data, 0, sizeof(*data));
1590 skin_data_init(data);
1591 #ifdef HAVE_REMOTE_LCD
1592 data->remote_wps = rwps;
1593 #endif
1596 #ifdef HAVE_LCD_BITMAP
1597 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1599 (void)wps_data; /* only needed for remote targets */
1600 bool loaded = false;
1601 char img_path[MAX_PATH];
1602 get_image_filename(bitmap->data, bmpdir,
1603 img_path, sizeof(img_path));
1605 /* load the image */
1606 int format;
1607 #ifdef HAVE_REMOTE_LCD
1608 if (wps_data->remote_wps)
1609 format = FORMAT_ANY|FORMAT_REMOTE;
1610 else
1611 #endif
1612 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1614 size_t max_buf;
1615 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1616 bitmap->data = imgbuf;
1617 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1619 if (ret > 0)
1621 skin_buffer_increment(ret, true);
1622 loaded = true;
1624 else
1626 /* Abort if we can't load an image */
1627 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1628 loaded = false;
1630 return loaded;
1633 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1635 struct skin_token_list *list;
1636 /* do the progressbars */
1637 list = wps_data->progressbars;
1638 while (list)
1640 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1641 if (pb->bm.data)
1643 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1645 list = list->next;
1647 /* regular images */
1648 list = wps_data->images;
1649 while (list)
1651 struct gui_img *img = (struct gui_img*)list->token->value.data;
1652 if (img->bm.data)
1654 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1655 if (img->loaded)
1656 img->subimage_height = img->bm.height / img->num_subimages;
1658 list = list->next;
1661 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1662 if (bmp_names[BACKDROP_BMP])
1664 int screen = SCREEN_MAIN;
1665 char img_path[MAX_PATH];
1666 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1667 img_path, sizeof(img_path));
1668 #if defined(HAVE_REMOTE_LCD)
1669 /* We only need to check LCD type if there is a remote LCD */
1670 if (wps_data->remote_wps)
1671 screen = SCREEN_REMOTE;
1672 #endif
1673 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1675 #endif /* has backdrop support */
1677 /* If we got here, everything was OK */
1678 return true;
1681 #endif /* HAVE_LCD_BITMAP */
1683 /* to setup up the wps-data from a format-buffer (isfile = false)
1684 from a (wps-)file (isfile = true)*/
1685 bool skin_data_load(struct wps_data *wps_data,
1686 struct screen *display,
1687 const char *buf,
1688 bool isfile)
1691 if (!wps_data || !buf)
1692 return false;
1693 #ifdef HAVE_ALBUMART
1694 int status;
1695 struct mp3entry *curtrack;
1696 long offset;
1697 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1698 if (wps_data->albumart)
1700 old_aa.state = wps_data->albumart->state;
1701 old_aa.height = wps_data->albumart->height;
1702 old_aa.width = wps_data->albumart->width;
1704 #endif
1706 wps_reset(wps_data);
1708 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1709 if (!curr_vp)
1710 return false;
1711 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1712 if (!list)
1713 return false;
1714 add_to_ll_chain(&wps_data->viewports, list);
1717 /* Initialise the first (default) viewport */
1718 curr_vp->label = VP_DEFAULT_LABEL;
1719 curr_vp->vp.x = 0;
1720 curr_vp->vp.width = display->getwidth();
1721 curr_vp->vp.height = display->getheight();
1722 curr_vp->pb = NULL;
1723 curr_vp->hidden_flags = 0;
1724 curr_vp->lines = NULL;
1726 curr_line = NULL;
1727 if (!skin_start_new_line(curr_vp, 0))
1728 return false;
1730 switch (statusbar_position(display->screen_type))
1732 case STATUSBAR_OFF:
1733 curr_vp->vp.y = 0;
1734 break;
1735 case STATUSBAR_TOP:
1736 curr_vp->vp.y = STATUSBAR_HEIGHT;
1737 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1738 break;
1739 case STATUSBAR_BOTTOM:
1740 curr_vp->vp.y = 0;
1741 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1742 break;
1744 #ifdef HAVE_LCD_BITMAP
1745 curr_vp->vp.font = FONT_UI;
1746 curr_vp->vp.drawmode = DRMODE_SOLID;
1747 #endif
1748 #if LCD_DEPTH > 1
1749 if (display->depth > 1)
1751 curr_vp->vp.fg_pattern = display->get_foreground();
1752 curr_vp->vp.bg_pattern = display->get_background();
1754 #endif
1755 if (!isfile)
1757 return wps_parse(wps_data, buf, false);
1759 else
1761 int fd = open_utf8(buf, O_RDONLY);
1763 if (fd < 0)
1764 return false;
1766 /* get buffer space from the plugin buffer */
1767 size_t buffersize = 0;
1768 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1770 if (!wps_buffer)
1771 return false;
1773 /* copy the file's content to the buffer for parsing,
1774 ensuring that every line ends with a newline char. */
1775 unsigned int start = 0;
1776 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1778 start += strlen(wps_buffer + start);
1779 if (start < buffersize - 1)
1781 wps_buffer[start++] = '\n';
1782 wps_buffer[start] = 0;
1786 close(fd);
1788 if (start <= 0)
1789 return false;
1791 #ifdef HAVE_LCD_BITMAP
1792 /* Set all filename pointers to NULL */
1793 memset(bmp_names, 0, sizeof(bmp_names));
1794 #endif
1796 /* parse the WPS source */
1797 if (!wps_parse(wps_data, wps_buffer, true)) {
1798 wps_reset(wps_data);
1799 return false;
1802 wps_data->wps_loaded = true;
1804 #ifdef HAVE_LCD_BITMAP
1805 /* get the bitmap dir */
1806 char bmpdir[MAX_PATH];
1807 char *dot = strrchr(buf, '.');
1809 strlcpy(bmpdir, buf, dot - buf + 1);
1811 /* load the bitmaps that were found by the parsing */
1812 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1813 wps_reset(wps_data);
1814 return false;
1816 #endif
1817 #ifdef HAVE_ALBUMART
1818 status = audio_status();
1819 if (status & AUDIO_STATUS_PLAY)
1821 struct skin_albumart *aa = wps_data->albumart;
1822 if (aa && ((aa->state && !old_aa.state) ||
1823 (aa->state &&
1824 (((old_aa.height != aa->height) ||
1825 (old_aa.width != aa->width))))))
1827 curtrack = audio_current_track();
1828 offset = curtrack->offset;
1829 audio_stop();
1830 if (!(status & AUDIO_STATUS_PAUSE))
1831 audio_play(offset);
1834 #endif
1835 #if defined(DEBUG) || defined(SIMULATOR)
1836 debug_skin_usage();
1837 #endif
1838 return true;