remove some gremlins... viewport_set_*() sets the font to the UI font for that screen...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob013baf5aaf125f056246b610f7f712abccecc271
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 default_vp->vp.font = FONT_UI;
549 return skip_end_of_line(wps_bufptr);
552 static int parse_statusbar_disable(const char *wps_bufptr,
553 struct wps_token *token,
554 struct wps_data *wps_data)
556 (void)token; /* Kill warnings */
557 wps_data->wps_sb_tag = true;
558 wps_data->show_sb_on_wps = false;
559 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
560 viewport_set_fullscreen(&default_vp->vp, curr_screen);
561 default_vp->vp.font = FONT_UI;
562 return skip_end_of_line(wps_bufptr);
565 static int get_image_id(int c)
567 if(c >= 'a' && c <= 'z')
568 return c - 'a';
569 else if(c >= 'A' && c <= 'Z')
570 return c - 'A' + 26;
571 else
572 return -1;
575 char *get_image_filename(const char *start, const char* bmpdir,
576 char *buf, int buf_size)
578 const char *end = strchr(start, '|');
579 int bmpdirlen = strlen(bmpdir);
581 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
583 buf[0] = '\0';
584 return NULL;
587 strcpy(buf, bmpdir);
588 buf[bmpdirlen] = '/';
589 memcpy( &buf[bmpdirlen + 1], start, end - start);
590 buf[bmpdirlen + 1 + end - start] = 0;
592 return buf;
595 static int parse_image_display(const char *wps_bufptr,
596 struct wps_token *token,
597 struct wps_data *wps_data)
599 char label = wps_bufptr[0];
600 int subimage;
601 struct gui_img *img;;
603 /* sanity check */
604 img = find_image(label, wps_data);
605 if (!img)
607 token->value.i = label; /* so debug works */
608 return WPS_ERROR_INVALID_PARAM;
611 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
613 if (subimage >= img->num_subimages)
614 return WPS_ERROR_INVALID_PARAM;
616 /* Store sub-image number to display in high bits */
617 token->value.i = label | (subimage << 8);
618 return 2; /* We have consumed 2 bytes */
619 } else {
620 token->value.i = label;
621 return 1; /* We have consumed 1 byte */
625 static int parse_image_load(const char *wps_bufptr,
626 struct wps_token *token,
627 struct wps_data *wps_data)
629 const char *ptr = wps_bufptr;
630 const char *pos;
631 const char* filename;
632 const char* id;
633 const char *newline;
634 int x,y;
635 struct gui_img *img;
637 /* format: %x|n|filename.bmp|x|y|
638 or %xl|n|filename.bmp|x|y|
639 or %xl|n|filename.bmp|x|y|num_subimages|
642 if (*ptr != '|')
643 return WPS_ERROR_INVALID_PARAM;
645 ptr++;
647 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
648 return WPS_ERROR_INVALID_PARAM;
650 /* Check there is a terminating | */
651 if (*ptr != '|')
652 return WPS_ERROR_INVALID_PARAM;
654 /* check the image number and load state */
655 if(find_image(*id, wps_data))
657 /* Invalid image ID */
658 return WPS_ERROR_INVALID_PARAM;
660 img = skin_buffer_alloc(sizeof(struct gui_img));
661 if (!img)
662 return WPS_ERROR_INVALID_PARAM;
663 /* save a pointer to the filename */
664 img->bm.data = (char*)filename;
665 img->label = *id;
666 img->x = x;
667 img->y = y;
668 img->num_subimages = 1;
669 img->always_display = false;
671 /* save current viewport */
672 img->vp = &curr_vp->vp;
674 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
676 img->always_display = true;
678 else
680 /* Parse the (optional) number of sub-images */
681 ptr++;
682 newline = strchr(ptr, '\n');
683 pos = strchr(ptr, '|');
684 if (pos && pos < newline)
685 img->num_subimages = atoi(ptr);
687 if (img->num_subimages <= 0)
688 return WPS_ERROR_INVALID_PARAM;
690 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
691 if (!item)
692 return WPS_ERROR_INVALID_PARAM;
693 add_to_ll_chain(&wps_data->images, item);
695 /* Skip the rest of the line */
696 return skip_end_of_line(wps_bufptr);
698 struct skin_font {
699 int id; /* the id from font_load */
700 char *name; /* filename without path and extension */
702 static struct skin_font skinfonts[MAXUSERFONTS];
703 static int parse_font_load(const char *wps_bufptr,
704 struct wps_token *token, struct wps_data *wps_data)
706 (void)wps_data; (void)token;
707 const char *ptr = wps_bufptr;
708 int id;
709 char *filename;
711 if (*ptr != '|')
712 return WPS_ERROR_INVALID_PARAM;
714 ptr++;
716 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
717 return WPS_ERROR_INVALID_PARAM;
719 /* Check there is a terminating | */
720 if (*ptr != '|')
721 return WPS_ERROR_INVALID_PARAM;
723 if (id <= FONT_UI || id >= MAXFONTS-1)
724 return WPS_ERROR_INVALID_PARAM;
725 #if defined(DEBUG) || defined(SIMULATOR)
726 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
728 DEBUGF("font id %d already being used\n", id);
730 #endif
731 /* make sure the filename contains .fnt,
732 * we dont actually use it, but require it anyway */
733 ptr = strchr(filename, '.');
734 if (!ptr || strncmp(ptr, ".fnt|", 5))
735 return WPS_ERROR_INVALID_PARAM;
736 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
737 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
739 return skip_end_of_line(wps_bufptr);
743 static int parse_viewport_display(const char *wps_bufptr,
744 struct wps_token *token,
745 struct wps_data *wps_data)
747 (void)wps_data;
748 char letter = wps_bufptr[0];
750 if (letter < 'a' || letter > 'z')
752 /* invalid viewport tag */
753 return WPS_ERROR_INVALID_PARAM;
755 token->value.i = letter;
756 return 1;
759 #ifdef HAVE_LCD_BITMAP
760 static int parse_playlistview_text(struct playlistviewer *viewer,
761 enum info_line_type line, char* text)
763 int cur_string = 0;
764 const struct wps_tag *tag;
765 int taglen = 0;
766 const char *start = text;
767 if (*text != '|')
768 return -1;
769 text++;
770 viewer->lines[line].count = 0;
771 viewer->lines[line].scroll = false;
772 while (*text != '|')
774 if (*text == '%') /* it is a token of some type */
776 text++;
777 taglen = 0;
778 switch(*text)
780 case '%':
781 case '<':
782 case '|':
783 case '>':
784 case ';':
785 case '#':
786 /* escaped characters */
787 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
788 viewer->lines[line].strings[cur_string][0] = *text;
789 viewer->lines[line].strings[cur_string++][1] = '\0';
790 break;
791 default:
792 for (tag = all_tags;
793 strncmp(text, tag->name, strlen(tag->name)) != 0;
794 tag++) ;
795 /* %s isnt stored as a tag so manually check for it */
796 if (tag->type == WPS_NO_TOKEN)
798 if (!strncmp(tag->name, "s", 1))
800 viewer->lines[line].scroll = true;
801 taglen = 1;
804 else if (tag->type == WPS_TOKEN_UNKNOWN)
806 int i = 0;
807 /* just copy the string */
808 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
809 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
811 viewer->lines[line].strings[cur_string][i] = text[i];
812 i++;
814 viewer->lines[line].strings[cur_string][i] = '\0';
815 cur_string++;
816 taglen = i;
818 else
820 if (tag->parse_func)
822 /* unsupported tag, reject */
823 return -1;
825 taglen = strlen(tag->name);
826 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
828 text += taglen;
831 else
833 /* regular string */
834 int i = 0;
835 /* just copy the string */
836 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
837 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
839 viewer->lines[line].strings[cur_string][i] = text[i];
840 i++;
842 viewer->lines[line].strings[cur_string][i] = '\0';
843 cur_string++;
844 text += i;
847 return text - start;
851 static int parse_playlistview(const char *wps_bufptr,
852 struct wps_token *token, struct wps_data *wps_data)
854 (void)wps_data;
855 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
856 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
857 char *ptr = strchr(wps_bufptr, '|');
858 int length;
859 if (!viewer || !ptr)
860 return WPS_ERROR_INVALID_PARAM;
861 viewer->vp = &curr_vp->vp;
862 viewer->show_icons = true;
863 viewer->start_offset = atoi(ptr+1);
864 token->value.data = (void*)viewer;
865 ptr = strchr(ptr+1, '|');
866 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
867 if (length < 0)
868 return WPS_ERROR_INVALID_PARAM;
869 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
870 if (length < 0)
871 return WPS_ERROR_INVALID_PARAM;
873 return skip_end_of_line(wps_bufptr);
875 #endif
877 static int parse_viewport(const char *wps_bufptr,
878 struct wps_token *token,
879 struct wps_data *wps_data)
881 (void)token; /* Kill warnings */
882 const char *ptr = wps_bufptr;
884 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
886 /* check for the optional letter to signify its a hideable viewport */
887 /* %Vl|<label>|<rest of tags>| */
888 skin_vp->hidden_flags = 0;
889 skin_vp->label = VP_NO_LABEL;
890 skin_vp->pb = NULL;
891 skin_vp->lines = NULL;
892 if (curr_line)
894 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
895 - (wps_data->num_tokens > 0 ? 1 : 0);
898 curr_line = NULL;
899 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
900 return WPS_ERROR_INVALID_PARAM;
903 if (*ptr == 'i')
905 skin_vp->label = VP_INFO_LABEL;
906 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
907 ++ptr;
909 else if (*ptr == 'l')
911 if (*(ptr+1) == '|')
913 char label = *(ptr+2);
914 if (label >= 'a' && label <= 'z')
916 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
917 skin_vp->label = label;
919 else
920 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
921 ptr += 3;
924 if (*ptr != '|')
925 return WPS_ERROR_INVALID_PARAM;
927 ptr++;
928 struct viewport *vp = &skin_vp->vp;
929 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
930 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
931 return WPS_ERROR_INVALID_PARAM;
933 /* Check for trailing | */
934 if (*ptr != '|')
935 return WPS_ERROR_INVALID_PARAM;
937 if (follow_lang_direction && lang_is_rtl())
939 vp->flags |= VP_FLAG_ALIGN_RIGHT;
940 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
942 else
943 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
945 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
946 if (!list)
947 return WPS_ERROR_INVALID_PARAM;
948 add_to_ll_chain(&wps_data->viewports, list);
949 curr_vp = skin_vp;
950 /* Skip the rest of the line */
951 return skip_end_of_line(wps_bufptr);
954 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
955 static int parse_image_special(const char *wps_bufptr,
956 struct wps_token *token,
957 struct wps_data *wps_data)
959 (void)wps_data; /* kill warning */
960 (void)token;
961 const char *pos = NULL;
962 const char *newline;
963 bool error = false;
965 pos = strchr(wps_bufptr + 1, '|');
966 newline = strchr(wps_bufptr, '\n');
968 error = (pos > newline);
971 #if LCD_DEPTH > 1
972 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
974 /* format: %X|filename.bmp| or %Xd */
975 if (*(wps_bufptr) == 'd')
977 wps_data->backdrop = NULL;
978 return skip_end_of_line(wps_bufptr);
980 else if (!error)
981 wps_data->backdrop = (char*)wps_bufptr + 1;
983 #endif
984 if (error)
985 return WPS_ERROR_INVALID_PARAM;
986 /* Skip the rest of the line */
987 return skip_end_of_line(wps_bufptr);
989 #endif
991 #endif /* HAVE_LCD_BITMAP */
993 static int parse_setting_and_lang(const char *wps_bufptr,
994 struct wps_token *token,
995 struct wps_data *wps_data)
997 /* NOTE: both the string validations that happen in here will
998 * automatically PASS on checkwps because its too hard to get
999 * settings_list.c and englinsh.lang built for it.
1000 * If that ever changes remove the #ifndef __PCTOOL__'s here
1002 (void)wps_data;
1003 const char *ptr = wps_bufptr;
1004 const char *end;
1005 int i = 0;
1006 char temp[64];
1008 /* Find the setting's cfg_name */
1009 if (*ptr != '|')
1010 return WPS_ERROR_INVALID_PARAM;
1011 ptr++;
1012 end = strchr(ptr,'|');
1013 if (!end)
1014 return WPS_ERROR_INVALID_PARAM;
1015 strlcpy(temp, ptr,end-ptr+1);
1017 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1019 #ifndef __PCTOOL__
1020 i = lang_english_to_id(temp);
1021 if (i < 0)
1022 return WPS_ERROR_INVALID_PARAM;
1023 #endif
1025 else
1027 /* Find the setting */
1028 for (i=0; i<nb_settings; i++)
1029 if (settings[i].cfg_name &&
1030 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1031 /* prevent matches on cfg_name prefixes */
1032 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1033 break;
1034 #ifndef __PCTOOL__
1035 if (i == nb_settings)
1036 return WPS_ERROR_INVALID_PARAM;
1037 #endif
1039 /* Store the setting number */
1040 token->value.i = i;
1042 /* Skip the rest of the line */
1043 return end-ptr+2;
1047 static int parse_dir_level(const char *wps_bufptr,
1048 struct wps_token *token,
1049 struct wps_data *wps_data)
1051 char val[] = { *wps_bufptr, '\0' };
1052 token->value.i = atoi(val);
1053 (void)wps_data; /* Kill warnings */
1054 return 1;
1057 static int parse_timeout(const char *wps_bufptr,
1058 struct wps_token *token,
1059 struct wps_data *wps_data)
1061 int skip = 0;
1062 int val = 0;
1063 bool have_point = false;
1064 bool have_tenth = false;
1066 (void)wps_data; /* Kill the warning */
1068 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1070 if (*wps_bufptr != '.')
1072 val *= 10;
1073 val += *wps_bufptr - '0';
1074 if (have_point)
1076 have_tenth = true;
1077 wps_bufptr++;
1078 skip++;
1079 break;
1082 else
1083 have_point = true;
1085 wps_bufptr++;
1086 skip++;
1089 if (have_tenth == false)
1090 val *= 10;
1092 if (val == 0 && skip == 0)
1094 /* decide what to do if no value was specified */
1095 switch (token->type)
1097 case WPS_TOKEN_SUBLINE_TIMEOUT:
1098 return -1;
1099 case WPS_TOKEN_BUTTON_VOLUME:
1100 val = 10;
1101 break;
1104 token->value.i = val;
1106 return skip;
1109 static int parse_progressbar(const char *wps_bufptr,
1110 struct wps_token *token,
1111 struct wps_data *wps_data)
1113 /* %pb or %pb|filename|x|y|width|height|
1114 using - for any of the params uses "sane" values */
1115 #ifdef HAVE_LCD_BITMAP
1116 enum {
1117 PB_FILENAME = 0,
1118 PB_X,
1119 PB_Y,
1120 PB_WIDTH,
1121 PB_HEIGHT
1123 const char *filename;
1124 int x, y, height, width;
1125 uint32_t set = 0;
1126 const char *ptr = wps_bufptr;
1127 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1128 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1130 if (!pb || !item)
1131 return WPS_ERROR_INVALID_PARAM;
1133 struct viewport *vp = &curr_vp->vp;
1134 /* we need to know what line number (viewport relative) this pb is,
1135 * so count them... */
1136 int line_num = -1;
1137 struct skin_line *line = curr_vp->lines;
1138 while (line)
1140 line_num++;
1141 line = line->next;
1143 pb->have_bitmap_pb = false;
1144 pb->bm.data = NULL; /* no bitmap specified */
1145 pb->follow_lang_direction = follow_lang_direction > 0;
1147 if (*wps_bufptr != '|') /* regular old style */
1149 pb->x = 0;
1150 pb->width = vp->width;
1151 pb->height = SYSFONT_HEIGHT-2;
1152 pb->y = -line_num - 1; /* Will be computed during the rendering */
1154 curr_vp->pb = pb;
1155 add_to_ll_chain(&wps_data->progressbars, item);
1156 return 0;
1158 ptr = wps_bufptr + 1;
1160 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1161 &x, &y, &width, &height)))
1162 return WPS_ERROR_INVALID_PARAM;
1164 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1165 pb->bm.data = (char*)filename;
1167 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1168 pb->x = x;
1169 else
1170 pb->x = vp->x;
1172 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1174 /* A zero width causes a divide-by-zero error later, so reject it */
1175 if (width == 0)
1176 return WPS_ERROR_INVALID_PARAM;
1178 pb->width = width;
1180 else
1181 pb->width = vp->width - pb->x;
1183 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1185 /* A zero height makes no sense - reject it */
1186 if (height == 0)
1187 return WPS_ERROR_INVALID_PARAM;
1189 pb->height = height;
1191 else
1193 if (vp->font > FONT_UI)
1194 pb->height = -1; /* calculate at display time */
1195 else
1197 #ifndef __PCTOOL__
1198 pb->height = font_get(vp->font)->height;
1199 #else
1200 pb->height = 8;
1201 #endif
1205 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1206 pb->y = y;
1207 else
1208 pb->y = -line_num - 1; /* Will be computed during the rendering */
1210 curr_vp->pb = pb;
1211 add_to_ll_chain(&wps_data->progressbars, item);
1213 /* Skip the rest of the line */
1214 return skip_end_of_line(wps_bufptr)-1;
1215 #else
1216 (void)token;
1218 if (*(wps_bufptr-1) == 'f')
1219 wps_data->full_line_progressbar = true;
1220 else
1221 wps_data->full_line_progressbar = false;
1223 return 0;
1225 #endif
1228 #ifdef HAVE_ALBUMART
1229 static int parse_int(const char *newline, const char **_pos, int *num)
1231 *_pos = parse_list("d", NULL, '|', *_pos, num);
1233 return (!*_pos || *_pos > newline || **_pos != '|');
1236 static int parse_albumart_load(const char *wps_bufptr,
1237 struct wps_token *token,
1238 struct wps_data *wps_data)
1240 const char *_pos, *newline;
1241 bool parsing;
1242 struct dim dimensions;
1243 int albumart_slot;
1244 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1245 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1246 (void)token; /* silence warning */
1247 if (!aa)
1248 return skip_end_of_line(wps_bufptr);
1250 /* reset albumart info in wps */
1251 aa->width = -1;
1252 aa->height = -1;
1253 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1254 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1255 aa->vp = &curr_vp->vp;
1257 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1259 newline = strchr(wps_bufptr, '\n');
1261 _pos = wps_bufptr;
1263 if (*_pos != '|')
1264 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1266 ++_pos;
1268 /* initial validation and parsing of x component */
1269 if (parse_int(newline, &_pos, &aa->x))
1270 return WPS_ERROR_INVALID_PARAM;
1272 ++_pos;
1274 /* initial validation and parsing of y component */
1275 if (parse_int(newline, &_pos, &aa->y))
1276 return WPS_ERROR_INVALID_PARAM;
1278 /* parsing width field */
1279 parsing = true;
1280 while (parsing)
1282 /* apply each modifier in turn */
1283 ++_pos;
1284 switch (*_pos)
1286 case 'l':
1287 case 'L':
1288 case '+':
1289 if (swap_for_rtl)
1290 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1291 else
1292 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1293 break;
1294 case 'c':
1295 case 'C':
1296 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1297 break;
1298 case 'r':
1299 case 'R':
1300 case '-':
1301 if (swap_for_rtl)
1302 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1303 else
1304 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1305 break;
1306 case 'd':
1307 case 'D':
1308 case 'i':
1309 case 'I':
1310 case 's':
1311 case 'S':
1312 /* simply ignored */
1313 break;
1314 default:
1315 parsing = false;
1316 break;
1319 /* extract max width data */
1320 if (*_pos != '|')
1322 if (parse_int(newline, &_pos, &aa->width))
1323 return WPS_ERROR_INVALID_PARAM;
1326 /* parsing height field */
1327 parsing = true;
1328 while (parsing)
1330 /* apply each modifier in turn */
1331 ++_pos;
1332 switch (*_pos)
1334 case 't':
1335 case 'T':
1336 case '-':
1337 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1338 break;
1339 case 'c':
1340 case 'C':
1341 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1342 break;
1343 case 'b':
1344 case 'B':
1345 case '+':
1346 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1347 break;
1348 case 'd':
1349 case 'D':
1350 case 'i':
1351 case 'I':
1352 case 's':
1353 case 'S':
1354 /* simply ignored */
1355 break;
1356 default:
1357 parsing = false;
1358 break;
1361 /* extract max height data */
1362 if (*_pos != '|')
1364 if (parse_int(newline, &_pos, &aa->height))
1365 return WPS_ERROR_INVALID_PARAM;
1368 /* if we got here, we parsed everything ok .. ! */
1369 if (aa->width < 0)
1370 aa->width = 0;
1371 else if (aa->width > LCD_WIDTH)
1372 aa->width = LCD_WIDTH;
1374 if (aa->height < 0)
1375 aa->height = 0;
1376 else if (aa->height > LCD_HEIGHT)
1377 aa->height = LCD_HEIGHT;
1379 if (swap_for_rtl)
1380 aa->x = LCD_WIDTH - (aa->x + aa->width);
1382 aa->state = WPS_ALBUMART_LOAD;
1383 aa->draw = false;
1384 wps_data->albumart = aa;
1386 dimensions.width = aa->width;
1387 dimensions.height = aa->height;
1389 albumart_slot = playback_claim_aa_slot(&dimensions);
1391 if (0 <= albumart_slot)
1392 wps_data->playback_aa_slot = albumart_slot;
1394 /* Skip the rest of the line */
1395 return skip_end_of_line(wps_bufptr);
1398 static int parse_albumart_display(const char *wps_bufptr,
1399 struct wps_token *token,
1400 struct wps_data *wps_data)
1402 (void)wps_bufptr;
1403 struct wps_token *prev = token-1;
1404 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1406 token->type = WPS_TOKEN_ALBUMART_FOUND;
1408 else if (wps_data->albumart)
1410 wps_data->albumart->vp = &curr_vp->vp;
1412 #if 0
1413 /* the old code did this so keep it here for now...
1414 * this is to allow the posibility to showing the next tracks AA! */
1415 if (wps_bufptr+1 == 'n')
1416 return 1;
1417 #endif
1418 return 0;
1420 #endif /* HAVE_ALBUMART */
1422 #ifdef HAVE_TOUCHSCREEN
1424 struct touchaction {const char* s; int action;};
1425 static const struct touchaction touchactions[] = {
1426 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1427 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1428 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1429 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1430 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1431 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1432 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1433 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1435 static int parse_touchregion(const char *wps_bufptr,
1436 struct wps_token *token, struct wps_data *wps_data)
1438 (void)token;
1439 unsigned i, imax;
1440 struct touchregion *region = NULL;
1441 const char *ptr = wps_bufptr;
1442 const char *action;
1443 const char pb_string[] = "progressbar";
1444 const char vol_string[] = "volume";
1445 int x,y,w,h;
1447 /* format: %T|x|y|width|height|action|
1448 * if action starts with & the area must be held to happen
1449 * action is one of:
1450 * play - play/pause playback
1451 * stop - stop playback, exit the wps
1452 * prev - prev track
1453 * next - next track
1454 * ffwd - seek forward
1455 * rwd - seek backwards
1456 * menu - go back to the main menu
1457 * browse - go back to the file/db browser
1458 * shuffle - toggle shuffle mode
1459 * repmode - cycle the repeat mode
1460 * quickscreen - go into the quickscreen
1461 * contextmenu - open the context menu
1462 * playlist - go into the playlist
1463 * pitch - go into the pitchscreen
1464 * volup - increase volume by one step
1465 * voldown - decrease volume by one step
1469 if (*ptr != '|')
1470 return WPS_ERROR_INVALID_PARAM;
1471 ptr++;
1473 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1474 return WPS_ERROR_INVALID_PARAM;
1476 /* Check there is a terminating | */
1477 if (*ptr != '|')
1478 return WPS_ERROR_INVALID_PARAM;
1480 region = skin_buffer_alloc(sizeof(struct touchregion));
1481 if (!region)
1482 return WPS_ERROR_INVALID_PARAM;
1484 /* should probably do some bounds checking here with the viewport... but later */
1485 region->action = ACTION_NONE;
1486 region->x = x;
1487 region->y = y;
1488 region->width = w;
1489 region->height = h;
1490 region->wvp = curr_vp;
1491 region->armed = false;
1493 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1494 && *(action + sizeof(pb_string)-1) == '|')
1495 region->type = WPS_TOUCHREGION_SCROLLBAR;
1496 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1497 && *(action + sizeof(vol_string)-1) == '|')
1498 region->type = WPS_TOUCHREGION_VOLUME;
1499 else
1501 region->type = WPS_TOUCHREGION_ACTION;
1503 if (*action == '&')
1505 action++;
1506 region->repeat = true;
1508 else
1509 region->repeat = false;
1511 i = 0;
1512 imax = ARRAYLEN(touchactions);
1513 while ((region->action == ACTION_NONE) &&
1514 (i < imax))
1516 /* try to match with one of our touchregion screens */
1517 int len = strlen(touchactions[i].s);
1518 if (!strncmp(touchactions[i].s, action, len)
1519 && *(action+len) == '|')
1520 region->action = touchactions[i].action;
1521 i++;
1523 if (region->action == ACTION_NONE)
1524 return WPS_ERROR_INVALID_PARAM;
1526 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1527 if (!item)
1528 return WPS_ERROR_INVALID_PARAM;
1529 add_to_ll_chain(&wps_data->touchregions, item);
1530 return skip_end_of_line(wps_bufptr);
1532 #endif
1534 /* Parse a generic token from the given string. Return the length read */
1535 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1537 int skip = 0, taglen = 0, ret;
1538 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1539 const struct wps_tag *tag;
1540 memset(token, 0, sizeof(*token));
1542 switch(*wps_bufptr)
1545 case '%':
1546 case '<':
1547 case '|':
1548 case '>':
1549 case ';':
1550 case '#':
1551 /* escaped characters */
1552 token->type = WPS_TOKEN_CHARACTER;
1553 token->value.c = *wps_bufptr;
1554 taglen = 1;
1555 wps_data->num_tokens++;
1556 break;
1558 case '?':
1559 /* conditional tag */
1560 token->type = WPS_TOKEN_CONDITIONAL;
1561 level++;
1562 condindex[level] = wps_data->num_tokens;
1563 numoptions[level] = 1;
1564 wps_data->num_tokens++;
1565 ret = parse_token(wps_bufptr + 1, wps_data);
1566 if (ret < 0) return ret;
1567 taglen = 1 + ret;
1568 break;
1570 default:
1571 /* find what tag we have */
1572 for (tag = all_tags;
1573 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1574 tag++) ;
1576 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1577 token->type = tag->type;
1578 curr_line->curr_subline->line_type |= tag->refresh_type;
1580 /* if the tag has a special parsing function, we call it */
1581 if (tag->parse_func)
1583 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1584 if (ret < 0) return ret;
1585 skip += ret;
1588 /* Some tags we don't want to save as tokens */
1589 if (tag->type == WPS_NO_TOKEN)
1590 break;
1592 /* tags that start with 'F', 'I' or 'D' are for the next file */
1593 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1594 *(tag->name) == 'D')
1595 token->next = true;
1597 wps_data->num_tokens++;
1598 break;
1601 skip += taglen;
1602 return skip;
1607 * Returns the number of bytes to skip the buf pointer to access the false
1608 * branch in a _binary_ conditional
1610 * That is:
1611 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1612 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1614 * depending on the features of a target it's not called from check_feature_tag,
1615 * hence the __attribute__ or it issues compiler warnings
1619 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1620 static int find_false_branch(const char *wps_bufptr)
1622 const char *buf = wps_bufptr;
1623 /* wps_bufptr is after the opening '<', hence level = 1*/
1624 int level = 1;
1625 char ch;
1628 ch = *buf;
1629 if (ch == '%')
1630 { /* filter out the characters we check later if they're printed
1631 * as literals */
1632 ch = *(++buf);
1633 if (ch == '<' || ch == '>' || ch == '|')
1634 continue;
1635 /* else: some tags/printed literals we skip over */
1637 else if (ch == '<') /* nested conditional */
1638 level++;
1639 else if (ch == '>')
1640 { /* closed our or a nested conditional,
1641 * do NOT skip over the '>' so that wps_parse() sees it for closing
1642 * if it is the closing one for our conditional */
1643 level--;
1645 else if (ch == '|' && level == 1)
1646 { /* we found our separator, point before and get out */
1647 break;
1649 /* if level is 0, we don't have a false branch */
1650 } while (level > 0 && *(++buf));
1652 return buf - wps_bufptr;
1656 * returns the number of bytes to get the appropriate branch of a binary
1657 * conditional
1659 * That means:
1660 * - if a feature is available, it returns 0 to not skip anything
1661 * - if the feature is not available, skip to the false branch and don't
1662 * parse the true branch at all
1664 * */
1665 static int check_feature_tag(const char *wps_bufptr, const int type)
1667 (void)wps_bufptr;
1668 switch (type)
1670 case WPS_TOKEN_RTC_PRESENT:
1671 #if CONFIG_RTC
1672 return 0;
1673 #else
1674 return find_false_branch(wps_bufptr);
1675 #endif
1676 case WPS_TOKEN_HAVE_RECORDING:
1677 #ifdef HAVE_RECORDING
1678 return 0;
1679 #else
1680 return find_false_branch(wps_bufptr);
1681 #endif
1682 default: /* not a tag we care about, just don't skip */
1683 return 0;
1688 /* Parses the WPS.
1689 data is the pointer to the structure where the parsed WPS should be stored.
1690 It is initialised.
1691 wps_bufptr points to the string containing the WPS tags */
1692 #define TOKEN_BLOCK_SIZE 128
1693 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1695 if (!data || !wps_bufptr || !*wps_bufptr)
1696 return false;
1697 enum wps_parse_error fail = PARSE_OK;
1698 int ret;
1699 int max_tokens = TOKEN_BLOCK_SIZE;
1700 size_t buf_free = 0;
1701 line_number = 0;
1702 level = -1;
1704 /* allocate enough RAM for a reasonable skin, grow as needed.
1705 * Free any used RAM before loading the images to be 100% RAM efficient */
1706 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1707 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1708 return false;
1709 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1710 data->num_tokens = 0;
1712 #if LCD_DEPTH > 1
1713 /* Backdrop defaults to the setting unless %X is used, so set it now */
1714 if (global_settings.backdrop_file[0])
1716 data->backdrop = "-";
1718 #endif
1720 while (*wps_bufptr && !fail)
1722 if (follow_lang_direction)
1723 follow_lang_direction--;
1724 /* first make sure there is enough room for tokens */
1725 if (max_tokens <= data->num_tokens + 5)
1727 int extra_tokens = TOKEN_BLOCK_SIZE;
1728 size_t needed = extra_tokens * sizeof(struct wps_token);
1729 /* do some smarts here to grow the array a bit */
1730 if (skin_buffer_freespace() < needed)
1732 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1733 break;
1735 skin_buffer_increment(needed, false);
1736 max_tokens += extra_tokens;
1739 switch(*wps_bufptr++)
1742 /* Regular tag */
1743 case '%':
1744 if ((ret = parse_token(wps_bufptr, data)) < 0)
1746 fail = PARSE_FAIL_COND_INVALID_PARAM;
1747 break;
1749 else if (level >= WPS_MAX_COND_LEVEL - 1)
1751 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1752 break;
1754 wps_bufptr += ret;
1755 break;
1757 /* Alternating sublines separator */
1758 case ';':
1759 if (level >= 0) /* there are unclosed conditionals */
1761 fail = PARSE_FAIL_UNCLOSED_COND;
1762 break;
1765 if (!skin_start_new_subline(curr_line, data->num_tokens))
1766 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1768 break;
1770 /* Conditional list start */
1771 case '<':
1772 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1774 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1775 break;
1777 wps_bufptr += check_feature_tag(wps_bufptr,
1778 data->tokens[data->num_tokens-1].type);
1779 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1780 lastcond[level] = data->num_tokens++;
1781 break;
1783 /* Conditional list end */
1784 case '>':
1785 if (level < 0) /* not in a conditional, invalid char */
1787 fail = PARSE_FAIL_INVALID_CHAR;
1788 break;
1791 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1792 if (lastcond[level])
1793 data->tokens[lastcond[level]].value.i = data->num_tokens;
1794 else
1796 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1797 break;
1800 lastcond[level] = 0;
1801 data->num_tokens++;
1802 data->tokens[condindex[level]].value.i = numoptions[level];
1803 level--;
1804 break;
1806 /* Conditional list option */
1807 case '|':
1808 if (level < 0) /* not in a conditional, invalid char */
1810 fail = PARSE_FAIL_INVALID_CHAR;
1811 break;
1814 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1815 if (lastcond[level])
1816 data->tokens[lastcond[level]].value.i = data->num_tokens;
1817 else
1819 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1820 break;
1823 lastcond[level] = data->num_tokens;
1824 numoptions[level]++;
1825 data->num_tokens++;
1826 break;
1828 /* Comment */
1829 case '#':
1830 if (level >= 0) /* there are unclosed conditionals */
1832 fail = PARSE_FAIL_UNCLOSED_COND;
1833 break;
1836 wps_bufptr += skip_end_of_line(wps_bufptr);
1837 break;
1839 /* End of this line */
1840 case '\n':
1841 if (level >= 0) /* there are unclosed conditionals */
1843 fail = PARSE_FAIL_UNCLOSED_COND;
1844 break;
1846 /* add a new token for the \n so empty lines are correct */
1847 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1848 data->tokens[data->num_tokens].value.c = '\n';
1849 data->tokens[data->num_tokens].next = false;
1850 data->num_tokens++;
1852 if (!skin_start_new_line(curr_vp, data->num_tokens))
1854 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1855 break;
1857 line_number++;
1859 break;
1861 /* String */
1862 default:
1864 unsigned int len = 1;
1865 const char *string_start = wps_bufptr - 1;
1867 /* find the length of the string */
1868 while (*wps_bufptr && *wps_bufptr != '#' &&
1869 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1870 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1871 *wps_bufptr != '|' && *wps_bufptr != '\n')
1873 wps_bufptr++;
1874 len++;
1877 /* look if we already have that string */
1878 char *str;
1879 bool found = false;
1880 struct skin_token_list *list = data->strings;
1881 while (list)
1883 str = (char*)list->token->value.data;
1884 found = (strlen(str) == len &&
1885 strncmp(string_start, str, len) == 0);
1886 if (found)
1887 break; /* break here because the list item is
1888 used if its found */
1889 list = list->next;
1891 /* If a matching string is found, found is true and i is
1892 the index of the string. If not, found is false */
1894 if (!found)
1896 /* new string */
1897 str = (char*)skin_buffer_alloc(len+1);
1898 if (!str)
1900 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1901 break;
1903 strlcpy(str, string_start, len+1);
1904 struct skin_token_list *item =
1905 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1906 if(!item)
1908 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1909 break;
1911 add_to_ll_chain(&data->strings, item);
1913 else
1915 /* another occurrence of an existing string */
1916 data->tokens[data->num_tokens].value.data = list->token->value.data;
1918 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1919 data->num_tokens++;
1921 break;
1925 if (!fail && level >= 0) /* there are unclosed conditionals */
1926 fail = PARSE_FAIL_UNCLOSED_COND;
1928 if (*wps_bufptr && !fail)
1929 /* one of the limits of the while loop was exceeded */
1930 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1932 /* Success! */
1933 curr_line->curr_subline->last_token_idx = data->num_tokens;
1934 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1935 /* freeup unused tokens */
1936 skin_buffer_free_from_front(sizeof(struct wps_token)
1937 * (max_tokens - data->num_tokens));
1939 #if defined(DEBUG) || defined(SIMULATOR)
1940 if (debug)
1942 print_debug_info(data, fail, line_number);
1943 debug_skin_usage();
1945 #else
1946 (void)debug;
1947 #endif
1949 return (fail == 0);
1954 * initial setup of wps_data; does reset everything
1955 * except fields which need to survive, i.e.
1958 static void skin_data_reset(struct wps_data *wps_data)
1960 #ifdef HAVE_LCD_BITMAP
1961 wps_data->images = NULL;
1962 wps_data->progressbars = NULL;
1963 #endif
1964 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1965 wps_data->backdrop = NULL;
1966 #endif
1967 #ifdef HAVE_TOUCHSCREEN
1968 wps_data->touchregions = NULL;
1969 #endif
1970 wps_data->viewports = NULL;
1971 wps_data->strings = NULL;
1972 #ifdef HAVE_ALBUMART
1973 wps_data->albumart = NULL;
1974 if (wps_data->playback_aa_slot >= 0)
1976 playback_release_aa_slot(wps_data->playback_aa_slot);
1977 wps_data->playback_aa_slot = -1;
1979 #endif
1980 wps_data->tokens = NULL;
1981 wps_data->num_tokens = 0;
1983 #ifdef HAVE_LCD_BITMAP
1984 wps_data->peak_meter_enabled = false;
1985 wps_data->wps_sb_tag = false;
1986 wps_data->show_sb_on_wps = false;
1987 #else /* HAVE_LCD_CHARCELLS */
1988 /* progress bars */
1989 int i;
1990 for (i = 0; i < 8; i++)
1992 wps_data->wps_progress_pat[i] = 0;
1994 wps_data->full_line_progressbar = false;
1995 #endif
1996 wps_data->wps_loaded = false;
1999 #ifdef HAVE_LCD_BITMAP
2000 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2002 (void)wps_data; /* only needed for remote targets */
2003 char img_path[MAX_PATH];
2004 get_image_filename(bitmap->data, bmpdir,
2005 img_path, sizeof(img_path));
2007 /* load the image */
2008 int format;
2009 #ifdef HAVE_REMOTE_LCD
2010 if (curr_screen == SCREEN_REMOTE)
2011 format = FORMAT_ANY|FORMAT_REMOTE;
2012 else
2013 #endif
2014 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2016 size_t max_buf;
2017 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2018 bitmap->data = imgbuf;
2019 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2021 if (ret > 0)
2023 skin_buffer_increment(ret, true);
2024 return true;
2026 else
2028 /* Abort if we can't load an image */
2029 DEBUGF("Couldn't load '%s'\n", img_path);
2030 return false;
2034 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2036 struct skin_token_list *list;
2037 bool retval = true; /* return false if a single image failed to load */
2038 /* do the progressbars */
2039 list = wps_data->progressbars;
2040 while (list)
2042 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2043 if (pb->bm.data)
2045 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2046 if (!pb->have_bitmap_pb) /* no success */
2047 retval = false;
2049 list = list->next;
2051 /* regular images */
2052 list = wps_data->images;
2053 while (list)
2055 struct gui_img *img = (struct gui_img*)list->token->value.data;
2056 if (img->bm.data)
2058 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2059 if (img->loaded)
2060 img->subimage_height = img->bm.height / img->num_subimages;
2061 else
2062 retval = false;
2064 list = list->next;
2067 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2068 /* Backdrop load scheme:
2069 * 1) %X|filename|
2070 * 2) load the backdrop from settings
2072 if (wps_data->backdrop)
2074 bool needed = wps_data->backdrop[0] != '-';
2075 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2076 bmpdir, curr_screen);
2077 if (!wps_data->backdrop && needed)
2078 retval = false;
2080 #endif /* has backdrop support */
2082 return retval;
2085 static bool skin_load_fonts(struct wps_data *data)
2087 /* don't spit out after the first failue to aid debugging */
2088 bool success = true;
2089 struct skin_token_list *vp_list;
2090 int font_id;
2091 /* walk though each viewport and assign its font */
2092 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2094 /* first, find the viewports that have a non-sys/ui-font font */
2095 struct skin_viewport *skin_vp =
2096 (struct skin_viewport*)vp_list->token->value.data;
2097 struct viewport *vp = &skin_vp->vp;
2100 if (vp->font <= FONT_UI)
2101 { /* the usual case -> built-in fonts */
2102 #ifdef HAVE_REMOTE_LCD
2103 if (vp->font == FONT_UI)
2104 vp->font += curr_screen;
2105 #endif
2106 continue;
2108 font_id = vp->font;
2110 /* now find the corresponding skin_font */
2111 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2112 if (!font->name)
2114 DEBUGF("font %d not specified\n", font_id);
2115 success = false;
2116 continue;
2119 /* load the font - will handle loading the same font again if
2120 * multiple viewports use the same */
2121 if (font->id < 0)
2123 char *dot = strchr(font->name, '.');
2124 *dot = '\0';
2125 font->id = skin_font_load(font->name);
2128 if (font->id < 0)
2130 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2131 font_id, font->name);
2132 success = false;
2133 continue;
2136 /* finally, assign the font_id to the viewport */
2137 vp->font = font->id;
2139 return success;
2142 #endif /* HAVE_LCD_BITMAP */
2144 /* to setup up the wps-data from a format-buffer (isfile = false)
2145 from a (wps-)file (isfile = true)*/
2146 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2147 const char *buf, bool isfile)
2150 if (!wps_data || !buf)
2151 return false;
2152 #ifdef HAVE_ALBUMART
2153 int status;
2154 struct mp3entry *curtrack;
2155 long offset;
2156 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2157 if (wps_data->albumart)
2159 old_aa.state = wps_data->albumart->state;
2160 old_aa.height = wps_data->albumart->height;
2161 old_aa.width = wps_data->albumart->width;
2163 #endif
2164 #ifdef HAVE_LCD_BITMAP
2165 int i;
2166 for (i=0;i<MAXUSERFONTS;i++)
2168 skinfonts[i].id = -1;
2169 skinfonts[i].name = NULL;
2171 #endif
2173 skin_data_reset(wps_data);
2174 curr_screen = screen;
2176 /* alloc default viewport, will be fixed up later */
2177 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2178 if (!curr_vp)
2179 return false;
2180 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2181 if (!list)
2182 return false;
2183 add_to_ll_chain(&wps_data->viewports, list);
2186 /* Initialise the first (default) viewport */
2187 curr_vp->label = VP_DEFAULT_LABEL;
2188 curr_vp->pb = NULL;
2189 curr_vp->hidden_flags = 0;
2190 curr_vp->lines = NULL;
2192 viewport_set_defaults(&curr_vp->vp, screen);
2193 #ifdef HAVE_LCD_BITMAP
2194 curr_vp->vp.font = FONT_UI;
2195 #endif
2197 curr_line = NULL;
2198 if (!skin_start_new_line(curr_vp, 0))
2199 return false;
2201 if (!isfile)
2203 if (wps_parse(wps_data, buf, false))
2205 #ifdef HAVE_LCD_BITMAP
2206 /* load the backdrop */
2207 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2208 skin_data_reset(wps_data);
2209 return false;
2211 #endif
2212 return true;
2214 return false;
2216 else
2218 int fd = open_utf8(buf, O_RDONLY);
2220 if (fd < 0)
2221 return false;
2223 /* get buffer space from the plugin buffer */
2224 size_t buffersize = 0;
2225 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2227 if (!wps_buffer)
2228 return false;
2230 /* copy the file's content to the buffer for parsing,
2231 ensuring that every line ends with a newline char. */
2232 unsigned int start = 0;
2233 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2235 start += strlen(wps_buffer + start);
2236 if (start < buffersize - 1)
2238 wps_buffer[start++] = '\n';
2239 wps_buffer[start] = 0;
2243 close(fd);
2245 if (start <= 0)
2246 return false;
2248 /* parse the WPS source */
2249 if (!wps_parse(wps_data, wps_buffer, true)) {
2250 skin_data_reset(wps_data);
2251 return false;
2254 wps_data->wps_loaded = true;
2256 #ifdef HAVE_LCD_BITMAP
2257 /* get the bitmap dir */
2258 char bmpdir[MAX_PATH];
2259 char *dot = strrchr(buf, '.');
2261 strlcpy(bmpdir, buf, dot - buf + 1);
2262 /* load the bitmaps that were found by the parsing */
2263 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2264 skin_data_reset(wps_data);
2265 wps_data->wps_loaded = false;
2266 return false;
2268 if (!skin_load_fonts(wps_data))
2270 skin_data_reset(wps_data);
2271 wps_data->wps_loaded = false;
2272 return false;
2274 #endif
2275 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2276 status = audio_status();
2277 if (status & AUDIO_STATUS_PLAY)
2279 struct skin_albumart *aa = wps_data->albumart;
2280 if (aa && ((aa->state && !old_aa.state) ||
2281 (aa->state &&
2282 (((old_aa.height != aa->height) ||
2283 (old_aa.width != aa->width))))))
2285 curtrack = audio_current_track();
2286 offset = curtrack->offset;
2287 audio_stop();
2288 if (!(status & AUDIO_STATUS_PAUSE))
2289 audio_play(offset);
2292 #endif
2293 #if defined(DEBUG) || defined(SIMULATOR)
2294 debug_skin_usage();
2295 #endif
2296 return true;