Factor out WPS' %V parsing function into viewport.c, in preperation of customlist...
[kugel-rb/myfork.git] / apps / gui / skin_engine / wps_parser.c
blob3df2c5f0aa1433e997d76eb3cae0210e2d833030
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"
28 #include "viewport.h"
30 #ifdef __PCTOOL__
31 #ifdef WPSEDITOR
32 #include "proxy.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 "font.h"
47 #include "wps_internals.h"
48 #include "skin_engine.h"
49 #include "settings.h"
50 #include "settings_list.h"
52 #ifdef HAVE_LCD_BITMAP
53 #include "bmp.h"
54 #endif
56 #include "backdrop.h"
58 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
59 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
61 #define WPS_ERROR_INVALID_PARAM -1
63 /* level of current conditional.
64 -1 means we're not in a conditional. */
65 static int level = -1;
67 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
68 or WPS_TOKEN_CONDITIONAL_START in current level */
69 static int lastcond[WPS_MAX_COND_LEVEL];
71 /* index of the WPS_TOKEN_CONDITIONAL in current level */
72 static int condindex[WPS_MAX_COND_LEVEL];
74 /* number of condtional options in current level */
75 static int numoptions[WPS_MAX_COND_LEVEL];
77 /* the current line in the file */
78 static int line;
80 #ifdef HAVE_LCD_BITMAP
82 #if LCD_DEPTH > 1
83 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
84 #else
85 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
86 #endif
88 #define PROGRESSBAR_BMP MAX_IMAGES
89 #define BACKDROP_BMP (MAX_BITMAPS-1)
91 /* pointers to the bitmap filenames in the WPS source */
92 static const char *bmp_names[MAX_BITMAPS];
94 #endif /* HAVE_LCD_BITMAP */
96 #if defined(DEBUG) || defined(SIMULATOR)
97 /* debugging function */
98 extern void print_debug_info(struct wps_data *data, int fail, int line);
99 #endif
101 static void wps_reset(struct wps_data *data);
103 /* Function for parsing of details for a token. At the moment the
104 function is called, the token type has already been set. The
105 function must fill in the details and possibly add more tokens
106 to the token array. It should return the number of chars that
107 has been consumed.
109 wps_bufptr points to the char following the tag (i.e. where
110 details begin).
111 token is the pointer to the 'main' token being parsed
113 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
114 struct wps_token *token, struct wps_data *wps_data);
116 struct wps_tag {
117 enum wps_token_type type;
118 const char name[3];
119 unsigned char refresh_type;
120 const wps_tag_parse_func parse_func;
122 static int skip_end_of_line(const char *wps_bufptr);
123 /* prototypes of all special parse functions : */
124 static int parse_timeout(const char *wps_bufptr,
125 struct wps_token *token, struct wps_data *wps_data);
126 static int parse_progressbar(const char *wps_bufptr,
127 struct wps_token *token, struct wps_data *wps_data);
128 static int parse_dir_level(const char *wps_bufptr,
129 struct wps_token *token, struct wps_data *wps_data);
130 static int parse_setting(const char *wps_bufptr,
131 struct wps_token *token, struct wps_data *wps_data);
133 #ifdef HAVE_LCD_BITMAP
134 static int parse_viewport_display(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_viewport(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_statusbar_enable(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
140 static int parse_statusbar_disable(const char *wps_bufptr,
141 struct wps_token *token, struct wps_data *wps_data);
142 static int parse_image_display(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_image_load(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 #endif /*HAVE_LCD_BITMAP */
147 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
148 static int parse_image_special(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 #endif
151 #ifdef HAVE_ALBUMART
152 static int parse_albumart_load(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 static int parse_albumart_conditional(const char *wps_bufptr,
155 struct wps_token *token, struct wps_data *wps_data);
156 #endif /* HAVE_ALBUMART */
157 #ifdef HAVE_TOUCHSCREEN
158 static int parse_touchregion(const char *wps_bufptr,
159 struct wps_token *token, struct wps_data *wps_data);
160 #else
161 static int fulline_tag_not_supported(const char *wps_bufptr,
162 struct wps_token *token, struct wps_data *wps_data)
164 (void)token; (void)wps_data;
165 return skip_end_of_line(wps_bufptr);
167 #define parse_touchregion fulline_tag_not_supported
168 #endif
169 #ifdef CONFIG_RTC
170 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
171 #else
172 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
173 #endif
175 /* array of available tags - those with more characters have to go first
176 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
177 static const struct wps_tag all_tags[] = {
179 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
180 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
181 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
183 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
184 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
185 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
186 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
187 #if CONFIG_CHARGING >= CHARGING_MONITOR
188 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
189 #endif
190 #if CONFIG_CHARGING
191 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
192 #endif
194 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
195 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
197 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
214 /* current file */
215 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
216 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
217 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
225 parse_dir_level },
227 /* next file */
228 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
238 parse_dir_level },
240 /* current metadata */
241 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
254 /* next metadata */
255 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
268 #if (CONFIG_CODEC != MAS3507D)
269 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
270 #endif
272 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
273 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
274 #endif
276 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
278 #ifdef HAS_REMOTE_BUTTON_HOLD
279 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
280 #else
281 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
282 #endif
284 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
285 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
286 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
287 parse_timeout },
289 #ifdef HAVE_LCD_BITMAP
290 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
291 #else
292 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
293 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
294 #endif
295 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
296 parse_progressbar },
298 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
300 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
301 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
302 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
303 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
305 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
306 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
307 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
308 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
310 #ifdef HAVE_TAGCACHE
311 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
312 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
313 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
314 #endif
316 #if CONFIG_CODEC == SWCODEC
317 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
318 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
319 #endif
321 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
322 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
324 #ifdef HAVE_LCD_BITMAP
325 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
326 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
328 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
330 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
331 parse_image_display },
333 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
334 #ifdef HAVE_ALBUMART
335 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
336 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
337 parse_albumart_conditional },
338 #endif
340 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
341 parse_viewport_display },
342 { WPS_NO_TOKEN, "V", 0, parse_viewport },
344 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
345 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
346 #endif
347 #endif
349 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
351 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
352 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
354 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
355 /* the array MUST end with an empty string (first char is \0) */
358 /* Returns the number of chars that should be skipped to jump
359 immediately after the first eol, i.e. to the start of the next line */
360 static int skip_end_of_line(const char *wps_bufptr)
362 line++;
363 int skip = 0;
364 while(*(wps_bufptr + skip) != '\n')
365 skip++;
366 return ++skip;
369 /* Starts a new subline in the current line during parsing */
370 static void wps_start_new_subline(struct wps_data *data)
372 data->num_sublines++;
373 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
374 data->lines[data->num_lines].num_sublines++;
377 #ifdef HAVE_LCD_BITMAP
379 static int parse_statusbar_enable(const char *wps_bufptr,
380 struct wps_token *token,
381 struct wps_data *wps_data)
383 (void)token; /* Kill warnings */
384 wps_data->wps_sb_tag = true;
385 wps_data->show_sb_on_wps = true;
386 if (wps_data->viewports[0].vp.y == 0)
388 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
389 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
391 return skip_end_of_line(wps_bufptr);
394 static int parse_statusbar_disable(const char *wps_bufptr,
395 struct wps_token *token,
396 struct wps_data *wps_data)
398 (void)token; /* Kill warnings */
399 wps_data->wps_sb_tag = true;
400 wps_data->show_sb_on_wps = false;
401 if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
403 wps_data->viewports[0].vp.y = 0;
404 wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
406 return skip_end_of_line(wps_bufptr);
409 static bool load_bitmap(struct wps_data *wps_data,
410 char* filename,
411 struct bitmap *bm)
413 int format;
414 #ifdef HAVE_REMOTE_LCD
415 if (wps_data->remote_wps)
416 format = FORMAT_ANY|FORMAT_REMOTE;
417 else
418 #endif
419 format = FORMAT_ANY|FORMAT_TRANSPARENT;
421 int ret = read_bmp_file(filename, bm,
422 wps_data->img_buf_free,
423 format,NULL);
425 if (ret > 0)
427 #if LCD_DEPTH == 16
428 if (ret % 2) ret++;
429 /* Always consume an even number of bytes */
430 #endif
431 wps_data->img_buf_ptr += ret;
432 wps_data->img_buf_free -= ret;
434 return true;
436 else
437 return false;
440 static int get_image_id(int c)
442 if(c >= 'a' && c <= 'z')
443 return c - 'a';
444 else if(c >= 'A' && c <= 'Z')
445 return c - 'A' + 26;
446 else
447 return -1;
450 static char *get_image_filename(const char *start, const char* bmpdir,
451 char *buf, int buf_size)
453 const char *end = strchr(start, '|');
455 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
457 buf = "\0";
458 return NULL;
461 int bmpdirlen = strlen(bmpdir);
463 strcpy(buf, bmpdir);
464 buf[bmpdirlen] = '/';
465 memcpy( &buf[bmpdirlen + 1], start, end - start);
466 buf[bmpdirlen + 1 + end - start] = 0;
468 return buf;
471 static int parse_image_display(const char *wps_bufptr,
472 struct wps_token *token,
473 struct wps_data *wps_data)
475 (void)wps_data;
476 int n = get_image_id(wps_bufptr[0]);
477 int subimage;
479 if (n == -1)
481 /* invalid picture display tag */
482 return WPS_ERROR_INVALID_PARAM;
485 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
487 /* Sanity check */
488 if (subimage >= wps_data->img[n].num_subimages)
489 return WPS_ERROR_INVALID_PARAM;
491 /* Store sub-image number to display in high bits */
492 token->value.i = n | (subimage << 8);
493 return 2; /* We have consumed 2 bytes */
494 } else {
495 token->value.i = n;
496 return 1; /* We have consumed 1 byte */
500 static int parse_image_load(const char *wps_bufptr,
501 struct wps_token *token,
502 struct wps_data *wps_data)
504 int n;
505 const char *ptr = wps_bufptr;
506 const char *pos;
507 const char* filename;
508 const char* id;
509 const char *newline;
510 int x,y;
512 /* format: %x|n|filename.bmp|x|y|
513 or %xl|n|filename.bmp|x|y|
514 or %xl|n|filename.bmp|x|y|num_subimages|
517 if (*ptr != '|')
518 return WPS_ERROR_INVALID_PARAM;
520 ptr++;
522 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
523 return WPS_ERROR_INVALID_PARAM;
525 /* Check there is a terminating | */
526 if (*ptr != '|')
527 return WPS_ERROR_INVALID_PARAM;
529 /* get the image ID */
530 n = get_image_id(*id);
532 /* check the image number and load state */
533 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
535 /* Invalid image ID */
536 return WPS_ERROR_INVALID_PARAM;
539 /* save a pointer to the filename */
540 bmp_names[n] = filename;
542 wps_data->img[n].x = x;
543 wps_data->img[n].y = y;
545 /* save current viewport */
546 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
548 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
550 wps_data->img[n].always_display = true;
552 else
554 /* Parse the (optional) number of sub-images */
555 ptr++;
556 newline = strchr(ptr, '\n');
557 pos = strchr(ptr, '|');
558 if (pos && pos < newline)
559 wps_data->img[n].num_subimages = atoi(ptr);
561 if (wps_data->img[n].num_subimages <= 0)
562 return WPS_ERROR_INVALID_PARAM;
565 /* Skip the rest of the line */
566 return skip_end_of_line(wps_bufptr);
569 static int parse_viewport_display(const char *wps_bufptr,
570 struct wps_token *token,
571 struct wps_data *wps_data)
573 (void)wps_data;
574 char letter = wps_bufptr[0];
576 if (letter < 'a' || letter > 'z')
578 /* invalid viewport tag */
579 return WPS_ERROR_INVALID_PARAM;
581 token->value.i = letter;
582 return 1;
585 static int parse_viewport(const char *wps_bufptr,
586 struct wps_token *token,
587 struct wps_data *wps_data)
589 (void)token; /* Kill warnings */
590 const char *ptr = wps_bufptr;
592 const int screen =
593 #ifdef HAVE_REMOTE_LCD
594 wps_data->remote_wps ? SCREEN_REMOTE :
595 #endif
596 SCREEN_MAIN;
598 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
599 return WPS_ERROR_INVALID_PARAM;
601 wps_data->num_viewports++;
602 /* check for the optional letter to signify its a hideable viewport */
603 /* %Vl|<label>|<rest of tags>| */
604 wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
606 if (*ptr == 'l')
608 if (*(ptr+1) == '|')
610 char label = *(ptr+2);
611 if (label >= 'a' && label <= 'z')
613 wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
614 wps_data->viewports[wps_data->num_viewports].label = label;
616 else
617 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
618 ptr += 3;
621 if (*ptr != '|')
622 return WPS_ERROR_INVALID_PARAM;
624 ptr++;
625 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
626 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
628 /* Set the defaults for fields not user-specified */
629 vp->drawmode = DRMODE_SOLID;
631 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
632 return WPS_ERROR_INVALID_PARAM;
634 /* Check for trailing | */
635 if (*ptr != '|')
636 return WPS_ERROR_INVALID_PARAM;
639 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
641 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
643 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
645 wps_data->lines[wps_data->num_lines].first_subline_idx =
646 wps_data->num_sublines;
648 wps_data->sublines[wps_data->num_sublines].first_token_idx =
649 wps_data->num_tokens;
652 /* Skip the rest of the line */
653 return skip_end_of_line(wps_bufptr);
656 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
657 static int parse_image_special(const char *wps_bufptr,
658 struct wps_token *token,
659 struct wps_data *wps_data)
661 (void)wps_data; /* kill warning */
662 (void)token;
663 const char *pos = NULL;
664 const char *newline;
666 pos = strchr(wps_bufptr + 1, '|');
667 newline = strchr(wps_bufptr, '\n');
669 if (pos > newline)
670 return WPS_ERROR_INVALID_PARAM;
671 #if LCD_DEPTH > 1
672 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
674 /* format: %X|filename.bmp| */
675 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
677 #endif
679 /* Skip the rest of the line */
680 return skip_end_of_line(wps_bufptr);
682 #endif
684 #endif /* HAVE_LCD_BITMAP */
686 static int parse_setting(const char *wps_bufptr,
687 struct wps_token *token,
688 struct wps_data *wps_data)
690 (void)wps_data;
691 const char *ptr = wps_bufptr;
692 const char *end;
693 int i;
695 /* Find the setting's cfg_name */
696 if (*ptr != '|')
697 return WPS_ERROR_INVALID_PARAM;
698 ptr++;
699 end = strchr(ptr,'|');
700 if (!end)
701 return WPS_ERROR_INVALID_PARAM;
703 /* Find the setting */
704 for (i=0; i<nb_settings; i++)
705 if (settings[i].cfg_name &&
706 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
707 /* prevent matches on cfg_name prefixes */
708 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
709 break;
710 if (i == nb_settings)
711 return WPS_ERROR_INVALID_PARAM;
713 /* Store the setting number */
714 token->value.i = i;
716 /* Skip the rest of the line */
717 return end-ptr+2;
721 static int parse_dir_level(const char *wps_bufptr,
722 struct wps_token *token,
723 struct wps_data *wps_data)
725 char val[] = { *wps_bufptr, '\0' };
726 token->value.i = atoi(val);
727 (void)wps_data; /* Kill warnings */
728 return 1;
731 static int parse_timeout(const char *wps_bufptr,
732 struct wps_token *token,
733 struct wps_data *wps_data)
735 int skip = 0;
736 int val = 0;
737 bool have_point = false;
738 bool have_tenth = false;
740 (void)wps_data; /* Kill the warning */
742 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
744 if (*wps_bufptr != '.')
746 val *= 10;
747 val += *wps_bufptr - '0';
748 if (have_point)
750 have_tenth = true;
751 wps_bufptr++;
752 skip++;
753 break;
756 else
757 have_point = true;
759 wps_bufptr++;
760 skip++;
763 if (have_tenth == false)
764 val *= 10;
766 if (val == 0 && skip == 0)
768 /* decide what to do if no value was specified */
769 switch (token->type)
771 case WPS_TOKEN_SUBLINE_TIMEOUT:
772 return -1;
773 case WPS_TOKEN_BUTTON_VOLUME:
774 val = 10;
775 break;
778 token->value.i = val;
780 return skip;
783 static int parse_progressbar(const char *wps_bufptr,
784 struct wps_token *token,
785 struct wps_data *wps_data)
787 (void)token; /* Kill warnings */
788 /* %pb or %pb|filename|x|y|width|height|
789 using - for any of the params uses "sane" values */
790 #ifdef HAVE_LCD_BITMAP
791 enum {
792 PB_FILENAME = 0,
793 PB_X,
794 PB_Y,
795 PB_WIDTH,
796 PB_HEIGHT
798 const char *filename;
799 int x, y, height, width;
800 uint32_t set = 0;
801 const char *ptr = wps_bufptr;
802 struct progressbar *pb;
803 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
804 #ifndef __PCTOOL__
805 int font_height = font_get(vp->font)->height;
806 #else
807 int font_height = 8;
808 #endif
809 int line_num = wps_data->num_lines -
810 wps_data->viewports[wps_data->num_viewports].first_line;
812 if (wps_data->progressbar_count >= MAX_PROGRESSBARS)
813 return WPS_ERROR_INVALID_PARAM;
815 pb = &wps_data->progressbar[wps_data->progressbar_count];
816 pb->have_bitmap_pb = false;
818 if (*wps_bufptr != '|') /* regular old style */
820 pb->x = 0;
821 pb->width = vp->width;
822 pb->height = SYSFONT_HEIGHT-2;
823 pb->y = -line_num - 1; /* Will be computed during the rendering */
825 wps_data->viewports[wps_data->num_viewports].pb = pb;
826 wps_data->progressbar_count++;
827 return 0;
829 ptr = wps_bufptr + 1;
831 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
832 &x, &y, &width, &height)))
833 return WPS_ERROR_INVALID_PARAM;
835 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
836 bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
838 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
839 pb->x = x;
840 else
841 pb->x = vp->x;
843 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
845 /* A zero width causes a divide-by-zero error later, so reject it */
846 if (width == 0)
847 return WPS_ERROR_INVALID_PARAM;
849 pb->width = width;
851 else
852 pb->width = vp->width - pb->x;
854 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
856 /* A zero height makes no sense - reject it */
857 if (height == 0)
858 return WPS_ERROR_INVALID_PARAM;
860 pb->height = height;
862 else
863 pb->height = font_height;
865 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
866 pb->y = y;
867 else
868 pb->y = -line_num - 1; /* Will be computed during the rendering */
870 wps_data->viewports[wps_data->num_viewports].pb = pb;
871 wps_data->progressbar_count++;
873 /* Skip the rest of the line */
874 return skip_end_of_line(wps_bufptr)-1;
875 #else
877 if (*(wps_bufptr-1) == 'f')
878 wps_data->full_line_progressbar = true;
879 else
880 wps_data->full_line_progressbar = false;
882 return 0;
884 #endif
887 #ifdef HAVE_ALBUMART
888 static int parse_albumart_load(const char *wps_bufptr,
889 struct wps_token *token,
890 struct wps_data *wps_data)
892 const char *_pos, *newline;
893 bool parsing;
894 (void)token; /* silence warning */
896 /* reset albumart info in wps */
897 wps_data->albumart_max_width = -1;
898 wps_data->albumart_max_height = -1;
899 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
900 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
902 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
904 newline = strchr(wps_bufptr, '\n');
906 /* initial validation and parsing of x and y components */
907 if (*wps_bufptr != '|')
908 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
910 _pos = wps_bufptr + 1;
911 if (!isdigit(*_pos))
912 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
913 wps_data->albumart_x = atoi(_pos);
915 _pos = strchr(_pos, '|');
916 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
917 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
919 wps_data->albumart_y = atoi(_pos);
921 _pos = strchr(_pos, '|');
922 if (!_pos || _pos > newline)
923 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
924 e.g. %Cl|7|59\n */
926 /* parsing width field */
927 parsing = true;
928 while (parsing)
930 /* apply each modifier in turn */
931 ++_pos;
932 switch (*_pos)
934 case 'l':
935 case 'L':
936 case '+':
937 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
938 break;
939 case 'c':
940 case 'C':
941 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
942 break;
943 case 'r':
944 case 'R':
945 case '-':
946 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
947 break;
948 case 'd':
949 case 'D':
950 case 'i':
951 case 'I':
952 case 's':
953 case 'S':
954 /* simply ignored */
955 break;
956 default:
957 parsing = false;
958 break;
961 /* extract max width data */
962 if (*_pos != '|')
964 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
965 return WPS_ERROR_INVALID_PARAM;
967 wps_data->albumart_max_width = atoi(_pos);
969 _pos = strchr(_pos, '|');
970 if (!_pos || _pos > newline)
971 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
972 e.g. %Cl|7|59|200\n */
975 /* parsing height field */
976 parsing = true;
977 while (parsing)
979 /* apply each modifier in turn */
980 ++_pos;
981 switch (*_pos)
983 case 't':
984 case 'T':
985 case '-':
986 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
987 break;
988 case 'c':
989 case 'C':
990 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
991 break;
992 case 'b':
993 case 'B':
994 case '+':
995 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
996 break;
997 case 'd':
998 case 'D':
999 case 'i':
1000 case 'I':
1001 case 's':
1002 case 'S':
1003 /* simply ignored */
1004 break;
1005 default:
1006 parsing = false;
1007 break;
1010 /* extract max height data */
1011 if (*_pos != '|')
1013 if (!isdigit(*_pos))
1014 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1016 wps_data->albumart_max_height = atoi(_pos);
1018 _pos = strchr(_pos, '|');
1019 if (!_pos || _pos > newline)
1020 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1021 e.g. %Cl|7|59|200|200\n */
1024 /* if we got here, we parsed everything ok .. ! */
1025 if (wps_data->albumart_max_width < 0)
1026 wps_data->albumart_max_width = 0;
1027 else if (wps_data->albumart_max_width > LCD_WIDTH)
1028 wps_data->albumart_max_width = LCD_WIDTH;
1030 if (wps_data->albumart_max_height < 0)
1031 wps_data->albumart_max_height = 0;
1032 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1033 wps_data->albumart_max_height = LCD_HEIGHT;
1035 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1037 /* Skip the rest of the line */
1038 return skip_end_of_line(wps_bufptr);
1041 static int parse_albumart_conditional(const char *wps_bufptr,
1042 struct wps_token *token,
1043 struct wps_data *wps_data)
1045 struct wps_token *prevtoken = token;
1046 --prevtoken;
1047 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1049 /* This %C is part of a %?C construct.
1050 It's either %?C<blah> or %?Cn<blah> */
1051 token->type = WPS_TOKEN_ALBUMART_FOUND;
1052 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1054 token->next = true;
1055 return 1;
1057 else if (*wps_bufptr == '<')
1059 return 0;
1061 else
1063 token->type = WPS_NO_TOKEN;
1064 return 0;
1067 else
1069 /* This %C tag is in a conditional construct. */
1070 wps_data->albumart_cond_index = condindex[level];
1071 return 0;
1074 #endif /* HAVE_ALBUMART */
1076 #ifdef HAVE_TOUCHSCREEN
1078 struct touchaction {char* s; int action;};
1079 static struct touchaction touchactions[] = {
1080 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1081 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1082 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1083 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1084 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1085 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1086 {"playlist", ACTION_WPS_VIEW_PLAYLIST },
1088 static int parse_touchregion(const char *wps_bufptr,
1089 struct wps_token *token, struct wps_data *wps_data)
1091 (void)token;
1092 unsigned i, imax;
1093 struct touchregion *region;
1094 const char *ptr = wps_bufptr;
1095 const char *action;
1096 int x,y,w,h;
1098 /* format: %T|x|y|width|height|action|
1099 * if action starts with & the area must be held to happen
1100 * action is one of:
1101 * play - play/pause playback
1102 * stop - stop playback, exit the wps
1103 * prev - prev track
1104 * next - next track
1105 * ffwd - seek forward
1106 * rwd - seek backwards
1107 * menu - go back to the main menu
1108 * browse - go back to the file/db browser
1109 * shuffle - toggle shuffle mode
1110 * repmode - cycle the repeat mode
1111 * quickscreen - go into the quickscreen
1112 * contextmenu - open the context menu
1116 if ((wps_data->touchregion_count +1 >= MAX_TOUCHREGIONS) || (*ptr != '|'))
1117 return WPS_ERROR_INVALID_PARAM;
1118 ptr++;
1120 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1121 return WPS_ERROR_INVALID_PARAM;
1123 /* Check there is a terminating | */
1124 if (*ptr != '|')
1125 return WPS_ERROR_INVALID_PARAM;
1127 /* should probably do some bounds checking here with the viewport... but later */
1128 region = &wps_data->touchregion[wps_data->touchregion_count];
1129 region->action = ACTION_NONE;
1130 region->x = x;
1131 region->y = y;
1132 region->width = w;
1133 region->height = h;
1134 region->wvp = &wps_data->viewports[wps_data->num_viewports];
1135 i = 0;
1136 if (*action == '&')
1138 action++;
1139 region->repeat = true;
1141 else
1142 region->repeat = false;
1144 imax = ARRAYLEN(touchactions);
1145 while ((region->action == ACTION_NONE) &&
1146 (i < imax))
1148 /* try to match with one of our touchregion screens */
1149 int len = strlen(touchactions[i].s);
1150 if (!strncmp(touchactions[i].s, action, len)
1151 && *(action+len) == '|')
1152 region->action = touchactions[i].action;
1153 i++;
1155 if (region->action == ACTION_NONE)
1156 return WPS_ERROR_INVALID_PARAM;
1157 wps_data->touchregion_count++;
1158 return skip_end_of_line(wps_bufptr);
1160 #endif
1162 /* Parse a generic token from the given string. Return the length read */
1163 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1165 int skip = 0, taglen = 0, ret;
1166 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1167 const struct wps_tag *tag;
1169 switch(*wps_bufptr)
1172 case '%':
1173 case '<':
1174 case '|':
1175 case '>':
1176 case ';':
1177 case '#':
1178 /* escaped characters */
1179 token->type = WPS_TOKEN_CHARACTER;
1180 token->value.c = *wps_bufptr;
1181 taglen = 1;
1182 wps_data->num_tokens++;
1183 break;
1185 case '?':
1186 /* conditional tag */
1187 token->type = WPS_TOKEN_CONDITIONAL;
1188 level++;
1189 condindex[level] = wps_data->num_tokens;
1190 numoptions[level] = 1;
1191 wps_data->num_tokens++;
1192 ret = parse_token(wps_bufptr + 1, wps_data);
1193 if (ret < 0) return ret;
1194 taglen = 1 + ret;
1195 break;
1197 default:
1198 /* find what tag we have */
1199 for (tag = all_tags;
1200 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1201 tag++) ;
1203 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1204 token->type = tag->type;
1205 wps_data->sublines[wps_data->num_sublines].line_type |=
1206 tag->refresh_type;
1208 /* if the tag has a special parsing function, we call it */
1209 if (tag->parse_func)
1211 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1212 if (ret < 0) return ret;
1213 skip += ret;
1216 /* Some tags we don't want to save as tokens */
1217 if (tag->type == WPS_NO_TOKEN)
1218 break;
1220 /* tags that start with 'F', 'I' or 'D' are for the next file */
1221 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1222 *(tag->name) == 'D')
1223 token->next = true;
1225 wps_data->num_tokens++;
1226 break;
1229 skip += taglen;
1230 return skip;
1233 /* Parses the WPS.
1234 data is the pointer to the structure where the parsed WPS should be stored.
1235 It is initialised.
1236 wps_bufptr points to the string containing the WPS tags */
1237 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1239 if (!data || !wps_bufptr || !*wps_bufptr)
1240 return false;
1242 char *stringbuf = data->string_buffer;
1243 int stringbuf_used = 0;
1244 enum wps_parse_error fail = PARSE_OK;
1245 int ret;
1246 line = 1;
1247 level = -1;
1249 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1250 && data->num_viewports < WPS_MAX_VIEWPORTS
1251 && data->num_lines < WPS_MAX_LINES)
1253 switch(*wps_bufptr++)
1256 /* Regular tag */
1257 case '%':
1258 if ((ret = parse_token(wps_bufptr, data)) < 0)
1260 fail = PARSE_FAIL_COND_INVALID_PARAM;
1261 break;
1263 else if (level >= WPS_MAX_COND_LEVEL - 1)
1265 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1266 break;
1268 wps_bufptr += ret;
1269 break;
1271 /* Alternating sublines separator */
1272 case ';':
1273 if (level >= 0) /* there are unclosed conditionals */
1275 fail = PARSE_FAIL_UNCLOSED_COND;
1276 break;
1279 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1280 wps_start_new_subline(data);
1281 else
1282 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1284 break;
1286 /* Conditional list start */
1287 case '<':
1288 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1290 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1291 break;
1294 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1295 lastcond[level] = data->num_tokens++;
1296 break;
1298 /* Conditional list end */
1299 case '>':
1300 if (level < 0) /* not in a conditional, invalid char */
1302 fail = PARSE_FAIL_INVALID_CHAR;
1303 break;
1306 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1307 if (lastcond[level])
1308 data->tokens[lastcond[level]].value.i = data->num_tokens;
1309 else
1311 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1312 break;
1315 lastcond[level] = 0;
1316 data->num_tokens++;
1317 data->tokens[condindex[level]].value.i = numoptions[level];
1318 level--;
1319 break;
1321 /* Conditional list option */
1322 case '|':
1323 if (level < 0) /* not in a conditional, invalid char */
1325 fail = PARSE_FAIL_INVALID_CHAR;
1326 break;
1329 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1330 if (lastcond[level])
1331 data->tokens[lastcond[level]].value.i = data->num_tokens;
1332 else
1334 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1335 break;
1338 lastcond[level] = data->num_tokens;
1339 numoptions[level]++;
1340 data->num_tokens++;
1341 break;
1343 /* Comment */
1344 case '#':
1345 if (level >= 0) /* there are unclosed conditionals */
1347 fail = PARSE_FAIL_UNCLOSED_COND;
1348 break;
1351 wps_bufptr += skip_end_of_line(wps_bufptr);
1352 break;
1354 /* End of this line */
1355 case '\n':
1356 if (level >= 0) /* there are unclosed conditionals */
1358 fail = PARSE_FAIL_UNCLOSED_COND;
1359 break;
1362 line++;
1363 wps_start_new_subline(data);
1364 data->num_lines++; /* Start a new line */
1366 if ((data->num_lines < WPS_MAX_LINES) &&
1367 (data->num_sublines < WPS_MAX_SUBLINES))
1369 data->lines[data->num_lines].first_subline_idx =
1370 data->num_sublines;
1372 data->sublines[data->num_sublines].first_token_idx =
1373 data->num_tokens;
1376 break;
1378 /* String */
1379 default:
1381 unsigned int len = 1;
1382 const char *string_start = wps_bufptr - 1;
1384 /* find the length of the string */
1385 while (*wps_bufptr && *wps_bufptr != '#' &&
1386 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1387 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1388 *wps_bufptr != '|' && *wps_bufptr != '\n')
1390 wps_bufptr++;
1391 len++;
1394 /* look if we already have that string */
1395 char **str;
1396 int i;
1397 bool found;
1398 for (i = 0, str = data->strings, found = false;
1399 i < data->num_strings &&
1400 !(found = (strlen(*str) == len &&
1401 strncmp(string_start, *str, len) == 0));
1402 i++, str++);
1403 /* If a matching string is found, found is true and i is
1404 the index of the string. If not, found is false */
1406 if (!found)
1408 /* new string */
1410 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1
1411 || data->num_strings >= WPS_MAX_STRINGS)
1413 /* too many strings or characters */
1414 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1415 break;
1418 strlcpy(stringbuf, string_start, len+1);
1420 data->strings[data->num_strings] = stringbuf;
1421 stringbuf += len + 1;
1422 stringbuf_used += len + 1;
1423 data->tokens[data->num_tokens].value.i =
1424 data->num_strings;
1425 data->num_strings++;
1427 else
1429 /* another occurrence of an existing string */
1430 data->tokens[data->num_tokens].value.i = i;
1432 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1433 data->num_tokens++;
1435 break;
1439 if (!fail && level >= 0) /* there are unclosed conditionals */
1440 fail = PARSE_FAIL_UNCLOSED_COND;
1442 if (*wps_bufptr && !fail)
1443 /* one of the limits of the while loop was exceeded */
1444 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1446 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1448 /* We have finished with the last viewport, so increment count */
1449 data->num_viewports++;
1451 #if defined(DEBUG) || defined(SIMULATOR)
1452 print_debug_info(data, fail, line);
1453 #endif
1455 return (fail == 0);
1458 static void wps_reset(struct wps_data *data)
1460 #ifdef HAVE_REMOTE_LCD
1461 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1462 #endif
1463 memset(data, 0, sizeof(*data));
1464 skin_data_init(data);
1465 #ifdef HAVE_REMOTE_LCD
1466 data->remote_wps = rwps;
1467 #endif
1470 #ifdef HAVE_LCD_BITMAP
1472 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1474 char img_path[MAX_PATH];
1475 struct bitmap *bitmap;
1476 bool *loaded;
1477 int n;
1478 for (n = 0; n < BACKDROP_BMP; n++)
1480 if (bmp_names[n])
1482 get_image_filename(bmp_names[n], bmpdir,
1483 img_path, sizeof(img_path));
1485 if (n >= PROGRESSBAR_BMP ) {
1486 /* progressbar bitmap */
1487 bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
1488 loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
1489 } else {
1490 /* regular bitmap */
1491 bitmap = &wps_data->img[n].bm;
1492 loaded = &wps_data->img[n].loaded;
1495 /* load the image */
1496 bitmap->data = wps_data->img_buf_ptr;
1497 if (load_bitmap(wps_data, img_path, bitmap))
1499 *loaded = true;
1501 /* Calculate and store height if this image has sub-images */
1502 if (n < MAX_IMAGES)
1503 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1504 wps_data->img[n].num_subimages;
1506 else
1508 /* Abort if we can't load an image */
1509 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1510 return false;
1515 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1516 if (bmp_names[BACKDROP_BMP])
1518 int screen = SCREEN_MAIN;
1519 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1520 img_path, sizeof(img_path));
1521 #if defined(HAVE_REMOTE_LCD)
1522 /* We only need to check LCD type if there is a remote LCD */
1523 if (wps_data->remote_wps)
1524 screen = SCREEN_REMOTE;
1525 #endif
1526 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1528 #endif /* has backdrop support */
1530 /* If we got here, everything was OK */
1531 return true;
1534 #endif /* HAVE_LCD_BITMAP */
1536 /* to setup up the wps-data from a format-buffer (isfile = false)
1537 from a (wps-)file (isfile = true)*/
1538 bool skin_data_load(struct wps_data *wps_data,
1539 struct screen *display,
1540 const char *buf,
1541 bool isfile)
1543 #ifdef HAVE_ALBUMART
1544 struct mp3entry *curtrack;
1545 long offset;
1546 int status;
1547 int wps_uses_albumart = wps_data->wps_uses_albumart;
1548 int albumart_max_height = wps_data->albumart_max_height;
1549 int albumart_max_width = wps_data->albumart_max_width;
1550 #endif
1551 if (!wps_data || !buf)
1552 return false;
1554 wps_reset(wps_data);
1556 /* Initialise the first (default) viewport */
1557 wps_data->viewports[0].vp.x = 0;
1558 wps_data->viewports[0].vp.width = display->getwidth();
1559 wps_data->viewports[0].vp.height = display->getheight();
1560 switch (statusbar_position(display->screen_type))
1562 case STATUSBAR_OFF:
1563 wps_data->viewports[0].vp.y = 0;
1564 break;
1565 case STATUSBAR_TOP:
1566 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1567 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
1568 break;
1569 case STATUSBAR_BOTTOM:
1570 wps_data->viewports[0].vp.y = 0;
1571 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
1572 break;
1574 #ifdef HAVE_LCD_BITMAP
1575 wps_data->viewports[0].vp.font = FONT_UI;
1576 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1577 #endif
1578 #if LCD_DEPTH > 1
1579 if (display->depth > 1)
1581 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1582 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1584 #endif
1585 if (!isfile)
1587 return wps_parse(wps_data, buf);
1589 else
1592 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1593 * wants to be a virtual file. Feel free to modify dirbrowse()
1594 * if you're feeling brave.
1596 #ifndef __PCTOOL__
1597 if (! strcmp(buf, WPS_DEFAULTCFG) )
1599 global_settings.wps_file[0] = 0;
1600 return false;
1603 #ifdef HAVE_REMOTE_LCD
1604 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1606 global_settings.rwps_file[0] = 0;
1607 return false;
1609 #endif
1610 #endif /* __PCTOOL__ */
1612 int fd = open_utf8(buf, O_RDONLY);
1614 if (fd < 0)
1615 return false;
1617 /* get buffer space from the plugin buffer */
1618 size_t buffersize = 0;
1619 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1621 if (!wps_buffer)
1622 return false;
1624 /* copy the file's content to the buffer for parsing,
1625 ensuring that every line ends with a newline char. */
1626 unsigned int start = 0;
1627 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1629 start += strlen(wps_buffer + start);
1630 if (start < buffersize - 1)
1632 wps_buffer[start++] = '\n';
1633 wps_buffer[start] = 0;
1637 close(fd);
1639 if (start <= 0)
1640 return false;
1642 #ifdef HAVE_LCD_BITMAP
1643 /* Set all filename pointers to NULL */
1644 memset(bmp_names, 0, sizeof(bmp_names));
1645 #endif
1647 /* parse the WPS source */
1648 if (!wps_parse(wps_data, wps_buffer)) {
1649 wps_reset(wps_data);
1650 return false;
1653 wps_data->wps_loaded = true;
1655 #ifdef HAVE_LCD_BITMAP
1656 /* get the bitmap dir */
1657 char bmpdir[MAX_PATH];
1658 char *dot = strrchr(buf, '.');
1660 strlcpy(bmpdir, buf, dot - buf + 1);
1662 /* load the bitmaps that were found by the parsing */
1663 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1664 wps_reset(wps_data);
1665 return false;
1667 #endif
1668 #ifdef HAVE_ALBUMART
1669 status = audio_status();
1670 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1671 (wps_data->wps_uses_albumart &&
1672 (albumart_max_height != wps_data->albumart_max_height ||
1673 albumart_max_width != wps_data->albumart_max_width))) &&
1674 status & AUDIO_STATUS_PLAY)
1676 curtrack = audio_current_track();
1677 offset = curtrack->offset;
1678 audio_stop();
1679 if (!(status & AUDIO_STATUS_PAUSE))
1680 audio_play(offset);
1682 #endif
1683 return true;