Update Beast installation instructions to use beastpatcher and remove instructions...
[kugel-rb.git] / apps / gui / wps_parser.c
blobba2e2173f18f09dc27ced615d4ed2129c19af72a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "file.h"
26 #include "misc.h"
27 #include "plugin.h"
29 #ifdef __PCTOOL__
30 #ifdef WPSEDITOR
31 #include "proxy.h"
32 #include "settings.h"
33 #include "sysfont.h"
34 #else
35 #include "checkwps.h"
36 #include "audio.h"
37 #define DEBUGF printf
38 #endif /*WPSEDITOR*/
39 #else
40 #include "debug.h"
41 #endif /*__PCTOOL__*/
43 #include <ctype.h>
44 #include <stdbool.h>
45 #include <string.h>
46 #include "font.h"
48 #include "gwps.h"
49 #ifndef __PCTOOL__
50 #include "settings.h"
51 #endif
52 #include "settings_list.h"
54 #ifdef HAVE_LCD_BITMAP
55 #include "bmp.h"
56 #endif
58 #include "backdrop.h"
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 */
80 static int line;
82 #ifdef HAVE_LCD_BITMAP
84 #if LCD_DEPTH > 1
85 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
86 #else
87 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
88 #endif
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);
101 #endif
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
109 has been consumed.
111 wps_bufptr points to the char following the tag (i.e. where
112 details begin).
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);
118 struct wps_tag {
119 enum wps_token_type type;
120 const char name[3];
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);
152 #endif
153 #ifdef HAVE_ALBUMART
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 */
160 #ifdef CONFIG_RTC
161 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
162 #else
163 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
164 #endif
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 },
180 #endif
181 #if CONFIG_CHARGING
182 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
183 #endif
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 },
204 /* current file */
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,
215 parse_dir_level },
217 /* next file */
218 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
219 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
220 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
221 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
222 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
223 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
224 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
225 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
226 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
227 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
228 parse_dir_level },
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 },
244 /* next metadata */
245 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
246 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
247 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
248 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
249 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
250 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
251 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
252 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
253 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
254 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
255 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
256 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
258 #if (CONFIG_CODEC != MAS3507D)
259 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
260 #endif
262 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
263 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
264 #endif
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 },
270 #else
271 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
272 #endif
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,
277 parse_timeout },
279 #ifdef HAVE_LCD_BITMAP
280 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
281 #else
282 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
283 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
284 #endif
285 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
286 parse_progressbar },
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 },
300 #ifdef HAVE_TAGCACHE
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 },
304 #endif
306 #if CONFIG_CODEC == SWCODEC
307 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
308 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
309 #endif
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 },
324 #ifdef HAVE_ALBUMART
325 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
326 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
327 parse_albumart_conditional },
328 #endif
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 },
336 #endif
337 #endif
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)
349 line++;
350 int skip = 0;
351 while(*(wps_bufptr + skip) != '\n')
352 skip++;
353 return ++skip;
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,
397 char* filename,
398 struct bitmap *bm)
400 int format;
401 #ifdef HAVE_REMOTE_LCD
402 if (wps_data->remote_wps)
403 format = FORMAT_ANY|FORMAT_REMOTE;
404 else
405 #endif
406 format = FORMAT_ANY|FORMAT_TRANSPARENT;
408 int ret = read_bmp_file(filename, bm,
409 wps_data->img_buf_free,
410 format,NULL);
412 if (ret > 0)
414 #if LCD_DEPTH == 16
415 if (ret % 2) ret++;
416 /* Always consume an even number of bytes */
417 #endif
418 wps_data->img_buf_ptr += ret;
419 wps_data->img_buf_free -= ret;
421 return true;
423 else
424 return false;
427 static int get_image_id(int c)
429 if(c >= 'a' && c <= 'z')
430 return c - 'a';
431 else if(c >= 'A' && c <= 'Z')
432 return c - 'A' + 26;
433 else
434 return -1;
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) )
444 buf = "\0";
445 return NULL;
448 int bmpdirlen = strlen(bmpdir);
450 strcpy(buf, bmpdir);
451 buf[bmpdirlen] = '/';
452 memcpy( &buf[bmpdirlen + 1], start, end - start);
453 buf[bmpdirlen + 1 + end - start] = 0;
455 return buf;
458 static int parse_image_display(const char *wps_bufptr,
459 struct wps_token *token,
460 struct wps_data *wps_data)
462 (void)wps_data;
463 int n = get_image_id(wps_bufptr[0]);
464 int subimage;
466 if (n == -1)
468 /* invalid picture display tag */
469 return WPS_ERROR_INVALID_PARAM;
472 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
474 /* Sanity check */
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 */
481 } else {
482 token->value.i = n;
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)
491 int n;
492 const char *ptr = wps_bufptr;
493 const char *pos;
494 const char* filename;
495 const char* id;
496 const char *newline;
497 int x,y;
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|
504 if (*ptr != '|')
505 return WPS_ERROR_INVALID_PARAM;
507 ptr++;
509 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
510 return WPS_ERROR_INVALID_PARAM;
512 /* Check there is a terminating | */
513 if (*ptr != '|')
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;
539 else
541 /* Parse the (optional) number of sub-images */
542 ptr++;
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)
560 (void)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;
569 return 1;
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;
578 struct viewport* vp;
579 int depth;
580 uint32_t set = 0;
581 enum {
582 PL_X = 0,
583 PL_Y,
584 PL_WIDTH,
585 PL_HEIGHT,
586 PL_FONT,
587 PL_FG,
588 PL_BG,
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;
597 #endif
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;
607 if (*ptr == 'l')
609 if (*(ptr+1) == '|')
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;
617 else
618 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
619 ptr += 3;
622 if (*ptr != '|')
623 return WPS_ERROR_INVALID_PARAM;
625 ptr++;
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);
635 #else
636 depth = LCD_DEPTH;
637 #endif
639 #ifdef HAVE_LCD_COLOR
640 if (depth == 16)
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;
646 else
647 #endif
648 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
649 if (depth == 2) {
650 /* Default to black on white */
651 vp->fg_pattern = 0;
652 vp->bg_pattern = 3;
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;
657 else
658 #endif
659 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
660 if (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;
666 else
667 #endif
670 /* Check for trailing | */
671 if (*ptr != '|')
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;
677 /* fix defaults */
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)))
686 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
699 if (depth == 16)
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;
706 #endif
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 */
731 (void)token;
732 const char *pos = NULL;
733 const char *newline;
735 pos = strchr(wps_bufptr + 1, '|');
736 newline = strchr(wps_bufptr, '\n');
738 if (pos > newline)
739 return WPS_ERROR_INVALID_PARAM;
740 #if LCD_DEPTH > 1
741 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
743 /* format: %X|filename.bmp| */
744 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
746 #endif
748 /* Skip the rest of the line */
749 return skip_end_of_line(wps_bufptr);
751 #endif
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)
759 (void)wps_data;
760 const char *ptr = wps_bufptr;
761 const char *end;
762 int i;
764 /* Find the setting's cfg_name */
765 if (*ptr != '|')
766 return WPS_ERROR_INVALID_PARAM;
767 ptr++;
768 end = strchr(ptr,'|');
769 if (!end)
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))
778 break;
779 if (i == nb_settings)
780 return WPS_ERROR_INVALID_PARAM;
782 /* Store the setting number */
783 token->value.i = i;
785 /* Skip the rest of the line */
786 return end-ptr+2;
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 */
797 return 1;
800 static int parse_timeout(const char *wps_bufptr,
801 struct wps_token *token,
802 struct wps_data *wps_data)
804 int skip = 0;
805 int val = 0;
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 != '.')
815 val *= 10;
816 val += *wps_bufptr - '0';
817 if (have_point)
819 have_tenth = true;
820 wps_bufptr++;
821 skip++;
822 break;
825 else
826 have_point = true;
828 wps_bufptr++;
829 skip++;
832 if (have_tenth == false)
833 val *= 10;
835 if (val == 0 && skip == 0)
837 /* decide what to do if no value was specified */
838 switch (token->type)
840 case WPS_TOKEN_SUBLINE_TIMEOUT:
841 return -1;
842 case WPS_TOKEN_BUTTON_VOLUME:
843 val = 10;
844 break;
847 token->value.i = val;
849 return skip;
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
860 enum {
861 PB_FILENAME = 0,
862 PB_X,
863 PB_Y,
864 PB_WIDTH,
865 PB_HEIGHT
867 const char *filename;
868 int x, y, height, width;
869 uint32_t set = 0;
870 const char *ptr = wps_bufptr;
871 struct progressbar *pb;
872 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
873 #ifndef __PCTOOL__
874 int font_height = font_get(vp->font)->height;
875 #else
876 int font_height = 8;
877 #endif
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 */
889 pb->x = 0;
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++;
896 return 0;
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 */
908 pb->x = x;
909 else
910 pb->x = vp->x;
912 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
914 /* A zero width causes a divide-by-zero error later, so reject it */
915 if (width == 0)
916 return WPS_ERROR_INVALID_PARAM;
918 pb->width = width;
920 else
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 */
926 if (height == 0)
927 return WPS_ERROR_INVALID_PARAM;
929 pb->height = height;
931 else
932 pb->height = font_height;
934 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
935 pb->y = y;
936 else
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;
944 #else
946 if (*(wps_bufptr-1) == 'f')
947 wps_data->full_line_progressbar = true;
948 else
949 wps_data->full_line_progressbar = false;
951 return 0;
953 #endif
956 #ifdef HAVE_ALBUMART
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;
962 bool parsing;
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;
980 if (!isdigit(*_pos))
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
993 e.g. %Cl|7|59\n */
995 /* parsing width field */
996 parsing = true;
997 while (parsing)
999 /* apply each modifier in turn */
1000 ++_pos;
1001 switch (*_pos)
1003 case 'l':
1004 case 'L':
1005 case '+':
1006 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
1007 break;
1008 case 'c':
1009 case 'C':
1010 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
1011 break;
1012 case 'r':
1013 case 'R':
1014 case '-':
1015 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
1016 break;
1017 case 'd':
1018 case 'D':
1019 case 'i':
1020 case 'I':
1021 case 's':
1022 case 'S':
1023 /* simply ignored */
1024 break;
1025 default:
1026 parsing = false;
1027 break;
1030 /* extract max width data */
1031 if (*_pos != '|')
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 */
1045 parsing = true;
1046 while (parsing)
1048 /* apply each modifier in turn */
1049 ++_pos;
1050 switch (*_pos)
1052 case 't':
1053 case 'T':
1054 case '-':
1055 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1056 break;
1057 case 'c':
1058 case 'C':
1059 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1060 break;
1061 case 'b':
1062 case 'B':
1063 case '+':
1064 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1065 break;
1066 case 'd':
1067 case 'D':
1068 case 'i':
1069 case 'I':
1070 case 's':
1071 case 'S':
1072 /* simply ignored */
1073 break;
1074 default:
1075 parsing = false;
1076 break;
1079 /* extract max height data */
1080 if (*_pos != '|')
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;
1115 --prevtoken;
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) == '<')
1123 token->next = true;
1124 return 1;
1126 else if (*wps_bufptr == '<')
1128 return 0;
1130 else
1132 token->type = WPS_NO_TOKEN;
1133 return 0;
1136 else
1138 /* This %C tag is in a conditional construct. */
1139 wps_data->albumart_cond_index = condindex[level];
1140 return 0;
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;
1152 switch(*wps_bufptr)
1155 case '%':
1156 case '<':
1157 case '|':
1158 case '>':
1159 case ';':
1160 case '#':
1161 /* escaped characters */
1162 token->type = WPS_TOKEN_CHARACTER;
1163 token->value.c = *wps_bufptr;
1164 taglen = 1;
1165 wps_data->num_tokens++;
1166 break;
1168 case '?':
1169 /* conditional tag */
1170 token->type = WPS_TOKEN_CONDITIONAL;
1171 level++;
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;
1177 taglen = 1 + ret;
1178 break;
1180 default:
1181 /* find what tag we have */
1182 for (tag = all_tags;
1183 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1184 tag++) ;
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 |=
1189 tag->refresh_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;
1196 skip += ret;
1199 /* Some tags we don't want to save as tokens */
1200 if (tag->type == WPS_NO_TOKEN)
1201 break;
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')
1206 token->next = true;
1208 wps_data->num_tokens++;
1209 break;
1212 skip += taglen;
1213 return skip;
1216 /* Parses the WPS.
1217 data is the pointer to the structure where the parsed WPS should be stored.
1218 It is initialised.
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)
1223 return false;
1225 char *stringbuf = data->string_buffer;
1226 int stringbuf_used = 0;
1227 enum wps_parse_error fail = PARSE_OK;
1228 int ret;
1229 line = 1;
1230 level = -1;
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++)
1239 /* Regular tag */
1240 case '%':
1241 if ((ret = parse_token(wps_bufptr, data)) < 0)
1243 fail = PARSE_FAIL_COND_INVALID_PARAM;
1244 break;
1246 else if (level >= WPS_MAX_COND_LEVEL - 1)
1248 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1249 break;
1251 wps_bufptr += ret;
1252 break;
1254 /* Alternating sublines separator */
1255 case ';':
1256 if (level >= 0) /* there are unclosed conditionals */
1258 fail = PARSE_FAIL_UNCLOSED_COND;
1259 break;
1262 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1263 wps_start_new_subline(data);
1264 else
1265 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1267 break;
1269 /* Conditional list start */
1270 case '<':
1271 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1273 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1274 break;
1277 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1278 lastcond[level] = data->num_tokens++;
1279 break;
1281 /* Conditional list end */
1282 case '>':
1283 if (level < 0) /* not in a conditional, invalid char */
1285 fail = PARSE_FAIL_INVALID_CHAR;
1286 break;
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;
1292 else
1294 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1295 break;
1298 lastcond[level] = 0;
1299 data->num_tokens++;
1300 data->tokens[condindex[level]].value.i = numoptions[level];
1301 level--;
1302 break;
1304 /* Conditional list option */
1305 case '|':
1306 if (level < 0) /* not in a conditional, invalid char */
1308 fail = PARSE_FAIL_INVALID_CHAR;
1309 break;
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;
1315 else
1317 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1318 break;
1321 lastcond[level] = data->num_tokens;
1322 numoptions[level]++;
1323 data->num_tokens++;
1324 break;
1326 /* Comment */
1327 case '#':
1328 if (level >= 0) /* there are unclosed conditionals */
1330 fail = PARSE_FAIL_UNCLOSED_COND;
1331 break;
1334 wps_bufptr += skip_end_of_line(wps_bufptr);
1335 break;
1337 /* End of this line */
1338 case '\n':
1339 if (level >= 0) /* there are unclosed conditionals */
1341 fail = PARSE_FAIL_UNCLOSED_COND;
1342 break;
1345 line++;
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 =
1353 data->num_sublines;
1355 data->sublines[data->num_sublines].first_token_idx =
1356 data->num_tokens;
1359 break;
1361 /* String */
1362 default:
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')
1373 wps_bufptr++;
1374 len++;
1377 /* look if we already have that string */
1378 char **str;
1379 int i;
1380 bool found;
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));
1385 i++, str++);
1386 /* If a matching string is found, found is true and i is
1387 the index of the string. If not, found is false */
1389 if (!found)
1391 /* new string */
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;
1398 break;
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 =
1408 data->num_strings;
1409 data->num_strings++;
1411 else
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;
1417 data->num_tokens++;
1419 break;
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);
1437 #endif
1439 return (fail == 0);
1442 #ifdef HAVE_LCD_BITMAP
1443 /* Clear the WPS image cache */
1444 static void wps_images_clear(struct wps_data *data)
1446 int i;
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;
1456 #endif
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;
1468 /* progress bars */
1469 wps_data->progressbar_count = 0;
1470 #else /* HAVE_LCD_CHARCELLS */
1471 int i;
1472 for (i = 0; i < 8; i++)
1474 wps_data->wps_progress_pat[i] = 0;
1476 wps_data->full_line_progressbar = false;
1477 #endif
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 */
1486 #endif
1487 memset(data, 0, sizeof(*data));
1488 wps_data_init(data);
1489 #ifdef HAVE_REMOTE_LCD
1490 data->remote_wps = rwps;
1491 #endif
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;
1500 bool *loaded;
1501 int n;
1502 for (n = 0; n < BACKDROP_BMP; n++)
1504 if (bmp_names[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;
1513 } else {
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))
1523 *loaded = true;
1525 /* Calculate and store height if this image has sub-images */
1526 if (n < MAX_IMAGES)
1527 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1528 wps_data->img[n].num_subimages;
1530 else
1532 /* Abort if we can't load an image */
1533 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1534 return false;
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)
1548 #endif
1550 /* Load backdrop for the main LCD */
1551 if (!load_wps_backdrop(img_path))
1552 return false;
1554 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1555 else
1557 /* Load backdrop for the remote LCD */
1558 if (!load_remote_wps_backdrop(img_path))
1559 return false;
1561 #endif
1563 #endif /* has backdrop support */
1565 /* If we got here, everything was OK */
1566 return true;
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,
1575 const char *buf,
1576 bool isfile)
1578 #ifdef HAVE_ALBUMART
1579 struct mp3entry *curtrack;
1580 long offset;
1581 int status;
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;
1585 #endif
1586 if (!wps_data || !buf)
1587 return false;
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();
1599 else
1601 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1602 wps_data->viewports[0].vp.height = display->getheight() -
1603 STATUSBAR_HEIGHT;
1605 #ifdef HAVE_LCD_BITMAP
1606 wps_data->viewports[0].vp.font = FONT_UI;
1607 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1608 #endif
1609 #if LCD_DEPTH > 1
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();
1615 #endif
1616 if (!isfile)
1618 return wps_parse(wps_data, buf);
1620 else
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.
1627 #ifndef __PCTOOL__
1628 if (! strcmp(buf, WPS_DEFAULTCFG) )
1630 global_settings.wps_file[0] = 0;
1631 return false;
1634 #ifdef HAVE_REMOTE_LCD
1635 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1637 global_settings.rwps_file[0] = 0;
1638 return false;
1640 #endif
1641 #endif /* __PCTOOL__ */
1643 int fd = open_utf8(buf, O_RDONLY);
1645 if (fd < 0)
1646 return false;
1648 /* get buffer space from the plugin buffer */
1649 size_t buffersize = 0;
1650 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1652 if (!wps_buffer)
1653 return false;
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;
1668 close(fd);
1670 if (start <= 0)
1671 return false;
1673 #ifdef HAVE_LCD_BITMAP
1674 /* Set all filename pointers to NULL */
1675 memset(bmp_names, 0, sizeof(bmp_names));
1676 #endif
1678 /* parse the WPS source */
1679 if (!wps_parse(wps_data, wps_buffer)) {
1680 wps_reset(wps_data);
1681 return false;
1684 wps_data->wps_loaded = true;
1686 #ifdef HAVE_LCD_BITMAP
1687 /* get the bitmap dir */
1688 char bmpdir[MAX_PATH];
1689 size_t bmpdirlen;
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);
1698 return false;
1700 #endif
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;
1711 audio_stop();
1712 if (!(status & AUDIO_STATUS_PAUSE))
1713 audio_play(offset);
1715 #endif
1716 return true;
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;
1740 else
1742 /* The last subline goes to the end */
1743 return data->num_tokens - 1;