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 skip_end_of_line(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
== '(')
1159 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
1161 if (*wps_bufptr
!= '.')
1164 val
+= *wps_bufptr
- '0';
1179 if (*wps_bufptr
!= ')')
1182 if (have_tenth
== false)
1185 if (val
== 0 && skip
== 0)
1187 /* decide what to do if no value was specified */
1188 switch (token
->type
)
1190 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1192 case WPS_TOKEN_BUTTON_VOLUME
:
1193 case WPS_TOKEN_TRACK_STARTING
:
1194 case WPS_TOKEN_TRACK_ENDING
:
1199 token
->value
.i
= val
;
1204 static int parse_progressbar(const char *wps_bufptr
,
1205 struct wps_token
*token
,
1206 struct wps_data
*wps_data
)
1208 /* %pb or %pb|filename|x|y|width|height|
1209 using - for any of the params uses "sane" values */
1210 #ifdef HAVE_LCD_BITMAP
1218 const char *filename
;
1219 int x
, y
, height
, width
;
1221 const char *ptr
= wps_bufptr
;
1222 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
1223 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
1226 return WPS_ERROR_INVALID_PARAM
;
1228 struct viewport
*vp
= &curr_vp
->vp
;
1229 /* we need to know what line number (viewport relative) this pb is,
1230 * so count them... */
1232 struct skin_line
*line
= curr_vp
->lines
;
1239 pb
->have_bitmap_pb
= false;
1240 pb
->bm
.data
= NULL
; /* no bitmap specified */
1241 pb
->follow_lang_direction
= follow_lang_direction
> 0;
1244 if (*wps_bufptr
!= '(') /* regular old style */
1247 pb
->width
= vp
->width
;
1248 pb
->height
= SYSFONT_HEIGHT
-2;
1249 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1250 if (token
->type
== WPS_TOKEN_VOLUME
|| token
->type
== WPS_TOKEN_BATTERY_PERCENT
)
1251 return 0; /* dont add it, let the regular token handling do the work */
1252 pb
->type
= token
->type
;
1253 add_to_ll_chain(&wps_data
->progressbars
, item
);
1256 ptr
= wps_bufptr
+ 1;
1258 if (!(ptr
= parse_list("sdddd", &set
, ',', ptr
, &filename
,
1259 &x
, &y
, &width
, &height
)))
1261 /* If we are in a conditional then we probably don't want to fail
1262 * if the above doesnt work. So assume the | is breaking the conditional
1263 * and move on. The next token will fail if this is incorrect. */
1266 return WPS_ERROR_INVALID_PARAM
;
1269 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
1270 pb
->bm
.data
= (char*)filename
;
1272 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
1277 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
1279 /* A zero width causes a divide-by-zero error later, so reject it */
1281 return WPS_ERROR_INVALID_PARAM
;
1286 pb
->width
= vp
->width
- pb
->x
;
1288 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
1290 /* A zero height makes no sense - reject it */
1292 return WPS_ERROR_INVALID_PARAM
;
1294 pb
->height
= height
;
1298 if (vp
->font
> FONT_UI
)
1299 pb
->height
= -1; /* calculate at display time */
1303 pb
->height
= font_get(vp
->font
)->height
;
1310 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
1313 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1315 add_to_ll_chain(&wps_data
->progressbars
, item
);
1316 if (token
->type
== WPS_TOKEN_VOLUME
)
1317 token
->type
= WPS_TOKEN_VOLUMEBAR
;
1318 else if (token
->type
== WPS_TOKEN_BATTERY_PERCENT
)
1319 token
->type
= WPS_TOKEN_BATTERY_PERCENTBAR
;
1320 pb
->type
= token
->type
;
1322 return ptr
+1-wps_bufptr
;
1325 if (token
->type
!= WPS_TOKEN_VOLUME
&&
1326 token
->type
!= WPS_TOKEN_BATTERY_PERCENTBAR
)
1328 wps_data
->full_line_progressbar
=
1329 token
->type
== WPS_TOKEN_PLAYER_PROGRESSBAR
;
1336 #ifdef HAVE_ALBUMART
1337 static int parse_int(const char *newline
, const char **_pos
, int *num
)
1339 *_pos
= parse_list("d", NULL
, ',', *_pos
, num
);
1340 return (!*_pos
|| *_pos
> newline
|| (**_pos
!= ',' && **_pos
!= ')'));
1343 static int parse_albumart_load(const char *wps_bufptr
,
1344 struct wps_token
*token
,
1345 struct wps_data
*wps_data
)
1347 const char *_pos
, *newline
;
1349 struct dim dimensions
;
1351 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
1352 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
1353 (void)token
; /* silence warning */
1355 return skip_end_of_line(wps_bufptr
);
1357 /* reset albumart info in wps */
1360 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1361 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1362 aa
->vp
= &curr_vp
->vp
;
1364 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1366 newline
= strchr(wps_bufptr
, '\n');
1371 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1375 /* initial validation and parsing of x component */
1376 if (parse_int(newline
, &_pos
, &aa
->x
))
1377 return WPS_ERROR_INVALID_PARAM
;
1381 /* initial validation and parsing of y component */
1382 if (parse_int(newline
, &_pos
, &aa
->y
))
1383 return WPS_ERROR_INVALID_PARAM
;
1385 /* parsing width field */
1389 /* apply each modifier in turn */
1397 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1399 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1403 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1409 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1411 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1419 /* simply ignored */
1426 /* extract max width data */
1429 if (parse_int(newline
, &_pos
, &aa
->width
))
1430 return WPS_ERROR_INVALID_PARAM
;
1433 /* parsing height field */
1437 /* apply each modifier in turn */
1444 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1448 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1453 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1461 /* simply ignored */
1468 /* extract max height data */
1471 if (parse_int(newline
, &_pos
, &aa
->height
))
1472 return WPS_ERROR_INVALID_PARAM
;
1475 /* if we got here, we parsed everything ok .. ! */
1478 else if (aa
->width
> LCD_WIDTH
)
1479 aa
->width
= LCD_WIDTH
;
1483 else if (aa
->height
> LCD_HEIGHT
)
1484 aa
->height
= LCD_HEIGHT
;
1487 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
1489 aa
->state
= WPS_ALBUMART_LOAD
;
1491 wps_data
->albumart
= aa
;
1493 dimensions
.width
= aa
->width
;
1494 dimensions
.height
= aa
->height
;
1496 albumart_slot
= playback_claim_aa_slot(&dimensions
);
1498 if (0 <= albumart_slot
)
1499 wps_data
->playback_aa_slot
= albumart_slot
;
1501 /* Skip the rest of the line */
1502 return skip_end_of_line(wps_bufptr
);
1505 static int parse_albumart_display(const char *wps_bufptr
,
1506 struct wps_token
*token
,
1507 struct wps_data
*wps_data
)
1510 struct wps_token
*prev
= token
-1;
1511 if ((wps_data
->num_tokens
>= 1) && (prev
->type
== WPS_TOKEN_CONDITIONAL
))
1513 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1515 else if (wps_data
->albumart
)
1517 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1520 /* the old code did this so keep it here for now...
1521 * this is to allow the posibility to showing the next tracks AA! */
1522 if (wps_bufptr
+1 == 'n')
1527 #endif /* HAVE_ALBUMART */
1529 #ifdef HAVE_TOUCHSCREEN
1531 struct touchaction
{const char* s
; int action
;};
1532 static const struct touchaction touchactions
[] = {
1533 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1534 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1535 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1536 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1537 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1538 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1539 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1540 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
1542 static int parse_touchregion(const char *wps_bufptr
,
1543 struct wps_token
*token
, struct wps_data
*wps_data
)
1547 struct touchregion
*region
= NULL
;
1548 const char *ptr
= wps_bufptr
;
1549 const char *action
, *end
;
1550 const char pb_string
[] = "progressbar";
1551 const char vol_string
[] = "volume";
1555 /* format: %T|x|y|width|height|action|
1556 * if action starts with & the area must be held to happen
1558 * play - play/pause playback
1559 * stop - stop playback, exit the wps
1562 * ffwd - seek forward
1563 * rwd - seek backwards
1564 * menu - go back to the main menu
1565 * browse - go back to the file/db browser
1566 * shuffle - toggle shuffle mode
1567 * repmode - cycle the repeat mode
1568 * quickscreen - go into the quickscreen
1569 * contextmenu - open the context menu
1570 * playlist - go into the playlist
1571 * pitch - go into the pitchscreen
1572 * volup - increase volume by one step
1573 * voldown - decrease volume by one step
1578 return WPS_ERROR_INVALID_PARAM
;
1581 if (!(ptr
= parse_list("dddds", NULL
, ',', ptr
, &x
, &y
, &w
, &h
, &action
)))
1582 return WPS_ERROR_INVALID_PARAM
;
1584 /* Check there is a terminating | */
1586 return WPS_ERROR_INVALID_PARAM
;
1588 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1590 return WPS_ERROR_INVALID_PARAM
;
1592 /* should probably do some bounds checking here with the viewport... but later */
1593 region
->action
= ACTION_NONE
;
1598 region
->wvp
= curr_vp
;
1599 region
->armed
= false;
1601 end
= strchr(action
, ')');
1602 if (!end
|| (size_t)(end
-action
+1) > sizeof temp
)
1603 return WPS_ERROR_INVALID_PARAM
;
1604 strlcpy(temp
, action
, end
-action
+1);
1607 if(!strcmp(pb_string
, action
))
1608 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1609 else if(!strcmp(vol_string
, action
))
1610 region
->type
= WPS_TOUCHREGION_VOLUME
;
1613 region
->type
= WPS_TOUCHREGION_ACTION
;
1618 region
->repeat
= true;
1621 region
->repeat
= false;
1623 imax
= ARRAYLEN(touchactions
);
1624 for (i
= 0; i
< imax
; i
++)
1626 /* try to match with one of our touchregion screens */
1627 if (!strcmp(touchactions
[i
].s
, action
))
1629 region
->action
= touchactions
[i
].action
;
1633 if (region
->action
== ACTION_NONE
)
1634 return WPS_ERROR_INVALID_PARAM
;
1636 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1638 return WPS_ERROR_INVALID_PARAM
;
1639 add_to_ll_chain(&wps_data
->touchregions
, item
);
1640 return skip_end_of_line(wps_bufptr
);
1644 /* Parse a generic token from the given string. Return the length read */
1645 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1647 int skip
= 0, taglen
= 0, ret
;
1648 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1649 const struct wps_tag
*tag
;
1650 memset(token
, 0, sizeof(*token
));
1664 /* escaped characters */
1665 token
->type
= WPS_TOKEN_CHARACTER
;
1666 token
->value
.c
= *wps_bufptr
;
1668 wps_data
->num_tokens
++;
1672 /* conditional tag */
1673 token
->type
= WPS_TOKEN_CONDITIONAL
;
1675 condindex
[level
] = wps_data
->num_tokens
;
1676 numoptions
[level
] = 1;
1677 wps_data
->num_tokens
++;
1678 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1679 if (ret
< 0) return ret
;
1684 /* find what tag we have */
1685 for (tag
= all_tags
;
1686 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1689 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1690 token
->type
= tag
->type
;
1691 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1693 /* if the tag has a special parsing function, we call it */
1694 if (tag
->parse_func
)
1696 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1697 if (ret
< 0) return ret
;
1701 /* Some tags we don't want to save as tokens */
1702 if (tag
->type
== WPS_NO_TOKEN
)
1705 /* tags that start with 'F', 'I' or 'D' are for the next file */
1706 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1707 *(tag
->name
) == 'D')
1710 wps_data
->num_tokens
++;
1720 * Returns the number of bytes to skip the buf pointer to access the false
1721 * branch in a _binary_ conditional
1724 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1725 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1727 * depending on the features of a target it's not called from check_feature_tag,
1728 * hence the __attribute__ or it issues compiler warnings
1732 static int find_false_branch(const char *wps_bufptr
) __attribute__((unused
));
1733 static int find_false_branch(const char *wps_bufptr
)
1735 const char *buf
= wps_bufptr
;
1736 /* wps_bufptr is after the opening '<', hence level = 1*/
1743 { /* filter out the characters we check later if they're printed
1746 if (ch
== '<' || ch
== '>' || ch
== '|')
1748 /* else: some tags/printed literals we skip over */
1750 else if (ch
== '<') /* nested conditional */
1753 { /* closed our or a nested conditional,
1754 * do NOT skip over the '>' so that wps_parse() sees it for closing
1755 * if it is the closing one for our conditional */
1758 else if (ch
== '|' && level
== 1)
1759 { /* we found our separator, point before and get out */
1762 /* if level is 0, we don't have a false branch */
1763 } while (level
> 0 && *(++buf
));
1765 return buf
- wps_bufptr
;
1769 * returns the number of bytes to get the appropriate branch of a binary
1773 * - if a feature is available, it returns 0 to not skip anything
1774 * - if the feature is not available, skip to the false branch and don't
1775 * parse the true branch at all
1778 static int check_feature_tag(const char *wps_bufptr
, const int type
)
1783 case WPS_TOKEN_RTC_PRESENT
:
1787 return find_false_branch(wps_bufptr
);
1789 case WPS_TOKEN_HAVE_RECORDING
:
1790 #ifdef HAVE_RECORDING
1793 return find_false_branch(wps_bufptr
);
1795 case WPS_TOKEN_HAVE_TUNER
:
1797 if (radio_hardware_present())
1800 return find_false_branch(wps_bufptr
);
1802 default: /* not a tag we care about, just don't skip */
1809 data is the pointer to the structure where the parsed WPS should be stored.
1811 wps_bufptr points to the string containing the WPS tags */
1812 #define TOKEN_BLOCK_SIZE 128
1813 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1815 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1817 enum wps_parse_error fail
= PARSE_OK
;
1819 int max_tokens
= TOKEN_BLOCK_SIZE
;
1820 size_t buf_free
= 0;
1824 /* allocate enough RAM for a reasonable skin, grow as needed.
1825 * Free any used RAM before loading the images to be 100% RAM efficient */
1826 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1827 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1829 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1830 data
->num_tokens
= 0;
1833 /* Backdrop defaults to the setting unless %X is used, so set it now */
1834 if (global_settings
.backdrop_file
[0])
1836 data
->backdrop
= "-";
1840 while (*wps_bufptr
&& !fail
)
1842 if (follow_lang_direction
)
1843 follow_lang_direction
--;
1844 /* first make sure there is enough room for tokens */
1845 if (max_tokens
<= data
->num_tokens
+ 5)
1847 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1848 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1849 /* do some smarts here to grow the array a bit */
1850 if (skin_buffer_freespace() < needed
)
1852 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1855 skin_buffer_increment(needed
, false);
1856 max_tokens
+= extra_tokens
;
1859 switch(*wps_bufptr
++)
1864 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1866 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1869 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1871 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1877 /* Alternating sublines separator */
1879 if (level
>= 0) /* there are unclosed conditionals */
1881 fail
= PARSE_FAIL_UNCLOSED_COND
;
1885 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1886 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1890 /* Conditional list start */
1892 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1894 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1897 wps_bufptr
+= check_feature_tag(wps_bufptr
,
1898 data
->tokens
[data
->num_tokens
-1].type
);
1899 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1900 lastcond
[level
] = data
->num_tokens
++;
1903 /* Conditional list end */
1905 if (level
< 0) /* not in a conditional, invalid char */
1907 fail
= PARSE_FAIL_INVALID_CHAR
;
1911 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1912 if (lastcond
[level
])
1913 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1916 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1920 lastcond
[level
] = 0;
1922 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1926 /* Conditional list option */
1928 if (level
< 0) /* not in a conditional, invalid char */
1930 fail
= PARSE_FAIL_INVALID_CHAR
;
1934 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1935 if (lastcond
[level
])
1936 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1939 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1943 lastcond
[level
] = data
->num_tokens
;
1944 numoptions
[level
]++;
1950 if (level
>= 0) /* there are unclosed conditionals */
1952 fail
= PARSE_FAIL_UNCLOSED_COND
;
1956 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1959 /* End of this line */
1961 if (level
>= 0) /* there are unclosed conditionals */
1963 fail
= PARSE_FAIL_UNCLOSED_COND
;
1966 /* add a new token for the \n so empty lines are correct */
1967 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1968 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1969 data
->tokens
[data
->num_tokens
].next
= false;
1972 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1974 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1984 unsigned int len
= 1;
1985 const char *string_start
= wps_bufptr
- 1;
1987 /* find the length of the string */
1988 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1989 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1990 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1991 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1997 /* look if we already have that string */
2000 struct skin_token_list
*list
= data
->strings
;
2003 str
= (char*)list
->token
->value
.data
;
2004 found
= (strlen(str
) == len
&&
2005 strncmp(string_start
, str
, len
) == 0);
2007 break; /* break here because the list item is
2008 used if its found */
2011 /* If a matching string is found, found is true and i is
2012 the index of the string. If not, found is false */
2017 str
= (char*)skin_buffer_alloc(len
+1);
2020 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
2023 strlcpy(str
, string_start
, len
+1);
2024 struct skin_token_list
*item
=
2025 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
2028 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
2031 add_to_ll_chain(&data
->strings
, item
);
2035 /* another occurrence of an existing string */
2036 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
2038 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
2045 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
2046 fail
= PARSE_FAIL_UNCLOSED_COND
;
2048 if (*wps_bufptr
&& !fail
)
2049 /* one of the limits of the while loop was exceeded */
2050 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
2053 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
2054 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
2055 /* freeup unused tokens */
2056 skin_buffer_free_from_front(sizeof(struct wps_token
)
2057 * (max_tokens
- data
->num_tokens
));
2059 #if defined(DEBUG) || defined(SIMULATOR)
2062 print_debug_info(data
, fail
, line_number
);
2074 * initial setup of wps_data; does reset everything
2075 * except fields which need to survive, i.e.
2078 static void skin_data_reset(struct wps_data
*wps_data
)
2080 #ifdef HAVE_LCD_BITMAP
2081 wps_data
->images
= NULL
;
2082 wps_data
->progressbars
= NULL
;
2084 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2085 wps_data
->backdrop
= NULL
;
2087 #ifdef HAVE_TOUCHSCREEN
2088 wps_data
->touchregions
= NULL
;
2090 wps_data
->viewports
= NULL
;
2091 wps_data
->strings
= NULL
;
2092 #ifdef HAVE_ALBUMART
2093 wps_data
->albumart
= NULL
;
2094 if (wps_data
->playback_aa_slot
>= 0)
2096 playback_release_aa_slot(wps_data
->playback_aa_slot
);
2097 wps_data
->playback_aa_slot
= -1;
2100 wps_data
->tokens
= NULL
;
2101 wps_data
->num_tokens
= 0;
2103 #ifdef HAVE_LCD_BITMAP
2104 wps_data
->peak_meter_enabled
= false;
2105 wps_data
->wps_sb_tag
= false;
2106 wps_data
->show_sb_on_wps
= false;
2107 #else /* HAVE_LCD_CHARCELLS */
2110 for (i
= 0; i
< 8; i
++)
2112 wps_data
->wps_progress_pat
[i
] = 0;
2114 wps_data
->full_line_progressbar
= false;
2116 wps_data
->wps_loaded
= false;
2119 #ifdef HAVE_LCD_BITMAP
2120 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
2122 (void)wps_data
; /* only needed for remote targets */
2123 char img_path
[MAX_PATH
];
2124 get_image_filename(bitmap
->data
, bmpdir
,
2125 img_path
, sizeof(img_path
));
2127 /* load the image */
2129 #ifdef HAVE_REMOTE_LCD
2130 if (curr_screen
== SCREEN_REMOTE
)
2131 format
= FORMAT_ANY
|FORMAT_REMOTE
;
2134 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
2137 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
2138 bitmap
->data
= imgbuf
;
2139 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
2143 skin_buffer_increment(ret
, true);
2148 /* Abort if we can't load an image */
2149 DEBUGF("Couldn't load '%s'\n", img_path
);
2154 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
2156 struct skin_token_list
*list
;
2157 bool retval
= true; /* return false if a single image failed to load */
2158 /* do the progressbars */
2159 list
= wps_data
->progressbars
;
2162 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
2165 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
2166 if (!pb
->have_bitmap_pb
) /* no success */
2171 /* regular images */
2172 list
= wps_data
->images
;
2175 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
2178 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
2180 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
2187 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2188 /* Backdrop load scheme:
2190 * 2) load the backdrop from settings
2192 if (wps_data
->backdrop
)
2194 bool needed
= wps_data
->backdrop
[0] != '-';
2195 wps_data
->backdrop
= skin_backdrop_load(wps_data
->backdrop
,
2196 bmpdir
, curr_screen
);
2197 if (!wps_data
->backdrop
&& needed
)
2200 #endif /* has backdrop support */
2205 static bool skin_load_fonts(struct wps_data
*data
)
2207 /* don't spit out after the first failue to aid debugging */
2208 bool success
= true;
2209 struct skin_token_list
*vp_list
;
2211 /* walk though each viewport and assign its font */
2212 for(vp_list
= data
->viewports
; vp_list
; vp_list
= vp_list
->next
)
2214 /* first, find the viewports that have a non-sys/ui-font font */
2215 struct skin_viewport
*skin_vp
=
2216 (struct skin_viewport
*)vp_list
->token
->value
.data
;
2217 struct viewport
*vp
= &skin_vp
->vp
;
2220 if (vp
->font
<= FONT_UI
)
2221 { /* the usual case -> built-in fonts */
2222 #ifdef HAVE_REMOTE_LCD
2223 if (vp
->font
== FONT_UI
)
2224 vp
->font
+= curr_screen
;
2230 /* now find the corresponding skin_font */
2231 struct skin_font
*font
= &skinfonts
[font_id
-FONT_FIRSTUSERFONT
];
2234 DEBUGF("font %d not specified\n", font_id
);
2239 /* load the font - will handle loading the same font again if
2240 * multiple viewports use the same */
2243 char *dot
= strchr(font
->name
, '.');
2245 font
->id
= skin_font_load(font
->name
);
2250 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2251 font_id
, font
->name
);
2256 /* finally, assign the font_id to the viewport */
2257 vp
->font
= font
->id
;
2262 #endif /* HAVE_LCD_BITMAP */
2264 /* to setup up the wps-data from a format-buffer (isfile = false)
2265 from a (wps-)file (isfile = true)*/
2266 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
2267 const char *buf
, bool isfile
)
2269 char *wps_buffer
= NULL
;
2270 if (!wps_data
|| !buf
)
2272 #ifdef HAVE_ALBUMART
2274 struct mp3entry
*curtrack
;
2276 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
2277 if (wps_data
->albumart
)
2279 old_aa
.state
= wps_data
->albumart
->state
;
2280 old_aa
.height
= wps_data
->albumart
->height
;
2281 old_aa
.width
= wps_data
->albumart
->width
;
2284 #ifdef HAVE_LCD_BITMAP
2286 for (i
=0;i
<MAXUSERFONTS
;i
++)
2288 skinfonts
[i
].id
= -1;
2289 skinfonts
[i
].name
= NULL
;
2292 #ifdef DEBUG_SKIN_ENGINE
2293 if (isfile
&& debug_wps
)
2295 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf
);
2299 skin_data_reset(wps_data
);
2300 wps_data
->wps_loaded
= false;
2301 curr_screen
= screen
;
2303 /* alloc default viewport, will be fixed up later */
2304 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
2307 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
2310 add_to_ll_chain(&wps_data
->viewports
, list
);
2313 /* Initialise the first (default) viewport */
2314 curr_vp
->label
= VP_DEFAULT_LABEL
;
2315 curr_vp
->hidden_flags
= 0;
2316 curr_vp
->lines
= NULL
;
2318 viewport_set_defaults(&curr_vp
->vp
, screen
);
2319 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2320 curr_vp
->start_fgcolour
= curr_vp
->vp
.fg_pattern
;
2321 curr_vp
->start_bgcolour
= curr_vp
->vp
.bg_pattern
;
2323 #ifdef HAVE_LCD_BITMAP
2324 curr_vp
->vp
.font
= FONT_UI
;
2328 if (!skin_start_new_line(curr_vp
, 0))
2333 int fd
= open_utf8(buf
, O_RDONLY
);
2338 /* get buffer space from the plugin buffer */
2339 size_t buffersize
= 0;
2340 wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
2345 /* copy the file's content to the buffer for parsing,
2346 ensuring that every line ends with a newline char. */
2347 unsigned int start
= 0;
2348 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
2350 start
+= strlen(wps_buffer
+ start
);
2351 if (start
< buffersize
- 1)
2353 wps_buffer
[start
++] = '\n';
2354 wps_buffer
[start
] = 0;
2363 wps_buffer
= (char*)buf
;
2365 /* parse the WPS source */
2366 if (!wps_parse(wps_data
, wps_buffer
, isfile
)) {
2367 skin_data_reset(wps_data
);
2371 #ifdef HAVE_LCD_BITMAP
2372 char bmpdir
[MAX_PATH
];
2375 /* get the bitmap dir */
2376 char *dot
= strrchr(buf
, '.');
2377 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
2381 snprintf(bmpdir
, MAX_PATH
, "%s", BACKDROP_DIR
);
2383 /* load the bitmaps that were found by the parsing */
2384 if (!load_skin_bitmaps(wps_data
, bmpdir
) ||
2385 !skin_load_fonts(wps_data
))
2387 skin_data_reset(wps_data
);
2391 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2392 status
= audio_status();
2393 if (status
& AUDIO_STATUS_PLAY
)
2395 struct skin_albumart
*aa
= wps_data
->albumart
;
2396 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
2398 (((old_aa
.height
!= aa
->height
) ||
2399 (old_aa
.width
!= aa
->width
))))))
2401 curtrack
= audio_current_track();
2402 offset
= curtrack
->offset
;
2404 if (!(status
& AUDIO_STATUS_PAUSE
))
2409 wps_data
->wps_loaded
= true;
2410 #ifdef DEBUG_SKIN_ENGINE
2411 if (isfile
&& debug_wps
)