add a hotkey option to the wps touchregion.
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob365a1e6a261aeac0a3f6623db33c2de2275c282c
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 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1501 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1502 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1503 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1504 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1505 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1506 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1507 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1508 {"hotkey", ACTION_WPS_HOTKEY}
1510 static int parse_touchregion(const char *wps_bufptr,
1511 struct wps_token *token, struct wps_data *wps_data)
1513 (void)token;
1514 unsigned i, imax;
1515 struct touchregion *region = NULL;
1516 const char *ptr = wps_bufptr;
1517 const char *action, *end;
1518 const char pb_string[] = "progressbar";
1519 const char vol_string[] = "volume";
1520 int x,y,w,h;
1521 char temp[20];
1523 /* format: %T|x|y|width|height|action|
1524 * if action starts with & the area must be held to happen
1525 * action is one of:
1526 * play - play/pause playback
1527 * stop - stop playback, exit the wps
1528 * prev - prev track
1529 * next - next track
1530 * ffwd - seek forward
1531 * rwd - seek backwards
1532 * menu - go back to the main menu
1533 * browse - go back to the file/db browser
1534 * shuffle - toggle shuffle mode
1535 * repmode - cycle the repeat mode
1536 * quickscreen - go into the quickscreen
1537 * contextmenu - open the context menu
1538 * playlist - go into the playlist
1539 * pitch - go into the pitchscreen
1540 * volup - increase volume by one step
1541 * voldown - decrease volume by one step
1545 if (*ptr != '(')
1546 return WPS_ERROR_INVALID_PARAM;
1547 ptr++;
1549 if (!(ptr = parse_list("dddds", NULL, ',', ptr, &x, &y, &w, &h, &action)))
1550 return WPS_ERROR_INVALID_PARAM;
1552 /* Check there is a terminating ) */
1553 if (*ptr != ')')
1554 return WPS_ERROR_INVALID_PARAM;
1556 region = skin_buffer_alloc(sizeof(struct touchregion));
1557 if (!region)
1558 return WPS_ERROR_INVALID_PARAM;
1560 /* should probably do some bounds checking here with the viewport... but later */
1561 region->action = ACTION_NONE;
1562 region->x = x;
1563 region->y = y;
1564 region->width = w;
1565 region->height = h;
1566 region->wvp = curr_vp;
1567 region->armed = false;
1569 end = strchr(action, ')');
1570 if (!end || (size_t)(end-action+1) > sizeof temp)
1571 return WPS_ERROR_INVALID_PARAM;
1572 strlcpy(temp, action, end-action+1);
1573 action = temp;
1575 if(!strcmp(pb_string, action))
1576 region->type = WPS_TOUCHREGION_SCROLLBAR;
1577 else if(!strcmp(vol_string, action))
1578 region->type = WPS_TOUCHREGION_VOLUME;
1579 else
1581 region->type = WPS_TOUCHREGION_ACTION;
1583 if (*action == '&')
1585 action++;
1586 region->repeat = true;
1588 else
1589 region->repeat = false;
1591 imax = ARRAYLEN(touchactions);
1592 for (i = 0; i < imax; i++)
1594 /* try to match with one of our touchregion screens */
1595 if (!strcmp(touchactions[i].s, action))
1597 region->action = touchactions[i].action;
1598 break;
1601 if (region->action == ACTION_NONE)
1602 return WPS_ERROR_INVALID_PARAM;
1604 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1605 if (!item)
1606 return WPS_ERROR_INVALID_PARAM;
1607 add_to_ll_chain(&wps_data->touchregions, item);
1608 return skip_end_of_line(wps_bufptr);
1610 #endif
1612 /* Parse a generic token from the given string. Return the length read */
1613 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1615 int skip = 0, taglen = 0, ret;
1616 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1617 const struct wps_tag *tag;
1618 memset(token, 0, sizeof(*token));
1620 switch(*wps_bufptr)
1623 case '%':
1624 case '<':
1625 case '|':
1626 case '>':
1627 case ';':
1628 case '#':
1629 case ')':
1630 case '(':
1631 case ',':
1632 /* escaped characters */
1633 token->type = WPS_TOKEN_CHARACTER;
1634 token->value.c = *wps_bufptr;
1635 taglen = 1;
1636 wps_data->num_tokens++;
1637 break;
1639 case '?':
1640 /* conditional tag */
1641 token->type = WPS_TOKEN_CONDITIONAL;
1642 level++;
1643 condindex[level] = wps_data->num_tokens;
1644 numoptions[level] = 1;
1645 wps_data->num_tokens++;
1646 ret = parse_token(wps_bufptr + 1, wps_data);
1647 if (ret < 0) return ret;
1648 taglen = 1 + ret;
1649 break;
1651 default:
1652 /* find what tag we have */
1653 for (tag = all_tags;
1654 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1655 tag++) ;
1657 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1658 token->type = tag->type;
1659 curr_line->curr_subline->line_type |= tag->refresh_type;
1661 /* if the tag has a special parsing function, we call it */
1662 if (tag->parse_func)
1664 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1665 if (ret < 0) return ret;
1666 skip += ret;
1669 /* Some tags we don't want to save as tokens */
1670 if (tag->type == WPS_NO_TOKEN)
1671 break;
1673 /* tags that start with 'F', 'I' or 'D' are for the next file */
1674 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1675 *(tag->name) == 'D')
1676 token->next = true;
1678 wps_data->num_tokens++;
1679 break;
1682 skip += taglen;
1683 return skip;
1688 * Returns the number of bytes to skip the buf pointer to access the false
1689 * branch in a _binary_ conditional
1691 * That is:
1692 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1693 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1695 * depending on the features of a target it's not called from check_feature_tag,
1696 * hence the __attribute__ or it issues compiler warnings
1700 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1701 static int find_false_branch(const char *wps_bufptr)
1703 const char *buf = wps_bufptr;
1704 /* wps_bufptr is after the opening '<', hence level = 1*/
1705 int level = 1;
1706 char ch;
1709 ch = *buf;
1710 if (ch == '%')
1711 { /* filter out the characters we check later if they're printed
1712 * as literals */
1713 ch = *(++buf);
1714 if (ch == '<' || ch == '>' || ch == '|')
1715 continue;
1716 /* else: some tags/printed literals we skip over */
1718 else if (ch == '<') /* nested conditional */
1719 level++;
1720 else if (ch == '>')
1721 { /* closed our or a nested conditional,
1722 * do NOT skip over the '>' so that wps_parse() sees it for closing
1723 * if it is the closing one for our conditional */
1724 level--;
1726 else if (ch == '|' && level == 1)
1727 { /* we found our separator, point before and get out */
1728 break;
1730 /* if level is 0, we don't have a false branch */
1731 } while (level > 0 && *(++buf));
1733 return buf - wps_bufptr;
1737 * returns the number of bytes to get the appropriate branch of a binary
1738 * conditional
1740 * That means:
1741 * - if a feature is available, it returns 0 to not skip anything
1742 * - if the feature is not available, skip to the false branch and don't
1743 * parse the true branch at all
1745 * */
1746 static int check_feature_tag(const char *wps_bufptr, const int type)
1748 (void)wps_bufptr;
1749 switch (type)
1751 case WPS_TOKEN_RTC_PRESENT:
1752 #if CONFIG_RTC
1753 return 0;
1754 #else
1755 return find_false_branch(wps_bufptr);
1756 #endif
1757 case WPS_TOKEN_HAVE_RECORDING:
1758 #ifdef HAVE_RECORDING
1759 return 0;
1760 #else
1761 return find_false_branch(wps_bufptr);
1762 #endif
1763 case WPS_TOKEN_HAVE_TUNER:
1764 #if CONFIG_TUNER
1765 if (radio_hardware_present())
1766 return 0;
1767 #endif
1768 return find_false_branch(wps_bufptr);
1770 default: /* not a tag we care about, just don't skip */
1771 return 0;
1776 /* Parses the WPS.
1777 data is the pointer to the structure where the parsed WPS should be stored.
1778 It is initialised.
1779 wps_bufptr points to the string containing the WPS tags */
1780 #define TOKEN_BLOCK_SIZE 128
1781 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1783 if (!data || !wps_bufptr || !*wps_bufptr)
1784 return false;
1785 enum wps_parse_error fail = PARSE_OK;
1786 int ret;
1787 int max_tokens = TOKEN_BLOCK_SIZE;
1788 size_t buf_free = 0;
1789 line_number = 0;
1790 level = -1;
1792 /* allocate enough RAM for a reasonable skin, grow as needed.
1793 * Free any used RAM before loading the images to be 100% RAM efficient */
1794 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1795 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1796 return false;
1797 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1798 data->num_tokens = 0;
1800 #if LCD_DEPTH > 1
1801 /* Backdrop defaults to the setting unless %X is used, so set it now */
1802 if (global_settings.backdrop_file[0])
1804 data->backdrop = "-";
1806 #endif
1808 while (*wps_bufptr && !fail)
1810 if (follow_lang_direction)
1811 follow_lang_direction--;
1812 /* first make sure there is enough room for tokens */
1813 if (max_tokens <= data->num_tokens + 5)
1815 int extra_tokens = TOKEN_BLOCK_SIZE;
1816 size_t needed = extra_tokens * sizeof(struct wps_token);
1817 /* do some smarts here to grow the array a bit */
1818 if (skin_buffer_freespace() < needed)
1820 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1821 break;
1823 skin_buffer_increment(needed, false);
1824 max_tokens += extra_tokens;
1827 switch(*wps_bufptr++)
1830 /* Regular tag */
1831 case '%':
1832 if ((ret = parse_token(wps_bufptr, data)) < 0)
1834 fail = PARSE_FAIL_COND_INVALID_PARAM;
1835 break;
1837 else if (level >= WPS_MAX_COND_LEVEL - 1)
1839 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1840 break;
1842 wps_bufptr += ret;
1843 break;
1845 /* Alternating sublines separator */
1846 case ';':
1847 if (level >= 0) /* there are unclosed conditionals */
1849 fail = PARSE_FAIL_UNCLOSED_COND;
1850 break;
1853 if (!skin_start_new_subline(curr_line, data->num_tokens))
1854 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1856 break;
1858 /* Conditional list start */
1859 case '<':
1860 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1862 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1863 break;
1865 wps_bufptr += check_feature_tag(wps_bufptr,
1866 data->tokens[data->num_tokens-1].type);
1867 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1868 lastcond[level] = data->num_tokens++;
1869 break;
1871 /* Conditional list end */
1872 case '>':
1873 if (level < 0) /* not in a conditional, invalid char */
1875 fail = PARSE_FAIL_INVALID_CHAR;
1876 break;
1879 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1880 if (lastcond[level])
1881 data->tokens[lastcond[level]].value.i = data->num_tokens;
1882 else
1884 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1885 break;
1888 lastcond[level] = 0;
1889 data->num_tokens++;
1890 data->tokens[condindex[level]].value.i = numoptions[level];
1891 level--;
1892 break;
1894 /* Conditional list option */
1895 case '|':
1896 if (level < 0) /* not in a conditional, invalid char */
1898 fail = PARSE_FAIL_INVALID_CHAR;
1899 break;
1902 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1903 if (lastcond[level])
1904 data->tokens[lastcond[level]].value.i = data->num_tokens;
1905 else
1907 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1908 break;
1911 lastcond[level] = data->num_tokens;
1912 numoptions[level]++;
1913 data->num_tokens++;
1914 break;
1916 /* Comment */
1917 case '#':
1918 if (level >= 0) /* there are unclosed conditionals */
1920 fail = PARSE_FAIL_UNCLOSED_COND;
1921 break;
1924 wps_bufptr += skip_end_of_line(wps_bufptr);
1925 break;
1927 /* End of this line */
1928 case '\n':
1929 if (level >= 0) /* there are unclosed conditionals */
1931 fail = PARSE_FAIL_UNCLOSED_COND;
1932 break;
1934 /* add a new token for the \n so empty lines are correct */
1935 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1936 data->tokens[data->num_tokens].value.c = '\n';
1937 data->tokens[data->num_tokens].next = false;
1938 data->num_tokens++;
1940 if (!skin_start_new_line(curr_vp, data->num_tokens))
1942 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1943 break;
1945 line_number++;
1947 break;
1949 /* String */
1950 default:
1952 unsigned int len = 1;
1953 const char *string_start = wps_bufptr - 1;
1955 /* find the length of the string */
1956 while (*wps_bufptr && *wps_bufptr != '#' &&
1957 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1958 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1959 *wps_bufptr != '|' && *wps_bufptr != '\n')
1961 wps_bufptr++;
1962 len++;
1965 /* look if we already have that string */
1966 char *str;
1967 bool found = false;
1968 struct skin_token_list *list = data->strings;
1969 while (list)
1971 str = (char*)list->token->value.data;
1972 found = (strlen(str) == len &&
1973 strncmp(string_start, str, len) == 0);
1974 if (found)
1975 break; /* break here because the list item is
1976 used if its found */
1977 list = list->next;
1979 /* If a matching string is found, found is true and i is
1980 the index of the string. If not, found is false */
1982 if (!found)
1984 /* new string */
1985 str = (char*)skin_buffer_alloc(len+1);
1986 if (!str)
1988 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1989 break;
1991 strlcpy(str, string_start, len+1);
1992 struct skin_token_list *item =
1993 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1994 if(!item)
1996 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1997 break;
1999 add_to_ll_chain(&data->strings, item);
2001 else
2003 /* another occurrence of an existing string */
2004 data->tokens[data->num_tokens].value.data = list->token->value.data;
2006 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
2007 data->num_tokens++;
2009 break;
2013 if (!fail && level >= 0) /* there are unclosed conditionals */
2014 fail = PARSE_FAIL_UNCLOSED_COND;
2016 if (*wps_bufptr && !fail)
2017 /* one of the limits of the while loop was exceeded */
2018 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2020 /* Success! */
2021 curr_line->curr_subline->last_token_idx = data->num_tokens;
2022 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
2023 /* freeup unused tokens */
2024 skin_buffer_free_from_front(sizeof(struct wps_token)
2025 * (max_tokens - data->num_tokens));
2027 #if defined(DEBUG) || defined(SIMULATOR)
2028 if (debug)
2030 print_debug_info(data, fail, line_number);
2031 debug_skin_usage();
2033 #else
2034 (void)debug;
2035 #endif
2037 return (fail == 0);
2042 * initial setup of wps_data; does reset everything
2043 * except fields which need to survive, i.e.
2046 static void skin_data_reset(struct wps_data *wps_data)
2048 #ifdef HAVE_LCD_BITMAP
2049 wps_data->images = NULL;
2050 wps_data->progressbars = NULL;
2051 #endif
2052 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2053 wps_data->backdrop = NULL;
2054 #endif
2055 #ifdef HAVE_TOUCHSCREEN
2056 wps_data->touchregions = NULL;
2057 #endif
2058 wps_data->viewports = NULL;
2059 wps_data->strings = NULL;
2060 #ifdef HAVE_ALBUMART
2061 wps_data->albumart = NULL;
2062 if (wps_data->playback_aa_slot >= 0)
2064 playback_release_aa_slot(wps_data->playback_aa_slot);
2065 wps_data->playback_aa_slot = -1;
2067 #endif
2068 wps_data->tokens = NULL;
2069 wps_data->num_tokens = 0;
2071 #ifdef HAVE_LCD_BITMAP
2072 wps_data->peak_meter_enabled = false;
2073 wps_data->wps_sb_tag = false;
2074 wps_data->show_sb_on_wps = false;
2075 #else /* HAVE_LCD_CHARCELLS */
2076 /* progress bars */
2077 int i;
2078 for (i = 0; i < 8; i++)
2080 wps_data->wps_progress_pat[i] = 0;
2082 wps_data->full_line_progressbar = false;
2083 #endif
2084 wps_data->wps_loaded = false;
2087 #ifdef HAVE_LCD_BITMAP
2088 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2090 (void)wps_data; /* only needed for remote targets */
2091 char img_path[MAX_PATH];
2092 get_image_filename(bitmap->data, bmpdir,
2093 img_path, sizeof(img_path));
2095 /* load the image */
2096 int format;
2097 #ifdef HAVE_REMOTE_LCD
2098 if (curr_screen == SCREEN_REMOTE)
2099 format = FORMAT_ANY|FORMAT_REMOTE;
2100 else
2101 #endif
2102 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2104 size_t max_buf;
2105 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2106 bitmap->data = imgbuf;
2107 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2109 if (ret > 0)
2111 skin_buffer_increment(ret, true);
2112 return true;
2114 else
2116 /* Abort if we can't load an image */
2117 DEBUGF("Couldn't load '%s'\n", img_path);
2118 return false;
2122 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2124 struct skin_token_list *list;
2125 bool retval = true; /* return false if a single image failed to load */
2126 /* do the progressbars */
2127 list = wps_data->progressbars;
2128 while (list)
2130 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2131 if (pb->bm.data)
2133 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2134 if (!pb->have_bitmap_pb) /* no success */
2135 retval = false;
2137 list = list->next;
2139 /* regular images */
2140 list = wps_data->images;
2141 while (list)
2143 struct gui_img *img = (struct gui_img*)list->token->value.data;
2144 if (img->bm.data)
2146 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2147 if (img->loaded)
2148 img->subimage_height = img->bm.height / img->num_subimages;
2149 else
2150 retval = false;
2152 list = list->next;
2155 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2156 /* Backdrop load scheme:
2157 * 1) %X|filename|
2158 * 2) load the backdrop from settings
2160 if (wps_data->backdrop)
2162 bool needed = wps_data->backdrop[0] != '-';
2163 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2164 bmpdir, curr_screen);
2165 if (!wps_data->backdrop && needed)
2166 retval = false;
2168 #endif /* has backdrop support */
2170 return retval;
2173 static bool skin_load_fonts(struct wps_data *data)
2175 /* don't spit out after the first failue to aid debugging */
2176 bool success = true;
2177 struct skin_token_list *vp_list;
2178 int font_id;
2179 /* walk though each viewport and assign its font */
2180 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2182 /* first, find the viewports that have a non-sys/ui-font font */
2183 struct skin_viewport *skin_vp =
2184 (struct skin_viewport*)vp_list->token->value.data;
2185 struct viewport *vp = &skin_vp->vp;
2188 if (vp->font <= FONT_UI)
2189 { /* the usual case -> built-in fonts */
2190 #ifdef HAVE_REMOTE_LCD
2191 if (vp->font == FONT_UI)
2192 vp->font += curr_screen;
2193 #endif
2194 continue;
2196 font_id = vp->font;
2198 /* now find the corresponding skin_font */
2199 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2200 if (!font->name)
2202 DEBUGF("font %d not specified\n", font_id);
2203 success = false;
2204 continue;
2207 /* load the font - will handle loading the same font again if
2208 * multiple viewports use the same */
2209 if (font->id < 0)
2211 char *dot = strchr(font->name, '.');
2212 *dot = '\0';
2213 font->id = skin_font_load(font->name);
2216 if (font->id < 0)
2218 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2219 font_id, font->name);
2220 success = false;
2221 continue;
2224 /* finally, assign the font_id to the viewport */
2225 vp->font = font->id;
2227 return success;
2230 #endif /* HAVE_LCD_BITMAP */
2232 /* to setup up the wps-data from a format-buffer (isfile = false)
2233 from a (wps-)file (isfile = true)*/
2234 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2235 const char *buf, bool isfile)
2237 char *wps_buffer = NULL;
2238 if (!wps_data || !buf)
2239 return false;
2240 #ifdef HAVE_ALBUMART
2241 int status;
2242 struct mp3entry *curtrack;
2243 long offset;
2244 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2245 if (wps_data->albumart)
2247 old_aa.state = wps_data->albumart->state;
2248 old_aa.height = wps_data->albumart->height;
2249 old_aa.width = wps_data->albumart->width;
2251 #endif
2252 #ifdef HAVE_LCD_BITMAP
2253 int i;
2254 for (i=0;i<MAXUSERFONTS;i++)
2256 skinfonts[i].id = -1;
2257 skinfonts[i].name = NULL;
2259 #endif
2260 #ifdef DEBUG_SKIN_ENGINE
2261 if (isfile && debug_wps)
2263 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2265 #endif
2267 skin_data_reset(wps_data);
2268 wps_data->wps_loaded = false;
2269 curr_screen = screen;
2271 /* alloc default viewport, will be fixed up later */
2272 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2273 if (!curr_vp)
2274 return false;
2275 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2276 if (!list)
2277 return false;
2278 add_to_ll_chain(&wps_data->viewports, list);
2281 /* Initialise the first (default) viewport */
2282 curr_vp->label = VP_DEFAULT_LABEL;
2283 curr_vp->hidden_flags = 0;
2284 curr_vp->lines = NULL;
2286 viewport_set_defaults(&curr_vp->vp, screen);
2287 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2288 curr_vp->start_fgcolour = curr_vp->vp.fg_pattern;
2289 curr_vp->start_bgcolour = curr_vp->vp.bg_pattern;
2290 #endif
2291 #ifdef HAVE_LCD_BITMAP
2292 curr_vp->vp.font = FONT_UI;
2293 #endif
2295 curr_line = NULL;
2296 if (!skin_start_new_line(curr_vp, 0))
2297 return false;
2299 if (isfile)
2301 int fd = open_utf8(buf, O_RDONLY);
2303 if (fd < 0)
2304 return false;
2306 /* get buffer space from the plugin buffer */
2307 size_t buffersize = 0;
2308 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2310 if (!wps_buffer)
2311 return false;
2313 /* copy the file's content to the buffer for parsing,
2314 ensuring that every line ends with a newline char. */
2315 unsigned int start = 0;
2316 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2318 start += strlen(wps_buffer + start);
2319 if (start < buffersize - 1)
2321 wps_buffer[start++] = '\n';
2322 wps_buffer[start] = 0;
2325 close(fd);
2326 if (start <= 0)
2327 return false;
2329 else
2331 wps_buffer = (char*)buf;
2333 /* parse the WPS source */
2334 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2335 skin_data_reset(wps_data);
2336 return false;
2339 #ifdef HAVE_LCD_BITMAP
2340 char bmpdir[MAX_PATH];
2341 if (isfile)
2343 /* get the bitmap dir */
2344 char *dot = strrchr(buf, '.');
2345 strlcpy(bmpdir, buf, dot - buf + 1);
2347 else
2349 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2351 /* load the bitmaps that were found by the parsing */
2352 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2353 !skin_load_fonts(wps_data))
2355 skin_data_reset(wps_data);
2356 return false;
2358 #endif
2359 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2360 status = audio_status();
2361 if (status & AUDIO_STATUS_PLAY)
2363 struct skin_albumart *aa = wps_data->albumart;
2364 if (aa && ((aa->state && !old_aa.state) ||
2365 (aa->state &&
2366 (((old_aa.height != aa->height) ||
2367 (old_aa.width != aa->width))))))
2369 curtrack = audio_current_track();
2370 offset = curtrack->offset;
2371 audio_stop();
2372 if (!(status & AUDIO_STATUS_PAUSE))
2373 audio_play(offset);
2376 #endif
2377 wps_data->wps_loaded = true;
2378 #ifdef DEBUG_SKIN_ENGINE
2379 if (isfile && debug_wps)
2380 debug_skin_usage();
2381 #endif
2382 return true;