added degree symbol
[Rockbox.git] / apps / gui / wps_parser.c
blob960eab43da1a0171585c4fd4d4f64fdfa2b78a7e
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 WPS_ERROR_INVALID_PARAM -1
47 #define PARSE_FAIL_UNCLOSED_COND 1
48 #define PARSE_FAIL_INVALID_CHAR 2
49 #define PARSE_FAIL_COND_SYNTAX_ERROR 3
50 #define PARSE_FAIL_COND_INVALID_PARAM 4
52 /* level of current conditional.
53 -1 means we're not in a conditional. */
54 static int level = -1;
56 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
57 or WPS_TOKEN_CONDITIONAL_START in current level */
58 static int lastcond[WPS_MAX_COND_LEVEL];
60 /* index of the WPS_TOKEN_CONDITIONAL in current level */
61 static int condindex[WPS_MAX_COND_LEVEL];
63 /* number of condtional options in current level */
64 static int numoptions[WPS_MAX_COND_LEVEL];
66 /* the current line in the file */
67 static int line;
69 #ifdef HAVE_LCD_BITMAP
71 #if LCD_DEPTH > 1
72 #define MAX_BITMAPS MAX_IMAGES+2 /* WPS images + pbar bitmap + backdrop */
73 #else
74 #define MAX_BITMAPS MAX_IMAGES+1 /* WPS images + pbar bitmap */
75 #endif
77 #define PROGRESSBAR_BMP MAX_IMAGES
78 #define BACKDROP_BMP MAX_IMAGES+1
80 /* pointers to the bitmap filenames in the WPS source */
81 static const char *bmp_names[MAX_BITMAPS];
83 #endif /* HAVE_LCD_BITMAP */
85 #ifdef DEBUG
86 /* debugging function */
87 extern void print_debug_info(struct wps_data *data, int fail, int line);
88 #endif
90 static void wps_reset(struct wps_data *data);
92 /* Function for parsing of details for a token. At the moment the
93 function is called, the token type has already been set. The
94 function must fill in the details and possibly add more tokens
95 to the token array. It should return the number of chars that
96 has been consumed.
98 wps_bufptr points to the char following the tag (i.e. where
99 details begin).
100 token is the pointer to the 'main' token being parsed
102 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
103 struct wps_token *token, struct wps_data *wps_data);
105 struct wps_tag {
106 enum wps_token_type type;
107 const char name[3];
108 unsigned char refresh_type;
109 const wps_tag_parse_func parse_func;
112 /* prototypes of all special parse functions : */
113 static int parse_subline_timeout(const char *wps_bufptr,
114 struct wps_token *token, struct wps_data *wps_data);
115 static int parse_progressbar(const char *wps_bufptr,
116 struct wps_token *token, struct wps_data *wps_data);
117 static int parse_dir_level(const char *wps_bufptr,
118 struct wps_token *token, struct wps_data *wps_data);
120 #ifdef HAVE_LCD_BITMAP
121 static int parse_leftmargin(const char *wps_bufptr,
122 struct wps_token *token, struct wps_data *wps_data);
123 static int parse_image_special(const char *wps_bufptr,
124 struct wps_token *token, struct wps_data *wps_data);
125 static int parse_statusbar_enable(const char *wps_bufptr,
126 struct wps_token *token, struct wps_data *wps_data);
127 static int parse_statusbar_disable(const char *wps_bufptr,
128 struct wps_token *token, struct wps_data *wps_data);
129 static int parse_image_display(const char *wps_bufptr,
130 struct wps_token *token, struct wps_data *wps_data);
131 static int parse_image_load(const char *wps_bufptr,
132 struct wps_token *token, struct wps_data *wps_data);
133 #endif /*HAVE_LCD_BITMAP */
135 #ifdef HAVE_ALBUMART
136 static int parse_albumart_load(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_albumart_conditional(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 #endif /* HAVE_ALBUMART */
142 #ifdef CONFIG_RTC
143 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
144 #else
145 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
146 #endif
148 /* array of available tags - those with more characters have to go first
149 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
150 static const struct wps_tag all_tags[] = {
152 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
153 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
154 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
156 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
157 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
158 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
159 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
160 #if CONFIG_CHARGING >= CHARGING_MONITOR
161 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
162 #endif
163 #if CONFIG_CHARGING
164 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
165 #endif
167 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
168 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
169 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
170 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
171 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
172 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
173 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
174 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
175 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
176 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
177 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
178 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
179 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
180 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
181 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
182 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
183 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
184 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
186 /* current file */
187 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
188 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
189 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
190 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
191 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
192 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
193 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
194 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
195 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
196 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
197 parse_dir_level },
199 /* next file */
200 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_DYNAMIC, NULL },
201 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
202 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
203 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
204 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
205 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
206 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
207 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
208 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
209 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
210 parse_dir_level },
212 /* current metadata */
213 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
214 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
215 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
216 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
217 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
226 /* next metadata */
227 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_DYNAMIC, NULL },
228 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
229 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
230 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
231 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
232 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
233 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
234 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
235 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
236 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
237 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
238 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
240 #if (CONFIG_CODEC != MAS3507D)
241 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
242 #endif
244 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
245 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
246 #endif
248 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
250 #ifdef HAS_REMOTE_BUTTON_HOLD
251 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
252 #else
253 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
254 #endif
256 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
257 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
259 #ifdef HAVE_LCD_BITMAP
260 { WPS_TOKEN_LEFTMARGIN, "m", 0, parse_leftmargin },
261 #endif
263 #ifdef HAVE_LCD_BITMAP
264 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
265 #else
266 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
267 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
268 #endif
269 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
270 parse_progressbar },
272 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
274 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
275 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
276 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
277 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
279 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
280 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
281 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
282 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
284 #ifdef HAVE_TAGCACHE
285 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
286 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
287 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
288 #endif
290 #if CONFIG_CODEC == SWCODEC
291 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
292 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
293 #endif
295 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
296 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_subline_timeout },
298 #ifdef HAVE_LCD_BITMAP
299 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
300 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
302 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
304 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
305 parse_image_display },
307 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
308 { WPS_TOKEN_IMAGE_PROGRESS_BAR, "P", 0, parse_image_special },
309 #ifdef HAVE_ALBUMART
310 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
311 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
312 parse_albumart_conditional },
313 #endif
314 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
315 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
316 #endif
317 #endif
319 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
320 /* the array MUST end with an empty string (first char is \0) */
323 /* Returns the number of chars that should be skipped to jump
324 immediately after the first eol, i.e. to the start of the next line */
325 static int skip_end_of_line(const char *wps_bufptr)
327 line++;
328 int skip = 0;
329 while(*(wps_bufptr + skip) != '\n')
330 skip++;
331 return ++skip;
334 /* Starts a new subline in the current line during parsing */
335 static void wps_start_new_subline(struct wps_data *data)
337 data->num_sublines++;
338 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
339 data->lines[data->num_lines].num_sublines++;
342 #ifdef HAVE_LCD_BITMAP
344 static int parse_statusbar_enable(const char *wps_bufptr,
345 struct wps_token *token,
346 struct wps_data *wps_data)
348 (void)token; /* Kill warnings */
349 wps_data->wps_sb_tag = true;
350 wps_data->show_sb_on_wps = true;
351 return skip_end_of_line(wps_bufptr);
354 static int parse_statusbar_disable(const char *wps_bufptr,
355 struct wps_token *token,
356 struct wps_data *wps_data)
358 (void)token; /* Kill warnings */
359 wps_data->wps_sb_tag = true;
360 wps_data->show_sb_on_wps = false;
361 return skip_end_of_line(wps_bufptr);
364 static bool load_bitmap(struct wps_data *wps_data,
365 char* filename,
366 struct bitmap *bm)
368 int format;
369 #ifdef HAVE_REMOTE_LCD
370 if (wps_data->remote_wps)
371 format = FORMAT_ANY|FORMAT_REMOTE;
372 else
373 #endif
374 format = FORMAT_ANY|FORMAT_TRANSPARENT;
376 int ret = read_bmp_file(filename, bm,
377 wps_data->img_buf_free,
378 format);
380 if (ret > 0)
382 #if LCD_DEPTH == 16
383 if (ret % 2) ret++;
384 /* Always consume an even number of bytes */
385 #endif
386 wps_data->img_buf_ptr += ret;
387 wps_data->img_buf_free -= ret;
389 return true;
391 else
392 return false;
395 static int get_image_id(int c)
397 if(c >= 'a' && c <= 'z')
398 return c - 'a';
399 else if(c >= 'A' && c <= 'Z')
400 return c - 'A' + 26;
401 else
402 return -1;
405 static char *get_image_filename(const char *start, const char* bmpdir,
406 char *buf, int buf_size)
408 const char *end = strchr(start, '|');
410 if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
412 buf = "\0";
413 return NULL;
416 int bmpdirlen = strlen(bmpdir);
418 strcpy(buf, bmpdir);
419 buf[bmpdirlen] = '/';
420 memcpy( &buf[bmpdirlen + 1], start, end - start);
421 buf[bmpdirlen + 1 + end - start] = 0;
423 return buf;
426 static int parse_image_display(const char *wps_bufptr,
427 struct wps_token *token,
428 struct wps_data *wps_data)
430 (void)wps_data;
431 int n = get_image_id(*wps_bufptr);
433 if (n == -1)
435 /* invalid picture display tag */
436 return WPS_ERROR_INVALID_PARAM;
439 token->value.i = n;
441 return 1;
444 static int parse_image_load(const char *wps_bufptr,
445 struct wps_token *token,
446 struct wps_data *wps_data)
448 int n;
449 const char *ptr = wps_bufptr;
450 const char *pos = NULL;
451 const char *newline;
453 /* format: %x|n|filename.bmp|x|y|
454 or %xl|n|filename.bmp|x|y| */
456 ptr = strchr(ptr, '|') + 1;
457 pos = strchr(ptr, '|');
458 newline = strchr(ptr, '\n');
460 if (!pos || pos > newline)
461 return 0;
463 /* get the image ID */
464 n = get_image_id(*ptr);
466 /* check the image number and load state */
467 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
469 /* Invalid image ID */
470 return WPS_ERROR_INVALID_PARAM;
473 ptr = pos + 1;
475 /* get image name */
476 bmp_names[n] = ptr;
478 pos = strchr(ptr, '|');
479 ptr = pos + 1;
481 /* get x-position */
482 pos = strchr(ptr, '|');
483 if (pos && pos < newline)
484 wps_data->img[n].x = atoi(ptr);
485 else
487 /* weird syntax, bail out */
488 return WPS_ERROR_INVALID_PARAM;
491 /* get y-position */
492 ptr = pos + 1;
493 pos = strchr(ptr, '|');
494 if (pos && pos < newline)
495 wps_data->img[n].y = atoi(ptr);
496 else
498 /* weird syntax, bail out */
499 return WPS_ERROR_INVALID_PARAM;
502 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
503 wps_data->img[n].always_display = true;
505 /* Skip the rest of the line */
506 return skip_end_of_line(wps_bufptr);
509 static int parse_image_special(const char *wps_bufptr,
510 struct wps_token *token,
511 struct wps_data *wps_data)
513 (void)wps_data; /* kill warning */
514 const char *pos = NULL;
515 const char *newline;
517 pos = strchr(wps_bufptr + 1, '|');
518 newline = strchr(wps_bufptr, '\n');
520 if (pos > newline)
521 return WPS_ERROR_INVALID_PARAM;
523 if (token->type == WPS_TOKEN_IMAGE_PROGRESS_BAR)
525 /* format: %P|filename.bmp| */
526 bmp_names[PROGRESSBAR_BMP] = wps_bufptr + 1;
528 #if LCD_DEPTH > 1
529 else if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
531 /* format: %X|filename.bmp| */
532 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
534 #endif
536 /* Skip the rest of the line */
537 return skip_end_of_line(wps_bufptr);
540 #endif /* HAVE_LCD_BITMAP */
542 static int parse_dir_level(const char *wps_bufptr,
543 struct wps_token *token,
544 struct wps_data *wps_data)
546 char val[] = { *wps_bufptr, '\0' };
547 token->value.i = atoi(val);
548 (void)wps_data; /* Kill warnings */
549 return 1;
552 static int parse_subline_timeout(const char *wps_bufptr,
553 struct wps_token *token,
554 struct wps_data *wps_data)
556 int skip = 0;
557 int val = 0;
558 bool have_point = false;
559 bool have_tenth = false;
561 (void)wps_data; /* Kill the warning */
563 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
565 if (*wps_bufptr != '.')
567 val *= 10;
568 val += *wps_bufptr - '0';
569 if (have_point)
571 have_tenth = true;
572 wps_bufptr++;
573 skip++;
574 break;
577 else
578 have_point = true;
580 wps_bufptr++;
581 skip++;
584 if (have_tenth == false)
585 val *= 10;
587 token->value.i = val;
589 return skip;
592 static int parse_progressbar(const char *wps_bufptr,
593 struct wps_token *token,
594 struct wps_data *wps_data)
596 (void)token; /* Kill warnings */
597 #ifdef HAVE_LCD_BITMAP
599 short *vals[] = {
600 &wps_data->progress_height,
601 &wps_data->progress_start,
602 &wps_data->progress_end,
603 &wps_data->progress_top };
605 /* default values : */
606 wps_data->progress_height = 6;
607 wps_data->progress_start = 0;
608 wps_data->progress_end = 0;
609 wps_data->progress_top = -1;
611 int i = 0;
612 char *newline = strchr(wps_bufptr, '\n');
613 char *prev = strchr(wps_bufptr, '|');
614 if (prev && prev < newline) {
615 char *next = strchr(prev+1, '|');
616 while (i < 4 && next && next < newline)
618 *(vals[i++]) = atoi(++prev);
619 prev = strchr(prev, '|');
620 next = strchr(++next, '|');
623 if (wps_data->progress_height < 3)
624 wps_data->progress_height = 3;
625 if (wps_data->progress_end < wps_data->progress_start + 3)
626 wps_data->progress_end = 0;
629 return newline - wps_bufptr;
631 #else
633 if (*(wps_bufptr-1) == 'f')
634 wps_data->full_line_progressbar = true;
635 else
636 wps_data->full_line_progressbar = false;
638 return 0;
640 #endif
643 #ifdef HAVE_ALBUMART
644 static int parse_albumart_load(const char *wps_bufptr,
645 struct wps_token *token,
646 struct wps_data *wps_data)
648 const char *_pos, *newline;
649 bool parsing;
650 const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
651 WPS_ALBUMART_ALIGN_CENTER |
652 WPS_ALBUMART_ALIGN_RIGHT;
653 const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
654 WPS_ALBUMART_ALIGN_CENTER |
655 WPS_ALBUMART_ALIGN_BOTTOM;
657 (void)token; /* silence warning */
659 /* reset albumart info in wps */
660 wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
661 wps_data->albumart_max_width = -1;
662 wps_data->albumart_max_height = -1;
663 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
664 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
666 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
668 newline = strchr(wps_bufptr, '\n');
670 /* initial validation and parsing of x and y components */
671 if (*wps_bufptr != '|')
672 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
674 _pos = wps_bufptr + 1;
675 if (!isdigit(*_pos))
676 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
677 wps_data->albumart_x = atoi(_pos);
679 _pos = strchr(_pos, '|');
680 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
681 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
683 wps_data->albumart_y = atoi(_pos);
685 _pos = strchr(_pos, '|');
686 if (!_pos || _pos > newline)
687 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
688 e.g. %Cl|7|59\n */
690 /* parsing width field */
691 parsing = true;
692 while (parsing)
694 /* apply each modifier in turn */
695 ++_pos;
696 switch (*_pos)
698 case 'l':
699 case 'L':
700 case '+':
701 wps_data->albumart_xalign =
702 (wps_data->albumart_xalign & xalign_mask) |
703 WPS_ALBUMART_ALIGN_LEFT;
704 break;
705 case 'c':
706 case 'C':
707 wps_data->albumart_xalign =
708 (wps_data->albumart_xalign & xalign_mask) |
709 WPS_ALBUMART_ALIGN_CENTER;
710 break;
711 case 'r':
712 case 'R':
713 case '-':
714 wps_data->albumart_xalign =
715 (wps_data->albumart_xalign & xalign_mask) |
716 WPS_ALBUMART_ALIGN_RIGHT;
717 break;
718 case 'd':
719 case 'D':
720 wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
721 break;
722 case 'i':
723 case 'I':
724 wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
725 break;
726 case 's':
727 case 'S':
728 wps_data->albumart_xalign |=
729 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
730 break;
731 default:
732 parsing = false;
733 break;
736 /* extract max width data */
737 if (*_pos != '|')
739 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
740 return WPS_ERROR_INVALID_PARAM;
742 wps_data->albumart_max_width = atoi(_pos);
744 _pos = strchr(_pos, '|');
745 if (!_pos || _pos > newline)
746 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
747 e.g. %Cl|7|59|200\n */
750 /* parsing height field */
751 parsing = true;
752 while (parsing)
754 /* apply each modifier in turn */
755 ++_pos;
756 switch (*_pos)
758 case 't':
759 case 'T':
760 case '-':
761 wps_data->albumart_yalign =
762 (wps_data->albumart_yalign & yalign_mask) |
763 WPS_ALBUMART_ALIGN_TOP;
764 break;
765 case 'c':
766 case 'C':
767 wps_data->albumart_yalign =
768 (wps_data->albumart_yalign & yalign_mask) |
769 WPS_ALBUMART_ALIGN_CENTER;
770 break;
771 case 'b':
772 case 'B':
773 case '+':
774 wps_data->albumart_yalign =
775 (wps_data->albumart_yalign & yalign_mask) |
776 WPS_ALBUMART_ALIGN_BOTTOM;
777 break;
778 case 'd':
779 case 'D':
780 wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
781 break;
782 case 'i':
783 case 'I':
784 wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
785 break;
786 case 's':
787 case 'S':
788 wps_data->albumart_yalign |=
789 (WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
790 break;
791 default:
792 parsing = false;
793 break;
796 /* extract max height data */
797 if (*_pos != '|')
799 if (!isdigit(*_pos))
800 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
802 wps_data->albumart_max_height = atoi(_pos);
804 _pos = strchr(_pos, '|');
805 if (!_pos || _pos > newline)
806 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
807 e.g. %Cl|7|59|200|200\n */
810 /* if we got here, we parsed everything ok .. ! */
811 if (wps_data->albumart_max_width < 0)
812 wps_data->albumart_max_width = 0;
813 else if (wps_data->albumart_max_width > LCD_WIDTH)
814 wps_data->albumart_max_width = LCD_WIDTH;
816 if (wps_data->albumart_max_height < 0)
817 wps_data->albumart_max_height = 0;
818 else if (wps_data->albumart_max_height > LCD_HEIGHT)
819 wps_data->albumart_max_height = LCD_HEIGHT;
821 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
823 /* Skip the rest of the line */
824 return skip_end_of_line(wps_bufptr);
827 static int parse_albumart_conditional(const char *wps_bufptr,
828 struct wps_token *token,
829 struct wps_data *wps_data)
831 struct wps_token *prevtoken = token;
832 --prevtoken;
833 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
835 /* This %C is part of a %?C construct.
836 It's either %?C<blah> or %?Cn<blah> */
837 token->type = WPS_TOKEN_ALBUMART_FOUND;
838 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
840 token->next = true;
841 return 1;
843 else if (*wps_bufptr == '<')
845 return 0;
847 else
849 token->type = WPS_NO_TOKEN;
850 return 0;
853 else
855 /* This %C tag is in a conditional construct. */
856 wps_data->albumart_cond_index = condindex[level];
857 return 0;
860 #endif /* HAVE_ALBUMART */
862 #ifdef HAVE_LCD_BITMAP
863 static int parse_leftmargin(const char *wps_bufptr, struct wps_token *token,
864 struct wps_data *wps_data)
866 const char* p;
867 const char* pend;
868 const char *newline;
870 (void)wps_data; /* Kill the warning */
872 /* valid tag looks like %m|12| */
873 if(*wps_bufptr == '|')
875 p = wps_bufptr + 1;
876 newline = strchr(wps_bufptr, '\n');
877 if(isdigit(*p) && (pend = strchr(p, '|')) && pend < newline)
879 token->value.i = atoi(p);
880 return pend - wps_bufptr + 1;
884 /* invalid tag syntax */
885 return WPS_ERROR_INVALID_PARAM;
887 #endif
890 /* Parse a generic token from the given string. Return the length read */
891 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
893 int skip = 0, taglen = 0, ret;
894 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
895 const struct wps_tag *tag;
897 switch(*wps_bufptr)
900 case '%':
901 case '<':
902 case '|':
903 case '>':
904 case ';':
905 case '#':
906 /* escaped characters */
907 token->type = WPS_TOKEN_CHARACTER;
908 token->value.c = *wps_bufptr;
909 taglen = 1;
910 wps_data->num_tokens++;
911 break;
913 case '?':
914 /* conditional tag */
915 token->type = WPS_TOKEN_CONDITIONAL;
916 level++;
917 condindex[level] = wps_data->num_tokens;
918 numoptions[level] = 1;
919 wps_data->num_tokens++;
920 ret = parse_token(wps_bufptr + 1, wps_data);
921 if (ret < 0) return ret;
922 taglen = 1 + ret;
923 break;
925 default:
926 /* find what tag we have */
927 for (tag = all_tags;
928 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
929 tag++) ;
931 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
932 token->type = tag->type;
933 wps_data->sublines[wps_data->num_sublines].line_type |=
934 tag->refresh_type;
936 /* if the tag has a special parsing function, we call it */
937 if (tag->parse_func)
939 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
940 if (ret < 0) return ret;
941 skip += ret;
944 /* Some tags we don't want to save as tokens */
945 if (tag->type == WPS_NO_TOKEN)
946 break;
948 /* tags that start with 'F', 'I' or 'D' are for the next file */
949 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
950 *(tag->name) == 'D')
951 token->next = true;
953 wps_data->num_tokens++;
954 break;
957 skip += taglen;
958 return skip;
961 /* Parses the WPS.
962 data is the pointer to the structure where the parsed WPS should be stored.
963 It is initialised.
964 wps_bufptr points to the string containing the WPS tags */
965 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
967 if (!data || !wps_bufptr || !*wps_bufptr)
968 return false;
970 char *stringbuf = data->string_buffer;
971 int stringbuf_used = 0;
972 int fail = 0;
973 int ret;
974 line = 1;
975 level = -1;
977 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
978 && data->num_lines < WPS_MAX_LINES)
980 switch(*wps_bufptr++)
983 /* Regular tag */
984 case '%':
985 if ((ret = parse_token(wps_bufptr, data)) < 0)
987 fail = PARSE_FAIL_COND_INVALID_PARAM;
988 break;
990 wps_bufptr += ret;
991 break;
993 /* Alternating sublines separator */
994 case ';':
995 if (level >= 0) /* there are unclosed conditionals */
997 fail = PARSE_FAIL_UNCLOSED_COND;
998 break;
1001 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1002 wps_start_new_subline(data);
1003 else
1004 wps_bufptr += skip_end_of_line(wps_bufptr);
1006 break;
1008 /* Conditional list start */
1009 case '<':
1010 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1012 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1013 break;
1016 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1017 lastcond[level] = data->num_tokens++;
1018 break;
1020 /* Conditional list end */
1021 case '>':
1022 if (level < 0) /* not in a conditional, invalid char */
1024 fail = PARSE_FAIL_INVALID_CHAR;
1025 break;
1028 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1029 if (lastcond[level])
1030 data->tokens[lastcond[level]].value.i = data->num_tokens;
1031 else
1033 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1034 break;
1037 lastcond[level] = 0;
1038 data->num_tokens++;
1039 data->tokens[condindex[level]].value.i = numoptions[level];
1040 level--;
1041 break;
1043 /* Conditional list option */
1044 case '|':
1045 if (level < 0) /* not in a conditional, invalid char */
1047 fail = PARSE_FAIL_INVALID_CHAR;
1048 break;
1051 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1052 if (lastcond[level])
1053 data->tokens[lastcond[level]].value.i = data->num_tokens;
1054 else
1056 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1057 break;
1060 lastcond[level] = data->num_tokens;
1061 numoptions[level]++;
1062 data->num_tokens++;
1063 break;
1065 /* Comment */
1066 case '#':
1067 if (level >= 0) /* there are unclosed conditionals */
1069 fail = PARSE_FAIL_UNCLOSED_COND;
1070 break;
1073 wps_bufptr += skip_end_of_line(wps_bufptr);
1074 break;
1076 /* End of this line */
1077 case '\n':
1078 if (level >= 0) /* there are unclosed conditionals */
1080 fail = PARSE_FAIL_UNCLOSED_COND;
1081 break;
1084 line++;
1085 wps_start_new_subline(data);
1086 data->num_lines++; /* Start a new line */
1088 if ((data->num_lines < WPS_MAX_LINES) &&
1089 (data->num_sublines < WPS_MAX_SUBLINES))
1091 data->lines[data->num_lines].first_subline_idx =
1092 data->num_sublines;
1094 data->sublines[data->num_sublines].first_token_idx =
1095 data->num_tokens;
1098 break;
1100 /* String */
1101 default:
1103 unsigned int len = 1;
1104 const char *string_start = wps_bufptr - 1;
1106 /* find the length of the string */
1107 while (*wps_bufptr && *wps_bufptr != '#' &&
1108 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1109 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1110 *wps_bufptr != '|' && *wps_bufptr != '\n')
1112 wps_bufptr++;
1113 len++;
1116 /* look if we already have that string */
1117 char **str;
1118 int i;
1119 bool found;
1120 for (i = 0, str = data->strings, found = false;
1121 i < data->num_strings &&
1122 !(found = (strlen(*str) == len &&
1123 strncmp(string_start, *str, len) == 0));
1124 i++, str++);
1125 /* If a matching string is found, found is true and i is
1126 the index of the string. If not, found is false */
1128 /* If it's NOT a duplicate, do nothing if we already have
1129 too many unique strings */
1130 if (found ||
1131 (stringbuf_used < STRING_BUFFER_SIZE - 1 &&
1132 data->num_strings < WPS_MAX_STRINGS))
1134 if (!found)
1136 /* new string */
1138 /* truncate? */
1139 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1)
1140 len = STRING_BUFFER_SIZE - stringbuf_used - 1;
1142 strncpy(stringbuf, string_start, len);
1143 *(stringbuf + len) = '\0';
1145 data->strings[data->num_strings] = stringbuf;
1146 stringbuf += len + 1;
1147 stringbuf_used += len + 1;
1148 data->tokens[data->num_tokens].value.i =
1149 data->num_strings;
1150 data->num_strings++;
1152 else
1154 /* another ocurrence of an existing string */
1155 data->tokens[data->num_tokens].value.i = i;
1157 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1158 data->num_tokens++;
1161 break;
1165 if (!fail && level >= 0) /* there are unclosed conditionals */
1166 fail = PARSE_FAIL_UNCLOSED_COND;
1168 #ifdef DEBUG
1169 print_debug_info(data, fail, line);
1170 #endif
1172 if (fail)
1173 wps_reset(data);
1175 return (fail == 0);
1178 #ifdef HAVE_LCD_BITMAP
1179 /* Clear the WPS image cache */
1180 static void wps_images_clear(struct wps_data *data)
1182 int i;
1183 /* set images to unloaded and not displayed */
1184 for (i = 0; i < MAX_IMAGES; i++)
1186 data->img[i].loaded = false;
1187 data->img[i].display = false;
1188 data->img[i].always_display = false;
1190 data->progressbar.have_bitmap_pb = false;
1192 #endif
1194 /* initial setup of wps_data */
1195 void wps_data_init(struct wps_data *wps_data)
1197 #ifdef HAVE_LCD_BITMAP
1198 wps_images_clear(wps_data);
1199 wps_data->wps_sb_tag = false;
1200 wps_data->show_sb_on_wps = false;
1201 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1202 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1203 wps_data->peak_meter_enabled = false;
1204 #else /* HAVE_LCD_CHARCELLS */
1205 int i;
1206 for (i = 0; i < 8; i++)
1208 wps_data->wps_progress_pat[i] = 0;
1210 wps_data->full_line_progressbar = false;
1211 #endif
1212 wps_data->wps_loaded = false;
1215 static void wps_reset(struct wps_data *data)
1217 #ifdef HAVE_REMOTE_LCD
1218 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1219 #endif
1220 memset(data, 0, sizeof(*data));
1221 #ifdef HAVE_ALBUMART
1222 data->wps_uses_albumart = WPS_ALBUMART_NONE;
1223 #endif
1224 wps_data_init(data);
1225 #ifdef HAVE_REMOTE_LCD
1226 data->remote_wps = rwps;
1227 #endif
1230 #ifdef HAVE_LCD_BITMAP
1233 static void clear_bmp_names(void)
1235 int n;
1236 for (n = 0; n < MAX_BITMAPS; n++)
1238 bmp_names[n] = NULL;
1242 static void load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1244 char img_path[MAX_PATH];
1245 struct bitmap *bitmap;
1246 bool *loaded;
1248 int n;
1249 for (n = 0; n < BACKDROP_BMP; n++)
1251 if (bmp_names[n])
1253 get_image_filename(bmp_names[n], bmpdir,
1254 img_path, sizeof(img_path));
1256 if (n == PROGRESSBAR_BMP) {
1257 /* progressbar bitmap */
1258 bitmap = &wps_data->progressbar.bm;
1259 loaded = &wps_data->progressbar.have_bitmap_pb;
1260 } else {
1261 /* regular bitmap */
1262 bitmap = &wps_data->img[n].bm;
1263 loaded = &wps_data->img[n].loaded;
1266 /* load the image */
1267 bitmap->data = wps_data->img_buf_ptr;
1268 if (load_bitmap(wps_data, img_path, bitmap))
1270 *loaded = true;
1275 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
1276 if (bmp_names[BACKDROP_BMP])
1278 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1279 img_path, sizeof(img_path));
1280 #ifdef HAVE_REMOTE_LCD
1281 if (wps_data->remote_wps)
1282 #if LCD_REMOTE_DEPTH > 1
1283 load_remote_wps_backdrop(img_path)
1284 #endif
1286 else
1287 #endif /* HAVE_REMOTE_LCD */
1288 load_wps_backdrop(img_path);
1290 #endif /* has backdrop support */
1293 #endif /* HAVE_LCD_BITMAP */
1295 /* Skip leading UTF-8 BOM, if present. */
1296 static char *skip_utf8_bom(char *buf)
1298 unsigned char *s = (unsigned char *)buf;
1300 if(s[0] == 0xef && s[1] == 0xbb && s[2] == 0xbf)
1302 buf += 3;
1305 return buf;
1308 /* to setup up the wps-data from a format-buffer (isfile = false)
1309 from a (wps-)file (isfile = true)*/
1310 bool wps_data_load(struct wps_data *wps_data,
1311 const char *buf,
1312 bool isfile)
1314 if (!wps_data || !buf)
1315 return false;
1317 wps_reset(wps_data);
1319 if (!isfile)
1321 return wps_parse(wps_data, buf);
1323 else
1326 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1327 * wants to be a virtual file. Feel free to modify dirbrowse()
1328 * if you're feeling brave.
1330 #ifndef __PCTOOL__
1331 if (! strcmp(buf, WPS_DEFAULTCFG) )
1333 global_settings.wps_file[0] = 0;
1334 return false;
1337 #ifdef HAVE_REMOTE_LCD
1338 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1340 global_settings.rwps_file[0] = 0;
1341 return false;
1343 #endif
1344 #endif /* __PCTOOL__ */
1346 int fd = open(buf, O_RDONLY);
1348 if (fd < 0)
1349 return false;
1351 /* get buffer space from the plugin buffer */
1352 size_t buffersize = 0;
1353 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1355 if (!wps_buffer)
1356 return false;
1358 /* copy the file's content to the buffer for parsing,
1359 ensuring that every line ends with a newline char. */
1360 unsigned int start = 0;
1361 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1363 start += strlen(wps_buffer + start);
1364 if (start < buffersize - 1)
1366 wps_buffer[start++] = '\n';
1367 wps_buffer[start] = 0;
1371 close(fd);
1373 if (start <= 0)
1374 return false;
1376 #ifdef HAVE_LCD_BITMAP
1377 clear_bmp_names();
1378 #endif
1380 /* Skip leading UTF-8 BOM, if present. */
1381 wps_buffer = skip_utf8_bom(wps_buffer);
1383 /* parse the WPS source */
1384 if (!wps_parse(wps_data, wps_buffer))
1385 return false;
1387 wps_data->wps_loaded = true;
1389 #ifdef HAVE_LCD_BITMAP
1390 /* get the bitmap dir */
1391 char bmpdir[MAX_PATH];
1392 size_t bmpdirlen;
1393 char *dot = strrchr(buf, '.');
1394 bmpdirlen = dot - buf;
1395 strncpy(bmpdir, buf, dot - buf);
1396 bmpdir[bmpdirlen] = 0;
1398 /* load the bitmaps that were found by the parsing */
1399 load_wps_bitmaps(wps_data, bmpdir);
1400 #endif
1401 return true;
1405 int wps_subline_index(struct wps_data *data, int line, int subline)
1407 return data->lines[line].first_subline_idx + subline;
1410 int wps_first_token_index(struct wps_data *data, int line, int subline)
1412 int first_subline_idx = data->lines[line].first_subline_idx;
1413 return data->sublines[first_subline_idx + subline].first_token_idx;
1416 int wps_last_token_index(struct wps_data *data, int line, int subline)
1418 int first_subline_idx = data->lines[line].first_subline_idx;
1419 int idx = first_subline_idx + subline;
1420 if (idx < data->num_sublines - 1)
1422 /* This subline ends where the next begins */
1423 return data->sublines[idx+1].first_token_idx - 1;
1425 else
1427 /* The last subline goes to the end */
1428 return data->num_tokens - 1;