Store the list of fonts in a linked list instead of in a static array (together with...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob8f802a12a4c01e7ae8d7297ba3e24235a94efbc0
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "config.h"
26 #include "file.h"
27 #include "misc.h"
28 #include "plugin.h"
29 #include "viewport.h"
31 #ifdef __PCTOOL__
32 #ifdef WPSEDITOR
33 #include "proxy.h"
34 #include "sysfont.h"
35 #else
36 #include "action.h"
37 #include "checkwps.h"
38 #include "audio.h"
39 #define lang_is_rtl() (false)
40 #define DEBUGF printf
41 #endif /*WPSEDITOR*/
42 #else
43 #include "debug.h"
44 #include "language.h"
45 #endif /*__PCTOOL__*/
47 #include <ctype.h>
48 #include <stdbool.h>
49 #include "font.h"
51 #include "wps_internals.h"
52 #include "skin_engine.h"
53 #include "settings.h"
54 #include "settings_list.h"
55 #include "skin_fonts.h"
57 #ifdef HAVE_LCD_BITMAP
58 #include "bmp.h"
59 #endif
61 #ifdef HAVE_ALBUMART
62 #include "playback.h"
63 #endif
65 #include "backdrop.h"
66 #include "statusbar-skinned.h"
68 #define WPS_ERROR_INVALID_PARAM -1
70 /* which screen are we parsing for? */
71 static enum screen_type curr_screen;
73 /* level of current conditional.
74 -1 means we're not in a conditional. */
75 static int level = -1;
77 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
78 or WPS_TOKEN_CONDITIONAL_START in current level */
79 static int lastcond[WPS_MAX_COND_LEVEL];
81 /* index of the WPS_TOKEN_CONDITIONAL in current level */
82 static int condindex[WPS_MAX_COND_LEVEL];
84 /* number of condtional options in current level */
85 static int numoptions[WPS_MAX_COND_LEVEL];
87 /* line number, debug only */
88 static int line_number;
90 /* the current viewport */
91 static struct skin_viewport *curr_vp;
92 /* the current line, linked to the above viewport */
93 static struct skin_line *curr_line;
95 static int follow_lang_direction = 0;
97 #if defined(DEBUG) || defined(SIMULATOR)
98 /* debugging function */
99 extern void print_debug_info(struct wps_data *data, int fail, int line);
100 extern void debug_skin_usage(void);
101 #endif
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
107 has been consumed.
109 wps_bufptr points to the char following the tag (i.e. where
110 details begin).
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
114 struct wps_token *token, struct wps_data *wps_data);
116 struct wps_tag {
117 enum wps_token_type type;
118 const char name[3];
119 unsigned char refresh_type;
120 const wps_tag_parse_func parse_func;
122 static int skip_end_of_line(const char *wps_bufptr);
123 /* prototypes of all special parse functions : */
124 static int parse_timeout(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126 static int parse_progressbar(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_dir_level(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_setting_and_lang(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_languagedirection(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data)
137 (void)wps_bufptr;
138 (void)token;
139 (void)wps_data;
140 follow_lang_direction = 2; /* 2 because it is decremented immediatly after
141 this token is parsed, after the next token it
142 will be 0 again. */
143 return 0;
146 #ifdef HAVE_LCD_BITMAP
147 static int parse_viewport_display(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 static int parse_playlistview(const char *wps_bufptr,
150 struct wps_token *token, struct wps_data *wps_data);
151 static int parse_viewport(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 static int parse_statusbar_enable(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 static int parse_statusbar_disable(const char *wps_bufptr,
156 struct wps_token *token, struct wps_data *wps_data);
157 static int parse_image_display(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_image_load(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 static int parse_font_load(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 #endif /*HAVE_LCD_BITMAP */
164 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
165 static int parse_image_special(const char *wps_bufptr,
166 struct wps_token *token, struct wps_data *wps_data);
167 #endif
168 #ifdef HAVE_ALBUMART
169 static int parse_albumart_load(const char *wps_bufptr,
170 struct wps_token *token, struct wps_data *wps_data);
171 static int parse_albumart_display(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 #endif /* HAVE_ALBUMART */
174 #ifdef HAVE_TOUCHSCREEN
175 static int parse_touchregion(const char *wps_bufptr,
176 struct wps_token *token, struct wps_data *wps_data);
177 #else
178 static int fulline_tag_not_supported(const char *wps_bufptr,
179 struct wps_token *token, struct wps_data *wps_data)
181 (void)token; (void)wps_data;
182 return skip_end_of_line(wps_bufptr);
184 #define parse_touchregion fulline_tag_not_supported
185 #endif
186 #ifdef CONFIG_RTC
187 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
188 #else
189 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
190 #endif
192 /* array of available tags - those with more characters have to go first
193 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
194 static const struct wps_tag all_tags[] = {
196 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
197 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
198 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
199 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
200 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
201 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
203 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
204 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
205 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
206 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
207 #if CONFIG_CHARGING >= CHARGING_MONITOR
208 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
209 #endif
210 #if CONFIG_CHARGING
211 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
212 #endif
213 #ifdef HAVE_USB_POWER
214 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
215 #endif
217 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
219 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
220 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
221 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
222 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
223 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
224 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
225 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
226 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
237 /* current file */
238 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
248 parse_dir_level },
250 /* next file */
251 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
261 parse_dir_level },
263 /* current metadata */
264 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
277 /* next metadata */
278 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
283 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
284 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
291 #if (CONFIG_CODEC != MAS3507D)
292 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
293 #endif
294 #if (CONFIG_CODEC == SWCODEC)
295 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
296 #endif
297 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
298 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
299 #endif
301 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
303 #ifdef HAS_REMOTE_BUTTON_HOLD
304 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
305 #else
306 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
307 #endif
309 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
311 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
312 parse_timeout },
314 #ifdef HAVE_LCD_BITMAP
315 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
316 #else
317 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
318 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
319 #endif
320 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
321 parse_progressbar },
323 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
325 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
326 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
327 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
328 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
330 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
331 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
332 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
333 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
335 #ifdef HAVE_TAGCACHE
336 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
337 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
338 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
339 #endif
341 #if CONFIG_CODEC == SWCODEC
342 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
343 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
344 #endif
346 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
347 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
349 #ifdef HAVE_LCD_BITMAP
350 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
351 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
353 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
355 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
356 parse_image_display },
358 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
359 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
360 #ifdef HAVE_ALBUMART
361 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
362 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
363 #endif
365 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
366 parse_viewport_display },
367 #ifdef HAVE_LCD_BITMAP
368 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
369 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
370 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
371 #endif
372 { WPS_NO_TOKEN, "V", 0, parse_viewport },
374 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
375 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
376 #endif
377 #endif
379 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
380 parse_setting_and_lang },
381 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
382 parse_setting_and_lang },
383 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
385 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
386 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
387 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
390 /* Recording Tokens */
391 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
392 #ifdef HAVE_RECORDING
393 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
394 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
395 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
396 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
397 #endif
398 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
399 /* the array MUST end with an empty string (first char is \0) */
403 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
404 * chains require the order to be kept.
406 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
408 if (*list == NULL)
409 *list = item;
410 else
412 struct skin_token_list *t = *list;
413 while (t->next)
414 t = t->next;
415 t->next = item;
419 /* traverse the image linked-list for an image */
420 #ifdef HAVE_LCD_BITMAP
421 struct gui_img* find_image(char label, struct wps_data *data)
423 struct skin_token_list *list = data->images;
424 while (list)
426 struct gui_img *img = (struct gui_img *)list->token->value.data;
427 if (img->label == label)
428 return img;
429 list = list->next;
431 return NULL;
434 struct skin_font* find_font(int id, struct wps_data *data)
436 struct skin_token_list *list = data->fonts;
437 while (list)
439 struct skin_font *f = (struct skin_font*)list->token->value.data;
440 if (f->id == id)
441 return f;
442 list = list->next;
444 return NULL;
446 #endif
448 /* traverse the viewport linked list for a viewport */
449 struct skin_viewport* find_viewport(char label, struct wps_data *data)
451 struct skin_token_list *list = data->viewports;
452 while (list)
454 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
455 if (vp->label == label)
456 return vp;
457 list = list->next;
459 return NULL;
463 /* create and init a new wpsll item.
464 * passing NULL to token will alloc a new one.
465 * You should only pass NULL for the token when the token type (table above)
466 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
468 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
469 void* token_data)
471 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
472 if (!token)
473 token = skin_buffer_alloc(sizeof(struct wps_token));
474 if (!llitem || !token)
475 return NULL;
476 llitem->next = NULL;
477 llitem->token = token;
478 if (token_data)
479 llitem->token->value.data = token_data;
480 return llitem;
483 /* Returns the number of chars that should be skipped to jump
484 immediately after the first eol, i.e. to the start of the next line */
485 static int skip_end_of_line(const char *wps_bufptr)
487 line_number++;
488 int skip = 0;
489 while(*(wps_bufptr + skip) != '\n')
490 skip++;
491 return ++skip;
494 /* Starts a new subline in the current line during parsing */
495 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
497 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
498 if (!subline)
499 return false;
501 subline->first_token_idx = curr_token;
502 subline->next = NULL;
504 subline->line_type = 0;
505 subline->time_mult = 0;
507 line->curr_subline->last_token_idx = curr_token-1;
508 line->curr_subline->next = subline;
509 line->curr_subline = subline;
510 return true;
513 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
515 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
516 struct skin_subline *subline = NULL;
517 if (!line)
518 return false;
520 /* init the subline */
521 subline = &line->sublines;
522 subline->first_token_idx = curr_token;
523 subline->next = NULL;
524 subline->line_type = 0;
525 subline->time_mult = 0;
527 /* init the new line */
528 line->curr_subline = &line->sublines;
529 line->next = NULL;
530 line->subline_expire_time = 0;
532 /* connect to curr_line and vp pointers.
533 * 1) close the previous lines subline
534 * 2) connect to vp pointer
535 * 3) connect to curr_line global pointer
537 if (curr_line)
539 curr_line->curr_subline->last_token_idx = curr_token - 1;
540 curr_line->next = line;
541 curr_line->curr_subline = NULL;
543 curr_line = line;
544 if (!vp->lines)
545 vp->lines = line;
546 return true;
549 #ifdef HAVE_LCD_BITMAP
551 static int parse_statusbar_enable(const char *wps_bufptr,
552 struct wps_token *token,
553 struct wps_data *wps_data)
555 (void)token; /* Kill warnings */
556 wps_data->wps_sb_tag = true;
557 wps_data->show_sb_on_wps = true;
558 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
559 viewport_set_defaults(&default_vp->vp, curr_screen);
560 return skip_end_of_line(wps_bufptr);
563 static int parse_statusbar_disable(const char *wps_bufptr,
564 struct wps_token *token,
565 struct wps_data *wps_data)
567 (void)token; /* Kill warnings */
568 wps_data->wps_sb_tag = true;
569 wps_data->show_sb_on_wps = false;
570 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
571 viewport_set_fullscreen(&default_vp->vp, curr_screen);
572 return skip_end_of_line(wps_bufptr);
575 static int get_image_id(int c)
577 if(c >= 'a' && c <= 'z')
578 return c - 'a';
579 else if(c >= 'A' && c <= 'Z')
580 return c - 'A' + 26;
581 else
582 return -1;
585 char *get_image_filename(const char *start, const char* bmpdir,
586 char *buf, int buf_size)
588 const char *end = strchr(start, '|');
589 int bmpdirlen = strlen(bmpdir);
591 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
593 buf[0] = '\0';
594 return NULL;
597 strcpy(buf, bmpdir);
598 buf[bmpdirlen] = '/';
599 memcpy( &buf[bmpdirlen + 1], start, end - start);
600 buf[bmpdirlen + 1 + end - start] = 0;
602 return buf;
605 static int parse_image_display(const char *wps_bufptr,
606 struct wps_token *token,
607 struct wps_data *wps_data)
609 char label = wps_bufptr[0];
610 int subimage;
611 struct gui_img *img;;
613 /* sanity check */
614 img = find_image(label, wps_data);
615 if (!img)
617 token->value.i = label; /* so debug works */
618 return WPS_ERROR_INVALID_PARAM;
621 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
623 if (subimage >= img->num_subimages)
624 return WPS_ERROR_INVALID_PARAM;
626 /* Store sub-image number to display in high bits */
627 token->value.i = label | (subimage << 8);
628 return 2; /* We have consumed 2 bytes */
629 } else {
630 token->value.i = label;
631 return 1; /* We have consumed 1 byte */
635 static int parse_image_load(const char *wps_bufptr,
636 struct wps_token *token,
637 struct wps_data *wps_data)
639 const char *ptr = wps_bufptr;
640 const char *pos;
641 const char* filename;
642 const char* id;
643 const char *newline;
644 int x,y;
645 struct gui_img *img;
647 /* format: %x|n|filename.bmp|x|y|
648 or %xl|n|filename.bmp|x|y|
649 or %xl|n|filename.bmp|x|y|num_subimages|
652 if (*ptr != '|')
653 return WPS_ERROR_INVALID_PARAM;
655 ptr++;
657 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
658 return WPS_ERROR_INVALID_PARAM;
660 /* Check there is a terminating | */
661 if (*ptr != '|')
662 return WPS_ERROR_INVALID_PARAM;
664 /* check the image number and load state */
665 if(find_image(*id, wps_data))
667 /* Invalid image ID */
668 return WPS_ERROR_INVALID_PARAM;
670 img = skin_buffer_alloc(sizeof(struct gui_img));
671 if (!img)
672 return WPS_ERROR_INVALID_PARAM;
673 /* save a pointer to the filename */
674 img->bm.data = (char*)filename;
675 img->label = *id;
676 img->x = x;
677 img->y = y;
678 img->num_subimages = 1;
679 img->always_display = false;
681 /* save current viewport */
682 img->vp = &curr_vp->vp;
684 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
686 img->always_display = true;
688 else
690 /* Parse the (optional) number of sub-images */
691 ptr++;
692 newline = strchr(ptr, '\n');
693 pos = strchr(ptr, '|');
694 if (pos && pos < newline)
695 img->num_subimages = atoi(ptr);
697 if (img->num_subimages <= 0)
698 return WPS_ERROR_INVALID_PARAM;
700 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
701 if (!item)
702 return WPS_ERROR_INVALID_PARAM;
703 add_to_ll_chain(&wps_data->images, item);
705 /* Skip the rest of the line */
706 return skip_end_of_line(wps_bufptr);
709 /* this array acts as a simple mapping between the id the user uses for a font
710 * and the id the font actually gets from the font loader.
711 * font id 2 is always the first skin font (regardless of how many screens */
712 static int parse_font_load(const char *wps_bufptr,
713 struct wps_token *token, struct wps_data *wps_data)
715 (void)wps_data; (void)token;
716 const char *ptr = wps_bufptr;
717 int id;
718 char *filename;
719 struct skin_font *font;
721 if (*ptr != '|')
722 return WPS_ERROR_INVALID_PARAM;
724 ptr++;
726 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
727 return WPS_ERROR_INVALID_PARAM;
729 /* Check there is a terminating | */
730 if (*ptr != '|')
731 return WPS_ERROR_INVALID_PARAM;
733 if (id <= FONT_UI || id >= MAXFONTS-1)
734 return WPS_ERROR_INVALID_PARAM;
736 font = skin_buffer_alloc(sizeof(struct skin_font));
737 int len = ptr-filename+1;
738 char* name = skin_buffer_alloc(len);
739 if (!font || !name)
740 return WPS_ERROR_INVALID_PARAM;
742 strlcpy(name, filename, len);
743 font->id = id;
744 font->font_id = -1;
745 font->name = name;
747 struct skin_token_list *item = new_skin_token_list_item(NULL, font);
749 if (!item)
750 return WPS_ERROR_INVALID_PARAM;
751 add_to_ll_chain(&wps_data->fonts, item);
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->pb = NULL;
905 skin_vp->lines = NULL;
906 if (curr_line)
908 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
909 - (wps_data->num_tokens > 0 ? 1 : 0);
912 curr_line = NULL;
913 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
914 return WPS_ERROR_INVALID_PARAM;
917 if (*ptr == 'i')
919 skin_vp->label = VP_INFO_LABEL;
920 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
921 ++ptr;
923 else if (*ptr == 'l')
925 if (*(ptr+1) == '|')
927 char label = *(ptr+2);
928 if (label >= 'a' && label <= 'z')
930 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
931 skin_vp->label = label;
933 else
934 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
935 ptr += 3;
938 if (*ptr != '|')
939 return WPS_ERROR_INVALID_PARAM;
941 ptr++;
942 struct viewport *vp = &skin_vp->vp;
943 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
944 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
945 return WPS_ERROR_INVALID_PARAM;
947 /* Check for trailing | */
948 if (*ptr != '|')
949 return WPS_ERROR_INVALID_PARAM;
951 if (follow_lang_direction && lang_is_rtl())
953 vp->flags |= VP_FLAG_ALIGN_RIGHT;
954 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
956 else
957 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
959 /* increment because font=2 and FONT_UI_REMOTE is ambiguous */
960 if (vp->font > FONT_UI)
961 vp->font++;
962 #ifdef HAVE_REMOTE_LCD
963 if (vp->font == FONT_UI && curr_screen == SCREEN_REMOTE)
964 vp->font = FONT_UI_REMOTE;
965 #endif
967 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
968 if (!list)
969 return WPS_ERROR_INVALID_PARAM;
970 add_to_ll_chain(&wps_data->viewports, list);
971 curr_vp = skin_vp;
972 /* Skip the rest of the line */
973 return skip_end_of_line(wps_bufptr);
976 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
977 static int parse_image_special(const char *wps_bufptr,
978 struct wps_token *token,
979 struct wps_data *wps_data)
981 (void)wps_data; /* kill warning */
982 (void)token;
983 const char *pos = NULL;
984 const char *newline;
985 bool error = false;
987 pos = strchr(wps_bufptr + 1, '|');
988 newline = strchr(wps_bufptr, '\n');
990 error = (pos > newline);
993 #if LCD_DEPTH > 1
994 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
996 /* format: %X|filename.bmp| or %Xd */
997 if (*(wps_bufptr) == 'd')
999 wps_data->backdrop = NULL;
1000 return skip_end_of_line(wps_bufptr);
1002 else if (!error)
1003 wps_data->backdrop = (char*)wps_bufptr + 1;
1005 #endif
1006 if (error)
1007 return WPS_ERROR_INVALID_PARAM;
1008 /* Skip the rest of the line */
1009 return skip_end_of_line(wps_bufptr);
1011 #endif
1013 #endif /* HAVE_LCD_BITMAP */
1015 static int parse_setting_and_lang(const char *wps_bufptr,
1016 struct wps_token *token,
1017 struct wps_data *wps_data)
1019 /* NOTE: both the string validations that happen in here will
1020 * automatically PASS on checkwps because its too hard to get
1021 * settings_list.c and englinsh.lang built for it.
1022 * If that ever changes remove the #ifndef __PCTOOL__'s here
1024 (void)wps_data;
1025 const char *ptr = wps_bufptr;
1026 const char *end;
1027 int i = 0;
1028 char temp[64];
1030 /* Find the setting's cfg_name */
1031 if (*ptr != '|')
1032 return WPS_ERROR_INVALID_PARAM;
1033 ptr++;
1034 end = strchr(ptr,'|');
1035 if (!end)
1036 return WPS_ERROR_INVALID_PARAM;
1037 strlcpy(temp, ptr,end-ptr+1);
1039 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1041 #ifndef __PCTOOL__
1042 i = lang_english_to_id(temp);
1043 if (i < 0)
1044 return WPS_ERROR_INVALID_PARAM;
1045 #endif
1047 else
1049 /* Find the setting */
1050 for (i=0; i<nb_settings; i++)
1051 if (settings[i].cfg_name &&
1052 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1053 /* prevent matches on cfg_name prefixes */
1054 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1055 break;
1056 #ifndef __PCTOOL__
1057 if (i == nb_settings)
1058 return WPS_ERROR_INVALID_PARAM;
1059 #endif
1061 /* Store the setting number */
1062 token->value.i = i;
1064 /* Skip the rest of the line */
1065 return end-ptr+2;
1069 static int parse_dir_level(const char *wps_bufptr,
1070 struct wps_token *token,
1071 struct wps_data *wps_data)
1073 char val[] = { *wps_bufptr, '\0' };
1074 token->value.i = atoi(val);
1075 (void)wps_data; /* Kill warnings */
1076 return 1;
1079 static int parse_timeout(const char *wps_bufptr,
1080 struct wps_token *token,
1081 struct wps_data *wps_data)
1083 int skip = 0;
1084 int val = 0;
1085 bool have_point = false;
1086 bool have_tenth = false;
1088 (void)wps_data; /* Kill the warning */
1090 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1092 if (*wps_bufptr != '.')
1094 val *= 10;
1095 val += *wps_bufptr - '0';
1096 if (have_point)
1098 have_tenth = true;
1099 wps_bufptr++;
1100 skip++;
1101 break;
1104 else
1105 have_point = true;
1107 wps_bufptr++;
1108 skip++;
1111 if (have_tenth == false)
1112 val *= 10;
1114 if (val == 0 && skip == 0)
1116 /* decide what to do if no value was specified */
1117 switch (token->type)
1119 case WPS_TOKEN_SUBLINE_TIMEOUT:
1120 return -1;
1121 case WPS_TOKEN_BUTTON_VOLUME:
1122 val = 10;
1123 break;
1126 token->value.i = val;
1128 return skip;
1131 static int parse_progressbar(const char *wps_bufptr,
1132 struct wps_token *token,
1133 struct wps_data *wps_data)
1135 /* %pb or %pb|filename|x|y|width|height|
1136 using - for any of the params uses "sane" values */
1137 #ifdef HAVE_LCD_BITMAP
1138 enum {
1139 PB_FILENAME = 0,
1140 PB_X,
1141 PB_Y,
1142 PB_WIDTH,
1143 PB_HEIGHT
1145 const char *filename;
1146 int x, y, height, width;
1147 uint32_t set = 0;
1148 const char *ptr = wps_bufptr;
1149 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1150 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1152 if (!pb || !item)
1153 return WPS_ERROR_INVALID_PARAM;
1155 struct viewport *vp = &curr_vp->vp;
1156 #ifndef __PCTOOL__
1157 int font_height = font_get(vp->font)->height;
1158 #else
1159 int font_height = 8;
1160 #endif
1161 /* we need to know what line number (viewport relative) this pb is,
1162 * so count them... */
1163 int line_num = -1;
1164 struct skin_line *line = curr_vp->lines;
1165 while (line)
1167 line_num++;
1168 line = line->next;
1170 pb->have_bitmap_pb = false;
1171 pb->bm.data = NULL; /* no bitmap specified */
1172 pb->follow_lang_direction = follow_lang_direction > 0;
1174 if (*wps_bufptr != '|') /* regular old style */
1176 pb->x = 0;
1177 pb->width = vp->width;
1178 pb->height = SYSFONT_HEIGHT-2;
1179 pb->y = -line_num - 1; /* Will be computed during the rendering */
1181 curr_vp->pb = pb;
1182 add_to_ll_chain(&wps_data->progressbars, item);
1183 return 0;
1185 ptr = wps_bufptr + 1;
1187 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1188 &x, &y, &width, &height)))
1189 return WPS_ERROR_INVALID_PARAM;
1191 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1192 pb->bm.data = (char*)filename;
1194 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1195 pb->x = x;
1196 else
1197 pb->x = vp->x;
1199 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1201 /* A zero width causes a divide-by-zero error later, so reject it */
1202 if (width == 0)
1203 return WPS_ERROR_INVALID_PARAM;
1205 pb->width = width;
1207 else
1208 pb->width = vp->width - pb->x;
1210 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1212 /* A zero height makes no sense - reject it */
1213 if (height == 0)
1214 return WPS_ERROR_INVALID_PARAM;
1216 pb->height = height;
1218 else
1219 pb->height = font_height;
1221 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1222 pb->y = y;
1223 else
1224 pb->y = -line_num - 1; /* Will be computed during the rendering */
1226 curr_vp->pb = pb;
1227 add_to_ll_chain(&wps_data->progressbars, item);
1229 /* Skip the rest of the line */
1230 return skip_end_of_line(wps_bufptr)-1;
1231 #else
1232 (void)token;
1234 if (*(wps_bufptr-1) == 'f')
1235 wps_data->full_line_progressbar = true;
1236 else
1237 wps_data->full_line_progressbar = false;
1239 return 0;
1241 #endif
1244 #ifdef HAVE_ALBUMART
1245 static int parse_int(const char *newline, const char **_pos, int *num)
1247 *_pos = parse_list("d", NULL, '|', *_pos, num);
1249 return (!*_pos || *_pos > newline || **_pos != '|');
1252 static int parse_albumart_load(const char *wps_bufptr,
1253 struct wps_token *token,
1254 struct wps_data *wps_data)
1256 const char *_pos, *newline;
1257 bool parsing;
1258 struct dim dimensions;
1259 int albumart_slot;
1260 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1261 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1262 (void)token; /* silence warning */
1263 if (!aa)
1264 return skip_end_of_line(wps_bufptr);
1266 /* reset albumart info in wps */
1267 aa->width = -1;
1268 aa->height = -1;
1269 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1270 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1271 aa->vp = &curr_vp->vp;
1273 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1275 newline = strchr(wps_bufptr, '\n');
1277 _pos = wps_bufptr;
1279 if (*_pos != '|')
1280 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1282 ++_pos;
1284 /* initial validation and parsing of x component */
1285 if (parse_int(newline, &_pos, &aa->x))
1286 return WPS_ERROR_INVALID_PARAM;
1288 ++_pos;
1290 /* initial validation and parsing of y component */
1291 if (parse_int(newline, &_pos, &aa->y))
1292 return WPS_ERROR_INVALID_PARAM;
1294 /* parsing width field */
1295 parsing = true;
1296 while (parsing)
1298 /* apply each modifier in turn */
1299 ++_pos;
1300 switch (*_pos)
1302 case 'l':
1303 case 'L':
1304 case '+':
1305 if (swap_for_rtl)
1306 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1307 else
1308 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1309 break;
1310 case 'c':
1311 case 'C':
1312 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1313 break;
1314 case 'r':
1315 case 'R':
1316 case '-':
1317 if (swap_for_rtl)
1318 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1319 else
1320 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1321 break;
1322 case 'd':
1323 case 'D':
1324 case 'i':
1325 case 'I':
1326 case 's':
1327 case 'S':
1328 /* simply ignored */
1329 break;
1330 default:
1331 parsing = false;
1332 break;
1335 /* extract max width data */
1336 if (*_pos != '|')
1338 if (parse_int(newline, &_pos, &aa->width))
1339 return WPS_ERROR_INVALID_PARAM;
1342 /* parsing height field */
1343 parsing = true;
1344 while (parsing)
1346 /* apply each modifier in turn */
1347 ++_pos;
1348 switch (*_pos)
1350 case 't':
1351 case 'T':
1352 case '-':
1353 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1354 break;
1355 case 'c':
1356 case 'C':
1357 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1358 break;
1359 case 'b':
1360 case 'B':
1361 case '+':
1362 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1363 break;
1364 case 'd':
1365 case 'D':
1366 case 'i':
1367 case 'I':
1368 case 's':
1369 case 'S':
1370 /* simply ignored */
1371 break;
1372 default:
1373 parsing = false;
1374 break;
1377 /* extract max height data */
1378 if (*_pos != '|')
1380 if (parse_int(newline, &_pos, &aa->height))
1381 return WPS_ERROR_INVALID_PARAM;
1384 /* if we got here, we parsed everything ok .. ! */
1385 if (aa->width < 0)
1386 aa->width = 0;
1387 else if (aa->width > LCD_WIDTH)
1388 aa->width = LCD_WIDTH;
1390 if (aa->height < 0)
1391 aa->height = 0;
1392 else if (aa->height > LCD_HEIGHT)
1393 aa->height = LCD_HEIGHT;
1395 if (swap_for_rtl)
1396 aa->x = LCD_WIDTH - (aa->x + aa->width);
1398 aa->state = WPS_ALBUMART_LOAD;
1399 aa->draw = false;
1400 wps_data->albumart = aa;
1402 dimensions.width = aa->width;
1403 dimensions.height = aa->height;
1405 albumart_slot = playback_claim_aa_slot(&dimensions);
1407 if (0 <= albumart_slot)
1408 wps_data->playback_aa_slot = albumart_slot;
1410 /* Skip the rest of the line */
1411 return skip_end_of_line(wps_bufptr);
1414 static int parse_albumart_display(const char *wps_bufptr,
1415 struct wps_token *token,
1416 struct wps_data *wps_data)
1418 (void)wps_bufptr;
1419 struct wps_token *prev = token-1;
1420 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1422 token->type = WPS_TOKEN_ALBUMART_FOUND;
1424 else if (wps_data->albumart)
1426 wps_data->albumart->vp = &curr_vp->vp;
1428 #if 0
1429 /* the old code did this so keep it here for now...
1430 * this is to allow the posibility to showing the next tracks AA! */
1431 if (wps_bufptr+1 == 'n')
1432 return 1;
1433 #endif
1434 return 0;
1436 #endif /* HAVE_ALBUMART */
1438 #ifdef HAVE_TOUCHSCREEN
1440 struct touchaction {const char* s; int action;};
1441 static const struct touchaction touchactions[] = {
1442 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1443 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1444 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1445 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1446 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1447 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1448 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1449 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1451 static int parse_touchregion(const char *wps_bufptr,
1452 struct wps_token *token, struct wps_data *wps_data)
1454 (void)token;
1455 unsigned i, imax;
1456 struct touchregion *region = NULL;
1457 const char *ptr = wps_bufptr;
1458 const char *action;
1459 const char pb_string[] = "progressbar";
1460 const char vol_string[] = "volume";
1461 int x,y,w,h;
1463 /* format: %T|x|y|width|height|action|
1464 * if action starts with & the area must be held to happen
1465 * action is one of:
1466 * play - play/pause playback
1467 * stop - stop playback, exit the wps
1468 * prev - prev track
1469 * next - next track
1470 * ffwd - seek forward
1471 * rwd - seek backwards
1472 * menu - go back to the main menu
1473 * browse - go back to the file/db browser
1474 * shuffle - toggle shuffle mode
1475 * repmode - cycle the repeat mode
1476 * quickscreen - go into the quickscreen
1477 * contextmenu - open the context menu
1478 * playlist - go into the playlist
1479 * pitch - go into the pitchscreen
1480 * volup - increase volume by one step
1481 * voldown - decrease volume by one step
1485 if (*ptr != '|')
1486 return WPS_ERROR_INVALID_PARAM;
1487 ptr++;
1489 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1490 return WPS_ERROR_INVALID_PARAM;
1492 /* Check there is a terminating | */
1493 if (*ptr != '|')
1494 return WPS_ERROR_INVALID_PARAM;
1496 region = skin_buffer_alloc(sizeof(struct touchregion));
1497 if (!region)
1498 return WPS_ERROR_INVALID_PARAM;
1500 /* should probably do some bounds checking here with the viewport... but later */
1501 region->action = ACTION_NONE;
1502 region->x = x;
1503 region->y = y;
1504 region->width = w;
1505 region->height = h;
1506 region->wvp = curr_vp;
1507 region->armed = false;
1509 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1510 && *(action + sizeof(pb_string)-1) == '|')
1511 region->type = WPS_TOUCHREGION_SCROLLBAR;
1512 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1513 && *(action + sizeof(vol_string)-1) == '|')
1514 region->type = WPS_TOUCHREGION_VOLUME;
1515 else
1517 region->type = WPS_TOUCHREGION_ACTION;
1519 if (*action == '&')
1521 action++;
1522 region->repeat = true;
1524 else
1525 region->repeat = false;
1527 i = 0;
1528 imax = ARRAYLEN(touchactions);
1529 while ((region->action == ACTION_NONE) &&
1530 (i < imax))
1532 /* try to match with one of our touchregion screens */
1533 int len = strlen(touchactions[i].s);
1534 if (!strncmp(touchactions[i].s, action, len)
1535 && *(action+len) == '|')
1536 region->action = touchactions[i].action;
1537 i++;
1539 if (region->action == ACTION_NONE)
1540 return WPS_ERROR_INVALID_PARAM;
1542 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1543 if (!item)
1544 return WPS_ERROR_INVALID_PARAM;
1545 add_to_ll_chain(&wps_data->touchregions, item);
1546 return skip_end_of_line(wps_bufptr);
1548 #endif
1550 /* Parse a generic token from the given string. Return the length read */
1551 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1553 int skip = 0, taglen = 0, ret;
1554 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1555 const struct wps_tag *tag;
1556 memset(token, 0, sizeof(*token));
1558 switch(*wps_bufptr)
1561 case '%':
1562 case '<':
1563 case '|':
1564 case '>':
1565 case ';':
1566 case '#':
1567 /* escaped characters */
1568 token->type = WPS_TOKEN_CHARACTER;
1569 token->value.c = *wps_bufptr;
1570 taglen = 1;
1571 wps_data->num_tokens++;
1572 break;
1574 case '?':
1575 /* conditional tag */
1576 token->type = WPS_TOKEN_CONDITIONAL;
1577 level++;
1578 condindex[level] = wps_data->num_tokens;
1579 numoptions[level] = 1;
1580 wps_data->num_tokens++;
1581 ret = parse_token(wps_bufptr + 1, wps_data);
1582 if (ret < 0) return ret;
1583 taglen = 1 + ret;
1584 break;
1586 default:
1587 /* find what tag we have */
1588 for (tag = all_tags;
1589 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1590 tag++) ;
1592 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1593 token->type = tag->type;
1594 curr_line->curr_subline->line_type |= tag->refresh_type;
1596 /* if the tag has a special parsing function, we call it */
1597 if (tag->parse_func)
1599 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1600 if (ret < 0) return ret;
1601 skip += ret;
1604 /* Some tags we don't want to save as tokens */
1605 if (tag->type == WPS_NO_TOKEN)
1606 break;
1608 /* tags that start with 'F', 'I' or 'D' are for the next file */
1609 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1610 *(tag->name) == 'D')
1611 token->next = true;
1613 wps_data->num_tokens++;
1614 break;
1617 skip += taglen;
1618 return skip;
1623 * Returns the number of bytes to skip the buf pointer to access the false
1624 * branch in a _binary_ conditional
1626 * That is:
1627 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1628 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1630 * depending on the features of a target it's not called from check_feature_tag,
1631 * hence the __attribute__ or it issues compiler warnings
1635 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1636 static int find_false_branch(const char *wps_bufptr)
1638 const char *buf = wps_bufptr;
1639 /* wps_bufptr is after the opening '<', hence level = 1*/
1640 int level = 1;
1641 char ch;
1644 ch = *buf;
1645 if (ch == '%')
1646 { /* filter out the characters we check later if they're printed
1647 * as literals */
1648 ch = *(++buf);
1649 if (ch == '<' || ch == '>' || ch == '|')
1650 continue;
1651 /* else: some tags/printed literals we skip over */
1653 else if (ch == '<') /* nested conditional */
1654 level++;
1655 else if (ch == '>')
1656 { /* closed our or a nested conditional,
1657 * do NOT skip over the '>' so that wps_parse() sees it for closing
1658 * if it is the closing one for our conditional */
1659 level--;
1661 else if (ch == '|' && level == 1)
1662 { /* we found our separator, point before and get out */
1663 break;
1665 /* if level is 0, we don't have a false branch */
1666 } while (level > 0 && *(++buf));
1668 return buf - wps_bufptr;
1672 * returns the number of bytes to get the appropriate branch of a binary
1673 * conditional
1675 * That means:
1676 * - if a feature is available, it returns 0 to not skip anything
1677 * - if the feature is not available, skip to the false branch and don't
1678 * parse the true branch at all
1680 * */
1681 static int check_feature_tag(const char *wps_bufptr, const int type)
1683 (void)wps_bufptr;
1684 switch (type)
1686 case WPS_TOKEN_RTC_PRESENT:
1687 #if CONFIG_RTC
1688 return 0;
1689 #else
1690 return find_false_branch(wps_bufptr);
1691 #endif
1692 case WPS_TOKEN_HAVE_RECORDING:
1693 #ifdef HAVE_RECORDING
1694 return 0;
1695 #else
1696 return find_false_branch(wps_bufptr);
1697 #endif
1698 default: /* not a tag we care about, just don't skip */
1699 return 0;
1704 /* Parses the WPS.
1705 data is the pointer to the structure where the parsed WPS should be stored.
1706 It is initialised.
1707 wps_bufptr points to the string containing the WPS tags */
1708 #define TOKEN_BLOCK_SIZE 128
1709 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1711 if (!data || !wps_bufptr || !*wps_bufptr)
1712 return false;
1713 enum wps_parse_error fail = PARSE_OK;
1714 int ret;
1715 int max_tokens = TOKEN_BLOCK_SIZE;
1716 size_t buf_free = 0;
1717 line_number = 0;
1718 level = -1;
1720 /* allocate enough RAM for a reasonable skin, grow as needed.
1721 * Free any used RAM before loading the images to be 100% RAM efficient */
1722 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1723 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1724 return false;
1725 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1726 data->num_tokens = 0;
1728 #if LCD_DEPTH > 1
1729 /* Backdrop defaults to the setting unless %X is used, so set it now */
1730 if (global_settings.backdrop_file[0])
1732 data->backdrop = "-";
1734 #endif
1736 while (*wps_bufptr && !fail)
1738 if (follow_lang_direction)
1739 follow_lang_direction--;
1740 /* first make sure there is enough room for tokens */
1741 if (max_tokens <= data->num_tokens + 5)
1743 int extra_tokens = TOKEN_BLOCK_SIZE;
1744 size_t needed = extra_tokens * sizeof(struct wps_token);
1745 /* do some smarts here to grow the array a bit */
1746 if (skin_buffer_freespace() < needed)
1748 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1749 break;
1751 skin_buffer_increment(needed, false);
1752 max_tokens += extra_tokens;
1755 switch(*wps_bufptr++)
1758 /* Regular tag */
1759 case '%':
1760 if ((ret = parse_token(wps_bufptr, data)) < 0)
1762 fail = PARSE_FAIL_COND_INVALID_PARAM;
1763 break;
1765 else if (level >= WPS_MAX_COND_LEVEL - 1)
1767 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1768 break;
1770 wps_bufptr += ret;
1771 break;
1773 /* Alternating sublines separator */
1774 case ';':
1775 if (level >= 0) /* there are unclosed conditionals */
1777 fail = PARSE_FAIL_UNCLOSED_COND;
1778 break;
1781 if (!skin_start_new_subline(curr_line, data->num_tokens))
1782 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1784 break;
1786 /* Conditional list start */
1787 case '<':
1788 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1790 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1791 break;
1793 wps_bufptr += check_feature_tag(wps_bufptr,
1794 data->tokens[data->num_tokens-1].type);
1795 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1796 lastcond[level] = data->num_tokens++;
1797 break;
1799 /* Conditional list end */
1800 case '>':
1801 if (level < 0) /* not in a conditional, invalid char */
1803 fail = PARSE_FAIL_INVALID_CHAR;
1804 break;
1807 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1808 if (lastcond[level])
1809 data->tokens[lastcond[level]].value.i = data->num_tokens;
1810 else
1812 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1813 break;
1816 lastcond[level] = 0;
1817 data->num_tokens++;
1818 data->tokens[condindex[level]].value.i = numoptions[level];
1819 level--;
1820 break;
1822 /* Conditional list option */
1823 case '|':
1824 if (level < 0) /* not in a conditional, invalid char */
1826 fail = PARSE_FAIL_INVALID_CHAR;
1827 break;
1830 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1831 if (lastcond[level])
1832 data->tokens[lastcond[level]].value.i = data->num_tokens;
1833 else
1835 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1836 break;
1839 lastcond[level] = data->num_tokens;
1840 numoptions[level]++;
1841 data->num_tokens++;
1842 break;
1844 /* Comment */
1845 case '#':
1846 if (level >= 0) /* there are unclosed conditionals */
1848 fail = PARSE_FAIL_UNCLOSED_COND;
1849 break;
1852 wps_bufptr += skip_end_of_line(wps_bufptr);
1853 break;
1855 /* End of this line */
1856 case '\n':
1857 if (level >= 0) /* there are unclosed conditionals */
1859 fail = PARSE_FAIL_UNCLOSED_COND;
1860 break;
1862 /* add a new token for the \n so empty lines are correct */
1863 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1864 data->tokens[data->num_tokens].value.c = '\n';
1865 data->tokens[data->num_tokens].next = false;
1866 data->num_tokens++;
1868 if (!skin_start_new_line(curr_vp, data->num_tokens))
1870 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1871 break;
1873 line_number++;
1875 break;
1877 /* String */
1878 default:
1880 unsigned int len = 1;
1881 const char *string_start = wps_bufptr - 1;
1883 /* find the length of the string */
1884 while (*wps_bufptr && *wps_bufptr != '#' &&
1885 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1886 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1887 *wps_bufptr != '|' && *wps_bufptr != '\n')
1889 wps_bufptr++;
1890 len++;
1893 /* look if we already have that string */
1894 char *str;
1895 bool found = false;
1896 struct skin_token_list *list = data->strings;
1897 while (list)
1899 str = (char*)list->token->value.data;
1900 found = (strlen(str) == len &&
1901 strncmp(string_start, str, len) == 0);
1902 if (found)
1903 break; /* break here because the list item is
1904 used if its found */
1905 list = list->next;
1907 /* If a matching string is found, found is true and i is
1908 the index of the string. If not, found is false */
1910 if (!found)
1912 /* new string */
1913 str = (char*)skin_buffer_alloc(len+1);
1914 if (!str)
1916 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1917 break;
1919 strlcpy(str, string_start, len+1);
1920 struct skin_token_list *item =
1921 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1922 if(!item)
1924 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1925 break;
1927 add_to_ll_chain(&data->strings, item);
1929 else
1931 /* another occurrence of an existing string */
1932 data->tokens[data->num_tokens].value.data = list->token->value.data;
1934 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1935 data->num_tokens++;
1937 break;
1941 if (!fail && level >= 0) /* there are unclosed conditionals */
1942 fail = PARSE_FAIL_UNCLOSED_COND;
1944 if (*wps_bufptr && !fail)
1945 /* one of the limits of the while loop was exceeded */
1946 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1948 /* Success! */
1949 curr_line->curr_subline->last_token_idx = data->num_tokens;
1950 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1951 /* freeup unused tokens */
1952 skin_buffer_free_from_front(sizeof(struct wps_token)
1953 * (max_tokens - data->num_tokens));
1955 #if defined(DEBUG) || defined(SIMULATOR)
1956 if (debug)
1958 print_debug_info(data, fail, line_number);
1959 debug_skin_usage();
1961 #else
1962 (void)debug;
1963 #endif
1965 return (fail == 0);
1970 * initial setup of wps_data; does reset everything
1971 * except fields which need to survive, i.e.
1974 static void skin_data_reset(struct wps_data *wps_data)
1976 #ifdef HAVE_LCD_BITMAP
1977 wps_data->images = wps_data->progressbars = wps_data->fonts = 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 /* walk though each viewport and assign its font */
2106 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2108 /* first, find the viewports that have a non-sys/ui-font font */
2109 struct skin_viewport *skin_vp =
2110 (struct skin_viewport*)vp_list->token->value.data;
2111 struct viewport *vp = &skin_vp->vp;
2114 if (vp->font < SYSTEMFONTCOUNT)
2115 { /* the usual case -> built-in fonts */
2116 continue;
2119 /* decrement, because font has been incremented in viewport parsing
2120 * due to the FONT_UI_REMOTE ambiguity */
2121 int skin_font_id = vp->font-1;
2123 /* now find the corresponding skin_font */
2124 struct skin_font *font = find_font(skin_font_id, data);
2125 if (!font)
2127 DEBUGF("Could not find font %d\n", skin_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->font_id < 0)
2135 font->font_id = skin_font_load(font->name);
2137 if (font->font_id < 0)
2139 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2140 skin_font_id, font->name);
2141 success = false;
2142 continue;
2145 /* finally, assign the font_id to the viewport */
2146 vp->font = font->font_id;
2148 return success;
2151 #endif /* HAVE_LCD_BITMAP */
2153 /* to setup up the wps-data from a format-buffer (isfile = false)
2154 from a (wps-)file (isfile = true)*/
2155 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2156 const char *buf, bool isfile)
2159 if (!wps_data || !buf)
2160 return false;
2161 #ifdef HAVE_ALBUMART
2162 int status;
2163 struct mp3entry *curtrack;
2164 long offset;
2165 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2166 if (wps_data->albumart)
2168 old_aa.state = wps_data->albumart->state;
2169 old_aa.height = wps_data->albumart->height;
2170 old_aa.width = wps_data->albumart->width;
2172 #endif
2174 skin_data_reset(wps_data);
2175 curr_screen = screen;
2177 /* alloc default viewport, will be fixed up later */
2178 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2179 if (!curr_vp)
2180 return false;
2181 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2182 if (!list)
2183 return false;
2184 add_to_ll_chain(&wps_data->viewports, list);
2187 /* Initialise the first (default) viewport */
2188 curr_vp->label = VP_DEFAULT_LABEL;
2189 curr_vp->pb = NULL;
2190 curr_vp->hidden_flags = 0;
2191 curr_vp->lines = NULL;
2193 viewport_set_defaults(&curr_vp->vp, screen);
2195 curr_line = NULL;
2196 if (!skin_start_new_line(curr_vp, 0))
2197 return false;
2199 if (!isfile)
2201 if (wps_parse(wps_data, buf, false))
2203 #ifdef HAVE_LCD_BITMAP
2204 /* load the backdrop */
2205 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2206 skin_data_reset(wps_data);
2207 return false;
2209 #endif
2210 return true;
2212 return false;
2214 else
2216 int fd = open_utf8(buf, O_RDONLY);
2218 if (fd < 0)
2219 return false;
2221 /* get buffer space from the plugin buffer */
2222 size_t buffersize = 0;
2223 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2225 if (!wps_buffer)
2226 return false;
2228 /* copy the file's content to the buffer for parsing,
2229 ensuring that every line ends with a newline char. */
2230 unsigned int start = 0;
2231 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2233 start += strlen(wps_buffer + start);
2234 if (start < buffersize - 1)
2236 wps_buffer[start++] = '\n';
2237 wps_buffer[start] = 0;
2241 close(fd);
2243 if (start <= 0)
2244 return false;
2246 /* parse the WPS source */
2247 if (!wps_parse(wps_data, wps_buffer, true)) {
2248 skin_data_reset(wps_data);
2249 return false;
2252 wps_data->wps_loaded = true;
2254 #ifdef HAVE_LCD_BITMAP
2255 /* get the bitmap dir */
2256 char bmpdir[MAX_PATH];
2257 char *dot = strrchr(buf, '.');
2259 strlcpy(bmpdir, buf, dot - buf + 1);
2260 /* load the bitmaps that were found by the parsing */
2261 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2262 skin_data_reset(wps_data);
2263 wps_data->wps_loaded = false;
2264 return false;
2266 if (!skin_load_fonts(wps_data))
2268 skin_data_reset(wps_data);
2269 wps_data->wps_loaded = false;
2270 return false;
2272 #endif
2273 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2274 status = audio_status();
2275 if (status & AUDIO_STATUS_PLAY)
2277 struct skin_albumart *aa = wps_data->albumart;
2278 if (aa && ((aa->state && !old_aa.state) ||
2279 (aa->state &&
2280 (((old_aa.height != aa->height) ||
2281 (old_aa.width != aa->width))))))
2283 curtrack = audio_current_track();
2284 offset = curtrack->offset;
2285 audio_stop();
2286 if (!(status & AUDIO_STATUS_PAUSE))
2287 audio_play(offset);
2290 #endif
2291 #if defined(DEBUG) || defined(SIMULATOR)
2292 debug_skin_usage();
2293 #endif
2294 return true;