FS#10898 - Add a playlist viewer to the WPS. http://www.rockbox.org/wiki/CustomWPS...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob7efd169092e40123d405f89f7d05fd59149e53a5
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"
56 #ifdef HAVE_LCD_BITMAP
57 #include "bmp.h"
58 #endif
60 #ifdef HAVE_ALBUMART
61 #include "playback.h"
62 #endif
64 #include "backdrop.h"
66 #define WPS_ERROR_INVALID_PARAM -1
68 /* which screen are we parsing for? */
69 static enum screen_type curr_screen;
71 /* level of current conditional.
72 -1 means we're not in a conditional. */
73 static int level = -1;
75 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
76 or WPS_TOKEN_CONDITIONAL_START in current level */
77 static int lastcond[WPS_MAX_COND_LEVEL];
79 /* index of the WPS_TOKEN_CONDITIONAL in current level */
80 static int condindex[WPS_MAX_COND_LEVEL];
82 /* number of condtional options in current level */
83 static int numoptions[WPS_MAX_COND_LEVEL];
85 /* line number, debug only */
86 static int line_number;
88 /* the current viewport */
89 static struct skin_viewport *curr_vp;
90 /* the current line, linked to the above viewport */
91 static struct skin_line *curr_line;
93 static int follow_lang_direction = 0;
95 #ifdef HAVE_LCD_BITMAP
97 #if LCD_DEPTH > 1
98 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
99 #else
100 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
101 #endif
103 #define PROGRESSBAR_BMP MAX_IMAGES
104 #define BACKDROP_BMP (MAX_BITMAPS-1)
106 /* pointers to the bitmap filenames in the WPS source */
107 static const char *bmp_names[MAX_BITMAPS];
109 #endif /* HAVE_LCD_BITMAP */
111 #if defined(DEBUG) || defined(SIMULATOR)
112 /* debugging function */
113 extern void print_debug_info(struct wps_data *data, int fail, int line);
114 extern void debug_skin_usage(void);
115 #endif
117 /* Function for parsing of details for a token. At the moment the
118 function is called, the token type has already been set. The
119 function must fill in the details and possibly add more tokens
120 to the token array. It should return the number of chars that
121 has been consumed.
123 wps_bufptr points to the char following the tag (i.e. where
124 details begin).
125 token is the pointer to the 'main' token being parsed
127 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
128 struct wps_token *token, struct wps_data *wps_data);
130 struct wps_tag {
131 enum wps_token_type type;
132 const char name[3];
133 unsigned char refresh_type;
134 const wps_tag_parse_func parse_func;
136 static int skip_end_of_line(const char *wps_bufptr);
137 /* prototypes of all special parse functions : */
138 static int parse_timeout(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_progressbar(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_dir_level(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_setting_and_lang(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
148 int parse_languagedirection(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data)
151 (void)wps_bufptr;
152 (void)token;
153 (void)wps_data;
154 follow_lang_direction = 2; /* 2 because it is decremented immediatly after
155 this token is parsed, after the next token it
156 will be 0 again. */
157 return 0;
160 #ifdef HAVE_LCD_BITMAP
161 static int parse_viewport_display(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 static int parse_playlistview(const char *wps_bufptr,
164 struct wps_token *token, struct wps_data *wps_data);
165 static int parse_viewport(const char *wps_bufptr,
166 struct wps_token *token, struct wps_data *wps_data);
167 static int parse_statusbar_enable(const char *wps_bufptr,
168 struct wps_token *token, struct wps_data *wps_data);
169 static int parse_statusbar_disable(const char *wps_bufptr,
170 struct wps_token *token, struct wps_data *wps_data);
171 static int parse_image_display(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 static int parse_image_load(const char *wps_bufptr,
174 struct wps_token *token, struct wps_data *wps_data);
175 #endif /*HAVE_LCD_BITMAP */
176 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
177 static int parse_image_special(const char *wps_bufptr,
178 struct wps_token *token, struct wps_data *wps_data);
179 #endif
180 #ifdef HAVE_ALBUMART
181 static int parse_albumart_load(const char *wps_bufptr,
182 struct wps_token *token, struct wps_data *wps_data);
183 static int parse_albumart_display(const char *wps_bufptr,
184 struct wps_token *token, struct wps_data *wps_data);
185 #endif /* HAVE_ALBUMART */
186 #ifdef HAVE_TOUCHSCREEN
187 static int parse_touchregion(const char *wps_bufptr,
188 struct wps_token *token, struct wps_data *wps_data);
189 #else
190 static int fulline_tag_not_supported(const char *wps_bufptr,
191 struct wps_token *token, struct wps_data *wps_data)
193 (void)token; (void)wps_data;
194 return skip_end_of_line(wps_bufptr);
196 #define parse_touchregion fulline_tag_not_supported
197 #endif
198 #ifdef CONFIG_RTC
199 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
200 #else
201 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
202 #endif
204 /* array of available tags - those with more characters have to go first
205 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
206 static const struct wps_tag all_tags[] = {
208 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
209 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
210 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
211 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
212 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
213 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
215 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
216 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
217 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
218 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
219 #if CONFIG_CHARGING >= CHARGING_MONITOR
220 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
221 #endif
222 #if CONFIG_CHARGING
223 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
224 #endif
225 #ifdef HAVE_USB_POWER
226 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
227 #endif
229 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
236 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
237 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
238 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
239 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
240 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
241 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
242 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
243 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
244 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
245 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
246 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
247 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
249 /* current file */
250 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
260 parse_dir_level },
262 /* next file */
263 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
273 parse_dir_level },
275 /* current metadata */
276 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
277 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
278 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
283 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
284 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
289 /* next metadata */
290 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
292 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
293 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
294 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
295 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
296 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
297 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
298 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
299 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
300 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
301 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
303 #if (CONFIG_CODEC != MAS3507D)
304 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
305 #endif
306 #if (CONFIG_CODEC == SWCODEC)
307 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
308 #endif
309 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
310 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
311 #endif
313 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
315 #ifdef HAS_REMOTE_BUTTON_HOLD
316 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
317 #else
318 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
319 #endif
321 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
322 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
323 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
324 parse_timeout },
326 #ifdef HAVE_LCD_BITMAP
327 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
328 #else
329 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
330 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
331 #endif
332 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
333 parse_progressbar },
335 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
337 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
338 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
339 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
340 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
342 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
343 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
344 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
345 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
347 #ifdef HAVE_TAGCACHE
348 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
349 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
350 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
351 #endif
353 #if CONFIG_CODEC == SWCODEC
354 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
355 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
356 #endif
358 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
359 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
361 #ifdef HAVE_LCD_BITMAP
362 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
363 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
365 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
367 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
368 parse_image_display },
370 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
371 #ifdef HAVE_ALBUMART
372 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
373 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
374 #endif
376 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
377 parse_viewport_display },
378 #ifdef HAVE_LCD_BITMAP
379 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
380 #endif
381 { WPS_NO_TOKEN, "V", 0, parse_viewport },
383 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
384 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
385 #endif
386 #endif
388 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
389 parse_setting_and_lang },
390 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
391 parse_setting_and_lang },
392 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
394 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
395 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
396 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
399 /* Recording Tokens */
400 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
401 #ifdef HAVE_RECORDING
402 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
403 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
404 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
405 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
406 #endif
407 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
408 /* the array MUST end with an empty string (first char is \0) */
412 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
413 * chains require the order to be kept.
415 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
417 if (*list == NULL)
418 *list = item;
419 else
421 struct skin_token_list *t = *list;
422 while (t->next)
423 t = t->next;
424 t->next = item;
428 /* traverse the image linked-list for an image */
429 #ifdef HAVE_LCD_BITMAP
430 struct gui_img* find_image(char label, struct wps_data *data)
432 struct skin_token_list *list = data->images;
433 while (list)
435 struct gui_img *img = (struct gui_img *)list->token->value.data;
436 if (img->label == label)
437 return img;
438 list = list->next;
440 return NULL;
442 #endif
444 /* traverse the viewport linked list for a viewport */
445 struct skin_viewport* find_viewport(char label, struct wps_data *data)
447 struct skin_token_list *list = data->viewports;
448 while (list)
450 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
451 if (vp->label == label)
452 return vp;
453 list = list->next;
455 return NULL;
459 /* create and init a new wpsll item.
460 * passing NULL to token will alloc a new one.
461 * You should only pass NULL for the token when the token type (table above)
462 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
464 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
465 void* token_data)
467 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
468 if (!token)
469 token = skin_buffer_alloc(sizeof(struct wps_token));
470 if (!llitem || !token)
471 return NULL;
472 llitem->next = NULL;
473 llitem->token = token;
474 if (token_data)
475 llitem->token->value.data = token_data;
476 return llitem;
479 /* Returns the number of chars that should be skipped to jump
480 immediately after the first eol, i.e. to the start of the next line */
481 static int skip_end_of_line(const char *wps_bufptr)
483 line_number++;
484 int skip = 0;
485 while(*(wps_bufptr + skip) != '\n')
486 skip++;
487 return ++skip;
490 /* Starts a new subline in the current line during parsing */
491 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
493 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
494 if (!subline)
495 return false;
497 subline->first_token_idx = curr_token;
498 subline->next = NULL;
500 subline->line_type = 0;
501 subline->time_mult = 0;
503 line->curr_subline->last_token_idx = curr_token-1;
504 line->curr_subline->next = subline;
505 line->curr_subline = subline;
506 return true;
509 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
511 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
512 struct skin_subline *subline = NULL;
513 if (!line)
514 return false;
516 /* init the subline */
517 subline = &line->sublines;
518 subline->first_token_idx = curr_token;
519 subline->next = NULL;
520 subline->line_type = 0;
521 subline->time_mult = 0;
523 /* init the new line */
524 line->curr_subline = &line->sublines;
525 line->next = NULL;
526 line->subline_expire_time = 0;
528 /* connect to curr_line and vp pointers.
529 * 1) close the previous lines subline
530 * 2) connect to vp pointer
531 * 3) connect to curr_line global pointer
533 if (curr_line)
535 curr_line->curr_subline->last_token_idx = curr_token - 1;
536 curr_line->next = line;
537 curr_line->curr_subline = NULL;
539 curr_line = line;
540 if (!vp->lines)
541 vp->lines = line;
542 return true;
545 #ifdef HAVE_LCD_BITMAP
547 static int parse_statusbar_enable(const char *wps_bufptr,
548 struct wps_token *token,
549 struct wps_data *wps_data)
551 (void)token; /* Kill warnings */
552 wps_data->wps_sb_tag = true;
553 wps_data->show_sb_on_wps = true;
554 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
555 viewport_set_defaults(&default_vp->vp, curr_screen);
556 return skip_end_of_line(wps_bufptr);
559 static int parse_statusbar_disable(const char *wps_bufptr,
560 struct wps_token *token,
561 struct wps_data *wps_data)
563 (void)token; /* Kill warnings */
564 wps_data->wps_sb_tag = true;
565 wps_data->show_sb_on_wps = false;
566 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
567 viewport_set_fullscreen(&default_vp->vp, curr_screen);
568 return skip_end_of_line(wps_bufptr);
571 static int get_image_id(int c)
573 if(c >= 'a' && c <= 'z')
574 return c - 'a';
575 else if(c >= 'A' && c <= 'Z')
576 return c - 'A' + 26;
577 else
578 return -1;
581 static char *get_image_filename(const char *start, const char* bmpdir,
582 char *buf, int buf_size)
584 const char *end = strchr(start, '|');
586 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
588 buf = "\0";
589 return NULL;
592 int bmpdirlen = strlen(bmpdir);
594 strcpy(buf, bmpdir);
595 buf[bmpdirlen] = '/';
596 memcpy( &buf[bmpdirlen + 1], start, end - start);
597 buf[bmpdirlen + 1 + end - start] = 0;
599 return buf;
602 static int parse_image_display(const char *wps_bufptr,
603 struct wps_token *token,
604 struct wps_data *wps_data)
606 char label = wps_bufptr[0];
607 int subimage;
608 struct gui_img *img;;
610 /* sanity check */
611 img = find_image(label, wps_data);
612 if (!img)
614 token->value.i = label; /* so debug works */
615 return WPS_ERROR_INVALID_PARAM;
618 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
620 if (subimage >= img->num_subimages)
621 return WPS_ERROR_INVALID_PARAM;
623 /* Store sub-image number to display in high bits */
624 token->value.i = label | (subimage << 8);
625 return 2; /* We have consumed 2 bytes */
626 } else {
627 token->value.i = label;
628 return 1; /* We have consumed 1 byte */
632 static int parse_image_load(const char *wps_bufptr,
633 struct wps_token *token,
634 struct wps_data *wps_data)
636 const char *ptr = wps_bufptr;
637 const char *pos;
638 const char* filename;
639 const char* id;
640 const char *newline;
641 int x,y;
642 struct gui_img *img;
644 /* format: %x|n|filename.bmp|x|y|
645 or %xl|n|filename.bmp|x|y|
646 or %xl|n|filename.bmp|x|y|num_subimages|
649 if (*ptr != '|')
650 return WPS_ERROR_INVALID_PARAM;
652 ptr++;
654 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
655 return WPS_ERROR_INVALID_PARAM;
657 /* Check there is a terminating | */
658 if (*ptr != '|')
659 return WPS_ERROR_INVALID_PARAM;
661 /* check the image number and load state */
662 if(find_image(*id, wps_data))
664 /* Invalid image ID */
665 return WPS_ERROR_INVALID_PARAM;
667 img = skin_buffer_alloc(sizeof(struct gui_img));
668 if (!img)
669 return WPS_ERROR_INVALID_PARAM;
670 /* save a pointer to the filename */
671 img->bm.data = (char*)filename;
672 img->label = *id;
673 img->x = x;
674 img->y = y;
675 img->num_subimages = 1;
676 img->always_display = false;
678 /* save current viewport */
679 img->vp = &curr_vp->vp;
681 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
683 img->always_display = true;
685 else
687 /* Parse the (optional) number of sub-images */
688 ptr++;
689 newline = strchr(ptr, '\n');
690 pos = strchr(ptr, '|');
691 if (pos && pos < newline)
692 img->num_subimages = atoi(ptr);
694 if (img->num_subimages <= 0)
695 return WPS_ERROR_INVALID_PARAM;
697 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
698 if (!item)
699 return WPS_ERROR_INVALID_PARAM;
700 add_to_ll_chain(&wps_data->images, item);
702 /* Skip the rest of the line */
703 return skip_end_of_line(wps_bufptr);
706 static int parse_viewport_display(const char *wps_bufptr,
707 struct wps_token *token,
708 struct wps_data *wps_data)
710 (void)wps_data;
711 char letter = wps_bufptr[0];
713 if (letter < 'a' || letter > 'z')
715 /* invalid viewport tag */
716 return WPS_ERROR_INVALID_PARAM;
718 token->value.i = letter;
719 return 1;
722 #ifdef HAVE_LCD_BITMAP
723 int parse_playlistview_text(struct playlistviewer *viewer,
724 enum info_line_type line, char* text)
726 int cur_string = 0;
727 const struct wps_tag *tag;
728 int taglen = 0;
729 const char *start = text;
730 if (*text != '|')
731 return -1;
732 text++;
733 viewer->lines[line].count = 0;
734 viewer->lines[line].scroll = false;
735 while (*text != '|')
737 if (*text == '%') /* it is a token of some type */
739 text++;
740 taglen = 0;
741 switch(*text)
743 case '%':
744 case '<':
745 case '|':
746 case '>':
747 case ';':
748 case '#':
749 /* escaped characters */
750 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
751 viewer->lines[line].strings[cur_string][0] = *text;
752 viewer->lines[line].strings[cur_string++][0] = '\0';
753 break;
754 default:
755 for (tag = all_tags;
756 strncmp(text, tag->name, strlen(tag->name)) != 0;
757 tag++) ;
758 /* %s isnt stored as a tag so manually check for it */
759 if (tag->type == WPS_NO_TOKEN)
761 if (!strncmp(tag->name, "s", 1))
763 viewer->lines[line].scroll = true;
764 taglen = 1;
767 else if (tag->type == WPS_TOKEN_UNKNOWN)
769 int i = 0;
770 /* just copy the string */
771 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
772 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
774 viewer->lines[line].strings[cur_string][i] = text[i];
775 i++;
777 viewer->lines[line].strings[cur_string][i] = '\0';
778 cur_string++;
779 taglen = i;
781 else
783 taglen = strlen(tag->name);
784 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
786 text += taglen;
789 else
791 /* regular string */
792 int i = 0;
793 /* just copy the string */
794 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
795 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
797 viewer->lines[line].strings[cur_string][i] = text[i];
798 i++;
800 viewer->lines[line].strings[cur_string][i] = '\0';
801 cur_string++;
802 text += i;
805 return text - start;
809 static int parse_playlistview(const char *wps_bufptr,
810 struct wps_token *token, struct wps_data *wps_data)
812 (void)wps_data;
813 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
814 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
815 char *ptr = strchr(wps_bufptr, '|');
816 int length;
817 if (!viewer || !ptr)
818 return WPS_ERROR_INVALID_PARAM;
819 viewer->vp = &curr_vp->vp;
820 viewer->show_icons = true;
821 viewer->start_offset = atoi(ptr+1);
822 token->value.data = (void*)viewer;
823 ptr = strchr(ptr+1, '|');
824 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
825 if (length < 0)
826 return WPS_ERROR_INVALID_PARAM;
827 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
828 if (length < 0)
829 return WPS_ERROR_INVALID_PARAM;
831 return skip_end_of_line(wps_bufptr);
833 #endif
835 static int parse_viewport(const char *wps_bufptr,
836 struct wps_token *token,
837 struct wps_data *wps_data)
839 (void)token; /* Kill warnings */
840 const char *ptr = wps_bufptr;
842 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
844 /* check for the optional letter to signify its a hideable viewport */
845 /* %Vl|<label>|<rest of tags>| */
846 skin_vp->hidden_flags = 0;
847 skin_vp->label = VP_NO_LABEL;
848 skin_vp->pb = NULL;
849 skin_vp->lines = NULL;
850 if (curr_line)
852 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
853 - (wps_data->num_tokens > 0 ? 1 : 0);
856 curr_line = NULL;
857 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
858 return WPS_ERROR_INVALID_PARAM;
861 if (*ptr == 'i')
863 skin_vp->label = VP_INFO_LABEL;
864 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
865 ++ptr;
867 else if (*ptr == 'l')
869 if (*(ptr+1) == '|')
871 char label = *(ptr+2);
872 if (label >= 'a' && label <= 'z')
874 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
875 skin_vp->label = label;
877 else
878 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
879 ptr += 3;
882 if (*ptr != '|')
883 return WPS_ERROR_INVALID_PARAM;
885 ptr++;
886 struct viewport *vp = &skin_vp->vp;
887 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
888 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
889 return WPS_ERROR_INVALID_PARAM;
891 /* Check for trailing | */
892 if (*ptr != '|')
893 return WPS_ERROR_INVALID_PARAM;
895 if (follow_lang_direction && lang_is_rtl())
897 vp->flags |= VP_FLAG_ALIGN_RIGHT;
898 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
900 else
901 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
905 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
906 if (!list)
907 return WPS_ERROR_INVALID_PARAM;
908 add_to_ll_chain(&wps_data->viewports, list);
909 curr_vp = skin_vp;
910 /* Skip the rest of the line */
911 return skip_end_of_line(wps_bufptr);
914 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
915 static int parse_image_special(const char *wps_bufptr,
916 struct wps_token *token,
917 struct wps_data *wps_data)
919 (void)wps_data; /* kill warning */
920 (void)token;
921 const char *pos = NULL;
922 const char *newline;
924 pos = strchr(wps_bufptr + 1, '|');
925 newline = strchr(wps_bufptr, '\n');
927 if (pos > newline)
928 return WPS_ERROR_INVALID_PARAM;
929 #if LCD_DEPTH > 1
930 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
932 /* format: %X|filename.bmp| */
933 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
935 #endif
937 /* Skip the rest of the line */
938 return skip_end_of_line(wps_bufptr);
940 #endif
942 #endif /* HAVE_LCD_BITMAP */
944 static int parse_setting_and_lang(const char *wps_bufptr,
945 struct wps_token *token,
946 struct wps_data *wps_data)
948 /* NOTE: both the string validations that happen in here will
949 * automatically PASS on checkwps because its too hard to get
950 * settings_list.c and englinsh.lang built for it.
951 * If that ever changes remove the #ifndef __PCTOOL__'s here
953 (void)wps_data;
954 const char *ptr = wps_bufptr;
955 const char *end;
956 int i = 0;
957 char temp[64];
959 /* Find the setting's cfg_name */
960 if (*ptr != '|')
961 return WPS_ERROR_INVALID_PARAM;
962 ptr++;
963 end = strchr(ptr,'|');
964 if (!end)
965 return WPS_ERROR_INVALID_PARAM;
966 strlcpy(temp, ptr,end-ptr+1);
968 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
970 #ifndef __PCTOOL__
971 i = lang_english_to_id(temp);
972 if (i < 0)
973 return WPS_ERROR_INVALID_PARAM;
974 #endif
976 else
978 /* Find the setting */
979 for (i=0; i<nb_settings; i++)
980 if (settings[i].cfg_name &&
981 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
982 /* prevent matches on cfg_name prefixes */
983 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
984 break;
985 #ifndef __PCTOOL__
986 if (i == nb_settings)
987 return WPS_ERROR_INVALID_PARAM;
988 #endif
990 /* Store the setting number */
991 token->value.i = i;
993 /* Skip the rest of the line */
994 return end-ptr+2;
998 static int parse_dir_level(const char *wps_bufptr,
999 struct wps_token *token,
1000 struct wps_data *wps_data)
1002 char val[] = { *wps_bufptr, '\0' };
1003 token->value.i = atoi(val);
1004 (void)wps_data; /* Kill warnings */
1005 return 1;
1008 static int parse_timeout(const char *wps_bufptr,
1009 struct wps_token *token,
1010 struct wps_data *wps_data)
1012 int skip = 0;
1013 int val = 0;
1014 bool have_point = false;
1015 bool have_tenth = false;
1017 (void)wps_data; /* Kill the warning */
1019 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1021 if (*wps_bufptr != '.')
1023 val *= 10;
1024 val += *wps_bufptr - '0';
1025 if (have_point)
1027 have_tenth = true;
1028 wps_bufptr++;
1029 skip++;
1030 break;
1033 else
1034 have_point = true;
1036 wps_bufptr++;
1037 skip++;
1040 if (have_tenth == false)
1041 val *= 10;
1043 if (val == 0 && skip == 0)
1045 /* decide what to do if no value was specified */
1046 switch (token->type)
1048 case WPS_TOKEN_SUBLINE_TIMEOUT:
1049 return -1;
1050 case WPS_TOKEN_BUTTON_VOLUME:
1051 val = 10;
1052 break;
1055 token->value.i = val;
1057 return skip;
1060 static int parse_progressbar(const char *wps_bufptr,
1061 struct wps_token *token,
1062 struct wps_data *wps_data)
1064 /* %pb or %pb|filename|x|y|width|height|
1065 using - for any of the params uses "sane" values */
1066 #ifdef HAVE_LCD_BITMAP
1067 enum {
1068 PB_FILENAME = 0,
1069 PB_X,
1070 PB_Y,
1071 PB_WIDTH,
1072 PB_HEIGHT
1074 const char *filename;
1075 int x, y, height, width;
1076 uint32_t set = 0;
1077 const char *ptr = wps_bufptr;
1078 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1079 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1081 if (!pb || !item)
1082 return WPS_ERROR_INVALID_PARAM;
1084 struct viewport *vp = &curr_vp->vp;
1085 #ifndef __PCTOOL__
1086 int font_height = font_get(vp->font)->height;
1087 #else
1088 int font_height = 8;
1089 #endif
1090 /* we need to know what line number (viewport relative) this pb is,
1091 * so count them... */
1092 int line_num = -1;
1093 struct skin_line *line = curr_vp->lines;
1094 while (line)
1096 line_num++;
1097 line = line->next;
1099 pb->have_bitmap_pb = false;
1100 pb->bm.data = NULL; /* no bitmap specified */
1101 pb->follow_lang_direction = follow_lang_direction > 0;
1103 if (*wps_bufptr != '|') /* regular old style */
1105 pb->x = 0;
1106 pb->width = vp->width;
1107 pb->height = SYSFONT_HEIGHT-2;
1108 pb->y = -line_num - 1; /* Will be computed during the rendering */
1110 curr_vp->pb = pb;
1111 add_to_ll_chain(&wps_data->progressbars, item);
1112 return 0;
1114 ptr = wps_bufptr + 1;
1116 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1117 &x, &y, &width, &height)))
1118 return WPS_ERROR_INVALID_PARAM;
1120 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1121 pb->bm.data = (char*)filename;
1123 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1124 pb->x = x;
1125 else
1126 pb->x = vp->x;
1128 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1130 /* A zero width causes a divide-by-zero error later, so reject it */
1131 if (width == 0)
1132 return WPS_ERROR_INVALID_PARAM;
1134 pb->width = width;
1136 else
1137 pb->width = vp->width - pb->x;
1139 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1141 /* A zero height makes no sense - reject it */
1142 if (height == 0)
1143 return WPS_ERROR_INVALID_PARAM;
1145 pb->height = height;
1147 else
1148 pb->height = font_height;
1150 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1151 pb->y = y;
1152 else
1153 pb->y = -line_num - 1; /* Will be computed during the rendering */
1155 curr_vp->pb = pb;
1156 add_to_ll_chain(&wps_data->progressbars, item);
1158 /* Skip the rest of the line */
1159 return skip_end_of_line(wps_bufptr)-1;
1160 #else
1161 (void)token;
1163 if (*(wps_bufptr-1) == 'f')
1164 wps_data->full_line_progressbar = true;
1165 else
1166 wps_data->full_line_progressbar = false;
1168 return 0;
1170 #endif
1173 #ifdef HAVE_ALBUMART
1174 static int parse_int(const char *newline, const char **_pos, int *num)
1176 *_pos = parse_list("d", NULL, '|', *_pos, num);
1178 return (!*_pos || *_pos > newline || **_pos != '|');
1181 static int parse_albumart_load(const char *wps_bufptr,
1182 struct wps_token *token,
1183 struct wps_data *wps_data)
1185 const char *_pos, *newline;
1186 bool parsing;
1187 struct dim dimensions;
1188 int albumart_slot;
1189 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1190 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1191 (void)token; /* silence warning */
1192 if (!aa)
1193 return skip_end_of_line(wps_bufptr);
1195 /* reset albumart info in wps */
1196 aa->width = -1;
1197 aa->height = -1;
1198 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1199 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1200 aa->vp = &curr_vp->vp;
1202 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1204 newline = strchr(wps_bufptr, '\n');
1206 _pos = wps_bufptr;
1208 if (*_pos != '|')
1209 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1211 ++_pos;
1213 /* initial validation and parsing of x component */
1214 if (parse_int(newline, &_pos, &aa->x))
1215 return WPS_ERROR_INVALID_PARAM;
1217 ++_pos;
1219 /* initial validation and parsing of y component */
1220 if (parse_int(newline, &_pos, &aa->y))
1221 return WPS_ERROR_INVALID_PARAM;
1223 /* parsing width field */
1224 parsing = true;
1225 while (parsing)
1227 /* apply each modifier in turn */
1228 ++_pos;
1229 switch (*_pos)
1231 case 'l':
1232 case 'L':
1233 case '+':
1234 if (swap_for_rtl)
1235 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1236 else
1237 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1238 break;
1239 case 'c':
1240 case 'C':
1241 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1242 break;
1243 case 'r':
1244 case 'R':
1245 case '-':
1246 if (swap_for_rtl)
1247 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1248 else
1249 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1250 break;
1251 case 'd':
1252 case 'D':
1253 case 'i':
1254 case 'I':
1255 case 's':
1256 case 'S':
1257 /* simply ignored */
1258 break;
1259 default:
1260 parsing = false;
1261 break;
1264 /* extract max width data */
1265 if (*_pos != '|')
1267 if (parse_int(newline, &_pos, &aa->width))
1268 return WPS_ERROR_INVALID_PARAM;
1271 /* parsing height field */
1272 parsing = true;
1273 while (parsing)
1275 /* apply each modifier in turn */
1276 ++_pos;
1277 switch (*_pos)
1279 case 't':
1280 case 'T':
1281 case '-':
1282 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1283 break;
1284 case 'c':
1285 case 'C':
1286 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1287 break;
1288 case 'b':
1289 case 'B':
1290 case '+':
1291 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1292 break;
1293 case 'd':
1294 case 'D':
1295 case 'i':
1296 case 'I':
1297 case 's':
1298 case 'S':
1299 /* simply ignored */
1300 break;
1301 default:
1302 parsing = false;
1303 break;
1306 /* extract max height data */
1307 if (*_pos != '|')
1309 if (parse_int(newline, &_pos, &aa->height))
1310 return WPS_ERROR_INVALID_PARAM;
1313 /* if we got here, we parsed everything ok .. ! */
1314 if (aa->width < 0)
1315 aa->width = 0;
1316 else if (aa->width > LCD_WIDTH)
1317 aa->width = LCD_WIDTH;
1319 if (aa->height < 0)
1320 aa->height = 0;
1321 else if (aa->height > LCD_HEIGHT)
1322 aa->height = LCD_HEIGHT;
1324 if (swap_for_rtl)
1325 aa->x = LCD_WIDTH - (aa->x + aa->width);
1327 aa->state = WPS_ALBUMART_LOAD;
1328 aa->draw = false;
1329 wps_data->albumart = aa;
1331 dimensions.width = aa->width;
1332 dimensions.height = aa->height;
1334 albumart_slot = playback_claim_aa_slot(&dimensions);
1336 if (0 <= albumart_slot)
1337 wps_data->playback_aa_slot = albumart_slot;
1339 /* Skip the rest of the line */
1340 return skip_end_of_line(wps_bufptr);
1343 static int parse_albumart_display(const char *wps_bufptr,
1344 struct wps_token *token,
1345 struct wps_data *wps_data)
1347 (void)wps_bufptr;
1348 struct wps_token *prev = token-1;
1349 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1351 token->type = WPS_TOKEN_ALBUMART_FOUND;
1353 else if (wps_data->albumart)
1355 wps_data->albumart->vp = &curr_vp->vp;
1357 #if 0
1358 /* the old code did this so keep it here for now...
1359 * this is to allow the posibility to showing the next tracks AA! */
1360 if (wps_bufptr+1 == 'n')
1361 return 1;
1362 #endif
1363 return 0;
1365 #endif /* HAVE_ALBUMART */
1367 #ifdef HAVE_TOUCHSCREEN
1369 struct touchaction {const char* s; int action;};
1370 static const struct touchaction touchactions[] = {
1371 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1372 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1373 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1374 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1375 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1376 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1377 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1378 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1380 static int parse_touchregion(const char *wps_bufptr,
1381 struct wps_token *token, struct wps_data *wps_data)
1383 (void)token;
1384 unsigned i, imax;
1385 struct touchregion *region = NULL;
1386 const char *ptr = wps_bufptr;
1387 const char *action;
1388 const char pb_string[] = "progressbar";
1389 const char vol_string[] = "volume";
1390 int x,y,w,h;
1392 /* format: %T|x|y|width|height|action|
1393 * if action starts with & the area must be held to happen
1394 * action is one of:
1395 * play - play/pause playback
1396 * stop - stop playback, exit the wps
1397 * prev - prev track
1398 * next - next track
1399 * ffwd - seek forward
1400 * rwd - seek backwards
1401 * menu - go back to the main menu
1402 * browse - go back to the file/db browser
1403 * shuffle - toggle shuffle mode
1404 * repmode - cycle the repeat mode
1405 * quickscreen - go into the quickscreen
1406 * contextmenu - open the context menu
1407 * playlist - go into the playlist
1408 * pitch - go into the pitchscreen
1409 * volup - increase volume by one step
1410 * voldown - decrease volume by one step
1414 if (*ptr != '|')
1415 return WPS_ERROR_INVALID_PARAM;
1416 ptr++;
1418 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1419 return WPS_ERROR_INVALID_PARAM;
1421 /* Check there is a terminating | */
1422 if (*ptr != '|')
1423 return WPS_ERROR_INVALID_PARAM;
1425 region = skin_buffer_alloc(sizeof(struct touchregion));
1426 if (!region)
1427 return WPS_ERROR_INVALID_PARAM;
1429 /* should probably do some bounds checking here with the viewport... but later */
1430 region->action = ACTION_NONE;
1431 region->x = x;
1432 region->y = y;
1433 region->width = w;
1434 region->height = h;
1435 region->wvp = curr_vp;
1437 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1438 && *(action + sizeof(pb_string)-1) == '|')
1439 region->type = WPS_TOUCHREGION_SCROLLBAR;
1440 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1441 && *(action + sizeof(vol_string)-1) == '|')
1442 region->type = WPS_TOUCHREGION_VOLUME;
1443 else
1445 region->type = WPS_TOUCHREGION_ACTION;
1447 if (*action == '&')
1449 action++;
1450 region->repeat = true;
1452 else
1453 region->repeat = false;
1455 i = 0;
1456 imax = ARRAYLEN(touchactions);
1457 while ((region->action == ACTION_NONE) &&
1458 (i < imax))
1460 /* try to match with one of our touchregion screens */
1461 int len = strlen(touchactions[i].s);
1462 if (!strncmp(touchactions[i].s, action, len)
1463 && *(action+len) == '|')
1464 region->action = touchactions[i].action;
1465 i++;
1467 if (region->action == ACTION_NONE)
1468 return WPS_ERROR_INVALID_PARAM;
1470 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1471 if (!item)
1472 return WPS_ERROR_INVALID_PARAM;
1473 add_to_ll_chain(&wps_data->touchregions, item);
1474 return skip_end_of_line(wps_bufptr);
1476 #endif
1478 /* Parse a generic token from the given string. Return the length read */
1479 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1481 int skip = 0, taglen = 0, ret;
1482 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1483 const struct wps_tag *tag;
1484 memset(token, 0, sizeof(*token));
1486 switch(*wps_bufptr)
1489 case '%':
1490 case '<':
1491 case '|':
1492 case '>':
1493 case ';':
1494 case '#':
1495 /* escaped characters */
1496 token->type = WPS_TOKEN_CHARACTER;
1497 token->value.c = *wps_bufptr;
1498 taglen = 1;
1499 wps_data->num_tokens++;
1500 break;
1502 case '?':
1503 /* conditional tag */
1504 token->type = WPS_TOKEN_CONDITIONAL;
1505 level++;
1506 condindex[level] = wps_data->num_tokens;
1507 numoptions[level] = 1;
1508 wps_data->num_tokens++;
1509 ret = parse_token(wps_bufptr + 1, wps_data);
1510 if (ret < 0) return ret;
1511 taglen = 1 + ret;
1512 break;
1514 default:
1515 /* find what tag we have */
1516 for (tag = all_tags;
1517 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1518 tag++) ;
1520 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1521 token->type = tag->type;
1522 curr_line->curr_subline->line_type |= tag->refresh_type;
1524 /* if the tag has a special parsing function, we call it */
1525 if (tag->parse_func)
1527 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1528 if (ret < 0) return ret;
1529 skip += ret;
1532 /* Some tags we don't want to save as tokens */
1533 if (tag->type == WPS_NO_TOKEN)
1534 break;
1536 /* tags that start with 'F', 'I' or 'D' are for the next file */
1537 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1538 *(tag->name) == 'D')
1539 token->next = true;
1541 wps_data->num_tokens++;
1542 break;
1545 skip += taglen;
1546 return skip;
1551 * Returns the number of bytes to skip the buf pointer to access the false
1552 * branch in a _binary_ conditional
1554 * That is:
1555 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1556 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1558 * depending on the features of a target it's not called from check_feature_tag,
1559 * hence the __attribute__ or it issues compiler warnings
1563 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1564 static int find_false_branch(const char *wps_bufptr)
1566 const char *buf = wps_bufptr;
1567 /* wps_bufptr is after the opening '<', hence level = 1*/
1568 int level = 1;
1569 char ch;
1572 ch = *buf;
1573 if (ch == '%')
1574 { /* filter out the characters we check later if they're printed
1575 * as literals */
1576 ch = *(++buf);
1577 if (ch == '<' || ch == '>' || ch == '|')
1578 continue;
1579 /* else: some tags/printed literals we skip over */
1581 else if (ch == '<') /* nested conditional */
1582 level++;
1583 else if (ch == '>')
1584 { /* closed our or a nested conditional,
1585 * do NOT skip over the '>' so that wps_parse() sees it for closing
1586 * if it is the closing one for our conditional */
1587 level--;
1589 else if (ch == '|' && level == 1)
1590 { /* we found our separator, point before and get out */
1591 break;
1593 /* if level is 0, we don't have a false branch */
1594 } while (level > 0 && *(++buf));
1596 return buf - wps_bufptr;
1600 * returns the number of bytes to get the appropriate branch of a binary
1601 * conditional
1603 * That means:
1604 * - if a feature is available, it returns 0 to not skip anything
1605 * - if the feature is not available, skip to the false branch and don't
1606 * parse the true branch at all
1608 * */
1609 static int check_feature_tag(const char *wps_bufptr, const int type)
1611 (void)wps_bufptr;
1612 switch (type)
1614 case WPS_TOKEN_RTC_PRESENT:
1615 #if CONFIG_RTC
1616 return 0;
1617 #else
1618 return find_false_branch(wps_bufptr);
1619 #endif
1620 case WPS_TOKEN_HAVE_RECORDING:
1621 #ifdef HAVE_RECORDING
1622 return 0;
1623 #else
1624 return find_false_branch(wps_bufptr);
1625 #endif
1626 default: /* not a tag we care about, just don't skip */
1627 return 0;
1632 /* Parses the WPS.
1633 data is the pointer to the structure where the parsed WPS should be stored.
1634 It is initialised.
1635 wps_bufptr points to the string containing the WPS tags */
1636 #define TOKEN_BLOCK_SIZE 128
1637 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1639 if (!data || !wps_bufptr || !*wps_bufptr)
1640 return false;
1641 enum wps_parse_error fail = PARSE_OK;
1642 int ret;
1643 int max_tokens = TOKEN_BLOCK_SIZE;
1644 size_t buf_free = 0;
1645 line_number = 0;
1646 level = -1;
1648 /* allocate enough RAM for a reasonable skin, grow as needed.
1649 * Free any used RAM before loading the images to be 100% RAM efficient */
1650 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1651 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1652 return false;
1653 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1654 data->num_tokens = 0;
1656 while (*wps_bufptr && !fail)
1658 if (follow_lang_direction)
1659 follow_lang_direction--;
1660 /* first make sure there is enough room for tokens */
1661 if (max_tokens <= data->num_tokens + 5)
1663 int extra_tokens = TOKEN_BLOCK_SIZE;
1664 size_t needed = extra_tokens * sizeof(struct wps_token);
1665 /* do some smarts here to grow the array a bit */
1666 if (skin_buffer_freespace() < needed)
1668 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1669 break;
1671 skin_buffer_increment(needed, false);
1672 max_tokens += extra_tokens;
1675 switch(*wps_bufptr++)
1678 /* Regular tag */
1679 case '%':
1680 if ((ret = parse_token(wps_bufptr, data)) < 0)
1682 fail = PARSE_FAIL_COND_INVALID_PARAM;
1683 break;
1685 else if (level >= WPS_MAX_COND_LEVEL - 1)
1687 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1688 break;
1690 wps_bufptr += ret;
1691 break;
1693 /* Alternating sublines separator */
1694 case ';':
1695 if (level >= 0) /* there are unclosed conditionals */
1697 fail = PARSE_FAIL_UNCLOSED_COND;
1698 break;
1701 if (!skin_start_new_subline(curr_line, data->num_tokens))
1702 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1704 break;
1706 /* Conditional list start */
1707 case '<':
1708 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1710 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1711 break;
1713 wps_bufptr += check_feature_tag(wps_bufptr,
1714 data->tokens[data->num_tokens-1].type);
1715 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1716 lastcond[level] = data->num_tokens++;
1717 break;
1719 /* Conditional list end */
1720 case '>':
1721 if (level < 0) /* not in a conditional, invalid char */
1723 fail = PARSE_FAIL_INVALID_CHAR;
1724 break;
1727 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1728 if (lastcond[level])
1729 data->tokens[lastcond[level]].value.i = data->num_tokens;
1730 else
1732 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1733 break;
1736 lastcond[level] = 0;
1737 data->num_tokens++;
1738 data->tokens[condindex[level]].value.i = numoptions[level];
1739 level--;
1740 break;
1742 /* Conditional list option */
1743 case '|':
1744 if (level < 0) /* not in a conditional, invalid char */
1746 fail = PARSE_FAIL_INVALID_CHAR;
1747 break;
1750 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1751 if (lastcond[level])
1752 data->tokens[lastcond[level]].value.i = data->num_tokens;
1753 else
1755 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1756 break;
1759 lastcond[level] = data->num_tokens;
1760 numoptions[level]++;
1761 data->num_tokens++;
1762 break;
1764 /* Comment */
1765 case '#':
1766 if (level >= 0) /* there are unclosed conditionals */
1768 fail = PARSE_FAIL_UNCLOSED_COND;
1769 break;
1772 wps_bufptr += skip_end_of_line(wps_bufptr);
1773 break;
1775 /* End of this line */
1776 case '\n':
1777 if (level >= 0) /* there are unclosed conditionals */
1779 fail = PARSE_FAIL_UNCLOSED_COND;
1780 break;
1782 /* add a new token for the \n so empty lines are correct */
1783 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1784 data->tokens[data->num_tokens].value.c = '\n';
1785 data->tokens[data->num_tokens].next = false;
1786 data->num_tokens++;
1788 if (!skin_start_new_line(curr_vp, data->num_tokens))
1790 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1791 break;
1793 line_number++;
1795 break;
1797 /* String */
1798 default:
1800 unsigned int len = 1;
1801 const char *string_start = wps_bufptr - 1;
1803 /* find the length of the string */
1804 while (*wps_bufptr && *wps_bufptr != '#' &&
1805 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1806 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1807 *wps_bufptr != '|' && *wps_bufptr != '\n')
1809 wps_bufptr++;
1810 len++;
1813 /* look if we already have that string */
1814 char *str;
1815 bool found = false;
1816 struct skin_token_list *list = data->strings;
1817 while (list)
1819 str = (char*)list->token->value.data;
1820 found = (strlen(str) == len &&
1821 strncmp(string_start, str, len) == 0);
1822 if (found)
1823 break; /* break here because the list item is
1824 used if its found */
1825 list = list->next;
1827 /* If a matching string is found, found is true and i is
1828 the index of the string. If not, found is false */
1830 if (!found)
1832 /* new string */
1833 str = (char*)skin_buffer_alloc(len+1);
1834 if (!str)
1836 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1837 break;
1839 strlcpy(str, string_start, len+1);
1840 struct skin_token_list *item =
1841 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1842 if(!item)
1844 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1845 break;
1847 add_to_ll_chain(&data->strings, item);
1849 else
1851 /* another occurrence of an existing string */
1852 data->tokens[data->num_tokens].value.data = list->token->value.data;
1854 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1855 data->num_tokens++;
1857 break;
1861 if (!fail && level >= 0) /* there are unclosed conditionals */
1862 fail = PARSE_FAIL_UNCLOSED_COND;
1864 if (*wps_bufptr && !fail)
1865 /* one of the limits of the while loop was exceeded */
1866 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1868 /* Success! */
1869 curr_line->curr_subline->last_token_idx = data->num_tokens;
1870 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1871 /* freeup unused tokens */
1872 skin_buffer_free_from_front(sizeof(struct wps_token)
1873 * (max_tokens - data->num_tokens));
1875 #if defined(DEBUG) || defined(SIMULATOR)
1876 if (debug)
1877 print_debug_info(data, fail, line_number);
1878 #else
1879 (void)debug;
1880 #endif
1882 return (fail == 0);
1887 * initial setup of wps_data; does reset everything
1888 * except fields which need to survive, i.e.
1891 static void skin_data_reset(struct wps_data *wps_data)
1893 #ifdef HAVE_LCD_BITMAP
1894 wps_data->images = NULL;
1895 wps_data->progressbars = NULL;
1896 #endif
1897 #ifdef HAVE_TOUCHSCREEN
1898 wps_data->touchregions = NULL;
1899 #endif
1900 wps_data->viewports = NULL;
1901 wps_data->strings = NULL;
1902 #ifdef HAVE_ALBUMART
1903 wps_data->albumart = NULL;
1904 if (wps_data->playback_aa_slot >= 0)
1906 playback_release_aa_slot(wps_data->playback_aa_slot);
1907 wps_data->playback_aa_slot = -1;
1909 #endif
1910 wps_data->tokens = NULL;
1911 wps_data->num_tokens = 0;
1913 #ifdef HAVE_LCD_BITMAP
1914 wps_data->peak_meter_enabled = false;
1915 wps_data->wps_sb_tag = false;
1916 wps_data->show_sb_on_wps = false;
1917 #else /* HAVE_LCD_CHARCELLS */
1918 /* progress bars */
1919 int i;
1920 for (i = 0; i < 8; i++)
1922 wps_data->wps_progress_pat[i] = 0;
1924 wps_data->full_line_progressbar = false;
1925 #endif
1926 wps_data->wps_loaded = false;
1929 #ifdef HAVE_LCD_BITMAP
1930 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1932 (void)wps_data; /* only needed for remote targets */
1933 bool loaded = false;
1934 char img_path[MAX_PATH];
1935 get_image_filename(bitmap->data, bmpdir,
1936 img_path, sizeof(img_path));
1938 /* load the image */
1939 int format;
1940 #ifdef HAVE_REMOTE_LCD
1941 if (curr_screen == SCREEN_REMOTE)
1942 format = FORMAT_ANY|FORMAT_REMOTE;
1943 else
1944 #endif
1945 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1947 size_t max_buf;
1948 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1949 bitmap->data = imgbuf;
1950 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1952 if (ret > 0)
1954 skin_buffer_increment(ret, true);
1955 loaded = true;
1957 else
1959 /* Abort if we can't load an image */
1960 loaded = false;
1962 return loaded;
1965 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1967 struct skin_token_list *list;
1968 /* do the progressbars */
1969 list = wps_data->progressbars;
1970 while (list)
1972 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1973 if (pb->bm.data)
1975 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1977 list = list->next;
1979 /* regular images */
1980 list = wps_data->images;
1981 while (list)
1983 struct gui_img *img = (struct gui_img*)list->token->value.data;
1984 if (img->bm.data)
1986 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1987 if (img->loaded)
1988 img->subimage_height = img->bm.height / img->num_subimages;
1990 list = list->next;
1993 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1994 if (bmp_names[BACKDROP_BMP])
1996 char img_path[MAX_PATH];
1997 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1998 img_path, sizeof(img_path));
1999 screens[curr_screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
2001 #endif /* has backdrop support */
2003 /* If we got here, everything was OK */
2004 return true;
2007 #endif /* HAVE_LCD_BITMAP */
2009 /* to setup up the wps-data from a format-buffer (isfile = false)
2010 from a (wps-)file (isfile = true)*/
2011 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2012 const char *buf, bool isfile)
2015 if (!wps_data || !buf)
2016 return false;
2017 #ifdef HAVE_ALBUMART
2018 int status;
2019 struct mp3entry *curtrack;
2020 long offset;
2021 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2022 if (wps_data->albumart)
2024 old_aa.state = wps_data->albumart->state;
2025 old_aa.height = wps_data->albumart->height;
2026 old_aa.width = wps_data->albumart->width;
2028 #endif
2030 skin_data_reset(wps_data);
2031 curr_screen = screen;
2033 /* alloc default viewport, will be fixed up later */
2034 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2035 if (!curr_vp)
2036 return false;
2037 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2038 if (!list)
2039 return false;
2040 add_to_ll_chain(&wps_data->viewports, list);
2043 /* Initialise the first (default) viewport */
2044 curr_vp->label = VP_DEFAULT_LABEL;
2045 curr_vp->pb = NULL;
2046 curr_vp->hidden_flags = 0;
2047 curr_vp->lines = NULL;
2049 viewport_set_defaults(&curr_vp->vp, screen);
2051 curr_line = NULL;
2052 if (!skin_start_new_line(curr_vp, 0))
2053 return false;
2055 if (!isfile)
2057 return wps_parse(wps_data, buf, false);
2059 else
2061 int fd = open_utf8(buf, O_RDONLY);
2063 if (fd < 0)
2064 return false;
2066 /* get buffer space from the plugin buffer */
2067 size_t buffersize = 0;
2068 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2070 if (!wps_buffer)
2071 return false;
2073 /* copy the file's content to the buffer for parsing,
2074 ensuring that every line ends with a newline char. */
2075 unsigned int start = 0;
2076 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2078 start += strlen(wps_buffer + start);
2079 if (start < buffersize - 1)
2081 wps_buffer[start++] = '\n';
2082 wps_buffer[start] = 0;
2086 close(fd);
2088 if (start <= 0)
2089 return false;
2091 #ifdef HAVE_LCD_BITMAP
2092 /* Set all filename pointers to NULL */
2093 memset(bmp_names, 0, sizeof(bmp_names));
2094 #endif
2096 /* parse the WPS source */
2097 if (!wps_parse(wps_data, wps_buffer, true)) {
2098 skin_data_reset(wps_data);
2099 return false;
2102 wps_data->wps_loaded = true;
2104 #ifdef HAVE_LCD_BITMAP
2105 /* get the bitmap dir */
2106 char bmpdir[MAX_PATH];
2107 char *dot = strrchr(buf, '.');
2109 strlcpy(bmpdir, buf, dot - buf + 1);
2111 /* load the bitmaps that were found by the parsing */
2112 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2113 skin_data_reset(wps_data);
2114 return false;
2116 #endif
2117 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2118 status = audio_status();
2119 if (status & AUDIO_STATUS_PLAY)
2121 struct skin_albumart *aa = wps_data->albumart;
2122 if (aa && ((aa->state && !old_aa.state) ||
2123 (aa->state &&
2124 (((old_aa.height != aa->height) ||
2125 (old_aa.width != aa->width))))))
2127 curtrack = audio_current_track();
2128 offset = curtrack->offset;
2129 audio_stop();
2130 if (!(status & AUDIO_STATUS_PAUSE))
2131 audio_play(offset);
2134 #endif
2135 #if defined(DEBUG) || defined(SIMULATOR)
2136 debug_skin_usage();
2137 #endif
2138 return true;