fix %pb when the height isnt given and it is in a viewport with a user font (so the...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobc4f8d1a8ca18679a94ee20923929499cb854e5c2
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 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
370 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
371 #endif
372 { WPS_NO_TOKEN, "V", 0, parse_viewport },
374 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
375 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
376 #endif
377 #endif
379 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
380 parse_setting_and_lang },
381 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
382 parse_setting_and_lang },
383 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
385 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
386 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
387 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
390 /* Recording Tokens */
391 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
392 #ifdef HAVE_RECORDING
393 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
394 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
395 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
396 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
397 #endif
398 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
399 /* the array MUST end with an empty string (first char is \0) */
403 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
404 * chains require the order to be kept.
406 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
408 if (*list == NULL)
409 *list = item;
410 else
412 struct skin_token_list *t = *list;
413 while (t->next)
414 t = t->next;
415 t->next = item;
419 /* traverse the image linked-list for an image */
420 #ifdef HAVE_LCD_BITMAP
421 struct gui_img* find_image(char label, struct wps_data *data)
423 struct skin_token_list *list = data->images;
424 while (list)
426 struct gui_img *img = (struct gui_img *)list->token->value.data;
427 if (img->label == label)
428 return img;
429 list = list->next;
431 return NULL;
434 #endif
436 /* traverse the viewport linked list for a viewport */
437 struct skin_viewport* find_viewport(char label, struct wps_data *data)
439 struct skin_token_list *list = data->viewports;
440 while (list)
442 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
443 if (vp->label == label)
444 return vp;
445 list = list->next;
447 return NULL;
451 /* create and init a new wpsll item.
452 * passing NULL to token will alloc a new one.
453 * You should only pass NULL for the token when the token type (table above)
454 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
456 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
457 void* token_data)
459 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
460 if (!token)
461 token = skin_buffer_alloc(sizeof(struct wps_token));
462 if (!llitem || !token)
463 return NULL;
464 llitem->next = NULL;
465 llitem->token = token;
466 if (token_data)
467 llitem->token->value.data = token_data;
468 return llitem;
471 /* Returns the number of chars that should be skipped to jump
472 immediately after the first eol, i.e. to the start of the next line */
473 static int skip_end_of_line(const char *wps_bufptr)
475 line_number++;
476 int skip = 0;
477 while(*(wps_bufptr + skip) != '\n')
478 skip++;
479 return ++skip;
482 /* Starts a new subline in the current line during parsing */
483 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
485 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
486 if (!subline)
487 return false;
489 subline->first_token_idx = curr_token;
490 subline->next = NULL;
492 subline->line_type = 0;
493 subline->time_mult = 0;
495 line->curr_subline->last_token_idx = curr_token-1;
496 line->curr_subline->next = subline;
497 line->curr_subline = subline;
498 return true;
501 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
503 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
504 struct skin_subline *subline = NULL;
505 if (!line)
506 return false;
508 /* init the subline */
509 subline = &line->sublines;
510 subline->first_token_idx = curr_token;
511 subline->next = NULL;
512 subline->line_type = 0;
513 subline->time_mult = 0;
515 /* init the new line */
516 line->curr_subline = &line->sublines;
517 line->next = NULL;
518 line->subline_expire_time = 0;
520 /* connect to curr_line and vp pointers.
521 * 1) close the previous lines subline
522 * 2) connect to vp pointer
523 * 3) connect to curr_line global pointer
525 if (curr_line)
527 curr_line->curr_subline->last_token_idx = curr_token - 1;
528 curr_line->next = line;
529 curr_line->curr_subline = NULL;
531 curr_line = line;
532 if (!vp->lines)
533 vp->lines = line;
534 return true;
537 #ifdef HAVE_LCD_BITMAP
539 static int parse_statusbar_enable(const char *wps_bufptr,
540 struct wps_token *token,
541 struct wps_data *wps_data)
543 (void)token; /* Kill warnings */
544 wps_data->wps_sb_tag = true;
545 wps_data->show_sb_on_wps = true;
546 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
547 viewport_set_defaults(&default_vp->vp, curr_screen);
548 return skip_end_of_line(wps_bufptr);
551 static int parse_statusbar_disable(const char *wps_bufptr,
552 struct wps_token *token,
553 struct wps_data *wps_data)
555 (void)token; /* Kill warnings */
556 wps_data->wps_sb_tag = true;
557 wps_data->show_sb_on_wps = false;
558 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
559 viewport_set_fullscreen(&default_vp->vp, curr_screen);
560 return skip_end_of_line(wps_bufptr);
563 static int get_image_id(int c)
565 if(c >= 'a' && c <= 'z')
566 return c - 'a';
567 else if(c >= 'A' && c <= 'Z')
568 return c - 'A' + 26;
569 else
570 return -1;
573 char *get_image_filename(const char *start, const char* bmpdir,
574 char *buf, int buf_size)
576 const char *end = strchr(start, '|');
577 int bmpdirlen = strlen(bmpdir);
579 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
581 buf[0] = '\0';
582 return NULL;
585 strcpy(buf, bmpdir);
586 buf[bmpdirlen] = '/';
587 memcpy( &buf[bmpdirlen + 1], start, end - start);
588 buf[bmpdirlen + 1 + end - start] = 0;
590 return buf;
593 static int parse_image_display(const char *wps_bufptr,
594 struct wps_token *token,
595 struct wps_data *wps_data)
597 char label = wps_bufptr[0];
598 int subimage;
599 struct gui_img *img;;
601 /* sanity check */
602 img = find_image(label, wps_data);
603 if (!img)
605 token->value.i = label; /* so debug works */
606 return WPS_ERROR_INVALID_PARAM;
609 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
611 if (subimage >= img->num_subimages)
612 return WPS_ERROR_INVALID_PARAM;
614 /* Store sub-image number to display in high bits */
615 token->value.i = label | (subimage << 8);
616 return 2; /* We have consumed 2 bytes */
617 } else {
618 token->value.i = label;
619 return 1; /* We have consumed 1 byte */
623 static int parse_image_load(const char *wps_bufptr,
624 struct wps_token *token,
625 struct wps_data *wps_data)
627 const char *ptr = wps_bufptr;
628 const char *pos;
629 const char* filename;
630 const char* id;
631 const char *newline;
632 int x,y;
633 struct gui_img *img;
635 /* format: %x|n|filename.bmp|x|y|
636 or %xl|n|filename.bmp|x|y|
637 or %xl|n|filename.bmp|x|y|num_subimages|
640 if (*ptr != '|')
641 return WPS_ERROR_INVALID_PARAM;
643 ptr++;
645 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
646 return WPS_ERROR_INVALID_PARAM;
648 /* Check there is a terminating | */
649 if (*ptr != '|')
650 return WPS_ERROR_INVALID_PARAM;
652 /* check the image number and load state */
653 if(find_image(*id, wps_data))
655 /* Invalid image ID */
656 return WPS_ERROR_INVALID_PARAM;
658 img = skin_buffer_alloc(sizeof(struct gui_img));
659 if (!img)
660 return WPS_ERROR_INVALID_PARAM;
661 /* save a pointer to the filename */
662 img->bm.data = (char*)filename;
663 img->label = *id;
664 img->x = x;
665 img->y = y;
666 img->num_subimages = 1;
667 img->always_display = false;
669 /* save current viewport */
670 img->vp = &curr_vp->vp;
672 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
674 img->always_display = true;
676 else
678 /* Parse the (optional) number of sub-images */
679 ptr++;
680 newline = strchr(ptr, '\n');
681 pos = strchr(ptr, '|');
682 if (pos && pos < newline)
683 img->num_subimages = atoi(ptr);
685 if (img->num_subimages <= 0)
686 return WPS_ERROR_INVALID_PARAM;
688 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
689 if (!item)
690 return WPS_ERROR_INVALID_PARAM;
691 add_to_ll_chain(&wps_data->images, item);
693 /* Skip the rest of the line */
694 return skip_end_of_line(wps_bufptr);
696 struct skin_font {
697 int id; /* the id from font_load */
698 char *name; /* filename without path and extension */
700 static struct skin_font skinfonts[MAXUSERFONTS];
701 static int parse_font_load(const char *wps_bufptr,
702 struct wps_token *token, struct wps_data *wps_data)
704 (void)wps_data; (void)token;
705 const char *ptr = wps_bufptr;
706 int id;
707 char *filename;
709 if (*ptr != '|')
710 return WPS_ERROR_INVALID_PARAM;
712 ptr++;
714 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
715 return WPS_ERROR_INVALID_PARAM;
717 /* Check there is a terminating | */
718 if (*ptr != '|')
719 return WPS_ERROR_INVALID_PARAM;
721 if (id <= FONT_UI || id >= MAXFONTS-1)
722 return WPS_ERROR_INVALID_PARAM;
723 #if defined(DEBUG) || defined(SIMULATOR)
724 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
726 DEBUGF("font id %d already being used\n", id);
728 #endif
729 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
730 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
732 return skip_end_of_line(wps_bufptr);
736 static int parse_viewport_display(const char *wps_bufptr,
737 struct wps_token *token,
738 struct wps_data *wps_data)
740 (void)wps_data;
741 char letter = wps_bufptr[0];
743 if (letter < 'a' || letter > 'z')
745 /* invalid viewport tag */
746 return WPS_ERROR_INVALID_PARAM;
748 token->value.i = letter;
749 return 1;
752 #ifdef HAVE_LCD_BITMAP
753 static int parse_playlistview_text(struct playlistviewer *viewer,
754 enum info_line_type line, char* text)
756 int cur_string = 0;
757 const struct wps_tag *tag;
758 int taglen = 0;
759 const char *start = text;
760 if (*text != '|')
761 return -1;
762 text++;
763 viewer->lines[line].count = 0;
764 viewer->lines[line].scroll = false;
765 while (*text != '|')
767 if (*text == '%') /* it is a token of some type */
769 text++;
770 taglen = 0;
771 switch(*text)
773 case '%':
774 case '<':
775 case '|':
776 case '>':
777 case ';':
778 case '#':
779 /* escaped characters */
780 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
781 viewer->lines[line].strings[cur_string][0] = *text;
782 viewer->lines[line].strings[cur_string++][1] = '\0';
783 break;
784 default:
785 for (tag = all_tags;
786 strncmp(text, tag->name, strlen(tag->name)) != 0;
787 tag++) ;
788 /* %s isnt stored as a tag so manually check for it */
789 if (tag->type == WPS_NO_TOKEN)
791 if (!strncmp(tag->name, "s", 1))
793 viewer->lines[line].scroll = true;
794 taglen = 1;
797 else if (tag->type == WPS_TOKEN_UNKNOWN)
799 int i = 0;
800 /* just copy the string */
801 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
802 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
804 viewer->lines[line].strings[cur_string][i] = text[i];
805 i++;
807 viewer->lines[line].strings[cur_string][i] = '\0';
808 cur_string++;
809 taglen = i;
811 else
813 if (tag->parse_func)
815 /* unsupported tag, reject */
816 return -1;
818 taglen = strlen(tag->name);
819 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
821 text += taglen;
824 else
826 /* regular string */
827 int i = 0;
828 /* just copy the string */
829 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
830 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
832 viewer->lines[line].strings[cur_string][i] = text[i];
833 i++;
835 viewer->lines[line].strings[cur_string][i] = '\0';
836 cur_string++;
837 text += i;
840 return text - start;
844 static int parse_playlistview(const char *wps_bufptr,
845 struct wps_token *token, struct wps_data *wps_data)
847 (void)wps_data;
848 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
849 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
850 char *ptr = strchr(wps_bufptr, '|');
851 int length;
852 if (!viewer || !ptr)
853 return WPS_ERROR_INVALID_PARAM;
854 viewer->vp = &curr_vp->vp;
855 viewer->show_icons = true;
856 viewer->start_offset = atoi(ptr+1);
857 token->value.data = (void*)viewer;
858 ptr = strchr(ptr+1, '|');
859 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
860 if (length < 0)
861 return WPS_ERROR_INVALID_PARAM;
862 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
863 if (length < 0)
864 return WPS_ERROR_INVALID_PARAM;
866 return skip_end_of_line(wps_bufptr);
868 #endif
870 static int parse_viewport(const char *wps_bufptr,
871 struct wps_token *token,
872 struct wps_data *wps_data)
874 (void)token; /* Kill warnings */
875 const char *ptr = wps_bufptr;
877 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
879 /* check for the optional letter to signify its a hideable viewport */
880 /* %Vl|<label>|<rest of tags>| */
881 skin_vp->hidden_flags = 0;
882 skin_vp->label = VP_NO_LABEL;
883 skin_vp->pb = NULL;
884 skin_vp->lines = NULL;
885 if (curr_line)
887 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
888 - (wps_data->num_tokens > 0 ? 1 : 0);
891 curr_line = NULL;
892 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
893 return WPS_ERROR_INVALID_PARAM;
896 if (*ptr == 'i')
898 skin_vp->label = VP_INFO_LABEL;
899 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
900 ++ptr;
902 else if (*ptr == 'l')
904 if (*(ptr+1) == '|')
906 char label = *(ptr+2);
907 if (label >= 'a' && label <= 'z')
909 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
910 skin_vp->label = label;
912 else
913 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
914 ptr += 3;
917 if (*ptr != '|')
918 return WPS_ERROR_INVALID_PARAM;
920 ptr++;
921 struct viewport *vp = &skin_vp->vp;
922 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
923 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
924 return WPS_ERROR_INVALID_PARAM;
926 /* Check for trailing | */
927 if (*ptr != '|')
928 return WPS_ERROR_INVALID_PARAM;
930 if (follow_lang_direction && lang_is_rtl())
932 vp->flags |= VP_FLAG_ALIGN_RIGHT;
933 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
935 else
936 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
938 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
939 if (!list)
940 return WPS_ERROR_INVALID_PARAM;
941 add_to_ll_chain(&wps_data->viewports, list);
942 curr_vp = skin_vp;
943 /* Skip the rest of the line */
944 return skip_end_of_line(wps_bufptr);
947 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
948 static int parse_image_special(const char *wps_bufptr,
949 struct wps_token *token,
950 struct wps_data *wps_data)
952 (void)wps_data; /* kill warning */
953 (void)token;
954 const char *pos = NULL;
955 const char *newline;
956 bool error = false;
958 pos = strchr(wps_bufptr + 1, '|');
959 newline = strchr(wps_bufptr, '\n');
961 error = (pos > newline);
964 #if LCD_DEPTH > 1
965 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
967 /* format: %X|filename.bmp| or %Xd */
968 if (*(wps_bufptr) == 'd')
970 wps_data->backdrop = NULL;
971 return skip_end_of_line(wps_bufptr);
973 else if (!error)
974 wps_data->backdrop = (char*)wps_bufptr + 1;
976 #endif
977 if (error)
978 return WPS_ERROR_INVALID_PARAM;
979 /* Skip the rest of the line */
980 return skip_end_of_line(wps_bufptr);
982 #endif
984 #endif /* HAVE_LCD_BITMAP */
986 static int parse_setting_and_lang(const char *wps_bufptr,
987 struct wps_token *token,
988 struct wps_data *wps_data)
990 /* NOTE: both the string validations that happen in here will
991 * automatically PASS on checkwps because its too hard to get
992 * settings_list.c and englinsh.lang built for it.
993 * If that ever changes remove the #ifndef __PCTOOL__'s here
995 (void)wps_data;
996 const char *ptr = wps_bufptr;
997 const char *end;
998 int i = 0;
999 char temp[64];
1001 /* Find the setting's cfg_name */
1002 if (*ptr != '|')
1003 return WPS_ERROR_INVALID_PARAM;
1004 ptr++;
1005 end = strchr(ptr,'|');
1006 if (!end)
1007 return WPS_ERROR_INVALID_PARAM;
1008 strlcpy(temp, ptr,end-ptr+1);
1010 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1012 #ifndef __PCTOOL__
1013 i = lang_english_to_id(temp);
1014 if (i < 0)
1015 return WPS_ERROR_INVALID_PARAM;
1016 #endif
1018 else
1020 /* Find the setting */
1021 for (i=0; i<nb_settings; i++)
1022 if (settings[i].cfg_name &&
1023 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1024 /* prevent matches on cfg_name prefixes */
1025 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1026 break;
1027 #ifndef __PCTOOL__
1028 if (i == nb_settings)
1029 return WPS_ERROR_INVALID_PARAM;
1030 #endif
1032 /* Store the setting number */
1033 token->value.i = i;
1035 /* Skip the rest of the line */
1036 return end-ptr+2;
1040 static int parse_dir_level(const char *wps_bufptr,
1041 struct wps_token *token,
1042 struct wps_data *wps_data)
1044 char val[] = { *wps_bufptr, '\0' };
1045 token->value.i = atoi(val);
1046 (void)wps_data; /* Kill warnings */
1047 return 1;
1050 static int parse_timeout(const char *wps_bufptr,
1051 struct wps_token *token,
1052 struct wps_data *wps_data)
1054 int skip = 0;
1055 int val = 0;
1056 bool have_point = false;
1057 bool have_tenth = false;
1059 (void)wps_data; /* Kill the warning */
1061 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1063 if (*wps_bufptr != '.')
1065 val *= 10;
1066 val += *wps_bufptr - '0';
1067 if (have_point)
1069 have_tenth = true;
1070 wps_bufptr++;
1071 skip++;
1072 break;
1075 else
1076 have_point = true;
1078 wps_bufptr++;
1079 skip++;
1082 if (have_tenth == false)
1083 val *= 10;
1085 if (val == 0 && skip == 0)
1087 /* decide what to do if no value was specified */
1088 switch (token->type)
1090 case WPS_TOKEN_SUBLINE_TIMEOUT:
1091 return -1;
1092 case WPS_TOKEN_BUTTON_VOLUME:
1093 val = 10;
1094 break;
1097 token->value.i = val;
1099 return skip;
1102 static int parse_progressbar(const char *wps_bufptr,
1103 struct wps_token *token,
1104 struct wps_data *wps_data)
1106 /* %pb or %pb|filename|x|y|width|height|
1107 using - for any of the params uses "sane" values */
1108 #ifdef HAVE_LCD_BITMAP
1109 enum {
1110 PB_FILENAME = 0,
1111 PB_X,
1112 PB_Y,
1113 PB_WIDTH,
1114 PB_HEIGHT
1116 const char *filename;
1117 int x, y, height, width;
1118 uint32_t set = 0;
1119 const char *ptr = wps_bufptr;
1120 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1121 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1123 if (!pb || !item)
1124 return WPS_ERROR_INVALID_PARAM;
1126 struct viewport *vp = &curr_vp->vp;
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
1186 if (vp->font > FONT_UI)
1187 pb->height = -1; /* calculate at display time */
1188 else
1190 #ifndef __PCTOOL__
1191 pb->height = font_get(vp->font)->height;
1192 #else
1193 pb->height = 8;
1194 #endif
1198 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1199 pb->y = y;
1200 else
1201 pb->y = -line_num - 1; /* Will be computed during the rendering */
1203 curr_vp->pb = pb;
1204 add_to_ll_chain(&wps_data->progressbars, item);
1206 /* Skip the rest of the line */
1207 return skip_end_of_line(wps_bufptr)-1;
1208 #else
1209 (void)token;
1211 if (*(wps_bufptr-1) == 'f')
1212 wps_data->full_line_progressbar = true;
1213 else
1214 wps_data->full_line_progressbar = false;
1216 return 0;
1218 #endif
1221 #ifdef HAVE_ALBUMART
1222 static int parse_int(const char *newline, const char **_pos, int *num)
1224 *_pos = parse_list("d", NULL, '|', *_pos, num);
1226 return (!*_pos || *_pos > newline || **_pos != '|');
1229 static int parse_albumart_load(const char *wps_bufptr,
1230 struct wps_token *token,
1231 struct wps_data *wps_data)
1233 const char *_pos, *newline;
1234 bool parsing;
1235 struct dim dimensions;
1236 int albumart_slot;
1237 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1238 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1239 (void)token; /* silence warning */
1240 if (!aa)
1241 return skip_end_of_line(wps_bufptr);
1243 /* reset albumart info in wps */
1244 aa->width = -1;
1245 aa->height = -1;
1246 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1247 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1248 aa->vp = &curr_vp->vp;
1250 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1252 newline = strchr(wps_bufptr, '\n');
1254 _pos = wps_bufptr;
1256 if (*_pos != '|')
1257 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1259 ++_pos;
1261 /* initial validation and parsing of x component */
1262 if (parse_int(newline, &_pos, &aa->x))
1263 return WPS_ERROR_INVALID_PARAM;
1265 ++_pos;
1267 /* initial validation and parsing of y component */
1268 if (parse_int(newline, &_pos, &aa->y))
1269 return WPS_ERROR_INVALID_PARAM;
1271 /* parsing width field */
1272 parsing = true;
1273 while (parsing)
1275 /* apply each modifier in turn */
1276 ++_pos;
1277 switch (*_pos)
1279 case 'l':
1280 case 'L':
1281 case '+':
1282 if (swap_for_rtl)
1283 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1284 else
1285 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1286 break;
1287 case 'c':
1288 case 'C':
1289 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1290 break;
1291 case 'r':
1292 case 'R':
1293 case '-':
1294 if (swap_for_rtl)
1295 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1296 else
1297 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1298 break;
1299 case 'd':
1300 case 'D':
1301 case 'i':
1302 case 'I':
1303 case 's':
1304 case 'S':
1305 /* simply ignored */
1306 break;
1307 default:
1308 parsing = false;
1309 break;
1312 /* extract max width data */
1313 if (*_pos != '|')
1315 if (parse_int(newline, &_pos, &aa->width))
1316 return WPS_ERROR_INVALID_PARAM;
1319 /* parsing height field */
1320 parsing = true;
1321 while (parsing)
1323 /* apply each modifier in turn */
1324 ++_pos;
1325 switch (*_pos)
1327 case 't':
1328 case 'T':
1329 case '-':
1330 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1331 break;
1332 case 'c':
1333 case 'C':
1334 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1335 break;
1336 case 'b':
1337 case 'B':
1338 case '+':
1339 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1340 break;
1341 case 'd':
1342 case 'D':
1343 case 'i':
1344 case 'I':
1345 case 's':
1346 case 'S':
1347 /* simply ignored */
1348 break;
1349 default:
1350 parsing = false;
1351 break;
1354 /* extract max height data */
1355 if (*_pos != '|')
1357 if (parse_int(newline, &_pos, &aa->height))
1358 return WPS_ERROR_INVALID_PARAM;
1361 /* if we got here, we parsed everything ok .. ! */
1362 if (aa->width < 0)
1363 aa->width = 0;
1364 else if (aa->width > LCD_WIDTH)
1365 aa->width = LCD_WIDTH;
1367 if (aa->height < 0)
1368 aa->height = 0;
1369 else if (aa->height > LCD_HEIGHT)
1370 aa->height = LCD_HEIGHT;
1372 if (swap_for_rtl)
1373 aa->x = LCD_WIDTH - (aa->x + aa->width);
1375 aa->state = WPS_ALBUMART_LOAD;
1376 aa->draw = false;
1377 wps_data->albumart = aa;
1379 dimensions.width = aa->width;
1380 dimensions.height = aa->height;
1382 albumart_slot = playback_claim_aa_slot(&dimensions);
1384 if (0 <= albumart_slot)
1385 wps_data->playback_aa_slot = albumart_slot;
1387 /* Skip the rest of the line */
1388 return skip_end_of_line(wps_bufptr);
1391 static int parse_albumart_display(const char *wps_bufptr,
1392 struct wps_token *token,
1393 struct wps_data *wps_data)
1395 (void)wps_bufptr;
1396 struct wps_token *prev = token-1;
1397 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1399 token->type = WPS_TOKEN_ALBUMART_FOUND;
1401 else if (wps_data->albumart)
1403 wps_data->albumart->vp = &curr_vp->vp;
1405 #if 0
1406 /* the old code did this so keep it here for now...
1407 * this is to allow the posibility to showing the next tracks AA! */
1408 if (wps_bufptr+1 == 'n')
1409 return 1;
1410 #endif
1411 return 0;
1413 #endif /* HAVE_ALBUMART */
1415 #ifdef HAVE_TOUCHSCREEN
1417 struct touchaction {const char* s; int action;};
1418 static const struct touchaction touchactions[] = {
1419 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1420 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1421 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1422 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1423 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1424 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1425 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1426 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1428 static int parse_touchregion(const char *wps_bufptr,
1429 struct wps_token *token, struct wps_data *wps_data)
1431 (void)token;
1432 unsigned i, imax;
1433 struct touchregion *region = NULL;
1434 const char *ptr = wps_bufptr;
1435 const char *action;
1436 const char pb_string[] = "progressbar";
1437 const char vol_string[] = "volume";
1438 int x,y,w,h;
1440 /* format: %T|x|y|width|height|action|
1441 * if action starts with & the area must be held to happen
1442 * action is one of:
1443 * play - play/pause playback
1444 * stop - stop playback, exit the wps
1445 * prev - prev track
1446 * next - next track
1447 * ffwd - seek forward
1448 * rwd - seek backwards
1449 * menu - go back to the main menu
1450 * browse - go back to the file/db browser
1451 * shuffle - toggle shuffle mode
1452 * repmode - cycle the repeat mode
1453 * quickscreen - go into the quickscreen
1454 * contextmenu - open the context menu
1455 * playlist - go into the playlist
1456 * pitch - go into the pitchscreen
1457 * volup - increase volume by one step
1458 * voldown - decrease volume by one step
1462 if (*ptr != '|')
1463 return WPS_ERROR_INVALID_PARAM;
1464 ptr++;
1466 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1467 return WPS_ERROR_INVALID_PARAM;
1469 /* Check there is a terminating | */
1470 if (*ptr != '|')
1471 return WPS_ERROR_INVALID_PARAM;
1473 region = skin_buffer_alloc(sizeof(struct touchregion));
1474 if (!region)
1475 return WPS_ERROR_INVALID_PARAM;
1477 /* should probably do some bounds checking here with the viewport... but later */
1478 region->action = ACTION_NONE;
1479 region->x = x;
1480 region->y = y;
1481 region->width = w;
1482 region->height = h;
1483 region->wvp = curr_vp;
1484 region->armed = false;
1486 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1487 && *(action + sizeof(pb_string)-1) == '|')
1488 region->type = WPS_TOUCHREGION_SCROLLBAR;
1489 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1490 && *(action + sizeof(vol_string)-1) == '|')
1491 region->type = WPS_TOUCHREGION_VOLUME;
1492 else
1494 region->type = WPS_TOUCHREGION_ACTION;
1496 if (*action == '&')
1498 action++;
1499 region->repeat = true;
1501 else
1502 region->repeat = false;
1504 i = 0;
1505 imax = ARRAYLEN(touchactions);
1506 while ((region->action == ACTION_NONE) &&
1507 (i < imax))
1509 /* try to match with one of our touchregion screens */
1510 int len = strlen(touchactions[i].s);
1511 if (!strncmp(touchactions[i].s, action, len)
1512 && *(action+len) == '|')
1513 region->action = touchactions[i].action;
1514 i++;
1516 if (region->action == ACTION_NONE)
1517 return WPS_ERROR_INVALID_PARAM;
1519 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1520 if (!item)
1521 return WPS_ERROR_INVALID_PARAM;
1522 add_to_ll_chain(&wps_data->touchregions, item);
1523 return skip_end_of_line(wps_bufptr);
1525 #endif
1527 /* Parse a generic token from the given string. Return the length read */
1528 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1530 int skip = 0, taglen = 0, ret;
1531 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1532 const struct wps_tag *tag;
1533 memset(token, 0, sizeof(*token));
1535 switch(*wps_bufptr)
1538 case '%':
1539 case '<':
1540 case '|':
1541 case '>':
1542 case ';':
1543 case '#':
1544 /* escaped characters */
1545 token->type = WPS_TOKEN_CHARACTER;
1546 token->value.c = *wps_bufptr;
1547 taglen = 1;
1548 wps_data->num_tokens++;
1549 break;
1551 case '?':
1552 /* conditional tag */
1553 token->type = WPS_TOKEN_CONDITIONAL;
1554 level++;
1555 condindex[level] = wps_data->num_tokens;
1556 numoptions[level] = 1;
1557 wps_data->num_tokens++;
1558 ret = parse_token(wps_bufptr + 1, wps_data);
1559 if (ret < 0) return ret;
1560 taglen = 1 + ret;
1561 break;
1563 default:
1564 /* find what tag we have */
1565 for (tag = all_tags;
1566 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1567 tag++) ;
1569 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1570 token->type = tag->type;
1571 curr_line->curr_subline->line_type |= tag->refresh_type;
1573 /* if the tag has a special parsing function, we call it */
1574 if (tag->parse_func)
1576 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1577 if (ret < 0) return ret;
1578 skip += ret;
1581 /* Some tags we don't want to save as tokens */
1582 if (tag->type == WPS_NO_TOKEN)
1583 break;
1585 /* tags that start with 'F', 'I' or 'D' are for the next file */
1586 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1587 *(tag->name) == 'D')
1588 token->next = true;
1590 wps_data->num_tokens++;
1591 break;
1594 skip += taglen;
1595 return skip;
1600 * Returns the number of bytes to skip the buf pointer to access the false
1601 * branch in a _binary_ conditional
1603 * That is:
1604 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1605 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1607 * depending on the features of a target it's not called from check_feature_tag,
1608 * hence the __attribute__ or it issues compiler warnings
1612 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1613 static int find_false_branch(const char *wps_bufptr)
1615 const char *buf = wps_bufptr;
1616 /* wps_bufptr is after the opening '<', hence level = 1*/
1617 int level = 1;
1618 char ch;
1621 ch = *buf;
1622 if (ch == '%')
1623 { /* filter out the characters we check later if they're printed
1624 * as literals */
1625 ch = *(++buf);
1626 if (ch == '<' || ch == '>' || ch == '|')
1627 continue;
1628 /* else: some tags/printed literals we skip over */
1630 else if (ch == '<') /* nested conditional */
1631 level++;
1632 else if (ch == '>')
1633 { /* closed our or a nested conditional,
1634 * do NOT skip over the '>' so that wps_parse() sees it for closing
1635 * if it is the closing one for our conditional */
1636 level--;
1638 else if (ch == '|' && level == 1)
1639 { /* we found our separator, point before and get out */
1640 break;
1642 /* if level is 0, we don't have a false branch */
1643 } while (level > 0 && *(++buf));
1645 return buf - wps_bufptr;
1649 * returns the number of bytes to get the appropriate branch of a binary
1650 * conditional
1652 * That means:
1653 * - if a feature is available, it returns 0 to not skip anything
1654 * - if the feature is not available, skip to the false branch and don't
1655 * parse the true branch at all
1657 * */
1658 static int check_feature_tag(const char *wps_bufptr, const int type)
1660 (void)wps_bufptr;
1661 switch (type)
1663 case WPS_TOKEN_RTC_PRESENT:
1664 #if CONFIG_RTC
1665 return 0;
1666 #else
1667 return find_false_branch(wps_bufptr);
1668 #endif
1669 case WPS_TOKEN_HAVE_RECORDING:
1670 #ifdef HAVE_RECORDING
1671 return 0;
1672 #else
1673 return find_false_branch(wps_bufptr);
1674 #endif
1675 default: /* not a tag we care about, just don't skip */
1676 return 0;
1681 /* Parses the WPS.
1682 data is the pointer to the structure where the parsed WPS should be stored.
1683 It is initialised.
1684 wps_bufptr points to the string containing the WPS tags */
1685 #define TOKEN_BLOCK_SIZE 128
1686 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1688 if (!data || !wps_bufptr || !*wps_bufptr)
1689 return false;
1690 enum wps_parse_error fail = PARSE_OK;
1691 int ret;
1692 int max_tokens = TOKEN_BLOCK_SIZE;
1693 size_t buf_free = 0;
1694 line_number = 0;
1695 level = -1;
1697 /* allocate enough RAM for a reasonable skin, grow as needed.
1698 * Free any used RAM before loading the images to be 100% RAM efficient */
1699 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1700 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1701 return false;
1702 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1703 data->num_tokens = 0;
1705 #if LCD_DEPTH > 1
1706 /* Backdrop defaults to the setting unless %X is used, so set it now */
1707 if (global_settings.backdrop_file[0])
1709 data->backdrop = "-";
1711 #endif
1713 while (*wps_bufptr && !fail)
1715 if (follow_lang_direction)
1716 follow_lang_direction--;
1717 /* first make sure there is enough room for tokens */
1718 if (max_tokens <= data->num_tokens + 5)
1720 int extra_tokens = TOKEN_BLOCK_SIZE;
1721 size_t needed = extra_tokens * sizeof(struct wps_token);
1722 /* do some smarts here to grow the array a bit */
1723 if (skin_buffer_freespace() < needed)
1725 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1726 break;
1728 skin_buffer_increment(needed, false);
1729 max_tokens += extra_tokens;
1732 switch(*wps_bufptr++)
1735 /* Regular tag */
1736 case '%':
1737 if ((ret = parse_token(wps_bufptr, data)) < 0)
1739 fail = PARSE_FAIL_COND_INVALID_PARAM;
1740 break;
1742 else if (level >= WPS_MAX_COND_LEVEL - 1)
1744 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1745 break;
1747 wps_bufptr += ret;
1748 break;
1750 /* Alternating sublines separator */
1751 case ';':
1752 if (level >= 0) /* there are unclosed conditionals */
1754 fail = PARSE_FAIL_UNCLOSED_COND;
1755 break;
1758 if (!skin_start_new_subline(curr_line, data->num_tokens))
1759 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1761 break;
1763 /* Conditional list start */
1764 case '<':
1765 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1767 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1768 break;
1770 wps_bufptr += check_feature_tag(wps_bufptr,
1771 data->tokens[data->num_tokens-1].type);
1772 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1773 lastcond[level] = data->num_tokens++;
1774 break;
1776 /* Conditional list end */
1777 case '>':
1778 if (level < 0) /* not in a conditional, invalid char */
1780 fail = PARSE_FAIL_INVALID_CHAR;
1781 break;
1784 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1785 if (lastcond[level])
1786 data->tokens[lastcond[level]].value.i = data->num_tokens;
1787 else
1789 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1790 break;
1793 lastcond[level] = 0;
1794 data->num_tokens++;
1795 data->tokens[condindex[level]].value.i = numoptions[level];
1796 level--;
1797 break;
1799 /* Conditional list option */
1800 case '|':
1801 if (level < 0) /* not in a conditional, invalid char */
1803 fail = PARSE_FAIL_INVALID_CHAR;
1804 break;
1807 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1808 if (lastcond[level])
1809 data->tokens[lastcond[level]].value.i = data->num_tokens;
1810 else
1812 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1813 break;
1816 lastcond[level] = data->num_tokens;
1817 numoptions[level]++;
1818 data->num_tokens++;
1819 break;
1821 /* Comment */
1822 case '#':
1823 if (level >= 0) /* there are unclosed conditionals */
1825 fail = PARSE_FAIL_UNCLOSED_COND;
1826 break;
1829 wps_bufptr += skip_end_of_line(wps_bufptr);
1830 break;
1832 /* End of this line */
1833 case '\n':
1834 if (level >= 0) /* there are unclosed conditionals */
1836 fail = PARSE_FAIL_UNCLOSED_COND;
1837 break;
1839 /* add a new token for the \n so empty lines are correct */
1840 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1841 data->tokens[data->num_tokens].value.c = '\n';
1842 data->tokens[data->num_tokens].next = false;
1843 data->num_tokens++;
1845 if (!skin_start_new_line(curr_vp, data->num_tokens))
1847 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1848 break;
1850 line_number++;
1852 break;
1854 /* String */
1855 default:
1857 unsigned int len = 1;
1858 const char *string_start = wps_bufptr - 1;
1860 /* find the length of the string */
1861 while (*wps_bufptr && *wps_bufptr != '#' &&
1862 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1863 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1864 *wps_bufptr != '|' && *wps_bufptr != '\n')
1866 wps_bufptr++;
1867 len++;
1870 /* look if we already have that string */
1871 char *str;
1872 bool found = false;
1873 struct skin_token_list *list = data->strings;
1874 while (list)
1876 str = (char*)list->token->value.data;
1877 found = (strlen(str) == len &&
1878 strncmp(string_start, str, len) == 0);
1879 if (found)
1880 break; /* break here because the list item is
1881 used if its found */
1882 list = list->next;
1884 /* If a matching string is found, found is true and i is
1885 the index of the string. If not, found is false */
1887 if (!found)
1889 /* new string */
1890 str = (char*)skin_buffer_alloc(len+1);
1891 if (!str)
1893 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1894 break;
1896 strlcpy(str, string_start, len+1);
1897 struct skin_token_list *item =
1898 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1899 if(!item)
1901 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1902 break;
1904 add_to_ll_chain(&data->strings, item);
1906 else
1908 /* another occurrence of an existing string */
1909 data->tokens[data->num_tokens].value.data = list->token->value.data;
1911 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1912 data->num_tokens++;
1914 break;
1918 if (!fail && level >= 0) /* there are unclosed conditionals */
1919 fail = PARSE_FAIL_UNCLOSED_COND;
1921 if (*wps_bufptr && !fail)
1922 /* one of the limits of the while loop was exceeded */
1923 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1925 /* Success! */
1926 curr_line->curr_subline->last_token_idx = data->num_tokens;
1927 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1928 /* freeup unused tokens */
1929 skin_buffer_free_from_front(sizeof(struct wps_token)
1930 * (max_tokens - data->num_tokens));
1932 #if defined(DEBUG) || defined(SIMULATOR)
1933 if (debug)
1935 print_debug_info(data, fail, line_number);
1936 debug_skin_usage();
1938 #else
1939 (void)debug;
1940 #endif
1942 return (fail == 0);
1947 * initial setup of wps_data; does reset everything
1948 * except fields which need to survive, i.e.
1951 static void skin_data_reset(struct wps_data *wps_data)
1953 #ifdef HAVE_LCD_BITMAP
1954 wps_data->images = NULL;
1955 wps_data->progressbars = NULL;
1956 #endif
1957 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1958 wps_data->backdrop = NULL;
1959 #endif
1960 #ifdef HAVE_TOUCHSCREEN
1961 wps_data->touchregions = NULL;
1962 #endif
1963 wps_data->viewports = NULL;
1964 wps_data->strings = NULL;
1965 #ifdef HAVE_ALBUMART
1966 wps_data->albumart = NULL;
1967 if (wps_data->playback_aa_slot >= 0)
1969 playback_release_aa_slot(wps_data->playback_aa_slot);
1970 wps_data->playback_aa_slot = -1;
1972 #endif
1973 wps_data->tokens = NULL;
1974 wps_data->num_tokens = 0;
1976 #ifdef HAVE_LCD_BITMAP
1977 wps_data->peak_meter_enabled = false;
1978 wps_data->wps_sb_tag = false;
1979 wps_data->show_sb_on_wps = false;
1980 #else /* HAVE_LCD_CHARCELLS */
1981 /* progress bars */
1982 int i;
1983 for (i = 0; i < 8; i++)
1985 wps_data->wps_progress_pat[i] = 0;
1987 wps_data->full_line_progressbar = false;
1988 #endif
1989 wps_data->wps_loaded = false;
1992 #ifdef HAVE_LCD_BITMAP
1993 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1995 (void)wps_data; /* only needed for remote targets */
1996 char img_path[MAX_PATH];
1997 get_image_filename(bitmap->data, bmpdir,
1998 img_path, sizeof(img_path));
2000 /* load the image */
2001 int format;
2002 #ifdef HAVE_REMOTE_LCD
2003 if (curr_screen == SCREEN_REMOTE)
2004 format = FORMAT_ANY|FORMAT_REMOTE;
2005 else
2006 #endif
2007 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2009 size_t max_buf;
2010 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2011 bitmap->data = imgbuf;
2012 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2014 if (ret > 0)
2016 skin_buffer_increment(ret, true);
2017 return true;
2019 else
2021 /* Abort if we can't load an image */
2022 DEBUGF("Couldn't load '%s'\n", img_path);
2023 return false;
2027 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2029 struct skin_token_list *list;
2030 bool retval = true; /* return false if a single image failed to load */
2031 /* do the progressbars */
2032 list = wps_data->progressbars;
2033 while (list)
2035 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2036 if (pb->bm.data)
2038 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2039 if (!pb->have_bitmap_pb) /* no success */
2040 retval = false;
2042 list = list->next;
2044 /* regular images */
2045 list = wps_data->images;
2046 while (list)
2048 struct gui_img *img = (struct gui_img*)list->token->value.data;
2049 if (img->bm.data)
2051 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2052 if (img->loaded)
2053 img->subimage_height = img->bm.height / img->num_subimages;
2054 else
2055 retval = false;
2057 list = list->next;
2060 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2061 /* Backdrop load scheme:
2062 * 1) %X|filename|
2063 * 2) load the backdrop from settings
2065 if (wps_data->backdrop)
2067 bool needed = wps_data->backdrop[0] != '-';
2068 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2069 bmpdir, curr_screen);
2070 if (!wps_data->backdrop && needed)
2071 retval = false;
2073 #endif /* has backdrop support */
2075 return retval;
2078 static bool skin_load_fonts(struct wps_data *data)
2080 /* don't spit out after the first failue to aid debugging */
2081 bool success = true;
2082 struct skin_token_list *vp_list;
2083 int font_id;
2084 /* walk though each viewport and assign its font */
2085 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2087 /* first, find the viewports that have a non-sys/ui-font font */
2088 struct skin_viewport *skin_vp =
2089 (struct skin_viewport*)vp_list->token->value.data;
2090 struct viewport *vp = &skin_vp->vp;
2093 if (vp->font <= FONT_UI)
2094 { /* the usual case -> built-in fonts */
2095 #ifdef HAVE_REMOTE_LCD
2096 if (vp->font == FONT_UI)
2097 vp->font += curr_screen;
2098 #endif
2099 continue;
2101 font_id = vp->font;
2103 /* now find the corresponding skin_font */
2104 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2105 if (!font)
2107 DEBUGF("Could not find font %d\n", font_id);
2108 success = false;
2109 continue;
2112 /* load the font - will handle loading the same font again if
2113 * multiple viewports use the same */
2114 if (font->id < 0)
2116 char *bar = strchr(font->name, '|');
2117 *bar = '\0';
2118 font->id = skin_font_load(font->name);
2121 if (font->id < 0)
2123 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2124 font_id, font->name);
2125 success = false;
2126 continue;
2129 /* finally, assign the font_id to the viewport */
2130 vp->font = font->id;
2132 return success;
2135 #endif /* HAVE_LCD_BITMAP */
2137 /* to setup up the wps-data from a format-buffer (isfile = false)
2138 from a (wps-)file (isfile = true)*/
2139 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2140 const char *buf, bool isfile)
2143 if (!wps_data || !buf)
2144 return false;
2145 #ifdef HAVE_ALBUMART
2146 int status;
2147 struct mp3entry *curtrack;
2148 long offset;
2149 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2150 if (wps_data->albumart)
2152 old_aa.state = wps_data->albumart->state;
2153 old_aa.height = wps_data->albumart->height;
2154 old_aa.width = wps_data->albumart->width;
2156 #endif
2157 #ifdef HAVE_LCD_BITMAP
2158 int i;
2159 for (i=0;i<MAXUSERFONTS;i++)
2161 skinfonts[i].id = -1;
2162 skinfonts[i].name = NULL;
2164 #endif
2166 skin_data_reset(wps_data);
2167 curr_screen = screen;
2169 /* alloc default viewport, will be fixed up later */
2170 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2171 if (!curr_vp)
2172 return false;
2173 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2174 if (!list)
2175 return false;
2176 add_to_ll_chain(&wps_data->viewports, list);
2179 /* Initialise the first (default) viewport */
2180 curr_vp->label = VP_DEFAULT_LABEL;
2181 curr_vp->pb = NULL;
2182 curr_vp->hidden_flags = 0;
2183 curr_vp->lines = NULL;
2185 viewport_set_defaults(&curr_vp->vp, screen);
2186 #ifdef HAVE_LCD_BITMAP
2187 curr_vp->vp.font = FONT_UI;
2188 #endif
2190 curr_line = NULL;
2191 if (!skin_start_new_line(curr_vp, 0))
2192 return false;
2194 if (!isfile)
2196 if (wps_parse(wps_data, buf, false))
2198 #ifdef HAVE_LCD_BITMAP
2199 /* load the backdrop */
2200 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2201 skin_data_reset(wps_data);
2202 return false;
2204 #endif
2205 return true;
2207 return false;
2209 else
2211 int fd = open_utf8(buf, O_RDONLY);
2213 if (fd < 0)
2214 return false;
2216 /* get buffer space from the plugin buffer */
2217 size_t buffersize = 0;
2218 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2220 if (!wps_buffer)
2221 return false;
2223 /* copy the file's content to the buffer for parsing,
2224 ensuring that every line ends with a newline char. */
2225 unsigned int start = 0;
2226 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2228 start += strlen(wps_buffer + start);
2229 if (start < buffersize - 1)
2231 wps_buffer[start++] = '\n';
2232 wps_buffer[start] = 0;
2236 close(fd);
2238 if (start <= 0)
2239 return false;
2241 /* parse the WPS source */
2242 if (!wps_parse(wps_data, wps_buffer, true)) {
2243 skin_data_reset(wps_data);
2244 return false;
2247 wps_data->wps_loaded = true;
2249 #ifdef HAVE_LCD_BITMAP
2250 /* get the bitmap dir */
2251 char bmpdir[MAX_PATH];
2252 char *dot = strrchr(buf, '.');
2254 strlcpy(bmpdir, buf, dot - buf + 1);
2255 /* load the bitmaps that were found by the parsing */
2256 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2257 skin_data_reset(wps_data);
2258 wps_data->wps_loaded = false;
2259 return false;
2261 if (!skin_load_fonts(wps_data))
2263 skin_data_reset(wps_data);
2264 wps_data->wps_loaded = false;
2265 return false;
2267 #endif
2268 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2269 status = audio_status();
2270 if (status & AUDIO_STATUS_PLAY)
2272 struct skin_albumart *aa = wps_data->albumart;
2273 if (aa && ((aa->state && !old_aa.state) ||
2274 (aa->state &&
2275 (((old_aa.height != aa->height) ||
2276 (old_aa.width != aa->width))))))
2278 curtrack = audio_current_track();
2279 offset = curtrack->offset;
2280 audio_stop();
2281 if (!(status & AUDIO_STATUS_PAUSE))
2282 audio_play(offset);
2285 #endif
2286 #if defined(DEBUG) || defined(SIMULATOR)
2287 debug_skin_usage();
2288 #endif
2289 return true;