Remove a viewport ambiguity by changing the screens width/heigth members into lcdwidt...
[kugel-rb.git] / apps / gui / wps_parser.c
blob73516f604da21d85f1d9d6b6c1736b3d987ab2d5
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 "gwps.h"
26 #include "file.h"
27 #include "misc.h"
28 #include "plugin.h"
30 #ifdef __PCTOOL__
31 #define DEBUGF printf
32 #define FONT_SYSFIXED 0
33 #define FONT_UI 1
34 #else
35 #include "debug.h"
36 #endif
38 #ifndef __PCTOOL__
39 #include <ctype.h>
40 #include <stdbool.h>
41 #include <string.h>
42 #include "font.h"
44 #include "gwps.h"
45 #include "settings.h"
47 #ifdef HAVE_LCD_BITMAP
48 #include "bmp.h"
49 #endif
51 #include "backdrop.h"
53 #endif
55 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
56 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
58 #define WPS_ERROR_INVALID_PARAM -1
60 #define PARSE_FAIL_UNCLOSED_COND 1
61 #define PARSE_FAIL_INVALID_CHAR 2
62 #define PARSE_FAIL_COND_SYNTAX_ERROR 3
63 #define PARSE_FAIL_COND_INVALID_PARAM 4
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 #ifdef DEBUG
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_subline_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);
133 #ifdef HAVE_LCD_BITMAP
134 static int parse_viewport_display(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_viewport(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_statusbar_enable(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_statusbar_disable(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_image_display(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_image_load(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 #endif /*HAVE_LCD_BITMAP */
147 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
148 static int parse_image_special(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 #endif
151 #ifdef HAVE_ALBUMART
152 static int parse_albumart_load(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 static int parse_albumart_conditional(const char *wps_bufptr,
155 struct wps_token *token, struct wps_data *wps_data);
156 #endif /* HAVE_ALBUMART */
158 #ifdef CONFIG_RTC
159 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
160 #else
161 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
162 #endif
164 /* array of available tags - those with more characters have to go first
165 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
166 static const struct wps_tag all_tags[] = {
168 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
169 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
170 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
172 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
173 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
174 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
175 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
176 #if CONFIG_CHARGING >= CHARGING_MONITOR
177 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
178 #endif
179 #if CONFIG_CHARGING
180 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
181 #endif
183 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
184 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
185 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
186 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
187 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
188 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
189 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
190 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
191 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
192 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
193 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
194 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
197 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
202 /* current file */
203 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
204 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
205 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
206 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
207 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
208 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
209 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
210 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
211 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
212 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
213 parse_dir_level },
215 /* next file */
216 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
217 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
218 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
219 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
220 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
221 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
222 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
223 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
224 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
225 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
226 parse_dir_level },
228 /* current metadata */
229 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
242 /* next metadata */
243 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
244 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
245 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
246 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
247 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
248 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
249 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
250 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
251 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
252 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
253 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
254 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
256 #if (CONFIG_CODEC != MAS3507D)
257 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
258 #endif
260 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
261 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
262 #endif
264 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
266 #ifdef HAS_REMOTE_BUTTON_HOLD
267 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
268 #else
269 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
270 #endif
272 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
273 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
275 #ifdef HAVE_LCD_BITMAP
276 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
277 #else
278 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
279 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
280 #endif
281 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
282 parse_progressbar },
284 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
286 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
287 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
288 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
289 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
292 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
293 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
294 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
296 #ifdef HAVE_TAGCACHE
297 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
298 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
299 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
300 #endif
302 #if CONFIG_CODEC == SWCODEC
303 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
304 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
305 #endif
307 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
308 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_subline_timeout },
310 #ifdef HAVE_LCD_BITMAP
311 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
312 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
314 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
316 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
317 parse_image_display },
319 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
320 #ifdef HAVE_ALBUMART
321 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
322 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
323 parse_albumart_conditional },
324 #endif
326 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
327 parse_viewport_display },
328 { WPS_NO_TOKEN, "V", 0, parse_viewport },
330 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
331 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
332 #endif
333 #endif
335 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
336 /* the array MUST end with an empty string (first char is \0) */
339 /* Returns the number of chars that should be skipped to jump
340 immediately after the first eol, i.e. to the start of the next line */
341 static int skip_end_of_line(const char *wps_bufptr)
343 line++;
344 int skip = 0;
345 while(*(wps_bufptr + skip) != '\n')
346 skip++;
347 return ++skip;
350 /* Starts a new subline in the current line during parsing */
351 static void wps_start_new_subline(struct wps_data *data)
353 data->num_sublines++;
354 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
355 data->lines[data->num_lines].num_sublines++;
358 #ifdef HAVE_LCD_BITMAP
360 static int parse_statusbar_enable(const char *wps_bufptr,
361 struct wps_token *token,
362 struct wps_data *wps_data)
364 (void)token; /* Kill warnings */
365 wps_data->wps_sb_tag = true;
366 wps_data->show_sb_on_wps = true;
367 if (wps_data->viewports[0].vp.y == 0)
369 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
370 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
372 return skip_end_of_line(wps_bufptr);
375 static int parse_statusbar_disable(const char *wps_bufptr,
376 struct wps_token *token,
377 struct wps_data *wps_data)
379 (void)token; /* Kill warnings */
380 wps_data->wps_sb_tag = true;
381 wps_data->show_sb_on_wps = false;
382 if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
384 wps_data->viewports[0].vp.y = 0;
385 wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
387 return skip_end_of_line(wps_bufptr);
390 static bool load_bitmap(struct wps_data *wps_data,
391 char* filename,
392 struct bitmap *bm)
394 int format;
395 #ifdef HAVE_REMOTE_LCD
396 if (wps_data->remote_wps)
397 format = FORMAT_ANY|FORMAT_REMOTE;
398 else
399 #endif
400 format = FORMAT_ANY|FORMAT_TRANSPARENT;
402 int ret = read_bmp_file(filename, bm,
403 wps_data->img_buf_free,
404 format);
406 if (ret > 0)
408 #if LCD_DEPTH == 16
409 if (ret % 2) ret++;
410 /* Always consume an even number of bytes */
411 #endif
412 wps_data->img_buf_ptr += ret;
413 wps_data->img_buf_free -= ret;
415 return true;
417 else
418 return false;
421 static int get_image_id(int c)
423 if(c >= 'a' && c <= 'z')
424 return c - 'a';
425 else if(c >= 'A' && c <= 'Z')
426 return c - 'A' + 26;
427 else
428 return -1;
431 static char *get_image_filename(const char *start, const char* bmpdir,
432 char *buf, int buf_size)
434 const char *end = strchr(start, '|');
436 if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
438 buf = "\0";
439 return NULL;
442 int bmpdirlen = strlen(bmpdir);
444 strcpy(buf, bmpdir);
445 buf[bmpdirlen] = '/';
446 memcpy( &buf[bmpdirlen + 1], start, end - start);
447 buf[bmpdirlen + 1 + end - start] = 0;
449 return buf;
452 static int parse_image_display(const char *wps_bufptr,
453 struct wps_token *token,
454 struct wps_data *wps_data)
456 (void)wps_data;
457 int n = get_image_id(wps_bufptr[0]);
458 int subimage;
460 if (n == -1)
462 /* invalid picture display tag */
463 return WPS_ERROR_INVALID_PARAM;
466 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
468 /* Sanity check */
469 if (subimage >= wps_data->img[n].num_subimages)
470 return WPS_ERROR_INVALID_PARAM;
472 /* Store sub-image number to display in high bits */
473 token->value.i = n | (subimage << 8);
474 return 2; /* We have consumed 2 bytes */
475 } else {
476 token->value.i = n;
477 return 1; /* We have consumed 1 byte */
481 static int parse_image_load(const char *wps_bufptr,
482 struct wps_token *token,
483 struct wps_data *wps_data)
485 int n;
486 const char *ptr = wps_bufptr;
487 const char *pos;
488 const char* filename;
489 const char* id;
490 const char *newline;
491 int x,y;
493 /* format: %x|n|filename.bmp|x|y|
494 or %xl|n|filename.bmp|x|y|
495 or %xl|n|filename.bmp|x|y|num_subimages|
498 if (*ptr != '|')
499 return WPS_ERROR_INVALID_PARAM;
501 ptr++;
503 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
504 return WPS_ERROR_INVALID_PARAM;
506 /* Check there is a terminating | */
507 if (*ptr != '|')
508 return WPS_ERROR_INVALID_PARAM;
510 /* get the image ID */
511 n = get_image_id(*id);
513 /* check the image number and load state */
514 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
516 /* Invalid image ID */
517 return WPS_ERROR_INVALID_PARAM;
520 /* save a pointer to the filename */
521 bmp_names[n] = filename;
523 wps_data->img[n].x = x;
524 wps_data->img[n].y = y;
526 /* save current viewport */
527 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
529 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
531 wps_data->img[n].always_display = true;
533 else
535 /* Parse the (optional) number of sub-images */
536 ptr++;
537 newline = strchr(ptr, '\n');
538 pos = strchr(ptr, '|');
539 if (pos && pos < newline)
540 wps_data->img[n].num_subimages = atoi(ptr);
542 if (wps_data->img[n].num_subimages <= 0)
543 return WPS_ERROR_INVALID_PARAM;
546 /* Skip the rest of the line */
547 return skip_end_of_line(wps_bufptr);
550 static int parse_viewport_display(const char *wps_bufptr,
551 struct wps_token *token,
552 struct wps_data *wps_data)
554 (void)wps_data;
555 char letter = wps_bufptr[0];
557 if (letter < 'a' || letter > 'z')
559 /* invalid viewport tag */
560 return WPS_ERROR_INVALID_PARAM;
562 token->value.i = letter;
563 return 1;
566 static int parse_viewport(const char *wps_bufptr,
567 struct wps_token *token,
568 struct wps_data *wps_data)
570 (void)token; /* Kill warnings */
571 const char *ptr = wps_bufptr;
572 struct viewport* vp;
573 int depth;
574 uint32_t set = 0;
575 enum {
576 PL_X = 0,
577 PL_Y,
578 PL_WIDTH,
579 PL_HEIGHT,
580 PL_FONT,
581 PL_FG,
582 PL_BG,
584 int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
585 #ifdef HAVE_REMOTE_LCD
586 if (wps_data->remote_wps)
588 lcd_width = LCD_REMOTE_WIDTH;
589 lcd_height = LCD_REMOTE_HEIGHT;
591 #endif
593 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
594 return WPS_ERROR_INVALID_PARAM;
596 wps_data->num_viewports++;
597 /* check for the optional letter to signify its a hideable viewport */
598 /* %Vl|<label>|<rest of tags>| */
599 wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
601 if (*ptr == 'l')
603 if (*(ptr+1) == '|')
605 char label = *(ptr+2);
606 if (label >= 'a' && label <= 'z')
608 wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
609 wps_data->viewports[wps_data->num_viewports].label = label;
611 else
612 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
613 ptr += 3;
616 if (*ptr != '|')
617 return WPS_ERROR_INVALID_PARAM;
619 ptr++;
620 vp = &wps_data->viewports[wps_data->num_viewports].vp;
621 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
623 /* Set the defaults for fields not user-specified */
624 vp->drawmode = DRMODE_SOLID;
626 /* Work out the depth of this display */
627 #ifdef HAVE_REMOTE_LCD
628 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
629 #else
630 depth = LCD_DEPTH;
631 #endif
633 #ifdef HAVE_LCD_COLOR
634 if (depth == 16)
636 if (!(ptr = parse_list("dddddcc", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
637 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
638 return WPS_ERROR_INVALID_PARAM;
640 else
641 #endif
642 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
643 if (depth == 2) {
644 /* Default to black on white */
645 vp->fg_pattern = 0;
646 vp->bg_pattern = 3;
647 if (!(ptr = parse_list("dddddgg", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
648 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
649 return WPS_ERROR_INVALID_PARAM;
651 else
652 #endif
653 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
654 if (depth == 1)
656 if (!(ptr = parse_list("ddddd", &set, '|', ptr, &vp->x, &vp->y,
657 &vp->width, &vp->height, &vp->font)))
658 return WPS_ERROR_INVALID_PARAM;
660 else
661 #endif
664 /* Check for trailing | */
665 if (*ptr != '|')
666 return WPS_ERROR_INVALID_PARAM;
668 if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
669 return WPS_ERROR_INVALID_PARAM;
671 /* fix defaults */
672 if (!LIST_VALUE_PARSED(set, PL_WIDTH))
673 vp->width = lcd_width - vp->x;
674 if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
675 vp->height = lcd_height - vp->y;
677 /* Default to using the user font if the font was an invalid number */
678 if (!LIST_VALUE_PARSED(set, PL_FONT) ||
679 ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
680 vp->font = FONT_UI;
682 /* Validate the viewport dimensions - we know that the numbers are
683 non-negative integers */
684 if ((vp->x >= lcd_width) ||
685 ((vp->x + vp->width) > lcd_width) ||
686 (vp->y >= lcd_height) ||
687 ((vp->y + vp->height) > lcd_height))
689 return WPS_ERROR_INVALID_PARAM;
692 #ifdef HAVE_LCD_COLOR
693 if (depth == 16)
695 if (!LIST_VALUE_PARSED(set, PL_FG))
696 vp->fg_pattern = global_settings.fg_color;
697 if (!LIST_VALUE_PARSED(set, PL_BG))
698 vp->bg_pattern = global_settings.bg_color;
700 #endif
702 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
704 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
706 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
708 wps_data->lines[wps_data->num_lines].first_subline_idx =
709 wps_data->num_sublines;
711 wps_data->sublines[wps_data->num_sublines].first_token_idx =
712 wps_data->num_tokens;
715 /* Skip the rest of the line */
716 return skip_end_of_line(wps_bufptr);
720 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
721 static int parse_image_special(const char *wps_bufptr,
722 struct wps_token *token,
723 struct wps_data *wps_data)
725 (void)wps_data; /* kill warning */
726 (void)token;
727 const char *pos = NULL;
728 const char *newline;
730 pos = strchr(wps_bufptr + 1, '|');
731 newline = strchr(wps_bufptr, '\n');
733 if (pos > newline)
734 return WPS_ERROR_INVALID_PARAM;
735 #if LCD_DEPTH > 1
736 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
738 /* format: %X|filename.bmp| */
739 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
741 #endif
743 /* Skip the rest of the line */
744 return skip_end_of_line(wps_bufptr);
746 #endif
748 #endif /* HAVE_LCD_BITMAP */
750 static int parse_dir_level(const char *wps_bufptr,
751 struct wps_token *token,
752 struct wps_data *wps_data)
754 char val[] = { *wps_bufptr, '\0' };
755 token->value.i = atoi(val);
756 (void)wps_data; /* Kill warnings */
757 return 1;
760 static int parse_subline_timeout(const char *wps_bufptr,
761 struct wps_token *token,
762 struct wps_data *wps_data)
764 int skip = 0;
765 int val = 0;
766 bool have_point = false;
767 bool have_tenth = false;
769 (void)wps_data; /* Kill the warning */
771 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
773 if (*wps_bufptr != '.')
775 val *= 10;
776 val += *wps_bufptr - '0';
777 if (have_point)
779 have_tenth = true;
780 wps_bufptr++;
781 skip++;
782 break;
785 else
786 have_point = true;
788 wps_bufptr++;
789 skip++;
792 if (have_tenth == false)
793 val *= 10;
795 token->value.i = val;
797 return skip;
800 static int parse_progressbar(const char *wps_bufptr,
801 struct wps_token *token,
802 struct wps_data *wps_data)
804 (void)token; /* Kill warnings */
805 /* %pb or %pb|filename|x|y|width|height|
806 using - for any of the params uses "sane" values */
807 #ifdef HAVE_LCD_BITMAP
808 enum {
809 PB_FILENAME = 0,
810 PB_X,
811 PB_Y,
812 PB_WIDTH,
813 PB_HEIGHT
815 const char *filename;
816 int x, y, height, width;
817 uint32_t set = 0;
818 const char *ptr = wps_bufptr;
819 struct progressbar *pb;
820 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
821 int font_height = font_get(vp->font)->height;
822 int line_y_pos = font_height*(wps_data->num_lines -
823 wps_data->viewports[wps_data->num_viewports].first_line);
825 if (wps_data->progressbar_count +1 >= MAX_PROGRESSBARS)
826 return WPS_ERROR_INVALID_PARAM;
828 pb = &wps_data->progressbar[wps_data->progressbar_count];
829 pb->have_bitmap_pb = false;
831 if (*wps_bufptr != '|') /* regular old style */
833 pb->x = 0;
834 pb->width = vp->width;
835 pb->height = SYSFONT_HEIGHT-2;
836 pb->y = line_y_pos + (font_height-pb->height)/2;
838 wps_data->viewports[wps_data->num_viewports].pb =
839 &wps_data->progressbar[wps_data->progressbar_count];
840 wps_data->progressbar_count++;
841 return 0;
843 ptr = wps_bufptr + 1;
845 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
846 &x, &y, &width, &height)))
847 return WPS_ERROR_INVALID_PARAM;
848 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
849 bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
850 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
851 pb->x = x;
852 else
853 pb->x = vp->x;
854 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
855 pb->width = width;
856 else
857 pb->width = vp->width - pb->x;
858 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
859 pb->height = height;
860 else
861 pb->height = font_height;
862 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
863 pb->y = y;
864 else
865 pb->y = line_y_pos + (font_height-pb->height)/2;
866 wps_data->progressbar[wps_data->progressbar_count].have_bitmap_pb = false;
867 wps_data->viewports[wps_data->num_viewports].pb =
868 &wps_data->progressbar[wps_data->progressbar_count];
869 wps_data->progressbar_count++;
870 /* Skip the rest of the line */
871 return skip_end_of_line(wps_bufptr)-1;
872 #else
874 if (*(wps_bufptr-1) == 'f')
875 wps_data->full_line_progressbar = true;
876 else
877 wps_data->full_line_progressbar = false;
879 return 0;
881 #endif
884 #ifdef HAVE_ALBUMART
885 static int parse_albumart_load(const char *wps_bufptr,
886 struct wps_token *token,
887 struct wps_data *wps_data)
889 const char *_pos, *newline;
890 bool parsing;
891 const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
892 WPS_ALBUMART_ALIGN_CENTER |
893 WPS_ALBUMART_ALIGN_RIGHT;
894 const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
895 WPS_ALBUMART_ALIGN_CENTER |
896 WPS_ALBUMART_ALIGN_BOTTOM;
898 (void)token; /* silence warning */
900 /* reset albumart info in wps */
901 wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
902 wps_data->albumart_max_width = -1;
903 wps_data->albumart_max_height = -1;
904 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
905 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
907 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
909 newline = strchr(wps_bufptr, '\n');
911 /* initial validation and parsing of x and y components */
912 if (*wps_bufptr != '|')
913 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
915 _pos = wps_bufptr + 1;
916 if (!isdigit(*_pos))
917 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
918 wps_data->albumart_x = atoi(_pos);
920 _pos = strchr(_pos, '|');
921 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
922 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
924 wps_data->albumart_y = atoi(_pos);
926 _pos = strchr(_pos, '|');
927 if (!_pos || _pos > newline)
928 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
929 e.g. %Cl|7|59\n */
931 /* parsing width field */
932 parsing = true;
933 while (parsing)
935 /* apply each modifier in turn */
936 ++_pos;
937 switch (*_pos)
939 case 'l':
940 case 'L':
941 case '+':
942 wps_data->albumart_xalign =
943 (wps_data->albumart_xalign & xalign_mask) |
944 WPS_ALBUMART_ALIGN_LEFT;
945 break;
946 case 'c':
947 case 'C':
948 wps_data->albumart_xalign =
949 (wps_data->albumart_xalign & xalign_mask) |
950 WPS_ALBUMART_ALIGN_CENTER;
951 break;
952 case 'r':
953 case 'R':
954 case '-':
955 wps_data->albumart_xalign =
956 (wps_data->albumart_xalign & xalign_mask) |
957 WPS_ALBUMART_ALIGN_RIGHT;
958 break;
959 case 'd':
960 case 'D':
961 wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
962 break;
963 case 'i':
964 case 'I':
965 wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
966 break;
967 case 's':
968 case 'S':
969 wps_data->albumart_xalign |=
970 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
971 break;
972 default:
973 parsing = false;
974 break;
977 /* extract max width data */
978 if (*_pos != '|')
980 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
981 return WPS_ERROR_INVALID_PARAM;
983 wps_data->albumart_max_width = atoi(_pos);
985 _pos = strchr(_pos, '|');
986 if (!_pos || _pos > newline)
987 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
988 e.g. %Cl|7|59|200\n */
991 /* parsing height field */
992 parsing = true;
993 while (parsing)
995 /* apply each modifier in turn */
996 ++_pos;
997 switch (*_pos)
999 case 't':
1000 case 'T':
1001 case '-':
1002 wps_data->albumart_yalign =
1003 (wps_data->albumart_yalign & yalign_mask) |
1004 WPS_ALBUMART_ALIGN_TOP;
1005 break;
1006 case 'c':
1007 case 'C':
1008 wps_data->albumart_yalign =
1009 (wps_data->albumart_yalign & yalign_mask) |
1010 WPS_ALBUMART_ALIGN_CENTER;
1011 break;
1012 case 'b':
1013 case 'B':
1014 case '+':
1015 wps_data->albumart_yalign =
1016 (wps_data->albumart_yalign & yalign_mask) |
1017 WPS_ALBUMART_ALIGN_BOTTOM;
1018 break;
1019 case 'd':
1020 case 'D':
1021 wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
1022 break;
1023 case 'i':
1024 case 'I':
1025 wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
1026 break;
1027 case 's':
1028 case 'S':
1029 wps_data->albumart_yalign |=
1030 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
1031 break;
1032 default:
1033 parsing = false;
1034 break;
1037 /* extract max height data */
1038 if (*_pos != '|')
1040 if (!isdigit(*_pos))
1041 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1043 wps_data->albumart_max_height = atoi(_pos);
1045 _pos = strchr(_pos, '|');
1046 if (!_pos || _pos > newline)
1047 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1048 e.g. %Cl|7|59|200|200\n */
1051 /* if we got here, we parsed everything ok .. ! */
1052 if (wps_data->albumart_max_width < 0)
1053 wps_data->albumart_max_width = 0;
1054 else if (wps_data->albumart_max_width > LCD_WIDTH)
1055 wps_data->albumart_max_width = LCD_WIDTH;
1057 if (wps_data->albumart_max_height < 0)
1058 wps_data->albumart_max_height = 0;
1059 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1060 wps_data->albumart_max_height = LCD_HEIGHT;
1062 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1064 /* Skip the rest of the line */
1065 return skip_end_of_line(wps_bufptr);
1068 static int parse_albumart_conditional(const char *wps_bufptr,
1069 struct wps_token *token,
1070 struct wps_data *wps_data)
1072 struct wps_token *prevtoken = token;
1073 --prevtoken;
1074 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1076 /* This %C is part of a %?C construct.
1077 It's either %?C<blah> or %?Cn<blah> */
1078 token->type = WPS_TOKEN_ALBUMART_FOUND;
1079 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1081 token->next = true;
1082 return 1;
1084 else if (*wps_bufptr == '<')
1086 return 0;
1088 else
1090 token->type = WPS_NO_TOKEN;
1091 return 0;
1094 else
1096 /* This %C tag is in a conditional construct. */
1097 wps_data->albumart_cond_index = condindex[level];
1098 return 0;
1101 #endif /* HAVE_ALBUMART */
1104 /* Parse a generic token from the given string. Return the length read */
1105 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1107 int skip = 0, taglen = 0, ret;
1108 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1109 const struct wps_tag *tag;
1111 switch(*wps_bufptr)
1114 case '%':
1115 case '<':
1116 case '|':
1117 case '>':
1118 case ';':
1119 case '#':
1120 /* escaped characters */
1121 token->type = WPS_TOKEN_CHARACTER;
1122 token->value.c = *wps_bufptr;
1123 taglen = 1;
1124 wps_data->num_tokens++;
1125 break;
1127 case '?':
1128 /* conditional tag */
1129 token->type = WPS_TOKEN_CONDITIONAL;
1130 level++;
1131 condindex[level] = wps_data->num_tokens;
1132 numoptions[level] = 1;
1133 wps_data->num_tokens++;
1134 ret = parse_token(wps_bufptr + 1, wps_data);
1135 if (ret < 0) return ret;
1136 taglen = 1 + ret;
1137 break;
1139 default:
1140 /* find what tag we have */
1141 for (tag = all_tags;
1142 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1143 tag++) ;
1145 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1146 token->type = tag->type;
1147 wps_data->sublines[wps_data->num_sublines].line_type |=
1148 tag->refresh_type;
1150 /* if the tag has a special parsing function, we call it */
1151 if (tag->parse_func)
1153 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1154 if (ret < 0) return ret;
1155 skip += ret;
1158 /* Some tags we don't want to save as tokens */
1159 if (tag->type == WPS_NO_TOKEN)
1160 break;
1162 /* tags that start with 'F', 'I' or 'D' are for the next file */
1163 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1164 *(tag->name) == 'D')
1165 token->next = true;
1167 wps_data->num_tokens++;
1168 break;
1171 skip += taglen;
1172 return skip;
1175 /* Parses the WPS.
1176 data is the pointer to the structure where the parsed WPS should be stored.
1177 It is initialised.
1178 wps_bufptr points to the string containing the WPS tags */
1179 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1181 if (!data || !wps_bufptr || !*wps_bufptr)
1182 return false;
1184 char *stringbuf = data->string_buffer;
1185 int stringbuf_used = 0;
1186 int fail = 0;
1187 int ret;
1188 line = 1;
1189 level = -1;
1191 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1192 && data->num_viewports < WPS_MAX_VIEWPORTS
1193 && data->num_lines < WPS_MAX_LINES)
1195 switch(*wps_bufptr++)
1198 /* Regular tag */
1199 case '%':
1200 if ((ret = parse_token(wps_bufptr, data)) < 0)
1202 fail = PARSE_FAIL_COND_INVALID_PARAM;
1203 break;
1205 wps_bufptr += ret;
1206 break;
1208 /* Alternating sublines separator */
1209 case ';':
1210 if (level >= 0) /* there are unclosed conditionals */
1212 fail = PARSE_FAIL_UNCLOSED_COND;
1213 break;
1216 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1217 wps_start_new_subline(data);
1218 else
1219 wps_bufptr += skip_end_of_line(wps_bufptr);
1221 break;
1223 /* Conditional list start */
1224 case '<':
1225 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1227 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1228 break;
1231 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1232 lastcond[level] = data->num_tokens++;
1233 break;
1235 /* Conditional list end */
1236 case '>':
1237 if (level < 0) /* not in a conditional, invalid char */
1239 fail = PARSE_FAIL_INVALID_CHAR;
1240 break;
1243 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1244 if (lastcond[level])
1245 data->tokens[lastcond[level]].value.i = data->num_tokens;
1246 else
1248 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1249 break;
1252 lastcond[level] = 0;
1253 data->num_tokens++;
1254 data->tokens[condindex[level]].value.i = numoptions[level];
1255 level--;
1256 break;
1258 /* Conditional list option */
1259 case '|':
1260 if (level < 0) /* not in a conditional, invalid char */
1262 fail = PARSE_FAIL_INVALID_CHAR;
1263 break;
1266 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1267 if (lastcond[level])
1268 data->tokens[lastcond[level]].value.i = data->num_tokens;
1269 else
1271 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1272 break;
1275 lastcond[level] = data->num_tokens;
1276 numoptions[level]++;
1277 data->num_tokens++;
1278 break;
1280 /* Comment */
1281 case '#':
1282 if (level >= 0) /* there are unclosed conditionals */
1284 fail = PARSE_FAIL_UNCLOSED_COND;
1285 break;
1288 wps_bufptr += skip_end_of_line(wps_bufptr);
1289 break;
1291 /* End of this line */
1292 case '\n':
1293 if (level >= 0) /* there are unclosed conditionals */
1295 fail = PARSE_FAIL_UNCLOSED_COND;
1296 break;
1299 line++;
1300 wps_start_new_subline(data);
1301 data->num_lines++; /* Start a new line */
1303 if ((data->num_lines < WPS_MAX_LINES) &&
1304 (data->num_sublines < WPS_MAX_SUBLINES))
1306 data->lines[data->num_lines].first_subline_idx =
1307 data->num_sublines;
1309 data->sublines[data->num_sublines].first_token_idx =
1310 data->num_tokens;
1313 break;
1315 /* String */
1316 default:
1318 unsigned int len = 1;
1319 const char *string_start = wps_bufptr - 1;
1321 /* find the length of the string */
1322 while (*wps_bufptr && *wps_bufptr != '#' &&
1323 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1324 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1325 *wps_bufptr != '|' && *wps_bufptr != '\n')
1327 wps_bufptr++;
1328 len++;
1331 /* look if we already have that string */
1332 char **str;
1333 int i;
1334 bool found;
1335 for (i = 0, str = data->strings, found = false;
1336 i < data->num_strings &&
1337 !(found = (strlen(*str) == len &&
1338 strncmp(string_start, *str, len) == 0));
1339 i++, str++);
1340 /* If a matching string is found, found is true and i is
1341 the index of the string. If not, found is false */
1343 /* If it's NOT a duplicate, do nothing if we already have
1344 too many unique strings */
1345 if (found ||
1346 (stringbuf_used < STRING_BUFFER_SIZE - 1 &&
1347 data->num_strings < WPS_MAX_STRINGS))
1349 if (!found)
1351 /* new string */
1353 /* truncate? */
1354 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1)
1355 len = STRING_BUFFER_SIZE - stringbuf_used - 1;
1357 strncpy(stringbuf, string_start, len);
1358 *(stringbuf + len) = '\0';
1360 data->strings[data->num_strings] = stringbuf;
1361 stringbuf += len + 1;
1362 stringbuf_used += len + 1;
1363 data->tokens[data->num_tokens].value.i =
1364 data->num_strings;
1365 data->num_strings++;
1367 else
1369 /* another ocurrence of an existing string */
1370 data->tokens[data->num_tokens].value.i = i;
1372 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1373 data->num_tokens++;
1376 break;
1380 if (!fail && level >= 0) /* there are unclosed conditionals */
1381 fail = PARSE_FAIL_UNCLOSED_COND;
1383 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1385 /* We have finished with the last viewport, so increment count */
1386 data->num_viewports++;
1388 #ifdef DEBUG
1389 print_debug_info(data, fail, line);
1390 #endif
1392 return (fail == 0);
1395 #ifdef HAVE_LCD_BITMAP
1396 /* Clear the WPS image cache */
1397 static void wps_images_clear(struct wps_data *data)
1399 int i;
1400 /* set images to unloaded and not displayed */
1401 for (i = 0; i < MAX_IMAGES; i++)
1403 data->img[i].loaded = false;
1404 data->img[i].display = -1;
1405 data->img[i].always_display = false;
1406 data->img[i].num_subimages = 1;
1409 #endif
1411 /* initial setup of wps_data */
1412 void wps_data_init(struct wps_data *wps_data)
1414 #ifdef HAVE_LCD_BITMAP
1415 wps_images_clear(wps_data);
1416 wps_data->wps_sb_tag = false;
1417 wps_data->show_sb_on_wps = false;
1418 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1419 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1420 wps_data->peak_meter_enabled = false;
1421 /* progress bars */
1422 wps_data->progressbar_count = 0;
1423 #else /* HAVE_LCD_CHARCELLS */
1424 int i;
1425 for (i = 0; i < 8; i++)
1427 wps_data->wps_progress_pat[i] = 0;
1429 wps_data->full_line_progressbar = false;
1430 #endif
1431 wps_data->wps_loaded = false;
1434 static void wps_reset(struct wps_data *data)
1436 #ifdef HAVE_REMOTE_LCD
1437 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1438 #endif
1439 memset(data, 0, sizeof(*data));
1440 #ifdef HAVE_ALBUMART
1441 data->wps_uses_albumart = WPS_ALBUMART_NONE;
1442 #endif
1443 wps_data_init(data);
1444 #ifdef HAVE_REMOTE_LCD
1445 data->remote_wps = rwps;
1446 #endif
1449 #ifdef HAVE_LCD_BITMAP
1451 static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1453 char img_path[MAX_PATH];
1454 struct bitmap *bitmap;
1455 bool *loaded;
1456 int n;
1457 for (n = 0; n < BACKDROP_BMP; n++)
1459 if (bmp_names[n])
1461 get_image_filename(bmp_names[n], bmpdir,
1462 img_path, sizeof(img_path));
1464 if (n >= PROGRESSBAR_BMP ) {
1465 /* progressbar bitmap */
1466 bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
1467 loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
1468 } else {
1469 /* regular bitmap */
1470 bitmap = &wps_data->img[n].bm;
1471 loaded = &wps_data->img[n].loaded;
1474 /* load the image */
1475 bitmap->data = wps_data->img_buf_ptr;
1476 if (load_bitmap(wps_data, img_path, bitmap))
1478 *loaded = true;
1480 /* Calculate and store height if this image has sub-images */
1481 if (n < MAX_IMAGES)
1482 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1483 wps_data->img[n].num_subimages;
1485 else
1487 /* Abort if we can't load an image */
1488 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1489 return false;
1494 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1495 if (bmp_names[BACKDROP_BMP])
1497 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1498 img_path, sizeof(img_path));
1500 #if defined(HAVE_REMOTE_LCD)
1501 /* We only need to check LCD type if there is a remote LCD */
1502 if (!wps_data->remote_wps)
1503 #endif
1505 /* Load backdrop for the main LCD */
1506 if (!load_wps_backdrop(img_path))
1507 return false;
1509 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1510 else
1512 /* Load backdrop for the remote LCD */
1513 if (!load_remote_wps_backdrop(img_path))
1514 return false;
1516 #endif
1518 #endif /* has backdrop support */
1520 /* If we got here, everything was OK */
1521 return true;
1524 #endif /* HAVE_LCD_BITMAP */
1526 /* Skip leading UTF-8 BOM, if present. */
1527 static char *skip_utf8_bom(char *buf)
1529 unsigned char *s = (unsigned char *)buf;
1531 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
1533 buf += 3;
1536 return buf;
1539 /* to setup up the wps-data from a format-buffer (isfile = false)
1540 from a (wps-)file (isfile = true)*/
1541 bool wps_data_load(struct wps_data *wps_data,
1542 struct screen *display,
1543 const char *buf,
1544 bool isfile)
1546 if (!wps_data || !buf)
1547 return false;
1549 wps_reset(wps_data);
1551 /* Initialise the first (default) viewport */
1552 wps_data->viewports[0].vp.x = 0;
1553 wps_data->viewports[0].vp.width = display->getwidth();
1554 if (!global_settings.statusbar)
1556 wps_data->viewports[0].vp.y = 0;
1557 wps_data->viewports[0].vp.height = display->getheight();
1559 else
1561 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1562 wps_data->viewports[0].vp.height = display->getheight() -
1563 STATUSBAR_HEIGHT;
1565 #ifdef HAVE_LCD_BITMAP
1566 wps_data->viewports[0].vp.font = FONT_UI;
1567 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1568 #endif
1569 #if LCD_DEPTH > 1
1570 if (display->depth > 1)
1572 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1573 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1575 #endif
1576 if (!isfile)
1578 return wps_parse(wps_data, buf);
1580 else
1583 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1584 * wants to be a virtual file. Feel free to modify dirbrowse()
1585 * if you're feeling brave.
1587 #ifndef __PCTOOL__
1588 if (! strcmp(buf, WPS_DEFAULTCFG) )
1590 global_settings.wps_file[0] = 0;
1591 return false;
1594 #ifdef HAVE_REMOTE_LCD
1595 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1597 global_settings.rwps_file[0] = 0;
1598 return false;
1600 #endif
1601 #endif /* __PCTOOL__ */
1603 int fd = open(buf, O_RDONLY);
1605 if (fd < 0)
1606 return false;
1608 /* get buffer space from the plugin buffer */
1609 size_t buffersize = 0;
1610 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1612 if (!wps_buffer)
1613 return false;
1615 /* copy the file's content to the buffer for parsing,
1616 ensuring that every line ends with a newline char. */
1617 unsigned int start = 0;
1618 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1620 start += strlen(wps_buffer + start);
1621 if (start < buffersize - 1)
1623 wps_buffer[start++] = '\n';
1624 wps_buffer[start] = 0;
1628 close(fd);
1630 if (start <= 0)
1631 return false;
1633 #ifdef HAVE_LCD_BITMAP
1634 /* Set all filename pointers to NULL */
1635 memset(bmp_names, 0, sizeof(bmp_names));
1636 #endif
1638 /* Skip leading UTF-8 BOM, if present. */
1639 wps_buffer = skip_utf8_bom(wps_buffer);
1641 /* parse the WPS source */
1642 if (!wps_parse(wps_data, wps_buffer)) {
1643 wps_reset(wps_data);
1644 return false;
1647 wps_data->wps_loaded = true;
1649 #ifdef HAVE_LCD_BITMAP
1650 /* get the bitmap dir */
1651 char bmpdir[MAX_PATH];
1652 size_t bmpdirlen;
1653 char *dot = strrchr(buf, '.');
1654 bmpdirlen = dot - buf;
1655 strncpy(bmpdir, buf, dot - buf);
1656 bmpdir[bmpdirlen] = 0;
1658 /* load the bitmaps that were found by the parsing */
1659 if (!load_wps_bitmaps(wps_data, bmpdir)) {
1660 wps_reset(wps_data);
1661 return false;
1663 #endif
1664 return true;
1668 int wps_subline_index(struct wps_data *data, int line, int subline)
1670 return data->lines[line].first_subline_idx + subline;
1673 int wps_first_token_index(struct wps_data *data, int line, int subline)
1675 int first_subline_idx = data->lines[line].first_subline_idx;
1676 return data->sublines[first_subline_idx + subline].first_token_idx;
1679 int wps_last_token_index(struct wps_data *data, int line, int subline)
1681 int first_subline_idx = data->lines[line].first_subline_idx;
1682 int idx = first_subline_idx + subline;
1683 if (idx < data->num_sublines - 1)
1685 /* This subline ends where the next begins */
1686 return data->sublines[idx+1].first_token_idx - 1;
1688 else
1690 /* The last subline goes to the end */
1691 return data->num_tokens - 1;