summaryrefslogtreecommitdiff
path: root/apps/gui/wps_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/wps_parser.c')
-rw-r--r--apps/gui/wps_parser.c957
1 files changed, 957 insertions, 0 deletions
diff --git a/apps/gui/wps_parser.c b/apps/gui/wps_parser.c
new file mode 100644
index 0000000000..ef9d446444
--- /dev/null
+++ b/apps/gui/wps_parser.c
@@ -0,0 +1,957 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
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#include "settings.h"
28#include "debug.h"
29#include "plugin.h"
30
31#ifdef HAVE_LCD_BITMAP
32#include "bmp.h"
33#if LCD_DEPTH > 1
34#include "backdrop.h"
35#endif
36#endif
37
38#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
39#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
40
41/* level of current conditional.
42 -1 means we're not in a conditional. */
43int level = -1;
44
45/* index of the last WPS_TOKEN_CONDITIONAL_OPTION
46 or WPS_TOKEN_CONDITIONAL_START in current level */
47int lastcond[WPS_MAX_COND_LEVEL];
48
49/* index of the WPS_TOKEN_CONDITIONAL in current level */
50int condindex[WPS_MAX_COND_LEVEL];
51
52/* number of condtional options in current level */
53int numoptions[WPS_MAX_COND_LEVEL];
54
55#ifdef HAVE_LCD_BITMAP
56/* pointers to the bitmap filenames in the WPS source */
57const char *bmp_names[MAX_IMAGES];
58const char *pb_bmp_name;
59#if LCD_DEPTH > 1
60const char *backdrop_bmp_name;
61#endif
62#endif
63
64#ifdef DEBUG
65/* debugging functions */
66extern void dump_wps_tokens(struct wps_data *data);
67extern void print_line_info(struct wps_data *data);
68extern void print_img_cond_indexes(struct wps_data *data);
69extern void print_wps_strings(struct wps_data *data);
70#endif
71
72typedef int (*wps_tag_parse_func)(const char *wps_token, struct wps_data *wps_data);
73
74struct wps_tag {
75 const char name[3];
76 enum wps_token_type type;
77 unsigned char refresh_type;
78 wps_tag_parse_func parse_func;
79};
80
81/* prototypes of all special parse functions : */
82
83static int parse_subline_timeout(const char *wps_token, struct wps_data *wps_data);
84static int parse_progressbar(const char *wps_token, struct wps_data *wps_data);
85static int parse_dir_level(const char *wps_token, struct wps_data *wps_data);
86#ifdef HAVE_LCD_BITMAP
87static int parse_image_special(const char *wps_token, struct wps_data *wps_data);
88static int parse_statusbar(const char *wps_token, struct wps_data *wps_data);
89static int parse_image_display(const char *wps_token, struct wps_data *wps_data);
90static int parse_image_load(const char *wps_token, struct wps_data *wps_data);
91#endif /*HAVE_LCD_BITMAP */
92#if CONFIG_RTC
93static int parse_rtc_format(const char *wps_token, struct wps_data *wps_data);
94
95/* RTC tokens array */
96static const struct wps_tag rtc_tags[] = {
97 { "d", WPS_TOKEN_RTC_DAY_OF_MONTH, 0, NULL },
98 { "e", WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED, 0, NULL },
99 { "H", WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, 0, NULL },
100 { "k", WPS_TOKEN_RTC_HOUR_24, 0, NULL },
101 { "I", WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, 0, NULL },
102 { "l", WPS_TOKEN_RTC_HOUR_12, 0, NULL },
103 { "m", WPS_TOKEN_RTC_MONTH, 0, NULL },
104 { "M", WPS_TOKEN_RTC_MINUTE, 0, NULL },
105 { "S", WPS_TOKEN_RTC_SECOND, 0, NULL },
106 { "y", WPS_TOKEN_RTC_YEAR_2_DIGITS, 0, NULL },
107 { "Y", WPS_TOKEN_RTC_YEAR_4_DIGITS, 0, NULL },
108 { "p", WPS_TOKEN_RTC_AM_PM_UPPER, 0, NULL },
109 { "P", WPS_TOKEN_RTC_AM_PM_LOWER, 0, NULL },
110 { "a", WPS_TOKEN_RTC_WEEKDAY_NAME, 0, NULL },
111 { "b", WPS_TOKEN_RTC_MONTH_NAME, 0, NULL },
112 { "u", WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, 0, NULL },
113 { "w", WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, 0, NULL },
114 { "\0",WPS_TOKEN_CHARACTER, 0, NULL }
115 /* the array MUST end with a "\0" token */
116};
117#endif
118
119/* array of available tags - those with more characters have to go first
120 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
121static const struct wps_tag all_tags[] = {
122
123 { "ac", WPS_TOKEN_ALIGN_CENTER, 0, NULL },
124 { "al", WPS_TOKEN_ALIGN_LEFT, 0, NULL },
125 { "ar", WPS_TOKEN_ALIGN_RIGHT, 0, NULL },
126
127 { "bl", WPS_TOKEN_BATTERY_PERCENT, WPS_REFRESH_DYNAMIC, NULL },
128 { "bv", WPS_TOKEN_BATTERY_VOLTS, WPS_REFRESH_DYNAMIC, NULL },
129 { "bt", WPS_TOKEN_BATTERY_TIME, WPS_REFRESH_DYNAMIC, NULL },
130 { "bs", WPS_TOKEN_BATTERY_SLEEPTIME, WPS_REFRESH_DYNAMIC, NULL },
131#if CONFIG_CHARGING >= CHARGING_MONITOR
132 { "bc", WPS_TOKEN_BATTERY_CHARGING, WPS_REFRESH_DYNAMIC, NULL },
133#endif
134#if CONFIG_CHARGING
135 { "bp", WPS_TOKEN_BATTERY_CHARGER_CONNECTED,WPS_REFRESH_DYNAMIC, NULL },
136#endif
137
138#if CONFIG_RTC
139 { "c", WPS_TOKEN_RTC, WPS_REFRESH_DYNAMIC, parse_rtc_format },
140#endif
141
142 /* current file */
143 { "fb", WPS_TOKEN_FILE_BITRATE, WPS_REFRESH_STATIC, NULL },
144 { "fc", WPS_TOKEN_FILE_CODEC, WPS_REFRESH_STATIC, NULL },
145 { "ff", WPS_TOKEN_FILE_FREQUENCY, WPS_REFRESH_STATIC, NULL },
146 { "fm", WPS_TOKEN_FILE_NAME_WITH_EXTENSION, WPS_REFRESH_STATIC, NULL },
147 { "fn", WPS_TOKEN_FILE_NAME, WPS_REFRESH_STATIC, NULL },
148 { "fp", WPS_TOKEN_FILE_PATH, WPS_REFRESH_STATIC, NULL },
149 { "fs", WPS_TOKEN_FILE_SIZE, WPS_REFRESH_STATIC, NULL },
150 { "fv", WPS_TOKEN_FILE_VBR, WPS_REFRESH_STATIC, NULL },
151 { "d", WPS_TOKEN_FILE_DIRECTORY, WPS_REFRESH_STATIC, parse_dir_level },
152
153 /* next file */
154 { "Fb", WPS_TOKEN_FILE_BITRATE, WPS_REFRESH_DYNAMIC, NULL },
155 { "Fc", WPS_TOKEN_FILE_CODEC, WPS_REFRESH_DYNAMIC, NULL },
156 { "Ff", WPS_TOKEN_FILE_FREQUENCY, WPS_REFRESH_DYNAMIC, NULL },
157 { "Fm", WPS_TOKEN_FILE_NAME_WITH_EXTENSION, WPS_REFRESH_DYNAMIC, NULL },
158 { "Fn", WPS_TOKEN_FILE_NAME, WPS_REFRESH_DYNAMIC, NULL },
159 { "Fp", WPS_TOKEN_FILE_PATH, WPS_REFRESH_DYNAMIC, NULL },
160 { "Fs", WPS_TOKEN_FILE_SIZE, WPS_REFRESH_DYNAMIC, NULL },
161 { "Fv", WPS_TOKEN_FILE_VBR, WPS_REFRESH_DYNAMIC, NULL },
162 { "D", WPS_TOKEN_FILE_DIRECTORY, WPS_REFRESH_DYNAMIC,parse_dir_level },
163
164 /* current metadata */
165 { "ia", WPS_TOKEN_METADATA_ARTIST, WPS_REFRESH_STATIC, NULL },
166 { "ic", WPS_TOKEN_METADATA_COMPOSER, WPS_REFRESH_STATIC, NULL },
167 { "id", WPS_TOKEN_METADATA_ALBUM, WPS_REFRESH_STATIC, NULL },
168 { "iA", WPS_TOKEN_METADATA_ALBUM_ARTIST, WPS_REFRESH_STATIC, NULL },
169 { "ig", WPS_TOKEN_METADATA_GENRE, WPS_REFRESH_STATIC, NULL },
170 { "in", WPS_TOKEN_METADATA_TRACK_NUMBER, WPS_REFRESH_STATIC, NULL },
171 { "it", WPS_TOKEN_METADATA_TRACK_TITLE, WPS_REFRESH_STATIC, NULL },
172 { "iv", WPS_TOKEN_METADATA_VERSION, WPS_REFRESH_STATIC, NULL },
173 { "iy", WPS_TOKEN_METADATA_YEAR, WPS_REFRESH_STATIC, NULL },
174 { "iC", WPS_TOKEN_METADATA_COMMENT, WPS_REFRESH_DYNAMIC, NULL },
175
176 /* next metadata */
177 { "Ia", WPS_TOKEN_METADATA_ARTIST, WPS_REFRESH_DYNAMIC, NULL },
178 { "Ic", WPS_TOKEN_METADATA_COMPOSER, WPS_REFRESH_DYNAMIC, NULL },
179 { "Id", WPS_TOKEN_METADATA_ALBUM, WPS_REFRESH_DYNAMIC, NULL },
180 { "IA", WPS_TOKEN_METADATA_ALBUM_ARTIST, WPS_REFRESH_STATIC, NULL },
181 { "Ig", WPS_TOKEN_METADATA_GENRE, WPS_REFRESH_DYNAMIC, NULL },
182 { "In", WPS_TOKEN_METADATA_TRACK_NUMBER, WPS_REFRESH_DYNAMIC, NULL },
183 { "It", WPS_TOKEN_METADATA_TRACK_TITLE, WPS_REFRESH_DYNAMIC, NULL },
184 { "Iv", WPS_TOKEN_METADATA_VERSION, WPS_REFRESH_DYNAMIC, NULL },
185 { "Iy", WPS_TOKEN_METADATA_YEAR, WPS_REFRESH_DYNAMIC, NULL },
186 { "IC", WPS_TOKEN_METADATA_COMMENT, WPS_REFRESH_DYNAMIC, NULL },
187
188#if (CONFIG_CODEC == SWCODEC)
189 { "Sp", WPS_TOKEN_SOUND_PITCH, WPS_REFRESH_DYNAMIC, NULL },
190#endif
191
192#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
193 { "lh", WPS_TOKEN_VLED_HDD, WPS_REFRESH_DYNAMIC, NULL },
194#endif
195
196#ifdef HAS_BUTTON_HOLD
197 { "mh", WPS_TOKEN_MAIN_HOLD, WPS_REFRESH_DYNAMIC, NULL },
198#endif
199#ifdef HAS_REMOTE_BUTTON_HOLD
200 { "mr", WPS_TOKEN_REMOTE_HOLD, WPS_REFRESH_DYNAMIC, NULL },
201#endif
202
203 { "mm", WPS_TOKEN_REPEAT_MODE, WPS_REFRESH_DYNAMIC, NULL },
204 { "mp", WPS_TOKEN_PLAYBACK_STATUS, WPS_REFRESH_DYNAMIC, NULL },
205
206#ifdef HAVE_LCD_BITMAP
207 { "pm", WPS_TOKEN_PEAKMETER,
208 WPS_REFRESH_PEAK_METER, NULL },
209#else
210 { "pf", WPS_TOKEN_PLAYER_PROGRESSBAR,
211 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS,
212 parse_progressbar },
213#endif
214 { "pb", WPS_TOKEN_PROGRESSBAR,
215 WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
216
217 { "pv", WPS_TOKEN_VOLUME, WPS_REFRESH_DYNAMIC, NULL },
218
219 { "pc", WPS_TOKEN_TRACK_TIME_ELAPSED, WPS_REFRESH_DYNAMIC, NULL },
220 { "pr", WPS_TOKEN_TRACK_TIME_REMAINING, WPS_REFRESH_DYNAMIC, NULL },
221 { "pt", WPS_TOKEN_TRACK_LENGTH, WPS_REFRESH_STATIC, NULL },
222
223 { "pp", WPS_TOKEN_PLAYLIST_POSITION, WPS_REFRESH_STATIC, NULL },
224 { "pe", WPS_TOKEN_PLAYLIST_ENTRIES, WPS_REFRESH_STATIC, NULL },
225 { "pn", WPS_TOKEN_PLAYLIST_NAME, WPS_REFRESH_STATIC, NULL },
226 { "ps", WPS_TOKEN_PLAYLIST_SHUFFLE, WPS_REFRESH_DYNAMIC, NULL },
227
228 { "rp", WPS_TOKEN_DATABASE_PLAYCOUNT, WPS_REFRESH_DYNAMIC, NULL },
229 { "rr", WPS_TOKEN_DATABASE_RATING, WPS_REFRESH_DYNAMIC, NULL },
230#if CONFIG_CODEC == SWCODEC
231 { "rg", WPS_TOKEN_REPLAYGAIN, WPS_REFRESH_STATIC, NULL },
232#endif
233
234 { "s", WPS_TOKEN_SCROLL, WPS_REFRESH_SCROLL, NULL },
235 { "t", WPS_TOKEN_SUBLINE_TIMEOUT, 0, parse_subline_timeout },
236
237#ifdef HAVE_LCD_BITMAP
238 { "we", WPS_TOKEN_STATUSBAR_ENABLED, 0, parse_statusbar },
239 { "wd", WPS_TOKEN_STATUSBAR_DISABLED, 0, parse_statusbar },
240
241 { "xl", WPS_NO_TOKEN, 0, parse_image_load },
242
243 { "xd", WPS_TOKEN_IMAGE_PRELOAD_DISPLAY,
244 WPS_REFRESH_STATIC, parse_image_display },
245
246 { "x", WPS_TOKEN_IMAGE_DISPLAY, 0, parse_image_load },
247 { "P", WPS_TOKEN_IMAGE_PROGRESS_BAR, 0, parse_image_special },
248#if LCD_DEPTH > 1
249 { "X", WPS_TOKEN_IMAGE_BACKDROP, 0, parse_image_special },
250#endif
251#endif
252
253 { "\0", WPS_TOKEN_UNKNOWN, 0, NULL }
254 /* the array MUST end with a "\0" token */
255};
256
257
258static int skip_end_of_line(const char *wps_token)
259{
260 int skip = 0;
261 while(*(wps_token + skip) != '\n')
262 skip++;
263 return ++skip;
264}
265
266#if CONFIG_RTC
267static int parse_rtc_format(const char *wps_token, struct wps_data *wps_data)
268{
269 int skip = 0, i;
270
271 /* RTC tag format ends with a c or a newline */
272 while (wps_token && *wps_token != 'c' && *wps_token != '\n')
273 {
274 /* find what format char we have */
275 i = 0;
276 while (*(rtc_tags[i].name) && *wps_token != *(rtc_tags[i].name))
277 i++;
278
279 wps_data->num_tokens++;
280 wps_data->tokens[wps_data->num_tokens].type = rtc_tags[i].type;
281 wps_data->tokens[wps_data->num_tokens].value.c = *wps_token;
282 skip ++;
283 wps_token++;
284 }
285
286 /* eat the unwanted c at the end of the format */
287 if (*wps_token == 'c')
288 skip++;
289
290 return skip;
291}
292#endif
293
294#ifdef HAVE_LCD_BITMAP
295
296static int parse_statusbar(const char *wps_token, struct wps_data *wps_data)
297{
298 wps_data->wps_sb_tag = true;
299
300 if (wps_data->tokens[wps_data->num_tokens].type == WPS_TOKEN_STATUSBAR_ENABLED)
301 wps_data->show_sb_on_wps = true;
302 else
303 wps_data->show_sb_on_wps = false;
304
305 /* Skip the rest of the line */
306 return skip_end_of_line(wps_token);
307}
308
309static bool load_bitmap(struct wps_data *wps_data,
310 char* filename,
311 struct bitmap *bm)
312{
313 int ret = read_bmp_file(filename, bm,
314 wps_data->img_buf_free,
315 FORMAT_ANY|FORMAT_TRANSPARENT);
316
317 if (ret > 0)
318 {
319#if LCD_DEPTH == 16
320 if (ret % 2) ret++;
321 /* Always consume an even number of bytes */
322#endif
323 wps_data->img_buf_ptr += ret;
324 wps_data->img_buf_free -= ret;
325
326 return true;
327 }
328 else
329 return false;
330}
331
332static int get_image_id(int c)
333{
334 if(c >= 'a' && c <= 'z')
335 return c - 'a';
336 else if(c >= 'A' && c <= 'Z')
337 return c - 'A' + 26;
338 else
339 return -1;
340}
341
342static char *get_image_filename(const char *start, const char* bmpdir,
343 char *buf, int buf_size)
344{
345 const char *end = strchr(start, '|');
346
347 if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
348 {
349 buf = "\0";
350 return NULL;
351 }
352
353 int bmpdirlen = strlen(bmpdir);
354
355 strcpy(buf, bmpdir);
356 buf[bmpdirlen] = '/';
357 memcpy( &buf[bmpdirlen + 1], start, end - start);
358 buf[bmpdirlen + 1 + end - start] = 0;
359
360 return buf;
361}
362
363static int parse_image_display(const char *wps_token, struct wps_data *wps_data)
364{
365 int n = get_image_id(*wps_token);
366 wps_data->tokens[wps_data->num_tokens].value.i = n;
367
368 /* if the image is in a conditional, remember it */
369 if (level >= 0)
370 wps_data->img[n].cond_index = condindex[level];
371
372 return 1;
373}
374
375static int parse_image_load(const char *wps_token, struct wps_data *wps_data)
376{
377 int n;
378 const char *ptr = wps_token;
379 char *pos = NULL;
380
381 /* format: %x|n|filename.bmp|x|y|
382 or %xl|n|filename.bmp|x|y| */
383
384 ptr = strchr(ptr, '|') + 1;
385 pos = strchr(ptr, '|');
386 if (pos)
387 {
388 /* get the image ID */
389 n = get_image_id(*ptr);
390
391 /* check the image number and load state */
392 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
393 {
394 /* Skip the rest of the line */
395 return skip_end_of_line(wps_token);
396 }
397
398 ptr = pos + 1;
399
400 /* get image name */
401 bmp_names[n] = ptr;
402
403 pos = strchr(ptr, '|');
404 ptr = pos + 1;
405
406 /* get x-position */
407 pos = strchr(ptr, '|');
408 if (pos)
409 wps_data->img[n].x = atoi(ptr);
410 else
411 {
412 /* weird syntax, bail out */
413 return skip_end_of_line(wps_token);
414 }
415
416 /* get y-position */
417 ptr = pos + 1;
418 pos = strchr(ptr, '|');
419 if (pos)
420 wps_data->img[n].y = atoi(ptr);
421 else
422 {
423 /* weird syntax, bail out */
424 return skip_end_of_line(wps_token);
425 }
426
427 if (wps_data->tokens[wps_data->num_tokens].type == WPS_TOKEN_IMAGE_DISPLAY)
428 wps_data->img[n].always_display = true;
429 }
430
431 /* Skip the rest of the line */
432 return skip_end_of_line(wps_token);
433}
434
435static int parse_image_special(const char *wps_token, struct wps_data *wps_data)
436{
437 if (wps_data->tokens[wps_data->num_tokens].type == WPS_TOKEN_IMAGE_PROGRESS_BAR)
438 {
439 /* format: %P|filename.bmp| */
440 pb_bmp_name = wps_token + 1;
441 }
442#if LCD_DEPTH > 1
443 else if (wps_data->tokens[wps_data->num_tokens].type == WPS_TOKEN_IMAGE_BACKDROP)
444 {
445 /* format: %X|filename.bmp| */
446 backdrop_bmp_name = wps_token + 1;
447 }
448#endif
449
450 (void)wps_data; /* to avoid a warning */
451
452 /* Skip the rest of the line */
453 return skip_end_of_line(wps_token);
454}
455
456#endif /* HAVE_LCD_BITMAP */
457
458static int parse_dir_level(const char *wps_token, struct wps_data *wps_data)
459{
460 char val[] = { *wps_token, '\0' };
461 wps_data->tokens[wps_data->num_tokens].value.i = atoi(val);
462 return 1;
463}
464
465static int parse_subline_timeout(const char *wps_token, struct wps_data *wps_data)
466{
467 int skip = 0;
468 int val = 0;
469 bool have_point = false;
470 bool have_tenth = false;
471
472 while ( isdigit(*wps_token) || *wps_token == '.' )
473 {
474 if (*wps_token != '.')
475 {
476 val *= 10;
477 val += *wps_token - '0';
478 if (have_point)
479 {
480 have_tenth = true;
481 wps_token++;
482 skip++;
483 break;
484 }
485 }
486 else
487 have_point = true;
488
489 wps_token++;
490 skip++;
491 }
492
493 if (have_tenth == false)
494 val *= 10;
495
496 if (val > 0)
497 {
498 int line = wps_data->num_lines;
499 int subline = wps_data->num_sublines[line];
500 wps_data->time_mult[line][subline] = val;
501 }
502
503 wps_data->tokens[wps_data->num_tokens].value.i = val;
504 return skip;
505}
506
507static int parse_progressbar(const char *wps_token, struct wps_data *wps_data)
508{
509#ifdef HAVE_LCD_BITMAP
510
511 short *vals[] = {
512 &wps_data->progress_height,
513 &wps_data->progress_start,
514 &wps_data->progress_end,
515 &wps_data->progress_top };
516
517 /* default values : */
518 wps_data->progress_height = 6;
519 wps_data->progress_start = 0;
520 wps_data->progress_end = 0;
521 wps_data->progress_top = -1;
522
523 int i = 0;
524 char *newline = strchr(wps_token, '\n');
525 char *prev = strchr(wps_token, '|');
526 if (prev && prev < newline) {
527 char *next = strchr(prev+1, '|');
528 while (i < 4 && next && next < newline)
529 {
530 *(vals[i++]) = atoi(++prev);
531 prev = strchr(prev, '|');
532 next = strchr(++next, '|');
533 }
534
535 if (wps_data->progress_height < 3)
536 wps_data->progress_height = 3;
537 if (wps_data->progress_end < wps_data->progress_start + 3)
538 wps_data->progress_end = 0;
539 }
540
541 return newline - wps_token;
542
543#else
544
545 if (*(wps_token-1) == 'f')
546 wps_data->full_line_progressbar = true;
547 else
548 wps_data->full_line_progressbar = false;
549
550 return 0;
551
552#endif
553}
554
555/* Parse a generic token from the given string. Return the length read */
556static int parse_token(const char *wps_token, struct wps_data *wps_data)
557{
558 int skip = 0, taglen = 0;
559 int i = 0;
560 int line = wps_data->num_lines;
561 int subline = wps_data->num_sublines[line];
562
563 switch(*wps_token)
564 {
565
566 case '%':
567 case '<':
568 case '|':
569 case '>':
570 case ';':
571 /* escaped characters */
572 wps_data->tokens[wps_data->num_tokens].type = WPS_TOKEN_CHARACTER;
573 wps_data->tokens[wps_data->num_tokens].value.c = *wps_token;
574 wps_data->num_tokens++;
575 skip++;
576 break;
577
578 case '?':
579 /* conditional tag */
580 wps_data->tokens[wps_data->num_tokens].type = WPS_TOKEN_CONDITIONAL;
581 level++;
582 condindex[level] = wps_data->num_tokens;
583 numoptions[level] = 1;
584 wps_data->num_tokens++;
585 wps_token++;
586 skip++;
587 /* no "break" because a '?' is followed by a regular tag */
588
589 default:
590 /* find what tag we have */
591 while (all_tags[i].name &&
592 strncmp(wps_token, all_tags[i].name, strlen(all_tags[i].name)))
593 i++;
594
595 taglen = strlen(all_tags[i].name);
596 skip += taglen;
597 wps_data->tokens[wps_data->num_tokens].type = all_tags[i].type;
598
599 /* if the tag has a special parsing function, we call it */
600 if (all_tags[i].parse_func)
601 skip += all_tags[i].parse_func(wps_token + taglen, wps_data);
602
603 /* Some tags we don't want to save as tokens */
604 if (all_tags[i].type == WPS_NO_TOKEN)
605 break;
606
607 /* tags that start with 'F', 'I' or 'D' are for the next file */
608 if ( *(all_tags[i].name) == 'I' || *(all_tags[i].name) == 'F'
609 || *(all_tags[i].name) == 'D')
610 wps_data->tokens[wps_data->num_tokens].next = true;
611
612 wps_data->line_type[line][subline] |= all_tags[i].refresh_type;
613 wps_data->num_tokens++;
614 break;
615 }
616
617 return skip;
618}
619
620static bool wps_parse(struct wps_data *data, const char *wps_buffer)
621{
622 if (!data || !wps_buffer || !*wps_buffer)
623 return false;
624
625 int subline;
626 data->num_tokens = 0;
627 char *current_string = data->string_buffer;
628
629 while(wps_buffer && *wps_buffer && data->num_tokens < WPS_MAX_TOKENS
630 && data->num_lines < WPS_MAX_LINES)
631 {
632 switch(*wps_buffer++)
633 {
634
635 /* Regular tag */
636 case '%':
637 wps_buffer += parse_token(wps_buffer, data);
638 break;
639
640 /* Alternating sublines separator */
641 case ';':
642 if (data->num_sublines[data->num_lines]+1 < WPS_MAX_SUBLINES)
643 {
644 data->tokens[data->num_tokens++].type = WPS_TOKEN_SUBLINE_SEPARATOR;
645 subline = ++(data->num_sublines[data->num_lines]);
646 data->format_lines[data->num_lines][subline] = data->num_tokens;
647 }
648 else
649 wps_buffer += skip_end_of_line(wps_buffer);
650
651 break;
652
653 /* Conditional list start */
654 case '<':
655 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
656 lastcond[level] = data->num_tokens++;
657 break;
658
659 /* Conditional list end */
660 case '>':
661 if (level < 0) /* not in a conditional, ignore the char */
662 break;
663
664condlistend: /* close a conditional. sometimes we want to close them even when
665 we don't have a closing token, e.g. at the end of a line. */
666
667 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
668 if (lastcond[level])
669 data->tokens[lastcond[level]].value.i = data->num_tokens;
670
671 lastcond[level] = 0;
672 data->num_tokens++;
673 data->tokens[condindex[level]].value.i = numoptions[level];
674 level--;
675 break;
676
677 /* Conditional list option */
678 case '|':
679 if (level < 0) /* not in a conditional, ignore the char */
680 break;
681
682 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
683 if (lastcond[level])
684 data->tokens[lastcond[level]].value.i = data->num_tokens;
685
686 lastcond[level] = data->num_tokens;
687 numoptions[level]++;
688 data->num_tokens++;
689 break;
690
691 /* Comment */
692 case '#':
693 wps_buffer += skip_end_of_line(wps_buffer);
694 break;
695
696 /* End of this line */
697 case '\n':
698 if (level >= 0)
699 {
700 /* We have unclosed conditionals, so we
701 close them before adding the EOL token */
702 wps_buffer--;
703 goto condlistend;
704 break;
705 }
706 data->tokens[data->num_tokens++].type = WPS_TOKEN_EOL;
707 (data->num_sublines[data->num_lines])++;
708 data->num_lines++;
709
710 if (data->num_lines < WPS_MAX_LINES)
711 {
712 data->format_lines[data->num_lines][0] = data->num_tokens;
713 }
714
715 break;
716
717 /* String */
718 default:
719 if (data->num_strings < WPS_MAX_STRINGS)
720 {
721 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
722 data->strings[data->num_strings] = current_string;
723 data->tokens[data->num_tokens].value.i = data->num_strings++;
724 data->num_tokens++;
725
726 /* Copy the first byte */
727 *current_string++ = *(wps_buffer - 1);
728
729 /* continue until we hit something that ends the string */
730 while(wps_buffer &&
731 *wps_buffer != '%' && //*wps_buffer != '#' &&
732 *wps_buffer != '<' && *wps_buffer != '>' &&
733 *wps_buffer != '|' && *wps_buffer != '\n')
734 {
735 *current_string++ = *wps_buffer++;
736 }
737
738 /* null terminate the string */
739 *current_string++ = '\0';
740 }
741
742 break;
743 }
744 }
745
746#ifdef DEBUG
747 /* debugging code */
748 if (false)
749 {
750 dump_wps_tokens(data);
751 print_line_info(data);
752 print_wps_strings(data);
753#ifdef HAVE_LCD_BITMAP
754 print_img_cond_indexes(data);
755#endif
756 }
757#endif
758
759 return true;
760}
761
762#ifdef HAVE_LCD_BITMAP
763/* Clear the WPS image cache */
764static void wps_images_clear(struct wps_data *data)
765{
766 int i;
767 /* set images to unloaded and not displayed */
768 for (i = 0; i < MAX_IMAGES; i++)
769 {
770 data->img[i].loaded = false;
771 data->img[i].display = false;
772 data->img[i].always_display = false;
773 }
774 data->progressbar.have_bitmap_pb = false;
775}
776#endif
777
778/* initial setup of wps_data */
779void wps_data_init(struct wps_data *wps_data)
780{
781#ifdef HAVE_LCD_BITMAP
782 wps_images_clear(wps_data);
783 wps_data->wps_sb_tag = false;
784 wps_data->show_sb_on_wps = false;
785 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
786 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
787 wps_data->peak_meter_enabled = false;
788#else /* HAVE_LCD_CHARCELLS */
789 int i;
790 for(i = 0; i < 8; i++)
791 {
792 wps_data->wps_progress_pat[i] = 0;
793 }
794 wps_data->full_line_progressbar = false;
795#endif
796 wps_data->wps_loaded = false;
797}
798
799static void wps_reset(struct wps_data *data)
800{
801 memset(data, 0, sizeof(*data));
802 data->wps_loaded = false;
803 wps_data_init(data);
804}
805
806#ifdef HAVE_LCD_BITMAP
807
808
809static void clear_bmp_names(void)
810{
811 int n;
812 for (n = 0; n < MAX_IMAGES; n++)
813 {
814 bmp_names[n] = NULL;
815 }
816 pb_bmp_name = NULL;
817#if LCD_DEPTH > 1
818 backdrop_bmp_name = NULL;
819#endif
820}
821
822static void load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
823{
824 char img_path[MAX_PATH];
825
826 int n;
827 for (n = 0; n < MAX_IMAGES; n++)
828 {
829 if (bmp_names[n])
830 {
831 get_image_filename(bmp_names[n], bmpdir,
832 img_path, sizeof(img_path));
833
834 /* load the image */
835 wps_data->img[n].bm.data = wps_data->img_buf_ptr;
836 if (load_bitmap(wps_data, img_path, &wps_data->img[n].bm))
837 {
838 wps_data->img[n].loaded = true;
839 }
840 }
841 }
842
843 if (pb_bmp_name)
844 {
845 get_image_filename(pb_bmp_name, bmpdir, img_path, sizeof(img_path));
846
847 /* load the image */
848 wps_data->progressbar.bm.data = wps_data->img_buf_ptr;
849 if (load_bitmap(wps_data, img_path, &wps_data->progressbar.bm)
850 && wps_data->progressbar.bm.width <= LCD_WIDTH)
851 {
852 wps_data->progressbar.have_bitmap_pb = true;
853 }
854 }
855
856#if LCD_DEPTH > 1
857 if (backdrop_bmp_name)
858 {
859 get_image_filename(backdrop_bmp_name, bmpdir,
860 img_path, sizeof(img_path));
861 load_wps_backdrop(img_path);
862 }
863#endif
864}
865
866#endif /* HAVE_LCD_BITMAP */
867
868/* to setup up the wps-data from a format-buffer (isfile = false)
869 from a (wps-)file (isfile = true)*/
870bool wps_data_load(struct wps_data *wps_data,
871 const char *buf,
872 bool isfile)
873{
874 if (!wps_data || !buf)
875 return false;
876
877 wps_reset(wps_data);
878
879 if (!isfile)
880 {
881 return wps_parse(wps_data, buf);
882 }
883 else
884 {
885 /*
886 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
887 * wants to be a virtual file. Feel free to modify dirbrowse()
888 * if you're feeling brave.
889 */
890 if (! strcmp(buf, WPS_DEFAULTCFG) )
891 {
892 global_settings.wps_file[0] = 0;
893 return false;
894 }
895
896#ifdef HAVE_REMOTE_LCD
897 if (! strcmp(buf, RWPS_DEFAULTCFG) )
898 {
899 global_settings.rwps_file[0] = 0;
900 return false;
901 }
902#endif
903
904 int fd = open(buf, O_RDONLY);
905
906 if (fd < 0)
907 return false;
908
909 /* get buffer space from the plugin buffer */
910 unsigned int buffersize = 0;
911 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
912
913 if (!wps_buffer)
914 return false;
915
916 /* copy the file's content to the buffer for parsing */
917 unsigned int start = 0;
918 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
919 {
920 start += strlen(wps_buffer + start);
921 if (start < buffersize - 1)
922 {
923 wps_buffer[start++] = '\n';
924 wps_buffer[start] = 0;
925 }
926 }
927
928 close(fd);
929
930 if (start <= 0)
931 return false;
932
933#ifdef HAVE_LCD_BITMAP
934 clear_bmp_names();
935#endif
936
937 /* parse the WPS source */
938 if (!wps_parse(wps_data, wps_buffer))
939 return false;
940
941 wps_data->wps_loaded = true;
942
943#ifdef HAVE_LCD_BITMAP
944 /* get the bitmap dir */
945 char bmpdir[MAX_PATH];
946 size_t bmpdirlen;
947 char *dot = strrchr(buf, '.');
948 bmpdirlen = dot - buf;
949 strncpy(bmpdir, buf, dot - buf);
950 bmpdir[bmpdirlen] = 0;
951
952 /* load the bitmaps that were found by the parsing */
953 load_wps_bitmaps(wps_data, bmpdir);
954#endif
955 return true;
956 }
957}