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 ****************************************************************************/
49 #include "wps_internals.h"
50 #include "skin_engine.h"
52 #include "settings_list.h"
54 #ifdef HAVE_LCD_BITMAP
60 #define WPS_ERROR_INVALID_PARAM -1
62 /* level of current conditional.
63 -1 means we're not in a conditional. */
64 static int level
= -1;
66 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
67 or WPS_TOKEN_CONDITIONAL_START in current level */
68 static int lastcond
[WPS_MAX_COND_LEVEL
];
70 /* index of the WPS_TOKEN_CONDITIONAL in current level */
71 static int condindex
[WPS_MAX_COND_LEVEL
];
73 /* number of condtional options in current level */
74 static int numoptions
[WPS_MAX_COND_LEVEL
];
76 /* line number, debug only */
77 static int line_number
;
79 /* the current viewport */
80 static struct skin_viewport
*curr_vp
;
81 /* the current line, linked to the above viewport */
82 static struct skin_line
*curr_line
;
84 #ifdef HAVE_LCD_BITMAP
87 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
89 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
92 #define PROGRESSBAR_BMP MAX_IMAGES
93 #define BACKDROP_BMP (MAX_BITMAPS-1)
95 /* pointers to the bitmap filenames in the WPS source */
96 static const char *bmp_names
[MAX_BITMAPS
];
98 #endif /* HAVE_LCD_BITMAP */
100 #if defined(DEBUG) || defined(SIMULATOR)
101 /* debugging function */
102 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
103 extern void debug_skin_usage(void);
106 static void wps_reset(struct wps_data
*data
);
108 /* Function for parsing of details for a token. At the moment the
109 function is called, the token type has already been set. The
110 function must fill in the details and possibly add more tokens
111 to the token array. It should return the number of chars that
114 wps_bufptr points to the char following the tag (i.e. where
116 token is the pointer to the 'main' token being parsed
118 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
119 struct wps_token
*token
, struct wps_data
*wps_data
);
122 enum wps_token_type type
;
124 unsigned char refresh_type
;
125 const wps_tag_parse_func parse_func
;
127 static int skip_end_of_line(const char *wps_bufptr
);
128 /* prototypes of all special parse functions : */
129 static int parse_timeout(const char *wps_bufptr
,
130 struct wps_token
*token
, struct wps_data
*wps_data
);
131 static int parse_progressbar(const char *wps_bufptr
,
132 struct wps_token
*token
, struct wps_data
*wps_data
);
133 static int parse_dir_level(const char *wps_bufptr
,
134 struct wps_token
*token
, struct wps_data
*wps_data
);
135 static int parse_setting(const char *wps_bufptr
,
136 struct wps_token
*token
, struct wps_data
*wps_data
);
138 #ifdef HAVE_LCD_BITMAP
139 static int parse_viewport_display(const char *wps_bufptr
,
140 struct wps_token
*token
, struct wps_data
*wps_data
);
141 static int parse_viewport(const char *wps_bufptr
,
142 struct wps_token
*token
, struct wps_data
*wps_data
);
143 static int parse_statusbar_enable(const char *wps_bufptr
,
144 struct wps_token
*token
, struct wps_data
*wps_data
);
145 static int parse_statusbar_disable(const char *wps_bufptr
,
146 struct wps_token
*token
, struct wps_data
*wps_data
);
147 static int parse_image_display(const char *wps_bufptr
,
148 struct wps_token
*token
, struct wps_data
*wps_data
);
149 static int parse_image_load(const char *wps_bufptr
,
150 struct wps_token
*token
, struct wps_data
*wps_data
);
151 #endif /*HAVE_LCD_BITMAP */
152 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
153 static int parse_image_special(const char *wps_bufptr
,
154 struct wps_token
*token
, struct wps_data
*wps_data
);
157 static int parse_albumart_load(const char *wps_bufptr
,
158 struct wps_token
*token
, struct wps_data
*wps_data
);
159 static int parse_albumart_display(const char *wps_bufptr
,
160 struct wps_token
*token
, struct wps_data
*wps_data
);
161 #endif /* HAVE_ALBUMART */
162 #ifdef HAVE_TOUCHSCREEN
163 static int parse_touchregion(const char *wps_bufptr
,
164 struct wps_token
*token
, struct wps_data
*wps_data
);
166 static int fulline_tag_not_supported(const char *wps_bufptr
,
167 struct wps_token
*token
, struct wps_data
*wps_data
)
169 (void)token
; (void)wps_data
;
170 return skip_end_of_line(wps_bufptr
);
172 #define parse_touchregion fulline_tag_not_supported
175 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
177 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
180 /* array of available tags - those with more characters have to go first
181 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
182 static const struct wps_tag all_tags
[] = {
184 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
185 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
186 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
188 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
189 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
190 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
191 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
192 #if CONFIG_CHARGING >= CHARGING_MONITOR
193 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
196 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
199 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
200 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
209 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
210 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
211 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
212 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
213 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
214 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
215 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
216 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
217 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
220 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
224 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
225 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
226 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
228 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
229 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
233 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
234 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
236 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
237 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
238 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
245 /* current metadata */
246 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
257 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
270 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
271 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
273 #if (CONFIG_CODEC != MAS3507D)
274 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
276 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
277 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
280 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
282 #ifdef HAS_REMOTE_BUTTON_HOLD
283 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
285 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
288 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
289 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
290 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
293 #ifdef HAVE_LCD_BITMAP
294 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
296 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
297 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
299 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
302 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
304 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
305 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
306 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
307 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
309 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
310 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
311 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
312 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
315 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
316 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
317 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
320 #if CONFIG_CODEC == SWCODEC
321 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
322 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
325 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
326 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
328 #ifdef HAVE_LCD_BITMAP
329 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
330 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
332 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
334 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
335 parse_image_display
},
337 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
339 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
340 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
, parse_albumart_display
},
343 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
344 parse_viewport_display
},
345 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
347 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
348 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
352 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
, parse_setting
},
354 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
355 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
357 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
358 /* the array MUST end with an empty string (first char is \0) */
362 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
363 * chains require the order to be kept.
365 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
371 struct skin_token_list
*t
= *list
;
377 /* create and init a new wpsll item.
378 * passing NULL to token will alloc a new one.
379 * You should only pass NULL for the token when the token type (table above)
380 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
382 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
385 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
387 token
= skin_buffer_alloc(sizeof(struct wps_token
));
388 if (!llitem
|| !token
)
391 llitem
->token
= token
;
393 llitem
->token
->value
.data
= token_data
;
397 /* Returns the number of chars that should be skipped to jump
398 immediately after the first eol, i.e. to the start of the next line */
399 static int skip_end_of_line(const char *wps_bufptr
)
403 while(*(wps_bufptr
+ skip
) != '\n')
408 /* Starts a new subline in the current line during parsing */
409 static bool skin_start_new_subline(struct skin_line
*line
, int curr_token
)
411 struct skin_subline
*subline
= skin_buffer_alloc(sizeof(struct skin_subline
));
415 subline
->first_token_idx
= curr_token
;
416 subline
->next
= NULL
;
418 subline
->line_type
= 0;
419 subline
->time_mult
= 0;
421 line
->curr_subline
->last_token_idx
= curr_token
-1;
422 line
->curr_subline
->next
= subline
;
423 line
->curr_subline
= subline
;
427 static bool skin_start_new_line(struct skin_viewport
*vp
, int curr_token
)
429 struct skin_line
*line
= skin_buffer_alloc(sizeof(struct skin_line
));
430 struct skin_subline
*subline
= NULL
;
434 /* init the subline */
435 subline
= &line
->sublines
;
436 subline
->first_token_idx
= curr_token
;
437 subline
->next
= NULL
;
438 subline
->line_type
= 0;
439 subline
->time_mult
= 0;
441 /* init the new line */
442 line
->curr_subline
= &line
->sublines
;
444 line
->subline_expire_time
= 0;
446 /* connect to curr_line and vp pointers.
447 * 1) close the previous lines subline
448 * 2) connect to vp pointer
449 * 3) connect to curr_line global pointer
453 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
454 curr_line
->next
= line
;
455 curr_line
->curr_subline
= NULL
;
464 #ifdef HAVE_LCD_BITMAP
466 static int parse_statusbar_enable(const char *wps_bufptr
,
467 struct wps_token
*token
,
468 struct wps_data
*wps_data
)
470 (void)token
; /* Kill warnings */
471 wps_data
->wps_sb_tag
= true;
472 wps_data
->show_sb_on_wps
= true;
473 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
474 if (default_vp
->vp
.y
== 0)
476 default_vp
->vp
.y
= STATUSBAR_HEIGHT
;
477 default_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
479 return skip_end_of_line(wps_bufptr
);
482 static int parse_statusbar_disable(const char *wps_bufptr
,
483 struct wps_token
*token
,
484 struct wps_data
*wps_data
)
486 (void)token
; /* Kill warnings */
487 wps_data
->wps_sb_tag
= true;
488 wps_data
->show_sb_on_wps
= false;
489 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
490 if (default_vp
->vp
.y
== STATUSBAR_HEIGHT
)
492 default_vp
->vp
.y
= 0;
493 default_vp
->vp
.height
+= STATUSBAR_HEIGHT
;
495 return skip_end_of_line(wps_bufptr
);
498 static int get_image_id(int c
)
500 if(c
>= 'a' && c
<= 'z')
502 else if(c
>= 'A' && c
<= 'Z')
508 static char *get_image_filename(const char *start
, const char* bmpdir
,
509 char *buf
, int buf_size
)
511 const char *end
= strchr(start
, '|');
513 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
519 int bmpdirlen
= strlen(bmpdir
);
522 buf
[bmpdirlen
] = '/';
523 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
524 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
529 static int parse_image_display(const char *wps_bufptr
,
530 struct wps_token
*token
,
531 struct wps_data
*wps_data
)
533 char label
= wps_bufptr
[0];
535 struct gui_img
*img
;;
538 img
= find_image(label
, wps_data
);
541 token
->value
.i
= label
; /* so debug works */
542 return WPS_ERROR_INVALID_PARAM
;
545 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
547 if (subimage
>= img
->num_subimages
)
548 return WPS_ERROR_INVALID_PARAM
;
550 /* Store sub-image number to display in high bits */
551 token
->value
.i
= label
| (subimage
<< 8);
552 return 2; /* We have consumed 2 bytes */
554 token
->value
.i
= label
;
555 return 1; /* We have consumed 1 byte */
559 static int parse_image_load(const char *wps_bufptr
,
560 struct wps_token
*token
,
561 struct wps_data
*wps_data
)
563 const char *ptr
= wps_bufptr
;
565 const char* filename
;
571 /* format: %x|n|filename.bmp|x|y|
572 or %xl|n|filename.bmp|x|y|
573 or %xl|n|filename.bmp|x|y|num_subimages|
577 return WPS_ERROR_INVALID_PARAM
;
581 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
582 return WPS_ERROR_INVALID_PARAM
;
584 /* Check there is a terminating | */
586 return WPS_ERROR_INVALID_PARAM
;
588 /* check the image number and load state */
589 if(find_image(*id
, wps_data
))
591 /* Invalid image ID */
592 return WPS_ERROR_INVALID_PARAM
;
594 img
= skin_buffer_alloc(sizeof(struct gui_img
));
596 return WPS_ERROR_INVALID_PARAM
;
597 /* save a pointer to the filename */
598 img
->bm
.data
= (char*)filename
;
602 img
->num_subimages
= 1;
603 img
->always_display
= false;
605 /* save current viewport */
606 img
->vp
= &curr_vp
->vp
;
608 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
610 img
->always_display
= true;
614 /* Parse the (optional) number of sub-images */
616 newline
= strchr(ptr
, '\n');
617 pos
= strchr(ptr
, '|');
618 if (pos
&& pos
< newline
)
619 img
->num_subimages
= atoi(ptr
);
621 if (img
->num_subimages
<= 0)
622 return WPS_ERROR_INVALID_PARAM
;
624 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
626 return WPS_ERROR_INVALID_PARAM
;
627 add_to_ll_chain(&wps_data
->images
, item
);
629 /* Skip the rest of the line */
630 return skip_end_of_line(wps_bufptr
);
633 static int parse_viewport_display(const char *wps_bufptr
,
634 struct wps_token
*token
,
635 struct wps_data
*wps_data
)
638 char letter
= wps_bufptr
[0];
640 if (letter
< 'a' || letter
> 'z')
642 /* invalid viewport tag */
643 return WPS_ERROR_INVALID_PARAM
;
645 token
->value
.i
= letter
;
649 static int parse_viewport(const char *wps_bufptr
,
650 struct wps_token
*token
,
651 struct wps_data
*wps_data
)
653 (void)token
; /* Kill warnings */
654 const char *ptr
= wps_bufptr
;
657 #ifdef HAVE_REMOTE_LCD
658 wps_data
->remote_wps
? SCREEN_REMOTE
:
662 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
664 /* check for the optional letter to signify its a hideable viewport */
665 /* %Vl|<label>|<rest of tags>| */
666 skin_vp
->hidden_flags
= 0;
667 skin_vp
->label
= VP_NO_LABEL
;
669 skin_vp
->lines
= NULL
;
672 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
673 - (wps_data
->num_tokens
> 0 ? 1 : 0);
677 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
678 return WPS_ERROR_INVALID_PARAM
;
684 char label
= *(ptr
+2);
685 if (label
>= 'a' && label
<= 'z')
687 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
688 skin_vp
->label
= label
;
691 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
696 return WPS_ERROR_INVALID_PARAM
;
699 struct viewport
*vp
= &skin_vp
->vp
;
700 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
702 if (!(ptr
= viewport_parse_viewport(vp
, screen
, ptr
, '|')))
703 return WPS_ERROR_INVALID_PARAM
;
705 /* Check for trailing | */
707 return WPS_ERROR_INVALID_PARAM
;
710 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
712 return WPS_ERROR_INVALID_PARAM
;
713 add_to_ll_chain(&wps_data
->viewports
, list
);
715 /* Skip the rest of the line */
716 return skip_end_of_line(wps_bufptr
);
719 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
720 static int parse_image_special(const char *wps_bufptr
,
721 struct wps_token
*token
,
722 struct wps_data
*wps_data
)
724 (void)wps_data
; /* kill warning */
726 const char *pos
= NULL
;
729 pos
= strchr(wps_bufptr
+ 1, '|');
730 newline
= strchr(wps_bufptr
, '\n');
733 return WPS_ERROR_INVALID_PARAM
;
735 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
737 /* format: %X|filename.bmp| */
738 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
742 /* Skip the rest of the line */
743 return skip_end_of_line(wps_bufptr
);
747 #endif /* HAVE_LCD_BITMAP */
749 static int parse_setting(const char *wps_bufptr
,
750 struct wps_token
*token
,
751 struct wps_data
*wps_data
)
754 const char *ptr
= wps_bufptr
;
758 /* Find the setting's cfg_name */
760 return WPS_ERROR_INVALID_PARAM
;
762 end
= strchr(ptr
,'|');
764 return WPS_ERROR_INVALID_PARAM
;
766 /* Find the setting */
767 for (i
=0; i
<nb_settings
; i
++)
768 if (settings
[i
].cfg_name
&&
769 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
770 /* prevent matches on cfg_name prefixes */
771 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
773 if (i
== nb_settings
)
774 return WPS_ERROR_INVALID_PARAM
;
776 /* Store the setting number */
779 /* Skip the rest of the line */
784 static int parse_dir_level(const char *wps_bufptr
,
785 struct wps_token
*token
,
786 struct wps_data
*wps_data
)
788 char val
[] = { *wps_bufptr
, '\0' };
789 token
->value
.i
= atoi(val
);
790 (void)wps_data
; /* Kill warnings */
794 static int parse_timeout(const char *wps_bufptr
,
795 struct wps_token
*token
,
796 struct wps_data
*wps_data
)
800 bool have_point
= false;
801 bool have_tenth
= false;
803 (void)wps_data
; /* Kill the warning */
805 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
807 if (*wps_bufptr
!= '.')
810 val
+= *wps_bufptr
- '0';
826 if (have_tenth
== false)
829 if (val
== 0 && skip
== 0)
831 /* decide what to do if no value was specified */
834 case WPS_TOKEN_SUBLINE_TIMEOUT
:
836 case WPS_TOKEN_BUTTON_VOLUME
:
841 token
->value
.i
= val
;
846 static int parse_progressbar(const char *wps_bufptr
,
847 struct wps_token
*token
,
848 struct wps_data
*wps_data
)
850 /* %pb or %pb|filename|x|y|width|height|
851 using - for any of the params uses "sane" values */
852 #ifdef HAVE_LCD_BITMAP
860 const char *filename
;
861 int x
, y
, height
, width
;
863 const char *ptr
= wps_bufptr
;
864 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
865 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
868 return WPS_ERROR_INVALID_PARAM
;
870 struct viewport
*vp
= &curr_vp
->vp
;
872 int font_height
= font_get(vp
->font
)->height
;
876 /* we need to know what line number (viewport relative) this pb is,
877 * so count them... */
879 struct skin_line
*line
= curr_vp
->lines
;
885 pb
->have_bitmap_pb
= false;
886 pb
->bm
.data
= NULL
; /* no bitmap specified */
888 if (*wps_bufptr
!= '|') /* regular old style */
891 pb
->width
= vp
->width
;
892 pb
->height
= SYSFONT_HEIGHT
-2;
893 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
896 add_to_ll_chain(&wps_data
->progressbars
, item
);
899 ptr
= wps_bufptr
+ 1;
901 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
902 &x
, &y
, &width
, &height
)))
903 return WPS_ERROR_INVALID_PARAM
;
905 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
906 pb
->bm
.data
= (char*)filename
;
908 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
913 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
915 /* A zero width causes a divide-by-zero error later, so reject it */
917 return WPS_ERROR_INVALID_PARAM
;
922 pb
->width
= vp
->width
- pb
->x
;
924 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
926 /* A zero height makes no sense - reject it */
928 return WPS_ERROR_INVALID_PARAM
;
933 pb
->height
= font_height
;
935 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
938 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
941 add_to_ll_chain(&wps_data
->progressbars
, item
);
943 /* Skip the rest of the line */
944 return skip_end_of_line(wps_bufptr
)-1;
948 if (*(wps_bufptr
-1) == 'f')
949 wps_data
->full_line_progressbar
= true;
951 wps_data
->full_line_progressbar
= false;
959 static int parse_albumart_load(const char *wps_bufptr
,
960 struct wps_token
*token
,
961 struct wps_data
*wps_data
)
963 const char *_pos
, *newline
;
965 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
966 (void)token
; /* silence warning */
968 return skip_end_of_line(wps_bufptr
);
970 /* reset albumart info in wps */
973 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
974 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
975 aa
->vp
= &curr_vp
->vp
;
977 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
979 newline
= strchr(wps_bufptr
, '\n');
981 /* initial validation and parsing of x and y components */
982 if (*wps_bufptr
!= '|')
983 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
985 _pos
= wps_bufptr
+ 1;
986 _pos
= parse_list("dd", NULL
, '|', _pos
, &aa
->x
, &aa
->y
);
988 if (!_pos
|| _pos
> newline
|| *_pos
!= '|')
989 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
992 /* parsing width field */
996 /* apply each modifier in turn */
1003 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1007 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1012 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1020 /* simply ignored */
1027 /* extract max width data */
1030 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
1031 return WPS_ERROR_INVALID_PARAM
;
1033 aa
->width
= atoi(_pos
);
1035 _pos
= strchr(_pos
, '|');
1036 if (!_pos
|| _pos
> newline
)
1037 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
1038 e.g. %Cl|7|59|200\n */
1041 /* parsing height field */
1045 /* apply each modifier in turn */
1052 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1056 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1061 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1069 /* simply ignored */
1076 /* extract max height data */
1079 if (!isdigit(*_pos
))
1080 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1082 aa
->height
= atoi(_pos
);
1084 _pos
= strchr(_pos
, '|');
1085 if (!_pos
|| _pos
> newline
)
1086 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1087 e.g. %Cl|7|59|200|200\n */
1090 /* if we got here, we parsed everything ok .. ! */
1093 else if (aa
->width
> LCD_WIDTH
)
1094 aa
->width
= LCD_WIDTH
;
1098 else if (aa
->height
> LCD_HEIGHT
)
1099 aa
->height
= LCD_HEIGHT
;
1101 aa
->state
= WPS_ALBUMART_LOAD
;
1103 wps_data
->albumart
= aa
;
1105 /* Skip the rest of the line */
1106 return skip_end_of_line(wps_bufptr
);
1109 static int parse_albumart_display(const char *wps_bufptr
,
1110 struct wps_token
*token
,
1111 struct wps_data
*wps_data
)
1114 struct wps_token
*prev
= token
-1;
1115 if ((wps_data
->num_tokens
> 1) && (prev
->type
== WPS_TOKEN_CONDITIONAL
))
1117 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1119 else if (wps_data
->albumart
)
1121 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1124 /* the old code did this so keep it here for now...
1125 * this is to allow the posibility to showing the next tracks AA! */
1126 if (wps_bufptr
+1 == 'n')
1131 #endif /* HAVE_ALBUMART */
1133 #ifdef HAVE_TOUCHSCREEN
1135 struct touchaction
{char* s
; int action
;};
1136 static struct touchaction touchactions
[] = {
1137 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1138 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1139 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1140 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1141 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1142 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1143 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1145 static int parse_touchregion(const char *wps_bufptr
,
1146 struct wps_token
*token
, struct wps_data
*wps_data
)
1150 struct touchregion
*region
= NULL
;
1151 const char *ptr
= wps_bufptr
;
1153 const char pb_string
[] = "progressbar";
1154 const char vol_string
[] = "volume";
1157 /* format: %T|x|y|width|height|action|
1158 * if action starts with & the area must be held to happen
1160 * play - play/pause playback
1161 * stop - stop playback, exit the wps
1164 * ffwd - seek forward
1165 * rwd - seek backwards
1166 * menu - go back to the main menu
1167 * browse - go back to the file/db browser
1168 * shuffle - toggle shuffle mode
1169 * repmode - cycle the repeat mode
1170 * quickscreen - go into the quickscreen
1171 * contextmenu - open the context menu
1172 * playlist - go into the playlist
1173 * pitch - go into the pitchscreen
1178 return WPS_ERROR_INVALID_PARAM
;
1181 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1182 return WPS_ERROR_INVALID_PARAM
;
1184 /* Check there is a terminating | */
1186 return WPS_ERROR_INVALID_PARAM
;
1188 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1190 return WPS_ERROR_INVALID_PARAM
;
1192 /* should probably do some bounds checking here with the viewport... but later */
1193 region
->action
= ACTION_NONE
;
1198 region
->wvp
= curr_vp
;
1200 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1201 && *(action
+ sizeof(pb_string
)-1) == '|')
1202 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1203 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1204 && *(action
+ sizeof(vol_string
)-1) == '|')
1205 region
->type
= WPS_TOUCHREGION_VOLUME
;
1208 region
->type
= WPS_TOUCHREGION_ACTION
;
1213 region
->repeat
= true;
1216 region
->repeat
= false;
1219 imax
= ARRAYLEN(touchactions
);
1220 while ((region
->action
== ACTION_NONE
) &&
1223 /* try to match with one of our touchregion screens */
1224 int len
= strlen(touchactions
[i
].s
);
1225 if (!strncmp(touchactions
[i
].s
, action
, len
)
1226 && *(action
+len
) == '|')
1227 region
->action
= touchactions
[i
].action
;
1230 if (region
->action
== ACTION_NONE
)
1231 return WPS_ERROR_INVALID_PARAM
;
1233 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1235 return WPS_ERROR_INVALID_PARAM
;
1236 add_to_ll_chain(&wps_data
->touchregions
, item
);
1237 return skip_end_of_line(wps_bufptr
);
1241 /* Parse a generic token from the given string. Return the length read */
1242 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1244 int skip
= 0, taglen
= 0, ret
;
1245 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1246 const struct wps_tag
*tag
;
1247 memset(token
, 0, sizeof(*token
));
1258 /* escaped characters */
1259 token
->type
= WPS_TOKEN_CHARACTER
;
1260 token
->value
.c
= *wps_bufptr
;
1262 wps_data
->num_tokens
++;
1266 /* conditional tag */
1267 token
->type
= WPS_TOKEN_CONDITIONAL
;
1269 condindex
[level
] = wps_data
->num_tokens
;
1270 numoptions
[level
] = 1;
1271 wps_data
->num_tokens
++;
1272 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1273 if (ret
< 0) return ret
;
1278 /* find what tag we have */
1279 for (tag
= all_tags
;
1280 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1283 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1284 token
->type
= tag
->type
;
1285 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1287 /* if the tag has a special parsing function, we call it */
1288 if (tag
->parse_func
)
1290 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1291 if (ret
< 0) return ret
;
1295 /* Some tags we don't want to save as tokens */
1296 if (tag
->type
== WPS_NO_TOKEN
)
1299 /* tags that start with 'F', 'I' or 'D' are for the next file */
1300 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1301 *(tag
->name
) == 'D')
1304 wps_data
->num_tokens
++;
1313 data is the pointer to the structure where the parsed WPS should be stored.
1315 wps_bufptr points to the string containing the WPS tags */
1316 #define TOKEN_BLOCK_SIZE 128
1317 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1319 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1321 enum wps_parse_error fail
= PARSE_OK
;
1323 int max_tokens
= TOKEN_BLOCK_SIZE
;
1324 size_t buf_free
= 0;
1328 /* allocate enough RAM for a reasonable skin, grow as needed.
1329 * Free any used RAM before loading the images to be 100% RAM efficient */
1330 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1331 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1333 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1334 data
->num_tokens
= 0;
1336 while (*wps_bufptr
&& !fail
)
1338 /* first make sure there is enough room for tokens */
1339 if (max_tokens
<= data
->num_tokens
+ 5)
1341 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1342 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1343 /* do some smarts here to grow the array a bit */
1344 if (skin_buffer_freespace() < needed
)
1346 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1349 skin_buffer_increment(needed
, false);
1350 max_tokens
+= extra_tokens
;
1353 switch(*wps_bufptr
++)
1358 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1360 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1363 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1365 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1371 /* Alternating sublines separator */
1373 if (level
>= 0) /* there are unclosed conditionals */
1375 fail
= PARSE_FAIL_UNCLOSED_COND
;
1379 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1380 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1384 /* Conditional list start */
1386 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1388 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1392 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1393 lastcond
[level
] = data
->num_tokens
++;
1396 /* Conditional list end */
1398 if (level
< 0) /* not in a conditional, invalid char */
1400 fail
= PARSE_FAIL_INVALID_CHAR
;
1404 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1405 if (lastcond
[level
])
1406 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1409 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1413 lastcond
[level
] = 0;
1415 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1419 /* Conditional list option */
1421 if (level
< 0) /* not in a conditional, invalid char */
1423 fail
= PARSE_FAIL_INVALID_CHAR
;
1427 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1428 if (lastcond
[level
])
1429 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1432 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1436 lastcond
[level
] = data
->num_tokens
;
1437 numoptions
[level
]++;
1443 if (level
>= 0) /* there are unclosed conditionals */
1445 fail
= PARSE_FAIL_UNCLOSED_COND
;
1449 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1452 /* End of this line */
1454 if (level
>= 0) /* there are unclosed conditionals */
1456 fail
= PARSE_FAIL_UNCLOSED_COND
;
1459 /* add a new token for the \n so empty lines are correct */
1460 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1461 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1462 data
->tokens
[data
->num_tokens
].next
= false;
1465 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1467 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1476 unsigned int len
= 1;
1477 const char *string_start
= wps_bufptr
- 1;
1479 /* find the length of the string */
1480 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1481 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1482 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1483 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1489 /* look if we already have that string */
1492 struct skin_token_list
*list
= data
->strings
;
1495 str
= (char*)list
->token
->value
.data
;
1496 found
= (strlen(str
) == len
&&
1497 strncmp(string_start
, str
, len
) == 0);
1499 break; /* break here because the list item is
1500 used if its found */
1503 /* If a matching string is found, found is true and i is
1504 the index of the string. If not, found is false */
1509 str
= (char*)skin_buffer_alloc(len
+1);
1512 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1515 strlcpy(str
, string_start
, len
+1);
1516 struct skin_token_list
*item
=
1517 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1520 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1523 add_to_ll_chain(&data
->strings
, item
);
1527 /* another occurrence of an existing string */
1528 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1530 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1537 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1538 fail
= PARSE_FAIL_UNCLOSED_COND
;
1540 if (*wps_bufptr
&& !fail
)
1541 /* one of the limits of the while loop was exceeded */
1542 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1545 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
1546 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
1547 /* freeup unused tokens */
1548 skin_buffer_free_from_front(sizeof(struct wps_token
)
1549 * (max_tokens
- data
->num_tokens
));
1551 #if defined(DEBUG) || defined(SIMULATOR)
1553 print_debug_info(data
, fail
, line_number
);
1561 static void wps_reset(struct wps_data
*data
)
1563 #ifdef HAVE_REMOTE_LCD
1564 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1566 memset(data
, 0, sizeof(*data
));
1567 skin_data_init(data
);
1568 #ifdef HAVE_REMOTE_LCD
1569 data
->remote_wps
= rwps
;
1573 #ifdef HAVE_LCD_BITMAP
1574 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1576 (void)wps_data
; /* only needed for remote targets */
1577 bool loaded
= false;
1578 char img_path
[MAX_PATH
];
1579 get_image_filename(bitmap
->data
, bmpdir
,
1580 img_path
, sizeof(img_path
));
1582 /* load the image */
1584 #ifdef HAVE_REMOTE_LCD
1585 if (wps_data
->remote_wps
)
1586 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1589 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1592 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
1593 bitmap
->data
= imgbuf
;
1594 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
1598 skin_buffer_increment(ret
, true);
1603 /* Abort if we can't load an image */
1604 DEBUGF("ERR: Failed to load image - %s\n",img_path
);
1610 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1612 struct skin_token_list
*list
;
1613 /* do the progressbars */
1614 list
= wps_data
->progressbars
;
1617 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
1620 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
1624 /* regular images */
1625 list
= wps_data
->images
;
1628 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1631 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1633 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
1638 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1639 if (bmp_names
[BACKDROP_BMP
])
1641 int screen
= SCREEN_MAIN
;
1642 char img_path
[MAX_PATH
];
1643 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1644 img_path
, sizeof(img_path
));
1645 #if defined(HAVE_REMOTE_LCD)
1646 /* We only need to check LCD type if there is a remote LCD */
1647 if (wps_data
->remote_wps
)
1648 screen
= SCREEN_REMOTE
;
1650 screens
[screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1652 #endif /* has backdrop support */
1654 /* If we got here, everything was OK */
1658 #endif /* HAVE_LCD_BITMAP */
1660 /* to setup up the wps-data from a format-buffer (isfile = false)
1661 from a (wps-)file (isfile = true)*/
1662 bool skin_data_load(struct wps_data
*wps_data
,
1663 struct screen
*display
,
1668 if (!wps_data
|| !buf
)
1670 #ifdef HAVE_ALBUMART
1672 struct mp3entry
*curtrack
;
1674 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
1675 if (wps_data
->albumart
)
1677 old_aa
.state
= wps_data
->albumart
->state
;
1678 old_aa
.height
= wps_data
->albumart
->height
;
1679 old_aa
.width
= wps_data
->albumart
->width
;
1683 wps_reset(wps_data
);
1685 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
1688 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
1691 add_to_ll_chain(&wps_data
->viewports
, list
);
1694 /* Initialise the first (default) viewport */
1695 curr_vp
->label
= VP_DEFAULT_LABEL
;
1697 curr_vp
->vp
.width
= display
->getwidth();
1698 curr_vp
->vp
.height
= display
->getheight();
1700 curr_vp
->hidden_flags
= 0;
1701 curr_vp
->lines
= NULL
;
1704 if (!skin_start_new_line(curr_vp
, 0))
1707 switch (statusbar_position(display
->screen_type
))
1713 curr_vp
->vp
.y
= STATUSBAR_HEIGHT
;
1714 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1716 case STATUSBAR_BOTTOM
:
1718 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1721 #ifdef HAVE_LCD_BITMAP
1722 curr_vp
->vp
.font
= FONT_UI
;
1723 curr_vp
->vp
.drawmode
= DRMODE_SOLID
;
1726 if (display
->depth
> 1)
1728 curr_vp
->vp
.fg_pattern
= display
->get_foreground();
1729 curr_vp
->vp
.bg_pattern
= display
->get_background();
1734 return wps_parse(wps_data
, buf
, false);
1738 int fd
= open_utf8(buf
, O_RDONLY
);
1743 /* get buffer space from the plugin buffer */
1744 size_t buffersize
= 0;
1745 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1750 /* copy the file's content to the buffer for parsing,
1751 ensuring that every line ends with a newline char. */
1752 unsigned int start
= 0;
1753 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1755 start
+= strlen(wps_buffer
+ start
);
1756 if (start
< buffersize
- 1)
1758 wps_buffer
[start
++] = '\n';
1759 wps_buffer
[start
] = 0;
1768 #ifdef HAVE_LCD_BITMAP
1769 /* Set all filename pointers to NULL */
1770 memset(bmp_names
, 0, sizeof(bmp_names
));
1773 /* parse the WPS source */
1774 if (!wps_parse(wps_data
, wps_buffer
, true)) {
1775 wps_reset(wps_data
);
1779 wps_data
->wps_loaded
= true;
1781 #ifdef HAVE_LCD_BITMAP
1782 /* get the bitmap dir */
1783 char bmpdir
[MAX_PATH
];
1784 char *dot
= strrchr(buf
, '.');
1786 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1788 /* load the bitmaps that were found by the parsing */
1789 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
1790 wps_reset(wps_data
);
1794 #ifdef HAVE_ALBUMART
1795 status
= audio_status();
1796 if (status
& AUDIO_STATUS_PLAY
)
1798 struct skin_albumart
*aa
= wps_data
->albumart
;
1799 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
1801 (((old_aa
.height
!= aa
->height
) ||
1802 (old_aa
.width
!= aa
->width
))))))
1804 curtrack
= audio_current_track();
1805 offset
= curtrack
->offset
;
1807 if (!(status
& AUDIO_STATUS_PAUSE
))
1812 #if defined(DEBUG) || defined(SIMULATOR)