get it working with images
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobd7700ecec728f79ba54a3c1075e334f1c963e9df
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "config.h"
26 #include "file.h"
27 #include "misc.h"
28 #include "plugin.h"
29 #include "viewport.h"
31 #ifdef __PCTOOL__
32 #ifdef WPSEDITOR
33 #include "proxy.h"
34 #include "sysfont.h"
35 #else
36 #include "action.h"
37 #include "checkwps.h"
38 #include "audio.h"
39 #define lang_is_rtl() (false)
40 #define DEBUGF printf
41 #endif /*WPSEDITOR*/
42 #else
43 #include "debug.h"
44 #include "language.h"
45 #endif /*__PCTOOL__*/
47 #include <ctype.h>
48 #include <stdbool.h>
49 #include "font.h"
51 #include "wps_internals.h"
52 #include "skin_engine.h"
53 #include "settings.h"
54 #include "settings_list.h"
55 #include "skin_fonts.h"
57 #ifdef HAVE_LCD_BITMAP
58 #include "bmp.h"
59 #endif
61 #ifdef HAVE_ALBUMART
62 #include "playback.h"
63 #endif
65 #include "backdrop.h"
66 #include "statusbar-skinned.h"
68 #define WPS_ERROR_INVALID_PARAM -1
70 /* which screen are we parsing for? */
71 static enum screen_type curr_screen;
73 /* level of current conditional.
74 -1 means we're not in a conditional. */
75 static int level = -1;
77 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
78 or WPS_TOKEN_CONDITIONAL_START in current level */
79 static int lastcond[WPS_MAX_COND_LEVEL];
81 /* index of the WPS_TOKEN_CONDITIONAL in current level */
82 static int condindex[WPS_MAX_COND_LEVEL];
84 /* number of condtional options in current level */
85 static int numoptions[WPS_MAX_COND_LEVEL];
87 /* line number, debug only */
88 static int line_number;
90 /* the current viewport */
91 static struct skin_viewport *curr_vp;
92 /* the current line, linked to the above viewport */
93 static struct skin_line *curr_line;
95 static size_t curr_size;
96 static bool single_pass;
98 static int follow_lang_direction = 0;
100 #if defined(DEBUG) || defined(SIMULATOR)
101 /* debugging function */
102 extern void print_debug_info(struct wps_data *data, int fail, int line);
103 extern void debug_skin_usage(void);
104 #endif
106 /* Function for parsing of details for a token. At the moment the
107 function is called, the token type has already been set. The
108 function must fill in the details and possibly add more tokens
109 to the token array. It should return the number of chars that
110 has been consumed.
112 wps_bufptr points to the char following the tag (i.e. where
113 details begin).
114 token is the pointer to the 'main' token being parsed
116 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
117 struct wps_token *token, struct wps_data *wps_data);
119 struct wps_tag {
120 enum wps_token_type type;
121 const char name[3];
122 unsigned char refresh_type;
123 const wps_tag_parse_func parse_func;
125 static int skip_end_of_line(const char *wps_bufptr);
126 /* prototypes of all special parse functions : */
127 static int parse_timeout(const char *wps_bufptr,
128 struct wps_token *token, struct wps_data *wps_data);
129 static int parse_progressbar(const char *wps_bufptr,
130 struct wps_token *token, struct wps_data *wps_data);
131 static int parse_dir_level(const char *wps_bufptr,
132 struct wps_token *token, struct wps_data *wps_data);
133 static int parse_setting_and_lang(const char *wps_bufptr,
134 struct wps_token *token, struct wps_data *wps_data);
137 static int parse_languagedirection(const char *wps_bufptr,
138 struct wps_token *token, struct wps_data *wps_data)
140 (void)wps_bufptr;
141 (void)token;
142 (void)wps_data;
143 follow_lang_direction = 2; /* 2 because it is decremented immediatly after
144 this token is parsed, after the next token it
145 will be 0 again. */
146 return 0;
149 #ifdef HAVE_LCD_BITMAP
150 static int parse_viewport_display(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 static int parse_playlistview(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 static int parse_viewport(const char *wps_bufptr,
155 struct wps_token *token, struct wps_data *wps_data);
156 static int parse_statusbar_enable(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 static int parse_statusbar_disable(const char *wps_bufptr,
159 struct wps_token *token, struct wps_data *wps_data);
160 static int parse_statusbar_inbuilt(const char *wps_bufptr,
161 struct wps_token *token, struct wps_data *wps_data);
162 static int parse_image_display(const char *wps_bufptr,
163 struct wps_token *token, struct wps_data *wps_data);
164 static int parse_image_load(const char *wps_bufptr,
165 struct wps_token *token, struct wps_data *wps_data);
166 static int parse_font_load(const char *wps_bufptr,
167 struct wps_token *token, struct wps_data *wps_data);
168 #endif /*HAVE_LCD_BITMAP */
169 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
170 static int parse_image_special(const char *wps_bufptr,
171 struct wps_token *token, struct wps_data *wps_data);
172 #endif
173 #ifdef HAVE_ALBUMART
174 static int parse_albumart_load(const char *wps_bufptr,
175 struct wps_token *token, struct wps_data *wps_data);
176 static int parse_albumart_display(const char *wps_bufptr,
177 struct wps_token *token, struct wps_data *wps_data);
178 #endif /* HAVE_ALBUMART */
179 #ifdef HAVE_TOUCHSCREEN
180 static int parse_touchregion(const char *wps_bufptr,
181 struct wps_token *token, struct wps_data *wps_data);
182 #else
183 static int fulline_tag_not_supported(const char *wps_bufptr,
184 struct wps_token *token, struct wps_data *wps_data)
186 (void)token; (void)wps_data;
187 return skip_end_of_line(wps_bufptr);
189 #define parse_touchregion fulline_tag_not_supported
190 #endif
191 #ifdef CONFIG_RTC
192 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
193 #else
194 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
195 #endif
197 /* array of available tags - those with more characters have to go first
198 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
199 static const struct wps_tag all_tags[] = {
201 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
202 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
203 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
204 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
205 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
206 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
208 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
209 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
210 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
211 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
212 #if CONFIG_CHARGING >= CHARGING_MONITOR
213 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
214 #endif
215 #if CONFIG_CHARGING
216 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
217 #endif
218 #ifdef HAVE_USB_POWER
219 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
220 #endif
222 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
224 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
225 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
226 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
236 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
237 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
238 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
239 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
240 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
242 /* current file */
243 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
253 parse_dir_level },
255 /* next file */
256 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
266 parse_dir_level },
268 /* current metadata */
269 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
276 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
277 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
278 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
282 /* next metadata */
283 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
284 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
290 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
292 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
293 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
294 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
296 #if (CONFIG_CODEC != MAS3507D)
297 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
298 #endif
299 #if (CONFIG_CODEC == SWCODEC)
300 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
301 #endif
302 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
303 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
304 #endif
306 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
308 #ifdef HAS_REMOTE_BUTTON_HOLD
309 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
310 #else
311 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
312 #endif
314 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
315 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
316 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
317 parse_timeout },
319 #ifdef HAVE_LCD_BITMAP
320 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
321 #else
322 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
323 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
324 #endif
325 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
326 parse_progressbar },
328 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
330 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
331 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
332 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
333 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
335 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
336 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
337 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
338 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
340 #ifdef HAVE_TAGCACHE
341 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
342 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
343 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
344 #endif
346 #if CONFIG_CODEC == SWCODEC
347 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
348 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
349 #endif
351 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
352 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
354 #ifdef HAVE_LCD_BITMAP
355 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
356 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
357 { WPS_TOKEN_DRAW_INBUILTBAR, "wi", WPS_REFRESH_DYNAMIC, parse_statusbar_inbuilt },
359 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
361 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
362 parse_image_display },
364 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
365 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
366 #ifdef HAVE_ALBUMART
367 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
368 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
369 #endif
371 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
372 parse_viewport_display },
373 #ifdef HAVE_LCD_BITMAP
374 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
375 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
376 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
377 #endif
378 { WPS_NO_TOKEN, "V", 0, parse_viewport },
380 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
381 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
382 #endif
383 #endif
385 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
386 parse_setting_and_lang },
387 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
388 parse_setting_and_lang },
389 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
391 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
392 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
393 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
396 /* Recording Tokens */
397 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
398 #ifdef HAVE_RECORDING
399 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
400 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
401 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
402 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
403 #endif
404 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
405 /* the array MUST end with an empty string (first char is \0) */
409 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
410 * chains require the order to be kept.
412 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
414 if (*list == NULL)
415 *list = item;
416 else
418 struct skin_token_list *t = *list;
419 while (t->next)
420 t = t->next;
421 t->next = item;
425 /* traverse the image linked-list for an image */
426 #ifdef HAVE_LCD_BITMAP
427 struct gui_img* find_image(char label, struct wps_data *data)
429 struct skin_token_list *list = data->images;
430 while (list)
432 struct gui_img *img = (struct gui_img *)list->token->value.data;
433 if (img->label == label)
434 return img;
435 list = list->next;
437 return NULL;
440 #endif
442 /* traverse the viewport linked list for a viewport */
443 struct skin_viewport* find_viewport(char label, struct wps_data *data)
445 struct skin_token_list *list = data->viewports;
446 while (list)
448 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
449 if (vp->label == label)
450 return vp;
451 list = list->next;
453 return NULL;
457 /* create and init a new wpsll item.
458 * passing NULL to token will alloc a new one.
459 * You should only pass NULL for the token when the token type (table above)
460 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
462 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
463 void* token_data)
465 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
466 if (!token)
467 token = skin_buffer_alloc(sizeof(struct wps_token));
468 if (!llitem || !token)
469 return NULL;
470 llitem->next = NULL;
471 llitem->token = token;
472 if (token_data)
473 llitem->token->value.data = token_data;
474 return llitem;
477 /* Returns the number of chars that should be skipped to jump
478 immediately after the first eol, i.e. to the start of the next line */
479 static int skip_end_of_line(const char *wps_bufptr)
481 line_number++;
482 int skip = 0;
483 while(*(wps_bufptr + skip) != '\n')
484 skip++;
485 return ++skip;
488 /* Starts a new subline in the current line during parsing */
489 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
491 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
492 if (!subline)
493 return false;
495 subline->first_token_idx = curr_token;
496 subline->next = NULL;
498 subline->line_type = 0;
499 subline->time_mult = 0;
501 line->curr_subline->last_token_idx = curr_token-1;
502 line->curr_subline->next = subline;
503 line->curr_subline = subline;
504 return true;
507 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
509 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
510 struct skin_subline *subline = NULL;
511 if (!line)
512 return false;
514 /* init the subline */
515 subline = &line->sublines;
516 subline->first_token_idx = curr_token;
517 subline->next = NULL;
518 subline->line_type = 0;
519 subline->time_mult = 0;
521 /* init the new line */
522 line->curr_subline = &line->sublines;
523 line->next = NULL;
524 line->subline_expire_time = 0;
526 /* connect to curr_line and vp pointers.
527 * 1) close the previous lines subline
528 * 2) connect to vp pointer
529 * 3) connect to curr_line global pointer
531 if (curr_line)
533 curr_line->curr_subline->last_token_idx = curr_token - 1;
534 curr_line->next = line;
535 curr_line->curr_subline = NULL;
537 curr_line = line;
538 if (!vp->lines)
539 vp->lines = line;
540 return true;
543 #ifdef HAVE_LCD_BITMAP
545 static int parse_statusbar_enable(const char *wps_bufptr,
546 struct wps_token *token,
547 struct wps_data *wps_data)
549 (void)token; /* Kill warnings */
550 wps_data->wps_sb_tag = true;
551 wps_data->show_sb_on_wps = true;
552 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
553 viewport_set_defaults(&default_vp->vp, curr_screen);
554 default_vp->vp.font = FONT_UI;
555 return skip_end_of_line(wps_bufptr);
558 static int parse_statusbar_disable(const char *wps_bufptr,
559 struct wps_token *token,
560 struct wps_data *wps_data)
562 (void)token; /* Kill warnings */
563 wps_data->wps_sb_tag = true;
564 wps_data->show_sb_on_wps = false;
565 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
566 viewport_set_fullscreen(&default_vp->vp, curr_screen);
567 default_vp->vp.font = FONT_UI;
568 return skip_end_of_line(wps_bufptr);
571 static int parse_statusbar_inbuilt(const char *wps_bufptr,
572 struct wps_token *token, struct wps_data *wps_data)
574 (void)wps_data;
575 token->value.data = (void*)&curr_vp->vp;
576 return skip_end_of_line(wps_bufptr);
579 static int get_image_id(int c)
581 if(c >= 'a' && c <= 'z')
582 return c - 'a';
583 else if(c >= 'A' && c <= 'Z')
584 return c - 'A' + 26;
585 else
586 return -1;
589 char *get_image_filename(const char *start, const char* bmpdir,
590 char *buf, int buf_size)
592 size_t len = strlcpy(buf, bmpdir, buf_size);
593 buf[len] = '/';
594 buf[len+1] = '\0';
595 strlcat(buf, start, buf_size-(len-1));
596 return buf;
599 static int parse_image_display(const char *wps_bufptr,
600 struct wps_token *token,
601 struct wps_data *wps_data)
603 char label = wps_bufptr[0];
604 int subimage;
605 struct gui_img *img;;
607 /* sanity check */
608 img = find_image(label, wps_data);
609 if (!img)
611 token->value.i = label; /* so debug works */
612 return WPS_ERROR_INVALID_PARAM;
615 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
617 if (subimage >= img->num_subimages)
618 return WPS_ERROR_INVALID_PARAM;
620 /* Store sub-image number to display in high bits */
621 token->value.i = label | (subimage << 8);
622 return 2; /* We have consumed 2 bytes */
623 } else {
624 token->value.i = label;
625 return 1; /* We have consumed 1 byte */
629 static int parse_image_load(const char *wps_bufptr,
630 struct wps_token *token,
631 struct wps_data *wps_data)
633 const char *ptr = wps_bufptr;
634 const char *pos;
635 const char* filename;
636 const char* id;
637 const char *newline;
638 int x,y;
639 struct gui_img *img;
641 /* format: %x|n|filename.bmp|x|y|
642 or %xl|n|filename.bmp|x|y|
643 or %xl|n|filename.bmp|x|y|num_subimages|
646 if (*ptr != '|')
647 return WPS_ERROR_INVALID_PARAM;
649 ptr++;
651 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
652 return WPS_ERROR_INVALID_PARAM;
654 /* Check there is a terminating | */
655 if (*ptr != '|')
656 return WPS_ERROR_INVALID_PARAM;
658 /* check the image number and load state */
659 if(find_image(*id, wps_data))
661 /* Invalid image ID */
662 return WPS_ERROR_INVALID_PARAM;
664 img = skin_buffer_alloc(sizeof(struct gui_img));
665 if (!img)
666 return WPS_ERROR_INVALID_PARAM;
667 /* save a pointer to the filename */
668 size_t len = strchr(filename, '|') - filename + 1;
669 img->bm.data = skin_buffer_alloc(len+1);
670 if (!img->bm.data)
671 return WPS_ERROR_INVALID_PARAM;
672 strlcpy(img->bm.data, filename, len);
674 printf("filename: %s\n", img->bm.data);
676 img->label = *id;
677 img->x = x;
678 img->y = y;
679 img->num_subimages = 1;
680 img->always_display = false;
682 /* save current viewport */
683 img->vp = &curr_vp->vp;
685 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
687 img->always_display = true;
689 else
691 /* Parse the (optional) number of sub-images */
692 ptr++;
693 newline = strchr(ptr, '\n');
694 pos = strchr(ptr, '|');
695 if (pos && pos < newline)
696 img->num_subimages = atoi(ptr);
698 if (img->num_subimages <= 0)
699 return WPS_ERROR_INVALID_PARAM;
701 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
702 if (!item)
703 return WPS_ERROR_INVALID_PARAM;
704 add_to_ll_chain(&wps_data->images, item);
706 /* Skip the rest of the line */
707 return skip_end_of_line(wps_bufptr);
709 struct skin_font {
710 int id; /* the id from font_load */
711 char *name; /* filename without path and extension */
713 static struct skin_font skinfonts[MAXUSERFONTS];
714 static int parse_font_load(const char *wps_bufptr,
715 struct wps_token *token, struct wps_data *wps_data)
717 (void)wps_data; (void)token;
718 const char *ptr = wps_bufptr;
719 int id;
720 char *filename;
722 if (*ptr != '|')
723 return WPS_ERROR_INVALID_PARAM;
725 ptr++;
727 if (!(ptr = parse_list("ds", NULL, '|', ptr, &id, &filename)))
728 return WPS_ERROR_INVALID_PARAM;
730 /* Check there is a terminating | */
731 if (*ptr != '|')
732 return WPS_ERROR_INVALID_PARAM;
734 if (id <= FONT_UI || id >= MAXFONTS-1)
735 return WPS_ERROR_INVALID_PARAM;
736 #if defined(DEBUG) || defined(SIMULATOR)
737 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
739 DEBUGF("font id %d already being used\n", id);
741 #endif
742 /* make sure the filename contains .fnt,
743 * we dont actually use it, but require it anyway */
744 ptr = strchr(filename, '.');
745 if (!ptr || strncmp(ptr, ".fnt|", 5))
746 return WPS_ERROR_INVALID_PARAM;
747 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
748 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
750 return skip_end_of_line(wps_bufptr);
753 static int parse_viewport_display(const char *wps_bufptr,
754 struct wps_token *token,
755 struct wps_data *wps_data)
757 (void)wps_data;
758 char letter = wps_bufptr[0];
760 if (letter < 'a' || letter > 'z')
762 /* invalid viewport tag */
763 return WPS_ERROR_INVALID_PARAM;
765 token->value.i = letter;
766 return 1;
769 #ifdef HAVE_LCD_BITMAP
770 static int parse_playlistview_text(struct playlistviewer *viewer,
771 enum info_line_type line, char* text)
773 int cur_string = 0;
774 const struct wps_tag *tag;
775 int taglen = 0;
776 const char *start = text;
777 if (*text != '|')
778 return -1;
779 text++;
780 viewer->lines[line].count = 0;
781 viewer->lines[line].scroll = false;
782 while (*text != '|')
784 if (*text == '%') /* it is a token of some type */
786 text++;
787 taglen = 0;
788 switch(*text)
790 case '%':
791 case '<':
792 case '|':
793 case '>':
794 case ';':
795 case '#':
796 /* escaped characters */
797 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
798 viewer->lines[line].strings[cur_string][0] = *text;
799 viewer->lines[line].strings[cur_string++][1] = '\0';
800 break;
801 default:
802 for (tag = all_tags;
803 strncmp(text, tag->name, strlen(tag->name)) != 0;
804 tag++) ;
805 /* %s isnt stored as a tag so manually check for it */
806 if (tag->type == WPS_NO_TOKEN)
808 if (!strncmp(tag->name, "s", 1))
810 viewer->lines[line].scroll = true;
811 taglen = 1;
814 else if (tag->type == WPS_TOKEN_UNKNOWN)
816 int i = 0;
817 /* just copy the string */
818 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
819 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
821 viewer->lines[line].strings[cur_string][i] = text[i];
822 i++;
824 viewer->lines[line].strings[cur_string][i] = '\0';
825 cur_string++;
826 taglen = i;
828 else
830 if (tag->parse_func)
832 /* unsupported tag, reject */
833 return -1;
835 taglen = strlen(tag->name);
836 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
838 text += taglen;
841 else
843 /* regular string */
844 int i = 0;
845 /* just copy the string */
846 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
847 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
849 viewer->lines[line].strings[cur_string][i] = text[i];
850 i++;
852 viewer->lines[line].strings[cur_string][i] = '\0';
853 cur_string++;
854 text += i;
857 return text - start;
861 static int parse_playlistview(const char *wps_bufptr,
862 struct wps_token *token, struct wps_data *wps_data)
864 (void)wps_data;
865 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
866 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
867 char *ptr = strchr(wps_bufptr, '|');
868 int length;
869 if (!viewer || !ptr)
870 return WPS_ERROR_INVALID_PARAM;
871 viewer->vp = &curr_vp->vp;
872 viewer->show_icons = true;
873 viewer->start_offset = atoi(ptr+1);
874 token->value.data = (void*)viewer;
875 ptr = strchr(ptr+1, '|');
876 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
877 if (length < 0)
878 return WPS_ERROR_INVALID_PARAM;
879 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
880 if (length < 0)
881 return WPS_ERROR_INVALID_PARAM;
883 return skip_end_of_line(wps_bufptr);
885 #endif
887 static int parse_viewport(const char *wps_bufptr,
888 struct wps_token *token,
889 struct wps_data *wps_data)
891 (void)token; /* Kill warnings */
892 const char *ptr = wps_bufptr;
894 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
896 /* check for the optional letter to signify its a hideable viewport */
897 /* %Vl|<label>|<rest of tags>| */
898 skin_vp->hidden_flags = 0;
899 skin_vp->label = VP_NO_LABEL;
900 skin_vp->pb = NULL;
901 skin_vp->lines = NULL;
902 if (curr_line)
904 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
905 - (wps_data->num_tokens > 0 ? 1 : 0);
908 curr_line = NULL;
909 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
910 return WPS_ERROR_INVALID_PARAM;
912 if (*ptr == 'i')
914 skin_vp->label = VP_INFO_LABEL;
915 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
916 ++ptr;
918 else if (*ptr == 'l')
920 if (*(ptr+1) == '|')
922 char label = *(ptr+2);
923 if (label >= 'a' && label <= 'z')
925 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
926 skin_vp->label = label;
928 else
929 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
930 ptr += 3;
933 if (*ptr != '|')
934 return WPS_ERROR_INVALID_PARAM;
936 ptr++;
937 struct viewport *vp = &skin_vp->vp;
938 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
939 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
940 return WPS_ERROR_INVALID_PARAM;
942 /* Check for trailing | */
943 if (*ptr != '|')
944 return WPS_ERROR_INVALID_PARAM;
946 if (follow_lang_direction && lang_is_rtl())
948 vp->flags |= VP_FLAG_ALIGN_RIGHT;
949 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
951 else
952 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
954 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
955 if (!list)
956 return WPS_ERROR_INVALID_PARAM;
957 add_to_ll_chain(&wps_data->viewports, list);
958 curr_vp = skin_vp;
959 /* Skip the rest of the line */
960 return skip_end_of_line(wps_bufptr);
963 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
964 static int parse_image_special(const char *wps_bufptr,
965 struct wps_token *token,
966 struct wps_data *wps_data)
968 (void)wps_data; /* kill warning */
969 (void)token;
970 const char *pos = NULL;
971 const char *newline;
972 bool error = false;
974 pos = strchr(wps_bufptr + 1, '|');
975 newline = strchr(wps_bufptr, '\n');
977 error = (pos > newline);
979 #if LCD_DEPTH > 1
980 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
982 /* format: %X|filename.bmp| or %Xd */
983 if (*(wps_bufptr) == 'd')
985 wps_data->backdrop = NULL;
986 return skip_end_of_line(wps_bufptr);
988 else if (!error)
990 const char* buf = wps_bufptr + 1;
991 size_t len = strchr(buf, '|') - buf + 1;
992 char *fn = skin_buffer_alloc(len+1);
993 strlcpy(fn, buf, len);
994 wps_data->backdrop = fn;
997 #endif
998 if (error)
999 return WPS_ERROR_INVALID_PARAM;
1000 /* Skip the rest of the line */
1001 return skip_end_of_line(wps_bufptr);
1003 #endif
1005 #endif /* HAVE_LCD_BITMAP */
1007 static int parse_setting_and_lang(const char *wps_bufptr,
1008 struct wps_token *token,
1009 struct wps_data *wps_data)
1011 /* NOTE: both the string validations that happen in here will
1012 * automatically PASS on checkwps because its too hard to get
1013 * settings_list.c and englinsh.lang built for it.
1014 * If that ever changes remove the #ifndef __PCTOOL__'s here
1016 (void)wps_data;
1017 const char *ptr = wps_bufptr;
1018 const char *end;
1019 int i = 0;
1020 char temp[64];
1022 /* Find the setting's cfg_name */
1023 if (*ptr != '|')
1024 return WPS_ERROR_INVALID_PARAM;
1025 ptr++;
1026 end = strchr(ptr,'|');
1027 if (!end)
1028 return WPS_ERROR_INVALID_PARAM;
1029 strlcpy(temp, ptr,end-ptr+1);
1031 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1033 #ifndef __PCTOOL__
1034 i = lang_english_to_id(temp);
1035 if (i < 0)
1036 return WPS_ERROR_INVALID_PARAM;
1037 #endif
1039 else
1041 /* Find the setting */
1042 for (i=0; i<nb_settings; i++)
1043 if (settings[i].cfg_name &&
1044 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1045 /* prevent matches on cfg_name prefixes */
1046 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1047 break;
1048 #ifndef __PCTOOL__
1049 if (i == nb_settings)
1050 return WPS_ERROR_INVALID_PARAM;
1051 #endif
1053 /* Store the setting number */
1054 token->value.i = i;
1056 /* Skip the rest of the line */
1057 return end-ptr+2;
1061 static int parse_dir_level(const char *wps_bufptr,
1062 struct wps_token *token,
1063 struct wps_data *wps_data)
1065 char val[] = { *wps_bufptr, '\0' };
1066 token->value.i = atoi(val);
1067 (void)wps_data; /* Kill warnings */
1068 return 1;
1071 static int parse_timeout(const char *wps_bufptr,
1072 struct wps_token *token,
1073 struct wps_data *wps_data)
1075 int skip = 0;
1076 int val = 0;
1077 bool have_point = false;
1078 bool have_tenth = false;
1080 (void)wps_data; /* Kill the warning */
1082 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1084 if (*wps_bufptr != '.')
1086 val *= 10;
1087 val += *wps_bufptr - '0';
1088 if (have_point)
1090 have_tenth = true;
1091 wps_bufptr++;
1092 skip++;
1093 break;
1096 else
1097 have_point = true;
1099 wps_bufptr++;
1100 skip++;
1103 if (have_tenth == false)
1104 val *= 10;
1106 if (val == 0 && skip == 0)
1108 /* decide what to do if no value was specified */
1109 switch (token->type)
1111 case WPS_TOKEN_SUBLINE_TIMEOUT:
1112 return -1;
1113 case WPS_TOKEN_BUTTON_VOLUME:
1114 val = 10;
1115 break;
1118 token->value.i = val;
1120 return skip;
1123 static int parse_progressbar(const char *wps_bufptr,
1124 struct wps_token *token,
1125 struct wps_data *wps_data)
1127 /* %pb or %pb|filename|x|y|width|height|
1128 using - for any of the params uses "sane" values */
1129 #ifdef HAVE_LCD_BITMAP
1130 enum {
1131 PB_FILENAME = 0,
1132 PB_X,
1133 PB_Y,
1134 PB_WIDTH,
1135 PB_HEIGHT
1137 const char *filename;
1138 int x, y, height, width;
1139 uint32_t set = 0;
1140 const char *ptr = wps_bufptr;
1141 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1142 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1144 if (!pb || !item)
1145 return WPS_ERROR_INVALID_PARAM;
1147 struct viewport *vp = &curr_vp->vp;
1148 /* we need to know what line number (viewport relative) this pb is,
1149 * so count them... */
1150 int line_num = -1;
1151 struct skin_line *line = curr_vp->lines;
1152 while (line)
1154 line_num++;
1155 line = line->next;
1157 pb->have_bitmap_pb = false;
1158 pb->bm.data = NULL; /* no bitmap specified */
1159 pb->follow_lang_direction = follow_lang_direction > 0;
1161 if (*wps_bufptr != '|') /* regular old style */
1163 pb->x = 0;
1164 pb->width = vp->width;
1165 pb->height = SYSFONT_HEIGHT-2;
1166 pb->y = -line_num - 1; /* Will be computed during the rendering */
1168 curr_vp->pb = pb;
1169 add_to_ll_chain(&wps_data->progressbars, item);
1170 return 0;
1172 ptr = wps_bufptr + 1;
1174 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1175 &x, &y, &width, &height)))
1176 return WPS_ERROR_INVALID_PARAM;
1178 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1180 size_t len = strchr(filename, '|') - filename + 1;
1181 pb->bm.data = skin_buffer_alloc(len+1);
1182 if (!pb->bm.data)
1183 return WPS_ERROR_INVALID_PARAM;
1184 strlcpy(pb->bm.data, filename, len);
1187 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1188 pb->x = x;
1189 else
1190 pb->x = vp->x;
1192 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1194 /* A zero width causes a divide-by-zero error later, so reject it */
1195 if (width == 0)
1196 return WPS_ERROR_INVALID_PARAM;
1198 pb->width = width;
1200 else
1201 pb->width = vp->width - pb->x;
1203 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1205 /* A zero height makes no sense - reject it */
1206 if (height == 0)
1207 return WPS_ERROR_INVALID_PARAM;
1209 pb->height = height;
1211 else
1213 if (vp->font > FONT_UI)
1214 pb->height = -1; /* calculate at display time */
1215 else
1217 #ifndef __PCTOOL__
1218 pb->height = font_get(vp->font)->height;
1219 #else
1220 pb->height = 8;
1221 #endif
1225 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1226 pb->y = y;
1227 else
1228 pb->y = -line_num - 1; /* Will be computed during the rendering */
1230 curr_vp->pb = pb;
1231 add_to_ll_chain(&wps_data->progressbars, item);
1233 /* Skip the rest of the line */
1234 return skip_end_of_line(wps_bufptr)-1;
1235 #else
1236 (void)token;
1238 if (*(wps_bufptr-1) == 'f')
1239 wps_data->full_line_progressbar = true;
1240 else
1241 wps_data->full_line_progressbar = false;
1243 return 0;
1245 #endif
1248 #ifdef HAVE_ALBUMART
1249 static int parse_int(const char *newline, const char **_pos, int *num)
1251 *_pos = parse_list("d", NULL, '|', *_pos, num);
1253 return (!*_pos || *_pos > newline || **_pos != '|');
1256 static int parse_albumart_load(const char *wps_bufptr,
1257 struct wps_token *token,
1258 struct wps_data *wps_data)
1260 const char *_pos, *newline;
1261 bool parsing;
1262 struct dim dimensions;
1263 int albumart_slot;
1264 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1265 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1266 (void)token; /* silence warning */
1267 if (!aa)
1268 return skip_end_of_line(wps_bufptr);
1270 /* reset albumart info in wps */
1271 aa->width = -1;
1272 aa->height = -1;
1273 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1274 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1275 aa->vp = &curr_vp->vp;
1277 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1279 newline = strchr(wps_bufptr, '\n');
1281 _pos = wps_bufptr;
1283 if (*_pos != '|')
1284 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1286 ++_pos;
1288 /* initial validation and parsing of x component */
1289 if (parse_int(newline, &_pos, &aa->x))
1290 return WPS_ERROR_INVALID_PARAM;
1292 ++_pos;
1294 /* initial validation and parsing of y component */
1295 if (parse_int(newline, &_pos, &aa->y))
1296 return WPS_ERROR_INVALID_PARAM;
1298 /* parsing width field */
1299 parsing = true;
1300 while (parsing)
1302 /* apply each modifier in turn */
1303 ++_pos;
1304 switch (*_pos)
1306 case 'l':
1307 case 'L':
1308 case '+':
1309 if (swap_for_rtl)
1310 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1311 else
1312 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1313 break;
1314 case 'c':
1315 case 'C':
1316 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1317 break;
1318 case 'r':
1319 case 'R':
1320 case '-':
1321 if (swap_for_rtl)
1322 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1323 else
1324 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1325 break;
1326 case 'd':
1327 case 'D':
1328 case 'i':
1329 case 'I':
1330 case 's':
1331 case 'S':
1332 /* simply ignored */
1333 break;
1334 default:
1335 parsing = false;
1336 break;
1339 /* extract max width data */
1340 if (*_pos != '|')
1342 if (parse_int(newline, &_pos, &aa->width))
1343 return WPS_ERROR_INVALID_PARAM;
1346 /* parsing height field */
1347 parsing = true;
1348 while (parsing)
1350 /* apply each modifier in turn */
1351 ++_pos;
1352 switch (*_pos)
1354 case 't':
1355 case 'T':
1356 case '-':
1357 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1358 break;
1359 case 'c':
1360 case 'C':
1361 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1362 break;
1363 case 'b':
1364 case 'B':
1365 case '+':
1366 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1367 break;
1368 case 'd':
1369 case 'D':
1370 case 'i':
1371 case 'I':
1372 case 's':
1373 case 'S':
1374 /* simply ignored */
1375 break;
1376 default:
1377 parsing = false;
1378 break;
1381 /* extract max height data */
1382 if (*_pos != '|')
1384 if (parse_int(newline, &_pos, &aa->height))
1385 return WPS_ERROR_INVALID_PARAM;
1388 /* if we got here, we parsed everything ok .. ! */
1389 if (aa->width < 0)
1390 aa->width = 0;
1391 else if (aa->width > LCD_WIDTH)
1392 aa->width = LCD_WIDTH;
1394 if (aa->height < 0)
1395 aa->height = 0;
1396 else if (aa->height > LCD_HEIGHT)
1397 aa->height = LCD_HEIGHT;
1399 if (swap_for_rtl)
1400 aa->x = LCD_WIDTH - (aa->x + aa->width);
1402 aa->state = WPS_ALBUMART_LOAD;
1403 aa->draw = false;
1404 wps_data->albumart = aa;
1406 dimensions.width = aa->width;
1407 dimensions.height = aa->height;
1409 albumart_slot = playback_claim_aa_slot(&dimensions);
1411 if (0 <= albumart_slot)
1412 wps_data->playback_aa_slot = albumart_slot;
1414 /* Skip the rest of the line */
1415 return skip_end_of_line(wps_bufptr);
1418 static int parse_albumart_display(const char *wps_bufptr,
1419 struct wps_token *token,
1420 struct wps_data *wps_data)
1422 (void)wps_bufptr;
1423 struct wps_token *prev = token-1;
1424 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1426 token->type = WPS_TOKEN_ALBUMART_FOUND;
1428 else if (wps_data->albumart)
1430 wps_data->albumart->vp = &curr_vp->vp;
1432 #if 0
1433 /* the old code did this so keep it here for now...
1434 * this is to allow the posibility to showing the next tracks AA! */
1435 if (wps_bufptr+1 == 'n')
1436 return 1;
1437 #endif
1438 return 0;
1440 #endif /* HAVE_ALBUMART */
1442 #ifdef HAVE_TOUCHSCREEN
1444 struct touchaction {const char* s; int action;};
1445 static const struct touchaction touchactions[] = {
1446 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1447 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1448 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1449 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1450 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1451 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1452 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1453 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1455 static int parse_touchregion(const char *wps_bufptr,
1456 struct wps_token *token, struct wps_data *wps_data)
1458 (void)token;
1459 unsigned i, imax;
1460 struct touchregion *region = NULL;
1461 const char *ptr = wps_bufptr;
1462 const char *action;
1463 const char pb_string[] = "progressbar";
1464 const char vol_string[] = "volume";
1465 int x,y,w,h;
1467 /* format: %T|x|y|width|height|action|
1468 * if action starts with & the area must be held to happen
1469 * action is one of:
1470 * play - play/pause playback
1471 * stop - stop playback, exit the wps
1472 * prev - prev track
1473 * next - next track
1474 * ffwd - seek forward
1475 * rwd - seek backwards
1476 * menu - go back to the main menu
1477 * browse - go back to the file/db browser
1478 * shuffle - toggle shuffle mode
1479 * repmode - cycle the repeat mode
1480 * quickscreen - go into the quickscreen
1481 * contextmenu - open the context menu
1482 * playlist - go into the playlist
1483 * pitch - go into the pitchscreen
1484 * volup - increase volume by one step
1485 * voldown - decrease volume by one step
1489 if (*ptr != '|')
1490 return WPS_ERROR_INVALID_PARAM;
1491 ptr++;
1493 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1494 return WPS_ERROR_INVALID_PARAM;
1496 /* Check there is a terminating | */
1497 if (*ptr != '|')
1498 return WPS_ERROR_INVALID_PARAM;
1500 region = skin_buffer_alloc(sizeof(struct touchregion));
1501 if (!region)
1502 return WPS_ERROR_INVALID_PARAM;
1504 /* should probably do some bounds checking here with the viewport... but later */
1505 region->action = ACTION_NONE;
1506 region->x = x;
1507 region->y = y;
1508 region->width = w;
1509 region->height = h;
1510 region->wvp = curr_vp;
1511 region->armed = false;
1513 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1514 && *(action + sizeof(pb_string)-1) == '|')
1515 region->type = WPS_TOUCHREGION_SCROLLBAR;
1516 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1517 && *(action + sizeof(vol_string)-1) == '|')
1518 region->type = WPS_TOUCHREGION_VOLUME;
1519 else
1521 region->type = WPS_TOUCHREGION_ACTION;
1523 if (*action == '&')
1525 action++;
1526 region->repeat = true;
1528 else
1529 region->repeat = false;
1531 i = 0;
1532 imax = ARRAYLEN(touchactions);
1533 while ((region->action == ACTION_NONE) &&
1534 (i < imax))
1536 /* try to match with one of our touchregion screens */
1537 int len = strlen(touchactions[i].s);
1538 if (!strncmp(touchactions[i].s, action, len)
1539 && *(action+len) == '|')
1540 region->action = touchactions[i].action;
1541 i++;
1543 if (region->action == ACTION_NONE)
1544 return WPS_ERROR_INVALID_PARAM;
1546 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1547 if (!item)
1548 return WPS_ERROR_INVALID_PARAM;
1549 add_to_ll_chain(&wps_data->touchregions, item);
1550 return skip_end_of_line(wps_bufptr);
1552 #endif
1554 /* Parse a generic token from the given string. Return the length read */
1555 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1557 int skip = 0, taglen = 0, ret;
1558 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1559 const struct wps_tag *tag;
1560 memset(token, 0, sizeof(*token));
1562 switch(*wps_bufptr)
1565 case '%':
1566 case '<':
1567 case '|':
1568 case '>':
1569 case ';':
1570 case '#':
1571 /* escaped characters */
1572 token->type = WPS_TOKEN_CHARACTER;
1573 token->value.c = *wps_bufptr;
1574 taglen = 1;
1575 wps_data->num_tokens++;
1576 break;
1578 case '?':
1579 /* conditional tag */
1580 token->type = WPS_TOKEN_CONDITIONAL;
1581 level++;
1582 condindex[level] = wps_data->num_tokens;
1583 numoptions[level] = 1;
1584 wps_data->num_tokens++;
1585 ret = parse_token(wps_bufptr + 1, wps_data);
1586 if (ret < 0) return ret;
1587 taglen = 1 + ret;
1588 break;
1590 default:
1591 /* find what tag we have */
1592 for (tag = all_tags;
1593 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1594 tag++) ;
1596 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1597 token->type = tag->type;
1598 curr_line->curr_subline->line_type |= tag->refresh_type;
1600 /* if the tag has a special parsing function, we call it */
1601 if (tag->parse_func)
1603 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1604 if (ret < 0) return ret;
1605 skip += ret;
1608 /* Some tags we don't want to save as tokens */
1609 if (tag->type == WPS_NO_TOKEN)
1610 break;
1612 /* tags that start with 'F', 'I' or 'D' are for the next file */
1613 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1614 *(tag->name) == 'D')
1615 token->next = true;
1617 wps_data->num_tokens++;
1618 break;
1621 skip += taglen;
1622 return skip;
1627 * Returns the number of bytes to skip the buf pointer to access the false
1628 * branch in a _binary_ conditional
1630 * That is:
1631 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1632 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1634 * depending on the features of a target it's not called from check_feature_tag,
1635 * hence the __attribute__ or it issues compiler warnings
1639 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1640 static int find_false_branch(const char *wps_bufptr)
1642 const char *buf = wps_bufptr;
1643 /* wps_bufptr is after the opening '<', hence level = 1*/
1644 int level = 1;
1645 char ch;
1648 ch = *buf;
1649 if (ch == '%')
1650 { /* filter out the characters we check later if they're printed
1651 * as literals */
1652 ch = *(++buf);
1653 if (ch == '<' || ch == '>' || ch == '|')
1654 continue;
1655 /* else: some tags/printed literals we skip over */
1657 else if (ch == '<') /* nested conditional */
1658 level++;
1659 else if (ch == '>')
1660 { /* closed our or a nested conditional,
1661 * do NOT skip over the '>' so that wps_parse() sees it for closing
1662 * if it is the closing one for our conditional */
1663 level--;
1665 else if (ch == '|' && level == 1)
1666 { /* we found our separator, point before and get out */
1667 break;
1669 /* if level is 0, we don't have a false branch */
1670 } while (level > 0 && *(++buf));
1672 return buf - wps_bufptr;
1676 * returns the number of bytes to get the appropriate branch of a binary
1677 * conditional
1679 * That means:
1680 * - if a feature is available, it returns 0 to not skip anything
1681 * - if the feature is not available, skip to the false branch and don't
1682 * parse the true branch at all
1684 * */
1685 static int check_feature_tag(const char *wps_bufptr, const int type)
1687 (void)wps_bufptr;
1688 switch (type)
1690 case WPS_TOKEN_RTC_PRESENT:
1691 #if CONFIG_RTC
1692 return 0;
1693 #else
1694 return find_false_branch(wps_bufptr);
1695 #endif
1696 case WPS_TOKEN_HAVE_RECORDING:
1697 #ifdef HAVE_RECORDING
1698 return 0;
1699 #else
1700 return find_false_branch(wps_bufptr);
1701 #endif
1702 default: /* not a tag we care about, just don't skip */
1703 return 0;
1708 /* Parses the WPS.
1709 data is the pointer to the structure where the parsed WPS should be stored.
1710 It is initialised.
1711 wps_bufptr points to the string containing the WPS tags */
1712 #define TOKEN_BLOCK_SIZE 128
1713 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1715 if (!data || !wps_bufptr || !*wps_bufptr)
1716 return false;
1717 enum wps_parse_error fail = PARSE_OK;
1718 int ret;
1719 int max_tokens = TOKEN_BLOCK_SIZE;
1720 size_t buf_free = 0;
1721 line_number = 0;
1722 level = -1;
1724 /* allocate enough RAM for a reasonable skin, grow as needed.
1725 * Free any used RAM before loading the images to be 100% RAM efficient */
1726 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1727 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1728 return false;
1729 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1730 data->num_tokens = 0;
1732 #if LCD_DEPTH > 1
1733 /* Backdrop defaults to the setting unless %X is used, so set it now */
1734 if (global_settings.backdrop_file[0]
1735 && (global_settings.backdrop_file[0] != '-'))
1737 data->backdrop = "-";
1739 #endif
1741 while (*wps_bufptr && !fail)
1743 if (follow_lang_direction)
1744 follow_lang_direction--;
1745 /* first make sure there is enough room for tokens */
1746 if (max_tokens <= data->num_tokens + 5)
1748 int extra_tokens = TOKEN_BLOCK_SIZE;
1749 size_t needed = extra_tokens * sizeof(struct wps_token);
1750 /* do some smarts here to grow the array a bit */
1751 if (skin_buffer_freespace() < needed)
1753 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1754 break;
1756 skin_buffer_increment(needed, false);
1757 max_tokens += extra_tokens;
1760 switch(*wps_bufptr++)
1763 /* Regular tag */
1764 case '%':
1765 if ((ret = parse_token(wps_bufptr, data)) < 0)
1767 fail = PARSE_FAIL_COND_INVALID_PARAM;
1768 break;
1770 else if (level >= WPS_MAX_COND_LEVEL - 1)
1772 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1773 break;
1775 wps_bufptr += ret;
1776 break;
1778 /* Alternating sublines separator */
1779 case ';':
1780 if (level >= 0) /* there are unclosed conditionals */
1782 fail = PARSE_FAIL_UNCLOSED_COND;
1783 break;
1786 if (!skin_start_new_subline(curr_line, data->num_tokens))
1787 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1789 break;
1791 /* Conditional list start */
1792 case '<':
1793 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1795 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1796 break;
1798 wps_bufptr += check_feature_tag(wps_bufptr,
1799 data->tokens[data->num_tokens-1].type);
1800 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1801 lastcond[level] = data->num_tokens++;
1802 break;
1804 /* Conditional list end */
1805 case '>':
1806 if (level < 0) /* not in a conditional, invalid char */
1808 fail = PARSE_FAIL_INVALID_CHAR;
1809 break;
1812 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1813 if (lastcond[level])
1814 data->tokens[lastcond[level]].value.i = data->num_tokens;
1815 else
1817 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1818 break;
1821 lastcond[level] = 0;
1822 data->num_tokens++;
1823 data->tokens[condindex[level]].value.i = numoptions[level];
1824 level--;
1825 break;
1827 /* Conditional list option */
1828 case '|':
1829 if (level < 0) /* not in a conditional, invalid char */
1831 fail = PARSE_FAIL_INVALID_CHAR;
1832 break;
1835 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1836 if (lastcond[level])
1837 data->tokens[lastcond[level]].value.i = data->num_tokens;
1838 else
1840 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1841 break;
1844 lastcond[level] = data->num_tokens;
1845 numoptions[level]++;
1846 data->num_tokens++;
1847 break;
1849 /* Comment */
1850 case '#':
1851 if (level >= 0) /* there are unclosed conditionals */
1853 fail = PARSE_FAIL_UNCLOSED_COND;
1854 break;
1857 wps_bufptr += skip_end_of_line(wps_bufptr);
1858 break;
1860 /* End of this line */
1861 case '\n':
1862 if (level >= 0) /* there are unclosed conditionals */
1864 fail = PARSE_FAIL_UNCLOSED_COND;
1865 break;
1867 /* add a new token for the \n so empty lines are correct */
1868 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1869 data->tokens[data->num_tokens].value.c = '\n';
1870 data->tokens[data->num_tokens].next = false;
1871 data->num_tokens++;
1873 if (!skin_start_new_line(curr_vp, data->num_tokens))
1875 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1876 break;
1878 line_number++;
1880 break;
1882 /* String */
1883 default:
1885 unsigned int len = 1;
1886 const char *string_start = wps_bufptr - 1;
1888 /* find the length of the string */
1889 while (*wps_bufptr && *wps_bufptr != '#' &&
1890 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1891 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1892 *wps_bufptr != '|' && *wps_bufptr != '\n')
1894 wps_bufptr++;
1895 len++;
1898 /* look if we already have that string */
1899 char *str;
1900 bool found = false;
1901 struct skin_token_list *list = data->strings;
1902 while (list)
1904 str = (char*)list->token->value.data;
1905 found = (strlen(str) == len &&
1906 strncmp(string_start, str, len) == 0);
1907 if (found)
1908 break; /* break here because the list item is
1909 used if its found */
1910 list = list->next;
1912 /* If a matching string is found, found is true and i is
1913 the index of the string. If not, found is false */
1915 if (!found)
1917 /* new string */
1918 str = (char*)skin_buffer_alloc(len+1);
1919 if (!str)
1921 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1922 break;
1924 strlcpy(str, string_start, len+1);
1925 struct skin_token_list *item =
1926 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1927 if(!item)
1929 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1930 break;
1932 add_to_ll_chain(&data->strings, item);
1934 else
1936 /* another occurrence of an existing string */
1937 data->tokens[data->num_tokens].value.data = list->token->value.data;
1939 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1940 data->num_tokens++;
1942 break;
1946 if (!fail && level >= 0) /* there are unclosed conditionals */
1947 fail = PARSE_FAIL_UNCLOSED_COND;
1949 if (*wps_bufptr && !fail)
1950 /* one of the limits of the while loop was exceeded */
1951 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1953 /* Success! */
1954 curr_line->curr_subline->last_token_idx = data->num_tokens;
1955 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1956 /* freeup unused tokens */
1957 skin_buffer_free_from_front(sizeof(struct wps_token)
1958 * (max_tokens - data->num_tokens));
1960 #if defined(DEBUG) || defined(SIMULATOR)
1961 if (debug)
1963 print_debug_info(data, fail, line_number);
1964 debug_skin_usage();
1966 #else
1967 (void)debug;
1968 #endif
1970 return (fail == 0);
1975 * initial setup of wps_data; does reset everything
1976 * except fields which need to survive, i.e.
1979 static void skin_data_reset(struct wps_data *wps_data)
1981 #ifdef HAVE_LCD_BITMAP
1982 wps_data->images = NULL;
1983 wps_data->progressbars = NULL;
1984 #endif
1985 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1986 wps_data->backdrop = NULL;
1987 #endif
1988 #ifdef HAVE_TOUCHSCREEN
1989 wps_data->touchregions = NULL;
1990 #endif
1991 wps_data->viewports = NULL;
1992 wps_data->strings = NULL;
1993 #ifdef HAVE_ALBUMART
1994 wps_data->albumart = NULL;
1995 if (wps_data->playback_aa_slot >= 0)
1997 playback_release_aa_slot(wps_data->playback_aa_slot);
1998 wps_data->playback_aa_slot = -1;
2000 #endif
2001 wps_data->tokens = NULL;
2002 wps_data->num_tokens = 0;
2004 #ifdef HAVE_LCD_BITMAP
2005 wps_data->peak_meter_enabled = false;
2006 wps_data->wps_sb_tag = false;
2007 wps_data->show_sb_on_wps = false;
2008 #else /* HAVE_LCD_CHARCELLS */
2009 /* progress bars */
2010 int i;
2011 for (i = 0; i < 8; i++)
2013 wps_data->wps_progress_pat[i] = 0;
2015 wps_data->full_line_progressbar = false;
2016 #endif
2017 wps_data->wps_loaded = false;
2020 #ifdef HAVE_LCD_BITMAP
2021 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2023 (void)wps_data; /* only needed for remote targets */
2024 char img_path[MAX_PATH];
2025 get_image_filename(bitmap->data, bmpdir,
2026 img_path, sizeof(img_path));
2028 /* load the image */
2029 int format;
2030 #ifdef HAVE_REMOTE_LCD
2031 if (curr_screen == SCREEN_REMOTE)
2032 format = FORMAT_ANY|FORMAT_REMOTE;
2033 else
2034 #endif
2035 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2037 if (!single_pass) /* we only want to know the size in the first pass */
2038 format |= FORMAT_RETURN_SIZE;
2040 size_t max_buf;
2041 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2042 unsigned char* old = bitmap->data;
2043 bitmap->data = imgbuf;
2044 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2046 if (ret > 0)
2048 if (single_pass)
2050 skin_buffer_increment(ret, true);
2052 else
2054 bitmap->data = old;
2056 printf("%s: %p:%s\n", __func__, bitmap->data, bitmap->data);
2058 curr_size += ret;
2060 return true;
2062 else
2064 /* Abort if we can't load an image */
2065 DEBUGF("Couldn't load '%s'\n", img_path);
2066 return false;
2070 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2072 struct skin_token_list *list;
2073 bool retval = true; /* return false if a single image failed to load */
2074 /* do the progressbars */
2075 list = wps_data->progressbars;
2076 while (list)
2078 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2079 if (pb->bm.data)
2081 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2082 if (!pb->have_bitmap_pb) /* no success */
2083 retval = false;
2085 list = list->next;
2087 /* regular images */
2088 list = wps_data->images;
2089 while (list)
2091 struct gui_img *img = (struct gui_img*)list->token->value.data;
2092 if (img->bm.data)
2095 printf("%s: %p - %p\n", __func__, list, img->bm.data);
2097 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2098 if (img->loaded)
2099 img->subimage_height = img->bm.height / img->num_subimages;
2100 else
2101 retval = false;
2103 list = list->next;
2107 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2108 /* Backdrop load scheme:
2109 * 1) %X|filename|
2110 * 2) load the backdrop from settings
2112 if (wps_data->backdrop)
2114 bool needed = wps_data->backdrop[0] != '-';
2116 /* fixme: this calculation currently doesn't take into consideration
2117 * that multiple skins can share a backdrop */
2118 if (!single_pass)
2120 #if HAVE_REMOTE_LCD
2121 if (curr_screen != SCREEN_MAIN)
2122 curr_size += REMOTE_LCD_BACKDROP_BYTES;
2123 else
2124 #endif
2125 curr_size += LCD_BACKDROP_BYTES;
2126 return retval;
2128 printf("%s: %p\n", __func__, wps_data->backdrop);
2129 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2130 bmpdir, curr_screen);
2131 if (!wps_data->backdrop && needed)
2132 retval = false;
2134 #endif /* has backdrop support */
2136 return retval;
2139 static bool skin_load_fonts(struct wps_data *data)
2141 /* don't spit out after the first failue to aid debugging */
2142 bool success = true;
2143 struct skin_token_list *vp_list;
2144 int font_id;
2145 /* walk though each viewport and assign its font */
2146 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2148 /* first, find the viewports that have a non-sys/ui-font font */
2149 struct skin_viewport *skin_vp =
2150 (struct skin_viewport*)vp_list->token->value.data;
2151 struct viewport *vp = &skin_vp->vp;
2154 if (vp->font <= FONT_UI)
2155 { /* the usual case -> built-in fonts */
2156 #ifdef HAVE_REMOTE_LCD
2157 if (vp->font == FONT_UI)
2158 vp->font += curr_screen;
2159 #endif
2160 continue;
2162 font_id = vp->font;
2164 /* now find the corresponding skin_font */
2165 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2166 if (!font->name)
2168 DEBUGF("font %d not specified\n", font_id);
2169 success = false;
2170 continue;
2173 /* load the font - will handle loading the same font again if
2174 * multiple viewports use the same */
2175 if (font->id < 0)
2177 char *dot = strchr(font->name, '.');
2178 *dot = '\0';
2179 font->id = skin_font_load(font->name);
2182 if (font->id < 0)
2184 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2185 font_id, font->name);
2186 success = false;
2187 continue;
2190 /* finally, assign the font_id to the viewport */
2191 vp->font = font->id;
2193 return success;
2196 #endif /* HAVE_LCD_BITMAP */
2198 /* to setup up the wps-data from a format-buffer (isfile = false)
2199 from a (wps-)file (isfile = true)*/
2201 void skin_data_load_finalize(struct wps_data *data, const char* buf)
2203 single_pass = true;
2204 /* get the bitmap dir */
2205 char bmpdir[MAX_PATH];
2206 char *dot = strrchr(buf, '.');
2208 strlcpy(bmpdir, buf, dot - buf + 1);
2209 /* load the bitmaps that were found by the parsing */
2210 if (!load_skin_bitmaps(data, bmpdir)) {
2211 skin_data_reset(data);
2212 data->wps_loaded = false;
2215 #if defined(DEBUG) || defined(SIMULATOR)
2216 debug_skin_usage();
2217 #endif
2221 int skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2222 const char *buf, bool isfile, bool _single_pass)
2224 if (!wps_data || !buf)
2225 return false;
2226 single_pass = _single_pass;
2227 curr_size = 0;
2228 size_t old_buf_usage = skin_buffer_usage();
2230 printf("single pass: %d\n", single_pass);
2232 #ifdef HAVE_ALBUMART
2233 int status;
2234 struct mp3entry *curtrack;
2235 long offset;
2236 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2237 if (wps_data->albumart)
2239 old_aa.state = wps_data->albumart->state;
2240 old_aa.height = wps_data->albumart->height;
2241 old_aa.width = wps_data->albumart->width;
2243 #endif
2244 #ifdef HAVE_LCD_BITMAP
2245 int i;
2246 for (i=0;i<MAXUSERFONTS;i++)
2248 skinfonts[i].id = -1;
2249 skinfonts[i].name = NULL;
2251 #endif
2252 #ifdef DEBUG_SKIN_ENGINE
2253 if (isfile && debug_wps)
2255 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2257 #endif
2259 skin_data_reset(wps_data);
2260 curr_screen = screen;
2262 /* alloc default viewport, will be fixed up later */
2263 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2264 if (!curr_vp)
2265 return 0;
2266 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2267 if (!list)
2268 return 0;
2269 add_to_ll_chain(&wps_data->viewports, list);
2272 /* Initialise the first (default) viewport */
2273 curr_vp->label = VP_DEFAULT_LABEL;
2274 curr_vp->pb = NULL;
2275 curr_vp->hidden_flags = 0;
2276 curr_vp->lines = NULL;
2278 viewport_set_defaults(&curr_vp->vp, screen);
2279 #ifdef HAVE_LCD_BITMAP
2280 curr_vp->vp.font = FONT_UI;
2281 #endif
2283 curr_line = NULL;
2284 if (!skin_start_new_line(curr_vp, 0))
2285 return 0;
2287 if (!isfile)
2289 if (wps_parse(wps_data, buf, false))
2291 #ifdef HAVE_LCD_BITMAP
2292 /* load the backdrop */
2293 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2294 skin_data_reset(wps_data);
2295 return 0;
2297 #endif
2298 goto end;
2300 return 0;
2302 else
2304 int fd = open_utf8(buf, O_RDONLY);
2306 if (fd < 0)
2307 return 0;
2309 /* get buffer space from the plugin buffer */
2310 size_t buffersize = 0;
2311 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2313 if (!wps_buffer)
2314 return 0;
2316 /* copy the file's content to the buffer for parsing,
2317 ensuring that every line ends with a newline char. */
2318 unsigned int start = 0;
2319 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2321 start += strlen(wps_buffer + start);
2322 if (start < buffersize - 1)
2324 wps_buffer[start++] = '\n';
2325 wps_buffer[start] = 0;
2329 close(fd);
2331 if (start <= 0)
2332 return 0;
2334 /* parse the WPS source */
2335 if (!wps_parse(wps_data, wps_buffer, true)) {
2336 skin_data_reset(wps_data);
2337 return 0;
2340 wps_data->wps_loaded = true;
2342 #ifdef HAVE_LCD_BITMAP
2343 /* get the bitmap dir */
2344 char bmpdir[MAX_PATH];
2345 char *dot = strrchr(buf, '.');
2347 strlcpy(bmpdir, buf, dot - buf + 1);
2348 /* load the bitmaps that were found by the parsing */
2349 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2350 skin_data_reset(wps_data);
2351 wps_data->wps_loaded = false;
2352 return 0;
2354 if (single_pass && !skin_load_fonts(wps_data))
2356 skin_data_reset(wps_data);
2357 wps_data->wps_loaded = false;
2358 return false;
2360 #endif
2361 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2362 status = audio_status();
2363 if (status & AUDIO_STATUS_PLAY)
2365 struct skin_albumart *aa = wps_data->albumart;
2366 if (aa && ((aa->state && !old_aa.state) ||
2367 (aa->state &&
2368 (((old_aa.height != aa->height) ||
2369 (old_aa.width != aa->width))))))
2371 curtrack = audio_current_track();
2372 offset = curtrack->offset;
2373 audio_stop();
2374 if (!(status & AUDIO_STATUS_PAUSE))
2375 audio_play(offset);
2378 #endif
2379 #if defined(DEBUG) || defined(SIMULATOR)
2380 debug_skin_usage();
2381 #endif
2382 end:
2383 //printf("buf %s\n, curr_size %d\n", buf, curr_size);
2384 return skin_buffer_usage() - old_buf_usage + curr_size;