initial custom statusbar commit
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob9a8d3f5ce4b93484a2171b29ff4f9ab57129092e
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 #ifdef HAVE_ALBUMART
60 #include "playback.h"
61 #endif
63 #include "backdrop.h"
65 #define WPS_ERROR_INVALID_PARAM -1
67 /* level of current conditional.
68 -1 means we're not in a conditional. */
69 static int level = -1;
71 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
72 or WPS_TOKEN_CONDITIONAL_START in current level */
73 static int lastcond[WPS_MAX_COND_LEVEL];
75 /* index of the WPS_TOKEN_CONDITIONAL in current level */
76 static int condindex[WPS_MAX_COND_LEVEL];
78 /* number of condtional options in current level */
79 static int numoptions[WPS_MAX_COND_LEVEL];
81 /* line number, debug only */
82 static int line_number;
84 /* the current viewport */
85 static struct skin_viewport *curr_vp;
86 /* the current line, linked to the above viewport */
87 static struct skin_line *curr_line;
89 #ifdef HAVE_LCD_BITMAP
91 #if LCD_DEPTH > 1
92 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
93 #else
94 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
95 #endif
97 #define PROGRESSBAR_BMP MAX_IMAGES
98 #define BACKDROP_BMP (MAX_BITMAPS-1)
100 /* pointers to the bitmap filenames in the WPS source */
101 static const char *bmp_names[MAX_BITMAPS];
103 #endif /* HAVE_LCD_BITMAP */
105 #if defined(DEBUG) || defined(SIMULATOR)
106 /* debugging function */
107 extern void print_debug_info(struct wps_data *data, int fail, int line);
108 extern void debug_skin_usage(void);
109 #endif
111 /* Function for parsing of details for a token. At the moment the
112 function is called, the token type has already been set. The
113 function must fill in the details and possibly add more tokens
114 to the token array. It should return the number of chars that
115 has been consumed.
117 wps_bufptr points to the char following the tag (i.e. where
118 details begin).
119 token is the pointer to the 'main' token being parsed
121 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
122 struct wps_token *token, struct wps_data *wps_data);
124 struct wps_tag {
125 enum wps_token_type type;
126 const char name[3];
127 unsigned char refresh_type;
128 const wps_tag_parse_func parse_func;
130 static int skip_end_of_line(const char *wps_bufptr);
131 /* prototypes of all special parse functions : */
132 static int parse_timeout(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_progressbar(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_dir_level(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_setting_and_lang(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
141 #ifdef HAVE_LCD_BITMAP
142 static int parse_viewport_display(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_viewport(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 static int parse_statusbar_enable(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 static int parse_statusbar_disable(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 static int parse_image_display(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 static int parse_image_load(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 #endif /*HAVE_LCD_BITMAP */
155 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
156 static int parse_image_special(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 #endif
159 #ifdef HAVE_ALBUMART
160 static int parse_albumart_load(const char *wps_bufptr,
161 struct wps_token *token, struct wps_data *wps_data);
162 static int parse_albumart_display(const char *wps_bufptr,
163 struct wps_token *token, struct wps_data *wps_data);
164 #endif /* HAVE_ALBUMART */
165 #ifdef HAVE_TOUCHSCREEN
166 static int parse_touchregion(const char *wps_bufptr,
167 struct wps_token *token, struct wps_data *wps_data);
168 #else
169 static int fulline_tag_not_supported(const char *wps_bufptr,
170 struct wps_token *token, struct wps_data *wps_data)
172 (void)token; (void)wps_data;
173 return skip_end_of_line(wps_bufptr);
175 #define parse_touchregion fulline_tag_not_supported
176 #endif
177 #ifdef CONFIG_RTC
178 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
179 #else
180 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
181 #endif
183 /* array of available tags - those with more characters have to go first
184 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
185 static const struct wps_tag all_tags[] = {
187 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
188 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
189 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
191 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
192 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
193 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
194 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
195 #if CONFIG_CHARGING >= CHARGING_MONITOR
196 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
197 #endif
198 #if CONFIG_CHARGING
199 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
200 #endif
202 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
203 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
216 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
217 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
218 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
219 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
220 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
222 /* current file */
223 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
233 parse_dir_level },
235 /* next file */
236 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
246 parse_dir_level },
248 /* current metadata */
249 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
262 /* next metadata */
263 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
276 #if (CONFIG_CODEC != MAS3507D)
277 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
278 #endif
279 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
280 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
281 #endif
283 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
285 #ifdef HAS_REMOTE_BUTTON_HOLD
286 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
287 #else
288 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
289 #endif
291 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
292 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
293 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
294 parse_timeout },
296 #ifdef HAVE_LCD_BITMAP
297 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
298 #else
299 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
300 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
301 #endif
302 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
303 parse_progressbar },
305 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
307 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
308 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
309 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
312 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
313 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
314 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
315 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
317 #ifdef HAVE_TAGCACHE
318 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
319 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
320 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
321 #endif
323 #if CONFIG_CODEC == SWCODEC
324 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
325 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
326 #endif
328 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
329 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
331 #ifdef HAVE_LCD_BITMAP
332 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
333 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
335 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
337 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
338 parse_image_display },
340 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
341 #ifdef HAVE_ALBUMART
342 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
343 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
344 #endif
346 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
347 parse_viewport_display },
348 { WPS_NO_TOKEN, "V", 0, parse_viewport },
350 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
351 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
352 #endif
353 #endif
355 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
356 parse_setting_and_lang },
357 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
358 parse_setting_and_lang },
360 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
361 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
362 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
364 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
365 /* the array MUST end with an empty string (first char is \0) */
369 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
370 * chains require the order to be kept.
372 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
374 if (*list == NULL)
375 *list = item;
376 else
378 struct skin_token_list *t = *list;
379 while (t->next)
380 t = t->next;
381 t->next = item;
384 /* create and init a new wpsll item.
385 * passing NULL to token will alloc a new one.
386 * You should only pass NULL for the token when the token type (table above)
387 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
389 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
390 void* token_data)
392 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
393 if (!token)
394 token = skin_buffer_alloc(sizeof(struct wps_token));
395 if (!llitem || !token)
396 return NULL;
397 llitem->next = NULL;
398 llitem->token = token;
399 if (token_data)
400 llitem->token->value.data = token_data;
401 return llitem;
404 /* Returns the number of chars that should be skipped to jump
405 immediately after the first eol, i.e. to the start of the next line */
406 static int skip_end_of_line(const char *wps_bufptr)
408 line_number++;
409 int skip = 0;
410 while(*(wps_bufptr + skip) != '\n')
411 skip++;
412 return ++skip;
415 /* Starts a new subline in the current line during parsing */
416 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
418 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
419 if (!subline)
420 return false;
422 subline->first_token_idx = curr_token;
423 subline->next = NULL;
425 subline->line_type = 0;
426 subline->time_mult = 0;
428 line->curr_subline->last_token_idx = curr_token-1;
429 line->curr_subline->next = subline;
430 line->curr_subline = subline;
431 return true;
434 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
436 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
437 struct skin_subline *subline = NULL;
438 if (!line)
439 return false;
441 /* init the subline */
442 subline = &line->sublines;
443 subline->first_token_idx = curr_token;
444 subline->next = NULL;
445 subline->line_type = 0;
446 subline->time_mult = 0;
448 /* init the new line */
449 line->curr_subline = &line->sublines;
450 line->next = NULL;
451 line->subline_expire_time = 0;
453 /* connect to curr_line and vp pointers.
454 * 1) close the previous lines subline
455 * 2) connect to vp pointer
456 * 3) connect to curr_line global pointer
458 if (curr_line)
460 curr_line->curr_subline->last_token_idx = curr_token - 1;
461 curr_line->next = line;
462 curr_line->curr_subline = NULL;
464 curr_line = line;
465 if (!vp->lines)
466 vp->lines = line;
467 line_number++;
468 return true;
471 #ifdef HAVE_LCD_BITMAP
473 static int parse_statusbar_enable(const char *wps_bufptr,
474 struct wps_token *token,
475 struct wps_data *wps_data)
477 (void)token; /* Kill warnings */
478 wps_data->wps_sb_tag = true;
479 wps_data->show_sb_on_wps = true;
480 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
481 if (default_vp->vp.y == 0)
483 default_vp->vp.y = STATUSBAR_HEIGHT;
484 default_vp->vp.height -= STATUSBAR_HEIGHT;
486 return skip_end_of_line(wps_bufptr);
489 static int parse_statusbar_disable(const char *wps_bufptr,
490 struct wps_token *token,
491 struct wps_data *wps_data)
493 (void)token; /* Kill warnings */
494 wps_data->wps_sb_tag = true;
495 wps_data->show_sb_on_wps = false;
496 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
497 if (default_vp->vp.y == STATUSBAR_HEIGHT)
499 default_vp->vp.y = 0;
500 default_vp->vp.height += STATUSBAR_HEIGHT;
502 return skip_end_of_line(wps_bufptr);
505 static int get_image_id(int c)
507 if(c >= 'a' && c <= 'z')
508 return c - 'a';
509 else if(c >= 'A' && c <= 'Z')
510 return c - 'A' + 26;
511 else
512 return -1;
515 static char *get_image_filename(const char *start, const char* bmpdir,
516 char *buf, int buf_size)
518 const char *end = strchr(start, '|');
520 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
522 buf = "\0";
523 return NULL;
526 int bmpdirlen = strlen(bmpdir);
528 strcpy(buf, bmpdir);
529 buf[bmpdirlen] = '/';
530 memcpy( &buf[bmpdirlen + 1], start, end - start);
531 buf[bmpdirlen + 1 + end - start] = 0;
533 return buf;
536 static int parse_image_display(const char *wps_bufptr,
537 struct wps_token *token,
538 struct wps_data *wps_data)
540 char label = wps_bufptr[0];
541 int subimage;
542 struct gui_img *img;;
544 /* sanity check */
545 img = find_image(label, wps_data);
546 if (!img)
548 token->value.i = label; /* so debug works */
549 return WPS_ERROR_INVALID_PARAM;
552 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
554 if (subimage >= img->num_subimages)
555 return WPS_ERROR_INVALID_PARAM;
557 /* Store sub-image number to display in high bits */
558 token->value.i = label | (subimage << 8);
559 return 2; /* We have consumed 2 bytes */
560 } else {
561 token->value.i = label;
562 return 1; /* We have consumed 1 byte */
566 static int parse_image_load(const char *wps_bufptr,
567 struct wps_token *token,
568 struct wps_data *wps_data)
570 const char *ptr = wps_bufptr;
571 const char *pos;
572 const char* filename;
573 const char* id;
574 const char *newline;
575 int x,y;
576 struct gui_img *img;
578 /* format: %x|n|filename.bmp|x|y|
579 or %xl|n|filename.bmp|x|y|
580 or %xl|n|filename.bmp|x|y|num_subimages|
583 if (*ptr != '|')
584 return WPS_ERROR_INVALID_PARAM;
586 ptr++;
588 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
589 return WPS_ERROR_INVALID_PARAM;
591 /* Check there is a terminating | */
592 if (*ptr != '|')
593 return WPS_ERROR_INVALID_PARAM;
595 /* check the image number and load state */
596 if(find_image(*id, wps_data))
598 /* Invalid image ID */
599 return WPS_ERROR_INVALID_PARAM;
601 img = skin_buffer_alloc(sizeof(struct gui_img));
602 if (!img)
603 return WPS_ERROR_INVALID_PARAM;
604 /* save a pointer to the filename */
605 img->bm.data = (char*)filename;
606 if (wps_data->debug)
607 DEBUGF("%s >> image parsed (label: %c)\n", __func__, *id);
608 img->label = *id;
609 img->x = x;
610 img->y = y;
611 img->num_subimages = 1;
612 img->always_display = false;
614 /* save current viewport */
615 img->vp = &curr_vp->vp;
617 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
619 img->always_display = true;
621 else
623 /* Parse the (optional) number of sub-images */
624 ptr++;
625 newline = strchr(ptr, '\n');
626 pos = strchr(ptr, '|');
627 if (pos && pos < newline)
628 img->num_subimages = atoi(ptr);
630 if (img->num_subimages <= 0)
631 return WPS_ERROR_INVALID_PARAM;
633 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
634 if (!item)
635 return WPS_ERROR_INVALID_PARAM;
636 add_to_ll_chain(&wps_data->images, item);
638 /* Skip the rest of the line */
639 return skip_end_of_line(wps_bufptr);
642 static int parse_viewport_display(const char *wps_bufptr,
643 struct wps_token *token,
644 struct wps_data *wps_data)
646 (void)wps_data;
647 char letter = wps_bufptr[0];
649 if (letter < 'a' || letter > 'z')
651 /* invalid viewport tag */
652 return WPS_ERROR_INVALID_PARAM;
654 token->value.i = letter;
655 return 1;
658 static int parse_viewport(const char *wps_bufptr,
659 struct wps_token *token,
660 struct wps_data *wps_data)
662 (void)token; /* Kill warnings */
663 const char *ptr = wps_bufptr;
665 const int screen =
666 #ifdef HAVE_REMOTE_LCD
667 wps_data->remote_wps ? SCREEN_REMOTE :
668 #endif
669 SCREEN_MAIN;
671 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
673 /* check for the optional letter to signify its a hideable viewport */
674 /* %Vl|<label>|<rest of tags>| */
675 skin_vp->hidden_flags = 0;
676 skin_vp->label = VP_NO_LABEL;
677 skin_vp->pb = NULL;
678 skin_vp->lines = NULL;
679 if (curr_line)
681 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
682 - (wps_data->num_tokens > 0 ? 1 : 0);
685 curr_line = NULL;
686 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
687 return WPS_ERROR_INVALID_PARAM;
689 if (*ptr == 'l')
691 if (*(ptr+1) == '|')
693 char label = *(ptr+2);
694 if (label >= 'a' && label <= 'z')
696 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
697 skin_vp->label = label;
699 else
700 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
701 ptr += 3;
704 if (*ptr != '|')
705 return WPS_ERROR_INVALID_PARAM;
707 ptr++;
708 struct viewport *vp = &skin_vp->vp;
709 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
711 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
712 return WPS_ERROR_INVALID_PARAM;
714 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
715 /* Check for trailing | */
716 if (*ptr != '|')
717 return WPS_ERROR_INVALID_PARAM;
720 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
721 if (!list)
722 return WPS_ERROR_INVALID_PARAM;
723 add_to_ll_chain(&wps_data->viewports, list);
724 curr_vp = skin_vp;
725 /* Skip the rest of the line */
726 return skip_end_of_line(wps_bufptr);
729 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
730 static int parse_image_special(const char *wps_bufptr,
731 struct wps_token *token,
732 struct wps_data *wps_data)
734 (void)wps_data; /* kill warning */
735 (void)token;
736 const char *pos = NULL;
737 const char *newline;
739 pos = strchr(wps_bufptr + 1, '|');
740 newline = strchr(wps_bufptr, '\n');
742 if (pos > newline)
743 return WPS_ERROR_INVALID_PARAM;
744 #if LCD_DEPTH > 1
745 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
747 /* format: %X|filename.bmp| */
748 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
750 #endif
752 /* Skip the rest of the line */
753 return skip_end_of_line(wps_bufptr);
755 #endif
757 #endif /* HAVE_LCD_BITMAP */
759 static int parse_setting_and_lang(const char *wps_bufptr,
760 struct wps_token *token,
761 struct wps_data *wps_data)
763 /* NOTE: both the string validations that happen in here will
764 * automatically PASS on checkwps because its too hard to get
765 * settings_list.c and englinsh.lang built for it.
766 * If that ever changes remove the #ifndef __PCTOOL__'s here
768 (void)wps_data;
769 const char *ptr = wps_bufptr;
770 const char *end;
771 int i = 0;
772 char temp[64];
774 /* Find the setting's cfg_name */
775 if (*ptr != '|')
776 return WPS_ERROR_INVALID_PARAM;
777 ptr++;
778 end = strchr(ptr,'|');
779 if (!end)
780 return WPS_ERROR_INVALID_PARAM;
781 strlcpy(temp, ptr,end-ptr+1);
783 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
785 #ifndef __PCTOOL__
786 i = lang_english_to_id(temp);
787 if (i < 0)
788 return WPS_ERROR_INVALID_PARAM;
789 #endif
791 else
793 /* Find the setting */
794 for (i=0; i<nb_settings; i++)
795 if (settings[i].cfg_name &&
796 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
797 /* prevent matches on cfg_name prefixes */
798 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
799 break;
800 #ifndef __PCTOOL__
801 if (i == nb_settings)
802 return WPS_ERROR_INVALID_PARAM;
803 #endif
805 /* Store the setting number */
806 token->value.i = i;
808 /* Skip the rest of the line */
809 return end-ptr+2;
813 static int parse_dir_level(const char *wps_bufptr,
814 struct wps_token *token,
815 struct wps_data *wps_data)
817 char val[] = { *wps_bufptr, '\0' };
818 token->value.i = atoi(val);
819 (void)wps_data; /* Kill warnings */
820 return 1;
823 static int parse_timeout(const char *wps_bufptr,
824 struct wps_token *token,
825 struct wps_data *wps_data)
827 int skip = 0;
828 int val = 0;
829 bool have_point = false;
830 bool have_tenth = false;
832 (void)wps_data; /* Kill the warning */
834 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
836 if (*wps_bufptr != '.')
838 val *= 10;
839 val += *wps_bufptr - '0';
840 if (have_point)
842 have_tenth = true;
843 wps_bufptr++;
844 skip++;
845 break;
848 else
849 have_point = true;
851 wps_bufptr++;
852 skip++;
855 if (have_tenth == false)
856 val *= 10;
858 if (val == 0 && skip == 0)
860 /* decide what to do if no value was specified */
861 switch (token->type)
863 case WPS_TOKEN_SUBLINE_TIMEOUT:
864 return -1;
865 case WPS_TOKEN_BUTTON_VOLUME:
866 val = 10;
867 break;
870 token->value.i = val;
872 return skip;
875 static int parse_progressbar(const char *wps_bufptr,
876 struct wps_token *token,
877 struct wps_data *wps_data)
879 /* %pb or %pb|filename|x|y|width|height|
880 using - for any of the params uses "sane" values */
881 #ifdef HAVE_LCD_BITMAP
882 enum {
883 PB_FILENAME = 0,
884 PB_X,
885 PB_Y,
886 PB_WIDTH,
887 PB_HEIGHT
889 const char *filename;
890 int x, y, height, width;
891 uint32_t set = 0;
892 const char *ptr = wps_bufptr;
893 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
894 struct skin_token_list *item = new_skin_token_list_item(token, pb);
896 if (!pb || !item)
897 return WPS_ERROR_INVALID_PARAM;
899 struct viewport *vp = &curr_vp->vp;
900 #ifndef __PCTOOL__
901 int font_height = font_get(vp->font)->height;
902 #else
903 int font_height = 8;
904 #endif
905 /* we need to know what line number (viewport relative) this pb is,
906 * so count them... */
907 int line_num = -1;
908 struct skin_line *line = curr_vp->lines;
909 while (line)
911 line_num++;
912 line = line->next;
914 pb->have_bitmap_pb = false;
915 pb->bm.data = NULL; /* no bitmap specified */
917 if (*wps_bufptr != '|') /* regular old style */
919 pb->x = 0;
920 pb->width = vp->width;
921 pb->height = SYSFONT_HEIGHT-2;
922 pb->y = -line_num - 1; /* Will be computed during the rendering */
924 curr_vp->pb = pb;
925 add_to_ll_chain(&wps_data->progressbars, item);
926 return 0;
928 ptr = wps_bufptr + 1;
930 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
931 &x, &y, &width, &height)))
932 return WPS_ERROR_INVALID_PARAM;
934 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
935 pb->bm.data = (char*)filename;
937 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
938 pb->x = x;
939 else
940 pb->x = vp->x;
942 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
944 /* A zero width causes a divide-by-zero error later, so reject it */
945 if (width == 0)
946 return WPS_ERROR_INVALID_PARAM;
948 pb->width = width;
950 else
951 pb->width = vp->width - pb->x;
953 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
955 /* A zero height makes no sense - reject it */
956 if (height == 0)
957 return WPS_ERROR_INVALID_PARAM;
959 pb->height = height;
961 else
962 pb->height = font_height;
964 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
965 pb->y = y;
966 else
967 pb->y = -line_num - 1; /* Will be computed during the rendering */
969 curr_vp->pb = pb;
970 add_to_ll_chain(&wps_data->progressbars, item);
972 /* Skip the rest of the line */
973 return skip_end_of_line(wps_bufptr)-1;
974 #else
975 (void)token;
977 if (*(wps_bufptr-1) == 'f')
978 wps_data->full_line_progressbar = true;
979 else
980 wps_data->full_line_progressbar = false;
982 return 0;
984 #endif
987 #ifdef HAVE_ALBUMART
988 static int parse_albumart_load(const char *wps_bufptr,
989 struct wps_token *token,
990 struct wps_data *wps_data)
992 const char *_pos, *newline;
993 bool parsing;
994 struct dim dimensions;
995 int albumart_slot;
996 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
997 (void)token; /* silence warning */
998 if (!aa)
999 return skip_end_of_line(wps_bufptr);
1001 /* reset albumart info in wps */
1002 aa->width = -1;
1003 aa->height = -1;
1004 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1005 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1006 aa->vp = &curr_vp->vp;
1008 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1010 newline = strchr(wps_bufptr, '\n');
1012 /* initial validation and parsing of x and y components */
1013 if (*wps_bufptr != '|')
1014 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1016 _pos = wps_bufptr + 1;
1017 _pos = parse_list("dd", NULL, '|', _pos, &aa->x, &aa->y);
1019 if (!_pos || _pos > newline || *_pos != '|')
1020 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1021 e.g. %Cl|7|59\n */
1023 /* parsing width field */
1024 parsing = true;
1025 while (parsing)
1027 /* apply each modifier in turn */
1028 ++_pos;
1029 switch (*_pos)
1031 case 'l':
1032 case 'L':
1033 case '+':
1034 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1035 break;
1036 case 'c':
1037 case 'C':
1038 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1039 break;
1040 case 'r':
1041 case 'R':
1042 case '-':
1043 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1044 break;
1045 case 'd':
1046 case 'D':
1047 case 'i':
1048 case 'I':
1049 case 's':
1050 case 'S':
1051 /* simply ignored */
1052 break;
1053 default:
1054 parsing = false;
1055 break;
1058 /* extract max width data */
1059 if (*_pos != '|')
1061 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1062 return WPS_ERROR_INVALID_PARAM;
1064 aa->width = atoi(_pos);
1066 _pos = strchr(_pos, '|');
1067 if (!_pos || _pos > newline)
1068 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1069 e.g. %Cl|7|59|200\n */
1072 /* parsing height field */
1073 parsing = true;
1074 while (parsing)
1076 /* apply each modifier in turn */
1077 ++_pos;
1078 switch (*_pos)
1080 case 't':
1081 case 'T':
1082 case '-':
1083 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1084 break;
1085 case 'c':
1086 case 'C':
1087 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1088 break;
1089 case 'b':
1090 case 'B':
1091 case '+':
1092 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1093 break;
1094 case 'd':
1095 case 'D':
1096 case 'i':
1097 case 'I':
1098 case 's':
1099 case 'S':
1100 /* simply ignored */
1101 break;
1102 default:
1103 parsing = false;
1104 break;
1107 /* extract max height data */
1108 if (*_pos != '|')
1110 if (!isdigit(*_pos))
1111 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1113 aa->height = atoi(_pos);
1115 _pos = strchr(_pos, '|');
1116 if (!_pos || _pos > newline)
1117 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1118 e.g. %Cl|7|59|200|200\n */
1121 /* if we got here, we parsed everything ok .. ! */
1122 if (aa->width < 0)
1123 aa->width = 0;
1124 else if (aa->width > LCD_WIDTH)
1125 aa->width = LCD_WIDTH;
1127 if (aa->height < 0)
1128 aa->height = 0;
1129 else if (aa->height > LCD_HEIGHT)
1130 aa->height = LCD_HEIGHT;
1132 aa->state = WPS_ALBUMART_LOAD;
1133 aa->draw = false;
1134 wps_data->albumart = aa;
1136 dimensions.width = aa->width;
1137 dimensions.height = aa->height;
1139 albumart_slot = playback_claim_aa_slot(&dimensions);
1141 if (albumart_slot < 0) /* didn't get a slot ? */
1142 return skip_end_of_line(wps_bufptr);
1143 else
1144 wps_data->playback_aa_slot = albumart_slot;
1146 /* Skip the rest of the line */
1147 return skip_end_of_line(wps_bufptr);
1150 static int parse_albumart_display(const char *wps_bufptr,
1151 struct wps_token *token,
1152 struct wps_data *wps_data)
1154 (void)wps_bufptr;
1155 struct wps_token *prev = token-1;
1156 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1158 token->type = WPS_TOKEN_ALBUMART_FOUND;
1160 else if (wps_data->albumart)
1162 wps_data->albumart->vp = &curr_vp->vp;
1164 #if 0
1165 /* the old code did this so keep it here for now...
1166 * this is to allow the posibility to showing the next tracks AA! */
1167 if (wps_bufptr+1 == 'n')
1168 return 1;
1169 #endif
1170 return 0;
1172 #endif /* HAVE_ALBUMART */
1174 #ifdef HAVE_TOUCHSCREEN
1176 struct touchaction {char* s; int action;};
1177 static struct touchaction touchactions[] = {
1178 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1179 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1180 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1181 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1182 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1183 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1184 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1186 static int parse_touchregion(const char *wps_bufptr,
1187 struct wps_token *token, struct wps_data *wps_data)
1189 (void)token;
1190 unsigned i, imax;
1191 struct touchregion *region = NULL;
1192 const char *ptr = wps_bufptr;
1193 const char *action;
1194 const char pb_string[] = "progressbar";
1195 const char vol_string[] = "volume";
1196 int x,y,w,h;
1198 /* format: %T|x|y|width|height|action|
1199 * if action starts with & the area must be held to happen
1200 * action is one of:
1201 * play - play/pause playback
1202 * stop - stop playback, exit the wps
1203 * prev - prev track
1204 * next - next track
1205 * ffwd - seek forward
1206 * rwd - seek backwards
1207 * menu - go back to the main menu
1208 * browse - go back to the file/db browser
1209 * shuffle - toggle shuffle mode
1210 * repmode - cycle the repeat mode
1211 * quickscreen - go into the quickscreen
1212 * contextmenu - open the context menu
1213 * playlist - go into the playlist
1214 * pitch - go into the pitchscreen
1218 if (*ptr != '|')
1219 return WPS_ERROR_INVALID_PARAM;
1220 ptr++;
1222 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1223 return WPS_ERROR_INVALID_PARAM;
1225 /* Check there is a terminating | */
1226 if (*ptr != '|')
1227 return WPS_ERROR_INVALID_PARAM;
1229 region = skin_buffer_alloc(sizeof(struct touchregion));
1230 if (!region)
1231 return WPS_ERROR_INVALID_PARAM;
1233 /* should probably do some bounds checking here with the viewport... but later */
1234 region->action = ACTION_NONE;
1235 region->x = x;
1236 region->y = y;
1237 region->width = w;
1238 region->height = h;
1239 region->wvp = curr_vp;
1241 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1242 && *(action + sizeof(pb_string)-1) == '|')
1243 region->type = WPS_TOUCHREGION_SCROLLBAR;
1244 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1245 && *(action + sizeof(vol_string)-1) == '|')
1246 region->type = WPS_TOUCHREGION_VOLUME;
1247 else
1249 region->type = WPS_TOUCHREGION_ACTION;
1251 if (*action == '&')
1253 action++;
1254 region->repeat = true;
1256 else
1257 region->repeat = false;
1259 i = 0;
1260 imax = ARRAYLEN(touchactions);
1261 while ((region->action == ACTION_NONE) &&
1262 (i < imax))
1264 /* try to match with one of our touchregion screens */
1265 int len = strlen(touchactions[i].s);
1266 if (!strncmp(touchactions[i].s, action, len)
1267 && *(action+len) == '|')
1268 region->action = touchactions[i].action;
1269 i++;
1271 if (region->action == ACTION_NONE)
1272 return WPS_ERROR_INVALID_PARAM;
1274 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1275 if (!item)
1276 return WPS_ERROR_INVALID_PARAM;
1277 add_to_ll_chain(&wps_data->touchregions, item);
1278 return skip_end_of_line(wps_bufptr);
1280 #endif
1282 /* Parse a generic token from the given string. Return the length read */
1283 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1285 int skip = 0, taglen = 0, ret;
1286 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1287 const struct wps_tag *tag;
1288 memset(token, 0, sizeof(*token));
1290 switch(*wps_bufptr)
1293 case '%':
1294 case '<':
1295 case '|':
1296 case '>':
1297 case ';':
1298 case '#':
1299 /* escaped characters */
1300 token->type = WPS_TOKEN_CHARACTER;
1301 token->value.c = *wps_bufptr;
1302 taglen = 1;
1303 wps_data->num_tokens++;
1304 break;
1306 case '?':
1307 /* conditional tag */
1308 token->type = WPS_TOKEN_CONDITIONAL;
1309 level++;
1310 condindex[level] = wps_data->num_tokens;
1311 numoptions[level] = 1;
1312 wps_data->num_tokens++;
1313 ret = parse_token(wps_bufptr + 1, wps_data);
1314 if (ret < 0) return ret;
1315 taglen = 1 + ret;
1316 break;
1318 default:
1319 /* find what tag we have */
1320 for (tag = all_tags;
1321 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1322 tag++) ;
1324 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1325 token->type = tag->type;
1326 curr_line->curr_subline->line_type |= tag->refresh_type;
1328 /* if the tag has a special parsing function, we call it */
1329 if (tag->parse_func)
1331 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1332 if (ret < 0) return ret;
1333 skip += ret;
1336 /* Some tags we don't want to save as tokens */
1337 if (tag->type == WPS_NO_TOKEN)
1338 break;
1340 /* tags that start with 'F', 'I' or 'D' are for the next file */
1341 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1342 *(tag->name) == 'D')
1343 token->next = true;
1345 wps_data->num_tokens++;
1346 break;
1349 skip += taglen;
1350 return skip;
1353 /* Parses the WPS.
1354 data is the pointer to the structure where the parsed WPS should be stored.
1355 It is initialised.
1356 wps_bufptr points to the string containing the WPS tags */
1357 #define TOKEN_BLOCK_SIZE 128
1358 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1360 if (!data || !wps_bufptr || !*wps_bufptr)
1361 return false;
1362 enum wps_parse_error fail = PARSE_OK;
1363 int ret;
1364 int max_tokens = TOKEN_BLOCK_SIZE;
1365 size_t buf_free = 0;
1366 line_number = 1;
1367 level = -1;
1369 /* allocate enough RAM for a reasonable skin, grow as needed.
1370 * Free any used RAM before loading the images to be 100% RAM efficient */
1371 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1372 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1373 return false;
1374 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1375 data->num_tokens = 0;
1377 while (*wps_bufptr && !fail)
1379 /* first make sure there is enough room for tokens */
1380 if (max_tokens <= data->num_tokens + 5)
1382 int extra_tokens = TOKEN_BLOCK_SIZE;
1383 size_t needed = extra_tokens * sizeof(struct wps_token);
1384 /* do some smarts here to grow the array a bit */
1385 if (skin_buffer_freespace() < needed)
1387 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1388 break;
1390 skin_buffer_increment(needed, false);
1391 max_tokens += extra_tokens;
1394 switch(*wps_bufptr++)
1397 /* Regular tag */
1398 case '%':
1399 if ((ret = parse_token(wps_bufptr, data)) < 0)
1401 fail = PARSE_FAIL_COND_INVALID_PARAM;
1402 break;
1404 else if (level >= WPS_MAX_COND_LEVEL - 1)
1406 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1407 break;
1409 wps_bufptr += ret;
1410 break;
1412 /* Alternating sublines separator */
1413 case ';':
1414 if (level >= 0) /* there are unclosed conditionals */
1416 fail = PARSE_FAIL_UNCLOSED_COND;
1417 break;
1420 if (!skin_start_new_subline(curr_line, data->num_tokens))
1421 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1423 break;
1425 /* Conditional list start */
1426 case '<':
1427 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1429 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1430 break;
1433 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1434 lastcond[level] = data->num_tokens++;
1435 break;
1437 /* Conditional list end */
1438 case '>':
1439 if (level < 0) /* not in a conditional, invalid char */
1441 fail = PARSE_FAIL_INVALID_CHAR;
1442 break;
1445 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1446 if (lastcond[level])
1447 data->tokens[lastcond[level]].value.i = data->num_tokens;
1448 else
1450 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1451 break;
1454 lastcond[level] = 0;
1455 data->num_tokens++;
1456 data->tokens[condindex[level]].value.i = numoptions[level];
1457 level--;
1458 break;
1460 /* Conditional list option */
1461 case '|':
1462 if (level < 0) /* not in a conditional, invalid char */
1464 fail = PARSE_FAIL_INVALID_CHAR;
1465 break;
1468 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1469 if (lastcond[level])
1470 data->tokens[lastcond[level]].value.i = data->num_tokens;
1471 else
1473 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1474 break;
1477 lastcond[level] = data->num_tokens;
1478 numoptions[level]++;
1479 data->num_tokens++;
1480 break;
1482 /* Comment */
1483 case '#':
1484 if (level >= 0) /* there are unclosed conditionals */
1486 fail = PARSE_FAIL_UNCLOSED_COND;
1487 break;
1490 wps_bufptr += skip_end_of_line(wps_bufptr);
1491 break;
1493 /* End of this line */
1494 case '\n':
1495 if (level >= 0) /* there are unclosed conditionals */
1497 fail = PARSE_FAIL_UNCLOSED_COND;
1498 break;
1500 /* add a new token for the \n so empty lines are correct */
1501 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1502 data->tokens[data->num_tokens].value.c = '\n';
1503 data->tokens[data->num_tokens].next = false;
1504 data->num_tokens++;
1506 if (!skin_start_new_line(curr_vp, data->num_tokens))
1508 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1509 break;
1512 break;
1514 /* String */
1515 default:
1517 unsigned int len = 1;
1518 const char *string_start = wps_bufptr - 1;
1520 /* find the length of the string */
1521 while (*wps_bufptr && *wps_bufptr != '#' &&
1522 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1523 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1524 *wps_bufptr != '|' && *wps_bufptr != '\n')
1526 wps_bufptr++;
1527 len++;
1530 /* look if we already have that string */
1531 char *str;
1532 bool found = false;
1533 struct skin_token_list *list = data->strings;
1534 while (list)
1536 str = (char*)list->token->value.data;
1537 found = (strlen(str) == len &&
1538 strncmp(string_start, str, len) == 0);
1539 if (found)
1540 break; /* break here because the list item is
1541 used if its found */
1542 list = list->next;
1544 /* If a matching string is found, found is true and i is
1545 the index of the string. If not, found is false */
1547 if (!found)
1549 /* new string */
1550 str = (char*)skin_buffer_alloc(len+1);
1551 if (!str)
1553 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1554 break;
1556 strlcpy(str, string_start, len+1);
1557 struct skin_token_list *item =
1558 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1559 if(!item)
1561 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1562 break;
1564 add_to_ll_chain(&data->strings, item);
1566 else
1568 /* another occurrence of an existing string */
1569 data->tokens[data->num_tokens].value.data = list->token->value.data;
1571 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1572 data->num_tokens++;
1574 break;
1578 if (!fail && level >= 0) /* there are unclosed conditionals */
1579 fail = PARSE_FAIL_UNCLOSED_COND;
1581 if (*wps_bufptr && !fail)
1582 /* one of the limits of the while loop was exceeded */
1583 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1585 /* Success! */
1586 curr_line->curr_subline->last_token_idx = data->num_tokens;
1587 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1588 /* freeup unused tokens */
1589 skin_buffer_free_from_front(sizeof(struct wps_token)
1590 * (max_tokens - data->num_tokens));
1592 #if defined(DEBUG) || defined(SIMULATOR)
1593 if (debug)
1594 print_debug_info(data, fail, line_number);
1595 #else
1596 (void)debug;
1597 #endif
1599 return (fail == 0);
1604 * initial setup of wps_data; does reset everything
1605 * except fields which need to survive, i.e.
1607 * wps_data->remote_wps
1609 void skin_data_reset(struct wps_data *wps_data)
1611 #ifdef HAVE_LCD_BITMAP
1612 wps_data->images = NULL;
1613 wps_data->progressbars = NULL;
1614 #endif
1615 #ifdef HAVE_TOUCHSCREEN
1616 wps_data->touchregions = NULL;
1617 #endif
1618 wps_data->viewports = NULL;
1619 wps_data->strings = NULL;
1620 #ifdef HAVE_ALBUMART
1621 wps_data->albumart = NULL;
1622 if (wps_data->playback_aa_slot >= 0)
1624 playback_release_aa_slot(wps_data->playback_aa_slot);
1625 wps_data->playback_aa_slot = -1;
1627 #endif
1628 wps_data->tokens = NULL;
1629 wps_data->num_tokens = 0;
1630 wps_data->button_time_volume = 0;
1632 #ifdef HAVE_LCD_BITMAP
1633 wps_data->peak_meter_enabled = false;
1634 wps_data->wps_sb_tag = false;
1635 wps_data->show_sb_on_wps = false;
1636 #else /* HAVE_LCD_CHARCELLS */
1637 /* progress bars */
1638 int i;
1639 for (i = 0; i < 8; i++)
1641 wps_data->wps_progress_pat[i] = 0;
1643 wps_data->full_line_progressbar = false;
1645 #ifdef HAVE_REMOTE_LCD
1646 data->remote_wps = rwps;
1647 #endif
1648 wps_data->wps_loaded = false;
1649 if (wps_data->debug)
1650 DEBUGF("%s >> wps_data trashed (%p)\n", __func__, data);
1653 #ifdef HAVE_LCD_BITMAP
1654 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1656 (void)wps_data; /* only needed for remote targets */
1657 bool loaded = false;
1658 char img_path[MAX_PATH];
1659 get_image_filename(bitmap->data, bmpdir,
1660 img_path, sizeof(img_path));
1662 /* load the image */
1663 int format;
1664 #ifdef HAVE_REMOTE_LCD
1665 if (wps_data->remote_wps)
1666 format = FORMAT_ANY|FORMAT_REMOTE;
1667 else
1668 #endif
1669 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1671 size_t max_buf;
1672 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1673 bitmap->data = imgbuf;
1674 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1676 if (ret > 0)
1678 skin_buffer_increment(ret, true);
1679 loaded = true;
1681 else
1683 /* Abort if we can't load an image */
1684 DEBUGF("ERR: Failed to load image - %s\n",img_path);
1685 loaded = false;
1687 return loaded;
1690 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1692 struct skin_token_list *list;
1693 /* do the progressbars */
1694 list = wps_data->progressbars;
1695 while (list)
1697 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1698 if (pb->bm.data)
1700 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1702 list = list->next;
1704 /* regular images */
1705 list = wps_data->images;
1706 while (list)
1708 struct gui_img *img = (struct gui_img*)list->token->value.data;
1709 if (img->bm.data)
1711 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1712 if (img->loaded)
1713 img->subimage_height = img->bm.height / img->num_subimages;
1715 list = list->next;
1718 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1719 if (bmp_names[BACKDROP_BMP])
1721 int screen = SCREEN_MAIN;
1722 char img_path[MAX_PATH];
1723 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1724 img_path, sizeof(img_path));
1725 #if defined(HAVE_REMOTE_LCD)
1726 /* We only need to check LCD type if there is a remote LCD */
1727 if (wps_data->remote_wps)
1728 screen = SCREEN_REMOTE;
1729 #endif
1730 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1732 #endif /* has backdrop support */
1734 /* If we got here, everything was OK */
1735 return true;
1738 #endif /* HAVE_LCD_BITMAP */
1740 /* to setup up the wps-data from a format-buffer (isfile = false)
1741 from a (wps-)file (isfile = true)*/
1742 bool skin_data_load(struct wps_data *wps_data,
1743 struct screen *display,
1744 const char *buf,
1745 bool isfile)
1748 if (!wps_data || !buf)
1749 return false;
1750 #ifdef HAVE_ALBUMART
1751 int status;
1752 struct mp3entry *curtrack;
1753 long offset;
1754 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1755 if (wps_data->albumart)
1757 old_aa.state = wps_data->albumart->state;
1758 old_aa.height = wps_data->albumart->height;
1759 old_aa.width = wps_data->albumart->width;
1761 #endif
1763 skin_data_reset(wps_data);
1765 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1766 if (!curr_vp)
1767 return false;
1768 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1769 if (!list)
1770 return false;
1771 add_to_ll_chain(&wps_data->viewports, list);
1774 /* Initialise the first (default) viewport */
1775 curr_vp->label = VP_DEFAULT_LABEL;
1776 curr_vp->vp.x = 0;
1777 curr_vp->vp.width = display->getwidth();
1778 curr_vp->vp.height = display->getheight();
1779 curr_vp->pb = NULL;
1780 curr_vp->hidden_flags = 0;
1781 curr_vp->lines = NULL;
1783 curr_line = NULL;
1784 if (!skin_start_new_line(curr_vp, 0))
1785 return false;
1787 switch (statusbar_position(display->screen_type))
1789 case STATUSBAR_OFF:
1790 curr_vp->vp.y = 0;
1791 break;
1792 case STATUSBAR_TOP:
1793 curr_vp->vp.y = STATUSBAR_HEIGHT;
1794 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1795 break;
1796 case STATUSBAR_BOTTOM:
1797 curr_vp->vp.y = 0;
1798 curr_vp->vp.height -= STATUSBAR_HEIGHT;
1799 break;
1801 #ifdef HAVE_LCD_BITMAP
1802 curr_vp->vp.font = FONT_UI;
1803 curr_vp->vp.drawmode = DRMODE_SOLID;
1804 #endif
1805 #if LCD_DEPTH > 1
1806 if (display->depth > 1)
1808 curr_vp->vp.fg_pattern = display->get_foreground();
1809 curr_vp->vp.bg_pattern = display->get_background();
1811 #endif
1812 if (!isfile)
1814 return wps_parse(wps_data, buf, false);
1816 else
1818 int fd = open_utf8(buf, O_RDONLY);
1820 if (fd < 0)
1821 return false;
1823 /* get buffer space from the plugin buffer */
1824 size_t buffersize = 0;
1825 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1827 if (!wps_buffer)
1828 return false;
1830 /* copy the file's content to the buffer for parsing,
1831 ensuring that every line ends with a newline char. */
1832 unsigned int start = 0;
1833 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1835 start += strlen(wps_buffer + start);
1836 if (start < buffersize - 1)
1838 wps_buffer[start++] = '\n';
1839 wps_buffer[start] = 0;
1843 close(fd);
1845 if (start <= 0)
1846 return false;
1848 #ifdef HAVE_LCD_BITMAP
1849 /* Set all filename pointers to NULL */
1850 memset(bmp_names, 0, sizeof(bmp_names));
1851 #endif
1853 /* parse the WPS source */
1854 if (!wps_parse(wps_data, wps_buffer, true)) {
1855 skin_data_reset(wps_data);
1856 return false;
1859 wps_data->wps_loaded = true;
1861 #ifdef HAVE_LCD_BITMAP
1862 /* get the bitmap dir */
1863 char bmpdir[MAX_PATH];
1864 char *dot = strrchr(buf, '.');
1866 strlcpy(bmpdir, buf, dot - buf + 1);
1868 /* load the bitmaps that were found by the parsing */
1869 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1870 skin_data_reset(wps_data);
1871 return false;
1873 #endif
1874 #ifdef HAVE_ALBUMART
1875 status = audio_status();
1876 if (status & AUDIO_STATUS_PLAY)
1878 struct skin_albumart *aa = wps_data->albumart;
1879 if (aa && ((aa->state && !old_aa.state) ||
1880 (aa->state &&
1881 (((old_aa.height != aa->height) ||
1882 (old_aa.width != aa->width))))))
1884 curtrack = audio_current_track();
1885 offset = curtrack->offset;
1886 audio_stop();
1887 if (!(status & AUDIO_STATUS_PAUSE))
1888 audio_play(offset);
1891 #endif
1892 #if defined(DEBUG) || defined(SIMULATOR)
1893 debug_skin_usage();
1894 #endif
1895 return true;