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"
47 #include "skin_engine.h"
49 #include "settings_list.h"
51 #ifdef HAVE_LCD_BITMAP
57 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
58 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
60 #define WPS_ERROR_INVALID_PARAM -1
62 /* level of current conditional.
63 -1 means we're not in a conditional. */
64 static int level
= -1;
66 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
67 or WPS_TOKEN_CONDITIONAL_START in current level */
68 static int lastcond
[WPS_MAX_COND_LEVEL
];
70 /* index of the WPS_TOKEN_CONDITIONAL in current level */
71 static int condindex
[WPS_MAX_COND_LEVEL
];
73 /* number of condtional options in current level */
74 static int numoptions
[WPS_MAX_COND_LEVEL
];
76 /* the current line in the file */
79 #ifdef HAVE_LCD_BITMAP
82 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
84 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
87 #define PROGRESSBAR_BMP MAX_IMAGES
88 #define BACKDROP_BMP (MAX_BITMAPS-1)
90 /* pointers to the bitmap filenames in the WPS source */
91 static const char *bmp_names
[MAX_BITMAPS
];
93 #endif /* HAVE_LCD_BITMAP */
95 #if defined(DEBUG) || defined(SIMULATOR)
96 /* debugging function */
97 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
100 static void wps_reset(struct wps_data
*data
);
102 /* Function for parsing of details for a token. At the moment the
103 function is called, the token type has already been set. The
104 function must fill in the details and possibly add more tokens
105 to the token array. It should return the number of chars that
108 wps_bufptr points to the char following the tag (i.e. where
110 token is the pointer to the 'main' token being parsed
112 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
113 struct wps_token
*token
, struct wps_data
*wps_data
);
116 enum wps_token_type type
;
118 unsigned char refresh_type
;
119 const wps_tag_parse_func parse_func
;
121 static int skip_end_of_line(const char *wps_bufptr
);
122 /* prototypes of all special parse functions : */
123 static int parse_timeout(const char *wps_bufptr
,
124 struct wps_token
*token
, struct wps_data
*wps_data
);
125 static int parse_progressbar(const char *wps_bufptr
,
126 struct wps_token
*token
, struct wps_data
*wps_data
);
127 static int parse_dir_level(const char *wps_bufptr
,
128 struct wps_token
*token
, struct wps_data
*wps_data
);
129 static int parse_setting(const char *wps_bufptr
,
130 struct wps_token
*token
, struct wps_data
*wps_data
);
132 #ifdef HAVE_LCD_BITMAP
133 static int parse_viewport_display(const char *wps_bufptr
,
134 struct wps_token
*token
, struct wps_data
*wps_data
);
135 static int parse_viewport(const char *wps_bufptr
,
136 struct wps_token
*token
, struct wps_data
*wps_data
);
137 static int parse_statusbar_enable(const char *wps_bufptr
,
138 struct wps_token
*token
, struct wps_data
*wps_data
);
139 static int parse_statusbar_disable(const char *wps_bufptr
,
140 struct wps_token
*token
, struct wps_data
*wps_data
);
141 static int parse_image_display(const char *wps_bufptr
,
142 struct wps_token
*token
, struct wps_data
*wps_data
);
143 static int parse_image_load(const char *wps_bufptr
,
144 struct wps_token
*token
, struct wps_data
*wps_data
);
145 #endif /*HAVE_LCD_BITMAP */
146 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
147 static int parse_image_special(const char *wps_bufptr
,
148 struct wps_token
*token
, struct wps_data
*wps_data
);
151 static int parse_albumart_load(const char *wps_bufptr
,
152 struct wps_token
*token
, struct wps_data
*wps_data
);
153 static int parse_albumart_conditional(const char *wps_bufptr
,
154 struct wps_token
*token
, struct wps_data
*wps_data
);
155 #endif /* HAVE_ALBUMART */
156 #ifdef HAVE_TOUCHSCREEN
157 static int parse_touchregion(const char *wps_bufptr
,
158 struct wps_token
*token
, struct wps_data
*wps_data
);
160 static int fulline_tag_not_supported(const char *wps_bufptr
,
161 struct wps_token
*token
, struct wps_data
*wps_data
)
163 (void)token
; (void)wps_data
;
164 return skip_end_of_line(wps_bufptr
);
166 #define parse_touchregion fulline_tag_not_supported
169 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
171 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
174 /* array of available tags - those with more characters have to go first
175 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
176 static const struct wps_tag all_tags
[] = {
178 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
179 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
180 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
182 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
183 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
184 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
185 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
186 #if CONFIG_CHARGING >= CHARGING_MONITOR
187 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
190 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
193 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
194 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
195 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
196 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
197 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
198 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
199 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
200 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
209 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
210 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
211 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
214 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
215 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
216 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
217 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
218 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
219 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
227 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
228 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
229 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
230 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
231 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
232 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
233 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
234 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
236 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
239 /* current metadata */
240 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
243 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
244 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
245 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
257 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
258 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
267 #if (CONFIG_CODEC != MAS3507D)
268 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
271 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
272 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
275 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
277 #ifdef HAS_REMOTE_BUTTON_HOLD
278 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
280 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
283 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
284 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
285 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
288 #ifdef HAVE_LCD_BITMAP
289 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
291 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
292 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
294 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
297 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
299 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
300 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
301 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
302 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
304 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
305 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
306 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
307 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
310 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
311 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
312 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
315 #if CONFIG_CODEC == SWCODEC
316 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
317 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
320 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
321 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
323 #ifdef HAVE_LCD_BITMAP
324 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
325 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
327 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
329 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
330 parse_image_display
},
332 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
334 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
335 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
,
336 parse_albumart_conditional
},
339 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
340 parse_viewport_display
},
341 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
343 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
344 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
348 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
, parse_setting
},
350 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
351 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
353 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
354 /* the array MUST end with an empty string (first char is \0) */
357 /* Returns the number of chars that should be skipped to jump
358 immediately after the first eol, i.e. to the start of the next line */
359 static int skip_end_of_line(const char *wps_bufptr
)
363 while(*(wps_bufptr
+ skip
) != '\n')
368 /* Starts a new subline in the current line during parsing */
369 static void wps_start_new_subline(struct wps_data
*data
)
371 data
->num_sublines
++;
372 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
373 data
->lines
[data
->num_lines
].num_sublines
++;
376 #ifdef HAVE_LCD_BITMAP
378 static int parse_statusbar_enable(const char *wps_bufptr
,
379 struct wps_token
*token
,
380 struct wps_data
*wps_data
)
382 (void)token
; /* Kill warnings */
383 wps_data
->wps_sb_tag
= true;
384 wps_data
->show_sb_on_wps
= true;
385 if (wps_data
->viewports
[0].vp
.y
== 0)
387 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
388 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
390 return skip_end_of_line(wps_bufptr
);
393 static int parse_statusbar_disable(const char *wps_bufptr
,
394 struct wps_token
*token
,
395 struct wps_data
*wps_data
)
397 (void)token
; /* Kill warnings */
398 wps_data
->wps_sb_tag
= true;
399 wps_data
->show_sb_on_wps
= false;
400 if (wps_data
->viewports
[0].vp
.y
== STATUSBAR_HEIGHT
)
402 wps_data
->viewports
[0].vp
.y
= 0;
403 wps_data
->viewports
[0].vp
.height
+= STATUSBAR_HEIGHT
;
405 return skip_end_of_line(wps_bufptr
);
408 static bool load_bitmap(struct wps_data
*wps_data
,
413 #ifdef HAVE_REMOTE_LCD
414 if (wps_data
->remote_wps
)
415 format
= FORMAT_ANY
|FORMAT_REMOTE
;
418 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
420 int ret
= read_bmp_file(filename
, bm
,
421 wps_data
->img_buf_free
,
428 /* Always consume an even number of bytes */
430 wps_data
->img_buf_ptr
+= ret
;
431 wps_data
->img_buf_free
-= ret
;
439 static int get_image_id(int c
)
441 if(c
>= 'a' && c
<= 'z')
443 else if(c
>= 'A' && c
<= 'Z')
449 static char *get_image_filename(const char *start
, const char* bmpdir
,
450 char *buf
, int buf_size
)
452 const char *end
= strchr(start
, '|');
454 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
460 int bmpdirlen
= strlen(bmpdir
);
463 buf
[bmpdirlen
] = '/';
464 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
465 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
470 static int parse_image_display(const char *wps_bufptr
,
471 struct wps_token
*token
,
472 struct wps_data
*wps_data
)
475 int n
= get_image_id(wps_bufptr
[0]);
480 /* invalid picture display tag */
481 return WPS_ERROR_INVALID_PARAM
;
484 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
487 if (subimage
>= wps_data
->img
[n
].num_subimages
)
488 return WPS_ERROR_INVALID_PARAM
;
490 /* Store sub-image number to display in high bits */
491 token
->value
.i
= n
| (subimage
<< 8);
492 return 2; /* We have consumed 2 bytes */
495 return 1; /* We have consumed 1 byte */
499 static int parse_image_load(const char *wps_bufptr
,
500 struct wps_token
*token
,
501 struct wps_data
*wps_data
)
504 const char *ptr
= wps_bufptr
;
506 const char* filename
;
511 /* format: %x|n|filename.bmp|x|y|
512 or %xl|n|filename.bmp|x|y|
513 or %xl|n|filename.bmp|x|y|num_subimages|
517 return WPS_ERROR_INVALID_PARAM
;
521 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
522 return WPS_ERROR_INVALID_PARAM
;
524 /* Check there is a terminating | */
526 return WPS_ERROR_INVALID_PARAM
;
528 /* get the image ID */
529 n
= get_image_id(*id
);
531 /* check the image number and load state */
532 if(n
< 0 || n
>= MAX_IMAGES
|| wps_data
->img
[n
].loaded
)
534 /* Invalid image ID */
535 return WPS_ERROR_INVALID_PARAM
;
538 /* save a pointer to the filename */
539 bmp_names
[n
] = filename
;
541 wps_data
->img
[n
].x
= x
;
542 wps_data
->img
[n
].y
= y
;
544 /* save current viewport */
545 wps_data
->img
[n
].vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
547 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
549 wps_data
->img
[n
].always_display
= true;
553 /* Parse the (optional) number of sub-images */
555 newline
= strchr(ptr
, '\n');
556 pos
= strchr(ptr
, '|');
557 if (pos
&& pos
< newline
)
558 wps_data
->img
[n
].num_subimages
= atoi(ptr
);
560 if (wps_data
->img
[n
].num_subimages
<= 0)
561 return WPS_ERROR_INVALID_PARAM
;
564 /* Skip the rest of the line */
565 return skip_end_of_line(wps_bufptr
);
568 static int parse_viewport_display(const char *wps_bufptr
,
569 struct wps_token
*token
,
570 struct wps_data
*wps_data
)
573 char letter
= wps_bufptr
[0];
575 if (letter
< 'a' || letter
> 'z')
577 /* invalid viewport tag */
578 return WPS_ERROR_INVALID_PARAM
;
580 token
->value
.i
= letter
;
584 static int parse_viewport(const char *wps_bufptr
,
585 struct wps_token
*token
,
586 struct wps_data
*wps_data
)
588 (void)token
; /* Kill warnings */
589 const char *ptr
= wps_bufptr
;
602 int lcd_width
= LCD_WIDTH
, lcd_height
= LCD_HEIGHT
;
603 #ifdef HAVE_REMOTE_LCD
604 if (wps_data
->remote_wps
)
606 lcd_width
= LCD_REMOTE_WIDTH
;
607 lcd_height
= LCD_REMOTE_HEIGHT
;
611 if (wps_data
->num_viewports
>= WPS_MAX_VIEWPORTS
)
612 return WPS_ERROR_INVALID_PARAM
;
614 wps_data
->num_viewports
++;
615 /* check for the optional letter to signify its a hideable viewport */
616 /* %Vl|<label>|<rest of tags>| */
617 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= 0;
623 char label
= *(ptr
+2);
624 if (label
>= 'a' && label
<= 'z')
626 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= VP_DRAW_HIDEABLE
;
627 wps_data
->viewports
[wps_data
->num_viewports
].label
= label
;
630 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
635 return WPS_ERROR_INVALID_PARAM
;
638 vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
639 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
641 /* Set the defaults for fields not user-specified */
642 vp
->drawmode
= DRMODE_SOLID
;
644 /* Work out the depth of this display */
645 #ifdef HAVE_REMOTE_LCD
646 depth
= (wps_data
->remote_wps
? LCD_REMOTE_DEPTH
: LCD_DEPTH
);
651 #ifdef HAVE_LCD_COLOR
654 if (!(ptr
= parse_list("dddddcc", &set
, '|', ptr
, &vp
->x
, &vp
->y
, &vp
->width
,
655 &vp
->height
, &vp
->font
, &vp
->fg_pattern
,&vp
->bg_pattern
)))
656 return WPS_ERROR_INVALID_PARAM
;
660 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
662 /* Default to black on white */
665 if (!(ptr
= parse_list("dddddgg", &set
, '|', ptr
, &vp
->x
, &vp
->y
, &vp
->width
,
666 &vp
->height
, &vp
->font
, &vp
->fg_pattern
, &vp
->bg_pattern
)))
667 return WPS_ERROR_INVALID_PARAM
;
671 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
674 if (!(ptr
= parse_list("ddddd", &set
, '|', ptr
, &vp
->x
, &vp
->y
,
675 &vp
->width
, &vp
->height
, &vp
->font
)))
676 return WPS_ERROR_INVALID_PARAM
;
682 /* Check for trailing | */
684 return WPS_ERROR_INVALID_PARAM
;
686 if (!LIST_VALUE_PARSED(set
, PL_X
) || !LIST_VALUE_PARSED(set
, PL_Y
))
687 return WPS_ERROR_INVALID_PARAM
;
690 if (!LIST_VALUE_PARSED(set
, PL_WIDTH
))
691 vp
->width
= lcd_width
- vp
->x
;
692 if (!LIST_VALUE_PARSED(set
, PL_HEIGHT
))
693 vp
->height
= lcd_height
- vp
->y
;
695 /* Default to using the user font if the font was an invalid number */
696 if (!LIST_VALUE_PARSED(set
, PL_FONT
) ||
697 ((vp
->font
!= FONT_SYSFIXED
) && (vp
->font
!= FONT_UI
)))
700 /* Validate the viewport dimensions - we know that the numbers are
701 non-negative integers */
702 if ((vp
->x
>= lcd_width
) ||
703 ((vp
->x
+ vp
->width
) > lcd_width
) ||
704 (vp
->y
>= lcd_height
) ||
705 ((vp
->y
+ vp
->height
) > lcd_height
))
707 return WPS_ERROR_INVALID_PARAM
;
710 #ifdef HAVE_LCD_COLOR
713 if (!LIST_VALUE_PARSED(set
, PL_FG
))
714 vp
->fg_pattern
= global_settings
.fg_color
;
715 if (!LIST_VALUE_PARSED(set
, PL_BG
))
716 vp
->bg_pattern
= global_settings
.bg_color
;
720 wps_data
->viewports
[wps_data
->num_viewports
-1].last_line
= wps_data
->num_lines
- 1;
722 wps_data
->viewports
[wps_data
->num_viewports
].first_line
= wps_data
->num_lines
;
724 if (wps_data
->num_sublines
< WPS_MAX_SUBLINES
)
726 wps_data
->lines
[wps_data
->num_lines
].first_subline_idx
=
727 wps_data
->num_sublines
;
729 wps_data
->sublines
[wps_data
->num_sublines
].first_token_idx
=
730 wps_data
->num_tokens
;
733 /* Skip the rest of the line */
734 return skip_end_of_line(wps_bufptr
);
737 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
738 static int parse_image_special(const char *wps_bufptr
,
739 struct wps_token
*token
,
740 struct wps_data
*wps_data
)
742 (void)wps_data
; /* kill warning */
744 const char *pos
= NULL
;
747 pos
= strchr(wps_bufptr
+ 1, '|');
748 newline
= strchr(wps_bufptr
, '\n');
751 return WPS_ERROR_INVALID_PARAM
;
753 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
755 /* format: %X|filename.bmp| */
756 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
760 /* Skip the rest of the line */
761 return skip_end_of_line(wps_bufptr
);
765 #endif /* HAVE_LCD_BITMAP */
767 static int parse_setting(const char *wps_bufptr
,
768 struct wps_token
*token
,
769 struct wps_data
*wps_data
)
772 const char *ptr
= wps_bufptr
;
776 /* Find the setting's cfg_name */
778 return WPS_ERROR_INVALID_PARAM
;
780 end
= strchr(ptr
,'|');
782 return WPS_ERROR_INVALID_PARAM
;
784 /* Find the setting */
785 for (i
=0; i
<nb_settings
; i
++)
786 if (settings
[i
].cfg_name
&&
787 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
788 /* prevent matches on cfg_name prefixes */
789 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
791 if (i
== nb_settings
)
792 return WPS_ERROR_INVALID_PARAM
;
794 /* Store the setting number */
797 /* Skip the rest of the line */
802 static int parse_dir_level(const char *wps_bufptr
,
803 struct wps_token
*token
,
804 struct wps_data
*wps_data
)
806 char val
[] = { *wps_bufptr
, '\0' };
807 token
->value
.i
= atoi(val
);
808 (void)wps_data
; /* Kill warnings */
812 static int parse_timeout(const char *wps_bufptr
,
813 struct wps_token
*token
,
814 struct wps_data
*wps_data
)
818 bool have_point
= false;
819 bool have_tenth
= false;
821 (void)wps_data
; /* Kill the warning */
823 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
825 if (*wps_bufptr
!= '.')
828 val
+= *wps_bufptr
- '0';
844 if (have_tenth
== false)
847 if (val
== 0 && skip
== 0)
849 /* decide what to do if no value was specified */
852 case WPS_TOKEN_SUBLINE_TIMEOUT
:
854 case WPS_TOKEN_BUTTON_VOLUME
:
859 token
->value
.i
= val
;
864 static int parse_progressbar(const char *wps_bufptr
,
865 struct wps_token
*token
,
866 struct wps_data
*wps_data
)
868 (void)token
; /* Kill warnings */
869 /* %pb or %pb|filename|x|y|width|height|
870 using - for any of the params uses "sane" values */
871 #ifdef HAVE_LCD_BITMAP
879 const char *filename
;
880 int x
, y
, height
, width
;
882 const char *ptr
= wps_bufptr
;
883 struct progressbar
*pb
;
884 struct viewport
*vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
886 int font_height
= font_get(vp
->font
)->height
;
890 int line_num
= wps_data
->num_lines
-
891 wps_data
->viewports
[wps_data
->num_viewports
].first_line
;
893 if (wps_data
->progressbar_count
>= MAX_PROGRESSBARS
)
894 return WPS_ERROR_INVALID_PARAM
;
896 pb
= &wps_data
->progressbar
[wps_data
->progressbar_count
];
897 pb
->have_bitmap_pb
= false;
899 if (*wps_bufptr
!= '|') /* regular old style */
902 pb
->width
= vp
->width
;
903 pb
->height
= SYSFONT_HEIGHT
-2;
904 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
906 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
907 wps_data
->progressbar_count
++;
910 ptr
= wps_bufptr
+ 1;
912 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
913 &x
, &y
, &width
, &height
)))
914 return WPS_ERROR_INVALID_PARAM
;
916 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
917 bmp_names
[PROGRESSBAR_BMP
+wps_data
->progressbar_count
] = filename
;
919 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
924 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
926 /* A zero width causes a divide-by-zero error later, so reject it */
928 return WPS_ERROR_INVALID_PARAM
;
933 pb
->width
= vp
->width
- pb
->x
;
935 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
937 /* A zero height makes no sense - reject it */
939 return WPS_ERROR_INVALID_PARAM
;
944 pb
->height
= font_height
;
946 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
949 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
951 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
952 wps_data
->progressbar_count
++;
954 /* Skip the rest of the line */
955 return skip_end_of_line(wps_bufptr
)-1;
958 if (*(wps_bufptr
-1) == 'f')
959 wps_data
->full_line_progressbar
= true;
961 wps_data
->full_line_progressbar
= false;
969 static int parse_albumart_load(const char *wps_bufptr
,
970 struct wps_token
*token
,
971 struct wps_data
*wps_data
)
973 const char *_pos
, *newline
;
975 (void)token
; /* silence warning */
977 /* reset albumart info in wps */
978 wps_data
->albumart_max_width
= -1;
979 wps_data
->albumart_max_height
= -1;
980 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
981 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
983 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
985 newline
= strchr(wps_bufptr
, '\n');
987 /* initial validation and parsing of x and y components */
988 if (*wps_bufptr
!= '|')
989 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
991 _pos
= wps_bufptr
+ 1;
993 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|@ */
994 wps_data
->albumart_x
= atoi(_pos
);
996 _pos
= strchr(_pos
, '|');
997 if (!_pos
|| _pos
> newline
|| !isdigit(*(++_pos
)))
998 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
1000 wps_data
->albumart_y
= atoi(_pos
);
1002 _pos
= strchr(_pos
, '|');
1003 if (!_pos
|| _pos
> newline
)
1004 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
1007 /* parsing width field */
1011 /* apply each modifier in turn */
1018 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1022 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1027 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1035 /* simply ignored */
1042 /* extract max width data */
1045 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
1046 return WPS_ERROR_INVALID_PARAM
;
1048 wps_data
->albumart_max_width
= atoi(_pos
);
1050 _pos
= strchr(_pos
, '|');
1051 if (!_pos
|| _pos
> newline
)
1052 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
1053 e.g. %Cl|7|59|200\n */
1056 /* parsing height field */
1060 /* apply each modifier in turn */
1067 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_TOP
;
1071 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1076 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1084 /* simply ignored */
1091 /* extract max height data */
1094 if (!isdigit(*_pos
))
1095 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1097 wps_data
->albumart_max_height
= atoi(_pos
);
1099 _pos
= strchr(_pos
, '|');
1100 if (!_pos
|| _pos
> newline
)
1101 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1102 e.g. %Cl|7|59|200|200\n */
1105 /* if we got here, we parsed everything ok .. ! */
1106 if (wps_data
->albumart_max_width
< 0)
1107 wps_data
->albumart_max_width
= 0;
1108 else if (wps_data
->albumart_max_width
> LCD_WIDTH
)
1109 wps_data
->albumart_max_width
= LCD_WIDTH
;
1111 if (wps_data
->albumart_max_height
< 0)
1112 wps_data
->albumart_max_height
= 0;
1113 else if (wps_data
->albumart_max_height
> LCD_HEIGHT
)
1114 wps_data
->albumart_max_height
= LCD_HEIGHT
;
1116 wps_data
->wps_uses_albumart
= WPS_ALBUMART_LOAD
;
1118 /* Skip the rest of the line */
1119 return skip_end_of_line(wps_bufptr
);
1122 static int parse_albumart_conditional(const char *wps_bufptr
,
1123 struct wps_token
*token
,
1124 struct wps_data
*wps_data
)
1126 struct wps_token
*prevtoken
= token
;
1128 if (wps_data
->num_tokens
>= 1 && prevtoken
->type
== WPS_TOKEN_CONDITIONAL
)
1130 /* This %C is part of a %?C construct.
1131 It's either %?C<blah> or %?Cn<blah> */
1132 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1133 if (*wps_bufptr
== 'n' && *(wps_bufptr
+ 1) == '<')
1138 else if (*wps_bufptr
== '<')
1144 token
->type
= WPS_NO_TOKEN
;
1150 /* This %C tag is in a conditional construct. */
1151 wps_data
->albumart_cond_index
= condindex
[level
];
1155 #endif /* HAVE_ALBUMART */
1157 #ifdef HAVE_TOUCHSCREEN
1159 struct touchaction
{char* s
; int action
;};
1160 static struct touchaction touchactions
[] = {
1161 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1162 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1163 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1164 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1165 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1166 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1167 {"playlist", ACTION_WPS_VIEW_PLAYLIST
},
1169 static int parse_touchregion(const char *wps_bufptr
,
1170 struct wps_token
*token
, struct wps_data
*wps_data
)
1174 struct touchregion
*region
;
1175 const char *ptr
= wps_bufptr
;
1179 /* format: %T|x|y|width|height|action|
1180 * if action starts with & the area must be held to happen
1182 * play - play/pause playback
1183 * stop - stop playback, exit the wps
1186 * ffwd - seek forward
1187 * rwd - seek backwards
1188 * menu - go back to the main menu
1189 * browse - go back to the file/db browser
1190 * shuffle - toggle shuffle mode
1191 * repmode - cycle the repeat mode
1192 * quickscreen - go into the quickscreen
1193 * contextmenu - open the context menu
1197 if ((wps_data
->touchregion_count
+1 >= MAX_TOUCHREGIONS
) || (*ptr
!= '|'))
1198 return WPS_ERROR_INVALID_PARAM
;
1201 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1202 return WPS_ERROR_INVALID_PARAM
;
1204 /* Check there is a terminating | */
1206 return WPS_ERROR_INVALID_PARAM
;
1208 /* should probably do some bounds checking here with the viewport... but later */
1209 region
= &wps_data
->touchregion
[wps_data
->touchregion_count
];
1210 region
->action
= ACTION_NONE
;
1215 region
->wvp
= &wps_data
->viewports
[wps_data
->num_viewports
];
1220 region
->repeat
= true;
1223 region
->repeat
= false;
1225 imax
= ARRAYLEN(touchactions
);
1226 while ((region
->action
== ACTION_NONE
) &&
1229 /* try to match with one of our touchregion screens */
1230 int len
= strlen(touchactions
[i
].s
);
1231 if (!strncmp(touchactions
[i
].s
, action
, len
)
1232 && *(action
+len
) == '|')
1233 region
->action
= touchactions
[i
].action
;
1236 if (region
->action
== ACTION_NONE
)
1237 return WPS_ERROR_INVALID_PARAM
;
1238 wps_data
->touchregion_count
++;
1239 return skip_end_of_line(wps_bufptr
);
1243 /* Parse a generic token from the given string. Return the length read */
1244 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1246 int skip
= 0, taglen
= 0, ret
;
1247 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1248 const struct wps_tag
*tag
;
1259 /* escaped characters */
1260 token
->type
= WPS_TOKEN_CHARACTER
;
1261 token
->value
.c
= *wps_bufptr
;
1263 wps_data
->num_tokens
++;
1267 /* conditional tag */
1268 token
->type
= WPS_TOKEN_CONDITIONAL
;
1270 condindex
[level
] = wps_data
->num_tokens
;
1271 numoptions
[level
] = 1;
1272 wps_data
->num_tokens
++;
1273 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1274 if (ret
< 0) return ret
;
1279 /* find what tag we have */
1280 for (tag
= all_tags
;
1281 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1284 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1285 token
->type
= tag
->type
;
1286 wps_data
->sublines
[wps_data
->num_sublines
].line_type
|=
1289 /* if the tag has a special parsing function, we call it */
1290 if (tag
->parse_func
)
1292 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1293 if (ret
< 0) return ret
;
1297 /* Some tags we don't want to save as tokens */
1298 if (tag
->type
== WPS_NO_TOKEN
)
1301 /* tags that start with 'F', 'I' or 'D' are for the next file */
1302 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1303 *(tag
->name
) == 'D')
1306 wps_data
->num_tokens
++;
1315 data is the pointer to the structure where the parsed WPS should be stored.
1317 wps_bufptr points to the string containing the WPS tags */
1318 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
)
1320 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1323 char *stringbuf
= data
->string_buffer
;
1324 int stringbuf_used
= 0;
1325 enum wps_parse_error fail
= PARSE_OK
;
1330 while(*wps_bufptr
&& !fail
&& data
->num_tokens
< WPS_MAX_TOKENS
- 1
1331 && data
->num_viewports
< WPS_MAX_VIEWPORTS
1332 && data
->num_lines
< WPS_MAX_LINES
)
1334 switch(*wps_bufptr
++)
1339 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1341 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1344 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1346 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1352 /* Alternating sublines separator */
1354 if (level
>= 0) /* there are unclosed conditionals */
1356 fail
= PARSE_FAIL_UNCLOSED_COND
;
1360 if (data
->num_sublines
+1 < WPS_MAX_SUBLINES
)
1361 wps_start_new_subline(data
);
1363 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1367 /* Conditional list start */
1369 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1371 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1375 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1376 lastcond
[level
] = data
->num_tokens
++;
1379 /* Conditional list end */
1381 if (level
< 0) /* not in a conditional, invalid char */
1383 fail
= PARSE_FAIL_INVALID_CHAR
;
1387 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1388 if (lastcond
[level
])
1389 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1392 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1396 lastcond
[level
] = 0;
1398 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1402 /* Conditional list option */
1404 if (level
< 0) /* not in a conditional, invalid char */
1406 fail
= PARSE_FAIL_INVALID_CHAR
;
1410 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1411 if (lastcond
[level
])
1412 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1415 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1419 lastcond
[level
] = data
->num_tokens
;
1420 numoptions
[level
]++;
1426 if (level
>= 0) /* there are unclosed conditionals */
1428 fail
= PARSE_FAIL_UNCLOSED_COND
;
1432 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1435 /* End of this line */
1437 if (level
>= 0) /* there are unclosed conditionals */
1439 fail
= PARSE_FAIL_UNCLOSED_COND
;
1444 wps_start_new_subline(data
);
1445 data
->num_lines
++; /* Start a new line */
1447 if ((data
->num_lines
< WPS_MAX_LINES
) &&
1448 (data
->num_sublines
< WPS_MAX_SUBLINES
))
1450 data
->lines
[data
->num_lines
].first_subline_idx
=
1453 data
->sublines
[data
->num_sublines
].first_token_idx
=
1462 unsigned int len
= 1;
1463 const char *string_start
= wps_bufptr
- 1;
1465 /* find the length of the string */
1466 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1467 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1468 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1469 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1475 /* look if we already have that string */
1479 for (i
= 0, str
= data
->strings
, found
= false;
1480 i
< data
->num_strings
&&
1481 !(found
= (strlen(*str
) == len
&&
1482 strncmp(string_start
, *str
, len
) == 0));
1484 /* If a matching string is found, found is true and i is
1485 the index of the string. If not, found is false */
1491 if (stringbuf_used
+ len
> STRING_BUFFER_SIZE
- 1
1492 || data
->num_strings
>= WPS_MAX_STRINGS
)
1494 /* too many strings or characters */
1495 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1499 strlcpy(stringbuf
, string_start
, len
+1);
1501 data
->strings
[data
->num_strings
] = stringbuf
;
1502 stringbuf
+= len
+ 1;
1503 stringbuf_used
+= len
+ 1;
1504 data
->tokens
[data
->num_tokens
].value
.i
=
1506 data
->num_strings
++;
1510 /* another occurrence of an existing string */
1511 data
->tokens
[data
->num_tokens
].value
.i
= i
;
1513 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1520 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1521 fail
= PARSE_FAIL_UNCLOSED_COND
;
1523 if (*wps_bufptr
&& !fail
)
1524 /* one of the limits of the while loop was exceeded */
1525 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1527 data
->viewports
[data
->num_viewports
].last_line
= data
->num_lines
- 1;
1529 /* We have finished with the last viewport, so increment count */
1530 data
->num_viewports
++;
1532 #if defined(DEBUG) || defined(SIMULATOR)
1533 print_debug_info(data
, fail
, line
);
1539 static void wps_reset(struct wps_data
*data
)
1541 #ifdef HAVE_REMOTE_LCD
1542 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1544 memset(data
, 0, sizeof(*data
));
1545 skin_data_init(data
);
1546 #ifdef HAVE_REMOTE_LCD
1547 data
->remote_wps
= rwps
;
1551 #ifdef HAVE_LCD_BITMAP
1553 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1555 char img_path
[MAX_PATH
];
1556 struct bitmap
*bitmap
;
1559 for (n
= 0; n
< BACKDROP_BMP
; n
++)
1563 get_image_filename(bmp_names
[n
], bmpdir
,
1564 img_path
, sizeof(img_path
));
1566 if (n
>= PROGRESSBAR_BMP
) {
1567 /* progressbar bitmap */
1568 bitmap
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].bm
;
1569 loaded
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].have_bitmap_pb
;
1571 /* regular bitmap */
1572 bitmap
= &wps_data
->img
[n
].bm
;
1573 loaded
= &wps_data
->img
[n
].loaded
;
1576 /* load the image */
1577 bitmap
->data
= wps_data
->img_buf_ptr
;
1578 if (load_bitmap(wps_data
, img_path
, bitmap
))
1582 /* Calculate and store height if this image has sub-images */
1584 wps_data
->img
[n
].subimage_height
= wps_data
->img
[n
].bm
.height
/
1585 wps_data
->img
[n
].num_subimages
;
1589 /* Abort if we can't load an image */
1590 DEBUGF("ERR: Failed to load image %d - %s\n",n
,img_path
);
1596 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1597 if (bmp_names
[BACKDROP_BMP
])
1599 int screen
= SCREEN_MAIN
;
1600 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1601 img_path
, sizeof(img_path
));
1602 #if defined(HAVE_REMOTE_LCD)
1603 /* We only need to check LCD type if there is a remote LCD */
1604 if (wps_data
->remote_wps
)
1605 screen
= SCREEN_REMOTE
;
1607 screens
[screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1609 #endif /* has backdrop support */
1611 /* If we got here, everything was OK */
1615 #endif /* HAVE_LCD_BITMAP */
1617 /* to setup up the wps-data from a format-buffer (isfile = false)
1618 from a (wps-)file (isfile = true)*/
1619 bool skin_data_load(struct wps_data
*wps_data
,
1620 struct screen
*display
,
1624 #ifdef HAVE_ALBUMART
1625 struct mp3entry
*curtrack
;
1628 int wps_uses_albumart
= wps_data
->wps_uses_albumart
;
1629 int albumart_max_height
= wps_data
->albumart_max_height
;
1630 int albumart_max_width
= wps_data
->albumart_max_width
;
1632 if (!wps_data
|| !buf
)
1635 wps_reset(wps_data
);
1637 /* Initialise the first (default) viewport */
1638 wps_data
->viewports
[0].vp
.x
= 0;
1639 wps_data
->viewports
[0].vp
.width
= display
->getwidth();
1640 wps_data
->viewports
[0].vp
.height
= display
->getheight();
1641 switch (statusbar_position(display
->screen_type
))
1644 wps_data
->viewports
[0].vp
.y
= 0;
1647 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
1648 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
1650 case STATUSBAR_BOTTOM
:
1651 wps_data
->viewports
[0].vp
.y
= 0;
1652 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
1655 #ifdef HAVE_LCD_BITMAP
1656 wps_data
->viewports
[0].vp
.font
= FONT_UI
;
1657 wps_data
->viewports
[0].vp
.drawmode
= DRMODE_SOLID
;
1660 if (display
->depth
> 1)
1662 wps_data
->viewports
[0].vp
.fg_pattern
= display
->get_foreground();
1663 wps_data
->viewports
[0].vp
.bg_pattern
= display
->get_background();
1668 return wps_parse(wps_data
, buf
);
1673 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1674 * wants to be a virtual file. Feel free to modify dirbrowse()
1675 * if you're feeling brave.
1678 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1680 global_settings
.wps_file
[0] = 0;
1684 #ifdef HAVE_REMOTE_LCD
1685 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1687 global_settings
.rwps_file
[0] = 0;
1691 #endif /* __PCTOOL__ */
1693 int fd
= open_utf8(buf
, O_RDONLY
);
1698 /* get buffer space from the plugin buffer */
1699 size_t buffersize
= 0;
1700 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1705 /* copy the file's content to the buffer for parsing,
1706 ensuring that every line ends with a newline char. */
1707 unsigned int start
= 0;
1708 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1710 start
+= strlen(wps_buffer
+ start
);
1711 if (start
< buffersize
- 1)
1713 wps_buffer
[start
++] = '\n';
1714 wps_buffer
[start
] = 0;
1723 #ifdef HAVE_LCD_BITMAP
1724 /* Set all filename pointers to NULL */
1725 memset(bmp_names
, 0, sizeof(bmp_names
));
1728 /* parse the WPS source */
1729 if (!wps_parse(wps_data
, wps_buffer
)) {
1730 wps_reset(wps_data
);
1734 wps_data
->wps_loaded
= true;
1736 #ifdef HAVE_LCD_BITMAP
1737 /* get the bitmap dir */
1738 char bmpdir
[MAX_PATH
];
1739 char *dot
= strrchr(buf
, '.');
1741 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1743 /* load the bitmaps that were found by the parsing */
1744 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
1745 wps_reset(wps_data
);
1749 #ifdef HAVE_ALBUMART
1750 status
= audio_status();
1751 if (((!wps_uses_albumart
&& wps_data
->wps_uses_albumart
) ||
1752 (wps_data
->wps_uses_albumart
&&
1753 (albumart_max_height
!= wps_data
->albumart_max_height
||
1754 albumart_max_width
!= wps_data
->albumart_max_width
))) &&
1755 status
& AUDIO_STATUS_PLAY
)
1757 curtrack
= audio_current_track();
1758 offset
= curtrack
->offset
;
1760 if (!(status
& AUDIO_STATUS_PAUSE
))