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 ****************************************************************************/
52 #include "settings_list.h"
54 #ifdef HAVE_LCD_BITMAP
60 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
61 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
63 #define WPS_ERROR_INVALID_PARAM -1
65 /* level of current conditional.
66 -1 means we're not in a conditional. */
67 static int level
= -1;
69 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
70 or WPS_TOKEN_CONDITIONAL_START in current level */
71 static int lastcond
[WPS_MAX_COND_LEVEL
];
73 /* index of the WPS_TOKEN_CONDITIONAL in current level */
74 static int condindex
[WPS_MAX_COND_LEVEL
];
76 /* number of condtional options in current level */
77 static int numoptions
[WPS_MAX_COND_LEVEL
];
79 /* the current line in the file */
82 #ifdef HAVE_LCD_BITMAP
85 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
87 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
90 #define PROGRESSBAR_BMP MAX_IMAGES
91 #define BACKDROP_BMP (MAX_BITMAPS-1)
93 /* pointers to the bitmap filenames in the WPS source */
94 static const char *bmp_names
[MAX_BITMAPS
];
96 #endif /* HAVE_LCD_BITMAP */
98 #if defined(DEBUG) || defined(SIMULATOR)
99 /* debugging function */
100 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
103 static void wps_reset(struct wps_data
*data
);
105 /* Function for parsing of details for a token. At the moment the
106 function is called, the token type has already been set. The
107 function must fill in the details and possibly add more tokens
108 to the token array. It should return the number of chars that
111 wps_bufptr points to the char following the tag (i.e. where
113 token is the pointer to the 'main' token being parsed
115 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
116 struct wps_token
*token
, struct wps_data
*wps_data
);
119 enum wps_token_type type
;
121 unsigned char refresh_type
;
122 const wps_tag_parse_func parse_func
;
125 /* prototypes of all special parse functions : */
126 static int parse_timeout(const char *wps_bufptr
,
127 struct wps_token
*token
, struct wps_data
*wps_data
);
128 static int parse_progressbar(const char *wps_bufptr
,
129 struct wps_token
*token
, struct wps_data
*wps_data
);
130 static int parse_dir_level(const char *wps_bufptr
,
131 struct wps_token
*token
, struct wps_data
*wps_data
);
132 static int parse_setting(const char *wps_bufptr
,
133 struct wps_token
*token
, struct wps_data
*wps_data
);
135 #ifdef HAVE_LCD_BITMAP
136 static int parse_viewport_display(const char *wps_bufptr
,
137 struct wps_token
*token
, struct wps_data
*wps_data
);
138 static int parse_viewport(const char *wps_bufptr
,
139 struct wps_token
*token
, struct wps_data
*wps_data
);
140 static int parse_statusbar_enable(const char *wps_bufptr
,
141 struct wps_token
*token
, struct wps_data
*wps_data
);
142 static int parse_statusbar_disable(const char *wps_bufptr
,
143 struct wps_token
*token
, struct wps_data
*wps_data
);
144 static int parse_image_display(const char *wps_bufptr
,
145 struct wps_token
*token
, struct wps_data
*wps_data
);
146 static int parse_image_load(const char *wps_bufptr
,
147 struct wps_token
*token
, struct wps_data
*wps_data
);
148 #endif /*HAVE_LCD_BITMAP */
149 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
150 static int parse_image_special(const char *wps_bufptr
,
151 struct wps_token
*token
, struct wps_data
*wps_data
);
154 static int parse_albumart_load(const char *wps_bufptr
,
155 struct wps_token
*token
, struct wps_data
*wps_data
);
156 static int parse_albumart_conditional(const char *wps_bufptr
,
157 struct wps_token
*token
, struct wps_data
*wps_data
);
158 #endif /* HAVE_ALBUMART */
161 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
163 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
166 /* array of available tags - those with more characters have to go first
167 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
168 static const struct wps_tag all_tags
[] = {
170 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
171 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
172 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
174 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
175 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
176 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
177 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
178 #if CONFIG_CHARGING >= CHARGING_MONITOR
179 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
182 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
185 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
186 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
187 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
188 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
189 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
190 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
191 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
192 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
193 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
194 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
195 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
196 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
197 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
198 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
199 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
200 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
206 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
207 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
208 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
209 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
210 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
211 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
212 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
213 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
214 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
218 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
219 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
224 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
225 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
226 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
230 /* current metadata */
231 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
232 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
233 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
234 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
236 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
237 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
238 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
245 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
258 #if (CONFIG_CODEC != MAS3507D)
259 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
262 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
263 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
266 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
268 #ifdef HAS_REMOTE_BUTTON_HOLD
269 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
271 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
274 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
275 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
276 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
279 #ifdef HAVE_LCD_BITMAP
280 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
282 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
283 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
285 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
288 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
290 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
291 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
292 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
293 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
295 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
296 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
297 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
298 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
301 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
302 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
303 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
306 #if CONFIG_CODEC == SWCODEC
307 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
308 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
311 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
312 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
314 #ifdef HAVE_LCD_BITMAP
315 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
316 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
318 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
320 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
321 parse_image_display
},
323 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
325 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
326 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
,
327 parse_albumart_conditional
},
330 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
331 parse_viewport_display
},
332 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
334 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
335 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
339 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
, parse_setting
},
341 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
342 /* the array MUST end with an empty string (first char is \0) */
345 /* Returns the number of chars that should be skipped to jump
346 immediately after the first eol, i.e. to the start of the next line */
347 static int skip_end_of_line(const char *wps_bufptr
)
351 while(*(wps_bufptr
+ skip
) != '\n')
356 /* Starts a new subline in the current line during parsing */
357 static void wps_start_new_subline(struct wps_data
*data
)
359 data
->num_sublines
++;
360 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
361 data
->lines
[data
->num_lines
].num_sublines
++;
364 #ifdef HAVE_LCD_BITMAP
366 static int parse_statusbar_enable(const char *wps_bufptr
,
367 struct wps_token
*token
,
368 struct wps_data
*wps_data
)
370 (void)token
; /* Kill warnings */
371 wps_data
->wps_sb_tag
= true;
372 wps_data
->show_sb_on_wps
= true;
373 if (wps_data
->viewports
[0].vp
.y
== 0)
375 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
376 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
378 return skip_end_of_line(wps_bufptr
);
381 static int parse_statusbar_disable(const char *wps_bufptr
,
382 struct wps_token
*token
,
383 struct wps_data
*wps_data
)
385 (void)token
; /* Kill warnings */
386 wps_data
->wps_sb_tag
= true;
387 wps_data
->show_sb_on_wps
= false;
388 if (wps_data
->viewports
[0].vp
.y
== STATUSBAR_HEIGHT
)
390 wps_data
->viewports
[0].vp
.y
= 0;
391 wps_data
->viewports
[0].vp
.height
+= STATUSBAR_HEIGHT
;
393 return skip_end_of_line(wps_bufptr
);
396 static bool load_bitmap(struct wps_data
*wps_data
,
401 #ifdef HAVE_REMOTE_LCD
402 if (wps_data
->remote_wps
)
403 format
= FORMAT_ANY
|FORMAT_REMOTE
;
406 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
408 int ret
= read_bmp_file(filename
, bm
,
409 wps_data
->img_buf_free
,
416 /* Always consume an even number of bytes */
418 wps_data
->img_buf_ptr
+= ret
;
419 wps_data
->img_buf_free
-= ret
;
427 static int get_image_id(int c
)
429 if(c
>= 'a' && c
<= 'z')
431 else if(c
>= 'A' && c
<= 'Z')
437 static char *get_image_filename(const char *start
, const char* bmpdir
,
438 char *buf
, int buf_size
)
440 const char *end
= strchr(start
, '|');
442 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
448 int bmpdirlen
= strlen(bmpdir
);
451 buf
[bmpdirlen
] = '/';
452 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
453 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
458 static int parse_image_display(const char *wps_bufptr
,
459 struct wps_token
*token
,
460 struct wps_data
*wps_data
)
463 int n
= get_image_id(wps_bufptr
[0]);
468 /* invalid picture display tag */
469 return WPS_ERROR_INVALID_PARAM
;
472 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
475 if (subimage
>= wps_data
->img
[n
].num_subimages
)
476 return WPS_ERROR_INVALID_PARAM
;
478 /* Store sub-image number to display in high bits */
479 token
->value
.i
= n
| (subimage
<< 8);
480 return 2; /* We have consumed 2 bytes */
483 return 1; /* We have consumed 1 byte */
487 static int parse_image_load(const char *wps_bufptr
,
488 struct wps_token
*token
,
489 struct wps_data
*wps_data
)
492 const char *ptr
= wps_bufptr
;
494 const char* filename
;
499 /* format: %x|n|filename.bmp|x|y|
500 or %xl|n|filename.bmp|x|y|
501 or %xl|n|filename.bmp|x|y|num_subimages|
505 return WPS_ERROR_INVALID_PARAM
;
509 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
510 return WPS_ERROR_INVALID_PARAM
;
512 /* Check there is a terminating | */
514 return WPS_ERROR_INVALID_PARAM
;
516 /* get the image ID */
517 n
= get_image_id(*id
);
519 /* check the image number and load state */
520 if(n
< 0 || n
>= MAX_IMAGES
|| wps_data
->img
[n
].loaded
)
522 /* Invalid image ID */
523 return WPS_ERROR_INVALID_PARAM
;
526 /* save a pointer to the filename */
527 bmp_names
[n
] = filename
;
529 wps_data
->img
[n
].x
= x
;
530 wps_data
->img
[n
].y
= y
;
532 /* save current viewport */
533 wps_data
->img
[n
].vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
535 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
537 wps_data
->img
[n
].always_display
= true;
541 /* Parse the (optional) number of sub-images */
543 newline
= strchr(ptr
, '\n');
544 pos
= strchr(ptr
, '|');
545 if (pos
&& pos
< newline
)
546 wps_data
->img
[n
].num_subimages
= atoi(ptr
);
548 if (wps_data
->img
[n
].num_subimages
<= 0)
549 return WPS_ERROR_INVALID_PARAM
;
552 /* Skip the rest of the line */
553 return skip_end_of_line(wps_bufptr
);
556 static int parse_viewport_display(const char *wps_bufptr
,
557 struct wps_token
*token
,
558 struct wps_data
*wps_data
)
561 char letter
= wps_bufptr
[0];
563 if (letter
< 'a' || letter
> 'z')
565 /* invalid viewport tag */
566 return WPS_ERROR_INVALID_PARAM
;
568 token
->value
.i
= letter
;
572 static int parse_viewport(const char *wps_bufptr
,
573 struct wps_token
*token
,
574 struct wps_data
*wps_data
)
576 (void)token
; /* Kill warnings */
577 const char *ptr
= wps_bufptr
;
590 int lcd_width
= LCD_WIDTH
, lcd_height
= LCD_HEIGHT
;
591 #ifdef HAVE_REMOTE_LCD
592 if (wps_data
->remote_wps
)
594 lcd_width
= LCD_REMOTE_WIDTH
;
595 lcd_height
= LCD_REMOTE_HEIGHT
;
599 if (wps_data
->num_viewports
>= WPS_MAX_VIEWPORTS
)
600 return WPS_ERROR_INVALID_PARAM
;
602 wps_data
->num_viewports
++;
603 /* check for the optional letter to signify its a hideable viewport */
604 /* %Vl|<label>|<rest of tags>| */
605 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= 0;
611 char label
= *(ptr
+2);
612 if (label
>= 'a' && label
<= 'z')
614 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= VP_DRAW_HIDEABLE
;
615 wps_data
->viewports
[wps_data
->num_viewports
].label
= label
;
618 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
623 return WPS_ERROR_INVALID_PARAM
;
626 vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
627 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
629 /* Set the defaults for fields not user-specified */
630 vp
->drawmode
= DRMODE_SOLID
;
632 /* Work out the depth of this display */
633 #ifdef HAVE_REMOTE_LCD
634 depth
= (wps_data
->remote_wps
? LCD_REMOTE_DEPTH
: LCD_DEPTH
);
639 #ifdef HAVE_LCD_COLOR
642 if (!(ptr
= parse_list("dddddcc", &set
, '|', ptr
, &vp
->x
, &vp
->y
, &vp
->width
,
643 &vp
->height
, &vp
->font
, &vp
->fg_pattern
,&vp
->bg_pattern
)))
644 return WPS_ERROR_INVALID_PARAM
;
648 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
650 /* Default to black on white */
653 if (!(ptr
= parse_list("dddddgg", &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 == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
662 if (!(ptr
= parse_list("ddddd", &set
, '|', ptr
, &vp
->x
, &vp
->y
,
663 &vp
->width
, &vp
->height
, &vp
->font
)))
664 return WPS_ERROR_INVALID_PARAM
;
670 /* Check for trailing | */
672 return WPS_ERROR_INVALID_PARAM
;
674 if (!LIST_VALUE_PARSED(set
, PL_X
) || !LIST_VALUE_PARSED(set
, PL_Y
))
675 return WPS_ERROR_INVALID_PARAM
;
678 if (!LIST_VALUE_PARSED(set
, PL_WIDTH
))
679 vp
->width
= lcd_width
- vp
->x
;
680 if (!LIST_VALUE_PARSED(set
, PL_HEIGHT
))
681 vp
->height
= lcd_height
- vp
->y
;
683 /* Default to using the user font if the font was an invalid number */
684 if (!LIST_VALUE_PARSED(set
, PL_FONT
) ||
685 ((vp
->font
!= FONT_SYSFIXED
) && (vp
->font
!= FONT_UI
)))
688 /* Validate the viewport dimensions - we know that the numbers are
689 non-negative integers */
690 if ((vp
->x
>= lcd_width
) ||
691 ((vp
->x
+ vp
->width
) > lcd_width
) ||
692 (vp
->y
>= lcd_height
) ||
693 ((vp
->y
+ vp
->height
) > lcd_height
))
695 return WPS_ERROR_INVALID_PARAM
;
698 #ifdef HAVE_LCD_COLOR
701 if (!LIST_VALUE_PARSED(set
, PL_FG
))
702 vp
->fg_pattern
= global_settings
.fg_color
;
703 if (!LIST_VALUE_PARSED(set
, PL_BG
))
704 vp
->bg_pattern
= global_settings
.bg_color
;
708 wps_data
->viewports
[wps_data
->num_viewports
-1].last_line
= wps_data
->num_lines
- 1;
710 wps_data
->viewports
[wps_data
->num_viewports
].first_line
= wps_data
->num_lines
;
712 if (wps_data
->num_sublines
< WPS_MAX_SUBLINES
)
714 wps_data
->lines
[wps_data
->num_lines
].first_subline_idx
=
715 wps_data
->num_sublines
;
717 wps_data
->sublines
[wps_data
->num_sublines
].first_token_idx
=
718 wps_data
->num_tokens
;
721 /* Skip the rest of the line */
722 return skip_end_of_line(wps_bufptr
);
725 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
726 static int parse_image_special(const char *wps_bufptr
,
727 struct wps_token
*token
,
728 struct wps_data
*wps_data
)
730 (void)wps_data
; /* kill warning */
732 const char *pos
= NULL
;
735 pos
= strchr(wps_bufptr
+ 1, '|');
736 newline
= strchr(wps_bufptr
, '\n');
739 return WPS_ERROR_INVALID_PARAM
;
741 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
743 /* format: %X|filename.bmp| */
744 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
748 /* Skip the rest of the line */
749 return skip_end_of_line(wps_bufptr
);
753 #endif /* HAVE_LCD_BITMAP */
755 static int parse_setting(const char *wps_bufptr
,
756 struct wps_token
*token
,
757 struct wps_data
*wps_data
)
760 const char *ptr
= wps_bufptr
;
764 /* Find the setting's cfg_name */
766 return WPS_ERROR_INVALID_PARAM
;
768 end
= strchr(ptr
,'|');
770 return WPS_ERROR_INVALID_PARAM
;
772 /* Find the setting */
773 for (i
=0; i
<nb_settings
; i
++)
774 if (settings
[i
].cfg_name
&&
775 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
776 /* prevent matches on cfg_name prefixes */
777 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
779 if (i
== nb_settings
)
780 return WPS_ERROR_INVALID_PARAM
;
782 /* Store the setting number */
785 /* Skip the rest of the line */
790 static int parse_dir_level(const char *wps_bufptr
,
791 struct wps_token
*token
,
792 struct wps_data
*wps_data
)
794 char val
[] = { *wps_bufptr
, '\0' };
795 token
->value
.i
= atoi(val
);
796 (void)wps_data
; /* Kill warnings */
800 static int parse_timeout(const char *wps_bufptr
,
801 struct wps_token
*token
,
802 struct wps_data
*wps_data
)
806 bool have_point
= false;
807 bool have_tenth
= false;
809 (void)wps_data
; /* Kill the warning */
811 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
813 if (*wps_bufptr
!= '.')
816 val
+= *wps_bufptr
- '0';
832 if (have_tenth
== false)
835 if (val
== 0 && skip
== 0)
837 /* decide what to do if no value was specified */
840 case WPS_TOKEN_SUBLINE_TIMEOUT
:
842 case WPS_TOKEN_BUTTON_VOLUME
:
847 token
->value
.i
= val
;
852 static int parse_progressbar(const char *wps_bufptr
,
853 struct wps_token
*token
,
854 struct wps_data
*wps_data
)
856 (void)token
; /* Kill warnings */
857 /* %pb or %pb|filename|x|y|width|height|
858 using - for any of the params uses "sane" values */
859 #ifdef HAVE_LCD_BITMAP
867 const char *filename
;
868 int x
, y
, height
, width
;
870 const char *ptr
= wps_bufptr
;
871 struct progressbar
*pb
;
872 struct viewport
*vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
874 int font_height
= font_get(vp
->font
)->height
;
878 int line_num
= wps_data
->num_lines
-
879 wps_data
->viewports
[wps_data
->num_viewports
].first_line
;
881 if (wps_data
->progressbar_count
>= MAX_PROGRESSBARS
)
882 return WPS_ERROR_INVALID_PARAM
;
884 pb
= &wps_data
->progressbar
[wps_data
->progressbar_count
];
885 pb
->have_bitmap_pb
= false;
887 if (*wps_bufptr
!= '|') /* regular old style */
890 pb
->width
= vp
->width
;
891 pb
->height
= SYSFONT_HEIGHT
-2;
892 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
894 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
895 wps_data
->progressbar_count
++;
898 ptr
= wps_bufptr
+ 1;
900 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
901 &x
, &y
, &width
, &height
)))
902 return WPS_ERROR_INVALID_PARAM
;
904 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
905 bmp_names
[PROGRESSBAR_BMP
+wps_data
->progressbar_count
] = filename
;
907 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
912 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
914 /* A zero width causes a divide-by-zero error later, so reject it */
916 return WPS_ERROR_INVALID_PARAM
;
921 pb
->width
= vp
->width
- pb
->x
;
923 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
925 /* A zero height makes no sense - reject it */
927 return WPS_ERROR_INVALID_PARAM
;
932 pb
->height
= font_height
;
934 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
937 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
939 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
940 wps_data
->progressbar_count
++;
942 /* Skip the rest of the line */
943 return skip_end_of_line(wps_bufptr
)-1;
946 if (*(wps_bufptr
-1) == 'f')
947 wps_data
->full_line_progressbar
= true;
949 wps_data
->full_line_progressbar
= false;
957 static int parse_albumart_load(const char *wps_bufptr
,
958 struct wps_token
*token
,
959 struct wps_data
*wps_data
)
961 const char *_pos
, *newline
;
963 (void)token
; /* silence warning */
965 /* reset albumart info in wps */
966 wps_data
->albumart_max_width
= -1;
967 wps_data
->albumart_max_height
= -1;
968 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
969 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
971 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
973 newline
= strchr(wps_bufptr
, '\n');
975 /* initial validation and parsing of x and y components */
976 if (*wps_bufptr
!= '|')
977 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
979 _pos
= wps_bufptr
+ 1;
981 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|@ */
982 wps_data
->albumart_x
= atoi(_pos
);
984 _pos
= strchr(_pos
, '|');
985 if (!_pos
|| _pos
> newline
|| !isdigit(*(++_pos
)))
986 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
988 wps_data
->albumart_y
= atoi(_pos
);
990 _pos
= strchr(_pos
, '|');
991 if (!_pos
|| _pos
> newline
)
992 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
995 /* parsing width field */
999 /* apply each modifier in turn */
1006 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1010 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1015 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1023 /* simply ignored */
1030 /* extract max width data */
1033 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
1034 return WPS_ERROR_INVALID_PARAM
;
1036 wps_data
->albumart_max_width
= atoi(_pos
);
1038 _pos
= strchr(_pos
, '|');
1039 if (!_pos
|| _pos
> newline
)
1040 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
1041 e.g. %Cl|7|59|200\n */
1044 /* parsing height field */
1048 /* apply each modifier in turn */
1055 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_TOP
;
1059 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1064 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1072 /* simply ignored */
1079 /* extract max height data */
1082 if (!isdigit(*_pos
))
1083 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1085 wps_data
->albumart_max_height
= atoi(_pos
);
1087 _pos
= strchr(_pos
, '|');
1088 if (!_pos
|| _pos
> newline
)
1089 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1090 e.g. %Cl|7|59|200|200\n */
1093 /* if we got here, we parsed everything ok .. ! */
1094 if (wps_data
->albumart_max_width
< 0)
1095 wps_data
->albumart_max_width
= 0;
1096 else if (wps_data
->albumart_max_width
> LCD_WIDTH
)
1097 wps_data
->albumart_max_width
= LCD_WIDTH
;
1099 if (wps_data
->albumart_max_height
< 0)
1100 wps_data
->albumart_max_height
= 0;
1101 else if (wps_data
->albumart_max_height
> LCD_HEIGHT
)
1102 wps_data
->albumart_max_height
= LCD_HEIGHT
;
1104 wps_data
->wps_uses_albumart
= WPS_ALBUMART_LOAD
;
1106 /* Skip the rest of the line */
1107 return skip_end_of_line(wps_bufptr
);
1110 static int parse_albumart_conditional(const char *wps_bufptr
,
1111 struct wps_token
*token
,
1112 struct wps_data
*wps_data
)
1114 struct wps_token
*prevtoken
= token
;
1116 if (wps_data
->num_tokens
>= 1 && prevtoken
->type
== WPS_TOKEN_CONDITIONAL
)
1118 /* This %C is part of a %?C construct.
1119 It's either %?C<blah> or %?Cn<blah> */
1120 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1121 if (*wps_bufptr
== 'n' && *(wps_bufptr
+ 1) == '<')
1126 else if (*wps_bufptr
== '<')
1132 token
->type
= WPS_NO_TOKEN
;
1138 /* This %C tag is in a conditional construct. */
1139 wps_data
->albumart_cond_index
= condindex
[level
];
1143 #endif /* HAVE_ALBUMART */
1145 /* Parse a generic token from the given string. Return the length read */
1146 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1148 int skip
= 0, taglen
= 0, ret
;
1149 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1150 const struct wps_tag
*tag
;
1161 /* escaped characters */
1162 token
->type
= WPS_TOKEN_CHARACTER
;
1163 token
->value
.c
= *wps_bufptr
;
1165 wps_data
->num_tokens
++;
1169 /* conditional tag */
1170 token
->type
= WPS_TOKEN_CONDITIONAL
;
1172 condindex
[level
] = wps_data
->num_tokens
;
1173 numoptions
[level
] = 1;
1174 wps_data
->num_tokens
++;
1175 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1176 if (ret
< 0) return ret
;
1181 /* find what tag we have */
1182 for (tag
= all_tags
;
1183 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1186 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1187 token
->type
= tag
->type
;
1188 wps_data
->sublines
[wps_data
->num_sublines
].line_type
|=
1191 /* if the tag has a special parsing function, we call it */
1192 if (tag
->parse_func
)
1194 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1195 if (ret
< 0) return ret
;
1199 /* Some tags we don't want to save as tokens */
1200 if (tag
->type
== WPS_NO_TOKEN
)
1203 /* tags that start with 'F', 'I' or 'D' are for the next file */
1204 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1205 *(tag
->name
) == 'D')
1208 wps_data
->num_tokens
++;
1217 data is the pointer to the structure where the parsed WPS should be stored.
1219 wps_bufptr points to the string containing the WPS tags */
1220 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
)
1222 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1225 char *stringbuf
= data
->string_buffer
;
1226 int stringbuf_used
= 0;
1227 enum wps_parse_error fail
= PARSE_OK
;
1232 while(*wps_bufptr
&& !fail
&& data
->num_tokens
< WPS_MAX_TOKENS
- 1
1233 && data
->num_viewports
< WPS_MAX_VIEWPORTS
1234 && data
->num_lines
< WPS_MAX_LINES
)
1236 switch(*wps_bufptr
++)
1241 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1243 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1246 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1248 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1254 /* Alternating sublines separator */
1256 if (level
>= 0) /* there are unclosed conditionals */
1258 fail
= PARSE_FAIL_UNCLOSED_COND
;
1262 if (data
->num_sublines
+1 < WPS_MAX_SUBLINES
)
1263 wps_start_new_subline(data
);
1265 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1269 /* Conditional list start */
1271 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1273 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1277 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1278 lastcond
[level
] = data
->num_tokens
++;
1281 /* Conditional list end */
1283 if (level
< 0) /* not in a conditional, invalid char */
1285 fail
= PARSE_FAIL_INVALID_CHAR
;
1289 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1290 if (lastcond
[level
])
1291 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1294 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1298 lastcond
[level
] = 0;
1300 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1304 /* Conditional list option */
1306 if (level
< 0) /* not in a conditional, invalid char */
1308 fail
= PARSE_FAIL_INVALID_CHAR
;
1312 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1313 if (lastcond
[level
])
1314 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1317 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1321 lastcond
[level
] = data
->num_tokens
;
1322 numoptions
[level
]++;
1328 if (level
>= 0) /* there are unclosed conditionals */
1330 fail
= PARSE_FAIL_UNCLOSED_COND
;
1334 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1337 /* End of this line */
1339 if (level
>= 0) /* there are unclosed conditionals */
1341 fail
= PARSE_FAIL_UNCLOSED_COND
;
1346 wps_start_new_subline(data
);
1347 data
->num_lines
++; /* Start a new line */
1349 if ((data
->num_lines
< WPS_MAX_LINES
) &&
1350 (data
->num_sublines
< WPS_MAX_SUBLINES
))
1352 data
->lines
[data
->num_lines
].first_subline_idx
=
1355 data
->sublines
[data
->num_sublines
].first_token_idx
=
1364 unsigned int len
= 1;
1365 const char *string_start
= wps_bufptr
- 1;
1367 /* find the length of the string */
1368 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1369 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1370 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1371 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1377 /* look if we already have that string */
1381 for (i
= 0, str
= data
->strings
, found
= false;
1382 i
< data
->num_strings
&&
1383 !(found
= (strlen(*str
) == len
&&
1384 strncmp(string_start
, *str
, len
) == 0));
1386 /* If a matching string is found, found is true and i is
1387 the index of the string. If not, found is false */
1393 if (stringbuf_used
+ len
> STRING_BUFFER_SIZE
- 1
1394 || data
->num_strings
>= WPS_MAX_STRINGS
)
1396 /* too many strings or characters */
1397 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1401 strncpy(stringbuf
, string_start
, len
);
1402 *(stringbuf
+ len
) = '\0';
1404 data
->strings
[data
->num_strings
] = stringbuf
;
1405 stringbuf
+= len
+ 1;
1406 stringbuf_used
+= len
+ 1;
1407 data
->tokens
[data
->num_tokens
].value
.i
=
1409 data
->num_strings
++;
1413 /* another occurrence of an existing string */
1414 data
->tokens
[data
->num_tokens
].value
.i
= i
;
1416 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1423 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1424 fail
= PARSE_FAIL_UNCLOSED_COND
;
1426 if (*wps_bufptr
&& !fail
)
1427 /* one of the limits of the while loop was exceeded */
1428 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1430 data
->viewports
[data
->num_viewports
].last_line
= data
->num_lines
- 1;
1432 /* We have finished with the last viewport, so increment count */
1433 data
->num_viewports
++;
1435 #if defined(DEBUG) || defined(SIMULATOR)
1436 print_debug_info(data
, fail
, line
);
1442 #ifdef HAVE_LCD_BITMAP
1443 /* Clear the WPS image cache */
1444 static void wps_images_clear(struct wps_data
*data
)
1447 /* set images to unloaded and not displayed */
1448 for (i
= 0; i
< MAX_IMAGES
; i
++)
1450 data
->img
[i
].loaded
= false;
1451 data
->img
[i
].display
= -1;
1452 data
->img
[i
].always_display
= false;
1453 data
->img
[i
].num_subimages
= 1;
1458 /* initial setup of wps_data */
1459 void wps_data_init(struct wps_data
*wps_data
)
1461 #ifdef HAVE_LCD_BITMAP
1462 wps_images_clear(wps_data
);
1463 wps_data
->wps_sb_tag
= false;
1464 wps_data
->show_sb_on_wps
= false;
1465 wps_data
->img_buf_ptr
= wps_data
->img_buf
; /* where in image buffer */
1466 wps_data
->img_buf_free
= IMG_BUFSIZE
; /* free space in image buffer */
1467 wps_data
->peak_meter_enabled
= false;
1469 wps_data
->progressbar_count
= 0;
1470 #else /* HAVE_LCD_CHARCELLS */
1472 for (i
= 0; i
< 8; i
++)
1474 wps_data
->wps_progress_pat
[i
] = 0;
1476 wps_data
->full_line_progressbar
= false;
1478 wps_data
->button_time_volume
= 0;
1479 wps_data
->wps_loaded
= false;
1482 static void wps_reset(struct wps_data
*data
)
1484 #ifdef HAVE_REMOTE_LCD
1485 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1487 memset(data
, 0, sizeof(*data
));
1488 wps_data_init(data
);
1489 #ifdef HAVE_REMOTE_LCD
1490 data
->remote_wps
= rwps
;
1494 #ifdef HAVE_LCD_BITMAP
1496 static bool load_wps_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1498 char img_path
[MAX_PATH
];
1499 struct bitmap
*bitmap
;
1502 for (n
= 0; n
< BACKDROP_BMP
; n
++)
1506 get_image_filename(bmp_names
[n
], bmpdir
,
1507 img_path
, sizeof(img_path
));
1509 if (n
>= PROGRESSBAR_BMP
) {
1510 /* progressbar bitmap */
1511 bitmap
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].bm
;
1512 loaded
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].have_bitmap_pb
;
1514 /* regular bitmap */
1515 bitmap
= &wps_data
->img
[n
].bm
;
1516 loaded
= &wps_data
->img
[n
].loaded
;
1519 /* load the image */
1520 bitmap
->data
= wps_data
->img_buf_ptr
;
1521 if (load_bitmap(wps_data
, img_path
, bitmap
))
1525 /* Calculate and store height if this image has sub-images */
1527 wps_data
->img
[n
].subimage_height
= wps_data
->img
[n
].bm
.height
/
1528 wps_data
->img
[n
].num_subimages
;
1532 /* Abort if we can't load an image */
1533 DEBUGF("ERR: Failed to load image %d - %s\n",n
,img_path
);
1539 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1540 if (bmp_names
[BACKDROP_BMP
])
1542 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1543 img_path
, sizeof(img_path
));
1545 #if defined(HAVE_REMOTE_LCD)
1546 /* We only need to check LCD type if there is a remote LCD */
1547 if (!wps_data
->remote_wps
)
1550 /* Load backdrop for the main LCD */
1551 if (!load_wps_backdrop(img_path
))
1554 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1557 /* Load backdrop for the remote LCD */
1558 if (!load_remote_wps_backdrop(img_path
))
1563 #endif /* has backdrop support */
1565 /* If we got here, everything was OK */
1569 #endif /* HAVE_LCD_BITMAP */
1571 /* to setup up the wps-data from a format-buffer (isfile = false)
1572 from a (wps-)file (isfile = true)*/
1573 bool wps_data_load(struct wps_data
*wps_data
,
1574 struct screen
*display
,
1578 #ifdef HAVE_ALBUMART
1579 struct mp3entry
*curtrack
;
1582 int wps_uses_albumart
= wps_data
->wps_uses_albumart
;
1583 int albumart_max_height
= wps_data
->albumart_max_height
;
1584 int albumart_max_width
= wps_data
->albumart_max_width
;
1586 if (!wps_data
|| !buf
)
1589 wps_reset(wps_data
);
1591 /* Initialise the first (default) viewport */
1592 wps_data
->viewports
[0].vp
.x
= 0;
1593 wps_data
->viewports
[0].vp
.width
= display
->getwidth();
1594 if (!global_settings
.statusbar
)
1596 wps_data
->viewports
[0].vp
.y
= 0;
1597 wps_data
->viewports
[0].vp
.height
= display
->getheight();
1601 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
1602 wps_data
->viewports
[0].vp
.height
= display
->getheight() -
1605 #ifdef HAVE_LCD_BITMAP
1606 wps_data
->viewports
[0].vp
.font
= FONT_UI
;
1607 wps_data
->viewports
[0].vp
.drawmode
= DRMODE_SOLID
;
1610 if (display
->depth
> 1)
1612 wps_data
->viewports
[0].vp
.fg_pattern
= display
->get_foreground();
1613 wps_data
->viewports
[0].vp
.bg_pattern
= display
->get_background();
1618 return wps_parse(wps_data
, buf
);
1623 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1624 * wants to be a virtual file. Feel free to modify dirbrowse()
1625 * if you're feeling brave.
1628 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1630 global_settings
.wps_file
[0] = 0;
1634 #ifdef HAVE_REMOTE_LCD
1635 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1637 global_settings
.rwps_file
[0] = 0;
1641 #endif /* __PCTOOL__ */
1643 int fd
= open_utf8(buf
, O_RDONLY
);
1648 /* get buffer space from the plugin buffer */
1649 size_t buffersize
= 0;
1650 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1655 /* copy the file's content to the buffer for parsing,
1656 ensuring that every line ends with a newline char. */
1657 unsigned int start
= 0;
1658 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1660 start
+= strlen(wps_buffer
+ start
);
1661 if (start
< buffersize
- 1)
1663 wps_buffer
[start
++] = '\n';
1664 wps_buffer
[start
] = 0;
1673 #ifdef HAVE_LCD_BITMAP
1674 /* Set all filename pointers to NULL */
1675 memset(bmp_names
, 0, sizeof(bmp_names
));
1678 /* parse the WPS source */
1679 if (!wps_parse(wps_data
, wps_buffer
)) {
1680 wps_reset(wps_data
);
1684 wps_data
->wps_loaded
= true;
1686 #ifdef HAVE_LCD_BITMAP
1687 /* get the bitmap dir */
1688 char bmpdir
[MAX_PATH
];
1690 char *dot
= strrchr(buf
, '.');
1691 bmpdirlen
= dot
- buf
;
1692 strncpy(bmpdir
, buf
, dot
- buf
);
1693 bmpdir
[bmpdirlen
] = 0;
1695 /* load the bitmaps that were found by the parsing */
1696 if (!load_wps_bitmaps(wps_data
, bmpdir
)) {
1697 wps_reset(wps_data
);
1701 #ifdef HAVE_ALBUMART
1702 status
= audio_status();
1703 if (((!wps_uses_albumart
&& wps_data
->wps_uses_albumart
) ||
1704 (wps_data
->wps_uses_albumart
&&
1705 (albumart_max_height
!= wps_data
->albumart_max_height
||
1706 albumart_max_width
!= wps_data
->albumart_max_width
))) &&
1707 status
& AUDIO_STATUS_PLAY
)
1709 curtrack
= audio_current_track();
1710 offset
= curtrack
->offset
;
1712 if (!(status
& AUDIO_STATUS_PAUSE
))
1720 int wps_subline_index(struct wps_data
*data
, int line
, int subline
)
1722 return data
->lines
[line
].first_subline_idx
+ subline
;
1725 int wps_first_token_index(struct wps_data
*data
, int line
, int subline
)
1727 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1728 return data
->sublines
[first_subline_idx
+ subline
].first_token_idx
;
1731 int wps_last_token_index(struct wps_data
*data
, int line
, int subline
)
1733 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1734 int idx
= first_subline_idx
+ subline
;
1735 if (idx
< data
->num_sublines
- 1)
1737 /* This subline ends where the next begins */
1738 return data
->sublines
[idx
+1].first_token_idx
- 1;
1742 /* The last subline goes to the end */
1743 return data
->num_tokens
- 1;