add Packard Bell Vibe 500 to RBUtil.
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob70ffe0b516a3d0d0e5aa7bd6d7de779d6aca6c92
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_statusbar_inbuilt(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_image_display(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 static int parse_image_load(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 static int parse_font_load(const char *wps_bufptr,
164 struct wps_token *token, struct wps_data *wps_data);
165 #endif /*HAVE_LCD_BITMAP */
166 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
167 static int parse_image_special(const char *wps_bufptr,
168 struct wps_token *token, struct wps_data *wps_data);
169 #endif
170 #ifdef HAVE_ALBUMART
171 static int parse_albumart_load(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 static int parse_albumart_display(const char *wps_bufptr,
174 struct wps_token *token, struct wps_data *wps_data);
175 #endif /* HAVE_ALBUMART */
176 #ifdef HAVE_TOUCHSCREEN
177 static int parse_touchregion(const char *wps_bufptr,
178 struct wps_token *token, struct wps_data *wps_data);
179 #else
180 static int fulline_tag_not_supported(const char *wps_bufptr,
181 struct wps_token *token, struct wps_data *wps_data)
183 (void)token; (void)wps_data;
184 return skip_end_of_line(wps_bufptr);
186 #define parse_touchregion fulline_tag_not_supported
187 #endif
188 #ifdef CONFIG_RTC
189 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
190 #else
191 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
192 #endif
194 /* array of available tags - those with more characters have to go first
195 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
196 static const struct wps_tag all_tags[] = {
198 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
199 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
200 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
201 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
202 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
203 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
205 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
206 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
207 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
208 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
209 #if CONFIG_CHARGING >= CHARGING_MONITOR
210 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
211 #endif
212 #if CONFIG_CHARGING
213 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
214 #endif
215 #ifdef HAVE_USB_POWER
216 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
217 #endif
219 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
221 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
222 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
223 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
224 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
225 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
226 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
236 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
237 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
239 /* current file */
240 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
250 parse_dir_level },
252 /* next file */
253 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
263 parse_dir_level },
265 /* current metadata */
266 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
276 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
277 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
279 /* next metadata */
280 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
283 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
284 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
290 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
293 #if (CONFIG_CODEC != MAS3507D)
294 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
295 #endif
296 #if (CONFIG_CODEC == SWCODEC)
297 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
298 #endif
299 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
300 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
301 #endif
303 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
305 #ifdef HAS_REMOTE_BUTTON_HOLD
306 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
307 #else
308 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
309 #endif
311 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
312 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
313 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
314 parse_timeout },
316 #ifdef HAVE_LCD_BITMAP
317 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
318 #else
319 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
320 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
321 #endif
322 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
323 parse_progressbar },
325 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
327 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
328 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
329 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
330 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
331 { WPS_TOKEN_TRACK_STARTING, "pS", WPS_REFRESH_DYNAMIC, parse_timeout },
332 { WPS_TOKEN_TRACK_ENDING, "pE", WPS_REFRESH_DYNAMIC, parse_timeout },
334 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
335 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
336 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
337 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
339 #ifdef HAVE_TAGCACHE
340 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
341 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
342 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
343 #endif
345 #if CONFIG_CODEC == SWCODEC
346 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
347 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
348 #endif
350 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
351 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
353 #ifdef HAVE_LCD_BITMAP
354 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
355 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
356 { WPS_TOKEN_DRAW_INBUILTBAR, "wi", WPS_REFRESH_DYNAMIC, parse_statusbar_inbuilt },
358 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
360 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
361 parse_image_display },
363 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
364 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
365 #ifdef HAVE_ALBUMART
366 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
367 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
368 #endif
370 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
371 parse_viewport_display },
372 #ifdef HAVE_LCD_BITMAP
373 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
374 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
375 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
376 #endif
377 { WPS_NO_TOKEN, "V", 0, parse_viewport },
379 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
380 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
381 #endif
382 #endif
384 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
385 parse_setting_and_lang },
386 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
387 parse_setting_and_lang },
388 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
390 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
391 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
392 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
395 /* Recording Tokens */
396 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
397 #ifdef HAVE_RECORDING
398 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
399 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
400 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
401 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
402 #endif
403 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
404 /* the array MUST end with an empty string (first char is \0) */
408 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
409 * chains require the order to be kept.
411 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
413 if (*list == NULL)
414 *list = item;
415 else
417 struct skin_token_list *t = *list;
418 while (t->next)
419 t = t->next;
420 t->next = item;
424 /* traverse the image linked-list for an image */
425 #ifdef HAVE_LCD_BITMAP
426 struct gui_img* find_image(char label, struct wps_data *data)
428 struct skin_token_list *list = data->images;
429 while (list)
431 struct gui_img *img = (struct gui_img *)list->token->value.data;
432 if (img->label == label)
433 return img;
434 list = list->next;
436 return NULL;
439 #endif
441 /* traverse the viewport linked list for a viewport */
442 struct skin_viewport* find_viewport(char label, struct wps_data *data)
444 struct skin_token_list *list = data->viewports;
445 while (list)
447 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
448 if (vp->label == label)
449 return vp;
450 list = list->next;
452 return NULL;
456 /* create and init a new wpsll item.
457 * passing NULL to token will alloc a new one.
458 * You should only pass NULL for the token when the token type (table above)
459 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
461 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
462 void* token_data)
464 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
465 if (!token)
466 token = skin_buffer_alloc(sizeof(struct wps_token));
467 if (!llitem || !token)
468 return NULL;
469 llitem->next = NULL;
470 llitem->token = token;
471 if (token_data)
472 llitem->token->value.data = token_data;
473 return llitem;
476 /* Returns the number of chars that should be skipped to jump
477 immediately after the first eol, i.e. to the start of the next line */
478 static int skip_end_of_line(const char *wps_bufptr)
480 line_number++;
481 int skip = 0;
482 while(*(wps_bufptr + skip) != '\n')
483 skip++;
484 return ++skip;
487 /* Starts a new subline in the current line during parsing */
488 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
490 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
491 if (!subline)
492 return false;
494 subline->first_token_idx = curr_token;
495 subline->next = NULL;
497 subline->line_type = 0;
498 subline->time_mult = 0;
500 line->curr_subline->last_token_idx = curr_token-1;
501 line->curr_subline->next = subline;
502 line->curr_subline = subline;
503 return true;
506 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
508 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
509 struct skin_subline *subline = NULL;
510 if (!line)
511 return false;
513 /* init the subline */
514 subline = &line->sublines;
515 subline->first_token_idx = curr_token;
516 subline->next = NULL;
517 subline->line_type = 0;
518 subline->time_mult = 0;
520 /* init the new line */
521 line->curr_subline = &line->sublines;
522 line->next = NULL;
523 line->subline_expire_time = 0;
525 /* connect to curr_line and vp pointers.
526 * 1) close the previous lines subline
527 * 2) connect to vp pointer
528 * 3) connect to curr_line global pointer
530 if (curr_line)
532 curr_line->curr_subline->last_token_idx = curr_token - 1;
533 curr_line->next = line;
534 curr_line->curr_subline = NULL;
536 curr_line = line;
537 if (!vp->lines)
538 vp->lines = line;
539 return true;
542 #ifdef HAVE_LCD_BITMAP
544 static int parse_statusbar_enable(const char *wps_bufptr,
545 struct wps_token *token,
546 struct wps_data *wps_data)
548 (void)token; /* Kill warnings */
549 wps_data->wps_sb_tag = true;
550 wps_data->show_sb_on_wps = true;
551 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
552 viewport_set_defaults(&default_vp->vp, curr_screen);
553 default_vp->vp.font = FONT_UI;
554 return skip_end_of_line(wps_bufptr);
557 static int parse_statusbar_disable(const char *wps_bufptr,
558 struct wps_token *token,
559 struct wps_data *wps_data)
561 (void)token; /* Kill warnings */
562 wps_data->wps_sb_tag = true;
563 wps_data->show_sb_on_wps = false;
564 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
565 viewport_set_fullscreen(&default_vp->vp, curr_screen);
566 default_vp->vp.font = FONT_UI;
567 return skip_end_of_line(wps_bufptr);
570 static int parse_statusbar_inbuilt(const char *wps_bufptr,
571 struct wps_token *token, struct wps_data *wps_data)
573 (void)wps_data;
574 token->value.data = (void*)&curr_vp->vp;
575 return skip_end_of_line(wps_bufptr);
578 static int get_image_id(int c)
580 if(c >= 'a' && c <= 'z')
581 return c - 'a';
582 else if(c >= 'A' && c <= 'Z')
583 return c - 'A' + 26;
584 else
585 return -1;
588 char *get_image_filename(const char *start, const char* bmpdir,
589 char *buf, int buf_size)
591 const char *end = strchr(start, '|');
592 int bmpdirlen = strlen(bmpdir);
594 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
596 buf[0] = '\0';
597 return NULL;
600 strcpy(buf, bmpdir);
601 buf[bmpdirlen] = '/';
602 memcpy( &buf[bmpdirlen + 1], start, end - start);
603 buf[bmpdirlen + 1 + end - start] = 0;
605 return buf;
608 static int parse_image_display(const char *wps_bufptr,
609 struct wps_token *token,
610 struct wps_data *wps_data)
612 char label = wps_bufptr[0];
613 int subimage;
614 struct gui_img *img;;
616 /* sanity check */
617 img = find_image(label, wps_data);
618 if (!img)
620 token->value.i = label; /* so debug works */
621 return WPS_ERROR_INVALID_PARAM;
624 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
626 if (subimage >= img->num_subimages)
627 return WPS_ERROR_INVALID_PARAM;
629 /* Store sub-image number to display in high bits */
630 token->value.i = label | (subimage << 8);
631 return 2; /* We have consumed 2 bytes */
632 } else {
633 token->value.i = label;
634 return 1; /* We have consumed 1 byte */
638 static int parse_image_load(const char *wps_bufptr,
639 struct wps_token *token,
640 struct wps_data *wps_data)
642 const char *ptr = wps_bufptr;
643 const char *pos;
644 const char* filename;
645 const char* id;
646 const char *newline;
647 int x,y;
648 struct gui_img *img;
650 /* format: %x|n|filename.bmp|x|y|
651 or %xl|n|filename.bmp|x|y|
652 or %xl|n|filename.bmp|x|y|num_subimages|
655 if (*ptr != '|')
656 return WPS_ERROR_INVALID_PARAM;
658 ptr++;
660 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
661 return WPS_ERROR_INVALID_PARAM;
663 /* Check there is a terminating | */
664 if (*ptr != '|')
665 return WPS_ERROR_INVALID_PARAM;
667 /* check the image number and load state */
668 if(find_image(*id, wps_data))
670 /* Invalid image ID */
671 return WPS_ERROR_INVALID_PARAM;
673 img = skin_buffer_alloc(sizeof(struct gui_img));
674 if (!img)
675 return WPS_ERROR_INVALID_PARAM;
676 /* save a pointer to the filename */
677 img->bm.data = (char*)filename;
678 img->label = *id;
679 img->x = x;
680 img->y = y;
681 img->num_subimages = 1;
682 img->always_display = false;
684 /* save current viewport */
685 img->vp = &curr_vp->vp;
687 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
689 img->always_display = true;
691 else
693 /* Parse the (optional) number of sub-images */
694 ptr++;
695 newline = strchr(ptr, '\n');
696 pos = strchr(ptr, '|');
697 if (pos && pos < newline)
698 img->num_subimages = atoi(ptr);
700 if (img->num_subimages <= 0)
701 return WPS_ERROR_INVALID_PARAM;
703 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
704 if (!item)
705 return WPS_ERROR_INVALID_PARAM;
706 add_to_ll_chain(&wps_data->images, item);
708 /* Skip the rest of the line */
709 return skip_end_of_line(wps_bufptr);
711 struct skin_font {
712 int id; /* the id from font_load */
713 char *name; /* filename without path and extension */
715 static struct skin_font skinfonts[MAXUSERFONTS];
716 static int parse_font_load(const char *wps_bufptr,
717 struct wps_token *token, struct wps_data *wps_data)
719 (void)wps_data; (void)token;
720 const char *ptr = wps_bufptr;
721 int id;
722 char *filename;
724 if (*ptr != '|')
725 return WPS_ERROR_INVALID_PARAM;
727 ptr++;
729 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
730 return WPS_ERROR_INVALID_PARAM;
732 /* Check there is a terminating | */
733 if (*ptr != '|')
734 return WPS_ERROR_INVALID_PARAM;
736 if (id <= FONT_UI || id >= MAXFONTS-1)
737 return WPS_ERROR_INVALID_PARAM;
738 #if defined(DEBUG) || defined(SIMULATOR)
739 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
741 DEBUGF("font id %d already being used\n", id);
743 #endif
744 /* make sure the filename contains .fnt,
745 * we dont actually use it, but require it anyway */
746 ptr = strchr(filename, '.');
747 if (!ptr || strncmp(ptr, ".fnt|", 5))
748 return WPS_ERROR_INVALID_PARAM;
749 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
750 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
752 return skip_end_of_line(wps_bufptr);
756 static int parse_viewport_display(const char *wps_bufptr,
757 struct wps_token *token,
758 struct wps_data *wps_data)
760 (void)wps_data;
761 char letter = wps_bufptr[0];
763 if (letter < 'a' || letter > 'z')
765 /* invalid viewport tag */
766 return WPS_ERROR_INVALID_PARAM;
768 token->value.i = letter;
769 return 1;
772 #ifdef HAVE_LCD_BITMAP
773 static int parse_playlistview_text(struct playlistviewer *viewer,
774 enum info_line_type line, char* text)
776 int cur_string = 0;
777 const struct wps_tag *tag;
778 int taglen = 0;
779 const char *start = text;
780 if (*text != '|')
781 return -1;
782 text++;
783 viewer->lines[line].count = 0;
784 viewer->lines[line].scroll = false;
785 while (*text != '|')
787 if (*text == '%') /* it is a token of some type */
789 text++;
790 taglen = 0;
791 switch(*text)
793 case '%':
794 case '<':
795 case '|':
796 case '>':
797 case ';':
798 case '#':
799 /* escaped characters */
800 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
801 viewer->lines[line].strings[cur_string][0] = *text;
802 viewer->lines[line].strings[cur_string++][1] = '\0';
803 break;
804 default:
805 for (tag = all_tags;
806 strncmp(text, tag->name, strlen(tag->name)) != 0;
807 tag++) ;
808 /* %s isnt stored as a tag so manually check for it */
809 if (tag->type == WPS_NO_TOKEN)
811 if (!strncmp(tag->name, "s", 1))
813 viewer->lines[line].scroll = true;
814 taglen = 1;
817 else if (tag->type == WPS_TOKEN_UNKNOWN)
819 int i = 0;
820 /* just copy the string */
821 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
822 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
824 viewer->lines[line].strings[cur_string][i] = text[i];
825 i++;
827 viewer->lines[line].strings[cur_string][i] = '\0';
828 cur_string++;
829 taglen = i;
831 else
833 if (tag->parse_func)
835 /* unsupported tag, reject */
836 return -1;
838 taglen = strlen(tag->name);
839 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
841 text += taglen;
844 else
846 /* regular string */
847 int i = 0;
848 /* just copy the string */
849 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
850 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
852 viewer->lines[line].strings[cur_string][i] = text[i];
853 i++;
855 viewer->lines[line].strings[cur_string][i] = '\0';
856 cur_string++;
857 text += i;
860 return text - start;
864 static int parse_playlistview(const char *wps_bufptr,
865 struct wps_token *token, struct wps_data *wps_data)
867 (void)wps_data;
868 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
869 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
870 char *ptr = strchr(wps_bufptr, '|');
871 int length;
872 if (!viewer || !ptr)
873 return WPS_ERROR_INVALID_PARAM;
874 viewer->vp = &curr_vp->vp;
875 viewer->show_icons = true;
876 viewer->start_offset = atoi(ptr+1);
877 token->value.data = (void*)viewer;
878 ptr = strchr(ptr+1, '|');
879 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
880 if (length < 0)
881 return WPS_ERROR_INVALID_PARAM;
882 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
883 if (length < 0)
884 return WPS_ERROR_INVALID_PARAM;
886 return skip_end_of_line(wps_bufptr);
888 #endif
890 static int parse_viewport(const char *wps_bufptr,
891 struct wps_token *token,
892 struct wps_data *wps_data)
894 (void)token; /* Kill warnings */
895 const char *ptr = wps_bufptr;
897 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
899 /* check for the optional letter to signify its a hideable viewport */
900 /* %Vl|<label>|<rest of tags>| */
901 skin_vp->hidden_flags = 0;
902 skin_vp->label = VP_NO_LABEL;
903 skin_vp->pb = NULL;
904 skin_vp->lines = NULL;
905 if (curr_line)
907 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
908 - (wps_data->num_tokens > 0 ? 1 : 0);
911 curr_line = NULL;
912 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
913 return WPS_ERROR_INVALID_PARAM;
915 if (*ptr == 'i')
917 skin_vp->label = VP_INFO_LABEL;
918 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
919 ++ptr;
921 else if (*ptr == 'l')
923 if (*(ptr+1) == '|')
925 char label = *(ptr+2);
926 if (label >= 'a' && label <= 'z')
928 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
929 skin_vp->label = label;
931 else
932 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
933 ptr += 3;
936 if (*ptr != '|')
937 return WPS_ERROR_INVALID_PARAM;
939 ptr++;
940 struct viewport *vp = &skin_vp->vp;
941 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
942 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
943 return WPS_ERROR_INVALID_PARAM;
945 /* Check for trailing | */
946 if (*ptr != '|')
947 return WPS_ERROR_INVALID_PARAM;
949 if (follow_lang_direction && lang_is_rtl())
951 vp->flags |= VP_FLAG_ALIGN_RIGHT;
952 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
954 else
955 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
957 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
958 if (!list)
959 return WPS_ERROR_INVALID_PARAM;
960 add_to_ll_chain(&wps_data->viewports, list);
961 curr_vp = skin_vp;
962 /* Skip the rest of the line */
963 return skip_end_of_line(wps_bufptr);
966 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
967 static int parse_image_special(const char *wps_bufptr,
968 struct wps_token *token,
969 struct wps_data *wps_data)
971 (void)wps_data; /* kill warning */
972 (void)token;
973 const char *pos = NULL;
974 const char *newline;
975 bool error = false;
977 pos = strchr(wps_bufptr + 1, '|');
978 newline = strchr(wps_bufptr, '\n');
980 error = (pos > newline);
982 #if LCD_DEPTH > 1
983 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
985 /* format: %X|filename.bmp| or %Xd */
986 if (*(wps_bufptr) == 'd')
988 wps_data->backdrop = NULL;
989 return skip_end_of_line(wps_bufptr);
991 else if (!error)
992 wps_data->backdrop = (char*)wps_bufptr + 1;
994 #endif
995 if (error)
996 return WPS_ERROR_INVALID_PARAM;
997 /* Skip the rest of the line */
998 return skip_end_of_line(wps_bufptr);
1000 #endif
1002 #endif /* HAVE_LCD_BITMAP */
1004 static int parse_setting_and_lang(const char *wps_bufptr,
1005 struct wps_token *token,
1006 struct wps_data *wps_data)
1008 /* NOTE: both the string validations that happen in here will
1009 * automatically PASS on checkwps because its too hard to get
1010 * settings_list.c and englinsh.lang built for it.
1011 * If that ever changes remove the #ifndef __PCTOOL__'s here
1013 (void)wps_data;
1014 const char *ptr = wps_bufptr;
1015 const char *end;
1016 int i = 0;
1017 char temp[64];
1019 /* Find the setting's cfg_name */
1020 if (*ptr != '|')
1021 return WPS_ERROR_INVALID_PARAM;
1022 ptr++;
1023 end = strchr(ptr,'|');
1024 if (!end)
1025 return WPS_ERROR_INVALID_PARAM;
1026 strlcpy(temp, ptr,end-ptr+1);
1028 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1030 #ifndef __PCTOOL__
1031 i = lang_english_to_id(temp);
1032 if (i < 0)
1033 return WPS_ERROR_INVALID_PARAM;
1034 #endif
1036 else
1038 /* Find the setting */
1039 for (i=0; i<nb_settings; i++)
1040 if (settings[i].cfg_name &&
1041 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1042 /* prevent matches on cfg_name prefixes */
1043 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1044 break;
1045 #ifndef __PCTOOL__
1046 if (i == nb_settings)
1047 return WPS_ERROR_INVALID_PARAM;
1048 #endif
1050 /* Store the setting number */
1051 token->value.i = i;
1053 /* Skip the rest of the line */
1054 return end-ptr+2;
1058 static int parse_dir_level(const char *wps_bufptr,
1059 struct wps_token *token,
1060 struct wps_data *wps_data)
1062 char val[] = { *wps_bufptr, '\0' };
1063 token->value.i = atoi(val);
1064 (void)wps_data; /* Kill warnings */
1065 return 1;
1068 static int parse_timeout(const char *wps_bufptr,
1069 struct wps_token *token,
1070 struct wps_data *wps_data)
1072 int skip = 0;
1073 int val = 0;
1074 bool have_point = false;
1075 bool have_tenth = false;
1077 (void)wps_data; /* Kill the warning */
1079 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1081 if (*wps_bufptr != '.')
1083 val *= 10;
1084 val += *wps_bufptr - '0';
1085 if (have_point)
1087 have_tenth = true;
1088 wps_bufptr++;
1089 skip++;
1090 break;
1093 else
1094 have_point = true;
1096 wps_bufptr++;
1097 skip++;
1100 if (have_tenth == false)
1101 val *= 10;
1103 if (val == 0 && skip == 0)
1105 /* decide what to do if no value was specified */
1106 switch (token->type)
1108 case WPS_TOKEN_SUBLINE_TIMEOUT:
1109 return -1;
1110 case WPS_TOKEN_BUTTON_VOLUME:
1111 case WPS_TOKEN_TRACK_STARTING:
1112 case WPS_TOKEN_TRACK_ENDING:
1113 val = 10;
1114 break;
1117 token->value.i = val;
1119 return skip;
1122 static int parse_progressbar(const char *wps_bufptr,
1123 struct wps_token *token,
1124 struct wps_data *wps_data)
1126 /* %pb or %pb|filename|x|y|width|height|
1127 using - for any of the params uses "sane" values */
1128 #ifdef HAVE_LCD_BITMAP
1129 enum {
1130 PB_FILENAME = 0,
1131 PB_X,
1132 PB_Y,
1133 PB_WIDTH,
1134 PB_HEIGHT
1136 const char *filename;
1137 int x, y, height, width;
1138 uint32_t set = 0;
1139 const char *ptr = wps_bufptr;
1140 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1141 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1143 if (!pb || !item)
1144 return WPS_ERROR_INVALID_PARAM;
1146 struct viewport *vp = &curr_vp->vp;
1147 /* we need to know what line number (viewport relative) this pb is,
1148 * so count them... */
1149 int line_num = -1;
1150 struct skin_line *line = curr_vp->lines;
1151 while (line)
1153 line_num++;
1154 line = line->next;
1156 pb->have_bitmap_pb = false;
1157 pb->bm.data = NULL; /* no bitmap specified */
1158 pb->follow_lang_direction = follow_lang_direction > 0;
1160 if (*wps_bufptr != '|') /* regular old style */
1162 pb->x = 0;
1163 pb->width = vp->width;
1164 pb->height = SYSFONT_HEIGHT-2;
1165 pb->y = -line_num - 1; /* Will be computed during the rendering */
1167 curr_vp->pb = pb;
1168 add_to_ll_chain(&wps_data->progressbars, item);
1169 return 0;
1171 ptr = wps_bufptr + 1;
1173 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1174 &x, &y, &width, &height)))
1175 return WPS_ERROR_INVALID_PARAM;
1177 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1178 pb->bm.data = (char*)filename;
1180 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1181 pb->x = x;
1182 else
1183 pb->x = vp->x;
1185 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1187 /* A zero width causes a divide-by-zero error later, so reject it */
1188 if (width == 0)
1189 return WPS_ERROR_INVALID_PARAM;
1191 pb->width = width;
1193 else
1194 pb->width = vp->width - pb->x;
1196 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1198 /* A zero height makes no sense - reject it */
1199 if (height == 0)
1200 return WPS_ERROR_INVALID_PARAM;
1202 pb->height = height;
1204 else
1206 if (vp->font > FONT_UI)
1207 pb->height = -1; /* calculate at display time */
1208 else
1210 #ifndef __PCTOOL__
1211 pb->height = font_get(vp->font)->height;
1212 #else
1213 pb->height = 8;
1214 #endif
1218 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1219 pb->y = y;
1220 else
1221 pb->y = -line_num - 1; /* Will be computed during the rendering */
1223 curr_vp->pb = pb;
1224 add_to_ll_chain(&wps_data->progressbars, item);
1226 /* Skip the rest of the line */
1227 return skip_end_of_line(wps_bufptr)-1;
1228 #else
1229 (void)token;
1231 if (*(wps_bufptr-1) == 'f')
1232 wps_data->full_line_progressbar = true;
1233 else
1234 wps_data->full_line_progressbar = false;
1236 return 0;
1238 #endif
1241 #ifdef HAVE_ALBUMART
1242 static int parse_int(const char *newline, const char **_pos, int *num)
1244 *_pos = parse_list("d", NULL, '|', *_pos, num);
1246 return (!*_pos || *_pos > newline || **_pos != '|');
1249 static int parse_albumart_load(const char *wps_bufptr,
1250 struct wps_token *token,
1251 struct wps_data *wps_data)
1253 const char *_pos, *newline;
1254 bool parsing;
1255 struct dim dimensions;
1256 int albumart_slot;
1257 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1258 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1259 (void)token; /* silence warning */
1260 if (!aa)
1261 return skip_end_of_line(wps_bufptr);
1263 /* reset albumart info in wps */
1264 aa->width = -1;
1265 aa->height = -1;
1266 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1267 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1268 aa->vp = &curr_vp->vp;
1270 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1272 newline = strchr(wps_bufptr, '\n');
1274 _pos = wps_bufptr;
1276 if (*_pos != '|')
1277 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1279 ++_pos;
1281 /* initial validation and parsing of x component */
1282 if (parse_int(newline, &_pos, &aa->x))
1283 return WPS_ERROR_INVALID_PARAM;
1285 ++_pos;
1287 /* initial validation and parsing of y component */
1288 if (parse_int(newline, &_pos, &aa->y))
1289 return WPS_ERROR_INVALID_PARAM;
1291 /* parsing width field */
1292 parsing = true;
1293 while (parsing)
1295 /* apply each modifier in turn */
1296 ++_pos;
1297 switch (*_pos)
1299 case 'l':
1300 case 'L':
1301 case '+':
1302 if (swap_for_rtl)
1303 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1304 else
1305 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1306 break;
1307 case 'c':
1308 case 'C':
1309 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1310 break;
1311 case 'r':
1312 case 'R':
1313 case '-':
1314 if (swap_for_rtl)
1315 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1316 else
1317 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1318 break;
1319 case 'd':
1320 case 'D':
1321 case 'i':
1322 case 'I':
1323 case 's':
1324 case 'S':
1325 /* simply ignored */
1326 break;
1327 default:
1328 parsing = false;
1329 break;
1332 /* extract max width data */
1333 if (*_pos != '|')
1335 if (parse_int(newline, &_pos, &aa->width))
1336 return WPS_ERROR_INVALID_PARAM;
1339 /* parsing height field */
1340 parsing = true;
1341 while (parsing)
1343 /* apply each modifier in turn */
1344 ++_pos;
1345 switch (*_pos)
1347 case 't':
1348 case 'T':
1349 case '-':
1350 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1351 break;
1352 case 'c':
1353 case 'C':
1354 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1355 break;
1356 case 'b':
1357 case 'B':
1358 case '+':
1359 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1360 break;
1361 case 'd':
1362 case 'D':
1363 case 'i':
1364 case 'I':
1365 case 's':
1366 case 'S':
1367 /* simply ignored */
1368 break;
1369 default:
1370 parsing = false;
1371 break;
1374 /* extract max height data */
1375 if (*_pos != '|')
1377 if (parse_int(newline, &_pos, &aa->height))
1378 return WPS_ERROR_INVALID_PARAM;
1381 /* if we got here, we parsed everything ok .. ! */
1382 if (aa->width < 0)
1383 aa->width = 0;
1384 else if (aa->width > LCD_WIDTH)
1385 aa->width = LCD_WIDTH;
1387 if (aa->height < 0)
1388 aa->height = 0;
1389 else if (aa->height > LCD_HEIGHT)
1390 aa->height = LCD_HEIGHT;
1392 if (swap_for_rtl)
1393 aa->x = LCD_WIDTH - (aa->x + aa->width);
1395 aa->state = WPS_ALBUMART_LOAD;
1396 aa->draw = false;
1397 wps_data->albumart = aa;
1399 dimensions.width = aa->width;
1400 dimensions.height = aa->height;
1402 albumart_slot = playback_claim_aa_slot(&dimensions);
1404 if (0 <= albumart_slot)
1405 wps_data->playback_aa_slot = albumart_slot;
1407 /* Skip the rest of the line */
1408 return skip_end_of_line(wps_bufptr);
1411 static int parse_albumart_display(const char *wps_bufptr,
1412 struct wps_token *token,
1413 struct wps_data *wps_data)
1415 (void)wps_bufptr;
1416 struct wps_token *prev = token-1;
1417 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1419 token->type = WPS_TOKEN_ALBUMART_FOUND;
1421 else if (wps_data->albumart)
1423 wps_data->albumart->vp = &curr_vp->vp;
1425 #if 0
1426 /* the old code did this so keep it here for now...
1427 * this is to allow the posibility to showing the next tracks AA! */
1428 if (wps_bufptr+1 == 'n')
1429 return 1;
1430 #endif
1431 return 0;
1433 #endif /* HAVE_ALBUMART */
1435 #ifdef HAVE_TOUCHSCREEN
1437 struct touchaction {const char* s; int action;};
1438 static const struct touchaction touchactions[] = {
1439 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1440 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1441 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1442 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1443 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1444 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1445 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1446 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1448 static int parse_touchregion(const char *wps_bufptr,
1449 struct wps_token *token, struct wps_data *wps_data)
1451 (void)token;
1452 unsigned i, imax;
1453 struct touchregion *region = NULL;
1454 const char *ptr = wps_bufptr;
1455 const char *action;
1456 const char pb_string[] = "progressbar";
1457 const char vol_string[] = "volume";
1458 int x,y,w,h;
1460 /* format: %T|x|y|width|height|action|
1461 * if action starts with & the area must be held to happen
1462 * action is one of:
1463 * play - play/pause playback
1464 * stop - stop playback, exit the wps
1465 * prev - prev track
1466 * next - next track
1467 * ffwd - seek forward
1468 * rwd - seek backwards
1469 * menu - go back to the main menu
1470 * browse - go back to the file/db browser
1471 * shuffle - toggle shuffle mode
1472 * repmode - cycle the repeat mode
1473 * quickscreen - go into the quickscreen
1474 * contextmenu - open the context menu
1475 * playlist - go into the playlist
1476 * pitch - go into the pitchscreen
1477 * volup - increase volume by one step
1478 * voldown - decrease volume by one step
1482 if (*ptr != '|')
1483 return WPS_ERROR_INVALID_PARAM;
1484 ptr++;
1486 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1487 return WPS_ERROR_INVALID_PARAM;
1489 /* Check there is a terminating | */
1490 if (*ptr != '|')
1491 return WPS_ERROR_INVALID_PARAM;
1493 region = skin_buffer_alloc(sizeof(struct touchregion));
1494 if (!region)
1495 return WPS_ERROR_INVALID_PARAM;
1497 /* should probably do some bounds checking here with the viewport... but later */
1498 region->action = ACTION_NONE;
1499 region->x = x;
1500 region->y = y;
1501 region->width = w;
1502 region->height = h;
1503 region->wvp = curr_vp;
1504 region->armed = false;
1506 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1507 && *(action + sizeof(pb_string)-1) == '|')
1508 region->type = WPS_TOUCHREGION_SCROLLBAR;
1509 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1510 && *(action + sizeof(vol_string)-1) == '|')
1511 region->type = WPS_TOUCHREGION_VOLUME;
1512 else
1514 region->type = WPS_TOUCHREGION_ACTION;
1516 if (*action == '&')
1518 action++;
1519 region->repeat = true;
1521 else
1522 region->repeat = false;
1524 i = 0;
1525 imax = ARRAYLEN(touchactions);
1526 while ((region->action == ACTION_NONE) &&
1527 (i < imax))
1529 /* try to match with one of our touchregion screens */
1530 int len = strlen(touchactions[i].s);
1531 if (!strncmp(touchactions[i].s, action, len)
1532 && *(action+len) == '|')
1533 region->action = touchactions[i].action;
1534 i++;
1536 if (region->action == ACTION_NONE)
1537 return WPS_ERROR_INVALID_PARAM;
1539 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1540 if (!item)
1541 return WPS_ERROR_INVALID_PARAM;
1542 add_to_ll_chain(&wps_data->touchregions, item);
1543 return skip_end_of_line(wps_bufptr);
1545 #endif
1547 /* Parse a generic token from the given string. Return the length read */
1548 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1550 int skip = 0, taglen = 0, ret;
1551 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1552 const struct wps_tag *tag;
1553 memset(token, 0, sizeof(*token));
1555 switch(*wps_bufptr)
1558 case '%':
1559 case '<':
1560 case '|':
1561 case '>':
1562 case ';':
1563 case '#':
1564 /* escaped characters */
1565 token->type = WPS_TOKEN_CHARACTER;
1566 token->value.c = *wps_bufptr;
1567 taglen = 1;
1568 wps_data->num_tokens++;
1569 break;
1571 case '?':
1572 /* conditional tag */
1573 token->type = WPS_TOKEN_CONDITIONAL;
1574 level++;
1575 condindex[level] = wps_data->num_tokens;
1576 numoptions[level] = 1;
1577 wps_data->num_tokens++;
1578 ret = parse_token(wps_bufptr + 1, wps_data);
1579 if (ret < 0) return ret;
1580 taglen = 1 + ret;
1581 break;
1583 default:
1584 /* find what tag we have */
1585 for (tag = all_tags;
1586 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1587 tag++) ;
1589 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1590 token->type = tag->type;
1591 curr_line->curr_subline->line_type |= tag->refresh_type;
1593 /* if the tag has a special parsing function, we call it */
1594 if (tag->parse_func)
1596 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1597 if (ret < 0) return ret;
1598 skip += ret;
1601 /* Some tags we don't want to save as tokens */
1602 if (tag->type == WPS_NO_TOKEN)
1603 break;
1605 /* tags that start with 'F', 'I' or 'D' are for the next file */
1606 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1607 *(tag->name) == 'D')
1608 token->next = true;
1610 wps_data->num_tokens++;
1611 break;
1614 skip += taglen;
1615 return skip;
1620 * Returns the number of bytes to skip the buf pointer to access the false
1621 * branch in a _binary_ conditional
1623 * That is:
1624 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1625 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1627 * depending on the features of a target it's not called from check_feature_tag,
1628 * hence the __attribute__ or it issues compiler warnings
1632 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1633 static int find_false_branch(const char *wps_bufptr)
1635 const char *buf = wps_bufptr;
1636 /* wps_bufptr is after the opening '<', hence level = 1*/
1637 int level = 1;
1638 char ch;
1641 ch = *buf;
1642 if (ch == '%')
1643 { /* filter out the characters we check later if they're printed
1644 * as literals */
1645 ch = *(++buf);
1646 if (ch == '<' || ch == '>' || ch == '|')
1647 continue;
1648 /* else: some tags/printed literals we skip over */
1650 else if (ch == '<') /* nested conditional */
1651 level++;
1652 else if (ch == '>')
1653 { /* closed our or a nested conditional,
1654 * do NOT skip over the '>' so that wps_parse() sees it for closing
1655 * if it is the closing one for our conditional */
1656 level--;
1658 else if (ch == '|' && level == 1)
1659 { /* we found our separator, point before and get out */
1660 break;
1662 /* if level is 0, we don't have a false branch */
1663 } while (level > 0 && *(++buf));
1665 return buf - wps_bufptr;
1669 * returns the number of bytes to get the appropriate branch of a binary
1670 * conditional
1672 * That means:
1673 * - if a feature is available, it returns 0 to not skip anything
1674 * - if the feature is not available, skip to the false branch and don't
1675 * parse the true branch at all
1677 * */
1678 static int check_feature_tag(const char *wps_bufptr, const int type)
1680 (void)wps_bufptr;
1681 switch (type)
1683 case WPS_TOKEN_RTC_PRESENT:
1684 #if CONFIG_RTC
1685 return 0;
1686 #else
1687 return find_false_branch(wps_bufptr);
1688 #endif
1689 case WPS_TOKEN_HAVE_RECORDING:
1690 #ifdef HAVE_RECORDING
1691 return 0;
1692 #else
1693 return find_false_branch(wps_bufptr);
1694 #endif
1695 default: /* not a tag we care about, just don't skip */
1696 return 0;
1701 /* Parses the WPS.
1702 data is the pointer to the structure where the parsed WPS should be stored.
1703 It is initialised.
1704 wps_bufptr points to the string containing the WPS tags */
1705 #define TOKEN_BLOCK_SIZE 128
1706 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1708 if (!data || !wps_bufptr || !*wps_bufptr)
1709 return false;
1710 enum wps_parse_error fail = PARSE_OK;
1711 int ret;
1712 int max_tokens = TOKEN_BLOCK_SIZE;
1713 size_t buf_free = 0;
1714 line_number = 0;
1715 level = -1;
1717 /* allocate enough RAM for a reasonable skin, grow as needed.
1718 * Free any used RAM before loading the images to be 100% RAM efficient */
1719 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1720 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1721 return false;
1722 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1723 data->num_tokens = 0;
1725 #if LCD_DEPTH > 1
1726 /* Backdrop defaults to the setting unless %X is used, so set it now */
1727 if (global_settings.backdrop_file[0])
1729 data->backdrop = "-";
1731 #endif
1733 while (*wps_bufptr && !fail)
1735 if (follow_lang_direction)
1736 follow_lang_direction--;
1737 /* first make sure there is enough room for tokens */
1738 if (max_tokens <= data->num_tokens + 5)
1740 int extra_tokens = TOKEN_BLOCK_SIZE;
1741 size_t needed = extra_tokens * sizeof(struct wps_token);
1742 /* do some smarts here to grow the array a bit */
1743 if (skin_buffer_freespace() < needed)
1745 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1746 break;
1748 skin_buffer_increment(needed, false);
1749 max_tokens += extra_tokens;
1752 switch(*wps_bufptr++)
1755 /* Regular tag */
1756 case '%':
1757 if ((ret = parse_token(wps_bufptr, data)) < 0)
1759 fail = PARSE_FAIL_COND_INVALID_PARAM;
1760 break;
1762 else if (level >= WPS_MAX_COND_LEVEL - 1)
1764 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1765 break;
1767 wps_bufptr += ret;
1768 break;
1770 /* Alternating sublines separator */
1771 case ';':
1772 if (level >= 0) /* there are unclosed conditionals */
1774 fail = PARSE_FAIL_UNCLOSED_COND;
1775 break;
1778 if (!skin_start_new_subline(curr_line, data->num_tokens))
1779 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1781 break;
1783 /* Conditional list start */
1784 case '<':
1785 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1787 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1788 break;
1790 wps_bufptr += check_feature_tag(wps_bufptr,
1791 data->tokens[data->num_tokens-1].type);
1792 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1793 lastcond[level] = data->num_tokens++;
1794 break;
1796 /* Conditional list end */
1797 case '>':
1798 if (level < 0) /* not in a conditional, invalid char */
1800 fail = PARSE_FAIL_INVALID_CHAR;
1801 break;
1804 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1805 if (lastcond[level])
1806 data->tokens[lastcond[level]].value.i = data->num_tokens;
1807 else
1809 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1810 break;
1813 lastcond[level] = 0;
1814 data->num_tokens++;
1815 data->tokens[condindex[level]].value.i = numoptions[level];
1816 level--;
1817 break;
1819 /* Conditional list option */
1820 case '|':
1821 if (level < 0) /* not in a conditional, invalid char */
1823 fail = PARSE_FAIL_INVALID_CHAR;
1824 break;
1827 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1828 if (lastcond[level])
1829 data->tokens[lastcond[level]].value.i = data->num_tokens;
1830 else
1832 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1833 break;
1836 lastcond[level] = data->num_tokens;
1837 numoptions[level]++;
1838 data->num_tokens++;
1839 break;
1841 /* Comment */
1842 case '#':
1843 if (level >= 0) /* there are unclosed conditionals */
1845 fail = PARSE_FAIL_UNCLOSED_COND;
1846 break;
1849 wps_bufptr += skip_end_of_line(wps_bufptr);
1850 break;
1852 /* End of this line */
1853 case '\n':
1854 if (level >= 0) /* there are unclosed conditionals */
1856 fail = PARSE_FAIL_UNCLOSED_COND;
1857 break;
1859 /* add a new token for the \n so empty lines are correct */
1860 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1861 data->tokens[data->num_tokens].value.c = '\n';
1862 data->tokens[data->num_tokens].next = false;
1863 data->num_tokens++;
1865 if (!skin_start_new_line(curr_vp, data->num_tokens))
1867 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1868 break;
1870 line_number++;
1872 break;
1874 /* String */
1875 default:
1877 unsigned int len = 1;
1878 const char *string_start = wps_bufptr - 1;
1880 /* find the length of the string */
1881 while (*wps_bufptr && *wps_bufptr != '#' &&
1882 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1883 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1884 *wps_bufptr != '|' && *wps_bufptr != '\n')
1886 wps_bufptr++;
1887 len++;
1890 /* look if we already have that string */
1891 char *str;
1892 bool found = false;
1893 struct skin_token_list *list = data->strings;
1894 while (list)
1896 str = (char*)list->token->value.data;
1897 found = (strlen(str) == len &&
1898 strncmp(string_start, str, len) == 0);
1899 if (found)
1900 break; /* break here because the list item is
1901 used if its found */
1902 list = list->next;
1904 /* If a matching string is found, found is true and i is
1905 the index of the string. If not, found is false */
1907 if (!found)
1909 /* new string */
1910 str = (char*)skin_buffer_alloc(len+1);
1911 if (!str)
1913 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1914 break;
1916 strlcpy(str, string_start, len+1);
1917 struct skin_token_list *item =
1918 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1919 if(!item)
1921 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1922 break;
1924 add_to_ll_chain(&data->strings, item);
1926 else
1928 /* another occurrence of an existing string */
1929 data->tokens[data->num_tokens].value.data = list->token->value.data;
1931 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1932 data->num_tokens++;
1934 break;
1938 if (!fail && level >= 0) /* there are unclosed conditionals */
1939 fail = PARSE_FAIL_UNCLOSED_COND;
1941 if (*wps_bufptr && !fail)
1942 /* one of the limits of the while loop was exceeded */
1943 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1945 /* Success! */
1946 curr_line->curr_subline->last_token_idx = data->num_tokens;
1947 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1948 /* freeup unused tokens */
1949 skin_buffer_free_from_front(sizeof(struct wps_token)
1950 * (max_tokens - data->num_tokens));
1952 #if defined(DEBUG) || defined(SIMULATOR)
1953 if (debug)
1955 print_debug_info(data, fail, line_number);
1956 debug_skin_usage();
1958 #else
1959 (void)debug;
1960 #endif
1962 return (fail == 0);
1967 * initial setup of wps_data; does reset everything
1968 * except fields which need to survive, i.e.
1971 static void skin_data_reset(struct wps_data *wps_data)
1973 #ifdef HAVE_LCD_BITMAP
1974 wps_data->images = NULL;
1975 wps_data->progressbars = NULL;
1976 #endif
1977 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1978 wps_data->backdrop = NULL;
1979 #endif
1980 #ifdef HAVE_TOUCHSCREEN
1981 wps_data->touchregions = NULL;
1982 #endif
1983 wps_data->viewports = NULL;
1984 wps_data->strings = NULL;
1985 #ifdef HAVE_ALBUMART
1986 wps_data->albumart = NULL;
1987 if (wps_data->playback_aa_slot >= 0)
1989 playback_release_aa_slot(wps_data->playback_aa_slot);
1990 wps_data->playback_aa_slot = -1;
1992 #endif
1993 wps_data->tokens = NULL;
1994 wps_data->num_tokens = 0;
1996 #ifdef HAVE_LCD_BITMAP
1997 wps_data->peak_meter_enabled = false;
1998 wps_data->wps_sb_tag = false;
1999 wps_data->show_sb_on_wps = false;
2000 #else /* HAVE_LCD_CHARCELLS */
2001 /* progress bars */
2002 int i;
2003 for (i = 0; i < 8; i++)
2005 wps_data->wps_progress_pat[i] = 0;
2007 wps_data->full_line_progressbar = false;
2008 #endif
2009 wps_data->wps_loaded = false;
2012 #ifdef HAVE_LCD_BITMAP
2013 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2015 (void)wps_data; /* only needed for remote targets */
2016 char img_path[MAX_PATH];
2017 get_image_filename(bitmap->data, bmpdir,
2018 img_path, sizeof(img_path));
2020 /* load the image */
2021 int format;
2022 #ifdef HAVE_REMOTE_LCD
2023 if (curr_screen == SCREEN_REMOTE)
2024 format = FORMAT_ANY|FORMAT_REMOTE;
2025 else
2026 #endif
2027 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2029 size_t max_buf;
2030 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2031 bitmap->data = imgbuf;
2032 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2034 if (ret > 0)
2036 skin_buffer_increment(ret, true);
2037 return true;
2039 else
2041 /* Abort if we can't load an image */
2042 DEBUGF("Couldn't load '%s'\n", img_path);
2043 return false;
2047 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2049 struct skin_token_list *list;
2050 bool retval = true; /* return false if a single image failed to load */
2051 /* do the progressbars */
2052 list = wps_data->progressbars;
2053 while (list)
2055 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2056 if (pb->bm.data)
2058 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2059 if (!pb->have_bitmap_pb) /* no success */
2060 retval = false;
2062 list = list->next;
2064 /* regular images */
2065 list = wps_data->images;
2066 while (list)
2068 struct gui_img *img = (struct gui_img*)list->token->value.data;
2069 if (img->bm.data)
2071 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2072 if (img->loaded)
2073 img->subimage_height = img->bm.height / img->num_subimages;
2074 else
2075 retval = false;
2077 list = list->next;
2080 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2081 /* Backdrop load scheme:
2082 * 1) %X|filename|
2083 * 2) load the backdrop from settings
2085 if (wps_data->backdrop)
2087 bool needed = wps_data->backdrop[0] != '-';
2088 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2089 bmpdir, curr_screen);
2090 if (!wps_data->backdrop && needed)
2091 retval = false;
2093 #endif /* has backdrop support */
2095 return retval;
2098 static bool skin_load_fonts(struct wps_data *data)
2100 /* don't spit out after the first failue to aid debugging */
2101 bool success = true;
2102 struct skin_token_list *vp_list;
2103 int font_id;
2104 /* walk though each viewport and assign its font */
2105 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2107 /* first, find the viewports that have a non-sys/ui-font font */
2108 struct skin_viewport *skin_vp =
2109 (struct skin_viewport*)vp_list->token->value.data;
2110 struct viewport *vp = &skin_vp->vp;
2113 if (vp->font <= FONT_UI)
2114 { /* the usual case -> built-in fonts */
2115 #ifdef HAVE_REMOTE_LCD
2116 if (vp->font == FONT_UI)
2117 vp->font += curr_screen;
2118 #endif
2119 continue;
2121 font_id = vp->font;
2123 /* now find the corresponding skin_font */
2124 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2125 if (!font->name)
2127 DEBUGF("font %d not specified\n", font_id);
2128 success = false;
2129 continue;
2132 /* load the font - will handle loading the same font again if
2133 * multiple viewports use the same */
2134 if (font->id < 0)
2136 char *dot = strchr(font->name, '.');
2137 *dot = '\0';
2138 font->id = skin_font_load(font->name);
2141 if (font->id < 0)
2143 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2144 font_id, font->name);
2145 success = false;
2146 continue;
2149 /* finally, assign the font_id to the viewport */
2150 vp->font = font->id;
2152 return success;
2155 #endif /* HAVE_LCD_BITMAP */
2157 /* to setup up the wps-data from a format-buffer (isfile = false)
2158 from a (wps-)file (isfile = true)*/
2159 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2160 const char *buf, bool isfile)
2162 char *wps_buffer = NULL;
2163 if (!wps_data || !buf)
2164 return false;
2165 #ifdef HAVE_ALBUMART
2166 int status;
2167 struct mp3entry *curtrack;
2168 long offset;
2169 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2170 if (wps_data->albumart)
2172 old_aa.state = wps_data->albumart->state;
2173 old_aa.height = wps_data->albumart->height;
2174 old_aa.width = wps_data->albumart->width;
2176 #endif
2177 #ifdef HAVE_LCD_BITMAP
2178 int i;
2179 for (i=0;i<MAXUSERFONTS;i++)
2181 skinfonts[i].id = -1;
2182 skinfonts[i].name = NULL;
2184 #endif
2185 #ifdef DEBUG_SKIN_ENGINE
2186 if (isfile && debug_wps)
2188 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2190 #endif
2192 skin_data_reset(wps_data);
2193 wps_data->wps_loaded = false;
2194 curr_screen = screen;
2196 /* alloc default viewport, will be fixed up later */
2197 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2198 if (!curr_vp)
2199 return false;
2200 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2201 if (!list)
2202 return false;
2203 add_to_ll_chain(&wps_data->viewports, list);
2206 /* Initialise the first (default) viewport */
2207 curr_vp->label = VP_DEFAULT_LABEL;
2208 curr_vp->pb = NULL;
2209 curr_vp->hidden_flags = 0;
2210 curr_vp->lines = NULL;
2212 viewport_set_defaults(&curr_vp->vp, screen);
2213 #ifdef HAVE_LCD_BITMAP
2214 curr_vp->vp.font = FONT_UI;
2215 #endif
2217 curr_line = NULL;
2218 if (!skin_start_new_line(curr_vp, 0))
2219 return false;
2221 if (isfile)
2223 int fd = open_utf8(buf, O_RDONLY);
2225 if (fd < 0)
2226 return false;
2228 /* get buffer space from the plugin buffer */
2229 size_t buffersize = 0;
2230 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2232 if (!wps_buffer)
2233 return false;
2235 /* copy the file's content to the buffer for parsing,
2236 ensuring that every line ends with a newline char. */
2237 unsigned int start = 0;
2238 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2240 start += strlen(wps_buffer + start);
2241 if (start < buffersize - 1)
2243 wps_buffer[start++] = '\n';
2244 wps_buffer[start] = 0;
2247 close(fd);
2248 if (start <= 0)
2249 return false;
2251 else
2253 wps_buffer = (char*)buf;
2255 /* parse the WPS source */
2256 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2257 skin_data_reset(wps_data);
2258 return false;
2261 #ifdef HAVE_LCD_BITMAP
2262 char bmpdir[MAX_PATH];
2263 if (isfile)
2265 /* get the bitmap dir */
2266 char *dot = strrchr(buf, '.');
2267 strlcpy(bmpdir, buf, dot - buf + 1);
2269 else
2271 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2273 /* load the bitmaps that were found by the parsing */
2274 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2275 !skin_load_fonts(wps_data))
2277 skin_data_reset(wps_data);
2278 return false;
2280 #endif
2281 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2282 status = audio_status();
2283 if (status & AUDIO_STATUS_PLAY)
2285 struct skin_albumart *aa = wps_data->albumart;
2286 if (aa && ((aa->state && !old_aa.state) ||
2287 (aa->state &&
2288 (((old_aa.height != aa->height) ||
2289 (old_aa.width != aa->width))))))
2291 curtrack = audio_current_track();
2292 offset = curtrack->offset;
2293 audio_stop();
2294 if (!(status & AUDIO_STATUS_PAUSE))
2295 audio_play(offset);
2298 #endif
2299 wps_data->wps_loaded = true;
2300 #ifdef DEBUG_SKIN_ENGINE
2301 if (isfile && debug_wps)
2302 debug_skin_usage();
2303 #endif
2304 return true;