Move find_viewport() and find_image() to skin_parser.c where the reset of the linked...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blobc05ebca45e8c117340a893394eebb4f30947640e
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 "config.h"
26 #include "file.h"
27 #include "misc.h"
28 #include "plugin.h"
29 #include "viewport.h"
31 #ifdef __PCTOOL__
32 #ifdef WPSEDITOR
33 #include "proxy.h"
34 #include "sysfont.h"
35 #else
36 #include "action.h"
37 #include "checkwps.h"
38 #include "audio.h"
39 #define DEBUGF printf
40 #endif /*WPSEDITOR*/
41 #else
42 #include "debug.h"
43 #include "language.h"
44 #endif /*__PCTOOL__*/
46 #include <ctype.h>
47 #include <stdbool.h>
48 #include "font.h"
50 #include "wps_internals.h"
51 #include "skin_engine.h"
52 #include "settings.h"
53 #include "settings_list.h"
55 #ifdef HAVE_LCD_BITMAP
56 #include "bmp.h"
57 #endif
59 #ifdef HAVE_ALBUMART
60 #include "playback.h"
61 #endif
63 #include "backdrop.h"
65 #define WPS_ERROR_INVALID_PARAM -1
67 /* level of current conditional.
68 -1 means we're not in a conditional. */
69 static int level = -1;
71 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
72 or WPS_TOKEN_CONDITIONAL_START in current level */
73 static int lastcond[WPS_MAX_COND_LEVEL];
75 /* index of the WPS_TOKEN_CONDITIONAL in current level */
76 static int condindex[WPS_MAX_COND_LEVEL];
78 /* number of condtional options in current level */
79 static int numoptions[WPS_MAX_COND_LEVEL];
81 /* line number, debug only */
82 static int line_number;
84 /* the current viewport */
85 static struct skin_viewport *curr_vp;
86 /* the current line, linked to the above viewport */
87 static struct skin_line *curr_line;
89 #ifdef HAVE_LCD_BITMAP
91 #if LCD_DEPTH > 1
92 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
93 #else
94 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
95 #endif
97 #define PROGRESSBAR_BMP MAX_IMAGES
98 #define BACKDROP_BMP (MAX_BITMAPS-1)
100 /* pointers to the bitmap filenames in the WPS source */
101 static const char *bmp_names[MAX_BITMAPS];
103 #endif /* HAVE_LCD_BITMAP */
105 #if defined(DEBUG) || defined(SIMULATOR)
106 /* debugging function */
107 extern void print_debug_info(struct wps_data *data, int fail, int line);
108 extern void debug_skin_usage(void);
109 #endif
111 /* Function for parsing of details for a token. At the moment the
112 function is called, the token type has already been set. The
113 function must fill in the details and possibly add more tokens
114 to the token array. It should return the number of chars that
115 has been consumed.
117 wps_bufptr points to the char following the tag (i.e. where
118 details begin).
119 token is the pointer to the 'main' token being parsed
121 typedef int (*wps_tag_parse_func)(const char *wps_bufptr,
122 struct wps_token *token, struct wps_data *wps_data);
124 struct wps_tag {
125 enum wps_token_type type;
126 const char name[3];
127 unsigned char refresh_type;
128 const wps_tag_parse_func parse_func;
130 static int skip_end_of_line(const char *wps_bufptr);
131 /* prototypes of all special parse functions : */
132 static int parse_timeout(const char *wps_bufptr,
133 struct wps_token *token, struct wps_data *wps_data);
134 static int parse_progressbar(const char *wps_bufptr,
135 struct wps_token *token, struct wps_data *wps_data);
136 static int parse_dir_level(const char *wps_bufptr,
137 struct wps_token *token, struct wps_data *wps_data);
138 static int parse_setting_and_lang(const char *wps_bufptr,
139 struct wps_token *token, struct wps_data *wps_data);
141 #ifdef HAVE_LCD_BITMAP
142 static int parse_viewport_display(const char *wps_bufptr,
143 struct wps_token *token, struct wps_data *wps_data);
144 static int parse_viewport(const char *wps_bufptr,
145 struct wps_token *token, struct wps_data *wps_data);
146 static int parse_statusbar_enable(const char *wps_bufptr,
147 struct wps_token *token, struct wps_data *wps_data);
148 static int parse_statusbar_disable(const char *wps_bufptr,
149 struct wps_token *token, struct wps_data *wps_data);
150 static int parse_image_display(const char *wps_bufptr,
151 struct wps_token *token, struct wps_data *wps_data);
152 static int parse_image_load(const char *wps_bufptr,
153 struct wps_token *token, struct wps_data *wps_data);
154 #endif /*HAVE_LCD_BITMAP */
155 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
156 static int parse_image_special(const char *wps_bufptr,
157 struct wps_token *token, struct wps_data *wps_data);
158 #endif
159 #ifdef HAVE_ALBUMART
160 static int parse_albumart_load(const char *wps_bufptr,
161 struct wps_token *token, struct wps_data *wps_data);
162 static int parse_albumart_display(const char *wps_bufptr,
163 struct wps_token *token, struct wps_data *wps_data);
164 #endif /* HAVE_ALBUMART */
165 #ifdef HAVE_TOUCHSCREEN
166 static int parse_touchregion(const char *wps_bufptr,
167 struct wps_token *token, struct wps_data *wps_data);
168 #else
169 static int fulline_tag_not_supported(const char *wps_bufptr,
170 struct wps_token *token, struct wps_data *wps_data)
172 (void)token; (void)wps_data;
173 return skip_end_of_line(wps_bufptr);
175 #define parse_touchregion fulline_tag_not_supported
176 #endif
177 #ifdef CONFIG_RTC
178 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
179 #else
180 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
181 #endif
183 /* array of available tags - those with more characters have to go first
184 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
185 static const struct wps_tag all_tags[] = {
187 { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL },
188 { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL },
189 { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL },
191 { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
192 { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL },
193 { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL },
194 { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL },
195 #if CONFIG_CHARGING >= CHARGING_MONITOR
196 { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL },
197 #endif
198 #if CONFIG_CHARGING
199 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL },
200 #endif
202 { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL },
203 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
204 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
216 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
217 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
218 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
219 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
220 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
222 /* current file */
223 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
224 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
233 parse_dir_level },
235 /* next file */
236 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
237 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
246 parse_dir_level },
248 /* current metadata */
249 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
250 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
262 /* next metadata */
263 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
264 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
276 #if (CONFIG_CODEC != MAS3507D)
277 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
278 #endif
279 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
280 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
281 #endif
283 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
285 #ifdef HAS_REMOTE_BUTTON_HOLD
286 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
287 #else
288 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
289 #endif
291 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
292 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
293 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
294 parse_timeout },
296 #ifdef HAVE_LCD_BITMAP
297 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
298 #else
299 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
300 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
301 #endif
302 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
303 parse_progressbar },
305 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
307 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
308 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
309 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
312 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
313 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
314 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
315 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
317 #ifdef HAVE_TAGCACHE
318 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
319 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
320 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
321 #endif
323 #if CONFIG_CODEC == SWCODEC
324 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
325 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
326 #endif
328 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
329 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
331 #ifdef HAVE_LCD_BITMAP
332 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
333 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
335 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
337 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
338 parse_image_display },
340 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
341 #ifdef HAVE_ALBUMART
342 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
343 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
344 #endif
346 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
347 parse_viewport_display },
348 { WPS_NO_TOKEN, "V", 0, parse_viewport },
350 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
351 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
352 #endif
353 #endif
355 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
356 parse_setting_and_lang },
357 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
358 parse_setting_and_lang },
360 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
361 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
362 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
364 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
365 /* the array MUST end with an empty string (first char is \0) */
369 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
370 * chains require the order to be kept.
372 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
374 if (*list == NULL)
375 *list = item;
376 else
378 struct skin_token_list *t = *list;
379 while (t->next)
380 t = t->next;
381 t->next = item;
385 /* traverse the image linked-list for an image */
386 #ifdef HAVE_LCD_BITMAP
387 struct gui_img* find_image(char label, struct wps_data *data)
389 struct skin_token_list *list = data->images;
390 while (list)
392 struct gui_img *img = (struct gui_img *)list->token->value.data;
393 if (img->label == label)
394 return img;
395 list = list->next;
397 return NULL;
399 #endif
401 /* traverse the viewport linked list for a viewport */
402 struct skin_viewport* find_viewport(char label, struct wps_data *data)
404 struct skin_token_list *list = data->viewports;
405 while (list)
407 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
408 if (vp->label == label)
409 return vp;
410 list = list->next;
412 return NULL;
416 /* create and init a new wpsll item.
417 * passing NULL to token will alloc a new one.
418 * You should only pass NULL for the token when the token type (table above)
419 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
421 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
422 void* token_data)
424 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
425 if (!token)
426 token = skin_buffer_alloc(sizeof(struct wps_token));
427 if (!llitem || !token)
428 return NULL;
429 llitem->next = NULL;
430 llitem->token = token;
431 if (token_data)
432 llitem->token->value.data = token_data;
433 return llitem;
436 /* Returns the number of chars that should be skipped to jump
437 immediately after the first eol, i.e. to the start of the next line */
438 static int skip_end_of_line(const char *wps_bufptr)
440 line_number++;
441 int skip = 0;
442 while(*(wps_bufptr + skip) != '\n')
443 skip++;
444 return ++skip;
447 /* Starts a new subline in the current line during parsing */
448 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
450 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
451 if (!subline)
452 return false;
454 subline->first_token_idx = curr_token;
455 subline->next = NULL;
457 subline->line_type = 0;
458 subline->time_mult = 0;
460 line->curr_subline->last_token_idx = curr_token-1;
461 line->curr_subline->next = subline;
462 line->curr_subline = subline;
463 return true;
466 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
468 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
469 struct skin_subline *subline = NULL;
470 if (!line)
471 return false;
473 /* init the subline */
474 subline = &line->sublines;
475 subline->first_token_idx = curr_token;
476 subline->next = NULL;
477 subline->line_type = 0;
478 subline->time_mult = 0;
480 /* init the new line */
481 line->curr_subline = &line->sublines;
482 line->next = NULL;
483 line->subline_expire_time = 0;
485 /* connect to curr_line and vp pointers.
486 * 1) close the previous lines subline
487 * 2) connect to vp pointer
488 * 3) connect to curr_line global pointer
490 if (curr_line)
492 curr_line->curr_subline->last_token_idx = curr_token - 1;
493 curr_line->next = line;
494 curr_line->curr_subline = NULL;
496 curr_line = line;
497 if (!vp->lines)
498 vp->lines = line;
499 line_number++;
500 return true;
503 #ifdef HAVE_LCD_BITMAP
505 static int parse_statusbar_enable(const char *wps_bufptr,
506 struct wps_token *token,
507 struct wps_data *wps_data)
509 (void)token; /* Kill warnings */
510 wps_data->wps_sb_tag = true;
511 wps_data->show_sb_on_wps = true;
512 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
513 if (default_vp->vp.y == 0)
515 default_vp->vp.y = STATUSBAR_HEIGHT;
516 default_vp->vp.height -= STATUSBAR_HEIGHT;
518 return skip_end_of_line(wps_bufptr);
521 static int parse_statusbar_disable(const char *wps_bufptr,
522 struct wps_token *token,
523 struct wps_data *wps_data)
525 (void)token; /* Kill warnings */
526 wps_data->wps_sb_tag = true;
527 wps_data->show_sb_on_wps = false;
528 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
529 if (default_vp->vp.y == STATUSBAR_HEIGHT)
531 default_vp->vp.y = 0;
532 default_vp->vp.height += STATUSBAR_HEIGHT;
534 return skip_end_of_line(wps_bufptr);
537 static int get_image_id(int c)
539 if(c >= 'a' && c <= 'z')
540 return c - 'a';
541 else if(c >= 'A' && c <= 'Z')
542 return c - 'A' + 26;
543 else
544 return -1;
547 static char *get_image_filename(const char *start, const char* bmpdir,
548 char *buf, int buf_size)
550 const char *end = strchr(start, '|');
552 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
554 buf = "\0";
555 return NULL;
558 int bmpdirlen = strlen(bmpdir);
560 strcpy(buf, bmpdir);
561 buf[bmpdirlen] = '/';
562 memcpy( &buf[bmpdirlen + 1], start, end - start);
563 buf[bmpdirlen + 1 + end - start] = 0;
565 return buf;
568 static int parse_image_display(const char *wps_bufptr,
569 struct wps_token *token,
570 struct wps_data *wps_data)
572 char label = wps_bufptr[0];
573 int subimage;
574 struct gui_img *img;;
576 /* sanity check */
577 img = find_image(label, wps_data);
578 if (!img)
580 token->value.i = label; /* so debug works */
581 return WPS_ERROR_INVALID_PARAM;
584 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
586 if (subimage >= img->num_subimages)
587 return WPS_ERROR_INVALID_PARAM;
589 /* Store sub-image number to display in high bits */
590 token->value.i = label | (subimage << 8);
591 return 2; /* We have consumed 2 bytes */
592 } else {
593 token->value.i = label;
594 return 1; /* We have consumed 1 byte */
598 static int parse_image_load(const char *wps_bufptr,
599 struct wps_token *token,
600 struct wps_data *wps_data)
602 const char *ptr = wps_bufptr;
603 const char *pos;
604 const char* filename;
605 const char* id;
606 const char *newline;
607 int x,y;
608 struct gui_img *img;
610 /* format: %x|n|filename.bmp|x|y|
611 or %xl|n|filename.bmp|x|y|
612 or %xl|n|filename.bmp|x|y|num_subimages|
615 if (*ptr != '|')
616 return WPS_ERROR_INVALID_PARAM;
618 ptr++;
620 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
621 return WPS_ERROR_INVALID_PARAM;
623 /* Check there is a terminating | */
624 if (*ptr != '|')
625 return WPS_ERROR_INVALID_PARAM;
627 /* check the image number and load state */
628 if(find_image(*id, wps_data))
630 /* Invalid image ID */
631 return WPS_ERROR_INVALID_PARAM;
633 img = skin_buffer_alloc(sizeof(struct gui_img));
634 if (!img)
635 return WPS_ERROR_INVALID_PARAM;
636 /* save a pointer to the filename */
637 img->bm.data = (char*)filename;
638 img->label = *id;
639 img->x = x;
640 img->y = y;
641 img->num_subimages = 1;
642 img->always_display = false;
644 /* save current viewport */
645 img->vp = &curr_vp->vp;
647 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
649 img->always_display = true;
651 else
653 /* Parse the (optional) number of sub-images */
654 ptr++;
655 newline = strchr(ptr, '\n');
656 pos = strchr(ptr, '|');
657 if (pos && pos < newline)
658 img->num_subimages = atoi(ptr);
660 if (img->num_subimages <= 0)
661 return WPS_ERROR_INVALID_PARAM;
663 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
664 if (!item)
665 return WPS_ERROR_INVALID_PARAM;
666 add_to_ll_chain(&wps_data->images, item);
668 /* Skip the rest of the line */
669 return skip_end_of_line(wps_bufptr);
672 static int parse_viewport_display(const char *wps_bufptr,
673 struct wps_token *token,
674 struct wps_data *wps_data)
676 (void)wps_data;
677 char letter = wps_bufptr[0];
679 if (letter < 'a' || letter > 'z')
681 /* invalid viewport tag */
682 return WPS_ERROR_INVALID_PARAM;
684 token->value.i = letter;
685 return 1;
688 static int parse_viewport(const char *wps_bufptr,
689 struct wps_token *token,
690 struct wps_data *wps_data)
692 (void)token; /* Kill warnings */
693 const char *ptr = wps_bufptr;
695 const int screen =
696 #ifdef HAVE_REMOTE_LCD
697 wps_data->remote_wps ? SCREEN_REMOTE :
698 #endif
699 SCREEN_MAIN;
701 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
703 /* check for the optional letter to signify its a hideable viewport */
704 /* %Vl|<label>|<rest of tags>| */
705 skin_vp->hidden_flags = 0;
706 skin_vp->label = VP_NO_LABEL;
707 skin_vp->pb = NULL;
708 skin_vp->lines = NULL;
709 if (curr_line)
711 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
712 - (wps_data->num_tokens > 0 ? 1 : 0);
715 curr_line = NULL;
716 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
717 return WPS_ERROR_INVALID_PARAM;
720 if (*ptr == 'i')
722 skin_vp->label = VP_INFO_LABEL;
723 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
724 ++ptr;
726 else if (*ptr == 'l')
728 if (*(ptr+1) == '|')
730 char label = *(ptr+2);
731 if (label >= 'a' && label <= 'z')
733 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
734 skin_vp->label = label;
736 else
737 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
738 ptr += 3;
741 if (*ptr != '|')
742 return WPS_ERROR_INVALID_PARAM;
744 ptr++;
745 struct viewport *vp = &skin_vp->vp;
746 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
748 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
749 return WPS_ERROR_INVALID_PARAM;
751 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
752 /* Check for trailing | */
753 if (*ptr != '|')
754 return WPS_ERROR_INVALID_PARAM;
757 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
758 if (!list)
759 return WPS_ERROR_INVALID_PARAM;
760 add_to_ll_chain(&wps_data->viewports, list);
761 curr_vp = skin_vp;
762 /* Skip the rest of the line */
763 return skip_end_of_line(wps_bufptr);
766 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
767 static int parse_image_special(const char *wps_bufptr,
768 struct wps_token *token,
769 struct wps_data *wps_data)
771 (void)wps_data; /* kill warning */
772 (void)token;
773 const char *pos = NULL;
774 const char *newline;
776 pos = strchr(wps_bufptr + 1, '|');
777 newline = strchr(wps_bufptr, '\n');
779 if (pos > newline)
780 return WPS_ERROR_INVALID_PARAM;
781 #if LCD_DEPTH > 1
782 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
784 /* format: %X|filename.bmp| */
785 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
787 #endif
789 /* Skip the rest of the line */
790 return skip_end_of_line(wps_bufptr);
792 #endif
794 #endif /* HAVE_LCD_BITMAP */
796 static int parse_setting_and_lang(const char *wps_bufptr,
797 struct wps_token *token,
798 struct wps_data *wps_data)
800 /* NOTE: both the string validations that happen in here will
801 * automatically PASS on checkwps because its too hard to get
802 * settings_list.c and englinsh.lang built for it.
803 * If that ever changes remove the #ifndef __PCTOOL__'s here
805 (void)wps_data;
806 const char *ptr = wps_bufptr;
807 const char *end;
808 int i = 0;
809 char temp[64];
811 /* Find the setting's cfg_name */
812 if (*ptr != '|')
813 return WPS_ERROR_INVALID_PARAM;
814 ptr++;
815 end = strchr(ptr,'|');
816 if (!end)
817 return WPS_ERROR_INVALID_PARAM;
818 strlcpy(temp, ptr,end-ptr+1);
820 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
822 #ifndef __PCTOOL__
823 i = lang_english_to_id(temp);
824 if (i < 0)
825 return WPS_ERROR_INVALID_PARAM;
826 #endif
828 else
830 /* Find the setting */
831 for (i=0; i<nb_settings; i++)
832 if (settings[i].cfg_name &&
833 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
834 /* prevent matches on cfg_name prefixes */
835 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
836 break;
837 #ifndef __PCTOOL__
838 if (i == nb_settings)
839 return WPS_ERROR_INVALID_PARAM;
840 #endif
842 /* Store the setting number */
843 token->value.i = i;
845 /* Skip the rest of the line */
846 return end-ptr+2;
850 static int parse_dir_level(const char *wps_bufptr,
851 struct wps_token *token,
852 struct wps_data *wps_data)
854 char val[] = { *wps_bufptr, '\0' };
855 token->value.i = atoi(val);
856 (void)wps_data; /* Kill warnings */
857 return 1;
860 static int parse_timeout(const char *wps_bufptr,
861 struct wps_token *token,
862 struct wps_data *wps_data)
864 int skip = 0;
865 int val = 0;
866 bool have_point = false;
867 bool have_tenth = false;
869 (void)wps_data; /* Kill the warning */
871 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
873 if (*wps_bufptr != '.')
875 val *= 10;
876 val += *wps_bufptr - '0';
877 if (have_point)
879 have_tenth = true;
880 wps_bufptr++;
881 skip++;
882 break;
885 else
886 have_point = true;
888 wps_bufptr++;
889 skip++;
892 if (have_tenth == false)
893 val *= 10;
895 if (val == 0 && skip == 0)
897 /* decide what to do if no value was specified */
898 switch (token->type)
900 case WPS_TOKEN_SUBLINE_TIMEOUT:
901 return -1;
902 case WPS_TOKEN_BUTTON_VOLUME:
903 val = 10;
904 break;
907 token->value.i = val;
909 return skip;
912 static int parse_progressbar(const char *wps_bufptr,
913 struct wps_token *token,
914 struct wps_data *wps_data)
916 /* %pb or %pb|filename|x|y|width|height|
917 using - for any of the params uses "sane" values */
918 #ifdef HAVE_LCD_BITMAP
919 enum {
920 PB_FILENAME = 0,
921 PB_X,
922 PB_Y,
923 PB_WIDTH,
924 PB_HEIGHT
926 const char *filename;
927 int x, y, height, width;
928 uint32_t set = 0;
929 const char *ptr = wps_bufptr;
930 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
931 struct skin_token_list *item = new_skin_token_list_item(token, pb);
933 if (!pb || !item)
934 return WPS_ERROR_INVALID_PARAM;
936 struct viewport *vp = &curr_vp->vp;
937 #ifndef __PCTOOL__
938 int font_height = font_get(vp->font)->height;
939 #else
940 int font_height = 8;
941 #endif
942 /* we need to know what line number (viewport relative) this pb is,
943 * so count them... */
944 int line_num = -1;
945 struct skin_line *line = curr_vp->lines;
946 while (line)
948 line_num++;
949 line = line->next;
951 pb->have_bitmap_pb = false;
952 pb->bm.data = NULL; /* no bitmap specified */
954 if (*wps_bufptr != '|') /* regular old style */
956 pb->x = 0;
957 pb->width = vp->width;
958 pb->height = SYSFONT_HEIGHT-2;
959 pb->y = -line_num - 1; /* Will be computed during the rendering */
961 curr_vp->pb = pb;
962 add_to_ll_chain(&wps_data->progressbars, item);
963 return 0;
965 ptr = wps_bufptr + 1;
967 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
968 &x, &y, &width, &height)))
969 return WPS_ERROR_INVALID_PARAM;
971 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
972 pb->bm.data = (char*)filename;
974 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
975 pb->x = x;
976 else
977 pb->x = vp->x;
979 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
981 /* A zero width causes a divide-by-zero error later, so reject it */
982 if (width == 0)
983 return WPS_ERROR_INVALID_PARAM;
985 pb->width = width;
987 else
988 pb->width = vp->width - pb->x;
990 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
992 /* A zero height makes no sense - reject it */
993 if (height == 0)
994 return WPS_ERROR_INVALID_PARAM;
996 pb->height = height;
998 else
999 pb->height = font_height;
1001 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1002 pb->y = y;
1003 else
1004 pb->y = -line_num - 1; /* Will be computed during the rendering */
1006 curr_vp->pb = pb;
1007 add_to_ll_chain(&wps_data->progressbars, item);
1009 /* Skip the rest of the line */
1010 return skip_end_of_line(wps_bufptr)-1;
1011 #else
1012 (void)token;
1014 if (*(wps_bufptr-1) == 'f')
1015 wps_data->full_line_progressbar = true;
1016 else
1017 wps_data->full_line_progressbar = false;
1019 return 0;
1021 #endif
1024 #ifdef HAVE_ALBUMART
1025 static int parse_albumart_load(const char *wps_bufptr,
1026 struct wps_token *token,
1027 struct wps_data *wps_data)
1029 const char *_pos, *newline;
1030 bool parsing;
1031 struct dim dimensions;
1032 int albumart_slot;
1033 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1034 (void)token; /* silence warning */
1035 if (!aa)
1036 return skip_end_of_line(wps_bufptr);
1038 /* reset albumart info in wps */
1039 aa->width = -1;
1040 aa->height = -1;
1041 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1042 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1043 aa->vp = &curr_vp->vp;
1045 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1047 newline = strchr(wps_bufptr, '\n');
1049 /* initial validation and parsing of x and y components */
1050 if (*wps_bufptr != '|')
1051 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1053 _pos = wps_bufptr + 1;
1054 _pos = parse_list("dd", NULL, '|', _pos, &aa->x, &aa->y);
1056 if (!_pos || _pos > newline || *_pos != '|')
1057 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1058 e.g. %Cl|7|59\n */
1060 /* parsing width field */
1061 parsing = true;
1062 while (parsing)
1064 /* apply each modifier in turn */
1065 ++_pos;
1066 switch (*_pos)
1068 case 'l':
1069 case 'L':
1070 case '+':
1071 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1072 break;
1073 case 'c':
1074 case 'C':
1075 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1076 break;
1077 case 'r':
1078 case 'R':
1079 case '-':
1080 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1081 break;
1082 case 'd':
1083 case 'D':
1084 case 'i':
1085 case 'I':
1086 case 's':
1087 case 'S':
1088 /* simply ignored */
1089 break;
1090 default:
1091 parsing = false;
1092 break;
1095 /* extract max width data */
1096 if (*_pos != '|')
1098 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1099 return WPS_ERROR_INVALID_PARAM;
1101 aa->width = atoi(_pos);
1103 _pos = strchr(_pos, '|');
1104 if (!_pos || _pos > newline)
1105 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1106 e.g. %Cl|7|59|200\n */
1109 /* parsing height field */
1110 parsing = true;
1111 while (parsing)
1113 /* apply each modifier in turn */
1114 ++_pos;
1115 switch (*_pos)
1117 case 't':
1118 case 'T':
1119 case '-':
1120 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1121 break;
1122 case 'c':
1123 case 'C':
1124 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1125 break;
1126 case 'b':
1127 case 'B':
1128 case '+':
1129 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1130 break;
1131 case 'd':
1132 case 'D':
1133 case 'i':
1134 case 'I':
1135 case 's':
1136 case 'S':
1137 /* simply ignored */
1138 break;
1139 default:
1140 parsing = false;
1141 break;
1144 /* extract max height data */
1145 if (*_pos != '|')
1147 if (!isdigit(*_pos))
1148 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1150 aa->height = atoi(_pos);
1152 _pos = strchr(_pos, '|');
1153 if (!_pos || _pos > newline)
1154 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1155 e.g. %Cl|7|59|200|200\n */
1158 /* if we got here, we parsed everything ok .. ! */
1159 if (aa->width < 0)
1160 aa->width = 0;
1161 else if (aa->width > LCD_WIDTH)
1162 aa->width = LCD_WIDTH;
1164 if (aa->height < 0)
1165 aa->height = 0;
1166 else if (aa->height > LCD_HEIGHT)
1167 aa->height = LCD_HEIGHT;
1169 aa->state = WPS_ALBUMART_LOAD;
1170 aa->draw = false;
1171 wps_data->albumart = aa;
1173 dimensions.width = aa->width;
1174 dimensions.height = aa->height;
1176 albumart_slot = playback_claim_aa_slot(&dimensions);
1178 if (albumart_slot < 0) /* didn't get a slot ? */
1179 return skip_end_of_line(wps_bufptr);
1180 else
1181 wps_data->playback_aa_slot = albumart_slot;
1183 /* Skip the rest of the line */
1184 return skip_end_of_line(wps_bufptr);
1187 static int parse_albumart_display(const char *wps_bufptr,
1188 struct wps_token *token,
1189 struct wps_data *wps_data)
1191 (void)wps_bufptr;
1192 struct wps_token *prev = token-1;
1193 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1195 token->type = WPS_TOKEN_ALBUMART_FOUND;
1197 else if (wps_data->albumart)
1199 wps_data->albumart->vp = &curr_vp->vp;
1201 #if 0
1202 /* the old code did this so keep it here for now...
1203 * this is to allow the posibility to showing the next tracks AA! */
1204 if (wps_bufptr+1 == 'n')
1205 return 1;
1206 #endif
1207 return 0;
1209 #endif /* HAVE_ALBUMART */
1211 #ifdef HAVE_TOUCHSCREEN
1213 struct touchaction {char* s; int action;};
1214 static struct touchaction touchactions[] = {
1215 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1216 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1217 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1218 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1219 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1220 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1221 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1223 static int parse_touchregion(const char *wps_bufptr,
1224 struct wps_token *token, struct wps_data *wps_data)
1226 (void)token;
1227 unsigned i, imax;
1228 struct touchregion *region = NULL;
1229 const char *ptr = wps_bufptr;
1230 const char *action;
1231 const char pb_string[] = "progressbar";
1232 const char vol_string[] = "volume";
1233 int x,y,w,h;
1235 /* format: %T|x|y|width|height|action|
1236 * if action starts with & the area must be held to happen
1237 * action is one of:
1238 * play - play/pause playback
1239 * stop - stop playback, exit the wps
1240 * prev - prev track
1241 * next - next track
1242 * ffwd - seek forward
1243 * rwd - seek backwards
1244 * menu - go back to the main menu
1245 * browse - go back to the file/db browser
1246 * shuffle - toggle shuffle mode
1247 * repmode - cycle the repeat mode
1248 * quickscreen - go into the quickscreen
1249 * contextmenu - open the context menu
1250 * playlist - go into the playlist
1251 * pitch - go into the pitchscreen
1255 if (*ptr != '|')
1256 return WPS_ERROR_INVALID_PARAM;
1257 ptr++;
1259 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1260 return WPS_ERROR_INVALID_PARAM;
1262 /* Check there is a terminating | */
1263 if (*ptr != '|')
1264 return WPS_ERROR_INVALID_PARAM;
1266 region = skin_buffer_alloc(sizeof(struct touchregion));
1267 if (!region)
1268 return WPS_ERROR_INVALID_PARAM;
1270 /* should probably do some bounds checking here with the viewport... but later */
1271 region->action = ACTION_NONE;
1272 region->x = x;
1273 region->y = y;
1274 region->width = w;
1275 region->height = h;
1276 region->wvp = curr_vp;
1278 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1279 && *(action + sizeof(pb_string)-1) == '|')
1280 region->type = WPS_TOUCHREGION_SCROLLBAR;
1281 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1282 && *(action + sizeof(vol_string)-1) == '|')
1283 region->type = WPS_TOUCHREGION_VOLUME;
1284 else
1286 region->type = WPS_TOUCHREGION_ACTION;
1288 if (*action == '&')
1290 action++;
1291 region->repeat = true;
1293 else
1294 region->repeat = false;
1296 i = 0;
1297 imax = ARRAYLEN(touchactions);
1298 while ((region->action == ACTION_NONE) &&
1299 (i < imax))
1301 /* try to match with one of our touchregion screens */
1302 int len = strlen(touchactions[i].s);
1303 if (!strncmp(touchactions[i].s, action, len)
1304 && *(action+len) == '|')
1305 region->action = touchactions[i].action;
1306 i++;
1308 if (region->action == ACTION_NONE)
1309 return WPS_ERROR_INVALID_PARAM;
1311 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1312 if (!item)
1313 return WPS_ERROR_INVALID_PARAM;
1314 add_to_ll_chain(&wps_data->touchregions, item);
1315 return skip_end_of_line(wps_bufptr);
1317 #endif
1319 /* Parse a generic token from the given string. Return the length read */
1320 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1322 int skip = 0, taglen = 0, ret;
1323 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1324 const struct wps_tag *tag;
1325 memset(token, 0, sizeof(*token));
1327 switch(*wps_bufptr)
1330 case '%':
1331 case '<':
1332 case '|':
1333 case '>':
1334 case ';':
1335 case '#':
1336 /* escaped characters */
1337 token->type = WPS_TOKEN_CHARACTER;
1338 token->value.c = *wps_bufptr;
1339 taglen = 1;
1340 wps_data->num_tokens++;
1341 break;
1343 case '?':
1344 /* conditional tag */
1345 token->type = WPS_TOKEN_CONDITIONAL;
1346 level++;
1347 condindex[level] = wps_data->num_tokens;
1348 numoptions[level] = 1;
1349 wps_data->num_tokens++;
1350 ret = parse_token(wps_bufptr + 1, wps_data);
1351 if (ret < 0) return ret;
1352 taglen = 1 + ret;
1353 break;
1355 default:
1356 /* find what tag we have */
1357 for (tag = all_tags;
1358 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1359 tag++) ;
1361 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1362 token->type = tag->type;
1363 curr_line->curr_subline->line_type |= tag->refresh_type;
1365 /* if the tag has a special parsing function, we call it */
1366 if (tag->parse_func)
1368 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1369 if (ret < 0) return ret;
1370 skip += ret;
1373 /* Some tags we don't want to save as tokens */
1374 if (tag->type == WPS_NO_TOKEN)
1375 break;
1377 /* tags that start with 'F', 'I' or 'D' are for the next file */
1378 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1379 *(tag->name) == 'D')
1380 token->next = true;
1382 wps_data->num_tokens++;
1383 break;
1386 skip += taglen;
1387 return skip;
1390 /* Parses the WPS.
1391 data is the pointer to the structure where the parsed WPS should be stored.
1392 It is initialised.
1393 wps_bufptr points to the string containing the WPS tags */
1394 #define TOKEN_BLOCK_SIZE 128
1395 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1397 if (!data || !wps_bufptr || !*wps_bufptr)
1398 return false;
1399 enum wps_parse_error fail = PARSE_OK;
1400 int ret;
1401 int max_tokens = TOKEN_BLOCK_SIZE;
1402 size_t buf_free = 0;
1403 line_number = 1;
1404 level = -1;
1406 /* allocate enough RAM for a reasonable skin, grow as needed.
1407 * Free any used RAM before loading the images to be 100% RAM efficient */
1408 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1409 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1410 return false;
1411 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1412 data->num_tokens = 0;
1414 while (*wps_bufptr && !fail)
1416 /* first make sure there is enough room for tokens */
1417 if (max_tokens <= data->num_tokens + 5)
1419 int extra_tokens = TOKEN_BLOCK_SIZE;
1420 size_t needed = extra_tokens * sizeof(struct wps_token);
1421 /* do some smarts here to grow the array a bit */
1422 if (skin_buffer_freespace() < needed)
1424 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1425 break;
1427 skin_buffer_increment(needed, false);
1428 max_tokens += extra_tokens;
1431 switch(*wps_bufptr++)
1434 /* Regular tag */
1435 case '%':
1436 if ((ret = parse_token(wps_bufptr, data)) < 0)
1438 fail = PARSE_FAIL_COND_INVALID_PARAM;
1439 break;
1441 else if (level >= WPS_MAX_COND_LEVEL - 1)
1443 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1444 break;
1446 wps_bufptr += ret;
1447 break;
1449 /* Alternating sublines separator */
1450 case ';':
1451 if (level >= 0) /* there are unclosed conditionals */
1453 fail = PARSE_FAIL_UNCLOSED_COND;
1454 break;
1457 if (!skin_start_new_subline(curr_line, data->num_tokens))
1458 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1460 break;
1462 /* Conditional list start */
1463 case '<':
1464 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1466 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1467 break;
1470 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1471 lastcond[level] = data->num_tokens++;
1472 break;
1474 /* Conditional list end */
1475 case '>':
1476 if (level < 0) /* not in a conditional, invalid char */
1478 fail = PARSE_FAIL_INVALID_CHAR;
1479 break;
1482 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1483 if (lastcond[level])
1484 data->tokens[lastcond[level]].value.i = data->num_tokens;
1485 else
1487 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1488 break;
1491 lastcond[level] = 0;
1492 data->num_tokens++;
1493 data->tokens[condindex[level]].value.i = numoptions[level];
1494 level--;
1495 break;
1497 /* Conditional list option */
1498 case '|':
1499 if (level < 0) /* not in a conditional, invalid char */
1501 fail = PARSE_FAIL_INVALID_CHAR;
1502 break;
1505 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1506 if (lastcond[level])
1507 data->tokens[lastcond[level]].value.i = data->num_tokens;
1508 else
1510 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1511 break;
1514 lastcond[level] = data->num_tokens;
1515 numoptions[level]++;
1516 data->num_tokens++;
1517 break;
1519 /* Comment */
1520 case '#':
1521 if (level >= 0) /* there are unclosed conditionals */
1523 fail = PARSE_FAIL_UNCLOSED_COND;
1524 break;
1527 wps_bufptr += skip_end_of_line(wps_bufptr);
1528 break;
1530 /* End of this line */
1531 case '\n':
1532 if (level >= 0) /* there are unclosed conditionals */
1534 fail = PARSE_FAIL_UNCLOSED_COND;
1535 break;
1537 /* add a new token for the \n so empty lines are correct */
1538 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1539 data->tokens[data->num_tokens].value.c = '\n';
1540 data->tokens[data->num_tokens].next = false;
1541 data->num_tokens++;
1543 if (!skin_start_new_line(curr_vp, data->num_tokens))
1545 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1546 break;
1549 break;
1551 /* String */
1552 default:
1554 unsigned int len = 1;
1555 const char *string_start = wps_bufptr - 1;
1557 /* find the length of the string */
1558 while (*wps_bufptr && *wps_bufptr != '#' &&
1559 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1560 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1561 *wps_bufptr != '|' && *wps_bufptr != '\n')
1563 wps_bufptr++;
1564 len++;
1567 /* look if we already have that string */
1568 char *str;
1569 bool found = false;
1570 struct skin_token_list *list = data->strings;
1571 while (list)
1573 str = (char*)list->token->value.data;
1574 found = (strlen(str) == len &&
1575 strncmp(string_start, str, len) == 0);
1576 if (found)
1577 break; /* break here because the list item is
1578 used if its found */
1579 list = list->next;
1581 /* If a matching string is found, found is true and i is
1582 the index of the string. If not, found is false */
1584 if (!found)
1586 /* new string */
1587 str = (char*)skin_buffer_alloc(len+1);
1588 if (!str)
1590 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1591 break;
1593 strlcpy(str, string_start, len+1);
1594 struct skin_token_list *item =
1595 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1596 if(!item)
1598 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1599 break;
1601 add_to_ll_chain(&data->strings, item);
1603 else
1605 /* another occurrence of an existing string */
1606 data->tokens[data->num_tokens].value.data = list->token->value.data;
1608 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1609 data->num_tokens++;
1611 break;
1615 if (!fail && level >= 0) /* there are unclosed conditionals */
1616 fail = PARSE_FAIL_UNCLOSED_COND;
1618 if (*wps_bufptr && !fail)
1619 /* one of the limits of the while loop was exceeded */
1620 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1622 /* Success! */
1623 curr_line->curr_subline->last_token_idx = data->num_tokens;
1624 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1625 /* freeup unused tokens */
1626 skin_buffer_free_from_front(sizeof(struct wps_token)
1627 * (max_tokens - data->num_tokens));
1629 #if defined(DEBUG) || defined(SIMULATOR)
1630 if (debug)
1631 print_debug_info(data, fail, line_number);
1632 #else
1633 (void)debug;
1634 #endif
1636 return (fail == 0);
1641 * initial setup of wps_data; does reset everything
1642 * except fields which need to survive, i.e.
1644 * wps_data->remote_wps
1646 void skin_data_reset(struct wps_data *wps_data)
1648 #ifdef HAVE_LCD_BITMAP
1649 wps_data->images = NULL;
1650 wps_data->progressbars = NULL;
1651 #endif
1652 #ifdef HAVE_TOUCHSCREEN
1653 wps_data->touchregions = NULL;
1654 #endif
1655 wps_data->viewports = NULL;
1656 wps_data->strings = NULL;
1657 #ifdef HAVE_ALBUMART
1658 wps_data->albumart = NULL;
1659 if (wps_data->playback_aa_slot >= 0)
1661 playback_release_aa_slot(wps_data->playback_aa_slot);
1662 wps_data->playback_aa_slot = -1;
1664 #endif
1665 wps_data->tokens = NULL;
1666 wps_data->num_tokens = 0;
1668 #ifdef HAVE_LCD_BITMAP
1669 wps_data->peak_meter_enabled = false;
1670 wps_data->wps_sb_tag = false;
1671 wps_data->show_sb_on_wps = false;
1672 #else /* HAVE_LCD_CHARCELLS */
1673 /* progress bars */
1674 int i;
1675 for (i = 0; i < 8; i++)
1677 wps_data->wps_progress_pat[i] = 0;
1679 wps_data->full_line_progressbar = false;
1680 #endif
1681 wps_data->wps_loaded = false;
1684 #ifdef HAVE_LCD_BITMAP
1685 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1687 (void)wps_data; /* only needed for remote targets */
1688 bool loaded = false;
1689 char img_path[MAX_PATH];
1690 get_image_filename(bitmap->data, bmpdir,
1691 img_path, sizeof(img_path));
1693 /* load the image */
1694 int format;
1695 #ifdef HAVE_REMOTE_LCD
1696 if (wps_data->remote_wps)
1697 format = FORMAT_ANY|FORMAT_REMOTE;
1698 else
1699 #endif
1700 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1702 size_t max_buf;
1703 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1704 bitmap->data = imgbuf;
1705 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1707 if (ret > 0)
1709 skin_buffer_increment(ret, true);
1710 loaded = true;
1712 else
1714 /* Abort if we can't load an image */
1715 loaded = false;
1717 return loaded;
1720 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1722 struct skin_token_list *list;
1723 /* do the progressbars */
1724 list = wps_data->progressbars;
1725 while (list)
1727 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1728 if (pb->bm.data)
1730 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1732 list = list->next;
1734 /* regular images */
1735 list = wps_data->images;
1736 while (list)
1738 struct gui_img *img = (struct gui_img*)list->token->value.data;
1739 if (img->bm.data)
1741 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1742 if (img->loaded)
1743 img->subimage_height = img->bm.height / img->num_subimages;
1745 list = list->next;
1748 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1749 if (bmp_names[BACKDROP_BMP])
1751 int screen = SCREEN_MAIN;
1752 char img_path[MAX_PATH];
1753 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1754 img_path, sizeof(img_path));
1755 #if defined(HAVE_REMOTE_LCD)
1756 /* We only need to check LCD type if there is a remote LCD */
1757 if (wps_data->remote_wps)
1758 screen = SCREEN_REMOTE;
1759 #endif
1760 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1762 #endif /* has backdrop support */
1764 /* If we got here, everything was OK */
1765 return true;
1768 #endif /* HAVE_LCD_BITMAP */
1770 /* to setup up the wps-data from a format-buffer (isfile = false)
1771 from a (wps-)file (isfile = true)*/
1772 bool skin_data_load(struct wps_data *wps_data,
1773 const char *buf,
1774 bool isfile)
1777 if (!wps_data || !buf)
1778 return false;
1779 #ifdef HAVE_ALBUMART
1780 int status;
1781 struct mp3entry *curtrack;
1782 long offset;
1783 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1784 if (wps_data->albumart)
1786 old_aa.state = wps_data->albumart->state;
1787 old_aa.height = wps_data->albumart->height;
1788 old_aa.width = wps_data->albumart->width;
1790 #endif
1792 skin_data_reset(wps_data);
1794 /* alloc default viewport, will be fixed up later */
1795 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1796 if (!curr_vp)
1797 return false;
1798 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1799 if (!list)
1800 return false;
1801 add_to_ll_chain(&wps_data->viewports, list);
1804 /* Initialise the first (default) viewport */
1805 curr_vp->label = VP_DEFAULT_LABEL;
1806 curr_vp->pb = NULL;
1807 curr_vp->hidden_flags = 0;
1808 curr_vp->lines = NULL;
1810 curr_line = NULL;
1811 if (!skin_start_new_line(curr_vp, 0))
1812 return false;
1814 if (!isfile)
1816 return wps_parse(wps_data, buf, false);
1818 else
1820 int fd = open_utf8(buf, O_RDONLY);
1822 if (fd < 0)
1823 return false;
1825 /* get buffer space from the plugin buffer */
1826 size_t buffersize = 0;
1827 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1829 if (!wps_buffer)
1830 return false;
1832 /* copy the file's content to the buffer for parsing,
1833 ensuring that every line ends with a newline char. */
1834 unsigned int start = 0;
1835 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1837 start += strlen(wps_buffer + start);
1838 if (start < buffersize - 1)
1840 wps_buffer[start++] = '\n';
1841 wps_buffer[start] = 0;
1845 close(fd);
1847 if (start <= 0)
1848 return false;
1850 #ifdef HAVE_LCD_BITMAP
1851 /* Set all filename pointers to NULL */
1852 memset(bmp_names, 0, sizeof(bmp_names));
1853 #endif
1855 /* parse the WPS source */
1856 if (!wps_parse(wps_data, wps_buffer, true)) {
1857 skin_data_reset(wps_data);
1858 return false;
1861 wps_data->wps_loaded = true;
1863 #ifdef HAVE_LCD_BITMAP
1864 /* get the bitmap dir */
1865 char bmpdir[MAX_PATH];
1866 char *dot = strrchr(buf, '.');
1868 strlcpy(bmpdir, buf, dot - buf + 1);
1870 /* load the bitmaps that were found by the parsing */
1871 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1872 skin_data_reset(wps_data);
1873 return false;
1875 #endif
1876 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1877 status = audio_status();
1878 if (status & AUDIO_STATUS_PLAY)
1880 struct skin_albumart *aa = wps_data->albumart;
1881 if (aa && ((aa->state && !old_aa.state) ||
1882 (aa->state &&
1883 (((old_aa.height != aa->height) ||
1884 (old_aa.width != aa->width))))))
1886 curtrack = audio_current_track();
1887 offset = curtrack->offset;
1888 audio_stop();
1889 if (!(status & AUDIO_STATUS_PAUSE))
1890 audio_play(offset);
1893 #endif
1894 #if defined(DEBUG) || defined(SIMULATOR)
1895 debug_skin_usage();
1896 #endif
1897 return true;