updates the README document.
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobbb8f8c57d1ce9a0b28e22d8f70d0e36b60464377
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)
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 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1088 /* prevent matches on cfg_name prefixes */
1089 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1090 break;
1091 #ifndef __PCTOOL__
1092 if (i == nb_settings)
1093 return WPS_ERROR_INVALID_PARAM;
1094 #endif
1096 /* Store the setting number */
1097 token->value.i = i;
1099 /* Skip the rest of the line */
1100 return end-ptr+2;
1104 static int parse_dir_level(const char *wps_bufptr,
1105 struct wps_token *token,
1106 struct wps_data *wps_data)
1108 char val[] = { *wps_bufptr, '\0' };
1109 token->value.i = atoi(val);
1110 (void)wps_data; /* Kill warnings */
1111 return 1;
1114 static int parse_timeout(const char *wps_bufptr,
1115 struct wps_token *token,
1116 struct wps_data *wps_data)
1118 int skip = 0;
1119 int val = 0;
1120 bool have_point = false;
1121 bool have_tenth = false;
1123 (void)wps_data; /* Kill the warning */
1125 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1127 if (*wps_bufptr != '.')
1129 val *= 10;
1130 val += *wps_bufptr - '0';
1131 if (have_point)
1133 have_tenth = true;
1134 wps_bufptr++;
1135 skip++;
1136 break;
1139 else
1140 have_point = true;
1142 wps_bufptr++;
1143 skip++;
1146 if (have_tenth == false)
1147 val *= 10;
1149 if (val == 0 && skip == 0)
1151 /* decide what to do if no value was specified */
1152 switch (token->type)
1154 case WPS_TOKEN_SUBLINE_TIMEOUT:
1155 return -1;
1156 case WPS_TOKEN_BUTTON_VOLUME:
1157 case WPS_TOKEN_TRACK_STARTING:
1158 case WPS_TOKEN_TRACK_ENDING:
1159 val = 10;
1160 break;
1163 token->value.i = val;
1165 return skip;
1168 static int parse_progressbar(const char *wps_bufptr,
1169 struct wps_token *token,
1170 struct wps_data *wps_data)
1172 /* %pb or %pb|filename|x|y|width|height|
1173 using - for any of the params uses "sane" values */
1174 #ifdef HAVE_LCD_BITMAP
1175 enum {
1176 PB_FILENAME = 0,
1177 PB_X,
1178 PB_Y,
1179 PB_WIDTH,
1180 PB_HEIGHT
1182 const char *filename;
1183 int x, y, height, width;
1184 uint32_t set = 0;
1185 const char *ptr = wps_bufptr;
1186 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1187 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1189 if (!pb || !item)
1190 return WPS_ERROR_INVALID_PARAM;
1192 struct viewport *vp = &curr_vp->vp;
1193 /* we need to know what line number (viewport relative) this pb is,
1194 * so count them... */
1195 int line_num = -1;
1196 struct skin_line *line = curr_vp->lines;
1197 while (line)
1199 line_num++;
1200 line = line->next;
1202 pb->vp = vp;
1203 pb->have_bitmap_pb = false;
1204 pb->bm.data = NULL; /* no bitmap specified */
1205 pb->follow_lang_direction = follow_lang_direction > 0;
1206 pb->draw = false;
1208 if (*wps_bufptr != '|') /* regular old style */
1210 pb->x = 0;
1211 pb->width = vp->width;
1212 pb->height = SYSFONT_HEIGHT-2;
1213 pb->y = -line_num - 1; /* Will be computed during the rendering */
1214 if (token->type == WPS_TOKEN_VOLUME || token->type == WPS_TOKEN_BATTERY_PERCENT)
1215 return 0; /* dont add it, let the regular token handling do the work */
1216 add_to_ll_chain(&wps_data->progressbars, item);
1217 return 0;
1219 ptr = wps_bufptr + 1;
1221 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1222 &x, &y, &width, &height)))
1224 /* If we are in a conditional then we probably don't want to fail
1225 * if the above doesnt work. So assume the | is breaking the conditional
1226 * and move on. The next token will fail if this is incorrect. */
1227 if (level >= 0)
1228 return 0;
1229 return WPS_ERROR_INVALID_PARAM;
1232 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1233 pb->bm.data = (char*)filename;
1235 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1236 pb->x = x;
1237 else
1238 pb->x = vp->x;
1240 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1242 /* A zero width causes a divide-by-zero error later, so reject it */
1243 if (width == 0)
1244 return WPS_ERROR_INVALID_PARAM;
1246 pb->width = width;
1248 else
1249 pb->width = vp->width - pb->x;
1251 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1253 /* A zero height makes no sense - reject it */
1254 if (height == 0)
1255 return WPS_ERROR_INVALID_PARAM;
1257 pb->height = height;
1259 else
1261 if (vp->font > FONT_UI)
1262 pb->height = -1; /* calculate at display time */
1263 else
1265 #ifndef __PCTOOL__
1266 pb->height = font_get(vp->font)->height;
1267 #else
1268 pb->height = 8;
1269 #endif
1273 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1274 pb->y = y;
1275 else
1276 pb->y = -line_num - 1; /* Will be computed during the rendering */
1278 add_to_ll_chain(&wps_data->progressbars, item);
1279 if (token->type == WPS_TOKEN_VOLUME)
1280 token->type = WPS_TOKEN_VOLUMEBAR;
1281 else if (token->type == WPS_TOKEN_BATTERY_PERCENT)
1282 token->type = WPS_TOKEN_BATTERY_PERCENTBAR;
1283 pb->type = token->type;
1285 return ptr+1-wps_bufptr;
1286 #else
1287 (void)wps_bufptr;
1288 if (token->type != WPS_TOKEN_VOLUME &&
1289 token->type != WPS_TOKEN_BATTERY_PERCENTBAR)
1291 wps_data->full_line_progressbar =
1292 token->type == WPS_TOKEN_PLAYER_PROGRESSBAR;
1294 return 0;
1296 #endif
1299 #ifdef HAVE_ALBUMART
1300 static int parse_int(const char *newline, const char **_pos, int *num)
1302 *_pos = parse_list("d", NULL, '|', *_pos, num);
1304 return (!*_pos || *_pos > newline || **_pos != '|');
1307 static int parse_albumart_load(const char *wps_bufptr,
1308 struct wps_token *token,
1309 struct wps_data *wps_data)
1311 const char *_pos, *newline;
1312 bool parsing;
1313 struct dim dimensions;
1314 int albumart_slot;
1315 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1316 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1317 (void)token; /* silence warning */
1318 if (!aa)
1319 return skip_end_of_line(wps_bufptr);
1321 /* reset albumart info in wps */
1322 aa->width = -1;
1323 aa->height = -1;
1324 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1325 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1326 aa->vp = &curr_vp->vp;
1328 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1330 newline = strchr(wps_bufptr, '\n');
1332 _pos = wps_bufptr;
1334 if (*_pos != '|')
1335 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1337 ++_pos;
1339 /* initial validation and parsing of x component */
1340 if (parse_int(newline, &_pos, &aa->x))
1341 return WPS_ERROR_INVALID_PARAM;
1343 ++_pos;
1345 /* initial validation and parsing of y component */
1346 if (parse_int(newline, &_pos, &aa->y))
1347 return WPS_ERROR_INVALID_PARAM;
1349 /* parsing width field */
1350 parsing = true;
1351 while (parsing)
1353 /* apply each modifier in turn */
1354 ++_pos;
1355 switch (*_pos)
1357 case 'l':
1358 case 'L':
1359 case '+':
1360 if (swap_for_rtl)
1361 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1362 else
1363 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1364 break;
1365 case 'c':
1366 case 'C':
1367 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1368 break;
1369 case 'r':
1370 case 'R':
1371 case '-':
1372 if (swap_for_rtl)
1373 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1374 else
1375 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1376 break;
1377 case 'd':
1378 case 'D':
1379 case 'i':
1380 case 'I':
1381 case 's':
1382 case 'S':
1383 /* simply ignored */
1384 break;
1385 default:
1386 parsing = false;
1387 break;
1390 /* extract max width data */
1391 if (*_pos != '|')
1393 if (parse_int(newline, &_pos, &aa->width))
1394 return WPS_ERROR_INVALID_PARAM;
1397 /* parsing height field */
1398 parsing = true;
1399 while (parsing)
1401 /* apply each modifier in turn */
1402 ++_pos;
1403 switch (*_pos)
1405 case 't':
1406 case 'T':
1407 case '-':
1408 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1409 break;
1410 case 'c':
1411 case 'C':
1412 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1413 break;
1414 case 'b':
1415 case 'B':
1416 case '+':
1417 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1418 break;
1419 case 'd':
1420 case 'D':
1421 case 'i':
1422 case 'I':
1423 case 's':
1424 case 'S':
1425 /* simply ignored */
1426 break;
1427 default:
1428 parsing = false;
1429 break;
1432 /* extract max height data */
1433 if (*_pos != '|')
1435 if (parse_int(newline, &_pos, &aa->height))
1436 return WPS_ERROR_INVALID_PARAM;
1439 /* if we got here, we parsed everything ok .. ! */
1440 if (aa->width < 0)
1441 aa->width = 0;
1442 else if (aa->width > LCD_WIDTH)
1443 aa->width = LCD_WIDTH;
1445 if (aa->height < 0)
1446 aa->height = 0;
1447 else if (aa->height > LCD_HEIGHT)
1448 aa->height = LCD_HEIGHT;
1450 if (swap_for_rtl)
1451 aa->x = LCD_WIDTH - (aa->x + aa->width);
1453 aa->state = WPS_ALBUMART_LOAD;
1454 aa->draw = false;
1455 wps_data->albumart = aa;
1457 dimensions.width = aa->width;
1458 dimensions.height = aa->height;
1460 albumart_slot = playback_claim_aa_slot(&dimensions);
1462 if (0 <= albumart_slot)
1463 wps_data->playback_aa_slot = albumart_slot;
1465 /* Skip the rest of the line */
1466 return skip_end_of_line(wps_bufptr);
1469 static int parse_albumart_display(const char *wps_bufptr,
1470 struct wps_token *token,
1471 struct wps_data *wps_data)
1473 (void)wps_bufptr;
1474 struct wps_token *prev = token-1;
1475 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1477 token->type = WPS_TOKEN_ALBUMART_FOUND;
1479 else if (wps_data->albumart)
1481 wps_data->albumart->vp = &curr_vp->vp;
1483 #if 0
1484 /* the old code did this so keep it here for now...
1485 * this is to allow the posibility to showing the next tracks AA! */
1486 if (wps_bufptr+1 == 'n')
1487 return 1;
1488 #endif
1489 return 0;
1491 #endif /* HAVE_ALBUMART */
1493 #ifdef HAVE_TOUCHSCREEN
1495 struct touchaction {const char* s; int action;};
1496 static const struct touchaction touchactions[] = {
1497 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1498 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1499 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1500 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1501 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1502 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1503 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1504 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1506 static int parse_touchregion(const char *wps_bufptr,
1507 struct wps_token *token, struct wps_data *wps_data)
1509 (void)token;
1510 unsigned i, imax;
1511 struct touchregion *region = NULL;
1512 const char *ptr = wps_bufptr;
1513 const char *action;
1514 const char pb_string[] = "progressbar";
1515 const char vol_string[] = "volume";
1516 int x,y,w,h;
1518 /* format: %T|x|y|width|height|action|
1519 * if action starts with & the area must be held to happen
1520 * action is one of:
1521 * play - play/pause playback
1522 * stop - stop playback, exit the wps
1523 * prev - prev track
1524 * next - next track
1525 * ffwd - seek forward
1526 * rwd - seek backwards
1527 * menu - go back to the main menu
1528 * browse - go back to the file/db browser
1529 * shuffle - toggle shuffle mode
1530 * repmode - cycle the repeat mode
1531 * quickscreen - go into the quickscreen
1532 * contextmenu - open the context menu
1533 * playlist - go into the playlist
1534 * pitch - go into the pitchscreen
1535 * volup - increase volume by one step
1536 * voldown - decrease volume by one step
1540 if (*ptr != '|')
1541 return WPS_ERROR_INVALID_PARAM;
1542 ptr++;
1544 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1545 return WPS_ERROR_INVALID_PARAM;
1547 /* Check there is a terminating | */
1548 if (*ptr != '|')
1549 return WPS_ERROR_INVALID_PARAM;
1551 region = skin_buffer_alloc(sizeof(struct touchregion));
1552 if (!region)
1553 return WPS_ERROR_INVALID_PARAM;
1555 /* should probably do some bounds checking here with the viewport... but later */
1556 region->action = ACTION_NONE;
1557 region->x = x;
1558 region->y = y;
1559 region->width = w;
1560 region->height = h;
1561 region->wvp = curr_vp;
1562 region->armed = false;
1564 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1565 && *(action + sizeof(pb_string)-1) == '|')
1566 region->type = WPS_TOUCHREGION_SCROLLBAR;
1567 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1568 && *(action + sizeof(vol_string)-1) == '|')
1569 region->type = WPS_TOUCHREGION_VOLUME;
1570 else
1572 region->type = WPS_TOUCHREGION_ACTION;
1574 if (*action == '&')
1576 action++;
1577 region->repeat = true;
1579 else
1580 region->repeat = false;
1582 i = 0;
1583 imax = ARRAYLEN(touchactions);
1584 while ((region->action == ACTION_NONE) &&
1585 (i < imax))
1587 /* try to match with one of our touchregion screens */
1588 int len = strlen(touchactions[i].s);
1589 if (!strncmp(touchactions[i].s, action, len)
1590 && *(action+len) == '|')
1591 region->action = touchactions[i].action;
1592 i++;
1594 if (region->action == ACTION_NONE)
1595 return WPS_ERROR_INVALID_PARAM;
1597 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1598 if (!item)
1599 return WPS_ERROR_INVALID_PARAM;
1600 add_to_ll_chain(&wps_data->touchregions, item);
1601 return skip_end_of_line(wps_bufptr);
1603 #endif
1605 /* Parse a generic token from the given string. Return the length read */
1606 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1608 int skip = 0, taglen = 0, ret;
1609 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1610 const struct wps_tag *tag;
1611 memset(token, 0, sizeof(*token));
1613 switch(*wps_bufptr)
1616 case '%':
1617 case '<':
1618 case '|':
1619 case '>':
1620 case ';':
1621 case '#':
1622 /* escaped characters */
1623 token->type = WPS_TOKEN_CHARACTER;
1624 token->value.c = *wps_bufptr;
1625 taglen = 1;
1626 wps_data->num_tokens++;
1627 break;
1629 case '?':
1630 /* conditional tag */
1631 token->type = WPS_TOKEN_CONDITIONAL;
1632 level++;
1633 condindex[level] = wps_data->num_tokens;
1634 numoptions[level] = 1;
1635 wps_data->num_tokens++;
1636 ret = parse_token(wps_bufptr + 1, wps_data);
1637 if (ret < 0) return ret;
1638 taglen = 1 + ret;
1639 break;
1641 default:
1642 /* find what tag we have */
1643 for (tag = all_tags;
1644 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1645 tag++) ;
1647 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1648 token->type = tag->type;
1649 curr_line->curr_subline->line_type |= tag->refresh_type;
1651 /* if the tag has a special parsing function, we call it */
1652 if (tag->parse_func)
1654 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1655 if (ret < 0) return ret;
1656 skip += ret;
1659 /* Some tags we don't want to save as tokens */
1660 if (tag->type == WPS_NO_TOKEN)
1661 break;
1663 /* tags that start with 'F', 'I' or 'D' are for the next file */
1664 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1665 *(tag->name) == 'D')
1666 token->next = true;
1668 wps_data->num_tokens++;
1669 break;
1672 skip += taglen;
1673 return skip;
1678 * Returns the number of bytes to skip the buf pointer to access the false
1679 * branch in a _binary_ conditional
1681 * That is:
1682 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1683 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1685 * depending on the features of a target it's not called from check_feature_tag,
1686 * hence the __attribute__ or it issues compiler warnings
1690 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1691 static int find_false_branch(const char *wps_bufptr)
1693 const char *buf = wps_bufptr;
1694 /* wps_bufptr is after the opening '<', hence level = 1*/
1695 int level = 1;
1696 char ch;
1699 ch = *buf;
1700 if (ch == '%')
1701 { /* filter out the characters we check later if they're printed
1702 * as literals */
1703 ch = *(++buf);
1704 if (ch == '<' || ch == '>' || ch == '|')
1705 continue;
1706 /* else: some tags/printed literals we skip over */
1708 else if (ch == '<') /* nested conditional */
1709 level++;
1710 else if (ch == '>')
1711 { /* closed our or a nested conditional,
1712 * do NOT skip over the '>' so that wps_parse() sees it for closing
1713 * if it is the closing one for our conditional */
1714 level--;
1716 else if (ch == '|' && level == 1)
1717 { /* we found our separator, point before and get out */
1718 break;
1720 /* if level is 0, we don't have a false branch */
1721 } while (level > 0 && *(++buf));
1723 return buf - wps_bufptr;
1727 * returns the number of bytes to get the appropriate branch of a binary
1728 * conditional
1730 * That means:
1731 * - if a feature is available, it returns 0 to not skip anything
1732 * - if the feature is not available, skip to the false branch and don't
1733 * parse the true branch at all
1735 * */
1736 static int check_feature_tag(const char *wps_bufptr, const int type)
1738 (void)wps_bufptr;
1739 switch (type)
1741 case WPS_TOKEN_RTC_PRESENT:
1742 #if CONFIG_RTC
1743 return 0;
1744 #else
1745 return find_false_branch(wps_bufptr);
1746 #endif
1747 case WPS_TOKEN_HAVE_RECORDING:
1748 #ifdef HAVE_RECORDING
1749 return 0;
1750 #else
1751 return find_false_branch(wps_bufptr);
1752 #endif
1753 case WPS_TOKEN_HAVE_TUNER:
1754 #if CONFIG_TUNER
1755 if (radio_hardware_present())
1756 return 0;
1757 #endif
1758 return find_false_branch(wps_bufptr);
1760 default: /* not a tag we care about, just don't skip */
1761 return 0;
1766 /* Parses the WPS.
1767 data is the pointer to the structure where the parsed WPS should be stored.
1768 It is initialised.
1769 wps_bufptr points to the string containing the WPS tags */
1770 #define TOKEN_BLOCK_SIZE 128
1771 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1773 if (!data || !wps_bufptr || !*wps_bufptr)
1774 return false;
1775 enum wps_parse_error fail = PARSE_OK;
1776 int ret;
1777 int max_tokens = TOKEN_BLOCK_SIZE;
1778 size_t buf_free = 0;
1779 line_number = 0;
1780 level = -1;
1782 /* allocate enough RAM for a reasonable skin, grow as needed.
1783 * Free any used RAM before loading the images to be 100% RAM efficient */
1784 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1785 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1786 return false;
1787 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1788 data->num_tokens = 0;
1790 #if LCD_DEPTH > 1
1791 /* Backdrop defaults to the setting unless %X is used, so set it now */
1792 if (global_settings.backdrop_file[0])
1794 data->backdrop = "-";
1796 #endif
1798 while (*wps_bufptr && !fail)
1800 if (follow_lang_direction)
1801 follow_lang_direction--;
1802 /* first make sure there is enough room for tokens */
1803 if (max_tokens <= data->num_tokens + 5)
1805 int extra_tokens = TOKEN_BLOCK_SIZE;
1806 size_t needed = extra_tokens * sizeof(struct wps_token);
1807 /* do some smarts here to grow the array a bit */
1808 if (skin_buffer_freespace() < needed)
1810 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1811 break;
1813 skin_buffer_increment(needed, false);
1814 max_tokens += extra_tokens;
1817 switch(*wps_bufptr++)
1820 /* Regular tag */
1821 case '%':
1822 if ((ret = parse_token(wps_bufptr, data)) < 0)
1824 fail = PARSE_FAIL_COND_INVALID_PARAM;
1825 break;
1827 else if (level >= WPS_MAX_COND_LEVEL - 1)
1829 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1830 break;
1832 wps_bufptr += ret;
1833 break;
1835 /* Alternating sublines separator */
1836 case ';':
1837 if (level >= 0) /* there are unclosed conditionals */
1839 fail = PARSE_FAIL_UNCLOSED_COND;
1840 break;
1843 if (!skin_start_new_subline(curr_line, data->num_tokens))
1844 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1846 break;
1848 /* Conditional list start */
1849 case '<':
1850 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1852 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1853 break;
1855 wps_bufptr += check_feature_tag(wps_bufptr,
1856 data->tokens[data->num_tokens-1].type);
1857 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1858 lastcond[level] = data->num_tokens++;
1859 break;
1861 /* Conditional list end */
1862 case '>':
1863 if (level < 0) /* not in a conditional, invalid char */
1865 fail = PARSE_FAIL_INVALID_CHAR;
1866 break;
1869 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1870 if (lastcond[level])
1871 data->tokens[lastcond[level]].value.i = data->num_tokens;
1872 else
1874 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1875 break;
1878 lastcond[level] = 0;
1879 data->num_tokens++;
1880 data->tokens[condindex[level]].value.i = numoptions[level];
1881 level--;
1882 break;
1884 /* Conditional list option */
1885 case '|':
1886 if (level < 0) /* not in a conditional, invalid char */
1888 fail = PARSE_FAIL_INVALID_CHAR;
1889 break;
1892 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1893 if (lastcond[level])
1894 data->tokens[lastcond[level]].value.i = data->num_tokens;
1895 else
1897 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1898 break;
1901 lastcond[level] = data->num_tokens;
1902 numoptions[level]++;
1903 data->num_tokens++;
1904 break;
1906 /* Comment */
1907 case '#':
1908 if (level >= 0) /* there are unclosed conditionals */
1910 fail = PARSE_FAIL_UNCLOSED_COND;
1911 break;
1914 wps_bufptr += skip_end_of_line(wps_bufptr);
1915 break;
1917 /* End of this line */
1918 case '\n':
1919 if (level >= 0) /* there are unclosed conditionals */
1921 fail = PARSE_FAIL_UNCLOSED_COND;
1922 break;
1924 /* add a new token for the \n so empty lines are correct */
1925 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1926 data->tokens[data->num_tokens].value.c = '\n';
1927 data->tokens[data->num_tokens].next = false;
1928 data->num_tokens++;
1930 if (!skin_start_new_line(curr_vp, data->num_tokens))
1932 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1933 break;
1935 line_number++;
1937 break;
1939 /* String */
1940 default:
1942 unsigned int len = 1;
1943 const char *string_start = wps_bufptr - 1;
1945 /* find the length of the string */
1946 while (*wps_bufptr && *wps_bufptr != '#' &&
1947 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1948 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1949 *wps_bufptr != '|' && *wps_bufptr != '\n')
1951 wps_bufptr++;
1952 len++;
1955 /* look if we already have that string */
1956 char *str;
1957 bool found = false;
1958 struct skin_token_list *list = data->strings;
1959 while (list)
1961 str = (char*)list->token->value.data;
1962 found = (strlen(str) == len &&
1963 strncmp(string_start, str, len) == 0);
1964 if (found)
1965 break; /* break here because the list item is
1966 used if its found */
1967 list = list->next;
1969 /* If a matching string is found, found is true and i is
1970 the index of the string. If not, found is false */
1972 if (!found)
1974 /* new string */
1975 str = (char*)skin_buffer_alloc(len+1);
1976 if (!str)
1978 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1979 break;
1981 strlcpy(str, string_start, len+1);
1982 struct skin_token_list *item =
1983 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1984 if(!item)
1986 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1987 break;
1989 add_to_ll_chain(&data->strings, item);
1991 else
1993 /* another occurrence of an existing string */
1994 data->tokens[data->num_tokens].value.data = list->token->value.data;
1996 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1997 data->num_tokens++;
1999 break;
2003 if (!fail && level >= 0) /* there are unclosed conditionals */
2004 fail = PARSE_FAIL_UNCLOSED_COND;
2006 if (*wps_bufptr && !fail)
2007 /* one of the limits of the while loop was exceeded */
2008 fail = PARSE_FAIL_LIMITS_EXCEEDED;
2010 /* Success! */
2011 curr_line->curr_subline->last_token_idx = data->num_tokens;
2012 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
2013 /* freeup unused tokens */
2014 skin_buffer_free_from_front(sizeof(struct wps_token)
2015 * (max_tokens - data->num_tokens));
2017 #if defined(DEBUG) || defined(SIMULATOR)
2018 if (debug)
2020 print_debug_info(data, fail, line_number);
2021 debug_skin_usage();
2023 #else
2024 (void)debug;
2025 #endif
2027 return (fail == 0);
2032 * initial setup of wps_data; does reset everything
2033 * except fields which need to survive, i.e.
2036 static void skin_data_reset(struct wps_data *wps_data)
2038 #ifdef HAVE_LCD_BITMAP
2039 wps_data->images = NULL;
2040 wps_data->progressbars = NULL;
2041 #endif
2042 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2043 wps_data->backdrop = NULL;
2044 #endif
2045 #ifdef HAVE_TOUCHSCREEN
2046 wps_data->touchregions = NULL;
2047 #endif
2048 wps_data->viewports = NULL;
2049 wps_data->strings = NULL;
2050 #ifdef HAVE_ALBUMART
2051 wps_data->albumart = NULL;
2052 if (wps_data->playback_aa_slot >= 0)
2054 playback_release_aa_slot(wps_data->playback_aa_slot);
2055 wps_data->playback_aa_slot = -1;
2057 #endif
2058 wps_data->tokens = NULL;
2059 wps_data->num_tokens = 0;
2061 #ifdef HAVE_LCD_BITMAP
2062 wps_data->peak_meter_enabled = false;
2063 wps_data->wps_sb_tag = false;
2064 wps_data->show_sb_on_wps = false;
2065 #else /* HAVE_LCD_CHARCELLS */
2066 /* progress bars */
2067 int i;
2068 for (i = 0; i < 8; i++)
2070 wps_data->wps_progress_pat[i] = 0;
2072 wps_data->full_line_progressbar = false;
2073 #endif
2074 wps_data->wps_loaded = false;
2077 #ifdef HAVE_LCD_BITMAP
2078 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2080 (void)wps_data; /* only needed for remote targets */
2081 char img_path[MAX_PATH];
2082 get_image_filename(bitmap->data, bmpdir,
2083 img_path, sizeof(img_path));
2085 /* load the image */
2086 int format;
2087 #ifdef HAVE_REMOTE_LCD
2088 if (curr_screen == SCREEN_REMOTE)
2089 format = FORMAT_ANY|FORMAT_REMOTE;
2090 else
2091 #endif
2092 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2094 size_t max_buf;
2095 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2096 bitmap->data = imgbuf;
2097 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2099 if (ret > 0)
2101 skin_buffer_increment(ret, true);
2102 return true;
2104 else
2106 /* Abort if we can't load an image */
2107 DEBUGF("Couldn't load '%s'\n", img_path);
2108 return false;
2112 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2114 struct skin_token_list *list;
2115 bool retval = true; /* return false if a single image failed to load */
2116 /* do the progressbars */
2117 list = wps_data->progressbars;
2118 while (list)
2120 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2121 if (pb->bm.data)
2123 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2124 if (!pb->have_bitmap_pb) /* no success */
2125 retval = false;
2127 list = list->next;
2129 /* regular images */
2130 list = wps_data->images;
2131 while (list)
2133 struct gui_img *img = (struct gui_img*)list->token->value.data;
2134 if (img->bm.data)
2136 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2137 if (img->loaded)
2138 img->subimage_height = img->bm.height / img->num_subimages;
2139 else
2140 retval = false;
2142 list = list->next;
2145 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2146 /* Backdrop load scheme:
2147 * 1) %X|filename|
2148 * 2) load the backdrop from settings
2150 if (wps_data->backdrop)
2152 bool needed = wps_data->backdrop[0] != '-';
2153 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2154 bmpdir, curr_screen);
2155 if (!wps_data->backdrop && needed)
2156 retval = false;
2158 #endif /* has backdrop support */
2160 return retval;
2163 static bool skin_load_fonts(struct wps_data *data)
2165 /* don't spit out after the first failue to aid debugging */
2166 bool success = true;
2167 struct skin_token_list *vp_list;
2168 int font_id;
2169 /* walk though each viewport and assign its font */
2170 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2172 /* first, find the viewports that have a non-sys/ui-font font */
2173 struct skin_viewport *skin_vp =
2174 (struct skin_viewport*)vp_list->token->value.data;
2175 struct viewport *vp = &skin_vp->vp;
2178 if (vp->font <= FONT_UI)
2179 { /* the usual case -> built-in fonts */
2180 #ifdef HAVE_REMOTE_LCD
2181 if (vp->font == FONT_UI)
2182 vp->font += curr_screen;
2183 #endif
2184 continue;
2186 font_id = vp->font;
2188 /* now find the corresponding skin_font */
2189 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2190 if (!font->name)
2192 DEBUGF("font %d not specified\n", font_id);
2193 success = false;
2194 continue;
2197 /* load the font - will handle loading the same font again if
2198 * multiple viewports use the same */
2199 if (font->id < 0)
2201 char *dot = strchr(font->name, '.');
2202 *dot = '\0';
2203 font->id = skin_font_load(font->name);
2206 if (font->id < 0)
2208 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2209 font_id, font->name);
2210 success = false;
2211 continue;
2214 /* finally, assign the font_id to the viewport */
2215 vp->font = font->id;
2217 return success;
2220 #endif /* HAVE_LCD_BITMAP */
2222 /* to setup up the wps-data from a format-buffer (isfile = false)
2223 from a (wps-)file (isfile = true)*/
2224 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2225 const char *buf, bool isfile)
2227 char *wps_buffer = NULL;
2228 if (!wps_data || !buf)
2229 return false;
2230 #ifdef HAVE_ALBUMART
2231 int status;
2232 struct mp3entry *curtrack;
2233 long offset;
2234 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2235 if (wps_data->albumart)
2237 old_aa.state = wps_data->albumart->state;
2238 old_aa.height = wps_data->albumart->height;
2239 old_aa.width = wps_data->albumart->width;
2241 #endif
2242 #ifdef HAVE_LCD_BITMAP
2243 int i;
2244 for (i=0;i<MAXUSERFONTS;i++)
2246 skinfonts[i].id = -1;
2247 skinfonts[i].name = NULL;
2249 #endif
2250 #ifdef DEBUG_SKIN_ENGINE
2251 if (isfile && debug_wps)
2253 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2255 #endif
2257 skin_data_reset(wps_data);
2258 wps_data->wps_loaded = false;
2259 curr_screen = screen;
2261 /* alloc default viewport, will be fixed up later */
2262 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2263 if (!curr_vp)
2264 return false;
2265 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2266 if (!list)
2267 return false;
2268 add_to_ll_chain(&wps_data->viewports, list);
2271 /* Initialise the first (default) viewport */
2272 curr_vp->label = VP_DEFAULT_LABEL;
2273 curr_vp->hidden_flags = 0;
2274 curr_vp->lines = NULL;
2276 viewport_set_defaults(&curr_vp->vp, screen);
2277 #ifdef HAVE_LCD_BITMAP
2278 curr_vp->vp.font = FONT_UI;
2279 #endif
2281 curr_line = NULL;
2282 if (!skin_start_new_line(curr_vp, 0))
2283 return false;
2285 if (isfile)
2287 int fd = open_utf8(buf, O_RDONLY);
2289 if (fd < 0)
2290 return false;
2292 /* get buffer space from the plugin buffer */
2293 size_t buffersize = 0;
2294 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2296 if (!wps_buffer)
2297 return false;
2299 /* copy the file's content to the buffer for parsing,
2300 ensuring that every line ends with a newline char. */
2301 unsigned int start = 0;
2302 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2304 start += strlen(wps_buffer + start);
2305 if (start < buffersize - 1)
2307 wps_buffer[start++] = '\n';
2308 wps_buffer[start] = 0;
2311 close(fd);
2312 if (start <= 0)
2313 return false;
2315 else
2317 wps_buffer = (char*)buf;
2319 /* parse the WPS source */
2320 if (!wps_parse(wps_data, wps_buffer, isfile)) {
2321 skin_data_reset(wps_data);
2322 return false;
2325 #ifdef HAVE_LCD_BITMAP
2326 char bmpdir[MAX_PATH];
2327 if (isfile)
2329 /* get the bitmap dir */
2330 char *dot = strrchr(buf, '.');
2331 strlcpy(bmpdir, buf, dot - buf + 1);
2333 else
2335 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2337 /* load the bitmaps that were found by the parsing */
2338 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2339 !skin_load_fonts(wps_data))
2341 skin_data_reset(wps_data);
2342 return false;
2344 #endif
2345 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2346 status = audio_status();
2347 if (status & AUDIO_STATUS_PLAY)
2349 struct skin_albumart *aa = wps_data->albumart;
2350 if (aa && ((aa->state && !old_aa.state) ||
2351 (aa->state &&
2352 (((old_aa.height != aa->height) ||
2353 (old_aa.width != aa->width))))))
2355 curtrack = audio_current_track();
2356 offset = curtrack->offset;
2357 audio_stop();
2358 if (!(status & AUDIO_STATUS_PAUSE))
2359 audio_play(offset);
2362 #endif
2363 wps_data->wps_loaded = true;
2364 #ifdef DEBUG_SKIN_ENGINE
2365 if (isfile && debug_wps)
2366 debug_skin_usage();
2367 #endif
2368 return true;