and actually fix multifont on remote lcd targets also
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob4e0cc42e180d7513a950d61b4c9ca61f75863240
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "config.h"
26 #include "file.h"
27 #include "misc.h"
28 #include "plugin.h"
29 #include "viewport.h"
31 #ifdef __PCTOOL__
32 #ifdef WPSEDITOR
33 #include "proxy.h"
34 #include "sysfont.h"
35 #else
36 #include "action.h"
37 #include "checkwps.h"
38 #include "audio.h"
39 #define lang_is_rtl() (false)
40 #define DEBUGF printf
41 #endif /*WPSEDITOR*/
42 #else
43 #include "debug.h"
44 #include "language.h"
45 #endif /*__PCTOOL__*/
47 #include <ctype.h>
48 #include <stdbool.h>
49 #include "font.h"
51 #include "wps_internals.h"
52 #include "skin_engine.h"
53 #include "settings.h"
54 #include "settings_list.h"
55 #include "skin_fonts.h"
57 #ifdef HAVE_LCD_BITMAP
58 #include "bmp.h"
59 #endif
61 #ifdef HAVE_ALBUMART
62 #include "playback.h"
63 #endif
65 #include "backdrop.h"
66 #include "statusbar-skinned.h"
68 #define WPS_ERROR_INVALID_PARAM -1
70 /* which screen are we parsing for? */
71 static enum screen_type curr_screen;
73 /* level of current conditional.
74 -1 means we're not in a conditional. */
75 static int level = -1;
77 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
78 or WPS_TOKEN_CONDITIONAL_START in current level */
79 static int lastcond[WPS_MAX_COND_LEVEL];
81 /* index of the WPS_TOKEN_CONDITIONAL in current level */
82 static int condindex[WPS_MAX_COND_LEVEL];
84 /* number of condtional options in current level */
85 static int numoptions[WPS_MAX_COND_LEVEL];
87 /* line number, debug only */
88 static int line_number;
90 /* the current viewport */
91 static struct skin_viewport *curr_vp;
92 /* the current line, linked to the above viewport */
93 static struct skin_line *curr_line;
95 static int follow_lang_direction = 0;
97 #if defined(DEBUG) || defined(SIMULATOR)
98 /* debugging function */
99 extern void print_debug_info(struct wps_data *data, int fail, int line);
100 extern void debug_skin_usage(void);
101 #endif
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
107 has been consumed.
109 wps_bufptr points to the char following the tag (i.e. where
110 details begin).
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
114 struct wps_token *token, struct wps_data *wps_data);
116 struct wps_tag {
117 enum wps_token_type type;
118 const char name[3];
119 unsigned char refresh_type;
120 const wps_tag_parse_func parse_func;
122 static int skip_end_of_line(const char *wps_bufptr);
123 /* prototypes of all special parse functions : */
124 static int parse_timeout(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126 static int parse_progressbar(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_dir_level(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_setting_and_lang(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_languagedirection(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data)
137 (void)wps_bufptr;
138 (void)token;
139 (void)wps_data;
140 follow_lang_direction = 2; /* 2 because it is decremented immediatly after
141 this token is parsed, after the next token it
142 will be 0 again. */
143 return 0;
146 #ifdef HAVE_LCD_BITMAP
147 static int parse_viewport_display(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 static int parse_playlistview(const char *wps_bufptr,
150 struct wps_token *token, struct wps_data *wps_data);
151 static int parse_viewport(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 static int parse_statusbar_enable(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 static int parse_statusbar_disable(const char *wps_bufptr,
156 struct wps_token *token, struct wps_data *wps_data);
157 static int parse_image_display(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_image_load(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 static int parse_font_load(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 #endif /*HAVE_LCD_BITMAP */
164 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
165 static int parse_image_special(const char *wps_bufptr,
166 struct wps_token *token, struct wps_data *wps_data);
167 #endif
168 #ifdef HAVE_ALBUMART
169 static int parse_albumart_load(const char *wps_bufptr,
170 struct wps_token *token, struct wps_data *wps_data);
171 static int parse_albumart_display(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 #endif /* HAVE_ALBUMART */
174 #ifdef HAVE_TOUCHSCREEN
175 static int parse_touchregion(const char *wps_bufptr,
176 struct wps_token *token, struct wps_data *wps_data);
177 #else
178 static int fulline_tag_not_supported(const char *wps_bufptr,
179 struct wps_token *token, struct wps_data *wps_data)
181 (void)token; (void)wps_data;
182 return skip_end_of_line(wps_bufptr);
184 #define parse_touchregion fulline_tag_not_supported
185 #endif
186 #ifdef CONFIG_RTC
187 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
188 #else
189 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
190 #endif
192 /* array of available tags - those with more characters have to go first
193 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
194 static const struct wps_tag all_tags[] = {
196 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
197 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
198 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
199 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
200 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
201 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
203 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
204 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
205 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
206 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
207 #if CONFIG_CHARGING >= CHARGING_MONITOR
208 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
209 #endif
210 #if CONFIG_CHARGING
211 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
212 #endif
213 #ifdef HAVE_USB_POWER
214 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
215 #endif
217 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
219 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
220 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
221 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
222 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
223 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
224 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
225 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
226 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
237 /* current file */
238 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
248 parse_dir_level },
250 /* next file */
251 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
261 parse_dir_level },
263 /* current metadata */
264 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
277 /* next metadata */
278 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
283 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
284 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
291 #if (CONFIG_CODEC != MAS3507D)
292 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
293 #endif
294 #if (CONFIG_CODEC == SWCODEC)
295 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
296 #endif
297 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
298 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
299 #endif
301 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
303 #ifdef HAS_REMOTE_BUTTON_HOLD
304 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
305 #else
306 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
307 #endif
309 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
311 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
312 parse_timeout },
314 #ifdef HAVE_LCD_BITMAP
315 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
316 #else
317 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
318 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
319 #endif
320 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
321 parse_progressbar },
323 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
325 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
326 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
327 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
328 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
330 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
331 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
332 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
333 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
335 #ifdef HAVE_TAGCACHE
336 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
337 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
338 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
339 #endif
341 #if CONFIG_CODEC == SWCODEC
342 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
343 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
344 #endif
346 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
347 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
349 #ifdef HAVE_LCD_BITMAP
350 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
351 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
353 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
355 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
356 parse_image_display },
358 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
359 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
360 #ifdef HAVE_ALBUMART
361 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
362 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
363 #endif
365 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
366 parse_viewport_display },
367 #ifdef HAVE_LCD_BITMAP
368 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
369 #endif
370 { WPS_NO_TOKEN, "V", 0, parse_viewport },
372 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
373 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
374 #endif
375 #endif
377 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
378 parse_setting_and_lang },
379 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
380 parse_setting_and_lang },
381 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
383 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
384 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
385 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
388 /* Recording Tokens */
389 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
390 #ifdef HAVE_RECORDING
391 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
392 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
393 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
394 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
395 #endif
396 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
397 /* the array MUST end with an empty string (first char is \0) */
401 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
402 * chains require the order to be kept.
404 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
406 if (*list == NULL)
407 *list = item;
408 else
410 struct skin_token_list *t = *list;
411 while (t->next)
412 t = t->next;
413 t->next = item;
417 /* traverse the image linked-list for an image */
418 #ifdef HAVE_LCD_BITMAP
419 struct gui_img* find_image(char label, struct wps_data *data)
421 struct skin_token_list *list = data->images;
422 while (list)
424 struct gui_img *img = (struct gui_img *)list->token->value.data;
425 if (img->label == label)
426 return img;
427 list = list->next;
429 return NULL;
431 #endif
433 /* traverse the viewport linked list for a viewport */
434 struct skin_viewport* find_viewport(char label, struct wps_data *data)
436 struct skin_token_list *list = data->viewports;
437 while (list)
439 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
440 if (vp->label == label)
441 return vp;
442 list = list->next;
444 return NULL;
448 /* create and init a new wpsll item.
449 * passing NULL to token will alloc a new one.
450 * You should only pass NULL for the token when the token type (table above)
451 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
453 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
454 void* token_data)
456 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
457 if (!token)
458 token = skin_buffer_alloc(sizeof(struct wps_token));
459 if (!llitem || !token)
460 return NULL;
461 llitem->next = NULL;
462 llitem->token = token;
463 if (token_data)
464 llitem->token->value.data = token_data;
465 return llitem;
468 /* Returns the number of chars that should be skipped to jump
469 immediately after the first eol, i.e. to the start of the next line */
470 static int skip_end_of_line(const char *wps_bufptr)
472 line_number++;
473 int skip = 0;
474 while(*(wps_bufptr + skip) != '\n')
475 skip++;
476 return ++skip;
479 /* Starts a new subline in the current line during parsing */
480 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
482 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
483 if (!subline)
484 return false;
486 subline->first_token_idx = curr_token;
487 subline->next = NULL;
489 subline->line_type = 0;
490 subline->time_mult = 0;
492 line->curr_subline->last_token_idx = curr_token-1;
493 line->curr_subline->next = subline;
494 line->curr_subline = subline;
495 return true;
498 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
500 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
501 struct skin_subline *subline = NULL;
502 if (!line)
503 return false;
505 /* init the subline */
506 subline = &line->sublines;
507 subline->first_token_idx = curr_token;
508 subline->next = NULL;
509 subline->line_type = 0;
510 subline->time_mult = 0;
512 /* init the new line */
513 line->curr_subline = &line->sublines;
514 line->next = NULL;
515 line->subline_expire_time = 0;
517 /* connect to curr_line and vp pointers.
518 * 1) close the previous lines subline
519 * 2) connect to vp pointer
520 * 3) connect to curr_line global pointer
522 if (curr_line)
524 curr_line->curr_subline->last_token_idx = curr_token - 1;
525 curr_line->next = line;
526 curr_line->curr_subline = NULL;
528 curr_line = line;
529 if (!vp->lines)
530 vp->lines = line;
531 return true;
534 #ifdef HAVE_LCD_BITMAP
536 static int parse_statusbar_enable(const char *wps_bufptr,
537 struct wps_token *token,
538 struct wps_data *wps_data)
540 (void)token; /* Kill warnings */
541 wps_data->wps_sb_tag = true;
542 wps_data->show_sb_on_wps = true;
543 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
544 viewport_set_defaults(&default_vp->vp, curr_screen);
545 return skip_end_of_line(wps_bufptr);
548 static int parse_statusbar_disable(const char *wps_bufptr,
549 struct wps_token *token,
550 struct wps_data *wps_data)
552 (void)token; /* Kill warnings */
553 wps_data->wps_sb_tag = true;
554 wps_data->show_sb_on_wps = false;
555 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
556 viewport_set_fullscreen(&default_vp->vp, curr_screen);
557 return skip_end_of_line(wps_bufptr);
560 static int get_image_id(int c)
562 if(c >= 'a' && c <= 'z')
563 return c - 'a';
564 else if(c >= 'A' && c <= 'Z')
565 return c - 'A' + 26;
566 else
567 return -1;
570 char *get_image_filename(const char *start, const char* bmpdir,
571 char *buf, int buf_size)
573 const char *end = strchr(start, '|');
575 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
577 buf = "\0";
578 return NULL;
581 int bmpdirlen = strlen(bmpdir);
583 strcpy(buf, bmpdir);
584 buf[bmpdirlen] = '/';
585 memcpy( &buf[bmpdirlen + 1], start, end - start);
586 buf[bmpdirlen + 1 + end - start] = 0;
588 return buf;
591 static int parse_image_display(const char *wps_bufptr,
592 struct wps_token *token,
593 struct wps_data *wps_data)
595 char label = wps_bufptr[0];
596 int subimage;
597 struct gui_img *img;;
599 /* sanity check */
600 img = find_image(label, wps_data);
601 if (!img)
603 token->value.i = label; /* so debug works */
604 return WPS_ERROR_INVALID_PARAM;
607 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
609 if (subimage >= img->num_subimages)
610 return WPS_ERROR_INVALID_PARAM;
612 /* Store sub-image number to display in high bits */
613 token->value.i = label | (subimage << 8);
614 return 2; /* We have consumed 2 bytes */
615 } else {
616 token->value.i = label;
617 return 1; /* We have consumed 1 byte */
621 static int parse_image_load(const char *wps_bufptr,
622 struct wps_token *token,
623 struct wps_data *wps_data)
625 const char *ptr = wps_bufptr;
626 const char *pos;
627 const char* filename;
628 const char* id;
629 const char *newline;
630 int x,y;
631 struct gui_img *img;
633 /* format: %x|n|filename.bmp|x|y|
634 or %xl|n|filename.bmp|x|y|
635 or %xl|n|filename.bmp|x|y|num_subimages|
638 if (*ptr != '|')
639 return WPS_ERROR_INVALID_PARAM;
641 ptr++;
643 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
644 return WPS_ERROR_INVALID_PARAM;
646 /* Check there is a terminating | */
647 if (*ptr != '|')
648 return WPS_ERROR_INVALID_PARAM;
650 /* check the image number and load state */
651 if(find_image(*id, wps_data))
653 /* Invalid image ID */
654 return WPS_ERROR_INVALID_PARAM;
656 img = skin_buffer_alloc(sizeof(struct gui_img));
657 if (!img)
658 return WPS_ERROR_INVALID_PARAM;
659 /* save a pointer to the filename */
660 img->bm.data = (char*)filename;
661 img->label = *id;
662 img->x = x;
663 img->y = y;
664 img->num_subimages = 1;
665 img->always_display = false;
667 /* save current viewport */
668 img->vp = &curr_vp->vp;
670 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
672 img->always_display = true;
674 else
676 /* Parse the (optional) number of sub-images */
677 ptr++;
678 newline = strchr(ptr, '\n');
679 pos = strchr(ptr, '|');
680 if (pos && pos < newline)
681 img->num_subimages = atoi(ptr);
683 if (img->num_subimages <= 0)
684 return WPS_ERROR_INVALID_PARAM;
686 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
687 if (!item)
688 return WPS_ERROR_INVALID_PARAM;
689 add_to_ll_chain(&wps_data->images, item);
691 /* Skip the rest of the line */
692 return skip_end_of_line(wps_bufptr);
695 /* this array acts as a simple mapping between the id the user uses for a font
696 * and the id the font actually gets from the font loader.
697 * font id 2 is always the first skin font (regardless of how many screens */
698 static int font_ids[MAXUSERFONTS];
699 static int parse_font_load(const char *wps_bufptr,
700 struct wps_token *token, struct wps_data *wps_data)
702 (void)wps_data; (void)token;
703 const char *ptr = wps_bufptr;
704 int id;
705 char *filename, buf[MAX_PATH];
707 if (*ptr != '|')
708 return WPS_ERROR_INVALID_PARAM;
710 ptr++;
712 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
713 return WPS_ERROR_INVALID_PARAM;
715 /* Check there is a terminating | */
716 if (*ptr != '|')
717 return WPS_ERROR_INVALID_PARAM;
719 if (id <= FONT_UI || id >= MAXFONTS-1)
720 return WPS_ERROR_INVALID_PARAM;
721 id -= FONT_UI;
723 memcpy(buf, filename, ptr-filename);
724 buf[ptr-filename] = '\0';
725 font_ids[id] = skin_font_load(buf);
727 return font_ids[id] >= 0 ? skip_end_of_line(wps_bufptr) : WPS_ERROR_INVALID_PARAM;
731 static int parse_viewport_display(const char *wps_bufptr,
732 struct wps_token *token,
733 struct wps_data *wps_data)
735 (void)wps_data;
736 char letter = wps_bufptr[0];
738 if (letter < 'a' || letter > 'z')
740 /* invalid viewport tag */
741 return WPS_ERROR_INVALID_PARAM;
743 token->value.i = letter;
744 return 1;
747 #ifdef HAVE_LCD_BITMAP
748 static int parse_playlistview_text(struct playlistviewer *viewer,
749 enum info_line_type line, char* text)
751 int cur_string = 0;
752 const struct wps_tag *tag;
753 int taglen = 0;
754 const char *start = text;
755 if (*text != '|')
756 return -1;
757 text++;
758 viewer->lines[line].count = 0;
759 viewer->lines[line].scroll = false;
760 while (*text != '|')
762 if (*text == '%') /* it is a token of some type */
764 text++;
765 taglen = 0;
766 switch(*text)
768 case '%':
769 case '<':
770 case '|':
771 case '>':
772 case ';':
773 case '#':
774 /* escaped characters */
775 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
776 viewer->lines[line].strings[cur_string][0] = *text;
777 viewer->lines[line].strings[cur_string++][1] = '\0';
778 break;
779 default:
780 for (tag = all_tags;
781 strncmp(text, tag->name, strlen(tag->name)) != 0;
782 tag++) ;
783 /* %s isnt stored as a tag so manually check for it */
784 if (tag->type == WPS_NO_TOKEN)
786 if (!strncmp(tag->name, "s", 1))
788 viewer->lines[line].scroll = true;
789 taglen = 1;
792 else if (tag->type == WPS_TOKEN_UNKNOWN)
794 int i = 0;
795 /* just copy the string */
796 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
797 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
799 viewer->lines[line].strings[cur_string][i] = text[i];
800 i++;
802 viewer->lines[line].strings[cur_string][i] = '\0';
803 cur_string++;
804 taglen = i;
806 else
808 if (tag->parse_func)
810 /* unsupported tag, reject */
811 return -1;
813 taglen = strlen(tag->name);
814 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
816 text += taglen;
819 else
821 /* regular string */
822 int i = 0;
823 /* just copy the string */
824 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
825 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
827 viewer->lines[line].strings[cur_string][i] = text[i];
828 i++;
830 viewer->lines[line].strings[cur_string][i] = '\0';
831 cur_string++;
832 text += i;
835 return text - start;
839 static int parse_playlistview(const char *wps_bufptr,
840 struct wps_token *token, struct wps_data *wps_data)
842 (void)wps_data;
843 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
844 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
845 char *ptr = strchr(wps_bufptr, '|');
846 int length;
847 if (!viewer || !ptr)
848 return WPS_ERROR_INVALID_PARAM;
849 viewer->vp = &curr_vp->vp;
850 viewer->show_icons = true;
851 viewer->start_offset = atoi(ptr+1);
852 token->value.data = (void*)viewer;
853 ptr = strchr(ptr+1, '|');
854 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
855 if (length < 0)
856 return WPS_ERROR_INVALID_PARAM;
857 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
858 if (length < 0)
859 return WPS_ERROR_INVALID_PARAM;
861 return skip_end_of_line(wps_bufptr);
863 #endif
865 static int parse_viewport(const char *wps_bufptr,
866 struct wps_token *token,
867 struct wps_data *wps_data)
869 (void)token; /* Kill warnings */
870 const char *ptr = wps_bufptr;
872 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
874 /* check for the optional letter to signify its a hideable viewport */
875 /* %Vl|<label>|<rest of tags>| */
876 skin_vp->hidden_flags = 0;
877 skin_vp->label = VP_NO_LABEL;
878 skin_vp->pb = NULL;
879 skin_vp->lines = NULL;
880 if (curr_line)
882 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
883 - (wps_data->num_tokens > 0 ? 1 : 0);
886 curr_line = NULL;
887 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
888 return WPS_ERROR_INVALID_PARAM;
891 if (*ptr == 'i')
893 skin_vp->label = VP_INFO_LABEL;
894 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
895 ++ptr;
897 else if (*ptr == 'l')
899 if (*(ptr+1) == '|')
901 char label = *(ptr+2);
902 if (label >= 'a' && label <= 'z')
904 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
905 skin_vp->label = label;
907 else
908 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
909 ptr += 3;
912 if (*ptr != '|')
913 return WPS_ERROR_INVALID_PARAM;
915 ptr++;
916 struct viewport *vp = &skin_vp->vp;
917 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
918 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
919 return WPS_ERROR_INVALID_PARAM;
921 /* Check for trailing | */
922 if (*ptr != '|')
923 return WPS_ERROR_INVALID_PARAM;
925 if (follow_lang_direction && lang_is_rtl())
927 vp->flags |= VP_FLAG_ALIGN_RIGHT;
928 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
930 else
931 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
933 if (vp->font > FONT_UI)
934 vp->font = font_ids[vp->font - FONT_UI];
936 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
937 if (!list)
938 return WPS_ERROR_INVALID_PARAM;
939 add_to_ll_chain(&wps_data->viewports, list);
940 curr_vp = skin_vp;
941 /* Skip the rest of the line */
942 return skip_end_of_line(wps_bufptr);
945 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
946 static int parse_image_special(const char *wps_bufptr,
947 struct wps_token *token,
948 struct wps_data *wps_data)
950 (void)wps_data; /* kill warning */
951 (void)token;
952 const char *pos = NULL;
953 const char *newline;
954 bool error = false;
956 pos = strchr(wps_bufptr + 1, '|');
957 newline = strchr(wps_bufptr, '\n');
959 error = (pos > newline);
962 #if LCD_DEPTH > 1
963 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
965 /* format: %X|filename.bmp| or %Xd */
966 if (*(wps_bufptr) == 'd')
968 wps_data->backdrop = NULL;
969 return skip_end_of_line(wps_bufptr);
971 else if (!error)
972 wps_data->backdrop = (char*)wps_bufptr + 1;
974 #endif
975 if (error)
976 return WPS_ERROR_INVALID_PARAM;
977 /* Skip the rest of the line */
978 return skip_end_of_line(wps_bufptr);
980 #endif
982 #endif /* HAVE_LCD_BITMAP */
984 static int parse_setting_and_lang(const char *wps_bufptr,
985 struct wps_token *token,
986 struct wps_data *wps_data)
988 /* NOTE: both the string validations that happen in here will
989 * automatically PASS on checkwps because its too hard to get
990 * settings_list.c and englinsh.lang built for it.
991 * If that ever changes remove the #ifndef __PCTOOL__'s here
993 (void)wps_data;
994 const char *ptr = wps_bufptr;
995 const char *end;
996 int i = 0;
997 char temp[64];
999 /* Find the setting's cfg_name */
1000 if (*ptr != '|')
1001 return WPS_ERROR_INVALID_PARAM;
1002 ptr++;
1003 end = strchr(ptr,'|');
1004 if (!end)
1005 return WPS_ERROR_INVALID_PARAM;
1006 strlcpy(temp, ptr,end-ptr+1);
1008 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1010 #ifndef __PCTOOL__
1011 i = lang_english_to_id(temp);
1012 if (i < 0)
1013 return WPS_ERROR_INVALID_PARAM;
1014 #endif
1016 else
1018 /* Find the setting */
1019 for (i=0; i<nb_settings; i++)
1020 if (settings[i].cfg_name &&
1021 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1022 /* prevent matches on cfg_name prefixes */
1023 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1024 break;
1025 #ifndef __PCTOOL__
1026 if (i == nb_settings)
1027 return WPS_ERROR_INVALID_PARAM;
1028 #endif
1030 /* Store the setting number */
1031 token->value.i = i;
1033 /* Skip the rest of the line */
1034 return end-ptr+2;
1038 static int parse_dir_level(const char *wps_bufptr,
1039 struct wps_token *token,
1040 struct wps_data *wps_data)
1042 char val[] = { *wps_bufptr, '\0' };
1043 token->value.i = atoi(val);
1044 (void)wps_data; /* Kill warnings */
1045 return 1;
1048 static int parse_timeout(const char *wps_bufptr,
1049 struct wps_token *token,
1050 struct wps_data *wps_data)
1052 int skip = 0;
1053 int val = 0;
1054 bool have_point = false;
1055 bool have_tenth = false;
1057 (void)wps_data; /* Kill the warning */
1059 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1061 if (*wps_bufptr != '.')
1063 val *= 10;
1064 val += *wps_bufptr - '0';
1065 if (have_point)
1067 have_tenth = true;
1068 wps_bufptr++;
1069 skip++;
1070 break;
1073 else
1074 have_point = true;
1076 wps_bufptr++;
1077 skip++;
1080 if (have_tenth == false)
1081 val *= 10;
1083 if (val == 0 && skip == 0)
1085 /* decide what to do if no value was specified */
1086 switch (token->type)
1088 case WPS_TOKEN_SUBLINE_TIMEOUT:
1089 return -1;
1090 case WPS_TOKEN_BUTTON_VOLUME:
1091 val = 10;
1092 break;
1095 token->value.i = val;
1097 return skip;
1100 static int parse_progressbar(const char *wps_bufptr,
1101 struct wps_token *token,
1102 struct wps_data *wps_data)
1104 /* %pb or %pb|filename|x|y|width|height|
1105 using - for any of the params uses "sane" values */
1106 #ifdef HAVE_LCD_BITMAP
1107 enum {
1108 PB_FILENAME = 0,
1109 PB_X,
1110 PB_Y,
1111 PB_WIDTH,
1112 PB_HEIGHT
1114 const char *filename;
1115 int x, y, height, width;
1116 uint32_t set = 0;
1117 const char *ptr = wps_bufptr;
1118 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1119 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1121 if (!pb || !item)
1122 return WPS_ERROR_INVALID_PARAM;
1124 struct viewport *vp = &curr_vp->vp;
1125 #ifndef __PCTOOL__
1126 int font_height = font_get(vp->font)->height;
1127 #else
1128 int font_height = 8;
1129 #endif
1130 /* we need to know what line number (viewport relative) this pb is,
1131 * so count them... */
1132 int line_num = -1;
1133 struct skin_line *line = curr_vp->lines;
1134 while (line)
1136 line_num++;
1137 line = line->next;
1139 pb->have_bitmap_pb = false;
1140 pb->bm.data = NULL; /* no bitmap specified */
1141 pb->follow_lang_direction = follow_lang_direction > 0;
1143 if (*wps_bufptr != '|') /* regular old style */
1145 pb->x = 0;
1146 pb->width = vp->width;
1147 pb->height = SYSFONT_HEIGHT-2;
1148 pb->y = -line_num - 1; /* Will be computed during the rendering */
1150 curr_vp->pb = pb;
1151 add_to_ll_chain(&wps_data->progressbars, item);
1152 return 0;
1154 ptr = wps_bufptr + 1;
1156 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1157 &x, &y, &width, &height)))
1158 return WPS_ERROR_INVALID_PARAM;
1160 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1161 pb->bm.data = (char*)filename;
1163 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1164 pb->x = x;
1165 else
1166 pb->x = vp->x;
1168 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1170 /* A zero width causes a divide-by-zero error later, so reject it */
1171 if (width == 0)
1172 return WPS_ERROR_INVALID_PARAM;
1174 pb->width = width;
1176 else
1177 pb->width = vp->width - pb->x;
1179 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1181 /* A zero height makes no sense - reject it */
1182 if (height == 0)
1183 return WPS_ERROR_INVALID_PARAM;
1185 pb->height = height;
1187 else
1188 pb->height = font_height;
1190 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1191 pb->y = y;
1192 else
1193 pb->y = -line_num - 1; /* Will be computed during the rendering */
1195 curr_vp->pb = pb;
1196 add_to_ll_chain(&wps_data->progressbars, item);
1198 /* Skip the rest of the line */
1199 return skip_end_of_line(wps_bufptr)-1;
1200 #else
1201 (void)token;
1203 if (*(wps_bufptr-1) == 'f')
1204 wps_data->full_line_progressbar = true;
1205 else
1206 wps_data->full_line_progressbar = false;
1208 return 0;
1210 #endif
1213 #ifdef HAVE_ALBUMART
1214 static int parse_int(const char *newline, const char **_pos, int *num)
1216 *_pos = parse_list("d", NULL, '|', *_pos, num);
1218 return (!*_pos || *_pos > newline || **_pos != '|');
1221 static int parse_albumart_load(const char *wps_bufptr,
1222 struct wps_token *token,
1223 struct wps_data *wps_data)
1225 const char *_pos, *newline;
1226 bool parsing;
1227 struct dim dimensions;
1228 int albumart_slot;
1229 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1230 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1231 (void)token; /* silence warning */
1232 if (!aa)
1233 return skip_end_of_line(wps_bufptr);
1235 /* reset albumart info in wps */
1236 aa->width = -1;
1237 aa->height = -1;
1238 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1239 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1240 aa->vp = &curr_vp->vp;
1242 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1244 newline = strchr(wps_bufptr, '\n');
1246 _pos = wps_bufptr;
1248 if (*_pos != '|')
1249 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1251 ++_pos;
1253 /* initial validation and parsing of x component */
1254 if (parse_int(newline, &_pos, &aa->x))
1255 return WPS_ERROR_INVALID_PARAM;
1257 ++_pos;
1259 /* initial validation and parsing of y component */
1260 if (parse_int(newline, &_pos, &aa->y))
1261 return WPS_ERROR_INVALID_PARAM;
1263 /* parsing width field */
1264 parsing = true;
1265 while (parsing)
1267 /* apply each modifier in turn */
1268 ++_pos;
1269 switch (*_pos)
1271 case 'l':
1272 case 'L':
1273 case '+':
1274 if (swap_for_rtl)
1275 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1276 else
1277 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1278 break;
1279 case 'c':
1280 case 'C':
1281 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1282 break;
1283 case 'r':
1284 case 'R':
1285 case '-':
1286 if (swap_for_rtl)
1287 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1288 else
1289 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1290 break;
1291 case 'd':
1292 case 'D':
1293 case 'i':
1294 case 'I':
1295 case 's':
1296 case 'S':
1297 /* simply ignored */
1298 break;
1299 default:
1300 parsing = false;
1301 break;
1304 /* extract max width data */
1305 if (*_pos != '|')
1307 if (parse_int(newline, &_pos, &aa->width))
1308 return WPS_ERROR_INVALID_PARAM;
1311 /* parsing height field */
1312 parsing = true;
1313 while (parsing)
1315 /* apply each modifier in turn */
1316 ++_pos;
1317 switch (*_pos)
1319 case 't':
1320 case 'T':
1321 case '-':
1322 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1323 break;
1324 case 'c':
1325 case 'C':
1326 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1327 break;
1328 case 'b':
1329 case 'B':
1330 case '+':
1331 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1332 break;
1333 case 'd':
1334 case 'D':
1335 case 'i':
1336 case 'I':
1337 case 's':
1338 case 'S':
1339 /* simply ignored */
1340 break;
1341 default:
1342 parsing = false;
1343 break;
1346 /* extract max height data */
1347 if (*_pos != '|')
1349 if (parse_int(newline, &_pos, &aa->height))
1350 return WPS_ERROR_INVALID_PARAM;
1353 /* if we got here, we parsed everything ok .. ! */
1354 if (aa->width < 0)
1355 aa->width = 0;
1356 else if (aa->width > LCD_WIDTH)
1357 aa->width = LCD_WIDTH;
1359 if (aa->height < 0)
1360 aa->height = 0;
1361 else if (aa->height > LCD_HEIGHT)
1362 aa->height = LCD_HEIGHT;
1364 if (swap_for_rtl)
1365 aa->x = LCD_WIDTH - (aa->x + aa->width);
1367 aa->state = WPS_ALBUMART_LOAD;
1368 aa->draw = false;
1369 wps_data->albumart = aa;
1371 dimensions.width = aa->width;
1372 dimensions.height = aa->height;
1374 albumart_slot = playback_claim_aa_slot(&dimensions);
1376 if (0 <= albumart_slot)
1377 wps_data->playback_aa_slot = albumart_slot;
1379 /* Skip the rest of the line */
1380 return skip_end_of_line(wps_bufptr);
1383 static int parse_albumart_display(const char *wps_bufptr,
1384 struct wps_token *token,
1385 struct wps_data *wps_data)
1387 (void)wps_bufptr;
1388 struct wps_token *prev = token-1;
1389 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1391 token->type = WPS_TOKEN_ALBUMART_FOUND;
1393 else if (wps_data->albumart)
1395 wps_data->albumart->vp = &curr_vp->vp;
1397 #if 0
1398 /* the old code did this so keep it here for now...
1399 * this is to allow the posibility to showing the next tracks AA! */
1400 if (wps_bufptr+1 == 'n')
1401 return 1;
1402 #endif
1403 return 0;
1405 #endif /* HAVE_ALBUMART */
1407 #ifdef HAVE_TOUCHSCREEN
1409 struct touchaction {const char* s; int action;};
1410 static const struct touchaction touchactions[] = {
1411 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1412 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1413 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1414 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1415 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1416 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1417 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1418 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1420 static int parse_touchregion(const char *wps_bufptr,
1421 struct wps_token *token, struct wps_data *wps_data)
1423 (void)token;
1424 unsigned i, imax;
1425 struct touchregion *region = NULL;
1426 const char *ptr = wps_bufptr;
1427 const char *action;
1428 const char pb_string[] = "progressbar";
1429 const char vol_string[] = "volume";
1430 int x,y,w,h;
1432 /* format: %T|x|y|width|height|action|
1433 * if action starts with & the area must be held to happen
1434 * action is one of:
1435 * play - play/pause playback
1436 * stop - stop playback, exit the wps
1437 * prev - prev track
1438 * next - next track
1439 * ffwd - seek forward
1440 * rwd - seek backwards
1441 * menu - go back to the main menu
1442 * browse - go back to the file/db browser
1443 * shuffle - toggle shuffle mode
1444 * repmode - cycle the repeat mode
1445 * quickscreen - go into the quickscreen
1446 * contextmenu - open the context menu
1447 * playlist - go into the playlist
1448 * pitch - go into the pitchscreen
1449 * volup - increase volume by one step
1450 * voldown - decrease volume by one step
1454 if (*ptr != '|')
1455 return WPS_ERROR_INVALID_PARAM;
1456 ptr++;
1458 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1459 return WPS_ERROR_INVALID_PARAM;
1461 /* Check there is a terminating | */
1462 if (*ptr != '|')
1463 return WPS_ERROR_INVALID_PARAM;
1465 region = skin_buffer_alloc(sizeof(struct touchregion));
1466 if (!region)
1467 return WPS_ERROR_INVALID_PARAM;
1469 /* should probably do some bounds checking here with the viewport... but later */
1470 region->action = ACTION_NONE;
1471 region->x = x;
1472 region->y = y;
1473 region->width = w;
1474 region->height = h;
1475 region->wvp = curr_vp;
1477 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1478 && *(action + sizeof(pb_string)-1) == '|')
1479 region->type = WPS_TOUCHREGION_SCROLLBAR;
1480 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1481 && *(action + sizeof(vol_string)-1) == '|')
1482 region->type = WPS_TOUCHREGION_VOLUME;
1483 else
1485 region->type = WPS_TOUCHREGION_ACTION;
1487 if (*action == '&')
1489 action++;
1490 region->repeat = true;
1492 else
1493 region->repeat = false;
1495 i = 0;
1496 imax = ARRAYLEN(touchactions);
1497 while ((region->action == ACTION_NONE) &&
1498 (i < imax))
1500 /* try to match with one of our touchregion screens */
1501 int len = strlen(touchactions[i].s);
1502 if (!strncmp(touchactions[i].s, action, len)
1503 && *(action+len) == '|')
1504 region->action = touchactions[i].action;
1505 i++;
1507 if (region->action == ACTION_NONE)
1508 return WPS_ERROR_INVALID_PARAM;
1510 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1511 if (!item)
1512 return WPS_ERROR_INVALID_PARAM;
1513 add_to_ll_chain(&wps_data->touchregions, item);
1514 return skip_end_of_line(wps_bufptr);
1516 #endif
1518 /* Parse a generic token from the given string. Return the length read */
1519 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1521 int skip = 0, taglen = 0, ret;
1522 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1523 const struct wps_tag *tag;
1524 memset(token, 0, sizeof(*token));
1526 switch(*wps_bufptr)
1529 case '%':
1530 case '<':
1531 case '|':
1532 case '>':
1533 case ';':
1534 case '#':
1535 /* escaped characters */
1536 token->type = WPS_TOKEN_CHARACTER;
1537 token->value.c = *wps_bufptr;
1538 taglen = 1;
1539 wps_data->num_tokens++;
1540 break;
1542 case '?':
1543 /* conditional tag */
1544 token->type = WPS_TOKEN_CONDITIONAL;
1545 level++;
1546 condindex[level] = wps_data->num_tokens;
1547 numoptions[level] = 1;
1548 wps_data->num_tokens++;
1549 ret = parse_token(wps_bufptr + 1, wps_data);
1550 if (ret < 0) return ret;
1551 taglen = 1 + ret;
1552 break;
1554 default:
1555 /* find what tag we have */
1556 for (tag = all_tags;
1557 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1558 tag++) ;
1560 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1561 token->type = tag->type;
1562 curr_line->curr_subline->line_type |= tag->refresh_type;
1564 /* if the tag has a special parsing function, we call it */
1565 if (tag->parse_func)
1567 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1568 if (ret < 0) return ret;
1569 skip += ret;
1572 /* Some tags we don't want to save as tokens */
1573 if (tag->type == WPS_NO_TOKEN)
1574 break;
1576 /* tags that start with 'F', 'I' or 'D' are for the next file */
1577 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1578 *(tag->name) == 'D')
1579 token->next = true;
1581 wps_data->num_tokens++;
1582 break;
1585 skip += taglen;
1586 return skip;
1591 * Returns the number of bytes to skip the buf pointer to access the false
1592 * branch in a _binary_ conditional
1594 * That is:
1595 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1596 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1598 * depending on the features of a target it's not called from check_feature_tag,
1599 * hence the __attribute__ or it issues compiler warnings
1603 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1604 static int find_false_branch(const char *wps_bufptr)
1606 const char *buf = wps_bufptr;
1607 /* wps_bufptr is after the opening '<', hence level = 1*/
1608 int level = 1;
1609 char ch;
1612 ch = *buf;
1613 if (ch == '%')
1614 { /* filter out the characters we check later if they're printed
1615 * as literals */
1616 ch = *(++buf);
1617 if (ch == '<' || ch == '>' || ch == '|')
1618 continue;
1619 /* else: some tags/printed literals we skip over */
1621 else if (ch == '<') /* nested conditional */
1622 level++;
1623 else if (ch == '>')
1624 { /* closed our or a nested conditional,
1625 * do NOT skip over the '>' so that wps_parse() sees it for closing
1626 * if it is the closing one for our conditional */
1627 level--;
1629 else if (ch == '|' && level == 1)
1630 { /* we found our separator, point before and get out */
1631 break;
1633 /* if level is 0, we don't have a false branch */
1634 } while (level > 0 && *(++buf));
1636 return buf - wps_bufptr;
1640 * returns the number of bytes to get the appropriate branch of a binary
1641 * conditional
1643 * That means:
1644 * - if a feature is available, it returns 0 to not skip anything
1645 * - if the feature is not available, skip to the false branch and don't
1646 * parse the true branch at all
1648 * */
1649 static int check_feature_tag(const char *wps_bufptr, const int type)
1651 (void)wps_bufptr;
1652 switch (type)
1654 case WPS_TOKEN_RTC_PRESENT:
1655 #if CONFIG_RTC
1656 return 0;
1657 #else
1658 return find_false_branch(wps_bufptr);
1659 #endif
1660 case WPS_TOKEN_HAVE_RECORDING:
1661 #ifdef HAVE_RECORDING
1662 return 0;
1663 #else
1664 return find_false_branch(wps_bufptr);
1665 #endif
1666 default: /* not a tag we care about, just don't skip */
1667 return 0;
1672 /* Parses the WPS.
1673 data is the pointer to the structure where the parsed WPS should be stored.
1674 It is initialised.
1675 wps_bufptr points to the string containing the WPS tags */
1676 #define TOKEN_BLOCK_SIZE 128
1677 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1679 if (!data || !wps_bufptr || !*wps_bufptr)
1680 return false;
1681 enum wps_parse_error fail = PARSE_OK;
1682 int ret;
1683 int max_tokens = TOKEN_BLOCK_SIZE;
1684 size_t buf_free = 0;
1685 line_number = 0;
1686 level = -1;
1688 /* allocate enough RAM for a reasonable skin, grow as needed.
1689 * Free any used RAM before loading the images to be 100% RAM efficient */
1690 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1691 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1692 return false;
1693 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1694 data->num_tokens = 0;
1696 #if LCD_DEPTH > 1
1697 /* Backdrop defaults to the setting unless %X is used, so set it now */
1698 if (global_settings.backdrop_file[0])
1700 data->backdrop = "-";
1702 #endif
1704 while (*wps_bufptr && !fail)
1706 if (follow_lang_direction)
1707 follow_lang_direction--;
1708 /* first make sure there is enough room for tokens */
1709 if (max_tokens <= data->num_tokens + 5)
1711 int extra_tokens = TOKEN_BLOCK_SIZE;
1712 size_t needed = extra_tokens * sizeof(struct wps_token);
1713 /* do some smarts here to grow the array a bit */
1714 if (skin_buffer_freespace() < needed)
1716 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1717 break;
1719 skin_buffer_increment(needed, false);
1720 max_tokens += extra_tokens;
1723 switch(*wps_bufptr++)
1726 /* Regular tag */
1727 case '%':
1728 if ((ret = parse_token(wps_bufptr, data)) < 0)
1730 fail = PARSE_FAIL_COND_INVALID_PARAM;
1731 break;
1733 else if (level >= WPS_MAX_COND_LEVEL - 1)
1735 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1736 break;
1738 wps_bufptr += ret;
1739 break;
1741 /* Alternating sublines separator */
1742 case ';':
1743 if (level >= 0) /* there are unclosed conditionals */
1745 fail = PARSE_FAIL_UNCLOSED_COND;
1746 break;
1749 if (!skin_start_new_subline(curr_line, data->num_tokens))
1750 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1752 break;
1754 /* Conditional list start */
1755 case '<':
1756 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1758 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1759 break;
1761 wps_bufptr += check_feature_tag(wps_bufptr,
1762 data->tokens[data->num_tokens-1].type);
1763 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1764 lastcond[level] = data->num_tokens++;
1765 break;
1767 /* Conditional list end */
1768 case '>':
1769 if (level < 0) /* not in a conditional, invalid char */
1771 fail = PARSE_FAIL_INVALID_CHAR;
1772 break;
1775 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1776 if (lastcond[level])
1777 data->tokens[lastcond[level]].value.i = data->num_tokens;
1778 else
1780 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1781 break;
1784 lastcond[level] = 0;
1785 data->num_tokens++;
1786 data->tokens[condindex[level]].value.i = numoptions[level];
1787 level--;
1788 break;
1790 /* Conditional list option */
1791 case '|':
1792 if (level < 0) /* not in a conditional, invalid char */
1794 fail = PARSE_FAIL_INVALID_CHAR;
1795 break;
1798 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1799 if (lastcond[level])
1800 data->tokens[lastcond[level]].value.i = data->num_tokens;
1801 else
1803 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1804 break;
1807 lastcond[level] = data->num_tokens;
1808 numoptions[level]++;
1809 data->num_tokens++;
1810 break;
1812 /* Comment */
1813 case '#':
1814 if (level >= 0) /* there are unclosed conditionals */
1816 fail = PARSE_FAIL_UNCLOSED_COND;
1817 break;
1820 wps_bufptr += skip_end_of_line(wps_bufptr);
1821 break;
1823 /* End of this line */
1824 case '\n':
1825 if (level >= 0) /* there are unclosed conditionals */
1827 fail = PARSE_FAIL_UNCLOSED_COND;
1828 break;
1830 /* add a new token for the \n so empty lines are correct */
1831 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1832 data->tokens[data->num_tokens].value.c = '\n';
1833 data->tokens[data->num_tokens].next = false;
1834 data->num_tokens++;
1836 if (!skin_start_new_line(curr_vp, data->num_tokens))
1838 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1839 break;
1841 line_number++;
1843 break;
1845 /* String */
1846 default:
1848 unsigned int len = 1;
1849 const char *string_start = wps_bufptr - 1;
1851 /* find the length of the string */
1852 while (*wps_bufptr && *wps_bufptr != '#' &&
1853 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1854 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1855 *wps_bufptr != '|' && *wps_bufptr != '\n')
1857 wps_bufptr++;
1858 len++;
1861 /* look if we already have that string */
1862 char *str;
1863 bool found = false;
1864 struct skin_token_list *list = data->strings;
1865 while (list)
1867 str = (char*)list->token->value.data;
1868 found = (strlen(str) == len &&
1869 strncmp(string_start, str, len) == 0);
1870 if (found)
1871 break; /* break here because the list item is
1872 used if its found */
1873 list = list->next;
1875 /* If a matching string is found, found is true and i is
1876 the index of the string. If not, found is false */
1878 if (!found)
1880 /* new string */
1881 str = (char*)skin_buffer_alloc(len+1);
1882 if (!str)
1884 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1885 break;
1887 strlcpy(str, string_start, len+1);
1888 struct skin_token_list *item =
1889 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1890 if(!item)
1892 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1893 break;
1895 add_to_ll_chain(&data->strings, item);
1897 else
1899 /* another occurrence of an existing string */
1900 data->tokens[data->num_tokens].value.data = list->token->value.data;
1902 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1903 data->num_tokens++;
1905 break;
1909 if (!fail && level >= 0) /* there are unclosed conditionals */
1910 fail = PARSE_FAIL_UNCLOSED_COND;
1912 if (*wps_bufptr && !fail)
1913 /* one of the limits of the while loop was exceeded */
1914 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1916 /* Success! */
1917 curr_line->curr_subline->last_token_idx = data->num_tokens;
1918 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1919 /* freeup unused tokens */
1920 skin_buffer_free_from_front(sizeof(struct wps_token)
1921 * (max_tokens - data->num_tokens));
1923 #if defined(DEBUG) || defined(SIMULATOR)
1924 if (debug)
1925 print_debug_info(data, fail, line_number);
1926 #else
1927 (void)debug;
1928 #endif
1930 return (fail == 0);
1935 * initial setup of wps_data; does reset everything
1936 * except fields which need to survive, i.e.
1939 static void skin_data_reset(struct wps_data *wps_data)
1941 #ifdef HAVE_LCD_BITMAP
1942 wps_data->images = NULL;
1943 wps_data->progressbars = NULL;
1944 #endif
1945 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1946 wps_data->backdrop = NULL;
1947 #endif
1948 #ifdef HAVE_TOUCHSCREEN
1949 wps_data->touchregions = NULL;
1950 #endif
1951 wps_data->viewports = NULL;
1952 wps_data->strings = NULL;
1953 #ifdef HAVE_ALBUMART
1954 wps_data->albumart = NULL;
1955 if (wps_data->playback_aa_slot >= 0)
1957 playback_release_aa_slot(wps_data->playback_aa_slot);
1958 wps_data->playback_aa_slot = -1;
1960 #endif
1961 wps_data->tokens = NULL;
1962 wps_data->num_tokens = 0;
1964 #ifdef HAVE_LCD_BITMAP
1965 wps_data->peak_meter_enabled = false;
1966 wps_data->wps_sb_tag = false;
1967 wps_data->show_sb_on_wps = false;
1968 #else /* HAVE_LCD_CHARCELLS */
1969 /* progress bars */
1970 int i;
1971 for (i = 0; i < 8; i++)
1973 wps_data->wps_progress_pat[i] = 0;
1975 wps_data->full_line_progressbar = false;
1976 #endif
1977 wps_data->wps_loaded = false;
1980 #ifdef HAVE_LCD_BITMAP
1981 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1983 (void)wps_data; /* only needed for remote targets */
1984 bool loaded = false;
1985 char img_path[MAX_PATH];
1986 get_image_filename(bitmap->data, bmpdir,
1987 img_path, sizeof(img_path));
1989 /* load the image */
1990 int format;
1991 #ifdef HAVE_REMOTE_LCD
1992 if (curr_screen == SCREEN_REMOTE)
1993 format = FORMAT_ANY|FORMAT_REMOTE;
1994 else
1995 #endif
1996 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1998 size_t max_buf;
1999 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2000 bitmap->data = imgbuf;
2001 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2003 if (ret > 0)
2005 skin_buffer_increment(ret, true);
2006 loaded = true;
2008 else
2010 /* Abort if we can't load an image */
2011 loaded = false;
2013 return loaded;
2016 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2018 struct skin_token_list *list;
2019 /* do the progressbars */
2020 list = wps_data->progressbars;
2021 while (list)
2023 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2024 if (pb->bm.data)
2026 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2028 list = list->next;
2030 /* regular images */
2031 list = wps_data->images;
2032 while (list)
2034 struct gui_img *img = (struct gui_img*)list->token->value.data;
2035 if (img->bm.data)
2037 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2038 if (img->loaded)
2039 img->subimage_height = img->bm.height / img->num_subimages;
2041 list = list->next;
2044 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2045 /* Backdrop load scheme:
2046 * 1) %X|filename|
2047 * 2) load the backdrop from settings
2049 if (wps_data->backdrop)
2051 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2052 bmpdir, curr_screen);
2054 #endif /* has backdrop support */
2056 /* If we got here, everything was OK */
2057 return true;
2060 #endif /* HAVE_LCD_BITMAP */
2062 /* to setup up the wps-data from a format-buffer (isfile = false)
2063 from a (wps-)file (isfile = true)*/
2064 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2065 const char *buf, bool isfile)
2068 if (!wps_data || !buf)
2069 return false;
2070 #ifdef HAVE_ALBUMART
2071 int status;
2072 struct mp3entry *curtrack;
2073 long offset;
2074 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2075 if (wps_data->albumart)
2077 old_aa.state = wps_data->albumart->state;
2078 old_aa.height = wps_data->albumart->height;
2079 old_aa.width = wps_data->albumart->width;
2081 #endif
2083 skin_data_reset(wps_data);
2084 curr_screen = screen;
2086 /* alloc default viewport, will be fixed up later */
2087 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2088 if (!curr_vp)
2089 return false;
2090 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2091 if (!list)
2092 return false;
2093 add_to_ll_chain(&wps_data->viewports, list);
2096 /* Initialise the first (default) viewport */
2097 curr_vp->label = VP_DEFAULT_LABEL;
2098 curr_vp->pb = NULL;
2099 curr_vp->hidden_flags = 0;
2100 curr_vp->lines = NULL;
2102 viewport_set_defaults(&curr_vp->vp, screen);
2104 curr_line = NULL;
2105 if (!skin_start_new_line(curr_vp, 0))
2106 return false;
2108 if (!isfile)
2110 if (wps_parse(wps_data, buf, false))
2112 #ifdef HAVE_LCD_BITMAP
2113 /* load the backdrop */
2114 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2115 skin_data_reset(wps_data);
2116 return false;
2118 #endif
2119 return true;
2121 return false;
2123 else
2125 int fd = open_utf8(buf, O_RDONLY);
2127 if (fd < 0)
2128 return false;
2130 /* get buffer space from the plugin buffer */
2131 size_t buffersize = 0;
2132 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2134 if (!wps_buffer)
2135 return false;
2137 /* copy the file's content to the buffer for parsing,
2138 ensuring that every line ends with a newline char. */
2139 unsigned int start = 0;
2140 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2142 start += strlen(wps_buffer + start);
2143 if (start < buffersize - 1)
2145 wps_buffer[start++] = '\n';
2146 wps_buffer[start] = 0;
2150 close(fd);
2152 if (start <= 0)
2153 return false;
2155 /* parse the WPS source */
2156 if (!wps_parse(wps_data, wps_buffer, true)) {
2157 skin_data_reset(wps_data);
2158 return false;
2161 wps_data->wps_loaded = true;
2163 #ifdef HAVE_LCD_BITMAP
2164 /* get the bitmap dir */
2165 char bmpdir[MAX_PATH];
2166 char *dot = strrchr(buf, '.');
2168 strlcpy(bmpdir, buf, dot - buf + 1);
2170 /* load the bitmaps that were found by the parsing */
2171 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2172 skin_data_reset(wps_data);
2173 wps_data->wps_loaded = false;
2174 return false;
2176 #endif
2177 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2178 status = audio_status();
2179 if (status & AUDIO_STATUS_PLAY)
2181 struct skin_albumart *aa = wps_data->albumart;
2182 if (aa && ((aa->state && !old_aa.state) ||
2183 (aa->state &&
2184 (((old_aa.height != aa->height) ||
2185 (old_aa.width != aa->width))))))
2187 curtrack = audio_current_track();
2188 offset = curtrack->offset;
2189 audio_stop();
2190 if (!(status & AUDIO_STATUS_PAUSE))
2191 audio_play(offset);
2194 #endif
2195 #if defined(DEBUG) || defined(SIMULATOR)
2196 debug_skin_usage();
2197 #endif
2198 return true;