Add a missing #include, make a function static, make touchpad_set_buttonlight impleme...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobc70a9759ee84b5078f35694c6b879cdd6042a835
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 static 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 static 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++][1] = '\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 if (tag->parse_func)
785 /* unsupported tag, reject */
786 return -1;
788 taglen = strlen(tag->name);
789 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
791 text += taglen;
794 else
796 /* regular string */
797 int i = 0;
798 /* just copy the string */
799 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
800 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
802 viewer->lines[line].strings[cur_string][i] = text[i];
803 i++;
805 viewer->lines[line].strings[cur_string][i] = '\0';
806 cur_string++;
807 text += i;
810 return text - start;
814 static int parse_playlistview(const char *wps_bufptr,
815 struct wps_token *token, struct wps_data *wps_data)
817 (void)wps_data;
818 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
819 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
820 char *ptr = strchr(wps_bufptr, '|');
821 int length;
822 if (!viewer || !ptr)
823 return WPS_ERROR_INVALID_PARAM;
824 viewer->vp = &curr_vp->vp;
825 viewer->show_icons = true;
826 viewer->start_offset = atoi(ptr+1);
827 token->value.data = (void*)viewer;
828 ptr = strchr(ptr+1, '|');
829 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
830 if (length < 0)
831 return WPS_ERROR_INVALID_PARAM;
832 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
833 if (length < 0)
834 return WPS_ERROR_INVALID_PARAM;
836 return skip_end_of_line(wps_bufptr);
838 #endif
840 static int parse_viewport(const char *wps_bufptr,
841 struct wps_token *token,
842 struct wps_data *wps_data)
844 (void)token; /* Kill warnings */
845 const char *ptr = wps_bufptr;
847 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
849 /* check for the optional letter to signify its a hideable viewport */
850 /* %Vl|<label>|<rest of tags>| */
851 skin_vp->hidden_flags = 0;
852 skin_vp->label = VP_NO_LABEL;
853 skin_vp->pb = NULL;
854 skin_vp->lines = NULL;
855 if (curr_line)
857 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
858 - (wps_data->num_tokens > 0 ? 1 : 0);
861 curr_line = NULL;
862 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
863 return WPS_ERROR_INVALID_PARAM;
866 if (*ptr == 'i')
868 skin_vp->label = VP_INFO_LABEL;
869 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
870 ++ptr;
872 else if (*ptr == 'l')
874 if (*(ptr+1) == '|')
876 char label = *(ptr+2);
877 if (label >= 'a' && label <= 'z')
879 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
880 skin_vp->label = label;
882 else
883 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
884 ptr += 3;
887 if (*ptr != '|')
888 return WPS_ERROR_INVALID_PARAM;
890 ptr++;
891 struct viewport *vp = &skin_vp->vp;
892 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
893 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
894 return WPS_ERROR_INVALID_PARAM;
896 /* Check for trailing | */
897 if (*ptr != '|')
898 return WPS_ERROR_INVALID_PARAM;
900 if (follow_lang_direction && lang_is_rtl())
902 vp->flags |= VP_FLAG_ALIGN_RIGHT;
903 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
905 else
906 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
910 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
911 if (!list)
912 return WPS_ERROR_INVALID_PARAM;
913 add_to_ll_chain(&wps_data->viewports, list);
914 curr_vp = skin_vp;
915 /* Skip the rest of the line */
916 return skip_end_of_line(wps_bufptr);
919 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
920 static int parse_image_special(const char *wps_bufptr,
921 struct wps_token *token,
922 struct wps_data *wps_data)
924 (void)wps_data; /* kill warning */
925 (void)token;
926 const char *pos = NULL;
927 const char *newline;
929 pos = strchr(wps_bufptr + 1, '|');
930 newline = strchr(wps_bufptr, '\n');
932 if (pos > newline)
933 return WPS_ERROR_INVALID_PARAM;
934 #if LCD_DEPTH > 1
935 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
937 /* format: %X|filename.bmp| */
938 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
940 #endif
942 /* Skip the rest of the line */
943 return skip_end_of_line(wps_bufptr);
945 #endif
947 #endif /* HAVE_LCD_BITMAP */
949 static int parse_setting_and_lang(const char *wps_bufptr,
950 struct wps_token *token,
951 struct wps_data *wps_data)
953 /* NOTE: both the string validations that happen in here will
954 * automatically PASS on checkwps because its too hard to get
955 * settings_list.c and englinsh.lang built for it.
956 * If that ever changes remove the #ifndef __PCTOOL__'s here
958 (void)wps_data;
959 const char *ptr = wps_bufptr;
960 const char *end;
961 int i = 0;
962 char temp[64];
964 /* Find the setting's cfg_name */
965 if (*ptr != '|')
966 return WPS_ERROR_INVALID_PARAM;
967 ptr++;
968 end = strchr(ptr,'|');
969 if (!end)
970 return WPS_ERROR_INVALID_PARAM;
971 strlcpy(temp, ptr,end-ptr+1);
973 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
975 #ifndef __PCTOOL__
976 i = lang_english_to_id(temp);
977 if (i < 0)
978 return WPS_ERROR_INVALID_PARAM;
979 #endif
981 else
983 /* Find the setting */
984 for (i=0; i<nb_settings; i++)
985 if (settings[i].cfg_name &&
986 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
987 /* prevent matches on cfg_name prefixes */
988 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
989 break;
990 #ifndef __PCTOOL__
991 if (i == nb_settings)
992 return WPS_ERROR_INVALID_PARAM;
993 #endif
995 /* Store the setting number */
996 token->value.i = i;
998 /* Skip the rest of the line */
999 return end-ptr+2;
1003 static int parse_dir_level(const char *wps_bufptr,
1004 struct wps_token *token,
1005 struct wps_data *wps_data)
1007 char val[] = { *wps_bufptr, '\0' };
1008 token->value.i = atoi(val);
1009 (void)wps_data; /* Kill warnings */
1010 return 1;
1013 static int parse_timeout(const char *wps_bufptr,
1014 struct wps_token *token,
1015 struct wps_data *wps_data)
1017 int skip = 0;
1018 int val = 0;
1019 bool have_point = false;
1020 bool have_tenth = false;
1022 (void)wps_data; /* Kill the warning */
1024 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1026 if (*wps_bufptr != '.')
1028 val *= 10;
1029 val += *wps_bufptr - '0';
1030 if (have_point)
1032 have_tenth = true;
1033 wps_bufptr++;
1034 skip++;
1035 break;
1038 else
1039 have_point = true;
1041 wps_bufptr++;
1042 skip++;
1045 if (have_tenth == false)
1046 val *= 10;
1048 if (val == 0 && skip == 0)
1050 /* decide what to do if no value was specified */
1051 switch (token->type)
1053 case WPS_TOKEN_SUBLINE_TIMEOUT:
1054 return -1;
1055 case WPS_TOKEN_BUTTON_VOLUME:
1056 val = 10;
1057 break;
1060 token->value.i = val;
1062 return skip;
1065 static int parse_progressbar(const char *wps_bufptr,
1066 struct wps_token *token,
1067 struct wps_data *wps_data)
1069 /* %pb or %pb|filename|x|y|width|height|
1070 using - for any of the params uses "sane" values */
1071 #ifdef HAVE_LCD_BITMAP
1072 enum {
1073 PB_FILENAME = 0,
1074 PB_X,
1075 PB_Y,
1076 PB_WIDTH,
1077 PB_HEIGHT
1079 const char *filename;
1080 int x, y, height, width;
1081 uint32_t set = 0;
1082 const char *ptr = wps_bufptr;
1083 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1084 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1086 if (!pb || !item)
1087 return WPS_ERROR_INVALID_PARAM;
1089 struct viewport *vp = &curr_vp->vp;
1090 #ifndef __PCTOOL__
1091 int font_height = font_get(vp->font)->height;
1092 #else
1093 int font_height = 8;
1094 #endif
1095 /* we need to know what line number (viewport relative) this pb is,
1096 * so count them... */
1097 int line_num = -1;
1098 struct skin_line *line = curr_vp->lines;
1099 while (line)
1101 line_num++;
1102 line = line->next;
1104 pb->have_bitmap_pb = false;
1105 pb->bm.data = NULL; /* no bitmap specified */
1106 pb->follow_lang_direction = follow_lang_direction > 0;
1108 if (*wps_bufptr != '|') /* regular old style */
1110 pb->x = 0;
1111 pb->width = vp->width;
1112 pb->height = SYSFONT_HEIGHT-2;
1113 pb->y = -line_num - 1; /* Will be computed during the rendering */
1115 curr_vp->pb = pb;
1116 add_to_ll_chain(&wps_data->progressbars, item);
1117 return 0;
1119 ptr = wps_bufptr + 1;
1121 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1122 &x, &y, &width, &height)))
1123 return WPS_ERROR_INVALID_PARAM;
1125 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1126 pb->bm.data = (char*)filename;
1128 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1129 pb->x = x;
1130 else
1131 pb->x = vp->x;
1133 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1135 /* A zero width causes a divide-by-zero error later, so reject it */
1136 if (width == 0)
1137 return WPS_ERROR_INVALID_PARAM;
1139 pb->width = width;
1141 else
1142 pb->width = vp->width - pb->x;
1144 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1146 /* A zero height makes no sense - reject it */
1147 if (height == 0)
1148 return WPS_ERROR_INVALID_PARAM;
1150 pb->height = height;
1152 else
1153 pb->height = font_height;
1155 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1156 pb->y = y;
1157 else
1158 pb->y = -line_num - 1; /* Will be computed during the rendering */
1160 curr_vp->pb = pb;
1161 add_to_ll_chain(&wps_data->progressbars, item);
1163 /* Skip the rest of the line */
1164 return skip_end_of_line(wps_bufptr)-1;
1165 #else
1166 (void)token;
1168 if (*(wps_bufptr-1) == 'f')
1169 wps_data->full_line_progressbar = true;
1170 else
1171 wps_data->full_line_progressbar = false;
1173 return 0;
1175 #endif
1178 #ifdef HAVE_ALBUMART
1179 static int parse_int(const char *newline, const char **_pos, int *num)
1181 *_pos = parse_list("d", NULL, '|', *_pos, num);
1183 return (!*_pos || *_pos > newline || **_pos != '|');
1186 static int parse_albumart_load(const char *wps_bufptr,
1187 struct wps_token *token,
1188 struct wps_data *wps_data)
1190 const char *_pos, *newline;
1191 bool parsing;
1192 struct dim dimensions;
1193 int albumart_slot;
1194 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1195 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1196 (void)token; /* silence warning */
1197 if (!aa)
1198 return skip_end_of_line(wps_bufptr);
1200 /* reset albumart info in wps */
1201 aa->width = -1;
1202 aa->height = -1;
1203 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1204 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1205 aa->vp = &curr_vp->vp;
1207 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1209 newline = strchr(wps_bufptr, '\n');
1211 _pos = wps_bufptr;
1213 if (*_pos != '|')
1214 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1216 ++_pos;
1218 /* initial validation and parsing of x component */
1219 if (parse_int(newline, &_pos, &aa->x))
1220 return WPS_ERROR_INVALID_PARAM;
1222 ++_pos;
1224 /* initial validation and parsing of y component */
1225 if (parse_int(newline, &_pos, &aa->y))
1226 return WPS_ERROR_INVALID_PARAM;
1228 /* parsing width field */
1229 parsing = true;
1230 while (parsing)
1232 /* apply each modifier in turn */
1233 ++_pos;
1234 switch (*_pos)
1236 case 'l':
1237 case 'L':
1238 case '+':
1239 if (swap_for_rtl)
1240 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1241 else
1242 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1243 break;
1244 case 'c':
1245 case 'C':
1246 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1247 break;
1248 case 'r':
1249 case 'R':
1250 case '-':
1251 if (swap_for_rtl)
1252 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1253 else
1254 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1255 break;
1256 case 'd':
1257 case 'D':
1258 case 'i':
1259 case 'I':
1260 case 's':
1261 case 'S':
1262 /* simply ignored */
1263 break;
1264 default:
1265 parsing = false;
1266 break;
1269 /* extract max width data */
1270 if (*_pos != '|')
1272 if (parse_int(newline, &_pos, &aa->width))
1273 return WPS_ERROR_INVALID_PARAM;
1276 /* parsing height field */
1277 parsing = true;
1278 while (parsing)
1280 /* apply each modifier in turn */
1281 ++_pos;
1282 switch (*_pos)
1284 case 't':
1285 case 'T':
1286 case '-':
1287 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1288 break;
1289 case 'c':
1290 case 'C':
1291 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1292 break;
1293 case 'b':
1294 case 'B':
1295 case '+':
1296 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1297 break;
1298 case 'd':
1299 case 'D':
1300 case 'i':
1301 case 'I':
1302 case 's':
1303 case 'S':
1304 /* simply ignored */
1305 break;
1306 default:
1307 parsing = false;
1308 break;
1311 /* extract max height data */
1312 if (*_pos != '|')
1314 if (parse_int(newline, &_pos, &aa->height))
1315 return WPS_ERROR_INVALID_PARAM;
1318 /* if we got here, we parsed everything ok .. ! */
1319 if (aa->width < 0)
1320 aa->width = 0;
1321 else if (aa->width > LCD_WIDTH)
1322 aa->width = LCD_WIDTH;
1324 if (aa->height < 0)
1325 aa->height = 0;
1326 else if (aa->height > LCD_HEIGHT)
1327 aa->height = LCD_HEIGHT;
1329 if (swap_for_rtl)
1330 aa->x = LCD_WIDTH - (aa->x + aa->width);
1332 aa->state = WPS_ALBUMART_LOAD;
1333 aa->draw = false;
1334 wps_data->albumart = aa;
1336 dimensions.width = aa->width;
1337 dimensions.height = aa->height;
1339 albumart_slot = playback_claim_aa_slot(&dimensions);
1341 if (0 <= albumart_slot)
1342 wps_data->playback_aa_slot = albumart_slot;
1344 /* Skip the rest of the line */
1345 return skip_end_of_line(wps_bufptr);
1348 static int parse_albumart_display(const char *wps_bufptr,
1349 struct wps_token *token,
1350 struct wps_data *wps_data)
1352 (void)wps_bufptr;
1353 struct wps_token *prev = token-1;
1354 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1356 token->type = WPS_TOKEN_ALBUMART_FOUND;
1358 else if (wps_data->albumart)
1360 wps_data->albumart->vp = &curr_vp->vp;
1362 #if 0
1363 /* the old code did this so keep it here for now...
1364 * this is to allow the posibility to showing the next tracks AA! */
1365 if (wps_bufptr+1 == 'n')
1366 return 1;
1367 #endif
1368 return 0;
1370 #endif /* HAVE_ALBUMART */
1372 #ifdef HAVE_TOUCHSCREEN
1374 struct touchaction {const char* s; int action;};
1375 static const struct touchaction touchactions[] = {
1376 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1377 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1378 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1379 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1380 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1381 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1382 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1383 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1385 static int parse_touchregion(const char *wps_bufptr,
1386 struct wps_token *token, struct wps_data *wps_data)
1388 (void)token;
1389 unsigned i, imax;
1390 struct touchregion *region = NULL;
1391 const char *ptr = wps_bufptr;
1392 const char *action;
1393 const char pb_string[] = "progressbar";
1394 const char vol_string[] = "volume";
1395 int x,y,w,h;
1397 /* format: %T|x|y|width|height|action|
1398 * if action starts with & the area must be held to happen
1399 * action is one of:
1400 * play - play/pause playback
1401 * stop - stop playback, exit the wps
1402 * prev - prev track
1403 * next - next track
1404 * ffwd - seek forward
1405 * rwd - seek backwards
1406 * menu - go back to the main menu
1407 * browse - go back to the file/db browser
1408 * shuffle - toggle shuffle mode
1409 * repmode - cycle the repeat mode
1410 * quickscreen - go into the quickscreen
1411 * contextmenu - open the context menu
1412 * playlist - go into the playlist
1413 * pitch - go into the pitchscreen
1414 * volup - increase volume by one step
1415 * voldown - decrease volume by one step
1419 if (*ptr != '|')
1420 return WPS_ERROR_INVALID_PARAM;
1421 ptr++;
1423 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1424 return WPS_ERROR_INVALID_PARAM;
1426 /* Check there is a terminating | */
1427 if (*ptr != '|')
1428 return WPS_ERROR_INVALID_PARAM;
1430 region = skin_buffer_alloc(sizeof(struct touchregion));
1431 if (!region)
1432 return WPS_ERROR_INVALID_PARAM;
1434 /* should probably do some bounds checking here with the viewport... but later */
1435 region->action = ACTION_NONE;
1436 region->x = x;
1437 region->y = y;
1438 region->width = w;
1439 region->height = h;
1440 region->wvp = curr_vp;
1442 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1443 && *(action + sizeof(pb_string)-1) == '|')
1444 region->type = WPS_TOUCHREGION_SCROLLBAR;
1445 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1446 && *(action + sizeof(vol_string)-1) == '|')
1447 region->type = WPS_TOUCHREGION_VOLUME;
1448 else
1450 region->type = WPS_TOUCHREGION_ACTION;
1452 if (*action == '&')
1454 action++;
1455 region->repeat = true;
1457 else
1458 region->repeat = false;
1460 i = 0;
1461 imax = ARRAYLEN(touchactions);
1462 while ((region->action == ACTION_NONE) &&
1463 (i < imax))
1465 /* try to match with one of our touchregion screens */
1466 int len = strlen(touchactions[i].s);
1467 if (!strncmp(touchactions[i].s, action, len)
1468 && *(action+len) == '|')
1469 region->action = touchactions[i].action;
1470 i++;
1472 if (region->action == ACTION_NONE)
1473 return WPS_ERROR_INVALID_PARAM;
1475 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1476 if (!item)
1477 return WPS_ERROR_INVALID_PARAM;
1478 add_to_ll_chain(&wps_data->touchregions, item);
1479 return skip_end_of_line(wps_bufptr);
1481 #endif
1483 /* Parse a generic token from the given string. Return the length read */
1484 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1486 int skip = 0, taglen = 0, ret;
1487 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1488 const struct wps_tag *tag;
1489 memset(token, 0, sizeof(*token));
1491 switch(*wps_bufptr)
1494 case '%':
1495 case '<':
1496 case '|':
1497 case '>':
1498 case ';':
1499 case '#':
1500 /* escaped characters */
1501 token->type = WPS_TOKEN_CHARACTER;
1502 token->value.c = *wps_bufptr;
1503 taglen = 1;
1504 wps_data->num_tokens++;
1505 break;
1507 case '?':
1508 /* conditional tag */
1509 token->type = WPS_TOKEN_CONDITIONAL;
1510 level++;
1511 condindex[level] = wps_data->num_tokens;
1512 numoptions[level] = 1;
1513 wps_data->num_tokens++;
1514 ret = parse_token(wps_bufptr + 1, wps_data);
1515 if (ret < 0) return ret;
1516 taglen = 1 + ret;
1517 break;
1519 default:
1520 /* find what tag we have */
1521 for (tag = all_tags;
1522 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1523 tag++) ;
1525 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1526 token->type = tag->type;
1527 curr_line->curr_subline->line_type |= tag->refresh_type;
1529 /* if the tag has a special parsing function, we call it */
1530 if (tag->parse_func)
1532 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1533 if (ret < 0) return ret;
1534 skip += ret;
1537 /* Some tags we don't want to save as tokens */
1538 if (tag->type == WPS_NO_TOKEN)
1539 break;
1541 /* tags that start with 'F', 'I' or 'D' are for the next file */
1542 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1543 *(tag->name) == 'D')
1544 token->next = true;
1546 wps_data->num_tokens++;
1547 break;
1550 skip += taglen;
1551 return skip;
1556 * Returns the number of bytes to skip the buf pointer to access the false
1557 * branch in a _binary_ conditional
1559 * That is:
1560 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1561 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1563 * depending on the features of a target it's not called from check_feature_tag,
1564 * hence the __attribute__ or it issues compiler warnings
1568 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1569 static int find_false_branch(const char *wps_bufptr)
1571 const char *buf = wps_bufptr;
1572 /* wps_bufptr is after the opening '<', hence level = 1*/
1573 int level = 1;
1574 char ch;
1577 ch = *buf;
1578 if (ch == '%')
1579 { /* filter out the characters we check later if they're printed
1580 * as literals */
1581 ch = *(++buf);
1582 if (ch == '<' || ch == '>' || ch == '|')
1583 continue;
1584 /* else: some tags/printed literals we skip over */
1586 else if (ch == '<') /* nested conditional */
1587 level++;
1588 else if (ch == '>')
1589 { /* closed our or a nested conditional,
1590 * do NOT skip over the '>' so that wps_parse() sees it for closing
1591 * if it is the closing one for our conditional */
1592 level--;
1594 else if (ch == '|' && level == 1)
1595 { /* we found our separator, point before and get out */
1596 break;
1598 /* if level is 0, we don't have a false branch */
1599 } while (level > 0 && *(++buf));
1601 return buf - wps_bufptr;
1605 * returns the number of bytes to get the appropriate branch of a binary
1606 * conditional
1608 * That means:
1609 * - if a feature is available, it returns 0 to not skip anything
1610 * - if the feature is not available, skip to the false branch and don't
1611 * parse the true branch at all
1613 * */
1614 static int check_feature_tag(const char *wps_bufptr, const int type)
1616 (void)wps_bufptr;
1617 switch (type)
1619 case WPS_TOKEN_RTC_PRESENT:
1620 #if CONFIG_RTC
1621 return 0;
1622 #else
1623 return find_false_branch(wps_bufptr);
1624 #endif
1625 case WPS_TOKEN_HAVE_RECORDING:
1626 #ifdef HAVE_RECORDING
1627 return 0;
1628 #else
1629 return find_false_branch(wps_bufptr);
1630 #endif
1631 default: /* not a tag we care about, just don't skip */
1632 return 0;
1637 /* Parses the WPS.
1638 data is the pointer to the structure where the parsed WPS should be stored.
1639 It is initialised.
1640 wps_bufptr points to the string containing the WPS tags */
1641 #define TOKEN_BLOCK_SIZE 128
1642 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1644 if (!data || !wps_bufptr || !*wps_bufptr)
1645 return false;
1646 enum wps_parse_error fail = PARSE_OK;
1647 int ret;
1648 int max_tokens = TOKEN_BLOCK_SIZE;
1649 size_t buf_free = 0;
1650 line_number = 0;
1651 level = -1;
1653 /* allocate enough RAM for a reasonable skin, grow as needed.
1654 * Free any used RAM before loading the images to be 100% RAM efficient */
1655 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1656 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1657 return false;
1658 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1659 data->num_tokens = 0;
1661 while (*wps_bufptr && !fail)
1663 if (follow_lang_direction)
1664 follow_lang_direction--;
1665 /* first make sure there is enough room for tokens */
1666 if (max_tokens <= data->num_tokens + 5)
1668 int extra_tokens = TOKEN_BLOCK_SIZE;
1669 size_t needed = extra_tokens * sizeof(struct wps_token);
1670 /* do some smarts here to grow the array a bit */
1671 if (skin_buffer_freespace() < needed)
1673 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1674 break;
1676 skin_buffer_increment(needed, false);
1677 max_tokens += extra_tokens;
1680 switch(*wps_bufptr++)
1683 /* Regular tag */
1684 case '%':
1685 if ((ret = parse_token(wps_bufptr, data)) < 0)
1687 fail = PARSE_FAIL_COND_INVALID_PARAM;
1688 break;
1690 else if (level >= WPS_MAX_COND_LEVEL - 1)
1692 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1693 break;
1695 wps_bufptr += ret;
1696 break;
1698 /* Alternating sublines separator */
1699 case ';':
1700 if (level >= 0) /* there are unclosed conditionals */
1702 fail = PARSE_FAIL_UNCLOSED_COND;
1703 break;
1706 if (!skin_start_new_subline(curr_line, data->num_tokens))
1707 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1709 break;
1711 /* Conditional list start */
1712 case '<':
1713 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1715 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1716 break;
1718 wps_bufptr += check_feature_tag(wps_bufptr,
1719 data->tokens[data->num_tokens-1].type);
1720 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1721 lastcond[level] = data->num_tokens++;
1722 break;
1724 /* Conditional list end */
1725 case '>':
1726 if (level < 0) /* not in a conditional, invalid char */
1728 fail = PARSE_FAIL_INVALID_CHAR;
1729 break;
1732 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1733 if (lastcond[level])
1734 data->tokens[lastcond[level]].value.i = data->num_tokens;
1735 else
1737 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1738 break;
1741 lastcond[level] = 0;
1742 data->num_tokens++;
1743 data->tokens[condindex[level]].value.i = numoptions[level];
1744 level--;
1745 break;
1747 /* Conditional list option */
1748 case '|':
1749 if (level < 0) /* not in a conditional, invalid char */
1751 fail = PARSE_FAIL_INVALID_CHAR;
1752 break;
1755 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1756 if (lastcond[level])
1757 data->tokens[lastcond[level]].value.i = data->num_tokens;
1758 else
1760 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1761 break;
1764 lastcond[level] = data->num_tokens;
1765 numoptions[level]++;
1766 data->num_tokens++;
1767 break;
1769 /* Comment */
1770 case '#':
1771 if (level >= 0) /* there are unclosed conditionals */
1773 fail = PARSE_FAIL_UNCLOSED_COND;
1774 break;
1777 wps_bufptr += skip_end_of_line(wps_bufptr);
1778 break;
1780 /* End of this line */
1781 case '\n':
1782 if (level >= 0) /* there are unclosed conditionals */
1784 fail = PARSE_FAIL_UNCLOSED_COND;
1785 break;
1787 /* add a new token for the \n so empty lines are correct */
1788 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1789 data->tokens[data->num_tokens].value.c = '\n';
1790 data->tokens[data->num_tokens].next = false;
1791 data->num_tokens++;
1793 if (!skin_start_new_line(curr_vp, data->num_tokens))
1795 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1796 break;
1798 line_number++;
1800 break;
1802 /* String */
1803 default:
1805 unsigned int len = 1;
1806 const char *string_start = wps_bufptr - 1;
1808 /* find the length of the string */
1809 while (*wps_bufptr && *wps_bufptr != '#' &&
1810 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1811 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1812 *wps_bufptr != '|' && *wps_bufptr != '\n')
1814 wps_bufptr++;
1815 len++;
1818 /* look if we already have that string */
1819 char *str;
1820 bool found = false;
1821 struct skin_token_list *list = data->strings;
1822 while (list)
1824 str = (char*)list->token->value.data;
1825 found = (strlen(str) == len &&
1826 strncmp(string_start, str, len) == 0);
1827 if (found)
1828 break; /* break here because the list item is
1829 used if its found */
1830 list = list->next;
1832 /* If a matching string is found, found is true and i is
1833 the index of the string. If not, found is false */
1835 if (!found)
1837 /* new string */
1838 str = (char*)skin_buffer_alloc(len+1);
1839 if (!str)
1841 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1842 break;
1844 strlcpy(str, string_start, len+1);
1845 struct skin_token_list *item =
1846 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1847 if(!item)
1849 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1850 break;
1852 add_to_ll_chain(&data->strings, item);
1854 else
1856 /* another occurrence of an existing string */
1857 data->tokens[data->num_tokens].value.data = list->token->value.data;
1859 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1860 data->num_tokens++;
1862 break;
1866 if (!fail && level >= 0) /* there are unclosed conditionals */
1867 fail = PARSE_FAIL_UNCLOSED_COND;
1869 if (*wps_bufptr && !fail)
1870 /* one of the limits of the while loop was exceeded */
1871 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1873 /* Success! */
1874 curr_line->curr_subline->last_token_idx = data->num_tokens;
1875 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1876 /* freeup unused tokens */
1877 skin_buffer_free_from_front(sizeof(struct wps_token)
1878 * (max_tokens - data->num_tokens));
1880 #if defined(DEBUG) || defined(SIMULATOR)
1881 if (debug)
1882 print_debug_info(data, fail, line_number);
1883 #else
1884 (void)debug;
1885 #endif
1887 return (fail == 0);
1892 * initial setup of wps_data; does reset everything
1893 * except fields which need to survive, i.e.
1896 static void skin_data_reset(struct wps_data *wps_data)
1898 #ifdef HAVE_LCD_BITMAP
1899 wps_data->images = NULL;
1900 wps_data->progressbars = NULL;
1901 #endif
1902 #ifdef HAVE_TOUCHSCREEN
1903 wps_data->touchregions = NULL;
1904 #endif
1905 wps_data->viewports = NULL;
1906 wps_data->strings = NULL;
1907 #ifdef HAVE_ALBUMART
1908 wps_data->albumart = NULL;
1909 if (wps_data->playback_aa_slot >= 0)
1911 playback_release_aa_slot(wps_data->playback_aa_slot);
1912 wps_data->playback_aa_slot = -1;
1914 #endif
1915 wps_data->tokens = NULL;
1916 wps_data->num_tokens = 0;
1918 #ifdef HAVE_LCD_BITMAP
1919 wps_data->peak_meter_enabled = false;
1920 wps_data->wps_sb_tag = false;
1921 wps_data->show_sb_on_wps = false;
1922 #else /* HAVE_LCD_CHARCELLS */
1923 /* progress bars */
1924 int i;
1925 for (i = 0; i < 8; i++)
1927 wps_data->wps_progress_pat[i] = 0;
1929 wps_data->full_line_progressbar = false;
1930 #endif
1931 wps_data->wps_loaded = false;
1934 #ifdef HAVE_LCD_BITMAP
1935 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1937 (void)wps_data; /* only needed for remote targets */
1938 bool loaded = false;
1939 char img_path[MAX_PATH];
1940 get_image_filename(bitmap->data, bmpdir,
1941 img_path, sizeof(img_path));
1943 /* load the image */
1944 int format;
1945 #ifdef HAVE_REMOTE_LCD
1946 if (curr_screen == SCREEN_REMOTE)
1947 format = FORMAT_ANY|FORMAT_REMOTE;
1948 else
1949 #endif
1950 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1952 size_t max_buf;
1953 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1954 bitmap->data = imgbuf;
1955 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1957 if (ret > 0)
1959 skin_buffer_increment(ret, true);
1960 loaded = true;
1962 else
1964 /* Abort if we can't load an image */
1965 loaded = false;
1967 return loaded;
1970 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1972 struct skin_token_list *list;
1973 /* do the progressbars */
1974 list = wps_data->progressbars;
1975 while (list)
1977 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1978 if (pb->bm.data)
1980 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1982 list = list->next;
1984 /* regular images */
1985 list = wps_data->images;
1986 while (list)
1988 struct gui_img *img = (struct gui_img*)list->token->value.data;
1989 if (img->bm.data)
1991 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1992 if (img->loaded)
1993 img->subimage_height = img->bm.height / img->num_subimages;
1995 list = list->next;
1998 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1999 if (bmp_names[BACKDROP_BMP])
2001 char img_path[MAX_PATH];
2002 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
2003 img_path, sizeof(img_path));
2004 screens[curr_screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
2006 #endif /* has backdrop support */
2008 /* If we got here, everything was OK */
2009 return true;
2012 #endif /* HAVE_LCD_BITMAP */
2014 /* to setup up the wps-data from a format-buffer (isfile = false)
2015 from a (wps-)file (isfile = true)*/
2016 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2017 const char *buf, bool isfile)
2020 if (!wps_data || !buf)
2021 return false;
2022 #ifdef HAVE_ALBUMART
2023 int status;
2024 struct mp3entry *curtrack;
2025 long offset;
2026 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2027 if (wps_data->albumart)
2029 old_aa.state = wps_data->albumart->state;
2030 old_aa.height = wps_data->albumart->height;
2031 old_aa.width = wps_data->albumart->width;
2033 #endif
2035 skin_data_reset(wps_data);
2036 curr_screen = screen;
2038 /* alloc default viewport, will be fixed up later */
2039 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2040 if (!curr_vp)
2041 return false;
2042 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2043 if (!list)
2044 return false;
2045 add_to_ll_chain(&wps_data->viewports, list);
2048 /* Initialise the first (default) viewport */
2049 curr_vp->label = VP_DEFAULT_LABEL;
2050 curr_vp->pb = NULL;
2051 curr_vp->hidden_flags = 0;
2052 curr_vp->lines = NULL;
2054 viewport_set_defaults(&curr_vp->vp, screen);
2056 curr_line = NULL;
2057 if (!skin_start_new_line(curr_vp, 0))
2058 return false;
2060 if (!isfile)
2062 return wps_parse(wps_data, buf, false);
2064 else
2066 int fd = open_utf8(buf, O_RDONLY);
2068 if (fd < 0)
2069 return false;
2071 /* get buffer space from the plugin buffer */
2072 size_t buffersize = 0;
2073 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2075 if (!wps_buffer)
2076 return false;
2078 /* copy the file's content to the buffer for parsing,
2079 ensuring that every line ends with a newline char. */
2080 unsigned int start = 0;
2081 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2083 start += strlen(wps_buffer + start);
2084 if (start < buffersize - 1)
2086 wps_buffer[start++] = '\n';
2087 wps_buffer[start] = 0;
2091 close(fd);
2093 if (start <= 0)
2094 return false;
2096 #ifdef HAVE_LCD_BITMAP
2097 /* Set all filename pointers to NULL */
2098 memset(bmp_names, 0, sizeof(bmp_names));
2099 #endif
2101 /* parse the WPS source */
2102 if (!wps_parse(wps_data, wps_buffer, true)) {
2103 skin_data_reset(wps_data);
2104 return false;
2107 wps_data->wps_loaded = true;
2109 #ifdef HAVE_LCD_BITMAP
2110 /* get the bitmap dir */
2111 char bmpdir[MAX_PATH];
2112 char *dot = strrchr(buf, '.');
2114 strlcpy(bmpdir, buf, dot - buf + 1);
2116 /* load the bitmaps that were found by the parsing */
2117 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2118 skin_data_reset(wps_data);
2119 return false;
2121 #endif
2122 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2123 status = audio_status();
2124 if (status & AUDIO_STATUS_PLAY)
2126 struct skin_albumart *aa = wps_data->albumart;
2127 if (aa && ((aa->state && !old_aa.state) ||
2128 (aa->state &&
2129 (((old_aa.height != aa->height) ||
2130 (old_aa.width != aa->width))))))
2132 curtrack = audio_current_track();
2133 offset = curtrack->offset;
2134 audio_stop();
2135 if (!(status & AUDIO_STATUS_PAUSE))
2136 audio_play(offset);
2139 #endif
2140 #if defined(DEBUG) || defined(SIMULATOR)
2141 debug_skin_usage();
2142 #endif
2143 return true;