skin_parser.c: fix possibile overflow in parse_setting_and_lang(). simplify compariso...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob932c3baf6bb4f6bee4c6412551cd475c43194710
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_image_special(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 #endif
174 #ifdef HAVE_ALBUMART
175 static int parse_albumart_load(const char *wps_bufptr,
176 struct wps_token *token, struct wps_data *wps_data);
177 static int parse_albumart_display(const char *wps_bufptr,
178 struct wps_token *token, struct wps_data *wps_data);
179 #endif /* HAVE_ALBUMART */
180 #ifdef HAVE_TOUCHSCREEN
181 static int parse_touchregion(const char *wps_bufptr,
182 struct wps_token *token, struct wps_data *wps_data);
183 #else
184 static int fulline_tag_not_supported(const char *wps_bufptr,
185 struct wps_token *token, struct wps_data *wps_data)
187 (void)token; (void)wps_data;
188 return skip_end_of_line(wps_bufptr);
190 #define parse_touchregion fulline_tag_not_supported
191 #endif
192 #ifdef CONFIG_RTC
193 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
194 #else
195 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
196 #endif
198 /* array of available tags - those with more characters have to go first
199 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
200 static const struct wps_tag all_tags[] = {
202 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
203 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
204 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
205 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
206 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
207 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
209 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, parse_progressbar },
210 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
211 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
212 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
213 #if CONFIG_CHARGING >= CHARGING_MONITOR
214 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
215 #endif
216 #if CONFIG_CHARGING
217 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
218 #endif
219 #ifdef HAVE_USB_POWER
220 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
221 #endif
223 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
225 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
226 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
236 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
237 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
238 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
239 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
240 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
241 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
243 /* current file */
244 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
254 parse_dir_level },
256 /* next file */
257 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
267 parse_dir_level },
269 /* current metadata */
270 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
276 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
277 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
278 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
283 /* next metadata */
284 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
290 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
292 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
293 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
294 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
295 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
297 #if (CONFIG_CODEC != MAS3507D)
298 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
299 #endif
300 #if (CONFIG_CODEC == SWCODEC)
301 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
302 #endif
303 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
304 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
305 #endif
307 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
309 #ifdef HAS_REMOTE_BUTTON_HOLD
310 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
311 #else
312 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
313 #endif
315 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
316 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
317 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
318 parse_timeout },
320 #ifdef HAVE_LCD_BITMAP
321 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
322 #else
323 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
324 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
325 #endif
326 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
327 parse_progressbar },
329 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC,
330 parse_progressbar },
332 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
333 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
334 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
335 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
336 { WPS_TOKEN_TRACK_STARTING, "pS", WPS_REFRESH_DYNAMIC, parse_timeout },
337 { WPS_TOKEN_TRACK_ENDING, "pE", WPS_REFRESH_DYNAMIC, parse_timeout },
339 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
340 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
341 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
342 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
344 #ifdef HAVE_TAGCACHE
345 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
346 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
347 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
348 #endif
350 #if CONFIG_CODEC == SWCODEC
351 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
352 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
353 #endif
355 { WPS_TOKEN_HAVE_TUNER, "tp", WPS_REFRESH_STATIC, NULL },
356 #if CONFIG_TUNER /* Re-uses the 't' and 'T' prefixes, be careful about doubleups */
357 { WPS_TOKEN_TUNER_TUNED, "tt", WPS_REFRESH_DYNAMIC, NULL },
358 { WPS_TOKEN_TUNER_SCANMODE, "tm", WPS_REFRESH_DYNAMIC, NULL },
359 { WPS_TOKEN_TUNER_STEREO, "ts", WPS_REFRESH_DYNAMIC, NULL },
360 { WPS_TOKEN_TUNER_MINFREQ, "ta", WPS_REFRESH_STATIC, NULL },
361 { WPS_TOKEN_TUNER_MAXFREQ, "tb", WPS_REFRESH_STATIC, NULL },
362 { WPS_TOKEN_TUNER_CURFREQ, "tf", WPS_REFRESH_DYNAMIC, NULL },
363 { WPS_TOKEN_PRESET_ID, "Ti", WPS_REFRESH_STATIC, NULL },
364 { WPS_TOKEN_PRESET_NAME, "Tn", WPS_REFRESH_STATIC, NULL },
365 { WPS_TOKEN_PRESET_FREQ, "Tf", WPS_REFRESH_STATIC, NULL },
366 { WPS_TOKEN_PRESET_COUNT, "Tc", WPS_REFRESH_STATIC, NULL },
367 { WPS_TOKEN_HAVE_RDS, "tx", WPS_REFRESH_STATIC, NULL },
368 #ifdef HAVE_RDS_CAP
369 { WPS_TOKEN_RDS_NAME, "ty", WPS_REFRESH_DYNAMIC, NULL },
370 { WPS_TOKEN_RDS_TEXT, "tz", WPS_REFRESH_DYNAMIC, NULL },
371 #endif
372 #endif /* CONFIG_TUNER */
374 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
375 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
377 #ifdef HAVE_LCD_BITMAP
378 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
379 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
380 { WPS_TOKEN_DRAW_INBUILTBAR, "wi", WPS_REFRESH_DYNAMIC, parse_statusbar_inbuilt },
382 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
384 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
385 parse_image_display },
387 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
388 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
389 #ifdef HAVE_ALBUMART
390 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
391 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
392 #endif
394 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
395 parse_viewport_display },
396 { WPS_TOKEN_UIVIEWPORT_ENABLE, "VI", WPS_REFRESH_STATIC,
397 parse_viewport_display },
398 #ifdef HAVE_LCD_BITMAP
399 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
400 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
401 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
402 #endif
403 { WPS_NO_TOKEN, "V", 0, parse_viewport },
405 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
406 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
407 #endif
408 #endif
410 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
411 parse_setting_and_lang },
412 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
413 parse_setting_and_lang },
414 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
416 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
417 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
418 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
421 /* Recording Tokens */
422 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
423 #ifdef HAVE_RECORDING
424 { WPS_TOKEN_IS_RECORDING, "Rr", WPS_REFRESH_DYNAMIC, NULL },
425 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
426 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
427 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
428 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
429 { WPS_TOKEN_REC_SECONDS, "Rs", WPS_REFRESH_DYNAMIC, NULL },
430 { WPS_TOKEN_REC_MINUTES, "Rn", WPS_REFRESH_DYNAMIC, NULL },
431 { WPS_TOKEN_REC_HOURS, "Rh", WPS_REFRESH_DYNAMIC, NULL },
432 #endif
433 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
434 /* the array MUST end with an empty string (first char is \0) */
438 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
439 * chains require the order to be kept.
441 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
443 if (*list == NULL)
444 *list = item;
445 else
447 struct skin_token_list *t = *list;
448 while (t->next)
449 t = t->next;
450 t->next = item;
454 /* traverse the image linked-list for an image */
455 #ifdef HAVE_LCD_BITMAP
456 struct gui_img* find_image(char label, struct wps_data *data)
458 struct skin_token_list *list = data->images;
459 while (list)
461 struct gui_img *img = (struct gui_img *)list->token->value.data;
462 if (img->label == label)
463 return img;
464 list = list->next;
466 return NULL;
469 #endif
471 /* traverse the viewport linked list for a viewport */
472 struct skin_viewport* find_viewport(char label, struct wps_data *data)
474 struct skin_token_list *list = data->viewports;
475 while (list)
477 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
478 if (vp->label == label)
479 return vp;
480 list = list->next;
482 return NULL;
486 /* create and init a new wpsll item.
487 * passing NULL to token will alloc a new one.
488 * You should only pass NULL for the token when the token type (table above)
489 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
491 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
492 void* token_data)
494 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
495 if (!token)
496 token = skin_buffer_alloc(sizeof(struct wps_token));
497 if (!llitem || !token)
498 return NULL;
499 llitem->next = NULL;
500 llitem->token = token;
501 if (token_data)
502 llitem->token->value.data = token_data;
503 return llitem;
506 /* Returns the number of chars that should be skipped to jump
507 immediately after the first eol, i.e. to the start of the next line */
508 static int skip_end_of_line(const char *wps_bufptr)
510 line_number++;
511 int skip = 0;
512 while(*(wps_bufptr + skip) != '\n')
513 skip++;
514 return ++skip;
517 /* Starts a new subline in the current line during parsing */
518 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
520 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
521 if (!subline)
522 return false;
524 subline->first_token_idx = curr_token;
525 subline->next = NULL;
527 subline->line_type = 0;
528 subline->time_mult = 0;
530 line->curr_subline->last_token_idx = curr_token-1;
531 line->curr_subline->next = subline;
532 line->curr_subline = subline;
533 return true;
536 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
538 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
539 struct skin_subline *subline = NULL;
540 if (!line)
541 return false;
543 /* init the subline */
544 subline = &line->sublines;
545 subline->first_token_idx = curr_token;
546 subline->next = NULL;
547 subline->line_type = 0;
548 subline->time_mult = 0;
550 /* init the new line */
551 line->curr_subline = &line->sublines;
552 line->next = NULL;
553 line->subline_expire_time = 0;
555 /* connect to curr_line and vp pointers.
556 * 1) close the previous lines subline
557 * 2) connect to vp pointer
558 * 3) connect to curr_line global pointer
560 if (curr_line)
562 curr_line->curr_subline->last_token_idx = curr_token - 1;
563 curr_line->next = line;
564 curr_line->curr_subline = NULL;
566 curr_line = line;
567 if (!vp->lines)
568 vp->lines = line;
569 return true;
572 #ifdef HAVE_LCD_BITMAP
574 static int parse_statusbar_enable(const char *wps_bufptr,
575 struct wps_token *token,
576 struct wps_data *wps_data)
578 (void)token; /* Kill warnings */
579 wps_data->wps_sb_tag = true;
580 wps_data->show_sb_on_wps = true;
581 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
582 viewport_set_defaults(&default_vp->vp, curr_screen);
583 default_vp->vp.font = FONT_UI;
584 return skip_end_of_line(wps_bufptr);
587 static int parse_statusbar_disable(const char *wps_bufptr,
588 struct wps_token *token,
589 struct wps_data *wps_data)
591 (void)token; /* Kill warnings */
592 wps_data->wps_sb_tag = true;
593 wps_data->show_sb_on_wps = false;
594 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
595 viewport_set_fullscreen(&default_vp->vp, curr_screen);
596 default_vp->vp.font = FONT_UI;
597 return skip_end_of_line(wps_bufptr);
600 static int parse_statusbar_inbuilt(const char *wps_bufptr,
601 struct wps_token *token, struct wps_data *wps_data)
603 (void)wps_data;
604 token->value.data = (void*)&curr_vp->vp;
605 return skip_end_of_line(wps_bufptr);
608 static int get_image_id(int c)
610 if(c >= 'a' && c <= 'z')
611 return c - 'a';
612 else if(c >= 'A' && c <= 'Z')
613 return c - 'A' + 26;
614 else
615 return -1;
618 char *get_image_filename(const char *start, const char* bmpdir,
619 char *buf, int buf_size)
621 const char *end = strchr(start, '|');
622 int bmpdirlen = strlen(bmpdir);
624 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
626 buf[0] = '\0';
627 return NULL;
630 strcpy(buf, bmpdir);
631 buf[bmpdirlen] = '/';
632 memcpy( &buf[bmpdirlen + 1], start, end - start);
633 buf[bmpdirlen + 1 + end - start] = 0;
635 return buf;
638 static int parse_image_display(const char *wps_bufptr,
639 struct wps_token *token,
640 struct wps_data *wps_data)
642 char label = wps_bufptr[0];
643 int subimage;
644 struct gui_img *img;;
646 /* sanity check */
647 img = find_image(label, wps_data);
648 if (!img)
650 token->value.i = label; /* so debug works */
651 return WPS_ERROR_INVALID_PARAM;
654 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
656 if (subimage >= img->num_subimages)
657 return WPS_ERROR_INVALID_PARAM;
659 /* Store sub-image number to display in high bits */
660 token->value.i = label | (subimage << 8);
661 return 2; /* We have consumed 2 bytes */
662 } else {
663 token->value.i = label;
664 return 1; /* We have consumed 1 byte */
668 static int parse_image_load(const char *wps_bufptr,
669 struct wps_token *token,
670 struct wps_data *wps_data)
672 const char *ptr = wps_bufptr;
673 const char *pos;
674 const char* filename;
675 const char* id;
676 const char *newline;
677 int x,y;
678 struct gui_img *img;
680 /* format: %x|n|filename.bmp|x|y|
681 or %xl|n|filename.bmp|x|y|
682 or %xl|n|filename.bmp|x|y|num_subimages|
685 if (*ptr != '|')
686 return WPS_ERROR_INVALID_PARAM;
688 ptr++;
690 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
691 return WPS_ERROR_INVALID_PARAM;
693 /* Check there is a terminating | */
694 if (*ptr != '|')
695 return WPS_ERROR_INVALID_PARAM;
697 /* check the image number and load state */
698 if(find_image(*id, wps_data))
700 /* Invalid image ID */
701 return WPS_ERROR_INVALID_PARAM;
703 img = skin_buffer_alloc(sizeof(struct gui_img));
704 if (!img)
705 return WPS_ERROR_INVALID_PARAM;
706 /* save a pointer to the filename */
707 img->bm.data = (char*)filename;
708 img->label = *id;
709 img->x = x;
710 img->y = y;
711 img->num_subimages = 1;
712 img->always_display = false;
714 /* save current viewport */
715 img->vp = &curr_vp->vp;
717 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
719 img->always_display = true;
721 else
723 /* Parse the (optional) number of sub-images */
724 ptr++;
725 newline = strchr(ptr, '\n');
726 pos = strchr(ptr, '|');
727 if (pos && pos < newline)
728 img->num_subimages = atoi(ptr);
730 if (img->num_subimages <= 0)
731 return WPS_ERROR_INVALID_PARAM;
733 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
734 if (!item)
735 return WPS_ERROR_INVALID_PARAM;
736 add_to_ll_chain(&wps_data->images, item);
738 /* Skip the rest of the line */
739 return skip_end_of_line(wps_bufptr);
741 struct skin_font {
742 int id; /* the id from font_load */
743 char *name; /* filename without path and extension */
745 static struct skin_font skinfonts[MAXUSERFONTS];
746 static int parse_font_load(const char *wps_bufptr,
747 struct wps_token *token, struct wps_data *wps_data)
749 (void)wps_data; (void)token;
750 const char *ptr = wps_bufptr;
751 int id;
752 char *filename;
754 if (*ptr != '|')
755 return WPS_ERROR_INVALID_PARAM;
757 ptr++;
759 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
760 return WPS_ERROR_INVALID_PARAM;
762 /* Check there is a terminating | */
763 if (*ptr != '|')
764 return WPS_ERROR_INVALID_PARAM;
766 if (id <= FONT_UI || id >= MAXFONTS-1)
767 return WPS_ERROR_INVALID_PARAM;
768 #if defined(DEBUG) || defined(SIMULATOR)
769 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
771 DEBUGF("font id %d already being used\n", id);
773 #endif
774 /* make sure the filename contains .fnt,
775 * we dont actually use it, but require it anyway */
776 ptr = strchr(filename, '.');
777 if (!ptr || strncmp(ptr, ".fnt|", 5))
778 return WPS_ERROR_INVALID_PARAM;
779 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
780 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
782 return skip_end_of_line(wps_bufptr);
786 static int parse_viewport_display(const char *wps_bufptr,
787 struct wps_token *token,
788 struct wps_data *wps_data)
790 (void)wps_data;
791 char letter = wps_bufptr[0];
793 if (letter < 'a' || letter > 'z')
795 /* invalid viewport tag */
796 return WPS_ERROR_INVALID_PARAM;
798 token->value.i = letter;
799 return 1;
802 #ifdef HAVE_LCD_BITMAP
803 static int parse_playlistview_text(struct playlistviewer *viewer,
804 enum info_line_type line, char* text)
806 int cur_string = 0;
807 const struct wps_tag *tag;
808 int taglen = 0;
809 const char *start = text;
810 if (*text != '|')
811 return -1;
812 text++;
813 viewer->lines[line].count = 0;
814 viewer->lines[line].scroll = false;
815 while (*text != '|')
817 if (*text == '%') /* it is a token of some type */
819 text++;
820 taglen = 0;
821 switch(*text)
823 case '%':
824 case '<':
825 case '|':
826 case '>':
827 case ';':
828 case '#':
829 /* escaped characters */
830 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
831 viewer->lines[line].strings[cur_string][0] = *text;
832 viewer->lines[line].strings[cur_string++][1] = '\0';
833 text++;
834 break;
835 default:
836 for (tag = all_tags;
837 strncmp(text, tag->name, strlen(tag->name)) != 0;
838 tag++) ;
839 /* %s isnt stored as a tag so manually check for it */
840 if (tag->type == WPS_NO_TOKEN)
842 if (!strncmp(tag->name, "s", 1))
844 viewer->lines[line].scroll = true;
845 taglen = 1;
848 else if (tag->type == WPS_TOKEN_UNKNOWN)
850 int i = 0;
851 /* just copy the string */
852 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
853 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
855 viewer->lines[line].strings[cur_string][i] = text[i];
856 i++;
858 viewer->lines[line].strings[cur_string][i] = '\0';
859 cur_string++;
860 taglen = i;
862 else
864 if (tag->parse_func)
866 /* unsupported tag, reject */
867 return -1;
869 taglen = strlen(tag->name);
870 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
872 text += taglen;
875 else
877 /* regular string */
878 int i = 0;
879 /* just copy the string */
880 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
881 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
883 viewer->lines[line].strings[cur_string][i] = text[i];
884 i++;
886 viewer->lines[line].strings[cur_string][i] = '\0';
887 cur_string++;
888 text += i;
891 return text - start;
895 static int parse_playlistview(const char *wps_bufptr,
896 struct wps_token *token, struct wps_data *wps_data)
898 (void)wps_data;
899 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
900 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
901 char *ptr = strchr(wps_bufptr, '|');
902 int length;
903 if (!viewer || !ptr)
904 return WPS_ERROR_INVALID_PARAM;
905 viewer->vp = &curr_vp->vp;
906 viewer->show_icons = true;
907 viewer->start_offset = atoi(ptr+1);
908 token->value.data = (void*)viewer;
909 ptr = strchr(ptr+1, '|');
910 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
911 if (length < 0)
912 return WPS_ERROR_INVALID_PARAM;
913 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
914 if (length < 0)
915 return WPS_ERROR_INVALID_PARAM;
917 return skip_end_of_line(wps_bufptr);
919 #endif
921 static int parse_viewport(const char *wps_bufptr,
922 struct wps_token *token,
923 struct wps_data *wps_data)
925 (void)token; /* Kill warnings */
926 const char *ptr = wps_bufptr;
928 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
930 /* check for the optional letter to signify its a hideable viewport */
931 /* %Vl|<label>|<rest of tags>| */
932 skin_vp->hidden_flags = 0;
933 skin_vp->label = VP_NO_LABEL;
934 skin_vp->lines = NULL;
935 if (curr_line)
937 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
938 - (wps_data->num_tokens > 0 ? 1 : 0);
941 curr_line = NULL;
942 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
943 return WPS_ERROR_INVALID_PARAM;
945 if (*ptr == 'i')
947 if (*(ptr+1) == '|')
949 char label = *(ptr+2);
950 if (label >= 'a' && label <= 'z')
952 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
953 skin_vp->label = VP_INFO_LABEL|label;
954 ptr += 3;
956 else
958 skin_vp->label = VP_INFO_LABEL|VP_DEFAULT_LABEL;
959 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
960 ++ptr;
963 else
964 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
967 else if (*ptr == 'l')
969 if (*(ptr+1) == '|')
971 char label = *(ptr+2);
972 if (label >= 'a' && label <= 'z')
974 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
975 skin_vp->label = label;
977 else
978 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
979 ptr += 3;
982 if (*ptr != '|')
983 return WPS_ERROR_INVALID_PARAM;
985 ptr++;
986 struct viewport *vp = &skin_vp->vp;
987 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
988 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
989 return WPS_ERROR_INVALID_PARAM;
991 /* Check for trailing | */
992 if (*ptr != '|')
993 return WPS_ERROR_INVALID_PARAM;
995 if (follow_lang_direction && lang_is_rtl())
997 vp->flags |= VP_FLAG_ALIGN_RIGHT;
998 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
1000 else
1001 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
1003 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
1004 if (!list)
1005 return WPS_ERROR_INVALID_PARAM;
1006 add_to_ll_chain(&wps_data->viewports, list);
1007 curr_vp = skin_vp;
1008 /* Skip the rest of the line */
1009 return skip_end_of_line(wps_bufptr);
1012 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1013 static int parse_image_special(const char *wps_bufptr,
1014 struct wps_token *token,
1015 struct wps_data *wps_data)
1017 (void)wps_data; /* kill warning */
1018 (void)token;
1019 const char *pos = NULL;
1020 const char *newline;
1021 bool error = false;
1023 pos = strchr(wps_bufptr + 1, '|');
1024 newline = strchr(wps_bufptr, '\n');
1026 error = (pos > newline);
1028 #if LCD_DEPTH > 1
1029 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
1031 /* format: %X|filename.bmp| or %Xd */
1032 if (*(wps_bufptr) == 'd')
1034 wps_data->backdrop = NULL;
1035 return skip_end_of_line(wps_bufptr);
1037 else if (!error)
1038 wps_data->backdrop = (char*)wps_bufptr + 1;
1040 #endif
1041 if (error)
1042 return WPS_ERROR_INVALID_PARAM;
1043 /* Skip the rest of the line */
1044 return skip_end_of_line(wps_bufptr);
1046 #endif
1048 #endif /* HAVE_LCD_BITMAP */
1050 static int parse_setting_and_lang(const char *wps_bufptr,
1051 struct wps_token *token,
1052 struct wps_data *wps_data)
1054 /* NOTE: both the string validations that happen in here will
1055 * automatically PASS on checkwps because its too hard to get
1056 * settings_list.c and englinsh.lang built for it.
1057 * If that ever changes remove the #ifndef __PCTOOL__'s here
1059 (void)wps_data;
1060 const char *ptr = wps_bufptr;
1061 const char *end;
1062 int i = 0;
1063 char temp[64];
1065 /* Find the setting's cfg_name */
1066 if (*ptr != '|')
1067 return WPS_ERROR_INVALID_PARAM;
1068 ptr++;
1069 end = strchr(ptr,'|');
1070 if (!end || (size_t)(end-ptr+1) > sizeof temp)
1071 return WPS_ERROR_INVALID_PARAM;
1072 strlcpy(temp, ptr,end-ptr+1);
1074 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1076 #ifndef __PCTOOL__
1077 i = lang_english_to_id(temp);
1078 if (i < 0)
1079 return WPS_ERROR_INVALID_PARAM;
1080 #endif
1082 else
1084 /* Find the setting */
1085 for (i=0; i<nb_settings; i++)
1086 if (settings[i].cfg_name &&
1087 !strcmp(settings[i].cfg_name, temp))
1088 break;
1089 #ifndef __PCTOOL__
1090 if (i == nb_settings)
1091 return WPS_ERROR_INVALID_PARAM;
1092 #endif
1094 /* Store the setting number */
1095 token->value.i = i;
1097 /* Skip the rest of the line */
1098 return end-ptr+2;
1102 static int parse_dir_level(const char *wps_bufptr,
1103 struct wps_token *token,
1104 struct wps_data *wps_data)
1106 char val[] = { *wps_bufptr, '\0' };
1107 token->value.i = atoi(val);
1108 (void)wps_data; /* Kill warnings */
1109 return 1;
1112 static int parse_timeout(const char *wps_bufptr,
1113 struct wps_token *token,
1114 struct wps_data *wps_data)
1116 int skip = 0;
1117 int val = 0;
1118 bool have_point = false;
1119 bool have_tenth = false;
1121 (void)wps_data; /* Kill the warning */
1123 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1125 if (*wps_bufptr != '.')
1127 val *= 10;
1128 val += *wps_bufptr - '0';
1129 if (have_point)
1131 have_tenth = true;
1132 wps_bufptr++;
1133 skip++;
1134 break;
1137 else
1138 have_point = true;
1140 wps_bufptr++;
1141 skip++;
1144 if (have_tenth == false)
1145 val *= 10;
1147 if (val == 0 && skip == 0)
1149 /* decide what to do if no value was specified */
1150 switch (token->type)
1152 case WPS_TOKEN_SUBLINE_TIMEOUT:
1153 return -1;
1154 case WPS_TOKEN_BUTTON_VOLUME:
1155 case WPS_TOKEN_TRACK_STARTING:
1156 case WPS_TOKEN_TRACK_ENDING:
1157 val = 10;
1158 break;
1161 token->value.i = val;
1163 return skip;
1166 static int parse_progressbar(const char *wps_bufptr,
1167 struct wps_token *token,
1168 struct wps_data *wps_data)
1170 /* %pb or %pb|filename|x|y|width|height|
1171 using - for any of the params uses "sane" values */
1172 #ifdef HAVE_LCD_BITMAP
1173 enum {
1174 PB_FILENAME = 0,
1175 PB_X,
1176 PB_Y,
1177 PB_WIDTH,
1178 PB_HEIGHT
1180 const char *filename;
1181 int x, y, height, width;
1182 uint32_t set = 0;
1183 const char *ptr = wps_bufptr;
1184 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1185 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1187 if (!pb || !item)
1188 return WPS_ERROR_INVALID_PARAM;
1190 struct viewport *vp = &curr_vp->vp;
1191 /* we need to know what line number (viewport relative) this pb is,
1192 * so count them... */
1193 int line_num = -1;
1194 struct skin_line *line = curr_vp->lines;
1195 while (line)
1197 line_num++;
1198 line = line->next;
1200 pb->vp = vp;
1201 pb->have_bitmap_pb = false;
1202 pb->bm.data = NULL; /* no bitmap specified */
1203 pb->follow_lang_direction = follow_lang_direction > 0;
1204 pb->draw = false;
1206 if (*wps_bufptr != '|') /* regular old style */
1208 pb->x = 0;
1209 pb->width = vp->width;
1210 pb->height = SYSFONT_HEIGHT-2;
1211 pb->y = -line_num - 1; /* Will be computed during the rendering */
1212 if (token->type == WPS_TOKEN_VOLUME || token->type == WPS_TOKEN_BATTERY_PERCENT)
1213 return 0; /* dont add it, let the regular token handling do the work */
1214 add_to_ll_chain(&wps_data->progressbars, item);
1215 return 0;
1217 ptr = wps_bufptr + 1;
1219 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1220 &x, &y, &width, &height)))
1222 /* If we are in a conditional then we probably don't want to fail
1223 * if the above doesnt work. So assume the | is breaking the conditional
1224 * and move on. The next token will fail if this is incorrect. */
1225 if (level >= 0)
1226 return 0;
1227 return WPS_ERROR_INVALID_PARAM;
1230 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1231 pb->bm.data = (char*)filename;
1233 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1234 pb->x = x;
1235 else
1236 pb->x = vp->x;
1238 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1240 /* A zero width causes a divide-by-zero error later, so reject it */
1241 if (width == 0)
1242 return WPS_ERROR_INVALID_PARAM;
1244 pb->width = width;
1246 else
1247 pb->width = vp->width - pb->x;
1249 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1251 /* A zero height makes no sense - reject it */
1252 if (height == 0)
1253 return WPS_ERROR_INVALID_PARAM;
1255 pb->height = height;
1257 else
1259 if (vp->font > FONT_UI)
1260 pb->height = -1; /* calculate at display time */
1261 else
1263 #ifndef __PCTOOL__
1264 pb->height = font_get(vp->font)->height;
1265 #else
1266 pb->height = 8;
1267 #endif
1271 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1272 pb->y = y;
1273 else
1274 pb->y = -line_num - 1; /* Will be computed during the rendering */
1276 add_to_ll_chain(&wps_data->progressbars, item);
1277 if (token->type == WPS_TOKEN_VOLUME)
1278 token->type = WPS_TOKEN_VOLUMEBAR;
1279 else if (token->type == WPS_TOKEN_BATTERY_PERCENT)
1280 token->type = WPS_TOKEN_BATTERY_PERCENTBAR;
1281 pb->type = token->type;
1283 return ptr+1-wps_bufptr;
1284 #else
1285 (void)wps_bufptr;
1286 if (token->type != WPS_TOKEN_VOLUME &&
1287 token->type != WPS_TOKEN_BATTERY_PERCENTBAR)
1289 wps_data->full_line_progressbar =
1290 token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
1292 return 0;
1294 #endif
1297 #ifdef HAVE_ALBUMART
1298 static int parse_int(const char *newline, const char **_pos, int *num)
1300 *_pos = parse_list("d", NULL, '|', *_pos, num);
1302 return (!*_pos || *_pos > newline || **_pos != '|');
1305 static int parse_albumart_load(const char *wps_bufptr,
1306 struct wps_token *token,
1307 struct wps_data *wps_data)
1309 const char *_pos, *newline;
1310 bool parsing;
1311 struct dim dimensions;
1312 int albumart_slot;
1313 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1314 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1315 (void)token; /* silence warning */
1316 if (!aa)
1317 return skip_end_of_line(wps_bufptr);
1319 /* reset albumart info in wps */
1320 aa->width = -1;
1321 aa->height = -1;
1322 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1323 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1324 aa->vp = &curr_vp->vp;
1326 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1328 newline = strchr(wps_bufptr, '\n');
1330 _pos = wps_bufptr;
1332 if (*_pos != '|')
1333 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1335 ++_pos;
1337 /* initial validation and parsing of x component */
1338 if (parse_int(newline, &_pos, &aa->x))
1339 return WPS_ERROR_INVALID_PARAM;
1341 ++_pos;
1343 /* initial validation and parsing of y component */
1344 if (parse_int(newline, &_pos, &aa->y))
1345 return WPS_ERROR_INVALID_PARAM;
1347 /* parsing width field */
1348 parsing = true;
1349 while (parsing)
1351 /* apply each modifier in turn */
1352 ++_pos;
1353 switch (*_pos)
1355 case 'l':
1356 case 'L':
1357 case '+':
1358 if (swap_for_rtl)
1359 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1360 else
1361 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1362 break;
1363 case 'c':
1364 case 'C':
1365 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1366 break;
1367 case 'r':
1368 case 'R':
1369 case '-':
1370 if (swap_for_rtl)
1371 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1372 else
1373 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1374 break;
1375 case 'd':
1376 case 'D':
1377 case 'i':
1378 case 'I':
1379 case 's':
1380 case 'S':
1381 /* simply ignored */
1382 break;
1383 default:
1384 parsing = false;
1385 break;
1388 /* extract max width data */
1389 if (*_pos != '|')
1391 if (parse_int(newline, &_pos, &aa->width))
1392 return WPS_ERROR_INVALID_PARAM;
1395 /* parsing height field */
1396 parsing = true;
1397 while (parsing)
1399 /* apply each modifier in turn */
1400 ++_pos;
1401 switch (*_pos)
1403 case 't':
1404 case 'T':
1405 case '-':
1406 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1407 break;
1408 case 'c':
1409 case 'C':
1410 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1411 break;
1412 case 'b':
1413 case 'B':
1414 case '+':
1415 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1416 break;
1417 case 'd':
1418 case 'D':
1419 case 'i':
1420 case 'I':
1421 case 's':
1422 case 'S':
1423 /* simply ignored */
1424 break;
1425 default:
1426 parsing = false;
1427 break;
1430 /* extract max height data */
1431 if (*_pos != '|')
1433 if (parse_int(newline, &_pos, &aa->height))
1434 return WPS_ERROR_INVALID_PARAM;
1437 /* if we got here, we parsed everything ok .. ! */
1438 if (aa->width < 0)
1439 aa->width = 0;
1440 else if (aa->width > LCD_WIDTH)
1441 aa->width = LCD_WIDTH;
1443 if (aa->height < 0)
1444 aa->height = 0;
1445 else if (aa->height > LCD_HEIGHT)
1446 aa->height = LCD_HEIGHT;
1448 if (swap_for_rtl)
1449 aa->x = LCD_WIDTH - (aa->x + aa->width);
1451 aa->state = WPS_ALBUMART_LOAD;
1452 aa->draw = false;
1453 wps_data->albumart = aa;
1455 dimensions.width = aa->width;
1456 dimensions.height = aa->height;
1458 albumart_slot = playback_claim_aa_slot(&dimensions);
1460 if (0 <= albumart_slot)
1461 wps_data->playback_aa_slot = albumart_slot;
1463 /* Skip the rest of the line */
1464 return skip_end_of_line(wps_bufptr);
1467 static int parse_albumart_display(const char *wps_bufptr,
1468 struct wps_token *token,
1469 struct wps_data *wps_data)
1471 (void)wps_bufptr;
1472 struct wps_token *prev = token-1;
1473 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1475 token->type = WPS_TOKEN_ALBUMART_FOUND;
1477 else if (wps_data->albumart)
1479 wps_data->albumart->vp = &curr_vp->vp;
1481 #if 0
1482 /* the old code did this so keep it here for now...
1483 * this is to allow the posibility to showing the next tracks AA! */
1484 if (wps_bufptr+1 == 'n')
1485 return 1;
1486 #endif
1487 return 0;
1489 #endif /* HAVE_ALBUMART */
1491 #ifdef HAVE_TOUCHSCREEN
1493 struct touchaction {const char* s; int action;};
1494 static const struct touchaction touchactions[] = {
1495 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1496 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1497 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1498 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1499 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1500 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1501 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1502 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1504 static int parse_touchregion(const char *wps_bufptr,
1505 struct wps_token *token, struct wps_data *wps_data)
1507 (void)token;
1508 unsigned i, imax;
1509 struct touchregion *region = NULL;
1510 const char *ptr = wps_bufptr;
1511 const char *action, *end;
1512 const char pb_string[] = "progressbar";
1513 const char vol_string[] = "volume";
1514 int x,y,w,h;
1515 char temp[20];
1517 /* format: %T|x|y|width|height|action|
1518 * if action starts with & the area must be held to happen
1519 * action is one of:
1520 * play - play/pause playback
1521 * stop - stop playback, exit the wps
1522 * prev - prev track
1523 * next - next track
1524 * ffwd - seek forward
1525 * rwd - seek backwards
1526 * menu - go back to the main menu
1527 * browse - go back to the file/db browser
1528 * shuffle - toggle shuffle mode
1529 * repmode - cycle the repeat mode
1530 * quickscreen - go into the quickscreen
1531 * contextmenu - open the context menu
1532 * playlist - go into the playlist
1533 * pitch - go into the pitchscreen
1534 * volup - increase volume by one step
1535 * voldown - decrease volume by one step
1539 if (*ptr != '|')
1540 return WPS_ERROR_INVALID_PARAM;
1541 ptr++;
1543 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1544 return WPS_ERROR_INVALID_PARAM;
1546 /* Check there is a terminating | */
1547 if (*ptr != '|')
1548 return WPS_ERROR_INVALID_PARAM;
1550 region = skin_buffer_alloc(sizeof(struct touchregion));
1551 if (!region)
1552 return WPS_ERROR_INVALID_PARAM;
1554 /* should probably do some bounds checking here with the viewport... but later */
1555 region->action = ACTION_NONE;
1556 region->x = x;
1557 region->y = y;
1558 region->width = w;
1559 region->height = h;
1560 region->wvp = curr_vp;
1561 region->armed = false;
1563 end = strchr(action, '|');
1564 if (!end || (size_t)(end-action+1) > sizeof temp)
1565 return WPS_ERROR_INVALID_PARAM;
1566 strlcpy(temp, action, end-action+1);
1567 action = temp;
1569 if(!strcmp(pb_string, action))
1570 region->type = WPS_TOUCHREGION_SCROLLBAR;
1571 else if(!strcmp(vol_string, action))
1572 region->type = WPS_TOUCHREGION_VOLUME;
1573 else
1575 region->type = WPS_TOUCHREGION_ACTION;
1577 if (*action == '&')
1579 action++;
1580 region->repeat = true;
1582 else
1583 region->repeat = false;
1585 imax = ARRAYLEN(touchactions);
1586 for (i = 0; i < imax; i++)
1588 /* try to match with one of our touchregion screens */
1589 if (!strcmp(touchactions[i].s, action))
1591 region->action = touchactions[i].action;
1592 break;
1595 if (region->action == ACTION_NONE)
1596 return WPS_ERROR_INVALID_PARAM;
1598 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1599 if (!item)
1600 return WPS_ERROR_INVALID_PARAM;
1601 add_to_ll_chain(&wps_data->touchregions, item);
1602 return skip_end_of_line(wps_bufptr);
1604 #endif
1606 /* Parse a generic token from the given string. Return the length read */
1607 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1609 int skip = 0, taglen = 0, ret;
1610 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1611 const struct wps_tag *tag;
1612 memset(token, 0, sizeof(*token));
1614 switch(*wps_bufptr)
1617 case '%':
1618 case '<':
1619 case '|':
1620 case '>':
1621 case ';':
1622 case '#':
1623 /* escaped characters */
1624 token->type = WPS_TOKEN_CHARACTER;
1625 token->value.c = *wps_bufptr;
1626 taglen = 1;
1627 wps_data->num_tokens++;
1628 break;
1630 case '?':
1631 /* conditional tag */
1632 token->type = WPS_TOKEN_CONDITIONAL;
1633 level++;
1634 condindex[level] = wps_data->num_tokens;
1635 numoptions[level] = 1;
1636 wps_data->num_tokens++;
1637 ret = parse_token(wps_bufptr + 1, wps_data);
1638 if (ret < 0) return ret;
1639 taglen = 1 + ret;
1640 break;
1642 default:
1643 /* find what tag we have */
1644 for (tag = all_tags;
1645 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1646 tag++) ;
1648 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1649 token->type = tag->type;
1650 curr_line->curr_subline->line_type |= tag->refresh_type;
1652 /* if the tag has a special parsing function, we call it */
1653 if (tag->parse_func)
1655 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1656 if (ret < 0) return ret;
1657 skip += ret;
1660 /* Some tags we don't want to save as tokens */
1661 if (tag->type == WPS_NO_TOKEN)
1662 break;
1664 /* tags that start with 'F', 'I' or 'D' are for the next file */
1665 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1666 *(tag->name) == 'D')
1667 token->next = true;
1669 wps_data->num_tokens++;
1670 break;
1673 skip += taglen;
1674 return skip;
1679 * Returns the number of bytes to skip the buf pointer to access the false
1680 * branch in a _binary_ conditional
1682 * That is:
1683 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1684 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1686 * depending on the features of a target it's not called from check_feature_tag,
1687 * hence the __attribute__ or it issues compiler warnings
1691 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1692 static int find_false_branch(const char *wps_bufptr)
1694 const char *buf = wps_bufptr;
1695 /* wps_bufptr is after the opening '<', hence level = 1*/
1696 int level = 1;
1697 char ch;
1700 ch = *buf;
1701 if (ch == '%')
1702 { /* filter out the characters we check later if they're printed
1703 * as literals */
1704 ch = *(++buf);
1705 if (ch == '<' || ch == '>' || ch == '|')
1706 continue;
1707 /* else: some tags/printed literals we skip over */
1709 else if (ch == '<') /* nested conditional */
1710 level++;
1711 else if (ch == '>')
1712 { /* closed our or a nested conditional,
1713 * do NOT skip over the '>' so that wps_parse() sees it for closing
1714 * if it is the closing one for our conditional */
1715 level--;
1717 else if (ch == '|' && level == 1)
1718 { /* we found our separator, point before and get out */
1719 break;
1721 /* if level is 0, we don't have a false branch */
1722 } while (level > 0 && *(++buf));
1724 return buf - wps_bufptr;
1728 * returns the number of bytes to get the appropriate branch of a binary
1729 * conditional
1731 * That means:
1732 * - if a feature is available, it returns 0 to not skip anything
1733 * - if the feature is not available, skip to the false branch and don't
1734 * parse the true branch at all
1736 * */
1737 static int check_feature_tag(const char *wps_bufptr, const int type)
1739 (void)wps_bufptr;
1740 switch (type)
1742 case WPS_TOKEN_RTC_PRESENT:
1743 #if CONFIG_RTC
1744 return 0;
1745 #else
1746 return find_false_branch(wps_bufptr);
1747 #endif
1748 case WPS_TOKEN_HAVE_RECORDING:
1749 #ifdef HAVE_RECORDING
1750 return 0;
1751 #else
1752 return find_false_branch(wps_bufptr);
1753 #endif
1754 case WPS_TOKEN_HAVE_TUNER:
1755 #if CONFIG_TUNER
1756 if (radio_hardware_present())
1757 return 0;
1758 #endif
1759 return find_false_branch(wps_bufptr);
1761 default: /* not a tag we care about, just don't skip */
1762 return 0;
1767 /* Parses the WPS.
1768 data is the pointer to the structure where the parsed WPS should be stored.
1769 It is initialised.
1770 wps_bufptr points to the string containing the WPS tags */
1771 #define TOKEN_BLOCK_SIZE 128
1772 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1774 if (!data || !wps_bufptr || !*wps_bufptr)
1775 return false;
1776 enum wps_parse_error fail = PARSE_OK;
1777 int ret;
1778 int max_tokens = TOKEN_BLOCK_SIZE;
1779 size_t buf_free = 0;
1780 line_number = 0;
1781 level = -1;
1783 /* allocate enough RAM for a reasonable skin, grow as needed.
1784 * Free any used RAM before loading the images to be 100% RAM efficient */
1785 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1786 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1787 return false;
1788 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1789 data->num_tokens = 0;
1791 #if LCD_DEPTH > 1
1792 /* Backdrop defaults to the setting unless %X is used, so set it now */
1793 if (global_settings.backdrop_file[0])
1795 data->backdrop = "-";
1797 #endif
1799 while (*wps_bufptr && !fail)
1801 if (follow_lang_direction)
1802 follow_lang_direction--;
1803 /* first make sure there is enough room for tokens */
1804 if (max_tokens <= data->num_tokens + 5)
1806 int extra_tokens = TOKEN_BLOCK_SIZE;
1807 size_t needed = extra_tokens * sizeof(struct wps_token);
1808 /* do some smarts here to grow the array a bit */
1809 if (skin_buffer_freespace() < needed)
1811 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1812 break;
1814 skin_buffer_increment(needed, false);
1815 max_tokens += extra_tokens;
1818 switch(*wps_bufptr++)
1821 /* Regular tag */
1822 case '%':
1823 if ((ret = parse_token(wps_bufptr, data)) < 0)
1825 fail = PARSE_FAIL_COND_INVALID_PARAM;
1826 break;
1828 else if (level >= WPS_MAX_COND_LEVEL - 1)
1830 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1831 break;
1833 wps_bufptr += ret;
1834 break;
1836 /* Alternating sublines separator */
1837 case ';':
1838 if (level >= 0) /* there are unclosed conditionals */
1840 fail = PARSE_FAIL_UNCLOSED_COND;
1841 break;
1844 if (!skin_start_new_subline(curr_line, data->num_tokens))
1845 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1847 break;
1849 /* Conditional list start */
1850 case '<':
1851 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1853 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1854 break;
1856 wps_bufptr += check_feature_tag(wps_bufptr,
1857 data->tokens[data->num_tokens-1].type);
1858 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1859 lastcond[level] = data->num_tokens++;
1860 break;
1862 /* Conditional list end */
1863 case '>':
1864 if (level < 0) /* not in a conditional, invalid char */
1866 fail = PARSE_FAIL_INVALID_CHAR;
1867 break;
1870 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1871 if (lastcond[level])
1872 data->tokens[lastcond[level]].value.i = data->num_tokens;
1873 else
1875 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1876 break;
1879 lastcond[level] = 0;
1880 data->num_tokens++;
1881 data->tokens[condindex[level]].value.i = numoptions[level];
1882 level--;
1883 break;
1885 /* Conditional list option */
1886 case '|':
1887 if (level < 0) /* not in a conditional, invalid char */
1889 fail = PARSE_FAIL_INVALID_CHAR;
1890 break;
1893 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1894 if (lastcond[level])
1895 data->tokens[lastcond[level]].value.i = data->num_tokens;
1896 else
1898 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1899 break;
1902 lastcond[level] = data->num_tokens;
1903 numoptions[level]++;
1904 data->num_tokens++;
1905 break;
1907 /* Comment */
1908 case '#':
1909 if (level >= 0) /* there are unclosed conditionals */
1911 fail = PARSE_FAIL_UNCLOSED_COND;
1912 break;
1915 wps_bufptr += skip_end_of_line(wps_bufptr);
1916 break;
1918 /* End of this line */
1919 case '\n':
1920 if (level >= 0) /* there are unclosed conditionals */
1922 fail = PARSE_FAIL_UNCLOSED_COND;
1923 break;
1925 /* add a new token for the \n so empty lines are correct */
1926 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1927 data->tokens[data->num_tokens].value.c = '\n';
1928 data->tokens[data->num_tokens].next = false;
1929 data->num_tokens++;
1931 if (!skin_start_new_line(curr_vp, data->num_tokens))
1933 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1934 break;
1936 line_number++;
1938 break;
1940 /* String */
1941 default:
1943 unsigned int len = 1;
1944 const char *string_start = wps_bufptr - 1;
1946 /* find the length of the string */
1947 while (*wps_bufptr && *wps_bufptr != '#' &&
1948 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1949 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1950 *wps_bufptr != '|' && *wps_bufptr != '\n')
1952 wps_bufptr++;
1953 len++;
1956 /* look if we already have that string */
1957 char *str;
1958 bool found = false;
1959 struct skin_token_list *list = data->strings;
1960 while (list)
1962 str = (char*)list->token->value.data;
1963 found = (strlen(str) == len &&
1964 strncmp(string_start, str, len) == 0);
1965 if (found)
1966 break; /* break here because the list item is
1967 used if its found */
1968 list = list->next;
1970 /* If a matching string is found, found is true and i is
1971 the index of the string. If not, found is false */
1973 if (!found)
1975 /* new string */
1976 str = (char*)skin_buffer_alloc(len+1);
1977 if (!str)
1979 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1980 break;
1982 strlcpy(str, string_start, len+1);
1983 struct skin_token_list *item =
1984 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1985 if(!item)
1987 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1988 break;
1990 add_to_ll_chain(&data->strings, item);
1992 else
1994 /* another occurrence of an existing string */
1995 data->tokens[data->num_tokens].value.data = list->token->value.data;
1997 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1998 data->num_tokens++;
2000 break;
2004 if (!fail && level >= 0) /* there are unclosed conditionals */
2005 fail = PARSE_FAIL_UNCLOSED_COND;
2007 if (*wps_bufptr && !fail)
2008 /* one of the limits of the while loop was exceeded */
2009 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2011 /* Success! */
2012 curr_line->curr_subline->last_token_idx = data->num_tokens;
2013 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
2014 /* freeup unused tokens */
2015 skin_buffer_free_from_front(sizeof(struct wps_token)
2016 * (max_tokens - data->num_tokens));
2018 #if defined(DEBUG) || defined(SIMULATOR)
2019 if (debug)
2021 print_debug_info(data, fail, line_number);
2022 debug_skin_usage();
2024 #else
2025 (void)debug;
2026 #endif
2028 return (fail == 0);
2033 * initial setup of wps_data; does reset everything
2034 * except fields which need to survive, i.e.
2037 static void skin_data_reset(struct wps_data *wps_data)
2039 #ifdef HAVE_LCD_BITMAP
2040 wps_data->images = NULL;
2041 wps_data->progressbars = NULL;
2042 #endif
2043 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2044 wps_data->backdrop = NULL;
2045 #endif
2046 #ifdef HAVE_TOUCHSCREEN
2047 wps_data->touchregions = NULL;
2048 #endif
2049 wps_data->viewports = NULL;
2050 wps_data->strings = NULL;
2051 #ifdef HAVE_ALBUMART
2052 wps_data->albumart = NULL;
2053 if (wps_data->playback_aa_slot >= 0)
2055 playback_release_aa_slot(wps_data->playback_aa_slot);
2056 wps_data->playback_aa_slot = -1;
2058 #endif
2059 wps_data->tokens = NULL;
2060 wps_data->num_tokens = 0;
2062 #ifdef HAVE_LCD_BITMAP
2063 wps_data->peak_meter_enabled = false;
2064 wps_data->wps_sb_tag = false;
2065 wps_data->show_sb_on_wps = false;
2066 #else /* HAVE_LCD_CHARCELLS */
2067 /* progress bars */
2068 int i;
2069 for (i = 0; i < 8; i++)
2071 wps_data->wps_progress_pat[i] = 0;
2073 wps_data->full_line_progressbar = false;
2074 #endif
2075 wps_data->wps_loaded = false;
2078 #ifdef HAVE_LCD_BITMAP
2079 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2081 (void)wps_data; /* only needed for remote targets */
2082 char img_path[MAX_PATH];
2083 get_image_filename(bitmap->data, bmpdir,
2084 img_path, sizeof(img_path));
2086 /* load the image */
2087 int format;
2088 #ifdef HAVE_REMOTE_LCD
2089 if (curr_screen == SCREEN_REMOTE)
2090 format = FORMAT_ANY|FORMAT_REMOTE;
2091 else
2092 #endif
2093 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2095 size_t max_buf;
2096 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2097 bitmap->data = imgbuf;
2098 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2100 if (ret > 0)
2102 skin_buffer_increment(ret, true);
2103 return true;
2105 else
2107 /* Abort if we can't load an image */
2108 DEBUGF("Couldn't load '%s'\n", img_path);
2109 return false;
2113 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2115 struct skin_token_list *list;
2116 bool retval = true; /* return false if a single image failed to load */
2117 /* do the progressbars */
2118 list = wps_data->progressbars;
2119 while (list)
2121 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2122 if (pb->bm.data)
2124 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2125 if (!pb->have_bitmap_pb) /* no success */
2126 retval = false;
2128 list = list->next;
2130 /* regular images */
2131 list = wps_data->images;
2132 while (list)
2134 struct gui_img *img = (struct gui_img*)list->token->value.data;
2135 if (img->bm.data)
2137 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2138 if (img->loaded)
2139 img->subimage_height = img->bm.height / img->num_subimages;
2140 else
2141 retval = false;
2143 list = list->next;
2146 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2147 /* Backdrop load scheme:
2148 * 1) %X|filename|
2149 * 2) load the backdrop from settings
2151 if (wps_data->backdrop)
2153 bool needed = wps_data->backdrop[0] != '-';
2154 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2155 bmpdir, curr_screen);
2156 if (!wps_data->backdrop && needed)
2157 retval = false;
2159 #endif /* has backdrop support */
2161 return retval;
2164 static bool skin_load_fonts(struct wps_data *data)
2166 /* don't spit out after the first failue to aid debugging */
2167 bool success = true;
2168 struct skin_token_list *vp_list;
2169 int font_id;
2170 /* walk though each viewport and assign its font */
2171 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2173 /* first, find the viewports that have a non-sys/ui-font font */
2174 struct skin_viewport *skin_vp =
2175 (struct skin_viewport*)vp_list->token->value.data;
2176 struct viewport *vp = &skin_vp->vp;
2179 if (vp->font <= FONT_UI)
2180 { /* the usual case -> built-in fonts */
2181 #ifdef HAVE_REMOTE_LCD
2182 if (vp->font == FONT_UI)
2183 vp->font += curr_screen;
2184 #endif
2185 continue;
2187 font_id = vp->font;
2189 /* now find the corresponding skin_font */
2190 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2191 if (!font->name)
2193 DEBUGF("font %d not specified\n", font_id);
2194 success = false;
2195 continue;
2198 /* load the font - will handle loading the same font again if
2199 * multiple viewports use the same */
2200 if (font->id < 0)
2202 char *dot = strchr(font->name, '.');
2203 *dot = '\0';
2204 font->id = skin_font_load(font->name);
2207 if (font->id < 0)
2209 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2210 font_id, font->name);
2211 success = false;
2212 continue;
2215 /* finally, assign the font_id to the viewport */
2216 vp->font = font->id;
2218 return success;
2221 #endif /* HAVE_LCD_BITMAP */
2223 /* to setup up the wps-data from a format-buffer (isfile = false)
2224 from a (wps-)file (isfile = true)*/
2225 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2226 const char *buf, bool isfile)
2228 char *wps_buffer = NULL;
2229 if (!wps_data || !buf)
2230 return false;
2231 #ifdef HAVE_ALBUMART
2232 int status;
2233 struct mp3entry *curtrack;
2234 long offset;
2235 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2236 if (wps_data->albumart)
2238 old_aa.state = wps_data->albumart->state;
2239 old_aa.height = wps_data->albumart->height;
2240 old_aa.width = wps_data->albumart->width;
2242 #endif
2243 #ifdef HAVE_LCD_BITMAP
2244 int i;
2245 for (i=0;i<MAXUSERFONTS;i++)
2247 skinfonts[i].id = -1;
2248 skinfonts[i].name = NULL;
2250 #endif
2251 #ifdef DEBUG_SKIN_ENGINE
2252 if (isfile && debug_wps)
2254 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2256 #endif
2258 skin_data_reset(wps_data);
2259 wps_data->wps_loaded = false;
2260 curr_screen = screen;
2262 /* alloc default viewport, will be fixed up later */
2263 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2264 if (!curr_vp)
2265 return false;
2266 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2267 if (!list)
2268 return false;
2269 add_to_ll_chain(&wps_data->viewports, list);
2272 /* Initialise the first (default) viewport */
2273 curr_vp->label = VP_DEFAULT_LABEL;
2274 curr_vp->hidden_flags = 0;
2275 curr_vp->lines = NULL;
2277 viewport_set_defaults(&curr_vp->vp, screen);
2278 #ifdef HAVE_LCD_BITMAP
2279 curr_vp->vp.font = FONT_UI;
2280 #endif
2282 curr_line = NULL;
2283 if (!skin_start_new_line(curr_vp, 0))
2284 return false;
2286 if (isfile)
2288 int fd = open_utf8(buf, O_RDONLY);
2290 if (fd < 0)
2291 return false;
2293 /* get buffer space from the plugin buffer */
2294 size_t buffersize = 0;
2295 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2297 if (!wps_buffer)
2298 return false;
2300 /* copy the file's content to the buffer for parsing,
2301 ensuring that every line ends with a newline char. */
2302 unsigned int start = 0;
2303 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2305 start += strlen(wps_buffer + start);
2306 if (start < buffersize - 1)
2308 wps_buffer[start++] = '\n';
2309 wps_buffer[start] = 0;
2312 close(fd);
2313 if (start <= 0)
2314 return false;
2316 else
2318 wps_buffer = (char*)buf;
2320 /* parse the WPS source */
2321 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2322 skin_data_reset(wps_data);
2323 return false;
2326 #ifdef HAVE_LCD_BITMAP
2327 char bmpdir[MAX_PATH];
2328 if (isfile)
2330 /* get the bitmap dir */
2331 char *dot = strrchr(buf, '.');
2332 strlcpy(bmpdir, buf, dot - buf + 1);
2334 else
2336 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2338 /* load the bitmaps that were found by the parsing */
2339 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2340 !skin_load_fonts(wps_data))
2342 skin_data_reset(wps_data);
2343 return false;
2345 #endif
2346 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2347 status = audio_status();
2348 if (status & AUDIO_STATUS_PLAY)
2350 struct skin_albumart *aa = wps_data->albumart;
2351 if (aa && ((aa->state && !old_aa.state) ||
2352 (aa->state &&
2353 (((old_aa.height != aa->height) ||
2354 (old_aa.width != aa->width))))))
2356 curtrack = audio_current_track();
2357 offset = curtrack->offset;
2358 audio_stop();
2359 if (!(status & AUDIO_STATUS_PAUSE))
2360 audio_play(offset);
2363 #endif
2364 wps_data->wps_loaded = true;
2365 #ifdef DEBUG_SKIN_ENGINE
2366 if (isfile && debug_wps)
2367 debug_skin_usage();
2368 #endif
2369 return true;