revert r24989 ad r24990 which are cuasing segfaults... they were working fine for...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob76527f664aa6ef87405ec00be1d20dae5c7414ed
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "config.h"
26 #include "file.h"
27 #include "misc.h"
28 #include "plugin.h"
29 #include "viewport.h"
31 #ifdef __PCTOOL__
32 #ifdef WPSEDITOR
33 #include "proxy.h"
34 #include "sysfont.h"
35 #else
36 #include "action.h"
37 #include "checkwps.h"
38 #include "audio.h"
39 #define lang_is_rtl() (false)
40 #define DEBUGF printf
41 #endif /*WPSEDITOR*/
42 #else
43 #include "debug.h"
44 #include "language.h"
45 #endif /*__PCTOOL__*/
47 #include <ctype.h>
48 #include <stdbool.h>
49 #include "font.h"
51 #include "wps_internals.h"
52 #include "skin_engine.h"
53 #include "settings.h"
54 #include "settings_list.h"
55 #include "skin_fonts.h"
57 #ifdef HAVE_LCD_BITMAP
58 #include "bmp.h"
59 #endif
61 #ifdef HAVE_ALBUMART
62 #include "playback.h"
63 #endif
65 #include "backdrop.h"
66 #include "statusbar-skinned.h"
68 #define WPS_ERROR_INVALID_PARAM -1
70 /* which screen are we parsing for? */
71 static enum screen_type curr_screen;
73 /* level of current conditional.
74 -1 means we're not in a conditional. */
75 static int level = -1;
77 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
78 or WPS_TOKEN_CONDITIONAL_START in current level */
79 static int lastcond[WPS_MAX_COND_LEVEL];
81 /* index of the WPS_TOKEN_CONDITIONAL in current level */
82 static int condindex[WPS_MAX_COND_LEVEL];
84 /* number of condtional options in current level */
85 static int numoptions[WPS_MAX_COND_LEVEL];
87 /* line number, debug only */
88 static int line_number;
90 /* the current viewport */
91 static struct skin_viewport *curr_vp;
92 /* the current line, linked to the above viewport */
93 static struct skin_line *curr_line;
95 static int follow_lang_direction = 0;
97 #if defined(DEBUG) || defined(SIMULATOR)
98 /* debugging function */
99 extern void print_debug_info(struct wps_data *data, int fail, int line);
100 extern void debug_skin_usage(void);
101 #endif
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
107 has been consumed.
109 wps_bufptr points to the char following the tag (i.e. where
110 details begin).
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
114 struct wps_token *token, struct wps_data *wps_data);
116 struct wps_tag {
117 enum wps_token_type type;
118 const char name[3];
119 unsigned char refresh_type;
120 const wps_tag_parse_func parse_func;
122 static int skip_end_of_line(const char *wps_bufptr);
123 /* prototypes of all special parse functions : */
124 static int parse_timeout(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126 static int parse_progressbar(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_dir_level(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_setting_and_lang(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_languagedirection(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data)
137 (void)wps_bufptr;
138 (void)token;
139 (void)wps_data;
140 follow_lang_direction = 2; /* 2 because it is decremented immediatly after
141 this token is parsed, after the next token it
142 will be 0 again. */
143 return 0;
146 #ifdef HAVE_LCD_BITMAP
147 static int parse_viewport_display(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 static int parse_playlistview(const char *wps_bufptr,
150 struct wps_token *token, struct wps_data *wps_data);
151 static int parse_viewport(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 static int parse_statusbar_enable(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 static int parse_statusbar_disable(const char *wps_bufptr,
156 struct wps_token *token, struct wps_data *wps_data);
157 static int parse_image_display(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_image_load(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 static int parse_font_load(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 #endif /*HAVE_LCD_BITMAP */
164 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
165 static int parse_image_special(const char *wps_bufptr,
166 struct wps_token *token, struct wps_data *wps_data);
167 #endif
168 #ifdef HAVE_ALBUMART
169 static int parse_albumart_load(const char *wps_bufptr,
170 struct wps_token *token, struct wps_data *wps_data);
171 static int parse_albumart_display(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 #endif /* HAVE_ALBUMART */
174 #ifdef HAVE_TOUCHSCREEN
175 static int parse_touchregion(const char *wps_bufptr,
176 struct wps_token *token, struct wps_data *wps_data);
177 #else
178 static int fulline_tag_not_supported(const char *wps_bufptr,
179 struct wps_token *token, struct wps_data *wps_data)
181 (void)token; (void)wps_data;
182 return skip_end_of_line(wps_bufptr);
184 #define parse_touchregion fulline_tag_not_supported
185 #endif
186 #ifdef CONFIG_RTC
187 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
188 #else
189 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
190 #endif
192 /* array of available tags - those with more characters have to go first
193 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
194 static const struct wps_tag all_tags[] = {
196 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
197 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
198 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
199 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
200 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
201 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
203 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
204 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
205 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
206 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
207 #if CONFIG_CHARGING >= CHARGING_MONITOR
208 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
209 #endif
210 #if CONFIG_CHARGING
211 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
212 #endif
213 #ifdef HAVE_USB_POWER
214 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
215 #endif
217 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
219 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
220 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
221 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
222 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
223 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
224 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
225 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
226 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
237 /* current file */
238 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
248 parse_dir_level },
250 /* next file */
251 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
261 parse_dir_level },
263 /* current metadata */
264 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
277 /* next metadata */
278 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
283 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
284 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
291 #if (CONFIG_CODEC != MAS3507D)
292 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
293 #endif
294 #if (CONFIG_CODEC == SWCODEC)
295 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
296 #endif
297 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
298 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
299 #endif
301 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
303 #ifdef HAS_REMOTE_BUTTON_HOLD
304 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
305 #else
306 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
307 #endif
309 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
311 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
312 parse_timeout },
314 #ifdef HAVE_LCD_BITMAP
315 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
316 #else
317 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
318 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
319 #endif
320 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
321 parse_progressbar },
323 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
325 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
326 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
327 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
328 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
330 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
331 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
332 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
333 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
335 #ifdef HAVE_TAGCACHE
336 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
337 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
338 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
339 #endif
341 #if CONFIG_CODEC == SWCODEC
342 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
343 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
344 #endif
346 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
347 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
349 #ifdef HAVE_LCD_BITMAP
350 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
351 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
353 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
355 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
356 parse_image_display },
358 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
359 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
360 #ifdef HAVE_ALBUMART
361 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
362 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
363 #endif
365 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
366 parse_viewport_display },
367 #ifdef HAVE_LCD_BITMAP
368 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
369 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
370 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
371 #endif
372 { WPS_NO_TOKEN, "V", 0, parse_viewport },
374 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
375 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
376 #endif
377 #endif
379 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
380 parse_setting_and_lang },
381 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
382 parse_setting_and_lang },
383 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
385 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
386 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
387 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
390 /* Recording Tokens */
391 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
392 #ifdef HAVE_RECORDING
393 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
394 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
395 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
396 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
397 #endif
398 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
399 /* the array MUST end with an empty string (first char is \0) */
403 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
404 * chains require the order to be kept.
406 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
408 if (*list == NULL)
409 *list = item;
410 else
412 struct skin_token_list *t = *list;
413 while (t->next)
414 t = t->next;
415 t->next = item;
419 /* traverse the image linked-list for an image */
420 #ifdef HAVE_LCD_BITMAP
421 struct gui_img* find_image(char label, struct wps_data *data)
423 struct skin_token_list *list = data->images;
424 while (list)
426 struct gui_img *img = (struct gui_img *)list->token->value.data;
427 if (img->label == label)
428 return img;
429 list = list->next;
431 return NULL;
434 #endif
436 /* traverse the viewport linked list for a viewport */
437 struct skin_viewport* find_viewport(char label, struct wps_data *data)
439 struct skin_token_list *list = data->viewports;
440 while (list)
442 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
443 if (vp->label == label)
444 return vp;
445 list = list->next;
447 return NULL;
451 /* create and init a new wpsll item.
452 * passing NULL to token will alloc a new one.
453 * You should only pass NULL for the token when the token type (table above)
454 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
456 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
457 void* token_data)
459 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
460 if (!token)
461 token = skin_buffer_alloc(sizeof(struct wps_token));
462 if (!llitem || !token)
463 return NULL;
464 llitem->next = NULL;
465 llitem->token = token;
466 if (token_data)
467 llitem->token->value.data = token_data;
468 return llitem;
471 /* Returns the number of chars that should be skipped to jump
472 immediately after the first eol, i.e. to the start of the next line */
473 static int skip_end_of_line(const char *wps_bufptr)
475 line_number++;
476 int skip = 0;
477 while(*(wps_bufptr + skip) != '\n')
478 skip++;
479 return ++skip;
482 /* Starts a new subline in the current line during parsing */
483 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
485 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
486 if (!subline)
487 return false;
489 subline->first_token_idx = curr_token;
490 subline->next = NULL;
492 subline->line_type = 0;
493 subline->time_mult = 0;
495 line->curr_subline->last_token_idx = curr_token-1;
496 line->curr_subline->next = subline;
497 line->curr_subline = subline;
498 return true;
501 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
503 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
504 struct skin_subline *subline = NULL;
505 if (!line)
506 return false;
508 /* init the subline */
509 subline = &line->sublines;
510 subline->first_token_idx = curr_token;
511 subline->next = NULL;
512 subline->line_type = 0;
513 subline->time_mult = 0;
515 /* init the new line */
516 line->curr_subline = &line->sublines;
517 line->next = NULL;
518 line->subline_expire_time = 0;
520 /* connect to curr_line and vp pointers.
521 * 1) close the previous lines subline
522 * 2) connect to vp pointer
523 * 3) connect to curr_line global pointer
525 if (curr_line)
527 curr_line->curr_subline->last_token_idx = curr_token - 1;
528 curr_line->next = line;
529 curr_line->curr_subline = NULL;
531 curr_line = line;
532 if (!vp->lines)
533 vp->lines = line;
534 return true;
537 #ifdef HAVE_LCD_BITMAP
539 static int parse_statusbar_enable(const char *wps_bufptr,
540 struct wps_token *token,
541 struct wps_data *wps_data)
543 (void)token; /* Kill warnings */
544 wps_data->wps_sb_tag = true;
545 wps_data->show_sb_on_wps = true;
546 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
547 viewport_set_defaults(&default_vp->vp, curr_screen);
548 return skip_end_of_line(wps_bufptr);
551 static int parse_statusbar_disable(const char *wps_bufptr,
552 struct wps_token *token,
553 struct wps_data *wps_data)
555 (void)token; /* Kill warnings */
556 wps_data->wps_sb_tag = true;
557 wps_data->show_sb_on_wps = false;
558 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
559 viewport_set_fullscreen(&default_vp->vp, curr_screen);
560 return skip_end_of_line(wps_bufptr);
563 static int get_image_id(int c)
565 if(c >= 'a' && c <= 'z')
566 return c - 'a';
567 else if(c >= 'A' && c <= 'Z')
568 return c - 'A' + 26;
569 else
570 return -1;
573 char *get_image_filename(const char *start, const char* bmpdir,
574 char *buf, int buf_size)
576 const char *end = strchr(start, '|');
577 int bmpdirlen = strlen(bmpdir);
579 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
581 buf[0] = '\0';
582 return NULL;
585 strcpy(buf, bmpdir);
586 buf[bmpdirlen] = '/';
587 memcpy( &buf[bmpdirlen + 1], start, end - start);
588 buf[bmpdirlen + 1 + end - start] = 0;
590 return buf;
593 static int parse_image_display(const char *wps_bufptr,
594 struct wps_token *token,
595 struct wps_data *wps_data)
597 char label = wps_bufptr[0];
598 int subimage;
599 struct gui_img *img;;
601 /* sanity check */
602 img = find_image(label, wps_data);
603 if (!img)
605 token->value.i = label; /* so debug works */
606 return WPS_ERROR_INVALID_PARAM;
609 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
611 if (subimage >= img->num_subimages)
612 return WPS_ERROR_INVALID_PARAM;
614 /* Store sub-image number to display in high bits */
615 token->value.i = label | (subimage << 8);
616 return 2; /* We have consumed 2 bytes */
617 } else {
618 token->value.i = label;
619 return 1; /* We have consumed 1 byte */
623 static int parse_image_load(const char *wps_bufptr,
624 struct wps_token *token,
625 struct wps_data *wps_data)
627 const char *ptr = wps_bufptr;
628 const char *pos;
629 const char* filename;
630 const char* id;
631 const char *newline;
632 int x,y;
633 struct gui_img *img;
635 /* format: %x|n|filename.bmp|x|y|
636 or %xl|n|filename.bmp|x|y|
637 or %xl|n|filename.bmp|x|y|num_subimages|
640 if (*ptr != '|')
641 return WPS_ERROR_INVALID_PARAM;
643 ptr++;
645 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
646 return WPS_ERROR_INVALID_PARAM;
648 /* Check there is a terminating | */
649 if (*ptr != '|')
650 return WPS_ERROR_INVALID_PARAM;
652 /* check the image number and load state */
653 if(find_image(*id, wps_data))
655 /* Invalid image ID */
656 return WPS_ERROR_INVALID_PARAM;
658 img = skin_buffer_alloc(sizeof(struct gui_img));
659 if (!img)
660 return WPS_ERROR_INVALID_PARAM;
661 /* save a pointer to the filename */
662 img->bm.data = (char*)filename;
663 img->label = *id;
664 img->x = x;
665 img->y = y;
666 img->num_subimages = 1;
667 img->always_display = false;
669 /* save current viewport */
670 img->vp = &curr_vp->vp;
672 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
674 img->always_display = true;
676 else
678 /* Parse the (optional) number of sub-images */
679 ptr++;
680 newline = strchr(ptr, '\n');
681 pos = strchr(ptr, '|');
682 if (pos && pos < newline)
683 img->num_subimages = atoi(ptr);
685 if (img->num_subimages <= 0)
686 return WPS_ERROR_INVALID_PARAM;
688 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
689 if (!item)
690 return WPS_ERROR_INVALID_PARAM;
691 add_to_ll_chain(&wps_data->images, item);
693 /* Skip the rest of the line */
694 return skip_end_of_line(wps_bufptr);
696 struct skin_font {
697 int id; /* the id from font_load */
698 char *name; /* filename without path and extension */
700 static struct skin_font skinfonts[MAXUSERFONTS];
701 static int parse_font_load(const char *wps_bufptr,
702 struct wps_token *token, struct wps_data *wps_data)
704 (void)wps_data; (void)token;
705 const char *ptr = wps_bufptr;
706 int id;
707 char *filename;
709 if (*ptr != '|')
710 return WPS_ERROR_INVALID_PARAM;
712 ptr++;
714 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
715 return WPS_ERROR_INVALID_PARAM;
717 /* Check there is a terminating | */
718 if (*ptr != '|')
719 return WPS_ERROR_INVALID_PARAM;
721 if (id <= FONT_UI || id >= MAXFONTS-1)
722 return WPS_ERROR_INVALID_PARAM;
723 #if defined(DEBUG) || defined(SIMULATOR)
724 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
726 DEBUGF("font id %d already being used\n", id);
728 #endif
729 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
730 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
732 return skip_end_of_line(wps_bufptr);
736 static int parse_viewport_display(const char *wps_bufptr,
737 struct wps_token *token,
738 struct wps_data *wps_data)
740 (void)wps_data;
741 char letter = wps_bufptr[0];
743 if (letter < 'a' || letter > 'z')
745 /* invalid viewport tag */
746 return WPS_ERROR_INVALID_PARAM;
748 token->value.i = letter;
749 return 1;
752 #ifdef HAVE_LCD_BITMAP
753 static int parse_playlistview_text(struct playlistviewer *viewer,
754 enum info_line_type line, char* text)
756 int cur_string = 0;
757 const struct wps_tag *tag;
758 int taglen = 0;
759 const char *start = text;
760 if (*text != '|')
761 return -1;
762 text++;
763 viewer->lines[line].count = 0;
764 viewer->lines[line].scroll = false;
765 while (*text != '|')
767 if (*text == '%') /* it is a token of some type */
769 text++;
770 taglen = 0;
771 switch(*text)
773 case '%':
774 case '<':
775 case '|':
776 case '>':
777 case ';':
778 case '#':
779 /* escaped characters */
780 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
781 viewer->lines[line].strings[cur_string][0] = *text;
782 viewer->lines[line].strings[cur_string++][1] = '\0';
783 break;
784 default:
785 for (tag = all_tags;
786 strncmp(text, tag->name, strlen(tag->name)) != 0;
787 tag++) ;
788 /* %s isnt stored as a tag so manually check for it */
789 if (tag->type == WPS_NO_TOKEN)
791 if (!strncmp(tag->name, "s", 1))
793 viewer->lines[line].scroll = true;
794 taglen = 1;
797 else if (tag->type == WPS_TOKEN_UNKNOWN)
799 int i = 0;
800 /* just copy the string */
801 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
802 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
804 viewer->lines[line].strings[cur_string][i] = text[i];
805 i++;
807 viewer->lines[line].strings[cur_string][i] = '\0';
808 cur_string++;
809 taglen = i;
811 else
813 if (tag->parse_func)
815 /* unsupported tag, reject */
816 return -1;
818 taglen = strlen(tag->name);
819 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
821 text += taglen;
824 else
826 /* regular string */
827 int i = 0;
828 /* just copy the string */
829 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
830 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
832 viewer->lines[line].strings[cur_string][i] = text[i];
833 i++;
835 viewer->lines[line].strings[cur_string][i] = '\0';
836 cur_string++;
837 text += i;
840 return text - start;
844 static int parse_playlistview(const char *wps_bufptr,
845 struct wps_token *token, struct wps_data *wps_data)
847 (void)wps_data;
848 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
849 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
850 char *ptr = strchr(wps_bufptr, '|');
851 int length;
852 if (!viewer || !ptr)
853 return WPS_ERROR_INVALID_PARAM;
854 viewer->vp = &curr_vp->vp;
855 viewer->show_icons = true;
856 viewer->start_offset = atoi(ptr+1);
857 token->value.data = (void*)viewer;
858 ptr = strchr(ptr+1, '|');
859 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
860 if (length < 0)
861 return WPS_ERROR_INVALID_PARAM;
862 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
863 if (length < 0)
864 return WPS_ERROR_INVALID_PARAM;
866 return skip_end_of_line(wps_bufptr);
868 #endif
870 static int parse_viewport(const char *wps_bufptr,
871 struct wps_token *token,
872 struct wps_data *wps_data)
874 (void)token; /* Kill warnings */
875 const char *ptr = wps_bufptr;
877 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
879 /* check for the optional letter to signify its a hideable viewport */
880 /* %Vl|<label>|<rest of tags>| */
881 skin_vp->hidden_flags = 0;
882 skin_vp->label = VP_NO_LABEL;
883 skin_vp->pb = NULL;
884 skin_vp->lines = NULL;
885 if (curr_line)
887 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
888 - (wps_data->num_tokens > 0 ? 1 : 0);
891 curr_line = NULL;
892 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
893 return WPS_ERROR_INVALID_PARAM;
896 if (*ptr == 'i')
898 skin_vp->label = VP_INFO_LABEL;
899 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
900 ++ptr;
902 else if (*ptr == 'l')
904 if (*(ptr+1) == '|')
906 char label = *(ptr+2);
907 if (label >= 'a' && label <= 'z')
909 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
910 skin_vp->label = label;
912 else
913 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
914 ptr += 3;
917 if (*ptr != '|')
918 return WPS_ERROR_INVALID_PARAM;
920 ptr++;
921 struct viewport *vp = &skin_vp->vp;
922 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
923 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
924 return WPS_ERROR_INVALID_PARAM;
926 /* Check for trailing | */
927 if (*ptr != '|')
928 return WPS_ERROR_INVALID_PARAM;
930 if (follow_lang_direction && lang_is_rtl())
932 vp->flags |= VP_FLAG_ALIGN_RIGHT;
933 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
935 else
936 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
938 /* increment because font==2 and FONT_UI_REMOTE is ambiguous */
939 if (vp->font > FONT_UI)
940 vp->font++;
941 #ifdef HAVE_REMOTE_LCD
942 if (vp->font == FONT_UI && curr_screen == SCREEN_REMOTE)
943 vp->font = FONT_UI_REMOTE;
944 #endif
946 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
947 if (!list)
948 return WPS_ERROR_INVALID_PARAM;
949 add_to_ll_chain(&wps_data->viewports, list);
950 curr_vp = skin_vp;
951 /* Skip the rest of the line */
952 return skip_end_of_line(wps_bufptr);
955 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
956 static int parse_image_special(const char *wps_bufptr,
957 struct wps_token *token,
958 struct wps_data *wps_data)
960 (void)wps_data; /* kill warning */
961 (void)token;
962 const char *pos = NULL;
963 const char *newline;
964 bool error = false;
966 pos = strchr(wps_bufptr + 1, '|');
967 newline = strchr(wps_bufptr, '\n');
969 error = (pos > newline);
972 #if LCD_DEPTH > 1
973 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
975 /* format: %X|filename.bmp| or %Xd */
976 if (*(wps_bufptr) == 'd')
978 wps_data->backdrop = NULL;
979 return skip_end_of_line(wps_bufptr);
981 else if (!error)
982 wps_data->backdrop = (char*)wps_bufptr + 1;
984 #endif
985 if (error)
986 return WPS_ERROR_INVALID_PARAM;
987 /* Skip the rest of the line */
988 return skip_end_of_line(wps_bufptr);
990 #endif
992 #endif /* HAVE_LCD_BITMAP */
994 static int parse_setting_and_lang(const char *wps_bufptr,
995 struct wps_token *token,
996 struct wps_data *wps_data)
998 /* NOTE: both the string validations that happen in here will
999 * automatically PASS on checkwps because its too hard to get
1000 * settings_list.c and englinsh.lang built for it.
1001 * If that ever changes remove the #ifndef __PCTOOL__'s here
1003 (void)wps_data;
1004 const char *ptr = wps_bufptr;
1005 const char *end;
1006 int i = 0;
1007 char temp[64];
1009 /* Find the setting's cfg_name */
1010 if (*ptr != '|')
1011 return WPS_ERROR_INVALID_PARAM;
1012 ptr++;
1013 end = strchr(ptr,'|');
1014 if (!end)
1015 return WPS_ERROR_INVALID_PARAM;
1016 strlcpy(temp, ptr,end-ptr+1);
1018 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1020 #ifndef __PCTOOL__
1021 i = lang_english_to_id(temp);
1022 if (i < 0)
1023 return WPS_ERROR_INVALID_PARAM;
1024 #endif
1026 else
1028 /* Find the setting */
1029 for (i=0; i<nb_settings; i++)
1030 if (settings[i].cfg_name &&
1031 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1032 /* prevent matches on cfg_name prefixes */
1033 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1034 break;
1035 #ifndef __PCTOOL__
1036 if (i == nb_settings)
1037 return WPS_ERROR_INVALID_PARAM;
1038 #endif
1040 /* Store the setting number */
1041 token->value.i = i;
1043 /* Skip the rest of the line */
1044 return end-ptr+2;
1048 static int parse_dir_level(const char *wps_bufptr,
1049 struct wps_token *token,
1050 struct wps_data *wps_data)
1052 char val[] = { *wps_bufptr, '\0' };
1053 token->value.i = atoi(val);
1054 (void)wps_data; /* Kill warnings */
1055 return 1;
1058 static int parse_timeout(const char *wps_bufptr,
1059 struct wps_token *token,
1060 struct wps_data *wps_data)
1062 int skip = 0;
1063 int val = 0;
1064 bool have_point = false;
1065 bool have_tenth = false;
1067 (void)wps_data; /* Kill the warning */
1069 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1071 if (*wps_bufptr != '.')
1073 val *= 10;
1074 val += *wps_bufptr - '0';
1075 if (have_point)
1077 have_tenth = true;
1078 wps_bufptr++;
1079 skip++;
1080 break;
1083 else
1084 have_point = true;
1086 wps_bufptr++;
1087 skip++;
1090 if (have_tenth == false)
1091 val *= 10;
1093 if (val == 0 && skip == 0)
1095 /* decide what to do if no value was specified */
1096 switch (token->type)
1098 case WPS_TOKEN_SUBLINE_TIMEOUT:
1099 return -1;
1100 case WPS_TOKEN_BUTTON_VOLUME:
1101 val = 10;
1102 break;
1105 token->value.i = val;
1107 return skip;
1110 static int parse_progressbar(const char *wps_bufptr,
1111 struct wps_token *token,
1112 struct wps_data *wps_data)
1114 /* %pb or %pb|filename|x|y|width|height|
1115 using - for any of the params uses "sane" values */
1116 #ifdef HAVE_LCD_BITMAP
1117 enum {
1118 PB_FILENAME = 0,
1119 PB_X,
1120 PB_Y,
1121 PB_WIDTH,
1122 PB_HEIGHT
1124 const char *filename;
1125 int x, y, height, width;
1126 uint32_t set = 0;
1127 const char *ptr = wps_bufptr;
1128 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1129 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1131 if (!pb || !item)
1132 return WPS_ERROR_INVALID_PARAM;
1134 struct viewport *vp = &curr_vp->vp;
1135 /* we need to know what line number (viewport relative) this pb is,
1136 * so count them... */
1137 int line_num = -1;
1138 struct skin_line *line = curr_vp->lines;
1139 while (line)
1141 line_num++;
1142 line = line->next;
1144 pb->have_bitmap_pb = false;
1145 pb->bm.data = NULL; /* no bitmap specified */
1146 pb->follow_lang_direction = follow_lang_direction > 0;
1148 if (*wps_bufptr != '|') /* regular old style */
1150 pb->x = 0;
1151 pb->width = vp->width;
1152 pb->height = SYSFONT_HEIGHT-2;
1153 pb->y = -line_num - 1; /* Will be computed during the rendering */
1155 curr_vp->pb = pb;
1156 add_to_ll_chain(&wps_data->progressbars, item);
1157 return 0;
1159 ptr = wps_bufptr + 1;
1161 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1162 &x, &y, &width, &height)))
1163 return WPS_ERROR_INVALID_PARAM;
1165 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1166 pb->bm.data = (char*)filename;
1168 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1169 pb->x = x;
1170 else
1171 pb->x = vp->x;
1173 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1175 /* A zero width causes a divide-by-zero error later, so reject it */
1176 if (width == 0)
1177 return WPS_ERROR_INVALID_PARAM;
1179 pb->width = width;
1181 else
1182 pb->width = vp->width - pb->x;
1184 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1186 /* A zero height makes no sense - reject it */
1187 if (height == 0)
1188 return WPS_ERROR_INVALID_PARAM;
1190 pb->height = height;
1192 else
1194 if (vp->font > FONT_UI)
1195 pb->height = -1; /* calculate at display time */
1196 else
1198 #ifndef __PCTOOL__
1199 pb->height = font_get(vp->font)->height;
1200 #else
1201 pb->height = 8;
1202 #endif
1206 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1207 pb->y = y;
1208 else
1209 pb->y = -line_num - 1; /* Will be computed during the rendering */
1211 curr_vp->pb = pb;
1212 add_to_ll_chain(&wps_data->progressbars, item);
1214 /* Skip the rest of the line */
1215 return skip_end_of_line(wps_bufptr)-1;
1216 #else
1217 (void)token;
1219 if (*(wps_bufptr-1) == 'f')
1220 wps_data->full_line_progressbar = true;
1221 else
1222 wps_data->full_line_progressbar = false;
1224 return 0;
1226 #endif
1229 #ifdef HAVE_ALBUMART
1230 static int parse_int(const char *newline, const char **_pos, int *num)
1232 *_pos = parse_list("d", NULL, '|', *_pos, num);
1234 return (!*_pos || *_pos > newline || **_pos != '|');
1237 static int parse_albumart_load(const char *wps_bufptr,
1238 struct wps_token *token,
1239 struct wps_data *wps_data)
1241 const char *_pos, *newline;
1242 bool parsing;
1243 struct dim dimensions;
1244 int albumart_slot;
1245 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1246 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1247 (void)token; /* silence warning */
1248 if (!aa)
1249 return skip_end_of_line(wps_bufptr);
1251 /* reset albumart info in wps */
1252 aa->width = -1;
1253 aa->height = -1;
1254 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1255 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1256 aa->vp = &curr_vp->vp;
1258 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1260 newline = strchr(wps_bufptr, '\n');
1262 _pos = wps_bufptr;
1264 if (*_pos != '|')
1265 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1267 ++_pos;
1269 /* initial validation and parsing of x component */
1270 if (parse_int(newline, &_pos, &aa->x))
1271 return WPS_ERROR_INVALID_PARAM;
1273 ++_pos;
1275 /* initial validation and parsing of y component */
1276 if (parse_int(newline, &_pos, &aa->y))
1277 return WPS_ERROR_INVALID_PARAM;
1279 /* parsing width field */
1280 parsing = true;
1281 while (parsing)
1283 /* apply each modifier in turn */
1284 ++_pos;
1285 switch (*_pos)
1287 case 'l':
1288 case 'L':
1289 case '+':
1290 if (swap_for_rtl)
1291 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1292 else
1293 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1294 break;
1295 case 'c':
1296 case 'C':
1297 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1298 break;
1299 case 'r':
1300 case 'R':
1301 case '-':
1302 if (swap_for_rtl)
1303 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1304 else
1305 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1306 break;
1307 case 'd':
1308 case 'D':
1309 case 'i':
1310 case 'I':
1311 case 's':
1312 case 'S':
1313 /* simply ignored */
1314 break;
1315 default:
1316 parsing = false;
1317 break;
1320 /* extract max width data */
1321 if (*_pos != '|')
1323 if (parse_int(newline, &_pos, &aa->width))
1324 return WPS_ERROR_INVALID_PARAM;
1327 /* parsing height field */
1328 parsing = true;
1329 while (parsing)
1331 /* apply each modifier in turn */
1332 ++_pos;
1333 switch (*_pos)
1335 case 't':
1336 case 'T':
1337 case '-':
1338 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1339 break;
1340 case 'c':
1341 case 'C':
1342 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1343 break;
1344 case 'b':
1345 case 'B':
1346 case '+':
1347 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1348 break;
1349 case 'd':
1350 case 'D':
1351 case 'i':
1352 case 'I':
1353 case 's':
1354 case 'S':
1355 /* simply ignored */
1356 break;
1357 default:
1358 parsing = false;
1359 break;
1362 /* extract max height data */
1363 if (*_pos != '|')
1365 if (parse_int(newline, &_pos, &aa->height))
1366 return WPS_ERROR_INVALID_PARAM;
1369 /* if we got here, we parsed everything ok .. ! */
1370 if (aa->width < 0)
1371 aa->width = 0;
1372 else if (aa->width > LCD_WIDTH)
1373 aa->width = LCD_WIDTH;
1375 if (aa->height < 0)
1376 aa->height = 0;
1377 else if (aa->height > LCD_HEIGHT)
1378 aa->height = LCD_HEIGHT;
1380 if (swap_for_rtl)
1381 aa->x = LCD_WIDTH - (aa->x + aa->width);
1383 aa->state = WPS_ALBUMART_LOAD;
1384 aa->draw = false;
1385 wps_data->albumart = aa;
1387 dimensions.width = aa->width;
1388 dimensions.height = aa->height;
1390 albumart_slot = playback_claim_aa_slot(&dimensions);
1392 if (0 <= albumart_slot)
1393 wps_data->playback_aa_slot = albumart_slot;
1395 /* Skip the rest of the line */
1396 return skip_end_of_line(wps_bufptr);
1399 static int parse_albumart_display(const char *wps_bufptr,
1400 struct wps_token *token,
1401 struct wps_data *wps_data)
1403 (void)wps_bufptr;
1404 struct wps_token *prev = token-1;
1405 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1407 token->type = WPS_TOKEN_ALBUMART_FOUND;
1409 else if (wps_data->albumart)
1411 wps_data->albumart->vp = &curr_vp->vp;
1413 #if 0
1414 /* the old code did this so keep it here for now...
1415 * this is to allow the posibility to showing the next tracks AA! */
1416 if (wps_bufptr+1 == 'n')
1417 return 1;
1418 #endif
1419 return 0;
1421 #endif /* HAVE_ALBUMART */
1423 #ifdef HAVE_TOUCHSCREEN
1425 struct touchaction {const char* s; int action;};
1426 static const struct touchaction touchactions[] = {
1427 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1428 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1429 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1430 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1431 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1432 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1433 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1434 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1436 static int parse_touchregion(const char *wps_bufptr,
1437 struct wps_token *token, struct wps_data *wps_data)
1439 (void)token;
1440 unsigned i, imax;
1441 struct touchregion *region = NULL;
1442 const char *ptr = wps_bufptr;
1443 const char *action;
1444 const char pb_string[] = "progressbar";
1445 const char vol_string[] = "volume";
1446 int x,y,w,h;
1448 /* format: %T|x|y|width|height|action|
1449 * if action starts with & the area must be held to happen
1450 * action is one of:
1451 * play - play/pause playback
1452 * stop - stop playback, exit the wps
1453 * prev - prev track
1454 * next - next track
1455 * ffwd - seek forward
1456 * rwd - seek backwards
1457 * menu - go back to the main menu
1458 * browse - go back to the file/db browser
1459 * shuffle - toggle shuffle mode
1460 * repmode - cycle the repeat mode
1461 * quickscreen - go into the quickscreen
1462 * contextmenu - open the context menu
1463 * playlist - go into the playlist
1464 * pitch - go into the pitchscreen
1465 * volup - increase volume by one step
1466 * voldown - decrease volume by one step
1470 if (*ptr != '|')
1471 return WPS_ERROR_INVALID_PARAM;
1472 ptr++;
1474 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1475 return WPS_ERROR_INVALID_PARAM;
1477 /* Check there is a terminating | */
1478 if (*ptr != '|')
1479 return WPS_ERROR_INVALID_PARAM;
1481 region = skin_buffer_alloc(sizeof(struct touchregion));
1482 if (!region)
1483 return WPS_ERROR_INVALID_PARAM;
1485 /* should probably do some bounds checking here with the viewport... but later */
1486 region->action = ACTION_NONE;
1487 region->x = x;
1488 region->y = y;
1489 region->width = w;
1490 region->height = h;
1491 region->wvp = curr_vp;
1492 region->armed = false;
1494 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1495 && *(action + sizeof(pb_string)-1) == '|')
1496 region->type = WPS_TOUCHREGION_SCROLLBAR;
1497 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1498 && *(action + sizeof(vol_string)-1) == '|')
1499 region->type = WPS_TOUCHREGION_VOLUME;
1500 else
1502 region->type = WPS_TOUCHREGION_ACTION;
1504 if (*action == '&')
1506 action++;
1507 region->repeat = true;
1509 else
1510 region->repeat = false;
1512 i = 0;
1513 imax = ARRAYLEN(touchactions);
1514 while ((region->action == ACTION_NONE) &&
1515 (i < imax))
1517 /* try to match with one of our touchregion screens */
1518 int len = strlen(touchactions[i].s);
1519 if (!strncmp(touchactions[i].s, action, len)
1520 && *(action+len) == '|')
1521 region->action = touchactions[i].action;
1522 i++;
1524 if (region->action == ACTION_NONE)
1525 return WPS_ERROR_INVALID_PARAM;
1527 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1528 if (!item)
1529 return WPS_ERROR_INVALID_PARAM;
1530 add_to_ll_chain(&wps_data->touchregions, item);
1531 return skip_end_of_line(wps_bufptr);
1533 #endif
1535 /* Parse a generic token from the given string. Return the length read */
1536 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1538 int skip = 0, taglen = 0, ret;
1539 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1540 const struct wps_tag *tag;
1541 memset(token, 0, sizeof(*token));
1543 switch(*wps_bufptr)
1546 case '%':
1547 case '<':
1548 case '|':
1549 case '>':
1550 case ';':
1551 case '#':
1552 /* escaped characters */
1553 token->type = WPS_TOKEN_CHARACTER;
1554 token->value.c = *wps_bufptr;
1555 taglen = 1;
1556 wps_data->num_tokens++;
1557 break;
1559 case '?':
1560 /* conditional tag */
1561 token->type = WPS_TOKEN_CONDITIONAL;
1562 level++;
1563 condindex[level] = wps_data->num_tokens;
1564 numoptions[level] = 1;
1565 wps_data->num_tokens++;
1566 ret = parse_token(wps_bufptr + 1, wps_data);
1567 if (ret < 0) return ret;
1568 taglen = 1 + ret;
1569 break;
1571 default:
1572 /* find what tag we have */
1573 for (tag = all_tags;
1574 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1575 tag++) ;
1577 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1578 token->type = tag->type;
1579 curr_line->curr_subline->line_type |= tag->refresh_type;
1581 /* if the tag has a special parsing function, we call it */
1582 if (tag->parse_func)
1584 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1585 if (ret < 0) return ret;
1586 skip += ret;
1589 /* Some tags we don't want to save as tokens */
1590 if (tag->type == WPS_NO_TOKEN)
1591 break;
1593 /* tags that start with 'F', 'I' or 'D' are for the next file */
1594 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1595 *(tag->name) == 'D')
1596 token->next = true;
1598 wps_data->num_tokens++;
1599 break;
1602 skip += taglen;
1603 return skip;
1608 * Returns the number of bytes to skip the buf pointer to access the false
1609 * branch in a _binary_ conditional
1611 * That is:
1612 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1613 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1615 * depending on the features of a target it's not called from check_feature_tag,
1616 * hence the __attribute__ or it issues compiler warnings
1620 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1621 static int find_false_branch(const char *wps_bufptr)
1623 const char *buf = wps_bufptr;
1624 /* wps_bufptr is after the opening '<', hence level = 1*/
1625 int level = 1;
1626 char ch;
1629 ch = *buf;
1630 if (ch == '%')
1631 { /* filter out the characters we check later if they're printed
1632 * as literals */
1633 ch = *(++buf);
1634 if (ch == '<' || ch == '>' || ch == '|')
1635 continue;
1636 /* else: some tags/printed literals we skip over */
1638 else if (ch == '<') /* nested conditional */
1639 level++;
1640 else if (ch == '>')
1641 { /* closed our or a nested conditional,
1642 * do NOT skip over the '>' so that wps_parse() sees it for closing
1643 * if it is the closing one for our conditional */
1644 level--;
1646 else if (ch == '|' && level == 1)
1647 { /* we found our separator, point before and get out */
1648 break;
1650 /* if level is 0, we don't have a false branch */
1651 } while (level > 0 && *(++buf));
1653 return buf - wps_bufptr;
1657 * returns the number of bytes to get the appropriate branch of a binary
1658 * conditional
1660 * That means:
1661 * - if a feature is available, it returns 0 to not skip anything
1662 * - if the feature is not available, skip to the false branch and don't
1663 * parse the true branch at all
1665 * */
1666 static int check_feature_tag(const char *wps_bufptr, const int type)
1668 (void)wps_bufptr;
1669 switch (type)
1671 case WPS_TOKEN_RTC_PRESENT:
1672 #if CONFIG_RTC
1673 return 0;
1674 #else
1675 return find_false_branch(wps_bufptr);
1676 #endif
1677 case WPS_TOKEN_HAVE_RECORDING:
1678 #ifdef HAVE_RECORDING
1679 return 0;
1680 #else
1681 return find_false_branch(wps_bufptr);
1682 #endif
1683 default: /* not a tag we care about, just don't skip */
1684 return 0;
1689 /* Parses the WPS.
1690 data is the pointer to the structure where the parsed WPS should be stored.
1691 It is initialised.
1692 wps_bufptr points to the string containing the WPS tags */
1693 #define TOKEN_BLOCK_SIZE 128
1694 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1696 if (!data || !wps_bufptr || !*wps_bufptr)
1697 return false;
1698 enum wps_parse_error fail = PARSE_OK;
1699 int ret;
1700 int max_tokens = TOKEN_BLOCK_SIZE;
1701 size_t buf_free = 0;
1702 line_number = 0;
1703 level = -1;
1705 /* allocate enough RAM for a reasonable skin, grow as needed.
1706 * Free any used RAM before loading the images to be 100% RAM efficient */
1707 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1708 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1709 return false;
1710 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1711 data->num_tokens = 0;
1713 #if LCD_DEPTH > 1
1714 /* Backdrop defaults to the setting unless %X is used, so set it now */
1715 if (global_settings.backdrop_file[0])
1717 data->backdrop = "-";
1719 #endif
1721 while (*wps_bufptr && !fail)
1723 if (follow_lang_direction)
1724 follow_lang_direction--;
1725 /* first make sure there is enough room for tokens */
1726 if (max_tokens <= data->num_tokens + 5)
1728 int extra_tokens = TOKEN_BLOCK_SIZE;
1729 size_t needed = extra_tokens * sizeof(struct wps_token);
1730 /* do some smarts here to grow the array a bit */
1731 if (skin_buffer_freespace() < needed)
1733 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1734 break;
1736 skin_buffer_increment(needed, false);
1737 max_tokens += extra_tokens;
1740 switch(*wps_bufptr++)
1743 /* Regular tag */
1744 case '%':
1745 if ((ret = parse_token(wps_bufptr, data)) < 0)
1747 fail = PARSE_FAIL_COND_INVALID_PARAM;
1748 break;
1750 else if (level >= WPS_MAX_COND_LEVEL - 1)
1752 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1753 break;
1755 wps_bufptr += ret;
1756 break;
1758 /* Alternating sublines separator */
1759 case ';':
1760 if (level >= 0) /* there are unclosed conditionals */
1762 fail = PARSE_FAIL_UNCLOSED_COND;
1763 break;
1766 if (!skin_start_new_subline(curr_line, data->num_tokens))
1767 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1769 break;
1771 /* Conditional list start */
1772 case '<':
1773 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1775 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1776 break;
1778 wps_bufptr += check_feature_tag(wps_bufptr,
1779 data->tokens[data->num_tokens-1].type);
1780 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1781 lastcond[level] = data->num_tokens++;
1782 break;
1784 /* Conditional list end */
1785 case '>':
1786 if (level < 0) /* not in a conditional, invalid char */
1788 fail = PARSE_FAIL_INVALID_CHAR;
1789 break;
1792 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1793 if (lastcond[level])
1794 data->tokens[lastcond[level]].value.i = data->num_tokens;
1795 else
1797 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1798 break;
1801 lastcond[level] = 0;
1802 data->num_tokens++;
1803 data->tokens[condindex[level]].value.i = numoptions[level];
1804 level--;
1805 break;
1807 /* Conditional list option */
1808 case '|':
1809 if (level < 0) /* not in a conditional, invalid char */
1811 fail = PARSE_FAIL_INVALID_CHAR;
1812 break;
1815 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1816 if (lastcond[level])
1817 data->tokens[lastcond[level]].value.i = data->num_tokens;
1818 else
1820 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1821 break;
1824 lastcond[level] = data->num_tokens;
1825 numoptions[level]++;
1826 data->num_tokens++;
1827 break;
1829 /* Comment */
1830 case '#':
1831 if (level >= 0) /* there are unclosed conditionals */
1833 fail = PARSE_FAIL_UNCLOSED_COND;
1834 break;
1837 wps_bufptr += skip_end_of_line(wps_bufptr);
1838 break;
1840 /* End of this line */
1841 case '\n':
1842 if (level >= 0) /* there are unclosed conditionals */
1844 fail = PARSE_FAIL_UNCLOSED_COND;
1845 break;
1847 /* add a new token for the \n so empty lines are correct */
1848 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1849 data->tokens[data->num_tokens].value.c = '\n';
1850 data->tokens[data->num_tokens].next = false;
1851 data->num_tokens++;
1853 if (!skin_start_new_line(curr_vp, data->num_tokens))
1855 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1856 break;
1858 line_number++;
1860 break;
1862 /* String */
1863 default:
1865 unsigned int len = 1;
1866 const char *string_start = wps_bufptr - 1;
1868 /* find the length of the string */
1869 while (*wps_bufptr && *wps_bufptr != '#' &&
1870 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1871 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1872 *wps_bufptr != '|' && *wps_bufptr != '\n')
1874 wps_bufptr++;
1875 len++;
1878 /* look if we already have that string */
1879 char *str;
1880 bool found = false;
1881 struct skin_token_list *list = data->strings;
1882 while (list)
1884 str = (char*)list->token->value.data;
1885 found = (strlen(str) == len &&
1886 strncmp(string_start, str, len) == 0);
1887 if (found)
1888 break; /* break here because the list item is
1889 used if its found */
1890 list = list->next;
1892 /* If a matching string is found, found is true and i is
1893 the index of the string. If not, found is false */
1895 if (!found)
1897 /* new string */
1898 str = (char*)skin_buffer_alloc(len+1);
1899 if (!str)
1901 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1902 break;
1904 strlcpy(str, string_start, len+1);
1905 struct skin_token_list *item =
1906 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1907 if(!item)
1909 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1910 break;
1912 add_to_ll_chain(&data->strings, item);
1914 else
1916 /* another occurrence of an existing string */
1917 data->tokens[data->num_tokens].value.data = list->token->value.data;
1919 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1920 data->num_tokens++;
1922 break;
1926 if (!fail && level >= 0) /* there are unclosed conditionals */
1927 fail = PARSE_FAIL_UNCLOSED_COND;
1929 if (*wps_bufptr && !fail)
1930 /* one of the limits of the while loop was exceeded */
1931 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1933 /* Success! */
1934 curr_line->curr_subline->last_token_idx = data->num_tokens;
1935 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1936 /* freeup unused tokens */
1937 skin_buffer_free_from_front(sizeof(struct wps_token)
1938 * (max_tokens - data->num_tokens));
1940 #if defined(DEBUG) || defined(SIMULATOR)
1941 if (debug)
1943 print_debug_info(data, fail, line_number);
1944 debug_skin_usage();
1946 #else
1947 (void)debug;
1948 #endif
1950 return (fail == 0);
1955 * initial setup of wps_data; does reset everything
1956 * except fields which need to survive, i.e.
1959 static void skin_data_reset(struct wps_data *wps_data)
1961 #ifdef HAVE_LCD_BITMAP
1962 wps_data->images = NULL;
1963 wps_data->progressbars = NULL;
1964 #endif
1965 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1966 wps_data->backdrop = NULL;
1967 #endif
1968 #ifdef HAVE_TOUCHSCREEN
1969 wps_data->touchregions = NULL;
1970 #endif
1971 wps_data->viewports = NULL;
1972 wps_data->strings = NULL;
1973 #ifdef HAVE_ALBUMART
1974 wps_data->albumart = NULL;
1975 if (wps_data->playback_aa_slot >= 0)
1977 playback_release_aa_slot(wps_data->playback_aa_slot);
1978 wps_data->playback_aa_slot = -1;
1980 #endif
1981 wps_data->tokens = NULL;
1982 wps_data->num_tokens = 0;
1984 #ifdef HAVE_LCD_BITMAP
1985 wps_data->peak_meter_enabled = false;
1986 wps_data->wps_sb_tag = false;
1987 wps_data->show_sb_on_wps = false;
1988 #else /* HAVE_LCD_CHARCELLS */
1989 /* progress bars */
1990 int i;
1991 for (i = 0; i < 8; i++)
1993 wps_data->wps_progress_pat[i] = 0;
1995 wps_data->full_line_progressbar = false;
1996 #endif
1997 wps_data->wps_loaded = false;
2000 #ifdef HAVE_LCD_BITMAP
2001 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2003 (void)wps_data; /* only needed for remote targets */
2004 char img_path[MAX_PATH];
2005 get_image_filename(bitmap->data, bmpdir,
2006 img_path, sizeof(img_path));
2008 /* load the image */
2009 int format;
2010 #ifdef HAVE_REMOTE_LCD
2011 if (curr_screen == SCREEN_REMOTE)
2012 format = FORMAT_ANY|FORMAT_REMOTE;
2013 else
2014 #endif
2015 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2017 size_t max_buf;
2018 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2019 bitmap->data = imgbuf;
2020 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2022 if (ret > 0)
2024 skin_buffer_increment(ret, true);
2025 return true;
2027 else
2029 /* Abort if we can't load an image */
2030 DEBUGF("Couldn't load '%s'\n", img_path);
2031 return false;
2035 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2037 struct skin_token_list *list;
2038 bool retval = true; /* return false if a single image failed to load */
2039 /* do the progressbars */
2040 list = wps_data->progressbars;
2041 while (list)
2043 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2044 if (pb->bm.data)
2046 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2047 if (!pb->have_bitmap_pb) /* no success */
2048 retval = false;
2050 list = list->next;
2052 /* regular images */
2053 list = wps_data->images;
2054 while (list)
2056 struct gui_img *img = (struct gui_img*)list->token->value.data;
2057 if (img->bm.data)
2059 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2060 if (img->loaded)
2061 img->subimage_height = img->bm.height / img->num_subimages;
2062 else
2063 retval = false;
2065 list = list->next;
2068 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2069 /* Backdrop load scheme:
2070 * 1) %X|filename|
2071 * 2) load the backdrop from settings
2073 if (wps_data->backdrop)
2075 bool needed = wps_data->backdrop[0] != '-';
2076 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2077 bmpdir, curr_screen);
2078 if (!wps_data->backdrop && needed)
2079 retval = false;
2081 #endif /* has backdrop support */
2083 return retval;
2086 static bool skin_load_fonts(struct wps_data *data)
2088 /* don't spit out after the first failue to aid debugging */
2089 bool success = true;
2090 struct skin_token_list *vp_list;
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 < SYSTEMFONTCOUNT)
2101 { /* the usual case -> built-in fonts */
2102 continue;
2105 /* decrement, because font has been incremented in viewport parsing
2106 * due to the FONT_UI_REMOTE ambiguity */
2107 int skin_font_id = vp->font-1;
2109 /* now find the corresponding skin_font */
2110 struct skin_font *font = &skinfonts[skin_font_id-FONT_FIRSTUSERFONT];
2111 if (!font)
2113 DEBUGF("Could not find font %d\n", skin_font_id);
2114 success = false;
2115 continue;
2118 /* load the font - will handle loading the same font again if
2119 * multiple viewports use the same */
2120 if (font->id < 0)
2122 char *bar = strchr(font->name, '|');
2123 *bar = '\0';
2124 font->id = skin_font_load(font->name);
2127 if (font->id < 0)
2129 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2130 skin_font_id, font->name);
2131 success = false;
2132 continue;
2135 /* finally, assign the font_id to the viewport */
2136 vp->font = font->id;
2138 return success;
2141 #endif /* HAVE_LCD_BITMAP */
2143 /* to setup up the wps-data from a format-buffer (isfile = false)
2144 from a (wps-)file (isfile = true)*/
2145 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2146 const char *buf, bool isfile)
2149 if (!wps_data || !buf)
2150 return false;
2151 #ifdef HAVE_ALBUMART
2152 int status;
2153 struct mp3entry *curtrack;
2154 long offset;
2155 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2156 if (wps_data->albumart)
2158 old_aa.state = wps_data->albumart->state;
2159 old_aa.height = wps_data->albumart->height;
2160 old_aa.width = wps_data->albumart->width;
2162 #endif
2164 skin_data_reset(wps_data);
2165 curr_screen = screen;
2167 /* alloc default viewport, will be fixed up later */
2168 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2169 if (!curr_vp)
2170 return false;
2171 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2172 if (!list)
2173 return false;
2174 add_to_ll_chain(&wps_data->viewports, list);
2177 /* Initialise the first (default) viewport */
2178 curr_vp->label = VP_DEFAULT_LABEL;
2179 curr_vp->pb = NULL;
2180 curr_vp->hidden_flags = 0;
2181 curr_vp->lines = NULL;
2183 viewport_set_defaults(&curr_vp->vp, screen);
2185 curr_line = NULL;
2186 if (!skin_start_new_line(curr_vp, 0))
2187 return false;
2189 if (!isfile)
2191 if (wps_parse(wps_data, buf, false))
2193 #ifdef HAVE_LCD_BITMAP
2194 /* load the backdrop */
2195 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2196 skin_data_reset(wps_data);
2197 return false;
2199 #endif
2200 return true;
2202 return false;
2204 else
2206 int fd = open_utf8(buf, O_RDONLY);
2208 if (fd < 0)
2209 return false;
2211 /* get buffer space from the plugin buffer */
2212 size_t buffersize = 0;
2213 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2215 if (!wps_buffer)
2216 return false;
2218 /* copy the file's content to the buffer for parsing,
2219 ensuring that every line ends with a newline char. */
2220 unsigned int start = 0;
2221 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2223 start += strlen(wps_buffer + start);
2224 if (start < buffersize - 1)
2226 wps_buffer[start++] = '\n';
2227 wps_buffer[start] = 0;
2231 close(fd);
2233 if (start <= 0)
2234 return false;
2236 /* parse the WPS source */
2237 if (!wps_parse(wps_data, wps_buffer, true)) {
2238 skin_data_reset(wps_data);
2239 return false;
2242 wps_data->wps_loaded = true;
2244 #ifdef HAVE_LCD_BITMAP
2245 /* get the bitmap dir */
2246 char bmpdir[MAX_PATH];
2247 char *dot = strrchr(buf, '.');
2249 strlcpy(bmpdir, buf, dot - buf + 1);
2250 /* load the bitmaps that were found by the parsing */
2251 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2252 skin_data_reset(wps_data);
2253 wps_data->wps_loaded = false;
2254 return false;
2256 if (!skin_load_fonts(wps_data))
2258 skin_data_reset(wps_data);
2259 wps_data->wps_loaded = false;
2260 return false;
2262 #endif
2263 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2264 status = audio_status();
2265 if (status & AUDIO_STATUS_PLAY)
2267 struct skin_albumart *aa = wps_data->albumart;
2268 if (aa && ((aa->state && !old_aa.state) ||
2269 (aa->state &&
2270 (((old_aa.height != aa->height) ||
2271 (old_aa.width != aa->width))))))
2273 curtrack = audio_current_track();
2274 offset = curtrack->offset;
2275 audio_stop();
2276 if (!(status & AUDIO_STATUS_PAUSE))
2277 audio_play(offset);
2280 #endif
2281 #if defined(DEBUG) || defined(SIMULATOR)
2282 debug_skin_usage();
2283 #endif
2284 return true;