autodetection: convert path to native separators before displaying it.
[Rockbox.git] / apps / gui / wps_parser.c
blobfe43272bb046e6376af5466ed4307c9ff9a26036
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 #ifdef __PCTOOL__
27 #define DEBUGF printf
28 #define FONT_SYSFIXED 0
29 #define FONT_UI 1
30 #else
31 #include "debug.h"
32 #endif
34 #ifndef __PCTOOL__
35 #include <ctype.h>
36 #include <stdbool.h>
37 #include <string.h>
38 #include "font.h"
40 #include "gwps.h"
41 #include "settings.h"
42 #include "plugin.h"
44 #ifdef HAVE_LCD_BITMAP
45 #include "bmp.h"
46 #endif
48 #include "backdrop.h"
50 #endif
52 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
53 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
55 #define WPS_ERROR_INVALID_PARAM -1
57 #define PARSE_FAIL_UNCLOSED_COND 1
58 #define PARSE_FAIL_INVALID_CHAR 2
59 #define PARSE_FAIL_COND_SYNTAX_ERROR 3
60 #define PARSE_FAIL_COND_INVALID_PARAM 4
62 /* level of current conditional.
63 -1 means we're not in a conditional. */
64 static int level = -1;
66 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
67 or WPS_TOKEN_CONDITIONAL_START in current level */
68 static int lastcond[WPS_MAX_COND_LEVEL];
70 /* index of the WPS_TOKEN_CONDITIONAL in current level */
71 static int condindex[WPS_MAX_COND_LEVEL];
73 /* number of condtional options in current level */
74 static int numoptions[WPS_MAX_COND_LEVEL];
76 /* the current line in the file */
77 static int line;
79 #ifdef HAVE_LCD_BITMAP
81 #if LCD_DEPTH > 1
82 #define MAX_BITMAPS (MAX_IMAGES+2) /* WPS images + pbar bitmap + backdrop */
83 #else
84 #define MAX_BITMAPS (MAX_IMAGES+1) /* WPS images + pbar bitmap */
85 #endif
87 #define PROGRESSBAR_BMP MAX_IMAGES
88 #define BACKDROP_BMP (MAX_IMAGES+1)
90 /* pointers to the bitmap filenames in the WPS source */
91 static const char *bmp_names[MAX_BITMAPS];
93 #endif /* HAVE_LCD_BITMAP */
95 #ifdef DEBUG
96 /* debugging function */
97 extern void print_debug_info(struct wps_data *data, int fail, int line);
98 #endif
100 static void wps_reset(struct wps_data *data);
102 /* Function for parsing of details for a token. At the moment the
103 function is called, the token type has already been set. The
104 function must fill in the details and possibly add more tokens
105 to the token array. It should return the number of chars that
106 has been consumed.
108 wps_bufptr points to the char following the tag (i.e. where
109 details begin).
110 token is the pointer to the 'main' token being parsed
112 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
113 struct wps_token *token, struct wps_data *wps_data);
115 struct wps_tag {
116 enum wps_token_type type;
117 const char name[3];
118 unsigned char refresh_type;
119 const wps_tag_parse_func parse_func;
122 /* prototypes of all special parse functions : */
123 static int parse_subline_timeout(const char *wps_bufptr,
124 struct wps_token *token, struct wps_data *wps_data);
125 static int parse_progressbar(const char *wps_bufptr,
126 struct wps_token *token, struct wps_data *wps_data);
127 static int parse_dir_level(const char *wps_bufptr,
128 struct wps_token *token, struct wps_data *wps_data);
130 #ifdef HAVE_LCD_BITMAP
131 static int parse_viewport(const char *wps_bufptr,
132 struct wps_token *token, struct wps_data *wps_data);
133 static int parse_leftmargin(const char *wps_bufptr,
134 struct wps_token *token, struct wps_data *wps_data);
135 static int parse_image_special(const char *wps_bufptr,
136 struct wps_token *token, struct wps_data *wps_data);
137 static int parse_statusbar_enable(const char *wps_bufptr,
138 struct wps_token *token, struct wps_data *wps_data);
139 static int parse_statusbar_disable(const char *wps_bufptr,
140 struct wps_token *token, struct wps_data *wps_data);
141 static int parse_image_display(const char *wps_bufptr,
142 struct wps_token *token, struct wps_data *wps_data);
143 static int parse_image_load(const char *wps_bufptr,
144 struct wps_token *token, struct wps_data *wps_data);
145 #endif /*HAVE_LCD_BITMAP */
146 #ifdef HAVE_ALBUMART
147 static int parse_albumart_load(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 static int parse_albumart_conditional(const char *wps_bufptr,
150 struct wps_token *token, struct wps_data *wps_data);
151 #endif /* HAVE_ALBUMART */
153 #ifdef CONFIG_RTC
154 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
155 #else
156 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
157 #endif
159 /* array of available tags - those with more characters have to go first
160 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
161 static const struct wps_tag all_tags[] = {
163 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
164 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
165 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
167 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
168 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
169 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
170 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
171 #if CONFIG_CHARGING >= CHARGING_MONITOR
172 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
173 #endif
174 #if CONFIG_CHARGING
175 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
176 #endif
178 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
179 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
180 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
181 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
182 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
183 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
184 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
185 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
186 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
187 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
188 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
189 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
190 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
191 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
192 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
193 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
194 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
197 /* current file */
198 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
199 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
200 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
201 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
202 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
203 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
204 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
205 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
206 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
207 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
208 parse_dir_level },
210 /* next file */
211 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
212 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
213 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
214 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
215 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
216 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
217 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
218 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
219 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
220 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
221 parse_dir_level },
223 /* current metadata */
224 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
237 /* next metadata */
238 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
239 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
240 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
241 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
242 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
243 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
244 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
245 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
246 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
247 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
248 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
249 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
251 #if (CONFIG_CODEC != MAS3507D)
252 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
253 #endif
255 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
256 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
257 #endif
259 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
261 #ifdef HAS_REMOTE_BUTTON_HOLD
262 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
263 #else
264 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
265 #endif
267 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
268 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
270 #ifdef HAVE_LCD_BITMAP
271 { WPS_TOKEN_LEFTMARGIN, "m", 0, parse_leftmargin },
272 #endif
274 #ifdef HAVE_LCD_BITMAP
275 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
276 #else
277 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
278 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
279 #endif
280 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
281 parse_progressbar },
283 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
285 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
286 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
287 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
288 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
290 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
292 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
293 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
295 #ifdef HAVE_TAGCACHE
296 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
297 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
298 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
299 #endif
301 #if CONFIG_CODEC == SWCODEC
302 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
303 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
304 #endif
306 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
307 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_subline_timeout },
309 #ifdef HAVE_LCD_BITMAP
310 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
311 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
313 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
315 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
316 parse_image_display },
318 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
319 { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special },
320 #ifdef HAVE_ALBUMART
321 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
322 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
323 parse_albumart_conditional },
324 #endif
326 { WPS_NO_TOKEN, "V", 0, parse_viewport },
328 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
329 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
330 #endif
331 #endif
333 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
334 /* the array MUST end with an empty string (first char is \0) */
337 /* Returns the number of chars that should be skipped to jump
338 immediately after the first eol, i.e. to the start of the next line */
339 static int skip_end_of_line(const char *wps_bufptr)
341 line++;
342 int skip = 0;
343 while(*(wps_bufptr + skip) != '\n')
344 skip++;
345 return ++skip;
348 /* Starts a new subline in the current line during parsing */
349 static void wps_start_new_subline(struct wps_data *data)
351 data->num_sublines++;
352 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
353 data->lines[data->num_lines].num_sublines++;
356 #ifdef HAVE_LCD_BITMAP
358 static int parse_statusbar_enable(const char *wps_bufptr,
359 struct wps_token *token,
360 struct wps_data *wps_data)
362 (void)token; /* Kill warnings */
363 wps_data->wps_sb_tag = true;
364 wps_data->show_sb_on_wps = true;
365 return skip_end_of_line(wps_bufptr);
368 static int parse_statusbar_disable(const char *wps_bufptr,
369 struct wps_token *token,
370 struct wps_data *wps_data)
372 (void)token; /* Kill warnings */
373 wps_data->wps_sb_tag = true;
374 wps_data->show_sb_on_wps = false;
375 return skip_end_of_line(wps_bufptr);
378 static bool load_bitmap(struct wps_data *wps_data,
379 char* filename,
380 struct bitmap *bm)
382 int format;
383 #ifdef HAVE_REMOTE_LCD
384 if (wps_data->remote_wps)
385 format = FORMAT_ANY|FORMAT_REMOTE;
386 else
387 #endif
388 format = FORMAT_ANY|FORMAT_TRANSPARENT;
390 int ret = read_bmp_file(filename, bm,
391 wps_data->img_buf_free,
392 format);
394 if (ret > 0)
396 #if LCD_DEPTH == 16
397 if (ret % 2) ret++;
398 /* Always consume an even number of bytes */
399 #endif
400 wps_data->img_buf_ptr += ret;
401 wps_data->img_buf_free -= ret;
403 return true;
405 else
406 return false;
409 static int get_image_id(int c)
411 if(c >= 'a' && c <= 'z')
412 return c - 'a';
413 else if(c >= 'A' && c <= 'Z')
414 return c - 'A' + 26;
415 else
416 return -1;
419 static char *get_image_filename(const char *start, const char* bmpdir,
420 char *buf, int buf_size)
422 const char *end = strchr(start, '|');
424 if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
426 buf = "\0";
427 return NULL;
430 int bmpdirlen = strlen(bmpdir);
432 strcpy(buf, bmpdir);
433 buf[bmpdirlen] = '/';
434 memcpy( &buf[bmpdirlen + 1], start, end - start);
435 buf[bmpdirlen + 1 + end - start] = 0;
437 return buf;
440 static int parse_image_display(const char *wps_bufptr,
441 struct wps_token *token,
442 struct wps_data *wps_data)
444 (void)wps_data;
445 int n = get_image_id(wps_bufptr[0]);
446 int subimage;
448 if (n == -1)
450 /* invalid picture display tag */
451 return WPS_ERROR_INVALID_PARAM;
454 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
456 /* Sanity check */
457 if (subimage >= wps_data->img[n].num_subimages)
458 return WPS_ERROR_INVALID_PARAM;
460 /* Store sub-image number to display in high bits */
461 token->value.i = n | (subimage << 8);
462 return 2; /* We have consumed 2 bytes */
463 } else {
464 token->value.i = n;
465 return 1; /* We have consumed 1 byte */
469 static int parse_image_load(const char *wps_bufptr,
470 struct wps_token *token,
471 struct wps_data *wps_data)
473 int n;
474 const char *ptr = wps_bufptr;
475 const char *pos;
476 const char* filename;
477 const char* id;
478 const char *newline;
479 int x,y;
481 /* format: %x|n|filename.bmp|x|y|
482 or %xl|n|filename.bmp|x|y|
483 or %xl|n|filename.bmp|x|y|num_subimages|
486 if (*ptr != '|')
487 return WPS_ERROR_INVALID_PARAM;
489 ptr++;
491 if (!(ptr = parse_list("ssdd", '|', ptr, &id, &filename, &x, &y)))
492 return WPS_ERROR_INVALID_PARAM;
494 /* Check there is a terminating | */
495 if (*ptr != '|')
496 return WPS_ERROR_INVALID_PARAM;
498 /* get the image ID */
499 n = get_image_id(*id);
501 /* check the image number and load state */
502 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
504 /* Invalid image ID */
505 return WPS_ERROR_INVALID_PARAM;
508 /* save a pointer to the filename */
509 bmp_names[n] = filename;
511 wps_data->img[n].x = x;
512 wps_data->img[n].y = y;
514 /* save current viewport */
515 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
517 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
519 wps_data->img[n].always_display = true;
521 else
523 /* Parse the (optional) number of sub-images */
524 ptr++;
525 newline = strchr(ptr, '\n');
526 pos = strchr(ptr, '|');
527 if (pos && pos < newline)
528 wps_data->img[n].num_subimages = atoi(ptr);
530 if (wps_data->img[n].num_subimages <= 0)
531 return WPS_ERROR_INVALID_PARAM;
534 /* Skip the rest of the line */
535 return skip_end_of_line(wps_bufptr);
538 static int parse_viewport(const char *wps_bufptr,
539 struct wps_token *token,
540 struct wps_data *wps_data)
542 const char *ptr = wps_bufptr;
543 struct viewport* vp;
544 int depth;
546 (void)token; /* Kill warnings */
548 if (*wps_bufptr != '|')
549 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
551 ptr = wps_bufptr + 1;
552 /* format: %V|x|y|width|height|fg_pattern|bg_pattern| */
554 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
555 return WPS_ERROR_INVALID_PARAM;
557 wps_data->num_viewports++;
558 vp = &wps_data->viewports[wps_data->num_viewports].vp;
560 /* Set the defaults for fields not user-specified */
561 vp->drawmode = DRMODE_SOLID;
562 vp->xmargin = 0;
563 vp->ymargin = 0;
565 /* Work out the depth of this display */
566 #ifdef HAVE_REMOTE_LCD
567 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
568 #else
569 depth = LCD_DEPTH;
570 #endif
572 #ifdef HAVE_LCD_COLOR
573 if (depth == 16)
575 if (!(ptr = parse_list("dddddcc", '|', ptr, &vp->x, &vp->y, &vp->width,
576 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
577 return WPS_ERROR_INVALID_PARAM;
579 else
580 #endif
581 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
582 if (depth == 2) {
583 if (!(ptr = parse_list("dddddgg", '|', ptr, &vp->x, &vp->y, &vp->width,
584 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
585 return WPS_ERROR_INVALID_PARAM;
587 else
588 #endif
589 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
590 if (depth == 1)
592 if (!(ptr = parse_list("ddddd", '|', ptr, &vp->x, &vp->y, &vp->width,
593 &vp->height, &vp->font)))
594 return WPS_ERROR_INVALID_PARAM;
596 else
597 #endif
600 /* Check for trailing | */
601 if (*ptr != '|')
602 return WPS_ERROR_INVALID_PARAM;
604 /* Default to using the user font if the font was an invalid number */
605 if ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI))
606 vp->font = FONT_UI;
608 /* Validate the viewport dimensions - we know that the numbers are
609 non-negative integers */
610 #ifdef HAVE_REMOTE_LCD
611 if (wps_data->remote_wps)
613 if ((vp->x >= LCD_REMOTE_WIDTH) ||
614 ((vp->x + vp->width) > LCD_REMOTE_WIDTH) ||
615 (vp->y >= LCD_REMOTE_HEIGHT) ||
616 ((vp->y + vp->height) > LCD_REMOTE_HEIGHT))
618 return WPS_ERROR_INVALID_PARAM;
621 else
622 #endif
624 if ((vp->x >= LCD_WIDTH) ||
625 ((vp->x + vp->width) > LCD_WIDTH) ||
626 (vp->y >= LCD_HEIGHT) ||
627 ((vp->y + vp->height) > LCD_HEIGHT))
629 return WPS_ERROR_INVALID_PARAM;
633 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
635 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
637 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
639 wps_data->lines[wps_data->num_lines].first_subline_idx =
640 wps_data->num_sublines;
642 wps_data->sublines[wps_data->num_sublines].first_token_idx =
643 wps_data->num_tokens;
646 /* Skip the rest of the line */
647 return skip_end_of_line(wps_bufptr);
651 static int parse_image_special(const char *wps_bufptr,
652 struct wps_token *token,
653 struct wps_data *wps_data)
655 (void)wps_data; /* kill warning */
656 const char *pos = NULL;
657 const char *newline;
659 pos = strchr(wps_bufptr + 1, '|');
660 newline = strchr(wps_bufptr, '\n');
662 if (pos > newline)
663 return WPS_ERROR_INVALID_PARAM;
665 if (token->type == WPS_TOKEN_IMAGE_PROGRESS_BAR)
667 /* format: %P|filename.bmp| */
668 bmp_names[PROGRESSBAR_BMP] = wps_bufptr + 1;
670 #if LCD_DEPTH > 1
671 else if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
673 /* format: %X|filename.bmp| */
674 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
676 #endif
678 /* Skip the rest of the line */
679 return skip_end_of_line(wps_bufptr);
682 #endif /* HAVE_LCD_BITMAP */
684 static int parse_dir_level(const char *wps_bufptr,
685 struct wps_token *token,
686 struct wps_data *wps_data)
688 char val[] = { *wps_bufptr, '\0' };
689 token->value.i = atoi(val);
690 (void)wps_data; /* Kill warnings */
691 return 1;
694 static int parse_subline_timeout(const char *wps_bufptr,
695 struct wps_token *token,
696 struct wps_data *wps_data)
698 int skip = 0;
699 int val = 0;
700 bool have_point = false;
701 bool have_tenth = false;
703 (void)wps_data; /* Kill the warning */
705 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
707 if (*wps_bufptr != '.')
709 val *= 10;
710 val += *wps_bufptr - '0';
711 if (have_point)
713 have_tenth = true;
714 wps_bufptr++;
715 skip++;
716 break;
719 else
720 have_point = true;
722 wps_bufptr++;
723 skip++;
726 if (have_tenth == false)
727 val *= 10;
729 token->value.i = val;
731 return skip;
734 static int parse_progressbar(const char *wps_bufptr,
735 struct wps_token *token,
736 struct wps_data *wps_data)
738 (void)token; /* Kill warnings */
739 #ifdef HAVE_LCD_BITMAP
741 short *vals[] = {
742 &wps_data->progress_height,
743 &wps_data->progress_start,
744 &wps_data->progress_end,
745 &wps_data->progress_top };
747 /* default values : */
748 wps_data->progress_height = 6;
749 wps_data->progress_start = 0;
750 wps_data->progress_end = 0;
751 wps_data->progress_top = -1;
753 int i = 0;
754 char *newline = strchr(wps_bufptr, '\n');
755 char *prev = strchr(wps_bufptr, '|');
756 if (prev && prev < newline) {
757 char *next = strchr(prev+1, '|');
758 while (i < 4 && next && next < newline)
760 *(vals[i++]) = atoi(++prev);
761 prev = strchr(prev, '|');
762 next = strchr(++next, '|');
765 if (wps_data->progress_height < 3)
766 wps_data->progress_height = 3;
767 if (wps_data->progress_end < wps_data->progress_start + 3)
768 wps_data->progress_end = 0;
771 return newline - wps_bufptr;
773 #else
775 if (*(wps_bufptr-1) == 'f')
776 wps_data->full_line_progressbar = true;
777 else
778 wps_data->full_line_progressbar = false;
780 return 0;
782 #endif
785 #ifdef HAVE_ALBUMART
786 static int parse_albumart_load(const char *wps_bufptr,
787 struct wps_token *token,
788 struct wps_data *wps_data)
790 const char *_pos, *newline;
791 bool parsing;
792 const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
793 WPS_ALBUMART_ALIGN_CENTER |
794 WPS_ALBUMART_ALIGN_RIGHT;
795 const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
796 WPS_ALBUMART_ALIGN_CENTER |
797 WPS_ALBUMART_ALIGN_BOTTOM;
799 (void)token; /* silence warning */
801 /* reset albumart info in wps */
802 wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
803 wps_data->albumart_max_width = -1;
804 wps_data->albumart_max_height = -1;
805 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
806 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
808 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
810 newline = strchr(wps_bufptr, '\n');
812 /* initial validation and parsing of x and y components */
813 if (*wps_bufptr != '|')
814 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
816 _pos = wps_bufptr + 1;
817 if (!isdigit(*_pos))
818 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
819 wps_data->albumart_x = atoi(_pos);
821 _pos = strchr(_pos, '|');
822 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
823 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
825 wps_data->albumart_y = atoi(_pos);
827 _pos = strchr(_pos, '|');
828 if (!_pos || _pos > newline)
829 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
830 e.g. %Cl|7|59\n */
832 /* parsing width field */
833 parsing = true;
834 while (parsing)
836 /* apply each modifier in turn */
837 ++_pos;
838 switch (*_pos)
840 case 'l':
841 case 'L':
842 case '+':
843 wps_data->albumart_xalign =
844 (wps_data->albumart_xalign & xalign_mask) |
845 WPS_ALBUMART_ALIGN_LEFT;
846 break;
847 case 'c':
848 case 'C':
849 wps_data->albumart_xalign =
850 (wps_data->albumart_xalign & xalign_mask) |
851 WPS_ALBUMART_ALIGN_CENTER;
852 break;
853 case 'r':
854 case 'R':
855 case '-':
856 wps_data->albumart_xalign =
857 (wps_data->albumart_xalign & xalign_mask) |
858 WPS_ALBUMART_ALIGN_RIGHT;
859 break;
860 case 'd':
861 case 'D':
862 wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
863 break;
864 case 'i':
865 case 'I':
866 wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
867 break;
868 case 's':
869 case 'S':
870 wps_data->albumart_xalign |=
871 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
872 break;
873 default:
874 parsing = false;
875 break;
878 /* extract max width data */
879 if (*_pos != '|')
881 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
882 return WPS_ERROR_INVALID_PARAM;
884 wps_data->albumart_max_width = atoi(_pos);
886 _pos = strchr(_pos, '|');
887 if (!_pos || _pos > newline)
888 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
889 e.g. %Cl|7|59|200\n */
892 /* parsing height field */
893 parsing = true;
894 while (parsing)
896 /* apply each modifier in turn */
897 ++_pos;
898 switch (*_pos)
900 case 't':
901 case 'T':
902 case '-':
903 wps_data->albumart_yalign =
904 (wps_data->albumart_yalign & yalign_mask) |
905 WPS_ALBUMART_ALIGN_TOP;
906 break;
907 case 'c':
908 case 'C':
909 wps_data->albumart_yalign =
910 (wps_data->albumart_yalign & yalign_mask) |
911 WPS_ALBUMART_ALIGN_CENTER;
912 break;
913 case 'b':
914 case 'B':
915 case '+':
916 wps_data->albumart_yalign =
917 (wps_data->albumart_yalign & yalign_mask) |
918 WPS_ALBUMART_ALIGN_BOTTOM;
919 break;
920 case 'd':
921 case 'D':
922 wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
923 break;
924 case 'i':
925 case 'I':
926 wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
927 break;
928 case 's':
929 case 'S':
930 wps_data->albumart_yalign |=
931 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
932 break;
933 default:
934 parsing = false;
935 break;
938 /* extract max height data */
939 if (*_pos != '|')
941 if (!isdigit(*_pos))
942 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
944 wps_data->albumart_max_height = atoi(_pos);
946 _pos = strchr(_pos, '|');
947 if (!_pos || _pos > newline)
948 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
949 e.g. %Cl|7|59|200|200\n */
952 /* if we got here, we parsed everything ok .. ! */
953 if (wps_data->albumart_max_width < 0)
954 wps_data->albumart_max_width = 0;
955 else if (wps_data->albumart_max_width > LCD_WIDTH)
956 wps_data->albumart_max_width = LCD_WIDTH;
958 if (wps_data->albumart_max_height < 0)
959 wps_data->albumart_max_height = 0;
960 else if (wps_data->albumart_max_height > LCD_HEIGHT)
961 wps_data->albumart_max_height = LCD_HEIGHT;
963 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
965 /* Skip the rest of the line */
966 return skip_end_of_line(wps_bufptr);
969 static int parse_albumart_conditional(const char *wps_bufptr,
970 struct wps_token *token,
971 struct wps_data *wps_data)
973 struct wps_token *prevtoken = token;
974 --prevtoken;
975 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
977 /* This %C is part of a %?C construct.
978 It's either %?C<blah> or %?Cn<blah> */
979 token->type = WPS_TOKEN_ALBUMART_FOUND;
980 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
982 token->next = true;
983 return 1;
985 else if (*wps_bufptr == '<')
987 return 0;
989 else
991 token->type = WPS_NO_TOKEN;
992 return 0;
995 else
997 /* This %C tag is in a conditional construct. */
998 wps_data->albumart_cond_index = condindex[level];
999 return 0;
1002 #endif /* HAVE_ALBUMART */
1004 #ifdef HAVE_LCD_BITMAP
1005 static int parse_leftmargin(const char *wps_bufptr, struct wps_token *token,
1006 struct wps_data *wps_data)
1008 const char* p;
1009 const char* pend;
1010 const char *newline;
1012 (void)wps_data; /* Kill the warning */
1014 /* valid tag looks like %m|12| */
1015 if(*wps_bufptr == '|')
1017 p = wps_bufptr + 1;
1018 newline = strchr(wps_bufptr, '\n');
1019 if(isdigit(*p) && (pend = strchr(p, '|')) && pend < newline)
1021 token->value.i = atoi(p);
1022 return pend - wps_bufptr + 1;
1026 /* invalid tag syntax */
1027 return WPS_ERROR_INVALID_PARAM;
1029 #endif
1032 /* Parse a generic token from the given string. Return the length read */
1033 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1035 int skip = 0, taglen = 0, ret;
1036 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1037 const struct wps_tag *tag;
1039 switch(*wps_bufptr)
1042 case '%':
1043 case '<':
1044 case '|':
1045 case '>':
1046 case ';':
1047 case '#':
1048 /* escaped characters */
1049 token->type = WPS_TOKEN_CHARACTER;
1050 token->value.c = *wps_bufptr;
1051 taglen = 1;
1052 wps_data->num_tokens++;
1053 break;
1055 case '?':
1056 /* conditional tag */
1057 token->type = WPS_TOKEN_CONDITIONAL;
1058 level++;
1059 condindex[level] = wps_data->num_tokens;
1060 numoptions[level] = 1;
1061 wps_data->num_tokens++;
1062 ret = parse_token(wps_bufptr + 1, wps_data);
1063 if (ret < 0) return ret;
1064 taglen = 1 + ret;
1065 break;
1067 default:
1068 /* find what tag we have */
1069 for (tag = all_tags;
1070 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1071 tag++) ;
1073 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1074 token->type = tag->type;
1075 wps_data->sublines[wps_data->num_sublines].line_type |=
1076 tag->refresh_type;
1078 /* if the tag has a special parsing function, we call it */
1079 if (tag->parse_func)
1081 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1082 if (ret < 0) return ret;
1083 skip += ret;
1086 /* Some tags we don't want to save as tokens */
1087 if (tag->type == WPS_NO_TOKEN)
1088 break;
1090 /* tags that start with 'F', 'I' or 'D' are for the next file */
1091 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1092 *(tag->name) == 'D')
1093 token->next = true;
1095 wps_data->num_tokens++;
1096 break;
1099 skip += taglen;
1100 return skip;
1103 /* Parses the WPS.
1104 data is the pointer to the structure where the parsed WPS should be stored.
1105 It is initialised.
1106 wps_bufptr points to the string containing the WPS tags */
1107 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1109 if (!data || !wps_bufptr || !*wps_bufptr)
1110 return false;
1112 char *stringbuf = data->string_buffer;
1113 int stringbuf_used = 0;
1114 int fail = 0;
1115 int ret;
1116 line = 1;
1117 level = -1;
1119 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1120 && data->num_viewports < WPS_MAX_VIEWPORTS
1121 && data->num_lines < WPS_MAX_LINES)
1123 switch(*wps_bufptr++)
1126 /* Regular tag */
1127 case '%':
1128 if ((ret = parse_token(wps_bufptr, data)) < 0)
1130 fail = PARSE_FAIL_COND_INVALID_PARAM;
1131 break;
1133 wps_bufptr += ret;
1134 break;
1136 /* Alternating sublines separator */
1137 case ';':
1138 if (level >= 0) /* there are unclosed conditionals */
1140 fail = PARSE_FAIL_UNCLOSED_COND;
1141 break;
1144 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1145 wps_start_new_subline(data);
1146 else
1147 wps_bufptr += skip_end_of_line(wps_bufptr);
1149 break;
1151 /* Conditional list start */
1152 case '<':
1153 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1155 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1156 break;
1159 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1160 lastcond[level] = data->num_tokens++;
1161 break;
1163 /* Conditional list end */
1164 case '>':
1165 if (level < 0) /* not in a conditional, invalid char */
1167 fail = PARSE_FAIL_INVALID_CHAR;
1168 break;
1171 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1172 if (lastcond[level])
1173 data->tokens[lastcond[level]].value.i = data->num_tokens;
1174 else
1176 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1177 break;
1180 lastcond[level] = 0;
1181 data->num_tokens++;
1182 data->tokens[condindex[level]].value.i = numoptions[level];
1183 level--;
1184 break;
1186 /* Conditional list option */
1187 case '|':
1188 if (level < 0) /* not in a conditional, invalid char */
1190 fail = PARSE_FAIL_INVALID_CHAR;
1191 break;
1194 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1195 if (lastcond[level])
1196 data->tokens[lastcond[level]].value.i = data->num_tokens;
1197 else
1199 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1200 break;
1203 lastcond[level] = data->num_tokens;
1204 numoptions[level]++;
1205 data->num_tokens++;
1206 break;
1208 /* Comment */
1209 case '#':
1210 if (level >= 0) /* there are unclosed conditionals */
1212 fail = PARSE_FAIL_UNCLOSED_COND;
1213 break;
1216 wps_bufptr += skip_end_of_line(wps_bufptr);
1217 break;
1219 /* End of this line */
1220 case '\n':
1221 if (level >= 0) /* there are unclosed conditionals */
1223 fail = PARSE_FAIL_UNCLOSED_COND;
1224 break;
1227 line++;
1228 wps_start_new_subline(data);
1229 data->num_lines++; /* Start a new line */
1231 if ((data->num_lines < WPS_MAX_LINES) &&
1232 (data->num_sublines < WPS_MAX_SUBLINES))
1234 data->lines[data->num_lines].first_subline_idx =
1235 data->num_sublines;
1237 data->sublines[data->num_sublines].first_token_idx =
1238 data->num_tokens;
1241 break;
1243 /* String */
1244 default:
1246 unsigned int len = 1;
1247 const char *string_start = wps_bufptr - 1;
1249 /* find the length of the string */
1250 while (*wps_bufptr && *wps_bufptr != '#' &&
1251 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1252 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1253 *wps_bufptr != '|' && *wps_bufptr != '\n')
1255 wps_bufptr++;
1256 len++;
1259 /* look if we already have that string */
1260 char **str;
1261 int i;
1262 bool found;
1263 for (i = 0, str = data->strings, found = false;
1264 i < data->num_strings &&
1265 !(found = (strlen(*str) == len &&
1266 strncmp(string_start, *str, len) == 0));
1267 i++, str++);
1268 /* If a matching string is found, found is true and i is
1269 the index of the string. If not, found is false */
1271 /* If it's NOT a duplicate, do nothing if we already have
1272 too many unique strings */
1273 if (found ||
1274 (stringbuf_used < STRING_BUFFER_SIZE - 1 &&
1275 data->num_strings < WPS_MAX_STRINGS))
1277 if (!found)
1279 /* new string */
1281 /* truncate? */
1282 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1)
1283 len = STRING_BUFFER_SIZE - stringbuf_used - 1;
1285 strncpy(stringbuf, string_start, len);
1286 *(stringbuf + len) = '\0';
1288 data->strings[data->num_strings] = stringbuf;
1289 stringbuf += len + 1;
1290 stringbuf_used += len + 1;
1291 data->tokens[data->num_tokens].value.i =
1292 data->num_strings;
1293 data->num_strings++;
1295 else
1297 /* another ocurrence of an existing string */
1298 data->tokens[data->num_tokens].value.i = i;
1300 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1301 data->num_tokens++;
1304 break;
1308 if (!fail && level >= 0) /* there are unclosed conditionals */
1309 fail = PARSE_FAIL_UNCLOSED_COND;
1311 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1313 /* We have finished with the last viewport, so increment count */
1314 data->num_viewports++;
1316 #ifdef DEBUG
1317 print_debug_info(data, fail, line);
1318 #endif
1320 return (fail == 0);
1323 #ifdef HAVE_LCD_BITMAP
1324 /* Clear the WPS image cache */
1325 static void wps_images_clear(struct wps_data *data)
1327 int i;
1328 /* set images to unloaded and not displayed */
1329 for (i = 0; i < MAX_IMAGES; i++)
1331 data->img[i].loaded = false;
1332 data->img[i].display = -1;
1333 data->img[i].always_display = false;
1334 data->img[i].num_subimages = 1;
1336 data->progressbar.have_bitmap_pb = false;
1338 #endif
1340 /* initial setup of wps_data */
1341 void wps_data_init(struct wps_data *wps_data)
1343 #ifdef HAVE_LCD_BITMAP
1344 wps_images_clear(wps_data);
1345 wps_data->wps_sb_tag = false;
1346 wps_data->show_sb_on_wps = false;
1347 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1348 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1349 wps_data->peak_meter_enabled = false;
1350 #else /* HAVE_LCD_CHARCELLS */
1351 int i;
1352 for (i = 0; i < 8; i++)
1354 wps_data->wps_progress_pat[i] = 0;
1356 wps_data->full_line_progressbar = false;
1357 #endif
1358 wps_data->wps_loaded = false;
1361 static void wps_reset(struct wps_data *data)
1363 #ifdef HAVE_REMOTE_LCD
1364 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1365 #endif
1366 memset(data, 0, sizeof(*data));
1367 #ifdef HAVE_ALBUMART
1368 data->wps_uses_albumart = WPS_ALBUMART_NONE;
1369 #endif
1370 wps_data_init(data);
1371 #ifdef HAVE_REMOTE_LCD
1372 data->remote_wps = rwps;
1373 #endif
1376 #ifdef HAVE_LCD_BITMAP
1378 static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1380 char img_path[MAX_PATH];
1381 struct bitmap *bitmap;
1382 bool *loaded;
1383 int n;
1384 for (n = 0; n < BACKDROP_BMP; n++)
1386 if (bmp_names[n])
1388 get_image_filename(bmp_names[n], bmpdir,
1389 img_path, sizeof(img_path));
1391 if (n == PROGRESSBAR_BMP) {
1392 /* progressbar bitmap */
1393 bitmap = &wps_data->progressbar.bm;
1394 loaded = &wps_data->progressbar.have_bitmap_pb;
1395 } else {
1396 /* regular bitmap */
1397 bitmap = &wps_data->img[n].bm;
1398 loaded = &wps_data->img[n].loaded;
1401 /* load the image */
1402 bitmap->data = wps_data->img_buf_ptr;
1403 if (load_bitmap(wps_data, img_path, bitmap))
1405 *loaded = true;
1407 /* Calculate and store height if this image has sub-images */
1408 if (n < MAX_IMAGES)
1409 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1410 wps_data->img[n].num_subimages;
1412 else
1414 /* Abort if we can't load an image */
1415 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1416 return false;
1421 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1422 if (bmp_names[BACKDROP_BMP])
1424 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1425 img_path, sizeof(img_path));
1427 #if defined(HAVE_REMOTE_LCD)
1428 /* We only need to check LCD type if there is a remote LCD */
1429 if (!wps_data->remote_wps)
1430 #endif
1432 /* Load backdrop for the main LCD */
1433 if (!load_wps_backdrop(img_path))
1434 return false;
1436 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1437 else
1439 /* Load backdrop for the remote LCD */
1440 if (!load_remote_wps_backdrop(img_path))
1441 return false;
1443 #endif
1445 #endif /* has backdrop support */
1447 /* If we got here, everything was OK */
1448 return true;
1451 #endif /* HAVE_LCD_BITMAP */
1453 /* Skip leading UTF-8 BOM, if present. */
1454 static char *skip_utf8_bom(char *buf)
1456 unsigned char *s = (unsigned char *)buf;
1458 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
1460 buf += 3;
1463 return buf;
1466 /* to setup up the wps-data from a format-buffer (isfile = false)
1467 from a (wps-)file (isfile = true)*/
1468 bool wps_data_load(struct wps_data *wps_data,
1469 struct screen *display,
1470 const char *buf,
1471 bool isfile)
1473 if (!wps_data || !buf)
1474 return false;
1476 wps_reset(wps_data);
1478 /* Initialise the first (default) viewport */
1479 wps_data->viewports[0].vp.x = 0;
1480 wps_data->viewports[0].vp.y = 0;
1481 wps_data->viewports[0].vp.width = display->width;
1482 wps_data->viewports[0].vp.height = display->height;
1483 #ifdef HAVE_LCD_BITMAP
1484 wps_data->viewports[0].vp.font = FONT_UI;
1485 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1486 #endif
1487 wps_data->viewports[0].vp.xmargin = display->getxmargin();
1488 wps_data->viewports[0].vp.ymargin = display->getymargin();
1489 #if LCD_DEPTH > 1
1490 if (display->depth > 1)
1492 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1493 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1495 #endif
1496 if (!isfile)
1498 return wps_parse(wps_data, buf);
1500 else
1503 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1504 * wants to be a virtual file. Feel free to modify dirbrowse()
1505 * if you're feeling brave.
1507 #ifndef __PCTOOL__
1508 if (! strcmp(buf, WPS_DEFAULTCFG) )
1510 global_settings.wps_file[0] = 0;
1511 return false;
1514 #ifdef HAVE_REMOTE_LCD
1515 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1517 global_settings.rwps_file[0] = 0;
1518 return false;
1520 #endif
1521 #endif /* __PCTOOL__ */
1523 int fd = open(buf, O_RDONLY);
1525 if (fd < 0)
1526 return false;
1528 /* get buffer space from the plugin buffer */
1529 size_t buffersize = 0;
1530 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1532 if (!wps_buffer)
1533 return false;
1535 /* copy the file's content to the buffer for parsing,
1536 ensuring that every line ends with a newline char. */
1537 unsigned int start = 0;
1538 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1540 start += strlen(wps_buffer + start);
1541 if (start < buffersize - 1)
1543 wps_buffer[start++] = '\n';
1544 wps_buffer[start] = 0;
1548 close(fd);
1550 if (start <= 0)
1551 return false;
1553 #ifdef HAVE_LCD_BITMAP
1554 /* Set all filename pointers to NULL */
1555 memset(bmp_names, 0, sizeof(bmp_names));
1556 #endif
1558 /* Skip leading UTF-8 BOM, if present. */
1559 wps_buffer = skip_utf8_bom(wps_buffer);
1561 /* parse the WPS source */
1562 if (!wps_parse(wps_data, wps_buffer)) {
1563 wps_reset(wps_data);
1564 return false;
1567 wps_data->wps_loaded = true;
1569 #ifdef HAVE_LCD_BITMAP
1570 /* get the bitmap dir */
1571 char bmpdir[MAX_PATH];
1572 size_t bmpdirlen;
1573 char *dot = strrchr(buf, '.');
1574 bmpdirlen = dot - buf;
1575 strncpy(bmpdir, buf, dot - buf);
1576 bmpdir[bmpdirlen] = 0;
1578 /* load the bitmaps that were found by the parsing */
1579 if (!load_wps_bitmaps(wps_data, bmpdir)) {
1580 wps_reset(wps_data);
1581 return false;
1583 #endif
1584 return true;
1588 int wps_subline_index(struct wps_data *data, int line, int subline)
1590 return data->lines[line].first_subline_idx + subline;
1593 int wps_first_token_index(struct wps_data *data, int line, int subline)
1595 int first_subline_idx = data->lines[line].first_subline_idx;
1596 return data->sublines[first_subline_idx + subline].first_token_idx;
1599 int wps_last_token_index(struct wps_data *data, int line, int subline)
1601 int first_subline_idx = data->lines[line].first_subline_idx;
1602 int idx = first_subline_idx + subline;
1603 if (idx < data->num_sublines - 1)
1605 /* This subline ends where the next begins */
1606 return data->sublines[idx+1].first_token_idx - 1;
1608 else
1610 /* The last subline goes to the end */
1611 return data->num_tokens - 1;