Rework how progressbars are managed so you can have as many bars in a viewport as...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob73702cbf0970b114b38068c0ca3347920798999a
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,
326 parse_progressbar },
328 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
329 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
330 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
331 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
332 { WPS_TOKEN_TRACK_STARTING, "pS", WPS_REFRESH_DYNAMIC, parse_timeout },
333 { WPS_TOKEN_TRACK_ENDING, "pE", WPS_REFRESH_DYNAMIC, parse_timeout },
335 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
336 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
337 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
338 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
340 #ifdef HAVE_TAGCACHE
341 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
342 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
343 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
344 #endif
346 #if CONFIG_CODEC == SWCODEC
347 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
348 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
349 #endif
351 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
352 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
354 #ifdef HAVE_LCD_BITMAP
355 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
356 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
357 { WPS_TOKEN_DRAW_INBUILTBAR, "wi", WPS_REFRESH_DYNAMIC, parse_statusbar_inbuilt },
359 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
361 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
362 parse_image_display },
364 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
365 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
366 #ifdef HAVE_ALBUMART
367 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
368 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
369 #endif
371 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
372 parse_viewport_display },
373 #ifdef HAVE_LCD_BITMAP
374 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
375 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
376 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
377 #endif
378 { WPS_NO_TOKEN, "V", 0, parse_viewport },
380 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
381 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
382 #endif
383 #endif
385 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
386 parse_setting_and_lang },
387 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
388 parse_setting_and_lang },
389 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
391 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
392 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
393 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
396 /* Recording Tokens */
397 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
398 #ifdef HAVE_RECORDING
399 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
400 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
401 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
402 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
403 #endif
404 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
405 /* the array MUST end with an empty string (first char is \0) */
409 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
410 * chains require the order to be kept.
412 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
414 if (*list == NULL)
415 *list = item;
416 else
418 struct skin_token_list *t = *list;
419 while (t->next)
420 t = t->next;
421 t->next = item;
425 /* traverse the image linked-list for an image */
426 #ifdef HAVE_LCD_BITMAP
427 struct gui_img* find_image(char label, struct wps_data *data)
429 struct skin_token_list *list = data->images;
430 while (list)
432 struct gui_img *img = (struct gui_img *)list->token->value.data;
433 if (img->label == label)
434 return img;
435 list = list->next;
437 return NULL;
440 #endif
442 /* traverse the viewport linked list for a viewport */
443 struct skin_viewport* find_viewport(char label, struct wps_data *data)
445 struct skin_token_list *list = data->viewports;
446 while (list)
448 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
449 if (vp->label == label)
450 return vp;
451 list = list->next;
453 return NULL;
457 /* create and init a new wpsll item.
458 * passing NULL to token will alloc a new one.
459 * You should only pass NULL for the token when the token type (table above)
460 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
462 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
463 void* token_data)
465 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
466 if (!token)
467 token = skin_buffer_alloc(sizeof(struct wps_token));
468 if (!llitem || !token)
469 return NULL;
470 llitem->next = NULL;
471 llitem->token = token;
472 if (token_data)
473 llitem->token->value.data = token_data;
474 return llitem;
477 /* Returns the number of chars that should be skipped to jump
478 immediately after the first eol, i.e. to the start of the next line */
479 static int skip_end_of_line(const char *wps_bufptr)
481 line_number++;
482 int skip = 0;
483 while(*(wps_bufptr + skip) != '\n')
484 skip++;
485 return ++skip;
488 /* Starts a new subline in the current line during parsing */
489 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
491 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
492 if (!subline)
493 return false;
495 subline->first_token_idx = curr_token;
496 subline->next = NULL;
498 subline->line_type = 0;
499 subline->time_mult = 0;
501 line->curr_subline->last_token_idx = curr_token-1;
502 line->curr_subline->next = subline;
503 line->curr_subline = subline;
504 return true;
507 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
509 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
510 struct skin_subline *subline = NULL;
511 if (!line)
512 return false;
514 /* init the subline */
515 subline = &line->sublines;
516 subline->first_token_idx = curr_token;
517 subline->next = NULL;
518 subline->line_type = 0;
519 subline->time_mult = 0;
521 /* init the new line */
522 line->curr_subline = &line->sublines;
523 line->next = NULL;
524 line->subline_expire_time = 0;
526 /* connect to curr_line and vp pointers.
527 * 1) close the previous lines subline
528 * 2) connect to vp pointer
529 * 3) connect to curr_line global pointer
531 if (curr_line)
533 curr_line->curr_subline->last_token_idx = curr_token - 1;
534 curr_line->next = line;
535 curr_line->curr_subline = NULL;
537 curr_line = line;
538 if (!vp->lines)
539 vp->lines = line;
540 return true;
543 #ifdef HAVE_LCD_BITMAP
545 static int parse_statusbar_enable(const char *wps_bufptr,
546 struct wps_token *token,
547 struct wps_data *wps_data)
549 (void)token; /* Kill warnings */
550 wps_data->wps_sb_tag = true;
551 wps_data->show_sb_on_wps = true;
552 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
553 viewport_set_defaults(&default_vp->vp, curr_screen);
554 default_vp->vp.font = FONT_UI;
555 return skip_end_of_line(wps_bufptr);
558 static int parse_statusbar_disable(const char *wps_bufptr,
559 struct wps_token *token,
560 struct wps_data *wps_data)
562 (void)token; /* Kill warnings */
563 wps_data->wps_sb_tag = true;
564 wps_data->show_sb_on_wps = false;
565 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
566 viewport_set_fullscreen(&default_vp->vp, curr_screen);
567 default_vp->vp.font = FONT_UI;
568 return skip_end_of_line(wps_bufptr);
571 static int parse_statusbar_inbuilt(const char *wps_bufptr,
572 struct wps_token *token, struct wps_data *wps_data)
574 (void)wps_data;
575 token->value.data = (void*)&curr_vp->vp;
576 return skip_end_of_line(wps_bufptr);
579 static int get_image_id(int c)
581 if(c >= 'a' && c <= 'z')
582 return c - 'a';
583 else if(c >= 'A' && c <= 'Z')
584 return c - 'A' + 26;
585 else
586 return -1;
589 char *get_image_filename(const char *start, const char* bmpdir,
590 char *buf, int buf_size)
592 const char *end = strchr(start, '|');
593 int bmpdirlen = strlen(bmpdir);
595 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
597 buf[0] = '\0';
598 return NULL;
601 strcpy(buf, bmpdir);
602 buf[bmpdirlen] = '/';
603 memcpy( &buf[bmpdirlen + 1], start, end - start);
604 buf[bmpdirlen + 1 + end - start] = 0;
606 return buf;
609 static int parse_image_display(const char *wps_bufptr,
610 struct wps_token *token,
611 struct wps_data *wps_data)
613 char label = wps_bufptr[0];
614 int subimage;
615 struct gui_img *img;;
617 /* sanity check */
618 img = find_image(label, wps_data);
619 if (!img)
621 token->value.i = label; /* so debug works */
622 return WPS_ERROR_INVALID_PARAM;
625 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
627 if (subimage >= img->num_subimages)
628 return WPS_ERROR_INVALID_PARAM;
630 /* Store sub-image number to display in high bits */
631 token->value.i = label | (subimage << 8);
632 return 2; /* We have consumed 2 bytes */
633 } else {
634 token->value.i = label;
635 return 1; /* We have consumed 1 byte */
639 static int parse_image_load(const char *wps_bufptr,
640 struct wps_token *token,
641 struct wps_data *wps_data)
643 const char *ptr = wps_bufptr;
644 const char *pos;
645 const char* filename;
646 const char* id;
647 const char *newline;
648 int x,y;
649 struct gui_img *img;
651 /* format: %x|n|filename.bmp|x|y|
652 or %xl|n|filename.bmp|x|y|
653 or %xl|n|filename.bmp|x|y|num_subimages|
656 if (*ptr != '|')
657 return WPS_ERROR_INVALID_PARAM;
659 ptr++;
661 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
662 return WPS_ERROR_INVALID_PARAM;
664 /* Check there is a terminating | */
665 if (*ptr != '|')
666 return WPS_ERROR_INVALID_PARAM;
668 /* check the image number and load state */
669 if(find_image(*id, wps_data))
671 /* Invalid image ID */
672 return WPS_ERROR_INVALID_PARAM;
674 img = skin_buffer_alloc(sizeof(struct gui_img));
675 if (!img)
676 return WPS_ERROR_INVALID_PARAM;
677 /* save a pointer to the filename */
678 img->bm.data = (char*)filename;
679 img->label = *id;
680 img->x = x;
681 img->y = y;
682 img->num_subimages = 1;
683 img->always_display = false;
685 /* save current viewport */
686 img->vp = &curr_vp->vp;
688 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
690 img->always_display = true;
692 else
694 /* Parse the (optional) number of sub-images */
695 ptr++;
696 newline = strchr(ptr, '\n');
697 pos = strchr(ptr, '|');
698 if (pos && pos < newline)
699 img->num_subimages = atoi(ptr);
701 if (img->num_subimages <= 0)
702 return WPS_ERROR_INVALID_PARAM;
704 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
705 if (!item)
706 return WPS_ERROR_INVALID_PARAM;
707 add_to_ll_chain(&wps_data->images, item);
709 /* Skip the rest of the line */
710 return skip_end_of_line(wps_bufptr);
712 struct skin_font {
713 int id; /* the id from font_load */
714 char *name; /* filename without path and extension */
716 static struct skin_font skinfonts[MAXUSERFONTS];
717 static int parse_font_load(const char *wps_bufptr,
718 struct wps_token *token, struct wps_data *wps_data)
720 (void)wps_data; (void)token;
721 const char *ptr = wps_bufptr;
722 int id;
723 char *filename;
725 if (*ptr != '|')
726 return WPS_ERROR_INVALID_PARAM;
728 ptr++;
730 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
731 return WPS_ERROR_INVALID_PARAM;
733 /* Check there is a terminating | */
734 if (*ptr != '|')
735 return WPS_ERROR_INVALID_PARAM;
737 if (id <= FONT_UI || id >= MAXFONTS-1)
738 return WPS_ERROR_INVALID_PARAM;
739 #if defined(DEBUG) || defined(SIMULATOR)
740 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
742 DEBUGF("font id %d already being used\n", id);
744 #endif
745 /* make sure the filename contains .fnt,
746 * we dont actually use it, but require it anyway */
747 ptr = strchr(filename, '.');
748 if (!ptr || strncmp(ptr, ".fnt|", 5))
749 return WPS_ERROR_INVALID_PARAM;
750 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
751 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
753 return skip_end_of_line(wps_bufptr);
757 static int parse_viewport_display(const char *wps_bufptr,
758 struct wps_token *token,
759 struct wps_data *wps_data)
761 (void)wps_data;
762 char letter = wps_bufptr[0];
764 if (letter < 'a' || letter > 'z')
766 /* invalid viewport tag */
767 return WPS_ERROR_INVALID_PARAM;
769 token->value.i = letter;
770 return 1;
773 #ifdef HAVE_LCD_BITMAP
774 static int parse_playlistview_text(struct playlistviewer *viewer,
775 enum info_line_type line, char* text)
777 int cur_string = 0;
778 const struct wps_tag *tag;
779 int taglen = 0;
780 const char *start = text;
781 if (*text != '|')
782 return -1;
783 text++;
784 viewer->lines[line].count = 0;
785 viewer->lines[line].scroll = false;
786 while (*text != '|')
788 if (*text == '%') /* it is a token of some type */
790 text++;
791 taglen = 0;
792 switch(*text)
794 case '%':
795 case '<':
796 case '|':
797 case '>':
798 case ';':
799 case '#':
800 /* escaped characters */
801 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
802 viewer->lines[line].strings[cur_string][0] = *text;
803 viewer->lines[line].strings[cur_string++][1] = '\0';
804 break;
805 default:
806 for (tag = all_tags;
807 strncmp(text, tag->name, strlen(tag->name)) != 0;
808 tag++) ;
809 /* %s isnt stored as a tag so manually check for it */
810 if (tag->type == WPS_NO_TOKEN)
812 if (!strncmp(tag->name, "s", 1))
814 viewer->lines[line].scroll = true;
815 taglen = 1;
818 else if (tag->type == WPS_TOKEN_UNKNOWN)
820 int i = 0;
821 /* just copy the string */
822 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
823 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
825 viewer->lines[line].strings[cur_string][i] = text[i];
826 i++;
828 viewer->lines[line].strings[cur_string][i] = '\0';
829 cur_string++;
830 taglen = i;
832 else
834 if (tag->parse_func)
836 /* unsupported tag, reject */
837 return -1;
839 taglen = strlen(tag->name);
840 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
842 text += taglen;
845 else
847 /* regular string */
848 int i = 0;
849 /* just copy the string */
850 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
851 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
853 viewer->lines[line].strings[cur_string][i] = text[i];
854 i++;
856 viewer->lines[line].strings[cur_string][i] = '\0';
857 cur_string++;
858 text += i;
861 return text - start;
865 static int parse_playlistview(const char *wps_bufptr,
866 struct wps_token *token, struct wps_data *wps_data)
868 (void)wps_data;
869 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
870 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
871 char *ptr = strchr(wps_bufptr, '|');
872 int length;
873 if (!viewer || !ptr)
874 return WPS_ERROR_INVALID_PARAM;
875 viewer->vp = &curr_vp->vp;
876 viewer->show_icons = true;
877 viewer->start_offset = atoi(ptr+1);
878 token->value.data = (void*)viewer;
879 ptr = strchr(ptr+1, '|');
880 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
881 if (length < 0)
882 return WPS_ERROR_INVALID_PARAM;
883 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
884 if (length < 0)
885 return WPS_ERROR_INVALID_PARAM;
887 return skip_end_of_line(wps_bufptr);
889 #endif
891 static int parse_viewport(const char *wps_bufptr,
892 struct wps_token *token,
893 struct wps_data *wps_data)
895 (void)token; /* Kill warnings */
896 const char *ptr = wps_bufptr;
898 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
900 /* check for the optional letter to signify its a hideable viewport */
901 /* %Vl|<label>|<rest of tags>| */
902 skin_vp->hidden_flags = 0;
903 skin_vp->label = VP_NO_LABEL;
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->vp = vp;
1157 pb->have_bitmap_pb = false;
1158 pb->bm.data = NULL; /* no bitmap specified */
1159 pb->follow_lang_direction = follow_lang_direction > 0;
1161 if (*wps_bufptr != '|') /* regular old style */
1163 pb->x = 0;
1164 pb->width = vp->width;
1165 pb->height = SYSFONT_HEIGHT-2;
1166 pb->y = -line_num - 1; /* Will be computed during the rendering */
1167 if (token->type == WPS_TOKEN_VOLUME)
1168 return 0; /* dont add it, let the regular token handling do the work */
1169 add_to_ll_chain(&wps_data->progressbars, item);
1170 return 0;
1172 ptr = wps_bufptr + 1;
1174 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1175 &x, &y, &width, &height)))
1176 return WPS_ERROR_INVALID_PARAM;
1178 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1179 pb->bm.data = (char*)filename;
1181 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1182 pb->x = x;
1183 else
1184 pb->x = vp->x;
1186 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1188 /* A zero width causes a divide-by-zero error later, so reject it */
1189 if (width == 0)
1190 return WPS_ERROR_INVALID_PARAM;
1192 pb->width = width;
1194 else
1195 pb->width = vp->width - pb->x;
1197 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1199 /* A zero height makes no sense - reject it */
1200 if (height == 0)
1201 return WPS_ERROR_INVALID_PARAM;
1203 pb->height = height;
1205 else
1207 if (vp->font > FONT_UI)
1208 pb->height = -1; /* calculate at display time */
1209 else
1211 #ifndef __PCTOOL__
1212 pb->height = font_get(vp->font)->height;
1213 #else
1214 pb->height = 8;
1215 #endif
1219 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1220 pb->y = y;
1221 else
1222 pb->y = -line_num - 1; /* Will be computed during the rendering */
1224 add_to_ll_chain(&wps_data->progressbars, item);
1225 if (token->type == WPS_TOKEN_VOLUME)
1226 token->type = WPS_TOKEN_VOLUMEBAR;
1227 pb->type = token->type;
1228 /* Skip the rest of the line */
1229 return skip_end_of_line(wps_bufptr)-1;
1230 #else
1231 (void)token;
1233 if (token->type != WPS_TOKEN_VOLUME)
1235 wps_data->full_line_progressbar =
1236 token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
1238 return 0;
1240 #endif
1243 #ifdef HAVE_ALBUMART
1244 static int parse_int(const char *newline, const char **_pos, int *num)
1246 *_pos = parse_list("d", NULL, '|', *_pos, num);
1248 return (!*_pos || *_pos > newline || **_pos != '|');
1251 static int parse_albumart_load(const char *wps_bufptr,
1252 struct wps_token *token,
1253 struct wps_data *wps_data)
1255 const char *_pos, *newline;
1256 bool parsing;
1257 struct dim dimensions;
1258 int albumart_slot;
1259 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1260 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1261 (void)token; /* silence warning */
1262 if (!aa)
1263 return skip_end_of_line(wps_bufptr);
1265 /* reset albumart info in wps */
1266 aa->width = -1;
1267 aa->height = -1;
1268 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1269 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1270 aa->vp = &curr_vp->vp;
1272 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1274 newline = strchr(wps_bufptr, '\n');
1276 _pos = wps_bufptr;
1278 if (*_pos != '|')
1279 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1281 ++_pos;
1283 /* initial validation and parsing of x component */
1284 if (parse_int(newline, &_pos, &aa->x))
1285 return WPS_ERROR_INVALID_PARAM;
1287 ++_pos;
1289 /* initial validation and parsing of y component */
1290 if (parse_int(newline, &_pos, &aa->y))
1291 return WPS_ERROR_INVALID_PARAM;
1293 /* parsing width field */
1294 parsing = true;
1295 while (parsing)
1297 /* apply each modifier in turn */
1298 ++_pos;
1299 switch (*_pos)
1301 case 'l':
1302 case 'L':
1303 case '+':
1304 if (swap_for_rtl)
1305 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1306 else
1307 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1308 break;
1309 case 'c':
1310 case 'C':
1311 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1312 break;
1313 case 'r':
1314 case 'R':
1315 case '-':
1316 if (swap_for_rtl)
1317 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1318 else
1319 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1320 break;
1321 case 'd':
1322 case 'D':
1323 case 'i':
1324 case 'I':
1325 case 's':
1326 case 'S':
1327 /* simply ignored */
1328 break;
1329 default:
1330 parsing = false;
1331 break;
1334 /* extract max width data */
1335 if (*_pos != '|')
1337 if (parse_int(newline, &_pos, &aa->width))
1338 return WPS_ERROR_INVALID_PARAM;
1341 /* parsing height field */
1342 parsing = true;
1343 while (parsing)
1345 /* apply each modifier in turn */
1346 ++_pos;
1347 switch (*_pos)
1349 case 't':
1350 case 'T':
1351 case '-':
1352 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1353 break;
1354 case 'c':
1355 case 'C':
1356 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1357 break;
1358 case 'b':
1359 case 'B':
1360 case '+':
1361 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1362 break;
1363 case 'd':
1364 case 'D':
1365 case 'i':
1366 case 'I':
1367 case 's':
1368 case 'S':
1369 /* simply ignored */
1370 break;
1371 default:
1372 parsing = false;
1373 break;
1376 /* extract max height data */
1377 if (*_pos != '|')
1379 if (parse_int(newline, &_pos, &aa->height))
1380 return WPS_ERROR_INVALID_PARAM;
1383 /* if we got here, we parsed everything ok .. ! */
1384 if (aa->width < 0)
1385 aa->width = 0;
1386 else if (aa->width > LCD_WIDTH)
1387 aa->width = LCD_WIDTH;
1389 if (aa->height < 0)
1390 aa->height = 0;
1391 else if (aa->height > LCD_HEIGHT)
1392 aa->height = LCD_HEIGHT;
1394 if (swap_for_rtl)
1395 aa->x = LCD_WIDTH - (aa->x + aa->width);
1397 aa->state = WPS_ALBUMART_LOAD;
1398 aa->draw = false;
1399 wps_data->albumart = aa;
1401 dimensions.width = aa->width;
1402 dimensions.height = aa->height;
1404 albumart_slot = playback_claim_aa_slot(&dimensions);
1406 if (0 <= albumart_slot)
1407 wps_data->playback_aa_slot = albumart_slot;
1409 /* Skip the rest of the line */
1410 return skip_end_of_line(wps_bufptr);
1413 static int parse_albumart_display(const char *wps_bufptr,
1414 struct wps_token *token,
1415 struct wps_data *wps_data)
1417 (void)wps_bufptr;
1418 struct wps_token *prev = token-1;
1419 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1421 token->type = WPS_TOKEN_ALBUMART_FOUND;
1423 else if (wps_data->albumart)
1425 wps_data->albumart->vp = &curr_vp->vp;
1427 #if 0
1428 /* the old code did this so keep it here for now...
1429 * this is to allow the posibility to showing the next tracks AA! */
1430 if (wps_bufptr+1 == 'n')
1431 return 1;
1432 #endif
1433 return 0;
1435 #endif /* HAVE_ALBUMART */
1437 #ifdef HAVE_TOUCHSCREEN
1439 struct touchaction {const char* s; int action;};
1440 static const struct touchaction touchactions[] = {
1441 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1442 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1443 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1444 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1445 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1446 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1447 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1448 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1450 static int parse_touchregion(const char *wps_bufptr,
1451 struct wps_token *token, struct wps_data *wps_data)
1453 (void)token;
1454 unsigned i, imax;
1455 struct touchregion *region = NULL;
1456 const char *ptr = wps_bufptr;
1457 const char *action;
1458 const char pb_string[] = "progressbar";
1459 const char vol_string[] = "volume";
1460 int x,y,w,h;
1462 /* format: %T|x|y|width|height|action|
1463 * if action starts with & the area must be held to happen
1464 * action is one of:
1465 * play - play/pause playback
1466 * stop - stop playback, exit the wps
1467 * prev - prev track
1468 * next - next track
1469 * ffwd - seek forward
1470 * rwd - seek backwards
1471 * menu - go back to the main menu
1472 * browse - go back to the file/db browser
1473 * shuffle - toggle shuffle mode
1474 * repmode - cycle the repeat mode
1475 * quickscreen - go into the quickscreen
1476 * contextmenu - open the context menu
1477 * playlist - go into the playlist
1478 * pitch - go into the pitchscreen
1479 * volup - increase volume by one step
1480 * voldown - decrease volume by one step
1484 if (*ptr != '|')
1485 return WPS_ERROR_INVALID_PARAM;
1486 ptr++;
1488 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1489 return WPS_ERROR_INVALID_PARAM;
1491 /* Check there is a terminating | */
1492 if (*ptr != '|')
1493 return WPS_ERROR_INVALID_PARAM;
1495 region = skin_buffer_alloc(sizeof(struct touchregion));
1496 if (!region)
1497 return WPS_ERROR_INVALID_PARAM;
1499 /* should probably do some bounds checking here with the viewport... but later */
1500 region->action = ACTION_NONE;
1501 region->x = x;
1502 region->y = y;
1503 region->width = w;
1504 region->height = h;
1505 region->wvp = curr_vp;
1506 region->armed = false;
1508 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1509 && *(action + sizeof(pb_string)-1) == '|')
1510 region->type = WPS_TOUCHREGION_SCROLLBAR;
1511 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1512 && *(action + sizeof(vol_string)-1) == '|')
1513 region->type = WPS_TOUCHREGION_VOLUME;
1514 else
1516 region->type = WPS_TOUCHREGION_ACTION;
1518 if (*action == '&')
1520 action++;
1521 region->repeat = true;
1523 else
1524 region->repeat = false;
1526 i = 0;
1527 imax = ARRAYLEN(touchactions);
1528 while ((region->action == ACTION_NONE) &&
1529 (i < imax))
1531 /* try to match with one of our touchregion screens */
1532 int len = strlen(touchactions[i].s);
1533 if (!strncmp(touchactions[i].s, action, len)
1534 && *(action+len) == '|')
1535 region->action = touchactions[i].action;
1536 i++;
1538 if (region->action == ACTION_NONE)
1539 return WPS_ERROR_INVALID_PARAM;
1541 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1542 if (!item)
1543 return WPS_ERROR_INVALID_PARAM;
1544 add_to_ll_chain(&wps_data->touchregions, item);
1545 return skip_end_of_line(wps_bufptr);
1547 #endif
1549 /* Parse a generic token from the given string. Return the length read */
1550 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1552 int skip = 0, taglen = 0, ret;
1553 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1554 const struct wps_tag *tag;
1555 memset(token, 0, sizeof(*token));
1557 switch(*wps_bufptr)
1560 case '%':
1561 case '<':
1562 case '|':
1563 case '>':
1564 case ';':
1565 case '#':
1566 /* escaped characters */
1567 token->type = WPS_TOKEN_CHARACTER;
1568 token->value.c = *wps_bufptr;
1569 taglen = 1;
1570 wps_data->num_tokens++;
1571 break;
1573 case '?':
1574 /* conditional tag */
1575 token->type = WPS_TOKEN_CONDITIONAL;
1576 level++;
1577 condindex[level] = wps_data->num_tokens;
1578 numoptions[level] = 1;
1579 wps_data->num_tokens++;
1580 ret = parse_token(wps_bufptr + 1, wps_data);
1581 if (ret < 0) return ret;
1582 taglen = 1 + ret;
1583 break;
1585 default:
1586 /* find what tag we have */
1587 for (tag = all_tags;
1588 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1589 tag++) ;
1591 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1592 token->type = tag->type;
1593 curr_line->curr_subline->line_type |= tag->refresh_type;
1595 /* if the tag has a special parsing function, we call it */
1596 if (tag->parse_func)
1598 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1599 if (ret < 0) return ret;
1600 skip += ret;
1603 /* Some tags we don't want to save as tokens */
1604 if (tag->type == WPS_NO_TOKEN)
1605 break;
1607 /* tags that start with 'F', 'I' or 'D' are for the next file */
1608 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1609 *(tag->name) == 'D')
1610 token->next = true;
1612 wps_data->num_tokens++;
1613 break;
1616 skip += taglen;
1617 return skip;
1622 * Returns the number of bytes to skip the buf pointer to access the false
1623 * branch in a _binary_ conditional
1625 * That is:
1626 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1627 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1629 * depending on the features of a target it's not called from check_feature_tag,
1630 * hence the __attribute__ or it issues compiler warnings
1634 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1635 static int find_false_branch(const char *wps_bufptr)
1637 const char *buf = wps_bufptr;
1638 /* wps_bufptr is after the opening '<', hence level = 1*/
1639 int level = 1;
1640 char ch;
1643 ch = *buf;
1644 if (ch == '%')
1645 { /* filter out the characters we check later if they're printed
1646 * as literals */
1647 ch = *(++buf);
1648 if (ch == '<' || ch == '>' || ch == '|')
1649 continue;
1650 /* else: some tags/printed literals we skip over */
1652 else if (ch == '<') /* nested conditional */
1653 level++;
1654 else if (ch == '>')
1655 { /* closed our or a nested conditional,
1656 * do NOT skip over the '>' so that wps_parse() sees it for closing
1657 * if it is the closing one for our conditional */
1658 level--;
1660 else if (ch == '|' && level == 1)
1661 { /* we found our separator, point before and get out */
1662 break;
1664 /* if level is 0, we don't have a false branch */
1665 } while (level > 0 && *(++buf));
1667 return buf - wps_bufptr;
1671 * returns the number of bytes to get the appropriate branch of a binary
1672 * conditional
1674 * That means:
1675 * - if a feature is available, it returns 0 to not skip anything
1676 * - if the feature is not available, skip to the false branch and don't
1677 * parse the true branch at all
1679 * */
1680 static int check_feature_tag(const char *wps_bufptr, const int type)
1682 (void)wps_bufptr;
1683 switch (type)
1685 case WPS_TOKEN_RTC_PRESENT:
1686 #if CONFIG_RTC
1687 return 0;
1688 #else
1689 return find_false_branch(wps_bufptr);
1690 #endif
1691 case WPS_TOKEN_HAVE_RECORDING:
1692 #ifdef HAVE_RECORDING
1693 return 0;
1694 #else
1695 return find_false_branch(wps_bufptr);
1696 #endif
1697 default: /* not a tag we care about, just don't skip */
1698 return 0;
1703 /* Parses the WPS.
1704 data is the pointer to the structure where the parsed WPS should be stored.
1705 It is initialised.
1706 wps_bufptr points to the string containing the WPS tags */
1707 #define TOKEN_BLOCK_SIZE 128
1708 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1710 if (!data || !wps_bufptr || !*wps_bufptr)
1711 return false;
1712 enum wps_parse_error fail = PARSE_OK;
1713 int ret;
1714 int max_tokens = TOKEN_BLOCK_SIZE;
1715 size_t buf_free = 0;
1716 line_number = 0;
1717 level = -1;
1719 /* allocate enough RAM for a reasonable skin, grow as needed.
1720 * Free any used RAM before loading the images to be 100% RAM efficient */
1721 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1722 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1723 return false;
1724 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1725 data->num_tokens = 0;
1727 #if LCD_DEPTH > 1
1728 /* Backdrop defaults to the setting unless %X is used, so set it now */
1729 if (global_settings.backdrop_file[0])
1731 data->backdrop = "-";
1733 #endif
1735 while (*wps_bufptr && !fail)
1737 if (follow_lang_direction)
1738 follow_lang_direction--;
1739 /* first make sure there is enough room for tokens */
1740 if (max_tokens <= data->num_tokens + 5)
1742 int extra_tokens = TOKEN_BLOCK_SIZE;
1743 size_t needed = extra_tokens * sizeof(struct wps_token);
1744 /* do some smarts here to grow the array a bit */
1745 if (skin_buffer_freespace() < needed)
1747 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1748 break;
1750 skin_buffer_increment(needed, false);
1751 max_tokens += extra_tokens;
1754 switch(*wps_bufptr++)
1757 /* Regular tag */
1758 case '%':
1759 if ((ret = parse_token(wps_bufptr, data)) < 0)
1761 fail = PARSE_FAIL_COND_INVALID_PARAM;
1762 break;
1764 else if (level >= WPS_MAX_COND_LEVEL - 1)
1766 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1767 break;
1769 wps_bufptr += ret;
1770 break;
1772 /* Alternating sublines separator */
1773 case ';':
1774 if (level >= 0) /* there are unclosed conditionals */
1776 fail = PARSE_FAIL_UNCLOSED_COND;
1777 break;
1780 if (!skin_start_new_subline(curr_line, data->num_tokens))
1781 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1783 break;
1785 /* Conditional list start */
1786 case '<':
1787 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1789 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1790 break;
1792 wps_bufptr += check_feature_tag(wps_bufptr,
1793 data->tokens[data->num_tokens-1].type);
1794 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1795 lastcond[level] = data->num_tokens++;
1796 break;
1798 /* Conditional list end */
1799 case '>':
1800 if (level < 0) /* not in a conditional, invalid char */
1802 fail = PARSE_FAIL_INVALID_CHAR;
1803 break;
1806 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1807 if (lastcond[level])
1808 data->tokens[lastcond[level]].value.i = data->num_tokens;
1809 else
1811 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1812 break;
1815 lastcond[level] = 0;
1816 data->num_tokens++;
1817 data->tokens[condindex[level]].value.i = numoptions[level];
1818 level--;
1819 break;
1821 /* Conditional list option */
1822 case '|':
1823 if (level < 0) /* not in a conditional, invalid char */
1825 fail = PARSE_FAIL_INVALID_CHAR;
1826 break;
1829 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1830 if (lastcond[level])
1831 data->tokens[lastcond[level]].value.i = data->num_tokens;
1832 else
1834 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1835 break;
1838 lastcond[level] = data->num_tokens;
1839 numoptions[level]++;
1840 data->num_tokens++;
1841 break;
1843 /* Comment */
1844 case '#':
1845 if (level >= 0) /* there are unclosed conditionals */
1847 fail = PARSE_FAIL_UNCLOSED_COND;
1848 break;
1851 wps_bufptr += skip_end_of_line(wps_bufptr);
1852 break;
1854 /* End of this line */
1855 case '\n':
1856 if (level >= 0) /* there are unclosed conditionals */
1858 fail = PARSE_FAIL_UNCLOSED_COND;
1859 break;
1861 /* add a new token for the \n so empty lines are correct */
1862 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1863 data->tokens[data->num_tokens].value.c = '\n';
1864 data->tokens[data->num_tokens].next = false;
1865 data->num_tokens++;
1867 if (!skin_start_new_line(curr_vp, data->num_tokens))
1869 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1870 break;
1872 line_number++;
1874 break;
1876 /* String */
1877 default:
1879 unsigned int len = 1;
1880 const char *string_start = wps_bufptr - 1;
1882 /* find the length of the string */
1883 while (*wps_bufptr && *wps_bufptr != '#' &&
1884 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1885 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1886 *wps_bufptr != '|' && *wps_bufptr != '\n')
1888 wps_bufptr++;
1889 len++;
1892 /* look if we already have that string */
1893 char *str;
1894 bool found = false;
1895 struct skin_token_list *list = data->strings;
1896 while (list)
1898 str = (char*)list->token->value.data;
1899 found = (strlen(str) == len &&
1900 strncmp(string_start, str, len) == 0);
1901 if (found)
1902 break; /* break here because the list item is
1903 used if its found */
1904 list = list->next;
1906 /* If a matching string is found, found is true and i is
1907 the index of the string. If not, found is false */
1909 if (!found)
1911 /* new string */
1912 str = (char*)skin_buffer_alloc(len+1);
1913 if (!str)
1915 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1916 break;
1918 strlcpy(str, string_start, len+1);
1919 struct skin_token_list *item =
1920 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1921 if(!item)
1923 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1924 break;
1926 add_to_ll_chain(&data->strings, item);
1928 else
1930 /* another occurrence of an existing string */
1931 data->tokens[data->num_tokens].value.data = list->token->value.data;
1933 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1934 data->num_tokens++;
1936 break;
1940 if (!fail && level >= 0) /* there are unclosed conditionals */
1941 fail = PARSE_FAIL_UNCLOSED_COND;
1943 if (*wps_bufptr && !fail)
1944 /* one of the limits of the while loop was exceeded */
1945 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1947 /* Success! */
1948 curr_line->curr_subline->last_token_idx = data->num_tokens;
1949 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1950 /* freeup unused tokens */
1951 skin_buffer_free_from_front(sizeof(struct wps_token)
1952 * (max_tokens - data->num_tokens));
1954 #if defined(DEBUG) || defined(SIMULATOR)
1955 if (debug)
1957 print_debug_info(data, fail, line_number);
1958 debug_skin_usage();
1960 #else
1961 (void)debug;
1962 #endif
1964 return (fail == 0);
1969 * initial setup of wps_data; does reset everything
1970 * except fields which need to survive, i.e.
1973 static void skin_data_reset(struct wps_data *wps_data)
1975 #ifdef HAVE_LCD_BITMAP
1976 wps_data->images = NULL;
1977 wps_data->progressbars = NULL;
1978 #endif
1979 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1980 wps_data->backdrop = NULL;
1981 #endif
1982 #ifdef HAVE_TOUCHSCREEN
1983 wps_data->touchregions = NULL;
1984 #endif
1985 wps_data->viewports = NULL;
1986 wps_data->strings = NULL;
1987 #ifdef HAVE_ALBUMART
1988 wps_data->albumart = NULL;
1989 if (wps_data->playback_aa_slot >= 0)
1991 playback_release_aa_slot(wps_data->playback_aa_slot);
1992 wps_data->playback_aa_slot = -1;
1994 #endif
1995 wps_data->tokens = NULL;
1996 wps_data->num_tokens = 0;
1998 #ifdef HAVE_LCD_BITMAP
1999 wps_data->peak_meter_enabled = false;
2000 wps_data->wps_sb_tag = false;
2001 wps_data->show_sb_on_wps = false;
2002 #else /* HAVE_LCD_CHARCELLS */
2003 /* progress bars */
2004 int i;
2005 for (i = 0; i < 8; i++)
2007 wps_data->wps_progress_pat[i] = 0;
2009 wps_data->full_line_progressbar = false;
2010 #endif
2011 wps_data->wps_loaded = false;
2014 #ifdef HAVE_LCD_BITMAP
2015 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2017 (void)wps_data; /* only needed for remote targets */
2018 char img_path[MAX_PATH];
2019 get_image_filename(bitmap->data, bmpdir,
2020 img_path, sizeof(img_path));
2022 /* load the image */
2023 int format;
2024 #ifdef HAVE_REMOTE_LCD
2025 if (curr_screen == SCREEN_REMOTE)
2026 format = FORMAT_ANY|FORMAT_REMOTE;
2027 else
2028 #endif
2029 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2031 size_t max_buf;
2032 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2033 bitmap->data = imgbuf;
2034 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2036 if (ret > 0)
2038 skin_buffer_increment(ret, true);
2039 return true;
2041 else
2043 /* Abort if we can't load an image */
2044 DEBUGF("Couldn't load '%s'\n", img_path);
2045 return false;
2049 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2051 struct skin_token_list *list;
2052 bool retval = true; /* return false if a single image failed to load */
2053 /* do the progressbars */
2054 list = wps_data->progressbars;
2055 while (list)
2057 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2058 if (pb->bm.data)
2060 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2061 if (!pb->have_bitmap_pb) /* no success */
2062 retval = false;
2064 list = list->next;
2066 /* regular images */
2067 list = wps_data->images;
2068 while (list)
2070 struct gui_img *img = (struct gui_img*)list->token->value.data;
2071 if (img->bm.data)
2073 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2074 if (img->loaded)
2075 img->subimage_height = img->bm.height / img->num_subimages;
2076 else
2077 retval = false;
2079 list = list->next;
2082 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2083 /* Backdrop load scheme:
2084 * 1) %X|filename|
2085 * 2) load the backdrop from settings
2087 if (wps_data->backdrop)
2089 bool needed = wps_data->backdrop[0] != '-';
2090 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2091 bmpdir, curr_screen);
2092 if (!wps_data->backdrop && needed)
2093 retval = false;
2095 #endif /* has backdrop support */
2097 return retval;
2100 static bool skin_load_fonts(struct wps_data *data)
2102 /* don't spit out after the first failue to aid debugging */
2103 bool success = true;
2104 struct skin_token_list *vp_list;
2105 int font_id;
2106 /* walk though each viewport and assign its font */
2107 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2109 /* first, find the viewports that have a non-sys/ui-font font */
2110 struct skin_viewport *skin_vp =
2111 (struct skin_viewport*)vp_list->token->value.data;
2112 struct viewport *vp = &skin_vp->vp;
2115 if (vp->font <= FONT_UI)
2116 { /* the usual case -> built-in fonts */
2117 #ifdef HAVE_REMOTE_LCD
2118 if (vp->font == FONT_UI)
2119 vp->font += curr_screen;
2120 #endif
2121 continue;
2123 font_id = vp->font;
2125 /* now find the corresponding skin_font */
2126 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2127 if (!font->name)
2129 DEBUGF("font %d not specified\n", font_id);
2130 success = false;
2131 continue;
2134 /* load the font - will handle loading the same font again if
2135 * multiple viewports use the same */
2136 if (font->id < 0)
2138 char *dot = strchr(font->name, '.');
2139 *dot = '\0';
2140 font->id = skin_font_load(font->name);
2143 if (font->id < 0)
2145 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2146 font_id, font->name);
2147 success = false;
2148 continue;
2151 /* finally, assign the font_id to the viewport */
2152 vp->font = font->id;
2154 return success;
2157 #endif /* HAVE_LCD_BITMAP */
2159 /* to setup up the wps-data from a format-buffer (isfile = false)
2160 from a (wps-)file (isfile = true)*/
2161 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2162 const char *buf, bool isfile)
2164 char *wps_buffer = NULL;
2165 if (!wps_data || !buf)
2166 return false;
2167 #ifdef HAVE_ALBUMART
2168 int status;
2169 struct mp3entry *curtrack;
2170 long offset;
2171 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2172 if (wps_data->albumart)
2174 old_aa.state = wps_data->albumart->state;
2175 old_aa.height = wps_data->albumart->height;
2176 old_aa.width = wps_data->albumart->width;
2178 #endif
2179 #ifdef HAVE_LCD_BITMAP
2180 int i;
2181 for (i=0;i<MAXUSERFONTS;i++)
2183 skinfonts[i].id = -1;
2184 skinfonts[i].name = NULL;
2186 #endif
2187 #ifdef DEBUG_SKIN_ENGINE
2188 if (isfile && debug_wps)
2190 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2192 #endif
2194 skin_data_reset(wps_data);
2195 wps_data->wps_loaded = false;
2196 curr_screen = screen;
2198 /* alloc default viewport, will be fixed up later */
2199 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2200 if (!curr_vp)
2201 return false;
2202 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2203 if (!list)
2204 return false;
2205 add_to_ll_chain(&wps_data->viewports, list);
2208 /* Initialise the first (default) viewport */
2209 curr_vp->label = VP_DEFAULT_LABEL;
2210 curr_vp->hidden_flags = 0;
2211 curr_vp->lines = NULL;
2213 viewport_set_defaults(&curr_vp->vp, screen);
2214 #ifdef HAVE_LCD_BITMAP
2215 curr_vp->vp.font = FONT_UI;
2216 #endif
2218 curr_line = NULL;
2219 if (!skin_start_new_line(curr_vp, 0))
2220 return false;
2222 if (isfile)
2224 int fd = open_utf8(buf, O_RDONLY);
2226 if (fd < 0)
2227 return false;
2229 /* get buffer space from the plugin buffer */
2230 size_t buffersize = 0;
2231 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2233 if (!wps_buffer)
2234 return false;
2236 /* copy the file's content to the buffer for parsing,
2237 ensuring that every line ends with a newline char. */
2238 unsigned int start = 0;
2239 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2241 start += strlen(wps_buffer + start);
2242 if (start < buffersize - 1)
2244 wps_buffer[start++] = '\n';
2245 wps_buffer[start] = 0;
2248 close(fd);
2249 if (start <= 0)
2250 return false;
2252 else
2254 wps_buffer = (char*)buf;
2256 /* parse the WPS source */
2257 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2258 skin_data_reset(wps_data);
2259 return false;
2262 #ifdef HAVE_LCD_BITMAP
2263 char bmpdir[MAX_PATH];
2264 if (isfile)
2266 /* get the bitmap dir */
2267 char *dot = strrchr(buf, '.');
2268 strlcpy(bmpdir, buf, dot - buf + 1);
2270 else
2272 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2274 /* load the bitmaps that were found by the parsing */
2275 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2276 !skin_load_fonts(wps_data))
2278 skin_data_reset(wps_data);
2279 return false;
2281 #endif
2282 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2283 status = audio_status();
2284 if (status & AUDIO_STATUS_PLAY)
2286 struct skin_albumart *aa = wps_data->albumart;
2287 if (aa && ((aa->state && !old_aa.state) ||
2288 (aa->state &&
2289 (((old_aa.height != aa->height) ||
2290 (old_aa.width != aa->width))))))
2292 curtrack = audio_current_track();
2293 offset = curtrack->offset;
2294 audio_stop();
2295 if (!(status & AUDIO_STATUS_PAUSE))
2296 audio_play(offset);
2299 #endif
2300 wps_data->wps_loaded = true;
2301 #ifdef DEBUG_SKIN_ENGINE
2302 if (isfile && debug_wps)
2303 debug_skin_usage();
2304 #endif
2305 return true;