get repeat/hold actions working on the touchscreen WPS. prepending an action with...
[kugel-rb.git] / apps / gui / wps_parser.c
blobbd599509ae71bcaaeb8a469c6c9aec696b55e0b4
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 "settings.h"
33 #include "sysfont.h"
34 #else
35 #include "checkwps.h"
36 #include "audio.h"
37 #define DEBUGF printf
38 #endif /*WPSEDITOR*/
39 #else
40 #include "debug.h"
41 #endif /*__PCTOOL__*/
43 #include <ctype.h>
44 #include <stdbool.h>
45 #include <string.h>
46 #include "font.h"
48 #include "gwps.h"
49 #ifndef __PCTOOL__
50 #include "settings.h"
51 #endif
52 #include "settings_list.h"
54 #ifdef HAVE_LCD_BITMAP
55 #include "bmp.h"
56 #endif
58 #include "backdrop.h"
60 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
61 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
63 #define WPS_ERROR_INVALID_PARAM -1
65 /* level of current conditional.
66 -1 means we're not in a conditional. */
67 static int level = -1;
69 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
70 or WPS_TOKEN_CONDITIONAL_START in current level */
71 static int lastcond[WPS_MAX_COND_LEVEL];
73 /* index of the WPS_TOKEN_CONDITIONAL in current level */
74 static int condindex[WPS_MAX_COND_LEVEL];
76 /* number of condtional options in current level */
77 static int numoptions[WPS_MAX_COND_LEVEL];
79 /* the current line in the file */
80 static int line;
82 #ifdef HAVE_LCD_BITMAP
84 #if LCD_DEPTH > 1
85 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
86 #else
87 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
88 #endif
90 #define PROGRESSBAR_BMP MAX_IMAGES
91 #define BACKDROP_BMP (MAX_BITMAPS-1)
93 /* pointers to the bitmap filenames in the WPS source */
94 static const char *bmp_names[MAX_BITMAPS];
96 #endif /* HAVE_LCD_BITMAP */
98 #if defined(DEBUG) || defined(SIMULATOR)
99 /* debugging function */
100 extern void print_debug_info(struct wps_data *data, int fail, int line);
101 #endif
103 static void wps_reset(struct wps_data *data);
105 /* Function for parsing of details for a token. At the moment the
106 function is called, the token type has already been set. The
107 function must fill in the details and possibly add more tokens
108 to the token array. It should return the number of chars that
109 has been consumed.
111 wps_bufptr points to the char following the tag (i.e. where
112 details begin).
113 token is the pointer to the 'main' token being parsed
115 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
116 struct wps_token *token, struct wps_data *wps_data);
118 struct wps_tag {
119 enum wps_token_type type;
120 const char name[3];
121 unsigned char refresh_type;
122 const wps_tag_parse_func parse_func;
124 static int skip_end_of_line(const char *wps_bufptr);
125 /* prototypes of all special parse functions : */
126 static int parse_timeout(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_progressbar(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_dir_level(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
132 static int parse_setting(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
135 #ifdef HAVE_LCD_BITMAP
136 static int parse_viewport_display(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_viewport(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_statusbar_enable(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_statusbar_disable(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_image_display(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 static int parse_image_load(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 #endif /*HAVE_LCD_BITMAP */
149 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
150 static int parse_image_special(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 #endif
153 #ifdef HAVE_ALBUMART
154 static int parse_albumart_load(const char *wps_bufptr,
155 struct wps_token *token, struct wps_data *wps_data);
156 static int parse_albumart_conditional(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 #endif /* HAVE_ALBUMART */
159 #ifdef HAVE_TOUCHSCREEN
160 static int parse_touchregion(const char *wps_bufptr,
161 struct wps_token *token, struct wps_data *wps_data);
162 #else
163 static int fulline_tag_not_supported(const char *wps_bufptr,
164 struct wps_token *token, struct wps_data *wps_data)
166 (void)token; (void)wps_data;
167 return skip_end_of_line(wps_bufptr);
169 #define parse_touchregion fulline_tag_not_supported
170 #endif
171 #ifdef CONFIG_RTC
172 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
173 #else
174 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
175 #endif
177 /* array of available tags - those with more characters have to go first
178 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
179 static const struct wps_tag all_tags[] = {
181 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
182 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
183 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
185 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
186 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
187 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
188 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
189 #if CONFIG_CHARGING >= CHARGING_MONITOR
190 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
191 #endif
192 #if CONFIG_CHARGING
193 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
194 #endif
196 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
197 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
216 /* current file */
217 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
227 parse_dir_level },
229 /* next file */
230 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
240 parse_dir_level },
242 /* current metadata */
243 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
256 /* next metadata */
257 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
270 #if (CONFIG_CODEC != MAS3507D)
271 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
272 #endif
274 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
275 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
276 #endif
278 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
280 #ifdef HAS_REMOTE_BUTTON_HOLD
281 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
282 #else
283 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
284 #endif
286 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
287 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
288 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
289 parse_timeout },
291 #ifdef HAVE_LCD_BITMAP
292 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
293 #else
294 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
295 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
296 #endif
297 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
298 parse_progressbar },
300 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
302 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
303 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
304 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
305 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
307 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
308 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
309 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
310 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
312 #ifdef HAVE_TAGCACHE
313 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
314 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
315 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
316 #endif
318 #if CONFIG_CODEC == SWCODEC
319 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
320 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
321 #endif
323 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
324 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
326 #ifdef HAVE_LCD_BITMAP
327 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
328 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
330 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
332 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
333 parse_image_display },
335 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
336 #ifdef HAVE_ALBUMART
337 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
338 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
339 parse_albumart_conditional },
340 #endif
342 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
343 parse_viewport_display },
344 { WPS_NO_TOKEN, "V", 0, parse_viewport },
346 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
347 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
348 #endif
349 #endif
351 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
353 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
354 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
356 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
357 /* the array MUST end with an empty string (first char is \0) */
360 /* Returns the number of chars that should be skipped to jump
361 immediately after the first eol, i.e. to the start of the next line */
362 static int skip_end_of_line(const char *wps_bufptr)
364 line++;
365 int skip = 0;
366 while(*(wps_bufptr + skip) != '\n')
367 skip++;
368 return ++skip;
371 /* Starts a new subline in the current line during parsing */
372 static void wps_start_new_subline(struct wps_data *data)
374 data->num_sublines++;
375 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
376 data->lines[data->num_lines].num_sublines++;
379 #ifdef HAVE_LCD_BITMAP
381 static int parse_statusbar_enable(const char *wps_bufptr,
382 struct wps_token *token,
383 struct wps_data *wps_data)
385 (void)token; /* Kill warnings */
386 wps_data->wps_sb_tag = true;
387 wps_data->show_sb_on_wps = true;
388 if (wps_data->viewports[0].vp.y == 0)
390 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
391 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
393 return skip_end_of_line(wps_bufptr);
396 static int parse_statusbar_disable(const char *wps_bufptr,
397 struct wps_token *token,
398 struct wps_data *wps_data)
400 (void)token; /* Kill warnings */
401 wps_data->wps_sb_tag = true;
402 wps_data->show_sb_on_wps = false;
403 if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
405 wps_data->viewports[0].vp.y = 0;
406 wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
408 return skip_end_of_line(wps_bufptr);
411 static bool load_bitmap(struct wps_data *wps_data,
412 char* filename,
413 struct bitmap *bm)
415 int format;
416 #ifdef HAVE_REMOTE_LCD
417 if (wps_data->remote_wps)
418 format = FORMAT_ANY|FORMAT_REMOTE;
419 else
420 #endif
421 format = FORMAT_ANY|FORMAT_TRANSPARENT;
423 int ret = read_bmp_file(filename, bm,
424 wps_data->img_buf_free,
425 format,NULL);
427 if (ret > 0)
429 #if LCD_DEPTH == 16
430 if (ret % 2) ret++;
431 /* Always consume an even number of bytes */
432 #endif
433 wps_data->img_buf_ptr += ret;
434 wps_data->img_buf_free -= ret;
436 return true;
438 else
439 return false;
442 static int get_image_id(int c)
444 if(c >= 'a' && c <= 'z')
445 return c - 'a';
446 else if(c >= 'A' && c <= 'Z')
447 return c - 'A' + 26;
448 else
449 return -1;
452 static char *get_image_filename(const char *start, const char* bmpdir,
453 char *buf, int buf_size)
455 const char *end = strchr(start, '|');
457 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
459 buf = "\0";
460 return NULL;
463 int bmpdirlen = strlen(bmpdir);
465 strcpy(buf, bmpdir);
466 buf[bmpdirlen] = '/';
467 memcpy( &buf[bmpdirlen + 1], start, end - start);
468 buf[bmpdirlen + 1 + end - start] = 0;
470 return buf;
473 static int parse_image_display(const char *wps_bufptr,
474 struct wps_token *token,
475 struct wps_data *wps_data)
477 (void)wps_data;
478 int n = get_image_id(wps_bufptr[0]);
479 int subimage;
481 if (n == -1)
483 /* invalid picture display tag */
484 return WPS_ERROR_INVALID_PARAM;
487 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
489 /* Sanity check */
490 if (subimage >= wps_data->img[n].num_subimages)
491 return WPS_ERROR_INVALID_PARAM;
493 /* Store sub-image number to display in high bits */
494 token->value.i = n | (subimage << 8);
495 return 2; /* We have consumed 2 bytes */
496 } else {
497 token->value.i = n;
498 return 1; /* We have consumed 1 byte */
502 static int parse_image_load(const char *wps_bufptr,
503 struct wps_token *token,
504 struct wps_data *wps_data)
506 int n;
507 const char *ptr = wps_bufptr;
508 const char *pos;
509 const char* filename;
510 const char* id;
511 const char *newline;
512 int x,y;
514 /* format: %x|n|filename.bmp|x|y|
515 or %xl|n|filename.bmp|x|y|
516 or %xl|n|filename.bmp|x|y|num_subimages|
519 if (*ptr != '|')
520 return WPS_ERROR_INVALID_PARAM;
522 ptr++;
524 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
525 return WPS_ERROR_INVALID_PARAM;
527 /* Check there is a terminating | */
528 if (*ptr != '|')
529 return WPS_ERROR_INVALID_PARAM;
531 /* get the image ID */
532 n = get_image_id(*id);
534 /* check the image number and load state */
535 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
537 /* Invalid image ID */
538 return WPS_ERROR_INVALID_PARAM;
541 /* save a pointer to the filename */
542 bmp_names[n] = filename;
544 wps_data->img[n].x = x;
545 wps_data->img[n].y = y;
547 /* save current viewport */
548 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
550 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
552 wps_data->img[n].always_display = true;
554 else
556 /* Parse the (optional) number of sub-images */
557 ptr++;
558 newline = strchr(ptr, '\n');
559 pos = strchr(ptr, '|');
560 if (pos && pos < newline)
561 wps_data->img[n].num_subimages = atoi(ptr);
563 if (wps_data->img[n].num_subimages <= 0)
564 return WPS_ERROR_INVALID_PARAM;
567 /* Skip the rest of the line */
568 return skip_end_of_line(wps_bufptr);
571 static int parse_viewport_display(const char *wps_bufptr,
572 struct wps_token *token,
573 struct wps_data *wps_data)
575 (void)wps_data;
576 char letter = wps_bufptr[0];
578 if (letter < 'a' || letter > 'z')
580 /* invalid viewport tag */
581 return WPS_ERROR_INVALID_PARAM;
583 token->value.i = letter;
584 return 1;
587 static int parse_viewport(const char *wps_bufptr,
588 struct wps_token *token,
589 struct wps_data *wps_data)
591 (void)token; /* Kill warnings */
592 const char *ptr = wps_bufptr;
593 struct viewport* vp;
594 int depth;
595 uint32_t set = 0;
596 enum {
597 PL_X = 0,
598 PL_Y,
599 PL_WIDTH,
600 PL_HEIGHT,
601 PL_FONT,
602 PL_FG,
603 PL_BG,
605 int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
606 #ifdef HAVE_REMOTE_LCD
607 if (wps_data->remote_wps)
609 lcd_width = LCD_REMOTE_WIDTH;
610 lcd_height = LCD_REMOTE_HEIGHT;
612 #endif
614 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
615 return WPS_ERROR_INVALID_PARAM;
617 wps_data->num_viewports++;
618 /* check for the optional letter to signify its a hideable viewport */
619 /* %Vl|<label>|<rest of tags>| */
620 wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
622 if (*ptr == 'l')
624 if (*(ptr+1) == '|')
626 char label = *(ptr+2);
627 if (label >= 'a' && label <= 'z')
629 wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
630 wps_data->viewports[wps_data->num_viewports].label = label;
632 else
633 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
634 ptr += 3;
637 if (*ptr != '|')
638 return WPS_ERROR_INVALID_PARAM;
640 ptr++;
641 vp = &wps_data->viewports[wps_data->num_viewports].vp;
642 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
644 /* Set the defaults for fields not user-specified */
645 vp->drawmode = DRMODE_SOLID;
647 /* Work out the depth of this display */
648 #ifdef HAVE_REMOTE_LCD
649 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
650 #else
651 depth = LCD_DEPTH;
652 #endif
654 #ifdef HAVE_LCD_COLOR
655 if (depth == 16)
657 if (!(ptr = parse_list("dddddcc", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
658 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
659 return WPS_ERROR_INVALID_PARAM;
661 else
662 #endif
663 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
664 if (depth == 2) {
665 /* Default to black on white */
666 vp->fg_pattern = 0;
667 vp->bg_pattern = 3;
668 if (!(ptr = parse_list("dddddgg", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
669 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
670 return WPS_ERROR_INVALID_PARAM;
672 else
673 #endif
674 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
675 if (depth == 1)
677 if (!(ptr = parse_list("ddddd", &set, '|', ptr, &vp->x, &vp->y,
678 &vp->width, &vp->height, &vp->font)))
679 return WPS_ERROR_INVALID_PARAM;
681 else
682 #endif
685 /* Check for trailing | */
686 if (*ptr != '|')
687 return WPS_ERROR_INVALID_PARAM;
689 if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
690 return WPS_ERROR_INVALID_PARAM;
692 /* fix defaults */
693 if (!LIST_VALUE_PARSED(set, PL_WIDTH))
694 vp->width = lcd_width - vp->x;
695 if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
696 vp->height = lcd_height - vp->y;
698 /* Default to using the user font if the font was an invalid number */
699 if (!LIST_VALUE_PARSED(set, PL_FONT) ||
700 ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
701 vp->font = FONT_UI;
703 /* Validate the viewport dimensions - we know that the numbers are
704 non-negative integers */
705 if ((vp->x >= lcd_width) ||
706 ((vp->x + vp->width) > lcd_width) ||
707 (vp->y >= lcd_height) ||
708 ((vp->y + vp->height) > lcd_height))
710 return WPS_ERROR_INVALID_PARAM;
713 #ifdef HAVE_LCD_COLOR
714 if (depth == 16)
716 if (!LIST_VALUE_PARSED(set, PL_FG))
717 vp->fg_pattern = global_settings.fg_color;
718 if (!LIST_VALUE_PARSED(set, PL_BG))
719 vp->bg_pattern = global_settings.bg_color;
721 #endif
723 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
725 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
727 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
729 wps_data->lines[wps_data->num_lines].first_subline_idx =
730 wps_data->num_sublines;
732 wps_data->sublines[wps_data->num_sublines].first_token_idx =
733 wps_data->num_tokens;
736 /* Skip the rest of the line */
737 return skip_end_of_line(wps_bufptr);
740 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
741 static int parse_image_special(const char *wps_bufptr,
742 struct wps_token *token,
743 struct wps_data *wps_data)
745 (void)wps_data; /* kill warning */
746 (void)token;
747 const char *pos = NULL;
748 const char *newline;
750 pos = strchr(wps_bufptr + 1, '|');
751 newline = strchr(wps_bufptr, '\n');
753 if (pos > newline)
754 return WPS_ERROR_INVALID_PARAM;
755 #if LCD_DEPTH > 1
756 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
758 /* format: %X|filename.bmp| */
759 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
761 #endif
763 /* Skip the rest of the line */
764 return skip_end_of_line(wps_bufptr);
766 #endif
768 #endif /* HAVE_LCD_BITMAP */
770 static int parse_setting(const char *wps_bufptr,
771 struct wps_token *token,
772 struct wps_data *wps_data)
774 (void)wps_data;
775 const char *ptr = wps_bufptr;
776 const char *end;
777 int i;
779 /* Find the setting's cfg_name */
780 if (*ptr != '|')
781 return WPS_ERROR_INVALID_PARAM;
782 ptr++;
783 end = strchr(ptr,'|');
784 if (!end)
785 return WPS_ERROR_INVALID_PARAM;
787 /* Find the setting */
788 for (i=0; i<nb_settings; i++)
789 if (settings[i].cfg_name &&
790 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
791 /* prevent matches on cfg_name prefixes */
792 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
793 break;
794 if (i == nb_settings)
795 return WPS_ERROR_INVALID_PARAM;
797 /* Store the setting number */
798 token->value.i = i;
800 /* Skip the rest of the line */
801 return end-ptr+2;
805 static int parse_dir_level(const char *wps_bufptr,
806 struct wps_token *token,
807 struct wps_data *wps_data)
809 char val[] = { *wps_bufptr, '\0' };
810 token->value.i = atoi(val);
811 (void)wps_data; /* Kill warnings */
812 return 1;
815 static int parse_timeout(const char *wps_bufptr,
816 struct wps_token *token,
817 struct wps_data *wps_data)
819 int skip = 0;
820 int val = 0;
821 bool have_point = false;
822 bool have_tenth = false;
824 (void)wps_data; /* Kill the warning */
826 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
828 if (*wps_bufptr != '.')
830 val *= 10;
831 val += *wps_bufptr - '0';
832 if (have_point)
834 have_tenth = true;
835 wps_bufptr++;
836 skip++;
837 break;
840 else
841 have_point = true;
843 wps_bufptr++;
844 skip++;
847 if (have_tenth == false)
848 val *= 10;
850 if (val == 0 && skip == 0)
852 /* decide what to do if no value was specified */
853 switch (token->type)
855 case WPS_TOKEN_SUBLINE_TIMEOUT:
856 return -1;
857 case WPS_TOKEN_BUTTON_VOLUME:
858 val = 10;
859 break;
862 token->value.i = val;
864 return skip;
867 static int parse_progressbar(const char *wps_bufptr,
868 struct wps_token *token,
869 struct wps_data *wps_data)
871 (void)token; /* Kill warnings */
872 /* %pb or %pb|filename|x|y|width|height|
873 using - for any of the params uses "sane" values */
874 #ifdef HAVE_LCD_BITMAP
875 enum {
876 PB_FILENAME = 0,
877 PB_X,
878 PB_Y,
879 PB_WIDTH,
880 PB_HEIGHT
882 const char *filename;
883 int x, y, height, width;
884 uint32_t set = 0;
885 const char *ptr = wps_bufptr;
886 struct progressbar *pb;
887 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
888 #ifndef __PCTOOL__
889 int font_height = font_get(vp->font)->height;
890 #else
891 int font_height = 8;
892 #endif
893 int line_num = wps_data->num_lines -
894 wps_data->viewports[wps_data->num_viewports].first_line;
896 if (wps_data->progressbar_count >= MAX_PROGRESSBARS)
897 return WPS_ERROR_INVALID_PARAM;
899 pb = &wps_data->progressbar[wps_data->progressbar_count];
900 pb->have_bitmap_pb = false;
902 if (*wps_bufptr != '|') /* regular old style */
904 pb->x = 0;
905 pb->width = vp->width;
906 pb->height = SYSFONT_HEIGHT-2;
907 pb->y = -line_num - 1; /* Will be computed during the rendering */
909 wps_data->viewports[wps_data->num_viewports].pb = pb;
910 wps_data->progressbar_count++;
911 return 0;
913 ptr = wps_bufptr + 1;
915 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
916 &x, &y, &width, &height)))
917 return WPS_ERROR_INVALID_PARAM;
919 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
920 bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
922 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
923 pb->x = x;
924 else
925 pb->x = vp->x;
927 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
929 /* A zero width causes a divide-by-zero error later, so reject it */
930 if (width == 0)
931 return WPS_ERROR_INVALID_PARAM;
933 pb->width = width;
935 else
936 pb->width = vp->width - pb->x;
938 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
940 /* A zero height makes no sense - reject it */
941 if (height == 0)
942 return WPS_ERROR_INVALID_PARAM;
944 pb->height = height;
946 else
947 pb->height = font_height;
949 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
950 pb->y = y;
951 else
952 pb->y = -line_num - 1; /* Will be computed during the rendering */
954 wps_data->viewports[wps_data->num_viewports].pb = pb;
955 wps_data->progressbar_count++;
957 /* Skip the rest of the line */
958 return skip_end_of_line(wps_bufptr)-1;
959 #else
961 if (*(wps_bufptr-1) == 'f')
962 wps_data->full_line_progressbar = true;
963 else
964 wps_data->full_line_progressbar = false;
966 return 0;
968 #endif
971 #ifdef HAVE_ALBUMART
972 static int parse_albumart_load(const char *wps_bufptr,
973 struct wps_token *token,
974 struct wps_data *wps_data)
976 const char *_pos, *newline;
977 bool parsing;
978 (void)token; /* silence warning */
980 /* reset albumart info in wps */
981 wps_data->albumart_max_width = -1;
982 wps_data->albumart_max_height = -1;
983 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
984 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
986 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
988 newline = strchr(wps_bufptr, '\n');
990 /* initial validation and parsing of x and y components */
991 if (*wps_bufptr != '|')
992 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
994 _pos = wps_bufptr + 1;
995 if (!isdigit(*_pos))
996 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
997 wps_data->albumart_x = atoi(_pos);
999 _pos = strchr(_pos, '|');
1000 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
1001 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
1003 wps_data->albumart_y = atoi(_pos);
1005 _pos = strchr(_pos, '|');
1006 if (!_pos || _pos > newline)
1007 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1008 e.g. %Cl|7|59\n */
1010 /* parsing width field */
1011 parsing = true;
1012 while (parsing)
1014 /* apply each modifier in turn */
1015 ++_pos;
1016 switch (*_pos)
1018 case 'l':
1019 case 'L':
1020 case '+':
1021 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
1022 break;
1023 case 'c':
1024 case 'C':
1025 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
1026 break;
1027 case 'r':
1028 case 'R':
1029 case '-':
1030 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
1031 break;
1032 case 'd':
1033 case 'D':
1034 case 'i':
1035 case 'I':
1036 case 's':
1037 case 'S':
1038 /* simply ignored */
1039 break;
1040 default:
1041 parsing = false;
1042 break;
1045 /* extract max width data */
1046 if (*_pos != '|')
1048 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1049 return WPS_ERROR_INVALID_PARAM;
1051 wps_data->albumart_max_width = atoi(_pos);
1053 _pos = strchr(_pos, '|');
1054 if (!_pos || _pos > newline)
1055 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1056 e.g. %Cl|7|59|200\n */
1059 /* parsing height field */
1060 parsing = true;
1061 while (parsing)
1063 /* apply each modifier in turn */
1064 ++_pos;
1065 switch (*_pos)
1067 case 't':
1068 case 'T':
1069 case '-':
1070 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1071 break;
1072 case 'c':
1073 case 'C':
1074 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1075 break;
1076 case 'b':
1077 case 'B':
1078 case '+':
1079 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1080 break;
1081 case 'd':
1082 case 'D':
1083 case 'i':
1084 case 'I':
1085 case 's':
1086 case 'S':
1087 /* simply ignored */
1088 break;
1089 default:
1090 parsing = false;
1091 break;
1094 /* extract max height data */
1095 if (*_pos != '|')
1097 if (!isdigit(*_pos))
1098 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1100 wps_data->albumart_max_height = atoi(_pos);
1102 _pos = strchr(_pos, '|');
1103 if (!_pos || _pos > newline)
1104 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1105 e.g. %Cl|7|59|200|200\n */
1108 /* if we got here, we parsed everything ok .. ! */
1109 if (wps_data->albumart_max_width < 0)
1110 wps_data->albumart_max_width = 0;
1111 else if (wps_data->albumart_max_width > LCD_WIDTH)
1112 wps_data->albumart_max_width = LCD_WIDTH;
1114 if (wps_data->albumart_max_height < 0)
1115 wps_data->albumart_max_height = 0;
1116 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1117 wps_data->albumart_max_height = LCD_HEIGHT;
1119 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1121 /* Skip the rest of the line */
1122 return skip_end_of_line(wps_bufptr);
1125 static int parse_albumart_conditional(const char *wps_bufptr,
1126 struct wps_token *token,
1127 struct wps_data *wps_data)
1129 struct wps_token *prevtoken = token;
1130 --prevtoken;
1131 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1133 /* This %C is part of a %?C construct.
1134 It's either %?C<blah> or %?Cn<blah> */
1135 token->type = WPS_TOKEN_ALBUMART_FOUND;
1136 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1138 token->next = true;
1139 return 1;
1141 else if (*wps_bufptr == '<')
1143 return 0;
1145 else
1147 token->type = WPS_NO_TOKEN;
1148 return 0;
1151 else
1153 /* This %C tag is in a conditional construct. */
1154 wps_data->albumart_cond_index = condindex[level];
1155 return 0;
1158 #endif /* HAVE_ALBUMART */
1160 #ifdef HAVE_TOUCHSCREEN
1162 struct touchaction {char* s; int action;};
1163 static struct touchaction touchactions[] = {
1164 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1165 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1166 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1167 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1168 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1169 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1171 static int parse_touchregion(const char *wps_bufptr,
1172 struct wps_token *token, struct wps_data *wps_data)
1174 (void)token;
1175 unsigned i;
1176 struct touchregion *region;
1177 const char *ptr = wps_bufptr;
1178 const char *action;
1179 int x,y,w,h;
1181 /* format: %T|x|y|width|height|action|
1182 * if action starts with & the area must be held to happen
1183 * action is one of:
1184 * play - play/pause playback
1185 * stop - stop playback, exit the wps
1186 * prev - prev track
1187 * next - next track
1188 * ffwd - seek forward
1189 * rwd - seek backwards
1190 * menu - go back to the main menu
1191 * browse - go back to the file/db browser
1192 * shuffle - toggle shuffle mode
1193 * repmode - cycle the repeat mode
1194 * quickscreen - go into the quickscreen
1195 * contextmenu - open the context menu
1198 if ((wps_data->touchregion_count +1 >= MAX_TOUCHREGIONS) || (*ptr != '|'))
1199 return WPS_ERROR_INVALID_PARAM;
1200 ptr++;
1202 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1203 return WPS_ERROR_INVALID_PARAM;
1205 /* Check there is a terminating | */
1206 if (*ptr != '|')
1207 return WPS_ERROR_INVALID_PARAM;
1209 /* should probably do some bounds checking here with the viewport... but later */
1210 region = &wps_data->touchregion[wps_data->touchregion_count];
1211 region->action = ACTION_NONE;
1212 region->x = x;
1213 region->y = y;
1214 region->width = w;
1215 region->height = h;
1216 region->wvp = &wps_data->viewports[wps_data->num_viewports];
1217 i = 0;
1218 if (*action == '&')
1220 action++;
1221 region->repeat = true;
1223 else
1224 region->repeat = false;
1226 while ((region->action == ACTION_NONE) &&
1227 (i < sizeof(touchactions)/sizeof(*touchactions)))
1229 if (!strncmp(touchactions[i].s, action, strlen(touchactions[i].s)))
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 strncpy(stringbuf, string_start, len);
1497 *(stringbuf + len) = '\0';
1499 data->strings[data->num_strings] = stringbuf;
1500 stringbuf += len + 1;
1501 stringbuf_used += len + 1;
1502 data->tokens[data->num_tokens].value.i =
1503 data->num_strings;
1504 data->num_strings++;
1506 else
1508 /* another occurrence of an existing string */
1509 data->tokens[data->num_tokens].value.i = i;
1511 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1512 data->num_tokens++;
1514 break;
1518 if (!fail && level >= 0) /* there are unclosed conditionals */
1519 fail = PARSE_FAIL_UNCLOSED_COND;
1521 if (*wps_bufptr && !fail)
1522 /* one of the limits of the while loop was exceeded */
1523 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1525 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1527 /* We have finished with the last viewport, so increment count */
1528 data->num_viewports++;
1530 #if defined(DEBUG) || defined(SIMULATOR)
1531 print_debug_info(data, fail, line);
1532 #endif
1534 return (fail == 0);
1537 #ifdef HAVE_LCD_BITMAP
1538 /* Clear the WPS image cache */
1539 static void wps_images_clear(struct wps_data *data)
1541 int i;
1542 /* set images to unloaded and not displayed */
1543 for (i = 0; i < MAX_IMAGES; i++)
1545 data->img[i].loaded = false;
1546 data->img[i].display = -1;
1547 data->img[i].always_display = false;
1548 data->img[i].num_subimages = 1;
1551 #endif
1553 /* initial setup of wps_data */
1554 void wps_data_init(struct wps_data *wps_data)
1556 #ifdef HAVE_LCD_BITMAP
1557 wps_images_clear(wps_data);
1558 wps_data->wps_sb_tag = false;
1559 wps_data->show_sb_on_wps = false;
1560 wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
1561 wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
1562 wps_data->peak_meter_enabled = false;
1563 /* progress bars */
1564 wps_data->progressbar_count = 0;
1565 #else /* HAVE_LCD_CHARCELLS */
1566 int i;
1567 for (i = 0; i < 8; i++)
1569 wps_data->wps_progress_pat[i] = 0;
1571 wps_data->full_line_progressbar = false;
1572 #endif
1573 wps_data->button_time_volume = 0;
1574 wps_data->wps_loaded = false;
1577 static void wps_reset(struct wps_data *data)
1579 #ifdef HAVE_REMOTE_LCD
1580 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1581 #endif
1582 memset(data, 0, sizeof(*data));
1583 wps_data_init(data);
1584 #ifdef HAVE_REMOTE_LCD
1585 data->remote_wps = rwps;
1586 #endif
1589 #ifdef HAVE_LCD_BITMAP
1591 static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
1593 char img_path[MAX_PATH];
1594 struct bitmap *bitmap;
1595 bool *loaded;
1596 int n;
1597 for (n = 0; n < BACKDROP_BMP; n++)
1599 if (bmp_names[n])
1601 get_image_filename(bmp_names[n], bmpdir,
1602 img_path, sizeof(img_path));
1604 if (n >= PROGRESSBAR_BMP ) {
1605 /* progressbar bitmap */
1606 bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
1607 loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
1608 } else {
1609 /* regular bitmap */
1610 bitmap = &wps_data->img[n].bm;
1611 loaded = &wps_data->img[n].loaded;
1614 /* load the image */
1615 bitmap->data = wps_data->img_buf_ptr;
1616 if (load_bitmap(wps_data, img_path, bitmap))
1618 *loaded = true;
1620 /* Calculate and store height if this image has sub-images */
1621 if (n < MAX_IMAGES)
1622 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1623 wps_data->img[n].num_subimages;
1625 else
1627 /* Abort if we can't load an image */
1628 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1629 return false;
1634 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1635 if (bmp_names[BACKDROP_BMP])
1637 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1638 img_path, sizeof(img_path));
1640 #if defined(HAVE_REMOTE_LCD)
1641 /* We only need to check LCD type if there is a remote LCD */
1642 if (!wps_data->remote_wps)
1643 #endif
1645 /* Load backdrop for the main LCD */
1646 if (!load_wps_backdrop(img_path))
1647 return false;
1649 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1650 else
1652 /* Load backdrop for the remote LCD */
1653 if (!load_remote_wps_backdrop(img_path))
1654 return false;
1656 #endif
1658 #endif /* has backdrop support */
1660 /* If we got here, everything was OK */
1661 return true;
1664 #endif /* HAVE_LCD_BITMAP */
1666 /* to setup up the wps-data from a format-buffer (isfile = false)
1667 from a (wps-)file (isfile = true)*/
1668 bool wps_data_load(struct wps_data *wps_data,
1669 struct screen *display,
1670 const char *buf,
1671 bool isfile)
1673 #ifdef HAVE_ALBUMART
1674 struct mp3entry *curtrack;
1675 long offset;
1676 int status;
1677 int wps_uses_albumart = wps_data->wps_uses_albumart;
1678 int albumart_max_height = wps_data->albumart_max_height;
1679 int albumart_max_width = wps_data->albumart_max_width;
1680 #endif
1681 if (!wps_data || !buf)
1682 return false;
1684 wps_reset(wps_data);
1686 /* Initialise the first (default) viewport */
1687 wps_data->viewports[0].vp.x = 0;
1688 wps_data->viewports[0].vp.width = display->getwidth();
1689 if (!global_settings.statusbar)
1691 wps_data->viewports[0].vp.y = 0;
1692 wps_data->viewports[0].vp.height = display->getheight();
1694 else
1696 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1697 wps_data->viewports[0].vp.height = display->getheight() -
1698 STATUSBAR_HEIGHT;
1700 #ifdef HAVE_LCD_BITMAP
1701 wps_data->viewports[0].vp.font = FONT_UI;
1702 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1703 #endif
1704 #if LCD_DEPTH > 1
1705 if (display->depth > 1)
1707 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1708 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1710 #endif
1711 if (!isfile)
1713 return wps_parse(wps_data, buf);
1715 else
1718 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1719 * wants to be a virtual file. Feel free to modify dirbrowse()
1720 * if you're feeling brave.
1722 #ifndef __PCTOOL__
1723 if (! strcmp(buf, WPS_DEFAULTCFG) )
1725 global_settings.wps_file[0] = 0;
1726 return false;
1729 #ifdef HAVE_REMOTE_LCD
1730 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1732 global_settings.rwps_file[0] = 0;
1733 return false;
1735 #endif
1736 #endif /* __PCTOOL__ */
1738 int fd = open_utf8(buf, O_RDONLY);
1740 if (fd < 0)
1741 return false;
1743 /* get buffer space from the plugin buffer */
1744 size_t buffersize = 0;
1745 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1747 if (!wps_buffer)
1748 return false;
1750 /* copy the file's content to the buffer for parsing,
1751 ensuring that every line ends with a newline char. */
1752 unsigned int start = 0;
1753 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1755 start += strlen(wps_buffer + start);
1756 if (start < buffersize - 1)
1758 wps_buffer[start++] = '\n';
1759 wps_buffer[start] = 0;
1763 close(fd);
1765 if (start <= 0)
1766 return false;
1768 #ifdef HAVE_LCD_BITMAP
1769 /* Set all filename pointers to NULL */
1770 memset(bmp_names, 0, sizeof(bmp_names));
1771 #endif
1773 /* parse the WPS source */
1774 if (!wps_parse(wps_data, wps_buffer)) {
1775 wps_reset(wps_data);
1776 return false;
1779 wps_data->wps_loaded = true;
1781 #ifdef HAVE_LCD_BITMAP
1782 /* get the bitmap dir */
1783 char bmpdir[MAX_PATH];
1784 size_t bmpdirlen;
1785 char *dot = strrchr(buf, '.');
1786 bmpdirlen = dot - buf;
1787 strncpy(bmpdir, buf, dot - buf);
1788 bmpdir[bmpdirlen] = 0;
1790 /* load the bitmaps that were found by the parsing */
1791 if (!load_wps_bitmaps(wps_data, bmpdir)) {
1792 wps_reset(wps_data);
1793 return false;
1795 #endif
1796 #ifdef HAVE_ALBUMART
1797 status = audio_status();
1798 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1799 (wps_data->wps_uses_albumart &&
1800 (albumart_max_height != wps_data->albumart_max_height ||
1801 albumart_max_width != wps_data->albumart_max_width))) &&
1802 status & AUDIO_STATUS_PLAY)
1804 curtrack = audio_current_track();
1805 offset = curtrack->offset;
1806 audio_stop();
1807 if (!(status & AUDIO_STATUS_PAUSE))
1808 audio_play(offset);
1810 #endif
1811 return true;
1815 int wps_subline_index(struct wps_data *data, int line, int subline)
1817 return data->lines[line].first_subline_idx + subline;
1820 int wps_first_token_index(struct wps_data *data, int line, int subline)
1822 int first_subline_idx = data->lines[line].first_subline_idx;
1823 return data->sublines[first_subline_idx + subline].first_token_idx;
1826 int wps_last_token_index(struct wps_data *data, int line, int subline)
1828 int first_subline_idx = data->lines[line].first_subline_idx;
1829 int idx = first_subline_idx + subline;
1830 if (idx < data->num_sublines - 1)
1832 /* This subline ends where the next begins */
1833 return data->sublines[idx+1].first_token_idx - 1;
1835 else
1837 /* The last subline goes to the end */
1838 return data->num_tokens - 1;