Make %tx work as intended, so that it ignores the true case for non-RDS targets.
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob650092557f6344a35451d2518c0b788850ee2f06
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 PB_HEIGHT,
1252 PB_FILENAME,
1254 const char *filename;
1255 int x, y, height, width;
1256 uint32_t set = 0;
1257 const char *ptr = wps_bufptr;
1258 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1259 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1261 if (!pb || !item)
1262 return WPS_ERROR_INVALID_PARAM;
1264 struct viewport *vp = &curr_vp->vp;
1265 /* we need to know what line number (viewport relative) this pb is,
1266 * so count them... */
1267 int line_num = -1;
1268 struct skin_line *line = curr_vp->lines;
1269 while (line)
1271 line_num++;
1272 line = line->next;
1274 if (curr_vp->label != VP_DEFAULT_LABEL)
1275 line_num--;
1276 pb->vp = vp;
1277 pb->have_bitmap_pb = false;
1278 pb->bm.data = NULL; /* no bitmap specified */
1279 pb->follow_lang_direction = follow_lang_direction > 0;
1280 pb->draw = false;
1282 if (*wps_bufptr != '(') /* regular old style */
1284 pb->x = 0;
1285 pb->width = vp->width;
1286 pb->height = SYSFONT_HEIGHT-2;
1287 pb->y = -line_num - 1; /* Will be computed during the rendering */
1288 if (token->type == WPS_TOKEN_VOLUME || token->type == WPS_TOKEN_BATTERY_PERCENT)
1289 return 0; /* dont add it, let the regular token handling do the work */
1290 pb->type = token->type;
1291 add_to_ll_chain(&wps_data->progressbars, item);
1292 return 0;
1294 ptr = wps_bufptr + 1;
1296 if (!(ptr = parse_list("dddds", &set, ',', ptr,
1297 &x, &y, &width, &height, &filename)))
1298 return WPS_ERROR_INVALID_PARAM;
1300 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1301 pb->bm.data = (char*)filename;
1303 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1304 pb->x = x;
1305 else
1306 pb->x = vp->x;
1308 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1310 /* A zero width causes a divide-by-zero error later, so reject it */
1311 if (width == 0)
1312 return WPS_ERROR_INVALID_PARAM;
1314 pb->width = width;
1316 else
1317 pb->width = vp->width - pb->x;
1319 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1321 /* A zero height makes no sense - reject it */
1322 if (height == 0)
1323 return WPS_ERROR_INVALID_PARAM;
1325 pb->height = height;
1327 else
1329 if (vp->font > FONT_UI)
1330 pb->height = -1; /* calculate at display time */
1331 else
1333 #ifndef __PCTOOL__
1334 pb->height = font_get(vp->font)->height;
1335 #else
1336 pb->height = 8;
1337 #endif
1341 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1342 pb->y = y;
1343 else
1344 pb->y = -line_num - 1; /* Will be computed during the rendering */
1346 if (*ptr != ')')
1347 return WPS_ERROR_INVALID_PARAM;
1349 add_to_ll_chain(&wps_data->progressbars, item);
1350 if (token->type == WPS_TOKEN_VOLUME)
1351 token->type = WPS_TOKEN_VOLUMEBAR;
1352 else if (token->type == WPS_TOKEN_BATTERY_PERCENT)
1353 token->type = WPS_TOKEN_BATTERY_PERCENTBAR;
1354 pb->type = token->type;
1356 return ptr+1-wps_bufptr;
1357 #else
1358 (void)wps_bufptr;
1359 if (token->type != WPS_TOKEN_VOLUME &&
1360 token->type != WPS_TOKEN_BATTERY_PERCENTBAR)
1362 wps_data->full_line_progressbar =
1363 token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
1365 return 0;
1367 #endif
1370 #ifdef HAVE_ALBUMART
1371 static int parse_albumart_load(const char *wps_bufptr,
1372 struct wps_token *token,
1373 struct wps_data *wps_data)
1375 const char *ptr = wps_bufptr;
1376 struct dim dimensions;
1377 int albumart_slot;
1378 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1379 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1380 (void)token; /* silence warning */
1381 if (!aa)
1382 return skip_end_of_line(wps_bufptr);
1384 /* reset albumart info in wps */
1385 aa->width = -1;
1386 aa->height = -1;
1387 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1388 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1389 aa->vp = &curr_vp->vp;
1391 if (*ptr != '(')
1392 return WPS_ERROR_INVALID_PARAM;
1393 ptr++;
1394 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1395 if (!(ptr = parse_list("dddd", NULL,',',ptr, &aa->x, &aa->y, &aa->width, &aa->height)))
1396 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1398 /* if we got here, we parsed everything ok .. ! */
1399 if (aa->width < 0)
1400 aa->width = 0;
1401 else if (aa->width > LCD_WIDTH)
1402 aa->width = LCD_WIDTH;
1404 if (aa->height < 0)
1405 aa->height = 0;
1406 else if (aa->height > LCD_HEIGHT)
1407 aa->height = LCD_HEIGHT;
1409 if (swap_for_rtl)
1410 aa->x = LCD_WIDTH - (aa->x + aa->width);
1412 aa->state = WPS_ALBUMART_LOAD;
1413 aa->draw = false;
1414 wps_data->albumart = aa;
1416 dimensions.width = aa->width;
1417 dimensions.height = aa->height;
1419 albumart_slot = playback_claim_aa_slot(&dimensions);
1421 if (0 <= albumart_slot)
1422 wps_data->playback_aa_slot = albumart_slot;
1424 if (*ptr == ',')
1426 ptr++;
1427 switch (*ptr)
1429 case 'l':
1430 case 'L':
1431 if (swap_for_rtl)
1432 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1433 else
1434 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1435 break;
1436 case 'c':
1437 case 'C':
1438 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1439 break;
1440 case 'r':
1441 case 'R':
1442 if (swap_for_rtl)
1443 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1444 else
1445 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1446 break;
1448 ptr++;
1450 if (*ptr == ',')
1452 ptr++;
1453 switch (*ptr)
1455 case 't':
1456 case 'T':
1457 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1458 break;
1459 case 'c':
1460 case 'C':
1461 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1462 break;
1463 case 'b':
1464 case 'B':
1465 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1466 break;
1468 ptr++;
1470 if (*ptr != ')')
1471 return WPS_ERROR_INVALID_PARAM;
1473 return skip_end_of_line(wps_bufptr);
1476 static int parse_albumart_display(const char *wps_bufptr,
1477 struct wps_token *token,
1478 struct wps_data *wps_data)
1480 (void)wps_bufptr;
1481 (void)token;
1482 if (wps_data->albumart)
1484 wps_data->albumart->vp = &curr_vp->vp;
1486 #if 0
1487 /* the old code did this so keep it here for now...
1488 * this is to allow the posibility to showing the next tracks AA! */
1489 if (wps_bufptr+1 == 'n')
1490 return 1;
1491 #endif
1492 return 0;
1494 #endif /* HAVE_ALBUMART */
1496 #ifdef HAVE_TOUCHSCREEN
1498 struct touchaction {const char* s; int action;};
1499 static const struct touchaction touchactions[] = {
1500 /* generic actions, convert to screen actions on use */
1501 {"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
1502 {"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
1503 {"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
1504 {"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
1505 {"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
1506 /* not really WPS specific, but no equivilant ACTION_STD_* */
1507 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1509 /* WPS specific actions */
1510 {"browse", ACTION_WPS_BROWSE },
1511 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1512 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1513 {"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
1515 #if CONFIG_TUNER
1516 /* FM screen actions */
1517 /* Also allow browse, play, stop from WPS codes */
1518 {"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
1519 {"presets", ACTION_FM_PRESET},
1520 #endif
1523 static int parse_touchregion(const char *wps_bufptr,
1524 struct wps_token *token, struct wps_data *wps_data)
1526 (void)token;
1527 unsigned i, imax;
1528 struct touchregion *region = NULL;
1529 const char *ptr = wps_bufptr;
1530 const char *action, *end;
1531 const char pb_string[] = "progressbar";
1532 const char vol_string[] = "volume";
1533 int x,y,w,h;
1534 char temp[20];
1536 /* format: %T|x|y|width|height|action|
1537 * if action starts with & the area must be held to happen
1538 * action is one of:
1539 * play - play/pause playback
1540 * stop - stop playback, exit the wps
1541 * prev - prev track
1542 * next - next track
1543 * ffwd - seek forward
1544 * rwd - seek backwards
1545 * menu - go back to the main menu
1546 * browse - go back to the file/db browser
1547 * shuffle - toggle shuffle mode
1548 * repmode - cycle the repeat mode
1549 * quickscreen - go into the quickscreen
1550 * contextmenu - open the context menu
1551 * playlist - go into the playlist
1552 * pitch - go into the pitchscreen
1553 * volup - increase volume by one step
1554 * voldown - decrease volume by one step
1558 if (*ptr != '(')
1559 return WPS_ERROR_INVALID_PARAM;
1560 ptr++;
1562 if (!(ptr = parse_list("dddds", NULL, ',', ptr, &x, &y, &w, &h, &action)))
1563 return WPS_ERROR_INVALID_PARAM;
1565 /* Check there is a terminating ) */
1566 if (*ptr != ')')
1567 return WPS_ERROR_INVALID_PARAM;
1569 region = skin_buffer_alloc(sizeof(struct touchregion));
1570 if (!region)
1571 return WPS_ERROR_INVALID_PARAM;
1573 /* should probably do some bounds checking here with the viewport... but later */
1574 region->action = ACTION_NONE;
1575 region->x = x;
1576 region->y = y;
1577 region->width = w;
1578 region->height = h;
1579 region->wvp = curr_vp;
1580 region->armed = false;
1581 region->reverse_bar = false;
1583 end = strchr(action, ')');
1584 if (!end || (size_t)(end-action+1) > sizeof temp)
1585 return WPS_ERROR_INVALID_PARAM;
1586 strlcpy(temp, action, end-action+1);
1587 action = temp;
1589 if (*action == '!')
1591 region->reverse_bar = true;
1592 action++;
1595 if(!strcmp(pb_string, action))
1596 region->type = WPS_TOUCHREGION_SCROLLBAR;
1597 else if(!strcmp(vol_string, action))
1598 region->type = WPS_TOUCHREGION_VOLUME;
1599 else
1601 region->type = WPS_TOUCHREGION_ACTION;
1603 if (*action == '&')
1605 action++;
1606 region->repeat = true;
1608 else
1609 region->repeat = false;
1611 imax = ARRAYLEN(touchactions);
1612 for (i = 0; i < imax; i++)
1614 /* try to match with one of our touchregion screens */
1615 if (!strcmp(touchactions[i].s, action))
1617 region->action = touchactions[i].action;
1618 break;
1621 if (region->action == ACTION_NONE)
1622 return WPS_ERROR_INVALID_PARAM;
1624 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1625 if (!item)
1626 return WPS_ERROR_INVALID_PARAM;
1627 add_to_ll_chain(&wps_data->touchregions, item);
1628 return skip_end_of_line(wps_bufptr);
1630 #endif
1632 /* Parse a generic token from the given string. Return the length read */
1633 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1635 int skip = 0, taglen = 0, ret;
1636 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1637 const struct wps_tag *tag;
1638 memset(token, 0, sizeof(*token));
1640 switch(*wps_bufptr)
1643 case '%':
1644 case '<':
1645 case '|':
1646 case '>':
1647 case ';':
1648 case '#':
1649 case ')':
1650 case '(':
1651 case ',':
1652 /* escaped characters */
1653 token->type = WPS_TOKEN_CHARACTER;
1654 token->value.c = *wps_bufptr;
1655 taglen = 1;
1656 wps_data->num_tokens++;
1657 break;
1659 case '?':
1660 /* conditional tag */
1661 token->type = WPS_TOKEN_CONDITIONAL;
1662 level++;
1663 condindex[level] = wps_data->num_tokens;
1664 numoptions[level] = 1;
1665 wps_data->num_tokens++;
1666 ret = parse_token(wps_bufptr + 1, wps_data);
1667 if (ret < 0) return ret;
1668 taglen = 1 + ret;
1669 break;
1671 default:
1672 /* find what tag we have */
1673 for (tag = all_tags;
1674 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1675 tag++) ;
1677 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1678 token->type = tag->type;
1679 curr_line->curr_subline->line_type |= tag->refresh_type;
1681 /* if the tag has a special parsing function, we call it */
1682 if (tag->parse_func)
1684 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1685 if (ret < 0) return ret;
1686 skip += ret;
1689 /* Some tags we don't want to save as tokens */
1690 if (tag->type == WPS_NO_TOKEN)
1691 break;
1693 /* tags that start with 'F', 'I' or 'D' are for the next file */
1694 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1695 *(tag->name) == 'D')
1696 token->next = true;
1698 wps_data->num_tokens++;
1699 break;
1702 skip += taglen;
1703 return skip;
1708 * Returns the number of bytes to skip the buf pointer to access the false
1709 * branch in a _binary_ conditional
1711 * That is:
1712 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1713 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1715 * depending on the features of a target it's not called from check_feature_tag,
1716 * hence the __attribute__ or it issues compiler warnings
1720 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1721 static int find_false_branch(const char *wps_bufptr)
1723 const char *buf = wps_bufptr;
1724 /* wps_bufptr is after the opening '<', hence level = 1*/
1725 int level = 1;
1726 char ch;
1729 ch = *buf;
1730 if (ch == '%')
1731 { /* filter out the characters we check later if they're printed
1732 * as literals */
1733 ch = *(++buf);
1734 if (ch == '<' || ch == '>' || ch == '|')
1735 continue;
1736 /* else: some tags/printed literals we skip over */
1738 else if (ch == '<') /* nested conditional */
1739 level++;
1740 else if (ch == '>')
1741 { /* closed our or a nested conditional,
1742 * do NOT skip over the '>' so that wps_parse() sees it for closing
1743 * if it is the closing one for our conditional */
1744 level--;
1746 else if (ch == '|' && level == 1)
1747 { /* we found our separator, point before and get out */
1748 break;
1750 /* if level is 0, we don't have a false branch */
1751 } while (level > 0 && *(++buf));
1753 return buf - wps_bufptr;
1757 * returns the number of bytes to get the appropriate branch of a binary
1758 * conditional
1760 * That means:
1761 * - if a feature is available, it returns 0 to not skip anything
1762 * - if the feature is not available, skip to the false branch and don't
1763 * parse the true branch at all
1765 * */
1766 static int check_feature_tag(const char *wps_bufptr, const int type)
1768 (void)wps_bufptr;
1769 switch (type)
1771 case WPS_TOKEN_RTC_PRESENT:
1772 #if CONFIG_RTC
1773 return 0;
1774 #else
1775 return find_false_branch(wps_bufptr);
1776 #endif
1777 case WPS_TOKEN_HAVE_RECORDING:
1778 #ifdef HAVE_RECORDING
1779 return 0;
1780 #else
1781 return find_false_branch(wps_bufptr);
1782 #endif
1783 case WPS_TOKEN_HAVE_TUNER:
1784 #if CONFIG_TUNER
1785 if (radio_hardware_present())
1786 return 0;
1787 #endif
1788 case WPS_TOKEN_HAVE_RDS:
1789 #ifdef HAVE_RDS_CAP
1790 return 0;
1791 #else
1792 return find_false_branch(wps_bufptr);
1793 #endif
1795 default: /* not a tag we care about, just don't skip */
1796 return 0;
1801 /* Parses the WPS.
1802 data is the pointer to the structure where the parsed WPS should be stored.
1803 It is initialised.
1804 wps_bufptr points to the string containing the WPS tags */
1805 #define TOKEN_BLOCK_SIZE 128
1806 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1808 if (!data || !wps_bufptr || !*wps_bufptr)
1809 return false;
1810 enum wps_parse_error fail = PARSE_OK;
1811 int ret;
1812 int max_tokens = TOKEN_BLOCK_SIZE;
1813 size_t buf_free = 0;
1814 line_number = 0;
1815 level = -1;
1817 /* allocate enough RAM for a reasonable skin, grow as needed.
1818 * Free any used RAM before loading the images to be 100% RAM efficient */
1819 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1820 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1821 return false;
1822 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1823 data->num_tokens = 0;
1825 #if LCD_DEPTH > 1
1826 /* Backdrop defaults to the setting unless %X is used, so set it now */
1827 if (global_settings.backdrop_file[0])
1829 data->backdrop = "-";
1831 #endif
1833 while (*wps_bufptr && !fail)
1835 if (follow_lang_direction)
1836 follow_lang_direction--;
1837 /* first make sure there is enough room for tokens */
1838 if (max_tokens <= data->num_tokens + 5)
1840 int extra_tokens = TOKEN_BLOCK_SIZE;
1841 size_t needed = extra_tokens * sizeof(struct wps_token);
1842 /* do some smarts here to grow the array a bit */
1843 if (skin_buffer_freespace() < needed)
1845 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1846 break;
1848 skin_buffer_increment(needed, false);
1849 max_tokens += extra_tokens;
1852 switch(*wps_bufptr++)
1855 /* Regular tag */
1856 case '%':
1857 if ((ret = parse_token(wps_bufptr, data)) < 0)
1859 fail = PARSE_FAIL_COND_INVALID_PARAM;
1860 break;
1862 else if (level >= WPS_MAX_COND_LEVEL - 1)
1864 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1865 break;
1867 wps_bufptr += ret;
1868 break;
1870 /* Alternating sublines separator */
1871 case ';':
1872 if (level >= 0) /* there are unclosed conditionals */
1874 fail = PARSE_FAIL_UNCLOSED_COND;
1875 break;
1878 if (!skin_start_new_subline(curr_line, data->num_tokens))
1879 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1881 break;
1883 /* Conditional list start */
1884 case '<':
1885 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1887 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1888 break;
1890 wps_bufptr += check_feature_tag(wps_bufptr,
1891 data->tokens[data->num_tokens-1].type);
1892 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1893 lastcond[level] = data->num_tokens++;
1894 break;
1896 /* Conditional list end */
1897 case '>':
1898 if (level < 0) /* not in a conditional, invalid char */
1900 fail = PARSE_FAIL_INVALID_CHAR;
1901 break;
1904 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1905 if (lastcond[level])
1906 data->tokens[lastcond[level]].value.i = data->num_tokens;
1907 else
1909 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1910 break;
1913 lastcond[level] = 0;
1914 data->num_tokens++;
1915 data->tokens[condindex[level]].value.i = numoptions[level];
1916 level--;
1917 break;
1919 /* Conditional list option */
1920 case '|':
1921 if (level < 0) /* not in a conditional, invalid char */
1923 fail = PARSE_FAIL_INVALID_CHAR;
1924 break;
1927 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1928 if (lastcond[level])
1929 data->tokens[lastcond[level]].value.i = data->num_tokens;
1930 else
1932 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1933 break;
1936 lastcond[level] = data->num_tokens;
1937 numoptions[level]++;
1938 data->num_tokens++;
1939 break;
1941 /* Comment */
1942 case '#':
1943 if (level >= 0) /* there are unclosed conditionals */
1945 fail = PARSE_FAIL_UNCLOSED_COND;
1946 break;
1949 wps_bufptr += skip_end_of_line(wps_bufptr);
1950 break;
1952 /* End of this line */
1953 case '\n':
1954 if (level >= 0) /* there are unclosed conditionals */
1956 fail = PARSE_FAIL_UNCLOSED_COND;
1957 break;
1959 /* add a new token for the \n so empty lines are correct */
1960 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1961 data->tokens[data->num_tokens].value.c = '\n';
1962 data->tokens[data->num_tokens].next = false;
1963 data->num_tokens++;
1965 if (!skin_start_new_line(curr_vp, data->num_tokens))
1967 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1968 break;
1970 line_number++;
1972 break;
1974 /* String */
1975 default:
1977 unsigned int len = 1;
1978 const char *string_start = wps_bufptr - 1;
1980 /* find the length of the string */
1981 while (*wps_bufptr && *wps_bufptr != '#' &&
1982 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1983 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1984 *wps_bufptr != '|' && *wps_bufptr != '\n')
1986 wps_bufptr++;
1987 len++;
1990 /* look if we already have that string */
1991 char *str;
1992 bool found = false;
1993 struct skin_token_list *list = data->strings;
1994 while (list)
1996 str = (char*)list->token->value.data;
1997 found = (strlen(str) == len &&
1998 strncmp(string_start, str, len) == 0);
1999 if (found)
2000 break; /* break here because the list item is
2001 used if its found */
2002 list = list->next;
2004 /* If a matching string is found, found is true and i is
2005 the index of the string. If not, found is false */
2007 if (!found)
2009 /* new string */
2010 str = (char*)skin_buffer_alloc(len+1);
2011 if (!str)
2013 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2014 break;
2016 strlcpy(str, string_start, len+1);
2017 struct skin_token_list *item =
2018 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
2019 if(!item)
2021 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2022 break;
2024 add_to_ll_chain(&data->strings, item);
2026 else
2028 /* another occurrence of an existing string */
2029 data->tokens[data->num_tokens].value.data = list->token->value.data;
2031 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
2032 data->num_tokens++;
2034 break;
2038 if (!fail && level >= 0) /* there are unclosed conditionals */
2039 fail = PARSE_FAIL_UNCLOSED_COND;
2041 if (*wps_bufptr && !fail)
2042 /* one of the limits of the while loop was exceeded */
2043 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2045 /* Success! */
2046 curr_line->curr_subline->last_token_idx = data->num_tokens;
2047 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
2048 /* freeup unused tokens */
2049 skin_buffer_free_from_front(sizeof(struct wps_token)
2050 * (max_tokens - data->num_tokens));
2052 #ifdef DEBUG_SKIN_ENGINE
2053 if (debug)
2055 print_debug_info(data, fail, line_number);
2056 debug_skin_usage();
2058 #else
2059 (void)debug;
2060 #endif
2062 return (fail == 0);
2067 * initial setup of wps_data; does reset everything
2068 * except fields which need to survive, i.e.
2071 static void skin_data_reset(struct wps_data *wps_data)
2073 #ifdef HAVE_LCD_BITMAP
2074 wps_data->images = NULL;
2075 wps_data->progressbars = NULL;
2076 #endif
2077 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2078 wps_data->backdrop = NULL;
2079 #endif
2080 #ifdef HAVE_TOUCHSCREEN
2081 wps_data->touchregions = NULL;
2082 #endif
2083 wps_data->viewports = NULL;
2084 wps_data->strings = NULL;
2085 #ifdef HAVE_ALBUMART
2086 wps_data->albumart = NULL;
2087 if (wps_data->playback_aa_slot >= 0)
2089 playback_release_aa_slot(wps_data->playback_aa_slot);
2090 wps_data->playback_aa_slot = -1;
2092 #endif
2093 wps_data->tokens = NULL;
2094 wps_data->num_tokens = 0;
2096 #ifdef HAVE_LCD_BITMAP
2097 wps_data->peak_meter_enabled = false;
2098 wps_data->wps_sb_tag = false;
2099 wps_data->show_sb_on_wps = false;
2100 #else /* HAVE_LCD_CHARCELLS */
2101 /* progress bars */
2102 int i;
2103 for (i = 0; i < 8; i++)
2105 wps_data->wps_progress_pat[i] = 0;
2107 wps_data->full_line_progressbar = false;
2108 #endif
2109 wps_data->wps_loaded = false;
2112 #ifdef HAVE_LCD_BITMAP
2113 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2115 (void)wps_data; /* only needed for remote targets */
2116 char img_path[MAX_PATH];
2117 get_image_filename(bitmap->data, bmpdir,
2118 img_path, sizeof(img_path));
2120 /* load the image */
2121 int format;
2122 #ifdef HAVE_REMOTE_LCD
2123 if (curr_screen == SCREEN_REMOTE)
2124 format = FORMAT_ANY|FORMAT_REMOTE;
2125 else
2126 #endif
2127 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2129 size_t max_buf;
2130 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2131 bitmap->data = imgbuf;
2132 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2134 if (ret > 0)
2136 skin_buffer_increment(ret, true);
2137 return true;
2139 else
2141 /* Abort if we can't load an image */
2142 DEBUGF("Couldn't load '%s'\n", img_path);
2143 return false;
2147 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2149 struct skin_token_list *list;
2150 bool retval = true; /* return false if a single image failed to load */
2151 /* do the progressbars */
2152 list = wps_data->progressbars;
2153 while (list)
2155 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2156 if (pb->bm.data)
2158 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2159 if (!pb->have_bitmap_pb) /* no success */
2160 retval = false;
2162 list = list->next;
2164 /* regular images */
2165 list = wps_data->images;
2166 while (list)
2168 struct gui_img *img = (struct gui_img*)list->token->value.data;
2169 if (img->bm.data)
2171 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2172 if (img->loaded)
2173 img->subimage_height = img->bm.height / img->num_subimages;
2174 else
2175 retval = false;
2177 list = list->next;
2180 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2181 /* Backdrop load scheme:
2182 * 1) %X|filename|
2183 * 2) load the backdrop from settings
2185 if (wps_data->backdrop)
2187 bool needed = wps_data->backdrop[0] != '-';
2188 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2189 bmpdir, curr_screen);
2190 if (!wps_data->backdrop && needed)
2191 retval = false;
2193 #endif /* has backdrop support */
2195 return retval;
2198 static bool skin_load_fonts(struct wps_data *data)
2200 /* don't spit out after the first failue to aid debugging */
2201 bool success = true;
2202 struct skin_token_list *vp_list;
2203 int font_id;
2204 /* walk though each viewport and assign its font */
2205 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2207 /* first, find the viewports that have a non-sys/ui-font font */
2208 struct skin_viewport *skin_vp =
2209 (struct skin_viewport*)vp_list->token->value.data;
2210 struct viewport *vp = &skin_vp->vp;
2213 if (vp->font <= FONT_UI)
2214 { /* the usual case -> built-in fonts */
2215 #ifdef HAVE_REMOTE_LCD
2216 if (vp->font == FONT_UI)
2217 vp->font += curr_screen;
2218 #endif
2219 continue;
2221 font_id = vp->font;
2223 /* now find the corresponding skin_font */
2224 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2225 if (!font->name)
2227 if (success)
2229 DEBUGF("font %d not specified\n", font_id);
2231 success = false;
2232 continue;
2235 /* load the font - will handle loading the same font again if
2236 * multiple viewports use the same */
2237 if (font->id < 0)
2239 char *dot = strchr(font->name, '.');
2240 *dot = '\0';
2241 font->id = skin_font_load(font->name);
2244 if (font->id < 0)
2246 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2247 font_id, font->name);
2248 success = false;
2249 font->name = NULL;
2250 continue;
2253 /* finally, assign the font_id to the viewport */
2254 vp->font = font->id;
2256 return success;
2259 #endif /* HAVE_LCD_BITMAP */
2261 /* to setup up the wps-data from a format-buffer (isfile = false)
2262 from a (wps-)file (isfile = true)*/
2263 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2264 const char *buf, bool isfile)
2266 char *wps_buffer = NULL;
2267 if (!wps_data || !buf)
2268 return false;
2269 #ifdef HAVE_ALBUMART
2270 int status;
2271 struct mp3entry *curtrack;
2272 long offset;
2273 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2274 if (wps_data->albumart)
2276 old_aa.state = wps_data->albumart->state;
2277 old_aa.height = wps_data->albumart->height;
2278 old_aa.width = wps_data->albumart->width;
2280 #endif
2281 #ifdef HAVE_LCD_BITMAP
2282 int i;
2283 for (i=0;i<MAXUSERFONTS;i++)
2285 skinfonts[i].id = -1;
2286 skinfonts[i].name = NULL;
2288 #endif
2289 #ifdef DEBUG_SKIN_ENGINE
2290 if (isfile && debug_wps)
2292 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2294 #endif
2296 skin_data_reset(wps_data);
2297 wps_data->wps_loaded = false;
2298 curr_screen = screen;
2300 /* alloc default viewport, will be fixed up later */
2301 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2302 if (!curr_vp)
2303 return false;
2304 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2305 if (!list)
2306 return false;
2307 add_to_ll_chain(&wps_data->viewports, list);
2310 /* Initialise the first (default) viewport */
2311 curr_vp->label = VP_DEFAULT_LABEL;
2312 curr_vp->hidden_flags = 0;
2313 curr_vp->lines = NULL;
2315 viewport_set_defaults(&curr_vp->vp, screen);
2316 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2317 curr_vp->start_fgcolour = curr_vp->vp.fg_pattern;
2318 curr_vp->start_bgcolour = curr_vp->vp.bg_pattern;
2319 #endif
2320 #ifdef HAVE_LCD_BITMAP
2321 curr_vp->vp.font = FONT_UI;
2322 #endif
2324 curr_line = NULL;
2325 if (!skin_start_new_line(curr_vp, 0))
2326 return false;
2328 if (isfile)
2330 int fd = open_utf8(buf, O_RDONLY);
2332 if (fd < 0)
2333 return false;
2335 /* get buffer space from the plugin buffer */
2336 size_t buffersize = 0;
2337 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2339 if (!wps_buffer)
2340 return false;
2342 /* copy the file's content to the buffer for parsing,
2343 ensuring that every line ends with a newline char. */
2344 unsigned int start = 0;
2345 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2347 start += strlen(wps_buffer + start);
2348 if (start < buffersize - 1)
2350 wps_buffer[start++] = '\n';
2351 wps_buffer[start] = 0;
2354 close(fd);
2355 if (start <= 0)
2356 return false;
2358 else
2360 wps_buffer = (char*)buf;
2362 /* parse the WPS source */
2363 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2364 skin_data_reset(wps_data);
2365 return false;
2368 #ifdef HAVE_LCD_BITMAP
2369 char bmpdir[MAX_PATH];
2370 if (isfile)
2372 /* get the bitmap dir */
2373 char *dot = strrchr(buf, '.');
2374 strlcpy(bmpdir, buf, dot - buf + 1);
2376 else
2378 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2380 /* load the bitmaps that were found by the parsing */
2381 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2382 !skin_load_fonts(wps_data))
2384 skin_data_reset(wps_data);
2385 return false;
2387 #endif
2388 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2389 status = audio_status();
2390 if (status & AUDIO_STATUS_PLAY)
2392 struct skin_albumart *aa = wps_data->albumart;
2393 if (aa && ((aa->state && !old_aa.state) ||
2394 (aa->state &&
2395 (((old_aa.height != aa->height) ||
2396 (old_aa.width != aa->width))))))
2398 curtrack = audio_current_track();
2399 offset = curtrack->offset;
2400 audio_stop();
2401 if (!(status & AUDIO_STATUS_PAUSE))
2402 audio_play(offset);
2405 #endif
2406 wps_data->wps_loaded = true;
2407 #ifdef DEBUG_SKIN_ENGINE
2408 if (isfile && debug_wps)
2409 debug_skin_usage();
2410 #endif
2411 return true;