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
61 #define WPS_ERROR_INVALID_PARAM -1
63 /* level of current conditional.
64 -1 means we're not in a conditional. */
65 static int level
= -1;
67 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
68 or WPS_TOKEN_CONDITIONAL_START in current level */
69 static int lastcond
[WPS_MAX_COND_LEVEL
];
71 /* index of the WPS_TOKEN_CONDITIONAL in current level */
72 static int condindex
[WPS_MAX_COND_LEVEL
];
74 /* number of condtional options in current level */
75 static int numoptions
[WPS_MAX_COND_LEVEL
];
77 /* line number, debug only */
78 static int line_number
;
80 /* the current viewport */
81 static struct skin_viewport
*curr_vp
;
82 /* the current line, linked to the above viewport */
83 static struct skin_line
*curr_line
;
85 #ifdef HAVE_LCD_BITMAP
88 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
90 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
93 #define PROGRESSBAR_BMP MAX_IMAGES
94 #define BACKDROP_BMP (MAX_BITMAPS-1)
96 /* pointers to the bitmap filenames in the WPS source */
97 static const char *bmp_names
[MAX_BITMAPS
];
99 #endif /* HAVE_LCD_BITMAP */
101 #if defined(DEBUG) || defined(SIMULATOR)
102 /* debugging function */
103 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
104 extern void debug_skin_usage(void);
107 /* Function for parsing of details for a token. At the moment the
108 function is called, the token type has already been set. The
109 function must fill in the details and possibly add more tokens
110 to the token array. It should return the number of chars that
113 wps_bufptr points to the char following the tag (i.e. where
115 token is the pointer to the 'main' token being parsed
117 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
118 struct wps_token
*token
, struct wps_data
*wps_data
);
121 enum wps_token_type type
;
123 unsigned char refresh_type
;
124 const wps_tag_parse_func parse_func
;
126 static int skip_end_of_line(const char *wps_bufptr
);
127 /* prototypes of all special parse functions : */
128 static int parse_timeout(const char *wps_bufptr
,
129 struct wps_token
*token
, struct wps_data
*wps_data
);
130 static int parse_progressbar(const char *wps_bufptr
,
131 struct wps_token
*token
, struct wps_data
*wps_data
);
132 static int parse_dir_level(const char *wps_bufptr
,
133 struct wps_token
*token
, struct wps_data
*wps_data
);
134 static int parse_setting_and_lang(const char *wps_bufptr
,
135 struct wps_token
*token
, struct wps_data
*wps_data
);
137 #ifdef HAVE_LCD_BITMAP
138 static int parse_viewport_display(const char *wps_bufptr
,
139 struct wps_token
*token
, struct wps_data
*wps_data
);
140 static int parse_viewport(const char *wps_bufptr
,
141 struct wps_token
*token
, struct wps_data
*wps_data
);
142 static int parse_statusbar_enable(const char *wps_bufptr
,
143 struct wps_token
*token
, struct wps_data
*wps_data
);
144 static int parse_statusbar_disable(const char *wps_bufptr
,
145 struct wps_token
*token
, struct wps_data
*wps_data
);
146 static int parse_image_display(const char *wps_bufptr
,
147 struct wps_token
*token
, struct wps_data
*wps_data
);
148 static int parse_image_load(const char *wps_bufptr
,
149 struct wps_token
*token
, struct wps_data
*wps_data
);
150 #endif /*HAVE_LCD_BITMAP */
151 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
152 static int parse_image_special(const char *wps_bufptr
,
153 struct wps_token
*token
, struct wps_data
*wps_data
);
156 static int parse_albumart_load(const char *wps_bufptr
,
157 struct wps_token
*token
, struct wps_data
*wps_data
);
158 static int parse_albumart_display(const char *wps_bufptr
,
159 struct wps_token
*token
, struct wps_data
*wps_data
);
160 #endif /* HAVE_ALBUMART */
161 #ifdef HAVE_TOUCHSCREEN
162 static int parse_touchregion(const char *wps_bufptr
,
163 struct wps_token
*token
, struct wps_data
*wps_data
);
165 static int fulline_tag_not_supported(const char *wps_bufptr
,
166 struct wps_token
*token
, struct wps_data
*wps_data
)
168 (void)token
; (void)wps_data
;
169 return skip_end_of_line(wps_bufptr
);
171 #define parse_touchregion fulline_tag_not_supported
174 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
176 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
179 /* array of available tags - those with more characters have to go first
180 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
181 static const struct wps_tag all_tags
[] = {
183 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
184 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
185 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
187 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
188 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
189 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
190 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
191 #if CONFIG_CHARGING >= CHARGING_MONITOR
192 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
195 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
198 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
199 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
200 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
209 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
210 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
211 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
212 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
213 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
214 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
215 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
216 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
219 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
224 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
225 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
226 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
228 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
232 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
233 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
234 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
236 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
237 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
238 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
244 /* current metadata */
245 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
270 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
272 #if (CONFIG_CODEC != MAS3507D)
273 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
275 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
276 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
279 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
281 #ifdef HAS_REMOTE_BUTTON_HOLD
282 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
284 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
287 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
288 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
289 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
292 #ifdef HAVE_LCD_BITMAP
293 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
295 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
296 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
298 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
301 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
303 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
304 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
305 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
306 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
308 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
309 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
310 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
311 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
314 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
315 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
316 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
319 #if CONFIG_CODEC == SWCODEC
320 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
321 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
324 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
325 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
327 #ifdef HAVE_LCD_BITMAP
328 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
329 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
331 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
333 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
334 parse_image_display
},
336 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
338 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
339 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
, parse_albumart_display
},
342 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
343 parse_viewport_display
},
344 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
346 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
347 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
351 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
,
352 parse_setting_and_lang
},
353 { WPS_TOKEN_TRANSLATEDSTRING
, "Sx", WPS_REFRESH_STATIC
,
354 parse_setting_and_lang
},
356 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
357 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
359 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
360 /* the array MUST end with an empty string (first char is \0) */
364 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
365 * chains require the order to be kept.
367 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
373 struct skin_token_list
*t
= *list
;
379 /* create and init a new wpsll item.
380 * passing NULL to token will alloc a new one.
381 * You should only pass NULL for the token when the token type (table above)
382 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
384 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
387 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
389 token
= skin_buffer_alloc(sizeof(struct wps_token
));
390 if (!llitem
|| !token
)
393 llitem
->token
= token
;
395 llitem
->token
->value
.data
= token_data
;
399 /* Returns the number of chars that should be skipped to jump
400 immediately after the first eol, i.e. to the start of the next line */
401 static int skip_end_of_line(const char *wps_bufptr
)
405 while(*(wps_bufptr
+ skip
) != '\n')
410 /* Starts a new subline in the current line during parsing */
411 static bool skin_start_new_subline(struct skin_line
*line
, int curr_token
)
413 struct skin_subline
*subline
= skin_buffer_alloc(sizeof(struct skin_subline
));
417 subline
->first_token_idx
= curr_token
;
418 subline
->next
= NULL
;
420 subline
->line_type
= 0;
421 subline
->time_mult
= 0;
423 line
->curr_subline
->last_token_idx
= curr_token
-1;
424 line
->curr_subline
->next
= subline
;
425 line
->curr_subline
= subline
;
429 static bool skin_start_new_line(struct skin_viewport
*vp
, int curr_token
)
431 struct skin_line
*line
= skin_buffer_alloc(sizeof(struct skin_line
));
432 struct skin_subline
*subline
= NULL
;
436 /* init the subline */
437 subline
= &line
->sublines
;
438 subline
->first_token_idx
= curr_token
;
439 subline
->next
= NULL
;
440 subline
->line_type
= 0;
441 subline
->time_mult
= 0;
443 /* init the new line */
444 line
->curr_subline
= &line
->sublines
;
446 line
->subline_expire_time
= 0;
448 /* connect to curr_line and vp pointers.
449 * 1) close the previous lines subline
450 * 2) connect to vp pointer
451 * 3) connect to curr_line global pointer
455 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
456 curr_line
->next
= line
;
457 curr_line
->curr_subline
= NULL
;
466 #ifdef HAVE_LCD_BITMAP
468 static int parse_statusbar_enable(const char *wps_bufptr
,
469 struct wps_token
*token
,
470 struct wps_data
*wps_data
)
472 (void)token
; /* Kill warnings */
473 wps_data
->wps_sb_tag
= true;
474 wps_data
->show_sb_on_wps
= true;
475 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
476 if (default_vp
->vp
.y
== 0)
478 default_vp
->vp
.y
= STATUSBAR_HEIGHT
;
479 default_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
481 return skip_end_of_line(wps_bufptr
);
484 static int parse_statusbar_disable(const char *wps_bufptr
,
485 struct wps_token
*token
,
486 struct wps_data
*wps_data
)
488 (void)token
; /* Kill warnings */
489 wps_data
->wps_sb_tag
= true;
490 wps_data
->show_sb_on_wps
= false;
491 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
492 if (default_vp
->vp
.y
== STATUSBAR_HEIGHT
)
494 default_vp
->vp
.y
= 0;
495 default_vp
->vp
.height
+= STATUSBAR_HEIGHT
;
497 return skip_end_of_line(wps_bufptr
);
500 static int get_image_id(int c
)
502 if(c
>= 'a' && c
<= 'z')
504 else if(c
>= 'A' && c
<= 'Z')
510 static char *get_image_filename(const char *start
, const char* bmpdir
,
511 char *buf
, int buf_size
)
513 const char *end
= strchr(start
, '|');
515 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
521 int bmpdirlen
= strlen(bmpdir
);
524 buf
[bmpdirlen
] = '/';
525 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
526 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
531 static int parse_image_display(const char *wps_bufptr
,
532 struct wps_token
*token
,
533 struct wps_data
*wps_data
)
535 char label
= wps_bufptr
[0];
537 struct gui_img
*img
;;
540 img
= find_image(label
, wps_data
);
543 token
->value
.i
= label
; /* so debug works */
544 return WPS_ERROR_INVALID_PARAM
;
547 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
549 if (subimage
>= img
->num_subimages
)
550 return WPS_ERROR_INVALID_PARAM
;
552 /* Store sub-image number to display in high bits */
553 token
->value
.i
= label
| (subimage
<< 8);
554 return 2; /* We have consumed 2 bytes */
556 token
->value
.i
= label
;
557 return 1; /* We have consumed 1 byte */
561 static int parse_image_load(const char *wps_bufptr
,
562 struct wps_token
*token
,
563 struct wps_data
*wps_data
)
565 const char *ptr
= wps_bufptr
;
567 const char* filename
;
573 /* format: %x|n|filename.bmp|x|y|
574 or %xl|n|filename.bmp|x|y|
575 or %xl|n|filename.bmp|x|y|num_subimages|
579 return WPS_ERROR_INVALID_PARAM
;
583 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
584 return WPS_ERROR_INVALID_PARAM
;
586 /* Check there is a terminating | */
588 return WPS_ERROR_INVALID_PARAM
;
590 /* check the image number and load state */
591 if(find_image(*id
, wps_data
))
593 /* Invalid image ID */
594 return WPS_ERROR_INVALID_PARAM
;
596 img
= skin_buffer_alloc(sizeof(struct gui_img
));
598 return WPS_ERROR_INVALID_PARAM
;
599 /* save a pointer to the filename */
600 img
->bm
.data
= (char*)filename
;
604 img
->num_subimages
= 1;
605 img
->always_display
= false;
607 /* save current viewport */
608 img
->vp
= &curr_vp
->vp
;
610 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
612 img
->always_display
= true;
616 /* Parse the (optional) number of sub-images */
618 newline
= strchr(ptr
, '\n');
619 pos
= strchr(ptr
, '|');
620 if (pos
&& pos
< newline
)
621 img
->num_subimages
= atoi(ptr
);
623 if (img
->num_subimages
<= 0)
624 return WPS_ERROR_INVALID_PARAM
;
626 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
628 return WPS_ERROR_INVALID_PARAM
;
629 add_to_ll_chain(&wps_data
->images
, item
);
631 /* Skip the rest of the line */
632 return skip_end_of_line(wps_bufptr
);
635 static int parse_viewport_display(const char *wps_bufptr
,
636 struct wps_token
*token
,
637 struct wps_data
*wps_data
)
640 char letter
= wps_bufptr
[0];
642 if (letter
< 'a' || letter
> 'z')
644 /* invalid viewport tag */
645 return WPS_ERROR_INVALID_PARAM
;
647 token
->value
.i
= letter
;
651 static int parse_viewport(const char *wps_bufptr
,
652 struct wps_token
*token
,
653 struct wps_data
*wps_data
)
655 (void)token
; /* Kill warnings */
656 const char *ptr
= wps_bufptr
;
659 #ifdef HAVE_REMOTE_LCD
660 wps_data
->remote_wps
? SCREEN_REMOTE
:
664 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
666 /* check for the optional letter to signify its a hideable viewport */
667 /* %Vl|<label>|<rest of tags>| */
668 skin_vp
->hidden_flags
= 0;
669 skin_vp
->label
= VP_NO_LABEL
;
671 skin_vp
->lines
= NULL
;
674 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
675 - (wps_data
->num_tokens
> 0 ? 1 : 0);
679 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
680 return WPS_ERROR_INVALID_PARAM
;
686 char label
= *(ptr
+2);
687 if (label
>= 'a' && label
<= 'z')
689 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
690 skin_vp
->label
= label
;
693 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
698 return WPS_ERROR_INVALID_PARAM
;
701 struct viewport
*vp
= &skin_vp
->vp
;
702 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
704 if (!(ptr
= viewport_parse_viewport(vp
, screen
, ptr
, '|')))
705 return WPS_ERROR_INVALID_PARAM
;
707 vp
->flags
&= ~VP_FLAG_ALIGN_RIGHT
; /* ignore right-to-left languages */
708 /* Check for trailing | */
710 return WPS_ERROR_INVALID_PARAM
;
713 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
715 return WPS_ERROR_INVALID_PARAM
;
716 add_to_ll_chain(&wps_data
->viewports
, list
);
718 /* Skip the rest of the line */
719 return skip_end_of_line(wps_bufptr
);
722 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
723 static int parse_image_special(const char *wps_bufptr
,
724 struct wps_token
*token
,
725 struct wps_data
*wps_data
)
727 (void)wps_data
; /* kill warning */
729 const char *pos
= NULL
;
732 pos
= strchr(wps_bufptr
+ 1, '|');
733 newline
= strchr(wps_bufptr
, '\n');
736 return WPS_ERROR_INVALID_PARAM
;
738 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
740 /* format: %X|filename.bmp| */
741 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
745 /* Skip the rest of the line */
746 return skip_end_of_line(wps_bufptr
);
750 #endif /* HAVE_LCD_BITMAP */
752 static int parse_setting_and_lang(const char *wps_bufptr
,
753 struct wps_token
*token
,
754 struct wps_data
*wps_data
)
756 /* NOTE: both the string validations that happen in here will
757 * automatically PASS on checkwps because its too hard to get
758 * settings_list.c and englinsh.lang built for it.
759 * If that ever changes remove the #ifndef __PCTOOL__'s here
762 const char *ptr
= wps_bufptr
;
767 /* Find the setting's cfg_name */
769 return WPS_ERROR_INVALID_PARAM
;
771 end
= strchr(ptr
,'|');
773 return WPS_ERROR_INVALID_PARAM
;
774 strlcpy(temp
, ptr
,end
-ptr
+1);
776 if (token
->type
== WPS_TOKEN_TRANSLATEDSTRING
)
779 i
= lang_english_to_id(temp
);
781 return WPS_ERROR_INVALID_PARAM
;
786 /* Find the setting */
787 for (i
=0; i
<nb_settings
; i
++)
788 if (settings
[i
].cfg_name
&&
789 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
790 /* prevent matches on cfg_name prefixes */
791 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
794 if (i
== nb_settings
)
795 return WPS_ERROR_INVALID_PARAM
;
798 /* Store the setting number */
801 /* Skip the rest of the line */
806 static int parse_dir_level(const char *wps_bufptr
,
807 struct wps_token
*token
,
808 struct wps_data
*wps_data
)
810 char val
[] = { *wps_bufptr
, '\0' };
811 token
->value
.i
= atoi(val
);
812 (void)wps_data
; /* Kill warnings */
816 static int parse_timeout(const char *wps_bufptr
,
817 struct wps_token
*token
,
818 struct wps_data
*wps_data
)
822 bool have_point
= false;
823 bool have_tenth
= false;
825 (void)wps_data
; /* Kill the warning */
827 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
829 if (*wps_bufptr
!= '.')
832 val
+= *wps_bufptr
- '0';
848 if (have_tenth
== false)
851 if (val
== 0 && skip
== 0)
853 /* decide what to do if no value was specified */
856 case WPS_TOKEN_SUBLINE_TIMEOUT
:
858 case WPS_TOKEN_BUTTON_VOLUME
:
863 token
->value
.i
= val
;
868 static int parse_progressbar(const char *wps_bufptr
,
869 struct wps_token
*token
,
870 struct wps_data
*wps_data
)
872 /* %pb or %pb|filename|x|y|width|height|
873 using - for any of the params uses "sane" values */
874 #ifdef HAVE_LCD_BITMAP
882 const char *filename
;
883 int x
, y
, height
, width
;
885 const char *ptr
= wps_bufptr
;
886 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
887 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
890 return WPS_ERROR_INVALID_PARAM
;
892 struct viewport
*vp
= &curr_vp
->vp
;
894 int font_height
= font_get(vp
->font
)->height
;
898 /* we need to know what line number (viewport relative) this pb is,
899 * so count them... */
901 struct skin_line
*line
= curr_vp
->lines
;
907 pb
->have_bitmap_pb
= false;
908 pb
->bm
.data
= NULL
; /* no bitmap specified */
910 if (*wps_bufptr
!= '|') /* regular old style */
913 pb
->width
= vp
->width
;
914 pb
->height
= SYSFONT_HEIGHT
-2;
915 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
918 add_to_ll_chain(&wps_data
->progressbars
, item
);
921 ptr
= wps_bufptr
+ 1;
923 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
924 &x
, &y
, &width
, &height
)))
925 return WPS_ERROR_INVALID_PARAM
;
927 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
928 pb
->bm
.data
= (char*)filename
;
930 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
935 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
937 /* A zero width causes a divide-by-zero error later, so reject it */
939 return WPS_ERROR_INVALID_PARAM
;
944 pb
->width
= vp
->width
- pb
->x
;
946 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
948 /* A zero height makes no sense - reject it */
950 return WPS_ERROR_INVALID_PARAM
;
955 pb
->height
= font_height
;
957 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
960 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
963 add_to_ll_chain(&wps_data
->progressbars
, item
);
965 /* Skip the rest of the line */
966 return skip_end_of_line(wps_bufptr
)-1;
970 if (*(wps_bufptr
-1) == 'f')
971 wps_data
->full_line_progressbar
= true;
973 wps_data
->full_line_progressbar
= false;
981 static int parse_albumart_load(const char *wps_bufptr
,
982 struct wps_token
*token
,
983 struct wps_data
*wps_data
)
985 const char *_pos
, *newline
;
987 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
988 (void)token
; /* silence warning */
990 return skip_end_of_line(wps_bufptr
);
992 /* reset albumart info in wps */
995 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
996 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
997 aa
->vp
= &curr_vp
->vp
;
999 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1001 newline
= strchr(wps_bufptr
, '\n');
1003 /* initial validation and parsing of x and y components */
1004 if (*wps_bufptr
!= '|')
1005 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1007 _pos
= wps_bufptr
+ 1;
1008 _pos
= parse_list("dd", NULL
, '|', _pos
, &aa
->x
, &aa
->y
);
1010 if (!_pos
|| _pos
> newline
|| *_pos
!= '|')
1011 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
1014 /* parsing width field */
1018 /* apply each modifier in turn */
1025 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1029 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1034 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1042 /* simply ignored */
1049 /* extract max width data */
1052 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
1053 return WPS_ERROR_INVALID_PARAM
;
1055 aa
->width
= atoi(_pos
);
1057 _pos
= strchr(_pos
, '|');
1058 if (!_pos
|| _pos
> newline
)
1059 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
1060 e.g. %Cl|7|59|200\n */
1063 /* parsing height field */
1067 /* apply each modifier in turn */
1074 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1078 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1083 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1091 /* simply ignored */
1098 /* extract max height data */
1101 if (!isdigit(*_pos
))
1102 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1104 aa
->height
= atoi(_pos
);
1106 _pos
= strchr(_pos
, '|');
1107 if (!_pos
|| _pos
> newline
)
1108 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1109 e.g. %Cl|7|59|200|200\n */
1112 /* if we got here, we parsed everything ok .. ! */
1115 else if (aa
->width
> LCD_WIDTH
)
1116 aa
->width
= LCD_WIDTH
;
1120 else if (aa
->height
> LCD_HEIGHT
)
1121 aa
->height
= LCD_HEIGHT
;
1123 aa
->state
= WPS_ALBUMART_LOAD
;
1125 wps_data
->albumart
= aa
;
1127 /* Skip the rest of the line */
1128 return skip_end_of_line(wps_bufptr
);
1131 static int parse_albumart_display(const char *wps_bufptr
,
1132 struct wps_token
*token
,
1133 struct wps_data
*wps_data
)
1136 struct wps_token
*prev
= token
-1;
1137 if ((wps_data
->num_tokens
>= 1) && (prev
->type
== WPS_TOKEN_CONDITIONAL
))
1139 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1141 else if (wps_data
->albumart
)
1143 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1146 /* the old code did this so keep it here for now...
1147 * this is to allow the posibility to showing the next tracks AA! */
1148 if (wps_bufptr
+1 == 'n')
1153 #endif /* HAVE_ALBUMART */
1155 #ifdef HAVE_TOUCHSCREEN
1157 struct touchaction
{char* s
; int action
;};
1158 static struct touchaction touchactions
[] = {
1159 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1160 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1161 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1162 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1163 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1164 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1165 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1167 static int parse_touchregion(const char *wps_bufptr
,
1168 struct wps_token
*token
, struct wps_data
*wps_data
)
1172 struct touchregion
*region
= NULL
;
1173 const char *ptr
= wps_bufptr
;
1175 const char pb_string
[] = "progressbar";
1176 const char vol_string
[] = "volume";
1179 /* format: %T|x|y|width|height|action|
1180 * if action starts with & the area must be held to happen
1182 * play - play/pause playback
1183 * stop - stop playback, exit the wps
1186 * ffwd - seek forward
1187 * rwd - seek backwards
1188 * menu - go back to the main menu
1189 * browse - go back to the file/db browser
1190 * shuffle - toggle shuffle mode
1191 * repmode - cycle the repeat mode
1192 * quickscreen - go into the quickscreen
1193 * contextmenu - open the context menu
1194 * playlist - go into the playlist
1195 * pitch - go into the pitchscreen
1200 return WPS_ERROR_INVALID_PARAM
;
1203 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1204 return WPS_ERROR_INVALID_PARAM
;
1206 /* Check there is a terminating | */
1208 return WPS_ERROR_INVALID_PARAM
;
1210 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1212 return WPS_ERROR_INVALID_PARAM
;
1214 /* should probably do some bounds checking here with the viewport... but later */
1215 region
->action
= ACTION_NONE
;
1220 region
->wvp
= curr_vp
;
1222 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1223 && *(action
+ sizeof(pb_string
)-1) == '|')
1224 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1225 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1226 && *(action
+ sizeof(vol_string
)-1) == '|')
1227 region
->type
= WPS_TOUCHREGION_VOLUME
;
1230 region
->type
= WPS_TOUCHREGION_ACTION
;
1235 region
->repeat
= true;
1238 region
->repeat
= false;
1241 imax
= ARRAYLEN(touchactions
);
1242 while ((region
->action
== ACTION_NONE
) &&
1245 /* try to match with one of our touchregion screens */
1246 int len
= strlen(touchactions
[i
].s
);
1247 if (!strncmp(touchactions
[i
].s
, action
, len
)
1248 && *(action
+len
) == '|')
1249 region
->action
= touchactions
[i
].action
;
1252 if (region
->action
== ACTION_NONE
)
1253 return WPS_ERROR_INVALID_PARAM
;
1255 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1257 return WPS_ERROR_INVALID_PARAM
;
1258 add_to_ll_chain(&wps_data
->touchregions
, item
);
1259 return skip_end_of_line(wps_bufptr
);
1263 /* Parse a generic token from the given string. Return the length read */
1264 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1266 int skip
= 0, taglen
= 0, ret
;
1267 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1268 const struct wps_tag
*tag
;
1269 memset(token
, 0, sizeof(*token
));
1280 /* escaped characters */
1281 token
->type
= WPS_TOKEN_CHARACTER
;
1282 token
->value
.c
= *wps_bufptr
;
1284 wps_data
->num_tokens
++;
1288 /* conditional tag */
1289 token
->type
= WPS_TOKEN_CONDITIONAL
;
1291 condindex
[level
] = wps_data
->num_tokens
;
1292 numoptions
[level
] = 1;
1293 wps_data
->num_tokens
++;
1294 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1295 if (ret
< 0) return ret
;
1300 /* find what tag we have */
1301 for (tag
= all_tags
;
1302 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1305 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1306 token
->type
= tag
->type
;
1307 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1309 /* if the tag has a special parsing function, we call it */
1310 if (tag
->parse_func
)
1312 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1313 if (ret
< 0) return ret
;
1317 /* Some tags we don't want to save as tokens */
1318 if (tag
->type
== WPS_NO_TOKEN
)
1321 /* tags that start with 'F', 'I' or 'D' are for the next file */
1322 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1323 *(tag
->name
) == 'D')
1326 wps_data
->num_tokens
++;
1335 data is the pointer to the structure where the parsed WPS should be stored.
1337 wps_bufptr points to the string containing the WPS tags */
1338 #define TOKEN_BLOCK_SIZE 128
1339 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1341 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1343 enum wps_parse_error fail
= PARSE_OK
;
1345 int max_tokens
= TOKEN_BLOCK_SIZE
;
1346 size_t buf_free
= 0;
1350 /* allocate enough RAM for a reasonable skin, grow as needed.
1351 * Free any used RAM before loading the images to be 100% RAM efficient */
1352 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1353 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1355 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1356 data
->num_tokens
= 0;
1358 while (*wps_bufptr
&& !fail
)
1360 /* first make sure there is enough room for tokens */
1361 if (max_tokens
<= data
->num_tokens
+ 5)
1363 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1364 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1365 /* do some smarts here to grow the array a bit */
1366 if (skin_buffer_freespace() < needed
)
1368 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1371 skin_buffer_increment(needed
, false);
1372 max_tokens
+= extra_tokens
;
1375 switch(*wps_bufptr
++)
1380 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1382 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1385 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1387 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1393 /* Alternating sublines separator */
1395 if (level
>= 0) /* there are unclosed conditionals */
1397 fail
= PARSE_FAIL_UNCLOSED_COND
;
1401 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1402 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1406 /* Conditional list start */
1408 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1410 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1414 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1415 lastcond
[level
] = data
->num_tokens
++;
1418 /* Conditional list end */
1420 if (level
< 0) /* not in a conditional, invalid char */
1422 fail
= PARSE_FAIL_INVALID_CHAR
;
1426 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1427 if (lastcond
[level
])
1428 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1431 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1435 lastcond
[level
] = 0;
1437 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1441 /* Conditional list option */
1443 if (level
< 0) /* not in a conditional, invalid char */
1445 fail
= PARSE_FAIL_INVALID_CHAR
;
1449 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1450 if (lastcond
[level
])
1451 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1454 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1458 lastcond
[level
] = data
->num_tokens
;
1459 numoptions
[level
]++;
1465 if (level
>= 0) /* there are unclosed conditionals */
1467 fail
= PARSE_FAIL_UNCLOSED_COND
;
1471 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1474 /* End of this line */
1476 if (level
>= 0) /* there are unclosed conditionals */
1478 fail
= PARSE_FAIL_UNCLOSED_COND
;
1481 /* add a new token for the \n so empty lines are correct */
1482 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1483 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1484 data
->tokens
[data
->num_tokens
].next
= false;
1487 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1489 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1498 unsigned int len
= 1;
1499 const char *string_start
= wps_bufptr
- 1;
1501 /* find the length of the string */
1502 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1503 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1504 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1505 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1511 /* look if we already have that string */
1514 struct skin_token_list
*list
= data
->strings
;
1517 str
= (char*)list
->token
->value
.data
;
1518 found
= (strlen(str
) == len
&&
1519 strncmp(string_start
, str
, len
) == 0);
1521 break; /* break here because the list item is
1522 used if its found */
1525 /* If a matching string is found, found is true and i is
1526 the index of the string. If not, found is false */
1531 str
= (char*)skin_buffer_alloc(len
+1);
1534 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1537 strlcpy(str
, string_start
, len
+1);
1538 struct skin_token_list
*item
=
1539 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1542 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1545 add_to_ll_chain(&data
->strings
, item
);
1549 /* another occurrence of an existing string */
1550 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1552 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1559 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1560 fail
= PARSE_FAIL_UNCLOSED_COND
;
1562 if (*wps_bufptr
&& !fail
)
1563 /* one of the limits of the while loop was exceeded */
1564 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1567 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
1568 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
1569 /* freeup unused tokens */
1570 skin_buffer_free_from_front(sizeof(struct wps_token
)
1571 * (max_tokens
- data
->num_tokens
));
1573 #if defined(DEBUG) || defined(SIMULATOR)
1575 print_debug_info(data
, fail
, line_number
);
1585 * initial setup of wps_data; does reset everything
1586 * except fields which need to survive, i.e.
1588 * wps_data->remote_wps
1590 void skin_data_reset(struct wps_data
*wps_data
)
1592 #ifdef HAVE_LCD_BITMAP
1593 wps_data
->images
= NULL
;
1594 wps_data
->progressbars
= NULL
;
1596 #ifdef HAVE_TOUCHSCREEN
1597 wps_data
->touchregions
= NULL
;
1599 wps_data
->viewports
= NULL
;
1600 wps_data
->strings
= NULL
;
1601 #ifdef HAVE_ALBUMART
1602 wps_data
->albumart
= NULL
;
1604 wps_data
->tokens
= NULL
;
1605 wps_data
->num_tokens
= 0;
1606 wps_data
->button_time_volume
= 0;
1608 #ifdef HAVE_LCD_BITMAP
1609 wps_data
->peak_meter_enabled
= false;
1610 wps_data
->wps_sb_tag
= false;
1611 wps_data
->show_sb_on_wps
= false;
1612 #else /* HAVE_LCD_CHARCELLS */
1615 for (i
= 0; i
< 8; i
++)
1617 wps_data
->wps_progress_pat
[i
] = 0;
1619 wps_data
->full_line_progressbar
= false;
1621 wps_data
->wps_loaded
= false;
1624 #ifdef HAVE_LCD_BITMAP
1625 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1627 (void)wps_data
; /* only needed for remote targets */
1628 bool loaded
= false;
1629 char img_path
[MAX_PATH
];
1630 get_image_filename(bitmap
->data
, bmpdir
,
1631 img_path
, sizeof(img_path
));
1633 /* load the image */
1635 #ifdef HAVE_REMOTE_LCD
1636 if (wps_data
->remote_wps
)
1637 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1640 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1643 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
1644 bitmap
->data
= imgbuf
;
1645 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
1649 skin_buffer_increment(ret
, true);
1654 /* Abort if we can't load an image */
1655 DEBUGF("ERR: Failed to load image - %s\n",img_path
);
1661 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1663 struct skin_token_list
*list
;
1664 /* do the progressbars */
1665 list
= wps_data
->progressbars
;
1668 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
1671 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
1675 /* regular images */
1676 list
= wps_data
->images
;
1679 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1682 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1684 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
1689 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1690 if (bmp_names
[BACKDROP_BMP
])
1692 int screen
= SCREEN_MAIN
;
1693 char img_path
[MAX_PATH
];
1694 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1695 img_path
, sizeof(img_path
));
1696 #if defined(HAVE_REMOTE_LCD)
1697 /* We only need to check LCD type if there is a remote LCD */
1698 if (wps_data
->remote_wps
)
1699 screen
= SCREEN_REMOTE
;
1701 screens
[screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1703 #endif /* has backdrop support */
1705 /* If we got here, everything was OK */
1709 #endif /* HAVE_LCD_BITMAP */
1711 /* to setup up the wps-data from a format-buffer (isfile = false)
1712 from a (wps-)file (isfile = true)*/
1713 bool skin_data_load(struct wps_data
*wps_data
,
1714 struct screen
*display
,
1719 if (!wps_data
|| !buf
)
1721 #ifdef HAVE_ALBUMART
1723 struct mp3entry
*curtrack
;
1725 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
1726 if (wps_data
->albumart
)
1728 old_aa
.state
= wps_data
->albumart
->state
;
1729 old_aa
.height
= wps_data
->albumart
->height
;
1730 old_aa
.width
= wps_data
->albumart
->width
;
1734 skin_data_reset(wps_data
);
1736 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
1739 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
1742 add_to_ll_chain(&wps_data
->viewports
, list
);
1745 /* Initialise the first (default) viewport */
1746 curr_vp
->label
= VP_DEFAULT_LABEL
;
1748 curr_vp
->vp
.width
= display
->getwidth();
1749 curr_vp
->vp
.height
= display
->getheight();
1751 curr_vp
->hidden_flags
= 0;
1752 curr_vp
->lines
= NULL
;
1755 if (!skin_start_new_line(curr_vp
, 0))
1758 switch (statusbar_position(display
->screen_type
))
1764 curr_vp
->vp
.y
= STATUSBAR_HEIGHT
;
1765 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1767 case STATUSBAR_BOTTOM
:
1769 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1772 #ifdef HAVE_LCD_BITMAP
1773 curr_vp
->vp
.font
= FONT_UI
;
1774 curr_vp
->vp
.drawmode
= DRMODE_SOLID
;
1777 if (display
->depth
> 1)
1779 curr_vp
->vp
.fg_pattern
= display
->get_foreground();
1780 curr_vp
->vp
.bg_pattern
= display
->get_background();
1785 return wps_parse(wps_data
, buf
, false);
1789 int fd
= open_utf8(buf
, O_RDONLY
);
1794 /* get buffer space from the plugin buffer */
1795 size_t buffersize
= 0;
1796 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1801 /* copy the file's content to the buffer for parsing,
1802 ensuring that every line ends with a newline char. */
1803 unsigned int start
= 0;
1804 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1806 start
+= strlen(wps_buffer
+ start
);
1807 if (start
< buffersize
- 1)
1809 wps_buffer
[start
++] = '\n';
1810 wps_buffer
[start
] = 0;
1819 #ifdef HAVE_LCD_BITMAP
1820 /* Set all filename pointers to NULL */
1821 memset(bmp_names
, 0, sizeof(bmp_names
));
1824 /* parse the WPS source */
1825 if (!wps_parse(wps_data
, wps_buffer
, true)) {
1826 skin_data_reset(wps_data
);
1830 wps_data
->wps_loaded
= true;
1832 #ifdef HAVE_LCD_BITMAP
1833 /* get the bitmap dir */
1834 char bmpdir
[MAX_PATH
];
1835 char *dot
= strrchr(buf
, '.');
1837 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1839 /* load the bitmaps that were found by the parsing */
1840 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
1841 skin_data_reset(wps_data
);
1845 #ifdef HAVE_ALBUMART
1846 status
= audio_status();
1847 if (status
& AUDIO_STATUS_PLAY
)
1849 struct skin_albumart
*aa
= wps_data
->albumart
;
1850 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
1852 (((old_aa
.height
!= aa
->height
) ||
1853 (old_aa
.width
!= aa
->width
))))))
1855 curtrack
= audio_current_track();
1856 offset
= curtrack
->offset
;
1858 if (!(status
& AUDIO_STATUS_PAUSE
))
1863 #if defined(DEBUG) || defined(SIMULATOR)