Make battery level (%bl) work with the bmp bars drawer like %pb and %pv
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob6ea0180534b0a978e0af2c8c0ae51ee24b993d4f
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, parse_progressbar },
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 text++;
805 break;
806 default:
807 for (tag = all_tags;
808 strncmp(text, tag->name, strlen(tag->name)) != 0;
809 tag++) ;
810 /* %s isnt stored as a tag so manually check for it */
811 if (tag->type == WPS_NO_TOKEN)
813 if (!strncmp(tag->name, "s", 1))
815 viewer->lines[line].scroll = true;
816 taglen = 1;
819 else if (tag->type == WPS_TOKEN_UNKNOWN)
821 int i = 0;
822 /* just copy the string */
823 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
824 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
826 viewer->lines[line].strings[cur_string][i] = text[i];
827 i++;
829 viewer->lines[line].strings[cur_string][i] = '\0';
830 cur_string++;
831 taglen = i;
833 else
835 if (tag->parse_func)
837 /* unsupported tag, reject */
838 return -1;
840 taglen = strlen(tag->name);
841 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
843 text += taglen;
846 else
848 /* regular string */
849 int i = 0;
850 /* just copy the string */
851 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
852 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
854 viewer->lines[line].strings[cur_string][i] = text[i];
855 i++;
857 viewer->lines[line].strings[cur_string][i] = '\0';
858 cur_string++;
859 text += i;
862 return text - start;
866 static int parse_playlistview(const char *wps_bufptr,
867 struct wps_token *token, struct wps_data *wps_data)
869 (void)wps_data;
870 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
871 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
872 char *ptr = strchr(wps_bufptr, '|');
873 int length;
874 if (!viewer || !ptr)
875 return WPS_ERROR_INVALID_PARAM;
876 viewer->vp = &curr_vp->vp;
877 viewer->show_icons = true;
878 viewer->start_offset = atoi(ptr+1);
879 token->value.data = (void*)viewer;
880 ptr = strchr(ptr+1, '|');
881 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
882 if (length < 0)
883 return WPS_ERROR_INVALID_PARAM;
884 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
885 if (length < 0)
886 return WPS_ERROR_INVALID_PARAM;
888 return skip_end_of_line(wps_bufptr);
890 #endif
892 static int parse_viewport(const char *wps_bufptr,
893 struct wps_token *token,
894 struct wps_data *wps_data)
896 (void)token; /* Kill warnings */
897 const char *ptr = wps_bufptr;
899 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
901 /* check for the optional letter to signify its a hideable viewport */
902 /* %Vl|<label>|<rest of tags>| */
903 skin_vp->hidden_flags = 0;
904 skin_vp->label = VP_NO_LABEL;
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;
916 if (*ptr == 'i')
918 skin_vp->label = VP_INFO_LABEL;
919 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
920 ++ptr;
922 else if (*ptr == 'l')
924 if (*(ptr+1) == '|')
926 char label = *(ptr+2);
927 if (label >= 'a' && label <= 'z')
929 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
930 skin_vp->label = label;
932 else
933 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
934 ptr += 3;
937 if (*ptr != '|')
938 return WPS_ERROR_INVALID_PARAM;
940 ptr++;
941 struct viewport *vp = &skin_vp->vp;
942 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
943 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
944 return WPS_ERROR_INVALID_PARAM;
946 /* Check for trailing | */
947 if (*ptr != '|')
948 return WPS_ERROR_INVALID_PARAM;
950 if (follow_lang_direction && lang_is_rtl())
952 vp->flags |= VP_FLAG_ALIGN_RIGHT;
953 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
955 else
956 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
958 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
959 if (!list)
960 return WPS_ERROR_INVALID_PARAM;
961 add_to_ll_chain(&wps_data->viewports, list);
962 curr_vp = skin_vp;
963 /* Skip the rest of the line */
964 return skip_end_of_line(wps_bufptr);
967 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
968 static int parse_image_special(const char *wps_bufptr,
969 struct wps_token *token,
970 struct wps_data *wps_data)
972 (void)wps_data; /* kill warning */
973 (void)token;
974 const char *pos = NULL;
975 const char *newline;
976 bool error = false;
978 pos = strchr(wps_bufptr + 1, '|');
979 newline = strchr(wps_bufptr, '\n');
981 error = (pos > newline);
983 #if LCD_DEPTH > 1
984 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
986 /* format: %X|filename.bmp| or %Xd */
987 if (*(wps_bufptr) == 'd')
989 wps_data->backdrop = NULL;
990 return skip_end_of_line(wps_bufptr);
992 else if (!error)
993 wps_data->backdrop = (char*)wps_bufptr + 1;
995 #endif
996 if (error)
997 return WPS_ERROR_INVALID_PARAM;
998 /* Skip the rest of the line */
999 return skip_end_of_line(wps_bufptr);
1001 #endif
1003 #endif /* HAVE_LCD_BITMAP */
1005 static int parse_setting_and_lang(const char *wps_bufptr,
1006 struct wps_token *token,
1007 struct wps_data *wps_data)
1009 /* NOTE: both the string validations that happen in here will
1010 * automatically PASS on checkwps because its too hard to get
1011 * settings_list.c and englinsh.lang built for it.
1012 * If that ever changes remove the #ifndef __PCTOOL__'s here
1014 (void)wps_data;
1015 const char *ptr = wps_bufptr;
1016 const char *end;
1017 int i = 0;
1018 char temp[64];
1020 /* Find the setting's cfg_name */
1021 if (*ptr != '|')
1022 return WPS_ERROR_INVALID_PARAM;
1023 ptr++;
1024 end = strchr(ptr,'|');
1025 if (!end)
1026 return WPS_ERROR_INVALID_PARAM;
1027 strlcpy(temp, ptr,end-ptr+1);
1029 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1031 #ifndef __PCTOOL__
1032 i = lang_english_to_id(temp);
1033 if (i < 0)
1034 return WPS_ERROR_INVALID_PARAM;
1035 #endif
1037 else
1039 /* Find the setting */
1040 for (i=0; i<nb_settings; i++)
1041 if (settings[i].cfg_name &&
1042 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1043 /* prevent matches on cfg_name prefixes */
1044 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1045 break;
1046 #ifndef __PCTOOL__
1047 if (i == nb_settings)
1048 return WPS_ERROR_INVALID_PARAM;
1049 #endif
1051 /* Store the setting number */
1052 token->value.i = i;
1054 /* Skip the rest of the line */
1055 return end-ptr+2;
1059 static int parse_dir_level(const char *wps_bufptr,
1060 struct wps_token *token,
1061 struct wps_data *wps_data)
1063 char val[] = { *wps_bufptr, '\0' };
1064 token->value.i = atoi(val);
1065 (void)wps_data; /* Kill warnings */
1066 return 1;
1069 static int parse_timeout(const char *wps_bufptr,
1070 struct wps_token *token,
1071 struct wps_data *wps_data)
1073 int skip = 0;
1074 int val = 0;
1075 bool have_point = false;
1076 bool have_tenth = false;
1078 (void)wps_data; /* Kill the warning */
1080 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1082 if (*wps_bufptr != '.')
1084 val *= 10;
1085 val += *wps_bufptr - '0';
1086 if (have_point)
1088 have_tenth = true;
1089 wps_bufptr++;
1090 skip++;
1091 break;
1094 else
1095 have_point = true;
1097 wps_bufptr++;
1098 skip++;
1101 if (have_tenth == false)
1102 val *= 10;
1104 if (val == 0 && skip == 0)
1106 /* decide what to do if no value was specified */
1107 switch (token->type)
1109 case WPS_TOKEN_SUBLINE_TIMEOUT:
1110 return -1;
1111 case WPS_TOKEN_BUTTON_VOLUME:
1112 case WPS_TOKEN_TRACK_STARTING:
1113 case WPS_TOKEN_TRACK_ENDING:
1114 val = 10;
1115 break;
1118 token->value.i = val;
1120 return skip;
1123 static int parse_progressbar(const char *wps_bufptr,
1124 struct wps_token *token,
1125 struct wps_data *wps_data)
1127 /* %pb or %pb|filename|x|y|width|height|
1128 using - for any of the params uses "sane" values */
1129 #ifdef HAVE_LCD_BITMAP
1130 enum {
1131 PB_FILENAME = 0,
1132 PB_X,
1133 PB_Y,
1134 PB_WIDTH,
1135 PB_HEIGHT
1137 const char *filename;
1138 int x, y, height, width;
1139 uint32_t set = 0;
1140 const char *ptr = wps_bufptr;
1141 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1142 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1144 if (!pb || !item)
1145 return WPS_ERROR_INVALID_PARAM;
1147 struct viewport *vp = &curr_vp->vp;
1148 /* we need to know what line number (viewport relative) this pb is,
1149 * so count them... */
1150 int line_num = -1;
1151 struct skin_line *line = curr_vp->lines;
1152 while (line)
1154 line_num++;
1155 line = line->next;
1157 pb->vp = vp;
1158 pb->have_bitmap_pb = false;
1159 pb->bm.data = NULL; /* no bitmap specified */
1160 pb->follow_lang_direction = follow_lang_direction > 0;
1161 pb->draw = false;
1163 if (*wps_bufptr != '|') /* regular old style */
1165 pb->x = 0;
1166 pb->width = vp->width;
1167 pb->height = SYSFONT_HEIGHT-2;
1168 pb->y = -line_num - 1; /* Will be computed during the rendering */
1169 if (token->type == WPS_TOKEN_VOLUME || token->type == WPS_TOKEN_BATTERY_PERCENT)
1170 return 0; /* dont add it, let the regular token handling do the work */
1171 add_to_ll_chain(&wps_data->progressbars, item);
1172 return 0;
1174 ptr = wps_bufptr + 1;
1176 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1177 &x, &y, &width, &height)))
1179 /* If we are in a conditional then we probably don't want to fail
1180 * if the above doesnt work. So assume the | is breaking the conditional
1181 * and move on. The next token will fail if this is incorrect. */
1182 if (level >= 0)
1183 return 0;
1184 return WPS_ERROR_INVALID_PARAM;
1187 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1188 pb->bm.data = (char*)filename;
1190 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1191 pb->x = x;
1192 else
1193 pb->x = vp->x;
1195 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1197 /* A zero width causes a divide-by-zero error later, so reject it */
1198 if (width == 0)
1199 return WPS_ERROR_INVALID_PARAM;
1201 pb->width = width;
1203 else
1204 pb->width = vp->width - pb->x;
1206 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1208 /* A zero height makes no sense - reject it */
1209 if (height == 0)
1210 return WPS_ERROR_INVALID_PARAM;
1212 pb->height = height;
1214 else
1216 if (vp->font > FONT_UI)
1217 pb->height = -1; /* calculate at display time */
1218 else
1220 #ifndef __PCTOOL__
1221 pb->height = font_get(vp->font)->height;
1222 #else
1223 pb->height = 8;
1224 #endif
1228 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1229 pb->y = y;
1230 else
1231 pb->y = -line_num - 1; /* Will be computed during the rendering */
1233 add_to_ll_chain(&wps_data->progressbars, item);
1234 if (token->type == WPS_TOKEN_VOLUME)
1235 token->type = WPS_TOKEN_VOLUMEBAR;
1236 else if (token->type == WPS_TOKEN_BATTERY_PERCENT)
1237 token->type = WPS_TOKEN_BATTERY_PERCENTBAR;
1238 pb->type = token->type;
1240 return ptr+1-wps_bufptr;
1241 #else
1242 (void)wps_bufptr;
1243 if (token->type != WPS_TOKEN_VOLUME &&
1244 token->type != WPS_TOKEN_BATTERY_PERCENTBAR)
1246 wps_data->full_line_progressbar =
1247 token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
1249 return 0;
1251 #endif
1254 #ifdef HAVE_ALBUMART
1255 static int parse_int(const char *newline, const char **_pos, int *num)
1257 *_pos = parse_list("d", NULL, '|', *_pos, num);
1259 return (!*_pos || *_pos > newline || **_pos != '|');
1262 static int parse_albumart_load(const char *wps_bufptr,
1263 struct wps_token *token,
1264 struct wps_data *wps_data)
1266 const char *_pos, *newline;
1267 bool parsing;
1268 struct dim dimensions;
1269 int albumart_slot;
1270 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1271 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1272 (void)token; /* silence warning */
1273 if (!aa)
1274 return skip_end_of_line(wps_bufptr);
1276 /* reset albumart info in wps */
1277 aa->width = -1;
1278 aa->height = -1;
1279 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1280 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1281 aa->vp = &curr_vp->vp;
1283 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1285 newline = strchr(wps_bufptr, '\n');
1287 _pos = wps_bufptr;
1289 if (*_pos != '|')
1290 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1292 ++_pos;
1294 /* initial validation and parsing of x component */
1295 if (parse_int(newline, &_pos, &aa->x))
1296 return WPS_ERROR_INVALID_PARAM;
1298 ++_pos;
1300 /* initial validation and parsing of y component */
1301 if (parse_int(newline, &_pos, &aa->y))
1302 return WPS_ERROR_INVALID_PARAM;
1304 /* parsing width field */
1305 parsing = true;
1306 while (parsing)
1308 /* apply each modifier in turn */
1309 ++_pos;
1310 switch (*_pos)
1312 case 'l':
1313 case 'L':
1314 case '+':
1315 if (swap_for_rtl)
1316 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1317 else
1318 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1319 break;
1320 case 'c':
1321 case 'C':
1322 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1323 break;
1324 case 'r':
1325 case 'R':
1326 case '-':
1327 if (swap_for_rtl)
1328 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1329 else
1330 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1331 break;
1332 case 'd':
1333 case 'D':
1334 case 'i':
1335 case 'I':
1336 case 's':
1337 case 'S':
1338 /* simply ignored */
1339 break;
1340 default:
1341 parsing = false;
1342 break;
1345 /* extract max width data */
1346 if (*_pos != '|')
1348 if (parse_int(newline, &_pos, &aa->width))
1349 return WPS_ERROR_INVALID_PARAM;
1352 /* parsing height field */
1353 parsing = true;
1354 while (parsing)
1356 /* apply each modifier in turn */
1357 ++_pos;
1358 switch (*_pos)
1360 case 't':
1361 case 'T':
1362 case '-':
1363 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1364 break;
1365 case 'c':
1366 case 'C':
1367 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1368 break;
1369 case 'b':
1370 case 'B':
1371 case '+':
1372 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1373 break;
1374 case 'd':
1375 case 'D':
1376 case 'i':
1377 case 'I':
1378 case 's':
1379 case 'S':
1380 /* simply ignored */
1381 break;
1382 default:
1383 parsing = false;
1384 break;
1387 /* extract max height data */
1388 if (*_pos != '|')
1390 if (parse_int(newline, &_pos, &aa->height))
1391 return WPS_ERROR_INVALID_PARAM;
1394 /* if we got here, we parsed everything ok .. ! */
1395 if (aa->width < 0)
1396 aa->width = 0;
1397 else if (aa->width > LCD_WIDTH)
1398 aa->width = LCD_WIDTH;
1400 if (aa->height < 0)
1401 aa->height = 0;
1402 else if (aa->height > LCD_HEIGHT)
1403 aa->height = LCD_HEIGHT;
1405 if (swap_for_rtl)
1406 aa->x = LCD_WIDTH - (aa->x + aa->width);
1408 aa->state = WPS_ALBUMART_LOAD;
1409 aa->draw = false;
1410 wps_data->albumart = aa;
1412 dimensions.width = aa->width;
1413 dimensions.height = aa->height;
1415 albumart_slot = playback_claim_aa_slot(&dimensions);
1417 if (0 <= albumart_slot)
1418 wps_data->playback_aa_slot = albumart_slot;
1420 /* Skip the rest of the line */
1421 return skip_end_of_line(wps_bufptr);
1424 static int parse_albumart_display(const char *wps_bufptr,
1425 struct wps_token *token,
1426 struct wps_data *wps_data)
1428 (void)wps_bufptr;
1429 struct wps_token *prev = token-1;
1430 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1432 token->type = WPS_TOKEN_ALBUMART_FOUND;
1434 else if (wps_data->albumart)
1436 wps_data->albumart->vp = &curr_vp->vp;
1438 #if 0
1439 /* the old code did this so keep it here for now...
1440 * this is to allow the posibility to showing the next tracks AA! */
1441 if (wps_bufptr+1 == 'n')
1442 return 1;
1443 #endif
1444 return 0;
1446 #endif /* HAVE_ALBUMART */
1448 #ifdef HAVE_TOUCHSCREEN
1450 struct touchaction {const char* s; int action;};
1451 static const struct touchaction touchactions[] = {
1452 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1453 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1454 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1455 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1456 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1457 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1458 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1459 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1461 static int parse_touchregion(const char *wps_bufptr,
1462 struct wps_token *token, struct wps_data *wps_data)
1464 (void)token;
1465 unsigned i, imax;
1466 struct touchregion *region = NULL;
1467 const char *ptr = wps_bufptr;
1468 const char *action;
1469 const char pb_string[] = "progressbar";
1470 const char vol_string[] = "volume";
1471 int x,y,w,h;
1473 /* format: %T|x|y|width|height|action|
1474 * if action starts with & the area must be held to happen
1475 * action is one of:
1476 * play - play/pause playback
1477 * stop - stop playback, exit the wps
1478 * prev - prev track
1479 * next - next track
1480 * ffwd - seek forward
1481 * rwd - seek backwards
1482 * menu - go back to the main menu
1483 * browse - go back to the file/db browser
1484 * shuffle - toggle shuffle mode
1485 * repmode - cycle the repeat mode
1486 * quickscreen - go into the quickscreen
1487 * contextmenu - open the context menu
1488 * playlist - go into the playlist
1489 * pitch - go into the pitchscreen
1490 * volup - increase volume by one step
1491 * voldown - decrease volume by one step
1495 if (*ptr != '|')
1496 return WPS_ERROR_INVALID_PARAM;
1497 ptr++;
1499 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1500 return WPS_ERROR_INVALID_PARAM;
1502 /* Check there is a terminating | */
1503 if (*ptr != '|')
1504 return WPS_ERROR_INVALID_PARAM;
1506 region = skin_buffer_alloc(sizeof(struct touchregion));
1507 if (!region)
1508 return WPS_ERROR_INVALID_PARAM;
1510 /* should probably do some bounds checking here with the viewport... but later */
1511 region->action = ACTION_NONE;
1512 region->x = x;
1513 region->y = y;
1514 region->width = w;
1515 region->height = h;
1516 region->wvp = curr_vp;
1517 region->armed = false;
1519 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1520 && *(action + sizeof(pb_string)-1) == '|')
1521 region->type = WPS_TOUCHREGION_SCROLLBAR;
1522 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1523 && *(action + sizeof(vol_string)-1) == '|')
1524 region->type = WPS_TOUCHREGION_VOLUME;
1525 else
1527 region->type = WPS_TOUCHREGION_ACTION;
1529 if (*action == '&')
1531 action++;
1532 region->repeat = true;
1534 else
1535 region->repeat = false;
1537 i = 0;
1538 imax = ARRAYLEN(touchactions);
1539 while ((region->action == ACTION_NONE) &&
1540 (i < imax))
1542 /* try to match with one of our touchregion screens */
1543 int len = strlen(touchactions[i].s);
1544 if (!strncmp(touchactions[i].s, action, len)
1545 && *(action+len) == '|')
1546 region->action = touchactions[i].action;
1547 i++;
1549 if (region->action == ACTION_NONE)
1550 return WPS_ERROR_INVALID_PARAM;
1552 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1553 if (!item)
1554 return WPS_ERROR_INVALID_PARAM;
1555 add_to_ll_chain(&wps_data->touchregions, item);
1556 return skip_end_of_line(wps_bufptr);
1558 #endif
1560 /* Parse a generic token from the given string. Return the length read */
1561 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1563 int skip = 0, taglen = 0, ret;
1564 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1565 const struct wps_tag *tag;
1566 memset(token, 0, sizeof(*token));
1568 switch(*wps_bufptr)
1571 case '%':
1572 case '<':
1573 case '|':
1574 case '>':
1575 case ';':
1576 case '#':
1577 /* escaped characters */
1578 token->type = WPS_TOKEN_CHARACTER;
1579 token->value.c = *wps_bufptr;
1580 taglen = 1;
1581 wps_data->num_tokens++;
1582 break;
1584 case '?':
1585 /* conditional tag */
1586 token->type = WPS_TOKEN_CONDITIONAL;
1587 level++;
1588 condindex[level] = wps_data->num_tokens;
1589 numoptions[level] = 1;
1590 wps_data->num_tokens++;
1591 ret = parse_token(wps_bufptr + 1, wps_data);
1592 if (ret < 0) return ret;
1593 taglen = 1 + ret;
1594 break;
1596 default:
1597 /* find what tag we have */
1598 for (tag = all_tags;
1599 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1600 tag++) ;
1602 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1603 token->type = tag->type;
1604 curr_line->curr_subline->line_type |= tag->refresh_type;
1606 /* if the tag has a special parsing function, we call it */
1607 if (tag->parse_func)
1609 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1610 if (ret < 0) return ret;
1611 skip += ret;
1614 /* Some tags we don't want to save as tokens */
1615 if (tag->type == WPS_NO_TOKEN)
1616 break;
1618 /* tags that start with 'F', 'I' or 'D' are for the next file */
1619 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1620 *(tag->name) == 'D')
1621 token->next = true;
1623 wps_data->num_tokens++;
1624 break;
1627 skip += taglen;
1628 return skip;
1633 * Returns the number of bytes to skip the buf pointer to access the false
1634 * branch in a _binary_ conditional
1636 * That is:
1637 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1638 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1640 * depending on the features of a target it's not called from check_feature_tag,
1641 * hence the __attribute__ or it issues compiler warnings
1645 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1646 static int find_false_branch(const char *wps_bufptr)
1648 const char *buf = wps_bufptr;
1649 /* wps_bufptr is after the opening '<', hence level = 1*/
1650 int level = 1;
1651 char ch;
1654 ch = *buf;
1655 if (ch == '%')
1656 { /* filter out the characters we check later if they're printed
1657 * as literals */
1658 ch = *(++buf);
1659 if (ch == '<' || ch == '>' || ch == '|')
1660 continue;
1661 /* else: some tags/printed literals we skip over */
1663 else if (ch == '<') /* nested conditional */
1664 level++;
1665 else if (ch == '>')
1666 { /* closed our or a nested conditional,
1667 * do NOT skip over the '>' so that wps_parse() sees it for closing
1668 * if it is the closing one for our conditional */
1669 level--;
1671 else if (ch == '|' && level == 1)
1672 { /* we found our separator, point before and get out */
1673 break;
1675 /* if level is 0, we don't have a false branch */
1676 } while (level > 0 && *(++buf));
1678 return buf - wps_bufptr;
1682 * returns the number of bytes to get the appropriate branch of a binary
1683 * conditional
1685 * That means:
1686 * - if a feature is available, it returns 0 to not skip anything
1687 * - if the feature is not available, skip to the false branch and don't
1688 * parse the true branch at all
1690 * */
1691 static int check_feature_tag(const char *wps_bufptr, const int type)
1693 (void)wps_bufptr;
1694 switch (type)
1696 case WPS_TOKEN_RTC_PRESENT:
1697 #if CONFIG_RTC
1698 return 0;
1699 #else
1700 return find_false_branch(wps_bufptr);
1701 #endif
1702 case WPS_TOKEN_HAVE_RECORDING:
1703 #ifdef HAVE_RECORDING
1704 return 0;
1705 #else
1706 return find_false_branch(wps_bufptr);
1707 #endif
1708 default: /* not a tag we care about, just don't skip */
1709 return 0;
1714 /* Parses the WPS.
1715 data is the pointer to the structure where the parsed WPS should be stored.
1716 It is initialised.
1717 wps_bufptr points to the string containing the WPS tags */
1718 #define TOKEN_BLOCK_SIZE 128
1719 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1721 if (!data || !wps_bufptr || !*wps_bufptr)
1722 return false;
1723 enum wps_parse_error fail = PARSE_OK;
1724 int ret;
1725 int max_tokens = TOKEN_BLOCK_SIZE;
1726 size_t buf_free = 0;
1727 line_number = 0;
1728 level = -1;
1730 /* allocate enough RAM for a reasonable skin, grow as needed.
1731 * Free any used RAM before loading the images to be 100% RAM efficient */
1732 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1733 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1734 return false;
1735 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1736 data->num_tokens = 0;
1738 #if LCD_DEPTH > 1
1739 /* Backdrop defaults to the setting unless %X is used, so set it now */
1740 if (global_settings.backdrop_file[0])
1742 data->backdrop = "-";
1744 #endif
1746 while (*wps_bufptr && !fail)
1748 if (follow_lang_direction)
1749 follow_lang_direction--;
1750 /* first make sure there is enough room for tokens */
1751 if (max_tokens <= data->num_tokens + 5)
1753 int extra_tokens = TOKEN_BLOCK_SIZE;
1754 size_t needed = extra_tokens * sizeof(struct wps_token);
1755 /* do some smarts here to grow the array a bit */
1756 if (skin_buffer_freespace() < needed)
1758 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1759 break;
1761 skin_buffer_increment(needed, false);
1762 max_tokens += extra_tokens;
1765 switch(*wps_bufptr++)
1768 /* Regular tag */
1769 case '%':
1770 if ((ret = parse_token(wps_bufptr, data)) < 0)
1772 fail = PARSE_FAIL_COND_INVALID_PARAM;
1773 break;
1775 else if (level >= WPS_MAX_COND_LEVEL - 1)
1777 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1778 break;
1780 wps_bufptr += ret;
1781 break;
1783 /* Alternating sublines separator */
1784 case ';':
1785 if (level >= 0) /* there are unclosed conditionals */
1787 fail = PARSE_FAIL_UNCLOSED_COND;
1788 break;
1791 if (!skin_start_new_subline(curr_line, data->num_tokens))
1792 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1794 break;
1796 /* Conditional list start */
1797 case '<':
1798 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1800 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1801 break;
1803 wps_bufptr += check_feature_tag(wps_bufptr,
1804 data->tokens[data->num_tokens-1].type);
1805 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1806 lastcond[level] = data->num_tokens++;
1807 break;
1809 /* Conditional list end */
1810 case '>':
1811 if (level < 0) /* not in a conditional, invalid char */
1813 fail = PARSE_FAIL_INVALID_CHAR;
1814 break;
1817 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1818 if (lastcond[level])
1819 data->tokens[lastcond[level]].value.i = data->num_tokens;
1820 else
1822 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1823 break;
1826 lastcond[level] = 0;
1827 data->num_tokens++;
1828 data->tokens[condindex[level]].value.i = numoptions[level];
1829 level--;
1830 break;
1832 /* Conditional list option */
1833 case '|':
1834 if (level < 0) /* not in a conditional, invalid char */
1836 fail = PARSE_FAIL_INVALID_CHAR;
1837 break;
1840 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1841 if (lastcond[level])
1842 data->tokens[lastcond[level]].value.i = data->num_tokens;
1843 else
1845 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1846 break;
1849 lastcond[level] = data->num_tokens;
1850 numoptions[level]++;
1851 data->num_tokens++;
1852 break;
1854 /* Comment */
1855 case '#':
1856 if (level >= 0) /* there are unclosed conditionals */
1858 fail = PARSE_FAIL_UNCLOSED_COND;
1859 break;
1862 wps_bufptr += skip_end_of_line(wps_bufptr);
1863 break;
1865 /* End of this line */
1866 case '\n':
1867 if (level >= 0) /* there are unclosed conditionals */
1869 fail = PARSE_FAIL_UNCLOSED_COND;
1870 break;
1872 /* add a new token for the \n so empty lines are correct */
1873 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1874 data->tokens[data->num_tokens].value.c = '\n';
1875 data->tokens[data->num_tokens].next = false;
1876 data->num_tokens++;
1878 if (!skin_start_new_line(curr_vp, data->num_tokens))
1880 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1881 break;
1883 line_number++;
1885 break;
1887 /* String */
1888 default:
1890 unsigned int len = 1;
1891 const char *string_start = wps_bufptr - 1;
1893 /* find the length of the string */
1894 while (*wps_bufptr && *wps_bufptr != '#' &&
1895 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1896 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1897 *wps_bufptr != '|' && *wps_bufptr != '\n')
1899 wps_bufptr++;
1900 len++;
1903 /* look if we already have that string */
1904 char *str;
1905 bool found = false;
1906 struct skin_token_list *list = data->strings;
1907 while (list)
1909 str = (char*)list->token->value.data;
1910 found = (strlen(str) == len &&
1911 strncmp(string_start, str, len) == 0);
1912 if (found)
1913 break; /* break here because the list item is
1914 used if its found */
1915 list = list->next;
1917 /* If a matching string is found, found is true and i is
1918 the index of the string. If not, found is false */
1920 if (!found)
1922 /* new string */
1923 str = (char*)skin_buffer_alloc(len+1);
1924 if (!str)
1926 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1927 break;
1929 strlcpy(str, string_start, len+1);
1930 struct skin_token_list *item =
1931 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1932 if(!item)
1934 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1935 break;
1937 add_to_ll_chain(&data->strings, item);
1939 else
1941 /* another occurrence of an existing string */
1942 data->tokens[data->num_tokens].value.data = list->token->value.data;
1944 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1945 data->num_tokens++;
1947 break;
1951 if (!fail && level >= 0) /* there are unclosed conditionals */
1952 fail = PARSE_FAIL_UNCLOSED_COND;
1954 if (*wps_bufptr && !fail)
1955 /* one of the limits of the while loop was exceeded */
1956 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1958 /* Success! */
1959 curr_line->curr_subline->last_token_idx = data->num_tokens;
1960 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1961 /* freeup unused tokens */
1962 skin_buffer_free_from_front(sizeof(struct wps_token)
1963 * (max_tokens - data->num_tokens));
1965 #if defined(DEBUG) || defined(SIMULATOR)
1966 if (debug)
1968 print_debug_info(data, fail, line_number);
1969 debug_skin_usage();
1971 #else
1972 (void)debug;
1973 #endif
1975 return (fail == 0);
1980 * initial setup of wps_data; does reset everything
1981 * except fields which need to survive, i.e.
1984 static void skin_data_reset(struct wps_data *wps_data)
1986 #ifdef HAVE_LCD_BITMAP
1987 wps_data->images = NULL;
1988 wps_data->progressbars = NULL;
1989 #endif
1990 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1991 wps_data->backdrop = NULL;
1992 #endif
1993 #ifdef HAVE_TOUCHSCREEN
1994 wps_data->touchregions = NULL;
1995 #endif
1996 wps_data->viewports = NULL;
1997 wps_data->strings = NULL;
1998 #ifdef HAVE_ALBUMART
1999 wps_data->albumart = NULL;
2000 if (wps_data->playback_aa_slot >= 0)
2002 playback_release_aa_slot(wps_data->playback_aa_slot);
2003 wps_data->playback_aa_slot = -1;
2005 #endif
2006 wps_data->tokens = NULL;
2007 wps_data->num_tokens = 0;
2009 #ifdef HAVE_LCD_BITMAP
2010 wps_data->peak_meter_enabled = false;
2011 wps_data->wps_sb_tag = false;
2012 wps_data->show_sb_on_wps = false;
2013 #else /* HAVE_LCD_CHARCELLS */
2014 /* progress bars */
2015 int i;
2016 for (i = 0; i < 8; i++)
2018 wps_data->wps_progress_pat[i] = 0;
2020 wps_data->full_line_progressbar = false;
2021 #endif
2022 wps_data->wps_loaded = false;
2025 #ifdef HAVE_LCD_BITMAP
2026 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2028 (void)wps_data; /* only needed for remote targets */
2029 char img_path[MAX_PATH];
2030 get_image_filename(bitmap->data, bmpdir,
2031 img_path, sizeof(img_path));
2033 /* load the image */
2034 int format;
2035 #ifdef HAVE_REMOTE_LCD
2036 if (curr_screen == SCREEN_REMOTE)
2037 format = FORMAT_ANY|FORMAT_REMOTE;
2038 else
2039 #endif
2040 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2042 size_t max_buf;
2043 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2044 bitmap->data = imgbuf;
2045 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2047 if (ret > 0)
2049 skin_buffer_increment(ret, true);
2050 return true;
2052 else
2054 /* Abort if we can't load an image */
2055 DEBUGF("Couldn't load '%s'\n", img_path);
2056 return false;
2060 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2062 struct skin_token_list *list;
2063 bool retval = true; /* return false if a single image failed to load */
2064 /* do the progressbars */
2065 list = wps_data->progressbars;
2066 while (list)
2068 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2069 if (pb->bm.data)
2071 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2072 if (!pb->have_bitmap_pb) /* no success */
2073 retval = false;
2075 list = list->next;
2077 /* regular images */
2078 list = wps_data->images;
2079 while (list)
2081 struct gui_img *img = (struct gui_img*)list->token->value.data;
2082 if (img->bm.data)
2084 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2085 if (img->loaded)
2086 img->subimage_height = img->bm.height / img->num_subimages;
2087 else
2088 retval = false;
2090 list = list->next;
2093 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2094 /* Backdrop load scheme:
2095 * 1) %X|filename|
2096 * 2) load the backdrop from settings
2098 if (wps_data->backdrop)
2100 bool needed = wps_data->backdrop[0] != '-';
2101 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2102 bmpdir, curr_screen);
2103 if (!wps_data->backdrop && needed)
2104 retval = false;
2106 #endif /* has backdrop support */
2108 return retval;
2111 static bool skin_load_fonts(struct wps_data *data)
2113 /* don't spit out after the first failue to aid debugging */
2114 bool success = true;
2115 struct skin_token_list *vp_list;
2116 int font_id;
2117 /* walk though each viewport and assign its font */
2118 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2120 /* first, find the viewports that have a non-sys/ui-font font */
2121 struct skin_viewport *skin_vp =
2122 (struct skin_viewport*)vp_list->token->value.data;
2123 struct viewport *vp = &skin_vp->vp;
2126 if (vp->font <= FONT_UI)
2127 { /* the usual case -> built-in fonts */
2128 #ifdef HAVE_REMOTE_LCD
2129 if (vp->font == FONT_UI)
2130 vp->font += curr_screen;
2131 #endif
2132 continue;
2134 font_id = vp->font;
2136 /* now find the corresponding skin_font */
2137 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2138 if (!font->name)
2140 DEBUGF("font %d not specified\n", font_id);
2141 success = false;
2142 continue;
2145 /* load the font - will handle loading the same font again if
2146 * multiple viewports use the same */
2147 if (font->id < 0)
2149 char *dot = strchr(font->name, '.');
2150 *dot = '\0';
2151 font->id = skin_font_load(font->name);
2154 if (font->id < 0)
2156 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2157 font_id, font->name);
2158 success = false;
2159 continue;
2162 /* finally, assign the font_id to the viewport */
2163 vp->font = font->id;
2165 return success;
2168 #endif /* HAVE_LCD_BITMAP */
2170 /* to setup up the wps-data from a format-buffer (isfile = false)
2171 from a (wps-)file (isfile = true)*/
2172 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2173 const char *buf, bool isfile)
2175 char *wps_buffer = NULL;
2176 if (!wps_data || !buf)
2177 return false;
2178 #ifdef HAVE_ALBUMART
2179 int status;
2180 struct mp3entry *curtrack;
2181 long offset;
2182 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2183 if (wps_data->albumart)
2185 old_aa.state = wps_data->albumart->state;
2186 old_aa.height = wps_data->albumart->height;
2187 old_aa.width = wps_data->albumart->width;
2189 #endif
2190 #ifdef HAVE_LCD_BITMAP
2191 int i;
2192 for (i=0;i<MAXUSERFONTS;i++)
2194 skinfonts[i].id = -1;
2195 skinfonts[i].name = NULL;
2197 #endif
2198 #ifdef DEBUG_SKIN_ENGINE
2199 if (isfile && debug_wps)
2201 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2203 #endif
2205 skin_data_reset(wps_data);
2206 wps_data->wps_loaded = false;
2207 curr_screen = screen;
2209 /* alloc default viewport, will be fixed up later */
2210 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2211 if (!curr_vp)
2212 return false;
2213 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2214 if (!list)
2215 return false;
2216 add_to_ll_chain(&wps_data->viewports, list);
2219 /* Initialise the first (default) viewport */
2220 curr_vp->label = VP_DEFAULT_LABEL;
2221 curr_vp->hidden_flags = 0;
2222 curr_vp->lines = NULL;
2224 viewport_set_defaults(&curr_vp->vp, screen);
2225 #ifdef HAVE_LCD_BITMAP
2226 curr_vp->vp.font = FONT_UI;
2227 #endif
2229 curr_line = NULL;
2230 if (!skin_start_new_line(curr_vp, 0))
2231 return false;
2233 if (isfile)
2235 int fd = open_utf8(buf, O_RDONLY);
2237 if (fd < 0)
2238 return false;
2240 /* get buffer space from the plugin buffer */
2241 size_t buffersize = 0;
2242 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2244 if (!wps_buffer)
2245 return false;
2247 /* copy the file's content to the buffer for parsing,
2248 ensuring that every line ends with a newline char. */
2249 unsigned int start = 0;
2250 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2252 start += strlen(wps_buffer + start);
2253 if (start < buffersize - 1)
2255 wps_buffer[start++] = '\n';
2256 wps_buffer[start] = 0;
2259 close(fd);
2260 if (start <= 0)
2261 return false;
2263 else
2265 wps_buffer = (char*)buf;
2267 /* parse the WPS source */
2268 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2269 skin_data_reset(wps_data);
2270 return false;
2273 #ifdef HAVE_LCD_BITMAP
2274 char bmpdir[MAX_PATH];
2275 if (isfile)
2277 /* get the bitmap dir */
2278 char *dot = strrchr(buf, '.');
2279 strlcpy(bmpdir, buf, dot - buf + 1);
2281 else
2283 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2285 /* load the bitmaps that were found by the parsing */
2286 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2287 !skin_load_fonts(wps_data))
2289 skin_data_reset(wps_data);
2290 return false;
2292 #endif
2293 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2294 status = audio_status();
2295 if (status & AUDIO_STATUS_PLAY)
2297 struct skin_albumart *aa = wps_data->albumart;
2298 if (aa && ((aa->state && !old_aa.state) ||
2299 (aa->state &&
2300 (((old_aa.height != aa->height) ||
2301 (old_aa.width != aa->width))))))
2303 curtrack = audio_current_track();
2304 offset = curtrack->offset;
2305 audio_stop();
2306 if (!(status & AUDIO_STATUS_PAUSE))
2307 audio_play(offset);
2310 #endif
2311 wps_data->wps_loaded = true;
2312 #ifdef DEBUG_SKIN_ENGINE
2313 if (isfile && debug_wps)
2314 debug_skin_usage();
2315 #endif
2316 return true;