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"
59 #include "skin_fonts.h"
61 #ifdef HAVE_LCD_BITMAP
70 #include "statusbar-skinned.h"
72 #define WPS_ERROR_INVALID_PARAM -1
74 /* which screen are we parsing for? */
75 static enum screen_type curr_screen
;
77 /* level of current conditional.
78 -1 means we're not in a conditional. */
79 static int level
= -1;
81 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
82 or WPS_TOKEN_CONDITIONAL_START in current level */
83 static int lastcond
[WPS_MAX_COND_LEVEL
];
85 /* index of the WPS_TOKEN_CONDITIONAL in current level */
86 static int condindex
[WPS_MAX_COND_LEVEL
];
88 /* number of condtional options in current level */
89 static int numoptions
[WPS_MAX_COND_LEVEL
];
91 /* line number, debug only */
92 static int line_number
;
94 /* the current viewport */
95 static struct skin_viewport
*curr_vp
;
96 /* the current line, linked to the above viewport */
97 static struct skin_line
*curr_line
;
99 static int follow_lang_direction
= 0;
101 #if defined(DEBUG) || defined(SIMULATOR)
102 /* debugging function */
103 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
104 extern void debug_skin_usage(void);
107 /* Function for parsing of details for a token. At the moment the
108 function is called, the token type has already been set. The
109 function must fill in the details and possibly add more tokens
110 to the token array. It should return the number of chars that
113 wps_bufptr points to the char following the tag (i.e. where
115 token is the pointer to the 'main' token being parsed
117 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
118 struct wps_token
*token
, struct wps_data
*wps_data
);
121 enum wps_token_type type
;
123 unsigned char refresh_type
;
124 const wps_tag_parse_func parse_func
;
126 static int skip_end_of_line(const char *wps_bufptr
);
127 /* prototypes of all special parse functions : */
128 static int parse_timeout(const char *wps_bufptr
,
129 struct wps_token
*token
, struct wps_data
*wps_data
);
130 static int parse_progressbar(const char *wps_bufptr
,
131 struct wps_token
*token
, struct wps_data
*wps_data
);
132 static int parse_dir_level(const char *wps_bufptr
,
133 struct wps_token
*token
, struct wps_data
*wps_data
);
134 static int parse_setting_and_lang(const char *wps_bufptr
,
135 struct wps_token
*token
, struct wps_data
*wps_data
);
138 static int parse_languagedirection(const char *wps_bufptr
,
139 struct wps_token
*token
, struct wps_data
*wps_data
)
144 follow_lang_direction
= 2; /* 2 because it is decremented immediatly after
145 this token is parsed, after the next token it
150 #ifdef HAVE_LCD_BITMAP
151 static int parse_viewport_display(const char *wps_bufptr
,
152 struct wps_token
*token
, struct wps_data
*wps_data
);
153 static int parse_playlistview(const char *wps_bufptr
,
154 struct wps_token
*token
, struct wps_data
*wps_data
);
155 static int parse_viewport(const char *wps_bufptr
,
156 struct wps_token
*token
, struct wps_data
*wps_data
);
157 static int parse_statusbar_enable(const char *wps_bufptr
,
158 struct wps_token
*token
, struct wps_data
*wps_data
);
159 static int parse_statusbar_disable(const char *wps_bufptr
,
160 struct wps_token
*token
, struct wps_data
*wps_data
);
161 static int parse_statusbar_inbuilt(const char *wps_bufptr
,
162 struct wps_token
*token
, struct wps_data
*wps_data
);
163 static int parse_image_display(const char *wps_bufptr
,
164 struct wps_token
*token
, struct wps_data
*wps_data
);
165 static int parse_image_load(const char *wps_bufptr
,
166 struct wps_token
*token
, struct wps_data
*wps_data
);
167 static int parse_font_load(const char *wps_bufptr
,
168 struct wps_token
*token
, struct wps_data
*wps_data
);
169 #endif /*HAVE_LCD_BITMAP */
170 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
171 static int parse_viewportcolour(const char *wps_bufptr
,
172 struct wps_token
*token
, struct wps_data
*wps_data
);
173 static int parse_image_special(const char *wps_bufptr
,
174 struct wps_token
*token
, struct wps_data
*wps_data
);
177 static int parse_albumart_load(const char *wps_bufptr
,
178 struct wps_token
*token
, struct wps_data
*wps_data
);
179 static int parse_albumart_display(const char *wps_bufptr
,
180 struct wps_token
*token
, struct wps_data
*wps_data
);
181 #endif /* HAVE_ALBUMART */
182 #ifdef HAVE_TOUCHSCREEN
183 static int parse_touchregion(const char *wps_bufptr
,
184 struct wps_token
*token
, struct wps_data
*wps_data
);
186 static int fulline_tag_not_supported(const char *wps_bufptr
,
187 struct wps_token
*token
, struct wps_data
*wps_data
)
189 (void)token
; (void)wps_data
;
190 return skip_end_of_line(wps_bufptr
);
192 #define parse_touchregion fulline_tag_not_supported
195 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
197 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
200 /* array of available tags - those with more characters have to go first
201 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
202 static const struct wps_tag all_tags
[] = {
204 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
205 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
206 { WPS_TOKEN_ALIGN_LEFT_RTL
, "aL", 0, NULL
},
207 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
208 { WPS_TOKEN_ALIGN_RIGHT_RTL
, "aR", 0, NULL
},
209 { WPS_NO_TOKEN
, "ax", 0, parse_languagedirection
},
211 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, parse_progressbar
},
212 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
213 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
214 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
215 #if CONFIG_CHARGING >= CHARGING_MONITOR
216 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
219 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
221 #ifdef HAVE_USB_POWER
222 { WPS_TOKEN_USB_POWERED
, "bu", WPS_REFRESH_DYNAMIC
, NULL
},
225 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
226 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
227 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
228 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
229 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
230 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
231 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
232 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
233 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
234 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
235 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
236 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
237 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
238 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
239 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
240 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
241 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
242 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
243 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
246 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
259 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
271 /* current metadata */
272 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
273 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
274 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
275 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
276 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
277 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
278 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
279 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
280 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
281 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
282 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
283 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
286 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
287 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
288 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
289 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
290 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
291 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
292 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
293 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
294 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
295 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
296 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
297 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
299 #if (CONFIG_CODEC != MAS3507D)
300 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
302 #if (CONFIG_CODEC == SWCODEC)
303 { WPS_TOKEN_SOUND_SPEED
, "Ss", WPS_REFRESH_DYNAMIC
, NULL
},
305 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
306 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
309 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
311 #ifdef HAS_REMOTE_BUTTON_HOLD
312 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
314 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
317 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
318 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
319 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
322 #ifdef HAVE_LCD_BITMAP
323 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
325 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
326 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
328 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
331 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
,
334 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
335 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
336 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
337 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
338 { WPS_TOKEN_TRACK_STARTING
, "pS", WPS_REFRESH_DYNAMIC
, parse_timeout
},
339 { WPS_TOKEN_TRACK_ENDING
, "pE", WPS_REFRESH_DYNAMIC
, parse_timeout
},
341 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
342 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
343 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
344 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
347 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
348 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
349 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
352 #if CONFIG_CODEC == SWCODEC
353 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
354 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
357 { WPS_TOKEN_HAVE_TUNER
, "tp", WPS_REFRESH_STATIC
, NULL
},
358 #if CONFIG_TUNER /* Re-uses the 't' and 'T' prefixes, be careful about doubleups */
359 { WPS_TOKEN_TUNER_TUNED
, "tt", WPS_REFRESH_DYNAMIC
, NULL
},
360 { WPS_TOKEN_TUNER_SCANMODE
, "tm", WPS_REFRESH_DYNAMIC
, NULL
},
361 { WPS_TOKEN_TUNER_STEREO
, "ts", WPS_REFRESH_DYNAMIC
, NULL
},
362 { WPS_TOKEN_TUNER_MINFREQ
, "ta", WPS_REFRESH_STATIC
, NULL
},
363 { WPS_TOKEN_TUNER_MAXFREQ
, "tb", WPS_REFRESH_STATIC
, NULL
},
364 { WPS_TOKEN_TUNER_CURFREQ
, "tf", WPS_REFRESH_DYNAMIC
, NULL
},
365 { WPS_TOKEN_PRESET_ID
, "Ti", WPS_REFRESH_STATIC
, NULL
},
366 { WPS_TOKEN_PRESET_NAME
, "Tn", WPS_REFRESH_STATIC
, NULL
},
367 { WPS_TOKEN_PRESET_FREQ
, "Tf", WPS_REFRESH_STATIC
, NULL
},
368 { WPS_TOKEN_PRESET_COUNT
, "Tc", WPS_REFRESH_STATIC
, NULL
},
369 { WPS_TOKEN_HAVE_RDS
, "tx", WPS_REFRESH_STATIC
, NULL
},
371 { WPS_TOKEN_RDS_NAME
, "ty", WPS_REFRESH_DYNAMIC
, NULL
},
372 { WPS_TOKEN_RDS_TEXT
, "tz", WPS_REFRESH_DYNAMIC
, NULL
},
374 #endif /* CONFIG_TUNER */
376 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
377 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
379 #ifdef HAVE_LCD_BITMAP
380 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
381 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
382 { WPS_TOKEN_DRAW_INBUILTBAR
, "wi", WPS_REFRESH_DYNAMIC
, parse_statusbar_inbuilt
},
384 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
386 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
387 parse_image_display
},
389 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
390 { WPS_NO_TOKEN
, "Fl", 0, parse_font_load
},
392 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
393 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
, parse_albumart_display
},
396 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
397 parse_viewport_display
},
398 { WPS_TOKEN_UIVIEWPORT_ENABLE
, "VI", WPS_REFRESH_STATIC
,
399 parse_viewport_display
},
400 #ifdef HAVE_LCD_BITMAP
401 { WPS_VIEWPORT_CUSTOMLIST
, "Vp", WPS_REFRESH_STATIC
, parse_playlistview
},
402 { WPS_TOKEN_LIST_TITLE_TEXT
, "Lt", WPS_REFRESH_DYNAMIC
, NULL
},
403 { WPS_TOKEN_LIST_TITLE_ICON
, "Li", WPS_REFRESH_DYNAMIC
, NULL
},
405 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
406 { WPS_TOKEN_VIEWPORT_FGCOLOUR
, "Vf", WPS_REFRESH_STATIC
, parse_viewportcolour
},
407 { WPS_TOKEN_VIEWPORT_BGCOLOUR
, "Vb", WPS_REFRESH_STATIC
, parse_viewportcolour
},
409 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
411 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
412 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
416 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
,
417 parse_setting_and_lang
},
418 { WPS_TOKEN_TRANSLATEDSTRING
, "Sx", WPS_REFRESH_STATIC
,
419 parse_setting_and_lang
},
420 { WPS_TOKEN_LANG_IS_RTL
, "Sr", WPS_REFRESH_STATIC
, NULL
},
422 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
423 { WPS_TOKEN_CURRENT_SCREEN
, "cs", WPS_REFRESH_DYNAMIC
, NULL
},
424 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
427 /* Recording Tokens */
428 { WPS_TOKEN_HAVE_RECORDING
, "Rp", WPS_REFRESH_STATIC
, NULL
},
429 #ifdef HAVE_RECORDING
430 { WPS_TOKEN_IS_RECORDING
, "Rr", WPS_REFRESH_DYNAMIC
, NULL
},
431 { WPS_TOKEN_REC_FREQ
, "Rf", WPS_REFRESH_DYNAMIC
, NULL
},
432 { WPS_TOKEN_REC_ENCODER
, "Re", WPS_REFRESH_DYNAMIC
, NULL
},
433 { WPS_TOKEN_REC_BITRATE
, "Rb", WPS_REFRESH_DYNAMIC
, NULL
},
434 { WPS_TOKEN_REC_MONO
, "Rm", WPS_REFRESH_DYNAMIC
, NULL
},
435 { WPS_TOKEN_REC_SECONDS
, "Rs", WPS_REFRESH_DYNAMIC
, NULL
},
436 { WPS_TOKEN_REC_MINUTES
, "Rn", WPS_REFRESH_DYNAMIC
, NULL
},
437 { WPS_TOKEN_REC_HOURS
, "Rh", WPS_REFRESH_DYNAMIC
, NULL
},
439 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
440 /* the array MUST end with an empty string (first char is \0) */
444 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
445 * chains require the order to be kept.
447 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
453 struct skin_token_list
*t
= *list
;
460 /* traverse the image linked-list for an image */
461 #ifdef HAVE_LCD_BITMAP
462 struct gui_img
* find_image(char label
, struct wps_data
*data
)
464 struct skin_token_list
*list
= data
->images
;
467 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
468 if (img
->label
== label
)
477 /* traverse the viewport linked list for a viewport */
478 struct skin_viewport
* find_viewport(char label
, struct wps_data
*data
)
480 struct skin_token_list
*list
= data
->viewports
;
483 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->token
->value
.data
;
484 if (vp
->label
== label
)
492 /* create and init a new wpsll item.
493 * passing NULL to token will alloc a new one.
494 * You should only pass NULL for the token when the token type (table above)
495 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
497 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
500 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
502 token
= skin_buffer_alloc(sizeof(struct wps_token
));
503 if (!llitem
|| !token
)
506 llitem
->token
= token
;
508 llitem
->token
->value
.data
= token_data
;
512 /* Returns the number of chars that should be skipped to jump
513 immediately after the first eol, i.e. to the start of the next line */
514 static int skip_end_of_line(const char *wps_bufptr
)
518 while(*(wps_bufptr
+ skip
) != '\n')
523 /* Starts a new subline in the current line during parsing */
524 static bool skin_start_new_subline(struct skin_line
*line
, int curr_token
)
526 struct skin_subline
*subline
= skin_buffer_alloc(sizeof(struct skin_subline
));
530 subline
->first_token_idx
= curr_token
;
531 subline
->next
= NULL
;
533 subline
->line_type
= 0;
534 subline
->time_mult
= 0;
536 line
->curr_subline
->last_token_idx
= curr_token
-1;
537 line
->curr_subline
->next
= subline
;
538 line
->curr_subline
= subline
;
542 static bool skin_start_new_line(struct skin_viewport
*vp
, int curr_token
)
544 struct skin_line
*line
= skin_buffer_alloc(sizeof(struct skin_line
));
545 struct skin_subline
*subline
= NULL
;
549 /* init the subline */
550 subline
= &line
->sublines
;
551 subline
->first_token_idx
= curr_token
;
552 subline
->next
= NULL
;
553 subline
->line_type
= 0;
554 subline
->time_mult
= 0;
556 /* init the new line */
557 line
->curr_subline
= &line
->sublines
;
559 line
->subline_expire_time
= 0;
561 /* connect to curr_line and vp pointers.
562 * 1) close the previous lines subline
563 * 2) connect to vp pointer
564 * 3) connect to curr_line global pointer
568 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
569 curr_line
->next
= line
;
570 curr_line
->curr_subline
= NULL
;
578 #ifdef HAVE_LCD_BITMAP
580 static int parse_statusbar_enable(const char *wps_bufptr
,
581 struct wps_token
*token
,
582 struct wps_data
*wps_data
)
584 (void)token
; /* Kill warnings */
585 wps_data
->wps_sb_tag
= true;
586 wps_data
->show_sb_on_wps
= true;
587 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
588 viewport_set_defaults(&default_vp
->vp
, curr_screen
);
589 default_vp
->vp
.font
= FONT_UI
;
590 return skip_end_of_line(wps_bufptr
);
593 static int parse_statusbar_disable(const char *wps_bufptr
,
594 struct wps_token
*token
,
595 struct wps_data
*wps_data
)
597 (void)token
; /* Kill warnings */
598 wps_data
->wps_sb_tag
= true;
599 wps_data
->show_sb_on_wps
= false;
600 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
601 viewport_set_fullscreen(&default_vp
->vp
, curr_screen
);
602 default_vp
->vp
.font
= FONT_UI
;
603 return skip_end_of_line(wps_bufptr
);
606 static int parse_statusbar_inbuilt(const char *wps_bufptr
,
607 struct wps_token
*token
, struct wps_data
*wps_data
)
610 token
->value
.data
= (void*)&curr_vp
->vp
;
611 return skip_end_of_line(wps_bufptr
);
614 static int get_image_id(int c
)
616 if(c
>= 'a' && c
<= 'z')
618 else if(c
>= 'A' && c
<= 'Z')
624 char *get_image_filename(const char *start
, const char* bmpdir
,
625 char *buf
, int buf_size
)
627 const char *end
= start
;
628 int bmpdirlen
= strlen(bmpdir
);
630 while (*end
&& *end
!= ',' && *end
!= ')')
632 if ( !end
|| (end
- start
) >= (buf_size
- bmpdirlen
- 2) )
639 buf
[bmpdirlen
] = '/';
640 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
641 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
646 static int parse_image_display(const char *wps_bufptr
,
647 struct wps_token
*token
,
648 struct wps_data
*wps_data
)
650 char label
= wps_bufptr
[1];
652 struct gui_img
*img
;;
655 img
= find_image(label
, wps_data
);
658 token
->value
.i
= label
; /* so debug works */
659 return WPS_ERROR_INVALID_PARAM
;
662 if ((subimage
= get_image_id(wps_bufptr
[2])) != -1)
664 if (subimage
>= img
->num_subimages
)
665 return WPS_ERROR_INVALID_PARAM
;
667 /* Store sub-image number to display in high bits */
668 token
->value
.i
= label
| (subimage
<< 8);
669 return 4; /* We have consumed 2 bytes */
671 token
->value
.i
= label
;
672 return 3; /* We have consumed 1 byte */
676 static int parse_image_load(const char *wps_bufptr
,
677 struct wps_token
*token
,
678 struct wps_data
*wps_data
)
680 const char *ptr
= wps_bufptr
;
681 const char* filename
;
686 /* format: %x|n|filename.bmp|x|y|
687 or %xl|n|filename.bmp|x|y|
688 or %xl|n|filename.bmp|x|y|num_subimages|
692 return WPS_ERROR_INVALID_PARAM
;
696 if (!(ptr
= parse_list("ssdd", NULL
, ',', ptr
, &id
, &filename
, &x
, &y
)))
697 return WPS_ERROR_INVALID_PARAM
;
699 /* Check there is a terminating ) */
700 if (*ptr
!= ')' && *ptr
!= ',')
701 return WPS_ERROR_INVALID_PARAM
;
703 /* check the image number and load state */
704 if(find_image(*id
, wps_data
))
706 /* Invalid image ID */
707 return WPS_ERROR_INVALID_PARAM
;
709 img
= skin_buffer_alloc(sizeof(struct gui_img
));
711 return WPS_ERROR_INVALID_PARAM
;
712 /* save a pointer to the filename */
713 img
->bm
.data
= (char*)filename
;
717 img
->num_subimages
= 1;
718 img
->always_display
= false;
720 /* save current viewport */
721 img
->vp
= &curr_vp
->vp
;
723 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
725 img
->always_display
= true;
727 else if (*ptr
== ',')
729 /* Parse the (optional) number of sub-images */
731 img
->num_subimages
= atoi(ptr
);
732 if (img
->num_subimages
<= 0)
733 return WPS_ERROR_INVALID_PARAM
;
735 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
737 return WPS_ERROR_INVALID_PARAM
;
738 add_to_ll_chain(&wps_data
->images
, item
);
740 /* Skip the rest of the line */
741 return skip_end_of_line(wps_bufptr
);
744 int id
; /* the id from font_load */
745 char *name
; /* filename without path and extension */
747 static struct skin_font skinfonts
[MAXUSERFONTS
];
748 static int parse_font_load(const char *wps_bufptr
,
749 struct wps_token
*token
, struct wps_data
*wps_data
)
751 (void)wps_data
; (void)token
;
752 const char *ptr
= wps_bufptr
;
757 return WPS_ERROR_INVALID_PARAM
;
761 if (!(ptr
= parse_list("ds", NULL
, ',', ptr
, &id
, &filename
)))
762 return WPS_ERROR_INVALID_PARAM
;
764 /* Check there is a terminating | */
766 return WPS_ERROR_INVALID_PARAM
;
768 if (id
<= FONT_UI
|| id
>= MAXFONTS
-1)
769 return WPS_ERROR_INVALID_PARAM
;
770 #if defined(DEBUG) || defined(SIMULATOR)
771 if (skinfonts
[id
-FONT_FIRSTUSERFONT
].name
!= NULL
)
773 DEBUGF("font id %d already being used\n", id
);
776 /* make sure the filename contains .fnt,
777 * we dont actually use it, but require it anyway */
778 ptr
= strchr(filename
, '.');
779 if (!ptr
|| strncmp(ptr
, ".fnt)", 5))
780 return WPS_ERROR_INVALID_PARAM
;
781 skinfonts
[id
-FONT_FIRSTUSERFONT
].id
= -1;
782 skinfonts
[id
-FONT_FIRSTUSERFONT
].name
= filename
;
784 return skip_end_of_line(wps_bufptr
);
788 static int parse_viewport_display(const char *wps_bufptr
,
789 struct wps_token
*token
,
790 struct wps_data
*wps_data
)
793 char letter
= wps_bufptr
[1];
795 if (letter
< 'a' || letter
> 'z')
797 /* invalid viewport tag */
798 return WPS_ERROR_INVALID_PARAM
;
800 token
->value
.i
= letter
;
804 #ifdef HAVE_LCD_BITMAP
805 static int parse_playlistview_text(struct playlistviewer
*viewer
,
806 enum info_line_type line
, char* text
)
809 const struct wps_tag
*tag
;
811 const char *start
= text
;
815 viewer
->lines
[line
].count
= 0;
816 viewer
->lines
[line
].scroll
= false;
817 while (*text
!= ',' && *text
!= ')')
819 if (*text
== '%') /* it is a token of some type */
834 /* escaped characters */
835 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_CHARACTER
;
836 viewer
->lines
[line
].strings
[cur_string
][0] = *text
;
837 viewer
->lines
[line
].strings
[cur_string
++][1] = '\0';
842 strncmp(text
, tag
->name
, strlen(tag
->name
)) != 0;
844 /* %s isnt stored as a tag so manually check for it */
845 if (tag
->type
== WPS_NO_TOKEN
)
847 if (!strncmp(tag
->name
, "s", 1))
849 viewer
->lines
[line
].scroll
= true;
853 else if (tag
->type
== WPS_TOKEN_UNKNOWN
)
856 /* just copy the string */
857 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_STRING
;
858 while (i
<(MAX_PLAYLISTLINE_STRLEN
-1) && text
[i
] != ',' && text
[i
] != ')' && text
[i
] != '%')
860 viewer
->lines
[line
].strings
[cur_string
][i
] = text
[i
];
863 viewer
->lines
[line
].strings
[cur_string
][i
] = '\0';
871 /* unsupported tag, reject */
874 taglen
= strlen(tag
->name
);
875 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = tag
->type
;
884 /* just copy the string */
885 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_STRING
;
886 while (i
<(MAX_PLAYLISTLINE_STRLEN
-1) && text
[i
] != ',' && text
[i
] != ')' && text
[i
] != '%')
888 viewer
->lines
[line
].strings
[cur_string
][i
] = text
[i
];
891 viewer
->lines
[line
].strings
[cur_string
][i
] = '\0';
899 static int parse_playlistview(const char *wps_bufptr
,
900 struct wps_token
*token
, struct wps_data
*wps_data
)
903 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
904 struct playlistviewer
*viewer
= skin_buffer_alloc(sizeof(struct playlistviewer
));
905 char *ptr
= strchr(wps_bufptr
, '(');
908 return WPS_ERROR_INVALID_PARAM
;
909 viewer
->vp
= &curr_vp
->vp
;
910 viewer
->show_icons
= true;
911 viewer
->start_offset
= atoi(ptr
+1);
912 token
->value
.data
= (void*)viewer
;
913 ptr
= strchr(ptr
+1, ',');
914 length
= parse_playlistview_text(viewer
, TRACK_HAS_INFO
, ptr
);
916 return WPS_ERROR_INVALID_PARAM
;
917 length
= parse_playlistview_text(viewer
, TRACK_HAS_NO_INFO
, ptr
+length
);
919 return WPS_ERROR_INVALID_PARAM
;
921 return skip_end_of_line(wps_bufptr
);
925 static int parse_viewport(const char *wps_bufptr
,
926 struct wps_token
*token
,
927 struct wps_data
*wps_data
)
929 (void)token
; /* Kill warnings */
930 const char *ptr
= wps_bufptr
;
932 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
934 /* check for the optional letter to signify its a hideable viewport */
935 /* %Vl|<label>|<rest of tags>| */
936 skin_vp
->hidden_flags
= 0;
937 skin_vp
->label
= VP_NO_LABEL
;
938 skin_vp
->lines
= NULL
;
941 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
942 - (wps_data
->num_tokens
> 0 ? 1 : 0);
946 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
947 return WPS_ERROR_INVALID_PARAM
;
953 char label
= *(ptr
+2);
954 if (label
>= 'a' && label
<= 'z')
956 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
957 skin_vp
->label
= VP_INFO_LABEL
|label
;
963 return WPS_ERROR_INVALID_PARAM
;
964 skin_vp
->label
= VP_INFO_LABEL
|VP_DEFAULT_LABEL
;
965 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
970 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
973 else if (*ptr
== 'l')
977 char label
= *(ptr
+2);
978 if (label
>= 'a' && label
<= 'z')
980 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
981 skin_vp
->label
= label
;
984 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
988 if (*ptr
!= ',' && *ptr
!= '(')
989 return WPS_ERROR_INVALID_PARAM
;
992 struct viewport
*vp
= &skin_vp
->vp
;
993 /* format: %V|x|y|width|height|font| */
994 if (!(ptr
= viewport_parse_viewport(vp
, curr_screen
, ptr
, ',')))
995 return WPS_ERROR_INVALID_PARAM
;
997 /* Check for trailing ) */
999 return WPS_ERROR_INVALID_PARAM
;
1001 if (follow_lang_direction
&& lang_is_rtl())
1003 vp
->flags
|= VP_FLAG_ALIGN_RIGHT
;
1004 vp
->x
= screens
[curr_screen
].lcdwidth
- vp
->width
- vp
->x
;
1007 vp
->flags
&= ~VP_FLAG_ALIGN_RIGHT
; /* ignore right-to-left languages */
1009 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1010 skin_vp
->start_fgcolour
= vp
->fg_pattern
;
1011 skin_vp
->start_bgcolour
= vp
->bg_pattern
;
1014 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
1016 return WPS_ERROR_INVALID_PARAM
;
1017 add_to_ll_chain(&wps_data
->viewports
, list
);
1019 /* Skip the rest of the line */
1020 return ptr
-wps_bufptr
;
1022 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1023 static int parse_viewportcolour(const char *wps_bufptr
,
1024 struct wps_token
*token
, struct wps_data
*wps_data
)
1027 const char *ptr
= wps_bufptr
;
1028 struct viewport_colour
*colour
= skin_buffer_alloc(sizeof(struct viewport_colour
));
1030 if (*ptr
!= '(' || !colour
)
1033 if (!(ptr
= parse_list("c", &set
, ',', ptr
, &colour
->colour
)))
1038 colour
->colour
= get_viewport_default_colour(curr_screen
,
1039 token
->type
== WPS_TOKEN_VIEWPORT_FGCOLOUR
);
1040 colour
->vp
= &curr_vp
->vp
;
1041 token
->value
.data
= colour
;
1043 return ptr
- wps_bufptr
;
1046 static int parse_image_special(const char *wps_bufptr
,
1047 struct wps_token
*token
,
1048 struct wps_data
*wps_data
)
1050 (void)wps_data
; /* kill warning */
1052 const char *pos
= NULL
;
1053 const char *newline
;
1056 pos
= strchr(wps_bufptr
+ 1, ')');
1057 newline
= strchr(wps_bufptr
, '\n');
1059 error
= (pos
> newline
);
1062 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
1064 /* format: %X|filename.bmp| or %Xd */
1065 if (*(wps_bufptr
+1) == 'd')
1067 wps_data
->backdrop
= NULL
;
1068 return skip_end_of_line(wps_bufptr
);
1071 wps_data
->backdrop
= (char*)wps_bufptr
+ 1;
1075 return WPS_ERROR_INVALID_PARAM
;
1076 /* Skip the rest of the line */
1077 return skip_end_of_line(wps_bufptr
);
1081 #endif /* HAVE_LCD_BITMAP */
1083 static int parse_setting_and_lang(const char *wps_bufptr
,
1084 struct wps_token
*token
,
1085 struct wps_data
*wps_data
)
1087 /* NOTE: both the string validations that happen in here will
1088 * automatically PASS on checkwps because its too hard to get
1089 * settings_list.c and englinsh.lang built for it.
1090 * If that ever changes remove the #ifndef __PCTOOL__'s here
1093 const char *ptr
= wps_bufptr
;
1098 /* Find the setting's cfg_name */
1100 return WPS_ERROR_INVALID_PARAM
;
1102 end
= strchr(ptr
,')');
1103 if (!end
|| (size_t)(end
-ptr
+1) > sizeof temp
)
1104 return WPS_ERROR_INVALID_PARAM
;
1105 strlcpy(temp
, ptr
,end
-ptr
+1);
1107 if (token
->type
== WPS_TOKEN_TRANSLATEDSTRING
)
1110 i
= lang_english_to_id(temp
);
1112 return WPS_ERROR_INVALID_PARAM
;
1117 /* Find the setting */
1118 for (i
=0; i
<nb_settings
; i
++)
1119 if (settings
[i
].cfg_name
&&
1120 !strcmp(settings
[i
].cfg_name
, temp
))
1123 if (i
== nb_settings
)
1124 return WPS_ERROR_INVALID_PARAM
;
1127 /* Store the setting number */
1130 /* Skip the rest of the line */
1135 static int parse_dir_level(const char *wps_bufptr
,
1136 struct wps_token
*token
,
1137 struct wps_data
*wps_data
)
1139 char val
[] = { wps_bufptr
[1], '\0' };
1140 if (wps_bufptr
[0] != '(' || wps_bufptr
[2] != ')')
1141 return WPS_ERROR_INVALID_PARAM
;
1142 token
->value
.i
= atoi(val
);
1143 (void)wps_data
; /* Kill warnings */
1147 static int parse_timeout(const char *wps_bufptr
,
1148 struct wps_token
*token
,
1149 struct wps_data
*wps_data
)
1153 bool have_point
= false;
1154 bool have_tenth
= false;
1156 (void)wps_data
; /* Kill the warning */
1157 if (*wps_bufptr
== '(')
1161 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
1163 if (*wps_bufptr
!= '.')
1166 val
+= *wps_bufptr
- '0';
1181 if (*wps_bufptr
!= ')')
1184 if (have_tenth
== false)
1187 if (val
== 0 && skip
== 0)
1189 /* decide what to do if no value was specified */
1190 switch (token
->type
)
1192 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1194 case WPS_TOKEN_BUTTON_VOLUME
:
1195 case WPS_TOKEN_TRACK_STARTING
:
1196 case WPS_TOKEN_TRACK_ENDING
:
1201 token
->value
.i
= val
;
1206 static int parse_progressbar(const char *wps_bufptr
,
1207 struct wps_token
*token
,
1208 struct wps_data
*wps_data
)
1210 /* %pb or %pb|filename|x|y|width|height|
1211 using - for any of the params uses "sane" values */
1212 #ifdef HAVE_LCD_BITMAP
1220 const char *filename
;
1221 int x
, y
, height
, width
;
1223 const char *ptr
= wps_bufptr
;
1224 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
1225 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
1228 return WPS_ERROR_INVALID_PARAM
;
1230 struct viewport
*vp
= &curr_vp
->vp
;
1231 /* we need to know what line number (viewport relative) this pb is,
1232 * so count them... */
1234 struct skin_line
*line
= curr_vp
->lines
;
1241 pb
->have_bitmap_pb
= false;
1242 pb
->bm
.data
= NULL
; /* no bitmap specified */
1243 pb
->follow_lang_direction
= follow_lang_direction
> 0;
1246 if (*wps_bufptr
!= '(') /* regular old style */
1249 pb
->width
= vp
->width
;
1250 pb
->height
= SYSFONT_HEIGHT
-2;
1251 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1252 if (token
->type
== WPS_TOKEN_VOLUME
|| token
->type
== WPS_TOKEN_BATTERY_PERCENT
)
1253 return 0; /* dont add it, let the regular token handling do the work */
1254 pb
->type
= token
->type
;
1255 add_to_ll_chain(&wps_data
->progressbars
, item
);
1258 ptr
= wps_bufptr
+ 1;
1260 if (!(ptr
= parse_list("sdddd", &set
, ',', ptr
, &filename
,
1261 &x
, &y
, &width
, &height
)))
1263 /* If we are in a conditional then we probably don't want to fail
1264 * if the above doesnt work. So assume the | is breaking the conditional
1265 * and move on. The next token will fail if this is incorrect. */
1268 return WPS_ERROR_INVALID_PARAM
;
1271 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
1272 pb
->bm
.data
= (char*)filename
;
1274 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
1279 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
1281 /* A zero width causes a divide-by-zero error later, so reject it */
1283 return WPS_ERROR_INVALID_PARAM
;
1288 pb
->width
= vp
->width
- pb
->x
;
1290 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
1292 /* A zero height makes no sense - reject it */
1294 return WPS_ERROR_INVALID_PARAM
;
1296 pb
->height
= height
;
1300 if (vp
->font
> FONT_UI
)
1301 pb
->height
= -1; /* calculate at display time */
1305 pb
->height
= font_get(vp
->font
)->height
;
1312 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
1315 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1317 add_to_ll_chain(&wps_data
->progressbars
, item
);
1318 if (token
->type
== WPS_TOKEN_VOLUME
)
1319 token
->type
= WPS_TOKEN_VOLUMEBAR
;
1320 else if (token
->type
== WPS_TOKEN_BATTERY_PERCENT
)
1321 token
->type
= WPS_TOKEN_BATTERY_PERCENTBAR
;
1322 pb
->type
= token
->type
;
1324 return ptr
+1-wps_bufptr
;
1327 if (token
->type
!= WPS_TOKEN_VOLUME
&&
1328 token
->type
!= WPS_TOKEN_BATTERY_PERCENTBAR
)
1330 wps_data
->full_line_progressbar
=
1331 token
->type
== WPS_TOKEN_PLAYER_PROGRESSBAR
;
1338 #ifdef HAVE_ALBUMART
1339 static int parse_albumart_load(const char *wps_bufptr
,
1340 struct wps_token
*token
,
1341 struct wps_data
*wps_data
)
1343 const char *ptr
= wps_bufptr
;
1344 struct dim dimensions
;
1346 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
1347 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
1348 (void)token
; /* silence warning */
1350 return skip_end_of_line(wps_bufptr
);
1352 /* reset albumart info in wps */
1355 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1356 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1357 aa
->vp
= &curr_vp
->vp
;
1360 return WPS_ERROR_INVALID_PARAM
;
1362 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1363 if (!(ptr
= parse_list("dddd", NULL
,',',ptr
, &aa
->x
, &aa
->y
, &aa
->width
, &aa
->height
)))
1364 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1366 /* if we got here, we parsed everything ok .. ! */
1369 else if (aa
->width
> LCD_WIDTH
)
1370 aa
->width
= LCD_WIDTH
;
1374 else if (aa
->height
> LCD_HEIGHT
)
1375 aa
->height
= LCD_HEIGHT
;
1378 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
1380 aa
->state
= WPS_ALBUMART_LOAD
;
1382 wps_data
->albumart
= aa
;
1384 dimensions
.width
= aa
->width
;
1385 dimensions
.height
= aa
->height
;
1387 albumart_slot
= playback_claim_aa_slot(&dimensions
);
1389 if (0 <= albumart_slot
)
1390 wps_data
->playback_aa_slot
= albumart_slot
;
1393 return skip_end_of_line(wps_bufptr
);
1394 else if (*ptr
!= ',')
1395 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1402 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1404 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1408 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1413 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1415 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1420 return skip_end_of_line(wps_bufptr
);
1421 else if (*ptr
!= ',')
1422 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1428 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1432 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1436 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1444 /* simply ignored */
1447 return skip_end_of_line(wps_bufptr
);
1450 static int parse_albumart_display(const char *wps_bufptr
,
1451 struct wps_token
*token
,
1452 struct wps_data
*wps_data
)
1455 struct wps_token
*prev
= token
-1;
1456 if ((wps_data
->num_tokens
>= 1) && (prev
->type
== WPS_TOKEN_CONDITIONAL
))
1458 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1460 else if (wps_data
->albumart
)
1462 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1465 /* the old code did this so keep it here for now...
1466 * this is to allow the posibility to showing the next tracks AA! */
1467 if (wps_bufptr
+1 == 'n')
1472 #endif /* HAVE_ALBUMART */
1474 #ifdef HAVE_TOUCHSCREEN
1476 struct touchaction
{const char* s
; int action
;};
1477 static const struct touchaction touchactions
[] = {
1478 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1479 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1480 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1481 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1482 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1483 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1484 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1485 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
1487 static int parse_touchregion(const char *wps_bufptr
,
1488 struct wps_token
*token
, struct wps_data
*wps_data
)
1492 struct touchregion
*region
= NULL
;
1493 const char *ptr
= wps_bufptr
;
1494 const char *action
, *end
;
1495 const char pb_string
[] = "progressbar";
1496 const char vol_string
[] = "volume";
1500 /* format: %T|x|y|width|height|action|
1501 * if action starts with & the area must be held to happen
1503 * play - play/pause playback
1504 * stop - stop playback, exit the wps
1507 * ffwd - seek forward
1508 * rwd - seek backwards
1509 * menu - go back to the main menu
1510 * browse - go back to the file/db browser
1511 * shuffle - toggle shuffle mode
1512 * repmode - cycle the repeat mode
1513 * quickscreen - go into the quickscreen
1514 * contextmenu - open the context menu
1515 * playlist - go into the playlist
1516 * pitch - go into the pitchscreen
1517 * volup - increase volume by one step
1518 * voldown - decrease volume by one step
1523 return WPS_ERROR_INVALID_PARAM
;
1526 if (!(ptr
= parse_list("dddds", NULL
, ',', ptr
, &x
, &y
, &w
, &h
, &action
)))
1527 return WPS_ERROR_INVALID_PARAM
;
1529 /* Check there is a terminating | */
1531 return WPS_ERROR_INVALID_PARAM
;
1533 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1535 return WPS_ERROR_INVALID_PARAM
;
1537 /* should probably do some bounds checking here with the viewport... but later */
1538 region
->action
= ACTION_NONE
;
1543 region
->wvp
= curr_vp
;
1544 region
->armed
= false;
1546 end
= strchr(action
, ')');
1547 if (!end
|| (size_t)(end
-action
+1) > sizeof temp
)
1548 return WPS_ERROR_INVALID_PARAM
;
1549 strlcpy(temp
, action
, end
-action
+1);
1552 if(!strcmp(pb_string
, action
))
1553 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1554 else if(!strcmp(vol_string
, action
))
1555 region
->type
= WPS_TOUCHREGION_VOLUME
;
1558 region
->type
= WPS_TOUCHREGION_ACTION
;
1563 region
->repeat
= true;
1566 region
->repeat
= false;
1568 imax
= ARRAYLEN(touchactions
);
1569 for (i
= 0; i
< imax
; i
++)
1571 /* try to match with one of our touchregion screens */
1572 if (!strcmp(touchactions
[i
].s
, action
))
1574 region
->action
= touchactions
[i
].action
;
1578 if (region
->action
== ACTION_NONE
)
1579 return WPS_ERROR_INVALID_PARAM
;
1581 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1583 return WPS_ERROR_INVALID_PARAM
;
1584 add_to_ll_chain(&wps_data
->touchregions
, item
);
1585 return skip_end_of_line(wps_bufptr
);
1589 /* Parse a generic token from the given string. Return the length read */
1590 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1592 int skip
= 0, taglen
= 0, ret
;
1593 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1594 const struct wps_tag
*tag
;
1595 memset(token
, 0, sizeof(*token
));
1609 /* escaped characters */
1610 token
->type
= WPS_TOKEN_CHARACTER
;
1611 token
->value
.c
= *wps_bufptr
;
1613 wps_data
->num_tokens
++;
1617 /* conditional tag */
1618 token
->type
= WPS_TOKEN_CONDITIONAL
;
1620 condindex
[level
] = wps_data
->num_tokens
;
1621 numoptions
[level
] = 1;
1622 wps_data
->num_tokens
++;
1623 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1624 if (ret
< 0) return ret
;
1629 /* find what tag we have */
1630 for (tag
= all_tags
;
1631 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1634 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1635 token
->type
= tag
->type
;
1636 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1638 /* if the tag has a special parsing function, we call it */
1639 if (tag
->parse_func
)
1641 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1642 if (ret
< 0) return ret
;
1646 /* Some tags we don't want to save as tokens */
1647 if (tag
->type
== WPS_NO_TOKEN
)
1650 /* tags that start with 'F', 'I' or 'D' are for the next file */
1651 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1652 *(tag
->name
) == 'D')
1655 wps_data
->num_tokens
++;
1665 * Returns the number of bytes to skip the buf pointer to access the false
1666 * branch in a _binary_ conditional
1669 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1670 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1672 * depending on the features of a target it's not called from check_feature_tag,
1673 * hence the __attribute__ or it issues compiler warnings
1677 static int find_false_branch(const char *wps_bufptr
) __attribute__((unused
));
1678 static int find_false_branch(const char *wps_bufptr
)
1680 const char *buf
= wps_bufptr
;
1681 /* wps_bufptr is after the opening '<', hence level = 1*/
1688 { /* filter out the characters we check later if they're printed
1691 if (ch
== '<' || ch
== '>' || ch
== '|')
1693 /* else: some tags/printed literals we skip over */
1695 else if (ch
== '<') /* nested conditional */
1698 { /* closed our or a nested conditional,
1699 * do NOT skip over the '>' so that wps_parse() sees it for closing
1700 * if it is the closing one for our conditional */
1703 else if (ch
== '|' && level
== 1)
1704 { /* we found our separator, point before and get out */
1707 /* if level is 0, we don't have a false branch */
1708 } while (level
> 0 && *(++buf
));
1710 return buf
- wps_bufptr
;
1714 * returns the number of bytes to get the appropriate branch of a binary
1718 * - if a feature is available, it returns 0 to not skip anything
1719 * - if the feature is not available, skip to the false branch and don't
1720 * parse the true branch at all
1723 static int check_feature_tag(const char *wps_bufptr
, const int type
)
1728 case WPS_TOKEN_RTC_PRESENT
:
1732 return find_false_branch(wps_bufptr
);
1734 case WPS_TOKEN_HAVE_RECORDING
:
1735 #ifdef HAVE_RECORDING
1738 return find_false_branch(wps_bufptr
);
1740 case WPS_TOKEN_HAVE_TUNER
:
1742 if (radio_hardware_present())
1745 return find_false_branch(wps_bufptr
);
1747 default: /* not a tag we care about, just don't skip */
1754 data is the pointer to the structure where the parsed WPS should be stored.
1756 wps_bufptr points to the string containing the WPS tags */
1757 #define TOKEN_BLOCK_SIZE 128
1758 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1760 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1762 enum wps_parse_error fail
= PARSE_OK
;
1764 int max_tokens
= TOKEN_BLOCK_SIZE
;
1765 size_t buf_free
= 0;
1769 /* allocate enough RAM for a reasonable skin, grow as needed.
1770 * Free any used RAM before loading the images to be 100% RAM efficient */
1771 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1772 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1774 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1775 data
->num_tokens
= 0;
1778 /* Backdrop defaults to the setting unless %X is used, so set it now */
1779 if (global_settings
.backdrop_file
[0])
1781 data
->backdrop
= "-";
1785 while (*wps_bufptr
&& !fail
)
1787 if (follow_lang_direction
)
1788 follow_lang_direction
--;
1789 /* first make sure there is enough room for tokens */
1790 if (max_tokens
<= data
->num_tokens
+ 5)
1792 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1793 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1794 /* do some smarts here to grow the array a bit */
1795 if (skin_buffer_freespace() < needed
)
1797 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1800 skin_buffer_increment(needed
, false);
1801 max_tokens
+= extra_tokens
;
1804 switch(*wps_bufptr
++)
1809 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1811 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1814 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1816 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1822 /* Alternating sublines separator */
1824 if (level
>= 0) /* there are unclosed conditionals */
1826 fail
= PARSE_FAIL_UNCLOSED_COND
;
1830 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1831 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1835 /* Conditional list start */
1837 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1839 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1842 wps_bufptr
+= check_feature_tag(wps_bufptr
,
1843 data
->tokens
[data
->num_tokens
-1].type
);
1844 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1845 lastcond
[level
] = data
->num_tokens
++;
1848 /* Conditional list end */
1850 if (level
< 0) /* not in a conditional, invalid char */
1852 fail
= PARSE_FAIL_INVALID_CHAR
;
1856 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1857 if (lastcond
[level
])
1858 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1861 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1865 lastcond
[level
] = 0;
1867 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1871 /* Conditional list option */
1873 if (level
< 0) /* not in a conditional, invalid char */
1875 fail
= PARSE_FAIL_INVALID_CHAR
;
1879 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1880 if (lastcond
[level
])
1881 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1884 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1888 lastcond
[level
] = data
->num_tokens
;
1889 numoptions
[level
]++;
1895 if (level
>= 0) /* there are unclosed conditionals */
1897 fail
= PARSE_FAIL_UNCLOSED_COND
;
1901 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1904 /* End of this line */
1906 if (level
>= 0) /* there are unclosed conditionals */
1908 fail
= PARSE_FAIL_UNCLOSED_COND
;
1911 /* add a new token for the \n so empty lines are correct */
1912 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1913 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1914 data
->tokens
[data
->num_tokens
].next
= false;
1917 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1919 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1929 unsigned int len
= 1;
1930 const char *string_start
= wps_bufptr
- 1;
1932 /* find the length of the string */
1933 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1934 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1935 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1936 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1942 /* look if we already have that string */
1945 struct skin_token_list
*list
= data
->strings
;
1948 str
= (char*)list
->token
->value
.data
;
1949 found
= (strlen(str
) == len
&&
1950 strncmp(string_start
, str
, len
) == 0);
1952 break; /* break here because the list item is
1953 used if its found */
1956 /* If a matching string is found, found is true and i is
1957 the index of the string. If not, found is false */
1962 str
= (char*)skin_buffer_alloc(len
+1);
1965 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1968 strlcpy(str
, string_start
, len
+1);
1969 struct skin_token_list
*item
=
1970 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1973 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1976 add_to_ll_chain(&data
->strings
, item
);
1980 /* another occurrence of an existing string */
1981 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1983 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1990 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1991 fail
= PARSE_FAIL_UNCLOSED_COND
;
1993 if (*wps_bufptr
&& !fail
)
1994 /* one of the limits of the while loop was exceeded */
1995 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1998 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
1999 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
2000 /* freeup unused tokens */
2001 skin_buffer_free_from_front(sizeof(struct wps_token
)
2002 * (max_tokens
- data
->num_tokens
));
2004 #if defined(DEBUG) || defined(SIMULATOR)
2007 print_debug_info(data
, fail
, line_number
);
2019 * initial setup of wps_data; does reset everything
2020 * except fields which need to survive, i.e.
2023 static void skin_data_reset(struct wps_data
*wps_data
)
2025 #ifdef HAVE_LCD_BITMAP
2026 wps_data
->images
= NULL
;
2027 wps_data
->progressbars
= NULL
;
2029 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2030 wps_data
->backdrop
= NULL
;
2032 #ifdef HAVE_TOUCHSCREEN
2033 wps_data
->touchregions
= NULL
;
2035 wps_data
->viewports
= NULL
;
2036 wps_data
->strings
= NULL
;
2037 #ifdef HAVE_ALBUMART
2038 wps_data
->albumart
= NULL
;
2039 if (wps_data
->playback_aa_slot
>= 0)
2041 playback_release_aa_slot(wps_data
->playback_aa_slot
);
2042 wps_data
->playback_aa_slot
= -1;
2045 wps_data
->tokens
= NULL
;
2046 wps_data
->num_tokens
= 0;
2048 #ifdef HAVE_LCD_BITMAP
2049 wps_data
->peak_meter_enabled
= false;
2050 wps_data
->wps_sb_tag
= false;
2051 wps_data
->show_sb_on_wps
= false;
2052 #else /* HAVE_LCD_CHARCELLS */
2055 for (i
= 0; i
< 8; i
++)
2057 wps_data
->wps_progress_pat
[i
] = 0;
2059 wps_data
->full_line_progressbar
= false;
2061 wps_data
->wps_loaded
= false;
2064 #ifdef HAVE_LCD_BITMAP
2065 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
2067 (void)wps_data
; /* only needed for remote targets */
2068 char img_path
[MAX_PATH
];
2069 get_image_filename(bitmap
->data
, bmpdir
,
2070 img_path
, sizeof(img_path
));
2072 /* load the image */
2074 #ifdef HAVE_REMOTE_LCD
2075 if (curr_screen
== SCREEN_REMOTE
)
2076 format
= FORMAT_ANY
|FORMAT_REMOTE
;
2079 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
2082 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
2083 bitmap
->data
= imgbuf
;
2084 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
2088 skin_buffer_increment(ret
, true);
2093 /* Abort if we can't load an image */
2094 DEBUGF("Couldn't load '%s'\n", img_path
);
2099 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
2101 struct skin_token_list
*list
;
2102 bool retval
= true; /* return false if a single image failed to load */
2103 /* do the progressbars */
2104 list
= wps_data
->progressbars
;
2107 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
2110 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
2111 if (!pb
->have_bitmap_pb
) /* no success */
2116 /* regular images */
2117 list
= wps_data
->images
;
2120 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
2123 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
2125 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
2132 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2133 /* Backdrop load scheme:
2135 * 2) load the backdrop from settings
2137 if (wps_data
->backdrop
)
2139 bool needed
= wps_data
->backdrop
[0] != '-';
2140 wps_data
->backdrop
= skin_backdrop_load(wps_data
->backdrop
,
2141 bmpdir
, curr_screen
);
2142 if (!wps_data
->backdrop
&& needed
)
2145 #endif /* has backdrop support */
2150 static bool skin_load_fonts(struct wps_data
*data
)
2152 /* don't spit out after the first failue to aid debugging */
2153 bool success
= true;
2154 struct skin_token_list
*vp_list
;
2156 /* walk though each viewport and assign its font */
2157 for(vp_list
= data
->viewports
; vp_list
; vp_list
= vp_list
->next
)
2159 /* first, find the viewports that have a non-sys/ui-font font */
2160 struct skin_viewport
*skin_vp
=
2161 (struct skin_viewport
*)vp_list
->token
->value
.data
;
2162 struct viewport
*vp
= &skin_vp
->vp
;
2165 if (vp
->font
<= FONT_UI
)
2166 { /* the usual case -> built-in fonts */
2167 #ifdef HAVE_REMOTE_LCD
2168 if (vp
->font
== FONT_UI
)
2169 vp
->font
+= curr_screen
;
2175 /* now find the corresponding skin_font */
2176 struct skin_font
*font
= &skinfonts
[font_id
-FONT_FIRSTUSERFONT
];
2179 DEBUGF("font %d not specified\n", font_id
);
2184 /* load the font - will handle loading the same font again if
2185 * multiple viewports use the same */
2188 char *dot
= strchr(font
->name
, '.');
2190 font
->id
= skin_font_load(font
->name
);
2195 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2196 font_id
, font
->name
);
2201 /* finally, assign the font_id to the viewport */
2202 vp
->font
= font
->id
;
2207 #endif /* HAVE_LCD_BITMAP */
2209 /* to setup up the wps-data from a format-buffer (isfile = false)
2210 from a (wps-)file (isfile = true)*/
2211 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
2212 const char *buf
, bool isfile
)
2214 char *wps_buffer
= NULL
;
2215 if (!wps_data
|| !buf
)
2217 #ifdef HAVE_ALBUMART
2219 struct mp3entry
*curtrack
;
2221 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
2222 if (wps_data
->albumart
)
2224 old_aa
.state
= wps_data
->albumart
->state
;
2225 old_aa
.height
= wps_data
->albumart
->height
;
2226 old_aa
.width
= wps_data
->albumart
->width
;
2229 #ifdef HAVE_LCD_BITMAP
2231 for (i
=0;i
<MAXUSERFONTS
;i
++)
2233 skinfonts
[i
].id
= -1;
2234 skinfonts
[i
].name
= NULL
;
2237 #ifdef DEBUG_SKIN_ENGINE
2238 if (isfile
&& debug_wps
)
2240 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf
);
2244 skin_data_reset(wps_data
);
2245 wps_data
->wps_loaded
= false;
2246 curr_screen
= screen
;
2248 /* alloc default viewport, will be fixed up later */
2249 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
2252 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
2255 add_to_ll_chain(&wps_data
->viewports
, list
);
2258 /* Initialise the first (default) viewport */
2259 curr_vp
->label
= VP_DEFAULT_LABEL
;
2260 curr_vp
->hidden_flags
= 0;
2261 curr_vp
->lines
= NULL
;
2263 viewport_set_defaults(&curr_vp
->vp
, screen
);
2264 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2265 curr_vp
->start_fgcolour
= curr_vp
->vp
.fg_pattern
;
2266 curr_vp
->start_bgcolour
= curr_vp
->vp
.bg_pattern
;
2268 #ifdef HAVE_LCD_BITMAP
2269 curr_vp
->vp
.font
= FONT_UI
;
2273 if (!skin_start_new_line(curr_vp
, 0))
2278 int fd
= open_utf8(buf
, O_RDONLY
);
2283 /* get buffer space from the plugin buffer */
2284 size_t buffersize
= 0;
2285 wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
2290 /* copy the file's content to the buffer for parsing,
2291 ensuring that every line ends with a newline char. */
2292 unsigned int start
= 0;
2293 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
2295 start
+= strlen(wps_buffer
+ start
);
2296 if (start
< buffersize
- 1)
2298 wps_buffer
[start
++] = '\n';
2299 wps_buffer
[start
] = 0;
2308 wps_buffer
= (char*)buf
;
2310 /* parse the WPS source */
2311 if (!wps_parse(wps_data
, wps_buffer
, isfile
)) {
2312 skin_data_reset(wps_data
);
2316 #ifdef HAVE_LCD_BITMAP
2317 char bmpdir
[MAX_PATH
];
2320 /* get the bitmap dir */
2321 char *dot
= strrchr(buf
, '.');
2322 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
2326 snprintf(bmpdir
, MAX_PATH
, "%s", BACKDROP_DIR
);
2328 /* load the bitmaps that were found by the parsing */
2329 if (!load_skin_bitmaps(wps_data
, bmpdir
) ||
2330 !skin_load_fonts(wps_data
))
2332 skin_data_reset(wps_data
);
2336 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2337 status
= audio_status();
2338 if (status
& AUDIO_STATUS_PLAY
)
2340 struct skin_albumart
*aa
= wps_data
->albumart
;
2341 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
2343 (((old_aa
.height
!= aa
->height
) ||
2344 (old_aa
.width
!= aa
->width
))))))
2346 curtrack
= audio_current_track();
2347 offset
= curtrack
->offset
;
2349 if (!(status
& AUDIO_STATUS_PAUSE
))
2354 wps_data
->wps_loaded
= true;
2355 #ifdef DEBUG_SKIN_ENGINE
2356 if (isfile
&& debug_wps
)