touchscreen regions:
[kugel-rb/myfork.git] / apps / gui / wps_parser.c
blob248269ddcb24be90549b6ec06618651a950e5155
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "file.h"
26 #include "misc.h"
27 #include "plugin.h"
29 #ifdef __PCTOOL__
30 #ifdef WPSEDITOR
31 #include "proxy.h"
32 #include "sysfont.h"
33 #else
34 #include "checkwps.h"
35 #include "audio.h"
36 #define DEBUGF printf
37 #endif /*WPSEDITOR*/
38 #else
39 #include "debug.h"
40 #endif /*__PCTOOL__*/
42 #include <ctype.h>
43 #include <stdbool.h>
44 #include "font.h"
46 #include "gwps.h"
47 #include "settings.h"
48 #include "settings_list.h"
50 #ifdef HAVE_LCD_BITMAP
51 #include "bmp.h"
52 #endif
54 #include "backdrop.h"
56 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
57 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
59 #define WPS_ERROR_INVALID_PARAM -1
61 /* level of current conditional.
62 -1 means we're not in a conditional. */
63 static int level = -1;
65 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
66 or WPS_TOKEN_CONDITIONAL_START in current level */
67 static int lastcond[WPS_MAX_COND_LEVEL];
69 /* index of the WPS_TOKEN_CONDITIONAL in current level */
70 static int condindex[WPS_MAX_COND_LEVEL];
72 /* number of condtional options in current level */
73 static int numoptions[WPS_MAX_COND_LEVEL];
75 /* the current line in the file */
76 static int line;
78 #ifdef HAVE_LCD_BITMAP
80 #if LCD_DEPTH > 1
81 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
82 #else
83 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
84 #endif
86 #define PROGRESSBAR_BMP MAX_IMAGES
87 #define BACKDROP_BMP (MAX_BITMAPS-1)
89 /* pointers to the bitmap filenames in the WPS source */
90 static const char *bmp_names[MAX_BITMAPS];
92 #endif /* HAVE_LCD_BITMAP */
94 #if defined(DEBUG) || defined(SIMULATOR)
95 /* debugging function */
96 extern void print_debug_info(struct wps_data *data, int fail, int line);
97 #endif
99 static void wps_reset(struct wps_data *data);
101 /* Function for parsing of details for a token. At the moment the
102 function is called, the token type has already been set. The
103 function must fill in the details and possibly add more tokens
104 to the token array. It should return the number of chars that
105 has been consumed.
107 wps_bufptr points to the char following the tag (i.e. where
108 details begin).
109 token is the pointer to the 'main' token being parsed
111 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
112 struct wps_token *token, struct wps_data *wps_data);
114 struct wps_tag {
115 enum wps_token_type type;
116 const char name[3];
117 unsigned char refresh_type;
118 const wps_tag_parse_func parse_func;
120 static int skip_end_of_line(const char *wps_bufptr);
121 /* prototypes of all special parse functions : */
122 static int parse_timeout(const char *wps_bufptr,
123 struct wps_token *token, struct wps_data *wps_data);
124 static int parse_progressbar(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126 static int parse_dir_level(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_setting(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
131 #ifdef HAVE_LCD_BITMAP
132 static int parse_viewport_display(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_viewport(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_statusbar_enable(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_statusbar_disable(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_image_display(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_image_load(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 #endif /*HAVE_LCD_BITMAP */
145 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
146 static int parse_image_special(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 #endif
149 #ifdef HAVE_ALBUMART
150 static int parse_albumart_load(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 static int parse_albumart_conditional(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 #endif /* HAVE_ALBUMART */
155 #ifdef HAVE_TOUCHSCREEN
156 static int parse_touchregion(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 #else
159 static int fulline_tag_not_supported(const char *wps_bufptr,
160 struct wps_token *token, struct wps_data *wps_data)
162 (void)token; (void)wps_data;
163 return skip_end_of_line(wps_bufptr);
165 #define parse_touchregion fulline_tag_not_supported
166 #endif
167 #ifdef CONFIG_RTC
168 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
169 #else
170 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
171 #endif
173 /* array of available tags - those with more characters have to go first
174 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
175 static const struct wps_tag all_tags[] = {
177 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
178 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
179 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
181 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
182 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
183 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
184 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
185 #if CONFIG_CHARGING >= CHARGING_MONITOR
186 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
187 #endif
188 #if CONFIG_CHARGING
189 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
190 #endif
192 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
193 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
194 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
197 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
212 /* current file */
213 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
214 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
215 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
216 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
217 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
223 parse_dir_level },
225 /* next file */
226 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
236 parse_dir_level },
238 /* current metadata */
239 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
252 /* next metadata */
253 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
266 #if (CONFIG_CODEC != MAS3507D)
267 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
268 #endif
270 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
271 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
272 #endif
274 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
276 #ifdef HAS_REMOTE_BUTTON_HOLD
277 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
278 #else
279 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
280 #endif
282 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
283 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
284 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
285 parse_timeout },
287 #ifdef HAVE_LCD_BITMAP
288 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
289 #else
290 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
291 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
292 #endif
293 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
294 parse_progressbar },
296 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
298 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
299 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
300 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
301 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
303 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
304 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
305 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
306 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
308 #ifdef HAVE_TAGCACHE
309 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
311 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
312 #endif
314 #if CONFIG_CODEC == SWCODEC
315 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
316 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
317 #endif
319 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
320 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
322 #ifdef HAVE_LCD_BITMAP
323 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
324 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
326 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
328 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
329 parse_image_display },
331 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
332 #ifdef HAVE_ALBUMART
333 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
334 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
335 parse_albumart_conditional },
336 #endif
338 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
339 parse_viewport_display },
340 { WPS_NO_TOKEN, "V", 0, parse_viewport },
342 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
343 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
344 #endif
345 #endif
347 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
349 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
350 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
352 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
353 /* the array MUST end with an empty string (first char is \0) */
356 /* Returns the number of chars that should be skipped to jump
357 immediately after the first eol, i.e. to the start of the next line */
358 static int skip_end_of_line(const char *wps_bufptr)
360 line++;
361 int skip = 0;
362 while(*(wps_bufptr + skip) != '\n')
363 skip++;
364 return ++skip;
367 /* Starts a new subline in the current line during parsing */
368 static void wps_start_new_subline(struct wps_data *data)
370 data->num_sublines++;
371 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
372 data->lines[data->num_lines].num_sublines++;
375 #ifdef HAVE_LCD_BITMAP
377 static int parse_statusbar_enable(const char *wps_bufptr,
378 struct wps_token *token,
379 struct wps_data *wps_data)
381 (void)token; /* Kill warnings */
382 wps_data->wps_sb_tag = true;
383 wps_data->show_sb_on_wps = true;
384 if (wps_data->viewports[0].vp.y == 0)
386 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
387 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
389 return skip_end_of_line(wps_bufptr);
392 static int parse_statusbar_disable(const char *wps_bufptr,
393 struct wps_token *token,
394 struct wps_data *wps_data)
396 (void)token; /* Kill warnings */
397 wps_data->wps_sb_tag = true;
398 wps_data->show_sb_on_wps = false;
399 if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
401 wps_data->viewports[0].vp.y = 0;
402 wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
404 return skip_end_of_line(wps_bufptr);
407 static bool load_bitmap(struct wps_data *wps_data,
408 char* filename,
409 struct bitmap *bm)
411 int format;
412 #ifdef HAVE_REMOTE_LCD
413 if (wps_data->remote_wps)
414 format = FORMAT_ANY|FORMAT_REMOTE;
415 else
416 #endif
417 format = FORMAT_ANY|FORMAT_TRANSPARENT;
419 int ret = read_bmp_file(filename, bm,
420 wps_data->img_buf_free,
421 format,NULL);
423 if (ret > 0)
425 #if LCD_DEPTH == 16
426 if (ret % 2) ret++;
427 /* Always consume an even number of bytes */
428 #endif
429 wps_data->img_buf_ptr += ret;
430 wps_data->img_buf_free -= ret;
432 return true;
434 else
435 return false;
438 static int get_image_id(int c)
440 if(c >= 'a' && c <= 'z')
441 return c - 'a';
442 else if(c >= 'A' && c <= 'Z')
443 return c - 'A' + 26;
444 else
445 return -1;
448 static char *get_image_filename(const char *start, const char* bmpdir,
449 char *buf, int buf_size)
451 const char *end = strchr(start, '|');
453 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
455 buf = "\0";
456 return NULL;
459 int bmpdirlen = strlen(bmpdir);
461 strcpy(buf, bmpdir);
462 buf[bmpdirlen] = '/';
463 memcpy( &buf[bmpdirlen + 1], start, end - start);
464 buf[bmpdirlen + 1 + end - start] = 0;
466 return buf;
469 static int parse_image_display(const char *wps_bufptr,
470 struct wps_token *token,
471 struct wps_data *wps_data)
473 (void)wps_data;
474 int n = get_image_id(wps_bufptr[0]);
475 int subimage;
477 if (n == -1)
479 /* invalid picture display tag */
480 return WPS_ERROR_INVALID_PARAM;
483 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
485 /* Sanity check */
486 if (subimage >= wps_data->img[n].num_subimages)
487 return WPS_ERROR_INVALID_PARAM;
489 /* Store sub-image number to display in high bits */
490 token->value.i = n | (subimage << 8);
491 return 2; /* We have consumed 2 bytes */
492 } else {
493 token->value.i = n;
494 return 1; /* We have consumed 1 byte */
498 static int parse_image_load(const char *wps_bufptr,
499 struct wps_token *token,
500 struct wps_data *wps_data)
502 int n;
503 const char *ptr = wps_bufptr;
504 const char *pos;
505 const char* filename;
506 const char* id;
507 const char *newline;
508 int x,y;
510 /* format: %x|n|filename.bmp|x|y|
511 or %xl|n|filename.bmp|x|y|
512 or %xl|n|filename.bmp|x|y|num_subimages|
515 if (*ptr != '|')
516 return WPS_ERROR_INVALID_PARAM;
518 ptr++;
520 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
521 return WPS_ERROR_INVALID_PARAM;
523 /* Check there is a terminating | */
524 if (*ptr != '|')
525 return WPS_ERROR_INVALID_PARAM;
527 /* get the image ID */
528 n = get_image_id(*id);
530 /* check the image number and load state */
531 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
533 /* Invalid image ID */
534 return WPS_ERROR_INVALID_PARAM;
537 /* save a pointer to the filename */
538 bmp_names[n] = filename;
540 wps_data->img[n].x = x;
541 wps_data->img[n].y = y;
543 /* save current viewport */
544 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
546 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
548 wps_data->img[n].always_display = true;
550 else
552 /* Parse the (optional) number of sub-images */
553 ptr++;
554 newline = strchr(ptr, '\n');
555 pos = strchr(ptr, '|');
556 if (pos && pos < newline)
557 wps_data->img[n].num_subimages = atoi(ptr);
559 if (wps_data->img[n].num_subimages <= 0)
560 return WPS_ERROR_INVALID_PARAM;
563 /* Skip the rest of the line */
564 return skip_end_of_line(wps_bufptr);
567 static int parse_viewport_display(const char *wps_bufptr,
568 struct wps_token *token,
569 struct wps_data *wps_data)
571 (void)wps_data;
572 char letter = wps_bufptr[0];
574 if (letter < 'a' || letter > 'z')
576 /* invalid viewport tag */
577 return WPS_ERROR_INVALID_PARAM;
579 token->value.i = letter;
580 return 1;
583 static int parse_viewport(const char *wps_bufptr,
584 struct wps_token *token,
585 struct wps_data *wps_data)
587 (void)token; /* Kill warnings */
588 const char *ptr = wps_bufptr;
589 struct viewport* vp;
590 int depth;
591 uint32_t set = 0;
592 enum {
593 PL_X = 0,
594 PL_Y,
595 PL_WIDTH,
596 PL_HEIGHT,
597 PL_FONT,
598 PL_FG,
599 PL_BG,
601 int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
602 #ifdef HAVE_REMOTE_LCD
603 if (wps_data->remote_wps)
605 lcd_width = LCD_REMOTE_WIDTH;
606 lcd_height = LCD_REMOTE_HEIGHT;
608 #endif
610 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
611 return WPS_ERROR_INVALID_PARAM;
613 wps_data->num_viewports++;
614 /* check for the optional letter to signify its a hideable viewport */
615 /* %Vl|<label>|<rest of tags>| */
616 wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
618 if (*ptr == 'l')
620 if (*(ptr+1) == '|')
622 char label = *(ptr+2);
623 if (label >= 'a' && label <= 'z')
625 wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
626 wps_data->viewports[wps_data->num_viewports].label = label;
628 else
629 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
630 ptr += 3;
633 if (*ptr != '|')
634 return WPS_ERROR_INVALID_PARAM;
636 ptr++;
637 vp = &wps_data->viewports[wps_data->num_viewports].vp;
638 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
640 /* Set the defaults for fields not user-specified */
641 vp->drawmode = DRMODE_SOLID;
643 /* Work out the depth of this display */
644 #ifdef HAVE_REMOTE_LCD
645 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
646 #else
647 depth = LCD_DEPTH;
648 #endif
650 #ifdef HAVE_LCD_COLOR
651 if (depth == 16)
653 if (!(ptr = parse_list("dddddcc", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
654 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
655 return WPS_ERROR_INVALID_PARAM;
657 else
658 #endif
659 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
660 if (depth == 2) {
661 /* Default to black on white */
662 vp->fg_pattern = 0;
663 vp->bg_pattern = 3;
664 if (!(ptr = parse_list("dddddgg", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
665 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
666 return WPS_ERROR_INVALID_PARAM;
668 else
669 #endif
670 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
671 if (depth == 1)
673 if (!(ptr = parse_list("ddddd", &set, '|', ptr, &vp->x, &vp->y,
674 &vp->width, &vp->height, &vp->font)))
675 return WPS_ERROR_INVALID_PARAM;
677 else
678 #endif
681 /* Check for trailing | */
682 if (*ptr != '|')
683 return WPS_ERROR_INVALID_PARAM;
685 if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
686 return WPS_ERROR_INVALID_PARAM;
688 /* fix defaults */
689 if (!LIST_VALUE_PARSED(set, PL_WIDTH))
690 vp->width = lcd_width - vp->x;
691 if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
692 vp->height = lcd_height - vp->y;
694 /* Default to using the user font if the font was an invalid number */
695 if (!LIST_VALUE_PARSED(set, PL_FONT) ||
696 ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
697 vp->font = FONT_UI;
699 /* Validate the viewport dimensions - we know that the numbers are
700 non-negative integers */
701 if ((vp->x >= lcd_width) ||
702 ((vp->x + vp->width) > lcd_width) ||
703 (vp->y >= lcd_height) ||
704 ((vp->y + vp->height) > lcd_height))
706 return WPS_ERROR_INVALID_PARAM;
709 #ifdef HAVE_LCD_COLOR
710 if (depth == 16)
712 if (!LIST_VALUE_PARSED(set, PL_FG))
713 vp->fg_pattern = global_settings.fg_color;
714 if (!LIST_VALUE_PARSED(set, PL_BG))
715 vp->bg_pattern = global_settings.bg_color;
717 #endif
719 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
721 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
723 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
725 wps_data->lines[wps_data->num_lines].first_subline_idx =
726 wps_data->num_sublines;
728 wps_data->sublines[wps_data->num_sublines].first_token_idx =
729 wps_data->num_tokens;
732 /* Skip the rest of the line */
733 return skip_end_of_line(wps_bufptr);
736 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
737 static int parse_image_special(const char *wps_bufptr,
738 struct wps_token *token,
739 struct wps_data *wps_data)
741 (void)wps_data; /* kill warning */
742 (void)token;
743 const char *pos = NULL;
744 const char *newline;
746 pos = strchr(wps_bufptr + 1, '|');
747 newline = strchr(wps_bufptr, '\n');
749 if (pos > newline)
750 return WPS_ERROR_INVALID_PARAM;
751 #if LCD_DEPTH > 1
752 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
754 /* format: %X|filename.bmp| */
755 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
757 #endif
759 /* Skip the rest of the line */
760 return skip_end_of_line(wps_bufptr);
762 #endif
764 #endif /* HAVE_LCD_BITMAP */
766 static int parse_setting(const char *wps_bufptr,
767 struct wps_token *token,
768 struct wps_data *wps_data)
770 (void)wps_data;
771 const char *ptr = wps_bufptr;
772 const char *end;
773 int i;
775 /* Find the setting's cfg_name */
776 if (*ptr != '|')
777 return WPS_ERROR_INVALID_PARAM;
778 ptr++;
779 end = strchr(ptr,'|');
780 if (!end)
781 return WPS_ERROR_INVALID_PARAM;
783 /* Find the setting */
784 for (i=0; i<nb_settings; i++)
785 if (settings[i].cfg_name &&
786 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
787 /* prevent matches on cfg_name prefixes */
788 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
789 break;
790 if (i == nb_settings)
791 return WPS_ERROR_INVALID_PARAM;
793 /* Store the setting number */
794 token->value.i = i;
796 /* Skip the rest of the line */
797 return end-ptr+2;
801 static int parse_dir_level(const char *wps_bufptr,
802 struct wps_token *token,
803 struct wps_data *wps_data)
805 char val[] = { *wps_bufptr, '\0' };
806 token->value.i = atoi(val);
807 (void)wps_data; /* Kill warnings */
808 return 1;
811 static int parse_timeout(const char *wps_bufptr,
812 struct wps_token *token,
813 struct wps_data *wps_data)
815 int skip = 0;
816 int val = 0;
817 bool have_point = false;
818 bool have_tenth = false;
820 (void)wps_data; /* Kill the warning */
822 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
824 if (*wps_bufptr != '.')
826 val *= 10;
827 val += *wps_bufptr - '0';
828 if (have_point)
830 have_tenth = true;
831 wps_bufptr++;
832 skip++;
833 break;
836 else
837 have_point = true;
839 wps_bufptr++;
840 skip++;
843 if (have_tenth == false)
844 val *= 10;
846 if (val == 0 && skip == 0)
848 /* decide what to do if no value was specified */
849 switch (token->type)
851 case WPS_TOKEN_SUBLINE_TIMEOUT:
852 return -1;
853 case WPS_TOKEN_BUTTON_VOLUME:
854 val = 10;
855 break;
858 token->value.i = val;
860 return skip;
863 static int parse_progressbar(const char *wps_bufptr,
864 struct wps_token *token,
865 struct wps_data *wps_data)
867 (void)token; /* Kill warnings */
868 /* %pb or %pb|filename|x|y|width|height|
869 using - for any of the params uses "sane" values */
870 #ifdef HAVE_LCD_BITMAP
871 enum {
872 PB_FILENAME = 0,
873 PB_X,
874 PB_Y,
875 PB_WIDTH,
876 PB_HEIGHT
878 const char *filename;
879 int x, y, height, width;
880 uint32_t set = 0;
881 const char *ptr = wps_bufptr;
882 struct progressbar *pb;
883 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
884 #ifndef __PCTOOL__
885 int font_height = font_get(vp->font)->height;
886 #else
887 int font_height = 8;
888 #endif
889 int line_num = wps_data->num_lines -
890 wps_data->viewports[wps_data->num_viewports].first_line;
892 if (wps_data->progressbar_count >= MAX_PROGRESSBARS)
893 return WPS_ERROR_INVALID_PARAM;
895 pb = &wps_data->progressbar[wps_data->progressbar_count];
896 pb->have_bitmap_pb = false;
898 if (*wps_bufptr != '|') /* regular old style */
900 pb->x = 0;
901 pb->width = vp->width;
902 pb->height = SYSFONT_HEIGHT-2;
903 pb->y = -line_num - 1; /* Will be computed during the rendering */
905 wps_data->viewports[wps_data->num_viewports].pb = pb;
906 wps_data->progressbar_count++;
907 return 0;
909 ptr = wps_bufptr + 1;
911 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
912 &x, &y, &width, &height)))
913 return WPS_ERROR_INVALID_PARAM;
915 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
916 bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
918 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
919 pb->x = x;
920 else
921 pb->x = vp->x;
923 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
925 /* A zero width causes a divide-by-zero error later, so reject it */
926 if (width == 0)
927 return WPS_ERROR_INVALID_PARAM;
929 pb->width = width;
931 else
932 pb->width = vp->width - pb->x;
934 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
936 /* A zero height makes no sense - reject it */
937 if (height == 0)
938 return WPS_ERROR_INVALID_PARAM;
940 pb->height = height;
942 else
943 pb->height = font_height;
945 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
946 pb->y = y;
947 else
948 pb->y = -line_num - 1; /* Will be computed during the rendering */
950 wps_data->viewports[wps_data->num_viewports].pb = pb;
951 wps_data->progressbar_count++;
953 /* Skip the rest of the line */
954 return skip_end_of_line(wps_bufptr)-1;
955 #else
957 if (*(wps_bufptr-1) == 'f')
958 wps_data->full_line_progressbar = true;
959 else
960 wps_data->full_line_progressbar = false;
962 return 0;
964 #endif
967 #ifdef HAVE_ALBUMART
968 static int parse_albumart_load(const char *wps_bufptr,
969 struct wps_token *token,
970 struct wps_data *wps_data)
972 const char *_pos, *newline;
973 bool parsing;
974 (void)token; /* silence warning */
976 /* reset albumart info in wps */
977 wps_data->albumart_max_width = -1;
978 wps_data->albumart_max_height = -1;
979 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
980 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
982 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
984 newline = strchr(wps_bufptr, '\n');
986 /* initial validation and parsing of x and y components */
987 if (*wps_bufptr != '|')
988 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
990 _pos = wps_bufptr + 1;
991 if (!isdigit(*_pos))
992 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
993 wps_data->albumart_x = atoi(_pos);
995 _pos = strchr(_pos, '|');
996 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
997 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
999 wps_data->albumart_y = atoi(_pos);
1001 _pos = strchr(_pos, '|');
1002 if (!_pos || _pos > newline)
1003 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1004 e.g. %Cl|7|59\n */
1006 /* parsing width field */
1007 parsing = true;
1008 while (parsing)
1010 /* apply each modifier in turn */
1011 ++_pos;
1012 switch (*_pos)
1014 case 'l':
1015 case 'L':
1016 case '+':
1017 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
1018 break;
1019 case 'c':
1020 case 'C':
1021 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
1022 break;
1023 case 'r':
1024 case 'R':
1025 case '-':
1026 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
1027 break;
1028 case 'd':
1029 case 'D':
1030 case 'i':
1031 case 'I':
1032 case 's':
1033 case 'S':
1034 /* simply ignored */
1035 break;
1036 default:
1037 parsing = false;
1038 break;
1041 /* extract max width data */
1042 if (*_pos != '|')
1044 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1045 return WPS_ERROR_INVALID_PARAM;
1047 wps_data->albumart_max_width = atoi(_pos);
1049 _pos = strchr(_pos, '|');
1050 if (!_pos || _pos > newline)
1051 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1052 e.g. %Cl|7|59|200\n */
1055 /* parsing height field */
1056 parsing = true;
1057 while (parsing)
1059 /* apply each modifier in turn */
1060 ++_pos;
1061 switch (*_pos)
1063 case 't':
1064 case 'T':
1065 case '-':
1066 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1067 break;
1068 case 'c':
1069 case 'C':
1070 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1071 break;
1072 case 'b':
1073 case 'B':
1074 case '+':
1075 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1076 break;
1077 case 'd':
1078 case 'D':
1079 case 'i':
1080 case 'I':
1081 case 's':
1082 case 'S':
1083 /* simply ignored */
1084 break;
1085 default:
1086 parsing = false;
1087 break;
1090 /* extract max height data */
1091 if (*_pos != '|')
1093 if (!isdigit(*_pos))
1094 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1096 wps_data->albumart_max_height = atoi(_pos);
1098 _pos = strchr(_pos, '|');
1099 if (!_pos || _pos > newline)
1100 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1101 e.g. %Cl|7|59|200|200\n */
1104 /* if we got here, we parsed everything ok .. ! */
1105 if (wps_data->albumart_max_width < 0)
1106 wps_data->albumart_max_width = 0;
1107 else if (wps_data->albumart_max_width > LCD_WIDTH)
1108 wps_data->albumart_max_width = LCD_WIDTH;
1110 if (wps_data->albumart_max_height < 0)
1111 wps_data->albumart_max_height = 0;
1112 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1113 wps_data->albumart_max_height = LCD_HEIGHT;
1115 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1117 /* Skip the rest of the line */
1118 return skip_end_of_line(wps_bufptr);
1121 static int parse_albumart_conditional(const char *wps_bufptr,
1122 struct wps_token *token,
1123 struct wps_data *wps_data)
1125 struct wps_token *prevtoken = token;
1126 --prevtoken;
1127 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1129 /* This %C is part of a %?C construct.
1130 It's either %?C<blah> or %?Cn<blah> */
1131 token->type = WPS_TOKEN_ALBUMART_FOUND;
1132 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1134 token->next = true;
1135 return 1;
1137 else if (*wps_bufptr == '<')
1139 return 0;
1141 else
1143 token->type = WPS_NO_TOKEN;
1144 return 0;
1147 else
1149 /* This %C tag is in a conditional construct. */
1150 wps_data->albumart_cond_index = condindex[level];
1151 return 0;
1154 #endif /* HAVE_ALBUMART */
1156 #ifdef HAVE_TOUCHSCREEN
1158 struct touchaction {char* s; int action;};
1159 static struct touchaction touchactions[] = {
1160 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1161 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1162 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1163 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1164 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1165 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1167 static int parse_touchregion(const char *wps_bufptr,
1168 struct wps_token *token, struct wps_data *wps_data)
1170 (void)token;
1171 unsigned i, imax;
1172 struct touchregion *region;
1173 const char *ptr = wps_bufptr;
1174 const char *action;
1175 int x,y,w,h;
1177 /* format: %T|x|y|width|height|action|
1178 * if action starts with & the area must be held to happen
1179 * action is one of:
1180 * play - play/pause playback
1181 * stop - stop playback, exit the wps
1182 * prev - prev track
1183 * next - next track
1184 * ffwd - seek forward
1185 * rwd - seek backwards
1186 * menu - go back to the main menu
1187 * browse - go back to the file/db browser
1188 * shuffle - toggle shuffle mode
1189 * repmode - cycle the repeat mode
1190 * quickscreen - go into the quickscreen
1191 * contextmenu - open the context menu
1194 if ((wps_data->touchregion_count +1 >= MAX_TOUCHREGIONS) || (*ptr != '|'))
1195 return WPS_ERROR_INVALID_PARAM;
1196 ptr++;
1198 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1199 return WPS_ERROR_INVALID_PARAM;
1201 /* Check there is a terminating | */
1202 if (*ptr != '|')
1203 return WPS_ERROR_INVALID_PARAM;
1205 /* should probably do some bounds checking here with the viewport... but later */
1206 region = &wps_data->touchregion[wps_data->touchregion_count];
1207 region->action = ACTION_NONE;
1208 region->x = x;
1209 region->y = y;
1210 region->width = w;
1211 region->height = h;
1212 region->wvp = &wps_data->viewports[wps_data->num_viewports];
1213 i = 0;
1214 if (*action == '&')
1216 action++;
1217 region->repeat = true;
1219 else
1220 region->repeat = false;
1222 imax = ARRAYLEN(touchactions);
1223 while ((region->action == ACTION_NONE) &&
1224 (i < imax))
1226 /* try to match with one of our touchregion screens */
1227 int len = strlen(touchactions[i].s);
1228 if (!strncmp(touchactions[i].s, action, len)
1229 && *(action+len) == '|')
1230 region->action = touchactions[i].action;
1231 i++;
1233 if (region->action == ACTION_NONE)
1234 return WPS_ERROR_INVALID_PARAM;
1235 wps_data->touchregion_count++;
1236 return skip_end_of_line(wps_bufptr);
1238 #endif
1240 /* Parse a generic token from the given string. Return the length read */
1241 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1243 int skip = 0, taglen = 0, ret;
1244 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1245 const struct wps_tag *tag;
1247 switch(*wps_bufptr)
1250 case '%':
1251 case '<':
1252 case '|':
1253 case '>':
1254 case ';':
1255 case '#':
1256 /* escaped characters */
1257 token->type = WPS_TOKEN_CHARACTER;
1258 token->value.c = *wps_bufptr;
1259 taglen = 1;
1260 wps_data->num_tokens++;
1261 break;
1263 case '?':
1264 /* conditional tag */
1265 token->type = WPS_TOKEN_CONDITIONAL;
1266 level++;
1267 condindex[level] = wps_data->num_tokens;
1268 numoptions[level] = 1;
1269 wps_data->num_tokens++;
1270 ret = parse_token(wps_bufptr + 1, wps_data);
1271 if (ret < 0) return ret;
1272 taglen = 1 + ret;
1273 break;
1275 default:
1276 /* find what tag we have */
1277 for (tag = all_tags;
1278 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1279 tag++) ;
1281 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1282 token->type = tag->type;
1283 wps_data->sublines[wps_data->num_sublines].line_type |=
1284 tag->refresh_type;
1286 /* if the tag has a special parsing function, we call it */
1287 if (tag->parse_func)
1289 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1290 if (ret < 0) return ret;
1291 skip += ret;
1294 /* Some tags we don't want to save as tokens */
1295 if (tag->type == WPS_NO_TOKEN)
1296 break;
1298 /* tags that start with 'F', 'I' or 'D' are for the next file */
1299 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1300 *(tag->name) == 'D')
1301 token->next = true;
1303 wps_data->num_tokens++;
1304 break;
1307 skip += taglen;
1308 return skip;
1311 /* Parses the WPS.
1312 data is the pointer to the structure where the parsed WPS should be stored.
1313 It is initialised.
1314 wps_bufptr points to the string containing the WPS tags */
1315 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1317 if (!data || !wps_bufptr || !*wps_bufptr)
1318 return false;
1320 char *stringbuf = data->string_buffer;
1321 int stringbuf_used = 0;
1322 enum wps_parse_error fail = PARSE_OK;
1323 int ret;
1324 line = 1;
1325 level = -1;
1327 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1328 && data->num_viewports < WPS_MAX_VIEWPORTS
1329 && data->num_lines < WPS_MAX_LINES)
1331 switch(*wps_bufptr++)
1334 /* Regular tag */
1335 case '%':
1336 if ((ret = parse_token(wps_bufptr, data)) < 0)
1338 fail = PARSE_FAIL_COND_INVALID_PARAM;
1339 break;
1341 else if (level >= WPS_MAX_COND_LEVEL - 1)
1343 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1344 break;
1346 wps_bufptr += ret;
1347 break;
1349 /* Alternating sublines separator */
1350 case ';':
1351 if (level >= 0) /* there are unclosed conditionals */
1353 fail = PARSE_FAIL_UNCLOSED_COND;
1354 break;
1357 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1358 wps_start_new_subline(data);
1359 else
1360 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1362 break;
1364 /* Conditional list start */
1365 case '<':
1366 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1368 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1369 break;
1372 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1373 lastcond[level] = data->num_tokens++;
1374 break;
1376 /* Conditional list end */
1377 case '>':
1378 if (level < 0) /* not in a conditional, invalid char */
1380 fail = PARSE_FAIL_INVALID_CHAR;
1381 break;
1384 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1385 if (lastcond[level])
1386 data->tokens[lastcond[level]].value.i = data->num_tokens;
1387 else
1389 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1390 break;
1393 lastcond[level] = 0;
1394 data->num_tokens++;
1395 data->tokens[condindex[level]].value.i = numoptions[level];
1396 level--;
1397 break;
1399 /* Conditional list option */
1400 case '|':
1401 if (level < 0) /* not in a conditional, invalid char */
1403 fail = PARSE_FAIL_INVALID_CHAR;
1404 break;
1407 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1408 if (lastcond[level])
1409 data->tokens[lastcond[level]].value.i = data->num_tokens;
1410 else
1412 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1413 break;
1416 lastcond[level] = data->num_tokens;
1417 numoptions[level]++;
1418 data->num_tokens++;
1419 break;
1421 /* Comment */
1422 case '#':
1423 if (level >= 0) /* there are unclosed conditionals */
1425 fail = PARSE_FAIL_UNCLOSED_COND;
1426 break;
1429 wps_bufptr += skip_end_of_line(wps_bufptr);
1430 break;
1432 /* End of this line */
1433 case '\n':
1434 if (level >= 0) /* there are unclosed conditionals */
1436 fail = PARSE_FAIL_UNCLOSED_COND;
1437 break;
1440 line++;
1441 wps_start_new_subline(data);
1442 data->num_lines++; /* Start a new line */
1444 if ((data->num_lines < WPS_MAX_LINES) &&
1445 (data->num_sublines < WPS_MAX_SUBLINES))
1447 data->lines[data->num_lines].first_subline_idx =
1448 data->num_sublines;
1450 data->sublines[data->num_sublines].first_token_idx =
1451 data->num_tokens;
1454 break;
1456 /* String */
1457 default:
1459 unsigned int len = 1;
1460 const char *string_start = wps_bufptr - 1;
1462 /* find the length of the string */
1463 while (*wps_bufptr && *wps_bufptr != '#' &&
1464 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1465 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1466 *wps_bufptr != '|' && *wps_bufptr != '\n')
1468 wps_bufptr++;
1469 len++;
1472 /* look if we already have that string */
1473 char **str;
1474 int i;
1475 bool found;
1476 for (i = 0, str = data->strings, found = false;
1477 i < data->num_strings &&
1478 !(found = (strlen(*str) == len &&
1479 strncmp(string_start, *str, len) == 0));
1480 i++, str++);
1481 /* If a matching string is found, found is true and i is
1482 the index of the string. If not, found is false */
1484 if (!found)
1486 /* new string */
1488 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1
1489 || data->num_strings >= WPS_MAX_STRINGS)
1491 /* too many strings or characters */
1492 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1493 break;
1496 strlcpy(stringbuf, string_start, len+1);
1498 data->strings[data->num_strings] = stringbuf;
1499 stringbuf += len + 1;
1500 stringbuf_used += len + 1;
1501 data->tokens[data->num_tokens].value.i =
1502 data->num_strings;
1503 data->num_strings++;
1505 else
1507 /* another occurrence of an existing string */
1508 data->tokens[data->num_tokens].value.i = i;
1510 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1511 data->num_tokens++;
1513 break;
1517 if (!fail && level >= 0) /* there are unclosed conditionals */
1518 fail = PARSE_FAIL_UNCLOSED_COND;
1520 if (*wps_bufptr && !fail)
1521 /* one of the limits of the while loop was exceeded */
1522 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1524 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1526 /* We have finished with the last viewport, so increment count */
1527 data->num_viewports++;
1529 #if defined(DEBUG) || defined(SIMULATOR)
1530 print_debug_info(data, fail, line);
1531 #endif
1533 return (fail == 0);
1536 #ifdef HAVE_LCD_BITMAP
1537 /* Clear the WPS image cache */
1538 static void wps_images_clear(struct wps_data *data)
1540 int i;
1541 /* set images to unloaded and not displayed */
1542 for (i = 0; i < MAX_IMAGES; i++)
1544 data->img[i].loaded = false;
1545 data->img[i].display = -1;
1546 data->img[i].always_display = false;
1547 data->img[i].num_subimages = 1;
1550 #endif
1552 /* initial setup of wps_data */
1553 void wps_data_init(struct wps_data *wps_data)
1555 #ifdef HAVE_LCD_BITMAP
1556 wps_images_clear(wps_data);
1557 wps_data->wps_sb_tag = false;
1558 wps_data->show_sb_on_wps = false;
1559 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1560 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1561 wps_data->peak_meter_enabled = false;
1562 /* progress bars */
1563 wps_data->progressbar_count = 0;
1564 #else /* HAVE_LCD_CHARCELLS */
1565 int i;
1566 for (i = 0; i < 8; i++)
1568 wps_data->wps_progress_pat[i] = 0;
1570 wps_data->full_line_progressbar = false;
1571 #endif
1572 wps_data->button_time_volume = 0;
1573 wps_data->wps_loaded = false;
1576 static void wps_reset(struct wps_data *data)
1578 #ifdef HAVE_REMOTE_LCD
1579 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1580 #endif
1581 memset(data, 0, sizeof(*data));
1582 wps_data_init(data);
1583 #ifdef HAVE_REMOTE_LCD
1584 data->remote_wps = rwps;
1585 #endif
1588 #ifdef HAVE_LCD_BITMAP
1590 static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1592 char img_path[MAX_PATH];
1593 struct bitmap *bitmap;
1594 bool *loaded;
1595 int n;
1596 for (n = 0; n < BACKDROP_BMP; n++)
1598 if (bmp_names[n])
1600 get_image_filename(bmp_names[n], bmpdir,
1601 img_path, sizeof(img_path));
1603 if (n >= PROGRESSBAR_BMP ) {
1604 /* progressbar bitmap */
1605 bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
1606 loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
1607 } else {
1608 /* regular bitmap */
1609 bitmap = &wps_data->img[n].bm;
1610 loaded = &wps_data->img[n].loaded;
1613 /* load the image */
1614 bitmap->data = wps_data->img_buf_ptr;
1615 if (load_bitmap(wps_data, img_path, bitmap))
1617 *loaded = true;
1619 /* Calculate and store height if this image has sub-images */
1620 if (n < MAX_IMAGES)
1621 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1622 wps_data->img[n].num_subimages;
1624 else
1626 /* Abort if we can't load an image */
1627 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1628 return false;
1633 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1634 if (bmp_names[BACKDROP_BMP])
1636 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1637 img_path, sizeof(img_path));
1639 #if defined(HAVE_REMOTE_LCD)
1640 /* We only need to check LCD type if there is a remote LCD */
1641 if (!wps_data->remote_wps)
1642 #endif
1644 /* Load backdrop for the main LCD */
1645 if (!load_wps_backdrop(img_path))
1646 return false;
1648 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1649 else
1651 /* Load backdrop for the remote LCD */
1652 if (!load_remote_wps_backdrop(img_path))
1653 return false;
1655 #endif
1657 #endif /* has backdrop support */
1659 /* If we got here, everything was OK */
1660 return true;
1663 #endif /* HAVE_LCD_BITMAP */
1665 /* to setup up the wps-data from a format-buffer (isfile = false)
1666 from a (wps-)file (isfile = true)*/
1667 bool wps_data_load(struct wps_data *wps_data,
1668 struct screen *display,
1669 const char *buf,
1670 bool isfile)
1672 #ifdef HAVE_ALBUMART
1673 struct mp3entry *curtrack;
1674 long offset;
1675 int status;
1676 int wps_uses_albumart = wps_data->wps_uses_albumart;
1677 int albumart_max_height = wps_data->albumart_max_height;
1678 int albumart_max_width = wps_data->albumart_max_width;
1679 #endif
1680 if (!wps_data || !buf)
1681 return false;
1683 wps_reset(wps_data);
1685 /* Initialise the first (default) viewport */
1686 wps_data->viewports[0].vp.x = 0;
1687 wps_data->viewports[0].vp.width = display->getwidth();
1688 wps_data->viewports[0].vp.height = display->getheight();
1689 switch (statusbar_position(display->screen_type))
1691 case STATUSBAR_OFF:
1692 wps_data->viewports[0].vp.y = 0;
1693 break;
1694 case STATUSBAR_TOP:
1695 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1696 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
1697 break;
1698 case STATUSBAR_BOTTOM:
1699 wps_data->viewports[0].vp.y = 0;
1700 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
1701 break;
1703 #ifdef HAVE_LCD_BITMAP
1704 wps_data->viewports[0].vp.font = FONT_UI;
1705 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1706 #endif
1707 #if LCD_DEPTH > 1
1708 if (display->depth > 1)
1710 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1711 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1713 #endif
1714 if (!isfile)
1716 return wps_parse(wps_data, buf);
1718 else
1721 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1722 * wants to be a virtual file. Feel free to modify dirbrowse()
1723 * if you're feeling brave.
1725 #ifndef __PCTOOL__
1726 if (! strcmp(buf, WPS_DEFAULTCFG) )
1728 global_settings.wps_file[0] = 0;
1729 return false;
1732 #ifdef HAVE_REMOTE_LCD
1733 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1735 global_settings.rwps_file[0] = 0;
1736 return false;
1738 #endif
1739 #endif /* __PCTOOL__ */
1741 int fd = open_utf8(buf, O_RDONLY);
1743 if (fd < 0)
1744 return false;
1746 /* get buffer space from the plugin buffer */
1747 size_t buffersize = 0;
1748 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1750 if (!wps_buffer)
1751 return false;
1753 /* copy the file's content to the buffer for parsing,
1754 ensuring that every line ends with a newline char. */
1755 unsigned int start = 0;
1756 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1758 start += strlen(wps_buffer + start);
1759 if (start < buffersize - 1)
1761 wps_buffer[start++] = '\n';
1762 wps_buffer[start] = 0;
1766 close(fd);
1768 if (start <= 0)
1769 return false;
1771 #ifdef HAVE_LCD_BITMAP
1772 /* Set all filename pointers to NULL */
1773 memset(bmp_names, 0, sizeof(bmp_names));
1774 #endif
1776 /* parse the WPS source */
1777 if (!wps_parse(wps_data, wps_buffer)) {
1778 wps_reset(wps_data);
1779 return false;
1782 wps_data->wps_loaded = true;
1784 #ifdef HAVE_LCD_BITMAP
1785 /* get the bitmap dir */
1786 char bmpdir[MAX_PATH];
1787 char *dot = strrchr(buf, '.');
1789 strlcpy(bmpdir, buf, dot - buf + 1);
1791 /* load the bitmaps that were found by the parsing */
1792 if (!load_wps_bitmaps(wps_data, bmpdir)) {
1793 wps_reset(wps_data);
1794 return false;
1796 #endif
1797 #ifdef HAVE_ALBUMART
1798 status = audio_status();
1799 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1800 (wps_data->wps_uses_albumart &&
1801 (albumart_max_height != wps_data->albumart_max_height ||
1802 albumart_max_width != wps_data->albumart_max_width))) &&
1803 status & AUDIO_STATUS_PLAY)
1805 curtrack = audio_current_track();
1806 offset = curtrack->offset;
1807 audio_stop();
1808 if (!(status & AUDIO_STATUS_PAUSE))
1809 audio_play(offset);
1811 #endif
1812 return true;
1816 int wps_subline_index(struct wps_data *data, int line, int subline)
1818 return data->lines[line].first_subline_idx + subline;
1821 int wps_first_token_index(struct wps_data *data, int line, int subline)
1823 int first_subline_idx = data->lines[line].first_subline_idx;
1824 return data->sublines[first_subline_idx + subline].first_token_idx;
1827 int wps_last_token_index(struct wps_data *data, int line, int subline)
1829 int first_subline_idx = data->lines[line].first_subline_idx;
1830 int idx = first_subline_idx + subline;
1831 if (idx < data->num_sublines - 1)
1833 /* This subline ends where the next begins */
1834 return data->sublines[idx+1].first_token_idx - 1;
1836 else
1838 /* The last subline goes to the end */
1839 return data->num_tokens - 1;