cleanup statusbar+base skin relationship...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobb66af75cb503d7f6051ad4dcbdaca624475f7680
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 int follow_lang_direction = 0;
97 #if defined(DEBUG) || defined(SIMULATOR)
98 /* debugging function */
99 extern void print_debug_info(struct wps_data *data, int fail, int line);
100 extern void debug_skin_usage(void);
101 #endif
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
107 has been consumed.
109 wps_bufptr points to the char following the tag (i.e. where
110 details begin).
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
114 struct wps_token *token, struct wps_data *wps_data);
116 struct wps_tag {
117 enum wps_token_type type;
118 const char name[3];
119 unsigned char refresh_type;
120 const wps_tag_parse_func parse_func;
122 static int skip_end_of_line(const char *wps_bufptr);
123 /* prototypes of all special parse functions : */
124 static int parse_timeout(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126 static int parse_progressbar(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_dir_level(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_setting_and_lang(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_languagedirection(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data)
137 (void)wps_bufptr;
138 (void)token;
139 (void)wps_data;
140 follow_lang_direction = 2; /* 2 because it is decremented immediatly after
141 this token is parsed, after the next token it
142 will be 0 again. */
143 return 0;
146 #ifdef HAVE_LCD_BITMAP
147 static int parse_viewport_display(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 static int parse_playlistview(const char *wps_bufptr,
150 struct wps_token *token, struct wps_data *wps_data);
151 static int parse_viewport(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 static int parse_statusbar_enable(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 static int parse_statusbar_disable(const char *wps_bufptr,
156 struct wps_token *token, struct wps_data *wps_data);
157 static int parse_statusbar_inbuilt(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 static int parse_image_display(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data);
161 static int parse_image_load(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data);
163 static int parse_font_load(const char *wps_bufptr,
164 struct wps_token *token, struct wps_data *wps_data);
165 #endif /*HAVE_LCD_BITMAP */
166 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
167 static int parse_image_special(const char *wps_bufptr,
168 struct wps_token *token, struct wps_data *wps_data);
169 #endif
170 #ifdef HAVE_ALBUMART
171 static int parse_albumart_load(const char *wps_bufptr,
172 struct wps_token *token, struct wps_data *wps_data);
173 static int parse_albumart_display(const char *wps_bufptr,
174 struct wps_token *token, struct wps_data *wps_data);
175 #endif /* HAVE_ALBUMART */
176 #ifdef HAVE_TOUCHSCREEN
177 static int parse_touchregion(const char *wps_bufptr,
178 struct wps_token *token, struct wps_data *wps_data);
179 #else
180 static int fulline_tag_not_supported(const char *wps_bufptr,
181 struct wps_token *token, struct wps_data *wps_data)
183 (void)token; (void)wps_data;
184 return skip_end_of_line(wps_bufptr);
186 #define parse_touchregion fulline_tag_not_supported
187 #endif
188 #ifdef CONFIG_RTC
189 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
190 #else
191 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
192 #endif
194 /* array of available tags - those with more characters have to go first
195 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
196 static const struct wps_tag all_tags[] = {
198 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
199 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
200 { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL },
201 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
202 { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL },
203 { WPS_NO_TOKEN, "ax", 0, parse_languagedirection },
205 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
206 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
207 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
208 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
209 #if CONFIG_CHARGING >= CHARGING_MONITOR
210 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
211 #endif
212 #if CONFIG_CHARGING
213 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
214 #endif
215 #ifdef HAVE_USB_POWER
216 { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL },
217 #endif
219 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
221 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
222 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
223 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
224 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
225 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
226 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
227 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
228 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
229 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
230 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
231 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
232 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
233 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
234 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
235 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
236 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
237 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
239 /* current file */
240 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
250 parse_dir_level },
252 /* next file */
253 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
263 parse_dir_level },
265 /* current metadata */
266 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
276 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
277 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
279 /* next metadata */
280 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
283 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
284 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
285 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
286 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
287 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
288 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
290 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
293 #if (CONFIG_CODEC != MAS3507D)
294 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
295 #endif
296 #if (CONFIG_CODEC == SWCODEC)
297 { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL },
298 #endif
299 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
300 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
301 #endif
303 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
305 #ifdef HAS_REMOTE_BUTTON_HOLD
306 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
307 #else
308 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
309 #endif
311 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
312 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
313 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
314 parse_timeout },
316 #ifdef HAVE_LCD_BITMAP
317 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
318 #else
319 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
320 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
321 #endif
322 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
323 parse_progressbar },
325 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
327 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
328 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
329 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
330 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
332 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
333 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
334 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
335 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
337 #ifdef HAVE_TAGCACHE
338 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
339 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
340 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
341 #endif
343 #if CONFIG_CODEC == SWCODEC
344 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
345 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
346 #endif
348 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
349 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
351 #ifdef HAVE_LCD_BITMAP
352 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
353 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
354 { WPS_TOKEN_DRAW_INBUILTBAR, "wi", WPS_REFRESH_DYNAMIC, parse_statusbar_inbuilt },
356 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
358 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
359 parse_image_display },
361 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
362 { WPS_NO_TOKEN, "Fl", 0, parse_font_load },
363 #ifdef HAVE_ALBUMART
364 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
365 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
366 #endif
368 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
369 parse_viewport_display },
370 #ifdef HAVE_LCD_BITMAP
371 { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview },
372 { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL },
373 { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL },
374 #endif
375 { WPS_NO_TOKEN, "V", 0, parse_viewport },
377 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
378 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
379 #endif
380 #endif
382 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
383 parse_setting_and_lang },
384 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
385 parse_setting_and_lang },
386 { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL },
388 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
389 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
390 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
393 /* Recording Tokens */
394 { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL },
395 #ifdef HAVE_RECORDING
396 { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL },
397 { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL },
398 { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL },
399 { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL },
400 #endif
401 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
402 /* the array MUST end with an empty string (first char is \0) */
406 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
407 * chains require the order to be kept.
409 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
411 if (*list == NULL)
412 *list = item;
413 else
415 struct skin_token_list *t = *list;
416 while (t->next)
417 t = t->next;
418 t->next = item;
422 /* traverse the image linked-list for an image */
423 #ifdef HAVE_LCD_BITMAP
424 struct gui_img* find_image(char label, struct wps_data *data)
426 struct skin_token_list *list = data->images;
427 while (list)
429 struct gui_img *img = (struct gui_img *)list->token->value.data;
430 if (img->label == label)
431 return img;
432 list = list->next;
434 return NULL;
437 #endif
439 /* traverse the viewport linked list for a viewport */
440 struct skin_viewport* find_viewport(char label, struct wps_data *data)
442 struct skin_token_list *list = data->viewports;
443 while (list)
445 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
446 if (vp->label == label)
447 return vp;
448 list = list->next;
450 return NULL;
454 /* create and init a new wpsll item.
455 * passing NULL to token will alloc a new one.
456 * You should only pass NULL for the token when the token type (table above)
457 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
459 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
460 void* token_data)
462 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
463 if (!token)
464 token = skin_buffer_alloc(sizeof(struct wps_token));
465 if (!llitem || !token)
466 return NULL;
467 llitem->next = NULL;
468 llitem->token = token;
469 if (token_data)
470 llitem->token->value.data = token_data;
471 return llitem;
474 /* Returns the number of chars that should be skipped to jump
475 immediately after the first eol, i.e. to the start of the next line */
476 static int skip_end_of_line(const char *wps_bufptr)
478 line_number++;
479 int skip = 0;
480 while(*(wps_bufptr + skip) != '\n')
481 skip++;
482 return ++skip;
485 /* Starts a new subline in the current line during parsing */
486 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
488 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
489 if (!subline)
490 return false;
492 subline->first_token_idx = curr_token;
493 subline->next = NULL;
495 subline->line_type = 0;
496 subline->time_mult = 0;
498 line->curr_subline->last_token_idx = curr_token-1;
499 line->curr_subline->next = subline;
500 line->curr_subline = subline;
501 return true;
504 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
506 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
507 struct skin_subline *subline = NULL;
508 if (!line)
509 return false;
511 /* init the subline */
512 subline = &line->sublines;
513 subline->first_token_idx = curr_token;
514 subline->next = NULL;
515 subline->line_type = 0;
516 subline->time_mult = 0;
518 /* init the new line */
519 line->curr_subline = &line->sublines;
520 line->next = NULL;
521 line->subline_expire_time = 0;
523 /* connect to curr_line and vp pointers.
524 * 1) close the previous lines subline
525 * 2) connect to vp pointer
526 * 3) connect to curr_line global pointer
528 if (curr_line)
530 curr_line->curr_subline->last_token_idx = curr_token - 1;
531 curr_line->next = line;
532 curr_line->curr_subline = NULL;
534 curr_line = line;
535 if (!vp->lines)
536 vp->lines = line;
537 return true;
540 #ifdef HAVE_LCD_BITMAP
542 static int parse_statusbar_enable(const char *wps_bufptr,
543 struct wps_token *token,
544 struct wps_data *wps_data)
546 (void)token; /* Kill warnings */
547 wps_data->wps_sb_tag = true;
548 wps_data->show_sb_on_wps = true;
549 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
550 viewport_set_defaults(&default_vp->vp, curr_screen);
551 default_vp->vp.font = FONT_UI;
552 return skip_end_of_line(wps_bufptr);
555 static int parse_statusbar_disable(const char *wps_bufptr,
556 struct wps_token *token,
557 struct wps_data *wps_data)
559 (void)token; /* Kill warnings */
560 wps_data->wps_sb_tag = true;
561 wps_data->show_sb_on_wps = false;
562 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
563 viewport_set_fullscreen(&default_vp->vp, curr_screen);
564 default_vp->vp.font = FONT_UI;
565 return skip_end_of_line(wps_bufptr);
568 static int parse_statusbar_inbuilt(const char *wps_bufptr,
569 struct wps_token *token, struct wps_data *wps_data)
571 (void)wps_data;
572 token->value.data = (void*)&curr_vp->vp;
573 return skip_end_of_line(wps_bufptr);
576 static int get_image_id(int c)
578 if(c >= 'a' && c <= 'z')
579 return c - 'a';
580 else if(c >= 'A' && c <= 'Z')
581 return c - 'A' + 26;
582 else
583 return -1;
586 char *get_image_filename(const char *start, const char* bmpdir,
587 char *buf, int buf_size)
589 const char *end = strchr(start, '|');
590 int bmpdirlen = strlen(bmpdir);
592 if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) )
594 buf[0] = '\0';
595 return NULL;
598 strcpy(buf, bmpdir);
599 buf[bmpdirlen] = '/';
600 memcpy( &buf[bmpdirlen + 1], start, end - start);
601 buf[bmpdirlen + 1 + end - start] = 0;
603 return buf;
606 static int parse_image_display(const char *wps_bufptr,
607 struct wps_token *token,
608 struct wps_data *wps_data)
610 char label = wps_bufptr[0];
611 int subimage;
612 struct gui_img *img;;
614 /* sanity check */
615 img = find_image(label, wps_data);
616 if (!img)
618 token->value.i = label; /* so debug works */
619 return WPS_ERROR_INVALID_PARAM;
622 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
624 if (subimage >= img->num_subimages)
625 return WPS_ERROR_INVALID_PARAM;
627 /* Store sub-image number to display in high bits */
628 token->value.i = label | (subimage << 8);
629 return 2; /* We have consumed 2 bytes */
630 } else {
631 token->value.i = label;
632 return 1; /* We have consumed 1 byte */
636 static int parse_image_load(const char *wps_bufptr,
637 struct wps_token *token,
638 struct wps_data *wps_data)
640 const char *ptr = wps_bufptr;
641 const char *pos;
642 const char* filename;
643 const char* id;
644 const char *newline;
645 int x,y;
646 struct gui_img *img;
648 /* format: %x|n|filename.bmp|x|y|
649 or %xl|n|filename.bmp|x|y|
650 or %xl|n|filename.bmp|x|y|num_subimages|
653 if (*ptr != '|')
654 return WPS_ERROR_INVALID_PARAM;
656 ptr++;
658 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
659 return WPS_ERROR_INVALID_PARAM;
661 /* Check there is a terminating | */
662 if (*ptr != '|')
663 return WPS_ERROR_INVALID_PARAM;
665 /* check the image number and load state */
666 if(find_image(*id, wps_data))
668 /* Invalid image ID */
669 return WPS_ERROR_INVALID_PARAM;
671 img = skin_buffer_alloc(sizeof(struct gui_img));
672 if (!img)
673 return WPS_ERROR_INVALID_PARAM;
674 /* save a pointer to the filename */
675 img->bm.data = (char*)filename;
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);
754 static int parse_viewport_display(const char *wps_bufptr,
755 struct wps_token *token,
756 struct wps_data *wps_data)
758 (void)wps_data;
759 char letter = wps_bufptr[0];
761 if (letter < 'a' || letter > 'z')
763 /* invalid viewport tag */
764 return WPS_ERROR_INVALID_PARAM;
766 token->value.i = letter;
767 return 1;
770 #ifdef HAVE_LCD_BITMAP
771 static int parse_playlistview_text(struct playlistviewer *viewer,
772 enum info_line_type line, char* text)
774 int cur_string = 0;
775 const struct wps_tag *tag;
776 int taglen = 0;
777 const char *start = text;
778 if (*text != '|')
779 return -1;
780 text++;
781 viewer->lines[line].count = 0;
782 viewer->lines[line].scroll = false;
783 while (*text != '|')
785 if (*text == '%') /* it is a token of some type */
787 text++;
788 taglen = 0;
789 switch(*text)
791 case '%':
792 case '<':
793 case '|':
794 case '>':
795 case ';':
796 case '#':
797 /* escaped characters */
798 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER;
799 viewer->lines[line].strings[cur_string][0] = *text;
800 viewer->lines[line].strings[cur_string++][1] = '\0';
801 break;
802 default:
803 for (tag = all_tags;
804 strncmp(text, tag->name, strlen(tag->name)) != 0;
805 tag++) ;
806 /* %s isnt stored as a tag so manually check for it */
807 if (tag->type == WPS_NO_TOKEN)
809 if (!strncmp(tag->name, "s", 1))
811 viewer->lines[line].scroll = true;
812 taglen = 1;
815 else if (tag->type == WPS_TOKEN_UNKNOWN)
817 int i = 0;
818 /* just copy the string */
819 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
820 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
822 viewer->lines[line].strings[cur_string][i] = text[i];
823 i++;
825 viewer->lines[line].strings[cur_string][i] = '\0';
826 cur_string++;
827 taglen = i;
829 else
831 if (tag->parse_func)
833 /* unsupported tag, reject */
834 return -1;
836 taglen = strlen(tag->name);
837 viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type;
839 text += taglen;
842 else
844 /* regular string */
845 int i = 0;
846 /* just copy the string */
847 viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING;
848 while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != '|' && text[i] != '%')
850 viewer->lines[line].strings[cur_string][i] = text[i];
851 i++;
853 viewer->lines[line].strings[cur_string][i] = '\0';
854 cur_string++;
855 text += i;
858 return text - start;
862 static int parse_playlistview(const char *wps_bufptr,
863 struct wps_token *token, struct wps_data *wps_data)
865 (void)wps_data;
866 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
867 struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer));
868 char *ptr = strchr(wps_bufptr, '|');
869 int length;
870 if (!viewer || !ptr)
871 return WPS_ERROR_INVALID_PARAM;
872 viewer->vp = &curr_vp->vp;
873 viewer->show_icons = true;
874 viewer->start_offset = atoi(ptr+1);
875 token->value.data = (void*)viewer;
876 ptr = strchr(ptr+1, '|');
877 length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr);
878 if (length < 0)
879 return WPS_ERROR_INVALID_PARAM;
880 length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length);
881 if (length < 0)
882 return WPS_ERROR_INVALID_PARAM;
884 return skip_end_of_line(wps_bufptr);
886 #endif
888 static int parse_viewport(const char *wps_bufptr,
889 struct wps_token *token,
890 struct wps_data *wps_data)
892 (void)token; /* Kill warnings */
893 const char *ptr = wps_bufptr;
895 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
897 /* check for the optional letter to signify its a hideable viewport */
898 /* %Vl|<label>|<rest of tags>| */
899 skin_vp->hidden_flags = 0;
900 skin_vp->label = VP_NO_LABEL;
901 skin_vp->pb = NULL;
902 skin_vp->lines = NULL;
903 if (curr_line)
905 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
906 - (wps_data->num_tokens > 0 ? 1 : 0);
909 curr_line = NULL;
910 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
911 return WPS_ERROR_INVALID_PARAM;
914 if (*ptr == 'i')
916 skin_vp->label = VP_INFO_LABEL;
917 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
918 ++ptr;
920 else if (*ptr == 'l')
922 if (*(ptr+1) == '|')
924 char label = *(ptr+2);
925 if (label >= 'a' && label <= 'z')
927 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
928 skin_vp->label = label;
930 else
931 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
932 ptr += 3;
935 if (*ptr != '|')
936 return WPS_ERROR_INVALID_PARAM;
938 ptr++;
939 struct viewport *vp = &skin_vp->vp;
940 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
941 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
942 return WPS_ERROR_INVALID_PARAM;
944 /* Check for trailing | */
945 if (*ptr != '|')
946 return WPS_ERROR_INVALID_PARAM;
948 if (follow_lang_direction && lang_is_rtl())
950 vp->flags |= VP_FLAG_ALIGN_RIGHT;
951 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
953 else
954 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
956 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
957 if (!list)
958 return WPS_ERROR_INVALID_PARAM;
959 add_to_ll_chain(&wps_data->viewports, list);
960 curr_vp = skin_vp;
961 /* Skip the rest of the line */
962 return skip_end_of_line(wps_bufptr);
965 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
966 static int parse_image_special(const char *wps_bufptr,
967 struct wps_token *token,
968 struct wps_data *wps_data)
970 (void)wps_data; /* kill warning */
971 (void)token;
972 const char *pos = NULL;
973 const char *newline;
974 bool error = false;
976 pos = strchr(wps_bufptr + 1, '|');
977 newline = strchr(wps_bufptr, '\n');
979 error = (pos > newline);
982 #if LCD_DEPTH > 1
983 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
985 /* format: %X|filename.bmp| or %Xd */
986 if (*(wps_bufptr) == 'd')
988 wps_data->backdrop = NULL;
989 return skip_end_of_line(wps_bufptr);
991 else if (!error)
992 wps_data->backdrop = (char*)wps_bufptr + 1;
994 #endif
995 if (error)
996 return WPS_ERROR_INVALID_PARAM;
997 /* Skip the rest of the line */
998 return skip_end_of_line(wps_bufptr);
1000 #endif
1002 #endif /* HAVE_LCD_BITMAP */
1004 static int parse_setting_and_lang(const char *wps_bufptr,
1005 struct wps_token *token,
1006 struct wps_data *wps_data)
1008 /* NOTE: both the string validations that happen in here will
1009 * automatically PASS on checkwps because its too hard to get
1010 * settings_list.c and englinsh.lang built for it.
1011 * If that ever changes remove the #ifndef __PCTOOL__'s here
1013 (void)wps_data;
1014 const char *ptr = wps_bufptr;
1015 const char *end;
1016 int i = 0;
1017 char temp[64];
1019 /* Find the setting's cfg_name */
1020 if (*ptr != '|')
1021 return WPS_ERROR_INVALID_PARAM;
1022 ptr++;
1023 end = strchr(ptr,'|');
1024 if (!end)
1025 return WPS_ERROR_INVALID_PARAM;
1026 strlcpy(temp, ptr,end-ptr+1);
1028 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1030 #ifndef __PCTOOL__
1031 i = lang_english_to_id(temp);
1032 if (i < 0)
1033 return WPS_ERROR_INVALID_PARAM;
1034 #endif
1036 else
1038 /* Find the setting */
1039 for (i=0; i<nb_settings; i++)
1040 if (settings[i].cfg_name &&
1041 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1042 /* prevent matches on cfg_name prefixes */
1043 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1044 break;
1045 #ifndef __PCTOOL__
1046 if (i == nb_settings)
1047 return WPS_ERROR_INVALID_PARAM;
1048 #endif
1050 /* Store the setting number */
1051 token->value.i = i;
1053 /* Skip the rest of the line */
1054 return end-ptr+2;
1058 static int parse_dir_level(const char *wps_bufptr,
1059 struct wps_token *token,
1060 struct wps_data *wps_data)
1062 char val[] = { *wps_bufptr, '\0' };
1063 token->value.i = atoi(val);
1064 (void)wps_data; /* Kill warnings */
1065 return 1;
1068 static int parse_timeout(const char *wps_bufptr,
1069 struct wps_token *token,
1070 struct wps_data *wps_data)
1072 int skip = 0;
1073 int val = 0;
1074 bool have_point = false;
1075 bool have_tenth = false;
1077 (void)wps_data; /* Kill the warning */
1079 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1081 if (*wps_bufptr != '.')
1083 val *= 10;
1084 val += *wps_bufptr - '0';
1085 if (have_point)
1087 have_tenth = true;
1088 wps_bufptr++;
1089 skip++;
1090 break;
1093 else
1094 have_point = true;
1096 wps_bufptr++;
1097 skip++;
1100 if (have_tenth == false)
1101 val *= 10;
1103 if (val == 0 && skip == 0)
1105 /* decide what to do if no value was specified */
1106 switch (token->type)
1108 case WPS_TOKEN_SUBLINE_TIMEOUT:
1109 return -1;
1110 case WPS_TOKEN_BUTTON_VOLUME:
1111 val = 10;
1112 break;
1115 token->value.i = val;
1117 return skip;
1120 static int parse_progressbar(const char *wps_bufptr,
1121 struct wps_token *token,
1122 struct wps_data *wps_data)
1124 /* %pb or %pb|filename|x|y|width|height|
1125 using - for any of the params uses "sane" values */
1126 #ifdef HAVE_LCD_BITMAP
1127 enum {
1128 PB_FILENAME = 0,
1129 PB_X,
1130 PB_Y,
1131 PB_WIDTH,
1132 PB_HEIGHT
1134 const char *filename;
1135 int x, y, height, width;
1136 uint32_t set = 0;
1137 const char *ptr = wps_bufptr;
1138 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1139 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1141 if (!pb || !item)
1142 return WPS_ERROR_INVALID_PARAM;
1144 struct viewport *vp = &curr_vp->vp;
1145 /* we need to know what line number (viewport relative) this pb is,
1146 * so count them... */
1147 int line_num = -1;
1148 struct skin_line *line = curr_vp->lines;
1149 while (line)
1151 line_num++;
1152 line = line->next;
1154 pb->have_bitmap_pb = false;
1155 pb->bm.data = NULL; /* no bitmap specified */
1156 pb->follow_lang_direction = follow_lang_direction > 0;
1158 if (*wps_bufptr != '|') /* regular old style */
1160 pb->x = 0;
1161 pb->width = vp->width;
1162 pb->height = SYSFONT_HEIGHT-2;
1163 pb->y = -line_num - 1; /* Will be computed during the rendering */
1165 curr_vp->pb = pb;
1166 add_to_ll_chain(&wps_data->progressbars, item);
1167 return 0;
1169 ptr = wps_bufptr + 1;
1171 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1172 &x, &y, &width, &height)))
1173 return WPS_ERROR_INVALID_PARAM;
1175 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1176 pb->bm.data = (char*)filename;
1178 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1179 pb->x = x;
1180 else
1181 pb->x = vp->x;
1183 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1185 /* A zero width causes a divide-by-zero error later, so reject it */
1186 if (width == 0)
1187 return WPS_ERROR_INVALID_PARAM;
1189 pb->width = width;
1191 else
1192 pb->width = vp->width - pb->x;
1194 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1196 /* A zero height makes no sense - reject it */
1197 if (height == 0)
1198 return WPS_ERROR_INVALID_PARAM;
1200 pb->height = height;
1202 else
1204 if (vp->font > FONT_UI)
1205 pb->height = -1; /* calculate at display time */
1206 else
1208 #ifndef __PCTOOL__
1209 pb->height = font_get(vp->font)->height;
1210 #else
1211 pb->height = 8;
1212 #endif
1216 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1217 pb->y = y;
1218 else
1219 pb->y = -line_num - 1; /* Will be computed during the rendering */
1221 curr_vp->pb = pb;
1222 add_to_ll_chain(&wps_data->progressbars, item);
1224 /* Skip the rest of the line */
1225 return skip_end_of_line(wps_bufptr)-1;
1226 #else
1227 (void)token;
1229 if (*(wps_bufptr-1) == 'f')
1230 wps_data->full_line_progressbar = true;
1231 else
1232 wps_data->full_line_progressbar = false;
1234 return 0;
1236 #endif
1239 #ifdef HAVE_ALBUMART
1240 static int parse_int(const char *newline, const char **_pos, int *num)
1242 *_pos = parse_list("d", NULL, '|', *_pos, num);
1244 return (!*_pos || *_pos > newline || **_pos != '|');
1247 static int parse_albumart_load(const char *wps_bufptr,
1248 struct wps_token *token,
1249 struct wps_data *wps_data)
1251 const char *_pos, *newline;
1252 bool parsing;
1253 struct dim dimensions;
1254 int albumart_slot;
1255 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1256 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1257 (void)token; /* silence warning */
1258 if (!aa)
1259 return skip_end_of_line(wps_bufptr);
1261 /* reset albumart info in wps */
1262 aa->width = -1;
1263 aa->height = -1;
1264 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1265 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1266 aa->vp = &curr_vp->vp;
1268 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1270 newline = strchr(wps_bufptr, '\n');
1272 _pos = wps_bufptr;
1274 if (*_pos != '|')
1275 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1277 ++_pos;
1279 /* initial validation and parsing of x component */
1280 if (parse_int(newline, &_pos, &aa->x))
1281 return WPS_ERROR_INVALID_PARAM;
1283 ++_pos;
1285 /* initial validation and parsing of y component */
1286 if (parse_int(newline, &_pos, &aa->y))
1287 return WPS_ERROR_INVALID_PARAM;
1289 /* parsing width field */
1290 parsing = true;
1291 while (parsing)
1293 /* apply each modifier in turn */
1294 ++_pos;
1295 switch (*_pos)
1297 case 'l':
1298 case 'L':
1299 case '+':
1300 if (swap_for_rtl)
1301 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1302 else
1303 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1304 break;
1305 case 'c':
1306 case 'C':
1307 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1308 break;
1309 case 'r':
1310 case 'R':
1311 case '-':
1312 if (swap_for_rtl)
1313 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1314 else
1315 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1316 break;
1317 case 'd':
1318 case 'D':
1319 case 'i':
1320 case 'I':
1321 case 's':
1322 case 'S':
1323 /* simply ignored */
1324 break;
1325 default:
1326 parsing = false;
1327 break;
1330 /* extract max width data */
1331 if (*_pos != '|')
1333 if (parse_int(newline, &_pos, &aa->width))
1334 return WPS_ERROR_INVALID_PARAM;
1337 /* parsing height field */
1338 parsing = true;
1339 while (parsing)
1341 /* apply each modifier in turn */
1342 ++_pos;
1343 switch (*_pos)
1345 case 't':
1346 case 'T':
1347 case '-':
1348 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1349 break;
1350 case 'c':
1351 case 'C':
1352 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1353 break;
1354 case 'b':
1355 case 'B':
1356 case '+':
1357 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1358 break;
1359 case 'd':
1360 case 'D':
1361 case 'i':
1362 case 'I':
1363 case 's':
1364 case 'S':
1365 /* simply ignored */
1366 break;
1367 default:
1368 parsing = false;
1369 break;
1372 /* extract max height data */
1373 if (*_pos != '|')
1375 if (parse_int(newline, &_pos, &aa->height))
1376 return WPS_ERROR_INVALID_PARAM;
1379 /* if we got here, we parsed everything ok .. ! */
1380 if (aa->width < 0)
1381 aa->width = 0;
1382 else if (aa->width > LCD_WIDTH)
1383 aa->width = LCD_WIDTH;
1385 if (aa->height < 0)
1386 aa->height = 0;
1387 else if (aa->height > LCD_HEIGHT)
1388 aa->height = LCD_HEIGHT;
1390 if (swap_for_rtl)
1391 aa->x = LCD_WIDTH - (aa->x + aa->width);
1393 aa->state = WPS_ALBUMART_LOAD;
1394 aa->draw = false;
1395 wps_data->albumart = aa;
1397 dimensions.width = aa->width;
1398 dimensions.height = aa->height;
1400 albumart_slot = playback_claim_aa_slot(&dimensions);
1402 if (0 <= albumart_slot)
1403 wps_data->playback_aa_slot = albumart_slot;
1405 /* Skip the rest of the line */
1406 return skip_end_of_line(wps_bufptr);
1409 static int parse_albumart_display(const char *wps_bufptr,
1410 struct wps_token *token,
1411 struct wps_data *wps_data)
1413 (void)wps_bufptr;
1414 struct wps_token *prev = token-1;
1415 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1417 token->type = WPS_TOKEN_ALBUMART_FOUND;
1419 else if (wps_data->albumart)
1421 wps_data->albumart->vp = &curr_vp->vp;
1423 #if 0
1424 /* the old code did this so keep it here for now...
1425 * this is to allow the posibility to showing the next tracks AA! */
1426 if (wps_bufptr+1 == 'n')
1427 return 1;
1428 #endif
1429 return 0;
1431 #endif /* HAVE_ALBUMART */
1433 #ifdef HAVE_TOUCHSCREEN
1435 struct touchaction {const char* s; int action;};
1436 static const struct touchaction touchactions[] = {
1437 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1438 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1439 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1440 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1441 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1442 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1443 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1444 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1446 static int parse_touchregion(const char *wps_bufptr,
1447 struct wps_token *token, struct wps_data *wps_data)
1449 (void)token;
1450 unsigned i, imax;
1451 struct touchregion *region = NULL;
1452 const char *ptr = wps_bufptr;
1453 const char *action;
1454 const char pb_string[] = "progressbar";
1455 const char vol_string[] = "volume";
1456 int x,y,w,h;
1458 /* format: %T|x|y|width|height|action|
1459 * if action starts with & the area must be held to happen
1460 * action is one of:
1461 * play - play/pause playback
1462 * stop - stop playback, exit the wps
1463 * prev - prev track
1464 * next - next track
1465 * ffwd - seek forward
1466 * rwd - seek backwards
1467 * menu - go back to the main menu
1468 * browse - go back to the file/db browser
1469 * shuffle - toggle shuffle mode
1470 * repmode - cycle the repeat mode
1471 * quickscreen - go into the quickscreen
1472 * contextmenu - open the context menu
1473 * playlist - go into the playlist
1474 * pitch - go into the pitchscreen
1475 * volup - increase volume by one step
1476 * voldown - decrease volume by one step
1480 if (*ptr != '|')
1481 return WPS_ERROR_INVALID_PARAM;
1482 ptr++;
1484 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1485 return WPS_ERROR_INVALID_PARAM;
1487 /* Check there is a terminating | */
1488 if (*ptr != '|')
1489 return WPS_ERROR_INVALID_PARAM;
1491 region = skin_buffer_alloc(sizeof(struct touchregion));
1492 if (!region)
1493 return WPS_ERROR_INVALID_PARAM;
1495 /* should probably do some bounds checking here with the viewport... but later */
1496 region->action = ACTION_NONE;
1497 region->x = x;
1498 region->y = y;
1499 region->width = w;
1500 region->height = h;
1501 region->wvp = curr_vp;
1502 region->armed = false;
1504 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1505 && *(action + sizeof(pb_string)-1) == '|')
1506 region->type = WPS_TOUCHREGION_SCROLLBAR;
1507 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1508 && *(action + sizeof(vol_string)-1) == '|')
1509 region->type = WPS_TOUCHREGION_VOLUME;
1510 else
1512 region->type = WPS_TOUCHREGION_ACTION;
1514 if (*action == '&')
1516 action++;
1517 region->repeat = true;
1519 else
1520 region->repeat = false;
1522 i = 0;
1523 imax = ARRAYLEN(touchactions);
1524 while ((region->action == ACTION_NONE) &&
1525 (i < imax))
1527 /* try to match with one of our touchregion screens */
1528 int len = strlen(touchactions[i].s);
1529 if (!strncmp(touchactions[i].s, action, len)
1530 && *(action+len) == '|')
1531 region->action = touchactions[i].action;
1532 i++;
1534 if (region->action == ACTION_NONE)
1535 return WPS_ERROR_INVALID_PARAM;
1537 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1538 if (!item)
1539 return WPS_ERROR_INVALID_PARAM;
1540 add_to_ll_chain(&wps_data->touchregions, item);
1541 return skip_end_of_line(wps_bufptr);
1543 #endif
1545 /* Parse a generic token from the given string. Return the length read */
1546 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1548 int skip = 0, taglen = 0, ret;
1549 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1550 const struct wps_tag *tag;
1551 memset(token, 0, sizeof(*token));
1553 switch(*wps_bufptr)
1556 case '%':
1557 case '<':
1558 case '|':
1559 case '>':
1560 case ';':
1561 case '#':
1562 /* escaped characters */
1563 token->type = WPS_TOKEN_CHARACTER;
1564 token->value.c = *wps_bufptr;
1565 taglen = 1;
1566 wps_data->num_tokens++;
1567 break;
1569 case '?':
1570 /* conditional tag */
1571 token->type = WPS_TOKEN_CONDITIONAL;
1572 level++;
1573 condindex[level] = wps_data->num_tokens;
1574 numoptions[level] = 1;
1575 wps_data->num_tokens++;
1576 ret = parse_token(wps_bufptr + 1, wps_data);
1577 if (ret < 0) return ret;
1578 taglen = 1 + ret;
1579 break;
1581 default:
1582 /* find what tag we have */
1583 for (tag = all_tags;
1584 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1585 tag++) ;
1587 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1588 token->type = tag->type;
1589 curr_line->curr_subline->line_type |= tag->refresh_type;
1591 /* if the tag has a special parsing function, we call it */
1592 if (tag->parse_func)
1594 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1595 if (ret < 0) return ret;
1596 skip += ret;
1599 /* Some tags we don't want to save as tokens */
1600 if (tag->type == WPS_NO_TOKEN)
1601 break;
1603 /* tags that start with 'F', 'I' or 'D' are for the next file */
1604 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1605 *(tag->name) == 'D')
1606 token->next = true;
1608 wps_data->num_tokens++;
1609 break;
1612 skip += taglen;
1613 return skip;
1618 * Returns the number of bytes to skip the buf pointer to access the false
1619 * branch in a _binary_ conditional
1621 * That is:
1622 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1623 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1625 * depending on the features of a target it's not called from check_feature_tag,
1626 * hence the __attribute__ or it issues compiler warnings
1630 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1631 static int find_false_branch(const char *wps_bufptr)
1633 const char *buf = wps_bufptr;
1634 /* wps_bufptr is after the opening '<', hence level = 1*/
1635 int level = 1;
1636 char ch;
1639 ch = *buf;
1640 if (ch == '%')
1641 { /* filter out the characters we check later if they're printed
1642 * as literals */
1643 ch = *(++buf);
1644 if (ch == '<' || ch == '>' || ch == '|')
1645 continue;
1646 /* else: some tags/printed literals we skip over */
1648 else if (ch == '<') /* nested conditional */
1649 level++;
1650 else if (ch == '>')
1651 { /* closed our or a nested conditional,
1652 * do NOT skip over the '>' so that wps_parse() sees it for closing
1653 * if it is the closing one for our conditional */
1654 level--;
1656 else if (ch == '|' && level == 1)
1657 { /* we found our separator, point before and get out */
1658 break;
1660 /* if level is 0, we don't have a false branch */
1661 } while (level > 0 && *(++buf));
1663 return buf - wps_bufptr;
1667 * returns the number of bytes to get the appropriate branch of a binary
1668 * conditional
1670 * That means:
1671 * - if a feature is available, it returns 0 to not skip anything
1672 * - if the feature is not available, skip to the false branch and don't
1673 * parse the true branch at all
1675 * */
1676 static int check_feature_tag(const char *wps_bufptr, const int type)
1678 (void)wps_bufptr;
1679 switch (type)
1681 case WPS_TOKEN_RTC_PRESENT:
1682 #if CONFIG_RTC
1683 return 0;
1684 #else
1685 return find_false_branch(wps_bufptr);
1686 #endif
1687 case WPS_TOKEN_HAVE_RECORDING:
1688 #ifdef HAVE_RECORDING
1689 return 0;
1690 #else
1691 return find_false_branch(wps_bufptr);
1692 #endif
1693 default: /* not a tag we care about, just don't skip */
1694 return 0;
1699 /* Parses the WPS.
1700 data is the pointer to the structure where the parsed WPS should be stored.
1701 It is initialised.
1702 wps_bufptr points to the string containing the WPS tags */
1703 #define TOKEN_BLOCK_SIZE 128
1704 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1706 if (!data || !wps_bufptr || !*wps_bufptr)
1707 return false;
1708 enum wps_parse_error fail = PARSE_OK;
1709 int ret;
1710 int max_tokens = TOKEN_BLOCK_SIZE;
1711 size_t buf_free = 0;
1712 line_number = 0;
1713 level = -1;
1715 /* allocate enough RAM for a reasonable skin, grow as needed.
1716 * Free any used RAM before loading the images to be 100% RAM efficient */
1717 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1718 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1719 return false;
1720 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1721 data->num_tokens = 0;
1723 #if LCD_DEPTH > 1
1724 /* Backdrop defaults to the setting unless %X is used, so set it now */
1725 if (global_settings.backdrop_file[0])
1727 data->backdrop = "-";
1729 #endif
1731 while (*wps_bufptr && !fail)
1733 if (follow_lang_direction)
1734 follow_lang_direction--;
1735 /* first make sure there is enough room for tokens */
1736 if (max_tokens <= data->num_tokens + 5)
1738 int extra_tokens = TOKEN_BLOCK_SIZE;
1739 size_t needed = extra_tokens * sizeof(struct wps_token);
1740 /* do some smarts here to grow the array a bit */
1741 if (skin_buffer_freespace() < needed)
1743 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1744 break;
1746 skin_buffer_increment(needed, false);
1747 max_tokens += extra_tokens;
1750 switch(*wps_bufptr++)
1753 /* Regular tag */
1754 case '%':
1755 if ((ret = parse_token(wps_bufptr, data)) < 0)
1757 fail = PARSE_FAIL_COND_INVALID_PARAM;
1758 break;
1760 else if (level >= WPS_MAX_COND_LEVEL - 1)
1762 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1763 break;
1765 wps_bufptr += ret;
1766 break;
1768 /* Alternating sublines separator */
1769 case ';':
1770 if (level >= 0) /* there are unclosed conditionals */
1772 fail = PARSE_FAIL_UNCLOSED_COND;
1773 break;
1776 if (!skin_start_new_subline(curr_line, data->num_tokens))
1777 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1779 break;
1781 /* Conditional list start */
1782 case '<':
1783 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1785 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1786 break;
1788 wps_bufptr += check_feature_tag(wps_bufptr,
1789 data->tokens[data->num_tokens-1].type);
1790 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1791 lastcond[level] = data->num_tokens++;
1792 break;
1794 /* Conditional list end */
1795 case '>':
1796 if (level < 0) /* not in a conditional, invalid char */
1798 fail = PARSE_FAIL_INVALID_CHAR;
1799 break;
1802 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1803 if (lastcond[level])
1804 data->tokens[lastcond[level]].value.i = data->num_tokens;
1805 else
1807 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1808 break;
1811 lastcond[level] = 0;
1812 data->num_tokens++;
1813 data->tokens[condindex[level]].value.i = numoptions[level];
1814 level--;
1815 break;
1817 /* Conditional list option */
1818 case '|':
1819 if (level < 0) /* not in a conditional, invalid char */
1821 fail = PARSE_FAIL_INVALID_CHAR;
1822 break;
1825 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1826 if (lastcond[level])
1827 data->tokens[lastcond[level]].value.i = data->num_tokens;
1828 else
1830 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1831 break;
1834 lastcond[level] = data->num_tokens;
1835 numoptions[level]++;
1836 data->num_tokens++;
1837 break;
1839 /* Comment */
1840 case '#':
1841 if (level >= 0) /* there are unclosed conditionals */
1843 fail = PARSE_FAIL_UNCLOSED_COND;
1844 break;
1847 wps_bufptr += skip_end_of_line(wps_bufptr);
1848 break;
1850 /* End of this line */
1851 case '\n':
1852 if (level >= 0) /* there are unclosed conditionals */
1854 fail = PARSE_FAIL_UNCLOSED_COND;
1855 break;
1857 /* add a new token for the \n so empty lines are correct */
1858 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1859 data->tokens[data->num_tokens].value.c = '\n';
1860 data->tokens[data->num_tokens].next = false;
1861 data->num_tokens++;
1863 if (!skin_start_new_line(curr_vp, data->num_tokens))
1865 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1866 break;
1868 line_number++;
1870 break;
1872 /* String */
1873 default:
1875 unsigned int len = 1;
1876 const char *string_start = wps_bufptr - 1;
1878 /* find the length of the string */
1879 while (*wps_bufptr && *wps_bufptr != '#' &&
1880 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1881 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1882 *wps_bufptr != '|' && *wps_bufptr != '\n')
1884 wps_bufptr++;
1885 len++;
1888 /* look if we already have that string */
1889 char *str;
1890 bool found = false;
1891 struct skin_token_list *list = data->strings;
1892 while (list)
1894 str = (char*)list->token->value.data;
1895 found = (strlen(str) == len &&
1896 strncmp(string_start, str, len) == 0);
1897 if (found)
1898 break; /* break here because the list item is
1899 used if its found */
1900 list = list->next;
1902 /* If a matching string is found, found is true and i is
1903 the index of the string. If not, found is false */
1905 if (!found)
1907 /* new string */
1908 str = (char*)skin_buffer_alloc(len+1);
1909 if (!str)
1911 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1912 break;
1914 strlcpy(str, string_start, len+1);
1915 struct skin_token_list *item =
1916 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1917 if(!item)
1919 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1920 break;
1922 add_to_ll_chain(&data->strings, item);
1924 else
1926 /* another occurrence of an existing string */
1927 data->tokens[data->num_tokens].value.data = list->token->value.data;
1929 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1930 data->num_tokens++;
1932 break;
1936 if (!fail && level >= 0) /* there are unclosed conditionals */
1937 fail = PARSE_FAIL_UNCLOSED_COND;
1939 if (*wps_bufptr && !fail)
1940 /* one of the limits of the while loop was exceeded */
1941 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1943 /* Success! */
1944 curr_line->curr_subline->last_token_idx = data->num_tokens;
1945 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1946 /* freeup unused tokens */
1947 skin_buffer_free_from_front(sizeof(struct wps_token)
1948 * (max_tokens - data->num_tokens));
1950 #if defined(DEBUG) || defined(SIMULATOR)
1951 if (debug)
1953 print_debug_info(data, fail, line_number);
1954 debug_skin_usage();
1956 #else
1957 (void)debug;
1958 #endif
1960 return (fail == 0);
1965 * initial setup of wps_data; does reset everything
1966 * except fields which need to survive, i.e.
1969 static void skin_data_reset(struct wps_data *wps_data)
1971 #ifdef HAVE_LCD_BITMAP
1972 wps_data->images = NULL;
1973 wps_data->progressbars = NULL;
1974 #endif
1975 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1976 wps_data->backdrop = NULL;
1977 #endif
1978 #ifdef HAVE_TOUCHSCREEN
1979 wps_data->touchregions = NULL;
1980 #endif
1981 wps_data->viewports = NULL;
1982 wps_data->strings = NULL;
1983 #ifdef HAVE_ALBUMART
1984 wps_data->albumart = NULL;
1985 if (wps_data->playback_aa_slot >= 0)
1987 playback_release_aa_slot(wps_data->playback_aa_slot);
1988 wps_data->playback_aa_slot = -1;
1990 #endif
1991 wps_data->tokens = NULL;
1992 wps_data->num_tokens = 0;
1994 #ifdef HAVE_LCD_BITMAP
1995 wps_data->peak_meter_enabled = false;
1996 wps_data->wps_sb_tag = false;
1997 wps_data->show_sb_on_wps = false;
1998 #else /* HAVE_LCD_CHARCELLS */
1999 /* progress bars */
2000 int i;
2001 for (i = 0; i < 8; i++)
2003 wps_data->wps_progress_pat[i] = 0;
2005 wps_data->full_line_progressbar = false;
2006 #endif
2007 wps_data->wps_loaded = false;
2010 #ifdef HAVE_LCD_BITMAP
2011 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2013 (void)wps_data; /* only needed for remote targets */
2014 char img_path[MAX_PATH];
2015 get_image_filename(bitmap->data, bmpdir,
2016 img_path, sizeof(img_path));
2018 /* load the image */
2019 int format;
2020 #ifdef HAVE_REMOTE_LCD
2021 if (curr_screen == SCREEN_REMOTE)
2022 format = FORMAT_ANY|FORMAT_REMOTE;
2023 else
2024 #endif
2025 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2027 size_t max_buf;
2028 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2029 bitmap->data = imgbuf;
2030 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2032 if (ret > 0)
2034 skin_buffer_increment(ret, true);
2035 return true;
2037 else
2039 /* Abort if we can't load an image */
2040 DEBUGF("Couldn't load '%s'\n", img_path);
2041 return false;
2045 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2047 struct skin_token_list *list;
2048 bool retval = true; /* return false if a single image failed to load */
2049 /* do the progressbars */
2050 list = wps_data->progressbars;
2051 while (list)
2053 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2054 if (pb->bm.data)
2056 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2057 if (!pb->have_bitmap_pb) /* no success */
2058 retval = false;
2060 list = list->next;
2062 /* regular images */
2063 list = wps_data->images;
2064 while (list)
2066 struct gui_img *img = (struct gui_img*)list->token->value.data;
2067 if (img->bm.data)
2069 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2070 if (img->loaded)
2071 img->subimage_height = img->bm.height / img->num_subimages;
2072 else
2073 retval = false;
2075 list = list->next;
2078 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2079 /* Backdrop load scheme:
2080 * 1) %X|filename|
2081 * 2) load the backdrop from settings
2083 if (wps_data->backdrop)
2085 bool needed = wps_data->backdrop[0] != '-';
2086 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2087 bmpdir, curr_screen);
2088 if (!wps_data->backdrop && needed)
2089 retval = false;
2091 #endif /* has backdrop support */
2093 return retval;
2096 static bool skin_load_fonts(struct wps_data *data)
2098 /* don't spit out after the first failue to aid debugging */
2099 bool success = true;
2100 struct skin_token_list *vp_list;
2101 int font_id;
2102 /* walk though each viewport and assign its font */
2103 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2105 /* first, find the viewports that have a non-sys/ui-font font */
2106 struct skin_viewport *skin_vp =
2107 (struct skin_viewport*)vp_list->token->value.data;
2108 struct viewport *vp = &skin_vp->vp;
2111 if (vp->font <= FONT_UI)
2112 { /* the usual case -> built-in fonts */
2113 #ifdef HAVE_REMOTE_LCD
2114 if (vp->font == FONT_UI)
2115 vp->font += curr_screen;
2116 #endif
2117 continue;
2119 font_id = vp->font;
2121 /* now find the corresponding skin_font */
2122 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2123 if (!font->name)
2125 DEBUGF("font %d not specified\n", font_id);
2126 success = false;
2127 continue;
2130 /* load the font - will handle loading the same font again if
2131 * multiple viewports use the same */
2132 if (font->id < 0)
2134 char *dot = strchr(font->name, '.');
2135 *dot = '\0';
2136 font->id = skin_font_load(font->name);
2139 if (font->id < 0)
2141 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2142 font_id, font->name);
2143 success = false;
2144 continue;
2147 /* finally, assign the font_id to the viewport */
2148 vp->font = font->id;
2150 return success;
2153 #endif /* HAVE_LCD_BITMAP */
2155 /* to setup up the wps-data from a format-buffer (isfile = false)
2156 from a (wps-)file (isfile = true)*/
2157 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2158 const char *buf, bool isfile)
2161 if (!wps_data || !buf)
2162 return false;
2163 #ifdef HAVE_ALBUMART
2164 int status;
2165 struct mp3entry *curtrack;
2166 long offset;
2167 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2168 if (wps_data->albumart)
2170 old_aa.state = wps_data->albumart->state;
2171 old_aa.height = wps_data->albumart->height;
2172 old_aa.width = wps_data->albumart->width;
2174 #endif
2175 #ifdef HAVE_LCD_BITMAP
2176 int i;
2177 for (i=0;i<MAXUSERFONTS;i++)
2179 skinfonts[i].id = -1;
2180 skinfonts[i].name = NULL;
2182 #endif
2183 #if defined(DEBUG) || defined(SIMULATOR)
2184 if (isfile)
2186 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2188 #endif
2190 skin_data_reset(wps_data);
2191 curr_screen = screen;
2193 /* alloc default viewport, will be fixed up later */
2194 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2195 if (!curr_vp)
2196 return false;
2197 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2198 if (!list)
2199 return false;
2200 add_to_ll_chain(&wps_data->viewports, list);
2203 /* Initialise the first (default) viewport */
2204 curr_vp->label = VP_DEFAULT_LABEL;
2205 curr_vp->pb = NULL;
2206 curr_vp->hidden_flags = 0;
2207 curr_vp->lines = NULL;
2209 viewport_set_defaults(&curr_vp->vp, screen);
2210 #ifdef HAVE_LCD_BITMAP
2211 curr_vp->vp.font = FONT_UI;
2212 #endif
2214 curr_line = NULL;
2215 if (!skin_start_new_line(curr_vp, 0))
2216 return false;
2218 if (!isfile)
2220 if (wps_parse(wps_data, buf, false))
2222 #ifdef HAVE_LCD_BITMAP
2223 /* load the backdrop */
2224 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2225 skin_data_reset(wps_data);
2226 return false;
2228 #endif
2229 return true;
2231 return false;
2233 else
2235 int fd = open_utf8(buf, O_RDONLY);
2237 if (fd < 0)
2238 return false;
2240 /* get buffer space from the plugin buffer */
2241 size_t buffersize = 0;
2242 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2244 if (!wps_buffer)
2245 return false;
2247 /* copy the file's content to the buffer for parsing,
2248 ensuring that every line ends with a newline char. */
2249 unsigned int start = 0;
2250 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2252 start += strlen(wps_buffer + start);
2253 if (start < buffersize - 1)
2255 wps_buffer[start++] = '\n';
2256 wps_buffer[start] = 0;
2260 close(fd);
2262 if (start <= 0)
2263 return false;
2265 /* parse the WPS source */
2266 if (!wps_parse(wps_data, wps_buffer, true)) {
2267 skin_data_reset(wps_data);
2268 return false;
2271 wps_data->wps_loaded = true;
2273 #ifdef HAVE_LCD_BITMAP
2274 /* get the bitmap dir */
2275 char bmpdir[MAX_PATH];
2276 char *dot = strrchr(buf, '.');
2278 strlcpy(bmpdir, buf, dot - buf + 1);
2279 /* load the bitmaps that were found by the parsing */
2280 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2281 skin_data_reset(wps_data);
2282 wps_data->wps_loaded = false;
2283 return false;
2285 if (!skin_load_fonts(wps_data))
2287 skin_data_reset(wps_data);
2288 wps_data->wps_loaded = false;
2289 return false;
2291 #endif
2292 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2293 status = audio_status();
2294 if (status & AUDIO_STATUS_PLAY)
2296 struct skin_albumart *aa = wps_data->albumart;
2297 if (aa && ((aa->state && !old_aa.state) ||
2298 (aa->state &&
2299 (((old_aa.height != aa->height) ||
2300 (old_aa.width != aa->width))))))
2302 curtrack = audio_current_track();
2303 offset = curtrack->offset;
2304 audio_stop();
2305 if (!(status & AUDIO_STATUS_PAUSE))
2306 audio_play(offset);
2309 #endif
2310 #if defined(DEBUG) || defined(SIMULATOR)
2311 debug_skin_usage();
2312 #endif
2313 return true;