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 ****************************************************************************/
47 #include "wps_internals.h"
48 #include "skin_engine.h"
50 #include "settings_list.h"
52 #ifdef HAVE_LCD_BITMAP
58 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
59 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
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 /* the current line in the file */
80 /* the current viewport */
81 static struct skin_viewport
*curr_vp
;
83 #ifdef HAVE_LCD_BITMAP
86 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
88 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
91 #define PROGRESSBAR_BMP MAX_IMAGES
92 #define BACKDROP_BMP (MAX_BITMAPS-1)
94 /* pointers to the bitmap filenames in the WPS source */
95 static const char *bmp_names
[MAX_BITMAPS
];
97 #endif /* HAVE_LCD_BITMAP */
99 #if defined(DEBUG) || defined(SIMULATOR)
100 /* debugging function */
101 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
104 static void wps_reset(struct wps_data
*data
);
106 /* Function for parsing of details for a token. At the moment the
107 function is called, the token type has already been set. The
108 function must fill in the details and possibly add more tokens
109 to the token array. It should return the number of chars that
112 wps_bufptr points to the char following the tag (i.e. where
114 token is the pointer to the 'main' token being parsed
116 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
117 struct wps_token
*token
, struct wps_data
*wps_data
);
120 enum wps_token_type type
;
122 unsigned char refresh_type
;
123 const wps_tag_parse_func parse_func
;
125 static int skip_end_of_line(const char *wps_bufptr
);
126 /* prototypes of all special parse functions : */
127 static int parse_timeout(const char *wps_bufptr
,
128 struct wps_token
*token
, struct wps_data
*wps_data
);
129 static int parse_progressbar(const char *wps_bufptr
,
130 struct wps_token
*token
, struct wps_data
*wps_data
);
131 static int parse_dir_level(const char *wps_bufptr
,
132 struct wps_token
*token
, struct wps_data
*wps_data
);
133 static int parse_setting(const char *wps_bufptr
,
134 struct wps_token
*token
, struct wps_data
*wps_data
);
136 #ifdef HAVE_LCD_BITMAP
137 static int parse_viewport_display(const char *wps_bufptr
,
138 struct wps_token
*token
, struct wps_data
*wps_data
);
139 static int parse_viewport(const char *wps_bufptr
,
140 struct wps_token
*token
, struct wps_data
*wps_data
);
141 static int parse_statusbar_enable(const char *wps_bufptr
,
142 struct wps_token
*token
, struct wps_data
*wps_data
);
143 static int parse_statusbar_disable(const char *wps_bufptr
,
144 struct wps_token
*token
, struct wps_data
*wps_data
);
145 static int parse_image_display(const char *wps_bufptr
,
146 struct wps_token
*token
, struct wps_data
*wps_data
);
147 static int parse_image_load(const char *wps_bufptr
,
148 struct wps_token
*token
, struct wps_data
*wps_data
);
149 #endif /*HAVE_LCD_BITMAP */
150 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
151 static int parse_image_special(const char *wps_bufptr
,
152 struct wps_token
*token
, struct wps_data
*wps_data
);
155 static int parse_albumart_load(const char *wps_bufptr
,
156 struct wps_token
*token
, struct wps_data
*wps_data
);
157 static int parse_albumart_conditional(const char *wps_bufptr
,
158 struct wps_token
*token
, struct wps_data
*wps_data
);
159 #endif /* HAVE_ALBUMART */
160 #ifdef HAVE_TOUCHSCREEN
161 static int parse_touchregion(const char *wps_bufptr
,
162 struct wps_token
*token
, struct wps_data
*wps_data
);
164 static int fulline_tag_not_supported(const char *wps_bufptr
,
165 struct wps_token
*token
, struct wps_data
*wps_data
)
167 (void)token
; (void)wps_data
;
168 return skip_end_of_line(wps_bufptr
);
170 #define parse_touchregion fulline_tag_not_supported
173 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
175 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
178 /* array of available tags - those with more characters have to go first
179 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
180 static const struct wps_tag all_tags
[] = {
182 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
183 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
184 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
186 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
187 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
188 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
189 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
190 #if CONFIG_CHARGING >= CHARGING_MONITOR
191 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
194 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
197 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
198 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
199 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
200 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
209 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
210 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
211 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
212 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
213 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
214 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
215 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
218 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
219 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
224 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
225 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
226 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
231 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
232 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
233 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
234 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
236 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
237 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
238 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
243 /* current metadata */
244 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
245 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
258 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
271 #if (CONFIG_CODEC != MAS3507D)
272 { 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
,
340 parse_albumart_conditional
},
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 void wps_start_new_subline(struct wps_data
*data
)
411 data
->num_sublines
++;
412 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
413 data
->lines
[data
->num_lines
].num_sublines
++;
416 #ifdef HAVE_LCD_BITMAP
418 static int parse_statusbar_enable(const char *wps_bufptr
,
419 struct wps_token
*token
,
420 struct wps_data
*wps_data
)
422 (void)token
; /* Kill warnings */
423 wps_data
->wps_sb_tag
= true;
424 wps_data
->show_sb_on_wps
= true;
425 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
426 if (default_vp
->vp
.y
== 0)
428 default_vp
->vp
.y
= STATUSBAR_HEIGHT
;
429 default_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
431 return skip_end_of_line(wps_bufptr
);
434 static int parse_statusbar_disable(const char *wps_bufptr
,
435 struct wps_token
*token
,
436 struct wps_data
*wps_data
)
438 (void)token
; /* Kill warnings */
439 wps_data
->wps_sb_tag
= true;
440 wps_data
->show_sb_on_wps
= false;
441 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
442 if (default_vp
->vp
.y
== STATUSBAR_HEIGHT
)
444 default_vp
->vp
.y
= 0;
445 default_vp
->vp
.height
+= STATUSBAR_HEIGHT
;
447 return skip_end_of_line(wps_bufptr
);
450 static int get_image_id(int c
)
452 if(c
>= 'a' && c
<= 'z')
454 else if(c
>= 'A' && c
<= 'Z')
460 static char *get_image_filename(const char *start
, const char* bmpdir
,
461 char *buf
, int buf_size
)
463 const char *end
= strchr(start
, '|');
465 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
471 int bmpdirlen
= strlen(bmpdir
);
474 buf
[bmpdirlen
] = '/';
475 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
476 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
481 static int parse_image_display(const char *wps_bufptr
,
482 struct wps_token
*token
,
483 struct wps_data
*wps_data
)
485 char label
= wps_bufptr
[0];
487 struct gui_img
*img
;;
490 img
= find_image(label
, wps_data
);
493 token
->value
.i
= label
; /* do debug works */
494 return WPS_ERROR_INVALID_PARAM
;
497 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
499 if (subimage
>= img
->num_subimages
)
500 return WPS_ERROR_INVALID_PARAM
;
502 /* Store sub-image number to display in high bits */
503 token
->value
.i
= label
| (subimage
<< 8);
504 return 2; /* We have consumed 2 bytes */
506 token
->value
.i
= label
;
507 return 1; /* We have consumed 1 byte */
511 static int parse_image_load(const char *wps_bufptr
,
512 struct wps_token
*token
,
513 struct wps_data
*wps_data
)
515 const char *ptr
= wps_bufptr
;
517 const char* filename
;
523 /* format: %x|n|filename.bmp|x|y|
524 or %xl|n|filename.bmp|x|y|
525 or %xl|n|filename.bmp|x|y|num_subimages|
529 return WPS_ERROR_INVALID_PARAM
;
533 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
534 return WPS_ERROR_INVALID_PARAM
;
536 /* Check there is a terminating | */
538 return WPS_ERROR_INVALID_PARAM
;
540 /* check the image number and load state */
541 if(find_image(*id
, wps_data
))
543 /* Invalid image ID */
544 return WPS_ERROR_INVALID_PARAM
;
546 img
= skin_buffer_alloc(sizeof(struct gui_img
));
548 return WPS_ERROR_INVALID_PARAM
;
549 /* save a pointer to the filename */
550 img
->bm
.data
= (char*)filename
;
554 img
->num_subimages
= 1;
555 img
->always_display
= false;
557 /* save current viewport */
558 img
->vp
= &curr_vp
->vp
;
560 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
562 img
->always_display
= true;
566 /* Parse the (optional) number of sub-images */
568 newline
= strchr(ptr
, '\n');
569 pos
= strchr(ptr
, '|');
570 if (pos
&& pos
< newline
)
571 img
->num_subimages
= atoi(ptr
);
573 if (img
->num_subimages
<= 0)
574 return WPS_ERROR_INVALID_PARAM
;
576 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
578 return WPS_ERROR_INVALID_PARAM
;
579 add_to_ll_chain(&wps_data
->images
, item
);
581 /* Skip the rest of the line */
582 return skip_end_of_line(wps_bufptr
);
585 static int parse_viewport_display(const char *wps_bufptr
,
586 struct wps_token
*token
,
587 struct wps_data
*wps_data
)
590 char letter
= wps_bufptr
[0];
592 if (letter
< 'a' || letter
> 'z')
594 /* invalid viewport tag */
595 return WPS_ERROR_INVALID_PARAM
;
597 token
->value
.i
= letter
;
601 static int parse_viewport(const char *wps_bufptr
,
602 struct wps_token
*token
,
603 struct wps_data
*wps_data
)
605 (void)token
; /* Kill warnings */
606 const char *ptr
= wps_bufptr
;
609 #ifdef HAVE_REMOTE_LCD
610 wps_data
->remote_wps
? SCREEN_REMOTE
:
614 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
616 /* check for the optional letter to signify its a hideable viewport */
617 /* %Vl|<label>|<rest of tags>| */
618 skin_vp
->hidden_flags
= 0;
619 skin_vp
->label
= VP_NO_LABEL
;
626 char label
= *(ptr
+2);
627 if (label
>= 'a' && label
<= 'z')
629 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
630 skin_vp
->label
= label
;
633 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
638 return WPS_ERROR_INVALID_PARAM
;
641 struct viewport
*vp
= &skin_vp
->vp
;
642 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
644 if (!(ptr
= viewport_parse_viewport(vp
, screen
, ptr
, '|')))
645 return WPS_ERROR_INVALID_PARAM
;
647 /* Check for trailing | */
649 return WPS_ERROR_INVALID_PARAM
;
651 curr_vp
->last_line
= wps_data
->num_lines
- 1;
653 skin_vp
->first_line
= wps_data
->num_lines
;
655 if (wps_data
->num_sublines
< WPS_MAX_SUBLINES
)
657 wps_data
->lines
[wps_data
->num_lines
].first_subline_idx
=
658 wps_data
->num_sublines
;
660 wps_data
->sublines
[wps_data
->num_sublines
].first_token_idx
=
661 wps_data
->num_tokens
;
664 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
666 return WPS_ERROR_INVALID_PARAM
;
667 add_to_ll_chain(&wps_data
->viewports
, list
);
669 /* Skip the rest of the line */
670 return skip_end_of_line(wps_bufptr
);
673 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
674 static int parse_image_special(const char *wps_bufptr
,
675 struct wps_token
*token
,
676 struct wps_data
*wps_data
)
678 (void)wps_data
; /* kill warning */
680 const char *pos
= NULL
;
683 pos
= strchr(wps_bufptr
+ 1, '|');
684 newline
= strchr(wps_bufptr
, '\n');
687 return WPS_ERROR_INVALID_PARAM
;
689 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
691 /* format: %X|filename.bmp| */
692 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
696 /* Skip the rest of the line */
697 return skip_end_of_line(wps_bufptr
);
701 #endif /* HAVE_LCD_BITMAP */
703 static int parse_setting(const char *wps_bufptr
,
704 struct wps_token
*token
,
705 struct wps_data
*wps_data
)
708 const char *ptr
= wps_bufptr
;
712 /* Find the setting's cfg_name */
714 return WPS_ERROR_INVALID_PARAM
;
716 end
= strchr(ptr
,'|');
718 return WPS_ERROR_INVALID_PARAM
;
720 /* Find the setting */
721 for (i
=0; i
<nb_settings
; i
++)
722 if (settings
[i
].cfg_name
&&
723 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
724 /* prevent matches on cfg_name prefixes */
725 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
727 if (i
== nb_settings
)
728 return WPS_ERROR_INVALID_PARAM
;
730 /* Store the setting number */
733 /* Skip the rest of the line */
738 static int parse_dir_level(const char *wps_bufptr
,
739 struct wps_token
*token
,
740 struct wps_data
*wps_data
)
742 char val
[] = { *wps_bufptr
, '\0' };
743 token
->value
.i
= atoi(val
);
744 (void)wps_data
; /* Kill warnings */
748 static int parse_timeout(const char *wps_bufptr
,
749 struct wps_token
*token
,
750 struct wps_data
*wps_data
)
754 bool have_point
= false;
755 bool have_tenth
= false;
757 (void)wps_data
; /* Kill the warning */
759 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
761 if (*wps_bufptr
!= '.')
764 val
+= *wps_bufptr
- '0';
780 if (have_tenth
== false)
783 if (val
== 0 && skip
== 0)
785 /* decide what to do if no value was specified */
788 case WPS_TOKEN_SUBLINE_TIMEOUT
:
790 case WPS_TOKEN_BUTTON_VOLUME
:
795 token
->value
.i
= val
;
800 static int parse_progressbar(const char *wps_bufptr
,
801 struct wps_token
*token
,
802 struct wps_data
*wps_data
)
804 /* %pb or %pb|filename|x|y|width|height|
805 using - for any of the params uses "sane" values */
806 #ifdef HAVE_LCD_BITMAP
814 const char *filename
;
815 int x
, y
, height
, width
;
817 const char *ptr
= wps_bufptr
;
818 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
819 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
822 return WPS_ERROR_INVALID_PARAM
;
824 struct viewport
*vp
= &curr_vp
->vp
;
826 int font_height
= font_get(vp
->font
)->height
;
830 int line_num
= wps_data
->num_lines
- curr_vp
->first_line
;
832 pb
->have_bitmap_pb
= false;
833 pb
->bm
.data
= NULL
; /* no bitmap specified */
835 if (*wps_bufptr
!= '|') /* regular old style */
838 pb
->width
= vp
->width
;
839 pb
->height
= SYSFONT_HEIGHT
-2;
840 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
843 add_to_ll_chain(&wps_data
->progressbars
, item
);
846 ptr
= wps_bufptr
+ 1;
848 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
849 &x
, &y
, &width
, &height
)))
850 return WPS_ERROR_INVALID_PARAM
;
852 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
853 pb
->bm
.data
= (char*)filename
;
855 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
860 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
862 /* A zero width causes a divide-by-zero error later, so reject it */
864 return WPS_ERROR_INVALID_PARAM
;
869 pb
->width
= vp
->width
- pb
->x
;
871 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
873 /* A zero height makes no sense - reject it */
875 return WPS_ERROR_INVALID_PARAM
;
880 pb
->height
= font_height
;
882 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
885 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
888 add_to_ll_chain(&wps_data
->progressbars
, item
);
890 /* Skip the rest of the line */
891 return skip_end_of_line(wps_bufptr
)-1;
895 if (*(wps_bufptr
-1) == 'f')
896 wps_data
->full_line_progressbar
= true;
898 wps_data
->full_line_progressbar
= false;
906 static int parse_albumart_load(const char *wps_bufptr
,
907 struct wps_token
*token
,
908 struct wps_data
*wps_data
)
910 const char *_pos
, *newline
;
912 (void)token
; /* silence warning */
914 /* reset albumart info in wps */
915 wps_data
->albumart_max_width
= -1;
916 wps_data
->albumart_max_height
= -1;
917 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
918 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
920 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
922 newline
= strchr(wps_bufptr
, '\n');
924 /* initial validation and parsing of x and y components */
925 if (*wps_bufptr
!= '|')
926 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
928 _pos
= wps_bufptr
+ 1;
930 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|@ */
931 wps_data
->albumart_x
= atoi(_pos
);
933 _pos
= strchr(_pos
, '|');
934 if (!_pos
|| _pos
> newline
|| !isdigit(*(++_pos
)))
935 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
937 wps_data
->albumart_y
= atoi(_pos
);
939 _pos
= strchr(_pos
, '|');
940 if (!_pos
|| _pos
> newline
)
941 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
944 /* parsing width field */
948 /* apply each modifier in turn */
955 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_LEFT
;
959 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
;
964 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
979 /* extract max width data */
982 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
983 return WPS_ERROR_INVALID_PARAM
;
985 wps_data
->albumart_max_width
= atoi(_pos
);
987 _pos
= strchr(_pos
, '|');
988 if (!_pos
|| _pos
> newline
)
989 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
990 e.g. %Cl|7|59|200\n */
993 /* parsing height field */
997 /* apply each modifier in turn */
1004 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_TOP
;
1008 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1013 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1021 /* simply ignored */
1028 /* extract max height data */
1031 if (!isdigit(*_pos
))
1032 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1034 wps_data
->albumart_max_height
= atoi(_pos
);
1036 _pos
= strchr(_pos
, '|');
1037 if (!_pos
|| _pos
> newline
)
1038 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1039 e.g. %Cl|7|59|200|200\n */
1042 /* if we got here, we parsed everything ok .. ! */
1043 if (wps_data
->albumart_max_width
< 0)
1044 wps_data
->albumart_max_width
= 0;
1045 else if (wps_data
->albumart_max_width
> LCD_WIDTH
)
1046 wps_data
->albumart_max_width
= LCD_WIDTH
;
1048 if (wps_data
->albumart_max_height
< 0)
1049 wps_data
->albumart_max_height
= 0;
1050 else if (wps_data
->albumart_max_height
> LCD_HEIGHT
)
1051 wps_data
->albumart_max_height
= LCD_HEIGHT
;
1053 wps_data
->wps_uses_albumart
= WPS_ALBUMART_LOAD
;
1055 /* Skip the rest of the line */
1056 return skip_end_of_line(wps_bufptr
);
1059 static int parse_albumart_conditional(const char *wps_bufptr
,
1060 struct wps_token
*token
,
1061 struct wps_data
*wps_data
)
1063 struct wps_token
*prevtoken
= token
;
1065 if (wps_data
->num_tokens
>= 1 && prevtoken
->type
== WPS_TOKEN_CONDITIONAL
)
1067 /* This %C is part of a %?C construct.
1068 It's either %?C<blah> or %?Cn<blah> */
1069 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1070 if (*wps_bufptr
== 'n' && *(wps_bufptr
+ 1) == '<')
1075 else if (*wps_bufptr
== '<')
1081 token
->type
= WPS_NO_TOKEN
;
1087 /* This %C tag is in a conditional construct. */
1088 wps_data
->albumart_cond_index
= condindex
[level
];
1092 #endif /* HAVE_ALBUMART */
1094 #ifdef HAVE_TOUCHSCREEN
1096 struct touchaction
{char* s
; int action
;};
1097 static struct touchaction touchactions
[] = {
1098 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1099 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1100 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1101 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1102 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1103 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1104 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1106 static int parse_touchregion(const char *wps_bufptr
,
1107 struct wps_token
*token
, struct wps_data
*wps_data
)
1111 struct touchregion
*region
= NULL
;
1112 const char *ptr
= wps_bufptr
;
1114 const char pb_string
[] = "progressbar";
1115 const char vol_string
[] = "volume";
1118 /* format: %T|x|y|width|height|action|
1119 * if action starts with & the area must be held to happen
1121 * play - play/pause playback
1122 * stop - stop playback, exit the wps
1125 * ffwd - seek forward
1126 * rwd - seek backwards
1127 * menu - go back to the main menu
1128 * browse - go back to the file/db browser
1129 * shuffle - toggle shuffle mode
1130 * repmode - cycle the repeat mode
1131 * quickscreen - go into the quickscreen
1132 * contextmenu - open the context menu
1133 * playlist - go into the playlist
1134 * pitch - go into the pitchscreen
1139 return WPS_ERROR_INVALID_PARAM
;
1142 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1143 return WPS_ERROR_INVALID_PARAM
;
1145 /* Check there is a terminating | */
1147 return WPS_ERROR_INVALID_PARAM
;
1149 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1151 return WPS_ERROR_INVALID_PARAM
;
1153 /* should probably do some bounds checking here with the viewport... but later */
1154 region
->action
= ACTION_NONE
;
1159 region
->wvp
= curr_vp
;
1161 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1162 && *(action
+ sizeof(pb_string
)-1) == '|')
1163 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1164 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1165 && *(action
+ sizeof(vol_string
)-1) == '|')
1166 region
->type
= WPS_TOUCHREGION_VOLUME
;
1169 region
->type
= WPS_TOUCHREGION_ACTION
;
1174 region
->repeat
= true;
1177 region
->repeat
= false;
1180 imax
= ARRAYLEN(touchactions
);
1181 while ((region
->action
== ACTION_NONE
) &&
1184 /* try to match with one of our touchregion screens */
1185 int len
= strlen(touchactions
[i
].s
);
1186 if (!strncmp(touchactions
[i
].s
, action
, len
)
1187 && *(action
+len
) == '|')
1188 region
->action
= touchactions
[i
].action
;
1191 if (region
->action
== ACTION_NONE
)
1192 return WPS_ERROR_INVALID_PARAM
;
1194 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1196 return WPS_ERROR_INVALID_PARAM
;
1197 add_to_ll_chain(&wps_data
->touchregions
, item
);
1198 return skip_end_of_line(wps_bufptr
);
1202 /* Parse a generic token from the given string. Return the length read */
1203 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1205 int skip
= 0, taglen
= 0, ret
;
1206 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1207 const struct wps_tag
*tag
;
1218 /* escaped characters */
1219 token
->type
= WPS_TOKEN_CHARACTER
;
1220 token
->value
.c
= *wps_bufptr
;
1222 wps_data
->num_tokens
++;
1226 /* conditional tag */
1227 token
->type
= WPS_TOKEN_CONDITIONAL
;
1229 condindex
[level
] = wps_data
->num_tokens
;
1230 numoptions
[level
] = 1;
1231 wps_data
->num_tokens
++;
1232 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1233 if (ret
< 0) return ret
;
1238 /* find what tag we have */
1239 for (tag
= all_tags
;
1240 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1243 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1244 token
->type
= tag
->type
;
1245 wps_data
->sublines
[wps_data
->num_sublines
].line_type
|=
1248 /* if the tag has a special parsing function, we call it */
1249 if (tag
->parse_func
)
1251 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1252 if (ret
< 0) return ret
;
1256 /* Some tags we don't want to save as tokens */
1257 if (tag
->type
== WPS_NO_TOKEN
)
1260 /* tags that start with 'F', 'I' or 'D' are for the next file */
1261 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1262 *(tag
->name
) == 'D')
1265 wps_data
->num_tokens
++;
1274 data is the pointer to the structure where the parsed WPS should be stored.
1276 wps_bufptr points to the string containing the WPS tags */
1277 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
)
1279 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1281 enum wps_parse_error fail
= PARSE_OK
;
1286 while(*wps_bufptr
&& !fail
&& data
->num_tokens
< WPS_MAX_TOKENS
- 1
1287 && data
->num_lines
< WPS_MAX_LINES
)
1289 switch(*wps_bufptr
++)
1294 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1296 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1299 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1301 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1307 /* Alternating sublines separator */
1309 if (level
>= 0) /* there are unclosed conditionals */
1311 fail
= PARSE_FAIL_UNCLOSED_COND
;
1315 if (data
->num_sublines
+1 < WPS_MAX_SUBLINES
)
1316 wps_start_new_subline(data
);
1318 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1322 /* Conditional list start */
1324 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1326 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1330 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1331 lastcond
[level
] = data
->num_tokens
++;
1334 /* Conditional list end */
1336 if (level
< 0) /* not in a conditional, invalid char */
1338 fail
= PARSE_FAIL_INVALID_CHAR
;
1342 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1343 if (lastcond
[level
])
1344 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1347 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1351 lastcond
[level
] = 0;
1353 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1357 /* Conditional list option */
1359 if (level
< 0) /* not in a conditional, invalid char */
1361 fail
= PARSE_FAIL_INVALID_CHAR
;
1365 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1366 if (lastcond
[level
])
1367 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1370 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1374 lastcond
[level
] = data
->num_tokens
;
1375 numoptions
[level
]++;
1381 if (level
>= 0) /* there are unclosed conditionals */
1383 fail
= PARSE_FAIL_UNCLOSED_COND
;
1387 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1390 /* End of this line */
1392 if (level
>= 0) /* there are unclosed conditionals */
1394 fail
= PARSE_FAIL_UNCLOSED_COND
;
1399 wps_start_new_subline(data
);
1400 data
->num_lines
++; /* Start a new line */
1402 if ((data
->num_lines
< WPS_MAX_LINES
) &&
1403 (data
->num_sublines
< WPS_MAX_SUBLINES
))
1405 data
->lines
[data
->num_lines
].first_subline_idx
=
1408 data
->sublines
[data
->num_sublines
].first_token_idx
=
1417 unsigned int len
= 1;
1418 const char *string_start
= wps_bufptr
- 1;
1420 /* find the length of the string */
1421 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1422 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1423 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1424 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1430 /* look if we already have that string */
1433 struct skin_token_list
*list
= data
->strings
;
1436 str
= (char*)list
->token
->value
.data
;
1437 found
= (strlen(str
) == len
&&
1438 strncmp(string_start
, str
, len
) == 0);
1440 break; /* break here because the list item is
1441 used if its found */
1444 /* If a matching string is found, found is true and i is
1445 the index of the string. If not, found is false */
1450 str
= (char*)skin_buffer_alloc(len
+1);
1453 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1456 strlcpy(str
, string_start
, len
+1);
1457 struct skin_token_list
*item
=
1458 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1461 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1464 add_to_ll_chain(&data
->strings
, item
);
1468 /* another occurrence of an existing string */
1469 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1471 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1478 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1479 fail
= PARSE_FAIL_UNCLOSED_COND
;
1481 if (*wps_bufptr
&& !fail
)
1482 /* one of the limits of the while loop was exceeded */
1483 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1485 curr_vp
->last_line
= data
->num_lines
- 1;
1487 #if defined(DEBUG) || defined(SIMULATOR)
1488 print_debug_info(data
, fail
, line
);
1494 static void wps_reset(struct wps_data
*data
)
1496 #ifdef HAVE_REMOTE_LCD
1497 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1499 memset(data
, 0, sizeof(*data
));
1500 skin_data_init(data
);
1501 #ifdef HAVE_REMOTE_LCD
1502 data
->remote_wps
= rwps
;
1506 #ifdef HAVE_LCD_BITMAP
1507 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1509 (void)wps_data
; /* only needed for remote targets */
1510 bool loaded
= false;
1511 char img_path
[MAX_PATH
];
1512 get_image_filename(bitmap
->data
, bmpdir
,
1513 img_path
, sizeof(img_path
));
1515 /* load the image */
1517 #ifdef HAVE_REMOTE_LCD
1518 if (wps_data
->remote_wps
)
1519 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1522 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1525 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
1526 bitmap
->data
= imgbuf
;
1527 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
1531 skin_buffer_increment(ret
);
1536 /* Abort if we can't load an image */
1537 DEBUGF("ERR: Failed to load image - %s\n",img_path
);
1543 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1545 struct skin_token_list
*list
;
1546 /* do the progressbars */
1547 list
= wps_data
->progressbars
;
1550 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
1553 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
1557 /* regular images */
1558 list
= wps_data
->images
;
1561 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1564 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1566 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
1571 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1572 if (bmp_names
[BACKDROP_BMP
])
1574 int screen
= SCREEN_MAIN
;
1575 char img_path
[MAX_PATH
];
1576 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1577 img_path
, sizeof(img_path
));
1578 #if defined(HAVE_REMOTE_LCD)
1579 /* We only need to check LCD type if there is a remote LCD */
1580 if (wps_data
->remote_wps
)
1581 screen
= SCREEN_REMOTE
;
1583 screens
[screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1585 #endif /* has backdrop support */
1587 /* If we got here, everything was OK */
1591 #endif /* HAVE_LCD_BITMAP */
1593 /* to setup up the wps-data from a format-buffer (isfile = false)
1594 from a (wps-)file (isfile = true)*/
1595 bool skin_data_load(struct wps_data
*wps_data
,
1596 struct screen
*display
,
1600 #ifdef HAVE_ALBUMART
1601 struct mp3entry
*curtrack
;
1604 int wps_uses_albumart
= wps_data
->wps_uses_albumart
;
1605 int albumart_max_height
= wps_data
->albumart_max_height
;
1606 int albumart_max_width
= wps_data
->albumart_max_width
;
1608 if (!wps_data
|| !buf
)
1611 wps_reset(wps_data
);
1613 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
1616 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
1619 add_to_ll_chain(&wps_data
->viewports
, list
);
1622 /* Initialise the first (default) viewport */
1623 curr_vp
->label
= VP_DEFAULT_LABEL
;
1625 curr_vp
->vp
.width
= display
->getwidth();
1626 curr_vp
->vp
.height
= display
->getheight();
1628 curr_vp
->hidden_flags
= 0;
1629 switch (statusbar_position(display
->screen_type
))
1635 curr_vp
->vp
.y
= STATUSBAR_HEIGHT
;
1636 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1638 case STATUSBAR_BOTTOM
:
1640 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1643 #ifdef HAVE_LCD_BITMAP
1644 curr_vp
->vp
.font
= FONT_UI
;
1645 curr_vp
->vp
.drawmode
= DRMODE_SOLID
;
1648 if (display
->depth
> 1)
1650 curr_vp
->vp
.fg_pattern
= display
->get_foreground();
1651 curr_vp
->vp
.bg_pattern
= display
->get_background();
1656 return wps_parse(wps_data
, buf
);
1661 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1662 * wants to be a virtual file. Feel free to modify dirbrowse()
1663 * if you're feeling brave.
1666 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1668 global_settings
.wps_file
[0] = 0;
1672 #ifdef HAVE_REMOTE_LCD
1673 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1675 global_settings
.rwps_file
[0] = 0;
1679 #endif /* __PCTOOL__ */
1681 int fd
= open_utf8(buf
, O_RDONLY
);
1686 /* get buffer space from the plugin buffer */
1687 size_t buffersize
= 0;
1688 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1693 /* copy the file's content to the buffer for parsing,
1694 ensuring that every line ends with a newline char. */
1695 unsigned int start
= 0;
1696 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1698 start
+= strlen(wps_buffer
+ start
);
1699 if (start
< buffersize
- 1)
1701 wps_buffer
[start
++] = '\n';
1702 wps_buffer
[start
] = 0;
1711 #ifdef HAVE_LCD_BITMAP
1712 /* Set all filename pointers to NULL */
1713 memset(bmp_names
, 0, sizeof(bmp_names
));
1716 /* parse the WPS source */
1717 if (!wps_parse(wps_data
, wps_buffer
)) {
1718 wps_reset(wps_data
);
1722 wps_data
->wps_loaded
= true;
1724 #ifdef HAVE_LCD_BITMAP
1725 /* get the bitmap dir */
1726 char bmpdir
[MAX_PATH
];
1727 char *dot
= strrchr(buf
, '.');
1729 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1731 /* load the bitmaps that were found by the parsing */
1732 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
1733 wps_reset(wps_data
);
1737 #ifdef HAVE_ALBUMART
1738 status
= audio_status();
1739 if (((!wps_uses_albumart
&& wps_data
->wps_uses_albumart
) ||
1740 (wps_data
->wps_uses_albumart
&&
1741 (albumart_max_height
!= wps_data
->albumart_max_height
||
1742 albumart_max_width
!= wps_data
->albumart_max_width
))) &&
1743 status
& AUDIO_STATUS_PLAY
)
1745 curtrack
= audio_current_track();
1746 offset
= curtrack
->offset
;
1748 if (!(status
& AUDIO_STATUS_PAUSE
))