Replace limiter with dynamic range compressor
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobede18714141a7c6976a3285a156643245b96b4fd
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "config.h"
26 #include "file.h"
27 #include "misc.h"
28 #include "plugin.h"
29 #include "viewport.h"
31 #ifdef __PCTOOL__
32 #ifdef WPSEDITOR
33 #include "proxy.h"
34 #include "sysfont.h"
35 #else
36 #include "action.h"
37 #include "checkwps.h"
38 #include "audio.h"
39 #define DEBUGF printf
40 #endif /*WPSEDITOR*/
41 #else
42 #include "debug.h"
43 #endif /*__PCTOOL__*/
45 #include <ctype.h>
46 #include <stdbool.h>
47 #include "font.h"
49 #include "wps_internals.h"
50 #include "skin_engine.h"
51 #include "settings.h"
52 #include "settings_list.h"
54 #ifdef HAVE_LCD_BITMAP
55 #include "bmp.h"
56 #endif
58 #include "backdrop.h"
60 #define WPS_ERROR_INVALID_PARAM -1
62 /* level of current conditional.
63 -1 means we're not in a conditional. */
64 static int level = -1;
66 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
67 or WPS_TOKEN_CONDITIONAL_START in current level */
68 static int lastcond[WPS_MAX_COND_LEVEL];
70 /* index of the WPS_TOKEN_CONDITIONAL in current level */
71 static int condindex[WPS_MAX_COND_LEVEL];
73 /* number of condtional options in current level */
74 static int numoptions[WPS_MAX_COND_LEVEL];
76 /* line number, debug only */
77 static int line_number;
79 /* the current viewport */
80 static struct skin_viewport *curr_vp;
81 /* the current line, linked to the above viewport */
82 static struct skin_line *curr_line;
84 #ifdef HAVE_LCD_BITMAP
86 #if LCD_DEPTH > 1
87 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
88 #else
89 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
90 #endif
92 #define PROGRESSBAR_BMP MAX_IMAGES
93 #define BACKDROP_BMP (MAX_BITMAPS-1)
95 /* pointers to the bitmap filenames in the WPS source */
96 static const char *bmp_names[MAX_BITMAPS];
98 #endif /* HAVE_LCD_BITMAP */
100 #if defined(DEBUG) || defined(SIMULATOR)
101 /* debugging function */
102 extern void print_debug_info(struct wps_data *data, int fail, int line);
103 extern void debug_skin_usage(void);
104 #endif
106 static void wps_reset(struct wps_data *data);
108 /* Function for parsing of details for a token. At the moment the
109 function is called, the token type has already been set. The
110 function must fill in the details and possibly add more tokens
111 to the token array. It should return the number of chars that
112 has been consumed.
114 wps_bufptr points to the char following the tag (i.e. where
115 details begin).
116 token is the pointer to the 'main' token being parsed
118 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
119 struct wps_token *token, struct wps_data *wps_data);
121 struct wps_tag {
122 enum wps_token_type type;
123 const char name[3];
124 unsigned char refresh_type;
125 const wps_tag_parse_func parse_func;
127 static int skip_end_of_line(const char *wps_bufptr);
128 /* prototypes of all special parse functions : */
129 static int parse_timeout(const char *wps_bufptr,
130 struct wps_token *token, struct wps_data *wps_data);
131 static int parse_progressbar(const char *wps_bufptr,
132 struct wps_token *token, struct wps_data *wps_data);
133 static int parse_dir_level(const char *wps_bufptr,
134 struct wps_token *token, struct wps_data *wps_data);
135 static int parse_setting(const char *wps_bufptr,
136 struct wps_token *token, struct wps_data *wps_data);
138 #ifdef HAVE_LCD_BITMAP
139 static int parse_viewport_display(const char *wps_bufptr,
140 struct wps_token *token, struct wps_data *wps_data);
141 static int parse_viewport(const char *wps_bufptr,
142 struct wps_token *token, struct wps_data *wps_data);
143 static int parse_statusbar_enable(const char *wps_bufptr,
144 struct wps_token *token, struct wps_data *wps_data);
145 static int parse_statusbar_disable(const char *wps_bufptr,
146 struct wps_token *token, struct wps_data *wps_data);
147 static int parse_image_display(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 static int parse_image_load(const char *wps_bufptr,
150 struct wps_token *token, struct wps_data *wps_data);
151 #endif /*HAVE_LCD_BITMAP */
152 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
153 static int parse_image_special(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 #endif
156 #ifdef HAVE_ALBUMART
157 static int parse_albumart_load(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_albumart_display(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 #endif /* HAVE_ALBUMART */
162 #ifdef HAVE_TOUCHSCREEN
163 static int parse_touchregion(const char *wps_bufptr,
164 struct wps_token *token, struct wps_data *wps_data);
165 #else
166 static int fulline_tag_not_supported(const char *wps_bufptr,
167 struct wps_token *token, struct wps_data *wps_data)
169 (void)token; (void)wps_data;
170 return skip_end_of_line(wps_bufptr);
172 #define parse_touchregion fulline_tag_not_supported
173 #endif
174 #ifdef CONFIG_RTC
175 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
176 #else
177 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
178 #endif
180 /* array of available tags - those with more characters have to go first
181 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
182 static const struct wps_tag all_tags[] = {
184 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
185 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
186 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
188 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
189 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
190 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
191 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
192 #if CONFIG_CHARGING >= CHARGING_MONITOR
193 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
194 #endif
195 #if CONFIG_CHARGING
196 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
197 #endif
199 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
200 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
216 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
217 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
219 /* current file */
220 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
230 parse_dir_level },
232 /* next file */
233 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
243 parse_dir_level },
245 /* current metadata */
246 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
259 /* next metadata */
260 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
273 #if (CONFIG_CODEC != MAS3507D)
274 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
275 #endif
276 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
277 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
278 #endif
280 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
282 #ifdef HAS_REMOTE_BUTTON_HOLD
283 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
284 #else
285 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
286 #endif
288 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
289 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
290 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
291 parse_timeout },
293 #ifdef HAVE_LCD_BITMAP
294 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
295 #else
296 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
297 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
298 #endif
299 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
300 parse_progressbar },
302 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
304 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
305 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
306 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
307 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
309 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
310 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
311 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
312 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
314 #ifdef HAVE_TAGCACHE
315 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
316 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
317 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
318 #endif
320 #if CONFIG_CODEC == SWCODEC
321 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
322 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
323 #endif
325 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
326 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
328 #ifdef HAVE_LCD_BITMAP
329 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
330 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
332 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
334 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
335 parse_image_display },
337 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
338 #ifdef HAVE_ALBUMART
339 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
340 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
341 #endif
343 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
344 parse_viewport_display },
345 { WPS_NO_TOKEN, "V", 0, parse_viewport },
347 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
348 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
349 #endif
350 #endif
352 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
354 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
355 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
357 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
358 /* the array MUST end with an empty string (first char is \0) */
362 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
363 * chains require the order to be kept.
365 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
367 if (*list == NULL)
368 *list = item;
369 else
371 struct skin_token_list *t = *list;
372 while (t->next)
373 t = t->next;
374 t->next = item;
377 /* create and init a new wpsll item.
378 * passing NULL to token will alloc a new one.
379 * You should only pass NULL for the token when the token type (table above)
380 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
382 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
383 void* token_data)
385 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
386 if (!token)
387 token = skin_buffer_alloc(sizeof(struct wps_token));
388 if (!llitem || !token)
389 return NULL;
390 llitem->next = NULL;
391 llitem->token = token;
392 if (token_data)
393 llitem->token->value.data = token_data;
394 return llitem;
397 /* Returns the number of chars that should be skipped to jump
398 immediately after the first eol, i.e. to the start of the next line */
399 static int skip_end_of_line(const char *wps_bufptr)
401 line_number++;
402 int skip = 0;
403 while(*(wps_bufptr + skip) != '\n')
404 skip++;
405 return ++skip;
408 /* Starts a new subline in the current line during parsing */
409 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
411 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
412 if (!subline)
413 return false;
415 subline->first_token_idx = curr_token;
416 subline->next = NULL;
418 subline->line_type = 0;
419 subline->time_mult = 0;
421 line->curr_subline->last_token_idx = curr_token-1;
422 line->curr_subline->next = subline;
423 line->curr_subline = subline;
424 return true;
427 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
429 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
430 struct skin_subline *subline = NULL;
431 if (!line)
432 return false;
434 /* init the subline */
435 subline = &line->sublines;
436 subline->first_token_idx = curr_token;
437 subline->next = NULL;
438 subline->line_type = 0;
439 subline->time_mult = 0;
441 /* init the new line */
442 line->curr_subline = &line->sublines;
443 line->next = NULL;
444 line->subline_expire_time = 0;
446 /* connect to curr_line and vp pointers.
447 * 1) close the previous lines subline
448 * 2) connect to vp pointer
449 * 3) connect to curr_line global pointer
451 if (curr_line)
453 curr_line->curr_subline->last_token_idx = curr_token - 1;
454 curr_line->next = line;
455 curr_line->curr_subline = NULL;
457 curr_line = line;
458 if (!vp->lines)
459 vp->lines = line;
460 line_number++;
461 return true;
464 #ifdef HAVE_LCD_BITMAP
466 static int parse_statusbar_enable(const char *wps_bufptr,
467 struct wps_token *token,
468 struct wps_data *wps_data)
470 (void)token; /* Kill warnings */
471 wps_data->wps_sb_tag = true;
472 wps_data->show_sb_on_wps = true;
473 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
474 if (default_vp->vp.y == 0)
476 default_vp->vp.y = STATUSBAR_HEIGHT;
477 default_vp->vp.height -= STATUSBAR_HEIGHT;
479 return skip_end_of_line(wps_bufptr);
482 static int parse_statusbar_disable(const char *wps_bufptr,
483 struct wps_token *token,
484 struct wps_data *wps_data)
486 (void)token; /* Kill warnings */
487 wps_data->wps_sb_tag = true;
488 wps_data->show_sb_on_wps = false;
489 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
490 if (default_vp->vp.y == STATUSBAR_HEIGHT)
492 default_vp->vp.y = 0;
493 default_vp->vp.height += STATUSBAR_HEIGHT;
495 return skip_end_of_line(wps_bufptr);
498 static int get_image_id(int c)
500 if(c >= 'a' && c <= 'z')
501 return c - 'a';
502 else if(c >= 'A' && c <= 'Z')
503 return c - 'A' + 26;
504 else
505 return -1;
508 static char *get_image_filename(const char *start, const char* bmpdir,
509 char *buf, int buf_size)
511 const char *end = strchr(start, '|');
513 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
515 buf = "\0";
516 return NULL;
519 int bmpdirlen = strlen(bmpdir);
521 strcpy(buf, bmpdir);
522 buf[bmpdirlen] = '/';
523 memcpy( &buf[bmpdirlen + 1], start, end - start);
524 buf[bmpdirlen + 1 + end - start] = 0;
526 return buf;
529 static int parse_image_display(const char *wps_bufptr,
530 struct wps_token *token,
531 struct wps_data *wps_data)
533 char label = wps_bufptr[0];
534 int subimage;
535 struct gui_img *img;;
537 /* sanity check */
538 img = find_image(label, wps_data);
539 if (!img)
541 token->value.i = label; /* so debug works */
542 return WPS_ERROR_INVALID_PARAM;
545 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
547 if (subimage >= img->num_subimages)
548 return WPS_ERROR_INVALID_PARAM;
550 /* Store sub-image number to display in high bits */
551 token->value.i = label | (subimage << 8);
552 return 2; /* We have consumed 2 bytes */
553 } else {
554 token->value.i = label;
555 return 1; /* We have consumed 1 byte */
559 static int parse_image_load(const char *wps_bufptr,
560 struct wps_token *token,
561 struct wps_data *wps_data)
563 const char *ptr = wps_bufptr;
564 const char *pos;
565 const char* filename;
566 const char* id;
567 const char *newline;
568 int x,y;
569 struct gui_img *img;
571 /* format: %x|n|filename.bmp|x|y|
572 or %xl|n|filename.bmp|x|y|
573 or %xl|n|filename.bmp|x|y|num_subimages|
576 if (*ptr != '|')
577 return WPS_ERROR_INVALID_PARAM;
579 ptr++;
581 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
582 return WPS_ERROR_INVALID_PARAM;
584 /* Check there is a terminating | */
585 if (*ptr != '|')
586 return WPS_ERROR_INVALID_PARAM;
588 /* check the image number and load state */
589 if(find_image(*id, wps_data))
591 /* Invalid image ID */
592 return WPS_ERROR_INVALID_PARAM;
594 img = skin_buffer_alloc(sizeof(struct gui_img));
595 if (!img)
596 return WPS_ERROR_INVALID_PARAM;
597 /* save a pointer to the filename */
598 img->bm.data = (char*)filename;
599 img->label = *id;
600 img->x = x;
601 img->y = y;
602 img->num_subimages = 1;
603 img->always_display = false;
605 /* save current viewport */
606 img->vp = &curr_vp->vp;
608 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
610 img->always_display = true;
612 else
614 /* Parse the (optional) number of sub-images */
615 ptr++;
616 newline = strchr(ptr, '\n');
617 pos = strchr(ptr, '|');
618 if (pos && pos < newline)
619 img->num_subimages = atoi(ptr);
621 if (img->num_subimages <= 0)
622 return WPS_ERROR_INVALID_PARAM;
624 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
625 if (!item)
626 return WPS_ERROR_INVALID_PARAM;
627 add_to_ll_chain(&wps_data->images, item);
629 /* Skip the rest of the line */
630 return skip_end_of_line(wps_bufptr);
633 static int parse_viewport_display(const char *wps_bufptr,
634 struct wps_token *token,
635 struct wps_data *wps_data)
637 (void)wps_data;
638 char letter = wps_bufptr[0];
640 if (letter < 'a' || letter > 'z')
642 /* invalid viewport tag */
643 return WPS_ERROR_INVALID_PARAM;
645 token->value.i = letter;
646 return 1;
649 static int parse_viewport(const char *wps_bufptr,
650 struct wps_token *token,
651 struct wps_data *wps_data)
653 (void)token; /* Kill warnings */
654 const char *ptr = wps_bufptr;
656 const int screen =
657 #ifdef HAVE_REMOTE_LCD
658 wps_data->remote_wps ? SCREEN_REMOTE :
659 #endif
660 SCREEN_MAIN;
662 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
664 /* check for the optional letter to signify its a hideable viewport */
665 /* %Vl|<label>|<rest of tags>| */
666 skin_vp->hidden_flags = 0;
667 skin_vp->label = VP_NO_LABEL;
668 skin_vp->pb = NULL;
669 skin_vp->lines = NULL;
670 if (curr_line)
672 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
673 - (wps_data->num_tokens > 0 ? 1 : 0);
676 curr_line = NULL;
677 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
678 return WPS_ERROR_INVALID_PARAM;
680 if (*ptr == 'l')
682 if (*(ptr+1) == '|')
684 char label = *(ptr+2);
685 if (label >= 'a' && label <= 'z')
687 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
688 skin_vp->label = label;
690 else
691 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
692 ptr += 3;
695 if (*ptr != '|')
696 return WPS_ERROR_INVALID_PARAM;
698 ptr++;
699 struct viewport *vp = &skin_vp->vp;
700 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
702 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
703 return WPS_ERROR_INVALID_PARAM;
705 /* Check for trailing | */
706 if (*ptr != '|')
707 return WPS_ERROR_INVALID_PARAM;
710 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
711 if (!list)
712 return WPS_ERROR_INVALID_PARAM;
713 add_to_ll_chain(&wps_data->viewports, list);
714 curr_vp = skin_vp;
715 /* Skip the rest of the line */
716 return skip_end_of_line(wps_bufptr);
719 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
720 static int parse_image_special(const char *wps_bufptr,
721 struct wps_token *token,
722 struct wps_data *wps_data)
724 (void)wps_data; /* kill warning */
725 (void)token;
726 const char *pos = NULL;
727 const char *newline;
729 pos = strchr(wps_bufptr + 1, '|');
730 newline = strchr(wps_bufptr, '\n');
732 if (pos > newline)
733 return WPS_ERROR_INVALID_PARAM;
734 #if LCD_DEPTH > 1
735 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
737 /* format: %X|filename.bmp| */
738 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
740 #endif
742 /* Skip the rest of the line */
743 return skip_end_of_line(wps_bufptr);
745 #endif
747 #endif /* HAVE_LCD_BITMAP */
749 static int parse_setting(const char *wps_bufptr,
750 struct wps_token *token,
751 struct wps_data *wps_data)
753 (void)wps_data;
754 const char *ptr = wps_bufptr;
755 const char *end;
756 int i;
758 /* Find the setting's cfg_name */
759 if (*ptr != '|')
760 return WPS_ERROR_INVALID_PARAM;
761 ptr++;
762 end = strchr(ptr,'|');
763 if (!end)
764 return WPS_ERROR_INVALID_PARAM;
766 /* Find the setting */
767 for (i=0; i<nb_settings; i++)
768 if (settings[i].cfg_name &&
769 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
770 /* prevent matches on cfg_name prefixes */
771 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
772 break;
773 if (i == nb_settings)
774 return WPS_ERROR_INVALID_PARAM;
776 /* Store the setting number */
777 token->value.i = i;
779 /* Skip the rest of the line */
780 return end-ptr+2;
784 static int parse_dir_level(const char *wps_bufptr,
785 struct wps_token *token,
786 struct wps_data *wps_data)
788 char val[] = { *wps_bufptr, '\0' };
789 token->value.i = atoi(val);
790 (void)wps_data; /* Kill warnings */
791 return 1;
794 static int parse_timeout(const char *wps_bufptr,
795 struct wps_token *token,
796 struct wps_data *wps_data)
798 int skip = 0;
799 int val = 0;
800 bool have_point = false;
801 bool have_tenth = false;
803 (void)wps_data; /* Kill the warning */
805 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
807 if (*wps_bufptr != '.')
809 val *= 10;
810 val += *wps_bufptr - '0';
811 if (have_point)
813 have_tenth = true;
814 wps_bufptr++;
815 skip++;
816 break;
819 else
820 have_point = true;
822 wps_bufptr++;
823 skip++;
826 if (have_tenth == false)
827 val *= 10;
829 if (val == 0 && skip == 0)
831 /* decide what to do if no value was specified */
832 switch (token->type)
834 case WPS_TOKEN_SUBLINE_TIMEOUT:
835 return -1;
836 case WPS_TOKEN_BUTTON_VOLUME:
837 val = 10;
838 break;
841 token->value.i = val;
843 return skip;
846 static int parse_progressbar(const char *wps_bufptr,
847 struct wps_token *token,
848 struct wps_data *wps_data)
850 /* %pb or %pb|filename|x|y|width|height|
851 using - for any of the params uses "sane" values */
852 #ifdef HAVE_LCD_BITMAP
853 enum {
854 PB_FILENAME = 0,
855 PB_X,
856 PB_Y,
857 PB_WIDTH,
858 PB_HEIGHT
860 const char *filename;
861 int x, y, height, width;
862 uint32_t set = 0;
863 const char *ptr = wps_bufptr;
864 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
865 struct skin_token_list *item = new_skin_token_list_item(token, pb);
867 if (!pb || !item)
868 return WPS_ERROR_INVALID_PARAM;
870 struct viewport *vp = &curr_vp->vp;
871 #ifndef __PCTOOL__
872 int font_height = font_get(vp->font)->height;
873 #else
874 int font_height = 8;
875 #endif
876 /* we need to know what line number (viewport relative) this pb is,
877 * so count them... */
878 int line_num = -1;
879 struct skin_line *line = curr_vp->lines;
880 while (line)
882 line_num++;
883 line = line->next;
885 pb->have_bitmap_pb = false;
886 pb->bm.data = NULL; /* no bitmap specified */
888 if (*wps_bufptr != '|') /* regular old style */
890 pb->x = 0;
891 pb->width = vp->width;
892 pb->height = SYSFONT_HEIGHT-2;
893 pb->y = -line_num - 1; /* Will be computed during the rendering */
895 curr_vp->pb = pb;
896 add_to_ll_chain(&wps_data->progressbars, item);
897 return 0;
899 ptr = wps_bufptr + 1;
901 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
902 &x, &y, &width, &height)))
903 return WPS_ERROR_INVALID_PARAM;
905 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
906 pb->bm.data = (char*)filename;
908 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
909 pb->x = x;
910 else
911 pb->x = vp->x;
913 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
915 /* A zero width causes a divide-by-zero error later, so reject it */
916 if (width == 0)
917 return WPS_ERROR_INVALID_PARAM;
919 pb->width = width;
921 else
922 pb->width = vp->width - pb->x;
924 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
926 /* A zero height makes no sense - reject it */
927 if (height == 0)
928 return WPS_ERROR_INVALID_PARAM;
930 pb->height = height;
932 else
933 pb->height = font_height;
935 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
936 pb->y = y;
937 else
938 pb->y = -line_num - 1; /* Will be computed during the rendering */
940 curr_vp->pb = pb;
941 add_to_ll_chain(&wps_data->progressbars, item);
943 /* Skip the rest of the line */
944 return skip_end_of_line(wps_bufptr)-1;
945 #else
946 (void)token;
948 if (*(wps_bufptr-1) == 'f')
949 wps_data->full_line_progressbar = true;
950 else
951 wps_data->full_line_progressbar = false;
953 return 0;
955 #endif
958 #ifdef HAVE_ALBUMART
959 static int parse_albumart_load(const char *wps_bufptr,
960 struct wps_token *token,
961 struct wps_data *wps_data)
963 const char *_pos, *newline;
964 bool parsing;
965 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
966 (void)token; /* silence warning */
967 if (!aa)
968 return skip_end_of_line(wps_bufptr);
970 /* reset albumart info in wps */
971 aa->albumart_max_width = -1;
972 aa->albumart_max_height = -1;
973 aa->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
974 aa->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
975 aa->vp = &curr_vp->vp;
977 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
979 newline = strchr(wps_bufptr, '\n');
981 /* initial validation and parsing of x and y components */
982 if (*wps_bufptr != '|')
983 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
985 _pos = wps_bufptr + 1;
986 if (!isdigit(*_pos))
987 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
988 aa->albumart_x = atoi(_pos);
990 _pos = strchr(_pos, '|');
991 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
992 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
994 aa->albumart_y = atoi(_pos);
996 _pos = strchr(_pos, '|');
997 if (!_pos || _pos > newline)
998 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
999 e.g. %Cl|7|59\n */
1001 /* parsing width field */
1002 parsing = true;
1003 while (parsing)
1005 /* apply each modifier in turn */
1006 ++_pos;
1007 switch (*_pos)
1009 case 'l':
1010 case 'L':
1011 case '+':
1012 aa->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
1013 break;
1014 case 'c':
1015 case 'C':
1016 aa->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
1017 break;
1018 case 'r':
1019 case 'R':
1020 case '-':
1021 aa->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
1022 break;
1023 case 'd':
1024 case 'D':
1025 case 'i':
1026 case 'I':
1027 case 's':
1028 case 'S':
1029 /* simply ignored */
1030 break;
1031 default:
1032 parsing = false;
1033 break;
1036 /* extract max width data */
1037 if (*_pos != '|')
1039 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1040 return WPS_ERROR_INVALID_PARAM;
1042 aa->albumart_max_width = atoi(_pos);
1044 _pos = strchr(_pos, '|');
1045 if (!_pos || _pos > newline)
1046 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1047 e.g. %Cl|7|59|200\n */
1050 /* parsing height field */
1051 parsing = true;
1052 while (parsing)
1054 /* apply each modifier in turn */
1055 ++_pos;
1056 switch (*_pos)
1058 case 't':
1059 case 'T':
1060 case '-':
1061 aa->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1062 break;
1063 case 'c':
1064 case 'C':
1065 aa->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1066 break;
1067 case 'b':
1068 case 'B':
1069 case '+':
1070 aa->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1071 break;
1072 case 'd':
1073 case 'D':
1074 case 'i':
1075 case 'I':
1076 case 's':
1077 case 'S':
1078 /* simply ignored */
1079 break;
1080 default:
1081 parsing = false;
1082 break;
1085 /* extract max height data */
1086 if (*_pos != '|')
1088 if (!isdigit(*_pos))
1089 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1091 aa->albumart_max_height = atoi(_pos);
1093 _pos = strchr(_pos, '|');
1094 if (!_pos || _pos > newline)
1095 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1096 e.g. %Cl|7|59|200|200\n */
1099 /* if we got here, we parsed everything ok .. ! */
1100 if (aa->albumart_max_width < 0)
1101 aa->albumart_max_width = 0;
1102 else if (aa->albumart_max_width > LCD_WIDTH)
1103 aa->albumart_max_width = LCD_WIDTH;
1105 if (aa->albumart_max_height < 0)
1106 aa->albumart_max_height = 0;
1107 else if (aa->albumart_max_height > LCD_HEIGHT)
1108 aa->albumart_max_height = LCD_HEIGHT;
1110 aa->wps_uses_albumart = WPS_ALBUMART_LOAD;
1111 aa->draw = false;
1112 wps_data->albumart = aa;
1114 /* Skip the rest of the line */
1115 return skip_end_of_line(wps_bufptr);
1118 static int parse_albumart_display(const char *wps_bufptr,
1119 struct wps_token *token,
1120 struct wps_data *wps_data)
1122 (void)wps_bufptr;
1123 struct wps_token *prev = token-1;
1124 if ((wps_data->num_tokens > 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1126 token->type = WPS_TOKEN_ALBUMART_FOUND;
1128 else if (wps_data->albumart)
1130 wps_data->albumart->vp = &curr_vp->vp;
1132 #if 0
1133 /* the old code did this so keep it here for now...
1134 * this is to allow the posibility to showing the next tracks AA! */
1135 if (wps_bufptr+1 == 'n')
1136 return 1;
1137 #endif
1138 return 0;
1140 #endif /* HAVE_ALBUMART */
1142 #ifdef HAVE_TOUCHSCREEN
1144 struct touchaction {char* s; int action;};
1145 static struct touchaction touchactions[] = {
1146 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1147 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1148 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1149 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1150 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1151 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1152 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1154 static int parse_touchregion(const char *wps_bufptr,
1155 struct wps_token *token, struct wps_data *wps_data)
1157 (void)token;
1158 unsigned i, imax;
1159 struct touchregion *region = NULL;
1160 const char *ptr = wps_bufptr;
1161 const char *action;
1162 const char pb_string[] = "progressbar";
1163 const char vol_string[] = "volume";
1164 int x,y,w,h;
1166 /* format: %T|x|y|width|height|action|
1167 * if action starts with & the area must be held to happen
1168 * action is one of:
1169 * play - play/pause playback
1170 * stop - stop playback, exit the wps
1171 * prev - prev track
1172 * next - next track
1173 * ffwd - seek forward
1174 * rwd - seek backwards
1175 * menu - go back to the main menu
1176 * browse - go back to the file/db browser
1177 * shuffle - toggle shuffle mode
1178 * repmode - cycle the repeat mode
1179 * quickscreen - go into the quickscreen
1180 * contextmenu - open the context menu
1181 * playlist - go into the playlist
1182 * pitch - go into the pitchscreen
1186 if (*ptr != '|')
1187 return WPS_ERROR_INVALID_PARAM;
1188 ptr++;
1190 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1191 return WPS_ERROR_INVALID_PARAM;
1193 /* Check there is a terminating | */
1194 if (*ptr != '|')
1195 return WPS_ERROR_INVALID_PARAM;
1197 region = skin_buffer_alloc(sizeof(struct touchregion));
1198 if (!region)
1199 return WPS_ERROR_INVALID_PARAM;
1201 /* should probably do some bounds checking here with the viewport... but later */
1202 region->action = ACTION_NONE;
1203 region->x = x;
1204 region->y = y;
1205 region->width = w;
1206 region->height = h;
1207 region->wvp = curr_vp;
1209 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1210 && *(action + sizeof(pb_string)-1) == '|')
1211 region->type = WPS_TOUCHREGION_SCROLLBAR;
1212 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1213 && *(action + sizeof(vol_string)-1) == '|')
1214 region->type = WPS_TOUCHREGION_VOLUME;
1215 else
1217 region->type = WPS_TOUCHREGION_ACTION;
1219 if (*action == '&')
1221 action++;
1222 region->repeat = true;
1224 else
1225 region->repeat = false;
1227 i = 0;
1228 imax = ARRAYLEN(touchactions);
1229 while ((region->action == ACTION_NONE) &&
1230 (i < imax))
1232 /* try to match with one of our touchregion screens */
1233 int len = strlen(touchactions[i].s);
1234 if (!strncmp(touchactions[i].s, action, len)
1235 && *(action+len) == '|')
1236 region->action = touchactions[i].action;
1237 i++;
1239 if (region->action == ACTION_NONE)
1240 return WPS_ERROR_INVALID_PARAM;
1242 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1243 if (!item)
1244 return WPS_ERROR_INVALID_PARAM;
1245 add_to_ll_chain(&wps_data->touchregions, item);
1246 return skip_end_of_line(wps_bufptr);
1248 #endif
1250 /* Parse a generic token from the given string. Return the length read */
1251 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1253 int skip = 0, taglen = 0, ret;
1254 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1255 const struct wps_tag *tag;
1256 memset(token, 0, sizeof(*token));
1258 switch(*wps_bufptr)
1261 case '%':
1262 case '<':
1263 case '|':
1264 case '>':
1265 case ';':
1266 case '#':
1267 /* escaped characters */
1268 token->type = WPS_TOKEN_CHARACTER;
1269 token->value.c = *wps_bufptr;
1270 taglen = 1;
1271 wps_data->num_tokens++;
1272 break;
1274 case '?':
1275 /* conditional tag */
1276 token->type = WPS_TOKEN_CONDITIONAL;
1277 level++;
1278 condindex[level] = wps_data->num_tokens;
1279 numoptions[level] = 1;
1280 wps_data->num_tokens++;
1281 ret = parse_token(wps_bufptr + 1, wps_data);
1282 if (ret < 0) return ret;
1283 taglen = 1 + ret;
1284 break;
1286 default:
1287 /* find what tag we have */
1288 for (tag = all_tags;
1289 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1290 tag++) ;
1292 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1293 token->type = tag->type;
1294 curr_line->curr_subline->line_type |= tag->refresh_type;
1296 /* if the tag has a special parsing function, we call it */
1297 if (tag->parse_func)
1299 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1300 if (ret < 0) return ret;
1301 skip += ret;
1304 /* Some tags we don't want to save as tokens */
1305 if (tag->type == WPS_NO_TOKEN)
1306 break;
1308 /* tags that start with 'F', 'I' or 'D' are for the next file */
1309 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1310 *(tag->name) == 'D')
1311 token->next = true;
1313 wps_data->num_tokens++;
1314 break;
1317 skip += taglen;
1318 return skip;
1321 /* Parses the WPS.
1322 data is the pointer to the structure where the parsed WPS should be stored.
1323 It is initialised.
1324 wps_bufptr points to the string containing the WPS tags */
1325 #define TOKEN_BLOCK_SIZE 128
1326 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1328 if (!data || !wps_bufptr || !*wps_bufptr)
1329 return false;
1330 enum wps_parse_error fail = PARSE_OK;
1331 int ret;
1332 int max_tokens = TOKEN_BLOCK_SIZE;
1333 size_t buf_free = 0;
1334 line_number = 1;
1335 level = -1;
1337 /* allocate enough RAM for a reasonable skin, grow as needed.
1338 * Free any used RAM before loading the images to be 100% RAM efficient */
1339 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1340 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1341 return false;
1342 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1343 data->num_tokens = 0;
1345 while (*wps_bufptr && !fail)
1347 /* first make sure there is enough room for tokens */
1348 if (max_tokens <= data->num_tokens + 5)
1350 int extra_tokens = TOKEN_BLOCK_SIZE;
1351 size_t needed = extra_tokens * sizeof(struct wps_token);
1352 /* do some smarts here to grow the array a bit */
1353 if (skin_buffer_freespace() < needed)
1355 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1356 break;
1358 skin_buffer_increment(needed, false);
1359 max_tokens += extra_tokens;
1362 switch(*wps_bufptr++)
1365 /* Regular tag */
1366 case '%':
1367 if ((ret = parse_token(wps_bufptr, data)) < 0)
1369 fail = PARSE_FAIL_COND_INVALID_PARAM;
1370 break;
1372 else if (level >= WPS_MAX_COND_LEVEL - 1)
1374 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1375 break;
1377 wps_bufptr += ret;
1378 break;
1380 /* Alternating sublines separator */
1381 case ';':
1382 if (level >= 0) /* there are unclosed conditionals */
1384 fail = PARSE_FAIL_UNCLOSED_COND;
1385 break;
1388 if (!skin_start_new_subline(curr_line, data->num_tokens))
1389 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1391 break;
1393 /* Conditional list start */
1394 case '<':
1395 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1397 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1398 break;
1401 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1402 lastcond[level] = data->num_tokens++;
1403 break;
1405 /* Conditional list end */
1406 case '>':
1407 if (level < 0) /* not in a conditional, invalid char */
1409 fail = PARSE_FAIL_INVALID_CHAR;
1410 break;
1413 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1414 if (lastcond[level])
1415 data->tokens[lastcond[level]].value.i = data->num_tokens;
1416 else
1418 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1419 break;
1422 lastcond[level] = 0;
1423 data->num_tokens++;
1424 data->tokens[condindex[level]].value.i = numoptions[level];
1425 level--;
1426 break;
1428 /* Conditional list option */
1429 case '|':
1430 if (level < 0) /* not in a conditional, invalid char */
1432 fail = PARSE_FAIL_INVALID_CHAR;
1433 break;
1436 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1437 if (lastcond[level])
1438 data->tokens[lastcond[level]].value.i = data->num_tokens;
1439 else
1441 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1442 break;
1445 lastcond[level] = data->num_tokens;
1446 numoptions[level]++;
1447 data->num_tokens++;
1448 break;
1450 /* Comment */
1451 case '#':
1452 if (level >= 0) /* there are unclosed conditionals */
1454 fail = PARSE_FAIL_UNCLOSED_COND;
1455 break;
1458 wps_bufptr += skip_end_of_line(wps_bufptr);
1459 break;
1461 /* End of this line */
1462 case '\n':
1463 if (level >= 0) /* there are unclosed conditionals */
1465 fail = PARSE_FAIL_UNCLOSED_COND;
1466 break;
1468 /* add a new token for the \n so empty lines are correct */
1469 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1470 data->tokens[data->num_tokens].value.c = '\n';
1471 data->tokens[data->num_tokens].next = false;
1472 data->num_tokens++;
1474 if (!skin_start_new_line(curr_vp, data->num_tokens))
1476 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1477 break;
1480 break;
1482 /* String */
1483 default:
1485 unsigned int len = 1;
1486 const char *string_start = wps_bufptr - 1;
1488 /* find the length of the string */
1489 while (*wps_bufptr && *wps_bufptr != '#' &&
1490 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1491 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1492 *wps_bufptr != '|' && *wps_bufptr != '\n')
1494 wps_bufptr++;
1495 len++;
1498 /* look if we already have that string */
1499 char *str;
1500 bool found = false;
1501 struct skin_token_list *list = data->strings;
1502 while (list)
1504 str = (char*)list->token->value.data;
1505 found = (strlen(str) == len &&
1506 strncmp(string_start, str, len) == 0);
1507 if (found)
1508 break; /* break here because the list item is
1509 used if its found */
1510 list = list->next;
1512 /* If a matching string is found, found is true and i is
1513 the index of the string. If not, found is false */
1515 if (!found)
1517 /* new string */
1518 str = (char*)skin_buffer_alloc(len+1);
1519 if (!str)
1521 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1522 break;
1524 strlcpy(str, string_start, len+1);
1525 struct skin_token_list *item =
1526 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1527 if(!item)
1529 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1530 break;
1532 add_to_ll_chain(&data->strings, item);
1534 else
1536 /* another occurrence of an existing string */
1537 data->tokens[data->num_tokens].value.data = list->token->value.data;
1539 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1540 data->num_tokens++;
1542 break;
1546 if (!fail && level >= 0) /* there are unclosed conditionals */
1547 fail = PARSE_FAIL_UNCLOSED_COND;
1549 if (*wps_bufptr && !fail)
1550 /* one of the limits of the while loop was exceeded */
1551 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1553 /* Success! */
1554 curr_line->curr_subline->last_token_idx = data->num_tokens;
1555 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1556 /* freeup unused tokens */
1557 skin_buffer_free_from_front(sizeof(struct wps_token)
1558 * (max_tokens - data->num_tokens));
1560 #if defined(DEBUG) || defined(SIMULATOR)
1561 if (debug)
1562 print_debug_info(data, fail, line_number);
1563 #else
1564 (void)debug;
1565 #endif
1567 return (fail == 0);
1570 static void wps_reset(struct wps_data *data)
1572 #ifdef HAVE_REMOTE_LCD
1573 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1574 #endif
1575 memset(data, 0, sizeof(*data));
1576 skin_data_init(data);
1577 #ifdef HAVE_REMOTE_LCD
1578 data->remote_wps = rwps;
1579 #endif
1582 #ifdef HAVE_LCD_BITMAP
1583 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1585 (void)wps_data; /* only needed for remote targets */
1586 bool loaded = false;
1587 char img_path[MAX_PATH];
1588 get_image_filename(bitmap->data, bmpdir,
1589 img_path, sizeof(img_path));
1591 /* load the image */
1592 int format;
1593 #ifdef HAVE_REMOTE_LCD
1594 if (wps_data->remote_wps)
1595 format = FORMAT_ANY|FORMAT_REMOTE;
1596 else
1597 #endif
1598 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1600 size_t max_buf;
1601 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1602 bitmap->data = imgbuf;
1603 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1605 if (ret > 0)
1607 skin_buffer_increment(ret, true);
1608 loaded = true;
1610 else
1612 /* Abort if we can't load an image */
1613 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1614 loaded = false;
1616 return loaded;
1619 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1621 struct skin_token_list *list;
1622 /* do the progressbars */
1623 list = wps_data->progressbars;
1624 while (list)
1626 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1627 if (pb->bm.data)
1629 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1631 list = list->next;
1633 /* regular images */
1634 list = wps_data->images;
1635 while (list)
1637 struct gui_img *img = (struct gui_img*)list->token->value.data;
1638 if (img->bm.data)
1640 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1641 if (img->loaded)
1642 img->subimage_height = img->bm.height / img->num_subimages;
1644 list = list->next;
1647 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1648 if (bmp_names[BACKDROP_BMP])
1650 int screen = SCREEN_MAIN;
1651 char img_path[MAX_PATH];
1652 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1653 img_path, sizeof(img_path));
1654 #if defined(HAVE_REMOTE_LCD)
1655 /* We only need to check LCD type if there is a remote LCD */
1656 if (wps_data->remote_wps)
1657 screen = SCREEN_REMOTE;
1658 #endif
1659 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1661 #endif /* has backdrop support */
1663 /* If we got here, everything was OK */
1664 return true;
1667 #endif /* HAVE_LCD_BITMAP */
1669 /* to setup up the wps-data from a format-buffer (isfile = false)
1670 from a (wps-)file (isfile = true)*/
1671 bool skin_data_load(struct wps_data *wps_data,
1672 struct screen *display,
1673 const char *buf,
1674 bool isfile)
1677 if (!wps_data || !buf)
1678 return false;
1679 #ifdef HAVE_ALBUMART
1680 int status;
1681 struct mp3entry *curtrack;
1682 long offset;
1683 struct skin_albumart old_aa = {.wps_uses_albumart = WPS_ALBUMART_NONE};
1684 if (wps_data->albumart)
1686 old_aa.wps_uses_albumart = wps_data->albumart->wps_uses_albumart;
1687 old_aa.albumart_max_height = wps_data->albumart->albumart_max_height;
1688 old_aa.albumart_max_width = wps_data->albumart->albumart_max_width;
1690 #endif
1692 wps_reset(wps_data);
1694 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1695 if (!curr_vp)
1696 return false;
1697 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1698 if (!list)
1699 return false;
1700 add_to_ll_chain(&wps_data->viewports, list);
1703 /* Initialise the first (default) viewport */
1704 curr_vp->label = VP_DEFAULT_LABEL;
1705 curr_vp->vp.x = 0;
1706 curr_vp->vp.width = display->getwidth();
1707 curr_vp->vp.height = display->getheight();
1708 curr_vp->pb = NULL;
1709 curr_vp->hidden_flags = 0;
1710 curr_vp->lines = NULL;
1712 curr_line = NULL;
1713 if (!skin_start_new_line(curr_vp, 0))
1714 return false;
1716 switch (statusbar_position(display->screen_type))
1718 case STATUSBAR_OFF:
1719 curr_vp->vp.y = 0;
1720 break;
1721 case STATUSBAR_TOP:
1722 curr_vp->vp.y = STATUSBAR_HEIGHT;
1723 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1724 break;
1725 case STATUSBAR_BOTTOM:
1726 curr_vp->vp.y = 0;
1727 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1728 break;
1730 #ifdef HAVE_LCD_BITMAP
1731 curr_vp->vp.font = FONT_UI;
1732 curr_vp->vp.drawmode = DRMODE_SOLID;
1733 #endif
1734 #if LCD_DEPTH > 1
1735 if (display->depth > 1)
1737 curr_vp->vp.fg_pattern = display->get_foreground();
1738 curr_vp->vp.bg_pattern = display->get_background();
1740 #endif
1741 if (!isfile)
1743 return wps_parse(wps_data, buf, false);
1745 else
1747 int fd = open_utf8(buf, O_RDONLY);
1749 if (fd < 0)
1750 return false;
1752 /* get buffer space from the plugin buffer */
1753 size_t buffersize = 0;
1754 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1756 if (!wps_buffer)
1757 return false;
1759 /* copy the file's content to the buffer for parsing,
1760 ensuring that every line ends with a newline char. */
1761 unsigned int start = 0;
1762 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1764 start += strlen(wps_buffer + start);
1765 if (start < buffersize - 1)
1767 wps_buffer[start++] = '\n';
1768 wps_buffer[start] = 0;
1772 close(fd);
1774 if (start <= 0)
1775 return false;
1777 #ifdef HAVE_LCD_BITMAP
1778 /* Set all filename pointers to NULL */
1779 memset(bmp_names, 0, sizeof(bmp_names));
1780 #endif
1782 /* parse the WPS source */
1783 if (!wps_parse(wps_data, wps_buffer, true)) {
1784 wps_reset(wps_data);
1785 return false;
1788 wps_data->wps_loaded = true;
1790 #ifdef HAVE_LCD_BITMAP
1791 /* get the bitmap dir */
1792 char bmpdir[MAX_PATH];
1793 char *dot = strrchr(buf, '.');
1795 strlcpy(bmpdir, buf, dot - buf + 1);
1797 /* load the bitmaps that were found by the parsing */
1798 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1799 wps_reset(wps_data);
1800 return false;
1802 #endif
1803 #ifdef HAVE_ALBUMART
1804 status = audio_status();
1805 if (status & AUDIO_STATUS_PLAY)
1807 struct skin_albumart *aa = wps_data->albumart;
1808 if (aa && ((aa->wps_uses_albumart && !old_aa.wps_uses_albumart) ||
1809 (aa->wps_uses_albumart &&
1810 (((old_aa.albumart_max_height != aa->albumart_max_height) ||
1811 (old_aa.albumart_max_width != aa->albumart_max_width))))))
1813 curtrack = audio_current_track();
1814 offset = curtrack->offset;
1815 audio_stop();
1816 if (!(status & AUDIO_STATUS_PAUSE))
1817 audio_play(offset);
1820 #endif
1821 #if defined(DEBUG) || defined(SIMULATOR)
1822 debug_skin_usage();
1823 #endif
1824 return true;