Fix wps image tags description.
[Rockbox.git] / apps / gui / wps_parser.c
blob9bbdb2e5d031d2b98a8bf550c59b63f0ed1e7a4e
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 "gwps.h"
23 #include "file.h"
24 #include "misc.h"
25 #ifdef __PCTOOL__
26 #define DEBUGF printf
27 #define FONT_SYSFIXED 0
28 #define FONT_UI 1
29 #else
30 #include "debug.h"
31 #endif
33 #ifndef __PCTOOL__
34 #include <ctype.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include "font.h"
41 #include "atoi.h"
42 #include "gwps.h"
43 #include "settings.h"
44 #include "plugin.h"
46 #ifdef HAVE_LCD_BITMAP
47 #include "bmp.h"
48 #endif
50 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
51 #include "backdrop.h"
52 #endif
54 #endif
56 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
57 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
59 #define WPS_ERROR_INVALID_PARAM -1
61 #define PARSE_FAIL_UNCLOSED_COND 1
62 #define PARSE_FAIL_INVALID_CHAR 2
63 #define PARSE_FAIL_COND_SYNTAX_ERROR 3
64 #define PARSE_FAIL_COND_INVALID_PARAM 4
66 /* level of current conditional.
67 -1 means we're not in a conditional. */
68 static int level = -1;
70 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
71 or WPS_TOKEN_CONDITIONAL_START in current level */
72 static int lastcond[WPS_MAX_COND_LEVEL];
74 /* index of the WPS_TOKEN_CONDITIONAL in current level */
75 static int condindex[WPS_MAX_COND_LEVEL];
77 /* number of condtional options in current level */
78 static int numoptions[WPS_MAX_COND_LEVEL];
80 /* the current line in the file */
81 static int line;
83 #ifdef HAVE_LCD_BITMAP
85 #if LCD_DEPTH > 1
86 #define MAX_BITMAPS (MAX_IMAGES+2) /* WPS images + pbar bitmap + backdrop */
87 #else
88 #define MAX_BITMAPS (MAX_IMAGES+1) /* WPS images + pbar bitmap */
89 #endif
91 #define PROGRESSBAR_BMP MAX_IMAGES
92 #define BACKDROP_BMP (MAX_IMAGES+1)
94 /* pointers to the bitmap filenames in the WPS source */
95 static const char *bmp_names[MAX_BITMAPS];
97 #endif /* HAVE_LCD_BITMAP */
99 #ifdef DEBUG
100 /* debugging function */
101 extern void print_debug_info(struct wps_data *data, int fail, int line);
102 #endif
104 static void wps_reset(struct wps_data *data);
106 /* Function for parsing of details for a token. At the moment the
107 function is called, the token type has already been set. The
108 function must fill in the details and possibly add more tokens
109 to the token array. It should return the number of chars that
110 has been consumed.
112 wps_bufptr points to the char following the tag (i.e. where
113 details begin).
114 token is the pointer to the 'main' token being parsed
116 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
117 struct wps_token *token, struct wps_data *wps_data);
119 struct wps_tag {
120 enum wps_token_type type;
121 const char name[3];
122 unsigned char refresh_type;
123 const wps_tag_parse_func parse_func;
126 /* prototypes of all special parse functions : */
127 static int parse_subline_timeout(const char *wps_bufptr,
128 struct wps_token *token, struct wps_data *wps_data);
129 static int parse_progressbar(const char *wps_bufptr,
130 struct wps_token *token, struct wps_data *wps_data);
131 static int parse_dir_level(const char *wps_bufptr,
132 struct wps_token *token, struct wps_data *wps_data);
134 #ifdef HAVE_LCD_BITMAP
135 static int parse_viewport(const char *wps_bufptr,
136 struct wps_token *token, struct wps_data *wps_data);
137 static int parse_leftmargin(const char *wps_bufptr,
138 struct wps_token *token, struct wps_data *wps_data);
139 static int parse_image_special(const char *wps_bufptr,
140 struct wps_token *token, struct wps_data *wps_data);
141 static int parse_statusbar_enable(const char *wps_bufptr,
142 struct wps_token *token, struct wps_data *wps_data);
143 static int parse_statusbar_disable(const char *wps_bufptr,
144 struct wps_token *token, struct wps_data *wps_data);
145 static int parse_image_display(const char *wps_bufptr,
146 struct wps_token *token, struct wps_data *wps_data);
147 static int parse_image_load(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 #endif /*HAVE_LCD_BITMAP */
150 #ifdef HAVE_ALBUMART
151 static int parse_albumart_load(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 static int parse_albumart_conditional(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 #endif /* HAVE_ALBUMART */
157 #ifdef CONFIG_RTC
158 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
159 #else
160 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
161 #endif
163 /* array of available tags - those with more characters have to go first
164 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
165 static const struct wps_tag all_tags[] = {
167 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
168 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
169 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
171 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
172 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
173 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
174 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
175 #if CONFIG_CHARGING >= CHARGING_MONITOR
176 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
177 #endif
178 #if CONFIG_CHARGING
179 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
180 #endif
182 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
183 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
184 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
185 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
186 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
187 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
188 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
189 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
190 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
191 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
192 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
193 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
194 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
197 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
201 /* current file */
202 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
203 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
204 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
205 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
206 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
207 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
208 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
209 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
210 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
211 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
212 parse_dir_level },
214 /* next file */
215 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
216 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
217 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
218 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
219 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
220 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
221 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
222 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
223 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
224 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
225 parse_dir_level },
227 /* current metadata */
228 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
241 /* next metadata */
242 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
243 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
244 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
245 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
246 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
247 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
248 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
249 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
250 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
251 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
252 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
253 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
255 #if (CONFIG_CODEC != MAS3507D)
256 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
257 #endif
259 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
260 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
261 #endif
263 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
265 #ifdef HAS_REMOTE_BUTTON_HOLD
266 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
267 #else
268 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
269 #endif
271 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
272 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
274 #ifdef HAVE_LCD_BITMAP
275 { WPS_TOKEN_LEFTMARGIN, "m", 0, parse_leftmargin },
276 #endif
278 #ifdef HAVE_LCD_BITMAP
279 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
280 #else
281 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
282 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
283 #endif
284 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
285 parse_progressbar },
287 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
289 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
290 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
291 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
292 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
294 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
295 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
296 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
297 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
299 #ifdef HAVE_TAGCACHE
300 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
301 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
302 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
303 #endif
305 #if CONFIG_CODEC == SWCODEC
306 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
307 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
308 #endif
310 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
311 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_subline_timeout },
313 #ifdef HAVE_LCD_BITMAP
314 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
315 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
317 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
319 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
320 parse_image_display },
322 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
323 { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special },
324 #ifdef HAVE_ALBUMART
325 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
326 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
327 parse_albumart_conditional },
328 #endif
330 { WPS_NO_TOKEN, "V", 0, parse_viewport },
332 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
333 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
334 #endif
335 #endif
337 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
338 /* the array MUST end with an empty string (first char is \0) */
341 /* Returns the number of chars that should be skipped to jump
342 immediately after the first eol, i.e. to the start of the next line */
343 static int skip_end_of_line(const char *wps_bufptr)
345 line++;
346 int skip = 0;
347 while(*(wps_bufptr + skip) != '\n')
348 skip++;
349 return ++skip;
352 /* Starts a new subline in the current line during parsing */
353 static void wps_start_new_subline(struct wps_data *data)
355 data->num_sublines++;
356 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
357 data->lines[data->num_lines].num_sublines++;
360 #ifdef HAVE_LCD_BITMAP
362 static int parse_statusbar_enable(const char *wps_bufptr,
363 struct wps_token *token,
364 struct wps_data *wps_data)
366 (void)token; /* Kill warnings */
367 wps_data->wps_sb_tag = true;
368 wps_data->show_sb_on_wps = true;
369 return skip_end_of_line(wps_bufptr);
372 static int parse_statusbar_disable(const char *wps_bufptr,
373 struct wps_token *token,
374 struct wps_data *wps_data)
376 (void)token; /* Kill warnings */
377 wps_data->wps_sb_tag = true;
378 wps_data->show_sb_on_wps = false;
379 return skip_end_of_line(wps_bufptr);
382 static bool load_bitmap(struct wps_data *wps_data,
383 char* filename,
384 struct bitmap *bm)
386 int format;
387 #ifdef HAVE_REMOTE_LCD
388 if (wps_data->remote_wps)
389 format = FORMAT_ANY|FORMAT_REMOTE;
390 else
391 #endif
392 format = FORMAT_ANY|FORMAT_TRANSPARENT;
394 int ret = read_bmp_file(filename, bm,
395 wps_data->img_buf_free,
396 format);
398 if (ret > 0)
400 #if LCD_DEPTH == 16
401 if (ret % 2) ret++;
402 /* Always consume an even number of bytes */
403 #endif
404 wps_data->img_buf_ptr += ret;
405 wps_data->img_buf_free -= ret;
407 return true;
409 else
410 return false;
413 static int get_image_id(int c)
415 if(c >= 'a' && c <= 'z')
416 return c - 'a';
417 else if(c >= 'A' && c <= 'Z')
418 return c - 'A' + 26;
419 else
420 return -1;
423 static char *get_image_filename(const char *start, const char* bmpdir,
424 char *buf, int buf_size)
426 const char *end = strchr(start, '|');
428 if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
430 buf = "\0";
431 return NULL;
434 int bmpdirlen = strlen(bmpdir);
436 strcpy(buf, bmpdir);
437 buf[bmpdirlen] = '/';
438 memcpy( &buf[bmpdirlen + 1], start, end - start);
439 buf[bmpdirlen + 1 + end - start] = 0;
441 return buf;
444 static int parse_image_display(const char *wps_bufptr,
445 struct wps_token *token,
446 struct wps_data *wps_data)
448 (void)wps_data;
449 int n = get_image_id(wps_bufptr[0]);
450 int subimage;
452 if (n == -1)
454 /* invalid picture display tag */
455 return WPS_ERROR_INVALID_PARAM;
458 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
460 /* Sanity check */
461 if (subimage >= wps_data->img[n].num_subimages)
462 return WPS_ERROR_INVALID_PARAM;
464 /* Store sub-image number to display in high bits */
465 token->value.i = n | (subimage << 8);
466 return 2; /* We have consumed 2 bytes */
467 } else {
468 token->value.i = n;
469 return 1; /* We have consumed 1 byte */
473 static int parse_image_load(const char *wps_bufptr,
474 struct wps_token *token,
475 struct wps_data *wps_data)
477 int n;
478 const char *ptr = wps_bufptr;
479 const char *pos;
480 const char* filename;
481 const char* id;
482 const char *newline;
483 int x,y;
485 /* format: %x|n|filename.bmp|x|y|
486 or %xl|n|filename.bmp|x|y|
487 or %xl|n|filename.bmp|x|y|num_subimages|
490 if (*ptr != '|')
491 return WPS_ERROR_INVALID_PARAM;
493 ptr++;
495 if (!(ptr = parse_list("ssdd", '|', ptr, &id, &filename, &x, &y)))
496 return WPS_ERROR_INVALID_PARAM;
498 /* Check there is a terminating | */
499 if (*ptr != '|')
500 return WPS_ERROR_INVALID_PARAM;
502 /* get the image ID */
503 n = get_image_id(*id);
505 /* check the image number and load state */
506 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
508 /* Invalid image ID */
509 return WPS_ERROR_INVALID_PARAM;
512 /* save a pointer to the filename */
513 bmp_names[n] = filename;
515 wps_data->img[n].x = x;
516 wps_data->img[n].y = y;
518 /* save current viewport */
519 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
521 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
523 wps_data->img[n].always_display = true;
525 else
527 /* Parse the (optional) number of sub-images */
528 ptr++;
529 newline = strchr(ptr, '\n');
530 pos = strchr(ptr, '|');
531 if (pos && pos < newline)
532 wps_data->img[n].num_subimages = atoi(ptr);
534 if (wps_data->img[n].num_subimages <= 0)
535 return WPS_ERROR_INVALID_PARAM;
538 /* Skip the rest of the line */
539 return skip_end_of_line(wps_bufptr);
542 static int parse_viewport(const char *wps_bufptr,
543 struct wps_token *token,
544 struct wps_data *wps_data)
546 const char *ptr = wps_bufptr;
547 struct viewport* vp;
548 int depth;
550 (void)token; /* Kill warnings */
552 if (*wps_bufptr != '|')
553 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
555 ptr = wps_bufptr + 1;
556 /* format: %V|x|y|width|height|fg_pattern|bg_pattern| */
558 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
559 return WPS_ERROR_INVALID_PARAM;
561 wps_data->num_viewports++;
562 vp = &wps_data->viewports[wps_data->num_viewports].vp;
564 /* Set the defaults for fields not user-specified */
565 vp->drawmode = DRMODE_SOLID;
566 vp->xmargin = 0;
567 vp->ymargin = 0;
569 /* Work out the depth of this display */
570 #ifdef HAVE_REMOTE_LCD
571 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
572 #else
573 depth = LCD_DEPTH;
574 #endif
576 #ifdef HAVE_LCD_COLOR
577 if (depth == 16)
579 if (!(ptr = parse_list("dddddcc", '|', ptr, &vp->x, &vp->y, &vp->width,
580 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
581 return WPS_ERROR_INVALID_PARAM;
583 else
584 #endif
585 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
586 if (depth == 2) {
587 if (!(ptr = parse_list("dddddgg", '|', ptr, &vp->x, &vp->y, &vp->width,
588 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
589 return WPS_ERROR_INVALID_PARAM;
591 else
592 #endif
593 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
594 if (depth == 1)
596 if (!(ptr = parse_list("ddddd", '|', ptr, &vp->x, &vp->y, &vp->width,
597 &vp->height, &vp->font)))
598 return WPS_ERROR_INVALID_PARAM;
600 else
601 #endif
604 /* Check for trailing | */
605 if (*ptr != '|')
606 return WPS_ERROR_INVALID_PARAM;
608 /* Default to using the user font if the font was an invalid number */
609 if ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI))
610 vp->font = FONT_UI;
612 /* Validate the viewport dimensions - we know that the numbers are
613 non-negative integers */
614 #ifdef HAVE_REMOTE_LCD
615 if (wps_data->remote_wps)
617 if ((vp->x >= LCD_REMOTE_WIDTH) ||
618 ((vp->x + vp->width) > LCD_REMOTE_WIDTH) ||
619 (vp->y >= LCD_REMOTE_HEIGHT) ||
620 ((vp->y + vp->height) > LCD_REMOTE_HEIGHT))
622 return WPS_ERROR_INVALID_PARAM;
625 else
626 #endif
628 if ((vp->x >= LCD_WIDTH) ||
629 ((vp->x + vp->width) > LCD_WIDTH) ||
630 (vp->y >= LCD_HEIGHT) ||
631 ((vp->y + vp->height) > LCD_HEIGHT))
633 return WPS_ERROR_INVALID_PARAM;
637 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
639 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
641 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
643 wps_data->lines[wps_data->num_lines].first_subline_idx =
644 wps_data->num_sublines;
646 wps_data->sublines[wps_data->num_sublines].first_token_idx =
647 wps_data->num_tokens;
650 /* Skip the rest of the line */
651 return skip_end_of_line(wps_bufptr);
655 static int parse_image_special(const char *wps_bufptr,
656 struct wps_token *token,
657 struct wps_data *wps_data)
659 (void)wps_data; /* kill warning */
660 const char *pos = NULL;
661 const char *newline;
663 pos = strchr(wps_bufptr + 1, '|');
664 newline = strchr(wps_bufptr, '\n');
666 if (pos > newline)
667 return WPS_ERROR_INVALID_PARAM;
669 if (token->type == WPS_TOKEN_IMAGE_PROGRESS_BAR)
671 /* format: %P|filename.bmp| */
672 bmp_names[PROGRESSBAR_BMP] = wps_bufptr + 1;
674 #if LCD_DEPTH > 1
675 else if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
677 /* format: %X|filename.bmp| */
678 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
680 #endif
682 /* Skip the rest of the line */
683 return skip_end_of_line(wps_bufptr);
686 #endif /* HAVE_LCD_BITMAP */
688 static int parse_dir_level(const char *wps_bufptr,
689 struct wps_token *token,
690 struct wps_data *wps_data)
692 char val[] = { *wps_bufptr, '\0' };
693 token->value.i = atoi(val);
694 (void)wps_data; /* Kill warnings */
695 return 1;
698 static int parse_subline_timeout(const char *wps_bufptr,
699 struct wps_token *token,
700 struct wps_data *wps_data)
702 int skip = 0;
703 int val = 0;
704 bool have_point = false;
705 bool have_tenth = false;
707 (void)wps_data; /* Kill the warning */
709 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
711 if (*wps_bufptr != '.')
713 val *= 10;
714 val += *wps_bufptr - '0';
715 if (have_point)
717 have_tenth = true;
718 wps_bufptr++;
719 skip++;
720 break;
723 else
724 have_point = true;
726 wps_bufptr++;
727 skip++;
730 if (have_tenth == false)
731 val *= 10;
733 token->value.i = val;
735 return skip;
738 static int parse_progressbar(const char *wps_bufptr,
739 struct wps_token *token,
740 struct wps_data *wps_data)
742 (void)token; /* Kill warnings */
743 #ifdef HAVE_LCD_BITMAP
745 short *vals[] = {
746 &wps_data->progress_height,
747 &wps_data->progress_start,
748 &wps_data->progress_end,
749 &wps_data->progress_top };
751 /* default values : */
752 wps_data->progress_height = 6;
753 wps_data->progress_start = 0;
754 wps_data->progress_end = 0;
755 wps_data->progress_top = -1;
757 int i = 0;
758 char *newline = strchr(wps_bufptr, '\n');
759 char *prev = strchr(wps_bufptr, '|');
760 if (prev && prev < newline) {
761 char *next = strchr(prev+1, '|');
762 while (i < 4 && next && next < newline)
764 *(vals[i++]) = atoi(++prev);
765 prev = strchr(prev, '|');
766 next = strchr(++next, '|');
769 if (wps_data->progress_height < 3)
770 wps_data->progress_height = 3;
771 if (wps_data->progress_end < wps_data->progress_start + 3)
772 wps_data->progress_end = 0;
775 return newline - wps_bufptr;
777 #else
779 if (*(wps_bufptr-1) == 'f')
780 wps_data->full_line_progressbar = true;
781 else
782 wps_data->full_line_progressbar = false;
784 return 0;
786 #endif
789 #ifdef HAVE_ALBUMART
790 static int parse_albumart_load(const char *wps_bufptr,
791 struct wps_token *token,
792 struct wps_data *wps_data)
794 const char *_pos, *newline;
795 bool parsing;
796 const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
797 WPS_ALBUMART_ALIGN_CENTER |
798 WPS_ALBUMART_ALIGN_RIGHT;
799 const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
800 WPS_ALBUMART_ALIGN_CENTER |
801 WPS_ALBUMART_ALIGN_BOTTOM;
803 (void)token; /* silence warning */
805 /* reset albumart info in wps */
806 wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
807 wps_data->albumart_max_width = -1;
808 wps_data->albumart_max_height = -1;
809 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
810 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
812 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
814 newline = strchr(wps_bufptr, '\n');
816 /* initial validation and parsing of x and y components */
817 if (*wps_bufptr != '|')
818 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
820 _pos = wps_bufptr + 1;
821 if (!isdigit(*_pos))
822 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
823 wps_data->albumart_x = atoi(_pos);
825 _pos = strchr(_pos, '|');
826 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
827 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
829 wps_data->albumart_y = atoi(_pos);
831 _pos = strchr(_pos, '|');
832 if (!_pos || _pos > newline)
833 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
834 e.g. %Cl|7|59\n */
836 /* parsing width field */
837 parsing = true;
838 while (parsing)
840 /* apply each modifier in turn */
841 ++_pos;
842 switch (*_pos)
844 case 'l':
845 case 'L':
846 case '+':
847 wps_data->albumart_xalign =
848 (wps_data->albumart_xalign & xalign_mask) |
849 WPS_ALBUMART_ALIGN_LEFT;
850 break;
851 case 'c':
852 case 'C':
853 wps_data->albumart_xalign =
854 (wps_data->albumart_xalign & xalign_mask) |
855 WPS_ALBUMART_ALIGN_CENTER;
856 break;
857 case 'r':
858 case 'R':
859 case '-':
860 wps_data->albumart_xalign =
861 (wps_data->albumart_xalign & xalign_mask) |
862 WPS_ALBUMART_ALIGN_RIGHT;
863 break;
864 case 'd':
865 case 'D':
866 wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
867 break;
868 case 'i':
869 case 'I':
870 wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
871 break;
872 case 's':
873 case 'S':
874 wps_data->albumart_xalign |=
875 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
876 break;
877 default:
878 parsing = false;
879 break;
882 /* extract max width data */
883 if (*_pos != '|')
885 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
886 return WPS_ERROR_INVALID_PARAM;
888 wps_data->albumart_max_width = atoi(_pos);
890 _pos = strchr(_pos, '|');
891 if (!_pos || _pos > newline)
892 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
893 e.g. %Cl|7|59|200\n */
896 /* parsing height field */
897 parsing = true;
898 while (parsing)
900 /* apply each modifier in turn */
901 ++_pos;
902 switch (*_pos)
904 case 't':
905 case 'T':
906 case '-':
907 wps_data->albumart_yalign =
908 (wps_data->albumart_yalign & yalign_mask) |
909 WPS_ALBUMART_ALIGN_TOP;
910 break;
911 case 'c':
912 case 'C':
913 wps_data->albumart_yalign =
914 (wps_data->albumart_yalign & yalign_mask) |
915 WPS_ALBUMART_ALIGN_CENTER;
916 break;
917 case 'b':
918 case 'B':
919 case '+':
920 wps_data->albumart_yalign =
921 (wps_data->albumart_yalign & yalign_mask) |
922 WPS_ALBUMART_ALIGN_BOTTOM;
923 break;
924 case 'd':
925 case 'D':
926 wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
927 break;
928 case 'i':
929 case 'I':
930 wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
931 break;
932 case 's':
933 case 'S':
934 wps_data->albumart_yalign |=
935 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
936 break;
937 default:
938 parsing = false;
939 break;
942 /* extract max height data */
943 if (*_pos != '|')
945 if (!isdigit(*_pos))
946 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
948 wps_data->albumart_max_height = atoi(_pos);
950 _pos = strchr(_pos, '|');
951 if (!_pos || _pos > newline)
952 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
953 e.g. %Cl|7|59|200|200\n */
956 /* if we got here, we parsed everything ok .. ! */
957 if (wps_data->albumart_max_width < 0)
958 wps_data->albumart_max_width = 0;
959 else if (wps_data->albumart_max_width > LCD_WIDTH)
960 wps_data->albumart_max_width = LCD_WIDTH;
962 if (wps_data->albumart_max_height < 0)
963 wps_data->albumart_max_height = 0;
964 else if (wps_data->albumart_max_height > LCD_HEIGHT)
965 wps_data->albumart_max_height = LCD_HEIGHT;
967 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
969 /* Skip the rest of the line */
970 return skip_end_of_line(wps_bufptr);
973 static int parse_albumart_conditional(const char *wps_bufptr,
974 struct wps_token *token,
975 struct wps_data *wps_data)
977 struct wps_token *prevtoken = token;
978 --prevtoken;
979 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
981 /* This %C is part of a %?C construct.
982 It's either %?C<blah> or %?Cn<blah> */
983 token->type = WPS_TOKEN_ALBUMART_FOUND;
984 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
986 token->next = true;
987 return 1;
989 else if (*wps_bufptr == '<')
991 return 0;
993 else
995 token->type = WPS_NO_TOKEN;
996 return 0;
999 else
1001 /* This %C tag is in a conditional construct. */
1002 wps_data->albumart_cond_index = condindex[level];
1003 return 0;
1006 #endif /* HAVE_ALBUMART */
1008 #ifdef HAVE_LCD_BITMAP
1009 static int parse_leftmargin(const char *wps_bufptr, struct wps_token *token,
1010 struct wps_data *wps_data)
1012 const char* p;
1013 const char* pend;
1014 const char *newline;
1016 (void)wps_data; /* Kill the warning */
1018 /* valid tag looks like %m|12| */
1019 if(*wps_bufptr == '|')
1021 p = wps_bufptr + 1;
1022 newline = strchr(wps_bufptr, '\n');
1023 if(isdigit(*p) && (pend = strchr(p, '|')) && pend < newline)
1025 token->value.i = atoi(p);
1026 return pend - wps_bufptr + 1;
1030 /* invalid tag syntax */
1031 return WPS_ERROR_INVALID_PARAM;
1033 #endif
1036 /* Parse a generic token from the given string. Return the length read */
1037 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1039 int skip = 0, taglen = 0, ret;
1040 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1041 const struct wps_tag *tag;
1043 switch(*wps_bufptr)
1046 case '%':
1047 case '<':
1048 case '|':
1049 case '>':
1050 case ';':
1051 case '#':
1052 /* escaped characters */
1053 token->type = WPS_TOKEN_CHARACTER;
1054 token->value.c = *wps_bufptr;
1055 taglen = 1;
1056 wps_data->num_tokens++;
1057 break;
1059 case '?':
1060 /* conditional tag */
1061 token->type = WPS_TOKEN_CONDITIONAL;
1062 level++;
1063 condindex[level] = wps_data->num_tokens;
1064 numoptions[level] = 1;
1065 wps_data->num_tokens++;
1066 ret = parse_token(wps_bufptr + 1, wps_data);
1067 if (ret < 0) return ret;
1068 taglen = 1 + ret;
1069 break;
1071 default:
1072 /* find what tag we have */
1073 for (tag = all_tags;
1074 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1075 tag++) ;
1077 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1078 token->type = tag->type;
1079 wps_data->sublines[wps_data->num_sublines].line_type |=
1080 tag->refresh_type;
1082 /* if the tag has a special parsing function, we call it */
1083 if (tag->parse_func)
1085 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1086 if (ret < 0) return ret;
1087 skip += ret;
1090 /* Some tags we don't want to save as tokens */
1091 if (tag->type == WPS_NO_TOKEN)
1092 break;
1094 /* tags that start with 'F', 'I' or 'D' are for the next file */
1095 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1096 *(tag->name) == 'D')
1097 token->next = true;
1099 wps_data->num_tokens++;
1100 break;
1103 skip += taglen;
1104 return skip;
1107 /* Parses the WPS.
1108 data is the pointer to the structure where the parsed WPS should be stored.
1109 It is initialised.
1110 wps_bufptr points to the string containing the WPS tags */
1111 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1113 if (!data || !wps_bufptr || !*wps_bufptr)
1114 return false;
1116 char *stringbuf = data->string_buffer;
1117 int stringbuf_used = 0;
1118 int fail = 0;
1119 int ret;
1120 line = 1;
1121 level = -1;
1123 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1124 && data->num_viewports < WPS_MAX_VIEWPORTS
1125 && data->num_lines < WPS_MAX_LINES)
1127 switch(*wps_bufptr++)
1130 /* Regular tag */
1131 case '%':
1132 if ((ret = parse_token(wps_bufptr, data)) < 0)
1134 fail = PARSE_FAIL_COND_INVALID_PARAM;
1135 break;
1137 wps_bufptr += ret;
1138 break;
1140 /* Alternating sublines separator */
1141 case ';':
1142 if (level >= 0) /* there are unclosed conditionals */
1144 fail = PARSE_FAIL_UNCLOSED_COND;
1145 break;
1148 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1149 wps_start_new_subline(data);
1150 else
1151 wps_bufptr += skip_end_of_line(wps_bufptr);
1153 break;
1155 /* Conditional list start */
1156 case '<':
1157 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1159 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1160 break;
1163 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1164 lastcond[level] = data->num_tokens++;
1165 break;
1167 /* Conditional list end */
1168 case '>':
1169 if (level < 0) /* not in a conditional, invalid char */
1171 fail = PARSE_FAIL_INVALID_CHAR;
1172 break;
1175 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1176 if (lastcond[level])
1177 data->tokens[lastcond[level]].value.i = data->num_tokens;
1178 else
1180 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1181 break;
1184 lastcond[level] = 0;
1185 data->num_tokens++;
1186 data->tokens[condindex[level]].value.i = numoptions[level];
1187 level--;
1188 break;
1190 /* Conditional list option */
1191 case '|':
1192 if (level < 0) /* not in a conditional, invalid char */
1194 fail = PARSE_FAIL_INVALID_CHAR;
1195 break;
1198 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1199 if (lastcond[level])
1200 data->tokens[lastcond[level]].value.i = data->num_tokens;
1201 else
1203 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1204 break;
1207 lastcond[level] = data->num_tokens;
1208 numoptions[level]++;
1209 data->num_tokens++;
1210 break;
1212 /* Comment */
1213 case '#':
1214 if (level >= 0) /* there are unclosed conditionals */
1216 fail = PARSE_FAIL_UNCLOSED_COND;
1217 break;
1220 wps_bufptr += skip_end_of_line(wps_bufptr);
1221 break;
1223 /* End of this line */
1224 case '\n':
1225 if (level >= 0) /* there are unclosed conditionals */
1227 fail = PARSE_FAIL_UNCLOSED_COND;
1228 break;
1231 line++;
1232 wps_start_new_subline(data);
1233 data->num_lines++; /* Start a new line */
1235 if ((data->num_lines < WPS_MAX_LINES) &&
1236 (data->num_sublines < WPS_MAX_SUBLINES))
1238 data->lines[data->num_lines].first_subline_idx =
1239 data->num_sublines;
1241 data->sublines[data->num_sublines].first_token_idx =
1242 data->num_tokens;
1245 break;
1247 /* String */
1248 default:
1250 unsigned int len = 1;
1251 const char *string_start = wps_bufptr - 1;
1253 /* find the length of the string */
1254 while (*wps_bufptr && *wps_bufptr != '#' &&
1255 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1256 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1257 *wps_bufptr != '|' && *wps_bufptr != '\n')
1259 wps_bufptr++;
1260 len++;
1263 /* look if we already have that string */
1264 char **str;
1265 int i;
1266 bool found;
1267 for (i = 0, str = data->strings, found = false;
1268 i < data->num_strings &&
1269 !(found = (strlen(*str) == len &&
1270 strncmp(string_start, *str, len) == 0));
1271 i++, str++);
1272 /* If a matching string is found, found is true and i is
1273 the index of the string. If not, found is false */
1275 /* If it's NOT a duplicate, do nothing if we already have
1276 too many unique strings */
1277 if (found ||
1278 (stringbuf_used < STRING_BUFFER_SIZE - 1 &&
1279 data->num_strings < WPS_MAX_STRINGS))
1281 if (!found)
1283 /* new string */
1285 /* truncate? */
1286 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1)
1287 len = STRING_BUFFER_SIZE - stringbuf_used - 1;
1289 strncpy(stringbuf, string_start, len);
1290 *(stringbuf + len) = '\0';
1292 data->strings[data->num_strings] = stringbuf;
1293 stringbuf += len + 1;
1294 stringbuf_used += len + 1;
1295 data->tokens[data->num_tokens].value.i =
1296 data->num_strings;
1297 data->num_strings++;
1299 else
1301 /* another ocurrence of an existing string */
1302 data->tokens[data->num_tokens].value.i = i;
1304 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1305 data->num_tokens++;
1308 break;
1312 if (!fail && level >= 0) /* there are unclosed conditionals */
1313 fail = PARSE_FAIL_UNCLOSED_COND;
1315 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1317 /* We have finished with the last viewport, so increment count */
1318 data->num_viewports++;
1320 #ifdef DEBUG
1321 print_debug_info(data, fail, line);
1322 #endif
1324 if (fail)
1325 wps_reset(data);
1327 return (fail == 0);
1330 #ifdef HAVE_LCD_BITMAP
1331 /* Clear the WPS image cache */
1332 static void wps_images_clear(struct wps_data *data)
1334 int i;
1335 /* set images to unloaded and not displayed */
1336 for (i = 0; i < MAX_IMAGES; i++)
1338 data->img[i].loaded = false;
1339 data->img[i].display = -1;
1340 data->img[i].always_display = false;
1341 data->img[i].num_subimages = 1;
1343 data->progressbar.have_bitmap_pb = false;
1345 #endif
1347 /* initial setup of wps_data */
1348 void wps_data_init(struct wps_data *wps_data)
1350 #ifdef HAVE_LCD_BITMAP
1351 wps_images_clear(wps_data);
1352 wps_data->wps_sb_tag = false;
1353 wps_data->show_sb_on_wps = false;
1354 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1355 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1356 wps_data->peak_meter_enabled = false;
1357 #else /* HAVE_LCD_CHARCELLS */
1358 int i;
1359 for (i = 0; i < 8; i++)
1361 wps_data->wps_progress_pat[i] = 0;
1363 wps_data->full_line_progressbar = false;
1364 #endif
1365 wps_data->wps_loaded = false;
1368 static void wps_reset(struct wps_data *data)
1370 #ifdef HAVE_REMOTE_LCD
1371 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1372 #endif
1373 memset(data, 0, sizeof(*data));
1374 #ifdef HAVE_ALBUMART
1375 data->wps_uses_albumart = WPS_ALBUMART_NONE;
1376 #endif
1377 wps_data_init(data);
1378 #ifdef HAVE_REMOTE_LCD
1379 data->remote_wps = rwps;
1380 #endif
1383 #ifdef HAVE_LCD_BITMAP
1385 static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1387 char img_path[MAX_PATH];
1388 struct bitmap *bitmap;
1389 bool *loaded;
1390 int n;
1391 for (n = 0; n < BACKDROP_BMP; n++)
1393 if (bmp_names[n])
1395 get_image_filename(bmp_names[n], bmpdir,
1396 img_path, sizeof(img_path));
1398 if (n == PROGRESSBAR_BMP) {
1399 /* progressbar bitmap */
1400 bitmap = &wps_data->progressbar.bm;
1401 loaded = &wps_data->progressbar.have_bitmap_pb;
1402 } else {
1403 /* regular bitmap */
1404 bitmap = &wps_data->img[n].bm;
1405 loaded = &wps_data->img[n].loaded;
1408 /* load the image */
1409 bitmap->data = wps_data->img_buf_ptr;
1410 if (load_bitmap(wps_data, img_path, bitmap))
1412 *loaded = true;
1414 /* Calculate and store height if this image has sub-images */
1415 if (n < MAX_IMAGES)
1416 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1417 wps_data->img[n].num_subimages;
1419 else
1421 /* Abort if we can't load an image */
1422 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1423 return false;
1428 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
1429 if (bmp_names[BACKDROP_BMP])
1431 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1432 img_path, sizeof(img_path));
1434 #if defined(HAVE_REMOTE_LCD)
1435 /* We only need to check LCD type if there is a remote LCD */
1436 if (!wps_data->remote_wps)
1437 #endif
1439 /* Load backdrop for the main LCD */
1440 if (!load_wps_backdrop(img_path))
1441 return false;
1443 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1444 else
1446 /* Load backdrop for the remote LCD */
1447 if (!load_remote_wps_backdrop(img_path))
1448 return false;
1450 #endif
1452 #endif /* has backdrop support */
1454 /* If we got here, everything was OK */
1455 return true;
1458 #endif /* HAVE_LCD_BITMAP */
1460 /* Skip leading UTF-8 BOM, if present. */
1461 static char *skip_utf8_bom(char *buf)
1463 unsigned char *s = (unsigned char *)buf;
1465 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
1467 buf += 3;
1470 return buf;
1473 /* to setup up the wps-data from a format-buffer (isfile = false)
1474 from a (wps-)file (isfile = true)*/
1475 bool wps_data_load(struct wps_data *wps_data,
1476 struct screen *display,
1477 const char *buf,
1478 bool isfile)
1480 if (!wps_data || !buf)
1481 return false;
1483 wps_reset(wps_data);
1485 /* Initialise the first (default) viewport */
1486 wps_data->viewports[0].vp.x = 0;
1487 wps_data->viewports[0].vp.y = 0;
1488 wps_data->viewports[0].vp.width = display->width;
1489 wps_data->viewports[0].vp.height = display->height;
1490 #ifdef HAVE_LCD_BITMAP
1491 wps_data->viewports[0].vp.font = FONT_UI;
1492 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1493 #endif
1494 wps_data->viewports[0].vp.xmargin = display->getxmargin();
1495 wps_data->viewports[0].vp.ymargin = display->getymargin();
1496 #if LCD_DEPTH > 1
1497 if (display->depth > 1)
1499 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1500 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1502 #endif
1503 if (!isfile)
1505 return wps_parse(wps_data, buf);
1507 else
1510 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1511 * wants to be a virtual file. Feel free to modify dirbrowse()
1512 * if you're feeling brave.
1514 #ifndef __PCTOOL__
1515 if (! strcmp(buf, WPS_DEFAULTCFG) )
1517 global_settings.wps_file[0] = 0;
1518 return false;
1521 #ifdef HAVE_REMOTE_LCD
1522 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1524 global_settings.rwps_file[0] = 0;
1525 return false;
1527 #endif
1528 #endif /* __PCTOOL__ */
1530 int fd = open(buf, O_RDONLY);
1532 if (fd < 0)
1533 return false;
1535 /* get buffer space from the plugin buffer */
1536 size_t buffersize = 0;
1537 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1539 if (!wps_buffer)
1540 return false;
1542 /* copy the file's content to the buffer for parsing,
1543 ensuring that every line ends with a newline char. */
1544 unsigned int start = 0;
1545 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1547 start += strlen(wps_buffer + start);
1548 if (start < buffersize - 1)
1550 wps_buffer[start++] = '\n';
1551 wps_buffer[start] = 0;
1555 close(fd);
1557 if (start <= 0)
1558 return false;
1560 #ifdef HAVE_LCD_BITMAP
1561 /* Set all filename pointers to NULL */
1562 memset(bmp_names, 0, sizeof(bmp_names));
1563 #endif
1565 /* Skip leading UTF-8 BOM, if present. */
1566 wps_buffer = skip_utf8_bom(wps_buffer);
1568 /* parse the WPS source */
1569 if (!wps_parse(wps_data, wps_buffer))
1570 return false;
1572 wps_data->wps_loaded = true;
1574 #ifdef HAVE_LCD_BITMAP
1575 /* get the bitmap dir */
1576 char bmpdir[MAX_PATH];
1577 size_t bmpdirlen;
1578 char *dot = strrchr(buf, '.');
1579 bmpdirlen = dot - buf;
1580 strncpy(bmpdir, buf, dot - buf);
1581 bmpdir[bmpdirlen] = 0;
1583 /* load the bitmaps that were found by the parsing */
1584 if (!load_wps_bitmaps(wps_data, bmpdir))
1585 return false;
1586 #endif
1587 return true;
1591 int wps_subline_index(struct wps_data *data, int line, int subline)
1593 return data->lines[line].first_subline_idx + subline;
1596 int wps_first_token_index(struct wps_data *data, int line, int subline)
1598 int first_subline_idx = data->lines[line].first_subline_idx;
1599 return data->sublines[first_subline_idx + subline].first_token_idx;
1602 int wps_last_token_index(struct wps_data *data, int line, int subline)
1604 int first_subline_idx = data->lines[line].first_subline_idx;
1605 int idx = first_subline_idx + subline;
1606 if (idx < data->num_sublines - 1)
1608 /* This subline ends where the next begins */
1609 return data->sublines[idx+1].first_token_idx - 1;
1611 else
1613 /* The last subline goes to the end */
1614 return data->num_tokens - 1;