1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
39 #define lang_is_rtl() (false)
51 #include "wps_internals.h"
52 #include "skin_engine.h"
54 #include "settings_list.h"
55 #include "skin_fonts.h"
57 #ifdef HAVE_LCD_BITMAP
66 #include "statusbar-skinned.h"
68 #define WPS_ERROR_INVALID_PARAM -1
70 /* which screen are we parsing for? */
71 static enum screen_type curr_screen
;
73 /* level of current conditional.
74 -1 means we're not in a conditional. */
75 static int level
= -1;
77 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
78 or WPS_TOKEN_CONDITIONAL_START in current level */
79 static int lastcond
[WPS_MAX_COND_LEVEL
];
81 /* index of the WPS_TOKEN_CONDITIONAL in current level */
82 static int condindex
[WPS_MAX_COND_LEVEL
];
84 /* number of condtional options in current level */
85 static int numoptions
[WPS_MAX_COND_LEVEL
];
87 /* line number, debug only */
88 static int line_number
;
90 /* the current viewport */
91 static struct skin_viewport
*curr_vp
;
92 /* the current line, linked to the above viewport */
93 static struct skin_line
*curr_line
;
95 static int follow_lang_direction
= 0;
97 #if defined(DEBUG) || defined(SIMULATOR)
98 /* debugging function */
99 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
100 extern void debug_skin_usage(void);
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
109 wps_bufptr points to the char following the tag (i.e. where
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
114 struct wps_token
*token
, struct wps_data
*wps_data
);
117 enum wps_token_type type
;
119 unsigned char refresh_type
;
120 const wps_tag_parse_func parse_func
;
122 static int skip_end_of_line(const char *wps_bufptr
);
123 /* prototypes of all special parse functions : */
124 static int parse_timeout(const char *wps_bufptr
,
125 struct wps_token
*token
, struct wps_data
*wps_data
);
126 static int parse_progressbar(const char *wps_bufptr
,
127 struct wps_token
*token
, struct wps_data
*wps_data
);
128 static int parse_dir_level(const char *wps_bufptr
,
129 struct wps_token
*token
, struct wps_data
*wps_data
);
130 static int parse_setting_and_lang(const char *wps_bufptr
,
131 struct wps_token
*token
, struct wps_data
*wps_data
);
134 static int parse_languagedirection(const char *wps_bufptr
,
135 struct wps_token
*token
, struct wps_data
*wps_data
)
140 follow_lang_direction
= 2; /* 2 because it is decremented immediatly after
141 this token is parsed, after the next token it
146 #ifdef HAVE_LCD_BITMAP
147 static int parse_viewport_display(const char *wps_bufptr
,
148 struct wps_token
*token
, struct wps_data
*wps_data
);
149 static int parse_playlistview(const char *wps_bufptr
,
150 struct wps_token
*token
, struct wps_data
*wps_data
);
151 static int parse_viewport(const char *wps_bufptr
,
152 struct wps_token
*token
, struct wps_data
*wps_data
);
153 static int parse_statusbar_enable(const char *wps_bufptr
,
154 struct wps_token
*token
, struct wps_data
*wps_data
);
155 static int parse_statusbar_disable(const char *wps_bufptr
,
156 struct wps_token
*token
, struct wps_data
*wps_data
);
157 static int parse_image_display(const char *wps_bufptr
,
158 struct wps_token
*token
, struct wps_data
*wps_data
);
159 static int parse_image_load(const char *wps_bufptr
,
160 struct wps_token
*token
, struct wps_data
*wps_data
);
161 static int parse_font_load(const char *wps_bufptr
,
162 struct wps_token
*token
, struct wps_data
*wps_data
);
163 #endif /*HAVE_LCD_BITMAP */
164 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
165 static int parse_image_special(const char *wps_bufptr
,
166 struct wps_token
*token
, struct wps_data
*wps_data
);
169 static int parse_albumart_load(const char *wps_bufptr
,
170 struct wps_token
*token
, struct wps_data
*wps_data
);
171 static int parse_albumart_display(const char *wps_bufptr
,
172 struct wps_token
*token
, struct wps_data
*wps_data
);
173 #endif /* HAVE_ALBUMART */
174 #ifdef HAVE_TOUCHSCREEN
175 static int parse_touchregion(const char *wps_bufptr
,
176 struct wps_token
*token
, struct wps_data
*wps_data
);
178 static int fulline_tag_not_supported(const char *wps_bufptr
,
179 struct wps_token
*token
, struct wps_data
*wps_data
)
181 (void)token
; (void)wps_data
;
182 return skip_end_of_line(wps_bufptr
);
184 #define parse_touchregion fulline_tag_not_supported
187 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
189 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
192 /* array of available tags - those with more characters have to go first
193 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
194 static const struct wps_tag all_tags
[] = {
196 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
197 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
198 { WPS_TOKEN_ALIGN_LEFT_RTL
, "aL", 0, NULL
},
199 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
200 { WPS_TOKEN_ALIGN_RIGHT_RTL
, "aR", 0, NULL
},
201 { WPS_NO_TOKEN
, "ax", 0, parse_languagedirection
},
203 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
204 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
205 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
206 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
207 #if CONFIG_CHARGING >= CHARGING_MONITOR
208 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
211 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
213 #ifdef HAVE_USB_POWER
214 { WPS_TOKEN_USB_POWERED
, "bu", WPS_REFRESH_DYNAMIC
, NULL
},
217 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
218 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
219 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
220 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
221 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
222 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
223 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
224 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
225 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
226 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
227 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
228 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
229 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
230 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
231 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
232 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
233 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
234 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
235 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
238 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
243 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
244 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
245 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
251 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
257 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
258 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
263 /* current metadata */
264 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
270 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
271 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
272 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
273 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
274 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
275 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
278 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
279 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
280 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
281 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
282 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
283 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
284 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
285 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
286 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
287 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
288 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
289 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
291 #if (CONFIG_CODEC != MAS3507D)
292 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
294 #if (CONFIG_CODEC == SWCODEC)
295 { WPS_TOKEN_SOUND_SPEED
, "Ss", WPS_REFRESH_DYNAMIC
, NULL
},
297 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
298 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
301 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
303 #ifdef HAS_REMOTE_BUTTON_HOLD
304 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
306 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
309 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
310 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
311 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
314 #ifdef HAVE_LCD_BITMAP
315 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
317 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
318 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
320 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
323 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
325 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
326 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
327 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
328 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
330 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
331 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
332 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
333 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
336 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
337 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
338 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
341 #if CONFIG_CODEC == SWCODEC
342 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
343 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
346 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
347 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
349 #ifdef HAVE_LCD_BITMAP
350 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
351 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
353 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
355 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
356 parse_image_display
},
358 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
359 { WPS_NO_TOKEN
, "Fl", 0, parse_font_load
},
361 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
362 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
, parse_albumart_display
},
365 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
366 parse_viewport_display
},
367 #ifdef HAVE_LCD_BITMAP
368 { WPS_VIEWPORT_CUSTOMLIST
, "Vp", WPS_REFRESH_STATIC
, parse_playlistview
},
370 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
372 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
373 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
377 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
,
378 parse_setting_and_lang
},
379 { WPS_TOKEN_TRANSLATEDSTRING
, "Sx", WPS_REFRESH_STATIC
,
380 parse_setting_and_lang
},
381 { WPS_TOKEN_LANG_IS_RTL
, "Sr", WPS_REFRESH_STATIC
, NULL
},
383 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
384 { WPS_TOKEN_CURRENT_SCREEN
, "cs", WPS_REFRESH_DYNAMIC
, NULL
},
385 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
388 /* Recording Tokens */
389 { WPS_TOKEN_HAVE_RECORDING
, "Rp", WPS_REFRESH_STATIC
, NULL
},
390 #ifdef HAVE_RECORDING
391 { WPS_TOKEN_REC_FREQ
, "Rf", WPS_REFRESH_DYNAMIC
, NULL
},
392 { WPS_TOKEN_REC_ENCODER
, "Re", WPS_REFRESH_DYNAMIC
, NULL
},
393 { WPS_TOKEN_REC_BITRATE
, "Rb", WPS_REFRESH_DYNAMIC
, NULL
},
394 { WPS_TOKEN_REC_MONO
, "Rm", WPS_REFRESH_DYNAMIC
, NULL
},
396 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
397 /* the array MUST end with an empty string (first char is \0) */
401 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
402 * chains require the order to be kept.
404 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
410 struct skin_token_list
*t
= *list
;
417 /* traverse the image linked-list for an image */
418 #ifdef HAVE_LCD_BITMAP
419 struct gui_img
* find_image(char label
, struct wps_data
*data
)
421 struct skin_token_list
*list
= data
->images
;
424 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
425 if (img
->label
== label
)
433 /* traverse the viewport linked list for a viewport */
434 struct skin_viewport
* find_viewport(char label
, struct wps_data
*data
)
436 struct skin_token_list
*list
= data
->viewports
;
439 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->token
->value
.data
;
440 if (vp
->label
== label
)
448 /* create and init a new wpsll item.
449 * passing NULL to token will alloc a new one.
450 * You should only pass NULL for the token when the token type (table above)
451 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
453 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
456 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
458 token
= skin_buffer_alloc(sizeof(struct wps_token
));
459 if (!llitem
|| !token
)
462 llitem
->token
= token
;
464 llitem
->token
->value
.data
= token_data
;
468 /* Returns the number of chars that should be skipped to jump
469 immediately after the first eol, i.e. to the start of the next line */
470 static int skip_end_of_line(const char *wps_bufptr
)
474 while(*(wps_bufptr
+ skip
) != '\n')
479 /* Starts a new subline in the current line during parsing */
480 static bool skin_start_new_subline(struct skin_line
*line
, int curr_token
)
482 struct skin_subline
*subline
= skin_buffer_alloc(sizeof(struct skin_subline
));
486 subline
->first_token_idx
= curr_token
;
487 subline
->next
= NULL
;
489 subline
->line_type
= 0;
490 subline
->time_mult
= 0;
492 line
->curr_subline
->last_token_idx
= curr_token
-1;
493 line
->curr_subline
->next
= subline
;
494 line
->curr_subline
= subline
;
498 static bool skin_start_new_line(struct skin_viewport
*vp
, int curr_token
)
500 struct skin_line
*line
= skin_buffer_alloc(sizeof(struct skin_line
));
501 struct skin_subline
*subline
= NULL
;
505 /* init the subline */
506 subline
= &line
->sublines
;
507 subline
->first_token_idx
= curr_token
;
508 subline
->next
= NULL
;
509 subline
->line_type
= 0;
510 subline
->time_mult
= 0;
512 /* init the new line */
513 line
->curr_subline
= &line
->sublines
;
515 line
->subline_expire_time
= 0;
517 /* connect to curr_line and vp pointers.
518 * 1) close the previous lines subline
519 * 2) connect to vp pointer
520 * 3) connect to curr_line global pointer
524 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
525 curr_line
->next
= line
;
526 curr_line
->curr_subline
= NULL
;
534 #ifdef HAVE_LCD_BITMAP
536 static int parse_statusbar_enable(const char *wps_bufptr
,
537 struct wps_token
*token
,
538 struct wps_data
*wps_data
)
540 (void)token
; /* Kill warnings */
541 wps_data
->wps_sb_tag
= true;
542 wps_data
->show_sb_on_wps
= true;
543 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
544 viewport_set_defaults(&default_vp
->vp
, curr_screen
);
545 return skip_end_of_line(wps_bufptr
);
548 static int parse_statusbar_disable(const char *wps_bufptr
,
549 struct wps_token
*token
,
550 struct wps_data
*wps_data
)
552 (void)token
; /* Kill warnings */
553 wps_data
->wps_sb_tag
= true;
554 wps_data
->show_sb_on_wps
= false;
555 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
556 viewport_set_fullscreen(&default_vp
->vp
, curr_screen
);
557 return skip_end_of_line(wps_bufptr
);
560 static int get_image_id(int c
)
562 if(c
>= 'a' && c
<= 'z')
564 else if(c
>= 'A' && c
<= 'Z')
570 char *get_image_filename(const char *start
, const char* bmpdir
,
571 char *buf
, int buf_size
)
573 const char *end
= strchr(start
, '|');
575 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
581 int bmpdirlen
= strlen(bmpdir
);
584 buf
[bmpdirlen
] = '/';
585 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
586 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
591 static int parse_image_display(const char *wps_bufptr
,
592 struct wps_token
*token
,
593 struct wps_data
*wps_data
)
595 char label
= wps_bufptr
[0];
597 struct gui_img
*img
;;
600 img
= find_image(label
, wps_data
);
603 token
->value
.i
= label
; /* so debug works */
604 return WPS_ERROR_INVALID_PARAM
;
607 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
609 if (subimage
>= img
->num_subimages
)
610 return WPS_ERROR_INVALID_PARAM
;
612 /* Store sub-image number to display in high bits */
613 token
->value
.i
= label
| (subimage
<< 8);
614 return 2; /* We have consumed 2 bytes */
616 token
->value
.i
= label
;
617 return 1; /* We have consumed 1 byte */
621 static int parse_image_load(const char *wps_bufptr
,
622 struct wps_token
*token
,
623 struct wps_data
*wps_data
)
625 const char *ptr
= wps_bufptr
;
627 const char* filename
;
633 /* format: %x|n|filename.bmp|x|y|
634 or %xl|n|filename.bmp|x|y|
635 or %xl|n|filename.bmp|x|y|num_subimages|
639 return WPS_ERROR_INVALID_PARAM
;
643 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
644 return WPS_ERROR_INVALID_PARAM
;
646 /* Check there is a terminating | */
648 return WPS_ERROR_INVALID_PARAM
;
650 /* check the image number and load state */
651 if(find_image(*id
, wps_data
))
653 /* Invalid image ID */
654 return WPS_ERROR_INVALID_PARAM
;
656 img
= skin_buffer_alloc(sizeof(struct gui_img
));
658 return WPS_ERROR_INVALID_PARAM
;
659 /* save a pointer to the filename */
660 img
->bm
.data
= (char*)filename
;
664 img
->num_subimages
= 1;
665 img
->always_display
= false;
667 /* save current viewport */
668 img
->vp
= &curr_vp
->vp
;
670 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
672 img
->always_display
= true;
676 /* Parse the (optional) number of sub-images */
678 newline
= strchr(ptr
, '\n');
679 pos
= strchr(ptr
, '|');
680 if (pos
&& pos
< newline
)
681 img
->num_subimages
= atoi(ptr
);
683 if (img
->num_subimages
<= 0)
684 return WPS_ERROR_INVALID_PARAM
;
686 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
688 return WPS_ERROR_INVALID_PARAM
;
689 add_to_ll_chain(&wps_data
->images
, item
);
691 /* Skip the rest of the line */
692 return skip_end_of_line(wps_bufptr
);
695 static int font_ids
[MAXUSERFONTS
];
696 static int parse_font_load(const char *wps_bufptr
,
697 struct wps_token
*token
, struct wps_data
*wps_data
)
699 (void)wps_data
; (void)token
;
700 const char *ptr
= wps_bufptr
;
702 char *filename
, buf
[MAX_PATH
];
705 return WPS_ERROR_INVALID_PARAM
;
709 if (!(ptr
= parse_list("ds", NULL
, '|', ptr
, &id
, &filename
)))
710 return WPS_ERROR_INVALID_PARAM
;
712 /* Check there is a terminating | */
714 return WPS_ERROR_INVALID_PARAM
;
716 if (id
<= FONT_UI
|| id
>= MAXFONTS
-1)
717 return WPS_ERROR_INVALID_PARAM
;
718 id
-= SYSTEMFONTCOUNT
;
720 memcpy(buf
, filename
, ptr
-filename
);
721 buf
[ptr
-filename
] = '\0';
722 font_ids
[id
] = skin_font_load(buf
);
724 return font_ids
[id
] >= 0 ? skip_end_of_line(wps_bufptr
) : WPS_ERROR_INVALID_PARAM
;
728 static int parse_viewport_display(const char *wps_bufptr
,
729 struct wps_token
*token
,
730 struct wps_data
*wps_data
)
733 char letter
= wps_bufptr
[0];
735 if (letter
< 'a' || letter
> 'z')
737 /* invalid viewport tag */
738 return WPS_ERROR_INVALID_PARAM
;
740 token
->value
.i
= letter
;
744 #ifdef HAVE_LCD_BITMAP
745 static int parse_playlistview_text(struct playlistviewer
*viewer
,
746 enum info_line_type line
, char* text
)
749 const struct wps_tag
*tag
;
751 const char *start
= text
;
755 viewer
->lines
[line
].count
= 0;
756 viewer
->lines
[line
].scroll
= false;
759 if (*text
== '%') /* it is a token of some type */
771 /* escaped characters */
772 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_CHARACTER
;
773 viewer
->lines
[line
].strings
[cur_string
][0] = *text
;
774 viewer
->lines
[line
].strings
[cur_string
++][1] = '\0';
778 strncmp(text
, tag
->name
, strlen(tag
->name
)) != 0;
780 /* %s isnt stored as a tag so manually check for it */
781 if (tag
->type
== WPS_NO_TOKEN
)
783 if (!strncmp(tag
->name
, "s", 1))
785 viewer
->lines
[line
].scroll
= true;
789 else if (tag
->type
== WPS_TOKEN_UNKNOWN
)
792 /* just copy the string */
793 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_STRING
;
794 while (i
<(MAX_PLAYLISTLINE_STRLEN
-1) && text
[i
] != '|' && text
[i
] != '%')
796 viewer
->lines
[line
].strings
[cur_string
][i
] = text
[i
];
799 viewer
->lines
[line
].strings
[cur_string
][i
] = '\0';
807 /* unsupported tag, reject */
810 taglen
= strlen(tag
->name
);
811 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = tag
->type
;
820 /* just copy the string */
821 viewer
->lines
[line
].tokens
[viewer
->lines
[line
].count
++] = WPS_TOKEN_STRING
;
822 while (i
<(MAX_PLAYLISTLINE_STRLEN
-1) && text
[i
] != '|' && text
[i
] != '%')
824 viewer
->lines
[line
].strings
[cur_string
][i
] = text
[i
];
827 viewer
->lines
[line
].strings
[cur_string
][i
] = '\0';
836 static int parse_playlistview(const char *wps_bufptr
,
837 struct wps_token
*token
, struct wps_data
*wps_data
)
840 /* %Vp|<use icons>|<start offset>|info line text|no info text| */
841 struct playlistviewer
*viewer
= skin_buffer_alloc(sizeof(struct playlistviewer
));
842 char *ptr
= strchr(wps_bufptr
, '|');
845 return WPS_ERROR_INVALID_PARAM
;
846 viewer
->vp
= &curr_vp
->vp
;
847 viewer
->show_icons
= true;
848 viewer
->start_offset
= atoi(ptr
+1);
849 token
->value
.data
= (void*)viewer
;
850 ptr
= strchr(ptr
+1, '|');
851 length
= parse_playlistview_text(viewer
, TRACK_HAS_INFO
, ptr
);
853 return WPS_ERROR_INVALID_PARAM
;
854 length
= parse_playlistview_text(viewer
, TRACK_HAS_NO_INFO
, ptr
+length
);
856 return WPS_ERROR_INVALID_PARAM
;
858 return skip_end_of_line(wps_bufptr
);
862 static int parse_viewport(const char *wps_bufptr
,
863 struct wps_token
*token
,
864 struct wps_data
*wps_data
)
866 (void)token
; /* Kill warnings */
867 const char *ptr
= wps_bufptr
;
869 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
871 /* check for the optional letter to signify its a hideable viewport */
872 /* %Vl|<label>|<rest of tags>| */
873 skin_vp
->hidden_flags
= 0;
874 skin_vp
->label
= VP_NO_LABEL
;
876 skin_vp
->lines
= NULL
;
879 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
880 - (wps_data
->num_tokens
> 0 ? 1 : 0);
884 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
885 return WPS_ERROR_INVALID_PARAM
;
890 skin_vp
->label
= VP_INFO_LABEL
;
891 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
894 else if (*ptr
== 'l')
898 char label
= *(ptr
+2);
899 if (label
>= 'a' && label
<= 'z')
901 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
902 skin_vp
->label
= label
;
905 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
910 return WPS_ERROR_INVALID_PARAM
;
913 struct viewport
*vp
= &skin_vp
->vp
;
914 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
915 if (!(ptr
= viewport_parse_viewport(vp
, curr_screen
, ptr
, '|')))
916 return WPS_ERROR_INVALID_PARAM
;
918 /* Check for trailing | */
920 return WPS_ERROR_INVALID_PARAM
;
922 if (follow_lang_direction
&& lang_is_rtl())
924 vp
->flags
|= VP_FLAG_ALIGN_RIGHT
;
925 vp
->x
= screens
[curr_screen
].lcdwidth
- vp
->width
- vp
->x
;
928 vp
->flags
&= ~VP_FLAG_ALIGN_RIGHT
; /* ignore right-to-left languages */
930 if (vp
->font
>= SYSTEMFONTCOUNT
)
931 vp
->font
= font_ids
[vp
->font
- SYSTEMFONTCOUNT
];
933 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
935 return WPS_ERROR_INVALID_PARAM
;
936 add_to_ll_chain(&wps_data
->viewports
, list
);
938 /* Skip the rest of the line */
939 return skip_end_of_line(wps_bufptr
);
942 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
943 static int parse_image_special(const char *wps_bufptr
,
944 struct wps_token
*token
,
945 struct wps_data
*wps_data
)
947 (void)wps_data
; /* kill warning */
949 const char *pos
= NULL
;
953 pos
= strchr(wps_bufptr
+ 1, '|');
954 newline
= strchr(wps_bufptr
, '\n');
956 error
= (pos
> newline
);
960 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
962 /* format: %X|filename.bmp| or %Xd */
963 if (*(wps_bufptr
) == 'd')
965 wps_data
->backdrop
= NULL
;
966 return skip_end_of_line(wps_bufptr
);
969 wps_data
->backdrop
= (char*)wps_bufptr
+ 1;
973 return WPS_ERROR_INVALID_PARAM
;
974 /* Skip the rest of the line */
975 return skip_end_of_line(wps_bufptr
);
979 #endif /* HAVE_LCD_BITMAP */
981 static int parse_setting_and_lang(const char *wps_bufptr
,
982 struct wps_token
*token
,
983 struct wps_data
*wps_data
)
985 /* NOTE: both the string validations that happen in here will
986 * automatically PASS on checkwps because its too hard to get
987 * settings_list.c and englinsh.lang built for it.
988 * If that ever changes remove the #ifndef __PCTOOL__'s here
991 const char *ptr
= wps_bufptr
;
996 /* Find the setting's cfg_name */
998 return WPS_ERROR_INVALID_PARAM
;
1000 end
= strchr(ptr
,'|');
1002 return WPS_ERROR_INVALID_PARAM
;
1003 strlcpy(temp
, ptr
,end
-ptr
+1);
1005 if (token
->type
== WPS_TOKEN_TRANSLATEDSTRING
)
1008 i
= lang_english_to_id(temp
);
1010 return WPS_ERROR_INVALID_PARAM
;
1015 /* Find the setting */
1016 for (i
=0; i
<nb_settings
; i
++)
1017 if (settings
[i
].cfg_name
&&
1018 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
1019 /* prevent matches on cfg_name prefixes */
1020 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
1023 if (i
== nb_settings
)
1024 return WPS_ERROR_INVALID_PARAM
;
1027 /* Store the setting number */
1030 /* Skip the rest of the line */
1035 static int parse_dir_level(const char *wps_bufptr
,
1036 struct wps_token
*token
,
1037 struct wps_data
*wps_data
)
1039 char val
[] = { *wps_bufptr
, '\0' };
1040 token
->value
.i
= atoi(val
);
1041 (void)wps_data
; /* Kill warnings */
1045 static int parse_timeout(const char *wps_bufptr
,
1046 struct wps_token
*token
,
1047 struct wps_data
*wps_data
)
1051 bool have_point
= false;
1052 bool have_tenth
= false;
1054 (void)wps_data
; /* Kill the warning */
1056 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
1058 if (*wps_bufptr
!= '.')
1061 val
+= *wps_bufptr
- '0';
1077 if (have_tenth
== false)
1080 if (val
== 0 && skip
== 0)
1082 /* decide what to do if no value was specified */
1083 switch (token
->type
)
1085 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1087 case WPS_TOKEN_BUTTON_VOLUME
:
1092 token
->value
.i
= val
;
1097 static int parse_progressbar(const char *wps_bufptr
,
1098 struct wps_token
*token
,
1099 struct wps_data
*wps_data
)
1101 /* %pb or %pb|filename|x|y|width|height|
1102 using - for any of the params uses "sane" values */
1103 #ifdef HAVE_LCD_BITMAP
1111 const char *filename
;
1112 int x
, y
, height
, width
;
1114 const char *ptr
= wps_bufptr
;
1115 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
1116 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
1119 return WPS_ERROR_INVALID_PARAM
;
1121 struct viewport
*vp
= &curr_vp
->vp
;
1123 int font_height
= font_get(vp
->font
)->height
;
1125 int font_height
= 8;
1127 /* we need to know what line number (viewport relative) this pb is,
1128 * so count them... */
1130 struct skin_line
*line
= curr_vp
->lines
;
1136 pb
->have_bitmap_pb
= false;
1137 pb
->bm
.data
= NULL
; /* no bitmap specified */
1138 pb
->follow_lang_direction
= follow_lang_direction
> 0;
1140 if (*wps_bufptr
!= '|') /* regular old style */
1143 pb
->width
= vp
->width
;
1144 pb
->height
= SYSFONT_HEIGHT
-2;
1145 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1148 add_to_ll_chain(&wps_data
->progressbars
, item
);
1151 ptr
= wps_bufptr
+ 1;
1153 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
1154 &x
, &y
, &width
, &height
)))
1155 return WPS_ERROR_INVALID_PARAM
;
1157 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
1158 pb
->bm
.data
= (char*)filename
;
1160 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
1165 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
1167 /* A zero width causes a divide-by-zero error later, so reject it */
1169 return WPS_ERROR_INVALID_PARAM
;
1174 pb
->width
= vp
->width
- pb
->x
;
1176 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
1178 /* A zero height makes no sense - reject it */
1180 return WPS_ERROR_INVALID_PARAM
;
1182 pb
->height
= height
;
1185 pb
->height
= font_height
;
1187 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
1190 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1193 add_to_ll_chain(&wps_data
->progressbars
, item
);
1195 /* Skip the rest of the line */
1196 return skip_end_of_line(wps_bufptr
)-1;
1200 if (*(wps_bufptr
-1) == 'f')
1201 wps_data
->full_line_progressbar
= true;
1203 wps_data
->full_line_progressbar
= false;
1210 #ifdef HAVE_ALBUMART
1211 static int parse_int(const char *newline
, const char **_pos
, int *num
)
1213 *_pos
= parse_list("d", NULL
, '|', *_pos
, num
);
1215 return (!*_pos
|| *_pos
> newline
|| **_pos
!= '|');
1218 static int parse_albumart_load(const char *wps_bufptr
,
1219 struct wps_token
*token
,
1220 struct wps_data
*wps_data
)
1222 const char *_pos
, *newline
;
1224 struct dim dimensions
;
1226 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
1227 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
1228 (void)token
; /* silence warning */
1230 return skip_end_of_line(wps_bufptr
);
1232 /* reset albumart info in wps */
1235 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1236 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1237 aa
->vp
= &curr_vp
->vp
;
1239 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1241 newline
= strchr(wps_bufptr
, '\n');
1246 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1250 /* initial validation and parsing of x component */
1251 if (parse_int(newline
, &_pos
, &aa
->x
))
1252 return WPS_ERROR_INVALID_PARAM
;
1256 /* initial validation and parsing of y component */
1257 if (parse_int(newline
, &_pos
, &aa
->y
))
1258 return WPS_ERROR_INVALID_PARAM
;
1260 /* parsing width field */
1264 /* apply each modifier in turn */
1272 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1274 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1278 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1284 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1286 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1294 /* simply ignored */
1301 /* extract max width data */
1304 if (parse_int(newline
, &_pos
, &aa
->width
))
1305 return WPS_ERROR_INVALID_PARAM
;
1308 /* parsing height field */
1312 /* apply each modifier in turn */
1319 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1323 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1328 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1336 /* simply ignored */
1343 /* extract max height data */
1346 if (parse_int(newline
, &_pos
, &aa
->height
))
1347 return WPS_ERROR_INVALID_PARAM
;
1350 /* if we got here, we parsed everything ok .. ! */
1353 else if (aa
->width
> LCD_WIDTH
)
1354 aa
->width
= LCD_WIDTH
;
1358 else if (aa
->height
> LCD_HEIGHT
)
1359 aa
->height
= LCD_HEIGHT
;
1362 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
1364 aa
->state
= WPS_ALBUMART_LOAD
;
1366 wps_data
->albumart
= aa
;
1368 dimensions
.width
= aa
->width
;
1369 dimensions
.height
= aa
->height
;
1371 albumart_slot
= playback_claim_aa_slot(&dimensions
);
1373 if (0 <= albumart_slot
)
1374 wps_data
->playback_aa_slot
= albumart_slot
;
1376 /* Skip the rest of the line */
1377 return skip_end_of_line(wps_bufptr
);
1380 static int parse_albumart_display(const char *wps_bufptr
,
1381 struct wps_token
*token
,
1382 struct wps_data
*wps_data
)
1385 struct wps_token
*prev
= token
-1;
1386 if ((wps_data
->num_tokens
>= 1) && (prev
->type
== WPS_TOKEN_CONDITIONAL
))
1388 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1390 else if (wps_data
->albumart
)
1392 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1395 /* the old code did this so keep it here for now...
1396 * this is to allow the posibility to showing the next tracks AA! */
1397 if (wps_bufptr
+1 == 'n')
1402 #endif /* HAVE_ALBUMART */
1404 #ifdef HAVE_TOUCHSCREEN
1406 struct touchaction
{const char* s
; int action
;};
1407 static const struct touchaction touchactions
[] = {
1408 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1409 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1410 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1411 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1412 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1413 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1414 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1415 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
1417 static int parse_touchregion(const char *wps_bufptr
,
1418 struct wps_token
*token
, struct wps_data
*wps_data
)
1422 struct touchregion
*region
= NULL
;
1423 const char *ptr
= wps_bufptr
;
1425 const char pb_string
[] = "progressbar";
1426 const char vol_string
[] = "volume";
1429 /* format: %T|x|y|width|height|action|
1430 * if action starts with & the area must be held to happen
1432 * play - play/pause playback
1433 * stop - stop playback, exit the wps
1436 * ffwd - seek forward
1437 * rwd - seek backwards
1438 * menu - go back to the main menu
1439 * browse - go back to the file/db browser
1440 * shuffle - toggle shuffle mode
1441 * repmode - cycle the repeat mode
1442 * quickscreen - go into the quickscreen
1443 * contextmenu - open the context menu
1444 * playlist - go into the playlist
1445 * pitch - go into the pitchscreen
1446 * volup - increase volume by one step
1447 * voldown - decrease volume by one step
1452 return WPS_ERROR_INVALID_PARAM
;
1455 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1456 return WPS_ERROR_INVALID_PARAM
;
1458 /* Check there is a terminating | */
1460 return WPS_ERROR_INVALID_PARAM
;
1462 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1464 return WPS_ERROR_INVALID_PARAM
;
1466 /* should probably do some bounds checking here with the viewport... but later */
1467 region
->action
= ACTION_NONE
;
1472 region
->wvp
= curr_vp
;
1474 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1475 && *(action
+ sizeof(pb_string
)-1) == '|')
1476 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1477 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1478 && *(action
+ sizeof(vol_string
)-1) == '|')
1479 region
->type
= WPS_TOUCHREGION_VOLUME
;
1482 region
->type
= WPS_TOUCHREGION_ACTION
;
1487 region
->repeat
= true;
1490 region
->repeat
= false;
1493 imax
= ARRAYLEN(touchactions
);
1494 while ((region
->action
== ACTION_NONE
) &&
1497 /* try to match with one of our touchregion screens */
1498 int len
= strlen(touchactions
[i
].s
);
1499 if (!strncmp(touchactions
[i
].s
, action
, len
)
1500 && *(action
+len
) == '|')
1501 region
->action
= touchactions
[i
].action
;
1504 if (region
->action
== ACTION_NONE
)
1505 return WPS_ERROR_INVALID_PARAM
;
1507 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1509 return WPS_ERROR_INVALID_PARAM
;
1510 add_to_ll_chain(&wps_data
->touchregions
, item
);
1511 return skip_end_of_line(wps_bufptr
);
1515 /* Parse a generic token from the given string. Return the length read */
1516 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1518 int skip
= 0, taglen
= 0, ret
;
1519 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1520 const struct wps_tag
*tag
;
1521 memset(token
, 0, sizeof(*token
));
1532 /* escaped characters */
1533 token
->type
= WPS_TOKEN_CHARACTER
;
1534 token
->value
.c
= *wps_bufptr
;
1536 wps_data
->num_tokens
++;
1540 /* conditional tag */
1541 token
->type
= WPS_TOKEN_CONDITIONAL
;
1543 condindex
[level
] = wps_data
->num_tokens
;
1544 numoptions
[level
] = 1;
1545 wps_data
->num_tokens
++;
1546 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1547 if (ret
< 0) return ret
;
1552 /* find what tag we have */
1553 for (tag
= all_tags
;
1554 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1557 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1558 token
->type
= tag
->type
;
1559 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1561 /* if the tag has a special parsing function, we call it */
1562 if (tag
->parse_func
)
1564 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1565 if (ret
< 0) return ret
;
1569 /* Some tags we don't want to save as tokens */
1570 if (tag
->type
== WPS_NO_TOKEN
)
1573 /* tags that start with 'F', 'I' or 'D' are for the next file */
1574 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1575 *(tag
->name
) == 'D')
1578 wps_data
->num_tokens
++;
1588 * Returns the number of bytes to skip the buf pointer to access the false
1589 * branch in a _binary_ conditional
1592 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1593 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1595 * depending on the features of a target it's not called from check_feature_tag,
1596 * hence the __attribute__ or it issues compiler warnings
1600 static int find_false_branch(const char *wps_bufptr
) __attribute__((unused
));
1601 static int find_false_branch(const char *wps_bufptr
)
1603 const char *buf
= wps_bufptr
;
1604 /* wps_bufptr is after the opening '<', hence level = 1*/
1611 { /* filter out the characters we check later if they're printed
1614 if (ch
== '<' || ch
== '>' || ch
== '|')
1616 /* else: some tags/printed literals we skip over */
1618 else if (ch
== '<') /* nested conditional */
1621 { /* closed our or a nested conditional,
1622 * do NOT skip over the '>' so that wps_parse() sees it for closing
1623 * if it is the closing one for our conditional */
1626 else if (ch
== '|' && level
== 1)
1627 { /* we found our separator, point before and get out */
1630 /* if level is 0, we don't have a false branch */
1631 } while (level
> 0 && *(++buf
));
1633 return buf
- wps_bufptr
;
1637 * returns the number of bytes to get the appropriate branch of a binary
1641 * - if a feature is available, it returns 0 to not skip anything
1642 * - if the feature is not available, skip to the false branch and don't
1643 * parse the true branch at all
1646 static int check_feature_tag(const char *wps_bufptr
, const int type
)
1651 case WPS_TOKEN_RTC_PRESENT
:
1655 return find_false_branch(wps_bufptr
);
1657 case WPS_TOKEN_HAVE_RECORDING
:
1658 #ifdef HAVE_RECORDING
1661 return find_false_branch(wps_bufptr
);
1663 default: /* not a tag we care about, just don't skip */
1670 data is the pointer to the structure where the parsed WPS should be stored.
1672 wps_bufptr points to the string containing the WPS tags */
1673 #define TOKEN_BLOCK_SIZE 128
1674 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1676 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1678 enum wps_parse_error fail
= PARSE_OK
;
1680 int max_tokens
= TOKEN_BLOCK_SIZE
;
1681 size_t buf_free
= 0;
1685 /* allocate enough RAM for a reasonable skin, grow as needed.
1686 * Free any used RAM before loading the images to be 100% RAM efficient */
1687 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1688 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1690 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1691 data
->num_tokens
= 0;
1694 /* Backdrop defaults to the setting unless %X is used, so set it now */
1695 if (global_settings
.backdrop_file
[0])
1697 data
->backdrop
= "-";
1701 while (*wps_bufptr
&& !fail
)
1703 if (follow_lang_direction
)
1704 follow_lang_direction
--;
1705 /* first make sure there is enough room for tokens */
1706 if (max_tokens
<= data
->num_tokens
+ 5)
1708 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1709 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1710 /* do some smarts here to grow the array a bit */
1711 if (skin_buffer_freespace() < needed
)
1713 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1716 skin_buffer_increment(needed
, false);
1717 max_tokens
+= extra_tokens
;
1720 switch(*wps_bufptr
++)
1725 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1727 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1730 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1732 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1738 /* Alternating sublines separator */
1740 if (level
>= 0) /* there are unclosed conditionals */
1742 fail
= PARSE_FAIL_UNCLOSED_COND
;
1746 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1747 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1751 /* Conditional list start */
1753 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1755 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1758 wps_bufptr
+= check_feature_tag(wps_bufptr
,
1759 data
->tokens
[data
->num_tokens
-1].type
);
1760 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1761 lastcond
[level
] = data
->num_tokens
++;
1764 /* Conditional list end */
1766 if (level
< 0) /* not in a conditional, invalid char */
1768 fail
= PARSE_FAIL_INVALID_CHAR
;
1772 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1773 if (lastcond
[level
])
1774 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1777 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1781 lastcond
[level
] = 0;
1783 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1787 /* Conditional list option */
1789 if (level
< 0) /* not in a conditional, invalid char */
1791 fail
= PARSE_FAIL_INVALID_CHAR
;
1795 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1796 if (lastcond
[level
])
1797 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1800 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1804 lastcond
[level
] = data
->num_tokens
;
1805 numoptions
[level
]++;
1811 if (level
>= 0) /* there are unclosed conditionals */
1813 fail
= PARSE_FAIL_UNCLOSED_COND
;
1817 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1820 /* End of this line */
1822 if (level
>= 0) /* there are unclosed conditionals */
1824 fail
= PARSE_FAIL_UNCLOSED_COND
;
1827 /* add a new token for the \n so empty lines are correct */
1828 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1829 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1830 data
->tokens
[data
->num_tokens
].next
= false;
1833 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1835 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1845 unsigned int len
= 1;
1846 const char *string_start
= wps_bufptr
- 1;
1848 /* find the length of the string */
1849 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1850 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1851 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1852 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1858 /* look if we already have that string */
1861 struct skin_token_list
*list
= data
->strings
;
1864 str
= (char*)list
->token
->value
.data
;
1865 found
= (strlen(str
) == len
&&
1866 strncmp(string_start
, str
, len
) == 0);
1868 break; /* break here because the list item is
1869 used if its found */
1872 /* If a matching string is found, found is true and i is
1873 the index of the string. If not, found is false */
1878 str
= (char*)skin_buffer_alloc(len
+1);
1881 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1884 strlcpy(str
, string_start
, len
+1);
1885 struct skin_token_list
*item
=
1886 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1889 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1892 add_to_ll_chain(&data
->strings
, item
);
1896 /* another occurrence of an existing string */
1897 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1899 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1906 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1907 fail
= PARSE_FAIL_UNCLOSED_COND
;
1909 if (*wps_bufptr
&& !fail
)
1910 /* one of the limits of the while loop was exceeded */
1911 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1914 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
1915 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
1916 /* freeup unused tokens */
1917 skin_buffer_free_from_front(sizeof(struct wps_token
)
1918 * (max_tokens
- data
->num_tokens
));
1920 #if defined(DEBUG) || defined(SIMULATOR)
1922 print_debug_info(data
, fail
, line_number
);
1932 * initial setup of wps_data; does reset everything
1933 * except fields which need to survive, i.e.
1936 static void skin_data_reset(struct wps_data
*wps_data
)
1938 #ifdef HAVE_LCD_BITMAP
1939 wps_data
->images
= NULL
;
1940 wps_data
->progressbars
= NULL
;
1942 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1943 wps_data
->backdrop
= NULL
;
1945 #ifdef HAVE_TOUCHSCREEN
1946 wps_data
->touchregions
= NULL
;
1948 wps_data
->viewports
= NULL
;
1949 wps_data
->strings
= NULL
;
1950 #ifdef HAVE_ALBUMART
1951 wps_data
->albumart
= NULL
;
1952 if (wps_data
->playback_aa_slot
>= 0)
1954 playback_release_aa_slot(wps_data
->playback_aa_slot
);
1955 wps_data
->playback_aa_slot
= -1;
1958 wps_data
->tokens
= NULL
;
1959 wps_data
->num_tokens
= 0;
1961 #ifdef HAVE_LCD_BITMAP
1962 wps_data
->peak_meter_enabled
= false;
1963 wps_data
->wps_sb_tag
= false;
1964 wps_data
->show_sb_on_wps
= false;
1965 #else /* HAVE_LCD_CHARCELLS */
1968 for (i
= 0; i
< 8; i
++)
1970 wps_data
->wps_progress_pat
[i
] = 0;
1972 wps_data
->full_line_progressbar
= false;
1974 wps_data
->wps_loaded
= false;
1977 #ifdef HAVE_LCD_BITMAP
1978 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1980 (void)wps_data
; /* only needed for remote targets */
1981 bool loaded
= false;
1982 char img_path
[MAX_PATH
];
1983 get_image_filename(bitmap
->data
, bmpdir
,
1984 img_path
, sizeof(img_path
));
1986 /* load the image */
1988 #ifdef HAVE_REMOTE_LCD
1989 if (curr_screen
== SCREEN_REMOTE
)
1990 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1993 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1996 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
1997 bitmap
->data
= imgbuf
;
1998 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
2002 skin_buffer_increment(ret
, true);
2007 /* Abort if we can't load an image */
2013 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
2015 struct skin_token_list
*list
;
2016 /* do the progressbars */
2017 list
= wps_data
->progressbars
;
2020 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
2023 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
2027 /* regular images */
2028 list
= wps_data
->images
;
2031 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
2034 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
2036 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
2041 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
2042 /* Backdrop load scheme:
2044 * 2) load the backdrop from settings
2046 if (wps_data
->backdrop
)
2048 wps_data
->backdrop
= skin_backdrop_load(wps_data
->backdrop
,
2049 bmpdir
, curr_screen
);
2051 #endif /* has backdrop support */
2053 /* If we got here, everything was OK */
2057 #endif /* HAVE_LCD_BITMAP */
2059 /* to setup up the wps-data from a format-buffer (isfile = false)
2060 from a (wps-)file (isfile = true)*/
2061 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
2062 const char *buf
, bool isfile
)
2065 if (!wps_data
|| !buf
)
2067 #ifdef HAVE_ALBUMART
2069 struct mp3entry
*curtrack
;
2071 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
2072 if (wps_data
->albumart
)
2074 old_aa
.state
= wps_data
->albumart
->state
;
2075 old_aa
.height
= wps_data
->albumart
->height
;
2076 old_aa
.width
= wps_data
->albumart
->width
;
2080 skin_data_reset(wps_data
);
2081 curr_screen
= screen
;
2083 /* alloc default viewport, will be fixed up later */
2084 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
2087 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
2090 add_to_ll_chain(&wps_data
->viewports
, list
);
2093 /* Initialise the first (default) viewport */
2094 curr_vp
->label
= VP_DEFAULT_LABEL
;
2096 curr_vp
->hidden_flags
= 0;
2097 curr_vp
->lines
= NULL
;
2099 viewport_set_defaults(&curr_vp
->vp
, screen
);
2102 if (!skin_start_new_line(curr_vp
, 0))
2107 if (wps_parse(wps_data
, buf
, false))
2109 #ifdef HAVE_LCD_BITMAP
2110 /* load the backdrop */
2111 if (!load_skin_bitmaps(wps_data
, BACKDROP_DIR
)) {
2112 skin_data_reset(wps_data
);
2122 int fd
= open_utf8(buf
, O_RDONLY
);
2127 /* get buffer space from the plugin buffer */
2128 size_t buffersize
= 0;
2129 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
2134 /* copy the file's content to the buffer for parsing,
2135 ensuring that every line ends with a newline char. */
2136 unsigned int start
= 0;
2137 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
2139 start
+= strlen(wps_buffer
+ start
);
2140 if (start
< buffersize
- 1)
2142 wps_buffer
[start
++] = '\n';
2143 wps_buffer
[start
] = 0;
2152 /* parse the WPS source */
2153 if (!wps_parse(wps_data
, wps_buffer
, true)) {
2154 skin_data_reset(wps_data
);
2158 wps_data
->wps_loaded
= true;
2160 #ifdef HAVE_LCD_BITMAP
2161 /* get the bitmap dir */
2162 char bmpdir
[MAX_PATH
];
2163 char *dot
= strrchr(buf
, '.');
2165 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
2167 /* load the bitmaps that were found by the parsing */
2168 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
2169 skin_data_reset(wps_data
);
2170 wps_data
->wps_loaded
= false;
2174 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2175 status
= audio_status();
2176 if (status
& AUDIO_STATUS_PLAY
)
2178 struct skin_albumart
*aa
= wps_data
->albumart
;
2179 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
2181 (((old_aa
.height
!= aa
->height
) ||
2182 (old_aa
.width
!= aa
->width
))))))
2184 curtrack
= audio_current_track();
2185 offset
= curtrack
->offset
;
2187 if (!(status
& AUDIO_STATUS_PAUSE
))
2192 #if defined(DEBUG) || defined(SIMULATOR)