Remove wps_reset(), and rename skin_data_init to skin_data_reset() and extend it...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobdbbdc32730a7df05e0a6cbbae0d3e568ff3afaa4
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 /* Function for parsing of details for a token. At the moment the
108 function is called, the token type has already been set. The
109 function must fill in the details and possibly add more tokens
110 to the token array. It should return the number of chars that
111 has been consumed.
113 wps_bufptr points to the char following the tag (i.e. where
114 details begin).
115 token is the pointer to the 'main' token being parsed
117 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
118 struct wps_token *token, struct wps_data *wps_data);
120 struct wps_tag {
121 enum wps_token_type type;
122 const char name[3];
123 unsigned char refresh_type;
124 const wps_tag_parse_func parse_func;
126 static int skip_end_of_line(const char *wps_bufptr);
127 /* prototypes of all special parse functions : */
128 static int parse_timeout(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_progressbar(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
132 static int parse_dir_level(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_setting_and_lang(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
137 #ifdef HAVE_LCD_BITMAP
138 static int parse_viewport_display(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_viewport(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_statusbar_enable(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_statusbar_disable(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 static int parse_image_display(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 static int parse_image_load(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 #endif /*HAVE_LCD_BITMAP */
151 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
152 static int parse_image_special(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 #endif
155 #ifdef HAVE_ALBUMART
156 static int parse_albumart_load(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 static int parse_albumart_display(const char *wps_bufptr,
159 struct wps_token *token, struct wps_data *wps_data);
160 #endif /* HAVE_ALBUMART */
161 #ifdef HAVE_TOUCHSCREEN
162 static int parse_touchregion(const char *wps_bufptr,
163 struct wps_token *token, struct wps_data *wps_data);
164 #else
165 static int fulline_tag_not_supported(const char *wps_bufptr,
166 struct wps_token *token, struct wps_data *wps_data)
168 (void)token; (void)wps_data;
169 return skip_end_of_line(wps_bufptr);
171 #define parse_touchregion fulline_tag_not_supported
172 #endif
173 #ifdef CONFIG_RTC
174 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
175 #else
176 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
177 #endif
179 /* array of available tags - those with more characters have to go first
180 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
181 static const struct wps_tag all_tags[] = {
183 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
184 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
185 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
187 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
188 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
189 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
190 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
191 #if CONFIG_CHARGING >= CHARGING_MONITOR
192 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
193 #endif
194 #if CONFIG_CHARGING
195 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
196 #endif
198 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
199 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
216 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
218 /* current file */
219 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
229 parse_dir_level },
231 /* next file */
232 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
242 parse_dir_level },
244 /* current metadata */
245 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
258 /* next metadata */
259 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
272 #if (CONFIG_CODEC != MAS3507D)
273 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
274 #endif
275 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
276 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
277 #endif
279 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
281 #ifdef HAS_REMOTE_BUTTON_HOLD
282 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
283 #else
284 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
285 #endif
287 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
288 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
289 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
290 parse_timeout },
292 #ifdef HAVE_LCD_BITMAP
293 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
294 #else
295 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
296 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
297 #endif
298 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
299 parse_progressbar },
301 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
303 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
304 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
305 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
306 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
308 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
309 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
310 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
311 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
313 #ifdef HAVE_TAGCACHE
314 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
315 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
316 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
317 #endif
319 #if CONFIG_CODEC == SWCODEC
320 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
321 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
322 #endif
324 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
325 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
327 #ifdef HAVE_LCD_BITMAP
328 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
329 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
331 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
333 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
334 parse_image_display },
336 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
337 #ifdef HAVE_ALBUMART
338 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
339 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
340 #endif
342 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
343 parse_viewport_display },
344 { WPS_NO_TOKEN, "V", 0, parse_viewport },
346 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
347 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
348 #endif
349 #endif
351 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
352 parse_setting_and_lang },
353 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
354 parse_setting_and_lang },
356 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
357 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
359 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
360 /* the array MUST end with an empty string (first char is \0) */
364 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
365 * chains require the order to be kept.
367 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
369 if (*list == NULL)
370 *list = item;
371 else
373 struct skin_token_list *t = *list;
374 while (t->next)
375 t = t->next;
376 t->next = item;
379 /* create and init a new wpsll item.
380 * passing NULL to token will alloc a new one.
381 * You should only pass NULL for the token when the token type (table above)
382 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
384 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
385 void* token_data)
387 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
388 if (!token)
389 token = skin_buffer_alloc(sizeof(struct wps_token));
390 if (!llitem || !token)
391 return NULL;
392 llitem->next = NULL;
393 llitem->token = token;
394 if (token_data)
395 llitem->token->value.data = token_data;
396 return llitem;
399 /* Returns the number of chars that should be skipped to jump
400 immediately after the first eol, i.e. to the start of the next line */
401 static int skip_end_of_line(const char *wps_bufptr)
403 line_number++;
404 int skip = 0;
405 while(*(wps_bufptr + skip) != '\n')
406 skip++;
407 return ++skip;
410 /* Starts a new subline in the current line during parsing */
411 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
413 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
414 if (!subline)
415 return false;
417 subline->first_token_idx = curr_token;
418 subline->next = NULL;
420 subline->line_type = 0;
421 subline->time_mult = 0;
423 line->curr_subline->last_token_idx = curr_token-1;
424 line->curr_subline->next = subline;
425 line->curr_subline = subline;
426 return true;
429 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
431 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
432 struct skin_subline *subline = NULL;
433 if (!line)
434 return false;
436 /* init the subline */
437 subline = &line->sublines;
438 subline->first_token_idx = curr_token;
439 subline->next = NULL;
440 subline->line_type = 0;
441 subline->time_mult = 0;
443 /* init the new line */
444 line->curr_subline = &line->sublines;
445 line->next = NULL;
446 line->subline_expire_time = 0;
448 /* connect to curr_line and vp pointers.
449 * 1) close the previous lines subline
450 * 2) connect to vp pointer
451 * 3) connect to curr_line global pointer
453 if (curr_line)
455 curr_line->curr_subline->last_token_idx = curr_token - 1;
456 curr_line->next = line;
457 curr_line->curr_subline = NULL;
459 curr_line = line;
460 if (!vp->lines)
461 vp->lines = line;
462 line_number++;
463 return true;
466 #ifdef HAVE_LCD_BITMAP
468 static int parse_statusbar_enable(const char *wps_bufptr,
469 struct wps_token *token,
470 struct wps_data *wps_data)
472 (void)token; /* Kill warnings */
473 wps_data->wps_sb_tag = true;
474 wps_data->show_sb_on_wps = true;
475 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
476 if (default_vp->vp.y == 0)
478 default_vp->vp.y = STATUSBAR_HEIGHT;
479 default_vp->vp.height -= STATUSBAR_HEIGHT;
481 return skip_end_of_line(wps_bufptr);
484 static int parse_statusbar_disable(const char *wps_bufptr,
485 struct wps_token *token,
486 struct wps_data *wps_data)
488 (void)token; /* Kill warnings */
489 wps_data->wps_sb_tag = true;
490 wps_data->show_sb_on_wps = false;
491 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
492 if (default_vp->vp.y == STATUSBAR_HEIGHT)
494 default_vp->vp.y = 0;
495 default_vp->vp.height += STATUSBAR_HEIGHT;
497 return skip_end_of_line(wps_bufptr);
500 static int get_image_id(int c)
502 if(c >= 'a' && c <= 'z')
503 return c - 'a';
504 else if(c >= 'A' && c <= 'Z')
505 return c - 'A' + 26;
506 else
507 return -1;
510 static char *get_image_filename(const char *start, const char* bmpdir,
511 char *buf, int buf_size)
513 const char *end = strchr(start, '|');
515 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
517 buf = "\0";
518 return NULL;
521 int bmpdirlen = strlen(bmpdir);
523 strcpy(buf, bmpdir);
524 buf[bmpdirlen] = '/';
525 memcpy( &buf[bmpdirlen + 1], start, end - start);
526 buf[bmpdirlen + 1 + end - start] = 0;
528 return buf;
531 static int parse_image_display(const char *wps_bufptr,
532 struct wps_token *token,
533 struct wps_data *wps_data)
535 char label = wps_bufptr[0];
536 int subimage;
537 struct gui_img *img;;
539 /* sanity check */
540 img = find_image(label, wps_data);
541 if (!img)
543 token->value.i = label; /* so debug works */
544 return WPS_ERROR_INVALID_PARAM;
547 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
549 if (subimage >= img->num_subimages)
550 return WPS_ERROR_INVALID_PARAM;
552 /* Store sub-image number to display in high bits */
553 token->value.i = label | (subimage << 8);
554 return 2; /* We have consumed 2 bytes */
555 } else {
556 token->value.i = label;
557 return 1; /* We have consumed 1 byte */
561 static int parse_image_load(const char *wps_bufptr,
562 struct wps_token *token,
563 struct wps_data *wps_data)
565 const char *ptr = wps_bufptr;
566 const char *pos;
567 const char* filename;
568 const char* id;
569 const char *newline;
570 int x,y;
571 struct gui_img *img;
573 /* format: %x|n|filename.bmp|x|y|
574 or %xl|n|filename.bmp|x|y|
575 or %xl|n|filename.bmp|x|y|num_subimages|
578 if (*ptr != '|')
579 return WPS_ERROR_INVALID_PARAM;
581 ptr++;
583 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
584 return WPS_ERROR_INVALID_PARAM;
586 /* Check there is a terminating | */
587 if (*ptr != '|')
588 return WPS_ERROR_INVALID_PARAM;
590 /* check the image number and load state */
591 if(find_image(*id, wps_data))
593 /* Invalid image ID */
594 return WPS_ERROR_INVALID_PARAM;
596 img = skin_buffer_alloc(sizeof(struct gui_img));
597 if (!img)
598 return WPS_ERROR_INVALID_PARAM;
599 /* save a pointer to the filename */
600 img->bm.data = (char*)filename;
601 img->label = *id;
602 img->x = x;
603 img->y = y;
604 img->num_subimages = 1;
605 img->always_display = false;
607 /* save current viewport */
608 img->vp = &curr_vp->vp;
610 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
612 img->always_display = true;
614 else
616 /* Parse the (optional) number of sub-images */
617 ptr++;
618 newline = strchr(ptr, '\n');
619 pos = strchr(ptr, '|');
620 if (pos && pos < newline)
621 img->num_subimages = atoi(ptr);
623 if (img->num_subimages <= 0)
624 return WPS_ERROR_INVALID_PARAM;
626 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
627 if (!item)
628 return WPS_ERROR_INVALID_PARAM;
629 add_to_ll_chain(&wps_data->images, item);
631 /* Skip the rest of the line */
632 return skip_end_of_line(wps_bufptr);
635 static int parse_viewport_display(const char *wps_bufptr,
636 struct wps_token *token,
637 struct wps_data *wps_data)
639 (void)wps_data;
640 char letter = wps_bufptr[0];
642 if (letter < 'a' || letter > 'z')
644 /* invalid viewport tag */
645 return WPS_ERROR_INVALID_PARAM;
647 token->value.i = letter;
648 return 1;
651 static int parse_viewport(const char *wps_bufptr,
652 struct wps_token *token,
653 struct wps_data *wps_data)
655 (void)token; /* Kill warnings */
656 const char *ptr = wps_bufptr;
658 const int screen =
659 #ifdef HAVE_REMOTE_LCD
660 wps_data->remote_wps ? SCREEN_REMOTE :
661 #endif
662 SCREEN_MAIN;
664 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
666 /* check for the optional letter to signify its a hideable viewport */
667 /* %Vl|<label>|<rest of tags>| */
668 skin_vp->hidden_flags = 0;
669 skin_vp->label = VP_NO_LABEL;
670 skin_vp->pb = NULL;
671 skin_vp->lines = NULL;
672 if (curr_line)
674 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
675 - (wps_data->num_tokens > 0 ? 1 : 0);
678 curr_line = NULL;
679 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
680 return WPS_ERROR_INVALID_PARAM;
682 if (*ptr == 'l')
684 if (*(ptr+1) == '|')
686 char label = *(ptr+2);
687 if (label >= 'a' && label <= 'z')
689 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
690 skin_vp->label = label;
692 else
693 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
694 ptr += 3;
697 if (*ptr != '|')
698 return WPS_ERROR_INVALID_PARAM;
700 ptr++;
701 struct viewport *vp = &skin_vp->vp;
702 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
704 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
705 return WPS_ERROR_INVALID_PARAM;
707 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
708 /* Check for trailing | */
709 if (*ptr != '|')
710 return WPS_ERROR_INVALID_PARAM;
713 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
714 if (!list)
715 return WPS_ERROR_INVALID_PARAM;
716 add_to_ll_chain(&wps_data->viewports, list);
717 curr_vp = skin_vp;
718 /* Skip the rest of the line */
719 return skip_end_of_line(wps_bufptr);
722 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
723 static int parse_image_special(const char *wps_bufptr,
724 struct wps_token *token,
725 struct wps_data *wps_data)
727 (void)wps_data; /* kill warning */
728 (void)token;
729 const char *pos = NULL;
730 const char *newline;
732 pos = strchr(wps_bufptr + 1, '|');
733 newline = strchr(wps_bufptr, '\n');
735 if (pos > newline)
736 return WPS_ERROR_INVALID_PARAM;
737 #if LCD_DEPTH > 1
738 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
740 /* format: %X|filename.bmp| */
741 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
743 #endif
745 /* Skip the rest of the line */
746 return skip_end_of_line(wps_bufptr);
748 #endif
750 #endif /* HAVE_LCD_BITMAP */
752 static int parse_setting_and_lang(const char *wps_bufptr,
753 struct wps_token *token,
754 struct wps_data *wps_data)
756 /* NOTE: both the string validations that happen in here will
757 * automatically PASS on checkwps because its too hard to get
758 * settings_list.c and englinsh.lang built for it.
759 * If that ever changes remove the #ifndef __PCTOOL__'s here
761 (void)wps_data;
762 const char *ptr = wps_bufptr;
763 const char *end;
764 int i = 0;
765 char temp[64];
767 /* Find the setting's cfg_name */
768 if (*ptr != '|')
769 return WPS_ERROR_INVALID_PARAM;
770 ptr++;
771 end = strchr(ptr,'|');
772 if (!end)
773 return WPS_ERROR_INVALID_PARAM;
774 strlcpy(temp, ptr,end-ptr+1);
776 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
778 #ifndef __PCTOOL__
779 i = lang_english_to_id(temp);
780 if (i < 0)
781 return WPS_ERROR_INVALID_PARAM;
782 #endif
784 else
786 /* Find the setting */
787 for (i=0; i<nb_settings; i++)
788 if (settings[i].cfg_name &&
789 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
790 /* prevent matches on cfg_name prefixes */
791 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
792 break;
793 #ifndef __PCTOOL__
794 if (i == nb_settings)
795 return WPS_ERROR_INVALID_PARAM;
796 #endif
798 /* Store the setting number */
799 token->value.i = i;
801 /* Skip the rest of the line */
802 return end-ptr+2;
806 static int parse_dir_level(const char *wps_bufptr,
807 struct wps_token *token,
808 struct wps_data *wps_data)
810 char val[] = { *wps_bufptr, '\0' };
811 token->value.i = atoi(val);
812 (void)wps_data; /* Kill warnings */
813 return 1;
816 static int parse_timeout(const char *wps_bufptr,
817 struct wps_token *token,
818 struct wps_data *wps_data)
820 int skip = 0;
821 int val = 0;
822 bool have_point = false;
823 bool have_tenth = false;
825 (void)wps_data; /* Kill the warning */
827 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
829 if (*wps_bufptr != '.')
831 val *= 10;
832 val += *wps_bufptr - '0';
833 if (have_point)
835 have_tenth = true;
836 wps_bufptr++;
837 skip++;
838 break;
841 else
842 have_point = true;
844 wps_bufptr++;
845 skip++;
848 if (have_tenth == false)
849 val *= 10;
851 if (val == 0 && skip == 0)
853 /* decide what to do if no value was specified */
854 switch (token->type)
856 case WPS_TOKEN_SUBLINE_TIMEOUT:
857 return -1;
858 case WPS_TOKEN_BUTTON_VOLUME:
859 val = 10;
860 break;
863 token->value.i = val;
865 return skip;
868 static int parse_progressbar(const char *wps_bufptr,
869 struct wps_token *token,
870 struct wps_data *wps_data)
872 /* %pb or %pb|filename|x|y|width|height|
873 using - for any of the params uses "sane" values */
874 #ifdef HAVE_LCD_BITMAP
875 enum {
876 PB_FILENAME = 0,
877 PB_X,
878 PB_Y,
879 PB_WIDTH,
880 PB_HEIGHT
882 const char *filename;
883 int x, y, height, width;
884 uint32_t set = 0;
885 const char *ptr = wps_bufptr;
886 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
887 struct skin_token_list *item = new_skin_token_list_item(token, pb);
889 if (!pb || !item)
890 return WPS_ERROR_INVALID_PARAM;
892 struct viewport *vp = &curr_vp->vp;
893 #ifndef __PCTOOL__
894 int font_height = font_get(vp->font)->height;
895 #else
896 int font_height = 8;
897 #endif
898 /* we need to know what line number (viewport relative) this pb is,
899 * so count them... */
900 int line_num = -1;
901 struct skin_line *line = curr_vp->lines;
902 while (line)
904 line_num++;
905 line = line->next;
907 pb->have_bitmap_pb = false;
908 pb->bm.data = NULL; /* no bitmap specified */
910 if (*wps_bufptr != '|') /* regular old style */
912 pb->x = 0;
913 pb->width = vp->width;
914 pb->height = SYSFONT_HEIGHT-2;
915 pb->y = -line_num - 1; /* Will be computed during the rendering */
917 curr_vp->pb = pb;
918 add_to_ll_chain(&wps_data->progressbars, item);
919 return 0;
921 ptr = wps_bufptr + 1;
923 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
924 &x, &y, &width, &height)))
925 return WPS_ERROR_INVALID_PARAM;
927 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
928 pb->bm.data = (char*)filename;
930 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
931 pb->x = x;
932 else
933 pb->x = vp->x;
935 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
937 /* A zero width causes a divide-by-zero error later, so reject it */
938 if (width == 0)
939 return WPS_ERROR_INVALID_PARAM;
941 pb->width = width;
943 else
944 pb->width = vp->width - pb->x;
946 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
948 /* A zero height makes no sense - reject it */
949 if (height == 0)
950 return WPS_ERROR_INVALID_PARAM;
952 pb->height = height;
954 else
955 pb->height = font_height;
957 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
958 pb->y = y;
959 else
960 pb->y = -line_num - 1; /* Will be computed during the rendering */
962 curr_vp->pb = pb;
963 add_to_ll_chain(&wps_data->progressbars, item);
965 /* Skip the rest of the line */
966 return skip_end_of_line(wps_bufptr)-1;
967 #else
968 (void)token;
970 if (*(wps_bufptr-1) == 'f')
971 wps_data->full_line_progressbar = true;
972 else
973 wps_data->full_line_progressbar = false;
975 return 0;
977 #endif
980 #ifdef HAVE_ALBUMART
981 static int parse_albumart_load(const char *wps_bufptr,
982 struct wps_token *token,
983 struct wps_data *wps_data)
985 const char *_pos, *newline;
986 bool parsing;
987 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
988 (void)token; /* silence warning */
989 if (!aa)
990 return skip_end_of_line(wps_bufptr);
992 /* reset albumart info in wps */
993 aa->width = -1;
994 aa->height = -1;
995 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
996 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
997 aa->vp = &curr_vp->vp;
999 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1001 newline = strchr(wps_bufptr, '\n');
1003 /* initial validation and parsing of x and y components */
1004 if (*wps_bufptr != '|')
1005 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1007 _pos = wps_bufptr + 1;
1008 _pos = parse_list("dd", NULL, '|', _pos, &aa->x, &aa->y);
1010 if (!_pos || _pos > newline || *_pos != '|')
1011 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1012 e.g. %Cl|7|59\n */
1014 /* parsing width field */
1015 parsing = true;
1016 while (parsing)
1018 /* apply each modifier in turn */
1019 ++_pos;
1020 switch (*_pos)
1022 case 'l':
1023 case 'L':
1024 case '+':
1025 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1026 break;
1027 case 'c':
1028 case 'C':
1029 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1030 break;
1031 case 'r':
1032 case 'R':
1033 case '-':
1034 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1035 break;
1036 case 'd':
1037 case 'D':
1038 case 'i':
1039 case 'I':
1040 case 's':
1041 case 'S':
1042 /* simply ignored */
1043 break;
1044 default:
1045 parsing = false;
1046 break;
1049 /* extract max width data */
1050 if (*_pos != '|')
1052 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1053 return WPS_ERROR_INVALID_PARAM;
1055 aa->width = atoi(_pos);
1057 _pos = strchr(_pos, '|');
1058 if (!_pos || _pos > newline)
1059 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1060 e.g. %Cl|7|59|200\n */
1063 /* parsing height field */
1064 parsing = true;
1065 while (parsing)
1067 /* apply each modifier in turn */
1068 ++_pos;
1069 switch (*_pos)
1071 case 't':
1072 case 'T':
1073 case '-':
1074 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1075 break;
1076 case 'c':
1077 case 'C':
1078 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1079 break;
1080 case 'b':
1081 case 'B':
1082 case '+':
1083 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1084 break;
1085 case 'd':
1086 case 'D':
1087 case 'i':
1088 case 'I':
1089 case 's':
1090 case 'S':
1091 /* simply ignored */
1092 break;
1093 default:
1094 parsing = false;
1095 break;
1098 /* extract max height data */
1099 if (*_pos != '|')
1101 if (!isdigit(*_pos))
1102 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1104 aa->height = atoi(_pos);
1106 _pos = strchr(_pos, '|');
1107 if (!_pos || _pos > newline)
1108 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1109 e.g. %Cl|7|59|200|200\n */
1112 /* if we got here, we parsed everything ok .. ! */
1113 if (aa->width < 0)
1114 aa->width = 0;
1115 else if (aa->width > LCD_WIDTH)
1116 aa->width = LCD_WIDTH;
1118 if (aa->height < 0)
1119 aa->height = 0;
1120 else if (aa->height > LCD_HEIGHT)
1121 aa->height = LCD_HEIGHT;
1123 aa->state = WPS_ALBUMART_LOAD;
1124 aa->draw = false;
1125 wps_data->albumart = aa;
1127 /* Skip the rest of the line */
1128 return skip_end_of_line(wps_bufptr);
1131 static int parse_albumart_display(const char *wps_bufptr,
1132 struct wps_token *token,
1133 struct wps_data *wps_data)
1135 (void)wps_bufptr;
1136 struct wps_token *prev = token-1;
1137 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1139 token->type = WPS_TOKEN_ALBUMART_FOUND;
1141 else if (wps_data->albumart)
1143 wps_data->albumart->vp = &curr_vp->vp;
1145 #if 0
1146 /* the old code did this so keep it here for now...
1147 * this is to allow the posibility to showing the next tracks AA! */
1148 if (wps_bufptr+1 == 'n')
1149 return 1;
1150 #endif
1151 return 0;
1153 #endif /* HAVE_ALBUMART */
1155 #ifdef HAVE_TOUCHSCREEN
1157 struct touchaction {char* s; int action;};
1158 static struct touchaction touchactions[] = {
1159 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1160 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1161 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1162 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1163 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1164 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1165 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1167 static int parse_touchregion(const char *wps_bufptr,
1168 struct wps_token *token, struct wps_data *wps_data)
1170 (void)token;
1171 unsigned i, imax;
1172 struct touchregion *region = NULL;
1173 const char *ptr = wps_bufptr;
1174 const char *action;
1175 const char pb_string[] = "progressbar";
1176 const char vol_string[] = "volume";
1177 int x,y,w,h;
1179 /* format: %T|x|y|width|height|action|
1180 * if action starts with & the area must be held to happen
1181 * action is one of:
1182 * play - play/pause playback
1183 * stop - stop playback, exit the wps
1184 * prev - prev track
1185 * next - next track
1186 * ffwd - seek forward
1187 * rwd - seek backwards
1188 * menu - go back to the main menu
1189 * browse - go back to the file/db browser
1190 * shuffle - toggle shuffle mode
1191 * repmode - cycle the repeat mode
1192 * quickscreen - go into the quickscreen
1193 * contextmenu - open the context menu
1194 * playlist - go into the playlist
1195 * pitch - go into the pitchscreen
1199 if (*ptr != '|')
1200 return WPS_ERROR_INVALID_PARAM;
1201 ptr++;
1203 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1204 return WPS_ERROR_INVALID_PARAM;
1206 /* Check there is a terminating | */
1207 if (*ptr != '|')
1208 return WPS_ERROR_INVALID_PARAM;
1210 region = skin_buffer_alloc(sizeof(struct touchregion));
1211 if (!region)
1212 return WPS_ERROR_INVALID_PARAM;
1214 /* should probably do some bounds checking here with the viewport... but later */
1215 region->action = ACTION_NONE;
1216 region->x = x;
1217 region->y = y;
1218 region->width = w;
1219 region->height = h;
1220 region->wvp = curr_vp;
1222 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1223 && *(action + sizeof(pb_string)-1) == '|')
1224 region->type = WPS_TOUCHREGION_SCROLLBAR;
1225 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1226 && *(action + sizeof(vol_string)-1) == '|')
1227 region->type = WPS_TOUCHREGION_VOLUME;
1228 else
1230 region->type = WPS_TOUCHREGION_ACTION;
1232 if (*action == '&')
1234 action++;
1235 region->repeat = true;
1237 else
1238 region->repeat = false;
1240 i = 0;
1241 imax = ARRAYLEN(touchactions);
1242 while ((region->action == ACTION_NONE) &&
1243 (i < imax))
1245 /* try to match with one of our touchregion screens */
1246 int len = strlen(touchactions[i].s);
1247 if (!strncmp(touchactions[i].s, action, len)
1248 && *(action+len) == '|')
1249 region->action = touchactions[i].action;
1250 i++;
1252 if (region->action == ACTION_NONE)
1253 return WPS_ERROR_INVALID_PARAM;
1255 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1256 if (!item)
1257 return WPS_ERROR_INVALID_PARAM;
1258 add_to_ll_chain(&wps_data->touchregions, item);
1259 return skip_end_of_line(wps_bufptr);
1261 #endif
1263 /* Parse a generic token from the given string. Return the length read */
1264 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1266 int skip = 0, taglen = 0, ret;
1267 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1268 const struct wps_tag *tag;
1269 memset(token, 0, sizeof(*token));
1271 switch(*wps_bufptr)
1274 case '%':
1275 case '<':
1276 case '|':
1277 case '>':
1278 case ';':
1279 case '#':
1280 /* escaped characters */
1281 token->type = WPS_TOKEN_CHARACTER;
1282 token->value.c = *wps_bufptr;
1283 taglen = 1;
1284 wps_data->num_tokens++;
1285 break;
1287 case '?':
1288 /* conditional tag */
1289 token->type = WPS_TOKEN_CONDITIONAL;
1290 level++;
1291 condindex[level] = wps_data->num_tokens;
1292 numoptions[level] = 1;
1293 wps_data->num_tokens++;
1294 ret = parse_token(wps_bufptr + 1, wps_data);
1295 if (ret < 0) return ret;
1296 taglen = 1 + ret;
1297 break;
1299 default:
1300 /* find what tag we have */
1301 for (tag = all_tags;
1302 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1303 tag++) ;
1305 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1306 token->type = tag->type;
1307 curr_line->curr_subline->line_type |= tag->refresh_type;
1309 /* if the tag has a special parsing function, we call it */
1310 if (tag->parse_func)
1312 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1313 if (ret < 0) return ret;
1314 skip += ret;
1317 /* Some tags we don't want to save as tokens */
1318 if (tag->type == WPS_NO_TOKEN)
1319 break;
1321 /* tags that start with 'F', 'I' or 'D' are for the next file */
1322 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1323 *(tag->name) == 'D')
1324 token->next = true;
1326 wps_data->num_tokens++;
1327 break;
1330 skip += taglen;
1331 return skip;
1334 /* Parses the WPS.
1335 data is the pointer to the structure where the parsed WPS should be stored.
1336 It is initialised.
1337 wps_bufptr points to the string containing the WPS tags */
1338 #define TOKEN_BLOCK_SIZE 128
1339 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1341 if (!data || !wps_bufptr || !*wps_bufptr)
1342 return false;
1343 enum wps_parse_error fail = PARSE_OK;
1344 int ret;
1345 int max_tokens = TOKEN_BLOCK_SIZE;
1346 size_t buf_free = 0;
1347 line_number = 1;
1348 level = -1;
1350 /* allocate enough RAM for a reasonable skin, grow as needed.
1351 * Free any used RAM before loading the images to be 100% RAM efficient */
1352 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1353 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1354 return false;
1355 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1356 data->num_tokens = 0;
1358 while (*wps_bufptr && !fail)
1360 /* first make sure there is enough room for tokens */
1361 if (max_tokens <= data->num_tokens + 5)
1363 int extra_tokens = TOKEN_BLOCK_SIZE;
1364 size_t needed = extra_tokens * sizeof(struct wps_token);
1365 /* do some smarts here to grow the array a bit */
1366 if (skin_buffer_freespace() < needed)
1368 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1369 break;
1371 skin_buffer_increment(needed, false);
1372 max_tokens += extra_tokens;
1375 switch(*wps_bufptr++)
1378 /* Regular tag */
1379 case '%':
1380 if ((ret = parse_token(wps_bufptr, data)) < 0)
1382 fail = PARSE_FAIL_COND_INVALID_PARAM;
1383 break;
1385 else if (level >= WPS_MAX_COND_LEVEL - 1)
1387 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1388 break;
1390 wps_bufptr += ret;
1391 break;
1393 /* Alternating sublines separator */
1394 case ';':
1395 if (level >= 0) /* there are unclosed conditionals */
1397 fail = PARSE_FAIL_UNCLOSED_COND;
1398 break;
1401 if (!skin_start_new_subline(curr_line, data->num_tokens))
1402 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1404 break;
1406 /* Conditional list start */
1407 case '<':
1408 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1410 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1411 break;
1414 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1415 lastcond[level] = data->num_tokens++;
1416 break;
1418 /* Conditional list end */
1419 case '>':
1420 if (level < 0) /* not in a conditional, invalid char */
1422 fail = PARSE_FAIL_INVALID_CHAR;
1423 break;
1426 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1427 if (lastcond[level])
1428 data->tokens[lastcond[level]].value.i = data->num_tokens;
1429 else
1431 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1432 break;
1435 lastcond[level] = 0;
1436 data->num_tokens++;
1437 data->tokens[condindex[level]].value.i = numoptions[level];
1438 level--;
1439 break;
1441 /* Conditional list option */
1442 case '|':
1443 if (level < 0) /* not in a conditional, invalid char */
1445 fail = PARSE_FAIL_INVALID_CHAR;
1446 break;
1449 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1450 if (lastcond[level])
1451 data->tokens[lastcond[level]].value.i = data->num_tokens;
1452 else
1454 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1455 break;
1458 lastcond[level] = data->num_tokens;
1459 numoptions[level]++;
1460 data->num_tokens++;
1461 break;
1463 /* Comment */
1464 case '#':
1465 if (level >= 0) /* there are unclosed conditionals */
1467 fail = PARSE_FAIL_UNCLOSED_COND;
1468 break;
1471 wps_bufptr += skip_end_of_line(wps_bufptr);
1472 break;
1474 /* End of this line */
1475 case '\n':
1476 if (level >= 0) /* there are unclosed conditionals */
1478 fail = PARSE_FAIL_UNCLOSED_COND;
1479 break;
1481 /* add a new token for the \n so empty lines are correct */
1482 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1483 data->tokens[data->num_tokens].value.c = '\n';
1484 data->tokens[data->num_tokens].next = false;
1485 data->num_tokens++;
1487 if (!skin_start_new_line(curr_vp, data->num_tokens))
1489 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1490 break;
1493 break;
1495 /* String */
1496 default:
1498 unsigned int len = 1;
1499 const char *string_start = wps_bufptr - 1;
1501 /* find the length of the string */
1502 while (*wps_bufptr && *wps_bufptr != '#' &&
1503 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1504 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1505 *wps_bufptr != '|' && *wps_bufptr != '\n')
1507 wps_bufptr++;
1508 len++;
1511 /* look if we already have that string */
1512 char *str;
1513 bool found = false;
1514 struct skin_token_list *list = data->strings;
1515 while (list)
1517 str = (char*)list->token->value.data;
1518 found = (strlen(str) == len &&
1519 strncmp(string_start, str, len) == 0);
1520 if (found)
1521 break; /* break here because the list item is
1522 used if its found */
1523 list = list->next;
1525 /* If a matching string is found, found is true and i is
1526 the index of the string. If not, found is false */
1528 if (!found)
1530 /* new string */
1531 str = (char*)skin_buffer_alloc(len+1);
1532 if (!str)
1534 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1535 break;
1537 strlcpy(str, string_start, len+1);
1538 struct skin_token_list *item =
1539 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1540 if(!item)
1542 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1543 break;
1545 add_to_ll_chain(&data->strings, item);
1547 else
1549 /* another occurrence of an existing string */
1550 data->tokens[data->num_tokens].value.data = list->token->value.data;
1552 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1553 data->num_tokens++;
1555 break;
1559 if (!fail && level >= 0) /* there are unclosed conditionals */
1560 fail = PARSE_FAIL_UNCLOSED_COND;
1562 if (*wps_bufptr && !fail)
1563 /* one of the limits of the while loop was exceeded */
1564 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1566 /* Success! */
1567 curr_line->curr_subline->last_token_idx = data->num_tokens;
1568 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1569 /* freeup unused tokens */
1570 skin_buffer_free_from_front(sizeof(struct wps_token)
1571 * (max_tokens - data->num_tokens));
1573 #if defined(DEBUG) || defined(SIMULATOR)
1574 if (debug)
1575 print_debug_info(data, fail, line_number);
1576 #else
1577 (void)debug;
1578 #endif
1580 return (fail == 0);
1585 * initial setup of wps_data; does reset everything
1586 * except fields which need to survive, i.e.
1588 * wps_data->remote_wps
1590 void skin_data_reset(struct wps_data *wps_data)
1592 #ifdef HAVE_LCD_BITMAP
1593 wps_data->images = NULL;
1594 wps_data->progressbars = NULL;
1595 #endif
1596 #ifdef HAVE_TOUCHSCREEN
1597 wps_data->touchregions = NULL;
1598 #endif
1599 wps_data->viewports = NULL;
1600 wps_data->strings = NULL;
1601 #ifdef HAVE_ALBUMART
1602 wps_data->albumart = NULL;
1603 #endif
1604 wps_data->tokens = NULL;
1605 wps_data->num_tokens = 0;
1606 wps_data->button_time_volume = 0;
1608 #ifdef HAVE_LCD_BITMAP
1609 wps_data->peak_meter_enabled = false;
1610 wps_data->wps_sb_tag = false;
1611 wps_data->show_sb_on_wps = false;
1612 #else /* HAVE_LCD_CHARCELLS */
1613 /* progress bars */
1614 int i;
1615 for (i = 0; i < 8; i++)
1617 wps_data->wps_progress_pat[i] = 0;
1619 wps_data->full_line_progressbar = false;
1620 #endif
1621 wps_data->wps_loaded = false;
1624 #ifdef HAVE_LCD_BITMAP
1625 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1627 (void)wps_data; /* only needed for remote targets */
1628 bool loaded = false;
1629 char img_path[MAX_PATH];
1630 get_image_filename(bitmap->data, bmpdir,
1631 img_path, sizeof(img_path));
1633 /* load the image */
1634 int format;
1635 #ifdef HAVE_REMOTE_LCD
1636 if (wps_data->remote_wps)
1637 format = FORMAT_ANY|FORMAT_REMOTE;
1638 else
1639 #endif
1640 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1642 size_t max_buf;
1643 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1644 bitmap->data = imgbuf;
1645 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1647 if (ret > 0)
1649 skin_buffer_increment(ret, true);
1650 loaded = true;
1652 else
1654 /* Abort if we can't load an image */
1655 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1656 loaded = false;
1658 return loaded;
1661 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1663 struct skin_token_list *list;
1664 /* do the progressbars */
1665 list = wps_data->progressbars;
1666 while (list)
1668 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1669 if (pb->bm.data)
1671 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1673 list = list->next;
1675 /* regular images */
1676 list = wps_data->images;
1677 while (list)
1679 struct gui_img *img = (struct gui_img*)list->token->value.data;
1680 if (img->bm.data)
1682 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1683 if (img->loaded)
1684 img->subimage_height = img->bm.height / img->num_subimages;
1686 list = list->next;
1689 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1690 if (bmp_names[BACKDROP_BMP])
1692 int screen = SCREEN_MAIN;
1693 char img_path[MAX_PATH];
1694 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1695 img_path, sizeof(img_path));
1696 #if defined(HAVE_REMOTE_LCD)
1697 /* We only need to check LCD type if there is a remote LCD */
1698 if (wps_data->remote_wps)
1699 screen = SCREEN_REMOTE;
1700 #endif
1701 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1703 #endif /* has backdrop support */
1705 /* If we got here, everything was OK */
1706 return true;
1709 #endif /* HAVE_LCD_BITMAP */
1711 /* to setup up the wps-data from a format-buffer (isfile = false)
1712 from a (wps-)file (isfile = true)*/
1713 bool skin_data_load(struct wps_data *wps_data,
1714 struct screen *display,
1715 const char *buf,
1716 bool isfile)
1719 if (!wps_data || !buf)
1720 return false;
1721 #ifdef HAVE_ALBUMART
1722 int status;
1723 struct mp3entry *curtrack;
1724 long offset;
1725 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1726 if (wps_data->albumart)
1728 old_aa.state = wps_data->albumart->state;
1729 old_aa.height = wps_data->albumart->height;
1730 old_aa.width = wps_data->albumart->width;
1732 #endif
1734 skin_data_reset(wps_data);
1736 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1737 if (!curr_vp)
1738 return false;
1739 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1740 if (!list)
1741 return false;
1742 add_to_ll_chain(&wps_data->viewports, list);
1745 /* Initialise the first (default) viewport */
1746 curr_vp->label = VP_DEFAULT_LABEL;
1747 curr_vp->vp.x = 0;
1748 curr_vp->vp.width = display->getwidth();
1749 curr_vp->vp.height = display->getheight();
1750 curr_vp->pb = NULL;
1751 curr_vp->hidden_flags = 0;
1752 curr_vp->lines = NULL;
1754 curr_line = NULL;
1755 if (!skin_start_new_line(curr_vp, 0))
1756 return false;
1758 switch (statusbar_position(display->screen_type))
1760 case STATUSBAR_OFF:
1761 curr_vp->vp.y = 0;
1762 break;
1763 case STATUSBAR_TOP:
1764 curr_vp->vp.y = STATUSBAR_HEIGHT;
1765 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1766 break;
1767 case STATUSBAR_BOTTOM:
1768 curr_vp->vp.y = 0;
1769 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1770 break;
1772 #ifdef HAVE_LCD_BITMAP
1773 curr_vp->vp.font = FONT_UI;
1774 curr_vp->vp.drawmode = DRMODE_SOLID;
1775 #endif
1776 #if LCD_DEPTH > 1
1777 if (display->depth > 1)
1779 curr_vp->vp.fg_pattern = display->get_foreground();
1780 curr_vp->vp.bg_pattern = display->get_background();
1782 #endif
1783 if (!isfile)
1785 return wps_parse(wps_data, buf, false);
1787 else
1789 int fd = open_utf8(buf, O_RDONLY);
1791 if (fd < 0)
1792 return false;
1794 /* get buffer space from the plugin buffer */
1795 size_t buffersize = 0;
1796 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1798 if (!wps_buffer)
1799 return false;
1801 /* copy the file's content to the buffer for parsing,
1802 ensuring that every line ends with a newline char. */
1803 unsigned int start = 0;
1804 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1806 start += strlen(wps_buffer + start);
1807 if (start < buffersize - 1)
1809 wps_buffer[start++] = '\n';
1810 wps_buffer[start] = 0;
1814 close(fd);
1816 if (start <= 0)
1817 return false;
1819 #ifdef HAVE_LCD_BITMAP
1820 /* Set all filename pointers to NULL */
1821 memset(bmp_names, 0, sizeof(bmp_names));
1822 #endif
1824 /* parse the WPS source */
1825 if (!wps_parse(wps_data, wps_buffer, true)) {
1826 skin_data_reset(wps_data);
1827 return false;
1830 wps_data->wps_loaded = true;
1832 #ifdef HAVE_LCD_BITMAP
1833 /* get the bitmap dir */
1834 char bmpdir[MAX_PATH];
1835 char *dot = strrchr(buf, '.');
1837 strlcpy(bmpdir, buf, dot - buf + 1);
1839 /* load the bitmaps that were found by the parsing */
1840 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1841 skin_data_reset(wps_data);
1842 return false;
1844 #endif
1845 #ifdef HAVE_ALBUMART
1846 status = audio_status();
1847 if (status & AUDIO_STATUS_PLAY)
1849 struct skin_albumart *aa = wps_data->albumart;
1850 if (aa && ((aa->state && !old_aa.state) ||
1851 (aa->state &&
1852 (((old_aa.height != aa->height) ||
1853 (old_aa.width != aa->width))))))
1855 curtrack = audio_current_track();
1856 offset = curtrack->offset;
1857 audio_stop();
1858 if (!(status & AUDIO_STATUS_PAUSE))
1859 audio_play(offset);
1862 #endif
1863 #if defined(DEBUG) || defined(SIMULATOR)
1864 debug_skin_usage();
1865 #endif
1866 return true;