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
, "Cd", WPS_REFRESH_STATIC
, parse_albumart_display
},
394 { WPS_TOKEN_ALBUMART_FOUND
, "C", WPS_REFRESH_STATIC
, NULL
},
397 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
398 parse_viewport_display
},
399 { WPS_TOKEN_UIVIEWPORT_ENABLE
, "VI", WPS_REFRESH_STATIC
,
400 parse_viewport_display
},
401 #ifdef HAVE_LCD_BITMAP
402 { WPS_VIEWPORT_CUSTOMLIST
, "Vp", WPS_REFRESH_STATIC
, parse_playlistview
},
403 { WPS_TOKEN_LIST_TITLE_TEXT
, "Lt", WPS_REFRESH_DYNAMIC
, NULL
},
404 { WPS_TOKEN_LIST_TITLE_ICON
, "Li", WPS_REFRESH_DYNAMIC
, NULL
},
406 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
407 { WPS_TOKEN_VIEWPORT_FGCOLOUR
, "Vf", WPS_REFRESH_STATIC
, parse_viewportcolour
},
408 { WPS_TOKEN_VIEWPORT_BGCOLOUR
, "Vb", WPS_REFRESH_STATIC
, parse_viewportcolour
},
410 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
412 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
413 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
417 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
,
418 parse_setting_and_lang
},
419 { WPS_TOKEN_TRANSLATEDSTRING
, "Sx", WPS_REFRESH_STATIC
,
420 parse_setting_and_lang
},
421 { WPS_TOKEN_LANG_IS_RTL
, "Sr", WPS_REFRESH_STATIC
, NULL
},
423 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
424 { WPS_TOKEN_CURRENT_SCREEN
, "cs", WPS_REFRESH_DYNAMIC
, NULL
},
425 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
428 /* Recording Tokens */
429 { WPS_TOKEN_HAVE_RECORDING
, "Rp", WPS_REFRESH_STATIC
, NULL
},
430 #ifdef HAVE_RECORDING
431 { WPS_TOKEN_IS_RECORDING
, "Rr", WPS_REFRESH_DYNAMIC
, NULL
},
432 { WPS_TOKEN_REC_FREQ
, "Rf", WPS_REFRESH_DYNAMIC
, NULL
},
433 { WPS_TOKEN_REC_ENCODER
, "Re", WPS_REFRESH_DYNAMIC
, NULL
},
434 { WPS_TOKEN_REC_BITRATE
, "Rb", WPS_REFRESH_DYNAMIC
, NULL
},
435 { WPS_TOKEN_REC_MONO
, "Rm", WPS_REFRESH_DYNAMIC
, NULL
},
436 { WPS_TOKEN_REC_SECONDS
, "Rs", WPS_REFRESH_DYNAMIC
, NULL
},
437 { WPS_TOKEN_REC_MINUTES
, "Rn", WPS_REFRESH_DYNAMIC
, NULL
},
438 { WPS_TOKEN_REC_HOURS
, "Rh", WPS_REFRESH_DYNAMIC
, NULL
},
440 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
441 /* the array MUST end with an empty string (first char is \0) */
445 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
446 * chains require the order to be kept.
448 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
454 struct skin_token_list
*t
= *list
;
461 /* traverse the image linked-list for an image */
462 #ifdef HAVE_LCD_BITMAP
463 struct gui_img
* find_image(char label
, struct wps_data
*data
)
465 struct skin_token_list
*list
= data
->images
;
468 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
469 if (img
->label
== label
)
478 /* traverse the viewport linked list for a viewport */
479 struct skin_viewport
* find_viewport(char label
, struct wps_data
*data
)
481 struct skin_token_list
*list
= data
->viewports
;
484 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->token
->value
.data
;
485 if (vp
->label
== label
)
493 /* create and init a new wpsll item.
494 * passing NULL to token will alloc a new one.
495 * You should only pass NULL for the token when the token type (table above)
496 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
498 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
501 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
503 token
= skin_buffer_alloc(sizeof(struct wps_token
));
504 if (!llitem
|| !token
)
507 llitem
->token
= token
;
509 llitem
->token
->value
.data
= token_data
;
513 /* Returns the number of chars that should be skipped to jump
514 immediately after the first eol, i.e. to the start of the next line */
515 static int skip_end_of_line(const char *wps_bufptr
)
519 while(*(wps_bufptr
+ skip
) != '\n')
524 /* Starts a new subline in the current line during parsing */
525 static bool skin_start_new_subline(struct skin_line
*line
, int curr_token
)
527 struct skin_subline
*subline
= skin_buffer_alloc(sizeof(struct skin_subline
));
531 subline
->first_token_idx
= curr_token
;
532 subline
->next
= NULL
;
534 subline
->line_type
= 0;
535 subline
->time_mult
= 0;
537 line
->curr_subline
->last_token_idx
= curr_token
-1;
538 line
->curr_subline
->next
= subline
;
539 line
->curr_subline
= subline
;
543 static bool skin_start_new_line(struct skin_viewport
*vp
, int curr_token
)
545 struct skin_line
*line
= skin_buffer_alloc(sizeof(struct skin_line
));
546 struct skin_subline
*subline
= NULL
;
550 /* init the subline */
551 subline
= &line
->sublines
;
552 subline
->first_token_idx
= curr_token
;
553 subline
->next
= NULL
;
554 subline
->line_type
= 0;
555 subline
->time_mult
= 0;
557 /* init the new line */
558 line
->curr_subline
= &line
->sublines
;
560 line
->subline_expire_time
= 0;
562 /* connect to curr_line and vp pointers.
563 * 1) close the previous lines subline
564 * 2) connect to vp pointer
565 * 3) connect to curr_line global pointer
569 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
570 curr_line
->next
= line
;
571 curr_line
->curr_subline
= NULL
;
579 #ifdef HAVE_LCD_BITMAP
581 static int parse_statusbar_enable(const char *wps_bufptr
,
582 struct wps_token
*token
,
583 struct wps_data
*wps_data
)
585 (void)token
; /* Kill warnings */
586 wps_data
->wps_sb_tag
= true;
587 wps_data
->show_sb_on_wps
= true;
588 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
589 viewport_set_defaults(&default_vp
->vp
, curr_screen
);
590 default_vp
->vp
.font
= FONT_UI
;
591 return skip_end_of_line(wps_bufptr
);
594 static int parse_statusbar_disable(const char *wps_bufptr
,
595 struct wps_token
*token
,
596 struct wps_data
*wps_data
)
598 (void)token
; /* Kill warnings */
599 wps_data
->wps_sb_tag
= true;
600 wps_data
->show_sb_on_wps
= false;
601 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
602 viewport_set_fullscreen(&default_vp
->vp
, curr_screen
);
603 default_vp
->vp
.font
= FONT_UI
;
604 return skip_end_of_line(wps_bufptr
);
607 static int parse_statusbar_inbuilt(const char *wps_bufptr
,
608 struct wps_token
*token
, struct wps_data
*wps_data
)
611 token
->value
.data
= (void*)&curr_vp
->vp
;
612 return skip_end_of_line(wps_bufptr
);
615 static int get_image_id(int c
)
617 if(c
>= 'a' && c
<= 'z')
619 else if(c
>= 'A' && c
<= 'Z')
625 char *get_image_filename(const char *start
, const char* bmpdir
,
626 char *buf
, int buf_size
)
628 const char *end
= start
;
629 int bmpdirlen
= strlen(bmpdir
);
631 while (*end
&& *end
!= ',' && *end
!= ')')
633 if ( !end
|| (end
- start
) >= (buf_size
- bmpdirlen
- 2) )
640 buf
[bmpdirlen
] = '/';
641 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
642 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
647 static int parse_image_display(const char *wps_bufptr
,
648 struct wps_token
*token
,
649 struct wps_data
*wps_data
)
651 char label
= wps_bufptr
[1];
653 struct gui_img
*img
;;
656 img
= find_image(label
, wps_data
);
659 token
->value
.i
= label
; /* so debug works */
660 return WPS_ERROR_INVALID_PARAM
;
663 if ((subimage
= get_image_id(wps_bufptr
[2])) != -1)
665 if (subimage
>= img
->num_subimages
)
666 return WPS_ERROR_INVALID_PARAM
;
668 /* Store sub-image number to display in high bits */
669 token
->value
.i
= label
| (subimage
<< 8);
670 return 4; /* We have consumed 2 bytes */
672 token
->value
.i
= label
;
673 return 3; /* We have consumed 1 byte */
677 static int parse_image_load(const char *wps_bufptr
,
678 struct wps_token
*token
,
679 struct wps_data
*wps_data
)
681 const char *ptr
= wps_bufptr
;
682 const char* filename
;
687 /* format: %x|n|filename.bmp|x|y|
688 or %xl|n|filename.bmp|x|y|
689 or %xl|n|filename.bmp|x|y|num_subimages|
693 return WPS_ERROR_INVALID_PARAM
;
697 if (!(ptr
= parse_list("ssdd", NULL
, ',', ptr
, &id
, &filename
, &x
, &y
)))
698 return WPS_ERROR_INVALID_PARAM
;
700 /* Check there is a terminating ) */
701 if (*ptr
!= ')' && *ptr
!= ',')
702 return WPS_ERROR_INVALID_PARAM
;
704 /* check the image number and load state */
705 if(find_image(*id
, wps_data
))
707 /* Invalid image ID */
708 return WPS_ERROR_INVALID_PARAM
;
710 img
= skin_buffer_alloc(sizeof(struct gui_img
));
712 return WPS_ERROR_INVALID_PARAM
;
713 /* save a pointer to the filename */
714 img
->bm
.data
= (char*)filename
;
718 img
->num_subimages
= 1;
719 img
->always_display
= false;
721 /* save current viewport */
722 img
->vp
= &curr_vp
->vp
;
724 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
726 img
->always_display
= true;
728 else if (*ptr
== ',')
730 /* Parse the (optional) number of sub-images */
732 img
->num_subimages
= atoi(ptr
);
733 if (img
->num_subimages
<= 0)
734 return WPS_ERROR_INVALID_PARAM
;
735 /* Check there is a terminating ) */
739 return WPS_ERROR_INVALID_PARAM
;
741 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
743 return WPS_ERROR_INVALID_PARAM
;
744 add_to_ll_chain(&wps_data
->images
, item
);
746 /* Skip the rest of the line */
747 return skip_end_of_line(wps_bufptr
);
750 int id
; /* the id from font_load */
751 char *name
; /* filename without path and extension */
753 static struct skin_font skinfonts
[MAXUSERFONTS
];
754 static int parse_font_load(const char *wps_bufptr
,
755 struct wps_token
*token
, struct wps_data
*wps_data
)
757 (void)wps_data
; (void)token
;
758 const char *ptr
= wps_bufptr
;
763 return WPS_ERROR_INVALID_PARAM
;
767 if (!(ptr
= parse_list("ds", NULL
, ',', ptr
, &id
, &filename
)))
768 return WPS_ERROR_INVALID_PARAM
;
770 /* Check there is a terminating ) */
772 return WPS_ERROR_INVALID_PARAM
;
774 if (id
<= FONT_UI
|| id
>= MAXFONTS
-1)
775 return WPS_ERROR_INVALID_PARAM
;
776 #if defined(DEBUG) || defined(SIMULATOR)
777 if (skinfonts
[id
-FONT_FIRSTUSERFONT
].name
!= NULL
)
779 DEBUGF("font id %d already being used\n", id
);
782 /* make sure the filename contains .fnt,
783 * we dont actually use it, but require it anyway */
784 ptr
= strchr(filename
, '.');
785 if (!ptr
|| strncmp(ptr
, ".fnt)", 5))
786 return WPS_ERROR_INVALID_PARAM
;
787 skinfonts
[id
-FONT_FIRSTUSERFONT
].id
= -1;
788 skinfonts
[id
-FONT_FIRSTUSERFONT
].name
= filename
;
790 return skip_end_of_line(wps_bufptr
);
794 static int parse_viewport_display(const char *wps_bufptr
,
795 struct wps_token
*token
,
796 struct wps_data
*wps_data
)
799 char letter
= wps_bufptr
[1];
801 if (letter
< 'a' || letter
> 'z')
803 /* invalid viewport tag */
804 return WPS_ERROR_INVALID_PARAM
;
806 token
->value
.i
= letter
;
810 #ifdef HAVE_LCD_BITMAP
811 static int parse_playlistview_text(struct playlistviewer
*viewer
,
812 enum info_line_type line
, char* text
)
815 const struct wps_tag
*tag
;
817 const char *start
= text
;
821 viewer
->lines
[line
].count
= 0;
822 viewer
->lines
[line
].scroll
= false;
823 while (*text
!= ',' && *text
!= ')')
825 if (*text
== '%') /* it is a token of some type */
840 /* escaped characters */
841 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_CHARACTER
;
842 viewer
->lines
[line
].strings
[cur_string
][0] = *text
;
843 viewer
->lines
[line
].strings
[cur_string
++][1] = '\0';
848 strncmp(text
, tag
->name
, strlen(tag
->name
)) != 0;
850 /* %s isnt stored as a tag so manually check for it */
851 if (tag
->type
== WPS_NO_TOKEN
)
853 if (!strncmp(tag
->name
, "s", 1))
855 viewer
->lines
[line
].scroll
= true;
859 else if (tag
->type
== WPS_TOKEN_UNKNOWN
)
862 /* just copy the string */
863 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_STRING
;
864 while (i
<(MAX_PLAYLISTLINE_STRLEN
-1) && text
[i
] != ',' && text
[i
] != ')' && text
[i
] != '%')
866 viewer
->lines
[line
].strings
[cur_string
][i
] = text
[i
];
869 viewer
->lines
[line
].strings
[cur_string
][i
] = '\0';
877 /* unsupported tag, reject */
880 taglen
= strlen(tag
->name
);
881 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = tag
->type
;
890 /* just copy the string */
891 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_STRING
;
892 while (i
<(MAX_PLAYLISTLINE_STRLEN
-1) && text
[i
] != ',' && text
[i
] != ')' && text
[i
] != '%')
894 viewer
->lines
[line
].strings
[cur_string
][i
] = text
[i
];
897 viewer
->lines
[line
].strings
[cur_string
][i
] = '\0';
905 static int parse_playlistview(const char *wps_bufptr
,
906 struct wps_token
*token
, struct wps_data
*wps_data
)
909 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
910 struct playlistviewer
*viewer
= skin_buffer_alloc(sizeof(struct playlistviewer
));
911 char *ptr
= strchr(wps_bufptr
, '(');
914 return WPS_ERROR_INVALID_PARAM
;
915 viewer
->vp
= &curr_vp
->vp
;
916 viewer
->show_icons
= true;
917 viewer
->start_offset
= atoi(ptr
+1);
918 token
->value
.data
= (void*)viewer
;
919 ptr
= strchr(ptr
+1, ',');
920 length
= parse_playlistview_text(viewer
, TRACK_HAS_INFO
, ptr
);
922 return WPS_ERROR_INVALID_PARAM
;
923 length
= parse_playlistview_text(viewer
, TRACK_HAS_NO_INFO
, ptr
+length
);
925 return WPS_ERROR_INVALID_PARAM
;
927 return skip_end_of_line(wps_bufptr
);
931 static int parse_viewport(const char *wps_bufptr
,
932 struct wps_token
*token
,
933 struct wps_data
*wps_data
)
935 (void)token
; /* Kill warnings */
936 const char *ptr
= wps_bufptr
;
938 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
940 /* check for the optional letter to signify its a hideable viewport */
941 /* %Vl|<label>|<rest of tags>| */
942 skin_vp
->hidden_flags
= 0;
943 skin_vp
->label
= VP_NO_LABEL
;
944 skin_vp
->lines
= NULL
;
947 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
948 - (wps_data
->num_tokens
> 0 ? 1 : 0);
952 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
953 return WPS_ERROR_INVALID_PARAM
;
959 char label
= *(ptr
+2);
960 if (label
>= 'a' && label
<= 'z')
962 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
963 skin_vp
->label
= VP_INFO_LABEL
|label
;
969 return WPS_ERROR_INVALID_PARAM
;
970 skin_vp
->label
= VP_INFO_LABEL
|VP_DEFAULT_LABEL
;
971 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
976 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
978 else if (*ptr
== 'l')
982 char label
= *(ptr
+2);
983 if (label
>= 'a' && label
<= 'z')
985 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
986 skin_vp
->label
= label
;
989 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
993 if (*ptr
!= ',' && *ptr
!= '(')
994 return WPS_ERROR_INVALID_PARAM
;
997 struct viewport
*vp
= &skin_vp
->vp
;
998 /* format: %V|x|y|width|height|font| */
999 if (!(ptr
= viewport_parse_viewport(vp
, curr_screen
, ptr
, ',')))
1000 return WPS_ERROR_INVALID_PARAM
;
1002 /* Check for trailing ) */
1004 return WPS_ERROR_INVALID_PARAM
;
1007 if (follow_lang_direction
&& lang_is_rtl())
1009 vp
->flags
|= VP_FLAG_ALIGN_RIGHT
;
1010 vp
->x
= screens
[curr_screen
].lcdwidth
- vp
->width
- vp
->x
;
1013 vp
->flags
&= ~VP_FLAG_ALIGN_RIGHT
; /* ignore right-to-left languages */
1015 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1016 skin_vp
->start_fgcolour
= vp
->fg_pattern
;
1017 skin_vp
->start_bgcolour
= vp
->bg_pattern
;
1020 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
1022 return WPS_ERROR_INVALID_PARAM
;
1023 add_to_ll_chain(&wps_data
->viewports
, list
);
1025 /* Skip the rest of the line */
1026 return ptr
-wps_bufptr
;
1028 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1029 static int parse_viewportcolour(const char *wps_bufptr
,
1030 struct wps_token
*token
, struct wps_data
*wps_data
)
1033 const char *ptr
= wps_bufptr
;
1036 struct viewport_colour
*colour
= skin_buffer_alloc(sizeof(struct viewport_colour
));
1038 if (*ptr
!= '(' || !colour
)
1041 if (!(ptr
= parse_list("c", &set
, ',', ptr
, &colour
->colour
)))
1046 colour
->colour
= get_viewport_default_colour(curr_screen
,
1047 token
->type
== WPS_TOKEN_VIEWPORT_FGCOLOUR
);
1048 colour
->vp
= &curr_vp
->vp
;
1049 token
->value
.data
= colour
;
1050 /* If there havnt been any text tags between the %V() line and here use
1051 * the colour as the viewport colour. fixes scrolling lines not
1052 * having the correct colour */
1053 i
= curr_vp
->lines
->sublines
.first_token_idx
;
1055 while (!found_text
&& i
< curr_vp
->lines
->sublines
.last_token_idx
)
1057 if (wps_data
->tokens
[i
++].type
!= WPS_TOKEN_CHARACTER
&&
1058 wps_data
->tokens
[i
++].type
!= WPS_TOKEN_VIEWPORT_FGCOLOUR
&&
1059 wps_data
->tokens
[i
++].type
!= WPS_TOKEN_VIEWPORT_BGCOLOUR
)
1064 if (token
->type
== WPS_TOKEN_VIEWPORT_FGCOLOUR
)
1066 curr_vp
->start_fgcolour
= colour
->colour
;
1067 curr_vp
->vp
.fg_pattern
= colour
->colour
;
1071 curr_vp
->start_bgcolour
= colour
->colour
;
1072 curr_vp
->vp
.bg_pattern
= colour
->colour
;
1076 return ptr
- wps_bufptr
;
1079 static int parse_image_special(const char *wps_bufptr
,
1080 struct wps_token
*token
,
1081 struct wps_data
*wps_data
)
1083 (void)wps_data
; /* kill warning */
1085 const char *pos
= NULL
;
1086 const char *newline
;
1089 pos
= strchr(wps_bufptr
+ 1, ')');
1090 newline
= strchr(wps_bufptr
, '\n');
1092 error
= (pos
> newline
);
1095 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
1097 /* format: %X|filename.bmp| or %Xd */
1098 if (!strncmp(wps_bufptr
, "(d)", 3))
1100 wps_data
->backdrop
= NULL
;
1101 return skip_end_of_line(wps_bufptr
);
1104 wps_data
->backdrop
= (char*)wps_bufptr
+ 1;
1108 return WPS_ERROR_INVALID_PARAM
;
1109 /* Skip the rest of the line */
1110 return skip_end_of_line(wps_bufptr
);
1114 #endif /* HAVE_LCD_BITMAP */
1116 static int parse_setting_and_lang(const char *wps_bufptr
,
1117 struct wps_token
*token
,
1118 struct wps_data
*wps_data
)
1120 /* NOTE: both the string validations that happen in here will
1121 * automatically PASS on checkwps because its too hard to get
1122 * settings_list.c and englinsh.lang built for it.
1123 * If that ever changes remove the #ifndef __PCTOOL__'s here
1126 const char *ptr
= wps_bufptr
;
1131 /* Find the setting's cfg_name */
1133 return WPS_ERROR_INVALID_PARAM
;
1135 end
= strchr(ptr
,')');
1136 if (!end
|| (size_t)(end
-ptr
+1) > sizeof temp
)
1137 return WPS_ERROR_INVALID_PARAM
;
1138 strlcpy(temp
, ptr
,end
-ptr
+1);
1140 if (token
->type
== WPS_TOKEN_TRANSLATEDSTRING
)
1143 i
= lang_english_to_id(temp
);
1145 return WPS_ERROR_INVALID_PARAM
;
1150 /* Find the setting */
1151 for (i
=0; i
<nb_settings
; i
++)
1152 if (settings
[i
].cfg_name
&&
1153 !strcmp(settings
[i
].cfg_name
, temp
))
1156 if (i
== nb_settings
)
1157 return WPS_ERROR_INVALID_PARAM
;
1160 /* Store the setting number */
1163 /* Skip the rest of the line */
1168 static int parse_dir_level(const char *wps_bufptr
,
1169 struct wps_token
*token
,
1170 struct wps_data
*wps_data
)
1172 char val
[] = { wps_bufptr
[1], '\0' };
1173 if (wps_bufptr
[0] != '(' || wps_bufptr
[2] != ')')
1174 return WPS_ERROR_INVALID_PARAM
;
1175 token
->value
.i
= atoi(val
);
1176 (void)wps_data
; /* Kill warnings */
1180 static int parse_timeout(const char *wps_bufptr
,
1181 struct wps_token
*token
,
1182 struct wps_data
*wps_data
)
1186 bool have_point
= false;
1187 bool have_tenth
= false;
1189 (void)wps_data
; /* Kill the warning */
1190 if (*wps_bufptr
== '(')
1194 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
1196 if (*wps_bufptr
!= '.')
1199 val
+= *wps_bufptr
- '0';
1214 if (*wps_bufptr
!= ')')
1218 if (have_tenth
== false)
1221 if (val
== 0 && skip
== 0)
1223 /* decide what to do if no value was specified */
1224 switch (token
->type
)
1226 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1228 case WPS_TOKEN_BUTTON_VOLUME
:
1229 case WPS_TOKEN_TRACK_STARTING
:
1230 case WPS_TOKEN_TRACK_ENDING
:
1235 token
->value
.i
= val
;
1240 static int parse_progressbar(const char *wps_bufptr
,
1241 struct wps_token
*token
,
1242 struct wps_data
*wps_data
)
1244 /* %pb or %pb|filename|x|y|width|height|
1245 using - for any of the params uses "sane" values */
1246 #ifdef HAVE_LCD_BITMAP
1254 const char *filename
;
1255 int x
, y
, height
, width
;
1257 const char *ptr
= wps_bufptr
;
1258 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
1259 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
1262 return WPS_ERROR_INVALID_PARAM
;
1264 struct viewport
*vp
= &curr_vp
->vp
;
1265 /* we need to know what line number (viewport relative) this pb is,
1266 * so count them... */
1268 struct skin_line
*line
= curr_vp
->lines
;
1274 if (curr_vp
->label
!= VP_DEFAULT_LABEL
)
1277 pb
->have_bitmap_pb
= false;
1278 pb
->bm
.data
= NULL
; /* no bitmap specified */
1279 pb
->follow_lang_direction
= follow_lang_direction
> 0;
1282 if (*wps_bufptr
!= '(') /* regular old style */
1285 pb
->width
= vp
->width
;
1286 pb
->height
= SYSFONT_HEIGHT
-2;
1287 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1288 if (token
->type
== WPS_TOKEN_VOLUME
|| token
->type
== WPS_TOKEN_BATTERY_PERCENT
)
1289 return 0; /* dont add it, let the regular token handling do the work */
1290 pb
->type
= token
->type
;
1291 add_to_ll_chain(&wps_data
->progressbars
, item
);
1294 ptr
= wps_bufptr
+ 1;
1296 if (!(ptr
= parse_list("dddds", &set
, ',', ptr
,
1297 &x
, &y
, &width
, &height
, &filename
)))
1298 return WPS_ERROR_INVALID_PARAM
;
1300 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
1301 pb
->bm
.data
= (char*)filename
;
1303 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
1308 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
1310 /* A zero width causes a divide-by-zero error later, so reject it */
1312 return WPS_ERROR_INVALID_PARAM
;
1317 pb
->width
= vp
->width
- pb
->x
;
1319 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
1321 /* A zero height makes no sense - reject it */
1323 return WPS_ERROR_INVALID_PARAM
;
1325 pb
->height
= height
;
1329 if (vp
->font
> FONT_UI
)
1330 pb
->height
= -1; /* calculate at display time */
1334 pb
->height
= font_get(vp
->font
)->height
;
1341 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
1344 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1347 return WPS_ERROR_INVALID_PARAM
;
1349 add_to_ll_chain(&wps_data
->progressbars
, item
);
1350 if (token
->type
== WPS_TOKEN_VOLUME
)
1351 token
->type
= WPS_TOKEN_VOLUMEBAR
;
1352 else if (token
->type
== WPS_TOKEN_BATTERY_PERCENT
)
1353 token
->type
= WPS_TOKEN_BATTERY_PERCENTBAR
;
1354 pb
->type
= token
->type
;
1356 return ptr
+1-wps_bufptr
;
1359 if (token
->type
!= WPS_TOKEN_VOLUME
&&
1360 token
->type
!= WPS_TOKEN_BATTERY_PERCENTBAR
)
1362 wps_data
->full_line_progressbar
=
1363 token
->type
== WPS_TOKEN_PLAYER_PROGRESSBAR
;
1370 #ifdef HAVE_ALBUMART
1371 static int parse_albumart_load(const char *wps_bufptr
,
1372 struct wps_token
*token
,
1373 struct wps_data
*wps_data
)
1375 const char *ptr
= wps_bufptr
;
1376 struct dim dimensions
;
1378 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
1379 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
1380 (void)token
; /* silence warning */
1382 return skip_end_of_line(wps_bufptr
);
1384 /* reset albumart info in wps */
1387 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1388 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1389 aa
->vp
= &curr_vp
->vp
;
1392 return WPS_ERROR_INVALID_PARAM
;
1394 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1395 if (!(ptr
= parse_list("dddd", NULL
,',',ptr
, &aa
->x
, &aa
->y
, &aa
->width
, &aa
->height
)))
1396 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1398 /* if we got here, we parsed everything ok .. ! */
1401 else if (aa
->width
> LCD_WIDTH
)
1402 aa
->width
= LCD_WIDTH
;
1406 else if (aa
->height
> LCD_HEIGHT
)
1407 aa
->height
= LCD_HEIGHT
;
1410 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
1412 aa
->state
= WPS_ALBUMART_LOAD
;
1414 wps_data
->albumart
= aa
;
1416 dimensions
.width
= aa
->width
;
1417 dimensions
.height
= aa
->height
;
1419 albumart_slot
= playback_claim_aa_slot(&dimensions
);
1421 if (0 <= albumart_slot
)
1422 wps_data
->playback_aa_slot
= albumart_slot
;
1432 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1434 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1438 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1443 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1445 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1457 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1461 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1465 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1471 return WPS_ERROR_INVALID_PARAM
;
1473 return skip_end_of_line(wps_bufptr
);
1476 static int parse_albumart_display(const char *wps_bufptr
,
1477 struct wps_token
*token
,
1478 struct wps_data
*wps_data
)
1482 if (wps_data
->albumart
)
1484 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1487 /* the old code did this so keep it here for now...
1488 * this is to allow the posibility to showing the next tracks AA! */
1489 if (wps_bufptr
+1 == 'n')
1494 #endif /* HAVE_ALBUMART */
1496 #ifdef HAVE_TOUCHSCREEN
1498 struct touchaction
{const char* s
; int action
;};
1499 static const struct touchaction touchactions
[] = {
1500 /* generic actions, convert to screen actions on use */
1501 {"prev", ACTION_STD_PREV
}, {"next", ACTION_STD_NEXT
},
1502 {"rwd", ACTION_STD_PREVREPEAT
}, {"ffwd", ACTION_STD_NEXTREPEAT
},
1503 {"hotkey", ACTION_STD_HOTKEY
}, {"select", ACTION_STD_OK
},
1504 {"menu", ACTION_STD_MENU
}, {"cancel", ACTION_STD_CANCEL
},
1505 {"contextmenu", ACTION_STD_CONTEXT
},{"quickscreen", ACTION_STD_QUICKSCREEN
},
1506 /* not really WPS specific, but no equivilant ACTION_STD_* */
1507 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
1509 /* WPS specific actions */
1510 {"browse", ACTION_WPS_BROWSE
},
1511 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1512 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1513 {"pitch", ACTION_WPS_PITCHSCREEN
}, {"playlist", ACTION_WPS_VIEW_PLAYLIST
},
1516 /* FM screen actions */
1517 /* Also allow browse, play, stop from WPS codes */
1518 {"mode", ACTION_FM_MODE
}, {"record", ACTION_FM_RECORD
},
1519 {"presets", ACTION_FM_PRESET
},
1523 static int parse_touchregion(const char *wps_bufptr
,
1524 struct wps_token
*token
, struct wps_data
*wps_data
)
1528 struct touchregion
*region
= NULL
;
1529 const char *ptr
= wps_bufptr
;
1530 const char *action
, *end
;
1531 const char pb_string
[] = "progressbar";
1532 const char vol_string
[] = "volume";
1536 /* format: %T|x|y|width|height|action|
1537 * if action starts with & the area must be held to happen
1539 * play - play/pause playback
1540 * stop - stop playback, exit the wps
1543 * ffwd - seek forward
1544 * rwd - seek backwards
1545 * menu - go back to the main menu
1546 * browse - go back to the file/db browser
1547 * shuffle - toggle shuffle mode
1548 * repmode - cycle the repeat mode
1549 * quickscreen - go into the quickscreen
1550 * contextmenu - open the context menu
1551 * playlist - go into the playlist
1552 * pitch - go into the pitchscreen
1553 * volup - increase volume by one step
1554 * voldown - decrease volume by one step
1559 return WPS_ERROR_INVALID_PARAM
;
1562 if (!(ptr
= parse_list("dddds", NULL
, ',', ptr
, &x
, &y
, &w
, &h
, &action
)))
1563 return WPS_ERROR_INVALID_PARAM
;
1565 /* Check there is a terminating ) */
1567 return WPS_ERROR_INVALID_PARAM
;
1569 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1571 return WPS_ERROR_INVALID_PARAM
;
1573 /* should probably do some bounds checking here with the viewport... but later */
1574 region
->action
= ACTION_NONE
;
1579 region
->wvp
= curr_vp
;
1580 region
->armed
= false;
1581 region
->reverse_bar
= false;
1583 end
= strchr(action
, ')');
1584 if (!end
|| (size_t)(end
-action
+1) > sizeof temp
)
1585 return WPS_ERROR_INVALID_PARAM
;
1586 strlcpy(temp
, action
, end
-action
+1);
1591 region
->reverse_bar
= true;
1595 if(!strcmp(pb_string
, action
))
1596 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1597 else if(!strcmp(vol_string
, action
))
1598 region
->type
= WPS_TOUCHREGION_VOLUME
;
1601 region
->type
= WPS_TOUCHREGION_ACTION
;
1606 region
->repeat
= true;
1609 region
->repeat
= false;
1611 imax
= ARRAYLEN(touchactions
);
1612 for (i
= 0; i
< imax
; i
++)
1614 /* try to match with one of our touchregion screens */
1615 if (!strcmp(touchactions
[i
].s
, action
))
1617 region
->action
= touchactions
[i
].action
;
1621 if (region
->action
== ACTION_NONE
)
1622 return WPS_ERROR_INVALID_PARAM
;
1624 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1626 return WPS_ERROR_INVALID_PARAM
;
1627 add_to_ll_chain(&wps_data
->touchregions
, item
);
1628 return skip_end_of_line(wps_bufptr
);
1632 /* Parse a generic token from the given string. Return the length read */
1633 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1635 int skip
= 0, taglen
= 0, ret
;
1636 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1637 const struct wps_tag
*tag
;
1638 memset(token
, 0, sizeof(*token
));
1652 /* escaped characters */
1653 token
->type
= WPS_TOKEN_CHARACTER
;
1654 token
->value
.c
= *wps_bufptr
;
1656 wps_data
->num_tokens
++;
1660 /* conditional tag */
1661 token
->type
= WPS_TOKEN_CONDITIONAL
;
1663 condindex
[level
] = wps_data
->num_tokens
;
1664 numoptions
[level
] = 1;
1665 wps_data
->num_tokens
++;
1666 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1667 if (ret
< 0) return ret
;
1672 /* find what tag we have */
1673 for (tag
= all_tags
;
1674 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1677 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1678 token
->type
= tag
->type
;
1679 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1681 /* if the tag has a special parsing function, we call it */
1682 if (tag
->parse_func
)
1684 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1685 if (ret
< 0) return ret
;
1689 /* Some tags we don't want to save as tokens */
1690 if (tag
->type
== WPS_NO_TOKEN
)
1693 /* tags that start with 'F', 'I' or 'D' are for the next file */
1694 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1695 *(tag
->name
) == 'D')
1698 wps_data
->num_tokens
++;
1708 * Returns the number of bytes to skip the buf pointer to access the false
1709 * branch in a _binary_ conditional
1712 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1713 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1715 * depending on the features of a target it's not called from check_feature_tag,
1716 * hence the __attribute__ or it issues compiler warnings
1720 static int find_false_branch(const char *wps_bufptr
) __attribute__((unused
));
1721 static int find_false_branch(const char *wps_bufptr
)
1723 const char *buf
= wps_bufptr
;
1724 /* wps_bufptr is after the opening '<', hence level = 1*/
1731 { /* filter out the characters we check later if they're printed
1734 if (ch
== '<' || ch
== '>' || ch
== '|')
1736 /* else: some tags/printed literals we skip over */
1738 else if (ch
== '<') /* nested conditional */
1741 { /* closed our or a nested conditional,
1742 * do NOT skip over the '>' so that wps_parse() sees it for closing
1743 * if it is the closing one for our conditional */
1746 else if (ch
== '|' && level
== 1)
1747 { /* we found our separator, point before and get out */
1750 /* if level is 0, we don't have a false branch */
1751 } while (level
> 0 && *(++buf
));
1753 return buf
- wps_bufptr
;
1757 * returns the number of bytes to get the appropriate branch of a binary
1761 * - if a feature is available, it returns 0 to not skip anything
1762 * - if the feature is not available, skip to the false branch and don't
1763 * parse the true branch at all
1766 static int check_feature_tag(const char *wps_bufptr
, const int type
)
1771 case WPS_TOKEN_RTC_PRESENT
:
1775 return find_false_branch(wps_bufptr
);
1777 case WPS_TOKEN_HAVE_RECORDING
:
1778 #ifdef HAVE_RECORDING
1781 return find_false_branch(wps_bufptr
);
1783 case WPS_TOKEN_HAVE_TUNER
:
1785 if (radio_hardware_present())
1788 case WPS_TOKEN_HAVE_RDS
:
1792 return find_false_branch(wps_bufptr
);
1795 default: /* not a tag we care about, just don't skip */
1802 data is the pointer to the structure where the parsed WPS should be stored.
1804 wps_bufptr points to the string containing the WPS tags */
1805 #define TOKEN_BLOCK_SIZE 128
1806 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1808 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1810 enum wps_parse_error fail
= PARSE_OK
;
1812 int max_tokens
= TOKEN_BLOCK_SIZE
;
1813 size_t buf_free
= 0;
1817 /* allocate enough RAM for a reasonable skin, grow as needed.
1818 * Free any used RAM before loading the images to be 100% RAM efficient */
1819 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1820 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1822 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1823 data
->num_tokens
= 0;
1826 /* Backdrop defaults to the setting unless %X is used, so set it now */
1827 if (global_settings
.backdrop_file
[0])
1829 data
->backdrop
= "-";
1833 while (*wps_bufptr
&& !fail
)
1835 if (follow_lang_direction
)
1836 follow_lang_direction
--;
1837 /* first make sure there is enough room for tokens */
1838 if (max_tokens
<= data
->num_tokens
+ 5)
1840 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1841 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1842 /* do some smarts here to grow the array a bit */
1843 if (skin_buffer_freespace() < needed
)
1845 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1848 skin_buffer_increment(needed
, false);
1849 max_tokens
+= extra_tokens
;
1852 switch(*wps_bufptr
++)
1857 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1859 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1862 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1864 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1870 /* Alternating sublines separator */
1872 if (level
>= 0) /* there are unclosed conditionals */
1874 fail
= PARSE_FAIL_UNCLOSED_COND
;
1878 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1879 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1883 /* Conditional list start */
1885 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1887 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1890 wps_bufptr
+= check_feature_tag(wps_bufptr
,
1891 data
->tokens
[data
->num_tokens
-1].type
);
1892 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1893 lastcond
[level
] = data
->num_tokens
++;
1896 /* Conditional list end */
1898 if (level
< 0) /* not in a conditional, invalid char */
1900 fail
= PARSE_FAIL_INVALID_CHAR
;
1904 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1905 if (lastcond
[level
])
1906 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1909 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1913 lastcond
[level
] = 0;
1915 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1919 /* Conditional list option */
1921 if (level
< 0) /* not in a conditional, invalid char */
1923 fail
= PARSE_FAIL_INVALID_CHAR
;
1927 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1928 if (lastcond
[level
])
1929 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1932 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1936 lastcond
[level
] = data
->num_tokens
;
1937 numoptions
[level
]++;
1943 if (level
>= 0) /* there are unclosed conditionals */
1945 fail
= PARSE_FAIL_UNCLOSED_COND
;
1949 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1952 /* End of this line */
1954 if (level
>= 0) /* there are unclosed conditionals */
1956 fail
= PARSE_FAIL_UNCLOSED_COND
;
1959 /* add a new token for the \n so empty lines are correct */
1960 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1961 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1962 data
->tokens
[data
->num_tokens
].next
= false;
1965 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1967 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1977 unsigned int len
= 1;
1978 const char *string_start
= wps_bufptr
- 1;
1980 /* find the length of the string */
1981 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1982 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1983 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1984 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1990 /* look if we already have that string */
1993 struct skin_token_list
*list
= data
->strings
;
1996 str
= (char*)list
->token
->value
.data
;
1997 found
= (strlen(str
) == len
&&
1998 strncmp(string_start
, str
, len
) == 0);
2000 break; /* break here because the list item is
2001 used if its found */
2004 /* If a matching string is found, found is true and i is
2005 the index of the string. If not, found is false */
2010 str
= (char*)skin_buffer_alloc(len
+1);
2013 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
2016 strlcpy(str
, string_start
, len
+1);
2017 struct skin_token_list
*item
=
2018 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
2021 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
2024 add_to_ll_chain(&data
->strings
, item
);
2028 /* another occurrence of an existing string */
2029 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
2031 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
2038 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
2039 fail
= PARSE_FAIL_UNCLOSED_COND
;
2041 if (*wps_bufptr
&& !fail
)
2042 /* one of the limits of the while loop was exceeded */
2043 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
2046 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
2047 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
2048 /* freeup unused tokens */
2049 skin_buffer_free_from_front(sizeof(struct wps_token
)
2050 * (max_tokens
- data
->num_tokens
));
2052 #ifdef DEBUG_SKIN_ENGINE
2055 print_debug_info(data
, fail
, line_number
);
2067 * initial setup of wps_data; does reset everything
2068 * except fields which need to survive, i.e.
2071 static void skin_data_reset(struct wps_data
*wps_data
)
2073 #ifdef HAVE_LCD_BITMAP
2074 wps_data
->images
= NULL
;
2075 wps_data
->progressbars
= NULL
;
2077 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2078 wps_data
->backdrop
= NULL
;
2080 #ifdef HAVE_TOUCHSCREEN
2081 wps_data
->touchregions
= NULL
;
2083 wps_data
->viewports
= NULL
;
2084 wps_data
->strings
= NULL
;
2085 #ifdef HAVE_ALBUMART
2086 wps_data
->albumart
= NULL
;
2087 if (wps_data
->playback_aa_slot
>= 0)
2089 playback_release_aa_slot(wps_data
->playback_aa_slot
);
2090 wps_data
->playback_aa_slot
= -1;
2093 wps_data
->tokens
= NULL
;
2094 wps_data
->num_tokens
= 0;
2096 #ifdef HAVE_LCD_BITMAP
2097 wps_data
->peak_meter_enabled
= false;
2098 wps_data
->wps_sb_tag
= false;
2099 wps_data
->show_sb_on_wps
= false;
2100 #else /* HAVE_LCD_CHARCELLS */
2103 for (i
= 0; i
< 8; i
++)
2105 wps_data
->wps_progress_pat
[i
] = 0;
2107 wps_data
->full_line_progressbar
= false;
2109 wps_data
->wps_loaded
= false;
2112 #ifdef HAVE_LCD_BITMAP
2113 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
2115 (void)wps_data
; /* only needed for remote targets */
2116 char img_path
[MAX_PATH
];
2117 get_image_filename(bitmap
->data
, bmpdir
,
2118 img_path
, sizeof(img_path
));
2120 /* load the image */
2122 #ifdef HAVE_REMOTE_LCD
2123 if (curr_screen
== SCREEN_REMOTE
)
2124 format
= FORMAT_ANY
|FORMAT_REMOTE
;
2127 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
2130 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
2131 bitmap
->data
= imgbuf
;
2132 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
2136 skin_buffer_increment(ret
, true);
2141 /* Abort if we can't load an image */
2142 DEBUGF("Couldn't load '%s'\n", img_path
);
2147 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
2149 struct skin_token_list
*list
;
2150 bool retval
= true; /* return false if a single image failed to load */
2151 /* do the progressbars */
2152 list
= wps_data
->progressbars
;
2155 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
2158 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
2159 if (!pb
->have_bitmap_pb
) /* no success */
2164 /* regular images */
2165 list
= wps_data
->images
;
2168 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
2171 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
2173 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
2180 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2181 /* Backdrop load scheme:
2183 * 2) load the backdrop from settings
2185 if (wps_data
->backdrop
)
2187 bool needed
= wps_data
->backdrop
[0] != '-';
2188 wps_data
->backdrop
= skin_backdrop_load(wps_data
->backdrop
,
2189 bmpdir
, curr_screen
);
2190 if (!wps_data
->backdrop
&& needed
)
2193 #endif /* has backdrop support */
2198 static bool skin_load_fonts(struct wps_data
*data
)
2200 /* don't spit out after the first failue to aid debugging */
2201 bool success
= true;
2202 struct skin_token_list
*vp_list
;
2204 /* walk though each viewport and assign its font */
2205 for(vp_list
= data
->viewports
; vp_list
; vp_list
= vp_list
->next
)
2207 /* first, find the viewports that have a non-sys/ui-font font */
2208 struct skin_viewport
*skin_vp
=
2209 (struct skin_viewport
*)vp_list
->token
->value
.data
;
2210 struct viewport
*vp
= &skin_vp
->vp
;
2213 if (vp
->font
<= FONT_UI
)
2214 { /* the usual case -> built-in fonts */
2215 #ifdef HAVE_REMOTE_LCD
2216 if (vp
->font
== FONT_UI
)
2217 vp
->font
+= curr_screen
;
2223 /* now find the corresponding skin_font */
2224 struct skin_font
*font
= &skinfonts
[font_id
-FONT_FIRSTUSERFONT
];
2229 DEBUGF("font %d not specified\n", font_id
);
2235 /* load the font - will handle loading the same font again if
2236 * multiple viewports use the same */
2239 char *dot
= strchr(font
->name
, '.');
2241 font
->id
= skin_font_load(font
->name
);
2246 DEBUGF("Unable to load font %d: '%s.fnt'\n",
2247 font_id
, font
->name
);
2253 /* finally, assign the font_id to the viewport */
2254 vp
->font
= font
->id
;
2259 #endif /* HAVE_LCD_BITMAP */
2261 /* to setup up the wps-data from a format-buffer (isfile = false)
2262 from a (wps-)file (isfile = true)*/
2263 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
2264 const char *buf
, bool isfile
)
2266 char *wps_buffer
= NULL
;
2267 if (!wps_data
|| !buf
)
2269 #ifdef HAVE_ALBUMART
2271 struct mp3entry
*curtrack
;
2273 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
2274 if (wps_data
->albumart
)
2276 old_aa
.state
= wps_data
->albumart
->state
;
2277 old_aa
.height
= wps_data
->albumart
->height
;
2278 old_aa
.width
= wps_data
->albumart
->width
;
2281 #ifdef HAVE_LCD_BITMAP
2283 for (i
=0;i
<MAXUSERFONTS
;i
++)
2285 skinfonts
[i
].id
= -1;
2286 skinfonts
[i
].name
= NULL
;
2289 #ifdef DEBUG_SKIN_ENGINE
2290 if (isfile
&& debug_wps
)
2292 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf
);
2296 skin_data_reset(wps_data
);
2297 wps_data
->wps_loaded
= false;
2298 curr_screen
= screen
;
2300 /* alloc default viewport, will be fixed up later */
2301 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
2304 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
2307 add_to_ll_chain(&wps_data
->viewports
, list
);
2310 /* Initialise the first (default) viewport */
2311 curr_vp
->label
= VP_DEFAULT_LABEL
;
2312 curr_vp
->hidden_flags
= 0;
2313 curr_vp
->lines
= NULL
;
2315 viewport_set_defaults(&curr_vp
->vp
, screen
);
2316 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2317 curr_vp
->start_fgcolour
= curr_vp
->vp
.fg_pattern
;
2318 curr_vp
->start_bgcolour
= curr_vp
->vp
.bg_pattern
;
2320 #ifdef HAVE_LCD_BITMAP
2321 curr_vp
->vp
.font
= FONT_UI
;
2325 if (!skin_start_new_line(curr_vp
, 0))
2330 int fd
= open_utf8(buf
, O_RDONLY
);
2335 /* get buffer space from the plugin buffer */
2336 size_t buffersize
= 0;
2337 wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
2342 /* copy the file's content to the buffer for parsing,
2343 ensuring that every line ends with a newline char. */
2344 unsigned int start
= 0;
2345 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
2347 start
+= strlen(wps_buffer
+ start
);
2348 if (start
< buffersize
- 1)
2350 wps_buffer
[start
++] = '\n';
2351 wps_buffer
[start
] = 0;
2360 wps_buffer
= (char*)buf
;
2362 /* parse the WPS source */
2363 if (!wps_parse(wps_data
, wps_buffer
, isfile
)) {
2364 skin_data_reset(wps_data
);
2368 #ifdef HAVE_LCD_BITMAP
2369 char bmpdir
[MAX_PATH
];
2372 /* get the bitmap dir */
2373 char *dot
= strrchr(buf
, '.');
2374 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
2378 snprintf(bmpdir
, MAX_PATH
, "%s", BACKDROP_DIR
);
2380 /* load the bitmaps that were found by the parsing */
2381 if (!load_skin_bitmaps(wps_data
, bmpdir
) ||
2382 !skin_load_fonts(wps_data
))
2384 skin_data_reset(wps_data
);
2388 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2389 status
= audio_status();
2390 if (status
& AUDIO_STATUS_PLAY
)
2392 struct skin_albumart
*aa
= wps_data
->albumart
;
2393 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
2395 (((old_aa
.height
!= aa
->height
) ||
2396 (old_aa
.width
!= aa
->width
))))))
2398 curtrack
= audio_current_track();
2399 offset
= curtrack
->offset
;
2401 if (!(status
& AUDIO_STATUS_PAUSE
))
2406 wps_data
->wps_loaded
= true;
2407 #ifdef DEBUG_SKIN_ENGINE
2408 if (isfile
&& debug_wps
)