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 ****************************************************************************/
50 #include "wps_internals.h"
51 #include "skin_engine.h"
53 #include "settings_list.h"
55 #ifdef HAVE_LCD_BITMAP
65 #define WPS_ERROR_INVALID_PARAM -1
67 /* which screen are we parsing for? */
68 static enum screen_type curr_screen
;
70 /* level of current conditional.
71 -1 means we're not in a conditional. */
72 static int level
= -1;
74 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
75 or WPS_TOKEN_CONDITIONAL_START in current level */
76 static int lastcond
[WPS_MAX_COND_LEVEL
];
78 /* index of the WPS_TOKEN_CONDITIONAL in current level */
79 static int condindex
[WPS_MAX_COND_LEVEL
];
81 /* number of condtional options in current level */
82 static int numoptions
[WPS_MAX_COND_LEVEL
];
84 /* line number, debug only */
85 static int line_number
;
87 /* the current viewport */
88 static struct skin_viewport
*curr_vp
;
89 /* the current line, linked to the above viewport */
90 static struct skin_line
*curr_line
;
92 static int follow_lang_direction
= 0;
94 #ifdef HAVE_LCD_BITMAP
97 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
99 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
102 #define PROGRESSBAR_BMP MAX_IMAGES
103 #define BACKDROP_BMP (MAX_BITMAPS-1)
105 /* pointers to the bitmap filenames in the WPS source */
106 static const char *bmp_names
[MAX_BITMAPS
];
108 #endif /* HAVE_LCD_BITMAP */
110 #if defined(DEBUG) || defined(SIMULATOR)
111 /* debugging function */
112 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
113 extern void debug_skin_usage(void);
116 /* Function for parsing of details for a token. At the moment the
117 function is called, the token type has already been set. The
118 function must fill in the details and possibly add more tokens
119 to the token array. It should return the number of chars that
122 wps_bufptr points to the char following the tag (i.e. where
124 token is the pointer to the 'main' token being parsed
126 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
127 struct wps_token
*token
, struct wps_data
*wps_data
);
130 enum wps_token_type type
;
132 unsigned char refresh_type
;
133 const wps_tag_parse_func parse_func
;
135 static int skip_end_of_line(const char *wps_bufptr
);
136 /* prototypes of all special parse functions : */
137 static int parse_timeout(const char *wps_bufptr
,
138 struct wps_token
*token
, struct wps_data
*wps_data
);
139 static int parse_progressbar(const char *wps_bufptr
,
140 struct wps_token
*token
, struct wps_data
*wps_data
);
141 static int parse_dir_level(const char *wps_bufptr
,
142 struct wps_token
*token
, struct wps_data
*wps_data
);
143 static int parse_setting_and_lang(const char *wps_bufptr
,
144 struct wps_token
*token
, struct wps_data
*wps_data
);
147 int parse_languagedirection(const char *wps_bufptr
,
148 struct wps_token
*token
, struct wps_data
*wps_data
)
153 follow_lang_direction
= 2; /* 2 because it is decremented immediatly after
154 this token is parsed, after the next token it
159 #ifdef HAVE_LCD_BITMAP
160 static int parse_viewport_display(const char *wps_bufptr
,
161 struct wps_token
*token
, struct wps_data
*wps_data
);
162 static int parse_viewport(const char *wps_bufptr
,
163 struct wps_token
*token
, struct wps_data
*wps_data
);
164 static int parse_statusbar_enable(const char *wps_bufptr
,
165 struct wps_token
*token
, struct wps_data
*wps_data
);
166 static int parse_statusbar_disable(const char *wps_bufptr
,
167 struct wps_token
*token
, struct wps_data
*wps_data
);
168 static int parse_image_display(const char *wps_bufptr
,
169 struct wps_token
*token
, struct wps_data
*wps_data
);
170 static int parse_image_load(const char *wps_bufptr
,
171 struct wps_token
*token
, struct wps_data
*wps_data
);
172 #endif /*HAVE_LCD_BITMAP */
173 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
174 static int parse_image_special(const char *wps_bufptr
,
175 struct wps_token
*token
, struct wps_data
*wps_data
);
178 static int parse_albumart_load(const char *wps_bufptr
,
179 struct wps_token
*token
, struct wps_data
*wps_data
);
180 static int parse_albumart_display(const char *wps_bufptr
,
181 struct wps_token
*token
, struct wps_data
*wps_data
);
182 #endif /* HAVE_ALBUMART */
183 #ifdef HAVE_TOUCHSCREEN
184 static int parse_touchregion(const char *wps_bufptr
,
185 struct wps_token
*token
, struct wps_data
*wps_data
);
187 static int fulline_tag_not_supported(const char *wps_bufptr
,
188 struct wps_token
*token
, struct wps_data
*wps_data
)
190 (void)token
; (void)wps_data
;
191 return skip_end_of_line(wps_bufptr
);
193 #define parse_touchregion fulline_tag_not_supported
196 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
198 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
201 /* array of available tags - those with more characters have to go first
202 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
203 static const struct wps_tag all_tags
[] = {
205 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
206 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
207 { WPS_TOKEN_ALIGN_LEFT_RTL
, "aL", 0, NULL
},
208 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
209 { WPS_TOKEN_ALIGN_RIGHT_RTL
, "aR", 0, NULL
},
210 { WPS_NO_TOKEN
, "ax", 0, parse_languagedirection
},
212 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
213 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
214 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
215 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
216 #if CONFIG_CHARGING >= CHARGING_MONITOR
217 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
220 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
222 #ifdef HAVE_USB_POWER
223 { WPS_TOKEN_USB_POWERED
, "bu", WPS_REFRESH_DYNAMIC
, NULL
},
226 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
228 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
229 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
230 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
231 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
232 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
233 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
234 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
235 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
236 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
237 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
238 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
239 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
240 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
241 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
242 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
243 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
244 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
247 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
260 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
272 /* current metadata */
273 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
274 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
275 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
276 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
277 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
278 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
279 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
280 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
281 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
282 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
283 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
284 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
287 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
288 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
289 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
290 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
291 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
292 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
293 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
294 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
295 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
296 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
297 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
298 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
300 #if (CONFIG_CODEC != MAS3507D)
301 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
303 #if (CONFIG_CODEC == SWCODEC)
304 { WPS_TOKEN_SOUND_SPEED
, "Ss", WPS_REFRESH_DYNAMIC
, NULL
},
306 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
307 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
310 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
312 #ifdef HAS_REMOTE_BUTTON_HOLD
313 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
315 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
318 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
319 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
320 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
323 #ifdef HAVE_LCD_BITMAP
324 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
326 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
327 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
329 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
332 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
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
},
339 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
340 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
341 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
342 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
345 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
346 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
347 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
350 #if CONFIG_CODEC == SWCODEC
351 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
352 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
355 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
356 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
358 #ifdef HAVE_LCD_BITMAP
359 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
360 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
362 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
364 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
365 parse_image_display
},
367 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
369 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
370 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
, parse_albumart_display
},
373 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
374 parse_viewport_display
},
375 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
377 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
378 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
382 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
,
383 parse_setting_and_lang
},
384 { WPS_TOKEN_TRANSLATEDSTRING
, "Sx", WPS_REFRESH_STATIC
,
385 parse_setting_and_lang
},
386 { WPS_TOKEN_LANG_IS_RTL
, "Sr", WPS_REFRESH_STATIC
, NULL
},
388 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
389 { WPS_TOKEN_CURRENT_SCREEN
, "cs", WPS_REFRESH_DYNAMIC
, NULL
},
390 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
393 /* Recording Tokens */
394 { WPS_TOKEN_HAVE_RECORDING
, "Rp", WPS_REFRESH_STATIC
, NULL
},
395 #ifdef HAVE_RECORDING
396 { WPS_TOKEN_REC_FREQ
, "Rf", WPS_REFRESH_DYNAMIC
, NULL
},
397 { WPS_TOKEN_REC_ENCODER
, "Re", WPS_REFRESH_DYNAMIC
, NULL
},
398 { WPS_TOKEN_REC_BITRATE
, "Rb", WPS_REFRESH_DYNAMIC
, NULL
},
399 { WPS_TOKEN_REC_MONO
, "Rm", WPS_REFRESH_DYNAMIC
, NULL
},
401 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
402 /* the array MUST end with an empty string (first char is \0) */
406 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
407 * chains require the order to be kept.
409 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
415 struct skin_token_list
*t
= *list
;
422 /* traverse the image linked-list for an image */
423 #ifdef HAVE_LCD_BITMAP
424 struct gui_img
* find_image(char label
, struct wps_data
*data
)
426 struct skin_token_list
*list
= data
->images
;
429 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
430 if (img
->label
== label
)
438 /* traverse the viewport linked list for a viewport */
439 struct skin_viewport
* find_viewport(char label
, struct wps_data
*data
)
441 struct skin_token_list
*list
= data
->viewports
;
444 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->token
->value
.data
;
445 if (vp
->label
== label
)
453 /* create and init a new wpsll item.
454 * passing NULL to token will alloc a new one.
455 * You should only pass NULL for the token when the token type (table above)
456 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
458 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
461 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
463 token
= skin_buffer_alloc(sizeof(struct wps_token
));
464 if (!llitem
|| !token
)
467 llitem
->token
= token
;
469 llitem
->token
->value
.data
= token_data
;
473 /* Returns the number of chars that should be skipped to jump
474 immediately after the first eol, i.e. to the start of the next line */
475 static int skip_end_of_line(const char *wps_bufptr
)
479 while(*(wps_bufptr
+ skip
) != '\n')
484 /* Starts a new subline in the current line during parsing */
485 static bool skin_start_new_subline(struct skin_line
*line
, int curr_token
)
487 struct skin_subline
*subline
= skin_buffer_alloc(sizeof(struct skin_subline
));
491 subline
->first_token_idx
= curr_token
;
492 subline
->next
= NULL
;
494 subline
->line_type
= 0;
495 subline
->time_mult
= 0;
497 line
->curr_subline
->last_token_idx
= curr_token
-1;
498 line
->curr_subline
->next
= subline
;
499 line
->curr_subline
= subline
;
503 static bool skin_start_new_line(struct skin_viewport
*vp
, int curr_token
)
505 struct skin_line
*line
= skin_buffer_alloc(sizeof(struct skin_line
));
506 struct skin_subline
*subline
= NULL
;
510 /* init the subline */
511 subline
= &line
->sublines
;
512 subline
->first_token_idx
= curr_token
;
513 subline
->next
= NULL
;
514 subline
->line_type
= 0;
515 subline
->time_mult
= 0;
517 /* init the new line */
518 line
->curr_subline
= &line
->sublines
;
520 line
->subline_expire_time
= 0;
522 /* connect to curr_line and vp pointers.
523 * 1) close the previous lines subline
524 * 2) connect to vp pointer
525 * 3) connect to curr_line global pointer
529 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
530 curr_line
->next
= line
;
531 curr_line
->curr_subline
= NULL
;
539 #ifdef HAVE_LCD_BITMAP
541 static int parse_statusbar_enable(const char *wps_bufptr
,
542 struct wps_token
*token
,
543 struct wps_data
*wps_data
)
545 (void)token
; /* Kill warnings */
546 wps_data
->wps_sb_tag
= true;
547 wps_data
->show_sb_on_wps
= true;
548 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
549 viewport_set_defaults(&default_vp
->vp
, curr_screen
);
550 return skip_end_of_line(wps_bufptr
);
553 static int parse_statusbar_disable(const char *wps_bufptr
,
554 struct wps_token
*token
,
555 struct wps_data
*wps_data
)
557 (void)token
; /* Kill warnings */
558 wps_data
->wps_sb_tag
= true;
559 wps_data
->show_sb_on_wps
= false;
560 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
561 viewport_set_fullscreen(&default_vp
->vp
, curr_screen
);
562 return skip_end_of_line(wps_bufptr
);
565 static int get_image_id(int c
)
567 if(c
>= 'a' && c
<= 'z')
569 else if(c
>= 'A' && c
<= 'Z')
575 static char *get_image_filename(const char *start
, const char* bmpdir
,
576 char *buf
, int buf_size
)
578 const char *end
= strchr(start
, '|');
580 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
586 int bmpdirlen
= strlen(bmpdir
);
589 buf
[bmpdirlen
] = '/';
590 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
591 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
596 static int parse_image_display(const char *wps_bufptr
,
597 struct wps_token
*token
,
598 struct wps_data
*wps_data
)
600 char label
= wps_bufptr
[0];
602 struct gui_img
*img
;;
605 img
= find_image(label
, wps_data
);
608 token
->value
.i
= label
; /* so debug works */
609 return WPS_ERROR_INVALID_PARAM
;
612 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
614 if (subimage
>= img
->num_subimages
)
615 return WPS_ERROR_INVALID_PARAM
;
617 /* Store sub-image number to display in high bits */
618 token
->value
.i
= label
| (subimage
<< 8);
619 return 2; /* We have consumed 2 bytes */
621 token
->value
.i
= label
;
622 return 1; /* We have consumed 1 byte */
626 static int parse_image_load(const char *wps_bufptr
,
627 struct wps_token
*token
,
628 struct wps_data
*wps_data
)
630 const char *ptr
= wps_bufptr
;
632 const char* filename
;
638 /* format: %x|n|filename.bmp|x|y|
639 or %xl|n|filename.bmp|x|y|
640 or %xl|n|filename.bmp|x|y|num_subimages|
644 return WPS_ERROR_INVALID_PARAM
;
648 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
649 return WPS_ERROR_INVALID_PARAM
;
651 /* Check there is a terminating | */
653 return WPS_ERROR_INVALID_PARAM
;
655 /* check the image number and load state */
656 if(find_image(*id
, wps_data
))
658 /* Invalid image ID */
659 return WPS_ERROR_INVALID_PARAM
;
661 img
= skin_buffer_alloc(sizeof(struct gui_img
));
663 return WPS_ERROR_INVALID_PARAM
;
664 /* save a pointer to the filename */
665 img
->bm
.data
= (char*)filename
;
669 img
->num_subimages
= 1;
670 img
->always_display
= false;
672 /* save current viewport */
673 img
->vp
= &curr_vp
->vp
;
675 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
677 img
->always_display
= true;
681 /* Parse the (optional) number of sub-images */
683 newline
= strchr(ptr
, '\n');
684 pos
= strchr(ptr
, '|');
685 if (pos
&& pos
< newline
)
686 img
->num_subimages
= atoi(ptr
);
688 if (img
->num_subimages
<= 0)
689 return WPS_ERROR_INVALID_PARAM
;
691 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
693 return WPS_ERROR_INVALID_PARAM
;
694 add_to_ll_chain(&wps_data
->images
, item
);
696 /* Skip the rest of the line */
697 return skip_end_of_line(wps_bufptr
);
700 static int parse_viewport_display(const char *wps_bufptr
,
701 struct wps_token
*token
,
702 struct wps_data
*wps_data
)
705 char letter
= wps_bufptr
[0];
707 if (letter
< 'a' || letter
> 'z')
709 /* invalid viewport tag */
710 return WPS_ERROR_INVALID_PARAM
;
712 token
->value
.i
= letter
;
716 static int parse_viewport(const char *wps_bufptr
,
717 struct wps_token
*token
,
718 struct wps_data
*wps_data
)
720 (void)token
; /* Kill warnings */
721 const char *ptr
= wps_bufptr
;
723 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
725 /* check for the optional letter to signify its a hideable viewport */
726 /* %Vl|<label>|<rest of tags>| */
727 skin_vp
->hidden_flags
= 0;
728 skin_vp
->label
= VP_NO_LABEL
;
730 skin_vp
->lines
= NULL
;
733 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
734 - (wps_data
->num_tokens
> 0 ? 1 : 0);
738 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
739 return WPS_ERROR_INVALID_PARAM
;
744 skin_vp
->label
= VP_INFO_LABEL
;
745 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
748 else if (*ptr
== 'l')
752 char label
= *(ptr
+2);
753 if (label
>= 'a' && label
<= 'z')
755 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
756 skin_vp
->label
= label
;
759 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
764 return WPS_ERROR_INVALID_PARAM
;
767 struct viewport
*vp
= &skin_vp
->vp
;
768 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
769 if (!(ptr
= viewport_parse_viewport(vp
, curr_screen
, ptr
, '|')))
770 return WPS_ERROR_INVALID_PARAM
;
772 /* Check for trailing | */
774 return WPS_ERROR_INVALID_PARAM
;
776 if (follow_lang_direction
&& lang_is_rtl())
778 vp
->flags
|= VP_FLAG_ALIGN_RIGHT
;
779 vp
->x
= screens
[curr_screen
].lcdwidth
- vp
->width
- vp
->x
;
782 vp
->flags
&= ~VP_FLAG_ALIGN_RIGHT
; /* ignore right-to-left languages */
786 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
788 return WPS_ERROR_INVALID_PARAM
;
789 add_to_ll_chain(&wps_data
->viewports
, list
);
791 /* Skip the rest of the line */
792 return skip_end_of_line(wps_bufptr
);
795 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
796 static int parse_image_special(const char *wps_bufptr
,
797 struct wps_token
*token
,
798 struct wps_data
*wps_data
)
800 (void)wps_data
; /* kill warning */
802 const char *pos
= NULL
;
805 pos
= strchr(wps_bufptr
+ 1, '|');
806 newline
= strchr(wps_bufptr
, '\n');
809 return WPS_ERROR_INVALID_PARAM
;
811 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
813 /* format: %X|filename.bmp| */
814 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
818 /* Skip the rest of the line */
819 return skip_end_of_line(wps_bufptr
);
823 #endif /* HAVE_LCD_BITMAP */
825 static int parse_setting_and_lang(const char *wps_bufptr
,
826 struct wps_token
*token
,
827 struct wps_data
*wps_data
)
829 /* NOTE: both the string validations that happen in here will
830 * automatically PASS on checkwps because its too hard to get
831 * settings_list.c and englinsh.lang built for it.
832 * If that ever changes remove the #ifndef __PCTOOL__'s here
835 const char *ptr
= wps_bufptr
;
840 /* Find the setting's cfg_name */
842 return WPS_ERROR_INVALID_PARAM
;
844 end
= strchr(ptr
,'|');
846 return WPS_ERROR_INVALID_PARAM
;
847 strlcpy(temp
, ptr
,end
-ptr
+1);
849 if (token
->type
== WPS_TOKEN_TRANSLATEDSTRING
)
852 i
= lang_english_to_id(temp
);
854 return WPS_ERROR_INVALID_PARAM
;
859 /* Find the setting */
860 for (i
=0; i
<nb_settings
; i
++)
861 if (settings
[i
].cfg_name
&&
862 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
863 /* prevent matches on cfg_name prefixes */
864 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
867 if (i
== nb_settings
)
868 return WPS_ERROR_INVALID_PARAM
;
871 /* Store the setting number */
874 /* Skip the rest of the line */
879 static int parse_dir_level(const char *wps_bufptr
,
880 struct wps_token
*token
,
881 struct wps_data
*wps_data
)
883 char val
[] = { *wps_bufptr
, '\0' };
884 token
->value
.i
= atoi(val
);
885 (void)wps_data
; /* Kill warnings */
889 static int parse_timeout(const char *wps_bufptr
,
890 struct wps_token
*token
,
891 struct wps_data
*wps_data
)
895 bool have_point
= false;
896 bool have_tenth
= false;
898 (void)wps_data
; /* Kill the warning */
900 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
902 if (*wps_bufptr
!= '.')
905 val
+= *wps_bufptr
- '0';
921 if (have_tenth
== false)
924 if (val
== 0 && skip
== 0)
926 /* decide what to do if no value was specified */
929 case WPS_TOKEN_SUBLINE_TIMEOUT
:
931 case WPS_TOKEN_BUTTON_VOLUME
:
936 token
->value
.i
= val
;
941 static int parse_progressbar(const char *wps_bufptr
,
942 struct wps_token
*token
,
943 struct wps_data
*wps_data
)
945 /* %pb or %pb|filename|x|y|width|height|
946 using - for any of the params uses "sane" values */
947 #ifdef HAVE_LCD_BITMAP
955 const char *filename
;
956 int x
, y
, height
, width
;
958 const char *ptr
= wps_bufptr
;
959 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
960 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
963 return WPS_ERROR_INVALID_PARAM
;
965 struct viewport
*vp
= &curr_vp
->vp
;
967 int font_height
= font_get(vp
->font
)->height
;
971 /* we need to know what line number (viewport relative) this pb is,
972 * so count them... */
974 struct skin_line
*line
= curr_vp
->lines
;
980 pb
->have_bitmap_pb
= false;
981 pb
->bm
.data
= NULL
; /* no bitmap specified */
982 pb
->follow_lang_direction
= follow_lang_direction
> 0;
984 if (*wps_bufptr
!= '|') /* regular old style */
987 pb
->width
= vp
->width
;
988 pb
->height
= SYSFONT_HEIGHT
-2;
989 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
992 add_to_ll_chain(&wps_data
->progressbars
, item
);
995 ptr
= wps_bufptr
+ 1;
997 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
998 &x
, &y
, &width
, &height
)))
999 return WPS_ERROR_INVALID_PARAM
;
1001 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
1002 pb
->bm
.data
= (char*)filename
;
1004 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
1009 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
1011 /* A zero width causes a divide-by-zero error later, so reject it */
1013 return WPS_ERROR_INVALID_PARAM
;
1018 pb
->width
= vp
->width
- pb
->x
;
1020 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
1022 /* A zero height makes no sense - reject it */
1024 return WPS_ERROR_INVALID_PARAM
;
1026 pb
->height
= height
;
1029 pb
->height
= font_height
;
1031 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
1034 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
1037 add_to_ll_chain(&wps_data
->progressbars
, item
);
1039 /* Skip the rest of the line */
1040 return skip_end_of_line(wps_bufptr
)-1;
1044 if (*(wps_bufptr
-1) == 'f')
1045 wps_data
->full_line_progressbar
= true;
1047 wps_data
->full_line_progressbar
= false;
1054 #ifdef HAVE_ALBUMART
1055 static int parse_int(const char *newline
, const char **_pos
, int *num
)
1057 *_pos
= parse_list("d", NULL
, '|', *_pos
, num
);
1059 return (!*_pos
|| *_pos
> newline
|| **_pos
!= '|');
1062 static int parse_albumart_load(const char *wps_bufptr
,
1063 struct wps_token
*token
,
1064 struct wps_data
*wps_data
)
1066 const char *_pos
, *newline
;
1068 struct dim dimensions
;
1070 bool swap_for_rtl
= lang_is_rtl() && follow_lang_direction
;
1071 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
1072 (void)token
; /* silence warning */
1074 return skip_end_of_line(wps_bufptr
);
1076 /* reset albumart info in wps */
1079 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1080 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
1081 aa
->vp
= &curr_vp
->vp
;
1083 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1085 newline
= strchr(wps_bufptr
, '\n');
1090 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1094 /* initial validation and parsing of x component */
1095 if (parse_int(newline
, &_pos
, &aa
->x
))
1096 return WPS_ERROR_INVALID_PARAM
;
1100 /* initial validation and parsing of y component */
1101 if (parse_int(newline
, &_pos
, &aa
->y
))
1102 return WPS_ERROR_INVALID_PARAM
;
1104 /* parsing width field */
1108 /* apply each modifier in turn */
1116 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1118 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1122 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1128 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1130 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1138 /* simply ignored */
1145 /* extract max width data */
1148 if (parse_int(newline
, &_pos
, &aa
->width
))
1149 return WPS_ERROR_INVALID_PARAM
;
1152 /* parsing height field */
1156 /* apply each modifier in turn */
1163 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1167 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1172 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1180 /* simply ignored */
1187 /* extract max height data */
1190 if (parse_int(newline
, &_pos
, &aa
->height
))
1191 return WPS_ERROR_INVALID_PARAM
;
1194 /* if we got here, we parsed everything ok .. ! */
1197 else if (aa
->width
> LCD_WIDTH
)
1198 aa
->width
= LCD_WIDTH
;
1202 else if (aa
->height
> LCD_HEIGHT
)
1203 aa
->height
= LCD_HEIGHT
;
1206 aa
->x
= LCD_WIDTH
- (aa
->x
+ aa
->width
);
1208 aa
->state
= WPS_ALBUMART_LOAD
;
1210 wps_data
->albumart
= aa
;
1212 dimensions
.width
= aa
->width
;
1213 dimensions
.height
= aa
->height
;
1215 albumart_slot
= playback_claim_aa_slot(&dimensions
);
1217 if (0 <= albumart_slot
)
1218 wps_data
->playback_aa_slot
= albumart_slot
;
1220 /* Skip the rest of the line */
1221 return skip_end_of_line(wps_bufptr
);
1224 static int parse_albumart_display(const char *wps_bufptr
,
1225 struct wps_token
*token
,
1226 struct wps_data
*wps_data
)
1229 struct wps_token
*prev
= token
-1;
1230 if ((wps_data
->num_tokens
>= 1) && (prev
->type
== WPS_TOKEN_CONDITIONAL
))
1232 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1234 else if (wps_data
->albumart
)
1236 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1239 /* the old code did this so keep it here for now...
1240 * this is to allow the posibility to showing the next tracks AA! */
1241 if (wps_bufptr
+1 == 'n')
1246 #endif /* HAVE_ALBUMART */
1248 #ifdef HAVE_TOUCHSCREEN
1250 struct touchaction
{const char* s
; int action
;};
1251 static const struct touchaction touchactions
[] = {
1252 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1253 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1254 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1255 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1256 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1257 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1258 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1259 {"voldown", ACTION_WPS_VOLDOWN
}, {"volup", ACTION_WPS_VOLUP
},
1261 static int parse_touchregion(const char *wps_bufptr
,
1262 struct wps_token
*token
, struct wps_data
*wps_data
)
1266 struct touchregion
*region
= NULL
;
1267 const char *ptr
= wps_bufptr
;
1269 const char pb_string
[] = "progressbar";
1270 const char vol_string
[] = "volume";
1273 /* format: %T|x|y|width|height|action|
1274 * if action starts with & the area must be held to happen
1276 * play - play/pause playback
1277 * stop - stop playback, exit the wps
1280 * ffwd - seek forward
1281 * rwd - seek backwards
1282 * menu - go back to the main menu
1283 * browse - go back to the file/db browser
1284 * shuffle - toggle shuffle mode
1285 * repmode - cycle the repeat mode
1286 * quickscreen - go into the quickscreen
1287 * contextmenu - open the context menu
1288 * playlist - go into the playlist
1289 * pitch - go into the pitchscreen
1290 * volup - increase volume by one step
1291 * voldown - decrease volume by one step
1296 return WPS_ERROR_INVALID_PARAM
;
1299 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1300 return WPS_ERROR_INVALID_PARAM
;
1302 /* Check there is a terminating | */
1304 return WPS_ERROR_INVALID_PARAM
;
1306 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1308 return WPS_ERROR_INVALID_PARAM
;
1310 /* should probably do some bounds checking here with the viewport... but later */
1311 region
->action
= ACTION_NONE
;
1316 region
->wvp
= curr_vp
;
1318 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1319 && *(action
+ sizeof(pb_string
)-1) == '|')
1320 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1321 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1322 && *(action
+ sizeof(vol_string
)-1) == '|')
1323 region
->type
= WPS_TOUCHREGION_VOLUME
;
1326 region
->type
= WPS_TOUCHREGION_ACTION
;
1331 region
->repeat
= true;
1334 region
->repeat
= false;
1337 imax
= ARRAYLEN(touchactions
);
1338 while ((region
->action
== ACTION_NONE
) &&
1341 /* try to match with one of our touchregion screens */
1342 int len
= strlen(touchactions
[i
].s
);
1343 if (!strncmp(touchactions
[i
].s
, action
, len
)
1344 && *(action
+len
) == '|')
1345 region
->action
= touchactions
[i
].action
;
1348 if (region
->action
== ACTION_NONE
)
1349 return WPS_ERROR_INVALID_PARAM
;
1351 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1353 return WPS_ERROR_INVALID_PARAM
;
1354 add_to_ll_chain(&wps_data
->touchregions
, item
);
1355 return skip_end_of_line(wps_bufptr
);
1359 /* Parse a generic token from the given string. Return the length read */
1360 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1362 int skip
= 0, taglen
= 0, ret
;
1363 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1364 const struct wps_tag
*tag
;
1365 memset(token
, 0, sizeof(*token
));
1376 /* escaped characters */
1377 token
->type
= WPS_TOKEN_CHARACTER
;
1378 token
->value
.c
= *wps_bufptr
;
1380 wps_data
->num_tokens
++;
1384 /* conditional tag */
1385 token
->type
= WPS_TOKEN_CONDITIONAL
;
1387 condindex
[level
] = wps_data
->num_tokens
;
1388 numoptions
[level
] = 1;
1389 wps_data
->num_tokens
++;
1390 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1391 if (ret
< 0) return ret
;
1396 /* find what tag we have */
1397 for (tag
= all_tags
;
1398 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1401 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1402 token
->type
= tag
->type
;
1403 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1405 /* if the tag has a special parsing function, we call it */
1406 if (tag
->parse_func
)
1408 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1409 if (ret
< 0) return ret
;
1413 /* Some tags we don't want to save as tokens */
1414 if (tag
->type
== WPS_NO_TOKEN
)
1417 /* tags that start with 'F', 'I' or 'D' are for the next file */
1418 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1419 *(tag
->name
) == 'D')
1422 wps_data
->num_tokens
++;
1432 * Returns the number of bytes to skip the buf pointer to access the false
1433 * branch in a _binary_ conditional
1436 * - before the '|' if we have a false branch, (%?<true|false> -> %?<|false>)
1437 * - or before the closing '>' if there's no false branch (%?<true> -> %?<>)
1439 * depending on the features of a target it's not called from check_feature_tag,
1440 * hence the __attribute__ or it issues compiler warnings
1444 static int find_false_branch(const char *wps_bufptr
) __attribute__((unused
));
1445 static int find_false_branch(const char *wps_bufptr
)
1447 const char *buf
= wps_bufptr
;
1448 /* wps_bufptr is after the opening '<', hence level = 1*/
1455 { /* filter out the characters we check later if they're printed
1458 if (ch
== '<' || ch
== '>' || ch
== '|')
1460 /* else: some tags/printed literals we skip over */
1462 else if (ch
== '<') /* nested conditional */
1465 { /* closed our or a nested conditional,
1466 * do NOT skip over the '>' so that wps_parse() sees it for closing
1467 * if it is the closing one for our conditional */
1470 else if (ch
== '|' && level
== 1)
1471 { /* we found our separator, point before and get out */
1474 /* if level is 0, we don't have a false branch */
1475 } while (level
> 0 && *(++buf
));
1477 return buf
- wps_bufptr
;
1481 * returns the number of bytes to get the appropriate branch of a binary
1485 * - if a feature is available, it returns 0 to not skip anything
1486 * - if the feature is not available, skip to the false branch and don't
1487 * parse the true branch at all
1490 static int check_feature_tag(const char *wps_bufptr
, const int type
)
1495 case WPS_TOKEN_RTC_PRESENT
:
1499 return find_false_branch(wps_bufptr
);
1501 case WPS_TOKEN_HAVE_RECORDING
:
1502 #ifdef HAVE_RECORDING
1505 return find_false_branch(wps_bufptr
);
1507 default: /* not a tag we care about, just don't skip */
1514 data is the pointer to the structure where the parsed WPS should be stored.
1516 wps_bufptr points to the string containing the WPS tags */
1517 #define TOKEN_BLOCK_SIZE 128
1518 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1520 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1522 enum wps_parse_error fail
= PARSE_OK
;
1524 int max_tokens
= TOKEN_BLOCK_SIZE
;
1525 size_t buf_free
= 0;
1529 /* allocate enough RAM for a reasonable skin, grow as needed.
1530 * Free any used RAM before loading the images to be 100% RAM efficient */
1531 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1532 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1534 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1535 data
->num_tokens
= 0;
1537 while (*wps_bufptr
&& !fail
)
1539 if (follow_lang_direction
)
1540 follow_lang_direction
--;
1541 /* first make sure there is enough room for tokens */
1542 if (max_tokens
<= data
->num_tokens
+ 5)
1544 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1545 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1546 /* do some smarts here to grow the array a bit */
1547 if (skin_buffer_freespace() < needed
)
1549 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1552 skin_buffer_increment(needed
, false);
1553 max_tokens
+= extra_tokens
;
1556 switch(*wps_bufptr
++)
1561 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1563 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1566 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1568 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1574 /* Alternating sublines separator */
1576 if (level
>= 0) /* there are unclosed conditionals */
1578 fail
= PARSE_FAIL_UNCLOSED_COND
;
1582 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1583 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1587 /* Conditional list start */
1589 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1591 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1594 wps_bufptr
+= check_feature_tag(wps_bufptr
,
1595 data
->tokens
[data
->num_tokens
-1].type
);
1596 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1597 lastcond
[level
] = data
->num_tokens
++;
1600 /* Conditional list end */
1602 if (level
< 0) /* not in a conditional, invalid char */
1604 fail
= PARSE_FAIL_INVALID_CHAR
;
1608 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1609 if (lastcond
[level
])
1610 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1613 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1617 lastcond
[level
] = 0;
1619 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1623 /* Conditional list option */
1625 if (level
< 0) /* not in a conditional, invalid char */
1627 fail
= PARSE_FAIL_INVALID_CHAR
;
1631 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1632 if (lastcond
[level
])
1633 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1636 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1640 lastcond
[level
] = data
->num_tokens
;
1641 numoptions
[level
]++;
1647 if (level
>= 0) /* there are unclosed conditionals */
1649 fail
= PARSE_FAIL_UNCLOSED_COND
;
1653 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1656 /* End of this line */
1658 if (level
>= 0) /* there are unclosed conditionals */
1660 fail
= PARSE_FAIL_UNCLOSED_COND
;
1663 /* add a new token for the \n so empty lines are correct */
1664 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1665 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1666 data
->tokens
[data
->num_tokens
].next
= false;
1669 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1671 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1681 unsigned int len
= 1;
1682 const char *string_start
= wps_bufptr
- 1;
1684 /* find the length of the string */
1685 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1686 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1687 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1688 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1694 /* look if we already have that string */
1697 struct skin_token_list
*list
= data
->strings
;
1700 str
= (char*)list
->token
->value
.data
;
1701 found
= (strlen(str
) == len
&&
1702 strncmp(string_start
, str
, len
) == 0);
1704 break; /* break here because the list item is
1705 used if its found */
1708 /* If a matching string is found, found is true and i is
1709 the index of the string. If not, found is false */
1714 str
= (char*)skin_buffer_alloc(len
+1);
1717 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1720 strlcpy(str
, string_start
, len
+1);
1721 struct skin_token_list
*item
=
1722 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1725 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1728 add_to_ll_chain(&data
->strings
, item
);
1732 /* another occurrence of an existing string */
1733 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1735 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1742 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1743 fail
= PARSE_FAIL_UNCLOSED_COND
;
1745 if (*wps_bufptr
&& !fail
)
1746 /* one of the limits of the while loop was exceeded */
1747 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1750 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
1751 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
1752 /* freeup unused tokens */
1753 skin_buffer_free_from_front(sizeof(struct wps_token
)
1754 * (max_tokens
- data
->num_tokens
));
1756 #if defined(DEBUG) || defined(SIMULATOR)
1758 print_debug_info(data
, fail
, line_number
);
1768 * initial setup of wps_data; does reset everything
1769 * except fields which need to survive, i.e.
1772 static void skin_data_reset(struct wps_data
*wps_data
)
1774 #ifdef HAVE_LCD_BITMAP
1775 wps_data
->images
= NULL
;
1776 wps_data
->progressbars
= NULL
;
1778 #ifdef HAVE_TOUCHSCREEN
1779 wps_data
->touchregions
= NULL
;
1781 wps_data
->viewports
= NULL
;
1782 wps_data
->strings
= NULL
;
1783 #ifdef HAVE_ALBUMART
1784 wps_data
->albumart
= NULL
;
1785 if (wps_data
->playback_aa_slot
>= 0)
1787 playback_release_aa_slot(wps_data
->playback_aa_slot
);
1788 wps_data
->playback_aa_slot
= -1;
1791 wps_data
->tokens
= NULL
;
1792 wps_data
->num_tokens
= 0;
1794 #ifdef HAVE_LCD_BITMAP
1795 wps_data
->peak_meter_enabled
= false;
1796 wps_data
->wps_sb_tag
= false;
1797 wps_data
->show_sb_on_wps
= false;
1798 #else /* HAVE_LCD_CHARCELLS */
1801 for (i
= 0; i
< 8; i
++)
1803 wps_data
->wps_progress_pat
[i
] = 0;
1805 wps_data
->full_line_progressbar
= false;
1807 wps_data
->wps_loaded
= false;
1810 #ifdef HAVE_LCD_BITMAP
1811 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1813 (void)wps_data
; /* only needed for remote targets */
1814 bool loaded
= false;
1815 char img_path
[MAX_PATH
];
1816 get_image_filename(bitmap
->data
, bmpdir
,
1817 img_path
, sizeof(img_path
));
1819 /* load the image */
1821 #ifdef HAVE_REMOTE_LCD
1822 if (curr_screen
== SCREEN_REMOTE
)
1823 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1826 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1829 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
1830 bitmap
->data
= imgbuf
;
1831 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
1835 skin_buffer_increment(ret
, true);
1840 /* Abort if we can't load an image */
1846 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1848 struct skin_token_list
*list
;
1849 /* do the progressbars */
1850 list
= wps_data
->progressbars
;
1853 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
1856 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
1860 /* regular images */
1861 list
= wps_data
->images
;
1864 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1867 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1869 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
1874 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1875 if (bmp_names
[BACKDROP_BMP
])
1877 char img_path
[MAX_PATH
];
1878 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1879 img_path
, sizeof(img_path
));
1880 screens
[curr_screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1882 #endif /* has backdrop support */
1884 /* If we got here, everything was OK */
1888 #endif /* HAVE_LCD_BITMAP */
1890 /* to setup up the wps-data from a format-buffer (isfile = false)
1891 from a (wps-)file (isfile = true)*/
1892 bool skin_data_load(enum screen_type screen
, struct wps_data
*wps_data
,
1893 const char *buf
, bool isfile
)
1896 if (!wps_data
|| !buf
)
1898 #ifdef HAVE_ALBUMART
1900 struct mp3entry
*curtrack
;
1902 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
1903 if (wps_data
->albumart
)
1905 old_aa
.state
= wps_data
->albumart
->state
;
1906 old_aa
.height
= wps_data
->albumart
->height
;
1907 old_aa
.width
= wps_data
->albumart
->width
;
1911 skin_data_reset(wps_data
);
1912 curr_screen
= screen
;
1914 /* alloc default viewport, will be fixed up later */
1915 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
1918 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
1921 add_to_ll_chain(&wps_data
->viewports
, list
);
1924 /* Initialise the first (default) viewport */
1925 curr_vp
->label
= VP_DEFAULT_LABEL
;
1927 curr_vp
->hidden_flags
= 0;
1928 curr_vp
->lines
= NULL
;
1930 viewport_set_defaults(&curr_vp
->vp
, screen
);
1933 if (!skin_start_new_line(curr_vp
, 0))
1938 return wps_parse(wps_data
, buf
, false);
1942 int fd
= open_utf8(buf
, O_RDONLY
);
1947 /* get buffer space from the plugin buffer */
1948 size_t buffersize
= 0;
1949 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1954 /* copy the file's content to the buffer for parsing,
1955 ensuring that every line ends with a newline char. */
1956 unsigned int start
= 0;
1957 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1959 start
+= strlen(wps_buffer
+ start
);
1960 if (start
< buffersize
- 1)
1962 wps_buffer
[start
++] = '\n';
1963 wps_buffer
[start
] = 0;
1972 #ifdef HAVE_LCD_BITMAP
1973 /* Set all filename pointers to NULL */
1974 memset(bmp_names
, 0, sizeof(bmp_names
));
1977 /* parse the WPS source */
1978 if (!wps_parse(wps_data
, wps_buffer
, true)) {
1979 skin_data_reset(wps_data
);
1983 wps_data
->wps_loaded
= true;
1985 #ifdef HAVE_LCD_BITMAP
1986 /* get the bitmap dir */
1987 char bmpdir
[MAX_PATH
];
1988 char *dot
= strrchr(buf
, '.');
1990 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1992 /* load the bitmaps that were found by the parsing */
1993 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
1994 skin_data_reset(wps_data
);
1998 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1999 status
= audio_status();
2000 if (status
& AUDIO_STATUS_PLAY
)
2002 struct skin_albumart
*aa
= wps_data
->albumart
;
2003 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
2005 (((old_aa
.height
!= aa
->height
) ||
2006 (old_aa
.width
!= aa
->width
))))))
2008 curtrack
= audio_current_track();
2009 offset
= curtrack
->offset
;
2011 if (!(status
& AUDIO_STATUS_PAUSE
))
2016 #if defined(DEBUG) || defined(SIMULATOR)