fix the remote font not working when there is no .rsbs loaded
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob3973128c88ba5d9c64a6f4c6b8675e7b66a0e85a
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;
913 if (*ptr == 'i')
915 skin_vp->label = VP_INFO_LABEL;
916 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
917 ++ptr;
919 else if (*ptr == 'l')
921 if (*(ptr+1) == '|')
923 char label = *(ptr+2);
924 if (label >= 'a' && label <= 'z')
926 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
927 skin_vp->label = label;
929 else
930 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
931 ptr += 3;
934 if (*ptr != '|')
935 return WPS_ERROR_INVALID_PARAM;
937 ptr++;
938 struct viewport *vp = &skin_vp->vp;
939 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
940 if (!(ptr = viewport_parse_viewport(vp, curr_screen, ptr, '|')))
941 return WPS_ERROR_INVALID_PARAM;
943 /* Check for trailing | */
944 if (*ptr != '|')
945 return WPS_ERROR_INVALID_PARAM;
947 if (follow_lang_direction && lang_is_rtl())
949 vp->flags |= VP_FLAG_ALIGN_RIGHT;
950 vp->x = screens[curr_screen].lcdwidth - vp->width - vp->x;
952 else
953 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
955 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
956 if (!list)
957 return WPS_ERROR_INVALID_PARAM;
958 add_to_ll_chain(&wps_data->viewports, list);
959 curr_vp = skin_vp;
960 /* Skip the rest of the line */
961 return skip_end_of_line(wps_bufptr);
964 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
965 static int parse_image_special(const char *wps_bufptr,
966 struct wps_token *token,
967 struct wps_data *wps_data)
969 (void)wps_data; /* kill warning */
970 (void)token;
971 const char *pos = NULL;
972 const char *newline;
973 bool error = false;
975 pos = strchr(wps_bufptr + 1, '|');
976 newline = strchr(wps_bufptr, '\n');
978 error = (pos > newline);
980 #if LCD_DEPTH > 1
981 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
983 /* format: %X|filename.bmp| or %Xd */
984 if (*(wps_bufptr) == 'd')
986 wps_data->backdrop = NULL;
987 return skip_end_of_line(wps_bufptr);
989 else if (!error)
990 wps_data->backdrop = (char*)wps_bufptr + 1;
992 #endif
993 if (error)
994 return WPS_ERROR_INVALID_PARAM;
995 /* Skip the rest of the line */
996 return skip_end_of_line(wps_bufptr);
998 #endif
1000 #endif /* HAVE_LCD_BITMAP */
1002 static int parse_setting_and_lang(const char *wps_bufptr,
1003 struct wps_token *token,
1004 struct wps_data *wps_data)
1006 /* NOTE: both the string validations that happen in here will
1007 * automatically PASS on checkwps because its too hard to get
1008 * settings_list.c and englinsh.lang built for it.
1009 * If that ever changes remove the #ifndef __PCTOOL__'s here
1011 (void)wps_data;
1012 const char *ptr = wps_bufptr;
1013 const char *end;
1014 int i = 0;
1015 char temp[64];
1017 /* Find the setting's cfg_name */
1018 if (*ptr != '|')
1019 return WPS_ERROR_INVALID_PARAM;
1020 ptr++;
1021 end = strchr(ptr,'|');
1022 if (!end)
1023 return WPS_ERROR_INVALID_PARAM;
1024 strlcpy(temp, ptr,end-ptr+1);
1026 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
1028 #ifndef __PCTOOL__
1029 i = lang_english_to_id(temp);
1030 if (i < 0)
1031 return WPS_ERROR_INVALID_PARAM;
1032 #endif
1034 else
1036 /* Find the setting */
1037 for (i=0; i<nb_settings; i++)
1038 if (settings[i].cfg_name &&
1039 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
1040 /* prevent matches on cfg_name prefixes */
1041 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
1042 break;
1043 #ifndef __PCTOOL__
1044 if (i == nb_settings)
1045 return WPS_ERROR_INVALID_PARAM;
1046 #endif
1048 /* Store the setting number */
1049 token->value.i = i;
1051 /* Skip the rest of the line */
1052 return end-ptr+2;
1056 static int parse_dir_level(const char *wps_bufptr,
1057 struct wps_token *token,
1058 struct wps_data *wps_data)
1060 char val[] = { *wps_bufptr, '\0' };
1061 token->value.i = atoi(val);
1062 (void)wps_data; /* Kill warnings */
1063 return 1;
1066 static int parse_timeout(const char *wps_bufptr,
1067 struct wps_token *token,
1068 struct wps_data *wps_data)
1070 int skip = 0;
1071 int val = 0;
1072 bool have_point = false;
1073 bool have_tenth = false;
1075 (void)wps_data; /* Kill the warning */
1077 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
1079 if (*wps_bufptr != '.')
1081 val *= 10;
1082 val += *wps_bufptr - '0';
1083 if (have_point)
1085 have_tenth = true;
1086 wps_bufptr++;
1087 skip++;
1088 break;
1091 else
1092 have_point = true;
1094 wps_bufptr++;
1095 skip++;
1098 if (have_tenth == false)
1099 val *= 10;
1101 if (val == 0 && skip == 0)
1103 /* decide what to do if no value was specified */
1104 switch (token->type)
1106 case WPS_TOKEN_SUBLINE_TIMEOUT:
1107 return -1;
1108 case WPS_TOKEN_BUTTON_VOLUME:
1109 val = 10;
1110 break;
1113 token->value.i = val;
1115 return skip;
1118 static int parse_progressbar(const char *wps_bufptr,
1119 struct wps_token *token,
1120 struct wps_data *wps_data)
1122 /* %pb or %pb|filename|x|y|width|height|
1123 using - for any of the params uses "sane" values */
1124 #ifdef HAVE_LCD_BITMAP
1125 enum {
1126 PB_FILENAME = 0,
1127 PB_X,
1128 PB_Y,
1129 PB_WIDTH,
1130 PB_HEIGHT
1132 const char *filename;
1133 int x, y, height, width;
1134 uint32_t set = 0;
1135 const char *ptr = wps_bufptr;
1136 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
1137 struct skin_token_list *item = new_skin_token_list_item(token, pb);
1139 if (!pb || !item)
1140 return WPS_ERROR_INVALID_PARAM;
1142 struct viewport *vp = &curr_vp->vp;
1143 /* we need to know what line number (viewport relative) this pb is,
1144 * so count them... */
1145 int line_num = -1;
1146 struct skin_line *line = curr_vp->lines;
1147 while (line)
1149 line_num++;
1150 line = line->next;
1152 pb->have_bitmap_pb = false;
1153 pb->bm.data = NULL; /* no bitmap specified */
1154 pb->follow_lang_direction = follow_lang_direction > 0;
1156 if (*wps_bufptr != '|') /* regular old style */
1158 pb->x = 0;
1159 pb->width = vp->width;
1160 pb->height = SYSFONT_HEIGHT-2;
1161 pb->y = -line_num - 1; /* Will be computed during the rendering */
1163 curr_vp->pb = pb;
1164 add_to_ll_chain(&wps_data->progressbars, item);
1165 return 0;
1167 ptr = wps_bufptr + 1;
1169 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
1170 &x, &y, &width, &height)))
1171 return WPS_ERROR_INVALID_PARAM;
1173 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
1174 pb->bm.data = (char*)filename;
1176 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
1177 pb->x = x;
1178 else
1179 pb->x = vp->x;
1181 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
1183 /* A zero width causes a divide-by-zero error later, so reject it */
1184 if (width == 0)
1185 return WPS_ERROR_INVALID_PARAM;
1187 pb->width = width;
1189 else
1190 pb->width = vp->width - pb->x;
1192 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
1194 /* A zero height makes no sense - reject it */
1195 if (height == 0)
1196 return WPS_ERROR_INVALID_PARAM;
1198 pb->height = height;
1200 else
1202 if (vp->font > FONT_UI)
1203 pb->height = -1; /* calculate at display time */
1204 else
1206 #ifndef __PCTOOL__
1207 pb->height = font_get(vp->font)->height;
1208 #else
1209 pb->height = 8;
1210 #endif
1214 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1215 pb->y = y;
1216 else
1217 pb->y = -line_num - 1; /* Will be computed during the rendering */
1219 curr_vp->pb = pb;
1220 add_to_ll_chain(&wps_data->progressbars, item);
1222 /* Skip the rest of the line */
1223 return skip_end_of_line(wps_bufptr)-1;
1224 #else
1225 (void)token;
1227 if (*(wps_bufptr-1) == 'f')
1228 wps_data->full_line_progressbar = true;
1229 else
1230 wps_data->full_line_progressbar = false;
1232 return 0;
1234 #endif
1237 #ifdef HAVE_ALBUMART
1238 static int parse_int(const char *newline, const char **_pos, int *num)
1240 *_pos = parse_list("d", NULL, '|', *_pos, num);
1242 return (!*_pos || *_pos > newline || **_pos != '|');
1245 static int parse_albumart_load(const char *wps_bufptr,
1246 struct wps_token *token,
1247 struct wps_data *wps_data)
1249 const char *_pos, *newline;
1250 bool parsing;
1251 struct dim dimensions;
1252 int albumart_slot;
1253 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
1254 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1255 (void)token; /* silence warning */
1256 if (!aa)
1257 return skip_end_of_line(wps_bufptr);
1259 /* reset albumart info in wps */
1260 aa->width = -1;
1261 aa->height = -1;
1262 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1263 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1264 aa->vp = &curr_vp->vp;
1266 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1268 newline = strchr(wps_bufptr, '\n');
1270 _pos = wps_bufptr;
1272 if (*_pos != '|')
1273 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1275 ++_pos;
1277 /* initial validation and parsing of x component */
1278 if (parse_int(newline, &_pos, &aa->x))
1279 return WPS_ERROR_INVALID_PARAM;
1281 ++_pos;
1283 /* initial validation and parsing of y component */
1284 if (parse_int(newline, &_pos, &aa->y))
1285 return WPS_ERROR_INVALID_PARAM;
1287 /* parsing width field */
1288 parsing = true;
1289 while (parsing)
1291 /* apply each modifier in turn */
1292 ++_pos;
1293 switch (*_pos)
1295 case 'l':
1296 case 'L':
1297 case '+':
1298 if (swap_for_rtl)
1299 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1300 else
1301 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1302 break;
1303 case 'c':
1304 case 'C':
1305 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1306 break;
1307 case 'r':
1308 case 'R':
1309 case '-':
1310 if (swap_for_rtl)
1311 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1312 else
1313 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1314 break;
1315 case 'd':
1316 case 'D':
1317 case 'i':
1318 case 'I':
1319 case 's':
1320 case 'S':
1321 /* simply ignored */
1322 break;
1323 default:
1324 parsing = false;
1325 break;
1328 /* extract max width data */
1329 if (*_pos != '|')
1331 if (parse_int(newline, &_pos, &aa->width))
1332 return WPS_ERROR_INVALID_PARAM;
1335 /* parsing height field */
1336 parsing = true;
1337 while (parsing)
1339 /* apply each modifier in turn */
1340 ++_pos;
1341 switch (*_pos)
1343 case 't':
1344 case 'T':
1345 case '-':
1346 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1347 break;
1348 case 'c':
1349 case 'C':
1350 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1351 break;
1352 case 'b':
1353 case 'B':
1354 case '+':
1355 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1356 break;
1357 case 'd':
1358 case 'D':
1359 case 'i':
1360 case 'I':
1361 case 's':
1362 case 'S':
1363 /* simply ignored */
1364 break;
1365 default:
1366 parsing = false;
1367 break;
1370 /* extract max height data */
1371 if (*_pos != '|')
1373 if (parse_int(newline, &_pos, &aa->height))
1374 return WPS_ERROR_INVALID_PARAM;
1377 /* if we got here, we parsed everything ok .. ! */
1378 if (aa->width < 0)
1379 aa->width = 0;
1380 else if (aa->width > LCD_WIDTH)
1381 aa->width = LCD_WIDTH;
1383 if (aa->height < 0)
1384 aa->height = 0;
1385 else if (aa->height > LCD_HEIGHT)
1386 aa->height = LCD_HEIGHT;
1388 if (swap_for_rtl)
1389 aa->x = LCD_WIDTH - (aa->x + aa->width);
1391 aa->state = WPS_ALBUMART_LOAD;
1392 aa->draw = false;
1393 wps_data->albumart = aa;
1395 dimensions.width = aa->width;
1396 dimensions.height = aa->height;
1398 albumart_slot = playback_claim_aa_slot(&dimensions);
1400 if (0 <= albumart_slot)
1401 wps_data->playback_aa_slot = albumart_slot;
1403 /* Skip the rest of the line */
1404 return skip_end_of_line(wps_bufptr);
1407 static int parse_albumart_display(const char *wps_bufptr,
1408 struct wps_token *token,
1409 struct wps_data *wps_data)
1411 (void)wps_bufptr;
1412 struct wps_token *prev = token-1;
1413 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1415 token->type = WPS_TOKEN_ALBUMART_FOUND;
1417 else if (wps_data->albumart)
1419 wps_data->albumart->vp = &curr_vp->vp;
1421 #if 0
1422 /* the old code did this so keep it here for now...
1423 * this is to allow the posibility to showing the next tracks AA! */
1424 if (wps_bufptr+1 == 'n')
1425 return 1;
1426 #endif
1427 return 0;
1429 #endif /* HAVE_ALBUMART */
1431 #ifdef HAVE_TOUCHSCREEN
1433 struct touchaction {const char* s; int action;};
1434 static const struct touchaction touchactions[] = {
1435 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1436 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1437 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1438 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1439 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1440 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1441 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1442 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1444 static int parse_touchregion(const char *wps_bufptr,
1445 struct wps_token *token, struct wps_data *wps_data)
1447 (void)token;
1448 unsigned i, imax;
1449 struct touchregion *region = NULL;
1450 const char *ptr = wps_bufptr;
1451 const char *action;
1452 const char pb_string[] = "progressbar";
1453 const char vol_string[] = "volume";
1454 int x,y,w,h;
1456 /* format: %T|x|y|width|height|action|
1457 * if action starts with & the area must be held to happen
1458 * action is one of:
1459 * play - play/pause playback
1460 * stop - stop playback, exit the wps
1461 * prev - prev track
1462 * next - next track
1463 * ffwd - seek forward
1464 * rwd - seek backwards
1465 * menu - go back to the main menu
1466 * browse - go back to the file/db browser
1467 * shuffle - toggle shuffle mode
1468 * repmode - cycle the repeat mode
1469 * quickscreen - go into the quickscreen
1470 * contextmenu - open the context menu
1471 * playlist - go into the playlist
1472 * pitch - go into the pitchscreen
1473 * volup - increase volume by one step
1474 * voldown - decrease volume by one step
1478 if (*ptr != '|')
1479 return WPS_ERROR_INVALID_PARAM;
1480 ptr++;
1482 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1483 return WPS_ERROR_INVALID_PARAM;
1485 /* Check there is a terminating | */
1486 if (*ptr != '|')
1487 return WPS_ERROR_INVALID_PARAM;
1489 region = skin_buffer_alloc(sizeof(struct touchregion));
1490 if (!region)
1491 return WPS_ERROR_INVALID_PARAM;
1493 /* should probably do some bounds checking here with the viewport... but later */
1494 region->action = ACTION_NONE;
1495 region->x = x;
1496 region->y = y;
1497 region->width = w;
1498 region->height = h;
1499 region->wvp = curr_vp;
1500 region->armed = false;
1502 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1503 && *(action + sizeof(pb_string)-1) == '|')
1504 region->type = WPS_TOUCHREGION_SCROLLBAR;
1505 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1506 && *(action + sizeof(vol_string)-1) == '|')
1507 region->type = WPS_TOUCHREGION_VOLUME;
1508 else
1510 region->type = WPS_TOUCHREGION_ACTION;
1512 if (*action == '&')
1514 action++;
1515 region->repeat = true;
1517 else
1518 region->repeat = false;
1520 i = 0;
1521 imax = ARRAYLEN(touchactions);
1522 while ((region->action == ACTION_NONE) &&
1523 (i < imax))
1525 /* try to match with one of our touchregion screens */
1526 int len = strlen(touchactions[i].s);
1527 if (!strncmp(touchactions[i].s, action, len)
1528 && *(action+len) == '|')
1529 region->action = touchactions[i].action;
1530 i++;
1532 if (region->action == ACTION_NONE)
1533 return WPS_ERROR_INVALID_PARAM;
1535 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1536 if (!item)
1537 return WPS_ERROR_INVALID_PARAM;
1538 add_to_ll_chain(&wps_data->touchregions, item);
1539 return skip_end_of_line(wps_bufptr);
1541 #endif
1543 /* Parse a generic token from the given string. Return the length read */
1544 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1546 int skip = 0, taglen = 0, ret;
1547 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1548 const struct wps_tag *tag;
1549 memset(token, 0, sizeof(*token));
1551 switch(*wps_bufptr)
1554 case '%':
1555 case '<':
1556 case '|':
1557 case '>':
1558 case ';':
1559 case '#':
1560 /* escaped characters */
1561 token->type = WPS_TOKEN_CHARACTER;
1562 token->value.c = *wps_bufptr;
1563 taglen = 1;
1564 wps_data->num_tokens++;
1565 break;
1567 case '?':
1568 /* conditional tag */
1569 token->type = WPS_TOKEN_CONDITIONAL;
1570 level++;
1571 condindex[level] = wps_data->num_tokens;
1572 numoptions[level] = 1;
1573 wps_data->num_tokens++;
1574 ret = parse_token(wps_bufptr + 1, wps_data);
1575 if (ret < 0) return ret;
1576 taglen = 1 + ret;
1577 break;
1579 default:
1580 /* find what tag we have */
1581 for (tag = all_tags;
1582 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1583 tag++) ;
1585 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1586 token->type = tag->type;
1587 curr_line->curr_subline->line_type |= tag->refresh_type;
1589 /* if the tag has a special parsing function, we call it */
1590 if (tag->parse_func)
1592 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1593 if (ret < 0) return ret;
1594 skip += ret;
1597 /* Some tags we don't want to save as tokens */
1598 if (tag->type == WPS_NO_TOKEN)
1599 break;
1601 /* tags that start with 'F', 'I' or 'D' are for the next file */
1602 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1603 *(tag->name) == 'D')
1604 token->next = true;
1606 wps_data->num_tokens++;
1607 break;
1610 skip += taglen;
1611 return skip;
1616 * Returns the number of bytes to skip the buf pointer to access the false
1617 * branch in a _binary_ conditional
1619 * That is:
1620 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1621 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1623 * depending on the features of a target it's not called from check_feature_tag,
1624 * hence the __attribute__ or it issues compiler warnings
1628 static int find_false_branch(const char *wps_bufptr) __attribute__((unused));
1629 static int find_false_branch(const char *wps_bufptr)
1631 const char *buf = wps_bufptr;
1632 /* wps_bufptr is after the opening '<', hence level = 1*/
1633 int level = 1;
1634 char ch;
1637 ch = *buf;
1638 if (ch == '%')
1639 { /* filter out the characters we check later if they're printed
1640 * as literals */
1641 ch = *(++buf);
1642 if (ch == '<' || ch == '>' || ch == '|')
1643 continue;
1644 /* else: some tags/printed literals we skip over */
1646 else if (ch == '<') /* nested conditional */
1647 level++;
1648 else if (ch == '>')
1649 { /* closed our or a nested conditional,
1650 * do NOT skip over the '>' so that wps_parse() sees it for closing
1651 * if it is the closing one for our conditional */
1652 level--;
1654 else if (ch == '|' && level == 1)
1655 { /* we found our separator, point before and get out */
1656 break;
1658 /* if level is 0, we don't have a false branch */
1659 } while (level > 0 && *(++buf));
1661 return buf - wps_bufptr;
1665 * returns the number of bytes to get the appropriate branch of a binary
1666 * conditional
1668 * That means:
1669 * - if a feature is available, it returns 0 to not skip anything
1670 * - if the feature is not available, skip to the false branch and don't
1671 * parse the true branch at all
1673 * */
1674 static int check_feature_tag(const char *wps_bufptr, const int type)
1676 (void)wps_bufptr;
1677 switch (type)
1679 case WPS_TOKEN_RTC_PRESENT:
1680 #if CONFIG_RTC
1681 return 0;
1682 #else
1683 return find_false_branch(wps_bufptr);
1684 #endif
1685 case WPS_TOKEN_HAVE_RECORDING:
1686 #ifdef HAVE_RECORDING
1687 return 0;
1688 #else
1689 return find_false_branch(wps_bufptr);
1690 #endif
1691 default: /* not a tag we care about, just don't skip */
1692 return 0;
1697 /* Parses the WPS.
1698 data is the pointer to the structure where the parsed WPS should be stored.
1699 It is initialised.
1700 wps_bufptr points to the string containing the WPS tags */
1701 #define TOKEN_BLOCK_SIZE 128
1702 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1704 if (!data || !wps_bufptr || !*wps_bufptr)
1705 return false;
1706 enum wps_parse_error fail = PARSE_OK;
1707 int ret;
1708 int max_tokens = TOKEN_BLOCK_SIZE;
1709 size_t buf_free = 0;
1710 line_number = 0;
1711 level = -1;
1713 /* allocate enough RAM for a reasonable skin, grow as needed.
1714 * Free any used RAM before loading the images to be 100% RAM efficient */
1715 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1716 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1717 return false;
1718 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1719 data->num_tokens = 0;
1721 #if LCD_DEPTH > 1
1722 /* Backdrop defaults to the setting unless %X is used, so set it now */
1723 if (global_settings.backdrop_file[0])
1725 data->backdrop = "-";
1727 #endif
1729 while (*wps_bufptr && !fail)
1731 if (follow_lang_direction)
1732 follow_lang_direction--;
1733 /* first make sure there is enough room for tokens */
1734 if (max_tokens <= data->num_tokens + 5)
1736 int extra_tokens = TOKEN_BLOCK_SIZE;
1737 size_t needed = extra_tokens * sizeof(struct wps_token);
1738 /* do some smarts here to grow the array a bit */
1739 if (skin_buffer_freespace() < needed)
1741 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1742 break;
1744 skin_buffer_increment(needed, false);
1745 max_tokens += extra_tokens;
1748 switch(*wps_bufptr++)
1751 /* Regular tag */
1752 case '%':
1753 if ((ret = parse_token(wps_bufptr, data)) < 0)
1755 fail = PARSE_FAIL_COND_INVALID_PARAM;
1756 break;
1758 else if (level >= WPS_MAX_COND_LEVEL - 1)
1760 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1761 break;
1763 wps_bufptr += ret;
1764 break;
1766 /* Alternating sublines separator */
1767 case ';':
1768 if (level >= 0) /* there are unclosed conditionals */
1770 fail = PARSE_FAIL_UNCLOSED_COND;
1771 break;
1774 if (!skin_start_new_subline(curr_line, data->num_tokens))
1775 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1777 break;
1779 /* Conditional list start */
1780 case '<':
1781 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1783 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1784 break;
1786 wps_bufptr += check_feature_tag(wps_bufptr,
1787 data->tokens[data->num_tokens-1].type);
1788 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1789 lastcond[level] = data->num_tokens++;
1790 break;
1792 /* Conditional list end */
1793 case '>':
1794 if (level < 0) /* not in a conditional, invalid char */
1796 fail = PARSE_FAIL_INVALID_CHAR;
1797 break;
1800 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1801 if (lastcond[level])
1802 data->tokens[lastcond[level]].value.i = data->num_tokens;
1803 else
1805 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1806 break;
1809 lastcond[level] = 0;
1810 data->num_tokens++;
1811 data->tokens[condindex[level]].value.i = numoptions[level];
1812 level--;
1813 break;
1815 /* Conditional list option */
1816 case '|':
1817 if (level < 0) /* not in a conditional, invalid char */
1819 fail = PARSE_FAIL_INVALID_CHAR;
1820 break;
1823 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1824 if (lastcond[level])
1825 data->tokens[lastcond[level]].value.i = data->num_tokens;
1826 else
1828 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1829 break;
1832 lastcond[level] = data->num_tokens;
1833 numoptions[level]++;
1834 data->num_tokens++;
1835 break;
1837 /* Comment */
1838 case '#':
1839 if (level >= 0) /* there are unclosed conditionals */
1841 fail = PARSE_FAIL_UNCLOSED_COND;
1842 break;
1845 wps_bufptr += skip_end_of_line(wps_bufptr);
1846 break;
1848 /* End of this line */
1849 case '\n':
1850 if (level >= 0) /* there are unclosed conditionals */
1852 fail = PARSE_FAIL_UNCLOSED_COND;
1853 break;
1855 /* add a new token for the \n so empty lines are correct */
1856 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1857 data->tokens[data->num_tokens].value.c = '\n';
1858 data->tokens[data->num_tokens].next = false;
1859 data->num_tokens++;
1861 if (!skin_start_new_line(curr_vp, data->num_tokens))
1863 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1864 break;
1866 line_number++;
1868 break;
1870 /* String */
1871 default:
1873 unsigned int len = 1;
1874 const char *string_start = wps_bufptr - 1;
1876 /* find the length of the string */
1877 while (*wps_bufptr && *wps_bufptr != '#' &&
1878 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1879 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1880 *wps_bufptr != '|' && *wps_bufptr != '\n')
1882 wps_bufptr++;
1883 len++;
1886 /* look if we already have that string */
1887 char *str;
1888 bool found = false;
1889 struct skin_token_list *list = data->strings;
1890 while (list)
1892 str = (char*)list->token->value.data;
1893 found = (strlen(str) == len &&
1894 strncmp(string_start, str, len) == 0);
1895 if (found)
1896 break; /* break here because the list item is
1897 used if its found */
1898 list = list->next;
1900 /* If a matching string is found, found is true and i is
1901 the index of the string. If not, found is false */
1903 if (!found)
1905 /* new string */
1906 str = (char*)skin_buffer_alloc(len+1);
1907 if (!str)
1909 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1910 break;
1912 strlcpy(str, string_start, len+1);
1913 struct skin_token_list *item =
1914 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1915 if(!item)
1917 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1918 break;
1920 add_to_ll_chain(&data->strings, item);
1922 else
1924 /* another occurrence of an existing string */
1925 data->tokens[data->num_tokens].value.data = list->token->value.data;
1927 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1928 data->num_tokens++;
1930 break;
1934 if (!fail && level >= 0) /* there are unclosed conditionals */
1935 fail = PARSE_FAIL_UNCLOSED_COND;
1937 if (*wps_bufptr && !fail)
1938 /* one of the limits of the while loop was exceeded */
1939 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1941 /* Success! */
1942 curr_line->curr_subline->last_token_idx = data->num_tokens;
1943 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1944 /* freeup unused tokens */
1945 skin_buffer_free_from_front(sizeof(struct wps_token)
1946 * (max_tokens - data->num_tokens));
1948 #if defined(DEBUG) || defined(SIMULATOR)
1949 if (debug)
1951 print_debug_info(data, fail, line_number);
1952 debug_skin_usage();
1954 #else
1955 (void)debug;
1956 #endif
1958 return (fail == 0);
1963 * initial setup of wps_data; does reset everything
1964 * except fields which need to survive, i.e.
1967 static void skin_data_reset(struct wps_data *wps_data)
1969 #ifdef HAVE_LCD_BITMAP
1970 wps_data->images = NULL;
1971 wps_data->progressbars = NULL;
1972 #endif
1973 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1974 wps_data->backdrop = NULL;
1975 #endif
1976 #ifdef HAVE_TOUCHSCREEN
1977 wps_data->touchregions = NULL;
1978 #endif
1979 wps_data->viewports = NULL;
1980 wps_data->strings = NULL;
1981 #ifdef HAVE_ALBUMART
1982 wps_data->albumart = NULL;
1983 if (wps_data->playback_aa_slot >= 0)
1985 playback_release_aa_slot(wps_data->playback_aa_slot);
1986 wps_data->playback_aa_slot = -1;
1988 #endif
1989 wps_data->tokens = NULL;
1990 wps_data->num_tokens = 0;
1992 #ifdef HAVE_LCD_BITMAP
1993 wps_data->peak_meter_enabled = false;
1994 wps_data->wps_sb_tag = false;
1995 wps_data->show_sb_on_wps = false;
1996 #else /* HAVE_LCD_CHARCELLS */
1997 /* progress bars */
1998 int i;
1999 for (i = 0; i < 8; i++)
2001 wps_data->wps_progress_pat[i] = 0;
2003 wps_data->full_line_progressbar = false;
2004 #endif
2005 wps_data->wps_loaded = false;
2008 #ifdef HAVE_LCD_BITMAP
2009 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
2011 (void)wps_data; /* only needed for remote targets */
2012 char img_path[MAX_PATH];
2013 get_image_filename(bitmap->data, bmpdir,
2014 img_path, sizeof(img_path));
2016 /* load the image */
2017 int format;
2018 #ifdef HAVE_REMOTE_LCD
2019 if (curr_screen == SCREEN_REMOTE)
2020 format = FORMAT_ANY|FORMAT_REMOTE;
2021 else
2022 #endif
2023 format = FORMAT_ANY|FORMAT_TRANSPARENT;
2025 size_t max_buf;
2026 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
2027 bitmap->data = imgbuf;
2028 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
2030 if (ret > 0)
2032 skin_buffer_increment(ret, true);
2033 return true;
2035 else
2037 /* Abort if we can't load an image */
2038 DEBUGF("Couldn't load '%s'\n", img_path);
2039 return false;
2043 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
2045 struct skin_token_list *list;
2046 bool retval = true; /* return false if a single image failed to load */
2047 /* do the progressbars */
2048 list = wps_data->progressbars;
2049 while (list)
2051 struct progressbar *pb = (struct progressbar*)list->token->value.data;
2052 if (pb->bm.data)
2054 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
2055 if (!pb->have_bitmap_pb) /* no success */
2056 retval = false;
2058 list = list->next;
2060 /* regular images */
2061 list = wps_data->images;
2062 while (list)
2064 struct gui_img *img = (struct gui_img*)list->token->value.data;
2065 if (img->bm.data)
2067 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
2068 if (img->loaded)
2069 img->subimage_height = img->bm.height / img->num_subimages;
2070 else
2071 retval = false;
2073 list = list->next;
2076 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2077 /* Backdrop load scheme:
2078 * 1) %X|filename|
2079 * 2) load the backdrop from settings
2081 if (wps_data->backdrop)
2083 bool needed = wps_data->backdrop[0] != '-';
2084 wps_data->backdrop = skin_backdrop_load(wps_data->backdrop,
2085 bmpdir, curr_screen);
2086 if (!wps_data->backdrop && needed)
2087 retval = false;
2089 #endif /* has backdrop support */
2091 return retval;
2094 static bool skin_load_fonts(struct wps_data *data)
2096 /* don't spit out after the first failue to aid debugging */
2097 bool success = true;
2098 struct skin_token_list *vp_list;
2099 int font_id;
2100 /* walk though each viewport and assign its font */
2101 for(vp_list = data->viewports; vp_list; vp_list = vp_list->next)
2103 /* first, find the viewports that have a non-sys/ui-font font */
2104 struct skin_viewport *skin_vp =
2105 (struct skin_viewport*)vp_list->token->value.data;
2106 struct viewport *vp = &skin_vp->vp;
2109 if (vp->font <= FONT_UI)
2110 { /* the usual case -> built-in fonts */
2111 #ifdef HAVE_REMOTE_LCD
2112 if (vp->font == FONT_UI)
2113 vp->font += curr_screen;
2114 #endif
2115 continue;
2117 font_id = vp->font;
2119 /* now find the corresponding skin_font */
2120 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
2121 if (!font->name)
2123 DEBUGF("font %d not specified\n", font_id);
2124 success = false;
2125 continue;
2128 /* load the font - will handle loading the same font again if
2129 * multiple viewports use the same */
2130 if (font->id < 0)
2132 char *dot = strchr(font->name, '.');
2133 *dot = '\0';
2134 font->id = skin_font_load(font->name);
2137 if (font->id < 0)
2139 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2140 font_id, font->name);
2141 success = false;
2142 continue;
2145 /* finally, assign the font_id to the viewport */
2146 vp->font = font->id;
2148 return success;
2151 #endif /* HAVE_LCD_BITMAP */
2153 /* to setup up the wps-data from a format-buffer (isfile = false)
2154 from a (wps-)file (isfile = true)*/
2155 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2156 const char *buf, bool isfile)
2159 if (!wps_data || !buf)
2160 return false;
2161 #ifdef HAVE_ALBUMART
2162 int status;
2163 struct mp3entry *curtrack;
2164 long offset;
2165 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2166 if (wps_data->albumart)
2168 old_aa.state = wps_data->albumart->state;
2169 old_aa.height = wps_data->albumart->height;
2170 old_aa.width = wps_data->albumart->width;
2172 #endif
2173 #ifdef HAVE_LCD_BITMAP
2174 int i;
2175 for (i=0;i<MAXUSERFONTS;i++)
2177 skinfonts[i].id = -1;
2178 skinfonts[i].name = NULL;
2180 #endif
2181 #ifdef DEBUG_SKIN_ENGINE
2182 if (isfile && debug_wps)
2184 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2186 #endif
2188 skin_data_reset(wps_data);
2189 curr_screen = screen;
2191 /* alloc default viewport, will be fixed up later */
2192 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
2193 if (!curr_vp)
2194 return false;
2195 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
2196 if (!list)
2197 return false;
2198 add_to_ll_chain(&wps_data->viewports, list);
2201 /* Initialise the first (default) viewport */
2202 curr_vp->label = VP_DEFAULT_LABEL;
2203 curr_vp->pb = NULL;
2204 curr_vp->hidden_flags = 0;
2205 curr_vp->lines = NULL;
2207 viewport_set_defaults(&curr_vp->vp, screen);
2208 #ifdef HAVE_LCD_BITMAP
2209 curr_vp->vp.font = FONT_UI;
2210 #endif
2212 curr_line = NULL;
2213 if (!skin_start_new_line(curr_vp, 0))
2214 return false;
2216 if (!isfile)
2218 if (wps_parse(wps_data, buf, false))
2220 #ifdef HAVE_LCD_BITMAP
2221 /* load the backdrop */
2222 if (!load_skin_bitmaps(wps_data, BACKDROP_DIR)) {
2223 skin_data_reset(wps_data);
2224 return false;
2226 if (!skin_load_fonts(wps_data))
2228 skin_data_reset(wps_data);
2229 return false;
2231 #endif
2232 return true;
2234 return false;
2236 else
2238 int fd = open_utf8(buf, O_RDONLY);
2240 if (fd < 0)
2241 return false;
2243 /* get buffer space from the plugin buffer */
2244 size_t buffersize = 0;
2245 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
2247 if (!wps_buffer)
2248 return false;
2250 /* copy the file's content to the buffer for parsing,
2251 ensuring that every line ends with a newline char. */
2252 unsigned int start = 0;
2253 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2255 start += strlen(wps_buffer + start);
2256 if (start < buffersize - 1)
2258 wps_buffer[start++] = '\n';
2259 wps_buffer[start] = 0;
2263 close(fd);
2265 if (start <= 0)
2266 return false;
2268 /* parse the WPS source */
2269 if (!wps_parse(wps_data, wps_buffer, true)) {
2270 skin_data_reset(wps_data);
2271 return false;
2274 wps_data->wps_loaded = true;
2276 #ifdef HAVE_LCD_BITMAP
2277 /* get the bitmap dir */
2278 char bmpdir[MAX_PATH];
2279 char *dot = strrchr(buf, '.');
2281 strlcpy(bmpdir, buf, dot - buf + 1);
2282 /* load the bitmaps that were found by the parsing */
2283 if (!load_skin_bitmaps(wps_data, bmpdir)) {
2284 skin_data_reset(wps_data);
2285 wps_data->wps_loaded = false;
2286 return false;
2288 if (!skin_load_fonts(wps_data))
2290 skin_data_reset(wps_data);
2291 wps_data->wps_loaded = false;
2292 return false;
2294 #endif
2295 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2296 status = audio_status();
2297 if (status & AUDIO_STATUS_PLAY)
2299 struct skin_albumart *aa = wps_data->albumart;
2300 if (aa && ((aa->state && !old_aa.state) ||
2301 (aa->state &&
2302 (((old_aa.height != aa->height) ||
2303 (old_aa.width != aa->width))))))
2305 curtrack = audio_current_track();
2306 offset = curtrack->offset;
2307 audio_stop();
2308 if (!(status & AUDIO_STATUS_PAUSE))
2309 audio_play(offset);
2312 #endif
2313 #if defined(DEBUG) || defined(SIMULATOR)
2314 debug_skin_usage();
2315 #endif
2316 return true;