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 ****************************************************************************/
42 #define SYSFONT_HEIGHT 8
45 #define FONT_SYSFIXED 0
60 #ifdef HAVE_LCD_BITMAP
68 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
69 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
71 #define WPS_ERROR_INVALID_PARAM -1
73 /* level of current conditional.
74 -1 means we're not in a conditional. */
75 static int level
= -1;
77 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
78 or WPS_TOKEN_CONDITIONAL_START in current level */
79 static int lastcond
[WPS_MAX_COND_LEVEL
];
81 /* index of the WPS_TOKEN_CONDITIONAL in current level */
82 static int condindex
[WPS_MAX_COND_LEVEL
];
84 /* number of condtional options in current level */
85 static int numoptions
[WPS_MAX_COND_LEVEL
];
87 /* the current line in the file */
90 #ifdef HAVE_LCD_BITMAP
93 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
95 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
98 #define PROGRESSBAR_BMP MAX_IMAGES
99 #define BACKDROP_BMP (MAX_BITMAPS-1)
101 /* pointers to the bitmap filenames in the WPS source */
102 static const char *bmp_names
[MAX_BITMAPS
];
104 #endif /* HAVE_LCD_BITMAP */
107 /* debugging function */
108 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
111 static void wps_reset(struct wps_data
*data
);
113 /* Function for parsing of details for a token. At the moment the
114 function is called, the token type has already been set. The
115 function must fill in the details and possibly add more tokens
116 to the token array. It should return the number of chars that
119 wps_bufptr points to the char following the tag (i.e. where
121 token is the pointer to the 'main' token being parsed
123 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
124 struct wps_token
*token
, struct wps_data
*wps_data
);
127 enum wps_token_type type
;
129 unsigned char refresh_type
;
130 const wps_tag_parse_func parse_func
;
133 /* prototypes of all special parse functions : */
134 static int parse_timeout(const char *wps_bufptr
,
135 struct wps_token
*token
, struct wps_data
*wps_data
);
136 static int parse_progressbar(const char *wps_bufptr
,
137 struct wps_token
*token
, struct wps_data
*wps_data
);
138 static int parse_dir_level(const char *wps_bufptr
,
139 struct wps_token
*token
, struct wps_data
*wps_data
);
141 #ifdef HAVE_LCD_BITMAP
142 static int parse_viewport_display(const char *wps_bufptr
,
143 struct wps_token
*token
, struct wps_data
*wps_data
);
144 static int parse_viewport(const char *wps_bufptr
,
145 struct wps_token
*token
, struct wps_data
*wps_data
);
146 static int parse_statusbar_enable(const char *wps_bufptr
,
147 struct wps_token
*token
, struct wps_data
*wps_data
);
148 static int parse_statusbar_disable(const char *wps_bufptr
,
149 struct wps_token
*token
, struct wps_data
*wps_data
);
150 static int parse_image_display(const char *wps_bufptr
,
151 struct wps_token
*token
, struct wps_data
*wps_data
);
152 static int parse_image_load(const char *wps_bufptr
,
153 struct wps_token
*token
, struct wps_data
*wps_data
);
154 #endif /*HAVE_LCD_BITMAP */
155 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
156 static int parse_image_special(const char *wps_bufptr
,
157 struct wps_token
*token
, struct wps_data
*wps_data
);
160 static int parse_albumart_load(const char *wps_bufptr
,
161 struct wps_token
*token
, struct wps_data
*wps_data
);
162 static int parse_albumart_conditional(const char *wps_bufptr
,
163 struct wps_token
*token
, struct wps_data
*wps_data
);
164 #endif /* HAVE_ALBUMART */
167 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
169 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
172 /* array of available tags - those with more characters have to go first
173 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
174 static const struct wps_tag all_tags
[] = {
176 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
177 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
178 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
180 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
181 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
182 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
183 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
184 #if CONFIG_CHARGING >= CHARGING_MONITOR
185 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
188 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
191 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
192 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
193 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
194 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
195 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
196 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
197 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
198 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
199 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
200 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
211 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
212 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
213 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
214 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
215 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
216 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
217 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
218 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
219 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
224 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_DYNAMIC
, NULL
},
225 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_DYNAMIC
, NULL
},
226 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_DYNAMIC
, NULL
},
227 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_DYNAMIC
, NULL
},
228 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_DYNAMIC
, NULL
},
229 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_DYNAMIC
, NULL
},
230 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_DYNAMIC
, NULL
},
231 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_DYNAMIC
, NULL
},
232 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_DYNAMIC
, NULL
},
233 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_DYNAMIC
,
236 /* current metadata */
237 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
238 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
242 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
243 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
244 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
245 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_DYNAMIC
, NULL
},
252 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_DYNAMIC
, NULL
},
253 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_DYNAMIC
, NULL
},
254 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_DYNAMIC
, NULL
},
255 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_DYNAMIC
, NULL
},
256 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_DYNAMIC
, NULL
},
257 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_DYNAMIC
, NULL
},
258 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_DYNAMIC
, NULL
},
259 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_DYNAMIC
, NULL
},
260 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_DYNAMIC
, NULL
},
261 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_DYNAMIC
, NULL
},
262 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_DYNAMIC
, NULL
},
264 #if (CONFIG_CODEC != MAS3507D)
265 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
268 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
269 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
272 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
274 #ifdef HAS_REMOTE_BUTTON_HOLD
275 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
277 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
280 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
281 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
282 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
285 #ifdef HAVE_LCD_BITMAP
286 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
288 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
289 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
291 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
294 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
296 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
297 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
298 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
299 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
301 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
302 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
303 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
304 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
307 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
308 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
309 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
312 #if CONFIG_CODEC == SWCODEC
313 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
314 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
317 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
318 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
320 #ifdef HAVE_LCD_BITMAP
321 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
322 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
324 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
326 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
327 parse_image_display
},
329 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
331 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
332 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
,
333 parse_albumart_conditional
},
336 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
337 parse_viewport_display
},
338 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
340 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
341 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
345 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
346 /* the array MUST end with an empty string (first char is \0) */
349 /* Returns the number of chars that should be skipped to jump
350 immediately after the first eol, i.e. to the start of the next line */
351 static int skip_end_of_line(const char *wps_bufptr
)
355 while(*(wps_bufptr
+ skip
) != '\n')
360 /* Starts a new subline in the current line during parsing */
361 static void wps_start_new_subline(struct wps_data
*data
)
363 data
->num_sublines
++;
364 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
365 data
->lines
[data
->num_lines
].num_sublines
++;
368 #ifdef HAVE_LCD_BITMAP
370 static int parse_statusbar_enable(const char *wps_bufptr
,
371 struct wps_token
*token
,
372 struct wps_data
*wps_data
)
374 (void)token
; /* Kill warnings */
375 wps_data
->wps_sb_tag
= true;
376 wps_data
->show_sb_on_wps
= true;
377 if (wps_data
->viewports
[0].vp
.y
== 0)
379 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
380 wps_data
->viewports
[0].vp
.height
-= STATUSBAR_HEIGHT
;
382 return skip_end_of_line(wps_bufptr
);
385 static int parse_statusbar_disable(const char *wps_bufptr
,
386 struct wps_token
*token
,
387 struct wps_data
*wps_data
)
389 (void)token
; /* Kill warnings */
390 wps_data
->wps_sb_tag
= true;
391 wps_data
->show_sb_on_wps
= false;
392 if (wps_data
->viewports
[0].vp
.y
== STATUSBAR_HEIGHT
)
394 wps_data
->viewports
[0].vp
.y
= 0;
395 wps_data
->viewports
[0].vp
.height
+= STATUSBAR_HEIGHT
;
397 return skip_end_of_line(wps_bufptr
);
400 static bool load_bitmap(struct wps_data
*wps_data
,
405 #ifdef HAVE_REMOTE_LCD
406 if (wps_data
->remote_wps
)
407 format
= FORMAT_ANY
|FORMAT_REMOTE
;
410 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
412 int ret
= read_bmp_file(filename
, bm
,
413 wps_data
->img_buf_free
,
420 /* Always consume an even number of bytes */
422 wps_data
->img_buf_ptr
+= ret
;
423 wps_data
->img_buf_free
-= ret
;
431 static int get_image_id(int c
)
433 if(c
>= 'a' && c
<= 'z')
435 else if(c
>= 'A' && c
<= 'Z')
441 static char *get_image_filename(const char *start
, const char* bmpdir
,
442 char *buf
, int buf_size
)
444 const char *end
= strchr(start
, '|');
446 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
452 int bmpdirlen
= strlen(bmpdir
);
455 buf
[bmpdirlen
] = '/';
456 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
457 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
462 static int parse_image_display(const char *wps_bufptr
,
463 struct wps_token
*token
,
464 struct wps_data
*wps_data
)
467 int n
= get_image_id(wps_bufptr
[0]);
472 /* invalid picture display tag */
473 return WPS_ERROR_INVALID_PARAM
;
476 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
479 if (subimage
>= wps_data
->img
[n
].num_subimages
)
480 return WPS_ERROR_INVALID_PARAM
;
482 /* Store sub-image number to display in high bits */
483 token
->value
.i
= n
| (subimage
<< 8);
484 return 2; /* We have consumed 2 bytes */
487 return 1; /* We have consumed 1 byte */
491 static int parse_image_load(const char *wps_bufptr
,
492 struct wps_token
*token
,
493 struct wps_data
*wps_data
)
496 const char *ptr
= wps_bufptr
;
498 const char* filename
;
503 /* format: %x|n|filename.bmp|x|y|
504 or %xl|n|filename.bmp|x|y|
505 or %xl|n|filename.bmp|x|y|num_subimages|
509 return WPS_ERROR_INVALID_PARAM
;
513 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
514 return WPS_ERROR_INVALID_PARAM
;
516 /* Check there is a terminating | */
518 return WPS_ERROR_INVALID_PARAM
;
520 /* get the image ID */
521 n
= get_image_id(*id
);
523 /* check the image number and load state */
524 if(n
< 0 || n
>= MAX_IMAGES
|| wps_data
->img
[n
].loaded
)
526 /* Invalid image ID */
527 return WPS_ERROR_INVALID_PARAM
;
530 /* save a pointer to the filename */
531 bmp_names
[n
] = filename
;
533 wps_data
->img
[n
].x
= x
;
534 wps_data
->img
[n
].y
= y
;
536 /* save current viewport */
537 wps_data
->img
[n
].vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
539 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
541 wps_data
->img
[n
].always_display
= true;
545 /* Parse the (optional) number of sub-images */
547 newline
= strchr(ptr
, '\n');
548 pos
= strchr(ptr
, '|');
549 if (pos
&& pos
< newline
)
550 wps_data
->img
[n
].num_subimages
= atoi(ptr
);
552 if (wps_data
->img
[n
].num_subimages
<= 0)
553 return WPS_ERROR_INVALID_PARAM
;
556 /* Skip the rest of the line */
557 return skip_end_of_line(wps_bufptr
);
560 static int parse_viewport_display(const char *wps_bufptr
,
561 struct wps_token
*token
,
562 struct wps_data
*wps_data
)
565 char letter
= wps_bufptr
[0];
567 if (letter
< 'a' || letter
> 'z')
569 /* invalid viewport tag */
570 return WPS_ERROR_INVALID_PARAM
;
572 token
->value
.i
= letter
;
576 static int parse_viewport(const char *wps_bufptr
,
577 struct wps_token
*token
,
578 struct wps_data
*wps_data
)
580 (void)token
; /* Kill warnings */
581 const char *ptr
= wps_bufptr
;
594 int lcd_width
= LCD_WIDTH
, lcd_height
= LCD_HEIGHT
;
595 #ifdef HAVE_REMOTE_LCD
596 if (wps_data
->remote_wps
)
598 lcd_width
= LCD_REMOTE_WIDTH
;
599 lcd_height
= LCD_REMOTE_HEIGHT
;
603 if (wps_data
->num_viewports
>= WPS_MAX_VIEWPORTS
)
604 return WPS_ERROR_INVALID_PARAM
;
606 wps_data
->num_viewports
++;
607 /* check for the optional letter to signify its a hideable viewport */
608 /* %Vl|<label>|<rest of tags>| */
609 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= 0;
615 char label
= *(ptr
+2);
616 if (label
>= 'a' && label
<= 'z')
618 wps_data
->viewports
[wps_data
->num_viewports
].hidden_flags
= VP_DRAW_HIDEABLE
;
619 wps_data
->viewports
[wps_data
->num_viewports
].label
= label
;
622 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
627 return WPS_ERROR_INVALID_PARAM
;
630 vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
631 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
633 /* Set the defaults for fields not user-specified */
634 vp
->drawmode
= DRMODE_SOLID
;
636 /* Work out the depth of this display */
637 #ifdef HAVE_REMOTE_LCD
638 depth
= (wps_data
->remote_wps
? LCD_REMOTE_DEPTH
: LCD_DEPTH
);
643 #ifdef HAVE_LCD_COLOR
646 if (!(ptr
= parse_list("dddddcc", &set
, '|', ptr
, &vp
->x
, &vp
->y
, &vp
->width
,
647 &vp
->height
, &vp
->font
, &vp
->fg_pattern
,&vp
->bg_pattern
)))
648 return WPS_ERROR_INVALID_PARAM
;
652 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
654 /* Default to black on white */
657 if (!(ptr
= parse_list("dddddgg", &set
, '|', ptr
, &vp
->x
, &vp
->y
, &vp
->width
,
658 &vp
->height
, &vp
->font
, &vp
->fg_pattern
, &vp
->bg_pattern
)))
659 return WPS_ERROR_INVALID_PARAM
;
663 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
666 if (!(ptr
= parse_list("ddddd", &set
, '|', ptr
, &vp
->x
, &vp
->y
,
667 &vp
->width
, &vp
->height
, &vp
->font
)))
668 return WPS_ERROR_INVALID_PARAM
;
674 /* Check for trailing | */
676 return WPS_ERROR_INVALID_PARAM
;
678 if (!LIST_VALUE_PARSED(set
, PL_X
) || !LIST_VALUE_PARSED(set
, PL_Y
))
679 return WPS_ERROR_INVALID_PARAM
;
682 if (!LIST_VALUE_PARSED(set
, PL_WIDTH
))
683 vp
->width
= lcd_width
- vp
->x
;
684 if (!LIST_VALUE_PARSED(set
, PL_HEIGHT
))
685 vp
->height
= lcd_height
- vp
->y
;
687 /* Default to using the user font if the font was an invalid number */
688 if (!LIST_VALUE_PARSED(set
, PL_FONT
) ||
689 ((vp
->font
!= FONT_SYSFIXED
) && (vp
->font
!= FONT_UI
)))
692 /* Validate the viewport dimensions - we know that the numbers are
693 non-negative integers */
694 if ((vp
->x
>= lcd_width
) ||
695 ((vp
->x
+ vp
->width
) > lcd_width
) ||
696 (vp
->y
>= lcd_height
) ||
697 ((vp
->y
+ vp
->height
) > lcd_height
))
699 return WPS_ERROR_INVALID_PARAM
;
702 #ifdef HAVE_LCD_COLOR
705 if (!LIST_VALUE_PARSED(set
, PL_FG
))
706 vp
->fg_pattern
= global_settings
.fg_color
;
707 if (!LIST_VALUE_PARSED(set
, PL_BG
))
708 vp
->bg_pattern
= global_settings
.bg_color
;
712 wps_data
->viewports
[wps_data
->num_viewports
-1].last_line
= wps_data
->num_lines
- 1;
714 wps_data
->viewports
[wps_data
->num_viewports
].first_line
= wps_data
->num_lines
;
716 if (wps_data
->num_sublines
< WPS_MAX_SUBLINES
)
718 wps_data
->lines
[wps_data
->num_lines
].first_subline_idx
=
719 wps_data
->num_sublines
;
721 wps_data
->sublines
[wps_data
->num_sublines
].first_token_idx
=
722 wps_data
->num_tokens
;
725 /* Skip the rest of the line */
726 return skip_end_of_line(wps_bufptr
);
730 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
731 static int parse_image_special(const char *wps_bufptr
,
732 struct wps_token
*token
,
733 struct wps_data
*wps_data
)
735 (void)wps_data
; /* kill warning */
737 const char *pos
= NULL
;
740 pos
= strchr(wps_bufptr
+ 1, '|');
741 newline
= strchr(wps_bufptr
, '\n');
744 return WPS_ERROR_INVALID_PARAM
;
746 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
748 /* format: %X|filename.bmp| */
749 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
753 /* Skip the rest of the line */
754 return skip_end_of_line(wps_bufptr
);
758 #endif /* HAVE_LCD_BITMAP */
760 static int parse_dir_level(const char *wps_bufptr
,
761 struct wps_token
*token
,
762 struct wps_data
*wps_data
)
764 char val
[] = { *wps_bufptr
, '\0' };
765 token
->value
.i
= atoi(val
);
766 (void)wps_data
; /* Kill warnings */
770 static int parse_timeout(const char *wps_bufptr
,
771 struct wps_token
*token
,
772 struct wps_data
*wps_data
)
776 bool have_point
= false;
777 bool have_tenth
= false;
779 (void)wps_data
; /* Kill the warning */
781 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
783 if (*wps_bufptr
!= '.')
786 val
+= *wps_bufptr
- '0';
802 if (have_tenth
== false)
805 if (val
== 0 && skip
== 0)
807 /* decide what to do if no value was specified */
810 case WPS_TOKEN_SUBLINE_TIMEOUT
:
812 case WPS_TOKEN_BUTTON_VOLUME
:
817 token
->value
.i
= val
;
822 static int parse_progressbar(const char *wps_bufptr
,
823 struct wps_token
*token
,
824 struct wps_data
*wps_data
)
826 (void)token
; /* Kill warnings */
827 /* %pb or %pb|filename|x|y|width|height|
828 using - for any of the params uses "sane" values */
829 #ifdef HAVE_LCD_BITMAP
837 const char *filename
;
838 int x
, y
, height
, width
;
840 const char *ptr
= wps_bufptr
;
841 struct progressbar
*pb
;
842 struct viewport
*vp
= &wps_data
->viewports
[wps_data
->num_viewports
].vp
;
844 int font_height
= font_get(vp
->font
)->height
;
848 int line_y_pos
= font_height
*(wps_data
->num_lines
-
849 wps_data
->viewports
[wps_data
->num_viewports
].first_line
);
851 if (wps_data
->progressbar_count
>= MAX_PROGRESSBARS
)
852 return WPS_ERROR_INVALID_PARAM
;
854 pb
= &wps_data
->progressbar
[wps_data
->progressbar_count
];
855 pb
->have_bitmap_pb
= false;
857 if (*wps_bufptr
!= '|') /* regular old style */
860 pb
->width
= vp
->width
;
861 pb
->height
= SYSFONT_HEIGHT
-2;
862 pb
->y
= line_y_pos
+ (font_height
-pb
->height
)/2;
864 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
865 wps_data
->progressbar_count
++;
868 ptr
= wps_bufptr
+ 1;
870 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
871 &x
, &y
, &width
, &height
)))
872 return WPS_ERROR_INVALID_PARAM
;
874 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
875 bmp_names
[PROGRESSBAR_BMP
+wps_data
->progressbar_count
] = filename
;
877 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
882 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
884 /* A zero width causes a divide-by-zero error later, so reject it */
886 return WPS_ERROR_INVALID_PARAM
;
891 pb
->width
= vp
->width
- pb
->x
;
893 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
895 /* A zero height makes no sense - reject it */
897 return WPS_ERROR_INVALID_PARAM
;
902 pb
->height
= font_height
;
904 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
907 pb
->y
= line_y_pos
+ (font_height
-pb
->height
)/2;
909 wps_data
->viewports
[wps_data
->num_viewports
].pb
= pb
;
910 wps_data
->progressbar_count
++;
912 /* Skip the rest of the line */
913 return skip_end_of_line(wps_bufptr
)-1;
916 if (*(wps_bufptr
-1) == 'f')
917 wps_data
->full_line_progressbar
= true;
919 wps_data
->full_line_progressbar
= false;
927 static int parse_albumart_load(const char *wps_bufptr
,
928 struct wps_token
*token
,
929 struct wps_data
*wps_data
)
931 const char *_pos
, *newline
;
933 const short xalign_mask
= WPS_ALBUMART_ALIGN_LEFT
|
934 WPS_ALBUMART_ALIGN_CENTER
|
935 WPS_ALBUMART_ALIGN_RIGHT
;
936 const short yalign_mask
= WPS_ALBUMART_ALIGN_TOP
|
937 WPS_ALBUMART_ALIGN_CENTER
|
938 WPS_ALBUMART_ALIGN_BOTTOM
;
940 (void)token
; /* silence warning */
942 /* reset albumart info in wps */
943 wps_data
->wps_uses_albumart
= WPS_ALBUMART_NONE
;
944 wps_data
->albumart_max_width
= -1;
945 wps_data
->albumart_max_height
= -1;
946 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
947 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
949 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
951 newline
= strchr(wps_bufptr
, '\n');
953 /* initial validation and parsing of x and y components */
954 if (*wps_bufptr
!= '|')
955 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
957 _pos
= wps_bufptr
+ 1;
959 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|@ */
960 wps_data
->albumart_x
= atoi(_pos
);
962 _pos
= strchr(_pos
, '|');
963 if (!_pos
|| _pos
> newline
|| !isdigit(*(++_pos
)))
964 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
966 wps_data
->albumart_y
= atoi(_pos
);
968 _pos
= strchr(_pos
, '|');
969 if (!_pos
|| _pos
> newline
)
970 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
973 /* parsing width field */
977 /* apply each modifier in turn */
984 wps_data
->albumart_xalign
=
985 (wps_data
->albumart_xalign
& xalign_mask
) |
986 WPS_ALBUMART_ALIGN_LEFT
;
990 wps_data
->albumart_xalign
=
991 (wps_data
->albumart_xalign
& xalign_mask
) |
992 WPS_ALBUMART_ALIGN_CENTER
;
997 wps_data
->albumart_xalign
=
998 (wps_data
->albumart_xalign
& xalign_mask
) |
999 WPS_ALBUMART_ALIGN_RIGHT
;
1003 wps_data
->albumart_xalign
|= WPS_ALBUMART_DECREASE
;
1007 wps_data
->albumart_xalign
|= WPS_ALBUMART_INCREASE
;
1011 wps_data
->albumart_xalign
|=
1012 (WPS_ALBUMART_DECREASE
| WPS_ALBUMART_INCREASE
);
1019 /* extract max width data */
1022 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
1023 return WPS_ERROR_INVALID_PARAM
;
1025 wps_data
->albumart_max_width
= atoi(_pos
);
1027 _pos
= strchr(_pos
, '|');
1028 if (!_pos
|| _pos
> newline
)
1029 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
1030 e.g. %Cl|7|59|200\n */
1033 /* parsing height field */
1037 /* apply each modifier in turn */
1044 wps_data
->albumart_yalign
=
1045 (wps_data
->albumart_yalign
& yalign_mask
) |
1046 WPS_ALBUMART_ALIGN_TOP
;
1050 wps_data
->albumart_yalign
=
1051 (wps_data
->albumart_yalign
& yalign_mask
) |
1052 WPS_ALBUMART_ALIGN_CENTER
;
1057 wps_data
->albumart_yalign
=
1058 (wps_data
->albumart_yalign
& yalign_mask
) |
1059 WPS_ALBUMART_ALIGN_BOTTOM
;
1063 wps_data
->albumart_yalign
|= WPS_ALBUMART_DECREASE
;
1067 wps_data
->albumart_yalign
|= WPS_ALBUMART_INCREASE
;
1071 wps_data
->albumart_yalign
|=
1072 (WPS_ALBUMART_DECREASE
| WPS_ALBUMART_INCREASE
);
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
++;
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 #ifdef HAVE_ALBUMART
1489 data
->wps_uses_albumart
= WPS_ALBUMART_NONE
;
1491 wps_data_init(data
);
1492 #ifdef HAVE_REMOTE_LCD
1493 data
->remote_wps
= rwps
;
1497 #ifdef HAVE_LCD_BITMAP
1499 static bool load_wps_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1501 char img_path
[MAX_PATH
];
1502 struct bitmap
*bitmap
;
1505 for (n
= 0; n
< BACKDROP_BMP
; n
++)
1509 get_image_filename(bmp_names
[n
], bmpdir
,
1510 img_path
, sizeof(img_path
));
1512 if (n
>= PROGRESSBAR_BMP
) {
1513 /* progressbar bitmap */
1514 bitmap
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].bm
;
1515 loaded
= &wps_data
->progressbar
[n
-PROGRESSBAR_BMP
].have_bitmap_pb
;
1517 /* regular bitmap */
1518 bitmap
= &wps_data
->img
[n
].bm
;
1519 loaded
= &wps_data
->img
[n
].loaded
;
1522 /* load the image */
1523 bitmap
->data
= wps_data
->img_buf_ptr
;
1524 if (load_bitmap(wps_data
, img_path
, bitmap
))
1528 /* Calculate and store height if this image has sub-images */
1530 wps_data
->img
[n
].subimage_height
= wps_data
->img
[n
].bm
.height
/
1531 wps_data
->img
[n
].num_subimages
;
1535 /* Abort if we can't load an image */
1536 DEBUGF("ERR: Failed to load image %d - %s\n",n
,img_path
);
1542 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1543 if (bmp_names
[BACKDROP_BMP
])
1545 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1546 img_path
, sizeof(img_path
));
1548 #if defined(HAVE_REMOTE_LCD)
1549 /* We only need to check LCD type if there is a remote LCD */
1550 if (!wps_data
->remote_wps
)
1553 /* Load backdrop for the main LCD */
1554 if (!load_wps_backdrop(img_path
))
1557 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1560 /* Load backdrop for the remote LCD */
1561 if (!load_remote_wps_backdrop(img_path
))
1566 #endif /* has backdrop support */
1568 /* If we got here, everything was OK */
1572 #endif /* HAVE_LCD_BITMAP */
1574 /* to setup up the wps-data from a format-buffer (isfile = false)
1575 from a (wps-)file (isfile = true)*/
1576 bool wps_data_load(struct wps_data
*wps_data
,
1577 struct screen
*display
,
1581 if (!wps_data
|| !buf
)
1584 wps_reset(wps_data
);
1586 /* Initialise the first (default) viewport */
1587 wps_data
->viewports
[0].vp
.x
= 0;
1588 wps_data
->viewports
[0].vp
.width
= display
->getwidth();
1589 if (!global_settings
.statusbar
)
1591 wps_data
->viewports
[0].vp
.y
= 0;
1592 wps_data
->viewports
[0].vp
.height
= display
->getheight();
1596 wps_data
->viewports
[0].vp
.y
= STATUSBAR_HEIGHT
;
1597 wps_data
->viewports
[0].vp
.height
= display
->getheight() -
1600 #ifdef HAVE_LCD_BITMAP
1601 wps_data
->viewports
[0].vp
.font
= FONT_UI
;
1602 wps_data
->viewports
[0].vp
.drawmode
= DRMODE_SOLID
;
1605 if (display
->depth
> 1)
1607 wps_data
->viewports
[0].vp
.fg_pattern
= display
->get_foreground();
1608 wps_data
->viewports
[0].vp
.bg_pattern
= display
->get_background();
1613 return wps_parse(wps_data
, buf
);
1618 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1619 * wants to be a virtual file. Feel free to modify dirbrowse()
1620 * if you're feeling brave.
1623 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1625 global_settings
.wps_file
[0] = 0;
1629 #ifdef HAVE_REMOTE_LCD
1630 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1632 global_settings
.rwps_file
[0] = 0;
1636 #endif /* __PCTOOL__ */
1638 int fd
= open_utf8(buf
, O_RDONLY
);
1643 /* get buffer space from the plugin buffer */
1644 size_t buffersize
= 0;
1645 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1650 /* copy the file's content to the buffer for parsing,
1651 ensuring that every line ends with a newline char. */
1652 unsigned int start
= 0;
1653 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1655 start
+= strlen(wps_buffer
+ start
);
1656 if (start
< buffersize
- 1)
1658 wps_buffer
[start
++] = '\n';
1659 wps_buffer
[start
] = 0;
1668 #ifdef HAVE_LCD_BITMAP
1669 /* Set all filename pointers to NULL */
1670 memset(bmp_names
, 0, sizeof(bmp_names
));
1673 /* parse the WPS source */
1674 if (!wps_parse(wps_data
, wps_buffer
)) {
1675 wps_reset(wps_data
);
1679 wps_data
->wps_loaded
= true;
1681 #ifdef HAVE_LCD_BITMAP
1682 /* get the bitmap dir */
1683 char bmpdir
[MAX_PATH
];
1685 char *dot
= strrchr(buf
, '.');
1686 bmpdirlen
= dot
- buf
;
1687 strncpy(bmpdir
, buf
, dot
- buf
);
1688 bmpdir
[bmpdirlen
] = 0;
1690 /* load the bitmaps that were found by the parsing */
1691 if (!load_wps_bitmaps(wps_data
, bmpdir
)) {
1692 wps_reset(wps_data
);
1700 int wps_subline_index(struct wps_data
*data
, int line
, int subline
)
1702 return data
->lines
[line
].first_subline_idx
+ subline
;
1705 int wps_first_token_index(struct wps_data
*data
, int line
, int subline
)
1707 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1708 return data
->sublines
[first_subline_idx
+ subline
].first_token_idx
;
1711 int wps_last_token_index(struct wps_data
*data
, int line
, int subline
)
1713 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1714 int idx
= first_subline_idx
+ subline
;
1715 if (idx
< data
->num_sublines
- 1)
1717 /* This subline ends where the next begins */
1718 return data
->sublines
[idx
+1].first_token_idx
- 1;
1722 /* The last subline goes to the end */
1723 return data
->num_tokens
- 1;