Reuse the backdrop buffers if 2 skins use the same backdrop (on the same screen of...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobfe68c7c57e696733dd269cb15d082d9f4d575338
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 lang_is_rtl() (false)
40 #define DEBUGF printf
41 #endif /*WPSEDITOR*/
42 #else
43 #include "debug.h"
44 #include "language.h"
45 #endif /*__PCTOOL__*/
47 #include <ctype.h>
48 #include <stdbool.h>
49 #include "font.h"
51 #include "wps_internals.h"
52 #include "skin_engine.h"
53 #include "settings.h"
54 #include "settings_list.h"
55 #include "skin_fonts.h"
57 #ifdef HAVE_LCD_BITMAP
58 #include "bmp.h"
59 #endif
61 #ifdef HAVE_ALBUMART
62 #include "playback.h"
63 #endif
65 #include "backdrop.h"
66 #include "statusbar-skinned.h"
68 #define WPS_ERROR_INVALID_PARAM -1
70 /* which screen are we parsing for? */
71 static enum screen_type curr_screen;
73 /* level of current conditional.
74 -1 means we're not in a conditional. */
75 static int level = -1;
77 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
78 or WPS_TOKEN_CONDITIONAL_START in current level */
79 static int lastcond[WPS_MAX_COND_LEVEL];
81 /* index of the WPS_TOKEN_CONDITIONAL in current level */
82 static int condindex[WPS_MAX_COND_LEVEL];
84 /* number of condtional options in current level */
85 static int numoptions[WPS_MAX_COND_LEVEL];
87 /* line number, debug only */
88 static int line_number;
90 /* the current viewport */
91 static struct skin_viewport *curr_vp;
92 /* the current line, linked to the above viewport */
93 static struct skin_line *curr_line;
95 static int follow_lang_direction = 0;
97 #if defined(DEBUG) || defined(SIMULATOR)
98 /* debugging function */
99 extern void print_debug_info(struct wps_data *data, int fail, int line);
100 extern void debug_skin_usage(void);
101 #endif
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
107 has been consumed.
109 wps_bufptr points to the char following the tag (i.e. where
110 details begin).
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
114 struct wps_token *token, struct wps_data *wps_data);
116 struct wps_tag {
117 enum wps_token_type type;
118 const char name[3];
119 unsigned char refresh_type;
120 const wps_tag_parse_func parse_func;
122 static int skip_end_of_line(const char *wps_bufptr);
123 /* prototypes of all special parse functions : */
124 static int parse_timeout(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126 static int parse_progressbar(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_dir_level(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_setting_and_lang(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_languagedirection(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data)
137 (void)wps_bufptr;
138 (void)token;
139 (void)wps_data;
140 follow_lang_direction = 2; /* 2 because it is decremented immediatly after
141 this token is parsed, after the next token it
142 will be 0 again. */
143 return 0;
146 #ifdef HAVE_LCD_BITMAP
147 static int parse_viewport_display(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 static int parse_playlistview(const char *wps_bufptr,
150 struct wps_token *token, struct wps_data *wps_data);
151 static int parse_viewport(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 static int parse_statusbar_enable(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 static int parse_statusbar_disable(const char *wps_bufptr,
156 struct wps_token *token, struct wps_data *wps_data);
157 static int parse_image_display(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_image_load(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 static int parse_font_load(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 #endif /*HAVE_LCD_BITMAP */
164 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
165 static int parse_image_special(const char *wps_bufptr,
166 struct wps_token *token, struct wps_data *wps_data);
167 #endif
168 #ifdef HAVE_ALBUMART
169 static int parse_albumart_load(const char *wps_bufptr,
170 struct wps_token *token, struct wps_data *wps_data);
171 static int parse_albumart_display(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 #endif /* HAVE_ALBUMART */
174 #ifdef HAVE_TOUCHSCREEN
175 static int parse_touchregion(const char *wps_bufptr,
176 struct wps_token *token, struct wps_data *wps_data);
177 #else
178 static int fulline_tag_not_supported(const char *wps_bufptr,
179 struct wps_token *token, struct wps_data *wps_data)
181 (void)token; (void)wps_data;
182 return skip_end_of_line(wps_bufptr);
184 #define parse_touchregion fulline_tag_not_supported
185 #endif
186 #ifdef CONFIG_RTC
187 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
188 #else
189 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
190 #endif
192 /* array of available tags - those with more characters have to go first
193 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
194 static const struct wps_tag all_tags[] = {
196 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
197 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
198 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
199 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
200 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
201 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
203 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
204 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
205 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
206 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
207 #if CONFIG_CHARGING >= CHARGING_MONITOR
208 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
209 #endif
210 #if CONFIG_CHARGING
211 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
212 #endif
213 #ifdef HAVE_USB_POWER
214 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
215 #endif
217 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
219 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
220 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
221 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
222 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
223 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
224 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
225 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
226 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
237 /* current file */
238 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
248 parse_dir_level },
250 /* next file */
251 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
261 parse_dir_level },
263 /* current metadata */
264 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
277 /* next metadata */
278 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
283 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
284 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
291 #if (CONFIG_CODEC != MAS3507D)
292 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
293 #endif
294 #if (CONFIG_CODEC == SWCODEC)
295 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
296 #endif
297 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
298 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
299 #endif
301 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
303 #ifdef HAS_REMOTE_BUTTON_HOLD
304 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
305 #else
306 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
307 #endif
309 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
311 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
312 parse_timeout },
314 #ifdef HAVE_LCD_BITMAP
315 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
316 #else
317 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
318 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
319 #endif
320 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
321 parse_progressbar },
323 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
325 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
326 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
327 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
328 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
330 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
331 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
332 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
333 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
335 #ifdef HAVE_TAGCACHE
336 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
337 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
338 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
339 #endif
341 #if CONFIG_CODEC == SWCODEC
342 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
343 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
344 #endif
346 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
347 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
349 #ifdef HAVE_LCD_BITMAP
350 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
351 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
353 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
355 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
356 parse_image_display },
358 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
359 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
360 #ifdef HAVE_ALBUMART
361 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
362 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
363 #endif
365 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
366 parse_viewport_display },
367 #ifdef HAVE_LCD_BITMAP
368 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
369 #endif
370 { WPS_NO_TOKEN, "V", 0, parse_viewport },
372 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
373 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
374 #endif
375 #endif
377 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
378 parse_setting_and_lang },
379 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
380 parse_setting_and_lang },
381 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
383 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
384 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
385 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
388 /* Recording Tokens */
389 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
390 #ifdef HAVE_RECORDING
391 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
392 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
393 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
394 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
395 #endif
396 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
397 /* the array MUST end with an empty string (first char is \0) */
401 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
402 * chains require the order to be kept.
404 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
406 if (*list == NULL)
407 *list = item;
408 else
410 struct skin_token_list *t = *list;
411 while (t->next)
412 t = t->next;
413 t->next = item;
417 /* traverse the image linked-list for an image */
418 #ifdef HAVE_LCD_BITMAP
419 struct gui_img* find_image(char label, struct wps_data *data)
421 struct skin_token_list *list = data->images;
422 while (list)
424 struct gui_img *img = (struct gui_img *)list->token->value.data;
425 if (img->label == label)
426 return img;
427 list = list->next;
429 return NULL;
431 #endif
433 /* traverse the viewport linked list for a viewport */
434 struct skin_viewport* find_viewport(char label, struct wps_data *data)
436 struct skin_token_list *list = data->viewports;
437 while (list)
439 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
440 if (vp->label == label)
441 return vp;
442 list = list->next;
444 return NULL;
448 /* create and init a new wpsll item.
449 * passing NULL to token will alloc a new one.
450 * You should only pass NULL for the token when the token type (table above)
451 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
453 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
454 void* token_data)
456 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
457 if (!token)
458 token = skin_buffer_alloc(sizeof(struct wps_token));
459 if (!llitem || !token)
460 return NULL;
461 llitem->next = NULL;
462 llitem->token = token;
463 if (token_data)
464 llitem->token->value.data = token_data;
465 return llitem;
468 /* Returns the number of chars that should be skipped to jump
469 immediately after the first eol, i.e. to the start of the next line */
470 static int skip_end_of_line(const char *wps_bufptr)
472 line_number++;
473 int skip = 0;
474 while(*(wps_bufptr + skip) != '\n')
475 skip++;
476 return ++skip;
479 /* Starts a new subline in the current line during parsing */
480 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
482 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
483 if (!subline)
484 return false;
486 subline->first_token_idx = curr_token;
487 subline->next = NULL;
489 subline->line_type = 0;
490 subline->time_mult = 0;
492 line->curr_subline->last_token_idx = curr_token-1;
493 line->curr_subline->next = subline;
494 line->curr_subline = subline;
495 return true;
498 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
500 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
501 struct skin_subline *subline = NULL;
502 if (!line)
503 return false;
505 /* init the subline */
506 subline = &line->sublines;
507 subline->first_token_idx = curr_token;
508 subline->next = NULL;
509 subline->line_type = 0;
510 subline->time_mult = 0;
512 /* init the new line */
513 line->curr_subline = &line->sublines;
514 line->next = NULL;
515 line->subline_expire_time = 0;
517 /* connect to curr_line and vp pointers.
518 * 1) close the previous lines subline
519 * 2) connect to vp pointer
520 * 3) connect to curr_line global pointer
522 if (curr_line)
524 curr_line->curr_subline->last_token_idx = curr_token - 1;
525 curr_line->next = line;
526 curr_line->curr_subline = NULL;
528 curr_line = line;
529 if (!vp->lines)
530 vp->lines = line;
531 return true;
534 #ifdef HAVE_LCD_BITMAP
536 static int parse_statusbar_enable(const char *wps_bufptr,
537 struct wps_token *token,
538 struct wps_data *wps_data)
540 (void)token; /* Kill warnings */
541 wps_data->wps_sb_tag = true;
542 wps_data->show_sb_on_wps = true;
543 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
544 viewport_set_defaults(&default_vp->vp, curr_screen);
545 return skip_end_of_line(wps_bufptr);
548 static int parse_statusbar_disable(const char *wps_bufptr,
549 struct wps_token *token,
550 struct wps_data *wps_data)
552 (void)token; /* Kill warnings */
553 wps_data->wps_sb_tag = true;
554 wps_data->show_sb_on_wps = false;
555 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
556 viewport_set_fullscreen(&default_vp->vp, curr_screen);
557 return skip_end_of_line(wps_bufptr);
560 static int get_image_id(int c)
562 if(c >= 'a' && c <= 'z')
563 return c - 'a';
564 else if(c >= 'A' && c <= 'Z')
565 return c - 'A' + 26;
566 else
567 return -1;
570 char *get_image_filename(const char *start, const char* bmpdir,
571 char *buf, int buf_size)
573 const char *end = strchr(start, '|');
575 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
577 buf = "\0";
578 return NULL;
581 int bmpdirlen = strlen(bmpdir);
583 strcpy(buf, bmpdir);
584 buf[bmpdirlen] = '/';
585 memcpy( &buf[bmpdirlen + 1], start, end - start);
586 buf[bmpdirlen + 1 + end - start] = 0;
588 return buf;
591 static int parse_image_display(const char *wps_bufptr,
592 struct wps_token *token,
593 struct wps_data *wps_data)
595 char label = wps_bufptr[0];
596 int subimage;
597 struct gui_img *img;;
599 /* sanity check */
600 img = find_image(label, wps_data);
601 if (!img)
603 token->value.i = label; /* so debug works */
604 return WPS_ERROR_INVALID_PARAM;
607 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
609 if (subimage >= img->num_subimages)
610 return WPS_ERROR_INVALID_PARAM;
612 /* Store sub-image number to display in high bits */
613 token->value.i = label | (subimage << 8);
614 return 2; /* We have consumed 2 bytes */
615 } else {
616 token->value.i = label;
617 return 1; /* We have consumed 1 byte */
621 static int parse_image_load(const char *wps_bufptr,
622 struct wps_token *token,
623 struct wps_data *wps_data)
625 const char *ptr = wps_bufptr;
626 const char *pos;
627 const char* filename;
628 const char* id;
629 const char *newline;
630 int x,y;
631 struct gui_img *img;
633 /* format: %x|n|filename.bmp|x|y|
634 or %xl|n|filename.bmp|x|y|
635 or %xl|n|filename.bmp|x|y|num_subimages|
638 if (*ptr != '|')
639 return WPS_ERROR_INVALID_PARAM;
641 ptr++;
643 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
644 return WPS_ERROR_INVALID_PARAM;
646 /* Check there is a terminating | */
647 if (*ptr != '|')
648 return WPS_ERROR_INVALID_PARAM;
650 /* check the image number and load state */
651 if(find_image(*id, wps_data))
653 /* Invalid image ID */
654 return WPS_ERROR_INVALID_PARAM;
656 img = skin_buffer_alloc(sizeof(struct gui_img));
657 if (!img)
658 return WPS_ERROR_INVALID_PARAM;
659 /* save a pointer to the filename */
660 img->bm.data = (char*)filename;
661 img->label = *id;
662 img->x = x;
663 img->y = y;
664 img->num_subimages = 1;
665 img->always_display = false;
667 /* save current viewport */
668 img->vp = &curr_vp->vp;
670 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
672 img->always_display = true;
674 else
676 /* Parse the (optional) number of sub-images */
677 ptr++;
678 newline = strchr(ptr, '\n');
679 pos = strchr(ptr, '|');
680 if (pos && pos < newline)
681 img->num_subimages = atoi(ptr);
683 if (img->num_subimages <= 0)
684 return WPS_ERROR_INVALID_PARAM;
686 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
687 if (!item)
688 return WPS_ERROR_INVALID_PARAM;
689 add_to_ll_chain(&wps_data->images, item);
691 /* Skip the rest of the line */
692 return skip_end_of_line(wps_bufptr);
695 static int font_ids[MAXUSERFONTS];
696 static int parse_font_load(const char *wps_bufptr,
697 struct wps_token *token, struct wps_data *wps_data)
699 (void)wps_data; (void)token;
700 const char *ptr = wps_bufptr;
701 int id;
702 char *filename, buf[MAX_PATH];
704 if (*ptr != '|')
705 return WPS_ERROR_INVALID_PARAM;
707 ptr++;
709 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
710 return WPS_ERROR_INVALID_PARAM;
712 /* Check there is a terminating | */
713 if (*ptr != '|')
714 return WPS_ERROR_INVALID_PARAM;
716 if (id <= FONT_UI || id >= MAXFONTS-1)
717 return WPS_ERROR_INVALID_PARAM;
718 id -= SYSTEMFONTCOUNT;
720 memcpy(buf, filename, ptr-filename);
721 buf[ptr-filename] = '\0';
722 font_ids[id] = skin_font_load(buf);
724 return font_ids[id] >= 0 ? skip_end_of_line(wps_bufptr) : WPS_ERROR_INVALID_PARAM;
728 static int parse_viewport_display(const char *wps_bufptr,
729 struct wps_token *token,
730 struct wps_data *wps_data)
732 (void)wps_data;
733 char letter = wps_bufptr[0];
735 if (letter < 'a' || letter > 'z')
737 /* invalid viewport tag */
738 return WPS_ERROR_INVALID_PARAM;
740 token->value.i = letter;
741 return 1;
744 #ifdef HAVE_LCD_BITMAP
745 static int parse_playlistview_text(struct playlistviewer *viewer,
746 enum info_line_type line, char* text)
748 int cur_string = 0;
749 const struct wps_tag *tag;
750 int taglen = 0;
751 const char *start = text;
752 if (*text != '|')
753 return -1;
754 text++;
755 viewer->lines[line].count = 0;
756 viewer->lines[line].scroll = false;
757 while (*text != '|')
759 if (*text == '%') /* it is a token of some type */
761 text++;
762 taglen = 0;
763 switch(*text)
765 case '%':
766 case '<':
767 case '|':
768 case '>':
769 case ';':
770 case '#':
771 /* escaped characters */
772 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
773 viewer->lines[line].strings[cur_string][0] = *text;
774 viewer->lines[line].strings[cur_string++][1] = '\0';
775 break;
776 default:
777 for (tag = all_tags;
778 strncmp(text, tag->name, strlen(tag->name)) != 0;
779 tag++) ;
780 /* %s isnt stored as a tag so manually check for it */
781 if (tag->type == WPS_NO_TOKEN)
783 if (!strncmp(tag->name, "s", 1))
785 viewer->lines[line].scroll = true;
786 taglen = 1;
789 else if (tag->type == WPS_TOKEN_UNKNOWN)
791 int i = 0;
792 /* just copy the string */
793 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
794 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
796 viewer->lines[line].strings[cur_string][i] = text[i];
797 i++;
799 viewer->lines[line].strings[cur_string][i] = '\0';
800 cur_string++;
801 taglen = i;
803 else
805 if (tag->parse_func)
807 /* unsupported tag, reject */
808 return -1;
810 taglen = strlen(tag->name);
811 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
813 text += taglen;
816 else
818 /* regular string */
819 int i = 0;
820 /* just copy the string */
821 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
822 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
824 viewer->lines[line].strings[cur_string][i] = text[i];
825 i++;
827 viewer->lines[line].strings[cur_string][i] = '\0';
828 cur_string++;
829 text += i;
832 return text - start;
836 static int parse_playlistview(const char *wps_bufptr,
837 struct wps_token *token, struct wps_data *wps_data)
839 (void)wps_data;
840 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
841 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
842 char *ptr = strchr(wps_bufptr, '|');
843 int length;
844 if (!viewer || !ptr)
845 return WPS_ERROR_INVALID_PARAM;
846 viewer->vp = &curr_vp->vp;
847 viewer->show_icons = true;
848 viewer->start_offset = atoi(ptr+1);
849 token->value.data = (void*)viewer;
850 ptr = strchr(ptr+1, '|');
851 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
852 if (length < 0)
853 return WPS_ERROR_INVALID_PARAM;
854 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
855 if (length < 0)
856 return WPS_ERROR_INVALID_PARAM;
858 return skip_end_of_line(wps_bufptr);
860 #endif
862 static int parse_viewport(const char *wps_bufptr,
863 struct wps_token *token,
864 struct wps_data *wps_data)
866 (void)token; /* Kill warnings */
867 const char *ptr = wps_bufptr;
869 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
871 /* check for the optional letter to signify its a hideable viewport */
872 /* %Vl|<label>|<rest of tags>| */
873 skin_vp->hidden_flags = 0;
874 skin_vp->label = VP_NO_LABEL;
875 skin_vp->pb = NULL;
876 skin_vp->lines = NULL;
877 if (curr_line)
879 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
880 - (wps_data->num_tokens > 0 ? 1 : 0);
883 curr_line = NULL;
884 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
885 return WPS_ERROR_INVALID_PARAM;
888 if (*ptr == 'i')
890 skin_vp->label = VP_INFO_LABEL;
891 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
892 ++ptr;
894 else if (*ptr == 'l')
896 if (*(ptr+1) == '|')
898 char label = *(ptr+2);
899 if (label >= 'a' && label <= 'z')
901 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
902 skin_vp->label = label;
904 else
905 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
906 ptr += 3;
909 if (*ptr != '|')
910 return WPS_ERROR_INVALID_PARAM;
912 ptr++;
913 struct viewport *vp = &skin_vp->vp;
914 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
915 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
916 return WPS_ERROR_INVALID_PARAM;
918 /* Check for trailing | */
919 if (*ptr != '|')
920 return WPS_ERROR_INVALID_PARAM;
922 if (follow_lang_direction && lang_is_rtl())
924 vp->flags |= VP_FLAG_ALIGN_RIGHT;
925 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
927 else
928 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
930 if (vp->font >= SYSTEMFONTCOUNT)
931 vp->font = font_ids[vp->font - SYSTEMFONTCOUNT];
933 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
934 if (!list)
935 return WPS_ERROR_INVALID_PARAM;
936 add_to_ll_chain(&wps_data->viewports, list);
937 curr_vp = skin_vp;
938 /* Skip the rest of the line */
939 return skip_end_of_line(wps_bufptr);
942 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
943 static int parse_image_special(const char *wps_bufptr,
944 struct wps_token *token,
945 struct wps_data *wps_data)
947 (void)wps_data; /* kill warning */
948 (void)token;
949 const char *pos = NULL;
950 const char *newline;
951 bool error = false;
953 pos = strchr(wps_bufptr + 1, '|');
954 newline = strchr(wps_bufptr, '\n');
956 error = (pos > newline);
959 #if LCD_DEPTH > 1
960 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
962 /* format: %X|filename.bmp| or %Xd */
963 if (*(wps_bufptr) == 'd')
965 wps_data->backdrop = NULL;
966 return skip_end_of_line(wps_bufptr);
968 else if (!error)
969 wps_data->backdrop = (char*)wps_bufptr + 1;
971 #endif
972 if (error)
973 return WPS_ERROR_INVALID_PARAM;
974 /* Skip the rest of the line */
975 return skip_end_of_line(wps_bufptr);
977 #endif
979 #endif /* HAVE_LCD_BITMAP */
981 static int parse_setting_and_lang(const char *wps_bufptr,
982 struct wps_token *token,
983 struct wps_data *wps_data)
985 /* NOTE: both the string validations that happen in here will
986 * automatically PASS on checkwps because its too hard to get
987 * settings_list.c and englinsh.lang built for it.
988 * If that ever changes remove the #ifndef __PCTOOL__'s here
990 (void)wps_data;
991 const char *ptr = wps_bufptr;
992 const char *end;
993 int i = 0;
994 char temp[64];
996 /* Find the setting's cfg_name */
997 if (*ptr != '|')
998 return WPS_ERROR_INVALID_PARAM;
999 ptr++;
1000 end = strchr(ptr,'|');
1001 if (!end)
1002 return WPS_ERROR_INVALID_PARAM;
1003 strlcpy(temp, ptr,end-ptr+1);
1005 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1007 #ifndef __PCTOOL__
1008 i = lang_english_to_id(temp);
1009 if (i < 0)
1010 return WPS_ERROR_INVALID_PARAM;
1011 #endif
1013 else
1015 /* Find the setting */
1016 for (i=0; i<nb_settings; i++)
1017 if (settings[i].cfg_name &&
1018 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1019 /* prevent matches on cfg_name prefixes */
1020 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1021 break;
1022 #ifndef __PCTOOL__
1023 if (i == nb_settings)
1024 return WPS_ERROR_INVALID_PARAM;
1025 #endif
1027 /* Store the setting number */
1028 token->value.i = i;
1030 /* Skip the rest of the line */
1031 return end-ptr+2;
1035 static int parse_dir_level(const char *wps_bufptr,
1036 struct wps_token *token,
1037 struct wps_data *wps_data)
1039 char val[] = { *wps_bufptr, '\0' };
1040 token->value.i = atoi(val);
1041 (void)wps_data; /* Kill warnings */
1042 return 1;
1045 static int parse_timeout(const char *wps_bufptr,
1046 struct wps_token *token,
1047 struct wps_data *wps_data)
1049 int skip = 0;
1050 int val = 0;
1051 bool have_point = false;
1052 bool have_tenth = false;
1054 (void)wps_data; /* Kill the warning */
1056 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1058 if (*wps_bufptr != '.')
1060 val *= 10;
1061 val += *wps_bufptr - '0';
1062 if (have_point)
1064 have_tenth = true;
1065 wps_bufptr++;
1066 skip++;
1067 break;
1070 else
1071 have_point = true;
1073 wps_bufptr++;
1074 skip++;
1077 if (have_tenth == false)
1078 val *= 10;
1080 if (val == 0 && skip == 0)
1082 /* decide what to do if no value was specified */
1083 switch (token->type)
1085 case WPS_TOKEN_SUBLINE_TIMEOUT:
1086 return -1;
1087 case WPS_TOKEN_BUTTON_VOLUME:
1088 val = 10;
1089 break;
1092 token->value.i = val;
1094 return skip;
1097 static int parse_progressbar(const char *wps_bufptr,
1098 struct wps_token *token,
1099 struct wps_data *wps_data)
1101 /* %pb or %pb|filename|x|y|width|height|
1102 using - for any of the params uses "sane" values */
1103 #ifdef HAVE_LCD_BITMAP
1104 enum {
1105 PB_FILENAME = 0,
1106 PB_X,
1107 PB_Y,
1108 PB_WIDTH,
1109 PB_HEIGHT
1111 const char *filename;
1112 int x, y, height, width;
1113 uint32_t set = 0;
1114 const char *ptr = wps_bufptr;
1115 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1116 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1118 if (!pb || !item)
1119 return WPS_ERROR_INVALID_PARAM;
1121 struct viewport *vp = &curr_vp->vp;
1122 #ifndef __PCTOOL__
1123 int font_height = font_get(vp->font)->height;
1124 #else
1125 int font_height = 8;
1126 #endif
1127 /* we need to know what line number (viewport relative) this pb is,
1128 * so count them... */
1129 int line_num = -1;
1130 struct skin_line *line = curr_vp->lines;
1131 while (line)
1133 line_num++;
1134 line = line->next;
1136 pb->have_bitmap_pb = false;
1137 pb->bm.data = NULL; /* no bitmap specified */
1138 pb->follow_lang_direction = follow_lang_direction > 0;
1140 if (*wps_bufptr != '|') /* regular old style */
1142 pb->x = 0;
1143 pb->width = vp->width;
1144 pb->height = SYSFONT_HEIGHT-2;
1145 pb->y = -line_num - 1; /* Will be computed during the rendering */
1147 curr_vp->pb = pb;
1148 add_to_ll_chain(&wps_data->progressbars, item);
1149 return 0;
1151 ptr = wps_bufptr + 1;
1153 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1154 &x, &y, &width, &height)))
1155 return WPS_ERROR_INVALID_PARAM;
1157 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1158 pb->bm.data = (char*)filename;
1160 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1161 pb->x = x;
1162 else
1163 pb->x = vp->x;
1165 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1167 /* A zero width causes a divide-by-zero error later, so reject it */
1168 if (width == 0)
1169 return WPS_ERROR_INVALID_PARAM;
1171 pb->width = width;
1173 else
1174 pb->width = vp->width - pb->x;
1176 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1178 /* A zero height makes no sense - reject it */
1179 if (height == 0)
1180 return WPS_ERROR_INVALID_PARAM;
1182 pb->height = height;
1184 else
1185 pb->height = font_height;
1187 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1188 pb->y = y;
1189 else
1190 pb->y = -line_num - 1; /* Will be computed during the rendering */
1192 curr_vp->pb = pb;
1193 add_to_ll_chain(&wps_data->progressbars, item);
1195 /* Skip the rest of the line */
1196 return skip_end_of_line(wps_bufptr)-1;
1197 #else
1198 (void)token;
1200 if (*(wps_bufptr-1) == 'f')
1201 wps_data->full_line_progressbar = true;
1202 else
1203 wps_data->full_line_progressbar = false;
1205 return 0;
1207 #endif
1210 #ifdef HAVE_ALBUMART
1211 static int parse_int(const char *newline, const char **_pos, int *num)
1213 *_pos = parse_list("d", NULL, '|', *_pos, num);
1215 return (!*_pos || *_pos > newline || **_pos != '|');
1218 static int parse_albumart_load(const char *wps_bufptr,
1219 struct wps_token *token,
1220 struct wps_data *wps_data)
1222 const char *_pos, *newline;
1223 bool parsing;
1224 struct dim dimensions;
1225 int albumart_slot;
1226 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1227 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1228 (void)token; /* silence warning */
1229 if (!aa)
1230 return skip_end_of_line(wps_bufptr);
1232 /* reset albumart info in wps */
1233 aa->width = -1;
1234 aa->height = -1;
1235 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1236 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1237 aa->vp = &curr_vp->vp;
1239 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1241 newline = strchr(wps_bufptr, '\n');
1243 _pos = wps_bufptr;
1245 if (*_pos != '|')
1246 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1248 ++_pos;
1250 /* initial validation and parsing of x component */
1251 if (parse_int(newline, &_pos, &aa->x))
1252 return WPS_ERROR_INVALID_PARAM;
1254 ++_pos;
1256 /* initial validation and parsing of y component */
1257 if (parse_int(newline, &_pos, &aa->y))
1258 return WPS_ERROR_INVALID_PARAM;
1260 /* parsing width field */
1261 parsing = true;
1262 while (parsing)
1264 /* apply each modifier in turn */
1265 ++_pos;
1266 switch (*_pos)
1268 case 'l':
1269 case 'L':
1270 case '+':
1271 if (swap_for_rtl)
1272 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1273 else
1274 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1275 break;
1276 case 'c':
1277 case 'C':
1278 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1279 break;
1280 case 'r':
1281 case 'R':
1282 case '-':
1283 if (swap_for_rtl)
1284 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1285 else
1286 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1287 break;
1288 case 'd':
1289 case 'D':
1290 case 'i':
1291 case 'I':
1292 case 's':
1293 case 'S':
1294 /* simply ignored */
1295 break;
1296 default:
1297 parsing = false;
1298 break;
1301 /* extract max width data */
1302 if (*_pos != '|')
1304 if (parse_int(newline, &_pos, &aa->width))
1305 return WPS_ERROR_INVALID_PARAM;
1308 /* parsing height field */
1309 parsing = true;
1310 while (parsing)
1312 /* apply each modifier in turn */
1313 ++_pos;
1314 switch (*_pos)
1316 case 't':
1317 case 'T':
1318 case '-':
1319 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1320 break;
1321 case 'c':
1322 case 'C':
1323 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1324 break;
1325 case 'b':
1326 case 'B':
1327 case '+':
1328 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1329 break;
1330 case 'd':
1331 case 'D':
1332 case 'i':
1333 case 'I':
1334 case 's':
1335 case 'S':
1336 /* simply ignored */
1337 break;
1338 default:
1339 parsing = false;
1340 break;
1343 /* extract max height data */
1344 if (*_pos != '|')
1346 if (parse_int(newline, &_pos, &aa->height))
1347 return WPS_ERROR_INVALID_PARAM;
1350 /* if we got here, we parsed everything ok .. ! */
1351 if (aa->width < 0)
1352 aa->width = 0;
1353 else if (aa->width > LCD_WIDTH)
1354 aa->width = LCD_WIDTH;
1356 if (aa->height < 0)
1357 aa->height = 0;
1358 else if (aa->height > LCD_HEIGHT)
1359 aa->height = LCD_HEIGHT;
1361 if (swap_for_rtl)
1362 aa->x = LCD_WIDTH - (aa->x + aa->width);
1364 aa->state = WPS_ALBUMART_LOAD;
1365 aa->draw = false;
1366 wps_data->albumart = aa;
1368 dimensions.width = aa->width;
1369 dimensions.height = aa->height;
1371 albumart_slot = playback_claim_aa_slot(&dimensions);
1373 if (0 <= albumart_slot)
1374 wps_data->playback_aa_slot = albumart_slot;
1376 /* Skip the rest of the line */
1377 return skip_end_of_line(wps_bufptr);
1380 static int parse_albumart_display(const char *wps_bufptr,
1381 struct wps_token *token,
1382 struct wps_data *wps_data)
1384 (void)wps_bufptr;
1385 struct wps_token *prev = token-1;
1386 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1388 token->type = WPS_TOKEN_ALBUMART_FOUND;
1390 else if (wps_data->albumart)
1392 wps_data->albumart->vp = &curr_vp->vp;
1394 #if 0
1395 /* the old code did this so keep it here for now...
1396 * this is to allow the posibility to showing the next tracks AA! */
1397 if (wps_bufptr+1 == 'n')
1398 return 1;
1399 #endif
1400 return 0;
1402 #endif /* HAVE_ALBUMART */
1404 #ifdef HAVE_TOUCHSCREEN
1406 struct touchaction {const char* s; int action;};
1407 static const struct touchaction touchactions[] = {
1408 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1409 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1410 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1411 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1412 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1413 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1414 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1415 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1417 static int parse_touchregion(const char *wps_bufptr,
1418 struct wps_token *token, struct wps_data *wps_data)
1420 (void)token;
1421 unsigned i, imax;
1422 struct touchregion *region = NULL;
1423 const char *ptr = wps_bufptr;
1424 const char *action;
1425 const char pb_string[] = "progressbar";
1426 const char vol_string[] = "volume";
1427 int x,y,w,h;
1429 /* format: %T|x|y|width|height|action|
1430 * if action starts with & the area must be held to happen
1431 * action is one of:
1432 * play - play/pause playback
1433 * stop - stop playback, exit the wps
1434 * prev - prev track
1435 * next - next track
1436 * ffwd - seek forward
1437 * rwd - seek backwards
1438 * menu - go back to the main menu
1439 * browse - go back to the file/db browser
1440 * shuffle - toggle shuffle mode
1441 * repmode - cycle the repeat mode
1442 * quickscreen - go into the quickscreen
1443 * contextmenu - open the context menu
1444 * playlist - go into the playlist
1445 * pitch - go into the pitchscreen
1446 * volup - increase volume by one step
1447 * voldown - decrease volume by one step
1451 if (*ptr != '|')
1452 return WPS_ERROR_INVALID_PARAM;
1453 ptr++;
1455 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1456 return WPS_ERROR_INVALID_PARAM;
1458 /* Check there is a terminating | */
1459 if (*ptr != '|')
1460 return WPS_ERROR_INVALID_PARAM;
1462 region = skin_buffer_alloc(sizeof(struct touchregion));
1463 if (!region)
1464 return WPS_ERROR_INVALID_PARAM;
1466 /* should probably do some bounds checking here with the viewport... but later */
1467 region->action = ACTION_NONE;
1468 region->x = x;
1469 region->y = y;
1470 region->width = w;
1471 region->height = h;
1472 region->wvp = curr_vp;
1474 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1475 && *(action + sizeof(pb_string)-1) == '|')
1476 region->type = WPS_TOUCHREGION_SCROLLBAR;
1477 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1478 && *(action + sizeof(vol_string)-1) == '|')
1479 region->type = WPS_TOUCHREGION_VOLUME;
1480 else
1482 region->type = WPS_TOUCHREGION_ACTION;
1484 if (*action == '&')
1486 action++;
1487 region->repeat = true;
1489 else
1490 region->repeat = false;
1492 i = 0;
1493 imax = ARRAYLEN(touchactions);
1494 while ((region->action == ACTION_NONE) &&
1495 (i < imax))
1497 /* try to match with one of our touchregion screens */
1498 int len = strlen(touchactions[i].s);
1499 if (!strncmp(touchactions[i].s, action, len)
1500 && *(action+len) == '|')
1501 region->action = touchactions[i].action;
1502 i++;
1504 if (region->action == ACTION_NONE)
1505 return WPS_ERROR_INVALID_PARAM;
1507 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1508 if (!item)
1509 return WPS_ERROR_INVALID_PARAM;
1510 add_to_ll_chain(&wps_data->touchregions, item);
1511 return skip_end_of_line(wps_bufptr);
1513 #endif
1515 /* Parse a generic token from the given string. Return the length read */
1516 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1518 int skip = 0, taglen = 0, ret;
1519 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1520 const struct wps_tag *tag;
1521 memset(token, 0, sizeof(*token));
1523 switch(*wps_bufptr)
1526 case '%':
1527 case '<':
1528 case '|':
1529 case '>':
1530 case ';':
1531 case '#':
1532 /* escaped characters */
1533 token->type = WPS_TOKEN_CHARACTER;
1534 token->value.c = *wps_bufptr;
1535 taglen = 1;
1536 wps_data->num_tokens++;
1537 break;
1539 case '?':
1540 /* conditional tag */
1541 token->type = WPS_TOKEN_CONDITIONAL;
1542 level++;
1543 condindex[level] = wps_data->num_tokens;
1544 numoptions[level] = 1;
1545 wps_data->num_tokens++;
1546 ret = parse_token(wps_bufptr + 1, wps_data);
1547 if (ret < 0) return ret;
1548 taglen = 1 + ret;
1549 break;
1551 default:
1552 /* find what tag we have */
1553 for (tag = all_tags;
1554 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1555 tag++) ;
1557 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1558 token->type = tag->type;
1559 curr_line->curr_subline->line_type |= tag->refresh_type;
1561 /* if the tag has a special parsing function, we call it */
1562 if (tag->parse_func)
1564 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1565 if (ret < 0) return ret;
1566 skip += ret;
1569 /* Some tags we don't want to save as tokens */
1570 if (tag->type == WPS_NO_TOKEN)
1571 break;
1573 /* tags that start with 'F', 'I' or 'D' are for the next file */
1574 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1575 *(tag->name) == 'D')
1576 token->next = true;
1578 wps_data->num_tokens++;
1579 break;
1582 skip += taglen;
1583 return skip;
1588 * Returns the number of bytes to skip the buf pointer to access the false
1589 * branch in a _binary_ conditional
1591 * That is:
1592 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1593 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1595 * depending on the features of a target it's not called from check_feature_tag,
1596 * hence the __attribute__ or it issues compiler warnings
1600 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1601 static int find_false_branch(const char *wps_bufptr)
1603 const char *buf = wps_bufptr;
1604 /* wps_bufptr is after the opening '<', hence level = 1*/
1605 int level = 1;
1606 char ch;
1609 ch = *buf;
1610 if (ch == '%')
1611 { /* filter out the characters we check later if they're printed
1612 * as literals */
1613 ch = *(++buf);
1614 if (ch == '<' || ch == '>' || ch == '|')
1615 continue;
1616 /* else: some tags/printed literals we skip over */
1618 else if (ch == '<') /* nested conditional */
1619 level++;
1620 else if (ch == '>')
1621 { /* closed our or a nested conditional,
1622 * do NOT skip over the '>' so that wps_parse() sees it for closing
1623 * if it is the closing one for our conditional */
1624 level--;
1626 else if (ch == '|' && level == 1)
1627 { /* we found our separator, point before and get out */
1628 break;
1630 /* if level is 0, we don't have a false branch */
1631 } while (level > 0 && *(++buf));
1633 return buf - wps_bufptr;
1637 * returns the number of bytes to get the appropriate branch of a binary
1638 * conditional
1640 * That means:
1641 * - if a feature is available, it returns 0 to not skip anything
1642 * - if the feature is not available, skip to the false branch and don't
1643 * parse the true branch at all
1645 * */
1646 static int check_feature_tag(const char *wps_bufptr, const int type)
1648 (void)wps_bufptr;
1649 switch (type)
1651 case WPS_TOKEN_RTC_PRESENT:
1652 #if CONFIG_RTC
1653 return 0;
1654 #else
1655 return find_false_branch(wps_bufptr);
1656 #endif
1657 case WPS_TOKEN_HAVE_RECORDING:
1658 #ifdef HAVE_RECORDING
1659 return 0;
1660 #else
1661 return find_false_branch(wps_bufptr);
1662 #endif
1663 default: /* not a tag we care about, just don't skip */
1664 return 0;
1669 /* Parses the WPS.
1670 data is the pointer to the structure where the parsed WPS should be stored.
1671 It is initialised.
1672 wps_bufptr points to the string containing the WPS tags */
1673 #define TOKEN_BLOCK_SIZE 128
1674 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1676 if (!data || !wps_bufptr || !*wps_bufptr)
1677 return false;
1678 enum wps_parse_error fail = PARSE_OK;
1679 int ret;
1680 int max_tokens = TOKEN_BLOCK_SIZE;
1681 size_t buf_free = 0;
1682 line_number = 0;
1683 level = -1;
1685 /* allocate enough RAM for a reasonable skin, grow as needed.
1686 * Free any used RAM before loading the images to be 100% RAM efficient */
1687 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1688 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1689 return false;
1690 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1691 data->num_tokens = 0;
1693 #if LCD_DEPTH > 1
1694 /* Backdrop defaults to the setting unless %X is used, so set it now */
1695 if (global_settings.backdrop_file[0])
1697 data->backdrop = "-";
1699 #endif
1701 while (*wps_bufptr && !fail)
1703 if (follow_lang_direction)
1704 follow_lang_direction--;
1705 /* first make sure there is enough room for tokens */
1706 if (max_tokens <= data->num_tokens + 5)
1708 int extra_tokens = TOKEN_BLOCK_SIZE;
1709 size_t needed = extra_tokens * sizeof(struct wps_token);
1710 /* do some smarts here to grow the array a bit */
1711 if (skin_buffer_freespace() < needed)
1713 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1714 break;
1716 skin_buffer_increment(needed, false);
1717 max_tokens += extra_tokens;
1720 switch(*wps_bufptr++)
1723 /* Regular tag */
1724 case '%':
1725 if ((ret = parse_token(wps_bufptr, data)) < 0)
1727 fail = PARSE_FAIL_COND_INVALID_PARAM;
1728 break;
1730 else if (level >= WPS_MAX_COND_LEVEL - 1)
1732 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1733 break;
1735 wps_bufptr += ret;
1736 break;
1738 /* Alternating sublines separator */
1739 case ';':
1740 if (level >= 0) /* there are unclosed conditionals */
1742 fail = PARSE_FAIL_UNCLOSED_COND;
1743 break;
1746 if (!skin_start_new_subline(curr_line, data->num_tokens))
1747 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1749 break;
1751 /* Conditional list start */
1752 case '<':
1753 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1755 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1756 break;
1758 wps_bufptr += check_feature_tag(wps_bufptr,
1759 data->tokens[data->num_tokens-1].type);
1760 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1761 lastcond[level] = data->num_tokens++;
1762 break;
1764 /* Conditional list end */
1765 case '>':
1766 if (level < 0) /* not in a conditional, invalid char */
1768 fail = PARSE_FAIL_INVALID_CHAR;
1769 break;
1772 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1773 if (lastcond[level])
1774 data->tokens[lastcond[level]].value.i = data->num_tokens;
1775 else
1777 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1778 break;
1781 lastcond[level] = 0;
1782 data->num_tokens++;
1783 data->tokens[condindex[level]].value.i = numoptions[level];
1784 level--;
1785 break;
1787 /* Conditional list option */
1788 case '|':
1789 if (level < 0) /* not in a conditional, invalid char */
1791 fail = PARSE_FAIL_INVALID_CHAR;
1792 break;
1795 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1796 if (lastcond[level])
1797 data->tokens[lastcond[level]].value.i = data->num_tokens;
1798 else
1800 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1801 break;
1804 lastcond[level] = data->num_tokens;
1805 numoptions[level]++;
1806 data->num_tokens++;
1807 break;
1809 /* Comment */
1810 case '#':
1811 if (level >= 0) /* there are unclosed conditionals */
1813 fail = PARSE_FAIL_UNCLOSED_COND;
1814 break;
1817 wps_bufptr += skip_end_of_line(wps_bufptr);
1818 break;
1820 /* End of this line */
1821 case '\n':
1822 if (level >= 0) /* there are unclosed conditionals */
1824 fail = PARSE_FAIL_UNCLOSED_COND;
1825 break;
1827 /* add a new token for the \n so empty lines are correct */
1828 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1829 data->tokens[data->num_tokens].value.c = '\n';
1830 data->tokens[data->num_tokens].next = false;
1831 data->num_tokens++;
1833 if (!skin_start_new_line(curr_vp, data->num_tokens))
1835 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1836 break;
1838 line_number++;
1840 break;
1842 /* String */
1843 default:
1845 unsigned int len = 1;
1846 const char *string_start = wps_bufptr - 1;
1848 /* find the length of the string */
1849 while (*wps_bufptr && *wps_bufptr != '#' &&
1850 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1851 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1852 *wps_bufptr != '|' && *wps_bufptr != '\n')
1854 wps_bufptr++;
1855 len++;
1858 /* look if we already have that string */
1859 char *str;
1860 bool found = false;
1861 struct skin_token_list *list = data->strings;
1862 while (list)
1864 str = (char*)list->token->value.data;
1865 found = (strlen(str) == len &&
1866 strncmp(string_start, str, len) == 0);
1867 if (found)
1868 break; /* break here because the list item is
1869 used if its found */
1870 list = list->next;
1872 /* If a matching string is found, found is true and i is
1873 the index of the string. If not, found is false */
1875 if (!found)
1877 /* new string */
1878 str = (char*)skin_buffer_alloc(len+1);
1879 if (!str)
1881 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1882 break;
1884 strlcpy(str, string_start, len+1);
1885 struct skin_token_list *item =
1886 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1887 if(!item)
1889 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1890 break;
1892 add_to_ll_chain(&data->strings, item);
1894 else
1896 /* another occurrence of an existing string */
1897 data->tokens[data->num_tokens].value.data = list->token->value.data;
1899 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1900 data->num_tokens++;
1902 break;
1906 if (!fail && level >= 0) /* there are unclosed conditionals */
1907 fail = PARSE_FAIL_UNCLOSED_COND;
1909 if (*wps_bufptr && !fail)
1910 /* one of the limits of the while loop was exceeded */
1911 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1913 /* Success! */
1914 curr_line->curr_subline->last_token_idx = data->num_tokens;
1915 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1916 /* freeup unused tokens */
1917 skin_buffer_free_from_front(sizeof(struct wps_token)
1918 * (max_tokens - data->num_tokens));
1920 #if defined(DEBUG) || defined(SIMULATOR)
1921 if (debug)
1922 print_debug_info(data, fail, line_number);
1923 #else
1924 (void)debug;
1925 #endif
1927 return (fail == 0);
1932 * initial setup of wps_data; does reset everything
1933 * except fields which need to survive, i.e.
1936 static void skin_data_reset(struct wps_data *wps_data)
1938 #ifdef HAVE_LCD_BITMAP
1939 wps_data->images = NULL;
1940 wps_data->progressbars = NULL;
1941 #endif
1942 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1943 wps_data->backdrop = NULL;
1944 #endif
1945 #ifdef HAVE_TOUCHSCREEN
1946 wps_data->touchregions = NULL;
1947 #endif
1948 wps_data->viewports = NULL;
1949 wps_data->strings = NULL;
1950 #ifdef HAVE_ALBUMART
1951 wps_data->albumart = NULL;
1952 if (wps_data->playback_aa_slot >= 0)
1954 playback_release_aa_slot(wps_data->playback_aa_slot);
1955 wps_data->playback_aa_slot = -1;
1957 #endif
1958 wps_data->tokens = NULL;
1959 wps_data->num_tokens = 0;
1961 #ifdef HAVE_LCD_BITMAP
1962 wps_data->peak_meter_enabled = false;
1963 wps_data->wps_sb_tag = false;
1964 wps_data->show_sb_on_wps = false;
1965 #else /* HAVE_LCD_CHARCELLS */
1966 /* progress bars */
1967 int i;
1968 for (i = 0; i < 8; i++)
1970 wps_data->wps_progress_pat[i] = 0;
1972 wps_data->full_line_progressbar = false;
1973 #endif
1974 wps_data->wps_loaded = false;
1977 #ifdef HAVE_LCD_BITMAP
1978 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1980 (void)wps_data; /* only needed for remote targets */
1981 bool loaded = false;
1982 char img_path[MAX_PATH];
1983 get_image_filename(bitmap->data, bmpdir,
1984 img_path, sizeof(img_path));
1986 /* load the image */
1987 int format;
1988 #ifdef HAVE_REMOTE_LCD
1989 if (curr_screen == SCREEN_REMOTE)
1990 format = FORMAT_ANY|FORMAT_REMOTE;
1991 else
1992 #endif
1993 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1995 size_t max_buf;
1996 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1997 bitmap->data = imgbuf;
1998 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2000 if (ret > 0)
2002 skin_buffer_increment(ret, true);
2003 loaded = true;
2005 else
2007 /* Abort if we can't load an image */
2008 loaded = false;
2010 return loaded;
2013 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2015 struct skin_token_list *list;
2016 /* do the progressbars */
2017 list = wps_data->progressbars;
2018 while (list)
2020 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2021 if (pb->bm.data)
2023 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2025 list = list->next;
2027 /* regular images */
2028 list = wps_data->images;
2029 while (list)
2031 struct gui_img *img = (struct gui_img*)list->token->value.data;
2032 if (img->bm.data)
2034 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2035 if (img->loaded)
2036 img->subimage_height = img->bm.height / img->num_subimages;
2038 list = list->next;
2041 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2042 /* Backdrop load scheme:
2043 * 1) %X|filename|
2044 * 2) load the backdrop from settings
2046 if (wps_data->backdrop)
2048 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2049 bmpdir, curr_screen);
2051 #endif /* has backdrop support */
2053 /* If we got here, everything was OK */
2054 return true;
2057 #endif /* HAVE_LCD_BITMAP */
2059 /* to setup up the wps-data from a format-buffer (isfile = false)
2060 from a (wps-)file (isfile = true)*/
2061 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2062 const char *buf, bool isfile)
2065 if (!wps_data || !buf)
2066 return false;
2067 #ifdef HAVE_ALBUMART
2068 int status;
2069 struct mp3entry *curtrack;
2070 long offset;
2071 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2072 if (wps_data->albumart)
2074 old_aa.state = wps_data->albumart->state;
2075 old_aa.height = wps_data->albumart->height;
2076 old_aa.width = wps_data->albumart->width;
2078 #endif
2080 skin_data_reset(wps_data);
2081 curr_screen = screen;
2083 /* alloc default viewport, will be fixed up later */
2084 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2085 if (!curr_vp)
2086 return false;
2087 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2088 if (!list)
2089 return false;
2090 add_to_ll_chain(&wps_data->viewports, list);
2093 /* Initialise the first (default) viewport */
2094 curr_vp->label = VP_DEFAULT_LABEL;
2095 curr_vp->pb = NULL;
2096 curr_vp->hidden_flags = 0;
2097 curr_vp->lines = NULL;
2099 viewport_set_defaults(&curr_vp->vp, screen);
2101 curr_line = NULL;
2102 if (!skin_start_new_line(curr_vp, 0))
2103 return false;
2105 if (!isfile)
2107 if (wps_parse(wps_data, buf, false))
2109 #ifdef HAVE_LCD_BITMAP
2110 /* load the backdrop */
2111 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2112 skin_data_reset(wps_data);
2113 return false;
2115 #endif
2116 return true;
2118 return false;
2120 else
2122 int fd = open_utf8(buf, O_RDONLY);
2124 if (fd < 0)
2125 return false;
2127 /* get buffer space from the plugin buffer */
2128 size_t buffersize = 0;
2129 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2131 if (!wps_buffer)
2132 return false;
2134 /* copy the file's content to the buffer for parsing,
2135 ensuring that every line ends with a newline char. */
2136 unsigned int start = 0;
2137 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2139 start += strlen(wps_buffer + start);
2140 if (start < buffersize - 1)
2142 wps_buffer[start++] = '\n';
2143 wps_buffer[start] = 0;
2147 close(fd);
2149 if (start <= 0)
2150 return false;
2152 /* parse the WPS source */
2153 if (!wps_parse(wps_data, wps_buffer, true)) {
2154 skin_data_reset(wps_data);
2155 return false;
2158 wps_data->wps_loaded = true;
2160 #ifdef HAVE_LCD_BITMAP
2161 /* get the bitmap dir */
2162 char bmpdir[MAX_PATH];
2163 char *dot = strrchr(buf, '.');
2165 strlcpy(bmpdir, buf, dot - buf + 1);
2167 /* load the bitmaps that were found by the parsing */
2168 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2169 skin_data_reset(wps_data);
2170 wps_data->wps_loaded = false;
2171 return false;
2173 #endif
2174 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2175 status = audio_status();
2176 if (status & AUDIO_STATUS_PLAY)
2178 struct skin_albumart *aa = wps_data->albumart;
2179 if (aa && ((aa->state && !old_aa.state) ||
2180 (aa->state &&
2181 (((old_aa.height != aa->height) ||
2182 (old_aa.width != aa->width))))))
2184 curtrack = audio_current_track();
2185 offset = curtrack->offset;
2186 audio_stop();
2187 if (!(status & AUDIO_STATUS_PAUSE))
2188 audio_play(offset);
2191 #endif
2192 #if defined(DEBUG) || defined(SIMULATOR)
2193 debug_skin_usage();
2194 #endif
2195 return true;