Fix typo in the new default values for %V, so that the background color is actually...
[kugel-rb.git] / apps / gui / wps_parser.c
blob859e5eebe2a6c28ffbca60e71824902056a7b237
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include "gwps.h"
24 #include "file.h"
25 #include "misc.h"
26 #include "plugin.h"
28 #ifdef __PCTOOL__
29 #define DEBUGF printf
30 #define FONT_SYSFIXED 0
31 #define FONT_UI 1
32 #else
33 #include "debug.h"
34 #endif
36 #ifndef __PCTOOL__
37 #include <ctype.h>
38 #include <stdbool.h>
39 #include <string.h>
40 #include "font.h"
42 #include "gwps.h"
43 #include "settings.h"
45 #ifdef HAVE_LCD_BITMAP
46 #include "bmp.h"
47 #endif
49 #include "backdrop.h"
51 #endif
53 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
54 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
56 #define WPS_ERROR_INVALID_PARAM -1
58 #define PARSE_FAIL_UNCLOSED_COND 1
59 #define PARSE_FAIL_INVALID_CHAR 2
60 #define PARSE_FAIL_COND_SYNTAX_ERROR 3
61 #define PARSE_FAIL_COND_INVALID_PARAM 4
63 /* level of current conditional.
64 -1 means we're not in a conditional. */
65 static int level = -1;
67 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
68 or WPS_TOKEN_CONDITIONAL_START in current level */
69 static int lastcond[WPS_MAX_COND_LEVEL];
71 /* index of the WPS_TOKEN_CONDITIONAL in current level */
72 static int condindex[WPS_MAX_COND_LEVEL];
74 /* number of condtional options in current level */
75 static int numoptions[WPS_MAX_COND_LEVEL];
77 /* the current line in the file */
78 static int line;
80 #ifdef HAVE_LCD_BITMAP
82 #if LCD_DEPTH > 1
83 #define MAX_BITMAPS (MAX_IMAGES+2) /* WPS images + pbar bitmap + backdrop */
84 #else
85 #define MAX_BITMAPS (MAX_IMAGES+1) /* WPS images + pbar bitmap */
86 #endif
88 #define PROGRESSBAR_BMP MAX_IMAGES
89 #define BACKDROP_BMP (MAX_IMAGES+1)
91 /* pointers to the bitmap filenames in the WPS source */
92 static const char *bmp_names[MAX_BITMAPS];
94 #endif /* HAVE_LCD_BITMAP */
96 #ifdef DEBUG
97 /* debugging function */
98 extern void print_debug_info(struct wps_data *data, int fail, int line);
99 #endif
101 static void wps_reset(struct wps_data *data);
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
107 has been consumed.
109 wps_bufptr points to the char following the tag (i.e. where
110 details begin).
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
114 struct wps_token *token, struct wps_data *wps_data);
116 struct wps_tag {
117 enum wps_token_type type;
118 const char name[3];
119 unsigned char refresh_type;
120 const wps_tag_parse_func parse_func;
123 /* prototypes of all special parse functions : */
124 static int parse_subline_timeout(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126 static int parse_progressbar(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_dir_level(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
131 #ifdef HAVE_LCD_BITMAP
132 static int parse_viewport(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_leftmargin(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_image_special(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 #ifdef HAVE_ALBUMART
148 static int parse_albumart_load(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 static int parse_albumart_conditional(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 #endif /* HAVE_ALBUMART */
154 #ifdef CONFIG_RTC
155 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
156 #else
157 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
158 #endif
160 /* array of available tags - those with more characters have to go first
161 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
162 static const struct wps_tag all_tags[] = {
164 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
165 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
166 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
168 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
169 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
170 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
171 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
172 #if CONFIG_CHARGING >= CHARGING_MONITOR
173 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
174 #endif
175 #if CONFIG_CHARGING
176 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
177 #endif
179 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
180 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
181 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
182 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
183 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
184 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
185 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
186 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
187 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
188 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
189 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
190 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
191 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
192 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
193 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
194 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
198 /* current file */
199 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
200 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
201 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
202 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
203 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
204 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
205 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
206 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
207 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
208 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
209 parse_dir_level },
211 /* next file */
212 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
213 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
214 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
215 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
216 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
217 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
218 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
219 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
220 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
221 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
222 parse_dir_level },
224 /* current metadata */
225 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
238 /* next metadata */
239 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
240 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
241 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
242 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
243 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
244 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
245 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
246 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
247 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
248 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
249 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
250 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
252 #if (CONFIG_CODEC != MAS3507D)
253 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
254 #endif
256 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
257 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
258 #endif
260 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
262 #ifdef HAS_REMOTE_BUTTON_HOLD
263 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
264 #else
265 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
266 #endif
268 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
269 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
271 #ifdef HAVE_LCD_BITMAP
272 { WPS_TOKEN_LEFTMARGIN, "m", 0, parse_leftmargin },
273 #endif
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 { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special },
321 #ifdef HAVE_ALBUMART
322 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
323 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
324 parse_albumart_conditional },
325 #endif
327 { WPS_NO_TOKEN, "V", 0, parse_viewport },
329 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
330 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
331 #endif
332 #endif
334 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
335 /* the array MUST end with an empty string (first char is \0) */
338 /* Returns the number of chars that should be skipped to jump
339 immediately after the first eol, i.e. to the start of the next line */
340 static int skip_end_of_line(const char *wps_bufptr)
342 line++;
343 int skip = 0;
344 while(*(wps_bufptr + skip) != '\n')
345 skip++;
346 return ++skip;
349 /* Starts a new subline in the current line during parsing */
350 static void wps_start_new_subline(struct wps_data *data)
352 data->num_sublines++;
353 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
354 data->lines[data->num_lines].num_sublines++;
357 #ifdef HAVE_LCD_BITMAP
359 static int parse_statusbar_enable(const char *wps_bufptr,
360 struct wps_token *token,
361 struct wps_data *wps_data)
363 (void)token; /* Kill warnings */
364 wps_data->wps_sb_tag = true;
365 wps_data->show_sb_on_wps = true;
366 return skip_end_of_line(wps_bufptr);
369 static int parse_statusbar_disable(const char *wps_bufptr,
370 struct wps_token *token,
371 struct wps_data *wps_data)
373 (void)token; /* Kill warnings */
374 wps_data->wps_sb_tag = true;
375 wps_data->show_sb_on_wps = false;
376 return skip_end_of_line(wps_bufptr);
379 static bool load_bitmap(struct wps_data *wps_data,
380 char* filename,
381 struct bitmap *bm)
383 int format;
384 #ifdef HAVE_REMOTE_LCD
385 if (wps_data->remote_wps)
386 format = FORMAT_ANY|FORMAT_REMOTE;
387 else
388 #endif
389 format = FORMAT_ANY|FORMAT_TRANSPARENT;
391 int ret = read_bmp_file(filename, bm,
392 wps_data->img_buf_free,
393 format);
395 if (ret > 0)
397 #if LCD_DEPTH == 16
398 if (ret % 2) ret++;
399 /* Always consume an even number of bytes */
400 #endif
401 wps_data->img_buf_ptr += ret;
402 wps_data->img_buf_free -= ret;
404 return true;
406 else
407 return false;
410 static int get_image_id(int c)
412 if(c >= 'a' && c <= 'z')
413 return c - 'a';
414 else if(c >= 'A' && c <= 'Z')
415 return c - 'A' + 26;
416 else
417 return -1;
420 static char *get_image_filename(const char *start, const char* bmpdir,
421 char *buf, int buf_size)
423 const char *end = strchr(start, '|');
425 if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
427 buf = "\0";
428 return NULL;
431 int bmpdirlen = strlen(bmpdir);
433 strcpy(buf, bmpdir);
434 buf[bmpdirlen] = '/';
435 memcpy( &buf[bmpdirlen + 1], start, end - start);
436 buf[bmpdirlen + 1 + end - start] = 0;
438 return buf;
441 static int parse_image_display(const char *wps_bufptr,
442 struct wps_token *token,
443 struct wps_data *wps_data)
445 (void)wps_data;
446 int n = get_image_id(wps_bufptr[0]);
447 int subimage;
449 if (n == -1)
451 /* invalid picture display tag */
452 return WPS_ERROR_INVALID_PARAM;
455 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
457 /* Sanity check */
458 if (subimage >= wps_data->img[n].num_subimages)
459 return WPS_ERROR_INVALID_PARAM;
461 /* Store sub-image number to display in high bits */
462 token->value.i = n | (subimage << 8);
463 return 2; /* We have consumed 2 bytes */
464 } else {
465 token->value.i = n;
466 return 1; /* We have consumed 1 byte */
470 static int parse_image_load(const char *wps_bufptr,
471 struct wps_token *token,
472 struct wps_data *wps_data)
474 int n;
475 const char *ptr = wps_bufptr;
476 const char *pos;
477 const char* filename;
478 const char* id;
479 const char *newline;
480 int x,y;
482 /* format: %x|n|filename.bmp|x|y|
483 or %xl|n|filename.bmp|x|y|
484 or %xl|n|filename.bmp|x|y|num_subimages|
487 if (*ptr != '|')
488 return WPS_ERROR_INVALID_PARAM;
490 ptr++;
492 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
493 return WPS_ERROR_INVALID_PARAM;
495 /* Check there is a terminating | */
496 if (*ptr != '|')
497 return WPS_ERROR_INVALID_PARAM;
499 /* get the image ID */
500 n = get_image_id(*id);
502 /* check the image number and load state */
503 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
505 /* Invalid image ID */
506 return WPS_ERROR_INVALID_PARAM;
509 /* save a pointer to the filename */
510 bmp_names[n] = filename;
512 wps_data->img[n].x = x;
513 wps_data->img[n].y = y;
515 /* save current viewport */
516 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
518 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
520 wps_data->img[n].always_display = true;
522 else
524 /* Parse the (optional) number of sub-images */
525 ptr++;
526 newline = strchr(ptr, '\n');
527 pos = strchr(ptr, '|');
528 if (pos && pos < newline)
529 wps_data->img[n].num_subimages = atoi(ptr);
531 if (wps_data->img[n].num_subimages <= 0)
532 return WPS_ERROR_INVALID_PARAM;
535 /* Skip the rest of the line */
536 return skip_end_of_line(wps_bufptr);
539 static int parse_viewport(const char *wps_bufptr,
540 struct wps_token *token,
541 struct wps_data *wps_data)
543 (void)token; /* Kill warnings */
544 const char *ptr = wps_bufptr;
545 struct viewport* vp;
546 int depth;
547 int valid = 0;
548 enum {
549 PL_X = 0,
550 PL_Y,
551 PL_WIDTH,
552 PL_HEIGHT,
553 PL_FONT,
554 PL_FG,
555 PL_BG,
557 int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
558 #ifdef HAVE_REMOTE_LCD
559 if (wps_data->remote_wps)
561 lcd_width = LCD_REMOTE_WIDTH;
562 lcd_height = LCD_REMOTE_HEIGHT;
564 #endif
566 if (*wps_bufptr != '|')
567 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
569 ptr = wps_bufptr + 1;
570 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
572 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
573 return WPS_ERROR_INVALID_PARAM;
575 wps_data->num_viewports++;
576 vp = &wps_data->viewports[wps_data->num_viewports].vp;
578 /* Set the defaults for fields not user-specified */
579 vp->drawmode = DRMODE_SOLID;
580 vp->xmargin = 0;
581 vp->ymargin = 0;
583 /* Work out the depth of this display */
584 #ifdef HAVE_REMOTE_LCD
585 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
586 #else
587 depth = LCD_DEPTH;
588 #endif
590 #ifdef HAVE_LCD_COLOR
591 if (depth == 16)
593 if (!(ptr = parse_list("dddddcc", &valid, '|', ptr, &vp->x, &vp->y, &vp->width,
594 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
595 return WPS_ERROR_INVALID_PARAM;
597 else
598 #endif
599 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
600 if (depth == 2) {
601 /* Default to black on white */
602 vp->fg_pattern = 0;
603 vp->bg_pattern = 3;
604 if (!(ptr = parse_list("dddddgg", &valid, '|', ptr, &vp->x, &vp->y, &vp->width,
605 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
606 return WPS_ERROR_INVALID_PARAM;
608 else
609 #endif
610 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
611 if (depth == 1)
613 if (!(ptr = parse_list("ddddd", &valid, '|', ptr, &vp->x, &vp->y,
614 &vp->width, &vp->height, &vp->font)))
615 return WPS_ERROR_INVALID_PARAM;
617 else
618 #endif
621 /* Check for trailing | */
622 if (*ptr != '|')
623 return WPS_ERROR_INVALID_PARAM;
625 if ((valid&(1<<PL_X)) == 0 || (valid&(1<<PL_Y)) == 0)
626 return WPS_ERROR_INVALID_PARAM;
628 /* fix defaults */
629 if ((valid&(1<<PL_WIDTH)) == 0)
630 vp->width = lcd_width - vp->x;
631 if ((valid&(1<<PL_HEIGHT)) == 0)
632 vp->height = lcd_height - vp->y;
634 /* Default to using the user font if the font was an invalid number */
635 if (((valid&(1<<PL_FONT)) == 0) ||
636 ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
637 vp->font = FONT_UI;
639 /* Validate the viewport dimensions - we know that the numbers are
640 non-negative integers */
641 if ((vp->x >= lcd_width) ||
642 ((vp->x + vp->width) > lcd_width) ||
643 (vp->y >= lcd_height) ||
644 ((vp->y + vp->height) > lcd_height))
646 return WPS_ERROR_INVALID_PARAM;
649 #ifdef HAVE_LCD_COLOR
650 if (depth == 16)
652 if ((valid&(1<<PL_FG)) == 0)
653 vp->fg_pattern = global_settings.fg_color;
654 if ((valid&(1<<PL_BG)) == 0)
655 vp->bg_pattern = global_settings.bg_color;
657 #endif
659 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
661 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
663 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
665 wps_data->lines[wps_data->num_lines].first_subline_idx =
666 wps_data->num_sublines;
668 wps_data->sublines[wps_data->num_sublines].first_token_idx =
669 wps_data->num_tokens;
672 /* Skip the rest of the line */
673 return skip_end_of_line(wps_bufptr);
677 static int parse_image_special(const char *wps_bufptr,
678 struct wps_token *token,
679 struct wps_data *wps_data)
681 (void)wps_data; /* kill warning */
682 const char *pos = NULL;
683 const char *newline;
685 pos = strchr(wps_bufptr + 1, '|');
686 newline = strchr(wps_bufptr, '\n');
688 if (pos > newline)
689 return WPS_ERROR_INVALID_PARAM;
691 if (token->type == WPS_TOKEN_IMAGE_PROGRESS_BAR)
693 /* format: %P|filename.bmp| */
694 bmp_names[PROGRESSBAR_BMP] = wps_bufptr + 1;
696 #if LCD_DEPTH > 1
697 else if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
699 /* format: %X|filename.bmp| */
700 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
702 #endif
704 /* Skip the rest of the line */
705 return skip_end_of_line(wps_bufptr);
708 #endif /* HAVE_LCD_BITMAP */
710 static int parse_dir_level(const char *wps_bufptr,
711 struct wps_token *token,
712 struct wps_data *wps_data)
714 char val[] = { *wps_bufptr, '\0' };
715 token->value.i = atoi(val);
716 (void)wps_data; /* Kill warnings */
717 return 1;
720 static int parse_subline_timeout(const char *wps_bufptr,
721 struct wps_token *token,
722 struct wps_data *wps_data)
724 int skip = 0;
725 int val = 0;
726 bool have_point = false;
727 bool have_tenth = false;
729 (void)wps_data; /* Kill the warning */
731 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
733 if (*wps_bufptr != '.')
735 val *= 10;
736 val += *wps_bufptr - '0';
737 if (have_point)
739 have_tenth = true;
740 wps_bufptr++;
741 skip++;
742 break;
745 else
746 have_point = true;
748 wps_bufptr++;
749 skip++;
752 if (have_tenth == false)
753 val *= 10;
755 token->value.i = val;
757 return skip;
760 static int parse_progressbar(const char *wps_bufptr,
761 struct wps_token *token,
762 struct wps_data *wps_data)
764 (void)token; /* Kill warnings */
765 #ifdef HAVE_LCD_BITMAP
767 short *vals[] = {
768 &wps_data->progress_height,
769 &wps_data->progress_start,
770 &wps_data->progress_end,
771 &wps_data->progress_top };
773 /* default values : */
774 wps_data->progress_height = 6;
775 wps_data->progress_start = 0;
776 wps_data->progress_end = 0;
777 wps_data->progress_top = -1;
779 int i = 0;
780 char *newline = strchr(wps_bufptr, '\n');
781 char *prev = strchr(wps_bufptr, '|');
782 if (prev && prev < newline) {
783 char *next = strchr(prev+1, '|');
784 while (i < 4 && next && next < newline)
786 *(vals[i++]) = atoi(++prev);
787 prev = strchr(prev, '|');
788 next = strchr(++next, '|');
791 if (wps_data->progress_height < 3)
792 wps_data->progress_height = 3;
793 if (wps_data->progress_end < wps_data->progress_start + 3)
794 wps_data->progress_end = 0;
797 return newline - wps_bufptr;
799 #else
801 if (*(wps_bufptr-1) == 'f')
802 wps_data->full_line_progressbar = true;
803 else
804 wps_data->full_line_progressbar = false;
806 return 0;
808 #endif
811 #ifdef HAVE_ALBUMART
812 static int parse_albumart_load(const char *wps_bufptr,
813 struct wps_token *token,
814 struct wps_data *wps_data)
816 const char *_pos, *newline;
817 bool parsing;
818 const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
819 WPS_ALBUMART_ALIGN_CENTER |
820 WPS_ALBUMART_ALIGN_RIGHT;
821 const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
822 WPS_ALBUMART_ALIGN_CENTER |
823 WPS_ALBUMART_ALIGN_BOTTOM;
825 (void)token; /* silence warning */
827 /* reset albumart info in wps */
828 wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
829 wps_data->albumart_max_width = -1;
830 wps_data->albumart_max_height = -1;
831 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
832 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
834 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
836 newline = strchr(wps_bufptr, '\n');
838 /* initial validation and parsing of x and y components */
839 if (*wps_bufptr != '|')
840 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
842 _pos = wps_bufptr + 1;
843 if (!isdigit(*_pos))
844 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
845 wps_data->albumart_x = atoi(_pos);
847 _pos = strchr(_pos, '|');
848 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
849 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
851 wps_data->albumart_y = atoi(_pos);
853 _pos = strchr(_pos, '|');
854 if (!_pos || _pos > newline)
855 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
856 e.g. %Cl|7|59\n */
858 /* parsing width field */
859 parsing = true;
860 while (parsing)
862 /* apply each modifier in turn */
863 ++_pos;
864 switch (*_pos)
866 case 'l':
867 case 'L':
868 case '+':
869 wps_data->albumart_xalign =
870 (wps_data->albumart_xalign & xalign_mask) |
871 WPS_ALBUMART_ALIGN_LEFT;
872 break;
873 case 'c':
874 case 'C':
875 wps_data->albumart_xalign =
876 (wps_data->albumart_xalign & xalign_mask) |
877 WPS_ALBUMART_ALIGN_CENTER;
878 break;
879 case 'r':
880 case 'R':
881 case '-':
882 wps_data->albumart_xalign =
883 (wps_data->albumart_xalign & xalign_mask) |
884 WPS_ALBUMART_ALIGN_RIGHT;
885 break;
886 case 'd':
887 case 'D':
888 wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
889 break;
890 case 'i':
891 case 'I':
892 wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
893 break;
894 case 's':
895 case 'S':
896 wps_data->albumart_xalign |=
897 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
898 break;
899 default:
900 parsing = false;
901 break;
904 /* extract max width data */
905 if (*_pos != '|')
907 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
908 return WPS_ERROR_INVALID_PARAM;
910 wps_data->albumart_max_width = atoi(_pos);
912 _pos = strchr(_pos, '|');
913 if (!_pos || _pos > newline)
914 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
915 e.g. %Cl|7|59|200\n */
918 /* parsing height field */
919 parsing = true;
920 while (parsing)
922 /* apply each modifier in turn */
923 ++_pos;
924 switch (*_pos)
926 case 't':
927 case 'T':
928 case '-':
929 wps_data->albumart_yalign =
930 (wps_data->albumart_yalign & yalign_mask) |
931 WPS_ALBUMART_ALIGN_TOP;
932 break;
933 case 'c':
934 case 'C':
935 wps_data->albumart_yalign =
936 (wps_data->albumart_yalign & yalign_mask) |
937 WPS_ALBUMART_ALIGN_CENTER;
938 break;
939 case 'b':
940 case 'B':
941 case '+':
942 wps_data->albumart_yalign =
943 (wps_data->albumart_yalign & yalign_mask) |
944 WPS_ALBUMART_ALIGN_BOTTOM;
945 break;
946 case 'd':
947 case 'D':
948 wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
949 break;
950 case 'i':
951 case 'I':
952 wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
953 break;
954 case 's':
955 case 'S':
956 wps_data->albumart_yalign |=
957 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
958 break;
959 default:
960 parsing = false;
961 break;
964 /* extract max height data */
965 if (*_pos != '|')
967 if (!isdigit(*_pos))
968 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
970 wps_data->albumart_max_height = atoi(_pos);
972 _pos = strchr(_pos, '|');
973 if (!_pos || _pos > newline)
974 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
975 e.g. %Cl|7|59|200|200\n */
978 /* if we got here, we parsed everything ok .. ! */
979 if (wps_data->albumart_max_width < 0)
980 wps_data->albumart_max_width = 0;
981 else if (wps_data->albumart_max_width > LCD_WIDTH)
982 wps_data->albumart_max_width = LCD_WIDTH;
984 if (wps_data->albumart_max_height < 0)
985 wps_data->albumart_max_height = 0;
986 else if (wps_data->albumart_max_height > LCD_HEIGHT)
987 wps_data->albumart_max_height = LCD_HEIGHT;
989 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
991 /* Skip the rest of the line */
992 return skip_end_of_line(wps_bufptr);
995 static int parse_albumart_conditional(const char *wps_bufptr,
996 struct wps_token *token,
997 struct wps_data *wps_data)
999 struct wps_token *prevtoken = token;
1000 --prevtoken;
1001 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1003 /* This %C is part of a %?C construct.
1004 It's either %?C<blah> or %?Cn<blah> */
1005 token->type = WPS_TOKEN_ALBUMART_FOUND;
1006 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1008 token->next = true;
1009 return 1;
1011 else if (*wps_bufptr == '<')
1013 return 0;
1015 else
1017 token->type = WPS_NO_TOKEN;
1018 return 0;
1021 else
1023 /* This %C tag is in a conditional construct. */
1024 wps_data->albumart_cond_index = condindex[level];
1025 return 0;
1028 #endif /* HAVE_ALBUMART */
1030 #ifdef HAVE_LCD_BITMAP
1031 static int parse_leftmargin(const char *wps_bufptr, struct wps_token *token,
1032 struct wps_data *wps_data)
1034 const char* p;
1035 const char* pend;
1036 const char *newline;
1038 (void)wps_data; /* Kill the warning */
1040 /* valid tag looks like %m|12| */
1041 if(*wps_bufptr == '|')
1043 p = wps_bufptr + 1;
1044 newline = strchr(wps_bufptr, '\n');
1045 if(isdigit(*p) && (pend = strchr(p, '|')) && pend < newline)
1047 token->value.i = atoi(p);
1048 return pend - wps_bufptr + 1;
1052 /* invalid tag syntax */
1053 return WPS_ERROR_INVALID_PARAM;
1055 #endif
1058 /* Parse a generic token from the given string. Return the length read */
1059 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1061 int skip = 0, taglen = 0, ret;
1062 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1063 const struct wps_tag *tag;
1065 switch(*wps_bufptr)
1068 case '%':
1069 case '<':
1070 case '|':
1071 case '>':
1072 case ';':
1073 case '#':
1074 /* escaped characters */
1075 token->type = WPS_TOKEN_CHARACTER;
1076 token->value.c = *wps_bufptr;
1077 taglen = 1;
1078 wps_data->num_tokens++;
1079 break;
1081 case '?':
1082 /* conditional tag */
1083 token->type = WPS_TOKEN_CONDITIONAL;
1084 level++;
1085 condindex[level] = wps_data->num_tokens;
1086 numoptions[level] = 1;
1087 wps_data->num_tokens++;
1088 ret = parse_token(wps_bufptr + 1, wps_data);
1089 if (ret < 0) return ret;
1090 taglen = 1 + ret;
1091 break;
1093 default:
1094 /* find what tag we have */
1095 for (tag = all_tags;
1096 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1097 tag++) ;
1099 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1100 token->type = tag->type;
1101 wps_data->sublines[wps_data->num_sublines].line_type |=
1102 tag->refresh_type;
1104 /* if the tag has a special parsing function, we call it */
1105 if (tag->parse_func)
1107 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1108 if (ret < 0) return ret;
1109 skip += ret;
1112 /* Some tags we don't want to save as tokens */
1113 if (tag->type == WPS_NO_TOKEN)
1114 break;
1116 /* tags that start with 'F', 'I' or 'D' are for the next file */
1117 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1118 *(tag->name) == 'D')
1119 token->next = true;
1121 wps_data->num_tokens++;
1122 break;
1125 skip += taglen;
1126 return skip;
1129 /* Parses the WPS.
1130 data is the pointer to the structure where the parsed WPS should be stored.
1131 It is initialised.
1132 wps_bufptr points to the string containing the WPS tags */
1133 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1135 if (!data || !wps_bufptr || !*wps_bufptr)
1136 return false;
1138 char *stringbuf = data->string_buffer;
1139 int stringbuf_used = 0;
1140 int fail = 0;
1141 int ret;
1142 line = 1;
1143 level = -1;
1145 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1146 && data->num_viewports < WPS_MAX_VIEWPORTS
1147 && data->num_lines < WPS_MAX_LINES)
1149 switch(*wps_bufptr++)
1152 /* Regular tag */
1153 case '%':
1154 if ((ret = parse_token(wps_bufptr, data)) < 0)
1156 fail = PARSE_FAIL_COND_INVALID_PARAM;
1157 break;
1159 wps_bufptr += ret;
1160 break;
1162 /* Alternating sublines separator */
1163 case ';':
1164 if (level >= 0) /* there are unclosed conditionals */
1166 fail = PARSE_FAIL_UNCLOSED_COND;
1167 break;
1170 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1171 wps_start_new_subline(data);
1172 else
1173 wps_bufptr += skip_end_of_line(wps_bufptr);
1175 break;
1177 /* Conditional list start */
1178 case '<':
1179 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1181 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1182 break;
1185 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1186 lastcond[level] = data->num_tokens++;
1187 break;
1189 /* Conditional list end */
1190 case '>':
1191 if (level < 0) /* not in a conditional, invalid char */
1193 fail = PARSE_FAIL_INVALID_CHAR;
1194 break;
1197 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1198 if (lastcond[level])
1199 data->tokens[lastcond[level]].value.i = data->num_tokens;
1200 else
1202 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1203 break;
1206 lastcond[level] = 0;
1207 data->num_tokens++;
1208 data->tokens[condindex[level]].value.i = numoptions[level];
1209 level--;
1210 break;
1212 /* Conditional list option */
1213 case '|':
1214 if (level < 0) /* not in a conditional, invalid char */
1216 fail = PARSE_FAIL_INVALID_CHAR;
1217 break;
1220 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1221 if (lastcond[level])
1222 data->tokens[lastcond[level]].value.i = data->num_tokens;
1223 else
1225 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1226 break;
1229 lastcond[level] = data->num_tokens;
1230 numoptions[level]++;
1231 data->num_tokens++;
1232 break;
1234 /* Comment */
1235 case '#':
1236 if (level >= 0) /* there are unclosed conditionals */
1238 fail = PARSE_FAIL_UNCLOSED_COND;
1239 break;
1242 wps_bufptr += skip_end_of_line(wps_bufptr);
1243 break;
1245 /* End of this line */
1246 case '\n':
1247 if (level >= 0) /* there are unclosed conditionals */
1249 fail = PARSE_FAIL_UNCLOSED_COND;
1250 break;
1253 line++;
1254 wps_start_new_subline(data);
1255 data->num_lines++; /* Start a new line */
1257 if ((data->num_lines < WPS_MAX_LINES) &&
1258 (data->num_sublines < WPS_MAX_SUBLINES))
1260 data->lines[data->num_lines].first_subline_idx =
1261 data->num_sublines;
1263 data->sublines[data->num_sublines].first_token_idx =
1264 data->num_tokens;
1267 break;
1269 /* String */
1270 default:
1272 unsigned int len = 1;
1273 const char *string_start = wps_bufptr - 1;
1275 /* find the length of the string */
1276 while (*wps_bufptr && *wps_bufptr != '#' &&
1277 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1278 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1279 *wps_bufptr != '|' && *wps_bufptr != '\n')
1281 wps_bufptr++;
1282 len++;
1285 /* look if we already have that string */
1286 char **str;
1287 int i;
1288 bool found;
1289 for (i = 0, str = data->strings, found = false;
1290 i < data->num_strings &&
1291 !(found = (strlen(*str) == len &&
1292 strncmp(string_start, *str, len) == 0));
1293 i++, str++);
1294 /* If a matching string is found, found is true and i is
1295 the index of the string. If not, found is false */
1297 /* If it's NOT a duplicate, do nothing if we already have
1298 too many unique strings */
1299 if (found ||
1300 (stringbuf_used < STRING_BUFFER_SIZE - 1 &&
1301 data->num_strings < WPS_MAX_STRINGS))
1303 if (!found)
1305 /* new string */
1307 /* truncate? */
1308 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1)
1309 len = STRING_BUFFER_SIZE - stringbuf_used - 1;
1311 strncpy(stringbuf, string_start, len);
1312 *(stringbuf + len) = '\0';
1314 data->strings[data->num_strings] = stringbuf;
1315 stringbuf += len + 1;
1316 stringbuf_used += len + 1;
1317 data->tokens[data->num_tokens].value.i =
1318 data->num_strings;
1319 data->num_strings++;
1321 else
1323 /* another ocurrence of an existing string */
1324 data->tokens[data->num_tokens].value.i = i;
1326 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1327 data->num_tokens++;
1330 break;
1334 if (!fail && level >= 0) /* there are unclosed conditionals */
1335 fail = PARSE_FAIL_UNCLOSED_COND;
1337 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1339 /* We have finished with the last viewport, so increment count */
1340 data->num_viewports++;
1342 #ifdef DEBUG
1343 print_debug_info(data, fail, line);
1344 #endif
1346 return (fail == 0);
1349 #ifdef HAVE_LCD_BITMAP
1350 /* Clear the WPS image cache */
1351 static void wps_images_clear(struct wps_data *data)
1353 int i;
1354 /* set images to unloaded and not displayed */
1355 for (i = 0; i < MAX_IMAGES; i++)
1357 data->img[i].loaded = false;
1358 data->img[i].display = -1;
1359 data->img[i].always_display = false;
1360 data->img[i].num_subimages = 1;
1362 data->progressbar.have_bitmap_pb = false;
1364 #endif
1366 /* initial setup of wps_data */
1367 void wps_data_init(struct wps_data *wps_data)
1369 #ifdef HAVE_LCD_BITMAP
1370 wps_images_clear(wps_data);
1371 wps_data->wps_sb_tag = false;
1372 wps_data->show_sb_on_wps = false;
1373 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1374 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1375 wps_data->peak_meter_enabled = false;
1376 #else /* HAVE_LCD_CHARCELLS */
1377 int i;
1378 for (i = 0; i < 8; i++)
1380 wps_data->wps_progress_pat[i] = 0;
1382 wps_data->full_line_progressbar = false;
1383 #endif
1384 wps_data->wps_loaded = false;
1387 static void wps_reset(struct wps_data *data)
1389 #ifdef HAVE_REMOTE_LCD
1390 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1391 #endif
1392 memset(data, 0, sizeof(*data));
1393 #ifdef HAVE_ALBUMART
1394 data->wps_uses_albumart = WPS_ALBUMART_NONE;
1395 #endif
1396 wps_data_init(data);
1397 #ifdef HAVE_REMOTE_LCD
1398 data->remote_wps = rwps;
1399 #endif
1402 #ifdef HAVE_LCD_BITMAP
1404 static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1406 char img_path[MAX_PATH];
1407 struct bitmap *bitmap;
1408 bool *loaded;
1409 int n;
1410 for (n = 0; n < BACKDROP_BMP; n++)
1412 if (bmp_names[n])
1414 get_image_filename(bmp_names[n], bmpdir,
1415 img_path, sizeof(img_path));
1417 if (n == PROGRESSBAR_BMP) {
1418 /* progressbar bitmap */
1419 bitmap = &wps_data->progressbar.bm;
1420 loaded = &wps_data->progressbar.have_bitmap_pb;
1421 } else {
1422 /* regular bitmap */
1423 bitmap = &wps_data->img[n].bm;
1424 loaded = &wps_data->img[n].loaded;
1427 /* load the image */
1428 bitmap->data = wps_data->img_buf_ptr;
1429 if (load_bitmap(wps_data, img_path, bitmap))
1431 *loaded = true;
1433 /* Calculate and store height if this image has sub-images */
1434 if (n < MAX_IMAGES)
1435 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1436 wps_data->img[n].num_subimages;
1438 else
1440 /* Abort if we can't load an image */
1441 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1442 return false;
1447 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1448 if (bmp_names[BACKDROP_BMP])
1450 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1451 img_path, sizeof(img_path));
1453 #if defined(HAVE_REMOTE_LCD)
1454 /* We only need to check LCD type if there is a remote LCD */
1455 if (!wps_data->remote_wps)
1456 #endif
1458 /* Load backdrop for the main LCD */
1459 if (!load_wps_backdrop(img_path))
1460 return false;
1462 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1463 else
1465 /* Load backdrop for the remote LCD */
1466 if (!load_remote_wps_backdrop(img_path))
1467 return false;
1469 #endif
1471 #endif /* has backdrop support */
1473 /* If we got here, everything was OK */
1474 return true;
1477 #endif /* HAVE_LCD_BITMAP */
1479 /* Skip leading UTF-8 BOM, if present. */
1480 static char *skip_utf8_bom(char *buf)
1482 unsigned char *s = (unsigned char *)buf;
1484 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
1486 buf += 3;
1489 return buf;
1492 /* to setup up the wps-data from a format-buffer (isfile = false)
1493 from a (wps-)file (isfile = true)*/
1494 bool wps_data_load(struct wps_data *wps_data,
1495 struct screen *display,
1496 const char *buf,
1497 bool isfile)
1499 if (!wps_data || !buf)
1500 return false;
1502 wps_reset(wps_data);
1504 /* Initialise the first (default) viewport */
1505 wps_data->viewports[0].vp.x = 0;
1506 wps_data->viewports[0].vp.y = 0;
1507 wps_data->viewports[0].vp.width = display->width;
1508 wps_data->viewports[0].vp.height = display->height;
1509 #ifdef HAVE_LCD_BITMAP
1510 wps_data->viewports[0].vp.font = FONT_UI;
1511 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1512 #endif
1513 wps_data->viewports[0].vp.xmargin = display->getxmargin();
1514 wps_data->viewports[0].vp.ymargin = display->getymargin();
1515 #if LCD_DEPTH > 1
1516 if (display->depth > 1)
1518 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1519 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1521 #endif
1522 if (!isfile)
1524 return wps_parse(wps_data, buf);
1526 else
1529 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1530 * wants to be a virtual file. Feel free to modify dirbrowse()
1531 * if you're feeling brave.
1533 #ifndef __PCTOOL__
1534 if (! strcmp(buf, WPS_DEFAULTCFG) )
1536 global_settings.wps_file[0] = 0;
1537 return false;
1540 #ifdef HAVE_REMOTE_LCD
1541 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1543 global_settings.rwps_file[0] = 0;
1544 return false;
1546 #endif
1547 #endif /* __PCTOOL__ */
1549 int fd = open(buf, O_RDONLY);
1551 if (fd < 0)
1552 return false;
1554 /* get buffer space from the plugin buffer */
1555 size_t buffersize = 0;
1556 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1558 if (!wps_buffer)
1559 return false;
1561 /* copy the file's content to the buffer for parsing,
1562 ensuring that every line ends with a newline char. */
1563 unsigned int start = 0;
1564 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1566 start += strlen(wps_buffer + start);
1567 if (start < buffersize - 1)
1569 wps_buffer[start++] = '\n';
1570 wps_buffer[start] = 0;
1574 close(fd);
1576 if (start <= 0)
1577 return false;
1579 #ifdef HAVE_LCD_BITMAP
1580 /* Set all filename pointers to NULL */
1581 memset(bmp_names, 0, sizeof(bmp_names));
1582 #endif
1584 /* Skip leading UTF-8 BOM, if present. */
1585 wps_buffer = skip_utf8_bom(wps_buffer);
1587 /* parse the WPS source */
1588 if (!wps_parse(wps_data, wps_buffer)) {
1589 wps_reset(wps_data);
1590 return false;
1593 wps_data->wps_loaded = true;
1595 #ifdef HAVE_LCD_BITMAP
1596 /* get the bitmap dir */
1597 char bmpdir[MAX_PATH];
1598 size_t bmpdirlen;
1599 char *dot = strrchr(buf, '.');
1600 bmpdirlen = dot - buf;
1601 strncpy(bmpdir, buf, dot - buf);
1602 bmpdir[bmpdirlen] = 0;
1604 /* load the bitmaps that were found by the parsing */
1605 if (!load_wps_bitmaps(wps_data, bmpdir)) {
1606 wps_reset(wps_data);
1607 return false;
1609 #endif
1610 return true;
1614 int wps_subline_index(struct wps_data *data, int line, int subline)
1616 return data->lines[line].first_subline_idx + subline;
1619 int wps_first_token_index(struct wps_data *data, int line, int subline)
1621 int first_subline_idx = data->lines[line].first_subline_idx;
1622 return data->sublines[first_subline_idx + subline].first_token_idx;
1625 int wps_last_token_index(struct wps_data *data, int line, int subline)
1627 int first_subline_idx = data->lines[line].first_subline_idx;
1628 int idx = first_subline_idx + subline;
1629 if (idx < data->num_sublines - 1)
1631 /* This subline ends where the next begins */
1632 return data->sublines[idx+1].first_token_idx - 1;
1634 else
1636 /* The last subline goes to the end */
1637 return data->num_tokens - 1;