fix errors
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
bloba0519c93a99da8ffb691b9c249aa344379ba31a9
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 #if CONFIG_TUNER
56 #include "radio.h"
57 #include "tuner.h"
58 #endif
59 #include "skin_fonts.h"
61 #ifdef HAVE_LCD_BITMAP
62 #include "bmp.h"
63 #endif
65 #ifdef HAVE_ALBUMART
66 #include "playback.h"
67 #endif
69 #include "backdrop.h"
70 #include "statusbar-skinned.h"
72 #define WPS_ERROR_INVALID_PARAM -1
74 /* which screen are we parsing for? */
75 static enum screen_type curr_screen;
77 /* level of current conditional.
78 -1 means we're not in a conditional. */
79 static int level = -1;
81 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
82 or WPS_TOKEN_CONDITIONAL_START in current level */
83 static int lastcond[WPS_MAX_COND_LEVEL];
85 /* index of the WPS_TOKEN_CONDITIONAL in current level */
86 static int condindex[WPS_MAX_COND_LEVEL];
88 /* number of condtional options in current level */
89 static int numoptions[WPS_MAX_COND_LEVEL];
91 /* line number, debug only */
92 static int line_number;
94 /* the current viewport */
95 static struct skin_viewport *curr_vp;
96 /* the current line, linked to the above viewport */
97 static struct skin_line *curr_line;
99 static int follow_lang_direction = 0;
101 #if defined(DEBUG) || defined(SIMULATOR)
102 /* debugging function */
103 extern void print_debug_info(struct wps_data *data, int fail, int line);
104 extern void debug_skin_usage(void);
105 #endif
107 /* Function for parsing of details for a token. At the moment the
108 function is called, the token type has already been set. The
109 function must fill in the details and possibly add more tokens
110 to the token array. It should return the number of chars that
111 has been consumed.
113 wps_bufptr points to the char following the tag (i.e. where
114 details begin).
115 token is the pointer to the 'main' token being parsed
117 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
118 struct wps_token *token, struct wps_data *wps_data);
120 struct wps_tag {
121 enum wps_token_type type;
122 const char name[3];
123 unsigned char refresh_type;
124 const wps_tag_parse_func parse_func;
126 static int skip_end_of_line(const char *wps_bufptr);
127 /* prototypes of all special parse functions : */
128 static int parse_timeout(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_progressbar(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
132 static int parse_dir_level(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_setting_and_lang(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_languagedirection(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data)
141 (void)wps_bufptr;
142 (void)token;
143 (void)wps_data;
144 follow_lang_direction = 2; /* 2 because it is decremented immediatly after
145 this token is parsed, after the next token it
146 will be 0 again. */
147 return 0;
150 #ifdef HAVE_LCD_BITMAP
151 static int parse_viewport_display(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 static int parse_playlistview(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 static int parse_viewport(const char *wps_bufptr,
156 struct wps_token *token, struct wps_data *wps_data);
157 static int parse_statusbar_enable(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_statusbar_disable(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 static int parse_statusbar_inbuilt(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 static int parse_image_display(const char *wps_bufptr,
164 struct wps_token *token, struct wps_data *wps_data);
165 static int parse_image_load(const char *wps_bufptr,
166 struct wps_token *token, struct wps_data *wps_data);
167 static int parse_font_load(const char *wps_bufptr,
168 struct wps_token *token, struct wps_data *wps_data);
169 #endif /*HAVE_LCD_BITMAP */
170 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
171 static int parse_viewportcolour(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 static int parse_image_special(const char *wps_bufptr,
174 struct wps_token *token, struct wps_data *wps_data);
175 #endif
176 #ifdef HAVE_ALBUMART
177 static int parse_albumart_load(const char *wps_bufptr,
178 struct wps_token *token, struct wps_data *wps_data);
179 static int parse_albumart_display(const char *wps_bufptr,
180 struct wps_token *token, struct wps_data *wps_data);
181 #endif /* HAVE_ALBUMART */
182 #ifdef HAVE_TOUCHSCREEN
183 static int parse_touchregion(const char *wps_bufptr,
184 struct wps_token *token, struct wps_data *wps_data);
185 #else
186 static int fulline_tag_not_supported(const char *wps_bufptr,
187 struct wps_token *token, struct wps_data *wps_data)
189 (void)token; (void)wps_data;
190 return skip_end_of_line(wps_bufptr);
192 #define parse_touchregion fulline_tag_not_supported
193 #endif
194 #ifdef CONFIG_RTC
195 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
196 #else
197 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
198 #endif
200 /* array of available tags - those with more characters have to go first
201 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
202 static const struct wps_tag all_tags[] = {
204 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
205 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
206 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
207 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
208 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
209 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
211 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, parse_progressbar },
212 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
213 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
214 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
215 #if CONFIG_CHARGING >= CHARGING_MONITOR
216 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
217 #endif
218 #if CONFIG_CHARGING
219 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
220 #endif
221 #ifdef HAVE_USB_POWER
222 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
223 #endif
225 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
236 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
237 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
238 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
239 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
240 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
241 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
242 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
243 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
245 /* current file */
246 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
256 parse_dir_level },
258 /* next file */
259 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
269 parse_dir_level },
271 /* current metadata */
272 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
276 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
277 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
278 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
283 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
285 /* next metadata */
286 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
290 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
292 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
293 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
294 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
295 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
296 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
297 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
299 #if (CONFIG_CODEC != MAS3507D)
300 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
301 #endif
302 #if (CONFIG_CODEC == SWCODEC)
303 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
304 #endif
305 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
306 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
307 #endif
309 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
311 #ifdef HAS_REMOTE_BUTTON_HOLD
312 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
313 #else
314 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
315 #endif
317 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
318 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
319 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
320 parse_timeout },
322 #ifdef HAVE_LCD_BITMAP
323 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
324 #else
325 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
326 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
327 #endif
328 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
329 parse_progressbar },
331 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC,
332 parse_progressbar },
334 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
335 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
336 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
337 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
338 { WPS_TOKEN_TRACK_STARTING, "pS", WPS_REFRESH_DYNAMIC, parse_timeout },
339 { WPS_TOKEN_TRACK_ENDING, "pE", WPS_REFRESH_DYNAMIC, parse_timeout },
341 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
342 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
343 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
344 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
346 #ifdef HAVE_TAGCACHE
347 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
348 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
349 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
350 #endif
352 #if CONFIG_CODEC == SWCODEC
353 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
354 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
355 #endif
357 { WPS_TOKEN_HAVE_TUNER, "tp", WPS_REFRESH_STATIC, NULL },
358 #if CONFIG_TUNER /* Re-uses the 't' and 'T' prefixes, be careful about doubleups */
359 { WPS_TOKEN_TUNER_TUNED, "tt", WPS_REFRESH_DYNAMIC, NULL },
360 { WPS_TOKEN_TUNER_SCANMODE, "tm", WPS_REFRESH_DYNAMIC, NULL },
361 { WPS_TOKEN_TUNER_STEREO, "ts", WPS_REFRESH_DYNAMIC, NULL },
362 { WPS_TOKEN_TUNER_MINFREQ, "ta", WPS_REFRESH_STATIC, NULL },
363 { WPS_TOKEN_TUNER_MAXFREQ, "tb", WPS_REFRESH_STATIC, NULL },
364 { WPS_TOKEN_TUNER_CURFREQ, "tf", WPS_REFRESH_DYNAMIC, NULL },
365 { WPS_TOKEN_PRESET_ID, "Ti", WPS_REFRESH_STATIC, NULL },
366 { WPS_TOKEN_PRESET_NAME, "Tn", WPS_REFRESH_STATIC, NULL },
367 { WPS_TOKEN_PRESET_FREQ, "Tf", WPS_REFRESH_STATIC, NULL },
368 { WPS_TOKEN_PRESET_COUNT, "Tc", WPS_REFRESH_STATIC, NULL },
369 { WPS_TOKEN_HAVE_RDS, "tx", WPS_REFRESH_STATIC, NULL },
370 #ifdef HAVE_RDS_CAP
371 { WPS_TOKEN_RDS_NAME, "ty", WPS_REFRESH_DYNAMIC, NULL },
372 { WPS_TOKEN_RDS_TEXT, "tz", WPS_REFRESH_DYNAMIC, NULL },
373 #endif
374 #endif /* CONFIG_TUNER */
376 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
377 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
379 #ifdef HAVE_LCD_BITMAP
380 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
381 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
382 { WPS_TOKEN_DRAW_INBUILTBAR, "wi", WPS_REFRESH_DYNAMIC, parse_statusbar_inbuilt },
384 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
386 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
387 parse_image_display },
389 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
390 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
391 #ifdef HAVE_ALBUMART
392 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
393 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
394 #endif
396 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
397 parse_viewport_display },
398 { WPS_TOKEN_UIVIEWPORT_ENABLE, "VI", WPS_REFRESH_STATIC,
399 parse_viewport_display },
400 #ifdef HAVE_LCD_BITMAP
401 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
402 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
403 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
404 #endif
405 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
406 { WPS_TOKEN_VIEWPORT_FGCOLOUR, "Vf", WPS_REFRESH_STATIC, parse_viewportcolour },
407 { WPS_TOKEN_VIEWPORT_BGCOLOUR, "Vb", WPS_REFRESH_STATIC, parse_viewportcolour },
408 #endif
409 { WPS_NO_TOKEN, "V", 0, parse_viewport },
411 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
412 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
413 #endif
414 #endif
416 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
417 parse_setting_and_lang },
418 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
419 parse_setting_and_lang },
420 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
422 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
423 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
424 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
427 /* Recording Tokens */
428 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
429 #ifdef HAVE_RECORDING
430 { WPS_TOKEN_IS_RECORDING, "Rr", WPS_REFRESH_DYNAMIC, NULL },
431 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
432 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
433 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
434 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
435 { WPS_TOKEN_REC_SECONDS, "Rs", WPS_REFRESH_DYNAMIC, NULL },
436 { WPS_TOKEN_REC_MINUTES, "Rn", WPS_REFRESH_DYNAMIC, NULL },
437 { WPS_TOKEN_REC_HOURS, "Rh", WPS_REFRESH_DYNAMIC, NULL },
438 #endif
439 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
440 /* the array MUST end with an empty string (first char is \0) */
444 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
445 * chains require the order to be kept.
447 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
449 if (*list == NULL)
450 *list = item;
451 else
453 struct skin_token_list *t = *list;
454 while (t->next)
455 t = t->next;
456 t->next = item;
460 /* traverse the image linked-list for an image */
461 #ifdef HAVE_LCD_BITMAP
462 struct gui_img* find_image(char label, struct wps_data *data)
464 struct skin_token_list *list = data->images;
465 while (list)
467 struct gui_img *img = (struct gui_img *)list->token->value.data;
468 if (img->label == label)
469 return img;
470 list = list->next;
472 return NULL;
475 #endif
477 /* traverse the viewport linked list for a viewport */
478 struct skin_viewport* find_viewport(char label, struct wps_data *data)
480 struct skin_token_list *list = data->viewports;
481 while (list)
483 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
484 if (vp->label == label)
485 return vp;
486 list = list->next;
488 return NULL;
492 /* create and init a new wpsll item.
493 * passing NULL to token will alloc a new one.
494 * You should only pass NULL for the token when the token type (table above)
495 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
497 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
498 void* token_data)
500 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
501 if (!token)
502 token = skin_buffer_alloc(sizeof(struct wps_token));
503 if (!llitem || !token)
504 return NULL;
505 llitem->next = NULL;
506 llitem->token = token;
507 if (token_data)
508 llitem->token->value.data = token_data;
509 return llitem;
512 /* Returns the number of chars that should be skipped to jump
513 immediately after the first eol, i.e. to the start of the next line */
514 static int skip_end_of_line(const char *wps_bufptr)
516 line_number++;
517 int skip = 0;
518 while(*(wps_bufptr + skip) != '\n')
519 skip++;
520 return ++skip;
523 /* Starts a new subline in the current line during parsing */
524 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
526 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
527 if (!subline)
528 return false;
530 subline->first_token_idx = curr_token;
531 subline->next = NULL;
533 subline->line_type = 0;
534 subline->time_mult = 0;
536 line->curr_subline->last_token_idx = curr_token-1;
537 line->curr_subline->next = subline;
538 line->curr_subline = subline;
539 return true;
542 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
544 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
545 struct skin_subline *subline = NULL;
546 if (!line)
547 return false;
549 /* init the subline */
550 subline = &line->sublines;
551 subline->first_token_idx = curr_token;
552 subline->next = NULL;
553 subline->line_type = 0;
554 subline->time_mult = 0;
556 /* init the new line */
557 line->curr_subline = &line->sublines;
558 line->next = NULL;
559 line->subline_expire_time = 0;
561 /* connect to curr_line and vp pointers.
562 * 1) close the previous lines subline
563 * 2) connect to vp pointer
564 * 3) connect to curr_line global pointer
566 if (curr_line)
568 curr_line->curr_subline->last_token_idx = curr_token - 1;
569 curr_line->next = line;
570 curr_line->curr_subline = NULL;
572 curr_line = line;
573 if (!vp->lines)
574 vp->lines = line;
575 return true;
578 #ifdef HAVE_LCD_BITMAP
580 static int parse_statusbar_enable(const char *wps_bufptr,
581 struct wps_token *token,
582 struct wps_data *wps_data)
584 (void)token; /* Kill warnings */
585 wps_data->wps_sb_tag = true;
586 wps_data->show_sb_on_wps = true;
587 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
588 viewport_set_defaults(&default_vp->vp, curr_screen);
589 default_vp->vp.font = FONT_UI;
590 return skip_end_of_line(wps_bufptr);
593 static int parse_statusbar_disable(const char *wps_bufptr,
594 struct wps_token *token,
595 struct wps_data *wps_data)
597 (void)token; /* Kill warnings */
598 wps_data->wps_sb_tag = true;
599 wps_data->show_sb_on_wps = false;
600 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
601 viewport_set_fullscreen(&default_vp->vp, curr_screen);
602 default_vp->vp.font = FONT_UI;
603 return skip_end_of_line(wps_bufptr);
606 static int parse_statusbar_inbuilt(const char *wps_bufptr,
607 struct wps_token *token, struct wps_data *wps_data)
609 (void)wps_data;
610 token->value.data = (void*)&curr_vp->vp;
611 return skip_end_of_line(wps_bufptr);
614 static int get_image_id(int c)
616 if(c >= 'a' && c <= 'z')
617 return c - 'a';
618 else if(c >= 'A' && c <= 'Z')
619 return c - 'A' + 26;
620 else
621 return -1;
624 char *get_image_filename(const char *start, const char* bmpdir,
625 char *buf, int buf_size)
627 const char *end = start;
628 int bmpdirlen = strlen(bmpdir);
630 while (*end && *end != ',' && *end != ')')
631 end++;
632 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
634 buf[0] = '\0';
635 return NULL;
638 strcpy(buf, bmpdir);
639 buf[bmpdirlen] = '/';
640 memcpy( &buf[bmpdirlen + 1], start, end - start);
641 buf[bmpdirlen + 1 + end - start] = 0;
643 return buf;
646 static int parse_image_display(const char *wps_bufptr,
647 struct wps_token *token,
648 struct wps_data *wps_data)
650 char label = wps_bufptr[1];
651 int subimage;
652 struct gui_img *img;;
654 /* sanity check */
655 img = find_image(label, wps_data);
656 if (!img)
658 token->value.i = label; /* so debug works */
659 return WPS_ERROR_INVALID_PARAM;
662 if ((subimage = get_image_id(wps_bufptr[2])) != -1)
664 if (subimage >= img->num_subimages)
665 return WPS_ERROR_INVALID_PARAM;
667 /* Store sub-image number to display in high bits */
668 token->value.i = label | (subimage << 8);
669 return 4; /* We have consumed 2 bytes */
670 } else {
671 token->value.i = label;
672 return 3; /* We have consumed 1 byte */
676 static int parse_image_load(const char *wps_bufptr,
677 struct wps_token *token,
678 struct wps_data *wps_data)
680 const char *ptr = wps_bufptr;
681 const char* filename;
682 const char* id;
683 int x,y;
684 struct gui_img *img;
686 /* format: %x|n|filename.bmp|x|y|
687 or %xl|n|filename.bmp|x|y|
688 or %xl|n|filename.bmp|x|y|num_subimages|
691 if (*ptr != '(')
692 return WPS_ERROR_INVALID_PARAM;
694 ptr++;
696 if (!(ptr = parse_list("ssdd", NULL, ',', ptr, &id, &filename, &x, &y)))
697 return WPS_ERROR_INVALID_PARAM;
699 /* Check there is a terminating ) */
700 if (*ptr != ')' && *ptr != ',')
701 return WPS_ERROR_INVALID_PARAM;
703 /* check the image number and load state */
704 if(find_image(*id, wps_data))
706 /* Invalid image ID */
707 return WPS_ERROR_INVALID_PARAM;
709 img = skin_buffer_alloc(sizeof(struct gui_img));
710 if (!img)
711 return WPS_ERROR_INVALID_PARAM;
712 /* save a pointer to the filename */
713 img->bm.data = (char*)filename;
714 img->label = *id;
715 img->x = x;
716 img->y = y;
717 img->num_subimages = 1;
718 img->always_display = false;
720 /* save current viewport */
721 img->vp = &curr_vp->vp;
723 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
725 img->always_display = true;
727 else if (*ptr == ',')
729 /* Parse the (optional) number of sub-images */
730 ptr++;
731 img->num_subimages = atoi(ptr);
732 if (img->num_subimages <= 0)
733 return WPS_ERROR_INVALID_PARAM;
735 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
736 if (!item)
737 return WPS_ERROR_INVALID_PARAM;
738 add_to_ll_chain(&wps_data->images, item);
740 /* Skip the rest of the line */
741 return skip_end_of_line(wps_bufptr);
743 struct skin_font {
744 int id; /* the id from font_load */
745 char *name; /* filename without path and extension */
747 static struct skin_font skinfonts[MAXUSERFONTS];
748 static int parse_font_load(const char *wps_bufptr,
749 struct wps_token *token, struct wps_data *wps_data)
751 (void)wps_data; (void)token;
752 const char *ptr = wps_bufptr;
753 int id;
754 char *filename;
756 if (*ptr != '(')
757 return WPS_ERROR_INVALID_PARAM;
759 ptr++;
761 if (!(ptr = parse_list("ds", NULL, ',', ptr, &id, &filename)))
762 return WPS_ERROR_INVALID_PARAM;
764 /* Check there is a terminating | */
765 if (*ptr != ')')
766 return WPS_ERROR_INVALID_PARAM;
768 if (id <= FONT_UI || id >= MAXFONTS-1)
769 return WPS_ERROR_INVALID_PARAM;
770 #if defined(DEBUG) || defined(SIMULATOR)
771 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
773 DEBUGF("font id %d already being used\n", id);
775 #endif
776 /* make sure the filename contains .fnt,
777 * we dont actually use it, but require it anyway */
778 ptr = strchr(filename, '.');
779 if (!ptr || strncmp(ptr, ".fnt)", 5))
780 return WPS_ERROR_INVALID_PARAM;
781 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
782 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
784 return skip_end_of_line(wps_bufptr);
788 static int parse_viewport_display(const char *wps_bufptr,
789 struct wps_token *token,
790 struct wps_data *wps_data)
792 (void)wps_data;
793 char letter = wps_bufptr[1];
795 if (letter < 'a' || letter > 'z')
797 /* invalid viewport tag */
798 return WPS_ERROR_INVALID_PARAM;
800 token->value.i = letter;
801 return 3;
804 #ifdef HAVE_LCD_BITMAP
805 static int parse_playlistview_text(struct playlistviewer *viewer,
806 enum info_line_type line, char* text)
808 int cur_string = 0;
809 const struct wps_tag *tag;
810 int taglen = 0;
811 const char *start = text;
812 if (*text != ',')
813 return -1;
814 text++;
815 viewer->lines[line].count = 0;
816 viewer->lines[line].scroll = false;
817 while (*text != ',' && *text != ')')
819 if (*text == '%') /* it is a token of some type */
821 text++;
822 taglen = 0;
823 switch(*text)
825 case '%':
826 case '<':
827 case '|':
828 case '>':
829 case ';':
830 case '#':
831 case '(':
832 case ')':
833 case ',':
834 /* escaped characters */
835 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
836 viewer->lines[line].strings[cur_string][0] = *text;
837 viewer->lines[line].strings[cur_string++][1] = '\0';
838 text++;
839 break;
840 default:
841 for (tag = all_tags;
842 strncmp(text, tag->name, strlen(tag->name)) != 0;
843 tag++) ;
844 /* %s isnt stored as a tag so manually check for it */
845 if (tag->type == WPS_NO_TOKEN)
847 if (!strncmp(tag->name, "s", 1))
849 viewer->lines[line].scroll = true;
850 taglen = 1;
853 else if (tag->type == WPS_TOKEN_UNKNOWN)
855 int i = 0;
856 /* just copy the string */
857 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
858 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != ',' && text[i] != ')' && text[i] != '%')
860 viewer->lines[line].strings[cur_string][i] = text[i];
861 i++;
863 viewer->lines[line].strings[cur_string][i] = '\0';
864 cur_string++;
865 taglen = i;
867 else
869 if (tag->parse_func)
871 /* unsupported tag, reject */
872 return -1;
874 taglen = strlen(tag->name);
875 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
877 text += taglen;
880 else
882 /* regular string */
883 int i = 0;
884 /* just copy the string */
885 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
886 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != ',' && text[i] != ')' && text[i] != '%')
888 viewer->lines[line].strings[cur_string][i] = text[i];
889 i++;
891 viewer->lines[line].strings[cur_string][i] = '\0';
892 cur_string++;
893 text += i;
896 return text - start;
899 static int parse_playlistview(const char *wps_bufptr,
900 struct wps_token *token, struct wps_data *wps_data)
902 (void)wps_data;
903 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
904 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
905 char *ptr = strchr(wps_bufptr, '(');
906 int length;
907 if (!viewer || !ptr)
908 return WPS_ERROR_INVALID_PARAM;
909 viewer->vp = &curr_vp->vp;
910 viewer->show_icons = true;
911 viewer->start_offset = atoi(ptr+1);
912 token->value.data = (void*)viewer;
913 ptr = strchr(ptr+1, ',');
914 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
915 if (length < 0)
916 return WPS_ERROR_INVALID_PARAM;
917 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
918 if (length < 0)
919 return WPS_ERROR_INVALID_PARAM;
921 return skip_end_of_line(wps_bufptr);
923 #endif
925 static int parse_viewport(const char *wps_bufptr,
926 struct wps_token *token,
927 struct wps_data *wps_data)
929 (void)token; /* Kill warnings */
930 const char *ptr = wps_bufptr;
932 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
934 /* check for the optional letter to signify its a hideable viewport */
935 /* %Vl|<label>|<rest of tags>| */
936 skin_vp->hidden_flags = 0;
937 skin_vp->label = VP_NO_LABEL;
938 skin_vp->lines = NULL;
939 if (curr_line)
941 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
942 - (wps_data->num_tokens > 0 ? 1 : 0);
945 curr_line = NULL;
946 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
947 return WPS_ERROR_INVALID_PARAM;
949 if (*ptr == 'i')
951 if (*(ptr+1) == '(')
953 char label = *(ptr+2);
954 if (label >= 'a' && label <= 'z')
956 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
957 skin_vp->label = VP_INFO_LABEL|label;
958 ptr += 3;
960 else
962 if (label != '-')
963 return WPS_ERROR_INVALID_PARAM;
964 skin_vp->label = VP_INFO_LABEL|VP_DEFAULT_LABEL;
965 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
966 ptr += 3;
969 else
970 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
973 else if (*ptr == 'l')
975 if (*(ptr+1) == '(')
977 char label = *(ptr+2);
978 if (label >= 'a' && label <= 'z')
980 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
981 skin_vp->label = label;
983 else
984 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
985 ptr += 3;
988 if (*ptr != ',' && *ptr != '(')
989 return WPS_ERROR_INVALID_PARAM;
991 ptr++;
992 struct viewport *vp = &skin_vp->vp;
993 /* format: %V|x|y|width|height|font| */
994 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, ',')))
995 return WPS_ERROR_INVALID_PARAM;
997 /* Check for trailing ) */
998 if (*ptr != ')')
999 return WPS_ERROR_INVALID_PARAM;
1001 if (follow_lang_direction && lang_is_rtl())
1003 vp->flags |= VP_FLAG_ALIGN_RIGHT;
1004 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
1006 else
1007 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
1009 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1010 skin_vp->start_fgcolour = vp->fg_pattern;
1011 skin_vp->start_bgcolour = vp->bg_pattern;
1012 #endif
1014 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
1015 if (!list)
1016 return WPS_ERROR_INVALID_PARAM;
1017 add_to_ll_chain(&wps_data->viewports, list);
1018 curr_vp = skin_vp;
1019 /* Skip the rest of the line */
1020 return skip_end_of_line(wps_bufptr);
1022 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1023 static int parse_viewportcolour(const char *wps_bufptr,
1024 struct wps_token *token, struct wps_data *wps_data)
1026 (void)wps_data;
1027 const char *ptr = wps_bufptr;
1028 struct viewport_colour *colour = skin_buffer_alloc(sizeof(struct viewport_colour));
1029 uint32_t set;
1030 if (*ptr != '(' || !colour)
1031 return -1;
1032 ptr++;
1033 if (!(ptr = parse_list("c", &set, ',', ptr, &colour->colour)))
1034 return -1;
1035 if (*ptr != ')')
1036 return -1;
1037 if (!set)
1038 colour->colour = get_viewport_default_colour(curr_screen,
1039 token->type == WPS_TOKEN_VIEWPORT_FGCOLOUR);
1040 colour->vp = &curr_vp->vp;
1041 token->value.data = colour;
1042 ptr++;
1043 return ptr - wps_bufptr;
1046 static int parse_image_special(const char *wps_bufptr,
1047 struct wps_token *token,
1048 struct wps_data *wps_data)
1050 (void)wps_data; /* kill warning */
1051 (void)token;
1052 const char *pos = NULL;
1053 const char *newline;
1054 bool error = false;
1056 pos = strchr(wps_bufptr + 1, ')');
1057 newline = strchr(wps_bufptr, '\n');
1059 error = (pos > newline);
1061 #if LCD_DEPTH > 1
1062 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
1064 /* format: %X|filename.bmp| or %Xd */
1065 if (*(wps_bufptr+1) == 'd')
1067 wps_data->backdrop = NULL;
1068 return skip_end_of_line(wps_bufptr);
1070 else if (!error)
1071 wps_data->backdrop = (char*)wps_bufptr + 1;
1073 #endif
1074 if (error)
1075 return WPS_ERROR_INVALID_PARAM;
1076 /* Skip the rest of the line */
1077 return skip_end_of_line(wps_bufptr);
1079 #endif
1081 #endif /* HAVE_LCD_BITMAP */
1083 static int parse_setting_and_lang(const char *wps_bufptr,
1084 struct wps_token *token,
1085 struct wps_data *wps_data)
1087 /* NOTE: both the string validations that happen in here will
1088 * automatically PASS on checkwps because its too hard to get
1089 * settings_list.c and englinsh.lang built for it.
1090 * If that ever changes remove the #ifndef __PCTOOL__'s here
1092 (void)wps_data;
1093 const char *ptr = wps_bufptr;
1094 const char *end;
1095 int i = 0;
1096 char temp[64];
1098 /* Find the setting's cfg_name */
1099 if (*ptr != '(')
1100 return WPS_ERROR_INVALID_PARAM;
1101 ptr++;
1102 end = strchr(ptr,')');
1103 if (!end || (size_t)(end-ptr+1) > sizeof temp)
1104 return WPS_ERROR_INVALID_PARAM;
1105 strlcpy(temp, ptr,end-ptr+1);
1107 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1109 #ifndef __PCTOOL__
1110 i = lang_english_to_id(temp);
1111 if (i < 0)
1112 return WPS_ERROR_INVALID_PARAM;
1113 #endif
1115 else
1117 /* Find the setting */
1118 for (i=0; i<nb_settings; i++)
1119 if (settings[i].cfg_name &&
1120 !strcmp(settings[i].cfg_name, temp))
1121 break;
1122 #ifndef __PCTOOL__
1123 if (i == nb_settings)
1124 return WPS_ERROR_INVALID_PARAM;
1125 #endif
1127 /* Store the setting number */
1128 token->value.i = i;
1130 /* Skip the rest of the line */
1131 return end-ptr+2;
1135 static int parse_dir_level(const char *wps_bufptr,
1136 struct wps_token *token,
1137 struct wps_data *wps_data)
1139 char val[] = { wps_bufptr[1], '\0' };
1140 if (wps_bufptr[0] != '(' || wps_bufptr[2] != ')')
1141 return WPS_ERROR_INVALID_PARAM;
1142 token->value.i = atoi(val);
1143 (void)wps_data; /* Kill warnings */
1144 return 3;
1147 static int parse_timeout(const char *wps_bufptr,
1148 struct wps_token *token,
1149 struct wps_data *wps_data)
1151 int skip = 0;
1152 int val = 0;
1153 bool have_point = false;
1154 bool have_tenth = false;
1156 (void)wps_data; /* Kill the warning */
1157 if (*wps_bufptr == '(')
1159 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1161 if (*wps_bufptr != '.')
1163 val *= 10;
1164 val += *wps_bufptr - '0';
1165 if (have_point)
1167 have_tenth = true;
1168 wps_bufptr++;
1169 skip++;
1170 break;
1173 else
1174 have_point = true;
1176 wps_bufptr++;
1177 skip++;
1179 if (*wps_bufptr != ')')
1180 return -1;
1182 if (have_tenth == false)
1183 val *= 10;
1185 if (val == 0 && skip == 0)
1187 /* decide what to do if no value was specified */
1188 switch (token->type)
1190 case WPS_TOKEN_SUBLINE_TIMEOUT:
1191 return -1;
1192 case WPS_TOKEN_BUTTON_VOLUME:
1193 case WPS_TOKEN_TRACK_STARTING:
1194 case WPS_TOKEN_TRACK_ENDING:
1195 val = 10;
1196 break;
1199 token->value.i = val;
1201 return skip;
1204 static int parse_progressbar(const char *wps_bufptr,
1205 struct wps_token *token,
1206 struct wps_data *wps_data)
1208 /* %pb or %pb|filename|x|y|width|height|
1209 using - for any of the params uses "sane" values */
1210 #ifdef HAVE_LCD_BITMAP
1211 enum {
1212 PB_FILENAME = 0,
1213 PB_X,
1214 PB_Y,
1215 PB_WIDTH,
1216 PB_HEIGHT
1218 const char *filename;
1219 int x, y, height, width;
1220 uint32_t set = 0;
1221 const char *ptr = wps_bufptr;
1222 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1223 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1225 if (!pb || !item)
1226 return WPS_ERROR_INVALID_PARAM;
1228 struct viewport *vp = &curr_vp->vp;
1229 /* we need to know what line number (viewport relative) this pb is,
1230 * so count them... */
1231 int line_num = -1;
1232 struct skin_line *line = curr_vp->lines;
1233 while (line)
1235 line_num++;
1236 line = line->next;
1238 pb->vp = vp;
1239 pb->have_bitmap_pb = false;
1240 pb->bm.data = NULL; /* no bitmap specified */
1241 pb->follow_lang_direction = follow_lang_direction > 0;
1242 pb->draw = false;
1244 if (*wps_bufptr != '(') /* regular old style */
1246 pb->x = 0;
1247 pb->width = vp->width;
1248 pb->height = SYSFONT_HEIGHT-2;
1249 pb->y = -line_num - 1; /* Will be computed during the rendering */
1250 if (token->type == WPS_TOKEN_VOLUME || token->type == WPS_TOKEN_BATTERY_PERCENT)
1251 return 0; /* dont add it, let the regular token handling do the work */
1252 pb->type = token->type;
1253 add_to_ll_chain(&wps_data->progressbars, item);
1254 return 0;
1256 ptr = wps_bufptr + 1;
1258 if (!(ptr = parse_list("sdddd", &set, ',', ptr, &filename,
1259 &x, &y, &width, &height)))
1261 /* If we are in a conditional then we probably don't want to fail
1262 * if the above doesnt work. So assume the | is breaking the conditional
1263 * and move on. The next token will fail if this is incorrect. */
1264 if (level >= 0)
1265 return 0;
1266 return WPS_ERROR_INVALID_PARAM;
1269 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1270 pb->bm.data = (char*)filename;
1272 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1273 pb->x = x;
1274 else
1275 pb->x = vp->x;
1277 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1279 /* A zero width causes a divide-by-zero error later, so reject it */
1280 if (width == 0)
1281 return WPS_ERROR_INVALID_PARAM;
1283 pb->width = width;
1285 else
1286 pb->width = vp->width - pb->x;
1288 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1290 /* A zero height makes no sense - reject it */
1291 if (height == 0)
1292 return WPS_ERROR_INVALID_PARAM;
1294 pb->height = height;
1296 else
1298 if (vp->font > FONT_UI)
1299 pb->height = -1; /* calculate at display time */
1300 else
1302 #ifndef __PCTOOL__
1303 pb->height = font_get(vp->font)->height;
1304 #else
1305 pb->height = 8;
1306 #endif
1310 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1311 pb->y = y;
1312 else
1313 pb->y = -line_num - 1; /* Will be computed during the rendering */
1315 add_to_ll_chain(&wps_data->progressbars, item);
1316 if (token->type == WPS_TOKEN_VOLUME)
1317 token->type = WPS_TOKEN_VOLUMEBAR;
1318 else if (token->type == WPS_TOKEN_BATTERY_PERCENT)
1319 token->type = WPS_TOKEN_BATTERY_PERCENTBAR;
1320 pb->type = token->type;
1322 return ptr+1-wps_bufptr;
1323 #else
1324 (void)wps_bufptr;
1325 if (token->type != WPS_TOKEN_VOLUME &&
1326 token->type != WPS_TOKEN_BATTERY_PERCENTBAR)
1328 wps_data->full_line_progressbar =
1329 token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
1331 return 0;
1333 #endif
1336 #ifdef HAVE_ALBUMART
1337 static int parse_int(const char *newline, const char **_pos, int *num)
1339 *_pos = parse_list("d", NULL, ',', *_pos, num);
1340 return (!*_pos || *_pos > newline || (**_pos != ',' && **_pos != ')'));
1343 static int parse_albumart_load(const char *wps_bufptr,
1344 struct wps_token *token,
1345 struct wps_data *wps_data)
1347 const char *_pos, *newline;
1348 bool parsing;
1349 struct dim dimensions;
1350 int albumart_slot;
1351 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1352 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1353 (void)token; /* silence warning */
1354 if (!aa)
1355 return skip_end_of_line(wps_bufptr);
1357 /* reset albumart info in wps */
1358 aa->width = -1;
1359 aa->height = -1;
1360 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1361 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1362 aa->vp = &curr_vp->vp;
1364 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1366 newline = strchr(wps_bufptr, '\n');
1368 _pos = wps_bufptr;
1370 if (*_pos != '(')
1371 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1373 ++_pos;
1375 /* initial validation and parsing of x component */
1376 if (parse_int(newline, &_pos, &aa->x))
1377 return WPS_ERROR_INVALID_PARAM;
1379 ++_pos;
1381 /* initial validation and parsing of y component */
1382 if (parse_int(newline, &_pos, &aa->y))
1383 return WPS_ERROR_INVALID_PARAM;
1385 /* parsing width field */
1386 parsing = true;
1387 while (parsing)
1389 /* apply each modifier in turn */
1390 ++_pos;
1391 switch (*_pos)
1393 case 'l':
1394 case 'L':
1395 case '+':
1396 if (swap_for_rtl)
1397 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1398 else
1399 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1400 break;
1401 case 'c':
1402 case 'C':
1403 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1404 break;
1405 case 'r':
1406 case 'R':
1407 case '-':
1408 if (swap_for_rtl)
1409 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1410 else
1411 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1412 break;
1413 case 'd':
1414 case 'D':
1415 case 'i':
1416 case 'I':
1417 case 's':
1418 case 'S':
1419 /* simply ignored */
1420 break;
1421 default:
1422 parsing = false;
1423 break;
1426 /* extract max width data */
1427 if (*_pos != ',')
1429 if (parse_int(newline, &_pos, &aa->width))
1430 return WPS_ERROR_INVALID_PARAM;
1433 /* parsing height field */
1434 parsing = true;
1435 while (parsing)
1437 /* apply each modifier in turn */
1438 ++_pos;
1439 switch (*_pos)
1441 case 't':
1442 case 'T':
1443 case '-':
1444 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1445 break;
1446 case 'c':
1447 case 'C':
1448 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1449 break;
1450 case 'b':
1451 case 'B':
1452 case '+':
1453 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1454 break;
1455 case 'd':
1456 case 'D':
1457 case 'i':
1458 case 'I':
1459 case 's':
1460 case 'S':
1461 /* simply ignored */
1462 break;
1463 default:
1464 parsing = false;
1465 break;
1468 /* extract max height data */
1469 if (*_pos != ',')
1471 if (parse_int(newline, &_pos, &aa->height))
1472 return WPS_ERROR_INVALID_PARAM;
1475 /* if we got here, we parsed everything ok .. ! */
1476 if (aa->width < 0)
1477 aa->width = 0;
1478 else if (aa->width > LCD_WIDTH)
1479 aa->width = LCD_WIDTH;
1481 if (aa->height < 0)
1482 aa->height = 0;
1483 else if (aa->height > LCD_HEIGHT)
1484 aa->height = LCD_HEIGHT;
1486 if (swap_for_rtl)
1487 aa->x = LCD_WIDTH - (aa->x + aa->width);
1489 aa->state = WPS_ALBUMART_LOAD;
1490 aa->draw = false;
1491 wps_data->albumart = aa;
1493 dimensions.width = aa->width;
1494 dimensions.height = aa->height;
1496 albumart_slot = playback_claim_aa_slot(&dimensions);
1498 if (0 <= albumart_slot)
1499 wps_data->playback_aa_slot = albumart_slot;
1501 /* Skip the rest of the line */
1502 return skip_end_of_line(wps_bufptr);
1505 static int parse_albumart_display(const char *wps_bufptr,
1506 struct wps_token *token,
1507 struct wps_data *wps_data)
1509 (void)wps_bufptr;
1510 struct wps_token *prev = token-1;
1511 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1513 token->type = WPS_TOKEN_ALBUMART_FOUND;
1515 else if (wps_data->albumart)
1517 wps_data->albumart->vp = &curr_vp->vp;
1519 #if 0
1520 /* the old code did this so keep it here for now...
1521 * this is to allow the posibility to showing the next tracks AA! */
1522 if (wps_bufptr+1 == 'n')
1523 return 1;
1524 #endif
1525 return 0;
1527 #endif /* HAVE_ALBUMART */
1529 #ifdef HAVE_TOUCHSCREEN
1531 struct touchaction {const char* s; int action;};
1532 static const struct touchaction touchactions[] = {
1533 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1534 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1535 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1536 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1537 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1538 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1539 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1540 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1542 static int parse_touchregion(const char *wps_bufptr,
1543 struct wps_token *token, struct wps_data *wps_data)
1545 (void)token;
1546 unsigned i, imax;
1547 struct touchregion *region = NULL;
1548 const char *ptr = wps_bufptr;
1549 const char *action, *end;
1550 const char pb_string[] = "progressbar";
1551 const char vol_string[] = "volume";
1552 int x,y,w,h;
1553 char temp[20];
1555 /* format: %T|x|y|width|height|action|
1556 * if action starts with & the area must be held to happen
1557 * action is one of:
1558 * play - play/pause playback
1559 * stop - stop playback, exit the wps
1560 * prev - prev track
1561 * next - next track
1562 * ffwd - seek forward
1563 * rwd - seek backwards
1564 * menu - go back to the main menu
1565 * browse - go back to the file/db browser
1566 * shuffle - toggle shuffle mode
1567 * repmode - cycle the repeat mode
1568 * quickscreen - go into the quickscreen
1569 * contextmenu - open the context menu
1570 * playlist - go into the playlist
1571 * pitch - go into the pitchscreen
1572 * volup - increase volume by one step
1573 * voldown - decrease volume by one step
1577 if (*ptr != '(')
1578 return WPS_ERROR_INVALID_PARAM;
1579 ptr++;
1581 if (!(ptr = parse_list("dddds", NULL, ',', ptr, &x, &y, &w, &h, &action)))
1582 return WPS_ERROR_INVALID_PARAM;
1584 /* Check there is a terminating | */
1585 if (*ptr != ')')
1586 return WPS_ERROR_INVALID_PARAM;
1588 region = skin_buffer_alloc(sizeof(struct touchregion));
1589 if (!region)
1590 return WPS_ERROR_INVALID_PARAM;
1592 /* should probably do some bounds checking here with the viewport... but later */
1593 region->action = ACTION_NONE;
1594 region->x = x;
1595 region->y = y;
1596 region->width = w;
1597 region->height = h;
1598 region->wvp = curr_vp;
1599 region->armed = false;
1601 end = strchr(action, ')');
1602 if (!end || (size_t)(end-action+1) > sizeof temp)
1603 return WPS_ERROR_INVALID_PARAM;
1604 strlcpy(temp, action, end-action+1);
1605 action = temp;
1607 if(!strcmp(pb_string, action))
1608 region->type = WPS_TOUCHREGION_SCROLLBAR;
1609 else if(!strcmp(vol_string, action))
1610 region->type = WPS_TOUCHREGION_VOLUME;
1611 else
1613 region->type = WPS_TOUCHREGION_ACTION;
1615 if (*action == '&')
1617 action++;
1618 region->repeat = true;
1620 else
1621 region->repeat = false;
1623 imax = ARRAYLEN(touchactions);
1624 for (i = 0; i < imax; i++)
1626 /* try to match with one of our touchregion screens */
1627 if (!strcmp(touchactions[i].s, action))
1629 region->action = touchactions[i].action;
1630 break;
1633 if (region->action == ACTION_NONE)
1634 return WPS_ERROR_INVALID_PARAM;
1636 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1637 if (!item)
1638 return WPS_ERROR_INVALID_PARAM;
1639 add_to_ll_chain(&wps_data->touchregions, item);
1640 return skip_end_of_line(wps_bufptr);
1642 #endif
1644 /* Parse a generic token from the given string. Return the length read */
1645 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1647 int skip = 0, taglen = 0, ret;
1648 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1649 const struct wps_tag *tag;
1650 memset(token, 0, sizeof(*token));
1652 switch(*wps_bufptr)
1655 case '%':
1656 case '<':
1657 case '|':
1658 case '>':
1659 case ';':
1660 case '#':
1661 case ')':
1662 case '(':
1663 case ',':
1664 /* escaped characters */
1665 token->type = WPS_TOKEN_CHARACTER;
1666 token->value.c = *wps_bufptr;
1667 taglen = 1;
1668 wps_data->num_tokens++;
1669 break;
1671 case '?':
1672 /* conditional tag */
1673 token->type = WPS_TOKEN_CONDITIONAL;
1674 level++;
1675 condindex[level] = wps_data->num_tokens;
1676 numoptions[level] = 1;
1677 wps_data->num_tokens++;
1678 ret = parse_token(wps_bufptr + 1, wps_data);
1679 if (ret < 0) return ret;
1680 taglen = 1 + ret;
1681 break;
1683 default:
1684 /* find what tag we have */
1685 for (tag = all_tags;
1686 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1687 tag++) ;
1689 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1690 token->type = tag->type;
1691 curr_line->curr_subline->line_type |= tag->refresh_type;
1693 /* if the tag has a special parsing function, we call it */
1694 if (tag->parse_func)
1696 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1697 if (ret < 0) return ret;
1698 skip += ret;
1701 /* Some tags we don't want to save as tokens */
1702 if (tag->type == WPS_NO_TOKEN)
1703 break;
1705 /* tags that start with 'F', 'I' or 'D' are for the next file */
1706 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1707 *(tag->name) == 'D')
1708 token->next = true;
1710 wps_data->num_tokens++;
1711 break;
1714 skip += taglen;
1715 return skip;
1720 * Returns the number of bytes to skip the buf pointer to access the false
1721 * branch in a _binary_ conditional
1723 * That is:
1724 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1725 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1727 * depending on the features of a target it's not called from check_feature_tag,
1728 * hence the __attribute__ or it issues compiler warnings
1732 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1733 static int find_false_branch(const char *wps_bufptr)
1735 const char *buf = wps_bufptr;
1736 /* wps_bufptr is after the opening '<', hence level = 1*/
1737 int level = 1;
1738 char ch;
1741 ch = *buf;
1742 if (ch == '%')
1743 { /* filter out the characters we check later if they're printed
1744 * as literals */
1745 ch = *(++buf);
1746 if (ch == '<' || ch == '>' || ch == '|')
1747 continue;
1748 /* else: some tags/printed literals we skip over */
1750 else if (ch == '<') /* nested conditional */
1751 level++;
1752 else if (ch == '>')
1753 { /* closed our or a nested conditional,
1754 * do NOT skip over the '>' so that wps_parse() sees it for closing
1755 * if it is the closing one for our conditional */
1756 level--;
1758 else if (ch == '|' && level == 1)
1759 { /* we found our separator, point before and get out */
1760 break;
1762 /* if level is 0, we don't have a false branch */
1763 } while (level > 0 && *(++buf));
1765 return buf - wps_bufptr;
1769 * returns the number of bytes to get the appropriate branch of a binary
1770 * conditional
1772 * That means:
1773 * - if a feature is available, it returns 0 to not skip anything
1774 * - if the feature is not available, skip to the false branch and don't
1775 * parse the true branch at all
1777 * */
1778 static int check_feature_tag(const char *wps_bufptr, const int type)
1780 (void)wps_bufptr;
1781 switch (type)
1783 case WPS_TOKEN_RTC_PRESENT:
1784 #if CONFIG_RTC
1785 return 0;
1786 #else
1787 return find_false_branch(wps_bufptr);
1788 #endif
1789 case WPS_TOKEN_HAVE_RECORDING:
1790 #ifdef HAVE_RECORDING
1791 return 0;
1792 #else
1793 return find_false_branch(wps_bufptr);
1794 #endif
1795 case WPS_TOKEN_HAVE_TUNER:
1796 #if CONFIG_TUNER
1797 if (radio_hardware_present())
1798 return 0;
1799 #endif
1800 return find_false_branch(wps_bufptr);
1802 default: /* not a tag we care about, just don't skip */
1803 return 0;
1808 /* Parses the WPS.
1809 data is the pointer to the structure where the parsed WPS should be stored.
1810 It is initialised.
1811 wps_bufptr points to the string containing the WPS tags */
1812 #define TOKEN_BLOCK_SIZE 128
1813 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1815 if (!data || !wps_bufptr || !*wps_bufptr)
1816 return false;
1817 enum wps_parse_error fail = PARSE_OK;
1818 int ret;
1819 int max_tokens = TOKEN_BLOCK_SIZE;
1820 size_t buf_free = 0;
1821 line_number = 0;
1822 level = -1;
1824 /* allocate enough RAM for a reasonable skin, grow as needed.
1825 * Free any used RAM before loading the images to be 100% RAM efficient */
1826 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1827 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1828 return false;
1829 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1830 data->num_tokens = 0;
1832 #if LCD_DEPTH > 1
1833 /* Backdrop defaults to the setting unless %X is used, so set it now */
1834 if (global_settings.backdrop_file[0])
1836 data->backdrop = "-";
1838 #endif
1840 while (*wps_bufptr && !fail)
1842 if (follow_lang_direction)
1843 follow_lang_direction--;
1844 /* first make sure there is enough room for tokens */
1845 if (max_tokens <= data->num_tokens + 5)
1847 int extra_tokens = TOKEN_BLOCK_SIZE;
1848 size_t needed = extra_tokens * sizeof(struct wps_token);
1849 /* do some smarts here to grow the array a bit */
1850 if (skin_buffer_freespace() < needed)
1852 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1853 break;
1855 skin_buffer_increment(needed, false);
1856 max_tokens += extra_tokens;
1859 switch(*wps_bufptr++)
1862 /* Regular tag */
1863 case '%':
1864 if ((ret = parse_token(wps_bufptr, data)) < 0)
1866 fail = PARSE_FAIL_COND_INVALID_PARAM;
1867 break;
1869 else if (level >= WPS_MAX_COND_LEVEL - 1)
1871 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1872 break;
1874 wps_bufptr += ret;
1875 break;
1877 /* Alternating sublines separator */
1878 case ';':
1879 if (level >= 0) /* there are unclosed conditionals */
1881 fail = PARSE_FAIL_UNCLOSED_COND;
1882 break;
1885 if (!skin_start_new_subline(curr_line, data->num_tokens))
1886 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1888 break;
1890 /* Conditional list start */
1891 case '<':
1892 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1894 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1895 break;
1897 wps_bufptr += check_feature_tag(wps_bufptr,
1898 data->tokens[data->num_tokens-1].type);
1899 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1900 lastcond[level] = data->num_tokens++;
1901 break;
1903 /* Conditional list end */
1904 case '>':
1905 if (level < 0) /* not in a conditional, invalid char */
1907 fail = PARSE_FAIL_INVALID_CHAR;
1908 break;
1911 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1912 if (lastcond[level])
1913 data->tokens[lastcond[level]].value.i = data->num_tokens;
1914 else
1916 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1917 break;
1920 lastcond[level] = 0;
1921 data->num_tokens++;
1922 data->tokens[condindex[level]].value.i = numoptions[level];
1923 level--;
1924 break;
1926 /* Conditional list option */
1927 case '|':
1928 if (level < 0) /* not in a conditional, invalid char */
1930 fail = PARSE_FAIL_INVALID_CHAR;
1931 break;
1934 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1935 if (lastcond[level])
1936 data->tokens[lastcond[level]].value.i = data->num_tokens;
1937 else
1939 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1940 break;
1943 lastcond[level] = data->num_tokens;
1944 numoptions[level]++;
1945 data->num_tokens++;
1946 break;
1948 /* Comment */
1949 case '#':
1950 if (level >= 0) /* there are unclosed conditionals */
1952 fail = PARSE_FAIL_UNCLOSED_COND;
1953 break;
1956 wps_bufptr += skip_end_of_line(wps_bufptr);
1957 break;
1959 /* End of this line */
1960 case '\n':
1961 if (level >= 0) /* there are unclosed conditionals */
1963 fail = PARSE_FAIL_UNCLOSED_COND;
1964 break;
1966 /* add a new token for the \n so empty lines are correct */
1967 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1968 data->tokens[data->num_tokens].value.c = '\n';
1969 data->tokens[data->num_tokens].next = false;
1970 data->num_tokens++;
1972 if (!skin_start_new_line(curr_vp, data->num_tokens))
1974 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1975 break;
1977 line_number++;
1979 break;
1981 /* String */
1982 default:
1984 unsigned int len = 1;
1985 const char *string_start = wps_bufptr - 1;
1987 /* find the length of the string */
1988 while (*wps_bufptr && *wps_bufptr != '#' &&
1989 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1990 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1991 *wps_bufptr != '|' && *wps_bufptr != '\n')
1993 wps_bufptr++;
1994 len++;
1997 /* look if we already have that string */
1998 char *str;
1999 bool found = false;
2000 struct skin_token_list *list = data->strings;
2001 while (list)
2003 str = (char*)list->token->value.data;
2004 found = (strlen(str) == len &&
2005 strncmp(string_start, str, len) == 0);
2006 if (found)
2007 break; /* break here because the list item is
2008 used if its found */
2009 list = list->next;
2011 /* If a matching string is found, found is true and i is
2012 the index of the string. If not, found is false */
2014 if (!found)
2016 /* new string */
2017 str = (char*)skin_buffer_alloc(len+1);
2018 if (!str)
2020 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2021 break;
2023 strlcpy(str, string_start, len+1);
2024 struct skin_token_list *item =
2025 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
2026 if(!item)
2028 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2029 break;
2031 add_to_ll_chain(&data->strings, item);
2033 else
2035 /* another occurrence of an existing string */
2036 data->tokens[data->num_tokens].value.data = list->token->value.data;
2038 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
2039 data->num_tokens++;
2041 break;
2045 if (!fail && level >= 0) /* there are unclosed conditionals */
2046 fail = PARSE_FAIL_UNCLOSED_COND;
2048 if (*wps_bufptr && !fail)
2049 /* one of the limits of the while loop was exceeded */
2050 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2052 /* Success! */
2053 curr_line->curr_subline->last_token_idx = data->num_tokens;
2054 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
2055 /* freeup unused tokens */
2056 skin_buffer_free_from_front(sizeof(struct wps_token)
2057 * (max_tokens - data->num_tokens));
2059 #if defined(DEBUG) || defined(SIMULATOR)
2060 if (debug)
2062 print_debug_info(data, fail, line_number);
2063 debug_skin_usage();
2065 #else
2066 (void)debug;
2067 #endif
2069 return (fail == 0);
2074 * initial setup of wps_data; does reset everything
2075 * except fields which need to survive, i.e.
2078 static void skin_data_reset(struct wps_data *wps_data)
2080 #ifdef HAVE_LCD_BITMAP
2081 wps_data->images = NULL;
2082 wps_data->progressbars = NULL;
2083 #endif
2084 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2085 wps_data->backdrop = NULL;
2086 #endif
2087 #ifdef HAVE_TOUCHSCREEN
2088 wps_data->touchregions = NULL;
2089 #endif
2090 wps_data->viewports = NULL;
2091 wps_data->strings = NULL;
2092 #ifdef HAVE_ALBUMART
2093 wps_data->albumart = NULL;
2094 if (wps_data->playback_aa_slot >= 0)
2096 playback_release_aa_slot(wps_data->playback_aa_slot);
2097 wps_data->playback_aa_slot = -1;
2099 #endif
2100 wps_data->tokens = NULL;
2101 wps_data->num_tokens = 0;
2103 #ifdef HAVE_LCD_BITMAP
2104 wps_data->peak_meter_enabled = false;
2105 wps_data->wps_sb_tag = false;
2106 wps_data->show_sb_on_wps = false;
2107 #else /* HAVE_LCD_CHARCELLS */
2108 /* progress bars */
2109 int i;
2110 for (i = 0; i < 8; i++)
2112 wps_data->wps_progress_pat[i] = 0;
2114 wps_data->full_line_progressbar = false;
2115 #endif
2116 wps_data->wps_loaded = false;
2119 #ifdef HAVE_LCD_BITMAP
2120 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2122 (void)wps_data; /* only needed for remote targets */
2123 char img_path[MAX_PATH];
2124 get_image_filename(bitmap->data, bmpdir,
2125 img_path, sizeof(img_path));
2127 /* load the image */
2128 int format;
2129 #ifdef HAVE_REMOTE_LCD
2130 if (curr_screen == SCREEN_REMOTE)
2131 format = FORMAT_ANY|FORMAT_REMOTE;
2132 else
2133 #endif
2134 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2136 size_t max_buf;
2137 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2138 bitmap->data = imgbuf;
2139 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2141 if (ret > 0)
2143 skin_buffer_increment(ret, true);
2144 return true;
2146 else
2148 /* Abort if we can't load an image */
2149 DEBUGF("Couldn't load '%s'\n", img_path);
2150 return false;
2154 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2156 struct skin_token_list *list;
2157 bool retval = true; /* return false if a single image failed to load */
2158 /* do the progressbars */
2159 list = wps_data->progressbars;
2160 while (list)
2162 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2163 if (pb->bm.data)
2165 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2166 if (!pb->have_bitmap_pb) /* no success */
2167 retval = false;
2169 list = list->next;
2171 /* regular images */
2172 list = wps_data->images;
2173 while (list)
2175 struct gui_img *img = (struct gui_img*)list->token->value.data;
2176 if (img->bm.data)
2178 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2179 if (img->loaded)
2180 img->subimage_height = img->bm.height / img->num_subimages;
2181 else
2182 retval = false;
2184 list = list->next;
2187 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2188 /* Backdrop load scheme:
2189 * 1) %X|filename|
2190 * 2) load the backdrop from settings
2192 if (wps_data->backdrop)
2194 bool needed = wps_data->backdrop[0] != '-';
2195 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2196 bmpdir, curr_screen);
2197 if (!wps_data->backdrop && needed)
2198 retval = false;
2200 #endif /* has backdrop support */
2202 return retval;
2205 static bool skin_load_fonts(struct wps_data *data)
2207 /* don't spit out after the first failue to aid debugging */
2208 bool success = true;
2209 struct skin_token_list *vp_list;
2210 int font_id;
2211 /* walk though each viewport and assign its font */
2212 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2214 /* first, find the viewports that have a non-sys/ui-font font */
2215 struct skin_viewport *skin_vp =
2216 (struct skin_viewport*)vp_list->token->value.data;
2217 struct viewport *vp = &skin_vp->vp;
2220 if (vp->font <= FONT_UI)
2221 { /* the usual case -> built-in fonts */
2222 #ifdef HAVE_REMOTE_LCD
2223 if (vp->font == FONT_UI)
2224 vp->font += curr_screen;
2225 #endif
2226 continue;
2228 font_id = vp->font;
2230 /* now find the corresponding skin_font */
2231 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2232 if (!font->name)
2234 DEBUGF("font %d not specified\n", font_id);
2235 success = false;
2236 continue;
2239 /* load the font - will handle loading the same font again if
2240 * multiple viewports use the same */
2241 if (font->id < 0)
2243 char *dot = strchr(font->name, '.');
2244 *dot = '\0';
2245 font->id = skin_font_load(font->name);
2248 if (font->id < 0)
2250 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2251 font_id, font->name);
2252 success = false;
2253 continue;
2256 /* finally, assign the font_id to the viewport */
2257 vp->font = font->id;
2259 return success;
2262 #endif /* HAVE_LCD_BITMAP */
2264 /* to setup up the wps-data from a format-buffer (isfile = false)
2265 from a (wps-)file (isfile = true)*/
2266 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2267 const char *buf, bool isfile)
2269 char *wps_buffer = NULL;
2270 if (!wps_data || !buf)
2271 return false;
2272 #ifdef HAVE_ALBUMART
2273 int status;
2274 struct mp3entry *curtrack;
2275 long offset;
2276 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2277 if (wps_data->albumart)
2279 old_aa.state = wps_data->albumart->state;
2280 old_aa.height = wps_data->albumart->height;
2281 old_aa.width = wps_data->albumart->width;
2283 #endif
2284 #ifdef HAVE_LCD_BITMAP
2285 int i;
2286 for (i=0;i<MAXUSERFONTS;i++)
2288 skinfonts[i].id = -1;
2289 skinfonts[i].name = NULL;
2291 #endif
2292 #ifdef DEBUG_SKIN_ENGINE
2293 if (isfile && debug_wps)
2295 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2297 #endif
2299 skin_data_reset(wps_data);
2300 wps_data->wps_loaded = false;
2301 curr_screen = screen;
2303 /* alloc default viewport, will be fixed up later */
2304 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2305 if (!curr_vp)
2306 return false;
2307 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2308 if (!list)
2309 return false;
2310 add_to_ll_chain(&wps_data->viewports, list);
2313 /* Initialise the first (default) viewport */
2314 curr_vp->label = VP_DEFAULT_LABEL;
2315 curr_vp->hidden_flags = 0;
2316 curr_vp->lines = NULL;
2318 viewport_set_defaults(&curr_vp->vp, screen);
2319 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2320 curr_vp->start_fgcolour = curr_vp->vp.fg_pattern;
2321 curr_vp->start_bgcolour = curr_vp->vp.bg_pattern;
2322 #endif
2323 #ifdef HAVE_LCD_BITMAP
2324 curr_vp->vp.font = FONT_UI;
2325 #endif
2327 curr_line = NULL;
2328 if (!skin_start_new_line(curr_vp, 0))
2329 return false;
2331 if (isfile)
2333 int fd = open_utf8(buf, O_RDONLY);
2335 if (fd < 0)
2336 return false;
2338 /* get buffer space from the plugin buffer */
2339 size_t buffersize = 0;
2340 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2342 if (!wps_buffer)
2343 return false;
2345 /* copy the file's content to the buffer for parsing,
2346 ensuring that every line ends with a newline char. */
2347 unsigned int start = 0;
2348 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2350 start += strlen(wps_buffer + start);
2351 if (start < buffersize - 1)
2353 wps_buffer[start++] = '\n';
2354 wps_buffer[start] = 0;
2357 close(fd);
2358 if (start <= 0)
2359 return false;
2361 else
2363 wps_buffer = (char*)buf;
2365 /* parse the WPS source */
2366 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2367 skin_data_reset(wps_data);
2368 return false;
2371 #ifdef HAVE_LCD_BITMAP
2372 char bmpdir[MAX_PATH];
2373 if (isfile)
2375 /* get the bitmap dir */
2376 char *dot = strrchr(buf, '.');
2377 strlcpy(bmpdir, buf, dot - buf + 1);
2379 else
2381 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2383 /* load the bitmaps that were found by the parsing */
2384 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2385 !skin_load_fonts(wps_data))
2387 skin_data_reset(wps_data);
2388 return false;
2390 #endif
2391 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2392 status = audio_status();
2393 if (status & AUDIO_STATUS_PLAY)
2395 struct skin_albumart *aa = wps_data->albumart;
2396 if (aa && ((aa->state && !old_aa.state) ||
2397 (aa->state &&
2398 (((old_aa.height != aa->height) ||
2399 (old_aa.width != aa->width))))))
2401 curtrack = audio_current_track();
2402 offset = curtrack->offset;
2403 audio_stop();
2404 if (!(status & AUDIO_STATUS_PAUSE))
2405 audio_play(offset);
2408 #endif
2409 wps_data->wps_loaded = true;
2410 #ifdef DEBUG_SKIN_ENGINE
2411 if (isfile && debug_wps)
2412 debug_skin_usage();
2413 #endif
2414 return true;