implement smooth seeking acceleration for audio playback and mpegplayer
[Rockbox.git] / apps / gui / wps_parser.c
blob67411ed0768646f6105acf0fa7c615befa4311f8
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+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
84 #else
85 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
86 #endif
88 #define PROGRESSBAR_BMP MAX_IMAGES
89 #define BACKDROP_BMP (MAX_BITMAPS-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_display(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_viewport(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_statusbar_enable(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_statusbar_disable(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_image_display(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_image_load(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 #endif /*HAVE_LCD_BITMAP */
145 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
146 static int parse_image_special(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 #endif
149 #ifdef HAVE_ALBUMART
150 static int parse_albumart_load(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 static int parse_albumart_conditional(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 #endif /* HAVE_ALBUMART */
156 #ifdef CONFIG_RTC
157 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
158 #else
159 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
160 #endif
162 /* array of available tags - those with more characters have to go first
163 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
164 static const struct wps_tag all_tags[] = {
166 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
167 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
168 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
170 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
171 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
172 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
173 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
174 #if CONFIG_CHARGING >= CHARGING_MONITOR
175 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
176 #endif
177 #if CONFIG_CHARGING
178 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
179 #endif
181 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
182 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
183 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
184 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
185 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
186 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
187 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
188 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
189 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
190 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
191 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
192 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
193 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
194 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
197 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
200 /* current file */
201 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
202 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
203 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
204 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
205 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
206 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
207 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
208 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
209 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
210 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
211 parse_dir_level },
213 /* next file */
214 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
215 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
216 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
217 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
218 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
219 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
220 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
221 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
222 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
223 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
224 parse_dir_level },
226 /* current metadata */
227 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
240 /* next metadata */
241 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
242 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
243 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
244 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
245 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
246 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
247 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
248 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
249 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
250 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
251 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
252 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
254 #if (CONFIG_CODEC != MAS3507D)
255 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
256 #endif
258 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
259 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
260 #endif
262 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
264 #ifdef HAS_REMOTE_BUTTON_HOLD
265 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
266 #else
267 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
268 #endif
270 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
271 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
273 #ifdef HAVE_LCD_BITMAP
274 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
275 #else
276 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
277 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
278 #endif
279 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
280 parse_progressbar },
282 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
284 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
285 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
286 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
287 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
289 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
290 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
291 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
292 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
294 #ifdef HAVE_TAGCACHE
295 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
296 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
297 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
298 #endif
300 #if CONFIG_CODEC == SWCODEC
301 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
302 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
303 #endif
305 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
306 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_subline_timeout },
308 #ifdef HAVE_LCD_BITMAP
309 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
310 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
312 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
314 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
315 parse_image_display },
317 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
318 #ifdef HAVE_ALBUMART
319 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
320 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
321 parse_albumart_conditional },
322 #endif
324 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
325 parse_viewport_display },
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 if (wps_data->viewports[0].vp.y == 0)
367 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
368 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
370 return skip_end_of_line(wps_bufptr);
373 static int parse_statusbar_disable(const char *wps_bufptr,
374 struct wps_token *token,
375 struct wps_data *wps_data)
377 (void)token; /* Kill warnings */
378 wps_data->wps_sb_tag = true;
379 wps_data->show_sb_on_wps = false;
380 if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
382 wps_data->viewports[0].vp.y = 0;
383 wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
385 return skip_end_of_line(wps_bufptr);
388 static bool load_bitmap(struct wps_data *wps_data,
389 char* filename,
390 struct bitmap *bm)
392 int format;
393 #ifdef HAVE_REMOTE_LCD
394 if (wps_data->remote_wps)
395 format = FORMAT_ANY|FORMAT_REMOTE;
396 else
397 #endif
398 format = FORMAT_ANY|FORMAT_TRANSPARENT;
400 int ret = read_bmp_file(filename, bm,
401 wps_data->img_buf_free,
402 format);
404 if (ret > 0)
406 #if LCD_DEPTH == 16
407 if (ret % 2) ret++;
408 /* Always consume an even number of bytes */
409 #endif
410 wps_data->img_buf_ptr += ret;
411 wps_data->img_buf_free -= ret;
413 return true;
415 else
416 return false;
419 static int get_image_id(int c)
421 if(c >= 'a' && c <= 'z')
422 return c - 'a';
423 else if(c >= 'A' && c <= 'Z')
424 return c - 'A' + 26;
425 else
426 return -1;
429 static char *get_image_filename(const char *start, const char* bmpdir,
430 char *buf, int buf_size)
432 const char *end = strchr(start, '|');
434 if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
436 buf = "\0";
437 return NULL;
440 int bmpdirlen = strlen(bmpdir);
442 strcpy(buf, bmpdir);
443 buf[bmpdirlen] = '/';
444 memcpy( &buf[bmpdirlen + 1], start, end - start);
445 buf[bmpdirlen + 1 + end - start] = 0;
447 return buf;
450 static int parse_image_display(const char *wps_bufptr,
451 struct wps_token *token,
452 struct wps_data *wps_data)
454 (void)wps_data;
455 int n = get_image_id(wps_bufptr[0]);
456 int subimage;
458 if (n == -1)
460 /* invalid picture display tag */
461 return WPS_ERROR_INVALID_PARAM;
464 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
466 /* Sanity check */
467 if (subimage >= wps_data->img[n].num_subimages)
468 return WPS_ERROR_INVALID_PARAM;
470 /* Store sub-image number to display in high bits */
471 token->value.i = n | (subimage << 8);
472 return 2; /* We have consumed 2 bytes */
473 } else {
474 token->value.i = n;
475 return 1; /* We have consumed 1 byte */
479 static int parse_image_load(const char *wps_bufptr,
480 struct wps_token *token,
481 struct wps_data *wps_data)
483 int n;
484 const char *ptr = wps_bufptr;
485 const char *pos;
486 const char* filename;
487 const char* id;
488 const char *newline;
489 int x,y;
491 /* format: %x|n|filename.bmp|x|y|
492 or %xl|n|filename.bmp|x|y|
493 or %xl|n|filename.bmp|x|y|num_subimages|
496 if (*ptr != '|')
497 return WPS_ERROR_INVALID_PARAM;
499 ptr++;
501 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
502 return WPS_ERROR_INVALID_PARAM;
504 /* Check there is a terminating | */
505 if (*ptr != '|')
506 return WPS_ERROR_INVALID_PARAM;
508 /* get the image ID */
509 n = get_image_id(*id);
511 /* check the image number and load state */
512 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
514 /* Invalid image ID */
515 return WPS_ERROR_INVALID_PARAM;
518 /* save a pointer to the filename */
519 bmp_names[n] = filename;
521 wps_data->img[n].x = x;
522 wps_data->img[n].y = y;
524 /* save current viewport */
525 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
527 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
529 wps_data->img[n].always_display = true;
531 else
533 /* Parse the (optional) number of sub-images */
534 ptr++;
535 newline = strchr(ptr, '\n');
536 pos = strchr(ptr, '|');
537 if (pos && pos < newline)
538 wps_data->img[n].num_subimages = atoi(ptr);
540 if (wps_data->img[n].num_subimages <= 0)
541 return WPS_ERROR_INVALID_PARAM;
544 /* Skip the rest of the line */
545 return skip_end_of_line(wps_bufptr);
548 static int parse_viewport_display(const char *wps_bufptr,
549 struct wps_token *token,
550 struct wps_data *wps_data)
552 (void)wps_data;
553 char letter = wps_bufptr[0];
555 if (letter < 'a' || letter > 'z')
557 /* invalid viewport tag */
558 return WPS_ERROR_INVALID_PARAM;
560 token->value.i = letter;
561 return 1;
564 static int parse_viewport(const char *wps_bufptr,
565 struct wps_token *token,
566 struct wps_data *wps_data)
568 (void)token; /* Kill warnings */
569 const char *ptr = wps_bufptr;
570 struct viewport* vp;
571 int depth;
572 uint32_t set = 0;
573 enum {
574 PL_X = 0,
575 PL_Y,
576 PL_WIDTH,
577 PL_HEIGHT,
578 PL_FONT,
579 PL_FG,
580 PL_BG,
582 int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
583 #ifdef HAVE_REMOTE_LCD
584 if (wps_data->remote_wps)
586 lcd_width = LCD_REMOTE_WIDTH;
587 lcd_height = LCD_REMOTE_HEIGHT;
589 #endif
591 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
592 return WPS_ERROR_INVALID_PARAM;
594 wps_data->num_viewports++;
595 /* check for the optional letter to signify its a hideable viewport */
596 /* %Vl|<label>|<rest of tags>| */
597 wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
599 if (*ptr == 'l')
601 if (*(ptr+1) == '|')
603 char label = *(ptr+2);
604 if (label >= 'a' && label <= 'z')
606 wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
607 wps_data->viewports[wps_data->num_viewports].label = label;
609 else
610 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
611 ptr += 3;
614 if (*ptr != '|')
615 return WPS_ERROR_INVALID_PARAM;
617 ptr++;
618 vp = &wps_data->viewports[wps_data->num_viewports].vp;
619 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
621 /* Set the defaults for fields not user-specified */
622 vp->drawmode = DRMODE_SOLID;
624 /* Work out the depth of this display */
625 #ifdef HAVE_REMOTE_LCD
626 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
627 #else
628 depth = LCD_DEPTH;
629 #endif
631 #ifdef HAVE_LCD_COLOR
632 if (depth == 16)
634 if (!(ptr = parse_list("dddddcc", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
635 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
636 return WPS_ERROR_INVALID_PARAM;
638 else
639 #endif
640 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
641 if (depth == 2) {
642 /* Default to black on white */
643 vp->fg_pattern = 0;
644 vp->bg_pattern = 3;
645 if (!(ptr = parse_list("dddddgg", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
646 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
647 return WPS_ERROR_INVALID_PARAM;
649 else
650 #endif
651 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
652 if (depth == 1)
654 if (!(ptr = parse_list("ddddd", &set, '|', ptr, &vp->x, &vp->y,
655 &vp->width, &vp->height, &vp->font)))
656 return WPS_ERROR_INVALID_PARAM;
658 else
659 #endif
662 /* Check for trailing | */
663 if (*ptr != '|')
664 return WPS_ERROR_INVALID_PARAM;
666 if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
667 return WPS_ERROR_INVALID_PARAM;
669 /* fix defaults */
670 if (!LIST_VALUE_PARSED(set, PL_WIDTH))
671 vp->width = lcd_width - vp->x;
672 if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
673 vp->height = lcd_height - vp->y;
675 /* Default to using the user font if the font was an invalid number */
676 if (!LIST_VALUE_PARSED(set, PL_FONT) ||
677 ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
678 vp->font = FONT_UI;
680 /* Validate the viewport dimensions - we know that the numbers are
681 non-negative integers */
682 if ((vp->x >= lcd_width) ||
683 ((vp->x + vp->width) > lcd_width) ||
684 (vp->y >= lcd_height) ||
685 ((vp->y + vp->height) > lcd_height))
687 return WPS_ERROR_INVALID_PARAM;
690 #ifdef HAVE_LCD_COLOR
691 if (depth == 16)
693 if (!LIST_VALUE_PARSED(set, PL_FG))
694 vp->fg_pattern = global_settings.fg_color;
695 if (!LIST_VALUE_PARSED(set, PL_BG))
696 vp->bg_pattern = global_settings.bg_color;
698 #endif
700 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
702 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
704 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
706 wps_data->lines[wps_data->num_lines].first_subline_idx =
707 wps_data->num_sublines;
709 wps_data->sublines[wps_data->num_sublines].first_token_idx =
710 wps_data->num_tokens;
713 /* Skip the rest of the line */
714 return skip_end_of_line(wps_bufptr);
718 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
719 static int parse_image_special(const char *wps_bufptr,
720 struct wps_token *token,
721 struct wps_data *wps_data)
723 (void)wps_data; /* kill warning */
724 (void)token;
725 const char *pos = NULL;
726 const char *newline;
728 pos = strchr(wps_bufptr + 1, '|');
729 newline = strchr(wps_bufptr, '\n');
731 if (pos > newline)
732 return WPS_ERROR_INVALID_PARAM;
733 #if LCD_DEPTH > 1
734 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
736 /* format: %X|filename.bmp| */
737 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
739 #endif
741 /* Skip the rest of the line */
742 return skip_end_of_line(wps_bufptr);
744 #endif
746 #endif /* HAVE_LCD_BITMAP */
748 static int parse_dir_level(const char *wps_bufptr,
749 struct wps_token *token,
750 struct wps_data *wps_data)
752 char val[] = { *wps_bufptr, '\0' };
753 token->value.i = atoi(val);
754 (void)wps_data; /* Kill warnings */
755 return 1;
758 static int parse_subline_timeout(const char *wps_bufptr,
759 struct wps_token *token,
760 struct wps_data *wps_data)
762 int skip = 0;
763 int val = 0;
764 bool have_point = false;
765 bool have_tenth = false;
767 (void)wps_data; /* Kill the warning */
769 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
771 if (*wps_bufptr != '.')
773 val *= 10;
774 val += *wps_bufptr - '0';
775 if (have_point)
777 have_tenth = true;
778 wps_bufptr++;
779 skip++;
780 break;
783 else
784 have_point = true;
786 wps_bufptr++;
787 skip++;
790 if (have_tenth == false)
791 val *= 10;
793 token->value.i = val;
795 return skip;
798 static int parse_progressbar(const char *wps_bufptr,
799 struct wps_token *token,
800 struct wps_data *wps_data)
802 (void)token; /* Kill warnings */
803 /* %pb or %pb|filename|x|y|width|height|
804 using - for any of the params uses "sane" values */
805 #ifdef HAVE_LCD_BITMAP
806 enum {
807 PB_FILENAME = 0,
808 PB_X,
809 PB_Y,
810 PB_WIDTH,
811 PB_HEIGHT
813 const char *filename;
814 int x, y, height, width;
815 uint32_t set = 0;
816 const char *ptr = wps_bufptr;
817 struct progressbar *pb;
818 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
819 int font_height = font_get(vp->font)->height;
820 int line_y_pos = font_height*(wps_data->num_lines -
821 wps_data->viewports[wps_data->num_viewports].first_line);
823 if (wps_data->progressbar_count +1 >= MAX_PROGRESSBARS)
824 return WPS_ERROR_INVALID_PARAM;
826 pb = &wps_data->progressbar[wps_data->progressbar_count];
827 pb->have_bitmap_pb = false;
829 if (*wps_bufptr != '|') /* regular old style */
831 pb->x = 0;
832 pb->width = vp->width;
833 pb->height = SYSFONT_HEIGHT-2;
834 pb->y = line_y_pos + (font_height-pb->height)/2;
836 wps_data->viewports[wps_data->num_viewports].pb =
837 &wps_data->progressbar[wps_data->progressbar_count];
838 wps_data->progressbar_count++;
839 return 0;
841 ptr = wps_bufptr + 1;
843 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
844 &x, &y, &width, &height)))
845 return WPS_ERROR_INVALID_PARAM;
846 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
847 bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
848 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
849 pb->x = x;
850 else
851 pb->x = vp->x;
852 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
853 pb->width = width;
854 else
855 pb->width = vp->width - pb->x;
856 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
857 pb->height = height;
858 else
859 pb->height = font_height;
860 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
861 pb->y = y;
862 else
863 pb->y = line_y_pos + (font_height-pb->height)/2;
864 wps_data->progressbar[wps_data->progressbar_count].have_bitmap_pb = false;
865 wps_data->viewports[wps_data->num_viewports].pb =
866 &wps_data->progressbar[wps_data->progressbar_count];
867 wps_data->progressbar_count++;
868 /* Skip the rest of the line */
869 return skip_end_of_line(wps_bufptr)-1;
870 #else
872 if (*(wps_bufptr-1) == 'f')
873 wps_data->full_line_progressbar = true;
874 else
875 wps_data->full_line_progressbar = false;
877 return 0;
879 #endif
882 #ifdef HAVE_ALBUMART
883 static int parse_albumart_load(const char *wps_bufptr,
884 struct wps_token *token,
885 struct wps_data *wps_data)
887 const char *_pos, *newline;
888 bool parsing;
889 const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
890 WPS_ALBUMART_ALIGN_CENTER |
891 WPS_ALBUMART_ALIGN_RIGHT;
892 const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
893 WPS_ALBUMART_ALIGN_CENTER |
894 WPS_ALBUMART_ALIGN_BOTTOM;
896 (void)token; /* silence warning */
898 /* reset albumart info in wps */
899 wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
900 wps_data->albumart_max_width = -1;
901 wps_data->albumart_max_height = -1;
902 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
903 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
905 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
907 newline = strchr(wps_bufptr, '\n');
909 /* initial validation and parsing of x and y components */
910 if (*wps_bufptr != '|')
911 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
913 _pos = wps_bufptr + 1;
914 if (!isdigit(*_pos))
915 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
916 wps_data->albumart_x = atoi(_pos);
918 _pos = strchr(_pos, '|');
919 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
920 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
922 wps_data->albumart_y = atoi(_pos);
924 _pos = strchr(_pos, '|');
925 if (!_pos || _pos > newline)
926 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
927 e.g. %Cl|7|59\n */
929 /* parsing width field */
930 parsing = true;
931 while (parsing)
933 /* apply each modifier in turn */
934 ++_pos;
935 switch (*_pos)
937 case 'l':
938 case 'L':
939 case '+':
940 wps_data->albumart_xalign =
941 (wps_data->albumart_xalign & xalign_mask) |
942 WPS_ALBUMART_ALIGN_LEFT;
943 break;
944 case 'c':
945 case 'C':
946 wps_data->albumart_xalign =
947 (wps_data->albumart_xalign & xalign_mask) |
948 WPS_ALBUMART_ALIGN_CENTER;
949 break;
950 case 'r':
951 case 'R':
952 case '-':
953 wps_data->albumart_xalign =
954 (wps_data->albumart_xalign & xalign_mask) |
955 WPS_ALBUMART_ALIGN_RIGHT;
956 break;
957 case 'd':
958 case 'D':
959 wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
960 break;
961 case 'i':
962 case 'I':
963 wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
964 break;
965 case 's':
966 case 'S':
967 wps_data->albumart_xalign |=
968 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
969 break;
970 default:
971 parsing = false;
972 break;
975 /* extract max width data */
976 if (*_pos != '|')
978 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
979 return WPS_ERROR_INVALID_PARAM;
981 wps_data->albumart_max_width = atoi(_pos);
983 _pos = strchr(_pos, '|');
984 if (!_pos || _pos > newline)
985 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
986 e.g. %Cl|7|59|200\n */
989 /* parsing height field */
990 parsing = true;
991 while (parsing)
993 /* apply each modifier in turn */
994 ++_pos;
995 switch (*_pos)
997 case 't':
998 case 'T':
999 case '-':
1000 wps_data->albumart_yalign =
1001 (wps_data->albumart_yalign & yalign_mask) |
1002 WPS_ALBUMART_ALIGN_TOP;
1003 break;
1004 case 'c':
1005 case 'C':
1006 wps_data->albumart_yalign =
1007 (wps_data->albumart_yalign & yalign_mask) |
1008 WPS_ALBUMART_ALIGN_CENTER;
1009 break;
1010 case 'b':
1011 case 'B':
1012 case '+':
1013 wps_data->albumart_yalign =
1014 (wps_data->albumart_yalign & yalign_mask) |
1015 WPS_ALBUMART_ALIGN_BOTTOM;
1016 break;
1017 case 'd':
1018 case 'D':
1019 wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
1020 break;
1021 case 'i':
1022 case 'I':
1023 wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
1024 break;
1025 case 's':
1026 case 'S':
1027 wps_data->albumart_yalign |=
1028 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
1029 break;
1030 default:
1031 parsing = false;
1032 break;
1035 /* extract max height data */
1036 if (*_pos != '|')
1038 if (!isdigit(*_pos))
1039 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1041 wps_data->albumart_max_height = atoi(_pos);
1043 _pos = strchr(_pos, '|');
1044 if (!_pos || _pos > newline)
1045 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1046 e.g. %Cl|7|59|200|200\n */
1049 /* if we got here, we parsed everything ok .. ! */
1050 if (wps_data->albumart_max_width < 0)
1051 wps_data->albumart_max_width = 0;
1052 else if (wps_data->albumart_max_width > LCD_WIDTH)
1053 wps_data->albumart_max_width = LCD_WIDTH;
1055 if (wps_data->albumart_max_height < 0)
1056 wps_data->albumart_max_height = 0;
1057 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1058 wps_data->albumart_max_height = LCD_HEIGHT;
1060 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1062 /* Skip the rest of the line */
1063 return skip_end_of_line(wps_bufptr);
1066 static int parse_albumart_conditional(const char *wps_bufptr,
1067 struct wps_token *token,
1068 struct wps_data *wps_data)
1070 struct wps_token *prevtoken = token;
1071 --prevtoken;
1072 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1074 /* This %C is part of a %?C construct.
1075 It's either %?C<blah> or %?Cn<blah> */
1076 token->type = WPS_TOKEN_ALBUMART_FOUND;
1077 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1079 token->next = true;
1080 return 1;
1082 else if (*wps_bufptr == '<')
1084 return 0;
1086 else
1088 token->type = WPS_NO_TOKEN;
1089 return 0;
1092 else
1094 /* This %C tag is in a conditional construct. */
1095 wps_data->albumart_cond_index = condindex[level];
1096 return 0;
1099 #endif /* HAVE_ALBUMART */
1102 /* Parse a generic token from the given string. Return the length read */
1103 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1105 int skip = 0, taglen = 0, ret;
1106 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1107 const struct wps_tag *tag;
1109 switch(*wps_bufptr)
1112 case '%':
1113 case '<':
1114 case '|':
1115 case '>':
1116 case ';':
1117 case '#':
1118 /* escaped characters */
1119 token->type = WPS_TOKEN_CHARACTER;
1120 token->value.c = *wps_bufptr;
1121 taglen = 1;
1122 wps_data->num_tokens++;
1123 break;
1125 case '?':
1126 /* conditional tag */
1127 token->type = WPS_TOKEN_CONDITIONAL;
1128 level++;
1129 condindex[level] = wps_data->num_tokens;
1130 numoptions[level] = 1;
1131 wps_data->num_tokens++;
1132 ret = parse_token(wps_bufptr + 1, wps_data);
1133 if (ret < 0) return ret;
1134 taglen = 1 + ret;
1135 break;
1137 default:
1138 /* find what tag we have */
1139 for (tag = all_tags;
1140 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1141 tag++) ;
1143 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1144 token->type = tag->type;
1145 wps_data->sublines[wps_data->num_sublines].line_type |=
1146 tag->refresh_type;
1148 /* if the tag has a special parsing function, we call it */
1149 if (tag->parse_func)
1151 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1152 if (ret < 0) return ret;
1153 skip += ret;
1156 /* Some tags we don't want to save as tokens */
1157 if (tag->type == WPS_NO_TOKEN)
1158 break;
1160 /* tags that start with 'F', 'I' or 'D' are for the next file */
1161 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1162 *(tag->name) == 'D')
1163 token->next = true;
1165 wps_data->num_tokens++;
1166 break;
1169 skip += taglen;
1170 return skip;
1173 /* Parses the WPS.
1174 data is the pointer to the structure where the parsed WPS should be stored.
1175 It is initialised.
1176 wps_bufptr points to the string containing the WPS tags */
1177 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1179 if (!data || !wps_bufptr || !*wps_bufptr)
1180 return false;
1182 char *stringbuf = data->string_buffer;
1183 int stringbuf_used = 0;
1184 int fail = 0;
1185 int ret;
1186 line = 1;
1187 level = -1;
1189 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1190 && data->num_viewports < WPS_MAX_VIEWPORTS
1191 && data->num_lines < WPS_MAX_LINES)
1193 switch(*wps_bufptr++)
1196 /* Regular tag */
1197 case '%':
1198 if ((ret = parse_token(wps_bufptr, data)) < 0)
1200 fail = PARSE_FAIL_COND_INVALID_PARAM;
1201 break;
1203 wps_bufptr += ret;
1204 break;
1206 /* Alternating sublines separator */
1207 case ';':
1208 if (level >= 0) /* there are unclosed conditionals */
1210 fail = PARSE_FAIL_UNCLOSED_COND;
1211 break;
1214 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1215 wps_start_new_subline(data);
1216 else
1217 wps_bufptr += skip_end_of_line(wps_bufptr);
1219 break;
1221 /* Conditional list start */
1222 case '<':
1223 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1225 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1226 break;
1229 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1230 lastcond[level] = data->num_tokens++;
1231 break;
1233 /* Conditional list end */
1234 case '>':
1235 if (level < 0) /* not in a conditional, invalid char */
1237 fail = PARSE_FAIL_INVALID_CHAR;
1238 break;
1241 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1242 if (lastcond[level])
1243 data->tokens[lastcond[level]].value.i = data->num_tokens;
1244 else
1246 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1247 break;
1250 lastcond[level] = 0;
1251 data->num_tokens++;
1252 data->tokens[condindex[level]].value.i = numoptions[level];
1253 level--;
1254 break;
1256 /* Conditional list option */
1257 case '|':
1258 if (level < 0) /* not in a conditional, invalid char */
1260 fail = PARSE_FAIL_INVALID_CHAR;
1261 break;
1264 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1265 if (lastcond[level])
1266 data->tokens[lastcond[level]].value.i = data->num_tokens;
1267 else
1269 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1270 break;
1273 lastcond[level] = data->num_tokens;
1274 numoptions[level]++;
1275 data->num_tokens++;
1276 break;
1278 /* Comment */
1279 case '#':
1280 if (level >= 0) /* there are unclosed conditionals */
1282 fail = PARSE_FAIL_UNCLOSED_COND;
1283 break;
1286 wps_bufptr += skip_end_of_line(wps_bufptr);
1287 break;
1289 /* End of this line */
1290 case '\n':
1291 if (level >= 0) /* there are unclosed conditionals */
1293 fail = PARSE_FAIL_UNCLOSED_COND;
1294 break;
1297 line++;
1298 wps_start_new_subline(data);
1299 data->num_lines++; /* Start a new line */
1301 if ((data->num_lines < WPS_MAX_LINES) &&
1302 (data->num_sublines < WPS_MAX_SUBLINES))
1304 data->lines[data->num_lines].first_subline_idx =
1305 data->num_sublines;
1307 data->sublines[data->num_sublines].first_token_idx =
1308 data->num_tokens;
1311 break;
1313 /* String */
1314 default:
1316 unsigned int len = 1;
1317 const char *string_start = wps_bufptr - 1;
1319 /* find the length of the string */
1320 while (*wps_bufptr && *wps_bufptr != '#' &&
1321 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1322 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1323 *wps_bufptr != '|' && *wps_bufptr != '\n')
1325 wps_bufptr++;
1326 len++;
1329 /* look if we already have that string */
1330 char **str;
1331 int i;
1332 bool found;
1333 for (i = 0, str = data->strings, found = false;
1334 i < data->num_strings &&
1335 !(found = (strlen(*str) == len &&
1336 strncmp(string_start, *str, len) == 0));
1337 i++, str++);
1338 /* If a matching string is found, found is true and i is
1339 the index of the string. If not, found is false */
1341 /* If it's NOT a duplicate, do nothing if we already have
1342 too many unique strings */
1343 if (found ||
1344 (stringbuf_used < STRING_BUFFER_SIZE - 1 &&
1345 data->num_strings < WPS_MAX_STRINGS))
1347 if (!found)
1349 /* new string */
1351 /* truncate? */
1352 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1)
1353 len = STRING_BUFFER_SIZE - stringbuf_used - 1;
1355 strncpy(stringbuf, string_start, len);
1356 *(stringbuf + len) = '\0';
1358 data->strings[data->num_strings] = stringbuf;
1359 stringbuf += len + 1;
1360 stringbuf_used += len + 1;
1361 data->tokens[data->num_tokens].value.i =
1362 data->num_strings;
1363 data->num_strings++;
1365 else
1367 /* another ocurrence of an existing string */
1368 data->tokens[data->num_tokens].value.i = i;
1370 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1371 data->num_tokens++;
1374 break;
1378 if (!fail && level >= 0) /* there are unclosed conditionals */
1379 fail = PARSE_FAIL_UNCLOSED_COND;
1381 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1383 /* We have finished with the last viewport, so increment count */
1384 data->num_viewports++;
1386 #ifdef DEBUG
1387 print_debug_info(data, fail, line);
1388 #endif
1390 return (fail == 0);
1393 #ifdef HAVE_LCD_BITMAP
1394 /* Clear the WPS image cache */
1395 static void wps_images_clear(struct wps_data *data)
1397 int i;
1398 /* set images to unloaded and not displayed */
1399 for (i = 0; i < MAX_IMAGES; i++)
1401 data->img[i].loaded = false;
1402 data->img[i].display = -1;
1403 data->img[i].always_display = false;
1404 data->img[i].num_subimages = 1;
1407 #endif
1409 /* initial setup of wps_data */
1410 void wps_data_init(struct wps_data *wps_data)
1412 #ifdef HAVE_LCD_BITMAP
1413 wps_images_clear(wps_data);
1414 wps_data->wps_sb_tag = false;
1415 wps_data->show_sb_on_wps = false;
1416 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1417 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1418 wps_data->peak_meter_enabled = false;
1419 /* progress bars */
1420 wps_data->progressbar_count = 0;
1421 #else /* HAVE_LCD_CHARCELLS */
1422 int i;
1423 for (i = 0; i < 8; i++)
1425 wps_data->wps_progress_pat[i] = 0;
1427 wps_data->full_line_progressbar = false;
1428 #endif
1429 wps_data->wps_loaded = false;
1432 static void wps_reset(struct wps_data *data)
1434 #ifdef HAVE_REMOTE_LCD
1435 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1436 #endif
1437 memset(data, 0, sizeof(*data));
1438 #ifdef HAVE_ALBUMART
1439 data->wps_uses_albumart = WPS_ALBUMART_NONE;
1440 #endif
1441 wps_data_init(data);
1442 #ifdef HAVE_REMOTE_LCD
1443 data->remote_wps = rwps;
1444 #endif
1447 #ifdef HAVE_LCD_BITMAP
1449 static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1451 char img_path[MAX_PATH];
1452 struct bitmap *bitmap;
1453 bool *loaded;
1454 int n;
1455 for (n = 0; n < BACKDROP_BMP; n++)
1457 if (bmp_names[n])
1459 get_image_filename(bmp_names[n], bmpdir,
1460 img_path, sizeof(img_path));
1462 if (n >= PROGRESSBAR_BMP ) {
1463 /* progressbar bitmap */
1464 bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
1465 loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
1466 } else {
1467 /* regular bitmap */
1468 bitmap = &wps_data->img[n].bm;
1469 loaded = &wps_data->img[n].loaded;
1472 /* load the image */
1473 bitmap->data = wps_data->img_buf_ptr;
1474 if (load_bitmap(wps_data, img_path, bitmap))
1476 *loaded = true;
1478 /* Calculate and store height if this image has sub-images */
1479 if (n < MAX_IMAGES)
1480 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1481 wps_data->img[n].num_subimages;
1483 else
1485 /* Abort if we can't load an image */
1486 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1487 return false;
1492 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1493 if (bmp_names[BACKDROP_BMP])
1495 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1496 img_path, sizeof(img_path));
1498 #if defined(HAVE_REMOTE_LCD)
1499 /* We only need to check LCD type if there is a remote LCD */
1500 if (!wps_data->remote_wps)
1501 #endif
1503 /* Load backdrop for the main LCD */
1504 if (!load_wps_backdrop(img_path))
1505 return false;
1507 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1508 else
1510 /* Load backdrop for the remote LCD */
1511 if (!load_remote_wps_backdrop(img_path))
1512 return false;
1514 #endif
1516 #endif /* has backdrop support */
1518 /* If we got here, everything was OK */
1519 return true;
1522 #endif /* HAVE_LCD_BITMAP */
1524 /* Skip leading UTF-8 BOM, if present. */
1525 static char *skip_utf8_bom(char *buf)
1527 unsigned char *s = (unsigned char *)buf;
1529 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
1531 buf += 3;
1534 return buf;
1537 /* to setup up the wps-data from a format-buffer (isfile = false)
1538 from a (wps-)file (isfile = true)*/
1539 bool wps_data_load(struct wps_data *wps_data,
1540 struct screen *display,
1541 const char *buf,
1542 bool isfile)
1544 if (!wps_data || !buf)
1545 return false;
1547 wps_reset(wps_data);
1549 /* Initialise the first (default) viewport */
1550 wps_data->viewports[0].vp.x = 0;
1551 wps_data->viewports[0].vp.width = display->width;
1552 if (!global_settings.statusbar)
1554 wps_data->viewports[0].vp.y = 0;
1555 wps_data->viewports[0].vp.height = display->height;
1557 else
1559 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1560 wps_data->viewports[0].vp.height = display->height - STATUSBAR_HEIGHT;
1562 #ifdef HAVE_LCD_BITMAP
1563 wps_data->viewports[0].vp.font = FONT_UI;
1564 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1565 #endif
1566 #if LCD_DEPTH > 1
1567 if (display->depth > 1)
1569 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1570 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1572 #endif
1573 if (!isfile)
1575 return wps_parse(wps_data, buf);
1577 else
1580 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1581 * wants to be a virtual file. Feel free to modify dirbrowse()
1582 * if you're feeling brave.
1584 #ifndef __PCTOOL__
1585 if (! strcmp(buf, WPS_DEFAULTCFG) )
1587 global_settings.wps_file[0] = 0;
1588 return false;
1591 #ifdef HAVE_REMOTE_LCD
1592 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1594 global_settings.rwps_file[0] = 0;
1595 return false;
1597 #endif
1598 #endif /* __PCTOOL__ */
1600 int fd = open(buf, O_RDONLY);
1602 if (fd < 0)
1603 return false;
1605 /* get buffer space from the plugin buffer */
1606 size_t buffersize = 0;
1607 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1609 if (!wps_buffer)
1610 return false;
1612 /* copy the file's content to the buffer for parsing,
1613 ensuring that every line ends with a newline char. */
1614 unsigned int start = 0;
1615 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1617 start += strlen(wps_buffer + start);
1618 if (start < buffersize - 1)
1620 wps_buffer[start++] = '\n';
1621 wps_buffer[start] = 0;
1625 close(fd);
1627 if (start <= 0)
1628 return false;
1630 #ifdef HAVE_LCD_BITMAP
1631 /* Set all filename pointers to NULL */
1632 memset(bmp_names, 0, sizeof(bmp_names));
1633 #endif
1635 /* Skip leading UTF-8 BOM, if present. */
1636 wps_buffer = skip_utf8_bom(wps_buffer);
1638 /* parse the WPS source */
1639 if (!wps_parse(wps_data, wps_buffer)) {
1640 wps_reset(wps_data);
1641 return false;
1644 wps_data->wps_loaded = true;
1646 #ifdef HAVE_LCD_BITMAP
1647 /* get the bitmap dir */
1648 char bmpdir[MAX_PATH];
1649 size_t bmpdirlen;
1650 char *dot = strrchr(buf, '.');
1651 bmpdirlen = dot - buf;
1652 strncpy(bmpdir, buf, dot - buf);
1653 bmpdir[bmpdirlen] = 0;
1655 /* load the bitmaps that were found by the parsing */
1656 if (!load_wps_bitmaps(wps_data, bmpdir)) {
1657 wps_reset(wps_data);
1658 return false;
1660 #endif
1661 return true;
1665 int wps_subline_index(struct wps_data *data, int line, int subline)
1667 return data->lines[line].first_subline_idx + subline;
1670 int wps_first_token_index(struct wps_data *data, int line, int subline)
1672 int first_subline_idx = data->lines[line].first_subline_idx;
1673 return data->sublines[first_subline_idx + subline].first_token_idx;
1676 int wps_last_token_index(struct wps_data *data, int line, int subline)
1678 int first_subline_idx = data->lines[line].first_subline_idx;
1679 int idx = first_subline_idx + subline;
1680 if (idx < data->num_sublines - 1)
1682 /* This subline ends where the next begins */
1683 return data->sublines[idx+1].first_token_idx - 1;
1685 else
1687 /* The last subline goes to the end */
1688 return data->num_tokens - 1;