1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
39 #define lang_is_rtl() (false)
51 #include "wps_internals.h"
52 #include "skin_engine.h"
54 #include "settings_list.h"
55 #include "skin_fonts.h"
57 #ifdef HAVE_LCD_BITMAP
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);
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
109 wps_bufptr points to the char following the tag (i.e. where
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
);
117 enum wps_token_type type
;
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
)
140 follow_lang_direction
= 2; /* 2 because it is decremented immediatly after
141 this token is parsed, after the next token it
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_image_display(const char *wps_bufptr
,
158 struct wps_token
*token
, struct wps_data
*wps_data
);
159 static int parse_image_load(const char *wps_bufptr
,
160 struct wps_token
*token
, struct wps_data
*wps_data
);
161 static int parse_font_load(const char *wps_bufptr
,
162 struct wps_token
*token
, struct wps_data
*wps_data
);
163 #endif /*HAVE_LCD_BITMAP */
164 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
165 static int parse_image_special(const char *wps_bufptr
,
166 struct wps_token
*token
, struct wps_data
*wps_data
);
169 static int parse_albumart_load(const char *wps_bufptr
,
170 struct wps_token
*token
, struct wps_data
*wps_data
);
171 static int parse_albumart_display(const char *wps_bufptr
,
172 struct wps_token
*token
, struct wps_data
*wps_data
);
173 #endif /* HAVE_ALBUMART */
174 #ifdef HAVE_TOUCHSCREEN
175 static int parse_touchregion(const char *wps_bufptr
,
176 struct wps_token
*token
, struct wps_data
*wps_data
);
178 static int fulline_tag_not_supported(const char *wps_bufptr
,
179 struct wps_token
*token
, struct wps_data
*wps_data
)
181 (void)token
; (void)wps_data
;
182 return skip_end_of_line(wps_bufptr
);
184 #define parse_touchregion fulline_tag_not_supported
187 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
189 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
192 /* array of available tags - those with more characters have to go first
193 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
194 static const struct wps_tag all_tags
[] = {
196 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
197 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
198 { WPS_TOKEN_ALIGN_LEFT_RTL
, "aL", 0, NULL
},
199 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
200 { WPS_TOKEN_ALIGN_RIGHT_RTL
, "aR", 0, NULL
},
201 { WPS_NO_TOKEN
, "ax", 0, parse_languagedirection
},
203 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
204 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
205 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
206 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
207 #if CONFIG_CHARGING >= CHARGING_MONITOR
208 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
211 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
213 #ifdef HAVE_USB_POWER
214 { WPS_TOKEN_USB_POWERED
, "bu", WPS_REFRESH_DYNAMIC
, NULL
},
217 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
218 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
219 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
220 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
221 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
222 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
223 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
224 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
225 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
226 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
227 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
228 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
229 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
230 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
231 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
232 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
233 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
234 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
235 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
238 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
243 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
244 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
245 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
251 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
257 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
258 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
263 /* current metadata */
264 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
270 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
271 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
272 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
273 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
274 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
275 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
278 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
279 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
280 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
281 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
282 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
283 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
284 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
285 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
286 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
287 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
288 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
289 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
291 #if (CONFIG_CODEC != MAS3507D)
292 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
294 #if (CONFIG_CODEC == SWCODEC)
295 { WPS_TOKEN_SOUND_SPEED
, "Ss", WPS_REFRESH_DYNAMIC
, NULL
},
297 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
298 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
301 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
303 #ifdef HAS_REMOTE_BUTTON_HOLD
304 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
306 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
309 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
310 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
311 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
314 #ifdef HAVE_LCD_BITMAP
315 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
317 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
318 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
320 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
323 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
325 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
326 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
327 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
328 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
330 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
331 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
332 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
333 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
336 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
337 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
338 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
341 #if CONFIG_CODEC == SWCODEC
342 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
343 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
346 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
347 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
349 #ifdef HAVE_LCD_BITMAP
350 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
351 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
353 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
355 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
356 parse_image_display
},
358 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
359 { WPS_NO_TOKEN
, "Fl", 0, parse_font_load
},
361 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
362 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
, parse_albumart_display
},
365 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
366 parse_viewport_display
},
367 #ifdef HAVE_LCD_BITMAP
368 { WPS_VIEWPORT_CUSTOMLIST
, "Vp", WPS_REFRESH_STATIC
, parse_playlistview
},
369 { WPS_TOKEN_LIST_TITLE_TEXT
, "Lt", WPS_REFRESH_DYNAMIC
, NULL
},
370 { WPS_TOKEN_LIST_TITLE_ICON
, "Li", WPS_REFRESH_DYNAMIC
, NULL
},
372 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
374 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
375 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
379 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
,
380 parse_setting_and_lang
},
381 { WPS_TOKEN_TRANSLATEDSTRING
, "Sx", WPS_REFRESH_STATIC
,
382 parse_setting_and_lang
},
383 { WPS_TOKEN_LANG_IS_RTL
, "Sr", WPS_REFRESH_STATIC
, NULL
},
385 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
386 { WPS_TOKEN_CURRENT_SCREEN
, "cs", WPS_REFRESH_DYNAMIC
, NULL
},
387 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
390 /* Recording Tokens */
391 { WPS_TOKEN_HAVE_RECORDING
, "Rp", WPS_REFRESH_STATIC
, NULL
},
392 #ifdef HAVE_RECORDING
393 { WPS_TOKEN_REC_FREQ
, "Rf", WPS_REFRESH_DYNAMIC
, NULL
},
394 { WPS_TOKEN_REC_ENCODER
, "Re", WPS_REFRESH_DYNAMIC
, NULL
},
395 { WPS_TOKEN_REC_BITRATE
, "Rb", WPS_REFRESH_DYNAMIC
, NULL
},
396 { WPS_TOKEN_REC_MONO
, "Rm", WPS_REFRESH_DYNAMIC
, NULL
},
398 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
399 /* the array MUST end with an empty string (first char is \0) */
403 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
404 * chains require the order to be kept.
406 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
412 struct skin_token_list
*t
= *list
;
419 /* traverse the image linked-list for an image */
420 #ifdef HAVE_LCD_BITMAP
421 struct gui_img
* find_image(char label
, struct wps_data
*data
)
423 struct skin_token_list
*list
= data
->images
;
426 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
427 if (img
->label
== label
)
436 /* traverse the viewport linked list for a viewport */
437 struct skin_viewport
* find_viewport(char label
, struct wps_data
*data
)
439 struct skin_token_list
*list
= data
->viewports
;
442 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->token
->value
.data
;
443 if (vp
->label
== label
)
451 /* create and init a new wpsll item.
452 * passing NULL to token will alloc a new one.
453 * You should only pass NULL for the token when the token type (table above)
454 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
456 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
459 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
461 token
= skin_buffer_alloc(sizeof(struct wps_token
));
462 if (!llitem
|| !token
)
465 llitem
->token
= token
;
467 llitem
->token
->value
.data
= token_data
;
471 /* Returns the number of chars that should be skipped to jump
472 immediately after the first eol, i.e. to the start of the next line */
473 static int skip_end_of_line(const char *wps_bufptr
)
477 while(*(wps_bufptr
+ skip
) != '\n')
482 /* Starts a new subline in the current line during parsing */
483 static bool skin_start_new_subline(struct skin_line
*line
, int curr_token
)
485 struct skin_subline
*subline
= skin_buffer_alloc(sizeof(struct skin_subline
));
489 subline
->first_token_idx
= curr_token
;
490 subline
->next
= NULL
;
492 subline
->line_type
= 0;
493 subline
->time_mult
= 0;
495 line
->curr_subline
->last_token_idx
= curr_token
-1;
496 line
->curr_subline
->next
= subline
;
497 line
->curr_subline
= subline
;
501 static bool skin_start_new_line(struct skin_viewport
*vp
, int curr_token
)
503 struct skin_line
*line
= skin_buffer_alloc(sizeof(struct skin_line
));
504 struct skin_subline
*subline
= NULL
;
508 /* init the subline */
509 subline
= &line
->sublines
;
510 subline
->first_token_idx
= curr_token
;
511 subline
->next
= NULL
;
512 subline
->line_type
= 0;
513 subline
->time_mult
= 0;
515 /* init the new line */
516 line
->curr_subline
= &line
->sublines
;
518 line
->subline_expire_time
= 0;
520 /* connect to curr_line and vp pointers.
521 * 1) close the previous lines subline
522 * 2) connect to vp pointer
523 * 3) connect to curr_line global pointer
527 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
528 curr_line
->next
= line
;
529 curr_line
->curr_subline
= NULL
;
537 #ifdef HAVE_LCD_BITMAP
539 static int parse_statusbar_enable(const char *wps_bufptr
,
540 struct wps_token
*token
,
541 struct wps_data
*wps_data
)
543 (void)token
; /* Kill warnings */
544 wps_data
->wps_sb_tag
= true;
545 wps_data
->show_sb_on_wps
= true;
546 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
547 viewport_set_defaults(&default_vp
->vp
, curr_screen
);
548 return skip_end_of_line(wps_bufptr
);
551 static int parse_statusbar_disable(const char *wps_bufptr
,
552 struct wps_token
*token
,
553 struct wps_data
*wps_data
)
555 (void)token
; /* Kill warnings */
556 wps_data
->wps_sb_tag
= true;
557 wps_data
->show_sb_on_wps
= false;
558 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
559 viewport_set_fullscreen(&default_vp
->vp
, curr_screen
);
560 return skip_end_of_line(wps_bufptr
);
563 static int get_image_id(int c
)
565 if(c
>= 'a' && c
<= 'z')
567 else if(c
>= 'A' && c
<= 'Z')
573 char *get_image_filename(const char *start
, const char* bmpdir
,
574 char *buf
, int buf_size
)
576 const char *end
= strchr(start
, '|');
577 int bmpdirlen
= strlen(bmpdir
);
579 if ( !end
|| (end
- start
) >= (buf_size
- bmpdirlen
- 2) )
586 buf
[bmpdirlen
] = '/';
587 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
588 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
593 static int parse_image_display(const char *wps_bufptr
,
594 struct wps_token
*token
,
595 struct wps_data
*wps_data
)
597 char label
= wps_bufptr
[0];
599 struct gui_img
*img
;;
602 img
= find_image(label
, wps_data
);
605 token
->value
.i
= label
; /* so debug works */
606 return WPS_ERROR_INVALID_PARAM
;
609 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
611 if (subimage
>= img
->num_subimages
)
612 return WPS_ERROR_INVALID_PARAM
;
614 /* Store sub-image number to display in high bits */
615 token
->value
.i
= label
| (subimage
<< 8);
616 return 2; /* We have consumed 2 bytes */
618 token
->value
.i
= label
;
619 return 1; /* We have consumed 1 byte */
623 static int parse_image_load(const char *wps_bufptr
,
624 struct wps_token
*token
,
625 struct wps_data
*wps_data
)
627 const char *ptr
= wps_bufptr
;
629 const char* filename
;
635 /* format: %x|n|filename.bmp|x|y|
636 or %xl|n|filename.bmp|x|y|
637 or %xl|n|filename.bmp|x|y|num_subimages|
641 return WPS_ERROR_INVALID_PARAM
;
645 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
646 return WPS_ERROR_INVALID_PARAM
;
648 /* Check there is a terminating | */
650 return WPS_ERROR_INVALID_PARAM
;
652 /* check the image number and load state */
653 if(find_image(*id
, wps_data
))
655 /* Invalid image ID */
656 return WPS_ERROR_INVALID_PARAM
;
658 img
= skin_buffer_alloc(sizeof(struct gui_img
));
660 return WPS_ERROR_INVALID_PARAM
;
661 /* save a pointer to the filename */
662 img
->bm
.data
= (char*)filename
;
666 img
->num_subimages
= 1;
667 img
->always_display
= false;
669 /* save current viewport */
670 img
->vp
= &curr_vp
->vp
;
672 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
674 img
->always_display
= true;
678 /* Parse the (optional) number of sub-images */
680 newline
= strchr(ptr
, '\n');
681 pos
= strchr(ptr
, '|');
682 if (pos
&& pos
< newline
)
683 img
->num_subimages
= atoi(ptr
);
685 if (img
->num_subimages
<= 0)
686 return WPS_ERROR_INVALID_PARAM
;
688 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
690 return WPS_ERROR_INVALID_PARAM
;
691 add_to_ll_chain(&wps_data
->images
, item
);
693 /* Skip the rest of the line */
694 return skip_end_of_line(wps_bufptr
);
697 int id
; /* the id from font_load */
698 char *name
; /* filename without path and extension */
700 static struct skin_font skinfonts
[MAXUSERFONTS
];
701 static int parse_font_load(const char *wps_bufptr
,
702 struct wps_token
*token
, struct wps_data
*wps_data
)
704 (void)wps_data
; (void)token
;
705 const char *ptr
= wps_bufptr
;
710 return WPS_ERROR_INVALID_PARAM
;
714 if (!(ptr
= parse_list("ds", NULL
, '|', ptr
, &id
, &filename
)))
715 return WPS_ERROR_INVALID_PARAM
;
717 /* Check there is a terminating | */
719 return WPS_ERROR_INVALID_PARAM
;
721 if (id
<= FONT_UI
|| id
>= MAXFONTS
-1)
722 return WPS_ERROR_INVALID_PARAM
;
723 #if defined(DEBUG) || defined(SIMULATOR)
724 if (skinfonts
[id
-FONT_FIRSTUSERFONT
].name
!= NULL
)
726 DEBUGF("font id %d already being used\n", id
);
729 skinfonts
[id
-FONT_FIRSTUSERFONT
].id
= -1;
730 skinfonts
[id
-FONT_FIRSTUSERFONT
].name
= filename
;
732 return skip_end_of_line(wps_bufptr
);
736 static int parse_viewport_display(const char *wps_bufptr
,
737 struct wps_token
*token
,
738 struct wps_data
*wps_data
)
741 char letter
= wps_bufptr
[0];
743 if (letter
< 'a' || letter
> 'z')
745 /* invalid viewport tag */
746 return WPS_ERROR_INVALID_PARAM
;
748 token
->value
.i
= letter
;
752 #ifdef HAVE_LCD_BITMAP
753 static int parse_playlistview_text(struct playlistviewer
*viewer
,
754 enum info_line_type line
, char* text
)
757 const struct wps_tag
*tag
;
759 const char *start
= text
;
763 viewer
->lines
[line
].count
= 0;
764 viewer
->lines
[line
].scroll
= false;
767 if (*text
== '%') /* it is a token of some type */
779 /* escaped characters */
780 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_CHARACTER
;
781 viewer
->lines
[line
].strings
[cur_string
][0] = *text
;
782 viewer
->lines
[line
].strings
[cur_string
++][1] = '\0';
786 strncmp(text
, tag
->name
, strlen(tag
->name
)) != 0;
788 /* %s isnt stored as a tag so manually check for it */
789 if (tag
->type
== WPS_NO_TOKEN
)
791 if (!strncmp(tag
->name
, "s", 1))
793 viewer
->lines
[line
].scroll
= true;
797 else if (tag
->type
== WPS_TOKEN_UNKNOWN
)
800 /* just copy the string */
801 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_STRING
;
802 while (i
<(MAX_PLAYLISTLINE_STRLEN
-1) && text
[i
] != '|' && text
[i
] != '%')
804 viewer
->lines
[line
].strings
[cur_string
][i
] = text
[i
];
807 viewer
->lines
[line
].strings
[cur_string
][i
] = '\0';
815 /* unsupported tag, reject */
818 taglen
= strlen(tag
->name
);
819 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = tag
->type
;
828 /* just copy the string */
829 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_STRING
;
830 while (i
<(MAX_PLAYLISTLINE_STRLEN
-1) && text
[i
] != '|' && text
[i
] != '%')
832 viewer
->lines
[line
].strings
[cur_string
][i
] = text
[i
];
835 viewer
->lines
[line
].strings
[cur_string
][i
] = '\0';
844 static int parse_playlistview(const char *wps_bufptr
,
845 struct wps_token
*token
, struct wps_data
*wps_data
)
848 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
849 struct playlistviewer
*viewer
= skin_buffer_alloc(sizeof(struct playlistviewer
));
850 char *ptr
= strchr(wps_bufptr
, '|');
853 return WPS_ERROR_INVALID_PARAM
;
854 viewer
->vp
= &curr_vp
->vp
;
855 viewer
->show_icons
= true;
856 viewer
->start_offset
= atoi(ptr
+1);
857 token
->value
.data
= (void*)viewer
;
858 ptr
= strchr(ptr
+1, '|');
859 length
= parse_playlistview_text(viewer
, TRACK_HAS_INFO
, ptr
);
861 return WPS_ERROR_INVALID_PARAM
;
862 length
= parse_playlistview_text(viewer
, TRACK_HAS_NO_INFO
, ptr
+length
);
864 return WPS_ERROR_INVALID_PARAM
;
866 return skip_end_of_line(wps_bufptr
);
870 static int parse_viewport(const char *wps_bufptr
,
871 struct wps_token
*token
,
872 struct wps_data
*wps_data
)
874 (void)token
; /* Kill warnings */
875 const char *ptr
= wps_bufptr
;
877 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
879 /* check for the optional letter to signify its a hideable viewport */
880 /* %Vl|<label>|<rest of tags>| */
881 skin_vp
->hidden_flags
= 0;
882 skin_vp
->label
= VP_NO_LABEL
;
884 skin_vp
->lines
= NULL
;
887 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
888 - (wps_data
->num_tokens
> 0 ? 1 : 0);
892 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
893 return WPS_ERROR_INVALID_PARAM
;
898 skin_vp
->label
= VP_INFO_LABEL
;
899 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
902 else if (*ptr
== 'l')
906 char label
= *(ptr
+2);
907 if (label
>= 'a' && label
<= 'z')
909 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
910 skin_vp
->label
= label
;
913 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
918 return WPS_ERROR_INVALID_PARAM
;
921 struct viewport
*vp
= &skin_vp
->vp
;
922 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
923 if (!(ptr
= viewport_parse_viewport(vp
, curr_screen
, ptr
, '|')))
924 return WPS_ERROR_INVALID_PARAM
;
926 /* Check for trailing | */
928 return WPS_ERROR_INVALID_PARAM
;
930 if (follow_lang_direction
&& lang_is_rtl())
932 vp
->flags
|= VP_FLAG_ALIGN_RIGHT
;
933 vp
->x
= screens
[curr_screen
].lcdwidth
- vp
->width
- vp
->x
;
936 vp
->flags
&= ~VP_FLAG_ALIGN_RIGHT
; /* ignore right-to-left languages */
938 /* increment because font==2 and FONT_UI_REMOTE is ambiguous */
939 if (vp
->font
> FONT_UI
)
941 #ifdef HAVE_REMOTE_LCD
942 if (vp
->font
== FONT_UI
&& curr_screen
== SCREEN_REMOTE
)
943 vp
->font
= FONT_UI_REMOTE
;
946 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
948 return WPS_ERROR_INVALID_PARAM
;
949 add_to_ll_chain(&wps_data
->viewports
, list
);
951 /* Skip the rest of the line */
952 return skip_end_of_line(wps_bufptr
);
955 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
956 static int parse_image_special(const char *wps_bufptr
,
957 struct wps_token
*token
,
958 struct wps_data
*wps_data
)
960 (void)wps_data
; /* kill warning */
962 const char *pos
= NULL
;
966 pos
= strchr(wps_bufptr
+ 1, '|');
967 newline
= strchr(wps_bufptr
, '\n');
969 error
= (pos
> newline
);
973 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
975 /* format: %X|filename.bmp| or %Xd */
976 if (*(wps_bufptr
) == 'd')
978 wps_data
->backdrop
= NULL
;
979 return skip_end_of_line(wps_bufptr
);
982 wps_data
->backdrop
= (char*)wps_bufptr
+ 1;
986 return WPS_ERROR_INVALID_PARAM
;
987 /* Skip the rest of the line */
988 return skip_end_of_line(wps_bufptr
);
992 #endif /* HAVE_LCD_BITMAP */
994 static int parse_setting_and_lang(const char *wps_bufptr
,
995 struct wps_token
*token
,
996 struct wps_data
*wps_data
)
998 /* NOTE: both the string validations that happen in here will
999 * automatically PASS on checkwps because its too hard to get
1000 * settings_list.c and englinsh.lang built for it.
1001 * If that ever changes remove the #ifndef __PCTOOL__'s here
1004 const char *ptr
= wps_bufptr
;
1009 /* Find the setting's cfg_name */
1011 return WPS_ERROR_INVALID_PARAM
;
1013 end
= strchr(ptr
,'|');
1015 return WPS_ERROR_INVALID_PARAM
;
1016 strlcpy(temp
, ptr
,end
-ptr
+1);
1018 if (token
->type
== WPS_TOKEN_TRANSLATEDSTRING
)
1021 i
= lang_english_to_id(temp
);
1023 return WPS_ERROR_INVALID_PARAM
;
1028 /* Find the setting */
1029 for (i
=0; i
<nb_settings
; i
++)
1030 if (settings
[i
].cfg_name
&&
1031 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
1032 /* prevent matches on cfg_name prefixes */
1033 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
1036 if (i
== nb_settings
)
1037 return WPS_ERROR_INVALID_PARAM
;
1040 /* Store the setting number */
1043 /* Skip the rest of the line */
1048 static int parse_dir_level(const char *wps_bufptr
,
1049 struct wps_token
*token
,
1050 struct wps_data
*wps_data
)
1052 char val
[] = { *wps_bufptr
, '\0' };
1053 token
->value
.i
= atoi(val
);
1054 (void)wps_data
; /* Kill warnings */
1058 static int parse_timeout(const char *wps_bufptr
,
1059 struct wps_token
*token
,
1060 struct wps_data
*wps_data
)
1064 bool have_point
= false;
1065 bool have_tenth
= false;
1067 (void)wps_data
; /* Kill the warning */
1069 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
1071 if (*wps_bufptr
!= '.')
1074 val
+= *wps_bufptr
- '0';
1090 if (have_tenth
== false)
1093 if (val
== 0 && skip
== 0)
1095 /* decide what to do if no value was specified */
1096 switch (token
->type
)
1098 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1100 case WPS_TOKEN_BUTTON_VOLUME
:
1105 token
->value
.i
= val
;
1110 static int parse_progressbar(const char *wps_bufptr
,
1111 struct wps_token
*token
,
1112 struct wps_data
*wps_data
)
1114 /* %pb or %pb|filename|x|y|width|height|
1115 using - for any of the params uses "sane" values */
1116 #ifdef HAVE_LCD_BITMAP
1124 const char *filename
;
1125 int x
, y
, height
, width
;
1127 const char *ptr
= wps_bufptr
;
1128 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
1129 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
1132 return WPS_ERROR_INVALID_PARAM
;
1134 struct viewport
*vp
= &curr_vp
->vp
;
1136 int font_height
= font_get(vp
->font
)->height
;
1138 int font_height
= 8;
1140 /* we need to know what line number (viewport relative) this pb is,
1141 * so count them... */
1143 struct skin_line
*line
= curr_vp
->lines
;
1149 pb
->have_bitmap_pb
= false;
1150 pb
->bm
.data
= NULL
; /* no bitmap specified */
1151 pb
->follow_lang_direction
= follow_lang_direction
> 0;
1153 if (*wps_bufptr
!= '|') /* regular old style */
1156 pb
->width
= vp
->width
;
1157 pb
->height
= SYSFONT_HEIGHT
-2;
1158 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1161 add_to_ll_chain(&wps_data
->progressbars
, item
);
1164 ptr
= wps_bufptr
+ 1;
1166 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
1167 &x
, &y
, &width
, &height
)))
1168 return WPS_ERROR_INVALID_PARAM
;
1170 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
1171 pb
->bm
.data
= (char*)filename
;
1173 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
1178 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
1180 /* A zero width causes a divide-by-zero error later, so reject it */
1182 return WPS_ERROR_INVALID_PARAM
;
1187 pb
->width
= vp
->width
- pb
->x
;
1189 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
1191 /* A zero height makes no sense - reject it */
1193 return WPS_ERROR_INVALID_PARAM
;
1195 pb
->height
= height
;
1198 pb
->height
= font_height
;
1200 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
1203 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1206 add_to_ll_chain(&wps_data
->progressbars
, item
);
1208 /* Skip the rest of the line */
1209 return skip_end_of_line(wps_bufptr
)-1;
1213 if (*(wps_bufptr
-1) == 'f')
1214 wps_data
->full_line_progressbar
= true;
1216 wps_data
->full_line_progressbar
= false;
1223 #ifdef HAVE_ALBUMART
1224 static int parse_int(const char *newline
, const char **_pos
, int *num
)
1226 *_pos
= parse_list("d", NULL
, '|', *_pos
, num
);
1228 return (!*_pos
|| *_pos
> newline
|| **_pos
!= '|');
1231 static int parse_albumart_load(const char *wps_bufptr
,
1232 struct wps_token
*token
,
1233 struct wps_data
*wps_data
)
1235 const char *_pos
, *newline
;
1237 struct dim dimensions
;
1239 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
1240 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
1241 (void)token
; /* silence warning */
1243 return skip_end_of_line(wps_bufptr
);
1245 /* reset albumart info in wps */
1248 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1249 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1250 aa
->vp
= &curr_vp
->vp
;
1252 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1254 newline
= strchr(wps_bufptr
, '\n');
1259 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1263 /* initial validation and parsing of x component */
1264 if (parse_int(newline
, &_pos
, &aa
->x
))
1265 return WPS_ERROR_INVALID_PARAM
;
1269 /* initial validation and parsing of y component */
1270 if (parse_int(newline
, &_pos
, &aa
->y
))
1271 return WPS_ERROR_INVALID_PARAM
;
1273 /* parsing width field */
1277 /* apply each modifier in turn */
1285 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1287 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1291 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1297 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1299 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1307 /* simply ignored */
1314 /* extract max width data */
1317 if (parse_int(newline
, &_pos
, &aa
->width
))
1318 return WPS_ERROR_INVALID_PARAM
;
1321 /* parsing height field */
1325 /* apply each modifier in turn */
1332 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1336 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1341 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1349 /* simply ignored */
1356 /* extract max height data */
1359 if (parse_int(newline
, &_pos
, &aa
->height
))
1360 return WPS_ERROR_INVALID_PARAM
;
1363 /* if we got here, we parsed everything ok .. ! */
1366 else if (aa
->width
> LCD_WIDTH
)
1367 aa
->width
= LCD_WIDTH
;
1371 else if (aa
->height
> LCD_HEIGHT
)
1372 aa
->height
= LCD_HEIGHT
;
1375 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
1377 aa
->state
= WPS_ALBUMART_LOAD
;
1379 wps_data
->albumart
= aa
;
1381 dimensions
.width
= aa
->width
;
1382 dimensions
.height
= aa
->height
;
1384 albumart_slot
= playback_claim_aa_slot(&dimensions
);
1386 if (0 <= albumart_slot
)
1387 wps_data
->playback_aa_slot
= albumart_slot
;
1389 /* Skip the rest of the line */
1390 return skip_end_of_line(wps_bufptr
);
1393 static int parse_albumart_display(const char *wps_bufptr
,
1394 struct wps_token
*token
,
1395 struct wps_data
*wps_data
)
1398 struct wps_token
*prev
= token
-1;
1399 if ((wps_data
->num_tokens
>= 1) && (prev
->type
== WPS_TOKEN_CONDITIONAL
))
1401 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1403 else if (wps_data
->albumart
)
1405 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1408 /* the old code did this so keep it here for now...
1409 * this is to allow the posibility to showing the next tracks AA! */
1410 if (wps_bufptr
+1 == 'n')
1415 #endif /* HAVE_ALBUMART */
1417 #ifdef HAVE_TOUCHSCREEN
1419 struct touchaction
{const char* s
; int action
;};
1420 static const struct touchaction touchactions
[] = {
1421 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1422 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1423 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1424 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1425 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1426 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1427 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1428 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
1430 static int parse_touchregion(const char *wps_bufptr
,
1431 struct wps_token
*token
, struct wps_data
*wps_data
)
1435 struct touchregion
*region
= NULL
;
1436 const char *ptr
= wps_bufptr
;
1438 const char pb_string
[] = "progressbar";
1439 const char vol_string
[] = "volume";
1442 /* format: %T|x|y|width|height|action|
1443 * if action starts with & the area must be held to happen
1445 * play - play/pause playback
1446 * stop - stop playback, exit the wps
1449 * ffwd - seek forward
1450 * rwd - seek backwards
1451 * menu - go back to the main menu
1452 * browse - go back to the file/db browser
1453 * shuffle - toggle shuffle mode
1454 * repmode - cycle the repeat mode
1455 * quickscreen - go into the quickscreen
1456 * contextmenu - open the context menu
1457 * playlist - go into the playlist
1458 * pitch - go into the pitchscreen
1459 * volup - increase volume by one step
1460 * voldown - decrease volume by one step
1465 return WPS_ERROR_INVALID_PARAM
;
1468 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1469 return WPS_ERROR_INVALID_PARAM
;
1471 /* Check there is a terminating | */
1473 return WPS_ERROR_INVALID_PARAM
;
1475 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1477 return WPS_ERROR_INVALID_PARAM
;
1479 /* should probably do some bounds checking here with the viewport... but later */
1480 region
->action
= ACTION_NONE
;
1485 region
->wvp
= curr_vp
;
1486 region
->armed
= false;
1488 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1489 && *(action
+ sizeof(pb_string
)-1) == '|')
1490 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1491 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1492 && *(action
+ sizeof(vol_string
)-1) == '|')
1493 region
->type
= WPS_TOUCHREGION_VOLUME
;
1496 region
->type
= WPS_TOUCHREGION_ACTION
;
1501 region
->repeat
= true;
1504 region
->repeat
= false;
1507 imax
= ARRAYLEN(touchactions
);
1508 while ((region
->action
== ACTION_NONE
) &&
1511 /* try to match with one of our touchregion screens */
1512 int len
= strlen(touchactions
[i
].s
);
1513 if (!strncmp(touchactions
[i
].s
, action
, len
)
1514 && *(action
+len
) == '|')
1515 region
->action
= touchactions
[i
].action
;
1518 if (region
->action
== ACTION_NONE
)
1519 return WPS_ERROR_INVALID_PARAM
;
1521 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1523 return WPS_ERROR_INVALID_PARAM
;
1524 add_to_ll_chain(&wps_data
->touchregions
, item
);
1525 return skip_end_of_line(wps_bufptr
);
1529 /* Parse a generic token from the given string. Return the length read */
1530 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1532 int skip
= 0, taglen
= 0, ret
;
1533 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1534 const struct wps_tag
*tag
;
1535 memset(token
, 0, sizeof(*token
));
1546 /* escaped characters */
1547 token
->type
= WPS_TOKEN_CHARACTER
;
1548 token
->value
.c
= *wps_bufptr
;
1550 wps_data
->num_tokens
++;
1554 /* conditional tag */
1555 token
->type
= WPS_TOKEN_CONDITIONAL
;
1557 condindex
[level
] = wps_data
->num_tokens
;
1558 numoptions
[level
] = 1;
1559 wps_data
->num_tokens
++;
1560 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1561 if (ret
< 0) return ret
;
1566 /* find what tag we have */
1567 for (tag
= all_tags
;
1568 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1571 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1572 token
->type
= tag
->type
;
1573 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1575 /* if the tag has a special parsing function, we call it */
1576 if (tag
->parse_func
)
1578 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1579 if (ret
< 0) return ret
;
1583 /* Some tags we don't want to save as tokens */
1584 if (tag
->type
== WPS_NO_TOKEN
)
1587 /* tags that start with 'F', 'I' or 'D' are for the next file */
1588 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1589 *(tag
->name
) == 'D')
1592 wps_data
->num_tokens
++;
1602 * Returns the number of bytes to skip the buf pointer to access the false
1603 * branch in a _binary_ conditional
1606 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1607 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1609 * depending on the features of a target it's not called from check_feature_tag,
1610 * hence the __attribute__ or it issues compiler warnings
1614 static int find_false_branch(const char *wps_bufptr
) __attribute__((unused
));
1615 static int find_false_branch(const char *wps_bufptr
)
1617 const char *buf
= wps_bufptr
;
1618 /* wps_bufptr is after the opening '<', hence level = 1*/
1625 { /* filter out the characters we check later if they're printed
1628 if (ch
== '<' || ch
== '>' || ch
== '|')
1630 /* else: some tags/printed literals we skip over */
1632 else if (ch
== '<') /* nested conditional */
1635 { /* closed our or a nested conditional,
1636 * do NOT skip over the '>' so that wps_parse() sees it for closing
1637 * if it is the closing one for our conditional */
1640 else if (ch
== '|' && level
== 1)
1641 { /* we found our separator, point before and get out */
1644 /* if level is 0, we don't have a false branch */
1645 } while (level
> 0 && *(++buf
));
1647 return buf
- wps_bufptr
;
1651 * returns the number of bytes to get the appropriate branch of a binary
1655 * - if a feature is available, it returns 0 to not skip anything
1656 * - if the feature is not available, skip to the false branch and don't
1657 * parse the true branch at all
1660 static int check_feature_tag(const char *wps_bufptr
, const int type
)
1665 case WPS_TOKEN_RTC_PRESENT
:
1669 return find_false_branch(wps_bufptr
);
1671 case WPS_TOKEN_HAVE_RECORDING
:
1672 #ifdef HAVE_RECORDING
1675 return find_false_branch(wps_bufptr
);
1677 default: /* not a tag we care about, just don't skip */
1684 data is the pointer to the structure where the parsed WPS should be stored.
1686 wps_bufptr points to the string containing the WPS tags */
1687 #define TOKEN_BLOCK_SIZE 128
1688 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1690 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1692 enum wps_parse_error fail
= PARSE_OK
;
1694 int max_tokens
= TOKEN_BLOCK_SIZE
;
1695 size_t buf_free
= 0;
1699 /* allocate enough RAM for a reasonable skin, grow as needed.
1700 * Free any used RAM before loading the images to be 100% RAM efficient */
1701 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1702 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1704 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1705 data
->num_tokens
= 0;
1708 /* Backdrop defaults to the setting unless %X is used, so set it now */
1709 if (global_settings
.backdrop_file
[0])
1711 data
->backdrop
= "-";
1715 while (*wps_bufptr
&& !fail
)
1717 if (follow_lang_direction
)
1718 follow_lang_direction
--;
1719 /* first make sure there is enough room for tokens */
1720 if (max_tokens
<= data
->num_tokens
+ 5)
1722 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1723 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1724 /* do some smarts here to grow the array a bit */
1725 if (skin_buffer_freespace() < needed
)
1727 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1730 skin_buffer_increment(needed
, false);
1731 max_tokens
+= extra_tokens
;
1734 switch(*wps_bufptr
++)
1739 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1741 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1744 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1746 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1752 /* Alternating sublines separator */
1754 if (level
>= 0) /* there are unclosed conditionals */
1756 fail
= PARSE_FAIL_UNCLOSED_COND
;
1760 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1761 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1765 /* Conditional list start */
1767 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1769 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1772 wps_bufptr
+= check_feature_tag(wps_bufptr
,
1773 data
->tokens
[data
->num_tokens
-1].type
);
1774 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1775 lastcond
[level
] = data
->num_tokens
++;
1778 /* Conditional list end */
1780 if (level
< 0) /* not in a conditional, invalid char */
1782 fail
= PARSE_FAIL_INVALID_CHAR
;
1786 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1787 if (lastcond
[level
])
1788 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1791 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1795 lastcond
[level
] = 0;
1797 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1801 /* Conditional list option */
1803 if (level
< 0) /* not in a conditional, invalid char */
1805 fail
= PARSE_FAIL_INVALID_CHAR
;
1809 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1810 if (lastcond
[level
])
1811 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1814 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1818 lastcond
[level
] = data
->num_tokens
;
1819 numoptions
[level
]++;
1825 if (level
>= 0) /* there are unclosed conditionals */
1827 fail
= PARSE_FAIL_UNCLOSED_COND
;
1831 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1834 /* End of this line */
1836 if (level
>= 0) /* there are unclosed conditionals */
1838 fail
= PARSE_FAIL_UNCLOSED_COND
;
1841 /* add a new token for the \n so empty lines are correct */
1842 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1843 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1844 data
->tokens
[data
->num_tokens
].next
= false;
1847 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1849 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1859 unsigned int len
= 1;
1860 const char *string_start
= wps_bufptr
- 1;
1862 /* find the length of the string */
1863 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1864 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1865 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1866 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1872 /* look if we already have that string */
1875 struct skin_token_list
*list
= data
->strings
;
1878 str
= (char*)list
->token
->value
.data
;
1879 found
= (strlen(str
) == len
&&
1880 strncmp(string_start
, str
, len
) == 0);
1882 break; /* break here because the list item is
1883 used if its found */
1886 /* If a matching string is found, found is true and i is
1887 the index of the string. If not, found is false */
1892 str
= (char*)skin_buffer_alloc(len
+1);
1895 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1898 strlcpy(str
, string_start
, len
+1);
1899 struct skin_token_list
*item
=
1900 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1903 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1906 add_to_ll_chain(&data
->strings
, item
);
1910 /* another occurrence of an existing string */
1911 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1913 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1920 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1921 fail
= PARSE_FAIL_UNCLOSED_COND
;
1923 if (*wps_bufptr
&& !fail
)
1924 /* one of the limits of the while loop was exceeded */
1925 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1928 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
1929 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
1930 /* freeup unused tokens */
1931 skin_buffer_free_from_front(sizeof(struct wps_token
)
1932 * (max_tokens
- data
->num_tokens
));
1934 #if defined(DEBUG) || defined(SIMULATOR)
1937 print_debug_info(data
, fail
, line_number
);
1949 * initial setup of wps_data; does reset everything
1950 * except fields which need to survive, i.e.
1953 static void skin_data_reset(struct wps_data
*wps_data
)
1955 #ifdef HAVE_LCD_BITMAP
1956 wps_data
->images
= NULL
;
1957 wps_data
->progressbars
= NULL
;
1959 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1960 wps_data
->backdrop
= NULL
;
1962 #ifdef HAVE_TOUCHSCREEN
1963 wps_data
->touchregions
= NULL
;
1965 wps_data
->viewports
= NULL
;
1966 wps_data
->strings
= NULL
;
1967 #ifdef HAVE_ALBUMART
1968 wps_data
->albumart
= NULL
;
1969 if (wps_data
->playback_aa_slot
>= 0)
1971 playback_release_aa_slot(wps_data
->playback_aa_slot
);
1972 wps_data
->playback_aa_slot
= -1;
1975 wps_data
->tokens
= NULL
;
1976 wps_data
->num_tokens
= 0;
1978 #ifdef HAVE_LCD_BITMAP
1979 wps_data
->peak_meter_enabled
= false;
1980 wps_data
->wps_sb_tag
= false;
1981 wps_data
->show_sb_on_wps
= false;
1982 #else /* HAVE_LCD_CHARCELLS */
1985 for (i
= 0; i
< 8; i
++)
1987 wps_data
->wps_progress_pat
[i
] = 0;
1989 wps_data
->full_line_progressbar
= false;
1991 wps_data
->wps_loaded
= false;
1994 #ifdef HAVE_LCD_BITMAP
1995 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1997 (void)wps_data
; /* only needed for remote targets */
1998 char img_path
[MAX_PATH
];
1999 get_image_filename(bitmap
->data
, bmpdir
,
2000 img_path
, sizeof(img_path
));
2002 /* load the image */
2004 #ifdef HAVE_REMOTE_LCD
2005 if (curr_screen
== SCREEN_REMOTE
)
2006 format
= FORMAT_ANY
|FORMAT_REMOTE
;
2009 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
2012 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
2013 bitmap
->data
= imgbuf
;
2014 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
2018 skin_buffer_increment(ret
, true);
2023 /* Abort if we can't load an image */
2024 DEBUGF("Couldn't load '%s'\n", img_path
);
2029 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
2031 struct skin_token_list
*list
;
2032 bool retval
= true; /* return false if a single image failed to load */
2033 /* do the progressbars */
2034 list
= wps_data
->progressbars
;
2037 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
2040 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
2041 if (!pb
->have_bitmap_pb
) /* no success */
2046 /* regular images */
2047 list
= wps_data
->images
;
2050 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
2053 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
2055 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
2062 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2063 /* Backdrop load scheme:
2065 * 2) load the backdrop from settings
2067 if (wps_data
->backdrop
)
2069 bool needed
= wps_data
->backdrop
[0] != '-';
2070 wps_data
->backdrop
= skin_backdrop_load(wps_data
->backdrop
,
2071 bmpdir
, curr_screen
);
2072 if (!wps_data
->backdrop
&& needed
)
2075 #endif /* has backdrop support */
2080 static bool skin_load_fonts(struct wps_data
*data
)
2082 /* don't spit out after the first failue to aid debugging */
2083 bool success
= true;
2084 struct skin_token_list
*vp_list
;
2085 /* walk though each viewport and assign its font */
2086 for(vp_list
= data
->viewports
; vp_list
; vp_list
= vp_list
->next
)
2088 /* first, find the viewports that have a non-sys/ui-font font */
2089 struct skin_viewport
*skin_vp
=
2090 (struct skin_viewport
*)vp_list
->token
->value
.data
;
2091 struct viewport
*vp
= &skin_vp
->vp
;
2094 if (vp
->font
< SYSTEMFONTCOUNT
)
2095 { /* the usual case -> built-in fonts */
2099 /* decrement, because font has been incremented in viewport parsing
2100 * due to the FONT_UI_REMOTE ambiguity */
2101 int skin_font_id
= vp
->font
-1;
2103 /* now find the corresponding skin_font */
2104 struct skin_font
*font
= &skinfonts
[skin_font_id
-FONT_FIRSTUSERFONT
];
2107 DEBUGF("Could not find font %d\n", skin_font_id
);
2112 /* load the font - will handle loading the same font again if
2113 * multiple viewports use the same */
2116 char *bar
= strchr(font
->name
, '|');
2118 font
->id
= skin_font_load(font
->name
);
2123 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2124 skin_font_id
, font
->name
);
2129 /* finally, assign the font_id to the viewport */
2130 vp
->font
= font
->id
;
2135 #endif /* HAVE_LCD_BITMAP */
2137 /* to setup up the wps-data from a format-buffer (isfile = false)
2138 from a (wps-)file (isfile = true)*/
2139 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
2140 const char *buf
, bool isfile
)
2143 if (!wps_data
|| !buf
)
2145 #ifdef HAVE_ALBUMART
2147 struct mp3entry
*curtrack
;
2149 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
2150 if (wps_data
->albumart
)
2152 old_aa
.state
= wps_data
->albumart
->state
;
2153 old_aa
.height
= wps_data
->albumart
->height
;
2154 old_aa
.width
= wps_data
->albumart
->width
;
2158 skin_data_reset(wps_data
);
2159 curr_screen
= screen
;
2161 /* alloc default viewport, will be fixed up later */
2162 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
2165 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
2168 add_to_ll_chain(&wps_data
->viewports
, list
);
2171 /* Initialise the first (default) viewport */
2172 curr_vp
->label
= VP_DEFAULT_LABEL
;
2174 curr_vp
->hidden_flags
= 0;
2175 curr_vp
->lines
= NULL
;
2177 viewport_set_defaults(&curr_vp
->vp
, screen
);
2180 if (!skin_start_new_line(curr_vp
, 0))
2185 if (wps_parse(wps_data
, buf
, false))
2187 #ifdef HAVE_LCD_BITMAP
2188 /* load the backdrop */
2189 if (!load_skin_bitmaps(wps_data
, BACKDROP_DIR
)) {
2190 skin_data_reset(wps_data
);
2200 int fd
= open_utf8(buf
, O_RDONLY
);
2205 /* get buffer space from the plugin buffer */
2206 size_t buffersize
= 0;
2207 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
2212 /* copy the file's content to the buffer for parsing,
2213 ensuring that every line ends with a newline char. */
2214 unsigned int start
= 0;
2215 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
2217 start
+= strlen(wps_buffer
+ start
);
2218 if (start
< buffersize
- 1)
2220 wps_buffer
[start
++] = '\n';
2221 wps_buffer
[start
] = 0;
2230 /* parse the WPS source */
2231 if (!wps_parse(wps_data
, wps_buffer
, true)) {
2232 skin_data_reset(wps_data
);
2236 wps_data
->wps_loaded
= true;
2238 #ifdef HAVE_LCD_BITMAP
2239 /* get the bitmap dir */
2240 char bmpdir
[MAX_PATH
];
2241 char *dot
= strrchr(buf
, '.');
2243 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
2244 /* load the bitmaps that were found by the parsing */
2245 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
2246 skin_data_reset(wps_data
);
2247 wps_data
->wps_loaded
= false;
2250 if (!skin_load_fonts(wps_data
))
2252 skin_data_reset(wps_data
);
2253 wps_data
->wps_loaded
= false;
2257 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2258 status
= audio_status();
2259 if (status
& AUDIO_STATUS_PLAY
)
2261 struct skin_albumart
*aa
= wps_data
->albumart
;
2262 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
2264 (((old_aa
.height
!= aa
->height
) ||
2265 (old_aa
.width
!= aa
->width
))))))
2267 curtrack
= audio_current_track();
2268 offset
= curtrack
->offset
;
2270 if (!(status
& AUDIO_STATUS_PAUSE
))
2275 #if defined(DEBUG) || defined(SIMULATOR)