Fix a few problems with RTL, statusbar and custom ui viewport.
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobe7c9f4fb52fd8c0787c0778434ea1bb1adb03c7c
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 vp->flags &= ~VP_FLAG_IS_RTL; /* ignore rright-to-left languages */
710 /* Check for trailing | */
711 if (*ptr != '|')
712 return WPS_ERROR_INVALID_PARAM;
715 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
716 if (!list)
717 return WPS_ERROR_INVALID_PARAM;
718 add_to_ll_chain(&wps_data->viewports, list);
719 curr_vp = skin_vp;
720 /* Skip the rest of the line */
721 return skip_end_of_line(wps_bufptr);
724 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
725 static int parse_image_special(const char *wps_bufptr,
726 struct wps_token *token,
727 struct wps_data *wps_data)
729 (void)wps_data; /* kill warning */
730 (void)token;
731 const char *pos = NULL;
732 const char *newline;
734 pos = strchr(wps_bufptr + 1, '|');
735 newline = strchr(wps_bufptr, '\n');
737 if (pos > newline)
738 return WPS_ERROR_INVALID_PARAM;
739 #if LCD_DEPTH > 1
740 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
742 /* format: %X|filename.bmp| */
743 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
745 #endif
747 /* Skip the rest of the line */
748 return skip_end_of_line(wps_bufptr);
750 #endif
752 #endif /* HAVE_LCD_BITMAP */
754 static int parse_setting_and_lang(const char *wps_bufptr,
755 struct wps_token *token,
756 struct wps_data *wps_data)
758 /* NOTE: both the string validations that happen in here will
759 * automatically PASS on checkwps because its too hard to get
760 * settings_list.c and englinsh.lang built for it.
761 * If that ever changes remove the #ifndef __PCTOOL__'s here
763 (void)wps_data;
764 const char *ptr = wps_bufptr;
765 const char *end;
766 int i = 0;
767 char temp[64];
769 /* Find the setting's cfg_name */
770 if (*ptr != '|')
771 return WPS_ERROR_INVALID_PARAM;
772 ptr++;
773 end = strchr(ptr,'|');
774 if (!end)
775 return WPS_ERROR_INVALID_PARAM;
776 strlcpy(temp, ptr,end-ptr+1);
778 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
780 #ifndef __PCTOOL__
781 i = lang_english_to_id(temp);
782 if (i < 0)
783 return WPS_ERROR_INVALID_PARAM;
784 #endif
786 else
788 /* Find the setting */
789 for (i=0; i<nb_settings; i++)
790 if (settings[i].cfg_name &&
791 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
792 /* prevent matches on cfg_name prefixes */
793 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
794 break;
795 #ifndef __PCTOOL__
796 if (i == nb_settings)
797 return WPS_ERROR_INVALID_PARAM;
798 #endif
800 /* Store the setting number */
801 token->value.i = i;
803 /* Skip the rest of the line */
804 return end-ptr+2;
808 static int parse_dir_level(const char *wps_bufptr,
809 struct wps_token *token,
810 struct wps_data *wps_data)
812 char val[] = { *wps_bufptr, '\0' };
813 token->value.i = atoi(val);
814 (void)wps_data; /* Kill warnings */
815 return 1;
818 static int parse_timeout(const char *wps_bufptr,
819 struct wps_token *token,
820 struct wps_data *wps_data)
822 int skip = 0;
823 int val = 0;
824 bool have_point = false;
825 bool have_tenth = false;
827 (void)wps_data; /* Kill the warning */
829 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
831 if (*wps_bufptr != '.')
833 val *= 10;
834 val += *wps_bufptr - '0';
835 if (have_point)
837 have_tenth = true;
838 wps_bufptr++;
839 skip++;
840 break;
843 else
844 have_point = true;
846 wps_bufptr++;
847 skip++;
850 if (have_tenth == false)
851 val *= 10;
853 if (val == 0 && skip == 0)
855 /* decide what to do if no value was specified */
856 switch (token->type)
858 case WPS_TOKEN_SUBLINE_TIMEOUT:
859 return -1;
860 case WPS_TOKEN_BUTTON_VOLUME:
861 val = 10;
862 break;
865 token->value.i = val;
867 return skip;
870 static int parse_progressbar(const char *wps_bufptr,
871 struct wps_token *token,
872 struct wps_data *wps_data)
874 /* %pb or %pb|filename|x|y|width|height|
875 using - for any of the params uses "sane" values */
876 #ifdef HAVE_LCD_BITMAP
877 enum {
878 PB_FILENAME = 0,
879 PB_X,
880 PB_Y,
881 PB_WIDTH,
882 PB_HEIGHT
884 const char *filename;
885 int x, y, height, width;
886 uint32_t set = 0;
887 const char *ptr = wps_bufptr;
888 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
889 struct skin_token_list *item = new_skin_token_list_item(token, pb);
891 if (!pb || !item)
892 return WPS_ERROR_INVALID_PARAM;
894 struct viewport *vp = &curr_vp->vp;
895 #ifndef __PCTOOL__
896 int font_height = font_get(vp->font)->height;
897 #else
898 int font_height = 8;
899 #endif
900 /* we need to know what line number (viewport relative) this pb is,
901 * so count them... */
902 int line_num = -1;
903 struct skin_line *line = curr_vp->lines;
904 while (line)
906 line_num++;
907 line = line->next;
909 pb->have_bitmap_pb = false;
910 pb->bm.data = NULL; /* no bitmap specified */
912 if (*wps_bufptr != '|') /* regular old style */
914 pb->x = 0;
915 pb->width = vp->width;
916 pb->height = SYSFONT_HEIGHT-2;
917 pb->y = -line_num - 1; /* Will be computed during the rendering */
919 curr_vp->pb = pb;
920 add_to_ll_chain(&wps_data->progressbars, item);
921 return 0;
923 ptr = wps_bufptr + 1;
925 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
926 &x, &y, &width, &height)))
927 return WPS_ERROR_INVALID_PARAM;
929 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
930 pb->bm.data = (char*)filename;
932 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
933 pb->x = x;
934 else
935 pb->x = vp->x;
937 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
939 /* A zero width causes a divide-by-zero error later, so reject it */
940 if (width == 0)
941 return WPS_ERROR_INVALID_PARAM;
943 pb->width = width;
945 else
946 pb->width = vp->width - pb->x;
948 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
950 /* A zero height makes no sense - reject it */
951 if (height == 0)
952 return WPS_ERROR_INVALID_PARAM;
954 pb->height = height;
956 else
957 pb->height = font_height;
959 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
960 pb->y = y;
961 else
962 pb->y = -line_num - 1; /* Will be computed during the rendering */
964 curr_vp->pb = pb;
965 add_to_ll_chain(&wps_data->progressbars, item);
967 /* Skip the rest of the line */
968 return skip_end_of_line(wps_bufptr)-1;
969 #else
970 (void)token;
972 if (*(wps_bufptr-1) == 'f')
973 wps_data->full_line_progressbar = true;
974 else
975 wps_data->full_line_progressbar = false;
977 return 0;
979 #endif
982 #ifdef HAVE_ALBUMART
983 static int parse_albumart_load(const char *wps_bufptr,
984 struct wps_token *token,
985 struct wps_data *wps_data)
987 const char *_pos, *newline;
988 bool parsing;
989 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
990 (void)token; /* silence warning */
991 if (!aa)
992 return skip_end_of_line(wps_bufptr);
994 /* reset albumart info in wps */
995 aa->width = -1;
996 aa->height = -1;
997 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
998 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
999 aa->vp = &curr_vp->vp;
1001 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1003 newline = strchr(wps_bufptr, '\n');
1005 /* initial validation and parsing of x and y components */
1006 if (*wps_bufptr != '|')
1007 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1009 _pos = wps_bufptr + 1;
1010 _pos = parse_list("dd", NULL, '|', _pos, &aa->x, &aa->y);
1012 if (!_pos || _pos > newline || *_pos != '|')
1013 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1014 e.g. %Cl|7|59\n */
1016 /* parsing width field */
1017 parsing = true;
1018 while (parsing)
1020 /* apply each modifier in turn */
1021 ++_pos;
1022 switch (*_pos)
1024 case 'l':
1025 case 'L':
1026 case '+':
1027 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1028 break;
1029 case 'c':
1030 case 'C':
1031 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1032 break;
1033 case 'r':
1034 case 'R':
1035 case '-':
1036 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1037 break;
1038 case 'd':
1039 case 'D':
1040 case 'i':
1041 case 'I':
1042 case 's':
1043 case 'S':
1044 /* simply ignored */
1045 break;
1046 default:
1047 parsing = false;
1048 break;
1051 /* extract max width data */
1052 if (*_pos != '|')
1054 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1055 return WPS_ERROR_INVALID_PARAM;
1057 aa->width = atoi(_pos);
1059 _pos = strchr(_pos, '|');
1060 if (!_pos || _pos > newline)
1061 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1062 e.g. %Cl|7|59|200\n */
1065 /* parsing height field */
1066 parsing = true;
1067 while (parsing)
1069 /* apply each modifier in turn */
1070 ++_pos;
1071 switch (*_pos)
1073 case 't':
1074 case 'T':
1075 case '-':
1076 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1077 break;
1078 case 'c':
1079 case 'C':
1080 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1081 break;
1082 case 'b':
1083 case 'B':
1084 case '+':
1085 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1086 break;
1087 case 'd':
1088 case 'D':
1089 case 'i':
1090 case 'I':
1091 case 's':
1092 case 'S':
1093 /* simply ignored */
1094 break;
1095 default:
1096 parsing = false;
1097 break;
1100 /* extract max height data */
1101 if (*_pos != '|')
1103 if (!isdigit(*_pos))
1104 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1106 aa->height = atoi(_pos);
1108 _pos = strchr(_pos, '|');
1109 if (!_pos || _pos > newline)
1110 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1111 e.g. %Cl|7|59|200|200\n */
1114 /* if we got here, we parsed everything ok .. ! */
1115 if (aa->width < 0)
1116 aa->width = 0;
1117 else if (aa->width > LCD_WIDTH)
1118 aa->width = LCD_WIDTH;
1120 if (aa->height < 0)
1121 aa->height = 0;
1122 else if (aa->height > LCD_HEIGHT)
1123 aa->height = LCD_HEIGHT;
1125 aa->state = WPS_ALBUMART_LOAD;
1126 aa->draw = false;
1127 wps_data->albumart = aa;
1129 /* Skip the rest of the line */
1130 return skip_end_of_line(wps_bufptr);
1133 static int parse_albumart_display(const char *wps_bufptr,
1134 struct wps_token *token,
1135 struct wps_data *wps_data)
1137 (void)wps_bufptr;
1138 struct wps_token *prev = token-1;
1139 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1141 token->type = WPS_TOKEN_ALBUMART_FOUND;
1143 else if (wps_data->albumart)
1145 wps_data->albumart->vp = &curr_vp->vp;
1147 #if 0
1148 /* the old code did this so keep it here for now...
1149 * this is to allow the posibility to showing the next tracks AA! */
1150 if (wps_bufptr+1 == 'n')
1151 return 1;
1152 #endif
1153 return 0;
1155 #endif /* HAVE_ALBUMART */
1157 #ifdef HAVE_TOUCHSCREEN
1159 struct touchaction {char* s; int action;};
1160 static struct touchaction touchactions[] = {
1161 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1162 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1163 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1164 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1165 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1166 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1167 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1169 static int parse_touchregion(const char *wps_bufptr,
1170 struct wps_token *token, struct wps_data *wps_data)
1172 (void)token;
1173 unsigned i, imax;
1174 struct touchregion *region = NULL;
1175 const char *ptr = wps_bufptr;
1176 const char *action;
1177 const char pb_string[] = "progressbar";
1178 const char vol_string[] = "volume";
1179 int x,y,w,h;
1181 /* format: %T|x|y|width|height|action|
1182 * if action starts with & the area must be held to happen
1183 * action is one of:
1184 * play - play/pause playback
1185 * stop - stop playback, exit the wps
1186 * prev - prev track
1187 * next - next track
1188 * ffwd - seek forward
1189 * rwd - seek backwards
1190 * menu - go back to the main menu
1191 * browse - go back to the file/db browser
1192 * shuffle - toggle shuffle mode
1193 * repmode - cycle the repeat mode
1194 * quickscreen - go into the quickscreen
1195 * contextmenu - open the context menu
1196 * playlist - go into the playlist
1197 * pitch - go into the pitchscreen
1201 if (*ptr != '|')
1202 return WPS_ERROR_INVALID_PARAM;
1203 ptr++;
1205 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1206 return WPS_ERROR_INVALID_PARAM;
1208 /* Check there is a terminating | */
1209 if (*ptr != '|')
1210 return WPS_ERROR_INVALID_PARAM;
1212 region = skin_buffer_alloc(sizeof(struct touchregion));
1213 if (!region)
1214 return WPS_ERROR_INVALID_PARAM;
1216 /* should probably do some bounds checking here with the viewport... but later */
1217 region->action = ACTION_NONE;
1218 region->x = x;
1219 region->y = y;
1220 region->width = w;
1221 region->height = h;
1222 region->wvp = curr_vp;
1224 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1225 && *(action + sizeof(pb_string)-1) == '|')
1226 region->type = WPS_TOUCHREGION_SCROLLBAR;
1227 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1228 && *(action + sizeof(vol_string)-1) == '|')
1229 region->type = WPS_TOUCHREGION_VOLUME;
1230 else
1232 region->type = WPS_TOUCHREGION_ACTION;
1234 if (*action == '&')
1236 action++;
1237 region->repeat = true;
1239 else
1240 region->repeat = false;
1242 i = 0;
1243 imax = ARRAYLEN(touchactions);
1244 while ((region->action == ACTION_NONE) &&
1245 (i < imax))
1247 /* try to match with one of our touchregion screens */
1248 int len = strlen(touchactions[i].s);
1249 if (!strncmp(touchactions[i].s, action, len)
1250 && *(action+len) == '|')
1251 region->action = touchactions[i].action;
1252 i++;
1254 if (region->action == ACTION_NONE)
1255 return WPS_ERROR_INVALID_PARAM;
1257 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1258 if (!item)
1259 return WPS_ERROR_INVALID_PARAM;
1260 add_to_ll_chain(&wps_data->touchregions, item);
1261 return skip_end_of_line(wps_bufptr);
1263 #endif
1265 /* Parse a generic token from the given string. Return the length read */
1266 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1268 int skip = 0, taglen = 0, ret;
1269 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1270 const struct wps_tag *tag;
1271 memset(token, 0, sizeof(*token));
1273 switch(*wps_bufptr)
1276 case '%':
1277 case '<':
1278 case '|':
1279 case '>':
1280 case ';':
1281 case '#':
1282 /* escaped characters */
1283 token->type = WPS_TOKEN_CHARACTER;
1284 token->value.c = *wps_bufptr;
1285 taglen = 1;
1286 wps_data->num_tokens++;
1287 break;
1289 case '?':
1290 /* conditional tag */
1291 token->type = WPS_TOKEN_CONDITIONAL;
1292 level++;
1293 condindex[level] = wps_data->num_tokens;
1294 numoptions[level] = 1;
1295 wps_data->num_tokens++;
1296 ret = parse_token(wps_bufptr + 1, wps_data);
1297 if (ret < 0) return ret;
1298 taglen = 1 + ret;
1299 break;
1301 default:
1302 /* find what tag we have */
1303 for (tag = all_tags;
1304 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1305 tag++) ;
1307 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1308 token->type = tag->type;
1309 curr_line->curr_subline->line_type |= tag->refresh_type;
1311 /* if the tag has a special parsing function, we call it */
1312 if (tag->parse_func)
1314 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1315 if (ret < 0) return ret;
1316 skip += ret;
1319 /* Some tags we don't want to save as tokens */
1320 if (tag->type == WPS_NO_TOKEN)
1321 break;
1323 /* tags that start with 'F', 'I' or 'D' are for the next file */
1324 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1325 *(tag->name) == 'D')
1326 token->next = true;
1328 wps_data->num_tokens++;
1329 break;
1332 skip += taglen;
1333 return skip;
1336 /* Parses the WPS.
1337 data is the pointer to the structure where the parsed WPS should be stored.
1338 It is initialised.
1339 wps_bufptr points to the string containing the WPS tags */
1340 #define TOKEN_BLOCK_SIZE 128
1341 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1343 if (!data || !wps_bufptr || !*wps_bufptr)
1344 return false;
1345 enum wps_parse_error fail = PARSE_OK;
1346 int ret;
1347 int max_tokens = TOKEN_BLOCK_SIZE;
1348 size_t buf_free = 0;
1349 line_number = 1;
1350 level = -1;
1352 /* allocate enough RAM for a reasonable skin, grow as needed.
1353 * Free any used RAM before loading the images to be 100% RAM efficient */
1354 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1355 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1356 return false;
1357 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1358 data->num_tokens = 0;
1360 while (*wps_bufptr && !fail)
1362 /* first make sure there is enough room for tokens */
1363 if (max_tokens <= data->num_tokens + 5)
1365 int extra_tokens = TOKEN_BLOCK_SIZE;
1366 size_t needed = extra_tokens * sizeof(struct wps_token);
1367 /* do some smarts here to grow the array a bit */
1368 if (skin_buffer_freespace() < needed)
1370 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1371 break;
1373 skin_buffer_increment(needed, false);
1374 max_tokens += extra_tokens;
1377 switch(*wps_bufptr++)
1380 /* Regular tag */
1381 case '%':
1382 if ((ret = parse_token(wps_bufptr, data)) < 0)
1384 fail = PARSE_FAIL_COND_INVALID_PARAM;
1385 break;
1387 else if (level >= WPS_MAX_COND_LEVEL - 1)
1389 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1390 break;
1392 wps_bufptr += ret;
1393 break;
1395 /* Alternating sublines separator */
1396 case ';':
1397 if (level >= 0) /* there are unclosed conditionals */
1399 fail = PARSE_FAIL_UNCLOSED_COND;
1400 break;
1403 if (!skin_start_new_subline(curr_line, data->num_tokens))
1404 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1406 break;
1408 /* Conditional list start */
1409 case '<':
1410 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1412 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1413 break;
1416 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1417 lastcond[level] = data->num_tokens++;
1418 break;
1420 /* Conditional list end */
1421 case '>':
1422 if (level < 0) /* not in a conditional, invalid char */
1424 fail = PARSE_FAIL_INVALID_CHAR;
1425 break;
1428 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1429 if (lastcond[level])
1430 data->tokens[lastcond[level]].value.i = data->num_tokens;
1431 else
1433 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1434 break;
1437 lastcond[level] = 0;
1438 data->num_tokens++;
1439 data->tokens[condindex[level]].value.i = numoptions[level];
1440 level--;
1441 break;
1443 /* Conditional list option */
1444 case '|':
1445 if (level < 0) /* not in a conditional, invalid char */
1447 fail = PARSE_FAIL_INVALID_CHAR;
1448 break;
1451 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1452 if (lastcond[level])
1453 data->tokens[lastcond[level]].value.i = data->num_tokens;
1454 else
1456 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1457 break;
1460 lastcond[level] = data->num_tokens;
1461 numoptions[level]++;
1462 data->num_tokens++;
1463 break;
1465 /* Comment */
1466 case '#':
1467 if (level >= 0) /* there are unclosed conditionals */
1469 fail = PARSE_FAIL_UNCLOSED_COND;
1470 break;
1473 wps_bufptr += skip_end_of_line(wps_bufptr);
1474 break;
1476 /* End of this line */
1477 case '\n':
1478 if (level >= 0) /* there are unclosed conditionals */
1480 fail = PARSE_FAIL_UNCLOSED_COND;
1481 break;
1483 /* add a new token for the \n so empty lines are correct */
1484 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1485 data->tokens[data->num_tokens].value.c = '\n';
1486 data->tokens[data->num_tokens].next = false;
1487 data->num_tokens++;
1489 if (!skin_start_new_line(curr_vp, data->num_tokens))
1491 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1492 break;
1495 break;
1497 /* String */
1498 default:
1500 unsigned int len = 1;
1501 const char *string_start = wps_bufptr - 1;
1503 /* find the length of the string */
1504 while (*wps_bufptr && *wps_bufptr != '#' &&
1505 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1506 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1507 *wps_bufptr != '|' && *wps_bufptr != '\n')
1509 wps_bufptr++;
1510 len++;
1513 /* look if we already have that string */
1514 char *str;
1515 bool found = false;
1516 struct skin_token_list *list = data->strings;
1517 while (list)
1519 str = (char*)list->token->value.data;
1520 found = (strlen(str) == len &&
1521 strncmp(string_start, str, len) == 0);
1522 if (found)
1523 break; /* break here because the list item is
1524 used if its found */
1525 list = list->next;
1527 /* If a matching string is found, found is true and i is
1528 the index of the string. If not, found is false */
1530 if (!found)
1532 /* new string */
1533 str = (char*)skin_buffer_alloc(len+1);
1534 if (!str)
1536 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1537 break;
1539 strlcpy(str, string_start, len+1);
1540 struct skin_token_list *item =
1541 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1542 if(!item)
1544 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1545 break;
1547 add_to_ll_chain(&data->strings, item);
1549 else
1551 /* another occurrence of an existing string */
1552 data->tokens[data->num_tokens].value.data = list->token->value.data;
1554 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1555 data->num_tokens++;
1557 break;
1561 if (!fail && level >= 0) /* there are unclosed conditionals */
1562 fail = PARSE_FAIL_UNCLOSED_COND;
1564 if (*wps_bufptr && !fail)
1565 /* one of the limits of the while loop was exceeded */
1566 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1568 /* Success! */
1569 curr_line->curr_subline->last_token_idx = data->num_tokens;
1570 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1571 /* freeup unused tokens */
1572 skin_buffer_free_from_front(sizeof(struct wps_token)
1573 * (max_tokens - data->num_tokens));
1575 #if defined(DEBUG) || defined(SIMULATOR)
1576 if (debug)
1577 print_debug_info(data, fail, line_number);
1578 #else
1579 (void)debug;
1580 #endif
1582 return (fail == 0);
1585 static void wps_reset(struct wps_data *data)
1587 #ifdef HAVE_REMOTE_LCD
1588 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1589 #endif
1590 memset(data, 0, sizeof(*data));
1591 skin_data_init(data);
1592 #ifdef HAVE_REMOTE_LCD
1593 data->remote_wps = rwps;
1594 #endif
1597 #ifdef HAVE_LCD_BITMAP
1598 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1600 (void)wps_data; /* only needed for remote targets */
1601 bool loaded = false;
1602 char img_path[MAX_PATH];
1603 get_image_filename(bitmap->data, bmpdir,
1604 img_path, sizeof(img_path));
1606 /* load the image */
1607 int format;
1608 #ifdef HAVE_REMOTE_LCD
1609 if (wps_data->remote_wps)
1610 format = FORMAT_ANY|FORMAT_REMOTE;
1611 else
1612 #endif
1613 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1615 size_t max_buf;
1616 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1617 bitmap->data = imgbuf;
1618 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1620 if (ret > 0)
1622 skin_buffer_increment(ret, true);
1623 loaded = true;
1625 else
1627 /* Abort if we can't load an image */
1628 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1629 loaded = false;
1631 return loaded;
1634 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1636 struct skin_token_list *list;
1637 /* do the progressbars */
1638 list = wps_data->progressbars;
1639 while (list)
1641 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1642 if (pb->bm.data)
1644 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1646 list = list->next;
1648 /* regular images */
1649 list = wps_data->images;
1650 while (list)
1652 struct gui_img *img = (struct gui_img*)list->token->value.data;
1653 if (img->bm.data)
1655 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1656 if (img->loaded)
1657 img->subimage_height = img->bm.height / img->num_subimages;
1659 list = list->next;
1662 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1663 if (bmp_names[BACKDROP_BMP])
1665 int screen = SCREEN_MAIN;
1666 char img_path[MAX_PATH];
1667 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1668 img_path, sizeof(img_path));
1669 #if defined(HAVE_REMOTE_LCD)
1670 /* We only need to check LCD type if there is a remote LCD */
1671 if (wps_data->remote_wps)
1672 screen = SCREEN_REMOTE;
1673 #endif
1674 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1676 #endif /* has backdrop support */
1678 /* If we got here, everything was OK */
1679 return true;
1682 #endif /* HAVE_LCD_BITMAP */
1684 /* to setup up the wps-data from a format-buffer (isfile = false)
1685 from a (wps-)file (isfile = true)*/
1686 bool skin_data_load(struct wps_data *wps_data,
1687 struct screen *display,
1688 const char *buf,
1689 bool isfile)
1692 if (!wps_data || !buf)
1693 return false;
1694 #ifdef HAVE_ALBUMART
1695 int status;
1696 struct mp3entry *curtrack;
1697 long offset;
1698 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1699 if (wps_data->albumart)
1701 old_aa.state = wps_data->albumart->state;
1702 old_aa.height = wps_data->albumart->height;
1703 old_aa.width = wps_data->albumart->width;
1705 #endif
1707 wps_reset(wps_data);
1709 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1710 if (!curr_vp)
1711 return false;
1712 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1713 if (!list)
1714 return false;
1715 add_to_ll_chain(&wps_data->viewports, list);
1718 /* Initialise the first (default) viewport */
1719 curr_vp->label = VP_DEFAULT_LABEL;
1720 curr_vp->vp.x = 0;
1721 curr_vp->vp.width = display->getwidth();
1722 curr_vp->vp.height = display->getheight();
1723 curr_vp->pb = NULL;
1724 curr_vp->hidden_flags = 0;
1725 curr_vp->lines = NULL;
1727 curr_line = NULL;
1728 if (!skin_start_new_line(curr_vp, 0))
1729 return false;
1731 switch (statusbar_position(display->screen_type))
1733 case STATUSBAR_OFF:
1734 curr_vp->vp.y = 0;
1735 break;
1736 case STATUSBAR_TOP:
1737 curr_vp->vp.y = STATUSBAR_HEIGHT;
1738 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1739 break;
1740 case STATUSBAR_BOTTOM:
1741 curr_vp->vp.y = 0;
1742 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1743 break;
1745 #ifdef HAVE_LCD_BITMAP
1746 curr_vp->vp.font = FONT_UI;
1747 curr_vp->vp.drawmode = DRMODE_SOLID;
1748 #endif
1749 #if LCD_DEPTH > 1
1750 if (display->depth > 1)
1752 curr_vp->vp.fg_pattern = display->get_foreground();
1753 curr_vp->vp.bg_pattern = display->get_background();
1755 #endif
1756 if (!isfile)
1758 return wps_parse(wps_data, buf, false);
1760 else
1762 int fd = open_utf8(buf, O_RDONLY);
1764 if (fd < 0)
1765 return false;
1767 /* get buffer space from the plugin buffer */
1768 size_t buffersize = 0;
1769 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1771 if (!wps_buffer)
1772 return false;
1774 /* copy the file's content to the buffer for parsing,
1775 ensuring that every line ends with a newline char. */
1776 unsigned int start = 0;
1777 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1779 start += strlen(wps_buffer + start);
1780 if (start < buffersize - 1)
1782 wps_buffer[start++] = '\n';
1783 wps_buffer[start] = 0;
1787 close(fd);
1789 if (start <= 0)
1790 return false;
1792 #ifdef HAVE_LCD_BITMAP
1793 /* Set all filename pointers to NULL */
1794 memset(bmp_names, 0, sizeof(bmp_names));
1795 #endif
1797 /* parse the WPS source */
1798 if (!wps_parse(wps_data, wps_buffer, true)) {
1799 wps_reset(wps_data);
1800 return false;
1803 wps_data->wps_loaded = true;
1805 #ifdef HAVE_LCD_BITMAP
1806 /* get the bitmap dir */
1807 char bmpdir[MAX_PATH];
1808 char *dot = strrchr(buf, '.');
1810 strlcpy(bmpdir, buf, dot - buf + 1);
1812 /* load the bitmaps that were found by the parsing */
1813 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1814 wps_reset(wps_data);
1815 return false;
1817 #endif
1818 #ifdef HAVE_ALBUMART
1819 status = audio_status();
1820 if (status & AUDIO_STATUS_PLAY)
1822 struct skin_albumart *aa = wps_data->albumart;
1823 if (aa && ((aa->state && !old_aa.state) ||
1824 (aa->state &&
1825 (((old_aa.height != aa->height) ||
1826 (old_aa.width != aa->width))))))
1828 curtrack = audio_current_track();
1829 offset = curtrack->offset;
1830 audio_stop();
1831 if (!(status & AUDIO_STATUS_PAUSE))
1832 audio_play(offset);
1835 #endif
1836 #if defined(DEBUG) || defined(SIMULATOR)
1837 debug_skin_usage();
1838 #endif
1839 return true;