Added support for configurable rockbox directory. FS#9567 by Alex Bennee.
[kugel-rb.git] / apps / gui / wps_parser.c
blob88601fd9eb42db5cd8c43498bf6d208d54b0e4c1
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 #ifdef WPSEDITOR
32 #include "proxy.h"
33 #include "settings.h"
34 #include "sysfont.h"
35 #include "gwps.h"
36 #include "font.h"
37 #include "bmp.h"
38 #include "backdrop.h"
39 #include "ctype.h"
40 #else
41 #include "checkwps.h"
42 #define SYSFONT_HEIGHT 8
43 #define DEBUGF printf
44 #endif /*WPSEDITOR*/
45 #define FONT_SYSFIXED 0
46 #define FONT_UI 1
47 #else
48 #include "debug.h"
49 #endif /*__PCTOOL__*/
51 #ifndef __PCTOOL__
52 #include <ctype.h>
53 #include <stdbool.h>
54 #include <string.h>
55 #include "font.h"
57 #include "gwps.h"
58 #include "settings.h"
60 #ifdef HAVE_LCD_BITMAP
61 #include "bmp.h"
62 #endif
64 #include "backdrop.h"
66 #endif
68 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
69 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
71 #define WPS_ERROR_INVALID_PARAM -1
73 /* level of current conditional.
74 -1 means we're not in a conditional. */
75 static int level = -1;
77 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
78 or WPS_TOKEN_CONDITIONAL_START in current level */
79 static int lastcond[WPS_MAX_COND_LEVEL];
81 /* index of the WPS_TOKEN_CONDITIONAL in current level */
82 static int condindex[WPS_MAX_COND_LEVEL];
84 /* number of condtional options in current level */
85 static int numoptions[WPS_MAX_COND_LEVEL];
87 /* the current line in the file */
88 static int line;
90 #ifdef HAVE_LCD_BITMAP
92 #if LCD_DEPTH > 1
93 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
94 #else
95 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
96 #endif
98 #define PROGRESSBAR_BMP MAX_IMAGES
99 #define BACKDROP_BMP (MAX_BITMAPS-1)
101 /* pointers to the bitmap filenames in the WPS source */
102 static const char *bmp_names[MAX_BITMAPS];
104 #endif /* HAVE_LCD_BITMAP */
106 #ifdef DEBUG
107 /* debugging function */
108 extern void print_debug_info(struct wps_data *data, int fail, int line);
109 #endif
111 static void wps_reset(struct wps_data *data);
113 /* Function for parsing of details for a token. At the moment the
114 function is called, the token type has already been set. The
115 function must fill in the details and possibly add more tokens
116 to the token array. It should return the number of chars that
117 has been consumed.
119 wps_bufptr points to the char following the tag (i.e. where
120 details begin).
121 token is the pointer to the 'main' token being parsed
123 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
124 struct wps_token *token, struct wps_data *wps_data);
126 struct wps_tag {
127 enum wps_token_type type;
128 const char name[3];
129 unsigned char refresh_type;
130 const wps_tag_parse_func parse_func;
133 /* prototypes of all special parse functions : */
134 static int parse_timeout(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_progressbar(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_dir_level(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
141 #ifdef HAVE_LCD_BITMAP
142 static int parse_viewport_display(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_viewport(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 static int parse_statusbar_enable(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 static int parse_statusbar_disable(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 static int parse_image_display(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 static int parse_image_load(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 #endif /*HAVE_LCD_BITMAP */
155 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
156 static int parse_image_special(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 #endif
159 #ifdef HAVE_ALBUMART
160 static int parse_albumart_load(const char *wps_bufptr,
161 struct wps_token *token, struct wps_data *wps_data);
162 static int parse_albumart_conditional(const char *wps_bufptr,
163 struct wps_token *token, struct wps_data *wps_data);
164 #endif /* HAVE_ALBUMART */
166 #ifdef CONFIG_RTC
167 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
168 #else
169 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
170 #endif
172 /* array of available tags - those with more characters have to go first
173 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
174 static const struct wps_tag all_tags[] = {
176 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
177 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
178 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
180 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
181 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
182 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
183 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
184 #if CONFIG_CHARGING >= CHARGING_MONITOR
185 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
186 #endif
187 #if CONFIG_CHARGING
188 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
189 #endif
191 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
192 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
193 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
194 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
197 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
210 /* current file */
211 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
212 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
213 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
214 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
215 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
216 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
217 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
221 parse_dir_level },
223 /* next file */
224 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
225 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
226 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
227 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
228 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
229 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
230 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
231 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
232 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
233 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
234 parse_dir_level },
236 /* current metadata */
237 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
250 /* next metadata */
251 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
252 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
253 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
254 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
255 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
256 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
257 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
258 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
259 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
260 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
261 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
262 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
264 #if (CONFIG_CODEC != MAS3507D)
265 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
266 #endif
268 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
269 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
270 #endif
272 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
274 #ifdef HAS_REMOTE_BUTTON_HOLD
275 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
276 #else
277 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
278 #endif
280 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
281 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
282 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
283 parse_timeout },
285 #ifdef HAVE_LCD_BITMAP
286 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
287 #else
288 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
289 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
290 #endif
291 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
292 parse_progressbar },
294 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
296 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
297 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
298 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
299 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
301 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
302 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
303 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
304 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
306 #ifdef HAVE_TAGCACHE
307 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
308 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
309 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
310 #endif
312 #if CONFIG_CODEC == SWCODEC
313 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
314 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
315 #endif
317 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
318 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
320 #ifdef HAVE_LCD_BITMAP
321 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
322 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
324 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
326 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
327 parse_image_display },
329 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
330 #ifdef HAVE_ALBUMART
331 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
332 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
333 parse_albumart_conditional },
334 #endif
336 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
337 parse_viewport_display },
338 { WPS_NO_TOKEN, "V", 0, parse_viewport },
340 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
341 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
342 #endif
343 #endif
345 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
346 /* the array MUST end with an empty string (first char is \0) */
349 /* Returns the number of chars that should be skipped to jump
350 immediately after the first eol, i.e. to the start of the next line */
351 static int skip_end_of_line(const char *wps_bufptr)
353 line++;
354 int skip = 0;
355 while(*(wps_bufptr + skip) != '\n')
356 skip++;
357 return ++skip;
360 /* Starts a new subline in the current line during parsing */
361 static void wps_start_new_subline(struct wps_data *data)
363 data->num_sublines++;
364 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
365 data->lines[data->num_lines].num_sublines++;
368 #ifdef HAVE_LCD_BITMAP
370 static int parse_statusbar_enable(const char *wps_bufptr,
371 struct wps_token *token,
372 struct wps_data *wps_data)
374 (void)token; /* Kill warnings */
375 wps_data->wps_sb_tag = true;
376 wps_data->show_sb_on_wps = true;
377 if (wps_data->viewports[0].vp.y == 0)
379 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
380 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
382 return skip_end_of_line(wps_bufptr);
385 static int parse_statusbar_disable(const char *wps_bufptr,
386 struct wps_token *token,
387 struct wps_data *wps_data)
389 (void)token; /* Kill warnings */
390 wps_data->wps_sb_tag = true;
391 wps_data->show_sb_on_wps = false;
392 if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
394 wps_data->viewports[0].vp.y = 0;
395 wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
397 return skip_end_of_line(wps_bufptr);
400 static bool load_bitmap(struct wps_data *wps_data,
401 char* filename,
402 struct bitmap *bm)
404 int format;
405 #ifdef HAVE_REMOTE_LCD
406 if (wps_data->remote_wps)
407 format = FORMAT_ANY|FORMAT_REMOTE;
408 else
409 #endif
410 format = FORMAT_ANY|FORMAT_TRANSPARENT;
412 int ret = read_bmp_file(filename, bm,
413 wps_data->img_buf_free,
414 format);
416 if (ret > 0)
418 #if LCD_DEPTH == 16
419 if (ret % 2) ret++;
420 /* Always consume an even number of bytes */
421 #endif
422 wps_data->img_buf_ptr += ret;
423 wps_data->img_buf_free -= ret;
425 return true;
427 else
428 return false;
431 static int get_image_id(int c)
433 if(c >= 'a' && c <= 'z')
434 return c - 'a';
435 else if(c >= 'A' && c <= 'Z')
436 return c - 'A' + 26;
437 else
438 return -1;
441 static char *get_image_filename(const char *start, const char* bmpdir,
442 char *buf, int buf_size)
444 const char *end = strchr(start, '|');
446 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
448 buf = "\0";
449 return NULL;
452 int bmpdirlen = strlen(bmpdir);
454 strcpy(buf, bmpdir);
455 buf[bmpdirlen] = '/';
456 memcpy( &buf[bmpdirlen + 1], start, end - start);
457 buf[bmpdirlen + 1 + end - start] = 0;
459 return buf;
462 static int parse_image_display(const char *wps_bufptr,
463 struct wps_token *token,
464 struct wps_data *wps_data)
466 (void)wps_data;
467 int n = get_image_id(wps_bufptr[0]);
468 int subimage;
470 if (n == -1)
472 /* invalid picture display tag */
473 return WPS_ERROR_INVALID_PARAM;
476 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
478 /* Sanity check */
479 if (subimage >= wps_data->img[n].num_subimages)
480 return WPS_ERROR_INVALID_PARAM;
482 /* Store sub-image number to display in high bits */
483 token->value.i = n | (subimage << 8);
484 return 2; /* We have consumed 2 bytes */
485 } else {
486 token->value.i = n;
487 return 1; /* We have consumed 1 byte */
491 static int parse_image_load(const char *wps_bufptr,
492 struct wps_token *token,
493 struct wps_data *wps_data)
495 int n;
496 const char *ptr = wps_bufptr;
497 const char *pos;
498 const char* filename;
499 const char* id;
500 const char *newline;
501 int x,y;
503 /* format: %x|n|filename.bmp|x|y|
504 or %xl|n|filename.bmp|x|y|
505 or %xl|n|filename.bmp|x|y|num_subimages|
508 if (*ptr != '|')
509 return WPS_ERROR_INVALID_PARAM;
511 ptr++;
513 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
514 return WPS_ERROR_INVALID_PARAM;
516 /* Check there is a terminating | */
517 if (*ptr != '|')
518 return WPS_ERROR_INVALID_PARAM;
520 /* get the image ID */
521 n = get_image_id(*id);
523 /* check the image number and load state */
524 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
526 /* Invalid image ID */
527 return WPS_ERROR_INVALID_PARAM;
530 /* save a pointer to the filename */
531 bmp_names[n] = filename;
533 wps_data->img[n].x = x;
534 wps_data->img[n].y = y;
536 /* save current viewport */
537 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
539 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
541 wps_data->img[n].always_display = true;
543 else
545 /* Parse the (optional) number of sub-images */
546 ptr++;
547 newline = strchr(ptr, '\n');
548 pos = strchr(ptr, '|');
549 if (pos && pos < newline)
550 wps_data->img[n].num_subimages = atoi(ptr);
552 if (wps_data->img[n].num_subimages <= 0)
553 return WPS_ERROR_INVALID_PARAM;
556 /* Skip the rest of the line */
557 return skip_end_of_line(wps_bufptr);
560 static int parse_viewport_display(const char *wps_bufptr,
561 struct wps_token *token,
562 struct wps_data *wps_data)
564 (void)wps_data;
565 char letter = wps_bufptr[0];
567 if (letter < 'a' || letter > 'z')
569 /* invalid viewport tag */
570 return WPS_ERROR_INVALID_PARAM;
572 token->value.i = letter;
573 return 1;
576 static int parse_viewport(const char *wps_bufptr,
577 struct wps_token *token,
578 struct wps_data *wps_data)
580 (void)token; /* Kill warnings */
581 const char *ptr = wps_bufptr;
582 struct viewport* vp;
583 int depth;
584 uint32_t set = 0;
585 enum {
586 PL_X = 0,
587 PL_Y,
588 PL_WIDTH,
589 PL_HEIGHT,
590 PL_FONT,
591 PL_FG,
592 PL_BG,
594 int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
595 #ifdef HAVE_REMOTE_LCD
596 if (wps_data->remote_wps)
598 lcd_width = LCD_REMOTE_WIDTH;
599 lcd_height = LCD_REMOTE_HEIGHT;
601 #endif
603 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
604 return WPS_ERROR_INVALID_PARAM;
606 wps_data->num_viewports++;
607 /* check for the optional letter to signify its a hideable viewport */
608 /* %Vl|<label>|<rest of tags>| */
609 wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
611 if (*ptr == 'l')
613 if (*(ptr+1) == '|')
615 char label = *(ptr+2);
616 if (label >= 'a' && label <= 'z')
618 wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
619 wps_data->viewports[wps_data->num_viewports].label = label;
621 else
622 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
623 ptr += 3;
626 if (*ptr != '|')
627 return WPS_ERROR_INVALID_PARAM;
629 ptr++;
630 vp = &wps_data->viewports[wps_data->num_viewports].vp;
631 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
633 /* Set the defaults for fields not user-specified */
634 vp->drawmode = DRMODE_SOLID;
636 /* Work out the depth of this display */
637 #ifdef HAVE_REMOTE_LCD
638 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
639 #else
640 depth = LCD_DEPTH;
641 #endif
643 #ifdef HAVE_LCD_COLOR
644 if (depth == 16)
646 if (!(ptr = parse_list("dddddcc", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
647 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
648 return WPS_ERROR_INVALID_PARAM;
650 else
651 #endif
652 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
653 if (depth == 2) {
654 /* Default to black on white */
655 vp->fg_pattern = 0;
656 vp->bg_pattern = 3;
657 if (!(ptr = parse_list("dddddgg", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
658 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
659 return WPS_ERROR_INVALID_PARAM;
661 else
662 #endif
663 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
664 if (depth == 1)
666 if (!(ptr = parse_list("ddddd", &set, '|', ptr, &vp->x, &vp->y,
667 &vp->width, &vp->height, &vp->font)))
668 return WPS_ERROR_INVALID_PARAM;
670 else
671 #endif
674 /* Check for trailing | */
675 if (*ptr != '|')
676 return WPS_ERROR_INVALID_PARAM;
678 if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
679 return WPS_ERROR_INVALID_PARAM;
681 /* fix defaults */
682 if (!LIST_VALUE_PARSED(set, PL_WIDTH))
683 vp->width = lcd_width - vp->x;
684 if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
685 vp->height = lcd_height - vp->y;
687 /* Default to using the user font if the font was an invalid number */
688 if (!LIST_VALUE_PARSED(set, PL_FONT) ||
689 ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
690 vp->font = FONT_UI;
692 /* Validate the viewport dimensions - we know that the numbers are
693 non-negative integers */
694 if ((vp->x >= lcd_width) ||
695 ((vp->x + vp->width) > lcd_width) ||
696 (vp->y >= lcd_height) ||
697 ((vp->y + vp->height) > lcd_height))
699 return WPS_ERROR_INVALID_PARAM;
702 #ifdef HAVE_LCD_COLOR
703 if (depth == 16)
705 if (!LIST_VALUE_PARSED(set, PL_FG))
706 vp->fg_pattern = global_settings.fg_color;
707 if (!LIST_VALUE_PARSED(set, PL_BG))
708 vp->bg_pattern = global_settings.bg_color;
710 #endif
712 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
714 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
716 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
718 wps_data->lines[wps_data->num_lines].first_subline_idx =
719 wps_data->num_sublines;
721 wps_data->sublines[wps_data->num_sublines].first_token_idx =
722 wps_data->num_tokens;
725 /* Skip the rest of the line */
726 return skip_end_of_line(wps_bufptr);
730 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
731 static int parse_image_special(const char *wps_bufptr,
732 struct wps_token *token,
733 struct wps_data *wps_data)
735 (void)wps_data; /* kill warning */
736 (void)token;
737 const char *pos = NULL;
738 const char *newline;
740 pos = strchr(wps_bufptr + 1, '|');
741 newline = strchr(wps_bufptr, '\n');
743 if (pos > newline)
744 return WPS_ERROR_INVALID_PARAM;
745 #if LCD_DEPTH > 1
746 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
748 /* format: %X|filename.bmp| */
749 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
751 #endif
753 /* Skip the rest of the line */
754 return skip_end_of_line(wps_bufptr);
756 #endif
758 #endif /* HAVE_LCD_BITMAP */
760 static int parse_dir_level(const char *wps_bufptr,
761 struct wps_token *token,
762 struct wps_data *wps_data)
764 char val[] = { *wps_bufptr, '\0' };
765 token->value.i = atoi(val);
766 (void)wps_data; /* Kill warnings */
767 return 1;
770 static int parse_timeout(const char *wps_bufptr,
771 struct wps_token *token,
772 struct wps_data *wps_data)
774 int skip = 0;
775 int val = 0;
776 bool have_point = false;
777 bool have_tenth = false;
779 (void)wps_data; /* Kill the warning */
781 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
783 if (*wps_bufptr != '.')
785 val *= 10;
786 val += *wps_bufptr - '0';
787 if (have_point)
789 have_tenth = true;
790 wps_bufptr++;
791 skip++;
792 break;
795 else
796 have_point = true;
798 wps_bufptr++;
799 skip++;
802 if (have_tenth == false)
803 val *= 10;
805 if (val == 0 && skip == 0)
807 /* decide what to do if no value was specified */
808 switch (token->type)
810 case WPS_TOKEN_SUBLINE_TIMEOUT:
811 return -1;
812 case WPS_TOKEN_BUTTON_VOLUME:
813 val = 10;
814 break;
817 token->value.i = val;
819 return skip;
822 static int parse_progressbar(const char *wps_bufptr,
823 struct wps_token *token,
824 struct wps_data *wps_data)
826 (void)token; /* Kill warnings */
827 /* %pb or %pb|filename|x|y|width|height|
828 using - for any of the params uses "sane" values */
829 #ifdef HAVE_LCD_BITMAP
830 enum {
831 PB_FILENAME = 0,
832 PB_X,
833 PB_Y,
834 PB_WIDTH,
835 PB_HEIGHT
837 const char *filename;
838 int x, y, height, width;
839 uint32_t set = 0;
840 const char *ptr = wps_bufptr;
841 struct progressbar *pb;
842 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
843 #ifndef __PCTOOL__
844 int font_height = font_get(vp->font)->height;
845 #else
846 int font_height = 8;
847 #endif
848 int line_y_pos = font_height*(wps_data->num_lines -
849 wps_data->viewports[wps_data->num_viewports].first_line);
851 if (wps_data->progressbar_count >= MAX_PROGRESSBARS)
852 return WPS_ERROR_INVALID_PARAM;
854 pb = &wps_data->progressbar[wps_data->progressbar_count];
855 pb->have_bitmap_pb = false;
857 if (*wps_bufptr != '|') /* regular old style */
859 pb->x = 0;
860 pb->width = vp->width;
861 pb->height = SYSFONT_HEIGHT-2;
862 pb->y = line_y_pos + (font_height-pb->height)/2;
864 wps_data->viewports[wps_data->num_viewports].pb = pb;
865 wps_data->progressbar_count++;
866 return 0;
868 ptr = wps_bufptr + 1;
870 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
871 &x, &y, &width, &height)))
872 return WPS_ERROR_INVALID_PARAM;
874 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
875 bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
877 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
878 pb->x = x;
879 else
880 pb->x = vp->x;
882 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
884 /* A zero width causes a divide-by-zero error later, so reject it */
885 if (width == 0)
886 return WPS_ERROR_INVALID_PARAM;
888 pb->width = width;
890 else
891 pb->width = vp->width - pb->x;
893 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
895 /* A zero height makes no sense - reject it */
896 if (height == 0)
897 return WPS_ERROR_INVALID_PARAM;
899 pb->height = height;
901 else
902 pb->height = font_height;
904 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
905 pb->y = y;
906 else
907 pb->y = line_y_pos + (font_height-pb->height)/2;
909 wps_data->viewports[wps_data->num_viewports].pb = pb;
910 wps_data->progressbar_count++;
912 /* Skip the rest of the line */
913 return skip_end_of_line(wps_bufptr)-1;
914 #else
916 if (*(wps_bufptr-1) == 'f')
917 wps_data->full_line_progressbar = true;
918 else
919 wps_data->full_line_progressbar = false;
921 return 0;
923 #endif
926 #ifdef HAVE_ALBUMART
927 static int parse_albumart_load(const char *wps_bufptr,
928 struct wps_token *token,
929 struct wps_data *wps_data)
931 const char *_pos, *newline;
932 bool parsing;
933 const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
934 WPS_ALBUMART_ALIGN_CENTER |
935 WPS_ALBUMART_ALIGN_RIGHT;
936 const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
937 WPS_ALBUMART_ALIGN_CENTER |
938 WPS_ALBUMART_ALIGN_BOTTOM;
940 (void)token; /* silence warning */
942 /* reset albumart info in wps */
943 wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
944 wps_data->albumart_max_width = -1;
945 wps_data->albumart_max_height = -1;
946 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
947 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
949 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
951 newline = strchr(wps_bufptr, '\n');
953 /* initial validation and parsing of x and y components */
954 if (*wps_bufptr != '|')
955 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
957 _pos = wps_bufptr + 1;
958 if (!isdigit(*_pos))
959 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
960 wps_data->albumart_x = atoi(_pos);
962 _pos = strchr(_pos, '|');
963 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
964 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
966 wps_data->albumart_y = atoi(_pos);
968 _pos = strchr(_pos, '|');
969 if (!_pos || _pos > newline)
970 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
971 e.g. %Cl|7|59\n */
973 /* parsing width field */
974 parsing = true;
975 while (parsing)
977 /* apply each modifier in turn */
978 ++_pos;
979 switch (*_pos)
981 case 'l':
982 case 'L':
983 case '+':
984 wps_data->albumart_xalign =
985 (wps_data->albumart_xalign & xalign_mask) |
986 WPS_ALBUMART_ALIGN_LEFT;
987 break;
988 case 'c':
989 case 'C':
990 wps_data->albumart_xalign =
991 (wps_data->albumart_xalign & xalign_mask) |
992 WPS_ALBUMART_ALIGN_CENTER;
993 break;
994 case 'r':
995 case 'R':
996 case '-':
997 wps_data->albumart_xalign =
998 (wps_data->albumart_xalign & xalign_mask) |
999 WPS_ALBUMART_ALIGN_RIGHT;
1000 break;
1001 case 'd':
1002 case 'D':
1003 wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
1004 break;
1005 case 'i':
1006 case 'I':
1007 wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
1008 break;
1009 case 's':
1010 case 'S':
1011 wps_data->albumart_xalign |=
1012 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
1013 break;
1014 default:
1015 parsing = false;
1016 break;
1019 /* extract max width data */
1020 if (*_pos != '|')
1022 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1023 return WPS_ERROR_INVALID_PARAM;
1025 wps_data->albumart_max_width = atoi(_pos);
1027 _pos = strchr(_pos, '|');
1028 if (!_pos || _pos > newline)
1029 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1030 e.g. %Cl|7|59|200\n */
1033 /* parsing height field */
1034 parsing = true;
1035 while (parsing)
1037 /* apply each modifier in turn */
1038 ++_pos;
1039 switch (*_pos)
1041 case 't':
1042 case 'T':
1043 case '-':
1044 wps_data->albumart_yalign =
1045 (wps_data->albumart_yalign & yalign_mask) |
1046 WPS_ALBUMART_ALIGN_TOP;
1047 break;
1048 case 'c':
1049 case 'C':
1050 wps_data->albumart_yalign =
1051 (wps_data->albumart_yalign & yalign_mask) |
1052 WPS_ALBUMART_ALIGN_CENTER;
1053 break;
1054 case 'b':
1055 case 'B':
1056 case '+':
1057 wps_data->albumart_yalign =
1058 (wps_data->albumart_yalign & yalign_mask) |
1059 WPS_ALBUMART_ALIGN_BOTTOM;
1060 break;
1061 case 'd':
1062 case 'D':
1063 wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
1064 break;
1065 case 'i':
1066 case 'I':
1067 wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
1068 break;
1069 case 's':
1070 case 'S':
1071 wps_data->albumart_yalign |=
1072 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
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 #ifdef DEBUG
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 #ifdef HAVE_ALBUMART
1489 data->wps_uses_albumart = WPS_ALBUMART_NONE;
1490 #endif
1491 wps_data_init(data);
1492 #ifdef HAVE_REMOTE_LCD
1493 data->remote_wps = rwps;
1494 #endif
1497 #ifdef HAVE_LCD_BITMAP
1499 static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1501 char img_path[MAX_PATH];
1502 struct bitmap *bitmap;
1503 bool *loaded;
1504 int n;
1505 for (n = 0; n < BACKDROP_BMP; n++)
1507 if (bmp_names[n])
1509 get_image_filename(bmp_names[n], bmpdir,
1510 img_path, sizeof(img_path));
1512 if (n >= PROGRESSBAR_BMP ) {
1513 /* progressbar bitmap */
1514 bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
1515 loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
1516 } else {
1517 /* regular bitmap */
1518 bitmap = &wps_data->img[n].bm;
1519 loaded = &wps_data->img[n].loaded;
1522 /* load the image */
1523 bitmap->data = wps_data->img_buf_ptr;
1524 if (load_bitmap(wps_data, img_path, bitmap))
1526 *loaded = true;
1528 /* Calculate and store height if this image has sub-images */
1529 if (n < MAX_IMAGES)
1530 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1531 wps_data->img[n].num_subimages;
1533 else
1535 /* Abort if we can't load an image */
1536 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1537 return false;
1542 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1543 if (bmp_names[BACKDROP_BMP])
1545 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1546 img_path, sizeof(img_path));
1548 #if defined(HAVE_REMOTE_LCD)
1549 /* We only need to check LCD type if there is a remote LCD */
1550 if (!wps_data->remote_wps)
1551 #endif
1553 /* Load backdrop for the main LCD */
1554 if (!load_wps_backdrop(img_path))
1555 return false;
1557 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1558 else
1560 /* Load backdrop for the remote LCD */
1561 if (!load_remote_wps_backdrop(img_path))
1562 return false;
1564 #endif
1566 #endif /* has backdrop support */
1568 /* If we got here, everything was OK */
1569 return true;
1572 #endif /* HAVE_LCD_BITMAP */
1574 /* to setup up the wps-data from a format-buffer (isfile = false)
1575 from a (wps-)file (isfile = true)*/
1576 bool wps_data_load(struct wps_data *wps_data,
1577 struct screen *display,
1578 const char *buf,
1579 bool isfile)
1581 if (!wps_data || !buf)
1582 return false;
1584 wps_reset(wps_data);
1586 /* Initialise the first (default) viewport */
1587 wps_data->viewports[0].vp.x = 0;
1588 wps_data->viewports[0].vp.width = display->getwidth();
1589 if (!global_settings.statusbar)
1591 wps_data->viewports[0].vp.y = 0;
1592 wps_data->viewports[0].vp.height = display->getheight();
1594 else
1596 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1597 wps_data->viewports[0].vp.height = display->getheight() -
1598 STATUSBAR_HEIGHT;
1600 #ifdef HAVE_LCD_BITMAP
1601 wps_data->viewports[0].vp.font = FONT_UI;
1602 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1603 #endif
1604 #if LCD_DEPTH > 1
1605 if (display->depth > 1)
1607 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1608 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1610 #endif
1611 if (!isfile)
1613 return wps_parse(wps_data, buf);
1615 else
1618 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1619 * wants to be a virtual file. Feel free to modify dirbrowse()
1620 * if you're feeling brave.
1622 #ifndef __PCTOOL__
1623 if (! strcmp(buf, WPS_DEFAULTCFG) )
1625 global_settings.wps_file[0] = 0;
1626 return false;
1629 #ifdef HAVE_REMOTE_LCD
1630 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1632 global_settings.rwps_file[0] = 0;
1633 return false;
1635 #endif
1636 #endif /* __PCTOOL__ */
1638 int fd = open_utf8(buf, O_RDONLY);
1640 if (fd < 0)
1641 return false;
1643 /* get buffer space from the plugin buffer */
1644 size_t buffersize = 0;
1645 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1647 if (!wps_buffer)
1648 return false;
1650 /* copy the file's content to the buffer for parsing,
1651 ensuring that every line ends with a newline char. */
1652 unsigned int start = 0;
1653 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1655 start += strlen(wps_buffer + start);
1656 if (start < buffersize - 1)
1658 wps_buffer[start++] = '\n';
1659 wps_buffer[start] = 0;
1663 close(fd);
1665 if (start <= 0)
1666 return false;
1668 #ifdef HAVE_LCD_BITMAP
1669 /* Set all filename pointers to NULL */
1670 memset(bmp_names, 0, sizeof(bmp_names));
1671 #endif
1673 /* parse the WPS source */
1674 if (!wps_parse(wps_data, wps_buffer)) {
1675 wps_reset(wps_data);
1676 return false;
1679 wps_data->wps_loaded = true;
1681 #ifdef HAVE_LCD_BITMAP
1682 /* get the bitmap dir */
1683 char bmpdir[MAX_PATH];
1684 size_t bmpdirlen;
1685 char *dot = strrchr(buf, '.');
1686 bmpdirlen = dot - buf;
1687 strncpy(bmpdir, buf, dot - buf);
1688 bmpdir[bmpdirlen] = 0;
1690 /* load the bitmaps that were found by the parsing */
1691 if (!load_wps_bitmaps(wps_data, bmpdir)) {
1692 wps_reset(wps_data);
1693 return false;
1695 #endif
1696 return true;
1700 int wps_subline_index(struct wps_data *data, int line, int subline)
1702 return data->lines[line].first_subline_idx + subline;
1705 int wps_first_token_index(struct wps_data *data, int line, int subline)
1707 int first_subline_idx = data->lines[line].first_subline_idx;
1708 return data->sublines[first_subline_idx + subline].first_token_idx;
1711 int wps_last_token_index(struct wps_data *data, int line, int subline)
1713 int first_subline_idx = data->lines[line].first_subline_idx;
1714 int idx = first_subline_idx + subline;
1715 if (idx < data->num_sublines - 1)
1717 /* This subline ends where the next begins */
1718 return data->sublines[idx+1].first_token_idx - 1;
1720 else
1722 /* The last subline goes to the end */
1723 return data->num_tokens - 1;