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 ****************************************************************************/
46 #include "wps_internals.h"
48 #include "settings_list.h"
50 #ifdef HAVE_LCD_BITMAP
56 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
57 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
59 #define WPS_ERROR_INVALID_PARAM -1
61 /* level of current conditional.
62 -1 means we're not in a conditional. */
63 static int level
= -1;
65 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
66 or WPS_TOKEN_CONDITIONAL_START in current level */
67 static int lastcond
[WPS_MAX_COND_LEVEL
];
69 /* index of the WPS_TOKEN_CONDITIONAL in current level */
70 static int condindex
[WPS_MAX_COND_LEVEL
];
72 /* number of condtional options in current level */
73 static int numoptions
[WPS_MAX_COND_LEVEL
];
75 /* the current line in the file */
78 #ifdef HAVE_LCD_BITMAP
81 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
83 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
86 #define PROGRESSBAR_BMP MAX_IMAGES
87 #define BACKDROP_BMP (MAX_BITMAPS-1)
89 /* pointers to the bitmap filenames in the WPS source */
90 static const char *bmp_names
[MAX_BITMAPS
];
92 #endif /* HAVE_LCD_BITMAP */
94 #if defined(DEBUG) || defined(SIMULATOR)
95 /* debugging function */
96 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
99 static void wps_reset(struct wps_data
*data
);
101 /* Function for parsing of details for a token. At the moment the
102 function is called, the token type has already been set. The
103 function must fill in the details and possibly add more tokens
104 to the token array. It should return the number of chars that
107 wps_bufptr points to the char following the tag (i.e. where
109 token is the pointer to the 'main' token being parsed
111 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
112 struct wps_token
*token
, struct wps_data
*wps_data
);
115 enum wps_token_type type
;
117 unsigned char refresh_type
;
118 const wps_tag_parse_func parse_func
;
120 static int skip_end_of_line(const char *wps_bufptr
);
121 /* prototypes of all special parse functions : */
122 static int parse_timeout(const char *wps_bufptr
,
123 struct wps_token
*token
, struct wps_data
*wps_data
);
124 static int parse_progressbar(const char *wps_bufptr
,
125 struct wps_token
*token
, struct wps_data
*wps_data
);
126 static int parse_dir_level(const char *wps_bufptr
,
127 struct wps_token
*token
, struct wps_data
*wps_data
);
128 static int parse_setting(const char *wps_bufptr
,
129 struct wps_token
*token
, struct wps_data
*wps_data
);
131 #ifdef HAVE_LCD_BITMAP
132 static int parse_viewport_display(const char *wps_bufptr
,
133 struct wps_token
*token
, struct wps_data
*wps_data
);
134 static int parse_viewport(const char *wps_bufptr
,
135 struct wps_token
*token
, struct wps_data
*wps_data
);
136 static int parse_statusbar_enable(const char *wps_bufptr
,
137 struct wps_token
*token
, struct wps_data
*wps_data
);
138 static int parse_statusbar_disable(const char *wps_bufptr
,
139 struct wps_token
*token
, struct wps_data
*wps_data
);
140 static int parse_image_display(const char *wps_bufptr
,
141 struct wps_token
*token
, struct wps_data
*wps_data
);
142 static int parse_image_load(const char *wps_bufptr
,
143 struct wps_token
*token
, struct wps_data
*wps_data
);
144 #endif /*HAVE_LCD_BITMAP */
145 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
146 static int parse_image_special(const char *wps_bufptr
,
147 struct wps_token
*token
, struct wps_data
*wps_data
);
150 static int parse_albumart_load(const char *wps_bufptr
,
151 struct wps_token
*token
, struct wps_data
*wps_data
);
152 static int parse_albumart_conditional(const char *wps_bufptr
,
153 struct wps_token
*token
, struct wps_data
*wps_data
);
154 #endif /* HAVE_ALBUMART */
155 #ifdef HAVE_TOUCHSCREEN
156 static int parse_touchregion(const char *wps_bufptr
,
157 struct wps_token
*token
, struct wps_data
*wps_data
);
159 static int fulline_tag_not_supported(const char *wps_bufptr
,
160 struct wps_token
*token
, struct wps_data
*wps_data
)
162 (void)token
; (void)wps_data
;
163 return skip_end_of_line(wps_bufptr
);
165 #define parse_touchregion fulline_tag_not_supported
168 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
170 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
173 /* array of available tags - those with more characters have to go first
174 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
175 static const struct wps_tag all_tags
[] = {
177 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
178 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
179 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
181 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
182 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
183 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
184 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
185 #if CONFIG_CHARGING >= CHARGING_MONITOR
186 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
189 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
192 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
193 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
194 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
195 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
196 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
197 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
198 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
199 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
200 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
209 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
210 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
213 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
214 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
215 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
216 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
217 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
218 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
219 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
226 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
228 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
229 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
230 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
231 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
232 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
233 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
234 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
238 /* current metadata */
239 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
243 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
244 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
245 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
257 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
258 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
266 #if (CONFIG_CODEC != MAS3507D)
267 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
270 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
271 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
274 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
276 #ifdef HAS_REMOTE_BUTTON_HOLD
277 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
279 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
282 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
283 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
284 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
287 #ifdef HAVE_LCD_BITMAP
288 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
290 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
291 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
293 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
296 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
298 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
299 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
300 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
301 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
303 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
304 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
305 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
306 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
309 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
310 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
311 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
314 #if CONFIG_CODEC == SWCODEC
315 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
316 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
319 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
320 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
322 #ifdef HAVE_LCD_BITMAP
323 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
324 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
326 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
328 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
329 parse_image_display
},
331 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
333 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
334 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
,
335 parse_albumart_conditional
},
338 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
339 parse_viewport_display
},
340 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
342 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
343 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
347 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
, parse_setting
},
349 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
350 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
352 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
353 /* the array MUST end with an empty string (first char is \0) */
356 /* Returns the number of chars that should be skipped to jump
357 immediately after the first eol, i.e. to the start of the next line */
358 static int skip_end_of_line(const char *wps_bufptr
)
362 while(*(wps_bufptr
+ skip
) != '\n')
367 /* Starts a new subline in the current line during parsing */
368 static void wps_start_new_subline(struct wps_data
*data
)
370 data
->num_sublines
++;
371 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
372 data
->lines
[data
->num_lines
].num_sublines
++;
375 #ifdef HAVE_LCD_BITMAP
377 static int parse_statusbar_enable(const char *wps_bufptr
,
378 struct wps_token
*token
,
379 struct wps_data
*wps_data
)
381 (void)token
; /* Kill warnings */
382 wps_data
->wps_sb_tag
= true;
383 wps_data
->show_sb_on_wps
= true;
384 if (wps_data
->viewports
[0].vp
.y
== 0)
386 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
387 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
389 return skip_end_of_line(wps_bufptr
);
392 static int parse_statusbar_disable(const char *wps_bufptr
,
393 struct wps_token
*token
,
394 struct wps_data
*wps_data
)
396 (void)token
; /* Kill warnings */
397 wps_data
->wps_sb_tag
= true;
398 wps_data
->show_sb_on_wps
= false;
399 if (wps_data
->viewports
[0].vp
.y
== STATUSBAR_HEIGHT
)
401 wps_data
->viewports
[0].vp
.y
= 0;
402 wps_data
->viewports
[0].vp
.height
+= STATUSBAR_HEIGHT
;
404 return skip_end_of_line(wps_bufptr
);
407 static bool load_bitmap(struct wps_data
*wps_data
,
412 #ifdef HAVE_REMOTE_LCD
413 if (wps_data
->remote_wps
)
414 format
= FORMAT_ANY
|FORMAT_REMOTE
;
417 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
419 int ret
= read_bmp_file(filename
, bm
,
420 wps_data
->img_buf_free
,
427 /* Always consume an even number of bytes */
429 wps_data
->img_buf_ptr
+= ret
;
430 wps_data
->img_buf_free
-= ret
;
438 static int get_image_id(int c
)
440 if(c
>= 'a' && c
<= 'z')
442 else if(c
>= 'A' && c
<= 'Z')
448 static char *get_image_filename(const char *start
, const char* bmpdir
,
449 char *buf
, int buf_size
)
451 const char *end
= strchr(start
, '|');
453 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
459 int bmpdirlen
= strlen(bmpdir
);
462 buf
[bmpdirlen
] = '/';
463 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
464 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
469 static int parse_image_display(const char *wps_bufptr
,
470 struct wps_token
*token
,
471 struct wps_data
*wps_data
)
474 int n
= get_image_id(wps_bufptr
[0]);
479 /* invalid picture display tag */
480 return WPS_ERROR_INVALID_PARAM
;
483 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
486 if (subimage
>= wps_data
->img
[n
].num_subimages
)
487 return WPS_ERROR_INVALID_PARAM
;
489 /* Store sub-image number to display in high bits */
490 token
->value
.i
= n
| (subimage
<< 8);
491 return 2; /* We have consumed 2 bytes */
494 return 1; /* We have consumed 1 byte */
498 static int parse_image_load(const char *wps_bufptr
,
499 struct wps_token
*token
,
500 struct wps_data
*wps_data
)
503 const char *ptr
= wps_bufptr
;
505 const char* filename
;
510 /* format: %x|n|filename.bmp|x|y|
511 or %xl|n|filename.bmp|x|y|
512 or %xl|n|filename.bmp|x|y|num_subimages|
516 return WPS_ERROR_INVALID_PARAM
;
520 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
521 return WPS_ERROR_INVALID_PARAM
;
523 /* Check there is a terminating | */
525 return WPS_ERROR_INVALID_PARAM
;
527 /* get the image ID */
528 n
= get_image_id(*id
);
530 /* check the image number and load state */
531 if(n
< 0 || n
>= MAX_IMAGES
|| wps_data
->img
[n
].loaded
)
533 /* Invalid image ID */
534 return WPS_ERROR_INVALID_PARAM
;
537 /* save a pointer to the filename */
538 bmp_names
[n
] = filename
;
540 wps_data
->img
[n
].x
= x
;
541 wps_data
->img
[n
].y
= y
;
543 /* save current viewport */
544 wps_data
->img
[n
].vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
546 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
548 wps_data
->img
[n
].always_display
= true;
552 /* Parse the (optional) number of sub-images */
554 newline
= strchr(ptr
, '\n');
555 pos
= strchr(ptr
, '|');
556 if (pos
&& pos
< newline
)
557 wps_data
->img
[n
].num_subimages
= atoi(ptr
);
559 if (wps_data
->img
[n
].num_subimages
<= 0)
560 return WPS_ERROR_INVALID_PARAM
;
563 /* Skip the rest of the line */
564 return skip_end_of_line(wps_bufptr
);
567 static int parse_viewport_display(const char *wps_bufptr
,
568 struct wps_token
*token
,
569 struct wps_data
*wps_data
)
572 char letter
= wps_bufptr
[0];
574 if (letter
< 'a' || letter
> 'z')
576 /* invalid viewport tag */
577 return WPS_ERROR_INVALID_PARAM
;
579 token
->value
.i
= letter
;
583 static int parse_viewport(const char *wps_bufptr
,
584 struct wps_token
*token
,
585 struct wps_data
*wps_data
)
587 (void)token
; /* Kill warnings */
588 const char *ptr
= wps_bufptr
;
601 int lcd_width
= LCD_WIDTH
, lcd_height
= LCD_HEIGHT
;
602 #ifdef HAVE_REMOTE_LCD
603 if (wps_data
->remote_wps
)
605 lcd_width
= LCD_REMOTE_WIDTH
;
606 lcd_height
= LCD_REMOTE_HEIGHT
;
610 if (wps_data
->num_viewports
>= WPS_MAX_VIEWPORTS
)
611 return WPS_ERROR_INVALID_PARAM
;
613 wps_data
->num_viewports
++;
614 /* check for the optional letter to signify its a hideable viewport */
615 /* %Vl|<label>|<rest of tags>| */
616 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= 0;
622 char label
= *(ptr
+2);
623 if (label
>= 'a' && label
<= 'z')
625 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= VP_DRAW_HIDEABLE
;
626 wps_data
->viewports
[wps_data
->num_viewports
].label
= label
;
629 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
634 return WPS_ERROR_INVALID_PARAM
;
637 vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
638 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
640 /* Set the defaults for fields not user-specified */
641 vp
->drawmode
= DRMODE_SOLID
;
643 /* Work out the depth of this display */
644 #ifdef HAVE_REMOTE_LCD
645 depth
= (wps_data
->remote_wps
? LCD_REMOTE_DEPTH
: LCD_DEPTH
);
650 #ifdef HAVE_LCD_COLOR
653 if (!(ptr
= parse_list("dddddcc", &set
, '|', ptr
, &vp
->x
, &vp
->y
, &vp
->width
,
654 &vp
->height
, &vp
->font
, &vp
->fg_pattern
,&vp
->bg_pattern
)))
655 return WPS_ERROR_INVALID_PARAM
;
659 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
661 /* Default to black on white */
664 if (!(ptr
= parse_list("dddddgg", &set
, '|', ptr
, &vp
->x
, &vp
->y
, &vp
->width
,
665 &vp
->height
, &vp
->font
, &vp
->fg_pattern
, &vp
->bg_pattern
)))
666 return WPS_ERROR_INVALID_PARAM
;
670 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
673 if (!(ptr
= parse_list("ddddd", &set
, '|', ptr
, &vp
->x
, &vp
->y
,
674 &vp
->width
, &vp
->height
, &vp
->font
)))
675 return WPS_ERROR_INVALID_PARAM
;
681 /* Check for trailing | */
683 return WPS_ERROR_INVALID_PARAM
;
685 if (!LIST_VALUE_PARSED(set
, PL_X
) || !LIST_VALUE_PARSED(set
, PL_Y
))
686 return WPS_ERROR_INVALID_PARAM
;
689 if (!LIST_VALUE_PARSED(set
, PL_WIDTH
))
690 vp
->width
= lcd_width
- vp
->x
;
691 if (!LIST_VALUE_PARSED(set
, PL_HEIGHT
))
692 vp
->height
= lcd_height
- vp
->y
;
694 /* Default to using the user font if the font was an invalid number */
695 if (!LIST_VALUE_PARSED(set
, PL_FONT
) ||
696 ((vp
->font
!= FONT_SYSFIXED
) && (vp
->font
!= FONT_UI
)))
699 /* Validate the viewport dimensions - we know that the numbers are
700 non-negative integers */
701 if ((vp
->x
>= lcd_width
) ||
702 ((vp
->x
+ vp
->width
) > lcd_width
) ||
703 (vp
->y
>= lcd_height
) ||
704 ((vp
->y
+ vp
->height
) > lcd_height
))
706 return WPS_ERROR_INVALID_PARAM
;
709 #ifdef HAVE_LCD_COLOR
712 if (!LIST_VALUE_PARSED(set
, PL_FG
))
713 vp
->fg_pattern
= global_settings
.fg_color
;
714 if (!LIST_VALUE_PARSED(set
, PL_BG
))
715 vp
->bg_pattern
= global_settings
.bg_color
;
719 wps_data
->viewports
[wps_data
->num_viewports
-1].last_line
= wps_data
->num_lines
- 1;
721 wps_data
->viewports
[wps_data
->num_viewports
].first_line
= wps_data
->num_lines
;
723 if (wps_data
->num_sublines
< WPS_MAX_SUBLINES
)
725 wps_data
->lines
[wps_data
->num_lines
].first_subline_idx
=
726 wps_data
->num_sublines
;
728 wps_data
->sublines
[wps_data
->num_sublines
].first_token_idx
=
729 wps_data
->num_tokens
;
732 /* Skip the rest of the line */
733 return skip_end_of_line(wps_bufptr
);
736 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
737 static int parse_image_special(const char *wps_bufptr
,
738 struct wps_token
*token
,
739 struct wps_data
*wps_data
)
741 (void)wps_data
; /* kill warning */
743 const char *pos
= NULL
;
746 pos
= strchr(wps_bufptr
+ 1, '|');
747 newline
= strchr(wps_bufptr
, '\n');
750 return WPS_ERROR_INVALID_PARAM
;
752 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
754 /* format: %X|filename.bmp| */
755 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
759 /* Skip the rest of the line */
760 return skip_end_of_line(wps_bufptr
);
764 #endif /* HAVE_LCD_BITMAP */
766 static int parse_setting(const char *wps_bufptr
,
767 struct wps_token
*token
,
768 struct wps_data
*wps_data
)
771 const char *ptr
= wps_bufptr
;
775 /* Find the setting's cfg_name */
777 return WPS_ERROR_INVALID_PARAM
;
779 end
= strchr(ptr
,'|');
781 return WPS_ERROR_INVALID_PARAM
;
783 /* Find the setting */
784 for (i
=0; i
<nb_settings
; i
++)
785 if (settings
[i
].cfg_name
&&
786 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
787 /* prevent matches on cfg_name prefixes */
788 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
790 if (i
== nb_settings
)
791 return WPS_ERROR_INVALID_PARAM
;
793 /* Store the setting number */
796 /* Skip the rest of the line */
801 static int parse_dir_level(const char *wps_bufptr
,
802 struct wps_token
*token
,
803 struct wps_data
*wps_data
)
805 char val
[] = { *wps_bufptr
, '\0' };
806 token
->value
.i
= atoi(val
);
807 (void)wps_data
; /* Kill warnings */
811 static int parse_timeout(const char *wps_bufptr
,
812 struct wps_token
*token
,
813 struct wps_data
*wps_data
)
817 bool have_point
= false;
818 bool have_tenth
= false;
820 (void)wps_data
; /* Kill the warning */
822 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
824 if (*wps_bufptr
!= '.')
827 val
+= *wps_bufptr
- '0';
843 if (have_tenth
== false)
846 if (val
== 0 && skip
== 0)
848 /* decide what to do if no value was specified */
851 case WPS_TOKEN_SUBLINE_TIMEOUT
:
853 case WPS_TOKEN_BUTTON_VOLUME
:
858 token
->value
.i
= val
;
863 static int parse_progressbar(const char *wps_bufptr
,
864 struct wps_token
*token
,
865 struct wps_data
*wps_data
)
867 (void)token
; /* Kill warnings */
868 /* %pb or %pb|filename|x|y|width|height|
869 using - for any of the params uses "sane" values */
870 #ifdef HAVE_LCD_BITMAP
878 const char *filename
;
879 int x
, y
, height
, width
;
881 const char *ptr
= wps_bufptr
;
882 struct progressbar
*pb
;
883 struct viewport
*vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
885 int font_height
= font_get(vp
->font
)->height
;
889 int line_num
= wps_data
->num_lines
-
890 wps_data
->viewports
[wps_data
->num_viewports
].first_line
;
892 if (wps_data
->progressbar_count
>= MAX_PROGRESSBARS
)
893 return WPS_ERROR_INVALID_PARAM
;
895 pb
= &wps_data
->progressbar
[wps_data
->progressbar_count
];
896 pb
->have_bitmap_pb
= false;
898 if (*wps_bufptr
!= '|') /* regular old style */
901 pb
->width
= vp
->width
;
902 pb
->height
= SYSFONT_HEIGHT
-2;
903 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
905 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
906 wps_data
->progressbar_count
++;
909 ptr
= wps_bufptr
+ 1;
911 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
912 &x
, &y
, &width
, &height
)))
913 return WPS_ERROR_INVALID_PARAM
;
915 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
916 bmp_names
[PROGRESSBAR_BMP
+wps_data
->progressbar_count
] = filename
;
918 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
923 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
925 /* A zero width causes a divide-by-zero error later, so reject it */
927 return WPS_ERROR_INVALID_PARAM
;
932 pb
->width
= vp
->width
- pb
->x
;
934 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
936 /* A zero height makes no sense - reject it */
938 return WPS_ERROR_INVALID_PARAM
;
943 pb
->height
= font_height
;
945 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
948 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
950 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
951 wps_data
->progressbar_count
++;
953 /* Skip the rest of the line */
954 return skip_end_of_line(wps_bufptr
)-1;
957 if (*(wps_bufptr
-1) == 'f')
958 wps_data
->full_line_progressbar
= true;
960 wps_data
->full_line_progressbar
= false;
968 static int parse_albumart_load(const char *wps_bufptr
,
969 struct wps_token
*token
,
970 struct wps_data
*wps_data
)
972 const char *_pos
, *newline
;
974 (void)token
; /* silence warning */
976 /* reset albumart info in wps */
977 wps_data
->albumart_max_width
= -1;
978 wps_data
->albumart_max_height
= -1;
979 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
980 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
982 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
984 newline
= strchr(wps_bufptr
, '\n');
986 /* initial validation and parsing of x and y components */
987 if (*wps_bufptr
!= '|')
988 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
990 _pos
= wps_bufptr
+ 1;
992 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|@ */
993 wps_data
->albumart_x
= atoi(_pos
);
995 _pos
= strchr(_pos
, '|');
996 if (!_pos
|| _pos
> newline
|| !isdigit(*(++_pos
)))
997 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
999 wps_data
->albumart_y
= atoi(_pos
);
1001 _pos
= strchr(_pos
, '|');
1002 if (!_pos
|| _pos
> newline
)
1003 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
1006 /* parsing width field */
1010 /* apply each modifier in turn */
1017 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1021 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1026 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1034 /* simply ignored */
1041 /* extract max width data */
1044 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
1045 return WPS_ERROR_INVALID_PARAM
;
1047 wps_data
->albumart_max_width
= atoi(_pos
);
1049 _pos
= strchr(_pos
, '|');
1050 if (!_pos
|| _pos
> newline
)
1051 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
1052 e.g. %Cl|7|59|200\n */
1055 /* parsing height field */
1059 /* apply each modifier in turn */
1066 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_TOP
;
1070 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1075 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1083 /* simply ignored */
1090 /* extract max height data */
1093 if (!isdigit(*_pos
))
1094 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1096 wps_data
->albumart_max_height
= atoi(_pos
);
1098 _pos
= strchr(_pos
, '|');
1099 if (!_pos
|| _pos
> newline
)
1100 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1101 e.g. %Cl|7|59|200|200\n */
1104 /* if we got here, we parsed everything ok .. ! */
1105 if (wps_data
->albumart_max_width
< 0)
1106 wps_data
->albumart_max_width
= 0;
1107 else if (wps_data
->albumart_max_width
> LCD_WIDTH
)
1108 wps_data
->albumart_max_width
= LCD_WIDTH
;
1110 if (wps_data
->albumart_max_height
< 0)
1111 wps_data
->albumart_max_height
= 0;
1112 else if (wps_data
->albumart_max_height
> LCD_HEIGHT
)
1113 wps_data
->albumart_max_height
= LCD_HEIGHT
;
1115 wps_data
->wps_uses_albumart
= WPS_ALBUMART_LOAD
;
1117 /* Skip the rest of the line */
1118 return skip_end_of_line(wps_bufptr
);
1121 static int parse_albumart_conditional(const char *wps_bufptr
,
1122 struct wps_token
*token
,
1123 struct wps_data
*wps_data
)
1125 struct wps_token
*prevtoken
= token
;
1127 if (wps_data
->num_tokens
>= 1 && prevtoken
->type
== WPS_TOKEN_CONDITIONAL
)
1129 /* This %C is part of a %?C construct.
1130 It's either %?C<blah> or %?Cn<blah> */
1131 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1132 if (*wps_bufptr
== 'n' && *(wps_bufptr
+ 1) == '<')
1137 else if (*wps_bufptr
== '<')
1143 token
->type
= WPS_NO_TOKEN
;
1149 /* This %C tag is in a conditional construct. */
1150 wps_data
->albumart_cond_index
= condindex
[level
];
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
},
1168 static int parse_touchregion(const char *wps_bufptr
,
1169 struct wps_token
*token
, struct wps_data
*wps_data
)
1173 struct touchregion
*region
;
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
1198 if ((wps_data
->touchregion_count
+1 >= MAX_TOUCHREGIONS
) || (*ptr
!= '|'))
1199 return WPS_ERROR_INVALID_PARAM
;
1202 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1203 return WPS_ERROR_INVALID_PARAM
;
1205 /* Check there is a terminating | */
1207 return WPS_ERROR_INVALID_PARAM
;
1209 /* should probably do some bounds checking here with the viewport... but later */
1210 region
= &wps_data
->touchregion
[wps_data
->touchregion_count
];
1211 region
->action
= ACTION_NONE
;
1216 region
->wvp
= &wps_data
->viewports
[wps_data
->num_viewports
];
1218 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1219 && *(action
+ sizeof(pb_string
)-1) == '|')
1220 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1221 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1222 && *(action
+ sizeof(vol_string
)-1) == '|')
1223 region
->type
= WPS_TOUCHREGION_VOLUME
;
1226 region
->type
= WPS_TOUCHREGION_ACTION
;
1231 region
->repeat
= true;
1234 region
->repeat
= false;
1237 imax
= ARRAYLEN(touchactions
);
1238 while ((region
->action
== ACTION_NONE
) &&
1241 /* try to match with one of our touchregion screens */
1242 int len
= strlen(touchactions
[i
].s
);
1243 if (!strncmp(touchactions
[i
].s
, action
, len
)
1244 && *(action
+len
) == '|')
1245 region
->action
= touchactions
[i
].action
;
1248 if (region
->action
== ACTION_NONE
)
1249 return WPS_ERROR_INVALID_PARAM
;
1252 wps_data
->touchregion_count
++;
1253 return skip_end_of_line(wps_bufptr
);
1257 /* Parse a generic token from the given string. Return the length read */
1258 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1260 int skip
= 0, taglen
= 0, ret
;
1261 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1262 const struct wps_tag
*tag
;
1273 /* escaped characters */
1274 token
->type
= WPS_TOKEN_CHARACTER
;
1275 token
->value
.c
= *wps_bufptr
;
1277 wps_data
->num_tokens
++;
1281 /* conditional tag */
1282 token
->type
= WPS_TOKEN_CONDITIONAL
;
1284 condindex
[level
] = wps_data
->num_tokens
;
1285 numoptions
[level
] = 1;
1286 wps_data
->num_tokens
++;
1287 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1288 if (ret
< 0) return ret
;
1293 /* find what tag we have */
1294 for (tag
= all_tags
;
1295 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1298 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1299 token
->type
= tag
->type
;
1300 wps_data
->sublines
[wps_data
->num_sublines
].line_type
|=
1303 /* if the tag has a special parsing function, we call it */
1304 if (tag
->parse_func
)
1306 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1307 if (ret
< 0) return ret
;
1311 /* Some tags we don't want to save as tokens */
1312 if (tag
->type
== WPS_NO_TOKEN
)
1315 /* tags that start with 'F', 'I' or 'D' are for the next file */
1316 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1317 *(tag
->name
) == 'D')
1320 wps_data
->num_tokens
++;
1329 data is the pointer to the structure where the parsed WPS should be stored.
1331 wps_bufptr points to the string containing the WPS tags */
1332 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
)
1334 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1337 char *stringbuf
= data
->string_buffer
;
1338 int stringbuf_used
= 0;
1339 enum wps_parse_error fail
= PARSE_OK
;
1344 while(*wps_bufptr
&& !fail
&& data
->num_tokens
< WPS_MAX_TOKENS
- 1
1345 && data
->num_viewports
< WPS_MAX_VIEWPORTS
1346 && data
->num_lines
< WPS_MAX_LINES
)
1348 switch(*wps_bufptr
++)
1353 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1355 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1358 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1360 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1366 /* Alternating sublines separator */
1368 if (level
>= 0) /* there are unclosed conditionals */
1370 fail
= PARSE_FAIL_UNCLOSED_COND
;
1374 if (data
->num_sublines
+1 < WPS_MAX_SUBLINES
)
1375 wps_start_new_subline(data
);
1377 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1381 /* Conditional list start */
1383 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1385 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1389 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1390 lastcond
[level
] = data
->num_tokens
++;
1393 /* Conditional list end */
1395 if (level
< 0) /* not in a conditional, invalid char */
1397 fail
= PARSE_FAIL_INVALID_CHAR
;
1401 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1402 if (lastcond
[level
])
1403 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1406 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1410 lastcond
[level
] = 0;
1412 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1416 /* Conditional list option */
1418 if (level
< 0) /* not in a conditional, invalid char */
1420 fail
= PARSE_FAIL_INVALID_CHAR
;
1424 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1425 if (lastcond
[level
])
1426 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1429 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1433 lastcond
[level
] = data
->num_tokens
;
1434 numoptions
[level
]++;
1440 if (level
>= 0) /* there are unclosed conditionals */
1442 fail
= PARSE_FAIL_UNCLOSED_COND
;
1446 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1449 /* End of this line */
1451 if (level
>= 0) /* there are unclosed conditionals */
1453 fail
= PARSE_FAIL_UNCLOSED_COND
;
1458 wps_start_new_subline(data
);
1459 data
->num_lines
++; /* Start a new line */
1461 if ((data
->num_lines
< WPS_MAX_LINES
) &&
1462 (data
->num_sublines
< WPS_MAX_SUBLINES
))
1464 data
->lines
[data
->num_lines
].first_subline_idx
=
1467 data
->sublines
[data
->num_sublines
].first_token_idx
=
1476 unsigned int len
= 1;
1477 const char *string_start
= wps_bufptr
- 1;
1479 /* find the length of the string */
1480 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1481 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1482 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1483 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1489 /* look if we already have that string */
1493 for (i
= 0, str
= data
->strings
, found
= false;
1494 i
< data
->num_strings
&&
1495 !(found
= (strlen(*str
) == len
&&
1496 strncmp(string_start
, *str
, len
) == 0));
1498 /* If a matching string is found, found is true and i is
1499 the index of the string. If not, found is false */
1505 if (stringbuf_used
+ len
> STRING_BUFFER_SIZE
- 1
1506 || data
->num_strings
>= WPS_MAX_STRINGS
)
1508 /* too many strings or characters */
1509 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1513 strlcpy(stringbuf
, string_start
, len
+1);
1515 data
->strings
[data
->num_strings
] = stringbuf
;
1516 stringbuf
+= len
+ 1;
1517 stringbuf_used
+= len
+ 1;
1518 data
->tokens
[data
->num_tokens
].value
.i
=
1520 data
->num_strings
++;
1524 /* another occurrence of an existing string */
1525 data
->tokens
[data
->num_tokens
].value
.i
= i
;
1527 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1534 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1535 fail
= PARSE_FAIL_UNCLOSED_COND
;
1537 if (*wps_bufptr
&& !fail
)
1538 /* one of the limits of the while loop was exceeded */
1539 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1541 data
->viewports
[data
->num_viewports
].last_line
= data
->num_lines
- 1;
1543 /* We have finished with the last viewport, so increment count */
1544 data
->num_viewports
++;
1546 #if defined(DEBUG) || defined(SIMULATOR)
1547 print_debug_info(data
, fail
, line
);
1553 #ifdef HAVE_LCD_BITMAP
1554 /* Clear the WPS image cache */
1555 static void wps_images_clear(struct wps_data
*data
)
1558 /* set images to unloaded and not displayed */
1559 for (i
= 0; i
< MAX_IMAGES
; i
++)
1561 data
->img
[i
].loaded
= false;
1562 data
->img
[i
].display
= -1;
1563 data
->img
[i
].always_display
= false;
1564 data
->img
[i
].num_subimages
= 1;
1569 /* initial setup of wps_data */
1570 void wps_data_init(struct wps_data
*wps_data
)
1572 #ifdef HAVE_LCD_BITMAP
1573 wps_images_clear(wps_data
);
1574 wps_data
->wps_sb_tag
= false;
1575 wps_data
->show_sb_on_wps
= false;
1576 wps_data
->img_buf_ptr
= wps_data
->img_buf
; /* where in image buffer */
1577 wps_data
->img_buf_free
= IMG_BUFSIZE
; /* free space in image buffer */
1578 wps_data
->peak_meter_enabled
= false;
1580 wps_data
->progressbar_count
= 0;
1581 #else /* HAVE_LCD_CHARCELLS */
1583 for (i
= 0; i
< 8; i
++)
1585 wps_data
->wps_progress_pat
[i
] = 0;
1587 wps_data
->full_line_progressbar
= false;
1589 wps_data
->button_time_volume
= 0;
1590 wps_data
->wps_loaded
= false;
1593 static void wps_reset(struct wps_data
*data
)
1595 #ifdef HAVE_REMOTE_LCD
1596 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1598 memset(data
, 0, sizeof(*data
));
1599 wps_data_init(data
);
1600 #ifdef HAVE_REMOTE_LCD
1601 data
->remote_wps
= rwps
;
1605 #ifdef HAVE_LCD_BITMAP
1607 static bool load_wps_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1609 char img_path
[MAX_PATH
];
1610 struct bitmap
*bitmap
;
1613 for (n
= 0; n
< BACKDROP_BMP
; n
++)
1617 get_image_filename(bmp_names
[n
], bmpdir
,
1618 img_path
, sizeof(img_path
));
1620 if (n
>= PROGRESSBAR_BMP
) {
1621 /* progressbar bitmap */
1622 bitmap
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].bm
;
1623 loaded
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].have_bitmap_pb
;
1625 /* regular bitmap */
1626 bitmap
= &wps_data
->img
[n
].bm
;
1627 loaded
= &wps_data
->img
[n
].loaded
;
1630 /* load the image */
1631 bitmap
->data
= wps_data
->img_buf_ptr
;
1632 if (load_bitmap(wps_data
, img_path
, bitmap
))
1636 /* Calculate and store height if this image has sub-images */
1638 wps_data
->img
[n
].subimage_height
= wps_data
->img
[n
].bm
.height
/
1639 wps_data
->img
[n
].num_subimages
;
1643 /* Abort if we can't load an image */
1644 DEBUGF("ERR: Failed to load image %d - %s\n",n
,img_path
);
1650 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1651 if (bmp_names
[BACKDROP_BMP
])
1653 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1654 img_path
, sizeof(img_path
));
1656 #if defined(HAVE_REMOTE_LCD)
1657 /* We only need to check LCD type if there is a remote LCD */
1658 if (!wps_data
->remote_wps
)
1661 /* Load backdrop for the main LCD */
1662 if (!load_wps_backdrop(img_path
))
1665 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1668 /* Load backdrop for the remote LCD */
1669 if (!load_remote_wps_backdrop(img_path
))
1674 #endif /* has backdrop support */
1676 /* If we got here, everything was OK */
1680 #endif /* HAVE_LCD_BITMAP */
1682 /* to setup up the wps-data from a format-buffer (isfile = false)
1683 from a (wps-)file (isfile = true)*/
1684 bool wps_data_load(struct wps_data
*wps_data
,
1685 struct screen
*display
,
1689 #ifdef HAVE_ALBUMART
1690 struct mp3entry
*curtrack
;
1693 int wps_uses_albumart
= wps_data
->wps_uses_albumart
;
1694 int albumart_max_height
= wps_data
->albumart_max_height
;
1695 int albumart_max_width
= wps_data
->albumart_max_width
;
1697 if (!wps_data
|| !buf
)
1700 wps_reset(wps_data
);
1702 /* Initialise the first (default) viewport */
1703 wps_data
->viewports
[0].vp
.x
= 0;
1704 wps_data
->viewports
[0].vp
.width
= display
->getwidth();
1705 wps_data
->viewports
[0].vp
.height
= display
->getheight();
1706 switch (statusbar_position(display
->screen_type
))
1709 wps_data
->viewports
[0].vp
.y
= 0;
1712 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
1713 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
1715 case STATUSBAR_BOTTOM
:
1716 wps_data
->viewports
[0].vp
.y
= 0;
1717 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
1720 #ifdef HAVE_LCD_BITMAP
1721 wps_data
->viewports
[0].vp
.font
= FONT_UI
;
1722 wps_data
->viewports
[0].vp
.drawmode
= DRMODE_SOLID
;
1725 if (display
->depth
> 1)
1727 wps_data
->viewports
[0].vp
.fg_pattern
= display
->get_foreground();
1728 wps_data
->viewports
[0].vp
.bg_pattern
= display
->get_background();
1733 return wps_parse(wps_data
, buf
);
1738 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1739 * wants to be a virtual file. Feel free to modify dirbrowse()
1740 * if you're feeling brave.
1743 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1745 global_settings
.wps_file
[0] = 0;
1749 #ifdef HAVE_REMOTE_LCD
1750 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1752 global_settings
.rwps_file
[0] = 0;
1756 #endif /* __PCTOOL__ */
1758 int fd
= open_utf8(buf
, O_RDONLY
);
1763 /* get buffer space from the plugin buffer */
1764 size_t buffersize
= 0;
1765 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1770 /* copy the file's content to the buffer for parsing,
1771 ensuring that every line ends with a newline char. */
1772 unsigned int start
= 0;
1773 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1775 start
+= strlen(wps_buffer
+ start
);
1776 if (start
< buffersize
- 1)
1778 wps_buffer
[start
++] = '\n';
1779 wps_buffer
[start
] = 0;
1788 #ifdef HAVE_LCD_BITMAP
1789 /* Set all filename pointers to NULL */
1790 memset(bmp_names
, 0, sizeof(bmp_names
));
1793 /* parse the WPS source */
1794 if (!wps_parse(wps_data
, wps_buffer
)) {
1795 wps_reset(wps_data
);
1799 wps_data
->wps_loaded
= true;
1801 #ifdef HAVE_LCD_BITMAP
1802 /* get the bitmap dir */
1803 char bmpdir
[MAX_PATH
];
1804 char *dot
= strrchr(buf
, '.');
1806 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1808 /* load the bitmaps that were found by the parsing */
1809 if (!load_wps_bitmaps(wps_data
, bmpdir
)) {
1810 wps_reset(wps_data
);
1814 #ifdef HAVE_ALBUMART
1815 status
= audio_status();
1816 if (((!wps_uses_albumart
&& wps_data
->wps_uses_albumart
) ||
1817 (wps_data
->wps_uses_albumart
&&
1818 (albumart_max_height
!= wps_data
->albumart_max_height
||
1819 albumart_max_width
!= wps_data
->albumart_max_width
))) &&
1820 status
& AUDIO_STATUS_PLAY
)
1822 curtrack
= audio_current_track();
1823 offset
= curtrack
->offset
;
1825 if (!(status
& AUDIO_STATUS_PAUSE
))
1833 int wps_subline_index(struct wps_data
*data
, int line
, int subline
)
1835 return data
->lines
[line
].first_subline_idx
+ subline
;
1838 int wps_first_token_index(struct wps_data
*data
, int line
, int subline
)
1840 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1841 return data
->sublines
[first_subline_idx
+ subline
].first_token_idx
;
1844 int wps_last_token_index(struct wps_data
*data
, int line
, int subline
)
1846 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1847 int idx
= first_subline_idx
+ subline
;
1848 if (idx
< data
->num_sublines
- 1)
1850 /* This subline ends where the next begins */
1851 return data
->sublines
[idx
+1].first_token_idx
- 1;
1855 /* The last subline goes to the end */
1856 return data
->num_tokens
- 1;