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 static void wps_reset(struct wps_data
*data
);
109 /* Function for parsing of details for a token. At the moment the
110 function is called, the token type has already been set. The
111 function must fill in the details and possibly add more tokens
112 to the token array. It should return the number of chars that
115 wps_bufptr points to the char following the tag (i.e. where
117 token is the pointer to the 'main' token being parsed
119 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
120 struct wps_token
*token
, struct wps_data
*wps_data
);
123 enum wps_token_type type
;
125 unsigned char refresh_type
;
126 const wps_tag_parse_func parse_func
;
128 static int skip_end_of_line(const char *wps_bufptr
);
129 /* prototypes of all special parse functions : */
130 static int parse_timeout(const char *wps_bufptr
,
131 struct wps_token
*token
, struct wps_data
*wps_data
);
132 static int parse_progressbar(const char *wps_bufptr
,
133 struct wps_token
*token
, struct wps_data
*wps_data
);
134 static int parse_dir_level(const char *wps_bufptr
,
135 struct wps_token
*token
, struct wps_data
*wps_data
);
136 static int parse_setting_and_lang(const char *wps_bufptr
,
137 struct wps_token
*token
, struct wps_data
*wps_data
);
139 #ifdef HAVE_LCD_BITMAP
140 static int parse_viewport_display(const char *wps_bufptr
,
141 struct wps_token
*token
, struct wps_data
*wps_data
);
142 static int parse_viewport(const char *wps_bufptr
,
143 struct wps_token
*token
, struct wps_data
*wps_data
);
144 static int parse_statusbar_enable(const char *wps_bufptr
,
145 struct wps_token
*token
, struct wps_data
*wps_data
);
146 static int parse_statusbar_disable(const char *wps_bufptr
,
147 struct wps_token
*token
, struct wps_data
*wps_data
);
148 static int parse_image_display(const char *wps_bufptr
,
149 struct wps_token
*token
, struct wps_data
*wps_data
);
150 static int parse_image_load(const char *wps_bufptr
,
151 struct wps_token
*token
, struct wps_data
*wps_data
);
152 #endif /*HAVE_LCD_BITMAP */
153 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
154 static int parse_image_special(const char *wps_bufptr
,
155 struct wps_token
*token
, struct wps_data
*wps_data
);
158 static int parse_albumart_load(const char *wps_bufptr
,
159 struct wps_token
*token
, struct wps_data
*wps_data
);
160 static int parse_albumart_display(const char *wps_bufptr
,
161 struct wps_token
*token
, struct wps_data
*wps_data
);
162 #endif /* HAVE_ALBUMART */
163 #ifdef HAVE_TOUCHSCREEN
164 static int parse_touchregion(const char *wps_bufptr
,
165 struct wps_token
*token
, struct wps_data
*wps_data
);
167 static int fulline_tag_not_supported(const char *wps_bufptr
,
168 struct wps_token
*token
, struct wps_data
*wps_data
)
170 (void)token
; (void)wps_data
;
171 return skip_end_of_line(wps_bufptr
);
173 #define parse_touchregion fulline_tag_not_supported
176 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
178 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
181 /* array of available tags - those with more characters have to go first
182 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
183 static const struct wps_tag all_tags
[] = {
185 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
186 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
187 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
189 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
190 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
191 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
192 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
193 #if CONFIG_CHARGING >= CHARGING_MONITOR
194 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
197 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
200 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
201 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
209 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
210 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
211 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
212 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
213 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
214 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
215 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
216 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
217 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
218 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
221 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
224 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
225 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
226 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
228 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
229 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
230 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
234 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
236 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
237 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
238 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
243 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
246 /* current metadata */
247 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
257 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
258 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
270 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
271 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
272 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
274 #if (CONFIG_CODEC != MAS3507D)
275 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
277 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
278 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
281 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
283 #ifdef HAS_REMOTE_BUTTON_HOLD
284 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
286 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
289 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
290 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
291 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
294 #ifdef HAVE_LCD_BITMAP
295 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
297 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
298 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
300 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
303 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
305 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
306 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
307 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
308 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
310 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
311 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
312 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
313 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
316 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
317 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
318 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
321 #if CONFIG_CODEC == SWCODEC
322 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
323 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
326 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
327 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
329 #ifdef HAVE_LCD_BITMAP
330 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
331 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
333 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
335 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
336 parse_image_display
},
338 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
340 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
341 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
, parse_albumart_display
},
344 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
345 parse_viewport_display
},
346 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
348 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
349 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
353 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
,
354 parse_setting_and_lang
},
355 { WPS_TOKEN_TRANSLATEDSTRING
, "Sx", WPS_REFRESH_STATIC
,
356 parse_setting_and_lang
},
358 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
359 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
361 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
362 /* the array MUST end with an empty string (first char is \0) */
366 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
367 * chains require the order to be kept.
369 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
375 struct skin_token_list
*t
= *list
;
381 /* create and init a new wpsll item.
382 * passing NULL to token will alloc a new one.
383 * You should only pass NULL for the token when the token type (table above)
384 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
386 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
389 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
391 token
= skin_buffer_alloc(sizeof(struct wps_token
));
392 if (!llitem
|| !token
)
395 llitem
->token
= token
;
397 llitem
->token
->value
.data
= token_data
;
401 /* Returns the number of chars that should be skipped to jump
402 immediately after the first eol, i.e. to the start of the next line */
403 static int skip_end_of_line(const char *wps_bufptr
)
407 while(*(wps_bufptr
+ skip
) != '\n')
412 /* Starts a new subline in the current line during parsing */
413 static bool skin_start_new_subline(struct skin_line
*line
, int curr_token
)
415 struct skin_subline
*subline
= skin_buffer_alloc(sizeof(struct skin_subline
));
419 subline
->first_token_idx
= curr_token
;
420 subline
->next
= NULL
;
422 subline
->line_type
= 0;
423 subline
->time_mult
= 0;
425 line
->curr_subline
->last_token_idx
= curr_token
-1;
426 line
->curr_subline
->next
= subline
;
427 line
->curr_subline
= subline
;
431 static bool skin_start_new_line(struct skin_viewport
*vp
, int curr_token
)
433 struct skin_line
*line
= skin_buffer_alloc(sizeof(struct skin_line
));
434 struct skin_subline
*subline
= NULL
;
438 /* init the subline */
439 subline
= &line
->sublines
;
440 subline
->first_token_idx
= curr_token
;
441 subline
->next
= NULL
;
442 subline
->line_type
= 0;
443 subline
->time_mult
= 0;
445 /* init the new line */
446 line
->curr_subline
= &line
->sublines
;
448 line
->subline_expire_time
= 0;
450 /* connect to curr_line and vp pointers.
451 * 1) close the previous lines subline
452 * 2) connect to vp pointer
453 * 3) connect to curr_line global pointer
457 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
458 curr_line
->next
= line
;
459 curr_line
->curr_subline
= NULL
;
468 #ifdef HAVE_LCD_BITMAP
470 static int parse_statusbar_enable(const char *wps_bufptr
,
471 struct wps_token
*token
,
472 struct wps_data
*wps_data
)
474 (void)token
; /* Kill warnings */
475 wps_data
->wps_sb_tag
= true;
476 wps_data
->show_sb_on_wps
= true;
477 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
478 if (default_vp
->vp
.y
== 0)
480 default_vp
->vp
.y
= STATUSBAR_HEIGHT
;
481 default_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
483 return skip_end_of_line(wps_bufptr
);
486 static int parse_statusbar_disable(const char *wps_bufptr
,
487 struct wps_token
*token
,
488 struct wps_data
*wps_data
)
490 (void)token
; /* Kill warnings */
491 wps_data
->wps_sb_tag
= true;
492 wps_data
->show_sb_on_wps
= false;
493 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
494 if (default_vp
->vp
.y
== STATUSBAR_HEIGHT
)
496 default_vp
->vp
.y
= 0;
497 default_vp
->vp
.height
+= STATUSBAR_HEIGHT
;
499 return skip_end_of_line(wps_bufptr
);
502 static int get_image_id(int c
)
504 if(c
>= 'a' && c
<= 'z')
506 else if(c
>= 'A' && c
<= 'Z')
512 static char *get_image_filename(const char *start
, const char* bmpdir
,
513 char *buf
, int buf_size
)
515 const char *end
= strchr(start
, '|');
517 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
523 int bmpdirlen
= strlen(bmpdir
);
526 buf
[bmpdirlen
] = '/';
527 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
528 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
533 static int parse_image_display(const char *wps_bufptr
,
534 struct wps_token
*token
,
535 struct wps_data
*wps_data
)
537 char label
= wps_bufptr
[0];
539 struct gui_img
*img
;;
542 img
= find_image(label
, wps_data
);
545 token
->value
.i
= label
; /* so debug works */
546 return WPS_ERROR_INVALID_PARAM
;
549 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
551 if (subimage
>= img
->num_subimages
)
552 return WPS_ERROR_INVALID_PARAM
;
554 /* Store sub-image number to display in high bits */
555 token
->value
.i
= label
| (subimage
<< 8);
556 return 2; /* We have consumed 2 bytes */
558 token
->value
.i
= label
;
559 return 1; /* We have consumed 1 byte */
563 static int parse_image_load(const char *wps_bufptr
,
564 struct wps_token
*token
,
565 struct wps_data
*wps_data
)
567 const char *ptr
= wps_bufptr
;
569 const char* filename
;
575 /* format: %x|n|filename.bmp|x|y|
576 or %xl|n|filename.bmp|x|y|
577 or %xl|n|filename.bmp|x|y|num_subimages|
581 return WPS_ERROR_INVALID_PARAM
;
585 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
586 return WPS_ERROR_INVALID_PARAM
;
588 /* Check there is a terminating | */
590 return WPS_ERROR_INVALID_PARAM
;
592 /* check the image number and load state */
593 if(find_image(*id
, wps_data
))
595 /* Invalid image ID */
596 return WPS_ERROR_INVALID_PARAM
;
598 img
= skin_buffer_alloc(sizeof(struct gui_img
));
600 return WPS_ERROR_INVALID_PARAM
;
601 /* save a pointer to the filename */
602 img
->bm
.data
= (char*)filename
;
606 img
->num_subimages
= 1;
607 img
->always_display
= false;
609 /* save current viewport */
610 img
->vp
= &curr_vp
->vp
;
612 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
614 img
->always_display
= true;
618 /* Parse the (optional) number of sub-images */
620 newline
= strchr(ptr
, '\n');
621 pos
= strchr(ptr
, '|');
622 if (pos
&& pos
< newline
)
623 img
->num_subimages
= atoi(ptr
);
625 if (img
->num_subimages
<= 0)
626 return WPS_ERROR_INVALID_PARAM
;
628 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
630 return WPS_ERROR_INVALID_PARAM
;
631 add_to_ll_chain(&wps_data
->images
, item
);
633 /* Skip the rest of the line */
634 return skip_end_of_line(wps_bufptr
);
637 static int parse_viewport_display(const char *wps_bufptr
,
638 struct wps_token
*token
,
639 struct wps_data
*wps_data
)
642 char letter
= wps_bufptr
[0];
644 if (letter
< 'a' || letter
> 'z')
646 /* invalid viewport tag */
647 return WPS_ERROR_INVALID_PARAM
;
649 token
->value
.i
= letter
;
653 static int parse_viewport(const char *wps_bufptr
,
654 struct wps_token
*token
,
655 struct wps_data
*wps_data
)
657 (void)token
; /* Kill warnings */
658 const char *ptr
= wps_bufptr
;
661 #ifdef HAVE_REMOTE_LCD
662 wps_data
->remote_wps
? SCREEN_REMOTE
:
666 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
668 /* check for the optional letter to signify its a hideable viewport */
669 /* %Vl|<label>|<rest of tags>| */
670 skin_vp
->hidden_flags
= 0;
671 skin_vp
->label
= VP_NO_LABEL
;
673 skin_vp
->lines
= NULL
;
676 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
677 - (wps_data
->num_tokens
> 0 ? 1 : 0);
681 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
682 return WPS_ERROR_INVALID_PARAM
;
688 char label
= *(ptr
+2);
689 if (label
>= 'a' && label
<= 'z')
691 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
692 skin_vp
->label
= label
;
695 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
700 return WPS_ERROR_INVALID_PARAM
;
703 struct viewport
*vp
= &skin_vp
->vp
;
704 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
706 if (!(ptr
= viewport_parse_viewport(vp
, screen
, ptr
, '|')))
707 return WPS_ERROR_INVALID_PARAM
;
709 /* Check for trailing | */
711 return WPS_ERROR_INVALID_PARAM
;
714 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
716 return WPS_ERROR_INVALID_PARAM
;
717 add_to_ll_chain(&wps_data
->viewports
, list
);
719 /* Skip the rest of the line */
720 return skip_end_of_line(wps_bufptr
);
723 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
724 static int parse_image_special(const char *wps_bufptr
,
725 struct wps_token
*token
,
726 struct wps_data
*wps_data
)
728 (void)wps_data
; /* kill warning */
730 const char *pos
= NULL
;
733 pos
= strchr(wps_bufptr
+ 1, '|');
734 newline
= strchr(wps_bufptr
, '\n');
737 return WPS_ERROR_INVALID_PARAM
;
739 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
741 /* format: %X|filename.bmp| */
742 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
746 /* Skip the rest of the line */
747 return skip_end_of_line(wps_bufptr
);
751 #endif /* HAVE_LCD_BITMAP */
753 static int parse_setting_and_lang(const char *wps_bufptr
,
754 struct wps_token
*token
,
755 struct wps_data
*wps_data
)
757 /* NOTE: both the string validations that happen in here will
758 * automatically PASS on checkwps because its too hard to get
759 * settings_list.c and englinsh.lang built for it.
760 * If that ever changes remove the #ifndef __PCTOOL__'s here
763 const char *ptr
= wps_bufptr
;
768 /* Find the setting's cfg_name */
770 return WPS_ERROR_INVALID_PARAM
;
772 end
= strchr(ptr
,'|');
774 return WPS_ERROR_INVALID_PARAM
;
775 strlcpy(temp
, ptr
,end
-ptr
+1);
777 if (token
->type
== WPS_TOKEN_TRANSLATEDSTRING
)
780 i
= lang_english_to_id(temp
);
782 return WPS_ERROR_INVALID_PARAM
;
787 /* Find the setting */
788 for (i
=0; i
<nb_settings
; i
++)
789 if (settings
[i
].cfg_name
&&
790 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
791 /* prevent matches on cfg_name prefixes */
792 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
795 if (i
== nb_settings
)
796 return WPS_ERROR_INVALID_PARAM
;
799 /* Store the setting number */
802 /* Skip the rest of the line */
807 static int parse_dir_level(const char *wps_bufptr
,
808 struct wps_token
*token
,
809 struct wps_data
*wps_data
)
811 char val
[] = { *wps_bufptr
, '\0' };
812 token
->value
.i
= atoi(val
);
813 (void)wps_data
; /* Kill warnings */
817 static int parse_timeout(const char *wps_bufptr
,
818 struct wps_token
*token
,
819 struct wps_data
*wps_data
)
823 bool have_point
= false;
824 bool have_tenth
= false;
826 (void)wps_data
; /* Kill the warning */
828 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
830 if (*wps_bufptr
!= '.')
833 val
+= *wps_bufptr
- '0';
849 if (have_tenth
== false)
852 if (val
== 0 && skip
== 0)
854 /* decide what to do if no value was specified */
857 case WPS_TOKEN_SUBLINE_TIMEOUT
:
859 case WPS_TOKEN_BUTTON_VOLUME
:
864 token
->value
.i
= val
;
869 static int parse_progressbar(const char *wps_bufptr
,
870 struct wps_token
*token
,
871 struct wps_data
*wps_data
)
873 /* %pb or %pb|filename|x|y|width|height|
874 using - for any of the params uses "sane" values */
875 #ifdef HAVE_LCD_BITMAP
883 const char *filename
;
884 int x
, y
, height
, width
;
886 const char *ptr
= wps_bufptr
;
887 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
888 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
891 return WPS_ERROR_INVALID_PARAM
;
893 struct viewport
*vp
= &curr_vp
->vp
;
895 int font_height
= font_get(vp
->font
)->height
;
899 /* we need to know what line number (viewport relative) this pb is,
900 * so count them... */
902 struct skin_line
*line
= curr_vp
->lines
;
908 pb
->have_bitmap_pb
= false;
909 pb
->bm
.data
= NULL
; /* no bitmap specified */
911 if (*wps_bufptr
!= '|') /* regular old style */
914 pb
->width
= vp
->width
;
915 pb
->height
= SYSFONT_HEIGHT
-2;
916 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
919 add_to_ll_chain(&wps_data
->progressbars
, item
);
922 ptr
= wps_bufptr
+ 1;
924 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
925 &x
, &y
, &width
, &height
)))
926 return WPS_ERROR_INVALID_PARAM
;
928 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
929 pb
->bm
.data
= (char*)filename
;
931 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
936 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
938 /* A zero width causes a divide-by-zero error later, so reject it */
940 return WPS_ERROR_INVALID_PARAM
;
945 pb
->width
= vp
->width
- pb
->x
;
947 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
949 /* A zero height makes no sense - reject it */
951 return WPS_ERROR_INVALID_PARAM
;
956 pb
->height
= font_height
;
958 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
961 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
964 add_to_ll_chain(&wps_data
->progressbars
, item
);
966 /* Skip the rest of the line */
967 return skip_end_of_line(wps_bufptr
)-1;
971 if (*(wps_bufptr
-1) == 'f')
972 wps_data
->full_line_progressbar
= true;
974 wps_data
->full_line_progressbar
= false;
982 static int parse_albumart_load(const char *wps_bufptr
,
983 struct wps_token
*token
,
984 struct wps_data
*wps_data
)
986 const char *_pos
, *newline
;
988 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
989 (void)token
; /* silence warning */
991 return skip_end_of_line(wps_bufptr
);
993 /* reset albumart info in wps */
996 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
997 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
998 aa
->vp
= &curr_vp
->vp
;
1000 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1002 newline
= strchr(wps_bufptr
, '\n');
1004 /* initial validation and parsing of x and y components */
1005 if (*wps_bufptr
!= '|')
1006 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
1008 _pos
= wps_bufptr
+ 1;
1009 _pos
= parse_list("dd", NULL
, '|', _pos
, &aa
->x
, &aa
->y
);
1011 if (!_pos
|| _pos
> newline
|| *_pos
!= '|')
1012 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
1015 /* parsing width field */
1019 /* apply each modifier in turn */
1026 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1030 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1035 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1043 /* simply ignored */
1050 /* extract max width data */
1053 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
1054 return WPS_ERROR_INVALID_PARAM
;
1056 aa
->width
= atoi(_pos
);
1058 _pos
= strchr(_pos
, '|');
1059 if (!_pos
|| _pos
> newline
)
1060 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
1061 e.g. %Cl|7|59|200\n */
1064 /* parsing height field */
1068 /* apply each modifier in turn */
1075 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1079 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1084 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1092 /* simply ignored */
1099 /* extract max height data */
1102 if (!isdigit(*_pos
))
1103 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1105 aa
->height
= atoi(_pos
);
1107 _pos
= strchr(_pos
, '|');
1108 if (!_pos
|| _pos
> newline
)
1109 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1110 e.g. %Cl|7|59|200|200\n */
1113 /* if we got here, we parsed everything ok .. ! */
1116 else if (aa
->width
> LCD_WIDTH
)
1117 aa
->width
= LCD_WIDTH
;
1121 else if (aa
->height
> LCD_HEIGHT
)
1122 aa
->height
= LCD_HEIGHT
;
1124 aa
->state
= WPS_ALBUMART_LOAD
;
1126 wps_data
->albumart
= aa
;
1128 /* Skip the rest of the line */
1129 return skip_end_of_line(wps_bufptr
);
1132 static int parse_albumart_display(const char *wps_bufptr
,
1133 struct wps_token
*token
,
1134 struct wps_data
*wps_data
)
1137 struct wps_token
*prev
= token
-1;
1138 if ((wps_data
->num_tokens
> 1) && (prev
->type
== WPS_TOKEN_CONDITIONAL
))
1140 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1142 else if (wps_data
->albumart
)
1144 wps_data
->albumart
->vp
= &curr_vp
->vp
;
1147 /* the old code did this so keep it here for now...
1148 * this is to allow the posibility to showing the next tracks AA! */
1149 if (wps_bufptr
+1 == 'n')
1154 #endif /* HAVE_ALBUMART */
1156 #ifdef HAVE_TOUCHSCREEN
1158 struct touchaction
{char* s
; int action
;};
1159 static struct touchaction touchactions
[] = {
1160 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1161 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1162 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1163 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1164 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1165 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1166 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1168 static int parse_touchregion(const char *wps_bufptr
,
1169 struct wps_token
*token
, struct wps_data
*wps_data
)
1173 struct touchregion
*region
= NULL
;
1174 const char *ptr
= wps_bufptr
;
1176 const char pb_string
[] = "progressbar";
1177 const char vol_string
[] = "volume";
1180 /* format: %T|x|y|width|height|action|
1181 * if action starts with & the area must be held to happen
1183 * play - play/pause playback
1184 * stop - stop playback, exit the wps
1187 * ffwd - seek forward
1188 * rwd - seek backwards
1189 * menu - go back to the main menu
1190 * browse - go back to the file/db browser
1191 * shuffle - toggle shuffle mode
1192 * repmode - cycle the repeat mode
1193 * quickscreen - go into the quickscreen
1194 * contextmenu - open the context menu
1195 * playlist - go into the playlist
1196 * pitch - go into the pitchscreen
1201 return WPS_ERROR_INVALID_PARAM
;
1204 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1205 return WPS_ERROR_INVALID_PARAM
;
1207 /* Check there is a terminating | */
1209 return WPS_ERROR_INVALID_PARAM
;
1211 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1213 return WPS_ERROR_INVALID_PARAM
;
1215 /* should probably do some bounds checking here with the viewport... but later */
1216 region
->action
= ACTION_NONE
;
1221 region
->wvp
= curr_vp
;
1223 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1224 && *(action
+ sizeof(pb_string
)-1) == '|')
1225 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1226 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1227 && *(action
+ sizeof(vol_string
)-1) == '|')
1228 region
->type
= WPS_TOUCHREGION_VOLUME
;
1231 region
->type
= WPS_TOUCHREGION_ACTION
;
1236 region
->repeat
= true;
1239 region
->repeat
= false;
1242 imax
= ARRAYLEN(touchactions
);
1243 while ((region
->action
== ACTION_NONE
) &&
1246 /* try to match with one of our touchregion screens */
1247 int len
= strlen(touchactions
[i
].s
);
1248 if (!strncmp(touchactions
[i
].s
, action
, len
)
1249 && *(action
+len
) == '|')
1250 region
->action
= touchactions
[i
].action
;
1253 if (region
->action
== ACTION_NONE
)
1254 return WPS_ERROR_INVALID_PARAM
;
1256 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1258 return WPS_ERROR_INVALID_PARAM
;
1259 add_to_ll_chain(&wps_data
->touchregions
, item
);
1260 return skip_end_of_line(wps_bufptr
);
1264 /* Parse a generic token from the given string. Return the length read */
1265 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1267 int skip
= 0, taglen
= 0, ret
;
1268 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1269 const struct wps_tag
*tag
;
1270 memset(token
, 0, sizeof(*token
));
1281 /* escaped characters */
1282 token
->type
= WPS_TOKEN_CHARACTER
;
1283 token
->value
.c
= *wps_bufptr
;
1285 wps_data
->num_tokens
++;
1289 /* conditional tag */
1290 token
->type
= WPS_TOKEN_CONDITIONAL
;
1292 condindex
[level
] = wps_data
->num_tokens
;
1293 numoptions
[level
] = 1;
1294 wps_data
->num_tokens
++;
1295 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1296 if (ret
< 0) return ret
;
1301 /* find what tag we have */
1302 for (tag
= all_tags
;
1303 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1306 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1307 token
->type
= tag
->type
;
1308 curr_line
->curr_subline
->line_type
|= tag
->refresh_type
;
1310 /* if the tag has a special parsing function, we call it */
1311 if (tag
->parse_func
)
1313 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1314 if (ret
< 0) return ret
;
1318 /* Some tags we don't want to save as tokens */
1319 if (tag
->type
== WPS_NO_TOKEN
)
1322 /* tags that start with 'F', 'I' or 'D' are for the next file */
1323 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1324 *(tag
->name
) == 'D')
1327 wps_data
->num_tokens
++;
1336 data is the pointer to the structure where the parsed WPS should be stored.
1338 wps_bufptr points to the string containing the WPS tags */
1339 #define TOKEN_BLOCK_SIZE 128
1340 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1342 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1344 enum wps_parse_error fail
= PARSE_OK
;
1346 int max_tokens
= TOKEN_BLOCK_SIZE
;
1347 size_t buf_free
= 0;
1351 /* allocate enough RAM for a reasonable skin, grow as needed.
1352 * Free any used RAM before loading the images to be 100% RAM efficient */
1353 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1354 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1356 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1357 data
->num_tokens
= 0;
1359 while (*wps_bufptr
&& !fail
)
1361 /* first make sure there is enough room for tokens */
1362 if (max_tokens
<= data
->num_tokens
+ 5)
1364 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1365 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1366 /* do some smarts here to grow the array a bit */
1367 if (skin_buffer_freespace() < needed
)
1369 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1372 skin_buffer_increment(needed
, false);
1373 max_tokens
+= extra_tokens
;
1376 switch(*wps_bufptr
++)
1381 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1383 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1386 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1388 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1394 /* Alternating sublines separator */
1396 if (level
>= 0) /* there are unclosed conditionals */
1398 fail
= PARSE_FAIL_UNCLOSED_COND
;
1402 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1403 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1407 /* Conditional list start */
1409 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1411 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1415 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1416 lastcond
[level
] = data
->num_tokens
++;
1419 /* Conditional list end */
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_END
;
1428 if (lastcond
[level
])
1429 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1432 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1436 lastcond
[level
] = 0;
1438 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1442 /* Conditional list option */
1444 if (level
< 0) /* not in a conditional, invalid char */
1446 fail
= PARSE_FAIL_INVALID_CHAR
;
1450 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1451 if (lastcond
[level
])
1452 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1455 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1459 lastcond
[level
] = data
->num_tokens
;
1460 numoptions
[level
]++;
1466 if (level
>= 0) /* there are unclosed conditionals */
1468 fail
= PARSE_FAIL_UNCLOSED_COND
;
1472 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1475 /* End of this line */
1477 if (level
>= 0) /* there are unclosed conditionals */
1479 fail
= PARSE_FAIL_UNCLOSED_COND
;
1482 /* add a new token for the \n so empty lines are correct */
1483 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CHARACTER
;
1484 data
->tokens
[data
->num_tokens
].value
.c
= '\n';
1485 data
->tokens
[data
->num_tokens
].next
= false;
1488 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1490 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1499 unsigned int len
= 1;
1500 const char *string_start
= wps_bufptr
- 1;
1502 /* find the length of the string */
1503 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1504 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1505 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1506 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1512 /* look if we already have that string */
1515 struct skin_token_list
*list
= data
->strings
;
1518 str
= (char*)list
->token
->value
.data
;
1519 found
= (strlen(str
) == len
&&
1520 strncmp(string_start
, str
, len
) == 0);
1522 break; /* break here because the list item is
1523 used if its found */
1526 /* If a matching string is found, found is true and i is
1527 the index of the string. If not, found is false */
1532 str
= (char*)skin_buffer_alloc(len
+1);
1535 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1538 strlcpy(str
, string_start
, len
+1);
1539 struct skin_token_list
*item
=
1540 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1543 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1546 add_to_ll_chain(&data
->strings
, item
);
1550 /* another occurrence of an existing string */
1551 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1553 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1560 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1561 fail
= PARSE_FAIL_UNCLOSED_COND
;
1563 if (*wps_bufptr
&& !fail
)
1564 /* one of the limits of the while loop was exceeded */
1565 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1568 curr_line
->curr_subline
->last_token_idx
= data
->num_tokens
;
1569 data
->tokens
[data
->num_tokens
++].type
= WPS_NO_TOKEN
;
1570 /* freeup unused tokens */
1571 skin_buffer_free_from_front(sizeof(struct wps_token
)
1572 * (max_tokens
- data
->num_tokens
));
1574 #if defined(DEBUG) || defined(SIMULATOR)
1576 print_debug_info(data
, fail
, line_number
);
1584 static void wps_reset(struct wps_data
*data
)
1586 #ifdef HAVE_REMOTE_LCD
1587 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1589 memset(data
, 0, sizeof(*data
));
1590 skin_data_init(data
);
1591 #ifdef HAVE_REMOTE_LCD
1592 data
->remote_wps
= rwps
;
1596 #ifdef HAVE_LCD_BITMAP
1597 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1599 (void)wps_data
; /* only needed for remote targets */
1600 bool loaded
= false;
1601 char img_path
[MAX_PATH
];
1602 get_image_filename(bitmap
->data
, bmpdir
,
1603 img_path
, sizeof(img_path
));
1605 /* load the image */
1607 #ifdef HAVE_REMOTE_LCD
1608 if (wps_data
->remote_wps
)
1609 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1612 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1615 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
1616 bitmap
->data
= imgbuf
;
1617 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
1621 skin_buffer_increment(ret
, true);
1626 /* Abort if we can't load an image */
1627 DEBUGF("ERR: Failed to load image - %s\n",img_path
);
1633 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1635 struct skin_token_list
*list
;
1636 /* do the progressbars */
1637 list
= wps_data
->progressbars
;
1640 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
1643 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
1647 /* regular images */
1648 list
= wps_data
->images
;
1651 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1654 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1656 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
1661 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1662 if (bmp_names
[BACKDROP_BMP
])
1664 int screen
= SCREEN_MAIN
;
1665 char img_path
[MAX_PATH
];
1666 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1667 img_path
, sizeof(img_path
));
1668 #if defined(HAVE_REMOTE_LCD)
1669 /* We only need to check LCD type if there is a remote LCD */
1670 if (wps_data
->remote_wps
)
1671 screen
= SCREEN_REMOTE
;
1673 screens
[screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1675 #endif /* has backdrop support */
1677 /* If we got here, everything was OK */
1681 #endif /* HAVE_LCD_BITMAP */
1683 /* to setup up the wps-data from a format-buffer (isfile = false)
1684 from a (wps-)file (isfile = true)*/
1685 bool skin_data_load(struct wps_data
*wps_data
,
1686 struct screen
*display
,
1691 if (!wps_data
|| !buf
)
1693 #ifdef HAVE_ALBUMART
1695 struct mp3entry
*curtrack
;
1697 struct skin_albumart old_aa
= {.state
= WPS_ALBUMART_NONE
};
1698 if (wps_data
->albumart
)
1700 old_aa
.state
= wps_data
->albumart
->state
;
1701 old_aa
.height
= wps_data
->albumart
->height
;
1702 old_aa
.width
= wps_data
->albumart
->width
;
1706 wps_reset(wps_data
);
1708 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
1711 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
1714 add_to_ll_chain(&wps_data
->viewports
, list
);
1717 /* Initialise the first (default) viewport */
1718 curr_vp
->label
= VP_DEFAULT_LABEL
;
1720 curr_vp
->vp
.width
= display
->getwidth();
1721 curr_vp
->vp
.height
= display
->getheight();
1723 curr_vp
->hidden_flags
= 0;
1724 curr_vp
->lines
= NULL
;
1727 if (!skin_start_new_line(curr_vp
, 0))
1730 switch (statusbar_position(display
->screen_type
))
1736 curr_vp
->vp
.y
= STATUSBAR_HEIGHT
;
1737 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1739 case STATUSBAR_BOTTOM
:
1741 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1744 #ifdef HAVE_LCD_BITMAP
1745 curr_vp
->vp
.font
= FONT_UI
;
1746 curr_vp
->vp
.drawmode
= DRMODE_SOLID
;
1749 if (display
->depth
> 1)
1751 curr_vp
->vp
.fg_pattern
= display
->get_foreground();
1752 curr_vp
->vp
.bg_pattern
= display
->get_background();
1757 return wps_parse(wps_data
, buf
, false);
1761 int fd
= open_utf8(buf
, O_RDONLY
);
1766 /* get buffer space from the plugin buffer */
1767 size_t buffersize
= 0;
1768 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1773 /* copy the file's content to the buffer for parsing,
1774 ensuring that every line ends with a newline char. */
1775 unsigned int start
= 0;
1776 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1778 start
+= strlen(wps_buffer
+ start
);
1779 if (start
< buffersize
- 1)
1781 wps_buffer
[start
++] = '\n';
1782 wps_buffer
[start
] = 0;
1791 #ifdef HAVE_LCD_BITMAP
1792 /* Set all filename pointers to NULL */
1793 memset(bmp_names
, 0, sizeof(bmp_names
));
1796 /* parse the WPS source */
1797 if (!wps_parse(wps_data
, wps_buffer
, true)) {
1798 wps_reset(wps_data
);
1802 wps_data
->wps_loaded
= true;
1804 #ifdef HAVE_LCD_BITMAP
1805 /* get the bitmap dir */
1806 char bmpdir
[MAX_PATH
];
1807 char *dot
= strrchr(buf
, '.');
1809 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1811 /* load the bitmaps that were found by the parsing */
1812 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
1813 wps_reset(wps_data
);
1817 #ifdef HAVE_ALBUMART
1818 status
= audio_status();
1819 if (status
& AUDIO_STATUS_PLAY
)
1821 struct skin_albumart
*aa
= wps_data
->albumart
;
1822 if (aa
&& ((aa
->state
&& !old_aa
.state
) ||
1824 (((old_aa
.height
!= aa
->height
) ||
1825 (old_aa
.width
!= aa
->width
))))))
1827 curtrack
= audio_current_track();
1828 offset
= curtrack
->offset
;
1830 if (!(status
& AUDIO_STATUS_PAUSE
))
1835 #if defined(DEBUG) || defined(SIMULATOR)