Fix the mechanism to fail to parse skins if images fail to load and fix having no...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob4363a293c86d5105f97ea28d27eabcdd539e8232
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 /* this array acts as a simple mapping between the id the user uses for a font
696 * and the id the font actually gets from the font loader.
697 * font id 2 is always the first skin font (regardless of how many screens */
698 static int font_ids[MAXUSERFONTS];
699 static int parse_font_load(const char *wps_bufptr,
700 struct wps_token *token, struct wps_data *wps_data)
702 (void)wps_data; (void)token;
703 const char *ptr = wps_bufptr;
704 int id;
705 char *filename, buf[MAX_PATH];
707 if (*ptr != '|')
708 return WPS_ERROR_INVALID_PARAM;
710 ptr++;
712 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
713 return WPS_ERROR_INVALID_PARAM;
715 /* Check there is a terminating | */
716 if (*ptr != '|')
717 return WPS_ERROR_INVALID_PARAM;
719 if (id <= FONT_UI || id >= MAXFONTS-1)
720 return WPS_ERROR_INVALID_PARAM;
721 id -= FONT_UI;
723 memcpy(buf, filename, ptr-filename);
724 buf[ptr-filename] = '\0';
725 font_ids[id] = skin_font_load(buf);
727 return font_ids[id] >= 0 ? skip_end_of_line(wps_bufptr) : WPS_ERROR_INVALID_PARAM;
731 static int parse_viewport_display(const char *wps_bufptr,
732 struct wps_token *token,
733 struct wps_data *wps_data)
735 (void)wps_data;
736 char letter = wps_bufptr[0];
738 if (letter < 'a' || letter > 'z')
740 /* invalid viewport tag */
741 return WPS_ERROR_INVALID_PARAM;
743 token->value.i = letter;
744 return 1;
747 #ifdef HAVE_LCD_BITMAP
748 static int parse_playlistview_text(struct playlistviewer *viewer,
749 enum info_line_type line, char* text)
751 int cur_string = 0;
752 const struct wps_tag *tag;
753 int taglen = 0;
754 const char *start = text;
755 if (*text != '|')
756 return -1;
757 text++;
758 viewer->lines[line].count = 0;
759 viewer->lines[line].scroll = false;
760 while (*text != '|')
762 if (*text == '%') /* it is a token of some type */
764 text++;
765 taglen = 0;
766 switch(*text)
768 case '%':
769 case '<':
770 case '|':
771 case '>':
772 case ';':
773 case '#':
774 /* escaped characters */
775 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
776 viewer->lines[line].strings[cur_string][0] = *text;
777 viewer->lines[line].strings[cur_string++][1] = '\0';
778 break;
779 default:
780 for (tag = all_tags;
781 strncmp(text, tag->name, strlen(tag->name)) != 0;
782 tag++) ;
783 /* %s isnt stored as a tag so manually check for it */
784 if (tag->type == WPS_NO_TOKEN)
786 if (!strncmp(tag->name, "s", 1))
788 viewer->lines[line].scroll = true;
789 taglen = 1;
792 else if (tag->type == WPS_TOKEN_UNKNOWN)
794 int i = 0;
795 /* just copy the string */
796 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
797 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
799 viewer->lines[line].strings[cur_string][i] = text[i];
800 i++;
802 viewer->lines[line].strings[cur_string][i] = '\0';
803 cur_string++;
804 taglen = i;
806 else
808 if (tag->parse_func)
810 /* unsupported tag, reject */
811 return -1;
813 taglen = strlen(tag->name);
814 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
816 text += taglen;
819 else
821 /* regular string */
822 int i = 0;
823 /* just copy the string */
824 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
825 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
827 viewer->lines[line].strings[cur_string][i] = text[i];
828 i++;
830 viewer->lines[line].strings[cur_string][i] = '\0';
831 cur_string++;
832 text += i;
835 return text - start;
839 static int parse_playlistview(const char *wps_bufptr,
840 struct wps_token *token, struct wps_data *wps_data)
842 (void)wps_data;
843 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
844 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
845 char *ptr = strchr(wps_bufptr, '|');
846 int length;
847 if (!viewer || !ptr)
848 return WPS_ERROR_INVALID_PARAM;
849 viewer->vp = &curr_vp->vp;
850 viewer->show_icons = true;
851 viewer->start_offset = atoi(ptr+1);
852 token->value.data = (void*)viewer;
853 ptr = strchr(ptr+1, '|');
854 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
855 if (length < 0)
856 return WPS_ERROR_INVALID_PARAM;
857 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
858 if (length < 0)
859 return WPS_ERROR_INVALID_PARAM;
861 return skip_end_of_line(wps_bufptr);
863 #endif
865 static int parse_viewport(const char *wps_bufptr,
866 struct wps_token *token,
867 struct wps_data *wps_data)
869 (void)token; /* Kill warnings */
870 const char *ptr = wps_bufptr;
872 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
874 /* check for the optional letter to signify its a hideable viewport */
875 /* %Vl|<label>|<rest of tags>| */
876 skin_vp->hidden_flags = 0;
877 skin_vp->label = VP_NO_LABEL;
878 skin_vp->pb = NULL;
879 skin_vp->lines = NULL;
880 if (curr_line)
882 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
883 - (wps_data->num_tokens > 0 ? 1 : 0);
886 curr_line = NULL;
887 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
888 return WPS_ERROR_INVALID_PARAM;
891 if (*ptr == 'i')
893 skin_vp->label = VP_INFO_LABEL;
894 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
895 ++ptr;
897 else if (*ptr == 'l')
899 if (*(ptr+1) == '|')
901 char label = *(ptr+2);
902 if (label >= 'a' && label <= 'z')
904 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
905 skin_vp->label = label;
907 else
908 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
909 ptr += 3;
912 if (*ptr != '|')
913 return WPS_ERROR_INVALID_PARAM;
915 ptr++;
916 struct viewport *vp = &skin_vp->vp;
917 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
918 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
919 return WPS_ERROR_INVALID_PARAM;
921 /* Check for trailing | */
922 if (*ptr != '|')
923 return WPS_ERROR_INVALID_PARAM;
925 if (follow_lang_direction && lang_is_rtl())
927 vp->flags |= VP_FLAG_ALIGN_RIGHT;
928 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
930 else
931 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
933 #ifdef HAVE_REMOTE_LCD
934 if (vp->font == FONT_UI && curr_screen == SCREEN_REMOTE)
935 vp->font = FONT_UI_REMOTE;
936 else
937 #endif
938 if (vp->font > FONT_UI)
939 vp->font = font_ids[vp->font - FONT_UI];
941 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
942 if (!list)
943 return WPS_ERROR_INVALID_PARAM;
944 add_to_ll_chain(&wps_data->viewports, list);
945 curr_vp = skin_vp;
946 /* Skip the rest of the line */
947 return skip_end_of_line(wps_bufptr);
950 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
951 static int parse_image_special(const char *wps_bufptr,
952 struct wps_token *token,
953 struct wps_data *wps_data)
955 (void)wps_data; /* kill warning */
956 (void)token;
957 const char *pos = NULL;
958 const char *newline;
959 bool error = false;
961 pos = strchr(wps_bufptr + 1, '|');
962 newline = strchr(wps_bufptr, '\n');
964 error = (pos > newline);
967 #if LCD_DEPTH > 1
968 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
970 /* format: %X|filename.bmp| or %Xd */
971 if (*(wps_bufptr) == 'd')
973 wps_data->backdrop = NULL;
974 return skip_end_of_line(wps_bufptr);
976 else if (!error)
977 wps_data->backdrop = (char*)wps_bufptr + 1;
979 #endif
980 if (error)
981 return WPS_ERROR_INVALID_PARAM;
982 /* Skip the rest of the line */
983 return skip_end_of_line(wps_bufptr);
985 #endif
987 #endif /* HAVE_LCD_BITMAP */
989 static int parse_setting_and_lang(const char *wps_bufptr,
990 struct wps_token *token,
991 struct wps_data *wps_data)
993 /* NOTE: both the string validations that happen in here will
994 * automatically PASS on checkwps because its too hard to get
995 * settings_list.c and englinsh.lang built for it.
996 * If that ever changes remove the #ifndef __PCTOOL__'s here
998 (void)wps_data;
999 const char *ptr = wps_bufptr;
1000 const char *end;
1001 int i = 0;
1002 char temp[64];
1004 /* Find the setting's cfg_name */
1005 if (*ptr != '|')
1006 return WPS_ERROR_INVALID_PARAM;
1007 ptr++;
1008 end = strchr(ptr,'|');
1009 if (!end)
1010 return WPS_ERROR_INVALID_PARAM;
1011 strlcpy(temp, ptr,end-ptr+1);
1013 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1015 #ifndef __PCTOOL__
1016 i = lang_english_to_id(temp);
1017 if (i < 0)
1018 return WPS_ERROR_INVALID_PARAM;
1019 #endif
1021 else
1023 /* Find the setting */
1024 for (i=0; i<nb_settings; i++)
1025 if (settings[i].cfg_name &&
1026 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1027 /* prevent matches on cfg_name prefixes */
1028 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1029 break;
1030 #ifndef __PCTOOL__
1031 if (i == nb_settings)
1032 return WPS_ERROR_INVALID_PARAM;
1033 #endif
1035 /* Store the setting number */
1036 token->value.i = i;
1038 /* Skip the rest of the line */
1039 return end-ptr+2;
1043 static int parse_dir_level(const char *wps_bufptr,
1044 struct wps_token *token,
1045 struct wps_data *wps_data)
1047 char val[] = { *wps_bufptr, '\0' };
1048 token->value.i = atoi(val);
1049 (void)wps_data; /* Kill warnings */
1050 return 1;
1053 static int parse_timeout(const char *wps_bufptr,
1054 struct wps_token *token,
1055 struct wps_data *wps_data)
1057 int skip = 0;
1058 int val = 0;
1059 bool have_point = false;
1060 bool have_tenth = false;
1062 (void)wps_data; /* Kill the warning */
1064 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1066 if (*wps_bufptr != '.')
1068 val *= 10;
1069 val += *wps_bufptr - '0';
1070 if (have_point)
1072 have_tenth = true;
1073 wps_bufptr++;
1074 skip++;
1075 break;
1078 else
1079 have_point = true;
1081 wps_bufptr++;
1082 skip++;
1085 if (have_tenth == false)
1086 val *= 10;
1088 if (val == 0 && skip == 0)
1090 /* decide what to do if no value was specified */
1091 switch (token->type)
1093 case WPS_TOKEN_SUBLINE_TIMEOUT:
1094 return -1;
1095 case WPS_TOKEN_BUTTON_VOLUME:
1096 val = 10;
1097 break;
1100 token->value.i = val;
1102 return skip;
1105 static int parse_progressbar(const char *wps_bufptr,
1106 struct wps_token *token,
1107 struct wps_data *wps_data)
1109 /* %pb or %pb|filename|x|y|width|height|
1110 using - for any of the params uses "sane" values */
1111 #ifdef HAVE_LCD_BITMAP
1112 enum {
1113 PB_FILENAME = 0,
1114 PB_X,
1115 PB_Y,
1116 PB_WIDTH,
1117 PB_HEIGHT
1119 const char *filename;
1120 int x, y, height, width;
1121 uint32_t set = 0;
1122 const char *ptr = wps_bufptr;
1123 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1124 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1126 if (!pb || !item)
1127 return WPS_ERROR_INVALID_PARAM;
1129 struct viewport *vp = &curr_vp->vp;
1130 #ifndef __PCTOOL__
1131 int font_height = font_get(vp->font)->height;
1132 #else
1133 int font_height = 8;
1134 #endif
1135 /* we need to know what line number (viewport relative) this pb is,
1136 * so count them... */
1137 int line_num = -1;
1138 struct skin_line *line = curr_vp->lines;
1139 while (line)
1141 line_num++;
1142 line = line->next;
1144 pb->have_bitmap_pb = false;
1145 pb->bm.data = NULL; /* no bitmap specified */
1146 pb->follow_lang_direction = follow_lang_direction > 0;
1148 if (*wps_bufptr != '|') /* regular old style */
1150 pb->x = 0;
1151 pb->width = vp->width;
1152 pb->height = SYSFONT_HEIGHT-2;
1153 pb->y = -line_num - 1; /* Will be computed during the rendering */
1155 curr_vp->pb = pb;
1156 add_to_ll_chain(&wps_data->progressbars, item);
1157 return 0;
1159 ptr = wps_bufptr + 1;
1161 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1162 &x, &y, &width, &height)))
1163 return WPS_ERROR_INVALID_PARAM;
1165 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1166 pb->bm.data = (char*)filename;
1168 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1169 pb->x = x;
1170 else
1171 pb->x = vp->x;
1173 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1175 /* A zero width causes a divide-by-zero error later, so reject it */
1176 if (width == 0)
1177 return WPS_ERROR_INVALID_PARAM;
1179 pb->width = width;
1181 else
1182 pb->width = vp->width - pb->x;
1184 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1186 /* A zero height makes no sense - reject it */
1187 if (height == 0)
1188 return WPS_ERROR_INVALID_PARAM;
1190 pb->height = height;
1192 else
1193 pb->height = font_height;
1195 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1196 pb->y = y;
1197 else
1198 pb->y = -line_num - 1; /* Will be computed during the rendering */
1200 curr_vp->pb = pb;
1201 add_to_ll_chain(&wps_data->progressbars, item);
1203 /* Skip the rest of the line */
1204 return skip_end_of_line(wps_bufptr)-1;
1205 #else
1206 (void)token;
1208 if (*(wps_bufptr-1) == 'f')
1209 wps_data->full_line_progressbar = true;
1210 else
1211 wps_data->full_line_progressbar = false;
1213 return 0;
1215 #endif
1218 #ifdef HAVE_ALBUMART
1219 static int parse_int(const char *newline, const char **_pos, int *num)
1221 *_pos = parse_list("d", NULL, '|', *_pos, num);
1223 return (!*_pos || *_pos > newline || **_pos != '|');
1226 static int parse_albumart_load(const char *wps_bufptr,
1227 struct wps_token *token,
1228 struct wps_data *wps_data)
1230 const char *_pos, *newline;
1231 bool parsing;
1232 struct dim dimensions;
1233 int albumart_slot;
1234 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1235 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1236 (void)token; /* silence warning */
1237 if (!aa)
1238 return skip_end_of_line(wps_bufptr);
1240 /* reset albumart info in wps */
1241 aa->width = -1;
1242 aa->height = -1;
1243 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1244 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1245 aa->vp = &curr_vp->vp;
1247 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1249 newline = strchr(wps_bufptr, '\n');
1251 _pos = wps_bufptr;
1253 if (*_pos != '|')
1254 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1256 ++_pos;
1258 /* initial validation and parsing of x component */
1259 if (parse_int(newline, &_pos, &aa->x))
1260 return WPS_ERROR_INVALID_PARAM;
1262 ++_pos;
1264 /* initial validation and parsing of y component */
1265 if (parse_int(newline, &_pos, &aa->y))
1266 return WPS_ERROR_INVALID_PARAM;
1268 /* parsing width field */
1269 parsing = true;
1270 while (parsing)
1272 /* apply each modifier in turn */
1273 ++_pos;
1274 switch (*_pos)
1276 case 'l':
1277 case 'L':
1278 case '+':
1279 if (swap_for_rtl)
1280 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1281 else
1282 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1283 break;
1284 case 'c':
1285 case 'C':
1286 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1287 break;
1288 case 'r':
1289 case 'R':
1290 case '-':
1291 if (swap_for_rtl)
1292 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1293 else
1294 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1295 break;
1296 case 'd':
1297 case 'D':
1298 case 'i':
1299 case 'I':
1300 case 's':
1301 case 'S':
1302 /* simply ignored */
1303 break;
1304 default:
1305 parsing = false;
1306 break;
1309 /* extract max width data */
1310 if (*_pos != '|')
1312 if (parse_int(newline, &_pos, &aa->width))
1313 return WPS_ERROR_INVALID_PARAM;
1316 /* parsing height field */
1317 parsing = true;
1318 while (parsing)
1320 /* apply each modifier in turn */
1321 ++_pos;
1322 switch (*_pos)
1324 case 't':
1325 case 'T':
1326 case '-':
1327 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1328 break;
1329 case 'c':
1330 case 'C':
1331 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1332 break;
1333 case 'b':
1334 case 'B':
1335 case '+':
1336 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1337 break;
1338 case 'd':
1339 case 'D':
1340 case 'i':
1341 case 'I':
1342 case 's':
1343 case 'S':
1344 /* simply ignored */
1345 break;
1346 default:
1347 parsing = false;
1348 break;
1351 /* extract max height data */
1352 if (*_pos != '|')
1354 if (parse_int(newline, &_pos, &aa->height))
1355 return WPS_ERROR_INVALID_PARAM;
1358 /* if we got here, we parsed everything ok .. ! */
1359 if (aa->width < 0)
1360 aa->width = 0;
1361 else if (aa->width > LCD_WIDTH)
1362 aa->width = LCD_WIDTH;
1364 if (aa->height < 0)
1365 aa->height = 0;
1366 else if (aa->height > LCD_HEIGHT)
1367 aa->height = LCD_HEIGHT;
1369 if (swap_for_rtl)
1370 aa->x = LCD_WIDTH - (aa->x + aa->width);
1372 aa->state = WPS_ALBUMART_LOAD;
1373 aa->draw = false;
1374 wps_data->albumart = aa;
1376 dimensions.width = aa->width;
1377 dimensions.height = aa->height;
1379 albumart_slot = playback_claim_aa_slot(&dimensions);
1381 if (0 <= albumart_slot)
1382 wps_data->playback_aa_slot = albumart_slot;
1384 /* Skip the rest of the line */
1385 return skip_end_of_line(wps_bufptr);
1388 static int parse_albumart_display(const char *wps_bufptr,
1389 struct wps_token *token,
1390 struct wps_data *wps_data)
1392 (void)wps_bufptr;
1393 struct wps_token *prev = token-1;
1394 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1396 token->type = WPS_TOKEN_ALBUMART_FOUND;
1398 else if (wps_data->albumart)
1400 wps_data->albumart->vp = &curr_vp->vp;
1402 #if 0
1403 /* the old code did this so keep it here for now...
1404 * this is to allow the posibility to showing the next tracks AA! */
1405 if (wps_bufptr+1 == 'n')
1406 return 1;
1407 #endif
1408 return 0;
1410 #endif /* HAVE_ALBUMART */
1412 #ifdef HAVE_TOUCHSCREEN
1414 struct touchaction {const char* s; int action;};
1415 static const struct touchaction touchactions[] = {
1416 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1417 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1418 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1419 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1420 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1421 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1422 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1423 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1425 static int parse_touchregion(const char *wps_bufptr,
1426 struct wps_token *token, struct wps_data *wps_data)
1428 (void)token;
1429 unsigned i, imax;
1430 struct touchregion *region = NULL;
1431 const char *ptr = wps_bufptr;
1432 const char *action;
1433 const char pb_string[] = "progressbar";
1434 const char vol_string[] = "volume";
1435 int x,y,w,h;
1437 /* format: %T|x|y|width|height|action|
1438 * if action starts with & the area must be held to happen
1439 * action is one of:
1440 * play - play/pause playback
1441 * stop - stop playback, exit the wps
1442 * prev - prev track
1443 * next - next track
1444 * ffwd - seek forward
1445 * rwd - seek backwards
1446 * menu - go back to the main menu
1447 * browse - go back to the file/db browser
1448 * shuffle - toggle shuffle mode
1449 * repmode - cycle the repeat mode
1450 * quickscreen - go into the quickscreen
1451 * contextmenu - open the context menu
1452 * playlist - go into the playlist
1453 * pitch - go into the pitchscreen
1454 * volup - increase volume by one step
1455 * voldown - decrease volume by one step
1459 if (*ptr != '|')
1460 return WPS_ERROR_INVALID_PARAM;
1461 ptr++;
1463 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1464 return WPS_ERROR_INVALID_PARAM;
1466 /* Check there is a terminating | */
1467 if (*ptr != '|')
1468 return WPS_ERROR_INVALID_PARAM;
1470 region = skin_buffer_alloc(sizeof(struct touchregion));
1471 if (!region)
1472 return WPS_ERROR_INVALID_PARAM;
1474 /* should probably do some bounds checking here with the viewport... but later */
1475 region->action = ACTION_NONE;
1476 region->x = x;
1477 region->y = y;
1478 region->width = w;
1479 region->height = h;
1480 region->wvp = curr_vp;
1482 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1483 && *(action + sizeof(pb_string)-1) == '|')
1484 region->type = WPS_TOUCHREGION_SCROLLBAR;
1485 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1486 && *(action + sizeof(vol_string)-1) == '|')
1487 region->type = WPS_TOUCHREGION_VOLUME;
1488 else
1490 region->type = WPS_TOUCHREGION_ACTION;
1492 if (*action == '&')
1494 action++;
1495 region->repeat = true;
1497 else
1498 region->repeat = false;
1500 i = 0;
1501 imax = ARRAYLEN(touchactions);
1502 while ((region->action == ACTION_NONE) &&
1503 (i < imax))
1505 /* try to match with one of our touchregion screens */
1506 int len = strlen(touchactions[i].s);
1507 if (!strncmp(touchactions[i].s, action, len)
1508 && *(action+len) == '|')
1509 region->action = touchactions[i].action;
1510 i++;
1512 if (region->action == ACTION_NONE)
1513 return WPS_ERROR_INVALID_PARAM;
1515 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1516 if (!item)
1517 return WPS_ERROR_INVALID_PARAM;
1518 add_to_ll_chain(&wps_data->touchregions, item);
1519 return skip_end_of_line(wps_bufptr);
1521 #endif
1523 /* Parse a generic token from the given string. Return the length read */
1524 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1526 int skip = 0, taglen = 0, ret;
1527 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1528 const struct wps_tag *tag;
1529 memset(token, 0, sizeof(*token));
1531 switch(*wps_bufptr)
1534 case '%':
1535 case '<':
1536 case '|':
1537 case '>':
1538 case ';':
1539 case '#':
1540 /* escaped characters */
1541 token->type = WPS_TOKEN_CHARACTER;
1542 token->value.c = *wps_bufptr;
1543 taglen = 1;
1544 wps_data->num_tokens++;
1545 break;
1547 case '?':
1548 /* conditional tag */
1549 token->type = WPS_TOKEN_CONDITIONAL;
1550 level++;
1551 condindex[level] = wps_data->num_tokens;
1552 numoptions[level] = 1;
1553 wps_data->num_tokens++;
1554 ret = parse_token(wps_bufptr + 1, wps_data);
1555 if (ret < 0) return ret;
1556 taglen = 1 + ret;
1557 break;
1559 default:
1560 /* find what tag we have */
1561 for (tag = all_tags;
1562 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1563 tag++) ;
1565 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1566 token->type = tag->type;
1567 curr_line->curr_subline->line_type |= tag->refresh_type;
1569 /* if the tag has a special parsing function, we call it */
1570 if (tag->parse_func)
1572 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1573 if (ret < 0) return ret;
1574 skip += ret;
1577 /* Some tags we don't want to save as tokens */
1578 if (tag->type == WPS_NO_TOKEN)
1579 break;
1581 /* tags that start with 'F', 'I' or 'D' are for the next file */
1582 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1583 *(tag->name) == 'D')
1584 token->next = true;
1586 wps_data->num_tokens++;
1587 break;
1590 skip += taglen;
1591 return skip;
1596 * Returns the number of bytes to skip the buf pointer to access the false
1597 * branch in a _binary_ conditional
1599 * That is:
1600 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1601 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1603 * depending on the features of a target it's not called from check_feature_tag,
1604 * hence the __attribute__ or it issues compiler warnings
1608 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1609 static int find_false_branch(const char *wps_bufptr)
1611 const char *buf = wps_bufptr;
1612 /* wps_bufptr is after the opening '<', hence level = 1*/
1613 int level = 1;
1614 char ch;
1617 ch = *buf;
1618 if (ch == '%')
1619 { /* filter out the characters we check later if they're printed
1620 * as literals */
1621 ch = *(++buf);
1622 if (ch == '<' || ch == '>' || ch == '|')
1623 continue;
1624 /* else: some tags/printed literals we skip over */
1626 else if (ch == '<') /* nested conditional */
1627 level++;
1628 else if (ch == '>')
1629 { /* closed our or a nested conditional,
1630 * do NOT skip over the '>' so that wps_parse() sees it for closing
1631 * if it is the closing one for our conditional */
1632 level--;
1634 else if (ch == '|' && level == 1)
1635 { /* we found our separator, point before and get out */
1636 break;
1638 /* if level is 0, we don't have a false branch */
1639 } while (level > 0 && *(++buf));
1641 return buf - wps_bufptr;
1645 * returns the number of bytes to get the appropriate branch of a binary
1646 * conditional
1648 * That means:
1649 * - if a feature is available, it returns 0 to not skip anything
1650 * - if the feature is not available, skip to the false branch and don't
1651 * parse the true branch at all
1653 * */
1654 static int check_feature_tag(const char *wps_bufptr, const int type)
1656 (void)wps_bufptr;
1657 switch (type)
1659 case WPS_TOKEN_RTC_PRESENT:
1660 #if CONFIG_RTC
1661 return 0;
1662 #else
1663 return find_false_branch(wps_bufptr);
1664 #endif
1665 case WPS_TOKEN_HAVE_RECORDING:
1666 #ifdef HAVE_RECORDING
1667 return 0;
1668 #else
1669 return find_false_branch(wps_bufptr);
1670 #endif
1671 default: /* not a tag we care about, just don't skip */
1672 return 0;
1677 /* Parses the WPS.
1678 data is the pointer to the structure where the parsed WPS should be stored.
1679 It is initialised.
1680 wps_bufptr points to the string containing the WPS tags */
1681 #define TOKEN_BLOCK_SIZE 128
1682 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1684 if (!data || !wps_bufptr || !*wps_bufptr)
1685 return false;
1686 enum wps_parse_error fail = PARSE_OK;
1687 int ret;
1688 int max_tokens = TOKEN_BLOCK_SIZE;
1689 size_t buf_free = 0;
1690 line_number = 0;
1691 level = -1;
1693 /* allocate enough RAM for a reasonable skin, grow as needed.
1694 * Free any used RAM before loading the images to be 100% RAM efficient */
1695 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1696 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1697 return false;
1698 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1699 data->num_tokens = 0;
1701 #if LCD_DEPTH > 1
1702 /* Backdrop defaults to the setting unless %X is used, so set it now */
1703 if (global_settings.backdrop_file[0])
1705 data->backdrop = "-";
1707 #endif
1709 while (*wps_bufptr && !fail)
1711 if (follow_lang_direction)
1712 follow_lang_direction--;
1713 /* first make sure there is enough room for tokens */
1714 if (max_tokens <= data->num_tokens + 5)
1716 int extra_tokens = TOKEN_BLOCK_SIZE;
1717 size_t needed = extra_tokens * sizeof(struct wps_token);
1718 /* do some smarts here to grow the array a bit */
1719 if (skin_buffer_freespace() < needed)
1721 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1722 break;
1724 skin_buffer_increment(needed, false);
1725 max_tokens += extra_tokens;
1728 switch(*wps_bufptr++)
1731 /* Regular tag */
1732 case '%':
1733 if ((ret = parse_token(wps_bufptr, data)) < 0)
1735 fail = PARSE_FAIL_COND_INVALID_PARAM;
1736 break;
1738 else if (level >= WPS_MAX_COND_LEVEL - 1)
1740 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1741 break;
1743 wps_bufptr += ret;
1744 break;
1746 /* Alternating sublines separator */
1747 case ';':
1748 if (level >= 0) /* there are unclosed conditionals */
1750 fail = PARSE_FAIL_UNCLOSED_COND;
1751 break;
1754 if (!skin_start_new_subline(curr_line, data->num_tokens))
1755 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1757 break;
1759 /* Conditional list start */
1760 case '<':
1761 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1763 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1764 break;
1766 wps_bufptr += check_feature_tag(wps_bufptr,
1767 data->tokens[data->num_tokens-1].type);
1768 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1769 lastcond[level] = data->num_tokens++;
1770 break;
1772 /* Conditional list end */
1773 case '>':
1774 if (level < 0) /* not in a conditional, invalid char */
1776 fail = PARSE_FAIL_INVALID_CHAR;
1777 break;
1780 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1781 if (lastcond[level])
1782 data->tokens[lastcond[level]].value.i = data->num_tokens;
1783 else
1785 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1786 break;
1789 lastcond[level] = 0;
1790 data->num_tokens++;
1791 data->tokens[condindex[level]].value.i = numoptions[level];
1792 level--;
1793 break;
1795 /* Conditional list option */
1796 case '|':
1797 if (level < 0) /* not in a conditional, invalid char */
1799 fail = PARSE_FAIL_INVALID_CHAR;
1800 break;
1803 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1804 if (lastcond[level])
1805 data->tokens[lastcond[level]].value.i = data->num_tokens;
1806 else
1808 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1809 break;
1812 lastcond[level] = data->num_tokens;
1813 numoptions[level]++;
1814 data->num_tokens++;
1815 break;
1817 /* Comment */
1818 case '#':
1819 if (level >= 0) /* there are unclosed conditionals */
1821 fail = PARSE_FAIL_UNCLOSED_COND;
1822 break;
1825 wps_bufptr += skip_end_of_line(wps_bufptr);
1826 break;
1828 /* End of this line */
1829 case '\n':
1830 if (level >= 0) /* there are unclosed conditionals */
1832 fail = PARSE_FAIL_UNCLOSED_COND;
1833 break;
1835 /* add a new token for the \n so empty lines are correct */
1836 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1837 data->tokens[data->num_tokens].value.c = '\n';
1838 data->tokens[data->num_tokens].next = false;
1839 data->num_tokens++;
1841 if (!skin_start_new_line(curr_vp, data->num_tokens))
1843 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1844 break;
1846 line_number++;
1848 break;
1850 /* String */
1851 default:
1853 unsigned int len = 1;
1854 const char *string_start = wps_bufptr - 1;
1856 /* find the length of the string */
1857 while (*wps_bufptr && *wps_bufptr != '#' &&
1858 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1859 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1860 *wps_bufptr != '|' && *wps_bufptr != '\n')
1862 wps_bufptr++;
1863 len++;
1866 /* look if we already have that string */
1867 char *str;
1868 bool found = false;
1869 struct skin_token_list *list = data->strings;
1870 while (list)
1872 str = (char*)list->token->value.data;
1873 found = (strlen(str) == len &&
1874 strncmp(string_start, str, len) == 0);
1875 if (found)
1876 break; /* break here because the list item is
1877 used if its found */
1878 list = list->next;
1880 /* If a matching string is found, found is true and i is
1881 the index of the string. If not, found is false */
1883 if (!found)
1885 /* new string */
1886 str = (char*)skin_buffer_alloc(len+1);
1887 if (!str)
1889 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1890 break;
1892 strlcpy(str, string_start, len+1);
1893 struct skin_token_list *item =
1894 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1895 if(!item)
1897 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1898 break;
1900 add_to_ll_chain(&data->strings, item);
1902 else
1904 /* another occurrence of an existing string */
1905 data->tokens[data->num_tokens].value.data = list->token->value.data;
1907 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1908 data->num_tokens++;
1910 break;
1914 if (!fail && level >= 0) /* there are unclosed conditionals */
1915 fail = PARSE_FAIL_UNCLOSED_COND;
1917 if (*wps_bufptr && !fail)
1918 /* one of the limits of the while loop was exceeded */
1919 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1921 /* Success! */
1922 curr_line->curr_subline->last_token_idx = data->num_tokens;
1923 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1924 /* freeup unused tokens */
1925 skin_buffer_free_from_front(sizeof(struct wps_token)
1926 * (max_tokens - data->num_tokens));
1928 #if defined(DEBUG) || defined(SIMULATOR)
1929 if (debug)
1930 print_debug_info(data, fail, line_number);
1931 #else
1932 (void)debug;
1933 #endif
1935 return (fail == 0);
1940 * initial setup of wps_data; does reset everything
1941 * except fields which need to survive, i.e.
1944 static void skin_data_reset(struct wps_data *wps_data)
1946 #ifdef HAVE_LCD_BITMAP
1947 wps_data->images = NULL;
1948 wps_data->progressbars = NULL;
1949 #endif
1950 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1951 wps_data->backdrop = NULL;
1952 #endif
1953 #ifdef HAVE_TOUCHSCREEN
1954 wps_data->touchregions = NULL;
1955 #endif
1956 wps_data->viewports = NULL;
1957 wps_data->strings = NULL;
1958 #ifdef HAVE_ALBUMART
1959 wps_data->albumart = NULL;
1960 if (wps_data->playback_aa_slot >= 0)
1962 playback_release_aa_slot(wps_data->playback_aa_slot);
1963 wps_data->playback_aa_slot = -1;
1965 #endif
1966 wps_data->tokens = NULL;
1967 wps_data->num_tokens = 0;
1969 #ifdef HAVE_LCD_BITMAP
1970 wps_data->peak_meter_enabled = false;
1971 wps_data->wps_sb_tag = false;
1972 wps_data->show_sb_on_wps = false;
1973 #else /* HAVE_LCD_CHARCELLS */
1974 /* progress bars */
1975 int i;
1976 for (i = 0; i < 8; i++)
1978 wps_data->wps_progress_pat[i] = 0;
1980 wps_data->full_line_progressbar = false;
1981 #endif
1982 wps_data->wps_loaded = false;
1985 #ifdef HAVE_LCD_BITMAP
1986 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1988 (void)wps_data; /* only needed for remote targets */
1989 char img_path[MAX_PATH];
1990 get_image_filename(bitmap->data, bmpdir,
1991 img_path, sizeof(img_path));
1993 /* load the image */
1994 int format;
1995 #ifdef HAVE_REMOTE_LCD
1996 if (curr_screen == SCREEN_REMOTE)
1997 format = FORMAT_ANY|FORMAT_REMOTE;
1998 else
1999 #endif
2000 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2002 size_t max_buf;
2003 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2004 bitmap->data = imgbuf;
2005 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2007 if (ret > 0)
2009 skin_buffer_increment(ret, true);
2010 return true;
2012 else
2014 /* Abort if we can't load an image */
2015 return false;
2019 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2021 struct skin_token_list *list;
2022 bool retval = true; /* return false if a single image failed to load */
2023 /* do the progressbars */
2024 list = wps_data->progressbars;
2025 while (list)
2027 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2028 if (pb->bm.data)
2030 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2031 if (!pb->have_bitmap_pb) /* no success */
2032 retval = false;
2034 list = list->next;
2036 /* regular images */
2037 list = wps_data->images;
2038 while (list)
2040 struct gui_img *img = (struct gui_img*)list->token->value.data;
2041 if (img->bm.data)
2043 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2044 if (img->loaded)
2045 img->subimage_height = img->bm.height / img->num_subimages;
2046 else
2047 retval = false;
2049 list = list->next;
2052 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2053 /* Backdrop load scheme:
2054 * 1) %X|filename|
2055 * 2) load the backdrop from settings
2057 if (wps_data->backdrop)
2059 bool needed = wps_data->backdrop[0] != '-';
2060 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2061 bmpdir, curr_screen);
2062 if (!wps_data->backdrop && needed)
2063 retval = false;
2065 #endif /* has backdrop support */
2067 return retval;
2070 #endif /* HAVE_LCD_BITMAP */
2072 /* to setup up the wps-data from a format-buffer (isfile = false)
2073 from a (wps-)file (isfile = true)*/
2074 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2075 const char *buf, bool isfile)
2078 if (!wps_data || !buf)
2079 return false;
2080 #ifdef HAVE_ALBUMART
2081 int status;
2082 struct mp3entry *curtrack;
2083 long offset;
2084 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2085 if (wps_data->albumart)
2087 old_aa.state = wps_data->albumart->state;
2088 old_aa.height = wps_data->albumart->height;
2089 old_aa.width = wps_data->albumart->width;
2091 #endif
2093 skin_data_reset(wps_data);
2094 curr_screen = screen;
2096 /* alloc default viewport, will be fixed up later */
2097 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2098 if (!curr_vp)
2099 return false;
2100 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2101 if (!list)
2102 return false;
2103 add_to_ll_chain(&wps_data->viewports, list);
2106 /* Initialise the first (default) viewport */
2107 curr_vp->label = VP_DEFAULT_LABEL;
2108 curr_vp->pb = NULL;
2109 curr_vp->hidden_flags = 0;
2110 curr_vp->lines = NULL;
2112 viewport_set_defaults(&curr_vp->vp, screen);
2114 curr_line = NULL;
2115 if (!skin_start_new_line(curr_vp, 0))
2116 return false;
2118 if (!isfile)
2120 if (wps_parse(wps_data, buf, false))
2122 #ifdef HAVE_LCD_BITMAP
2123 /* load the backdrop */
2124 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2125 skin_data_reset(wps_data);
2126 return false;
2128 #endif
2129 return true;
2131 return false;
2133 else
2135 int fd = open_utf8(buf, O_RDONLY);
2137 if (fd < 0)
2138 return false;
2140 /* get buffer space from the plugin buffer */
2141 size_t buffersize = 0;
2142 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2144 if (!wps_buffer)
2145 return false;
2147 /* copy the file's content to the buffer for parsing,
2148 ensuring that every line ends with a newline char. */
2149 unsigned int start = 0;
2150 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2152 start += strlen(wps_buffer + start);
2153 if (start < buffersize - 1)
2155 wps_buffer[start++] = '\n';
2156 wps_buffer[start] = 0;
2160 close(fd);
2162 if (start <= 0)
2163 return false;
2165 /* parse the WPS source */
2166 if (!wps_parse(wps_data, wps_buffer, true)) {
2167 skin_data_reset(wps_data);
2168 return false;
2171 wps_data->wps_loaded = true;
2173 #ifdef HAVE_LCD_BITMAP
2174 /* get the bitmap dir */
2175 char bmpdir[MAX_PATH];
2176 char *dot = strrchr(buf, '.');
2178 strlcpy(bmpdir, buf, dot - buf + 1);
2180 /* load the bitmaps that were found by the parsing */
2181 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2182 skin_data_reset(wps_data);
2183 wps_data->wps_loaded = false;
2184 return false;
2186 #endif
2187 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2188 status = audio_status();
2189 if (status & AUDIO_STATUS_PLAY)
2191 struct skin_albumart *aa = wps_data->albumart;
2192 if (aa && ((aa->state && !old_aa.state) ||
2193 (aa->state &&
2194 (((old_aa.height != aa->height) ||
2195 (old_aa.width != aa->width))))))
2197 curtrack = audio_current_track();
2198 offset = curtrack->offset;
2199 audio_stop();
2200 if (!(status & AUDIO_STATUS_PAUSE))
2201 audio_play(offset);
2204 #endif
2205 #if defined(DEBUG) || defined(SIMULATOR)
2206 debug_skin_usage();
2207 #endif
2208 return true;