Update current usage for Fuzev1/e200v2/Clipv1
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob78ec26eaf91d9bbd892463ec0a9d8cae6c10f4b0
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 { WPS_TOKEN_UIVIEWPORT_ENABLE, "VI", WPS_REFRESH_STATIC,
374 parse_viewport_display },
375 #ifdef HAVE_LCD_BITMAP
376 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
377 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
378 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
379 #endif
380 { WPS_NO_TOKEN, "V", 0, parse_viewport },
382 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
383 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
384 #endif
385 #endif
387 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
388 parse_setting_and_lang },
389 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
390 parse_setting_and_lang },
391 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
393 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
394 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
395 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
398 /* Recording Tokens */
399 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
400 #ifdef HAVE_RECORDING
401 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
402 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
403 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
404 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
405 #endif
406 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
407 /* the array MUST end with an empty string (first char is \0) */
411 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
412 * chains require the order to be kept.
414 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
416 if (*list == NULL)
417 *list = item;
418 else
420 struct skin_token_list *t = *list;
421 while (t->next)
422 t = t->next;
423 t->next = item;
427 /* traverse the image linked-list for an image */
428 #ifdef HAVE_LCD_BITMAP
429 struct gui_img* find_image(char label, struct wps_data *data)
431 struct skin_token_list *list = data->images;
432 while (list)
434 struct gui_img *img = (struct gui_img *)list->token->value.data;
435 if (img->label == label)
436 return img;
437 list = list->next;
439 return NULL;
442 #endif
444 /* traverse the viewport linked list for a viewport */
445 struct skin_viewport* find_viewport(char label, struct wps_data *data)
447 struct skin_token_list *list = data->viewports;
448 while (list)
450 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
451 if (vp->label == label)
452 return vp;
453 list = list->next;
455 return NULL;
459 /* create and init a new wpsll item.
460 * passing NULL to token will alloc a new one.
461 * You should only pass NULL for the token when the token type (table above)
462 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
464 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
465 void* token_data)
467 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
468 if (!token)
469 token = skin_buffer_alloc(sizeof(struct wps_token));
470 if (!llitem || !token)
471 return NULL;
472 llitem->next = NULL;
473 llitem->token = token;
474 if (token_data)
475 llitem->token->value.data = token_data;
476 return llitem;
479 /* Returns the number of chars that should be skipped to jump
480 immediately after the first eol, i.e. to the start of the next line */
481 static int skip_end_of_line(const char *wps_bufptr)
483 line_number++;
484 int skip = 0;
485 while(*(wps_bufptr + skip) != '\n')
486 skip++;
487 return ++skip;
490 /* Starts a new subline in the current line during parsing */
491 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
493 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
494 if (!subline)
495 return false;
497 subline->first_token_idx = curr_token;
498 subline->next = NULL;
500 subline->line_type = 0;
501 subline->time_mult = 0;
503 line->curr_subline->last_token_idx = curr_token-1;
504 line->curr_subline->next = subline;
505 line->curr_subline = subline;
506 return true;
509 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
511 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
512 struct skin_subline *subline = NULL;
513 if (!line)
514 return false;
516 /* init the subline */
517 subline = &line->sublines;
518 subline->first_token_idx = curr_token;
519 subline->next = NULL;
520 subline->line_type = 0;
521 subline->time_mult = 0;
523 /* init the new line */
524 line->curr_subline = &line->sublines;
525 line->next = NULL;
526 line->subline_expire_time = 0;
528 /* connect to curr_line and vp pointers.
529 * 1) close the previous lines subline
530 * 2) connect to vp pointer
531 * 3) connect to curr_line global pointer
533 if (curr_line)
535 curr_line->curr_subline->last_token_idx = curr_token - 1;
536 curr_line->next = line;
537 curr_line->curr_subline = NULL;
539 curr_line = line;
540 if (!vp->lines)
541 vp->lines = line;
542 return true;
545 #ifdef HAVE_LCD_BITMAP
547 static int parse_statusbar_enable(const char *wps_bufptr,
548 struct wps_token *token,
549 struct wps_data *wps_data)
551 (void)token; /* Kill warnings */
552 wps_data->wps_sb_tag = true;
553 wps_data->show_sb_on_wps = true;
554 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
555 viewport_set_defaults(&default_vp->vp, curr_screen);
556 default_vp->vp.font = FONT_UI;
557 return skip_end_of_line(wps_bufptr);
560 static int parse_statusbar_disable(const char *wps_bufptr,
561 struct wps_token *token,
562 struct wps_data *wps_data)
564 (void)token; /* Kill warnings */
565 wps_data->wps_sb_tag = true;
566 wps_data->show_sb_on_wps = false;
567 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
568 viewport_set_fullscreen(&default_vp->vp, curr_screen);
569 default_vp->vp.font = FONT_UI;
570 return skip_end_of_line(wps_bufptr);
573 static int parse_statusbar_inbuilt(const char *wps_bufptr,
574 struct wps_token *token, struct wps_data *wps_data)
576 (void)wps_data;
577 token->value.data = (void*)&curr_vp->vp;
578 return skip_end_of_line(wps_bufptr);
581 static int get_image_id(int c)
583 if(c >= 'a' && c <= 'z')
584 return c - 'a';
585 else if(c >= 'A' && c <= 'Z')
586 return c - 'A' + 26;
587 else
588 return -1;
591 char *get_image_filename(const char *start, const char* bmpdir,
592 char *buf, int buf_size)
594 const char *end = strchr(start, '|');
595 int bmpdirlen = strlen(bmpdir);
597 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
599 buf[0] = '\0';
600 return NULL;
603 strcpy(buf, bmpdir);
604 buf[bmpdirlen] = '/';
605 memcpy( &buf[bmpdirlen + 1], start, end - start);
606 buf[bmpdirlen + 1 + end - start] = 0;
608 return buf;
611 static int parse_image_display(const char *wps_bufptr,
612 struct wps_token *token,
613 struct wps_data *wps_data)
615 char label = wps_bufptr[0];
616 int subimage;
617 struct gui_img *img;;
619 /* sanity check */
620 img = find_image(label, wps_data);
621 if (!img)
623 token->value.i = label; /* so debug works */
624 return WPS_ERROR_INVALID_PARAM;
627 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
629 if (subimage >= img->num_subimages)
630 return WPS_ERROR_INVALID_PARAM;
632 /* Store sub-image number to display in high bits */
633 token->value.i = label | (subimage << 8);
634 return 2; /* We have consumed 2 bytes */
635 } else {
636 token->value.i = label;
637 return 1; /* We have consumed 1 byte */
641 static int parse_image_load(const char *wps_bufptr,
642 struct wps_token *token,
643 struct wps_data *wps_data)
645 const char *ptr = wps_bufptr;
646 const char *pos;
647 const char* filename;
648 const char* id;
649 const char *newline;
650 int x,y;
651 struct gui_img *img;
653 /* format: %x|n|filename.bmp|x|y|
654 or %xl|n|filename.bmp|x|y|
655 or %xl|n|filename.bmp|x|y|num_subimages|
658 if (*ptr != '|')
659 return WPS_ERROR_INVALID_PARAM;
661 ptr++;
663 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
664 return WPS_ERROR_INVALID_PARAM;
666 /* Check there is a terminating | */
667 if (*ptr != '|')
668 return WPS_ERROR_INVALID_PARAM;
670 /* check the image number and load state */
671 if(find_image(*id, wps_data))
673 /* Invalid image ID */
674 return WPS_ERROR_INVALID_PARAM;
676 img = skin_buffer_alloc(sizeof(struct gui_img));
677 if (!img)
678 return WPS_ERROR_INVALID_PARAM;
679 /* save a pointer to the filename */
680 img->bm.data = (char*)filename;
681 img->label = *id;
682 img->x = x;
683 img->y = y;
684 img->num_subimages = 1;
685 img->always_display = false;
687 /* save current viewport */
688 img->vp = &curr_vp->vp;
690 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
692 img->always_display = true;
694 else
696 /* Parse the (optional) number of sub-images */
697 ptr++;
698 newline = strchr(ptr, '\n');
699 pos = strchr(ptr, '|');
700 if (pos && pos < newline)
701 img->num_subimages = atoi(ptr);
703 if (img->num_subimages <= 0)
704 return WPS_ERROR_INVALID_PARAM;
706 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
707 if (!item)
708 return WPS_ERROR_INVALID_PARAM;
709 add_to_ll_chain(&wps_data->images, item);
711 /* Skip the rest of the line */
712 return skip_end_of_line(wps_bufptr);
714 struct skin_font {
715 int id; /* the id from font_load */
716 char *name; /* filename without path and extension */
718 static struct skin_font skinfonts[MAXUSERFONTS];
719 static int parse_font_load(const char *wps_bufptr,
720 struct wps_token *token, struct wps_data *wps_data)
722 (void)wps_data; (void)token;
723 const char *ptr = wps_bufptr;
724 int id;
725 char *filename;
727 if (*ptr != '|')
728 return WPS_ERROR_INVALID_PARAM;
730 ptr++;
732 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
733 return WPS_ERROR_INVALID_PARAM;
735 /* Check there is a terminating | */
736 if (*ptr != '|')
737 return WPS_ERROR_INVALID_PARAM;
739 if (id <= FONT_UI || id >= MAXFONTS-1)
740 return WPS_ERROR_INVALID_PARAM;
741 #if defined(DEBUG) || defined(SIMULATOR)
742 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
744 DEBUGF("font id %d already being used\n", id);
746 #endif
747 /* make sure the filename contains .fnt,
748 * we dont actually use it, but require it anyway */
749 ptr = strchr(filename, '.');
750 if (!ptr || strncmp(ptr, ".fnt|", 5))
751 return WPS_ERROR_INVALID_PARAM;
752 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
753 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
755 return skip_end_of_line(wps_bufptr);
759 static int parse_viewport_display(const char *wps_bufptr,
760 struct wps_token *token,
761 struct wps_data *wps_data)
763 (void)wps_data;
764 char letter = wps_bufptr[0];
766 if (letter < 'a' || letter > 'z')
768 /* invalid viewport tag */
769 return WPS_ERROR_INVALID_PARAM;
771 token->value.i = letter;
772 return 1;
775 #ifdef HAVE_LCD_BITMAP
776 static int parse_playlistview_text(struct playlistviewer *viewer,
777 enum info_line_type line, char* text)
779 int cur_string = 0;
780 const struct wps_tag *tag;
781 int taglen = 0;
782 const char *start = text;
783 if (*text != '|')
784 return -1;
785 text++;
786 viewer->lines[line].count = 0;
787 viewer->lines[line].scroll = false;
788 while (*text != '|')
790 if (*text == '%') /* it is a token of some type */
792 text++;
793 taglen = 0;
794 switch(*text)
796 case '%':
797 case '<':
798 case '|':
799 case '>':
800 case ';':
801 case '#':
802 /* escaped characters */
803 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
804 viewer->lines[line].strings[cur_string][0] = *text;
805 viewer->lines[line].strings[cur_string++][1] = '\0';
806 text++;
807 break;
808 default:
809 for (tag = all_tags;
810 strncmp(text, tag->name, strlen(tag->name)) != 0;
811 tag++) ;
812 /* %s isnt stored as a tag so manually check for it */
813 if (tag->type == WPS_NO_TOKEN)
815 if (!strncmp(tag->name, "s", 1))
817 viewer->lines[line].scroll = true;
818 taglen = 1;
821 else if (tag->type == WPS_TOKEN_UNKNOWN)
823 int i = 0;
824 /* just copy the string */
825 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
826 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
828 viewer->lines[line].strings[cur_string][i] = text[i];
829 i++;
831 viewer->lines[line].strings[cur_string][i] = '\0';
832 cur_string++;
833 taglen = i;
835 else
837 if (tag->parse_func)
839 /* unsupported tag, reject */
840 return -1;
842 taglen = strlen(tag->name);
843 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
845 text += taglen;
848 else
850 /* regular string */
851 int i = 0;
852 /* just copy the string */
853 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
854 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
856 viewer->lines[line].strings[cur_string][i] = text[i];
857 i++;
859 viewer->lines[line].strings[cur_string][i] = '\0';
860 cur_string++;
861 text += i;
864 return text - start;
868 static int parse_playlistview(const char *wps_bufptr,
869 struct wps_token *token, struct wps_data *wps_data)
871 (void)wps_data;
872 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
873 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
874 char *ptr = strchr(wps_bufptr, '|');
875 int length;
876 if (!viewer || !ptr)
877 return WPS_ERROR_INVALID_PARAM;
878 viewer->vp = &curr_vp->vp;
879 viewer->show_icons = true;
880 viewer->start_offset = atoi(ptr+1);
881 token->value.data = (void*)viewer;
882 ptr = strchr(ptr+1, '|');
883 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
884 if (length < 0)
885 return WPS_ERROR_INVALID_PARAM;
886 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
887 if (length < 0)
888 return WPS_ERROR_INVALID_PARAM;
890 return skip_end_of_line(wps_bufptr);
892 #endif
894 static int parse_viewport(const char *wps_bufptr,
895 struct wps_token *token,
896 struct wps_data *wps_data)
898 (void)token; /* Kill warnings */
899 const char *ptr = wps_bufptr;
901 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
903 /* check for the optional letter to signify its a hideable viewport */
904 /* %Vl|<label>|<rest of tags>| */
905 skin_vp->hidden_flags = 0;
906 skin_vp->label = VP_NO_LABEL;
907 skin_vp->lines = NULL;
908 if (curr_line)
910 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
911 - (wps_data->num_tokens > 0 ? 1 : 0);
914 curr_line = NULL;
915 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
916 return WPS_ERROR_INVALID_PARAM;
918 if (*ptr == 'i')
920 if (*(ptr+1) == '|')
922 char label = *(ptr+2);
923 if (label >= 'a' && label <= 'z')
925 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
926 skin_vp->label = VP_INFO_LABEL|label;
927 ptr += 3;
929 else
931 skin_vp->label = VP_INFO_LABEL|VP_DEFAULT_LABEL;
932 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
933 ++ptr;
936 else
937 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
940 else if (*ptr == 'l')
942 if (*(ptr+1) == '|')
944 char label = *(ptr+2);
945 if (label >= 'a' && label <= 'z')
947 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
948 skin_vp->label = label;
950 else
951 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
952 ptr += 3;
955 if (*ptr != '|')
956 return WPS_ERROR_INVALID_PARAM;
958 ptr++;
959 struct viewport *vp = &skin_vp->vp;
960 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
961 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
962 return WPS_ERROR_INVALID_PARAM;
964 /* Check for trailing | */
965 if (*ptr != '|')
966 return WPS_ERROR_INVALID_PARAM;
968 if (follow_lang_direction && lang_is_rtl())
970 vp->flags |= VP_FLAG_ALIGN_RIGHT;
971 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
973 else
974 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
976 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
977 if (!list)
978 return WPS_ERROR_INVALID_PARAM;
979 add_to_ll_chain(&wps_data->viewports, list);
980 curr_vp = skin_vp;
981 /* Skip the rest of the line */
982 return skip_end_of_line(wps_bufptr);
985 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
986 static int parse_image_special(const char *wps_bufptr,
987 struct wps_token *token,
988 struct wps_data *wps_data)
990 (void)wps_data; /* kill warning */
991 (void)token;
992 const char *pos = NULL;
993 const char *newline;
994 bool error = false;
996 pos = strchr(wps_bufptr + 1, '|');
997 newline = strchr(wps_bufptr, '\n');
999 error = (pos > newline);
1001 #if LCD_DEPTH > 1
1002 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
1004 /* format: %X|filename.bmp| or %Xd */
1005 if (*(wps_bufptr) == 'd')
1007 wps_data->backdrop = NULL;
1008 return skip_end_of_line(wps_bufptr);
1010 else if (!error)
1011 wps_data->backdrop = (char*)wps_bufptr + 1;
1013 #endif
1014 if (error)
1015 return WPS_ERROR_INVALID_PARAM;
1016 /* Skip the rest of the line */
1017 return skip_end_of_line(wps_bufptr);
1019 #endif
1021 #endif /* HAVE_LCD_BITMAP */
1023 static int parse_setting_and_lang(const char *wps_bufptr,
1024 struct wps_token *token,
1025 struct wps_data *wps_data)
1027 /* NOTE: both the string validations that happen in here will
1028 * automatically PASS on checkwps because its too hard to get
1029 * settings_list.c and englinsh.lang built for it.
1030 * If that ever changes remove the #ifndef __PCTOOL__'s here
1032 (void)wps_data;
1033 const char *ptr = wps_bufptr;
1034 const char *end;
1035 int i = 0;
1036 char temp[64];
1038 /* Find the setting's cfg_name */
1039 if (*ptr != '|')
1040 return WPS_ERROR_INVALID_PARAM;
1041 ptr++;
1042 end = strchr(ptr,'|');
1043 if (!end)
1044 return WPS_ERROR_INVALID_PARAM;
1045 strlcpy(temp, ptr,end-ptr+1);
1047 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1049 #ifndef __PCTOOL__
1050 i = lang_english_to_id(temp);
1051 if (i < 0)
1052 return WPS_ERROR_INVALID_PARAM;
1053 #endif
1055 else
1057 /* Find the setting */
1058 for (i=0; i<nb_settings; i++)
1059 if (settings[i].cfg_name &&
1060 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1061 /* prevent matches on cfg_name prefixes */
1062 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1063 break;
1064 #ifndef __PCTOOL__
1065 if (i == nb_settings)
1066 return WPS_ERROR_INVALID_PARAM;
1067 #endif
1069 /* Store the setting number */
1070 token->value.i = i;
1072 /* Skip the rest of the line */
1073 return end-ptr+2;
1077 static int parse_dir_level(const char *wps_bufptr,
1078 struct wps_token *token,
1079 struct wps_data *wps_data)
1081 char val[] = { *wps_bufptr, '\0' };
1082 token->value.i = atoi(val);
1083 (void)wps_data; /* Kill warnings */
1084 return 1;
1087 static int parse_timeout(const char *wps_bufptr,
1088 struct wps_token *token,
1089 struct wps_data *wps_data)
1091 int skip = 0;
1092 int val = 0;
1093 bool have_point = false;
1094 bool have_tenth = false;
1096 (void)wps_data; /* Kill the warning */
1098 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1100 if (*wps_bufptr != '.')
1102 val *= 10;
1103 val += *wps_bufptr - '0';
1104 if (have_point)
1106 have_tenth = true;
1107 wps_bufptr++;
1108 skip++;
1109 break;
1112 else
1113 have_point = true;
1115 wps_bufptr++;
1116 skip++;
1119 if (have_tenth == false)
1120 val *= 10;
1122 if (val == 0 && skip == 0)
1124 /* decide what to do if no value was specified */
1125 switch (token->type)
1127 case WPS_TOKEN_SUBLINE_TIMEOUT:
1128 return -1;
1129 case WPS_TOKEN_BUTTON_VOLUME:
1130 case WPS_TOKEN_TRACK_STARTING:
1131 case WPS_TOKEN_TRACK_ENDING:
1132 val = 10;
1133 break;
1136 token->value.i = val;
1138 return skip;
1141 static int parse_progressbar(const char *wps_bufptr,
1142 struct wps_token *token,
1143 struct wps_data *wps_data)
1145 /* %pb or %pb|filename|x|y|width|height|
1146 using - for any of the params uses "sane" values */
1147 #ifdef HAVE_LCD_BITMAP
1148 enum {
1149 PB_FILENAME = 0,
1150 PB_X,
1151 PB_Y,
1152 PB_WIDTH,
1153 PB_HEIGHT
1155 const char *filename;
1156 int x, y, height, width;
1157 uint32_t set = 0;
1158 const char *ptr = wps_bufptr;
1159 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1160 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1162 if (!pb || !item)
1163 return WPS_ERROR_INVALID_PARAM;
1165 struct viewport *vp = &curr_vp->vp;
1166 /* we need to know what line number (viewport relative) this pb is,
1167 * so count them... */
1168 int line_num = -1;
1169 struct skin_line *line = curr_vp->lines;
1170 while (line)
1172 line_num++;
1173 line = line->next;
1175 pb->vp = vp;
1176 pb->have_bitmap_pb = false;
1177 pb->bm.data = NULL; /* no bitmap specified */
1178 pb->follow_lang_direction = follow_lang_direction > 0;
1179 pb->draw = false;
1181 if (*wps_bufptr != '|') /* regular old style */
1183 pb->x = 0;
1184 pb->width = vp->width;
1185 pb->height = SYSFONT_HEIGHT-2;
1186 pb->y = -line_num - 1; /* Will be computed during the rendering */
1187 if (token->type == WPS_TOKEN_VOLUME || token->type == WPS_TOKEN_BATTERY_PERCENT)
1188 return 0; /* dont add it, let the regular token handling do the work */
1189 add_to_ll_chain(&wps_data->progressbars, item);
1190 return 0;
1192 ptr = wps_bufptr + 1;
1194 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1195 &x, &y, &width, &height)))
1197 /* If we are in a conditional then we probably don't want to fail
1198 * if the above doesnt work. So assume the | is breaking the conditional
1199 * and move on. The next token will fail if this is incorrect. */
1200 if (level >= 0)
1201 return 0;
1202 return WPS_ERROR_INVALID_PARAM;
1205 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1206 pb->bm.data = (char*)filename;
1208 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1209 pb->x = x;
1210 else
1211 pb->x = vp->x;
1213 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1215 /* A zero width causes a divide-by-zero error later, so reject it */
1216 if (width == 0)
1217 return WPS_ERROR_INVALID_PARAM;
1219 pb->width = width;
1221 else
1222 pb->width = vp->width - pb->x;
1224 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1226 /* A zero height makes no sense - reject it */
1227 if (height == 0)
1228 return WPS_ERROR_INVALID_PARAM;
1230 pb->height = height;
1232 else
1234 if (vp->font > FONT_UI)
1235 pb->height = -1; /* calculate at display time */
1236 else
1238 #ifndef __PCTOOL__
1239 pb->height = font_get(vp->font)->height;
1240 #else
1241 pb->height = 8;
1242 #endif
1246 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1247 pb->y = y;
1248 else
1249 pb->y = -line_num - 1; /* Will be computed during the rendering */
1251 add_to_ll_chain(&wps_data->progressbars, item);
1252 if (token->type == WPS_TOKEN_VOLUME)
1253 token->type = WPS_TOKEN_VOLUMEBAR;
1254 else if (token->type == WPS_TOKEN_BATTERY_PERCENT)
1255 token->type = WPS_TOKEN_BATTERY_PERCENTBAR;
1256 pb->type = token->type;
1258 return ptr+1-wps_bufptr;
1259 #else
1260 (void)wps_bufptr;
1261 if (token->type != WPS_TOKEN_VOLUME &&
1262 token->type != WPS_TOKEN_BATTERY_PERCENTBAR)
1264 wps_data->full_line_progressbar =
1265 token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
1267 return 0;
1269 #endif
1272 #ifdef HAVE_ALBUMART
1273 static int parse_int(const char *newline, const char **_pos, int *num)
1275 *_pos = parse_list("d", NULL, '|', *_pos, num);
1277 return (!*_pos || *_pos > newline || **_pos != '|');
1280 static int parse_albumart_load(const char *wps_bufptr,
1281 struct wps_token *token,
1282 struct wps_data *wps_data)
1284 const char *_pos, *newline;
1285 bool parsing;
1286 struct dim dimensions;
1287 int albumart_slot;
1288 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1289 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1290 (void)token; /* silence warning */
1291 if (!aa)
1292 return skip_end_of_line(wps_bufptr);
1294 /* reset albumart info in wps */
1295 aa->width = -1;
1296 aa->height = -1;
1297 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1298 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1299 aa->vp = &curr_vp->vp;
1301 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1303 newline = strchr(wps_bufptr, '\n');
1305 _pos = wps_bufptr;
1307 if (*_pos != '|')
1308 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1310 ++_pos;
1312 /* initial validation and parsing of x component */
1313 if (parse_int(newline, &_pos, &aa->x))
1314 return WPS_ERROR_INVALID_PARAM;
1316 ++_pos;
1318 /* initial validation and parsing of y component */
1319 if (parse_int(newline, &_pos, &aa->y))
1320 return WPS_ERROR_INVALID_PARAM;
1322 /* parsing width field */
1323 parsing = true;
1324 while (parsing)
1326 /* apply each modifier in turn */
1327 ++_pos;
1328 switch (*_pos)
1330 case 'l':
1331 case 'L':
1332 case '+':
1333 if (swap_for_rtl)
1334 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1335 else
1336 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1337 break;
1338 case 'c':
1339 case 'C':
1340 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1341 break;
1342 case 'r':
1343 case 'R':
1344 case '-':
1345 if (swap_for_rtl)
1346 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1347 else
1348 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1349 break;
1350 case 'd':
1351 case 'D':
1352 case 'i':
1353 case 'I':
1354 case 's':
1355 case 'S':
1356 /* simply ignored */
1357 break;
1358 default:
1359 parsing = false;
1360 break;
1363 /* extract max width data */
1364 if (*_pos != '|')
1366 if (parse_int(newline, &_pos, &aa->width))
1367 return WPS_ERROR_INVALID_PARAM;
1370 /* parsing height field */
1371 parsing = true;
1372 while (parsing)
1374 /* apply each modifier in turn */
1375 ++_pos;
1376 switch (*_pos)
1378 case 't':
1379 case 'T':
1380 case '-':
1381 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1382 break;
1383 case 'c':
1384 case 'C':
1385 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1386 break;
1387 case 'b':
1388 case 'B':
1389 case '+':
1390 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1391 break;
1392 case 'd':
1393 case 'D':
1394 case 'i':
1395 case 'I':
1396 case 's':
1397 case 'S':
1398 /* simply ignored */
1399 break;
1400 default:
1401 parsing = false;
1402 break;
1405 /* extract max height data */
1406 if (*_pos != '|')
1408 if (parse_int(newline, &_pos, &aa->height))
1409 return WPS_ERROR_INVALID_PARAM;
1412 /* if we got here, we parsed everything ok .. ! */
1413 if (aa->width < 0)
1414 aa->width = 0;
1415 else if (aa->width > LCD_WIDTH)
1416 aa->width = LCD_WIDTH;
1418 if (aa->height < 0)
1419 aa->height = 0;
1420 else if (aa->height > LCD_HEIGHT)
1421 aa->height = LCD_HEIGHT;
1423 if (swap_for_rtl)
1424 aa->x = LCD_WIDTH - (aa->x + aa->width);
1426 aa->state = WPS_ALBUMART_LOAD;
1427 aa->draw = false;
1428 wps_data->albumart = aa;
1430 dimensions.width = aa->width;
1431 dimensions.height = aa->height;
1433 albumart_slot = playback_claim_aa_slot(&dimensions);
1435 if (0 <= albumart_slot)
1436 wps_data->playback_aa_slot = albumart_slot;
1438 /* Skip the rest of the line */
1439 return skip_end_of_line(wps_bufptr);
1442 static int parse_albumart_display(const char *wps_bufptr,
1443 struct wps_token *token,
1444 struct wps_data *wps_data)
1446 (void)wps_bufptr;
1447 struct wps_token *prev = token-1;
1448 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1450 token->type = WPS_TOKEN_ALBUMART_FOUND;
1452 else if (wps_data->albumart)
1454 wps_data->albumart->vp = &curr_vp->vp;
1456 #if 0
1457 /* the old code did this so keep it here for now...
1458 * this is to allow the posibility to showing the next tracks AA! */
1459 if (wps_bufptr+1 == 'n')
1460 return 1;
1461 #endif
1462 return 0;
1464 #endif /* HAVE_ALBUMART */
1466 #ifdef HAVE_TOUCHSCREEN
1468 struct touchaction {const char* s; int action;};
1469 static const struct touchaction touchactions[] = {
1470 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1471 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1472 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1473 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1474 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1475 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1476 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1477 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1479 static int parse_touchregion(const char *wps_bufptr,
1480 struct wps_token *token, struct wps_data *wps_data)
1482 (void)token;
1483 unsigned i, imax;
1484 struct touchregion *region = NULL;
1485 const char *ptr = wps_bufptr;
1486 const char *action;
1487 const char pb_string[] = "progressbar";
1488 const char vol_string[] = "volume";
1489 int x,y,w,h;
1491 /* format: %T|x|y|width|height|action|
1492 * if action starts with & the area must be held to happen
1493 * action is one of:
1494 * play - play/pause playback
1495 * stop - stop playback, exit the wps
1496 * prev - prev track
1497 * next - next track
1498 * ffwd - seek forward
1499 * rwd - seek backwards
1500 * menu - go back to the main menu
1501 * browse - go back to the file/db browser
1502 * shuffle - toggle shuffle mode
1503 * repmode - cycle the repeat mode
1504 * quickscreen - go into the quickscreen
1505 * contextmenu - open the context menu
1506 * playlist - go into the playlist
1507 * pitch - go into the pitchscreen
1508 * volup - increase volume by one step
1509 * voldown - decrease volume by one step
1513 if (*ptr != '|')
1514 return WPS_ERROR_INVALID_PARAM;
1515 ptr++;
1517 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1518 return WPS_ERROR_INVALID_PARAM;
1520 /* Check there is a terminating | */
1521 if (*ptr != '|')
1522 return WPS_ERROR_INVALID_PARAM;
1524 region = skin_buffer_alloc(sizeof(struct touchregion));
1525 if (!region)
1526 return WPS_ERROR_INVALID_PARAM;
1528 /* should probably do some bounds checking here with the viewport... but later */
1529 region->action = ACTION_NONE;
1530 region->x = x;
1531 region->y = y;
1532 region->width = w;
1533 region->height = h;
1534 region->wvp = curr_vp;
1535 region->armed = false;
1537 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1538 && *(action + sizeof(pb_string)-1) == '|')
1539 region->type = WPS_TOUCHREGION_SCROLLBAR;
1540 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1541 && *(action + sizeof(vol_string)-1) == '|')
1542 region->type = WPS_TOUCHREGION_VOLUME;
1543 else
1545 region->type = WPS_TOUCHREGION_ACTION;
1547 if (*action == '&')
1549 action++;
1550 region->repeat = true;
1552 else
1553 region->repeat = false;
1555 i = 0;
1556 imax = ARRAYLEN(touchactions);
1557 while ((region->action == ACTION_NONE) &&
1558 (i < imax))
1560 /* try to match with one of our touchregion screens */
1561 int len = strlen(touchactions[i].s);
1562 if (!strncmp(touchactions[i].s, action, len)
1563 && *(action+len) == '|')
1564 region->action = touchactions[i].action;
1565 i++;
1567 if (region->action == ACTION_NONE)
1568 return WPS_ERROR_INVALID_PARAM;
1570 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1571 if (!item)
1572 return WPS_ERROR_INVALID_PARAM;
1573 add_to_ll_chain(&wps_data->touchregions, item);
1574 return skip_end_of_line(wps_bufptr);
1576 #endif
1578 /* Parse a generic token from the given string. Return the length read */
1579 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1581 int skip = 0, taglen = 0, ret;
1582 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1583 const struct wps_tag *tag;
1584 memset(token, 0, sizeof(*token));
1586 switch(*wps_bufptr)
1589 case '%':
1590 case '<':
1591 case '|':
1592 case '>':
1593 case ';':
1594 case '#':
1595 /* escaped characters */
1596 token->type = WPS_TOKEN_CHARACTER;
1597 token->value.c = *wps_bufptr;
1598 taglen = 1;
1599 wps_data->num_tokens++;
1600 break;
1602 case '?':
1603 /* conditional tag */
1604 token->type = WPS_TOKEN_CONDITIONAL;
1605 level++;
1606 condindex[level] = wps_data->num_tokens;
1607 numoptions[level] = 1;
1608 wps_data->num_tokens++;
1609 ret = parse_token(wps_bufptr + 1, wps_data);
1610 if (ret < 0) return ret;
1611 taglen = 1 + ret;
1612 break;
1614 default:
1615 /* find what tag we have */
1616 for (tag = all_tags;
1617 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1618 tag++) ;
1620 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1621 token->type = tag->type;
1622 curr_line->curr_subline->line_type |= tag->refresh_type;
1624 /* if the tag has a special parsing function, we call it */
1625 if (tag->parse_func)
1627 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1628 if (ret < 0) return ret;
1629 skip += ret;
1632 /* Some tags we don't want to save as tokens */
1633 if (tag->type == WPS_NO_TOKEN)
1634 break;
1636 /* tags that start with 'F', 'I' or 'D' are for the next file */
1637 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1638 *(tag->name) == 'D')
1639 token->next = true;
1641 wps_data->num_tokens++;
1642 break;
1645 skip += taglen;
1646 return skip;
1651 * Returns the number of bytes to skip the buf pointer to access the false
1652 * branch in a _binary_ conditional
1654 * That is:
1655 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1656 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1658 * depending on the features of a target it's not called from check_feature_tag,
1659 * hence the __attribute__ or it issues compiler warnings
1663 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1664 static int find_false_branch(const char *wps_bufptr)
1666 const char *buf = wps_bufptr;
1667 /* wps_bufptr is after the opening '<', hence level = 1*/
1668 int level = 1;
1669 char ch;
1672 ch = *buf;
1673 if (ch == '%')
1674 { /* filter out the characters we check later if they're printed
1675 * as literals */
1676 ch = *(++buf);
1677 if (ch == '<' || ch == '>' || ch == '|')
1678 continue;
1679 /* else: some tags/printed literals we skip over */
1681 else if (ch == '<') /* nested conditional */
1682 level++;
1683 else if (ch == '>')
1684 { /* closed our or a nested conditional,
1685 * do NOT skip over the '>' so that wps_parse() sees it for closing
1686 * if it is the closing one for our conditional */
1687 level--;
1689 else if (ch == '|' && level == 1)
1690 { /* we found our separator, point before and get out */
1691 break;
1693 /* if level is 0, we don't have a false branch */
1694 } while (level > 0 && *(++buf));
1696 return buf - wps_bufptr;
1700 * returns the number of bytes to get the appropriate branch of a binary
1701 * conditional
1703 * That means:
1704 * - if a feature is available, it returns 0 to not skip anything
1705 * - if the feature is not available, skip to the false branch and don't
1706 * parse the true branch at all
1708 * */
1709 static int check_feature_tag(const char *wps_bufptr, const int type)
1711 (void)wps_bufptr;
1712 switch (type)
1714 case WPS_TOKEN_RTC_PRESENT:
1715 #if CONFIG_RTC
1716 return 0;
1717 #else
1718 return find_false_branch(wps_bufptr);
1719 #endif
1720 case WPS_TOKEN_HAVE_RECORDING:
1721 #ifdef HAVE_RECORDING
1722 return 0;
1723 #else
1724 return find_false_branch(wps_bufptr);
1725 #endif
1726 default: /* not a tag we care about, just don't skip */
1727 return 0;
1732 /* Parses the WPS.
1733 data is the pointer to the structure where the parsed WPS should be stored.
1734 It is initialised.
1735 wps_bufptr points to the string containing the WPS tags */
1736 #define TOKEN_BLOCK_SIZE 128
1737 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1739 if (!data || !wps_bufptr || !*wps_bufptr)
1740 return false;
1741 enum wps_parse_error fail = PARSE_OK;
1742 int ret;
1743 int max_tokens = TOKEN_BLOCK_SIZE;
1744 size_t buf_free = 0;
1745 line_number = 0;
1746 level = -1;
1748 /* allocate enough RAM for a reasonable skin, grow as needed.
1749 * Free any used RAM before loading the images to be 100% RAM efficient */
1750 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1751 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1752 return false;
1753 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1754 data->num_tokens = 0;
1756 #if LCD_DEPTH > 1
1757 /* Backdrop defaults to the setting unless %X is used, so set it now */
1758 if (global_settings.backdrop_file[0])
1760 data->backdrop = "-";
1762 #endif
1764 while (*wps_bufptr && !fail)
1766 if (follow_lang_direction)
1767 follow_lang_direction--;
1768 /* first make sure there is enough room for tokens */
1769 if (max_tokens <= data->num_tokens + 5)
1771 int extra_tokens = TOKEN_BLOCK_SIZE;
1772 size_t needed = extra_tokens * sizeof(struct wps_token);
1773 /* do some smarts here to grow the array a bit */
1774 if (skin_buffer_freespace() < needed)
1776 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1777 break;
1779 skin_buffer_increment(needed, false);
1780 max_tokens += extra_tokens;
1783 switch(*wps_bufptr++)
1786 /* Regular tag */
1787 case '%':
1788 if ((ret = parse_token(wps_bufptr, data)) < 0)
1790 fail = PARSE_FAIL_COND_INVALID_PARAM;
1791 break;
1793 else if (level >= WPS_MAX_COND_LEVEL - 1)
1795 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1796 break;
1798 wps_bufptr += ret;
1799 break;
1801 /* Alternating sublines separator */
1802 case ';':
1803 if (level >= 0) /* there are unclosed conditionals */
1805 fail = PARSE_FAIL_UNCLOSED_COND;
1806 break;
1809 if (!skin_start_new_subline(curr_line, data->num_tokens))
1810 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1812 break;
1814 /* Conditional list start */
1815 case '<':
1816 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1818 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1819 break;
1821 wps_bufptr += check_feature_tag(wps_bufptr,
1822 data->tokens[data->num_tokens-1].type);
1823 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1824 lastcond[level] = data->num_tokens++;
1825 break;
1827 /* Conditional list end */
1828 case '>':
1829 if (level < 0) /* not in a conditional, invalid char */
1831 fail = PARSE_FAIL_INVALID_CHAR;
1832 break;
1835 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1836 if (lastcond[level])
1837 data->tokens[lastcond[level]].value.i = data->num_tokens;
1838 else
1840 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1841 break;
1844 lastcond[level] = 0;
1845 data->num_tokens++;
1846 data->tokens[condindex[level]].value.i = numoptions[level];
1847 level--;
1848 break;
1850 /* Conditional list option */
1851 case '|':
1852 if (level < 0) /* not in a conditional, invalid char */
1854 fail = PARSE_FAIL_INVALID_CHAR;
1855 break;
1858 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1859 if (lastcond[level])
1860 data->tokens[lastcond[level]].value.i = data->num_tokens;
1861 else
1863 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1864 break;
1867 lastcond[level] = data->num_tokens;
1868 numoptions[level]++;
1869 data->num_tokens++;
1870 break;
1872 /* Comment */
1873 case '#':
1874 if (level >= 0) /* there are unclosed conditionals */
1876 fail = PARSE_FAIL_UNCLOSED_COND;
1877 break;
1880 wps_bufptr += skip_end_of_line(wps_bufptr);
1881 break;
1883 /* End of this line */
1884 case '\n':
1885 if (level >= 0) /* there are unclosed conditionals */
1887 fail = PARSE_FAIL_UNCLOSED_COND;
1888 break;
1890 /* add a new token for the \n so empty lines are correct */
1891 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1892 data->tokens[data->num_tokens].value.c = '\n';
1893 data->tokens[data->num_tokens].next = false;
1894 data->num_tokens++;
1896 if (!skin_start_new_line(curr_vp, data->num_tokens))
1898 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1899 break;
1901 line_number++;
1903 break;
1905 /* String */
1906 default:
1908 unsigned int len = 1;
1909 const char *string_start = wps_bufptr - 1;
1911 /* find the length of the string */
1912 while (*wps_bufptr && *wps_bufptr != '#' &&
1913 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1914 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1915 *wps_bufptr != '|' && *wps_bufptr != '\n')
1917 wps_bufptr++;
1918 len++;
1921 /* look if we already have that string */
1922 char *str;
1923 bool found = false;
1924 struct skin_token_list *list = data->strings;
1925 while (list)
1927 str = (char*)list->token->value.data;
1928 found = (strlen(str) == len &&
1929 strncmp(string_start, str, len) == 0);
1930 if (found)
1931 break; /* break here because the list item is
1932 used if its found */
1933 list = list->next;
1935 /* If a matching string is found, found is true and i is
1936 the index of the string. If not, found is false */
1938 if (!found)
1940 /* new string */
1941 str = (char*)skin_buffer_alloc(len+1);
1942 if (!str)
1944 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1945 break;
1947 strlcpy(str, string_start, len+1);
1948 struct skin_token_list *item =
1949 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1950 if(!item)
1952 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1953 break;
1955 add_to_ll_chain(&data->strings, item);
1957 else
1959 /* another occurrence of an existing string */
1960 data->tokens[data->num_tokens].value.data = list->token->value.data;
1962 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1963 data->num_tokens++;
1965 break;
1969 if (!fail && level >= 0) /* there are unclosed conditionals */
1970 fail = PARSE_FAIL_UNCLOSED_COND;
1972 if (*wps_bufptr && !fail)
1973 /* one of the limits of the while loop was exceeded */
1974 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1976 /* Success! */
1977 curr_line->curr_subline->last_token_idx = data->num_tokens;
1978 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1979 /* freeup unused tokens */
1980 skin_buffer_free_from_front(sizeof(struct wps_token)
1981 * (max_tokens - data->num_tokens));
1983 #if defined(DEBUG) || defined(SIMULATOR)
1984 if (debug)
1986 print_debug_info(data, fail, line_number);
1987 debug_skin_usage();
1989 #else
1990 (void)debug;
1991 #endif
1993 return (fail == 0);
1998 * initial setup of wps_data; does reset everything
1999 * except fields which need to survive, i.e.
2002 static void skin_data_reset(struct wps_data *wps_data)
2004 #ifdef HAVE_LCD_BITMAP
2005 wps_data->images = NULL;
2006 wps_data->progressbars = NULL;
2007 #endif
2008 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2009 wps_data->backdrop = NULL;
2010 #endif
2011 #ifdef HAVE_TOUCHSCREEN
2012 wps_data->touchregions = NULL;
2013 #endif
2014 wps_data->viewports = NULL;
2015 wps_data->strings = NULL;
2016 #ifdef HAVE_ALBUMART
2017 wps_data->albumart = NULL;
2018 if (wps_data->playback_aa_slot >= 0)
2020 playback_release_aa_slot(wps_data->playback_aa_slot);
2021 wps_data->playback_aa_slot = -1;
2023 #endif
2024 wps_data->tokens = NULL;
2025 wps_data->num_tokens = 0;
2027 #ifdef HAVE_LCD_BITMAP
2028 wps_data->peak_meter_enabled = false;
2029 wps_data->wps_sb_tag = false;
2030 wps_data->show_sb_on_wps = false;
2031 #else /* HAVE_LCD_CHARCELLS */
2032 /* progress bars */
2033 int i;
2034 for (i = 0; i < 8; i++)
2036 wps_data->wps_progress_pat[i] = 0;
2038 wps_data->full_line_progressbar = false;
2039 #endif
2040 wps_data->wps_loaded = false;
2043 #ifdef HAVE_LCD_BITMAP
2044 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2046 (void)wps_data; /* only needed for remote targets */
2047 char img_path[MAX_PATH];
2048 get_image_filename(bitmap->data, bmpdir,
2049 img_path, sizeof(img_path));
2051 /* load the image */
2052 int format;
2053 #ifdef HAVE_REMOTE_LCD
2054 if (curr_screen == SCREEN_REMOTE)
2055 format = FORMAT_ANY|FORMAT_REMOTE;
2056 else
2057 #endif
2058 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2060 size_t max_buf;
2061 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2062 bitmap->data = imgbuf;
2063 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2065 if (ret > 0)
2067 skin_buffer_increment(ret, true);
2068 return true;
2070 else
2072 /* Abort if we can't load an image */
2073 DEBUGF("Couldn't load '%s'\n", img_path);
2074 return false;
2078 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2080 struct skin_token_list *list;
2081 bool retval = true; /* return false if a single image failed to load */
2082 /* do the progressbars */
2083 list = wps_data->progressbars;
2084 while (list)
2086 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2087 if (pb->bm.data)
2089 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2090 if (!pb->have_bitmap_pb) /* no success */
2091 retval = false;
2093 list = list->next;
2095 /* regular images */
2096 list = wps_data->images;
2097 while (list)
2099 struct gui_img *img = (struct gui_img*)list->token->value.data;
2100 if (img->bm.data)
2102 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2103 if (img->loaded)
2104 img->subimage_height = img->bm.height / img->num_subimages;
2105 else
2106 retval = false;
2108 list = list->next;
2111 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2112 /* Backdrop load scheme:
2113 * 1) %X|filename|
2114 * 2) load the backdrop from settings
2116 if (wps_data->backdrop)
2118 bool needed = wps_data->backdrop[0] != '-';
2119 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2120 bmpdir, curr_screen);
2121 if (!wps_data->backdrop && needed)
2122 retval = false;
2124 #endif /* has backdrop support */
2126 return retval;
2129 static bool skin_load_fonts(struct wps_data *data)
2131 /* don't spit out after the first failue to aid debugging */
2132 bool success = true;
2133 struct skin_token_list *vp_list;
2134 int font_id;
2135 /* walk though each viewport and assign its font */
2136 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2138 /* first, find the viewports that have a non-sys/ui-font font */
2139 struct skin_viewport *skin_vp =
2140 (struct skin_viewport*)vp_list->token->value.data;
2141 struct viewport *vp = &skin_vp->vp;
2144 if (vp->font <= FONT_UI)
2145 { /* the usual case -> built-in fonts */
2146 #ifdef HAVE_REMOTE_LCD
2147 if (vp->font == FONT_UI)
2148 vp->font += curr_screen;
2149 #endif
2150 continue;
2152 font_id = vp->font;
2154 /* now find the corresponding skin_font */
2155 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2156 if (!font->name)
2158 DEBUGF("font %d not specified\n", font_id);
2159 success = false;
2160 continue;
2163 /* load the font - will handle loading the same font again if
2164 * multiple viewports use the same */
2165 if (font->id < 0)
2167 char *dot = strchr(font->name, '.');
2168 *dot = '\0';
2169 font->id = skin_font_load(font->name);
2172 if (font->id < 0)
2174 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2175 font_id, font->name);
2176 success = false;
2177 continue;
2180 /* finally, assign the font_id to the viewport */
2181 vp->font = font->id;
2183 return success;
2186 #endif /* HAVE_LCD_BITMAP */
2188 /* to setup up the wps-data from a format-buffer (isfile = false)
2189 from a (wps-)file (isfile = true)*/
2190 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2191 const char *buf, bool isfile)
2193 char *wps_buffer = NULL;
2194 if (!wps_data || !buf)
2195 return false;
2196 #ifdef HAVE_ALBUMART
2197 int status;
2198 struct mp3entry *curtrack;
2199 long offset;
2200 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2201 if (wps_data->albumart)
2203 old_aa.state = wps_data->albumart->state;
2204 old_aa.height = wps_data->albumart->height;
2205 old_aa.width = wps_data->albumart->width;
2207 #endif
2208 #ifdef HAVE_LCD_BITMAP
2209 int i;
2210 for (i=0;i<MAXUSERFONTS;i++)
2212 skinfonts[i].id = -1;
2213 skinfonts[i].name = NULL;
2215 #endif
2216 #ifdef DEBUG_SKIN_ENGINE
2217 if (isfile && debug_wps)
2219 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2221 #endif
2223 skin_data_reset(wps_data);
2224 wps_data->wps_loaded = false;
2225 curr_screen = screen;
2227 /* alloc default viewport, will be fixed up later */
2228 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2229 if (!curr_vp)
2230 return false;
2231 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2232 if (!list)
2233 return false;
2234 add_to_ll_chain(&wps_data->viewports, list);
2237 /* Initialise the first (default) viewport */
2238 curr_vp->label = VP_DEFAULT_LABEL;
2239 curr_vp->hidden_flags = 0;
2240 curr_vp->lines = NULL;
2242 viewport_set_defaults(&curr_vp->vp, screen);
2243 #ifdef HAVE_LCD_BITMAP
2244 curr_vp->vp.font = FONT_UI;
2245 #endif
2247 curr_line = NULL;
2248 if (!skin_start_new_line(curr_vp, 0))
2249 return false;
2251 if (isfile)
2253 int fd = open_utf8(buf, O_RDONLY);
2255 if (fd < 0)
2256 return false;
2258 /* get buffer space from the plugin buffer */
2259 size_t buffersize = 0;
2260 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2262 if (!wps_buffer)
2263 return false;
2265 /* copy the file's content to the buffer for parsing,
2266 ensuring that every line ends with a newline char. */
2267 unsigned int start = 0;
2268 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2270 start += strlen(wps_buffer + start);
2271 if (start < buffersize - 1)
2273 wps_buffer[start++] = '\n';
2274 wps_buffer[start] = 0;
2277 close(fd);
2278 if (start <= 0)
2279 return false;
2281 else
2283 wps_buffer = (char*)buf;
2285 /* parse the WPS source */
2286 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2287 skin_data_reset(wps_data);
2288 return false;
2291 #ifdef HAVE_LCD_BITMAP
2292 char bmpdir[MAX_PATH];
2293 if (isfile)
2295 /* get the bitmap dir */
2296 char *dot = strrchr(buf, '.');
2297 strlcpy(bmpdir, buf, dot - buf + 1);
2299 else
2301 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2303 /* load the bitmaps that were found by the parsing */
2304 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2305 !skin_load_fonts(wps_data))
2307 skin_data_reset(wps_data);
2308 return false;
2310 #endif
2311 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2312 status = audio_status();
2313 if (status & AUDIO_STATUS_PLAY)
2315 struct skin_albumart *aa = wps_data->albumart;
2316 if (aa && ((aa->state && !old_aa.state) ||
2317 (aa->state &&
2318 (((old_aa.height != aa->height) ||
2319 (old_aa.width != aa->width))))))
2321 curtrack = audio_current_track();
2322 offset = curtrack->offset;
2323 audio_stop();
2324 if (!(status & AUDIO_STATUS_PAUSE))
2325 audio_play(offset);
2328 #endif
2329 wps_data->wps_loaded = true;
2330 #ifdef DEBUG_SKIN_ENGINE
2331 if (isfile && debug_wps)
2332 debug_skin_usage();
2333 #endif
2334 return true;