SKIN BREAK: %pb, %bl and %pv (bar types) changed so the image is the last param inste...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob561eb978a2718d814ba700b25c9dc0aa76e3555d
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, "Cd", WPS_REFRESH_STATIC, parse_albumart_display },
394 { WPS_TOKEN_ALBUMART_FOUND, "C", WPS_REFRESH_STATIC, NULL },
395 #endif
397 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
398 parse_viewport_display },
399 { WPS_TOKEN_UIVIEWPORT_ENABLE, "VI", WPS_REFRESH_STATIC,
400 parse_viewport_display },
401 #ifdef HAVE_LCD_BITMAP
402 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
403 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
404 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
405 #endif
406 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
407 { WPS_TOKEN_VIEWPORT_FGCOLOUR, "Vf", WPS_REFRESH_STATIC, parse_viewportcolour },
408 { WPS_TOKEN_VIEWPORT_BGCOLOUR, "Vb", WPS_REFRESH_STATIC, parse_viewportcolour },
409 #endif
410 { WPS_NO_TOKEN, "V", 0, parse_viewport },
412 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
413 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
414 #endif
415 #endif
417 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
418 parse_setting_and_lang },
419 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
420 parse_setting_and_lang },
421 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
423 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
424 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
425 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
428 /* Recording Tokens */
429 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
430 #ifdef HAVE_RECORDING
431 { WPS_TOKEN_IS_RECORDING, "Rr", WPS_REFRESH_DYNAMIC, NULL },
432 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
433 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
434 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
435 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
436 { WPS_TOKEN_REC_SECONDS, "Rs", WPS_REFRESH_DYNAMIC, NULL },
437 { WPS_TOKEN_REC_MINUTES, "Rn", WPS_REFRESH_DYNAMIC, NULL },
438 { WPS_TOKEN_REC_HOURS, "Rh", WPS_REFRESH_DYNAMIC, NULL },
439 #endif
440 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
441 /* the array MUST end with an empty string (first char is \0) */
445 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
446 * chains require the order to be kept.
448 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
450 if (*list == NULL)
451 *list = item;
452 else
454 struct skin_token_list *t = *list;
455 while (t->next)
456 t = t->next;
457 t->next = item;
461 /* traverse the image linked-list for an image */
462 #ifdef HAVE_LCD_BITMAP
463 struct gui_img* find_image(char label, struct wps_data *data)
465 struct skin_token_list *list = data->images;
466 while (list)
468 struct gui_img *img = (struct gui_img *)list->token->value.data;
469 if (img->label == label)
470 return img;
471 list = list->next;
473 return NULL;
476 #endif
478 /* traverse the viewport linked list for a viewport */
479 struct skin_viewport* find_viewport(char label, struct wps_data *data)
481 struct skin_token_list *list = data->viewports;
482 while (list)
484 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
485 if (vp->label == label)
486 return vp;
487 list = list->next;
489 return NULL;
493 /* create and init a new wpsll item.
494 * passing NULL to token will alloc a new one.
495 * You should only pass NULL for the token when the token type (table above)
496 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
498 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
499 void* token_data)
501 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
502 if (!token)
503 token = skin_buffer_alloc(sizeof(struct wps_token));
504 if (!llitem || !token)
505 return NULL;
506 llitem->next = NULL;
507 llitem->token = token;
508 if (token_data)
509 llitem->token->value.data = token_data;
510 return llitem;
513 /* Returns the number of chars that should be skipped to jump
514 immediately after the first eol, i.e. to the start of the next line */
515 static int skip_end_of_line(const char *wps_bufptr)
517 line_number++;
518 int skip = 0;
519 while(*(wps_bufptr + skip) != '\n')
520 skip++;
521 return ++skip;
524 /* Starts a new subline in the current line during parsing */
525 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
527 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
528 if (!subline)
529 return false;
531 subline->first_token_idx = curr_token;
532 subline->next = NULL;
534 subline->line_type = 0;
535 subline->time_mult = 0;
537 line->curr_subline->last_token_idx = curr_token-1;
538 line->curr_subline->next = subline;
539 line->curr_subline = subline;
540 return true;
543 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
545 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
546 struct skin_subline *subline = NULL;
547 if (!line)
548 return false;
550 /* init the subline */
551 subline = &line->sublines;
552 subline->first_token_idx = curr_token;
553 subline->next = NULL;
554 subline->line_type = 0;
555 subline->time_mult = 0;
557 /* init the new line */
558 line->curr_subline = &line->sublines;
559 line->next = NULL;
560 line->subline_expire_time = 0;
562 /* connect to curr_line and vp pointers.
563 * 1) close the previous lines subline
564 * 2) connect to vp pointer
565 * 3) connect to curr_line global pointer
567 if (curr_line)
569 curr_line->curr_subline->last_token_idx = curr_token - 1;
570 curr_line->next = line;
571 curr_line->curr_subline = NULL;
573 curr_line = line;
574 if (!vp->lines)
575 vp->lines = line;
576 return true;
579 #ifdef HAVE_LCD_BITMAP
581 static int parse_statusbar_enable(const char *wps_bufptr,
582 struct wps_token *token,
583 struct wps_data *wps_data)
585 (void)token; /* Kill warnings */
586 wps_data->wps_sb_tag = true;
587 wps_data->show_sb_on_wps = true;
588 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
589 viewport_set_defaults(&default_vp->vp, curr_screen);
590 default_vp->vp.font = FONT_UI;
591 return skip_end_of_line(wps_bufptr);
594 static int parse_statusbar_disable(const char *wps_bufptr,
595 struct wps_token *token,
596 struct wps_data *wps_data)
598 (void)token; /* Kill warnings */
599 wps_data->wps_sb_tag = true;
600 wps_data->show_sb_on_wps = false;
601 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
602 viewport_set_fullscreen(&default_vp->vp, curr_screen);
603 default_vp->vp.font = FONT_UI;
604 return skip_end_of_line(wps_bufptr);
607 static int parse_statusbar_inbuilt(const char *wps_bufptr,
608 struct wps_token *token, struct wps_data *wps_data)
610 (void)wps_data;
611 token->value.data = (void*)&curr_vp->vp;
612 return skip_end_of_line(wps_bufptr);
615 static int get_image_id(int c)
617 if(c >= 'a' && c <= 'z')
618 return c - 'a';
619 else if(c >= 'A' && c <= 'Z')
620 return c - 'A' + 26;
621 else
622 return -1;
625 char *get_image_filename(const char *start, const char* bmpdir,
626 char *buf, int buf_size)
628 const char *end = start;
629 int bmpdirlen = strlen(bmpdir);
631 while (*end && *end != ',' && *end != ')')
632 end++;
633 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
635 buf[0] = '\0';
636 return NULL;
639 strcpy(buf, bmpdir);
640 buf[bmpdirlen] = '/';
641 memcpy( &buf[bmpdirlen + 1], start, end - start);
642 buf[bmpdirlen + 1 + end - start] = 0;
644 return buf;
647 static int parse_image_display(const char *wps_bufptr,
648 struct wps_token *token,
649 struct wps_data *wps_data)
651 char label = wps_bufptr[1];
652 int subimage;
653 struct gui_img *img;;
655 /* sanity check */
656 img = find_image(label, wps_data);
657 if (!img)
659 token->value.i = label; /* so debug works */
660 return WPS_ERROR_INVALID_PARAM;
663 if ((subimage = get_image_id(wps_bufptr[2])) != -1)
665 if (subimage >= img->num_subimages)
666 return WPS_ERROR_INVALID_PARAM;
668 /* Store sub-image number to display in high bits */
669 token->value.i = label | (subimage << 8);
670 return 4; /* We have consumed 2 bytes */
671 } else {
672 token->value.i = label;
673 return 3; /* We have consumed 1 byte */
677 static int parse_image_load(const char *wps_bufptr,
678 struct wps_token *token,
679 struct wps_data *wps_data)
681 const char *ptr = wps_bufptr;
682 const char* filename;
683 const char* id;
684 int x,y;
685 struct gui_img *img;
687 /* format: %x|n|filename.bmp|x|y|
688 or %xl|n|filename.bmp|x|y|
689 or %xl|n|filename.bmp|x|y|num_subimages|
692 if (*ptr != '(')
693 return WPS_ERROR_INVALID_PARAM;
695 ptr++;
697 if (!(ptr = parse_list("ssdd", NULL, ',', ptr, &id, &filename, &x, &y)))
698 return WPS_ERROR_INVALID_PARAM;
700 /* Check there is a terminating ) */
701 if (*ptr != ')' && *ptr != ',')
702 return WPS_ERROR_INVALID_PARAM;
704 /* check the image number and load state */
705 if(find_image(*id, wps_data))
707 /* Invalid image ID */
708 return WPS_ERROR_INVALID_PARAM;
710 img = skin_buffer_alloc(sizeof(struct gui_img));
711 if (!img)
712 return WPS_ERROR_INVALID_PARAM;
713 /* save a pointer to the filename */
714 img->bm.data = (char*)filename;
715 img->label = *id;
716 img->x = x;
717 img->y = y;
718 img->num_subimages = 1;
719 img->always_display = false;
721 /* save current viewport */
722 img->vp = &curr_vp->vp;
724 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
726 img->always_display = true;
728 else if (*ptr == ',')
730 /* Parse the (optional) number of sub-images */
731 ptr++;
732 img->num_subimages = atoi(ptr);
733 if (img->num_subimages <= 0)
734 return WPS_ERROR_INVALID_PARAM;
735 /* Check there is a terminating ) */
736 while(isdigit(*ptr))
737 ptr++;
738 if (*ptr != ')')
739 return WPS_ERROR_INVALID_PARAM;
741 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
742 if (!item)
743 return WPS_ERROR_INVALID_PARAM;
744 add_to_ll_chain(&wps_data->images, item);
746 /* Skip the rest of the line */
747 return skip_end_of_line(wps_bufptr);
749 struct skin_font {
750 int id; /* the id from font_load */
751 char *name; /* filename without path and extension */
753 static struct skin_font skinfonts[MAXUSERFONTS];
754 static int parse_font_load(const char *wps_bufptr,
755 struct wps_token *token, struct wps_data *wps_data)
757 (void)wps_data; (void)token;
758 const char *ptr = wps_bufptr;
759 int id;
760 char *filename;
762 if (*ptr != '(')
763 return WPS_ERROR_INVALID_PARAM;
765 ptr++;
767 if (!(ptr = parse_list("ds", NULL, ',', ptr, &id, &filename)))
768 return WPS_ERROR_INVALID_PARAM;
770 /* Check there is a terminating ) */
771 if (*ptr != ')')
772 return WPS_ERROR_INVALID_PARAM;
774 if (id <= FONT_UI || id >= MAXFONTS-1)
775 return WPS_ERROR_INVALID_PARAM;
776 #if defined(DEBUG) || defined(SIMULATOR)
777 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
779 DEBUGF("font id %d already being used\n", id);
781 #endif
782 /* make sure the filename contains .fnt,
783 * we dont actually use it, but require it anyway */
784 ptr = strchr(filename, '.');
785 if (!ptr || strncmp(ptr, ".fnt)", 5))
786 return WPS_ERROR_INVALID_PARAM;
787 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
788 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
790 return skip_end_of_line(wps_bufptr);
794 static int parse_viewport_display(const char *wps_bufptr,
795 struct wps_token *token,
796 struct wps_data *wps_data)
798 (void)wps_data;
799 char letter = wps_bufptr[1];
801 if (letter < 'a' || letter > 'z')
803 /* invalid viewport tag */
804 return WPS_ERROR_INVALID_PARAM;
806 token->value.i = letter;
807 return 3;
810 #ifdef HAVE_LCD_BITMAP
811 static int parse_playlistview_text(struct playlistviewer *viewer,
812 enum info_line_type line, char* text)
814 int cur_string = 0;
815 const struct wps_tag *tag;
816 int taglen = 0;
817 const char *start = text;
818 if (*text != ',')
819 return -1;
820 text++;
821 viewer->lines[line].count = 0;
822 viewer->lines[line].scroll = false;
823 while (*text != ',' && *text != ')')
825 if (*text == '%') /* it is a token of some type */
827 text++;
828 taglen = 0;
829 switch(*text)
831 case '%':
832 case '<':
833 case '|':
834 case '>':
835 case ';':
836 case '#':
837 case '(':
838 case ')':
839 case ',':
840 /* escaped characters */
841 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
842 viewer->lines[line].strings[cur_string][0] = *text;
843 viewer->lines[line].strings[cur_string++][1] = '\0';
844 text++;
845 break;
846 default:
847 for (tag = all_tags;
848 strncmp(text, tag->name, strlen(tag->name)) != 0;
849 tag++) ;
850 /* %s isnt stored as a tag so manually check for it */
851 if (tag->type == WPS_NO_TOKEN)
853 if (!strncmp(tag->name, "s", 1))
855 viewer->lines[line].scroll = true;
856 taglen = 1;
859 else if (tag->type == WPS_TOKEN_UNKNOWN)
861 int i = 0;
862 /* just copy the string */
863 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
864 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != ',' && text[i] != ')' && text[i] != '%')
866 viewer->lines[line].strings[cur_string][i] = text[i];
867 i++;
869 viewer->lines[line].strings[cur_string][i] = '\0';
870 cur_string++;
871 taglen = i;
873 else
875 if (tag->parse_func)
877 /* unsupported tag, reject */
878 return -1;
880 taglen = strlen(tag->name);
881 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
883 text += taglen;
886 else
888 /* regular string */
889 int i = 0;
890 /* just copy the string */
891 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
892 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != ',' && text[i] != ')' && text[i] != '%')
894 viewer->lines[line].strings[cur_string][i] = text[i];
895 i++;
897 viewer->lines[line].strings[cur_string][i] = '\0';
898 cur_string++;
899 text += i;
902 return text - start;
905 static int parse_playlistview(const char *wps_bufptr,
906 struct wps_token *token, struct wps_data *wps_data)
908 (void)wps_data;
909 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
910 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
911 char *ptr = strchr(wps_bufptr, '(');
912 int length;
913 if (!viewer || !ptr)
914 return WPS_ERROR_INVALID_PARAM;
915 viewer->vp = &curr_vp->vp;
916 viewer->show_icons = true;
917 viewer->start_offset = atoi(ptr+1);
918 token->value.data = (void*)viewer;
919 ptr = strchr(ptr+1, ',');
920 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
921 if (length < 0)
922 return WPS_ERROR_INVALID_PARAM;
923 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
924 if (length < 0)
925 return WPS_ERROR_INVALID_PARAM;
927 return skip_end_of_line(wps_bufptr);
929 #endif
931 static int parse_viewport(const char *wps_bufptr,
932 struct wps_token *token,
933 struct wps_data *wps_data)
935 (void)token; /* Kill warnings */
936 const char *ptr = wps_bufptr;
938 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
940 /* check for the optional letter to signify its a hideable viewport */
941 /* %Vl|<label>|<rest of tags>| */
942 skin_vp->hidden_flags = 0;
943 skin_vp->label = VP_NO_LABEL;
944 skin_vp->lines = NULL;
945 if (curr_line)
947 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
948 - (wps_data->num_tokens > 0 ? 1 : 0);
951 curr_line = NULL;
952 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
953 return WPS_ERROR_INVALID_PARAM;
955 if (*ptr == 'i')
957 if (*(ptr+1) == '(')
959 char label = *(ptr+2);
960 if (label >= 'a' && label <= 'z')
962 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
963 skin_vp->label = VP_INFO_LABEL|label;
964 ptr += 3;
966 else
968 if (label != '-')
969 return WPS_ERROR_INVALID_PARAM;
970 skin_vp->label = VP_INFO_LABEL|VP_DEFAULT_LABEL;
971 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
972 ptr += 3;
975 else
976 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
978 else if (*ptr == 'l')
980 if (*(ptr+1) == '(')
982 char label = *(ptr+2);
983 if (label >= 'a' && label <= 'z')
985 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
986 skin_vp->label = label;
988 else
989 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
990 ptr += 3;
993 if (*ptr != ',' && *ptr != '(')
994 return WPS_ERROR_INVALID_PARAM;
996 ptr++;
997 struct viewport *vp = &skin_vp->vp;
998 /* format: %V|x|y|width|height|font| */
999 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, ',')))
1000 return WPS_ERROR_INVALID_PARAM;
1002 /* Check for trailing ) */
1003 if (*ptr != ')')
1004 return WPS_ERROR_INVALID_PARAM;
1005 ptr++;
1007 if (follow_lang_direction && lang_is_rtl())
1009 vp->flags |= VP_FLAG_ALIGN_RIGHT;
1010 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
1012 else
1013 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
1015 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1016 skin_vp->start_fgcolour = vp->fg_pattern;
1017 skin_vp->start_bgcolour = vp->bg_pattern;
1018 #endif
1020 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
1021 if (!list)
1022 return WPS_ERROR_INVALID_PARAM;
1023 add_to_ll_chain(&wps_data->viewports, list);
1024 curr_vp = skin_vp;
1025 /* Skip the rest of the line */
1026 return ptr-wps_bufptr;
1028 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1029 static int parse_viewportcolour(const char *wps_bufptr,
1030 struct wps_token *token, struct wps_data *wps_data)
1032 (void)wps_data;
1033 const char *ptr = wps_bufptr;
1034 int i;
1035 bool found_text;
1036 struct viewport_colour *colour = skin_buffer_alloc(sizeof(struct viewport_colour));
1037 uint32_t set;
1038 if (*ptr != '(' || !colour)
1039 return -1;
1040 ptr++;
1041 if (!(ptr = parse_list("c", &set, ',', ptr, &colour->colour)))
1042 return -1;
1043 if (*ptr != ')')
1044 return -1;
1045 if (!set)
1046 colour->colour = get_viewport_default_colour(curr_screen,
1047 token->type == WPS_TOKEN_VIEWPORT_FGCOLOUR);
1048 colour->vp = &curr_vp->vp;
1049 token->value.data = colour;
1050 /* If there havnt been any text tags between the %V() line and here use
1051 * the colour as the viewport colour. fixes scrolling lines not
1052 * having the correct colour */
1053 i = curr_vp->lines->sublines.first_token_idx;
1054 found_text = false;
1055 while (!found_text && i< curr_vp->lines->sublines.last_token_idx)
1057 if (wps_data->tokens[i++].type != WPS_TOKEN_CHARACTER &&
1058 wps_data->tokens[i++].type != WPS_TOKEN_VIEWPORT_FGCOLOUR &&
1059 wps_data->tokens[i++].type != WPS_TOKEN_VIEWPORT_BGCOLOUR )
1060 found_text = true;
1062 if (!found_text)
1064 if (token->type == WPS_TOKEN_VIEWPORT_FGCOLOUR)
1066 curr_vp->start_fgcolour = colour->colour;
1067 curr_vp->vp.fg_pattern = colour->colour;
1069 else
1071 curr_vp->start_bgcolour = colour->colour;
1072 curr_vp->vp.bg_pattern = colour->colour;
1075 ptr++;
1076 return ptr - wps_bufptr;
1079 static int parse_image_special(const char *wps_bufptr,
1080 struct wps_token *token,
1081 struct wps_data *wps_data)
1083 (void)wps_data; /* kill warning */
1084 (void)token;
1085 const char *pos = NULL;
1086 const char *newline;
1087 bool error = false;
1089 pos = strchr(wps_bufptr + 1, ')');
1090 newline = strchr(wps_bufptr, '\n');
1092 error = (pos > newline);
1094 #if LCD_DEPTH > 1
1095 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
1097 /* format: %X|filename.bmp| or %Xd */
1098 if (!strncmp(wps_bufptr, "(d)", 3))
1100 wps_data->backdrop = NULL;
1101 return skip_end_of_line(wps_bufptr);
1103 else if (!error)
1104 wps_data->backdrop = (char*)wps_bufptr + 1;
1106 #endif
1107 if (error)
1108 return WPS_ERROR_INVALID_PARAM;
1109 /* Skip the rest of the line */
1110 return skip_end_of_line(wps_bufptr);
1112 #endif
1114 #endif /* HAVE_LCD_BITMAP */
1116 static int parse_setting_and_lang(const char *wps_bufptr,
1117 struct wps_token *token,
1118 struct wps_data *wps_data)
1120 /* NOTE: both the string validations that happen in here will
1121 * automatically PASS on checkwps because its too hard to get
1122 * settings_list.c and englinsh.lang built for it.
1123 * If that ever changes remove the #ifndef __PCTOOL__'s here
1125 (void)wps_data;
1126 const char *ptr = wps_bufptr;
1127 const char *end;
1128 int i = 0;
1129 char temp[64];
1131 /* Find the setting's cfg_name */
1132 if (*ptr != '(')
1133 return WPS_ERROR_INVALID_PARAM;
1134 ptr++;
1135 end = strchr(ptr,')');
1136 if (!end || (size_t)(end-ptr+1) > sizeof temp)
1137 return WPS_ERROR_INVALID_PARAM;
1138 strlcpy(temp, ptr,end-ptr+1);
1140 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1142 #ifndef __PCTOOL__
1143 i = lang_english_to_id(temp);
1144 if (i < 0)
1145 return WPS_ERROR_INVALID_PARAM;
1146 #endif
1148 else
1150 /* Find the setting */
1151 for (i=0; i<nb_settings; i++)
1152 if (settings[i].cfg_name &&
1153 !strcmp(settings[i].cfg_name, temp))
1154 break;
1155 #ifndef __PCTOOL__
1156 if (i == nb_settings)
1157 return WPS_ERROR_INVALID_PARAM;
1158 #endif
1160 /* Store the setting number */
1161 token->value.i = i;
1163 /* Skip the rest of the line */
1164 return end-ptr+2;
1168 static int parse_dir_level(const char *wps_bufptr,
1169 struct wps_token *token,
1170 struct wps_data *wps_data)
1172 char val[] = { wps_bufptr[1], '\0' };
1173 if (wps_bufptr[0] != '(' || wps_bufptr[2] != ')')
1174 return WPS_ERROR_INVALID_PARAM;
1175 token->value.i = atoi(val);
1176 (void)wps_data; /* Kill warnings */
1177 return 3;
1180 static int parse_timeout(const char *wps_bufptr,
1181 struct wps_token *token,
1182 struct wps_data *wps_data)
1184 int skip = 0;
1185 int val = 0;
1186 bool have_point = false;
1187 bool have_tenth = false;
1189 (void)wps_data; /* Kill the warning */
1190 if (*wps_bufptr == '(')
1192 wps_bufptr++;
1193 skip++;
1194 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1196 if (*wps_bufptr != '.')
1198 val *= 10;
1199 val += *wps_bufptr - '0';
1200 if (have_point)
1202 have_tenth = true;
1203 wps_bufptr++;
1204 skip++;
1205 break;
1208 else
1209 have_point = true;
1211 wps_bufptr++;
1212 skip++;
1214 if (*wps_bufptr != ')')
1215 return -1;
1216 skip++;
1218 if (have_tenth == false)
1219 val *= 10;
1221 if (val == 0 && skip == 0)
1223 /* decide what to do if no value was specified */
1224 switch (token->type)
1226 case WPS_TOKEN_SUBLINE_TIMEOUT:
1227 return -1;
1228 case WPS_TOKEN_BUTTON_VOLUME:
1229 case WPS_TOKEN_TRACK_STARTING:
1230 case WPS_TOKEN_TRACK_ENDING:
1231 val = 10;
1232 break;
1235 token->value.i = val;
1237 return skip;
1240 static int parse_progressbar(const char *wps_bufptr,
1241 struct wps_token *token,
1242 struct wps_data *wps_data)
1244 /* %pb or %pb|filename|x|y|width|height|
1245 using - for any of the params uses "sane" values */
1246 #ifdef HAVE_LCD_BITMAP
1247 enum {
1248 PB_X = 0,
1249 PB_Y,
1250 PB_WIDTH,
1251 <<<<<<< .mine
1252 PB_HEIGHT,
1253 PB_FILENAME,
1254 =======
1255 PB_HEIGHT,
1256 >>>>>>> .r26726
1258 const char *filename;
1259 int x, y, height, width;
1260 uint32_t set = 0;
1261 const char *ptr = wps_bufptr;
1262 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1263 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1265 if (!pb || !item)
1266 return WPS_ERROR_INVALID_PARAM;
1268 struct viewport *vp = &curr_vp->vp;
1269 /* we need to know what line number (viewport relative) this pb is,
1270 * so count them... */
1271 int line_num = -1;
1272 struct skin_line *line = curr_vp->lines;
1273 while (line)
1275 line_num++;
1276 line = line->next;
1278 if (curr_vp->label != VP_DEFAULT_LABEL)
1279 line_num--;
1280 pb->vp = vp;
1281 pb->have_bitmap_pb = false;
1282 pb->bm.data = NULL; /* no bitmap specified */
1283 pb->follow_lang_direction = follow_lang_direction > 0;
1284 pb->draw = false;
1286 if (*wps_bufptr != '(') /* regular old style */
1288 pb->x = 0;
1289 pb->width = vp->width;
1290 pb->height = SYSFONT_HEIGHT-2;
1291 pb->y = -line_num - 1; /* Will be computed during the rendering */
1292 if (token->type == WPS_TOKEN_VOLUME || token->type == WPS_TOKEN_BATTERY_PERCENT)
1293 return 0; /* dont add it, let the regular token handling do the work */
1294 pb->type = token->type;
1295 add_to_ll_chain(&wps_data->progressbars, item);
1296 return 0;
1298 ptr = wps_bufptr + 1;
1300 if (!(ptr = parse_list("dddds", &set, ',', ptr,
1301 &x, &y, &width, &height, &filename)))
1302 return WPS_ERROR_INVALID_PARAM;
1304 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1305 pb->bm.data = (char*)filename;
1307 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1308 pb->x = x;
1309 else
1310 pb->x = vp->x;
1312 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1314 /* A zero width causes a divide-by-zero error later, so reject it */
1315 if (width == 0)
1316 return WPS_ERROR_INVALID_PARAM;
1318 pb->width = width;
1320 else
1321 pb->width = vp->width - pb->x;
1323 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1325 /* A zero height makes no sense - reject it */
1326 if (height == 0)
1327 return WPS_ERROR_INVALID_PARAM;
1329 pb->height = height;
1331 else
1333 if (vp->font > FONT_UI)
1334 pb->height = -1; /* calculate at display time */
1335 else
1337 #ifndef __PCTOOL__
1338 pb->height = font_get(vp->font)->height;
1339 #else
1340 pb->height = 8;
1341 #endif
1345 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1346 pb->y = y;
1347 else
1348 pb->y = -line_num - 1; /* Will be computed during the rendering */
1350 if (*ptr != ')')
1351 return WPS_ERROR_INVALID_PARAM;
1353 add_to_ll_chain(&wps_data->progressbars, item);
1354 if (token->type == WPS_TOKEN_VOLUME)
1355 token->type = WPS_TOKEN_VOLUMEBAR;
1356 else if (token->type == WPS_TOKEN_BATTERY_PERCENT)
1357 token->type = WPS_TOKEN_BATTERY_PERCENTBAR;
1358 pb->type = token->type;
1360 return ptr+1-wps_bufptr;
1361 #else
1362 (void)wps_bufptr;
1363 if (token->type != WPS_TOKEN_VOLUME &&
1364 token->type != WPS_TOKEN_BATTERY_PERCENTBAR)
1366 wps_data->full_line_progressbar =
1367 token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
1369 return 0;
1371 #endif
1374 #ifdef HAVE_ALBUMART
1375 static int parse_albumart_load(const char *wps_bufptr,
1376 struct wps_token *token,
1377 struct wps_data *wps_data)
1379 const char *ptr = wps_bufptr;
1380 struct dim dimensions;
1381 int albumart_slot;
1382 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1383 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1384 (void)token; /* silence warning */
1385 if (!aa)
1386 return skip_end_of_line(wps_bufptr);
1388 /* reset albumart info in wps */
1389 aa->width = -1;
1390 aa->height = -1;
1391 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1392 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1393 aa->vp = &curr_vp->vp;
1395 if (*ptr != '(')
1396 return WPS_ERROR_INVALID_PARAM;
1397 ptr++;
1398 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1399 if (!(ptr = parse_list("dddd", NULL,',',ptr, &aa->x, &aa->y, &aa->width, &aa->height)))
1400 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1402 /* if we got here, we parsed everything ok .. ! */
1403 if (aa->width < 0)
1404 aa->width = 0;
1405 else if (aa->width > LCD_WIDTH)
1406 aa->width = LCD_WIDTH;
1408 if (aa->height < 0)
1409 aa->height = 0;
1410 else if (aa->height > LCD_HEIGHT)
1411 aa->height = LCD_HEIGHT;
1413 if (swap_for_rtl)
1414 aa->x = LCD_WIDTH - (aa->x + aa->width);
1416 aa->state = WPS_ALBUMART_LOAD;
1417 aa->draw = false;
1418 wps_data->albumart = aa;
1420 dimensions.width = aa->width;
1421 dimensions.height = aa->height;
1423 albumart_slot = playback_claim_aa_slot(&dimensions);
1425 if (0 <= albumart_slot)
1426 wps_data->playback_aa_slot = albumart_slot;
1428 if (*ptr == ',')
1430 ptr++;
1431 switch (*ptr)
1433 case 'l':
1434 case 'L':
1435 if (swap_for_rtl)
1436 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1437 else
1438 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1439 break;
1440 case 'c':
1441 case 'C':
1442 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1443 break;
1444 case 'r':
1445 case 'R':
1446 if (swap_for_rtl)
1447 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1448 else
1449 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1450 break;
1452 ptr++;
1454 if (*ptr == ',')
1456 ptr++;
1457 switch (*ptr)
1459 case 't':
1460 case 'T':
1461 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1462 break;
1463 case 'c':
1464 case 'C':
1465 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1466 break;
1467 case 'b':
1468 case 'B':
1469 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1470 break;
1472 ptr++;
1474 if (*ptr != ')')
1475 return WPS_ERROR_INVALID_PARAM;
1477 return skip_end_of_line(wps_bufptr);
1480 static int parse_albumart_display(const char *wps_bufptr,
1481 struct wps_token *token,
1482 struct wps_data *wps_data)
1484 (void)wps_bufptr;
1485 (void)token;
1486 if (wps_data->albumart)
1488 wps_data->albumart->vp = &curr_vp->vp;
1490 #if 0
1491 /* the old code did this so keep it here for now...
1492 * this is to allow the posibility to showing the next tracks AA! */
1493 if (wps_bufptr+1 == 'n')
1494 return 1;
1495 #endif
1496 return 0;
1498 #endif /* HAVE_ALBUMART */
1500 #ifdef HAVE_TOUCHSCREEN
1502 struct touchaction {const char* s; int action;};
1503 static const struct touchaction touchactions[] = {
1504 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1505 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1506 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1507 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1508 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1509 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1510 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1511 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1513 static int parse_touchregion(const char *wps_bufptr,
1514 struct wps_token *token, struct wps_data *wps_data)
1516 (void)token;
1517 unsigned i, imax;
1518 struct touchregion *region = NULL;
1519 const char *ptr = wps_bufptr;
1520 const char *action, *end;
1521 const char pb_string[] = "progressbar";
1522 const char vol_string[] = "volume";
1523 int x,y,w,h;
1524 char temp[20];
1526 /* format: %T|x|y|width|height|action|
1527 * if action starts with & the area must be held to happen
1528 * action is one of:
1529 * play - play/pause playback
1530 * stop - stop playback, exit the wps
1531 * prev - prev track
1532 * next - next track
1533 * ffwd - seek forward
1534 * rwd - seek backwards
1535 * menu - go back to the main menu
1536 * browse - go back to the file/db browser
1537 * shuffle - toggle shuffle mode
1538 * repmode - cycle the repeat mode
1539 * quickscreen - go into the quickscreen
1540 * contextmenu - open the context menu
1541 * playlist - go into the playlist
1542 * pitch - go into the pitchscreen
1543 * volup - increase volume by one step
1544 * voldown - decrease volume by one step
1548 if (*ptr != '(')
1549 return WPS_ERROR_INVALID_PARAM;
1550 ptr++;
1552 if (!(ptr = parse_list("dddds", NULL, ',', ptr, &x, &y, &w, &h, &action)))
1553 return WPS_ERROR_INVALID_PARAM;
1555 /* Check there is a terminating ) */
1556 if (*ptr != ')')
1557 return WPS_ERROR_INVALID_PARAM;
1559 region = skin_buffer_alloc(sizeof(struct touchregion));
1560 if (!region)
1561 return WPS_ERROR_INVALID_PARAM;
1563 /* should probably do some bounds checking here with the viewport... but later */
1564 region->action = ACTION_NONE;
1565 region->x = x;
1566 region->y = y;
1567 region->width = w;
1568 region->height = h;
1569 region->wvp = curr_vp;
1570 region->armed = false;
1572 end = strchr(action, ')');
1573 if (!end || (size_t)(end-action+1) > sizeof temp)
1574 return WPS_ERROR_INVALID_PARAM;
1575 strlcpy(temp, action, end-action+1);
1576 action = temp;
1578 if(!strcmp(pb_string, action))
1579 region->type = WPS_TOUCHREGION_SCROLLBAR;
1580 else if(!strcmp(vol_string, action))
1581 region->type = WPS_TOUCHREGION_VOLUME;
1582 else
1584 region->type = WPS_TOUCHREGION_ACTION;
1586 if (*action == '&')
1588 action++;
1589 region->repeat = true;
1591 else
1592 region->repeat = false;
1594 imax = ARRAYLEN(touchactions);
1595 for (i = 0; i < imax; i++)
1597 /* try to match with one of our touchregion screens */
1598 if (!strcmp(touchactions[i].s, action))
1600 region->action = touchactions[i].action;
1601 break;
1604 if (region->action == ACTION_NONE)
1605 return WPS_ERROR_INVALID_PARAM;
1607 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1608 if (!item)
1609 return WPS_ERROR_INVALID_PARAM;
1610 add_to_ll_chain(&wps_data->touchregions, item);
1611 return skip_end_of_line(wps_bufptr);
1613 #endif
1615 /* Parse a generic token from the given string. Return the length read */
1616 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1618 int skip = 0, taglen = 0, ret;
1619 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1620 const struct wps_tag *tag;
1621 memset(token, 0, sizeof(*token));
1623 switch(*wps_bufptr)
1626 case '%':
1627 case '<':
1628 case '|':
1629 case '>':
1630 case ';':
1631 case '#':
1632 case ')':
1633 case '(':
1634 case ',':
1635 /* escaped characters */
1636 token->type = WPS_TOKEN_CHARACTER;
1637 token->value.c = *wps_bufptr;
1638 taglen = 1;
1639 wps_data->num_tokens++;
1640 break;
1642 case '?':
1643 /* conditional tag */
1644 token->type = WPS_TOKEN_CONDITIONAL;
1645 level++;
1646 condindex[level] = wps_data->num_tokens;
1647 numoptions[level] = 1;
1648 wps_data->num_tokens++;
1649 ret = parse_token(wps_bufptr + 1, wps_data);
1650 if (ret < 0) return ret;
1651 taglen = 1 + ret;
1652 break;
1654 default:
1655 /* find what tag we have */
1656 for (tag = all_tags;
1657 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1658 tag++) ;
1660 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1661 token->type = tag->type;
1662 curr_line->curr_subline->line_type |= tag->refresh_type;
1664 /* if the tag has a special parsing function, we call it */
1665 if (tag->parse_func)
1667 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1668 if (ret < 0) return ret;
1669 skip += ret;
1672 /* Some tags we don't want to save as tokens */
1673 if (tag->type == WPS_NO_TOKEN)
1674 break;
1676 /* tags that start with 'F', 'I' or 'D' are for the next file */
1677 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1678 *(tag->name) == 'D')
1679 token->next = true;
1681 wps_data->num_tokens++;
1682 break;
1685 skip += taglen;
1686 return skip;
1691 * Returns the number of bytes to skip the buf pointer to access the false
1692 * branch in a _binary_ conditional
1694 * That is:
1695 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1696 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1698 * depending on the features of a target it's not called from check_feature_tag,
1699 * hence the __attribute__ or it issues compiler warnings
1703 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1704 static int find_false_branch(const char *wps_bufptr)
1706 const char *buf = wps_bufptr;
1707 /* wps_bufptr is after the opening '<', hence level = 1*/
1708 int level = 1;
1709 char ch;
1712 ch = *buf;
1713 if (ch == '%')
1714 { /* filter out the characters we check later if they're printed
1715 * as literals */
1716 ch = *(++buf);
1717 if (ch == '<' || ch == '>' || ch == '|')
1718 continue;
1719 /* else: some tags/printed literals we skip over */
1721 else if (ch == '<') /* nested conditional */
1722 level++;
1723 else if (ch == '>')
1724 { /* closed our or a nested conditional,
1725 * do NOT skip over the '>' so that wps_parse() sees it for closing
1726 * if it is the closing one for our conditional */
1727 level--;
1729 else if (ch == '|' && level == 1)
1730 { /* we found our separator, point before and get out */
1731 break;
1733 /* if level is 0, we don't have a false branch */
1734 } while (level > 0 && *(++buf));
1736 return buf - wps_bufptr;
1740 * returns the number of bytes to get the appropriate branch of a binary
1741 * conditional
1743 * That means:
1744 * - if a feature is available, it returns 0 to not skip anything
1745 * - if the feature is not available, skip to the false branch and don't
1746 * parse the true branch at all
1748 * */
1749 static int check_feature_tag(const char *wps_bufptr, const int type)
1751 (void)wps_bufptr;
1752 switch (type)
1754 case WPS_TOKEN_RTC_PRESENT:
1755 #if CONFIG_RTC
1756 return 0;
1757 #else
1758 return find_false_branch(wps_bufptr);
1759 #endif
1760 case WPS_TOKEN_HAVE_RECORDING:
1761 #ifdef HAVE_RECORDING
1762 return 0;
1763 #else
1764 return find_false_branch(wps_bufptr);
1765 #endif
1766 case WPS_TOKEN_HAVE_TUNER:
1767 #if CONFIG_TUNER
1768 if (radio_hardware_present())
1769 return 0;
1770 #endif
1771 return find_false_branch(wps_bufptr);
1773 default: /* not a tag we care about, just don't skip */
1774 return 0;
1779 /* Parses the WPS.
1780 data is the pointer to the structure where the parsed WPS should be stored.
1781 It is initialised.
1782 wps_bufptr points to the string containing the WPS tags */
1783 #define TOKEN_BLOCK_SIZE 128
1784 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1786 if (!data || !wps_bufptr || !*wps_bufptr)
1787 return false;
1788 enum wps_parse_error fail = PARSE_OK;
1789 int ret;
1790 int max_tokens = TOKEN_BLOCK_SIZE;
1791 size_t buf_free = 0;
1792 line_number = 0;
1793 level = -1;
1795 /* allocate enough RAM for a reasonable skin, grow as needed.
1796 * Free any used RAM before loading the images to be 100% RAM efficient */
1797 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1798 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1799 return false;
1800 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1801 data->num_tokens = 0;
1803 #if LCD_DEPTH > 1
1804 /* Backdrop defaults to the setting unless %X is used, so set it now */
1805 if (global_settings.backdrop_file[0])
1807 data->backdrop = "-";
1809 #endif
1811 while (*wps_bufptr && !fail)
1813 if (follow_lang_direction)
1814 follow_lang_direction--;
1815 /* first make sure there is enough room for tokens */
1816 if (max_tokens <= data->num_tokens + 5)
1818 int extra_tokens = TOKEN_BLOCK_SIZE;
1819 size_t needed = extra_tokens * sizeof(struct wps_token);
1820 /* do some smarts here to grow the array a bit */
1821 if (skin_buffer_freespace() < needed)
1823 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1824 break;
1826 skin_buffer_increment(needed, false);
1827 max_tokens += extra_tokens;
1830 switch(*wps_bufptr++)
1833 /* Regular tag */
1834 case '%':
1835 if ((ret = parse_token(wps_bufptr, data)) < 0)
1837 fail = PARSE_FAIL_COND_INVALID_PARAM;
1838 break;
1840 else if (level >= WPS_MAX_COND_LEVEL - 1)
1842 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1843 break;
1845 wps_bufptr += ret;
1846 break;
1848 /* Alternating sublines separator */
1849 case ';':
1850 if (level >= 0) /* there are unclosed conditionals */
1852 fail = PARSE_FAIL_UNCLOSED_COND;
1853 break;
1856 if (!skin_start_new_subline(curr_line, data->num_tokens))
1857 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1859 break;
1861 /* Conditional list start */
1862 case '<':
1863 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1865 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1866 break;
1868 wps_bufptr += check_feature_tag(wps_bufptr,
1869 data->tokens[data->num_tokens-1].type);
1870 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1871 lastcond[level] = data->num_tokens++;
1872 break;
1874 /* Conditional list end */
1875 case '>':
1876 if (level < 0) /* not in a conditional, invalid char */
1878 fail = PARSE_FAIL_INVALID_CHAR;
1879 break;
1882 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1883 if (lastcond[level])
1884 data->tokens[lastcond[level]].value.i = data->num_tokens;
1885 else
1887 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1888 break;
1891 lastcond[level] = 0;
1892 data->num_tokens++;
1893 data->tokens[condindex[level]].value.i = numoptions[level];
1894 level--;
1895 break;
1897 /* Conditional list option */
1898 case '|':
1899 if (level < 0) /* not in a conditional, invalid char */
1901 fail = PARSE_FAIL_INVALID_CHAR;
1902 break;
1905 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1906 if (lastcond[level])
1907 data->tokens[lastcond[level]].value.i = data->num_tokens;
1908 else
1910 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1911 break;
1914 lastcond[level] = data->num_tokens;
1915 numoptions[level]++;
1916 data->num_tokens++;
1917 break;
1919 /* Comment */
1920 case '#':
1921 if (level >= 0) /* there are unclosed conditionals */
1923 fail = PARSE_FAIL_UNCLOSED_COND;
1924 break;
1927 wps_bufptr += skip_end_of_line(wps_bufptr);
1928 break;
1930 /* End of this line */
1931 case '\n':
1932 if (level >= 0) /* there are unclosed conditionals */
1934 fail = PARSE_FAIL_UNCLOSED_COND;
1935 break;
1937 /* add a new token for the \n so empty lines are correct */
1938 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1939 data->tokens[data->num_tokens].value.c = '\n';
1940 data->tokens[data->num_tokens].next = false;
1941 data->num_tokens++;
1943 if (!skin_start_new_line(curr_vp, data->num_tokens))
1945 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1946 break;
1948 line_number++;
1950 break;
1952 /* String */
1953 default:
1955 unsigned int len = 1;
1956 const char *string_start = wps_bufptr - 1;
1958 /* find the length of the string */
1959 while (*wps_bufptr && *wps_bufptr != '#' &&
1960 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1961 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1962 *wps_bufptr != '|' && *wps_bufptr != '\n')
1964 wps_bufptr++;
1965 len++;
1968 /* look if we already have that string */
1969 char *str;
1970 bool found = false;
1971 struct skin_token_list *list = data->strings;
1972 while (list)
1974 str = (char*)list->token->value.data;
1975 found = (strlen(str) == len &&
1976 strncmp(string_start, str, len) == 0);
1977 if (found)
1978 break; /* break here because the list item is
1979 used if its found */
1980 list = list->next;
1982 /* If a matching string is found, found is true and i is
1983 the index of the string. If not, found is false */
1985 if (!found)
1987 /* new string */
1988 str = (char*)skin_buffer_alloc(len+1);
1989 if (!str)
1991 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1992 break;
1994 strlcpy(str, string_start, len+1);
1995 struct skin_token_list *item =
1996 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1997 if(!item)
1999 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2000 break;
2002 add_to_ll_chain(&data->strings, item);
2004 else
2006 /* another occurrence of an existing string */
2007 data->tokens[data->num_tokens].value.data = list->token->value.data;
2009 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
2010 data->num_tokens++;
2012 break;
2016 if (!fail && level >= 0) /* there are unclosed conditionals */
2017 fail = PARSE_FAIL_UNCLOSED_COND;
2019 if (*wps_bufptr && !fail)
2020 /* one of the limits of the while loop was exceeded */
2021 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2023 /* Success! */
2024 curr_line->curr_subline->last_token_idx = data->num_tokens;
2025 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
2026 /* freeup unused tokens */
2027 skin_buffer_free_from_front(sizeof(struct wps_token)
2028 * (max_tokens - data->num_tokens));
2030 #if defined(DEBUG) || defined(SIMULATOR)
2031 if (debug)
2033 print_debug_info(data, fail, line_number);
2034 debug_skin_usage();
2036 #else
2037 (void)debug;
2038 #endif
2040 return (fail == 0);
2045 * initial setup of wps_data; does reset everything
2046 * except fields which need to survive, i.e.
2049 static void skin_data_reset(struct wps_data *wps_data)
2051 #ifdef HAVE_LCD_BITMAP
2052 wps_data->images = NULL;
2053 wps_data->progressbars = NULL;
2054 #endif
2055 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2056 wps_data->backdrop = NULL;
2057 #endif
2058 #ifdef HAVE_TOUCHSCREEN
2059 wps_data->touchregions = NULL;
2060 #endif
2061 wps_data->viewports = NULL;
2062 wps_data->strings = NULL;
2063 #ifdef HAVE_ALBUMART
2064 wps_data->albumart = NULL;
2065 if (wps_data->playback_aa_slot >= 0)
2067 playback_release_aa_slot(wps_data->playback_aa_slot);
2068 wps_data->playback_aa_slot = -1;
2070 #endif
2071 wps_data->tokens = NULL;
2072 wps_data->num_tokens = 0;
2074 #ifdef HAVE_LCD_BITMAP
2075 wps_data->peak_meter_enabled = false;
2076 wps_data->wps_sb_tag = false;
2077 wps_data->show_sb_on_wps = false;
2078 #else /* HAVE_LCD_CHARCELLS */
2079 /* progress bars */
2080 int i;
2081 for (i = 0; i < 8; i++)
2083 wps_data->wps_progress_pat[i] = 0;
2085 wps_data->full_line_progressbar = false;
2086 #endif
2087 wps_data->wps_loaded = false;
2090 #ifdef HAVE_LCD_BITMAP
2091 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2093 (void)wps_data; /* only needed for remote targets */
2094 char img_path[MAX_PATH];
2095 get_image_filename(bitmap->data, bmpdir,
2096 img_path, sizeof(img_path));
2098 /* load the image */
2099 int format;
2100 #ifdef HAVE_REMOTE_LCD
2101 if (curr_screen == SCREEN_REMOTE)
2102 format = FORMAT_ANY|FORMAT_REMOTE;
2103 else
2104 #endif
2105 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2107 size_t max_buf;
2108 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2109 bitmap->data = imgbuf;
2110 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2112 if (ret > 0)
2114 skin_buffer_increment(ret, true);
2115 return true;
2117 else
2119 /* Abort if we can't load an image */
2120 DEBUGF("Couldn't load '%s'\n", img_path);
2121 return false;
2125 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2127 struct skin_token_list *list;
2128 bool retval = true; /* return false if a single image failed to load */
2129 /* do the progressbars */
2130 list = wps_data->progressbars;
2131 while (list)
2133 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2134 if (pb->bm.data)
2136 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2137 if (!pb->have_bitmap_pb) /* no success */
2138 retval = false;
2140 list = list->next;
2142 /* regular images */
2143 list = wps_data->images;
2144 while (list)
2146 struct gui_img *img = (struct gui_img*)list->token->value.data;
2147 if (img->bm.data)
2149 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2150 if (img->loaded)
2151 img->subimage_height = img->bm.height / img->num_subimages;
2152 else
2153 retval = false;
2155 list = list->next;
2158 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2159 /* Backdrop load scheme:
2160 * 1) %X|filename|
2161 * 2) load the backdrop from settings
2163 if (wps_data->backdrop)
2165 bool needed = wps_data->backdrop[0] != '-';
2166 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2167 bmpdir, curr_screen);
2168 if (!wps_data->backdrop && needed)
2169 retval = false;
2171 #endif /* has backdrop support */
2173 return retval;
2176 static bool skin_load_fonts(struct wps_data *data)
2178 /* don't spit out after the first failue to aid debugging */
2179 bool success = true;
2180 struct skin_token_list *vp_list;
2181 int font_id;
2182 /* walk though each viewport and assign its font */
2183 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2185 /* first, find the viewports that have a non-sys/ui-font font */
2186 struct skin_viewport *skin_vp =
2187 (struct skin_viewport*)vp_list->token->value.data;
2188 struct viewport *vp = &skin_vp->vp;
2191 if (vp->font <= FONT_UI)
2192 { /* the usual case -> built-in fonts */
2193 #ifdef HAVE_REMOTE_LCD
2194 if (vp->font == FONT_UI)
2195 vp->font += curr_screen;
2196 #endif
2197 continue;
2199 font_id = vp->font;
2201 /* now find the corresponding skin_font */
2202 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2203 if (!font->name)
2205 DEBUGF("font %d not specified\n", font_id);
2206 success = false;
2207 continue;
2210 /* load the font - will handle loading the same font again if
2211 * multiple viewports use the same */
2212 if (font->id < 0)
2214 char *dot = strchr(font->name, '.');
2215 *dot = '\0';
2216 font->id = skin_font_load(font->name);
2219 if (font->id < 0)
2221 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2222 font_id, font->name);
2223 success = false;
2224 continue;
2227 /* finally, assign the font_id to the viewport */
2228 vp->font = font->id;
2230 return success;
2233 #endif /* HAVE_LCD_BITMAP */
2235 /* to setup up the wps-data from a format-buffer (isfile = false)
2236 from a (wps-)file (isfile = true)*/
2237 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2238 const char *buf, bool isfile)
2240 char *wps_buffer = NULL;
2241 if (!wps_data || !buf)
2242 return false;
2243 #ifdef HAVE_ALBUMART
2244 int status;
2245 struct mp3entry *curtrack;
2246 long offset;
2247 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2248 if (wps_data->albumart)
2250 old_aa.state = wps_data->albumart->state;
2251 old_aa.height = wps_data->albumart->height;
2252 old_aa.width = wps_data->albumart->width;
2254 #endif
2255 #ifdef HAVE_LCD_BITMAP
2256 int i;
2257 for (i=0;i<MAXUSERFONTS;i++)
2259 skinfonts[i].id = -1;
2260 skinfonts[i].name = NULL;
2262 #endif
2263 #ifdef DEBUG_SKIN_ENGINE
2264 if (isfile && debug_wps)
2266 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2268 #endif
2270 skin_data_reset(wps_data);
2271 wps_data->wps_loaded = false;
2272 curr_screen = screen;
2274 /* alloc default viewport, will be fixed up later */
2275 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2276 if (!curr_vp)
2277 return false;
2278 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2279 if (!list)
2280 return false;
2281 add_to_ll_chain(&wps_data->viewports, list);
2284 /* Initialise the first (default) viewport */
2285 curr_vp->label = VP_DEFAULT_LABEL;
2286 curr_vp->hidden_flags = 0;
2287 curr_vp->lines = NULL;
2289 viewport_set_defaults(&curr_vp->vp, screen);
2290 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2291 curr_vp->start_fgcolour = curr_vp->vp.fg_pattern;
2292 curr_vp->start_bgcolour = curr_vp->vp.bg_pattern;
2293 #endif
2294 #ifdef HAVE_LCD_BITMAP
2295 curr_vp->vp.font = FONT_UI;
2296 #endif
2298 curr_line = NULL;
2299 if (!skin_start_new_line(curr_vp, 0))
2300 return false;
2302 if (isfile)
2304 int fd = open_utf8(buf, O_RDONLY);
2306 if (fd < 0)
2307 return false;
2309 /* get buffer space from the plugin buffer */
2310 size_t buffersize = 0;
2311 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2313 if (!wps_buffer)
2314 return false;
2316 /* copy the file's content to the buffer for parsing,
2317 ensuring that every line ends with a newline char. */
2318 unsigned int start = 0;
2319 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2321 start += strlen(wps_buffer + start);
2322 if (start < buffersize - 1)
2324 wps_buffer[start++] = '\n';
2325 wps_buffer[start] = 0;
2328 close(fd);
2329 if (start <= 0)
2330 return false;
2332 else
2334 wps_buffer = (char*)buf;
2336 /* parse the WPS source */
2337 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2338 skin_data_reset(wps_data);
2339 return false;
2342 #ifdef HAVE_LCD_BITMAP
2343 char bmpdir[MAX_PATH];
2344 if (isfile)
2346 /* get the bitmap dir */
2347 char *dot = strrchr(buf, '.');
2348 strlcpy(bmpdir, buf, dot - buf + 1);
2350 else
2352 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2354 /* load the bitmaps that were found by the parsing */
2355 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2356 !skin_load_fonts(wps_data))
2358 skin_data_reset(wps_data);
2359 return false;
2361 #endif
2362 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2363 status = audio_status();
2364 if (status & AUDIO_STATUS_PLAY)
2366 struct skin_albumart *aa = wps_data->albumart;
2367 if (aa && ((aa->state && !old_aa.state) ||
2368 (aa->state &&
2369 (((old_aa.height != aa->height) ||
2370 (old_aa.width != aa->width))))))
2372 curtrack = audio_current_track();
2373 offset = curtrack->offset;
2374 audio_stop();
2375 if (!(status & AUDIO_STATUS_PAUSE))
2376 audio_play(offset);
2379 #endif
2380 wps_data->wps_loaded = true;
2381 #ifdef DEBUG_SKIN_ENGINE
2382 if (isfile && debug_wps)
2383 debug_skin_usage();
2384 #endif
2385 return true;