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 #ifdef HAVE_LCD_BITMAP
83 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
85 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
88 #define PROGRESSBAR_BMP MAX_IMAGES
89 #define BACKDROP_BMP (MAX_BITMAPS-1)
91 /* pointers to the bitmap filenames in the WPS source */
92 static const char *bmp_names
[MAX_BITMAPS
];
94 #endif /* HAVE_LCD_BITMAP */
96 #if defined(DEBUG) || defined(SIMULATOR)
97 /* debugging function */
98 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
101 static void wps_reset(struct wps_data
*data
);
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
109 wps_bufptr points to the char following the tag (i.e. where
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
114 struct wps_token
*token
, struct wps_data
*wps_data
);
117 enum wps_token_type type
;
119 unsigned char refresh_type
;
120 const wps_tag_parse_func parse_func
;
122 static int skip_end_of_line(const char *wps_bufptr
);
123 /* prototypes of all special parse functions : */
124 static int parse_timeout(const char *wps_bufptr
,
125 struct wps_token
*token
, struct wps_data
*wps_data
);
126 static int parse_progressbar(const char *wps_bufptr
,
127 struct wps_token
*token
, struct wps_data
*wps_data
);
128 static int parse_dir_level(const char *wps_bufptr
,
129 struct wps_token
*token
, struct wps_data
*wps_data
);
130 static int parse_setting(const char *wps_bufptr
,
131 struct wps_token
*token
, struct wps_data
*wps_data
);
133 #ifdef HAVE_LCD_BITMAP
134 static int parse_viewport_display(const char *wps_bufptr
,
135 struct wps_token
*token
, struct wps_data
*wps_data
);
136 static int parse_viewport(const char *wps_bufptr
,
137 struct wps_token
*token
, struct wps_data
*wps_data
);
138 static int parse_statusbar_enable(const char *wps_bufptr
,
139 struct wps_token
*token
, struct wps_data
*wps_data
);
140 static int parse_statusbar_disable(const char *wps_bufptr
,
141 struct wps_token
*token
, struct wps_data
*wps_data
);
142 static int parse_image_display(const char *wps_bufptr
,
143 struct wps_token
*token
, struct wps_data
*wps_data
);
144 static int parse_image_load(const char *wps_bufptr
,
145 struct wps_token
*token
, struct wps_data
*wps_data
);
146 #endif /*HAVE_LCD_BITMAP */
147 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
148 static int parse_image_special(const char *wps_bufptr
,
149 struct wps_token
*token
, struct wps_data
*wps_data
);
152 static int parse_albumart_load(const char *wps_bufptr
,
153 struct wps_token
*token
, struct wps_data
*wps_data
);
154 static int parse_albumart_conditional(const char *wps_bufptr
,
155 struct wps_token
*token
, struct wps_data
*wps_data
);
156 #endif /* HAVE_ALBUMART */
157 #ifdef HAVE_TOUCHSCREEN
158 static int parse_touchregion(const char *wps_bufptr
,
159 struct wps_token
*token
, struct wps_data
*wps_data
);
161 static int fulline_tag_not_supported(const char *wps_bufptr
,
162 struct wps_token
*token
, struct wps_data
*wps_data
)
164 (void)token
; (void)wps_data
;
165 return skip_end_of_line(wps_bufptr
);
167 #define parse_touchregion fulline_tag_not_supported
170 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
172 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
175 /* array of available tags - those with more characters have to go first
176 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
177 static const struct wps_tag all_tags
[] = {
179 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
180 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
181 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
183 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
184 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
185 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
186 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
187 #if CONFIG_CHARGING >= CHARGING_MONITOR
188 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
191 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
194 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
195 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
196 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
197 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
198 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
199 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
200 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
209 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
210 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
211 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
212 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
215 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
216 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
217 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
218 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
219 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
224 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
228 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
229 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
230 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
231 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
232 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
233 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
234 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
236 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
237 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
240 /* current metadata */
241 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
243 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
244 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
245 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
257 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
258 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
268 #if (CONFIG_CODEC != MAS3507D)
269 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
272 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
273 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
276 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
278 #ifdef HAS_REMOTE_BUTTON_HOLD
279 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
281 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
284 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
285 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
286 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
289 #ifdef HAVE_LCD_BITMAP
290 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
292 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
293 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
295 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
298 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
300 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
301 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
302 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
303 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
305 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
306 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
307 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
308 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
311 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
312 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
313 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
316 #if CONFIG_CODEC == SWCODEC
317 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
318 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
321 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
322 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
324 #ifdef HAVE_LCD_BITMAP
325 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
326 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
328 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
330 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
331 parse_image_display
},
333 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
335 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
336 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
,
337 parse_albumart_conditional
},
340 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
341 parse_viewport_display
},
342 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
344 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
345 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
349 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
, parse_setting
},
351 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
352 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
354 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
355 /* the array MUST end with an empty string (first char is \0) */
358 /* Returns the number of chars that should be skipped to jump
359 immediately after the first eol, i.e. to the start of the next line */
360 static int skip_end_of_line(const char *wps_bufptr
)
364 while(*(wps_bufptr
+ skip
) != '\n')
369 /* Starts a new subline in the current line during parsing */
370 static void wps_start_new_subline(struct wps_data
*data
)
372 data
->num_sublines
++;
373 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
374 data
->lines
[data
->num_lines
].num_sublines
++;
377 #ifdef HAVE_LCD_BITMAP
379 static int parse_statusbar_enable(const char *wps_bufptr
,
380 struct wps_token
*token
,
381 struct wps_data
*wps_data
)
383 (void)token
; /* Kill warnings */
384 wps_data
->wps_sb_tag
= true;
385 wps_data
->show_sb_on_wps
= true;
386 if (wps_data
->viewports
[0].vp
.y
== 0)
388 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
389 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
391 return skip_end_of_line(wps_bufptr
);
394 static int parse_statusbar_disable(const char *wps_bufptr
,
395 struct wps_token
*token
,
396 struct wps_data
*wps_data
)
398 (void)token
; /* Kill warnings */
399 wps_data
->wps_sb_tag
= true;
400 wps_data
->show_sb_on_wps
= false;
401 if (wps_data
->viewports
[0].vp
.y
== STATUSBAR_HEIGHT
)
403 wps_data
->viewports
[0].vp
.y
= 0;
404 wps_data
->viewports
[0].vp
.height
+= STATUSBAR_HEIGHT
;
406 return skip_end_of_line(wps_bufptr
);
409 static bool load_bitmap(struct wps_data
*wps_data
,
414 #ifdef HAVE_REMOTE_LCD
415 if (wps_data
->remote_wps
)
416 format
= FORMAT_ANY
|FORMAT_REMOTE
;
419 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
421 int ret
= read_bmp_file(filename
, bm
,
422 wps_data
->img_buf_free
,
429 /* Always consume an even number of bytes */
431 wps_data
->img_buf_ptr
+= ret
;
432 wps_data
->img_buf_free
-= ret
;
440 static int get_image_id(int c
)
442 if(c
>= 'a' && c
<= 'z')
444 else if(c
>= 'A' && c
<= 'Z')
450 static char *get_image_filename(const char *start
, const char* bmpdir
,
451 char *buf
, int buf_size
)
453 const char *end
= strchr(start
, '|');
455 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
461 int bmpdirlen
= strlen(bmpdir
);
464 buf
[bmpdirlen
] = '/';
465 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
466 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
471 static int parse_image_display(const char *wps_bufptr
,
472 struct wps_token
*token
,
473 struct wps_data
*wps_data
)
476 int n
= get_image_id(wps_bufptr
[0]);
481 /* invalid picture display tag */
482 return WPS_ERROR_INVALID_PARAM
;
485 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
488 if (subimage
>= wps_data
->img
[n
].num_subimages
)
489 return WPS_ERROR_INVALID_PARAM
;
491 /* Store sub-image number to display in high bits */
492 token
->value
.i
= n
| (subimage
<< 8);
493 return 2; /* We have consumed 2 bytes */
496 return 1; /* We have consumed 1 byte */
500 static int parse_image_load(const char *wps_bufptr
,
501 struct wps_token
*token
,
502 struct wps_data
*wps_data
)
505 const char *ptr
= wps_bufptr
;
507 const char* filename
;
512 /* format: %x|n|filename.bmp|x|y|
513 or %xl|n|filename.bmp|x|y|
514 or %xl|n|filename.bmp|x|y|num_subimages|
518 return WPS_ERROR_INVALID_PARAM
;
522 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
523 return WPS_ERROR_INVALID_PARAM
;
525 /* Check there is a terminating | */
527 return WPS_ERROR_INVALID_PARAM
;
529 /* get the image ID */
530 n
= get_image_id(*id
);
532 /* check the image number and load state */
533 if(n
< 0 || n
>= MAX_IMAGES
|| wps_data
->img
[n
].loaded
)
535 /* Invalid image ID */
536 return WPS_ERROR_INVALID_PARAM
;
539 /* save a pointer to the filename */
540 bmp_names
[n
] = filename
;
542 wps_data
->img
[n
].x
= x
;
543 wps_data
->img
[n
].y
= y
;
545 /* save current viewport */
546 wps_data
->img
[n
].vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
548 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
550 wps_data
->img
[n
].always_display
= true;
554 /* Parse the (optional) number of sub-images */
556 newline
= strchr(ptr
, '\n');
557 pos
= strchr(ptr
, '|');
558 if (pos
&& pos
< newline
)
559 wps_data
->img
[n
].num_subimages
= atoi(ptr
);
561 if (wps_data
->img
[n
].num_subimages
<= 0)
562 return WPS_ERROR_INVALID_PARAM
;
565 /* Skip the rest of the line */
566 return skip_end_of_line(wps_bufptr
);
569 static int parse_viewport_display(const char *wps_bufptr
,
570 struct wps_token
*token
,
571 struct wps_data
*wps_data
)
574 char letter
= wps_bufptr
[0];
576 if (letter
< 'a' || letter
> 'z')
578 /* invalid viewport tag */
579 return WPS_ERROR_INVALID_PARAM
;
581 token
->value
.i
= letter
;
585 static int parse_viewport(const char *wps_bufptr
,
586 struct wps_token
*token
,
587 struct wps_data
*wps_data
)
589 (void)token
; /* Kill warnings */
590 const char *ptr
= wps_bufptr
;
593 #ifdef HAVE_REMOTE_LCD
594 wps_data
->remote_wps
? SCREEN_REMOTE
:
598 if (wps_data
->num_viewports
>= WPS_MAX_VIEWPORTS
)
599 return WPS_ERROR_INVALID_PARAM
;
601 wps_data
->num_viewports
++;
602 /* check for the optional letter to signify its a hideable viewport */
603 /* %Vl|<label>|<rest of tags>| */
604 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= 0;
610 char label
= *(ptr
+2);
611 if (label
>= 'a' && label
<= 'z')
613 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= VP_DRAW_HIDEABLE
;
614 wps_data
->viewports
[wps_data
->num_viewports
].label
= label
;
617 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
622 return WPS_ERROR_INVALID_PARAM
;
625 struct viewport
*vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
626 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
628 /* Set the defaults for fields not user-specified */
629 vp
->drawmode
= DRMODE_SOLID
;
631 if (!(ptr
= viewport_parse_viewport(vp
, screen
, ptr
, '|')))
632 return WPS_ERROR_INVALID_PARAM
;
634 /* Check for trailing | */
636 return WPS_ERROR_INVALID_PARAM
;
639 wps_data
->viewports
[wps_data
->num_viewports
-1].last_line
= wps_data
->num_lines
- 1;
641 wps_data
->viewports
[wps_data
->num_viewports
].first_line
= wps_data
->num_lines
;
643 if (wps_data
->num_sublines
< WPS_MAX_SUBLINES
)
645 wps_data
->lines
[wps_data
->num_lines
].first_subline_idx
=
646 wps_data
->num_sublines
;
648 wps_data
->sublines
[wps_data
->num_sublines
].first_token_idx
=
649 wps_data
->num_tokens
;
652 /* Skip the rest of the line */
653 return skip_end_of_line(wps_bufptr
);
656 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
657 static int parse_image_special(const char *wps_bufptr
,
658 struct wps_token
*token
,
659 struct wps_data
*wps_data
)
661 (void)wps_data
; /* kill warning */
663 const char *pos
= NULL
;
666 pos
= strchr(wps_bufptr
+ 1, '|');
667 newline
= strchr(wps_bufptr
, '\n');
670 return WPS_ERROR_INVALID_PARAM
;
672 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
674 /* format: %X|filename.bmp| */
675 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
679 /* Skip the rest of the line */
680 return skip_end_of_line(wps_bufptr
);
684 #endif /* HAVE_LCD_BITMAP */
686 static int parse_setting(const char *wps_bufptr
,
687 struct wps_token
*token
,
688 struct wps_data
*wps_data
)
691 const char *ptr
= wps_bufptr
;
695 /* Find the setting's cfg_name */
697 return WPS_ERROR_INVALID_PARAM
;
699 end
= strchr(ptr
,'|');
701 return WPS_ERROR_INVALID_PARAM
;
703 /* Find the setting */
704 for (i
=0; i
<nb_settings
; i
++)
705 if (settings
[i
].cfg_name
&&
706 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
707 /* prevent matches on cfg_name prefixes */
708 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
710 if (i
== nb_settings
)
711 return WPS_ERROR_INVALID_PARAM
;
713 /* Store the setting number */
716 /* Skip the rest of the line */
721 static int parse_dir_level(const char *wps_bufptr
,
722 struct wps_token
*token
,
723 struct wps_data
*wps_data
)
725 char val
[] = { *wps_bufptr
, '\0' };
726 token
->value
.i
= atoi(val
);
727 (void)wps_data
; /* Kill warnings */
731 static int parse_timeout(const char *wps_bufptr
,
732 struct wps_token
*token
,
733 struct wps_data
*wps_data
)
737 bool have_point
= false;
738 bool have_tenth
= false;
740 (void)wps_data
; /* Kill the warning */
742 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
744 if (*wps_bufptr
!= '.')
747 val
+= *wps_bufptr
- '0';
763 if (have_tenth
== false)
766 if (val
== 0 && skip
== 0)
768 /* decide what to do if no value was specified */
771 case WPS_TOKEN_SUBLINE_TIMEOUT
:
773 case WPS_TOKEN_BUTTON_VOLUME
:
778 token
->value
.i
= val
;
783 static int parse_progressbar(const char *wps_bufptr
,
784 struct wps_token
*token
,
785 struct wps_data
*wps_data
)
787 (void)token
; /* Kill warnings */
788 /* %pb or %pb|filename|x|y|width|height|
789 using - for any of the params uses "sane" values */
790 #ifdef HAVE_LCD_BITMAP
798 const char *filename
;
799 int x
, y
, height
, width
;
801 const char *ptr
= wps_bufptr
;
802 struct progressbar
*pb
;
803 struct viewport
*vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
805 int font_height
= font_get(vp
->font
)->height
;
809 int line_num
= wps_data
->num_lines
-
810 wps_data
->viewports
[wps_data
->num_viewports
].first_line
;
812 if (wps_data
->progressbar_count
>= MAX_PROGRESSBARS
)
813 return WPS_ERROR_INVALID_PARAM
;
815 pb
= &wps_data
->progressbar
[wps_data
->progressbar_count
];
816 pb
->have_bitmap_pb
= false;
818 if (*wps_bufptr
!= '|') /* regular old style */
821 pb
->width
= vp
->width
;
822 pb
->height
= SYSFONT_HEIGHT
-2;
823 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
825 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
826 wps_data
->progressbar_count
++;
829 ptr
= wps_bufptr
+ 1;
831 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
832 &x
, &y
, &width
, &height
)))
833 return WPS_ERROR_INVALID_PARAM
;
835 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
836 bmp_names
[PROGRESSBAR_BMP
+wps_data
->progressbar_count
] = filename
;
838 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
843 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
845 /* A zero width causes a divide-by-zero error later, so reject it */
847 return WPS_ERROR_INVALID_PARAM
;
852 pb
->width
= vp
->width
- pb
->x
;
854 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
856 /* A zero height makes no sense - reject it */
858 return WPS_ERROR_INVALID_PARAM
;
863 pb
->height
= font_height
;
865 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
868 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
870 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
871 wps_data
->progressbar_count
++;
873 /* Skip the rest of the line */
874 return skip_end_of_line(wps_bufptr
)-1;
877 if (*(wps_bufptr
-1) == 'f')
878 wps_data
->full_line_progressbar
= true;
880 wps_data
->full_line_progressbar
= false;
888 static int parse_albumart_load(const char *wps_bufptr
,
889 struct wps_token
*token
,
890 struct wps_data
*wps_data
)
892 const char *_pos
, *newline
;
894 (void)token
; /* silence warning */
896 /* reset albumart info in wps */
897 wps_data
->albumart_max_width
= -1;
898 wps_data
->albumart_max_height
= -1;
899 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
900 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
902 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
904 newline
= strchr(wps_bufptr
, '\n');
906 /* initial validation and parsing of x and y components */
907 if (*wps_bufptr
!= '|')
908 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
910 _pos
= wps_bufptr
+ 1;
912 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|@ */
913 wps_data
->albumart_x
= atoi(_pos
);
915 _pos
= strchr(_pos
, '|');
916 if (!_pos
|| _pos
> newline
|| !isdigit(*(++_pos
)))
917 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
919 wps_data
->albumart_y
= atoi(_pos
);
921 _pos
= strchr(_pos
, '|');
922 if (!_pos
|| _pos
> newline
)
923 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
926 /* parsing width field */
930 /* apply each modifier in turn */
937 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_LEFT
;
941 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
;
946 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
961 /* extract max width data */
964 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
965 return WPS_ERROR_INVALID_PARAM
;
967 wps_data
->albumart_max_width
= atoi(_pos
);
969 _pos
= strchr(_pos
, '|');
970 if (!_pos
|| _pos
> newline
)
971 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
972 e.g. %Cl|7|59|200\n */
975 /* parsing height field */
979 /* apply each modifier in turn */
986 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_TOP
;
990 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
;
995 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1003 /* simply ignored */
1010 /* extract max height data */
1013 if (!isdigit(*_pos
))
1014 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1016 wps_data
->albumart_max_height
= atoi(_pos
);
1018 _pos
= strchr(_pos
, '|');
1019 if (!_pos
|| _pos
> newline
)
1020 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1021 e.g. %Cl|7|59|200|200\n */
1024 /* if we got here, we parsed everything ok .. ! */
1025 if (wps_data
->albumart_max_width
< 0)
1026 wps_data
->albumart_max_width
= 0;
1027 else if (wps_data
->albumart_max_width
> LCD_WIDTH
)
1028 wps_data
->albumart_max_width
= LCD_WIDTH
;
1030 if (wps_data
->albumart_max_height
< 0)
1031 wps_data
->albumart_max_height
= 0;
1032 else if (wps_data
->albumart_max_height
> LCD_HEIGHT
)
1033 wps_data
->albumart_max_height
= LCD_HEIGHT
;
1035 wps_data
->wps_uses_albumart
= WPS_ALBUMART_LOAD
;
1037 /* Skip the rest of the line */
1038 return skip_end_of_line(wps_bufptr
);
1041 static int parse_albumart_conditional(const char *wps_bufptr
,
1042 struct wps_token
*token
,
1043 struct wps_data
*wps_data
)
1045 struct wps_token
*prevtoken
= token
;
1047 if (wps_data
->num_tokens
>= 1 && prevtoken
->type
== WPS_TOKEN_CONDITIONAL
)
1049 /* This %C is part of a %?C construct.
1050 It's either %?C<blah> or %?Cn<blah> */
1051 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1052 if (*wps_bufptr
== 'n' && *(wps_bufptr
+ 1) == '<')
1057 else if (*wps_bufptr
== '<')
1063 token
->type
= WPS_NO_TOKEN
;
1069 /* This %C tag is in a conditional construct. */
1070 wps_data
->albumart_cond_index
= condindex
[level
];
1074 #endif /* HAVE_ALBUMART */
1076 #ifdef HAVE_TOUCHSCREEN
1078 struct touchaction
{char* s
; int action
;};
1079 static struct touchaction touchactions
[] = {
1080 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1081 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1082 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1083 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1084 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1085 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1086 {"playlist", ACTION_WPS_VIEW_PLAYLIST
},
1088 static int parse_touchregion(const char *wps_bufptr
,
1089 struct wps_token
*token
, struct wps_data
*wps_data
)
1093 struct touchregion
*region
;
1094 const char *ptr
= wps_bufptr
;
1098 /* format: %T|x|y|width|height|action|
1099 * if action starts with & the area must be held to happen
1101 * play - play/pause playback
1102 * stop - stop playback, exit the wps
1105 * ffwd - seek forward
1106 * rwd - seek backwards
1107 * menu - go back to the main menu
1108 * browse - go back to the file/db browser
1109 * shuffle - toggle shuffle mode
1110 * repmode - cycle the repeat mode
1111 * quickscreen - go into the quickscreen
1112 * contextmenu - open the context menu
1116 if ((wps_data
->touchregion_count
+1 >= MAX_TOUCHREGIONS
) || (*ptr
!= '|'))
1117 return WPS_ERROR_INVALID_PARAM
;
1120 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1121 return WPS_ERROR_INVALID_PARAM
;
1123 /* Check there is a terminating | */
1125 return WPS_ERROR_INVALID_PARAM
;
1127 /* should probably do some bounds checking here with the viewport... but later */
1128 region
= &wps_data
->touchregion
[wps_data
->touchregion_count
];
1129 region
->action
= ACTION_NONE
;
1134 region
->wvp
= &wps_data
->viewports
[wps_data
->num_viewports
];
1139 region
->repeat
= true;
1142 region
->repeat
= false;
1144 imax
= ARRAYLEN(touchactions
);
1145 while ((region
->action
== ACTION_NONE
) &&
1148 /* try to match with one of our touchregion screens */
1149 int len
= strlen(touchactions
[i
].s
);
1150 if (!strncmp(touchactions
[i
].s
, action
, len
)
1151 && *(action
+len
) == '|')
1152 region
->action
= touchactions
[i
].action
;
1155 if (region
->action
== ACTION_NONE
)
1156 return WPS_ERROR_INVALID_PARAM
;
1157 wps_data
->touchregion_count
++;
1158 return skip_end_of_line(wps_bufptr
);
1162 /* Parse a generic token from the given string. Return the length read */
1163 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1165 int skip
= 0, taglen
= 0, ret
;
1166 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1167 const struct wps_tag
*tag
;
1178 /* escaped characters */
1179 token
->type
= WPS_TOKEN_CHARACTER
;
1180 token
->value
.c
= *wps_bufptr
;
1182 wps_data
->num_tokens
++;
1186 /* conditional tag */
1187 token
->type
= WPS_TOKEN_CONDITIONAL
;
1189 condindex
[level
] = wps_data
->num_tokens
;
1190 numoptions
[level
] = 1;
1191 wps_data
->num_tokens
++;
1192 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1193 if (ret
< 0) return ret
;
1198 /* find what tag we have */
1199 for (tag
= all_tags
;
1200 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1203 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1204 token
->type
= tag
->type
;
1205 wps_data
->sublines
[wps_data
->num_sublines
].line_type
|=
1208 /* if the tag has a special parsing function, we call it */
1209 if (tag
->parse_func
)
1211 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1212 if (ret
< 0) return ret
;
1216 /* Some tags we don't want to save as tokens */
1217 if (tag
->type
== WPS_NO_TOKEN
)
1220 /* tags that start with 'F', 'I' or 'D' are for the next file */
1221 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1222 *(tag
->name
) == 'D')
1225 wps_data
->num_tokens
++;
1234 data is the pointer to the structure where the parsed WPS should be stored.
1236 wps_bufptr points to the string containing the WPS tags */
1237 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
)
1239 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1242 char *stringbuf
= data
->string_buffer
;
1243 int stringbuf_used
= 0;
1244 enum wps_parse_error fail
= PARSE_OK
;
1249 while(*wps_bufptr
&& !fail
&& data
->num_tokens
< WPS_MAX_TOKENS
- 1
1250 && data
->num_viewports
< WPS_MAX_VIEWPORTS
1251 && data
->num_lines
< WPS_MAX_LINES
)
1253 switch(*wps_bufptr
++)
1258 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1260 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1263 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1265 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1271 /* Alternating sublines separator */
1273 if (level
>= 0) /* there are unclosed conditionals */
1275 fail
= PARSE_FAIL_UNCLOSED_COND
;
1279 if (data
->num_sublines
+1 < WPS_MAX_SUBLINES
)
1280 wps_start_new_subline(data
);
1282 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1286 /* Conditional list start */
1288 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1290 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1294 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1295 lastcond
[level
] = data
->num_tokens
++;
1298 /* Conditional list end */
1300 if (level
< 0) /* not in a conditional, invalid char */
1302 fail
= PARSE_FAIL_INVALID_CHAR
;
1306 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1307 if (lastcond
[level
])
1308 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1311 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1315 lastcond
[level
] = 0;
1317 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1321 /* Conditional list option */
1323 if (level
< 0) /* not in a conditional, invalid char */
1325 fail
= PARSE_FAIL_INVALID_CHAR
;
1329 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1330 if (lastcond
[level
])
1331 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1334 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1338 lastcond
[level
] = data
->num_tokens
;
1339 numoptions
[level
]++;
1345 if (level
>= 0) /* there are unclosed conditionals */
1347 fail
= PARSE_FAIL_UNCLOSED_COND
;
1351 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1354 /* End of this line */
1356 if (level
>= 0) /* there are unclosed conditionals */
1358 fail
= PARSE_FAIL_UNCLOSED_COND
;
1363 wps_start_new_subline(data
);
1364 data
->num_lines
++; /* Start a new line */
1366 if ((data
->num_lines
< WPS_MAX_LINES
) &&
1367 (data
->num_sublines
< WPS_MAX_SUBLINES
))
1369 data
->lines
[data
->num_lines
].first_subline_idx
=
1372 data
->sublines
[data
->num_sublines
].first_token_idx
=
1381 unsigned int len
= 1;
1382 const char *string_start
= wps_bufptr
- 1;
1384 /* find the length of the string */
1385 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1386 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1387 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1388 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1394 /* look if we already have that string */
1398 for (i
= 0, str
= data
->strings
, found
= false;
1399 i
< data
->num_strings
&&
1400 !(found
= (strlen(*str
) == len
&&
1401 strncmp(string_start
, *str
, len
) == 0));
1403 /* If a matching string is found, found is true and i is
1404 the index of the string. If not, found is false */
1410 if (stringbuf_used
+ len
> STRING_BUFFER_SIZE
- 1
1411 || data
->num_strings
>= WPS_MAX_STRINGS
)
1413 /* too many strings or characters */
1414 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1418 strlcpy(stringbuf
, string_start
, len
+1);
1420 data
->strings
[data
->num_strings
] = stringbuf
;
1421 stringbuf
+= len
+ 1;
1422 stringbuf_used
+= len
+ 1;
1423 data
->tokens
[data
->num_tokens
].value
.i
=
1425 data
->num_strings
++;
1429 /* another occurrence of an existing string */
1430 data
->tokens
[data
->num_tokens
].value
.i
= i
;
1432 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1439 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1440 fail
= PARSE_FAIL_UNCLOSED_COND
;
1442 if (*wps_bufptr
&& !fail
)
1443 /* one of the limits of the while loop was exceeded */
1444 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1446 data
->viewports
[data
->num_viewports
].last_line
= data
->num_lines
- 1;
1448 /* We have finished with the last viewport, so increment count */
1449 data
->num_viewports
++;
1451 #if defined(DEBUG) || defined(SIMULATOR)
1452 print_debug_info(data
, fail
, line
);
1458 static void wps_reset(struct wps_data
*data
)
1460 #ifdef HAVE_REMOTE_LCD
1461 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1463 memset(data
, 0, sizeof(*data
));
1464 skin_data_init(data
);
1465 #ifdef HAVE_REMOTE_LCD
1466 data
->remote_wps
= rwps
;
1470 #ifdef HAVE_LCD_BITMAP
1472 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1474 char img_path
[MAX_PATH
];
1475 struct bitmap
*bitmap
;
1478 for (n
= 0; n
< BACKDROP_BMP
; n
++)
1482 get_image_filename(bmp_names
[n
], bmpdir
,
1483 img_path
, sizeof(img_path
));
1485 if (n
>= PROGRESSBAR_BMP
) {
1486 /* progressbar bitmap */
1487 bitmap
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].bm
;
1488 loaded
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].have_bitmap_pb
;
1490 /* regular bitmap */
1491 bitmap
= &wps_data
->img
[n
].bm
;
1492 loaded
= &wps_data
->img
[n
].loaded
;
1495 /* load the image */
1496 bitmap
->data
= wps_data
->img_buf_ptr
;
1497 if (load_bitmap(wps_data
, img_path
, bitmap
))
1501 /* Calculate and store height if this image has sub-images */
1503 wps_data
->img
[n
].subimage_height
= wps_data
->img
[n
].bm
.height
/
1504 wps_data
->img
[n
].num_subimages
;
1508 /* Abort if we can't load an image */
1509 DEBUGF("ERR: Failed to load image %d - %s\n",n
,img_path
);
1515 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1516 if (bmp_names
[BACKDROP_BMP
])
1518 int screen
= SCREEN_MAIN
;
1519 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1520 img_path
, sizeof(img_path
));
1521 #if defined(HAVE_REMOTE_LCD)
1522 /* We only need to check LCD type if there is a remote LCD */
1523 if (wps_data
->remote_wps
)
1524 screen
= SCREEN_REMOTE
;
1526 screens
[screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1528 #endif /* has backdrop support */
1530 /* If we got here, everything was OK */
1534 #endif /* HAVE_LCD_BITMAP */
1536 /* to setup up the wps-data from a format-buffer (isfile = false)
1537 from a (wps-)file (isfile = true)*/
1538 bool skin_data_load(struct wps_data
*wps_data
,
1539 struct screen
*display
,
1543 #ifdef HAVE_ALBUMART
1544 struct mp3entry
*curtrack
;
1547 int wps_uses_albumart
= wps_data
->wps_uses_albumart
;
1548 int albumart_max_height
= wps_data
->albumart_max_height
;
1549 int albumart_max_width
= wps_data
->albumart_max_width
;
1551 if (!wps_data
|| !buf
)
1554 wps_reset(wps_data
);
1556 /* Initialise the first (default) viewport */
1557 wps_data
->viewports
[0].vp
.x
= 0;
1558 wps_data
->viewports
[0].vp
.width
= display
->getwidth();
1559 wps_data
->viewports
[0].vp
.height
= display
->getheight();
1560 switch (statusbar_position(display
->screen_type
))
1563 wps_data
->viewports
[0].vp
.y
= 0;
1566 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
1567 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
1569 case STATUSBAR_BOTTOM
:
1570 wps_data
->viewports
[0].vp
.y
= 0;
1571 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
1574 #ifdef HAVE_LCD_BITMAP
1575 wps_data
->viewports
[0].vp
.font
= FONT_UI
;
1576 wps_data
->viewports
[0].vp
.drawmode
= DRMODE_SOLID
;
1579 if (display
->depth
> 1)
1581 wps_data
->viewports
[0].vp
.fg_pattern
= display
->get_foreground();
1582 wps_data
->viewports
[0].vp
.bg_pattern
= display
->get_background();
1587 return wps_parse(wps_data
, buf
);
1592 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1593 * wants to be a virtual file. Feel free to modify dirbrowse()
1594 * if you're feeling brave.
1597 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1599 global_settings
.wps_file
[0] = 0;
1603 #ifdef HAVE_REMOTE_LCD
1604 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1606 global_settings
.rwps_file
[0] = 0;
1610 #endif /* __PCTOOL__ */
1612 int fd
= open_utf8(buf
, O_RDONLY
);
1617 /* get buffer space from the plugin buffer */
1618 size_t buffersize
= 0;
1619 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1624 /* copy the file's content to the buffer for parsing,
1625 ensuring that every line ends with a newline char. */
1626 unsigned int start
= 0;
1627 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1629 start
+= strlen(wps_buffer
+ start
);
1630 if (start
< buffersize
- 1)
1632 wps_buffer
[start
++] = '\n';
1633 wps_buffer
[start
] = 0;
1642 #ifdef HAVE_LCD_BITMAP
1643 /* Set all filename pointers to NULL */
1644 memset(bmp_names
, 0, sizeof(bmp_names
));
1647 /* parse the WPS source */
1648 if (!wps_parse(wps_data
, wps_buffer
)) {
1649 wps_reset(wps_data
);
1653 wps_data
->wps_loaded
= true;
1655 #ifdef HAVE_LCD_BITMAP
1656 /* get the bitmap dir */
1657 char bmpdir
[MAX_PATH
];
1658 char *dot
= strrchr(buf
, '.');
1660 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1662 /* load the bitmaps that were found by the parsing */
1663 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
1664 wps_reset(wps_data
);
1668 #ifdef HAVE_ALBUMART
1669 status
= audio_status();
1670 if (((!wps_uses_albumart
&& wps_data
->wps_uses_albumart
) ||
1671 (wps_data
->wps_uses_albumart
&&
1672 (albumart_max_height
!= wps_data
->albumart_max_height
||
1673 albumart_max_width
!= wps_data
->albumart_max_width
))) &&
1674 status
& AUDIO_STATUS_PLAY
)
1676 curtrack
= audio_current_track();
1677 offset
= curtrack
->offset
;
1679 if (!(status
& AUDIO_STATUS_PAUSE
))