after resizing the theme installation window don't use the scaled image for the real...
[Rockbox.git] / apps / gui / wps_parser.c
blob8471bff7d89050bc8ef3215f70ce33beae2f2633
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 <ctype.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "atoi.h"
26 #include "gwps.h"
27 #ifndef __PCTOOL__
28 #include "settings.h"
29 #include "debug.h"
30 #include "plugin.h"
32 #ifdef HAVE_LCD_BITMAP
33 #include "bmp.h"
34 #endif
36 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
37 #include "backdrop.h"
38 #endif
40 #endif
42 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
43 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
45 #define PARSE_FAIL_UNCLOSED_COND 1
46 #define PARSE_FAIL_INVALID_CHAR 2
47 #define PARSE_FAIL_COND_SYNTAX_ERROR 3
49 /* level of current conditional.
50 -1 means we're not in a conditional. */
51 static int level = -1;
53 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
54 or WPS_TOKEN_CONDITIONAL_START in current level */
55 static int lastcond[WPS_MAX_COND_LEVEL];
57 /* index of the WPS_TOKEN_CONDITIONAL in current level */
58 static int condindex[WPS_MAX_COND_LEVEL];
60 /* number of condtional options in current level */
61 static int numoptions[WPS_MAX_COND_LEVEL];
63 /* the current line in the file */
64 static int line;
66 #ifdef HAVE_LCD_BITMAP
68 #if LCD_DEPTH > 1
69 #define MAX_BITMAPS MAX_IMAGES+2 /* WPS images + pbar bitmap + backdrop */
70 #else
71 #define MAX_BITMAPS MAX_IMAGES+1 /* WPS images + pbar bitmap */
72 #endif
74 #define PROGRESSBAR_BMP MAX_IMAGES
75 #define BACKDROP_BMP MAX_IMAGES+1
77 /* pointers to the bitmap filenames in the WPS source */
78 static const char *bmp_names[MAX_BITMAPS];
80 #endif /* HAVE_LCD_BITMAP */
82 #ifdef DEBUG
83 /* debugging function */
84 extern void print_debug_info(struct wps_data *data, int fail, int line);
85 #endif
87 static void wps_reset(struct wps_data *data);
89 /* Function for parsing of details for a token. At the moment the
90 function is called, the token type has already been set. The
91 function must fill in the details and possibly add more tokens
92 to the token array. It should return the number of chars that
93 has been consumed.
95 wps_bufptr points to the char following the tag (i.e. where
96 details begin).
97 token is the pointer to the 'main' token being parsed
99 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
100 struct wps_token *token, struct wps_data *wps_data);
102 struct wps_tag {
103 enum wps_token_type type;
104 const char name[3];
105 unsigned char refresh_type;
106 const wps_tag_parse_func parse_func;
109 /* prototypes of all special parse functions : */
110 static int parse_subline_timeout(const char *wps_bufptr,
111 struct wps_token *token, struct wps_data *wps_data);
112 static int parse_progressbar(const char *wps_bufptr,
113 struct wps_token *token, struct wps_data *wps_data);
114 static int parse_dir_level(const char *wps_bufptr,
115 struct wps_token *token, struct wps_data *wps_data);
116 #ifdef HAVE_LCD_BITMAP
117 static int parse_image_special(const char *wps_bufptr,
118 struct wps_token *token, struct wps_data *wps_data);
119 static int parse_statusbar_enable(const char *wps_bufptr,
120 struct wps_token *token, struct wps_data *wps_data);
121 static int parse_statusbar_disable(const char *wps_bufptr,
122 struct wps_token *token, struct wps_data *wps_data);
123 static int parse_image_display(const char *wps_bufptr,
124 struct wps_token *token, struct wps_data *wps_data);
125 static int parse_image_load(const char *wps_bufptr,
126 struct wps_token *token, struct wps_data *wps_data);
127 #endif /*HAVE_LCD_BITMAP */
129 #ifdef CONFIG_RTC
130 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
131 #else
132 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
133 #endif
135 /* array of available tags - those with more characters have to go first
136 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
137 static const struct wps_tag all_tags[] = {
139 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
140 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
141 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
143 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
144 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
145 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
146 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
147 #if CONFIG_CHARGING >= CHARGING_MONITOR
148 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
149 #endif
150 #if CONFIG_CHARGING
151 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
152 #endif
154 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
155 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
156 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
157 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
158 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
159 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
160 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
161 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
162 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
163 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
164 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
165 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
166 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
167 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
168 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
169 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
170 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
172 /* current file */
173 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
174 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
175 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
176 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
177 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
178 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
179 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
180 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
181 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
182 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
183 parse_dir_level },
185 /* next file */
186 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
187 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
188 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
189 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
190 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
191 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
192 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
193 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
194 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
195 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
196 parse_dir_level },
198 /* current metadata */
199 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
200 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
201 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
202 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
203 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
204 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
205 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
206 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
207 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
208 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
209 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
210 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
212 /* next metadata */
213 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
214 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
215 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
216 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
217 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
218 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
219 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
220 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
221 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
222 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
223 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
224 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
226 #if (CONFIG_CODEC != MAS3507D)
227 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
228 #endif
230 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
231 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
232 #endif
234 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
236 #ifdef HAS_REMOTE_BUTTON_HOLD
237 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
238 #endif
240 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
241 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
243 #ifdef HAVE_LCD_BITMAP
244 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
245 #else
246 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
247 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
248 #endif
249 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
250 parse_progressbar },
252 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
254 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
255 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
256 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
257 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
264 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
265 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
266 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
267 #if CONFIG_CODEC == SWCODEC
268 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
270 #endif
272 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
273 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_subline_timeout },
275 #ifdef HAVE_LCD_BITMAP
276 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
277 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
279 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
281 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
282 parse_image_display },
284 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
285 { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special },
286 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
287 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
288 #endif
289 #endif
291 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
292 /* the array MUST end with an empty string (first char is \0) */
295 /* Returns the number of chars that should be skipped to jump
296 immediately after the first eol, i.e. to the start of the next line */
297 static int skip_end_of_line(const char *wps_bufptr)
299 line++;
300 int skip = 0;
301 while(*(wps_bufptr + skip) != '\n')
302 skip++;
303 return ++skip;
306 /* Starts a new subline in the current line during parsing */
307 static void wps_start_new_subline(struct wps_data *data)
309 data->num_sublines++;
310 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
311 data->lines[data->num_lines].num_sublines++;
314 #ifdef HAVE_LCD_BITMAP
316 static int parse_statusbar_enable(const char *wps_bufptr,
317 struct wps_token *token,
318 struct wps_data *wps_data)
320 (void)token; /* Kill warnings */
321 wps_data->wps_sb_tag = true;
322 wps_data->show_sb_on_wps = true;
323 return skip_end_of_line(wps_bufptr);
326 static int parse_statusbar_disable(const char *wps_bufptr,
327 struct wps_token *token,
328 struct wps_data *wps_data)
330 (void)token; /* Kill warnings */
331 wps_data->wps_sb_tag = true;
332 wps_data->show_sb_on_wps = false;
333 return skip_end_of_line(wps_bufptr);
336 static bool load_bitmap(struct wps_data *wps_data,
337 char* filename,
338 struct bitmap *bm)
340 int format;
341 #ifdef HAVE_REMOTE_LCD
342 if (wps_data->remote_wps)
343 format = FORMAT_ANY|FORMAT_REMOTE;
344 else
345 #endif
346 format = FORMAT_ANY|FORMAT_TRANSPARENT;
348 int ret = read_bmp_file(filename, bm,
349 wps_data->img_buf_free,
350 format);
352 if (ret > 0)
354 #if LCD_DEPTH == 16
355 if (ret % 2) ret++;
356 /* Always consume an even number of bytes */
357 #endif
358 wps_data->img_buf_ptr += ret;
359 wps_data->img_buf_free -= ret;
361 return true;
363 else
364 return false;
367 static int get_image_id(int c)
369 if(c >= 'a' && c <= 'z')
370 return c - 'a';
371 else if(c >= 'A' && c <= 'Z')
372 return c - 'A' + 26;
373 else
374 return -1;
377 static char *get_image_filename(const char *start, const char* bmpdir,
378 char *buf, int buf_size)
380 const char *end = strchr(start, '|');
382 if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
384 buf = "\0";
385 return NULL;
388 int bmpdirlen = strlen(bmpdir);
390 strcpy(buf, bmpdir);
391 buf[bmpdirlen] = '/';
392 memcpy( &buf[bmpdirlen + 1], start, end - start);
393 buf[bmpdirlen + 1 + end - start] = 0;
395 return buf;
398 static int parse_image_display(const char *wps_bufptr,
399 struct wps_token *token,
400 struct wps_data *wps_data)
402 int n = get_image_id(*wps_bufptr);
404 if (n == -1)
406 /* invalid picture display tag */
407 token->type = WPS_TOKEN_UNKNOWN;
408 return 0;
411 token->value.i = n;
413 /* if the image is in a conditional, remember it */
414 if (level >= 0)
415 wps_data->img[n].cond_index = condindex[level];
417 return 1;
420 static int parse_image_load(const char *wps_bufptr,
421 struct wps_token *token,
422 struct wps_data *wps_data)
424 int n;
425 const char *ptr = wps_bufptr;
426 char *pos = NULL;
428 /* format: %x|n|filename.bmp|x|y|
429 or %xl|n|filename.bmp|x|y| */
431 ptr = strchr(ptr, '|') + 1;
432 pos = strchr(ptr, '|');
434 if (!pos)
435 return 0;
437 /* get the image ID */
438 n = get_image_id(*ptr);
440 /* check the image number and load state */
441 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
443 /* Skip the rest of the line */
444 return 0;
447 ptr = pos + 1;
449 /* get image name */
450 bmp_names[n] = ptr;
452 pos = strchr(ptr, '|');
453 ptr = pos + 1;
455 /* get x-position */
456 pos = strchr(ptr, '|');
457 if (pos)
458 wps_data->img[n].x = atoi(ptr);
459 else
461 /* weird syntax, bail out */
462 return 0;
465 /* get y-position */
466 ptr = pos + 1;
467 pos = strchr(ptr, '|');
468 if (pos)
469 wps_data->img[n].y = atoi(ptr);
470 else
472 /* weird syntax, bail out */
473 return 0;
476 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
477 wps_data->img[n].always_display = true;
479 /* Skip the rest of the line */
480 return skip_end_of_line(wps_bufptr);
483 static int parse_image_special(const char *wps_bufptr,
484 struct wps_token *token,
485 struct wps_data *wps_data)
487 if (token->type == WPS_TOKEN_IMAGE_PROGRESS_BAR)
489 /* format: %P|filename.bmp| */
490 bmp_names[PROGRESSBAR_BMP] = wps_bufptr + 1;
492 #if LCD_DEPTH > 1
493 else if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
495 /* format: %X|filename.bmp| */
496 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
498 #endif
500 (void)wps_data; /* to avoid a warning */
502 /* Skip the rest of the line */
503 return skip_end_of_line(wps_bufptr);
506 #endif /* HAVE_LCD_BITMAP */
508 static int parse_dir_level(const char *wps_bufptr,
509 struct wps_token *token,
510 struct wps_data *wps_data)
512 char val[] = { *wps_bufptr, '\0' };
513 token->value.i = atoi(val);
514 (void)wps_data; /* Kill warnings */
515 return 1;
518 static int parse_subline_timeout(const char *wps_bufptr,
519 struct wps_token *token,
520 struct wps_data *wps_data)
522 int skip = 0;
523 int val = 0;
524 bool have_point = false;
525 bool have_tenth = false;
527 (void)wps_data; /* Kill the warning */
529 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
531 if (*wps_bufptr != '.')
533 val *= 10;
534 val += *wps_bufptr - '0';
535 if (have_point)
537 have_tenth = true;
538 wps_bufptr++;
539 skip++;
540 break;
543 else
544 have_point = true;
546 wps_bufptr++;
547 skip++;
550 if (have_tenth == false)
551 val *= 10;
553 token->value.i = val;
555 return skip;
558 static int parse_progressbar(const char *wps_bufptr,
559 struct wps_token *token,
560 struct wps_data *wps_data)
562 (void)token; /* Kill warnings */
563 #ifdef HAVE_LCD_BITMAP
565 short *vals[] = {
566 &wps_data->progress_height,
567 &wps_data->progress_start,
568 &wps_data->progress_end,
569 &wps_data->progress_top };
571 /* default values : */
572 wps_data->progress_height = 6;
573 wps_data->progress_start = 0;
574 wps_data->progress_end = 0;
575 wps_data->progress_top = -1;
577 int i = 0;
578 char *newline = strchr(wps_bufptr, '\n');
579 char *prev = strchr(wps_bufptr, '|');
580 if (prev && prev < newline) {
581 char *next = strchr(prev+1, '|');
582 while (i < 4 && next && next < newline)
584 *(vals[i++]) = atoi(++prev);
585 prev = strchr(prev, '|');
586 next = strchr(++next, '|');
589 if (wps_data->progress_height < 3)
590 wps_data->progress_height = 3;
591 if (wps_data->progress_end < wps_data->progress_start + 3)
592 wps_data->progress_end = 0;
595 return newline - wps_bufptr;
597 #else
599 if (*(wps_bufptr-1) == 'f')
600 wps_data->full_line_progressbar = true;
601 else
602 wps_data->full_line_progressbar = false;
604 return 0;
606 #endif
609 /* Parse a generic token from the given string. Return the length read */
610 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
612 int skip = 0, taglen = 0;
613 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
614 const struct wps_tag *tag;
616 switch(*wps_bufptr)
619 case '%':
620 case '<':
621 case '|':
622 case '>':
623 case ';':
624 case '#':
625 /* escaped characters */
626 token->type = WPS_TOKEN_CHARACTER;
627 token->value.c = *wps_bufptr;
628 taglen = 1;
629 wps_data->num_tokens++;
630 break;
632 case '?':
633 /* conditional tag */
634 token->type = WPS_TOKEN_CONDITIONAL;
635 level++;
636 condindex[level] = wps_data->num_tokens;
637 numoptions[level] = 1;
638 wps_data->num_tokens++;
639 taglen = 1 + parse_token(wps_bufptr + 1, wps_data);
640 break;
642 default:
643 /* find what tag we have */
644 for (tag = all_tags;
645 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
646 tag++) ;
648 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
649 token->type = tag->type;
650 wps_data->sublines[wps_data->num_sublines].line_type |=
651 tag->refresh_type;
653 /* if the tag has a special parsing function, we call it */
654 if (tag->parse_func)
655 skip += tag->parse_func(wps_bufptr + taglen, token, wps_data);
657 /* Some tags we don't want to save as tokens */
658 if (tag->type == WPS_NO_TOKEN)
659 break;
661 /* tags that start with 'F', 'I' or 'D' are for the next file */
662 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
663 *(tag->name) == 'D')
664 token->next = true;
666 wps_data->num_tokens++;
667 break;
670 skip += taglen;
671 return skip;
674 /* Parses the WPS.
675 data is the pointer to the structure where the parsed WPS should be stored.
676 It is initialised.
677 wps_bufptr points to the string containing the WPS tags */
678 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
680 if (!data || !wps_bufptr || !*wps_bufptr)
681 return false;
683 char *stringbuf = data->string_buffer;
684 int stringbuf_used = 0;
685 int fail = 0;
686 line = 1;
687 level = -1;
689 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
690 && data->num_lines < WPS_MAX_LINES)
692 switch(*wps_bufptr++)
695 /* Regular tag */
696 case '%':
697 wps_bufptr += parse_token(wps_bufptr, data);
698 break;
700 /* Alternating sublines separator */
701 case ';':
702 if (level >= 0) /* there are unclosed conditionals */
704 fail = PARSE_FAIL_UNCLOSED_COND;
705 break;
708 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
709 wps_start_new_subline(data);
710 else
711 wps_bufptr += skip_end_of_line(wps_bufptr);
713 break;
715 /* Conditional list start */
716 case '<':
717 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
719 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
720 break;
723 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
724 lastcond[level] = data->num_tokens++;
725 break;
727 /* Conditional list end */
728 case '>':
729 if (level < 0) /* not in a conditional, invalid char */
731 fail = PARSE_FAIL_INVALID_CHAR;
732 break;
735 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
736 if (lastcond[level])
737 data->tokens[lastcond[level]].value.i = data->num_tokens;
739 lastcond[level] = 0;
740 data->num_tokens++;
741 data->tokens[condindex[level]].value.i = numoptions[level];
742 level--;
743 break;
745 /* Conditional list option */
746 case '|':
747 if (level < 0) /* not in a conditional, invalid char */
749 fail = PARSE_FAIL_INVALID_CHAR;
750 break;
753 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
754 if (lastcond[level])
755 data->tokens[lastcond[level]].value.i = data->num_tokens;
757 lastcond[level] = data->num_tokens;
758 numoptions[level]++;
759 data->num_tokens++;
760 break;
762 /* Comment */
763 case '#':
764 if (level >= 0) /* there are unclosed conditionals */
766 fail = PARSE_FAIL_UNCLOSED_COND;
767 break;
770 wps_bufptr += skip_end_of_line(wps_bufptr);
771 break;
773 /* End of this line */
774 case '\n':
775 if (level >= 0) /* there are unclosed conditionals */
777 fail = PARSE_FAIL_UNCLOSED_COND;
778 break;
781 line++;
782 wps_start_new_subline(data);
783 data->num_lines++; /* Start a new line */
785 if ((data->num_lines < WPS_MAX_LINES) &&
786 (data->num_sublines < WPS_MAX_SUBLINES))
788 data->lines[data->num_lines].first_subline_idx =
789 data->num_sublines;
791 data->sublines[data->num_sublines].first_token_idx =
792 data->num_tokens;
795 break;
797 /* String */
798 default:
800 unsigned int len = 1;
801 const char *string_start = wps_bufptr - 1;
803 /* find the length of the string */
804 while (*wps_bufptr && *wps_bufptr != '#' &&
805 *wps_bufptr != '%' && *wps_bufptr != ';' &&
806 *wps_bufptr != '<' && *wps_bufptr != '>' &&
807 *wps_bufptr != '|' && *wps_bufptr != '\n')
809 wps_bufptr++;
810 len++;
813 /* look if we already have that string */
814 char **str;
815 int i;
816 bool found;
817 for (i = 0, str = data->strings, found = false;
818 i < data->num_strings &&
819 !(found = (strlen(*str) == len &&
820 strncmp(string_start, *str, len) == 0));
821 i++, str++);
822 /* If a matching string is found, found is true and i is
823 the index of the string. If not, found is false */
825 /* If it's NOT a duplicate, do nothing if we already have
826 too many unique strings */
827 if (found ||
828 (stringbuf_used < STRING_BUFFER_SIZE - 1 &&
829 data->num_strings < WPS_MAX_STRINGS))
831 if (!found)
833 /* new string */
835 /* truncate? */
836 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1)
837 len = STRING_BUFFER_SIZE - stringbuf_used - 1;
839 strncpy(stringbuf, string_start, len);
840 *(stringbuf + len) = '\0';
842 data->strings[data->num_strings] = stringbuf;
843 stringbuf += len + 1;
844 stringbuf_used += len + 1;
845 data->tokens[data->num_tokens].value.i =
846 data->num_strings;
847 data->num_strings++;
849 else
851 /* another ocurrence of an existing string */
852 data->tokens[data->num_tokens].value.i = i;
854 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
855 data->num_tokens++;
858 break;
862 if (level >= 0) /* there are unclosed conditionals */
863 fail = PARSE_FAIL_UNCLOSED_COND;
865 #ifdef DEBUG
866 print_debug_info(data, fail, line);
867 #endif
869 if (fail)
870 wps_reset(data);
872 return (fail == 0);
875 #ifdef HAVE_LCD_BITMAP
876 /* Clear the WPS image cache */
877 static void wps_images_clear(struct wps_data *data)
879 int i;
880 /* set images to unloaded and not displayed */
881 for (i = 0; i < MAX_IMAGES; i++)
883 data->img[i].loaded = false;
884 data->img[i].display = false;
885 data->img[i].always_display = false;
887 data->progressbar.have_bitmap_pb = false;
889 #endif
891 /* initial setup of wps_data */
892 void wps_data_init(struct wps_data *wps_data)
894 #ifdef HAVE_LCD_BITMAP
895 wps_images_clear(wps_data);
896 wps_data->wps_sb_tag = false;
897 wps_data->show_sb_on_wps = false;
898 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
899 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
900 wps_data->peak_meter_enabled = false;
901 #else /* HAVE_LCD_CHARCELLS */
902 int i;
903 for (i = 0; i < 8; i++)
905 wps_data->wps_progress_pat[i] = 0;
907 wps_data->full_line_progressbar = false;
908 #endif
909 wps_data->wps_loaded = false;
912 static void wps_reset(struct wps_data *data)
914 #ifdef HAVE_REMOTE_LCD
915 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
916 #endif
917 memset(data, 0, sizeof(*data));
918 wps_data_init(data);
919 #ifdef HAVE_REMOTE_LCD
920 data->remote_wps = rwps;
921 #endif
924 #ifdef HAVE_LCD_BITMAP
927 static void clear_bmp_names(void)
929 int n;
930 for (n = 0; n < MAX_BITMAPS; n++)
932 bmp_names[n] = NULL;
936 static void load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
938 char img_path[MAX_PATH];
939 struct bitmap *bitmap;
940 bool *loaded;
942 int n;
943 for (n = 0; n < BACKDROP_BMP; n++)
945 if (bmp_names[n])
947 get_image_filename(bmp_names[n], bmpdir,
948 img_path, sizeof(img_path));
950 if (n == PROGRESSBAR_BMP) {
951 /* progressbar bitmap */
952 bitmap = &wps_data->progressbar.bm;
953 loaded = &wps_data->progressbar.have_bitmap_pb;
954 } else {
955 /* regular bitmap */
956 bitmap = &wps_data->img[n].bm;
957 loaded = &wps_data->img[n].loaded;
960 /* load the image */
961 bitmap->data = wps_data->img_buf_ptr;
962 if (load_bitmap(wps_data, img_path, bitmap))
964 *loaded = true;
969 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
970 if (bmp_names[BACKDROP_BMP])
972 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
973 img_path, sizeof(img_path));
974 #ifdef HAVE_REMOTE_LCD
975 if (wps_data->remote_wps)
976 #if LCD_REMOTE_DEPTH > 1
977 load_remote_wps_backdrop(img_path)
978 #endif
980 else
981 #endif /* HAVE_REMOTE_LCD */
982 load_wps_backdrop(img_path);
984 #endif /* has backdrop support */
987 #endif /* HAVE_LCD_BITMAP */
989 /* Skip leading UTF-8 BOM, if present. */
990 static char *skip_utf8_bom(char *buf)
992 unsigned char *s = (unsigned char *)buf;
994 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
996 buf += 3;
999 return buf;
1002 /* to setup up the wps-data from a format-buffer (isfile = false)
1003 from a (wps-)file (isfile = true)*/
1004 bool wps_data_load(struct wps_data *wps_data,
1005 const char *buf,
1006 bool isfile)
1008 if (!wps_data || !buf)
1009 return false;
1011 wps_reset(wps_data);
1013 if (!isfile)
1015 return wps_parse(wps_data, buf);
1017 else
1020 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1021 * wants to be a virtual file. Feel free to modify dirbrowse()
1022 * if you're feeling brave.
1024 #ifndef __PCTOOL__
1025 if (! strcmp(buf, WPS_DEFAULTCFG) )
1027 global_settings.wps_file[0] = 0;
1028 return false;
1031 #ifdef HAVE_REMOTE_LCD
1032 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1034 global_settings.rwps_file[0] = 0;
1035 return false;
1037 #endif
1038 #endif /* __PCTOOL__ */
1040 int fd = open(buf, O_RDONLY);
1042 if (fd < 0)
1043 return false;
1045 /* get buffer space from the plugin buffer */
1046 size_t buffersize = 0;
1047 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1049 if (!wps_buffer)
1050 return false;
1052 /* copy the file's content to the buffer for parsing,
1053 ensuring that every line ends with a newline char. */
1054 unsigned int start = 0;
1055 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1057 start += strlen(wps_buffer + start);
1058 if (start < buffersize - 1)
1060 wps_buffer[start++] = '\n';
1061 wps_buffer[start] = 0;
1065 close(fd);
1067 if (start <= 0)
1068 return false;
1070 #ifdef HAVE_LCD_BITMAP
1071 clear_bmp_names();
1072 #endif
1074 /* Skip leading UTF-8 BOM, if present. */
1075 wps_buffer = skip_utf8_bom(wps_buffer);
1077 /* parse the WPS source */
1078 if (!wps_parse(wps_data, wps_buffer))
1079 return false;
1081 wps_data->wps_loaded = true;
1083 #ifdef HAVE_LCD_BITMAP
1084 /* get the bitmap dir */
1085 char bmpdir[MAX_PATH];
1086 size_t bmpdirlen;
1087 char *dot = strrchr(buf, '.');
1088 bmpdirlen = dot - buf;
1089 strncpy(bmpdir, buf, dot - buf);
1090 bmpdir[bmpdirlen] = 0;
1092 /* load the bitmaps that were found by the parsing */
1093 load_wps_bitmaps(wps_data, bmpdir);
1094 #endif
1095 return true;
1099 int wps_subline_index(struct wps_data *data, int line, int subline)
1101 return data->lines[line].first_subline_idx + subline;
1104 int wps_first_token_index(struct wps_data *data, int line, int subline)
1106 int first_subline_idx = data->lines[line].first_subline_idx;
1107 return data->sublines[first_subline_idx + subline].first_token_idx;
1110 int wps_last_token_index(struct wps_data *data, int line, int subline)
1112 int first_subline_idx = data->lines[line].first_subline_idx;
1113 int idx = first_subline_idx + subline;
1114 if (idx < data->num_sublines - 1)
1116 /* This subline ends where the next begins */
1117 return data->sublines[idx+1].first_token_idx - 1;
1119 else
1121 /* The last subline goes to the end */
1122 return data->num_tokens - 1;