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