more wps->skin moving/renaming
[kugel-rb.git] / apps / gui / skin_engine / wps_parser.c
blob440133327a3230ea80ab4961c7ab1da6d97bf0de
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include "file.h"
26 #include "misc.h"
27 #include "plugin.h"
29 #ifdef __PCTOOL__
30 #ifdef WPSEDITOR
31 #include "proxy.h"
32 #include "sysfont.h"
33 #else
34 #include "checkwps.h"
35 #include "audio.h"
36 #define DEBUGF printf
37 #endif /*WPSEDITOR*/
38 #else
39 #include "debug.h"
40 #endif /*__PCTOOL__*/
42 #include <ctype.h>
43 #include <stdbool.h>
44 #include "font.h"
46 #include "wps_internals.h"
47 #include "skin_engine.h"
48 #include "settings.h"
49 #include "settings_list.h"
51 #ifdef HAVE_LCD_BITMAP
52 #include "bmp.h"
53 #endif
55 #include "backdrop.h"
57 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
58 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
60 #define WPS_ERROR_INVALID_PARAM -1
62 /* level of current conditional.
63 -1 means we're not in a conditional. */
64 static int level = -1;
66 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
67 or WPS_TOKEN_CONDITIONAL_START in current level */
68 static int lastcond[WPS_MAX_COND_LEVEL];
70 /* index of the WPS_TOKEN_CONDITIONAL in current level */
71 static int condindex[WPS_MAX_COND_LEVEL];
73 /* number of condtional options in current level */
74 static int numoptions[WPS_MAX_COND_LEVEL];
76 /* the current line in the file */
77 static int line;
79 #ifdef HAVE_LCD_BITMAP
81 #if LCD_DEPTH > 1
82 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
83 #else
84 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
85 #endif
87 #define PROGRESSBAR_BMP MAX_IMAGES
88 #define BACKDROP_BMP (MAX_BITMAPS-1)
90 /* pointers to the bitmap filenames in the WPS source */
91 static const char *bmp_names[MAX_BITMAPS];
93 #endif /* HAVE_LCD_BITMAP */
95 #if defined(DEBUG) || defined(SIMULATOR)
96 /* debugging function */
97 extern void print_debug_info(struct wps_data *data, int fail, int line);
98 #endif
100 static void wps_reset(struct wps_data *data);
102 /* Function for parsing of details for a token. At the moment the
103 function is called, the token type has already been set. The
104 function must fill in the details and possibly add more tokens
105 to the token array. It should return the number of chars that
106 has been consumed.
108 wps_bufptr points to the char following the tag (i.e. where
109 details begin).
110 token is the pointer to the 'main' token being parsed
112 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
113 struct wps_token *token, struct wps_data *wps_data);
115 struct wps_tag {
116 enum wps_token_type type;
117 const char name[3];
118 unsigned char refresh_type;
119 const wps_tag_parse_func parse_func;
121 static int skip_end_of_line(const char *wps_bufptr);
122 /* prototypes of all special parse functions : */
123 static int parse_timeout(const char *wps_bufptr,
124 struct wps_token *token, struct wps_data *wps_data);
125 static int parse_progressbar(const char *wps_bufptr,
126 struct wps_token *token, struct wps_data *wps_data);
127 static int parse_dir_level(const char *wps_bufptr,
128 struct wps_token *token, struct wps_data *wps_data);
129 static int parse_setting(const char *wps_bufptr,
130 struct wps_token *token, struct wps_data *wps_data);
132 #ifdef HAVE_LCD_BITMAP
133 static int parse_viewport_display(const char *wps_bufptr,
134 struct wps_token *token, struct wps_data *wps_data);
135 static int parse_viewport(const char *wps_bufptr,
136 struct wps_token *token, struct wps_data *wps_data);
137 static int parse_statusbar_enable(const char *wps_bufptr,
138 struct wps_token *token, struct wps_data *wps_data);
139 static int parse_statusbar_disable(const char *wps_bufptr,
140 struct wps_token *token, struct wps_data *wps_data);
141 static int parse_image_display(const char *wps_bufptr,
142 struct wps_token *token, struct wps_data *wps_data);
143 static int parse_image_load(const char *wps_bufptr,
144 struct wps_token *token, struct wps_data *wps_data);
145 #endif /*HAVE_LCD_BITMAP */
146 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
147 static int parse_image_special(const char *wps_bufptr,
148 struct wps_token *token, struct wps_data *wps_data);
149 #endif
150 #ifdef HAVE_ALBUMART
151 static int parse_albumart_load(const char *wps_bufptr,
152 struct wps_token *token, struct wps_data *wps_data);
153 static int parse_albumart_conditional(const char *wps_bufptr,
154 struct wps_token *token, struct wps_data *wps_data);
155 #endif /* HAVE_ALBUMART */
156 #ifdef HAVE_TOUCHSCREEN
157 static int parse_touchregion(const char *wps_bufptr,
158 struct wps_token *token, struct wps_data *wps_data);
159 #else
160 static int fulline_tag_not_supported(const char *wps_bufptr,
161 struct wps_token *token, struct wps_data *wps_data)
163 (void)token; (void)wps_data;
164 return skip_end_of_line(wps_bufptr);
166 #define parse_touchregion fulline_tag_not_supported
167 #endif
168 #ifdef CONFIG_RTC
169 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
170 #else
171 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
172 #endif
174 /* array of available tags - those with more characters have to go first
175 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
176 static const struct wps_tag all_tags[] = {
178 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
179 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
180 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
182 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
183 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
184 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
185 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
186 #if CONFIG_CHARGING >= CHARGING_MONITOR
187 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
188 #endif
189 #if CONFIG_CHARGING
190 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
191 #endif
193 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
194 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
195 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
196 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
197 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
198 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
199 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
200 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
201 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
202 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
203 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
213 /* current file */
214 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
215 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
216 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
217 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
218 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
219 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
220 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
221 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
222 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
223 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
224 parse_dir_level },
226 /* next file */
227 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
234 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
235 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
236 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
237 parse_dir_level },
239 /* current metadata */
240 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
247 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
248 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
249 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
253 /* next metadata */
254 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
262 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
263 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
267 #if (CONFIG_CODEC != MAS3507D)
268 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
269 #endif
271 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
272 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
273 #endif
275 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
277 #ifdef HAS_REMOTE_BUTTON_HOLD
278 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
279 #else
280 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
281 #endif
283 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
284 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
285 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
286 parse_timeout },
288 #ifdef HAVE_LCD_BITMAP
289 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
290 #else
291 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
292 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
293 #endif
294 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
295 parse_progressbar },
297 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
299 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
300 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
301 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
302 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
304 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
305 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
306 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
307 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
309 #ifdef HAVE_TAGCACHE
310 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
311 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
312 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
313 #endif
315 #if CONFIG_CODEC == SWCODEC
316 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
317 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
318 #endif
320 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
321 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
323 #ifdef HAVE_LCD_BITMAP
324 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
325 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
327 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
329 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
330 parse_image_display },
332 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
333 #ifdef HAVE_ALBUMART
334 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
335 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
336 parse_albumart_conditional },
337 #endif
339 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
340 parse_viewport_display },
341 { WPS_NO_TOKEN, "V", 0, parse_viewport },
343 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
344 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
345 #endif
346 #endif
348 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting },
350 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
351 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
353 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
354 /* the array MUST end with an empty string (first char is \0) */
357 /* Returns the number of chars that should be skipped to jump
358 immediately after the first eol, i.e. to the start of the next line */
359 static int skip_end_of_line(const char *wps_bufptr)
361 line++;
362 int skip = 0;
363 while(*(wps_bufptr + skip) != '\n')
364 skip++;
365 return ++skip;
368 /* Starts a new subline in the current line during parsing */
369 static void wps_start_new_subline(struct wps_data *data)
371 data->num_sublines++;
372 data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
373 data->lines[data->num_lines].num_sublines++;
376 #ifdef HAVE_LCD_BITMAP
378 static int parse_statusbar_enable(const char *wps_bufptr,
379 struct wps_token *token,
380 struct wps_data *wps_data)
382 (void)token; /* Kill warnings */
383 wps_data->wps_sb_tag = true;
384 wps_data->show_sb_on_wps = true;
385 if (wps_data->viewports[0].vp.y == 0)
387 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
388 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
390 return skip_end_of_line(wps_bufptr);
393 static int parse_statusbar_disable(const char *wps_bufptr,
394 struct wps_token *token,
395 struct wps_data *wps_data)
397 (void)token; /* Kill warnings */
398 wps_data->wps_sb_tag = true;
399 wps_data->show_sb_on_wps = false;
400 if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
402 wps_data->viewports[0].vp.y = 0;
403 wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
405 return skip_end_of_line(wps_bufptr);
408 static bool load_bitmap(struct wps_data *wps_data,
409 char* filename,
410 struct bitmap *bm)
412 int format;
413 #ifdef HAVE_REMOTE_LCD
414 if (wps_data->remote_wps)
415 format = FORMAT_ANY|FORMAT_REMOTE;
416 else
417 #endif
418 format = FORMAT_ANY|FORMAT_TRANSPARENT;
420 int ret = read_bmp_file(filename, bm,
421 wps_data->img_buf_free,
422 format,NULL);
424 if (ret > 0)
426 #if LCD_DEPTH == 16
427 if (ret % 2) ret++;
428 /* Always consume an even number of bytes */
429 #endif
430 wps_data->img_buf_ptr += ret;
431 wps_data->img_buf_free -= ret;
433 return true;
435 else
436 return false;
439 static int get_image_id(int c)
441 if(c >= 'a' && c <= 'z')
442 return c - 'a';
443 else if(c >= 'A' && c <= 'Z')
444 return c - 'A' + 26;
445 else
446 return -1;
449 static char *get_image_filename(const char *start, const char* bmpdir,
450 char *buf, int buf_size)
452 const char *end = strchr(start, '|');
454 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
456 buf = "\0";
457 return NULL;
460 int bmpdirlen = strlen(bmpdir);
462 strcpy(buf, bmpdir);
463 buf[bmpdirlen] = '/';
464 memcpy( &buf[bmpdirlen + 1], start, end - start);
465 buf[bmpdirlen + 1 + end - start] = 0;
467 return buf;
470 static int parse_image_display(const char *wps_bufptr,
471 struct wps_token *token,
472 struct wps_data *wps_data)
474 (void)wps_data;
475 int n = get_image_id(wps_bufptr[0]);
476 int subimage;
478 if (n == -1)
480 /* invalid picture display tag */
481 return WPS_ERROR_INVALID_PARAM;
484 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
486 /* Sanity check */
487 if (subimage >= wps_data->img[n].num_subimages)
488 return WPS_ERROR_INVALID_PARAM;
490 /* Store sub-image number to display in high bits */
491 token->value.i = n | (subimage << 8);
492 return 2; /* We have consumed 2 bytes */
493 } else {
494 token->value.i = n;
495 return 1; /* We have consumed 1 byte */
499 static int parse_image_load(const char *wps_bufptr,
500 struct wps_token *token,
501 struct wps_data *wps_data)
503 int n;
504 const char *ptr = wps_bufptr;
505 const char *pos;
506 const char* filename;
507 const char* id;
508 const char *newline;
509 int x,y;
511 /* format: %x|n|filename.bmp|x|y|
512 or %xl|n|filename.bmp|x|y|
513 or %xl|n|filename.bmp|x|y|num_subimages|
516 if (*ptr != '|')
517 return WPS_ERROR_INVALID_PARAM;
519 ptr++;
521 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
522 return WPS_ERROR_INVALID_PARAM;
524 /* Check there is a terminating | */
525 if (*ptr != '|')
526 return WPS_ERROR_INVALID_PARAM;
528 /* get the image ID */
529 n = get_image_id(*id);
531 /* check the image number and load state */
532 if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
534 /* Invalid image ID */
535 return WPS_ERROR_INVALID_PARAM;
538 /* save a pointer to the filename */
539 bmp_names[n] = filename;
541 wps_data->img[n].x = x;
542 wps_data->img[n].y = y;
544 /* save current viewport */
545 wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
547 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
549 wps_data->img[n].always_display = true;
551 else
553 /* Parse the (optional) number of sub-images */
554 ptr++;
555 newline = strchr(ptr, '\n');
556 pos = strchr(ptr, '|');
557 if (pos && pos < newline)
558 wps_data->img[n].num_subimages = atoi(ptr);
560 if (wps_data->img[n].num_subimages <= 0)
561 return WPS_ERROR_INVALID_PARAM;
564 /* Skip the rest of the line */
565 return skip_end_of_line(wps_bufptr);
568 static int parse_viewport_display(const char *wps_bufptr,
569 struct wps_token *token,
570 struct wps_data *wps_data)
572 (void)wps_data;
573 char letter = wps_bufptr[0];
575 if (letter < 'a' || letter > 'z')
577 /* invalid viewport tag */
578 return WPS_ERROR_INVALID_PARAM;
580 token->value.i = letter;
581 return 1;
584 static int parse_viewport(const char *wps_bufptr,
585 struct wps_token *token,
586 struct wps_data *wps_data)
588 (void)token; /* Kill warnings */
589 const char *ptr = wps_bufptr;
590 struct viewport* vp;
591 int depth;
592 uint32_t set = 0;
593 enum {
594 PL_X = 0,
595 PL_Y,
596 PL_WIDTH,
597 PL_HEIGHT,
598 PL_FONT,
599 PL_FG,
600 PL_BG,
602 int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
603 #ifdef HAVE_REMOTE_LCD
604 if (wps_data->remote_wps)
606 lcd_width = LCD_REMOTE_WIDTH;
607 lcd_height = LCD_REMOTE_HEIGHT;
609 #endif
611 if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
612 return WPS_ERROR_INVALID_PARAM;
614 wps_data->num_viewports++;
615 /* check for the optional letter to signify its a hideable viewport */
616 /* %Vl|<label>|<rest of tags>| */
617 wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
619 if (*ptr == 'l')
621 if (*(ptr+1) == '|')
623 char label = *(ptr+2);
624 if (label >= 'a' && label <= 'z')
626 wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
627 wps_data->viewports[wps_data->num_viewports].label = label;
629 else
630 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
631 ptr += 3;
634 if (*ptr != '|')
635 return WPS_ERROR_INVALID_PARAM;
637 ptr++;
638 vp = &wps_data->viewports[wps_data->num_viewports].vp;
639 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
641 /* Set the defaults for fields not user-specified */
642 vp->drawmode = DRMODE_SOLID;
644 /* Work out the depth of this display */
645 #ifdef HAVE_REMOTE_LCD
646 depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
647 #else
648 depth = LCD_DEPTH;
649 #endif
651 #ifdef HAVE_LCD_COLOR
652 if (depth == 16)
654 if (!(ptr = parse_list("dddddcc", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
655 &vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
656 return WPS_ERROR_INVALID_PARAM;
658 else
659 #endif
660 #if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
661 if (depth == 2) {
662 /* Default to black on white */
663 vp->fg_pattern = 0;
664 vp->bg_pattern = 3;
665 if (!(ptr = parse_list("dddddgg", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
666 &vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
667 return WPS_ERROR_INVALID_PARAM;
669 else
670 #endif
671 #if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
672 if (depth == 1)
674 if (!(ptr = parse_list("ddddd", &set, '|', ptr, &vp->x, &vp->y,
675 &vp->width, &vp->height, &vp->font)))
676 return WPS_ERROR_INVALID_PARAM;
678 else
679 #endif
682 /* Check for trailing | */
683 if (*ptr != '|')
684 return WPS_ERROR_INVALID_PARAM;
686 if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
687 return WPS_ERROR_INVALID_PARAM;
689 /* fix defaults */
690 if (!LIST_VALUE_PARSED(set, PL_WIDTH))
691 vp->width = lcd_width - vp->x;
692 if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
693 vp->height = lcd_height - vp->y;
695 /* Default to using the user font if the font was an invalid number */
696 if (!LIST_VALUE_PARSED(set, PL_FONT) ||
697 ((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
698 vp->font = FONT_UI;
700 /* Validate the viewport dimensions - we know that the numbers are
701 non-negative integers */
702 if ((vp->x >= lcd_width) ||
703 ((vp->x + vp->width) > lcd_width) ||
704 (vp->y >= lcd_height) ||
705 ((vp->y + vp->height) > lcd_height))
707 return WPS_ERROR_INVALID_PARAM;
710 #ifdef HAVE_LCD_COLOR
711 if (depth == 16)
713 if (!LIST_VALUE_PARSED(set, PL_FG))
714 vp->fg_pattern = global_settings.fg_color;
715 if (!LIST_VALUE_PARSED(set, PL_BG))
716 vp->bg_pattern = global_settings.bg_color;
718 #endif
720 wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
722 wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
724 if (wps_data->num_sublines < WPS_MAX_SUBLINES)
726 wps_data->lines[wps_data->num_lines].first_subline_idx =
727 wps_data->num_sublines;
729 wps_data->sublines[wps_data->num_sublines].first_token_idx =
730 wps_data->num_tokens;
733 /* Skip the rest of the line */
734 return skip_end_of_line(wps_bufptr);
737 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
738 static int parse_image_special(const char *wps_bufptr,
739 struct wps_token *token,
740 struct wps_data *wps_data)
742 (void)wps_data; /* kill warning */
743 (void)token;
744 const char *pos = NULL;
745 const char *newline;
747 pos = strchr(wps_bufptr + 1, '|');
748 newline = strchr(wps_bufptr, '\n');
750 if (pos > newline)
751 return WPS_ERROR_INVALID_PARAM;
752 #if LCD_DEPTH > 1
753 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
755 /* format: %X|filename.bmp| */
756 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
758 #endif
760 /* Skip the rest of the line */
761 return skip_end_of_line(wps_bufptr);
763 #endif
765 #endif /* HAVE_LCD_BITMAP */
767 static int parse_setting(const char *wps_bufptr,
768 struct wps_token *token,
769 struct wps_data *wps_data)
771 (void)wps_data;
772 const char *ptr = wps_bufptr;
773 const char *end;
774 int i;
776 /* Find the setting's cfg_name */
777 if (*ptr != '|')
778 return WPS_ERROR_INVALID_PARAM;
779 ptr++;
780 end = strchr(ptr,'|');
781 if (!end)
782 return WPS_ERROR_INVALID_PARAM;
784 /* Find the setting */
785 for (i=0; i<nb_settings; i++)
786 if (settings[i].cfg_name &&
787 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
788 /* prevent matches on cfg_name prefixes */
789 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
790 break;
791 if (i == nb_settings)
792 return WPS_ERROR_INVALID_PARAM;
794 /* Store the setting number */
795 token->value.i = i;
797 /* Skip the rest of the line */
798 return end-ptr+2;
802 static int parse_dir_level(const char *wps_bufptr,
803 struct wps_token *token,
804 struct wps_data *wps_data)
806 char val[] = { *wps_bufptr, '\0' };
807 token->value.i = atoi(val);
808 (void)wps_data; /* Kill warnings */
809 return 1;
812 static int parse_timeout(const char *wps_bufptr,
813 struct wps_token *token,
814 struct wps_data *wps_data)
816 int skip = 0;
817 int val = 0;
818 bool have_point = false;
819 bool have_tenth = false;
821 (void)wps_data; /* Kill the warning */
823 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
825 if (*wps_bufptr != '.')
827 val *= 10;
828 val += *wps_bufptr - '0';
829 if (have_point)
831 have_tenth = true;
832 wps_bufptr++;
833 skip++;
834 break;
837 else
838 have_point = true;
840 wps_bufptr++;
841 skip++;
844 if (have_tenth == false)
845 val *= 10;
847 if (val == 0 && skip == 0)
849 /* decide what to do if no value was specified */
850 switch (token->type)
852 case WPS_TOKEN_SUBLINE_TIMEOUT:
853 return -1;
854 case WPS_TOKEN_BUTTON_VOLUME:
855 val = 10;
856 break;
859 token->value.i = val;
861 return skip;
864 static int parse_progressbar(const char *wps_bufptr,
865 struct wps_token *token,
866 struct wps_data *wps_data)
868 (void)token; /* Kill warnings */
869 /* %pb or %pb|filename|x|y|width|height|
870 using - for any of the params uses "sane" values */
871 #ifdef HAVE_LCD_BITMAP
872 enum {
873 PB_FILENAME = 0,
874 PB_X,
875 PB_Y,
876 PB_WIDTH,
877 PB_HEIGHT
879 const char *filename;
880 int x, y, height, width;
881 uint32_t set = 0;
882 const char *ptr = wps_bufptr;
883 struct progressbar *pb;
884 struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
885 #ifndef __PCTOOL__
886 int font_height = font_get(vp->font)->height;
887 #else
888 int font_height = 8;
889 #endif
890 int line_num = wps_data->num_lines -
891 wps_data->viewports[wps_data->num_viewports].first_line;
893 if (wps_data->progressbar_count >= MAX_PROGRESSBARS)
894 return WPS_ERROR_INVALID_PARAM;
896 pb = &wps_data->progressbar[wps_data->progressbar_count];
897 pb->have_bitmap_pb = false;
899 if (*wps_bufptr != '|') /* regular old style */
901 pb->x = 0;
902 pb->width = vp->width;
903 pb->height = SYSFONT_HEIGHT-2;
904 pb->y = -line_num - 1; /* Will be computed during the rendering */
906 wps_data->viewports[wps_data->num_viewports].pb = pb;
907 wps_data->progressbar_count++;
908 return 0;
910 ptr = wps_bufptr + 1;
912 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
913 &x, &y, &width, &height)))
914 return WPS_ERROR_INVALID_PARAM;
916 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
917 bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
919 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
920 pb->x = x;
921 else
922 pb->x = vp->x;
924 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
926 /* A zero width causes a divide-by-zero error later, so reject it */
927 if (width == 0)
928 return WPS_ERROR_INVALID_PARAM;
930 pb->width = width;
932 else
933 pb->width = vp->width - pb->x;
935 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
937 /* A zero height makes no sense - reject it */
938 if (height == 0)
939 return WPS_ERROR_INVALID_PARAM;
941 pb->height = height;
943 else
944 pb->height = font_height;
946 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
947 pb->y = y;
948 else
949 pb->y = -line_num - 1; /* Will be computed during the rendering */
951 wps_data->viewports[wps_data->num_viewports].pb = pb;
952 wps_data->progressbar_count++;
954 /* Skip the rest of the line */
955 return skip_end_of_line(wps_bufptr)-1;
956 #else
958 if (*(wps_bufptr-1) == 'f')
959 wps_data->full_line_progressbar = true;
960 else
961 wps_data->full_line_progressbar = false;
963 return 0;
965 #endif
968 #ifdef HAVE_ALBUMART
969 static int parse_albumart_load(const char *wps_bufptr,
970 struct wps_token *token,
971 struct wps_data *wps_data)
973 const char *_pos, *newline;
974 bool parsing;
975 (void)token; /* silence warning */
977 /* reset albumart info in wps */
978 wps_data->albumart_max_width = -1;
979 wps_data->albumart_max_height = -1;
980 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
981 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
983 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
985 newline = strchr(wps_bufptr, '\n');
987 /* initial validation and parsing of x and y components */
988 if (*wps_bufptr != '|')
989 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
991 _pos = wps_bufptr + 1;
992 if (!isdigit(*_pos))
993 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
994 wps_data->albumart_x = atoi(_pos);
996 _pos = strchr(_pos, '|');
997 if (!_pos || _pos > newline || !isdigit(*(++_pos)))
998 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
1000 wps_data->albumart_y = atoi(_pos);
1002 _pos = strchr(_pos, '|');
1003 if (!_pos || _pos > newline)
1004 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1005 e.g. %Cl|7|59\n */
1007 /* parsing width field */
1008 parsing = true;
1009 while (parsing)
1011 /* apply each modifier in turn */
1012 ++_pos;
1013 switch (*_pos)
1015 case 'l':
1016 case 'L':
1017 case '+':
1018 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_LEFT;
1019 break;
1020 case 'c':
1021 case 'C':
1022 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER;
1023 break;
1024 case 'r':
1025 case 'R':
1026 case '-':
1027 wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_RIGHT;
1028 break;
1029 case 'd':
1030 case 'D':
1031 case 'i':
1032 case 'I':
1033 case 's':
1034 case 'S':
1035 /* simply ignored */
1036 break;
1037 default:
1038 parsing = false;
1039 break;
1042 /* extract max width data */
1043 if (*_pos != '|')
1045 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1046 return WPS_ERROR_INVALID_PARAM;
1048 wps_data->albumart_max_width = atoi(_pos);
1050 _pos = strchr(_pos, '|');
1051 if (!_pos || _pos > newline)
1052 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1053 e.g. %Cl|7|59|200\n */
1056 /* parsing height field */
1057 parsing = true;
1058 while (parsing)
1060 /* apply each modifier in turn */
1061 ++_pos;
1062 switch (*_pos)
1064 case 't':
1065 case 'T':
1066 case '-':
1067 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_TOP;
1068 break;
1069 case 'c':
1070 case 'C':
1071 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER;
1072 break;
1073 case 'b':
1074 case 'B':
1075 case '+':
1076 wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1077 break;
1078 case 'd':
1079 case 'D':
1080 case 'i':
1081 case 'I':
1082 case 's':
1083 case 'S':
1084 /* simply ignored */
1085 break;
1086 default:
1087 parsing = false;
1088 break;
1091 /* extract max height data */
1092 if (*_pos != '|')
1094 if (!isdigit(*_pos))
1095 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1097 wps_data->albumart_max_height = atoi(_pos);
1099 _pos = strchr(_pos, '|');
1100 if (!_pos || _pos > newline)
1101 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1102 e.g. %Cl|7|59|200|200\n */
1105 /* if we got here, we parsed everything ok .. ! */
1106 if (wps_data->albumart_max_width < 0)
1107 wps_data->albumart_max_width = 0;
1108 else if (wps_data->albumart_max_width > LCD_WIDTH)
1109 wps_data->albumart_max_width = LCD_WIDTH;
1111 if (wps_data->albumart_max_height < 0)
1112 wps_data->albumart_max_height = 0;
1113 else if (wps_data->albumart_max_height > LCD_HEIGHT)
1114 wps_data->albumart_max_height = LCD_HEIGHT;
1116 wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
1118 /* Skip the rest of the line */
1119 return skip_end_of_line(wps_bufptr);
1122 static int parse_albumart_conditional(const char *wps_bufptr,
1123 struct wps_token *token,
1124 struct wps_data *wps_data)
1126 struct wps_token *prevtoken = token;
1127 --prevtoken;
1128 if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
1130 /* This %C is part of a %?C construct.
1131 It's either %?C<blah> or %?Cn<blah> */
1132 token->type = WPS_TOKEN_ALBUMART_FOUND;
1133 if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
1135 token->next = true;
1136 return 1;
1138 else if (*wps_bufptr == '<')
1140 return 0;
1142 else
1144 token->type = WPS_NO_TOKEN;
1145 return 0;
1148 else
1150 /* This %C tag is in a conditional construct. */
1151 wps_data->albumart_cond_index = condindex[level];
1152 return 0;
1155 #endif /* HAVE_ALBUMART */
1157 #ifdef HAVE_TOUCHSCREEN
1159 struct touchaction {char* s; int action;};
1160 static struct touchaction touchactions[] = {
1161 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1162 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1163 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1164 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1165 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1166 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1167 {"playlist", ACTION_WPS_VIEW_PLAYLIST },
1169 static int parse_touchregion(const char *wps_bufptr,
1170 struct wps_token *token, struct wps_data *wps_data)
1172 (void)token;
1173 unsigned i, imax;
1174 struct touchregion *region;
1175 const char *ptr = wps_bufptr;
1176 const char *action;
1177 int x,y,w,h;
1179 /* format: %T|x|y|width|height|action|
1180 * if action starts with & the area must be held to happen
1181 * action is one of:
1182 * play - play/pause playback
1183 * stop - stop playback, exit the wps
1184 * prev - prev track
1185 * next - next track
1186 * ffwd - seek forward
1187 * rwd - seek backwards
1188 * menu - go back to the main menu
1189 * browse - go back to the file/db browser
1190 * shuffle - toggle shuffle mode
1191 * repmode - cycle the repeat mode
1192 * quickscreen - go into the quickscreen
1193 * contextmenu - open the context menu
1197 if ((wps_data->touchregion_count +1 >= MAX_TOUCHREGIONS) || (*ptr != '|'))
1198 return WPS_ERROR_INVALID_PARAM;
1199 ptr++;
1201 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1202 return WPS_ERROR_INVALID_PARAM;
1204 /* Check there is a terminating | */
1205 if (*ptr != '|')
1206 return WPS_ERROR_INVALID_PARAM;
1208 /* should probably do some bounds checking here with the viewport... but later */
1209 region = &wps_data->touchregion[wps_data->touchregion_count];
1210 region->action = ACTION_NONE;
1211 region->x = x;
1212 region->y = y;
1213 region->width = w;
1214 region->height = h;
1215 region->wvp = &wps_data->viewports[wps_data->num_viewports];
1216 i = 0;
1217 if (*action == '&')
1219 action++;
1220 region->repeat = true;
1222 else
1223 region->repeat = false;
1225 imax = ARRAYLEN(touchactions);
1226 while ((region->action == ACTION_NONE) &&
1227 (i < imax))
1229 /* try to match with one of our touchregion screens */
1230 int len = strlen(touchactions[i].s);
1231 if (!strncmp(touchactions[i].s, action, len)
1232 && *(action+len) == '|')
1233 region->action = touchactions[i].action;
1234 i++;
1236 if (region->action == ACTION_NONE)
1237 return WPS_ERROR_INVALID_PARAM;
1238 wps_data->touchregion_count++;
1239 return skip_end_of_line(wps_bufptr);
1241 #endif
1243 /* Parse a generic token from the given string. Return the length read */
1244 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1246 int skip = 0, taglen = 0, ret;
1247 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1248 const struct wps_tag *tag;
1250 switch(*wps_bufptr)
1253 case '%':
1254 case '<':
1255 case '|':
1256 case '>':
1257 case ';':
1258 case '#':
1259 /* escaped characters */
1260 token->type = WPS_TOKEN_CHARACTER;
1261 token->value.c = *wps_bufptr;
1262 taglen = 1;
1263 wps_data->num_tokens++;
1264 break;
1266 case '?':
1267 /* conditional tag */
1268 token->type = WPS_TOKEN_CONDITIONAL;
1269 level++;
1270 condindex[level] = wps_data->num_tokens;
1271 numoptions[level] = 1;
1272 wps_data->num_tokens++;
1273 ret = parse_token(wps_bufptr + 1, wps_data);
1274 if (ret < 0) return ret;
1275 taglen = 1 + ret;
1276 break;
1278 default:
1279 /* find what tag we have */
1280 for (tag = all_tags;
1281 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1282 tag++) ;
1284 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1285 token->type = tag->type;
1286 wps_data->sublines[wps_data->num_sublines].line_type |=
1287 tag->refresh_type;
1289 /* if the tag has a special parsing function, we call it */
1290 if (tag->parse_func)
1292 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1293 if (ret < 0) return ret;
1294 skip += ret;
1297 /* Some tags we don't want to save as tokens */
1298 if (tag->type == WPS_NO_TOKEN)
1299 break;
1301 /* tags that start with 'F', 'I' or 'D' are for the next file */
1302 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1303 *(tag->name) == 'D')
1304 token->next = true;
1306 wps_data->num_tokens++;
1307 break;
1310 skip += taglen;
1311 return skip;
1314 /* Parses the WPS.
1315 data is the pointer to the structure where the parsed WPS should be stored.
1316 It is initialised.
1317 wps_bufptr points to the string containing the WPS tags */
1318 static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
1320 if (!data || !wps_bufptr || !*wps_bufptr)
1321 return false;
1323 char *stringbuf = data->string_buffer;
1324 int stringbuf_used = 0;
1325 enum wps_parse_error fail = PARSE_OK;
1326 int ret;
1327 line = 1;
1328 level = -1;
1330 while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
1331 && data->num_viewports < WPS_MAX_VIEWPORTS
1332 && data->num_lines < WPS_MAX_LINES)
1334 switch(*wps_bufptr++)
1337 /* Regular tag */
1338 case '%':
1339 if ((ret = parse_token(wps_bufptr, data)) < 0)
1341 fail = PARSE_FAIL_COND_INVALID_PARAM;
1342 break;
1344 else if (level >= WPS_MAX_COND_LEVEL - 1)
1346 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1347 break;
1349 wps_bufptr += ret;
1350 break;
1352 /* Alternating sublines separator */
1353 case ';':
1354 if (level >= 0) /* there are unclosed conditionals */
1356 fail = PARSE_FAIL_UNCLOSED_COND;
1357 break;
1360 if (data->num_sublines+1 < WPS_MAX_SUBLINES)
1361 wps_start_new_subline(data);
1362 else
1363 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1365 break;
1367 /* Conditional list start */
1368 case '<':
1369 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1371 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1372 break;
1375 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1376 lastcond[level] = data->num_tokens++;
1377 break;
1379 /* Conditional list end */
1380 case '>':
1381 if (level < 0) /* not in a conditional, invalid char */
1383 fail = PARSE_FAIL_INVALID_CHAR;
1384 break;
1387 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1388 if (lastcond[level])
1389 data->tokens[lastcond[level]].value.i = data->num_tokens;
1390 else
1392 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1393 break;
1396 lastcond[level] = 0;
1397 data->num_tokens++;
1398 data->tokens[condindex[level]].value.i = numoptions[level];
1399 level--;
1400 break;
1402 /* Conditional list option */
1403 case '|':
1404 if (level < 0) /* not in a conditional, invalid char */
1406 fail = PARSE_FAIL_INVALID_CHAR;
1407 break;
1410 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1411 if (lastcond[level])
1412 data->tokens[lastcond[level]].value.i = data->num_tokens;
1413 else
1415 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1416 break;
1419 lastcond[level] = data->num_tokens;
1420 numoptions[level]++;
1421 data->num_tokens++;
1422 break;
1424 /* Comment */
1425 case '#':
1426 if (level >= 0) /* there are unclosed conditionals */
1428 fail = PARSE_FAIL_UNCLOSED_COND;
1429 break;
1432 wps_bufptr += skip_end_of_line(wps_bufptr);
1433 break;
1435 /* End of this line */
1436 case '\n':
1437 if (level >= 0) /* there are unclosed conditionals */
1439 fail = PARSE_FAIL_UNCLOSED_COND;
1440 break;
1443 line++;
1444 wps_start_new_subline(data);
1445 data->num_lines++; /* Start a new line */
1447 if ((data->num_lines < WPS_MAX_LINES) &&
1448 (data->num_sublines < WPS_MAX_SUBLINES))
1450 data->lines[data->num_lines].first_subline_idx =
1451 data->num_sublines;
1453 data->sublines[data->num_sublines].first_token_idx =
1454 data->num_tokens;
1457 break;
1459 /* String */
1460 default:
1462 unsigned int len = 1;
1463 const char *string_start = wps_bufptr - 1;
1465 /* find the length of the string */
1466 while (*wps_bufptr && *wps_bufptr != '#' &&
1467 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1468 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1469 *wps_bufptr != '|' && *wps_bufptr != '\n')
1471 wps_bufptr++;
1472 len++;
1475 /* look if we already have that string */
1476 char **str;
1477 int i;
1478 bool found;
1479 for (i = 0, str = data->strings, found = false;
1480 i < data->num_strings &&
1481 !(found = (strlen(*str) == len &&
1482 strncmp(string_start, *str, len) == 0));
1483 i++, str++);
1484 /* If a matching string is found, found is true and i is
1485 the index of the string. If not, found is false */
1487 if (!found)
1489 /* new string */
1491 if (stringbuf_used + len > STRING_BUFFER_SIZE - 1
1492 || data->num_strings >= WPS_MAX_STRINGS)
1494 /* too many strings or characters */
1495 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1496 break;
1499 strlcpy(stringbuf, string_start, len+1);
1501 data->strings[data->num_strings] = stringbuf;
1502 stringbuf += len + 1;
1503 stringbuf_used += len + 1;
1504 data->tokens[data->num_tokens].value.i =
1505 data->num_strings;
1506 data->num_strings++;
1508 else
1510 /* another occurrence of an existing string */
1511 data->tokens[data->num_tokens].value.i = i;
1513 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1514 data->num_tokens++;
1516 break;
1520 if (!fail && level >= 0) /* there are unclosed conditionals */
1521 fail = PARSE_FAIL_UNCLOSED_COND;
1523 if (*wps_bufptr && !fail)
1524 /* one of the limits of the while loop was exceeded */
1525 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1527 data->viewports[data->num_viewports].last_line = data->num_lines - 1;
1529 /* We have finished with the last viewport, so increment count */
1530 data->num_viewports++;
1532 #if defined(DEBUG) || defined(SIMULATOR)
1533 print_debug_info(data, fail, line);
1534 #endif
1536 return (fail == 0);
1539 static void wps_reset(struct wps_data *data)
1541 #ifdef HAVE_REMOTE_LCD
1542 bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
1543 #endif
1544 memset(data, 0, sizeof(*data));
1545 skin_data_init(data);
1546 #ifdef HAVE_REMOTE_LCD
1547 data->remote_wps = rwps;
1548 #endif
1551 #ifdef HAVE_LCD_BITMAP
1553 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1555 char img_path[MAX_PATH];
1556 struct bitmap *bitmap;
1557 bool *loaded;
1558 int n;
1559 for (n = 0; n < BACKDROP_BMP; n++)
1561 if (bmp_names[n])
1563 get_image_filename(bmp_names[n], bmpdir,
1564 img_path, sizeof(img_path));
1566 if (n >= PROGRESSBAR_BMP ) {
1567 /* progressbar bitmap */
1568 bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
1569 loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
1570 } else {
1571 /* regular bitmap */
1572 bitmap = &wps_data->img[n].bm;
1573 loaded = &wps_data->img[n].loaded;
1576 /* load the image */
1577 bitmap->data = wps_data->img_buf_ptr;
1578 if (load_bitmap(wps_data, img_path, bitmap))
1580 *loaded = true;
1582 /* Calculate and store height if this image has sub-images */
1583 if (n < MAX_IMAGES)
1584 wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
1585 wps_data->img[n].num_subimages;
1587 else
1589 /* Abort if we can't load an image */
1590 DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
1591 return false;
1596 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1597 if (bmp_names[BACKDROP_BMP])
1599 int screen = SCREEN_MAIN;
1600 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1601 img_path, sizeof(img_path));
1602 #if defined(HAVE_REMOTE_LCD)
1603 /* We only need to check LCD type if there is a remote LCD */
1604 if (wps_data->remote_wps)
1605 screen = SCREEN_REMOTE;
1606 #endif
1607 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1609 #endif /* has backdrop support */
1611 /* If we got here, everything was OK */
1612 return true;
1615 #endif /* HAVE_LCD_BITMAP */
1617 /* to setup up the wps-data from a format-buffer (isfile = false)
1618 from a (wps-)file (isfile = true)*/
1619 bool skin_data_load(struct wps_data *wps_data,
1620 struct screen *display,
1621 const char *buf,
1622 bool isfile)
1624 #ifdef HAVE_ALBUMART
1625 struct mp3entry *curtrack;
1626 long offset;
1627 int status;
1628 int wps_uses_albumart = wps_data->wps_uses_albumart;
1629 int albumart_max_height = wps_data->albumart_max_height;
1630 int albumart_max_width = wps_data->albumart_max_width;
1631 #endif
1632 if (!wps_data || !buf)
1633 return false;
1635 wps_reset(wps_data);
1637 /* Initialise the first (default) viewport */
1638 wps_data->viewports[0].vp.x = 0;
1639 wps_data->viewports[0].vp.width = display->getwidth();
1640 wps_data->viewports[0].vp.height = display->getheight();
1641 switch (statusbar_position(display->screen_type))
1643 case STATUSBAR_OFF:
1644 wps_data->viewports[0].vp.y = 0;
1645 break;
1646 case STATUSBAR_TOP:
1647 wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
1648 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
1649 break;
1650 case STATUSBAR_BOTTOM:
1651 wps_data->viewports[0].vp.y = 0;
1652 wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
1653 break;
1655 #ifdef HAVE_LCD_BITMAP
1656 wps_data->viewports[0].vp.font = FONT_UI;
1657 wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
1658 #endif
1659 #if LCD_DEPTH > 1
1660 if (display->depth > 1)
1662 wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
1663 wps_data->viewports[0].vp.bg_pattern = display->get_background();
1665 #endif
1666 if (!isfile)
1668 return wps_parse(wps_data, buf);
1670 else
1673 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1674 * wants to be a virtual file. Feel free to modify dirbrowse()
1675 * if you're feeling brave.
1677 #ifndef __PCTOOL__
1678 if (! strcmp(buf, WPS_DEFAULTCFG) )
1680 global_settings.wps_file[0] = 0;
1681 return false;
1684 #ifdef HAVE_REMOTE_LCD
1685 if (! strcmp(buf, RWPS_DEFAULTCFG) )
1687 global_settings.rwps_file[0] = 0;
1688 return false;
1690 #endif
1691 #endif /* __PCTOOL__ */
1693 int fd = open_utf8(buf, O_RDONLY);
1695 if (fd < 0)
1696 return false;
1698 /* get buffer space from the plugin buffer */
1699 size_t buffersize = 0;
1700 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1702 if (!wps_buffer)
1703 return false;
1705 /* copy the file's content to the buffer for parsing,
1706 ensuring that every line ends with a newline char. */
1707 unsigned int start = 0;
1708 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1710 start += strlen(wps_buffer + start);
1711 if (start < buffersize - 1)
1713 wps_buffer[start++] = '\n';
1714 wps_buffer[start] = 0;
1718 close(fd);
1720 if (start <= 0)
1721 return false;
1723 #ifdef HAVE_LCD_BITMAP
1724 /* Set all filename pointers to NULL */
1725 memset(bmp_names, 0, sizeof(bmp_names));
1726 #endif
1728 /* parse the WPS source */
1729 if (!wps_parse(wps_data, wps_buffer)) {
1730 wps_reset(wps_data);
1731 return false;
1734 wps_data->wps_loaded = true;
1736 #ifdef HAVE_LCD_BITMAP
1737 /* get the bitmap dir */
1738 char bmpdir[MAX_PATH];
1739 char *dot = strrchr(buf, '.');
1741 strlcpy(bmpdir, buf, dot - buf + 1);
1743 /* load the bitmaps that were found by the parsing */
1744 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1745 wps_reset(wps_data);
1746 return false;
1748 #endif
1749 #ifdef HAVE_ALBUMART
1750 status = audio_status();
1751 if (((!wps_uses_albumart && wps_data->wps_uses_albumart) ||
1752 (wps_data->wps_uses_albumart &&
1753 (albumart_max_height != wps_data->albumart_max_height ||
1754 albumart_max_width != wps_data->albumart_max_width))) &&
1755 status & AUDIO_STATUS_PLAY)
1757 curtrack = audio_current_track();
1758 offset = curtrack->offset;
1759 audio_stop();
1760 if (!(status & AUDIO_STATUS_PAUSE))
1761 audio_play(offset);
1763 #endif
1764 return true;