new skin token: %cx - 24 hour time format enabled in the setting.. e.g %?cx<24 hour...
[kugel-rb.git] / apps / gui / skin_engine / skin_parser.c
blob26ccf473356a05543f53665e88a88806b119f202
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_24HR_SETTING_MODE, "cx", WPS_REFRESH_STATIC, NULL },
204 { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL },
205 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL },
206 { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL },
207 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL },
208 { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL },
209 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL },
210 { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL },
211 { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL },
212 { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL },
213 { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL },
214 { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL },
215 { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL },
216 { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL },
217 { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL },
218 { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL },
219 { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL },
220 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL },
221 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL },
223 /* current file */
224 { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL },
225 { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL },
226 { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL },
227 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL },
228 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL },
229 { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL },
230 { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL },
231 { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL },
232 { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL },
233 { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC,
234 parse_dir_level },
236 /* next file */
237 { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL },
238 { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL },
239 { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL },
240 { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL },
241 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL },
242 { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL },
243 { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL },
244 { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL },
245 { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL },
246 { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC,
247 parse_dir_level },
249 /* current metadata */
250 { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL },
251 { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL },
252 { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL },
253 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL },
254 { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL },
255 { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL },
256 { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL },
257 { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL },
258 { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL },
259 { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL },
260 { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL },
261 { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL },
263 /* next metadata */
264 { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL },
265 { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL },
266 { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL },
267 { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL },
268 { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL },
269 { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL },
270 { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL },
271 { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL },
272 { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL },
273 { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL },
274 { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL },
275 { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL },
277 #if (CONFIG_CODEC != MAS3507D)
278 { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL },
279 #endif
280 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
281 { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL },
282 #endif
284 { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL },
286 #ifdef HAS_REMOTE_BUTTON_HOLD
287 { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL },
288 #else
289 { WPS_TOKEN_UNKNOWN, "mr", 0, NULL },
290 #endif
292 { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL },
293 { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL },
294 { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC,
295 parse_timeout },
297 #ifdef HAVE_LCD_BITMAP
298 { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL },
299 #else
300 { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf",
301 WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar },
302 #endif
303 { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS,
304 parse_progressbar },
306 { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, NULL },
308 { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL },
309 { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL },
310 { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL },
311 { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL },
313 { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL },
314 { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL },
315 { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL },
316 { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL },
318 #ifdef HAVE_TAGCACHE
319 { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL },
320 { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL },
321 { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL },
322 #endif
324 #if CONFIG_CODEC == SWCODEC
325 { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL },
326 { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL },
327 #endif
329 { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL },
330 { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout },
332 #ifdef HAVE_LCD_BITMAP
333 { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable },
334 { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable },
336 { WPS_NO_TOKEN, "xl", 0, parse_image_load },
338 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC,
339 parse_image_display },
341 { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load },
342 #ifdef HAVE_ALBUMART
343 { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
344 { WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC, parse_albumart_display },
345 #endif
347 { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
348 parse_viewport_display },
349 { WPS_NO_TOKEN, "V", 0, parse_viewport },
351 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
352 { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special },
353 #endif
354 #endif
356 { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC,
357 parse_setting_and_lang },
358 { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC,
359 parse_setting_and_lang },
361 { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout },
362 { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL },
363 { WPS_NO_TOKEN, "T", 0, parse_touchregion },
365 { WPS_TOKEN_UNKNOWN, "", 0, NULL }
366 /* the array MUST end with an empty string (first char is \0) */
370 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
371 * chains require the order to be kept.
373 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
375 if (*list == NULL)
376 *list = item;
377 else
379 struct skin_token_list *t = *list;
380 while (t->next)
381 t = t->next;
382 t->next = item;
386 /* traverse the image linked-list for an image */
387 #ifdef HAVE_LCD_BITMAP
388 struct gui_img* find_image(char label, struct wps_data *data)
390 struct skin_token_list *list = data->images;
391 while (list)
393 struct gui_img *img = (struct gui_img *)list->token->value.data;
394 if (img->label == label)
395 return img;
396 list = list->next;
398 return NULL;
400 #endif
402 /* traverse the viewport linked list for a viewport */
403 struct skin_viewport* find_viewport(char label, struct wps_data *data)
405 struct skin_token_list *list = data->viewports;
406 while (list)
408 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
409 if (vp->label == label)
410 return vp;
411 list = list->next;
413 return NULL;
417 /* create and init a new wpsll item.
418 * passing NULL to token will alloc a new one.
419 * You should only pass NULL for the token when the token type (table above)
420 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
422 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
423 void* token_data)
425 struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list));
426 if (!token)
427 token = skin_buffer_alloc(sizeof(struct wps_token));
428 if (!llitem || !token)
429 return NULL;
430 llitem->next = NULL;
431 llitem->token = token;
432 if (token_data)
433 llitem->token->value.data = token_data;
434 return llitem;
437 /* Returns the number of chars that should be skipped to jump
438 immediately after the first eol, i.e. to the start of the next line */
439 static int skip_end_of_line(const char *wps_bufptr)
441 line_number++;
442 int skip = 0;
443 while(*(wps_bufptr + skip) != '\n')
444 skip++;
445 return ++skip;
448 /* Starts a new subline in the current line during parsing */
449 static bool skin_start_new_subline(struct skin_line *line, int curr_token)
451 struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline));
452 if (!subline)
453 return false;
455 subline->first_token_idx = curr_token;
456 subline->next = NULL;
458 subline->line_type = 0;
459 subline->time_mult = 0;
461 line->curr_subline->last_token_idx = curr_token-1;
462 line->curr_subline->next = subline;
463 line->curr_subline = subline;
464 return true;
467 static bool skin_start_new_line(struct skin_viewport *vp, int curr_token)
469 struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line));
470 struct skin_subline *subline = NULL;
471 if (!line)
472 return false;
474 /* init the subline */
475 subline = &line->sublines;
476 subline->first_token_idx = curr_token;
477 subline->next = NULL;
478 subline->line_type = 0;
479 subline->time_mult = 0;
481 /* init the new line */
482 line->curr_subline = &line->sublines;
483 line->next = NULL;
484 line->subline_expire_time = 0;
486 /* connect to curr_line and vp pointers.
487 * 1) close the previous lines subline
488 * 2) connect to vp pointer
489 * 3) connect to curr_line global pointer
491 if (curr_line)
493 curr_line->curr_subline->last_token_idx = curr_token - 1;
494 curr_line->next = line;
495 curr_line->curr_subline = NULL;
497 curr_line = line;
498 if (!vp->lines)
499 vp->lines = line;
500 line_number++;
501 return true;
504 #ifdef HAVE_LCD_BITMAP
506 static int parse_statusbar_enable(const char *wps_bufptr,
507 struct wps_token *token,
508 struct wps_data *wps_data)
510 (void)token; /* Kill warnings */
511 wps_data->wps_sb_tag = true;
512 wps_data->show_sb_on_wps = true;
513 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
514 if (default_vp->vp.y == 0)
516 default_vp->vp.y = STATUSBAR_HEIGHT;
517 default_vp->vp.height -= STATUSBAR_HEIGHT;
519 return skip_end_of_line(wps_bufptr);
522 static int parse_statusbar_disable(const char *wps_bufptr,
523 struct wps_token *token,
524 struct wps_data *wps_data)
526 (void)token; /* Kill warnings */
527 wps_data->wps_sb_tag = true;
528 wps_data->show_sb_on_wps = false;
529 struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data);
530 if (default_vp->vp.y == STATUSBAR_HEIGHT)
532 default_vp->vp.y = 0;
533 default_vp->vp.height += STATUSBAR_HEIGHT;
535 return skip_end_of_line(wps_bufptr);
538 static int get_image_id(int c)
540 if(c >= 'a' && c <= 'z')
541 return c - 'a';
542 else if(c >= 'A' && c <= 'Z')
543 return c - 'A' + 26;
544 else
545 return -1;
548 static char *get_image_filename(const char *start, const char* bmpdir,
549 char *buf, int buf_size)
551 const char *end = strchr(start, '|');
553 if ( !end || (end - start) >= (buf_size - (int)ROCKBOX_DIR_LEN - 2) )
555 buf = "\0";
556 return NULL;
559 int bmpdirlen = strlen(bmpdir);
561 strcpy(buf, bmpdir);
562 buf[bmpdirlen] = '/';
563 memcpy( &buf[bmpdirlen + 1], start, end - start);
564 buf[bmpdirlen + 1 + end - start] = 0;
566 return buf;
569 static int parse_image_display(const char *wps_bufptr,
570 struct wps_token *token,
571 struct wps_data *wps_data)
573 char label = wps_bufptr[0];
574 int subimage;
575 struct gui_img *img;;
577 /* sanity check */
578 img = find_image(label, wps_data);
579 if (!img)
581 token->value.i = label; /* so debug works */
582 return WPS_ERROR_INVALID_PARAM;
585 if ((subimage = get_image_id(wps_bufptr[1])) != -1)
587 if (subimage >= img->num_subimages)
588 return WPS_ERROR_INVALID_PARAM;
590 /* Store sub-image number to display in high bits */
591 token->value.i = label | (subimage << 8);
592 return 2; /* We have consumed 2 bytes */
593 } else {
594 token->value.i = label;
595 return 1; /* We have consumed 1 byte */
599 static int parse_image_load(const char *wps_bufptr,
600 struct wps_token *token,
601 struct wps_data *wps_data)
603 const char *ptr = wps_bufptr;
604 const char *pos;
605 const char* filename;
606 const char* id;
607 const char *newline;
608 int x,y;
609 struct gui_img *img;
611 /* format: %x|n|filename.bmp|x|y|
612 or %xl|n|filename.bmp|x|y|
613 or %xl|n|filename.bmp|x|y|num_subimages|
616 if (*ptr != '|')
617 return WPS_ERROR_INVALID_PARAM;
619 ptr++;
621 if (!(ptr = parse_list("ssdd", NULL, '|', ptr, &id, &filename, &x, &y)))
622 return WPS_ERROR_INVALID_PARAM;
624 /* Check there is a terminating | */
625 if (*ptr != '|')
626 return WPS_ERROR_INVALID_PARAM;
628 /* check the image number and load state */
629 if(find_image(*id, wps_data))
631 /* Invalid image ID */
632 return WPS_ERROR_INVALID_PARAM;
634 img = skin_buffer_alloc(sizeof(struct gui_img));
635 if (!img)
636 return WPS_ERROR_INVALID_PARAM;
637 /* save a pointer to the filename */
638 img->bm.data = (char*)filename;
639 img->label = *id;
640 img->x = x;
641 img->y = y;
642 img->num_subimages = 1;
643 img->always_display = false;
645 /* save current viewport */
646 img->vp = &curr_vp->vp;
648 if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
650 img->always_display = true;
652 else
654 /* Parse the (optional) number of sub-images */
655 ptr++;
656 newline = strchr(ptr, '\n');
657 pos = strchr(ptr, '|');
658 if (pos && pos < newline)
659 img->num_subimages = atoi(ptr);
661 if (img->num_subimages <= 0)
662 return WPS_ERROR_INVALID_PARAM;
664 struct skin_token_list *item = new_skin_token_list_item(NULL, img);
665 if (!item)
666 return WPS_ERROR_INVALID_PARAM;
667 add_to_ll_chain(&wps_data->images, item);
669 /* Skip the rest of the line */
670 return skip_end_of_line(wps_bufptr);
673 static int parse_viewport_display(const char *wps_bufptr,
674 struct wps_token *token,
675 struct wps_data *wps_data)
677 (void)wps_data;
678 char letter = wps_bufptr[0];
680 if (letter < 'a' || letter > 'z')
682 /* invalid viewport tag */
683 return WPS_ERROR_INVALID_PARAM;
685 token->value.i = letter;
686 return 1;
689 static int parse_viewport(const char *wps_bufptr,
690 struct wps_token *token,
691 struct wps_data *wps_data)
693 (void)token; /* Kill warnings */
694 const char *ptr = wps_bufptr;
696 const int screen =
697 #ifdef HAVE_REMOTE_LCD
698 wps_data->remote_wps ? SCREEN_REMOTE :
699 #endif
700 SCREEN_MAIN;
702 struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
704 /* check for the optional letter to signify its a hideable viewport */
705 /* %Vl|<label>|<rest of tags>| */
706 skin_vp->hidden_flags = 0;
707 skin_vp->label = VP_NO_LABEL;
708 skin_vp->pb = NULL;
709 skin_vp->lines = NULL;
710 if (curr_line)
712 curr_line->curr_subline->last_token_idx = wps_data->num_tokens
713 - (wps_data->num_tokens > 0 ? 1 : 0);
716 curr_line = NULL;
717 if (!skin_start_new_line(skin_vp, wps_data->num_tokens))
718 return WPS_ERROR_INVALID_PARAM;
721 if (*ptr == 'i')
723 skin_vp->label = VP_INFO_LABEL;
724 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
725 ++ptr;
727 else if (*ptr == 'l')
729 if (*(ptr+1) == '|')
731 char label = *(ptr+2);
732 if (label >= 'a' && label <= 'z')
734 skin_vp->hidden_flags = VP_DRAW_HIDEABLE;
735 skin_vp->label = label;
737 else
738 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
739 ptr += 3;
742 if (*ptr != '|')
743 return WPS_ERROR_INVALID_PARAM;
745 ptr++;
746 struct viewport *vp = &skin_vp->vp;
747 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
749 if (!(ptr = viewport_parse_viewport(vp, screen, ptr, '|')))
750 return WPS_ERROR_INVALID_PARAM;
752 vp->flags &= ~VP_FLAG_ALIGN_RIGHT; /* ignore right-to-left languages */
753 /* Check for trailing | */
754 if (*ptr != '|')
755 return WPS_ERROR_INVALID_PARAM;
758 struct skin_token_list *list = new_skin_token_list_item(NULL, skin_vp);
759 if (!list)
760 return WPS_ERROR_INVALID_PARAM;
761 add_to_ll_chain(&wps_data->viewports, list);
762 curr_vp = skin_vp;
763 /* Skip the rest of the line */
764 return skip_end_of_line(wps_bufptr);
767 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
768 static int parse_image_special(const char *wps_bufptr,
769 struct wps_token *token,
770 struct wps_data *wps_data)
772 (void)wps_data; /* kill warning */
773 (void)token;
774 const char *pos = NULL;
775 const char *newline;
777 pos = strchr(wps_bufptr + 1, '|');
778 newline = strchr(wps_bufptr, '\n');
780 if (pos > newline)
781 return WPS_ERROR_INVALID_PARAM;
782 #if LCD_DEPTH > 1
783 if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
785 /* format: %X|filename.bmp| */
786 bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
788 #endif
790 /* Skip the rest of the line */
791 return skip_end_of_line(wps_bufptr);
793 #endif
795 #endif /* HAVE_LCD_BITMAP */
797 static int parse_setting_and_lang(const char *wps_bufptr,
798 struct wps_token *token,
799 struct wps_data *wps_data)
801 /* NOTE: both the string validations that happen in here will
802 * automatically PASS on checkwps because its too hard to get
803 * settings_list.c and englinsh.lang built for it.
804 * If that ever changes remove the #ifndef __PCTOOL__'s here
806 (void)wps_data;
807 const char *ptr = wps_bufptr;
808 const char *end;
809 int i = 0;
810 char temp[64];
812 /* Find the setting's cfg_name */
813 if (*ptr != '|')
814 return WPS_ERROR_INVALID_PARAM;
815 ptr++;
816 end = strchr(ptr,'|');
817 if (!end)
818 return WPS_ERROR_INVALID_PARAM;
819 strlcpy(temp, ptr,end-ptr+1);
821 if (token->type == WPS_TOKEN_TRANSLATEDSTRING)
823 #ifndef __PCTOOL__
824 i = lang_english_to_id(temp);
825 if (i < 0)
826 return WPS_ERROR_INVALID_PARAM;
827 #endif
829 else
831 /* Find the setting */
832 for (i=0; i<nb_settings; i++)
833 if (settings[i].cfg_name &&
834 !strncmp(settings[i].cfg_name,ptr,end-ptr) &&
835 /* prevent matches on cfg_name prefixes */
836 strlen(settings[i].cfg_name)==(size_t)(end-ptr))
837 break;
838 #ifndef __PCTOOL__
839 if (i == nb_settings)
840 return WPS_ERROR_INVALID_PARAM;
841 #endif
843 /* Store the setting number */
844 token->value.i = i;
846 /* Skip the rest of the line */
847 return end-ptr+2;
851 static int parse_dir_level(const char *wps_bufptr,
852 struct wps_token *token,
853 struct wps_data *wps_data)
855 char val[] = { *wps_bufptr, '\0' };
856 token->value.i = atoi(val);
857 (void)wps_data; /* Kill warnings */
858 return 1;
861 static int parse_timeout(const char *wps_bufptr,
862 struct wps_token *token,
863 struct wps_data *wps_data)
865 int skip = 0;
866 int val = 0;
867 bool have_point = false;
868 bool have_tenth = false;
870 (void)wps_data; /* Kill the warning */
872 while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
874 if (*wps_bufptr != '.')
876 val *= 10;
877 val += *wps_bufptr - '0';
878 if (have_point)
880 have_tenth = true;
881 wps_bufptr++;
882 skip++;
883 break;
886 else
887 have_point = true;
889 wps_bufptr++;
890 skip++;
893 if (have_tenth == false)
894 val *= 10;
896 if (val == 0 && skip == 0)
898 /* decide what to do if no value was specified */
899 switch (token->type)
901 case WPS_TOKEN_SUBLINE_TIMEOUT:
902 return -1;
903 case WPS_TOKEN_BUTTON_VOLUME:
904 val = 10;
905 break;
908 token->value.i = val;
910 return skip;
913 static int parse_progressbar(const char *wps_bufptr,
914 struct wps_token *token,
915 struct wps_data *wps_data)
917 /* %pb or %pb|filename|x|y|width|height|
918 using - for any of the params uses "sane" values */
919 #ifdef HAVE_LCD_BITMAP
920 enum {
921 PB_FILENAME = 0,
922 PB_X,
923 PB_Y,
924 PB_WIDTH,
925 PB_HEIGHT
927 const char *filename;
928 int x, y, height, width;
929 uint32_t set = 0;
930 const char *ptr = wps_bufptr;
931 struct progressbar *pb = skin_buffer_alloc(sizeof(struct progressbar));
932 struct skin_token_list *item = new_skin_token_list_item(token, pb);
934 if (!pb || !item)
935 return WPS_ERROR_INVALID_PARAM;
937 struct viewport *vp = &curr_vp->vp;
938 #ifndef __PCTOOL__
939 int font_height = font_get(vp->font)->height;
940 #else
941 int font_height = 8;
942 #endif
943 /* we need to know what line number (viewport relative) this pb is,
944 * so count them... */
945 int line_num = -1;
946 struct skin_line *line = curr_vp->lines;
947 while (line)
949 line_num++;
950 line = line->next;
952 pb->have_bitmap_pb = false;
953 pb->bm.data = NULL; /* no bitmap specified */
955 if (*wps_bufptr != '|') /* regular old style */
957 pb->x = 0;
958 pb->width = vp->width;
959 pb->height = SYSFONT_HEIGHT-2;
960 pb->y = -line_num - 1; /* Will be computed during the rendering */
962 curr_vp->pb = pb;
963 add_to_ll_chain(&wps_data->progressbars, item);
964 return 0;
966 ptr = wps_bufptr + 1;
968 if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
969 &x, &y, &width, &height)))
970 return WPS_ERROR_INVALID_PARAM;
972 if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
973 pb->bm.data = (char*)filename;
975 if (LIST_VALUE_PARSED(set, PB_X)) /* x */
976 pb->x = x;
977 else
978 pb->x = vp->x;
980 if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
982 /* A zero width causes a divide-by-zero error later, so reject it */
983 if (width == 0)
984 return WPS_ERROR_INVALID_PARAM;
986 pb->width = width;
988 else
989 pb->width = vp->width - pb->x;
991 if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
993 /* A zero height makes no sense - reject it */
994 if (height == 0)
995 return WPS_ERROR_INVALID_PARAM;
997 pb->height = height;
999 else
1000 pb->height = font_height;
1002 if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
1003 pb->y = y;
1004 else
1005 pb->y = -line_num - 1; /* Will be computed during the rendering */
1007 curr_vp->pb = pb;
1008 add_to_ll_chain(&wps_data->progressbars, item);
1010 /* Skip the rest of the line */
1011 return skip_end_of_line(wps_bufptr)-1;
1012 #else
1013 (void)token;
1015 if (*(wps_bufptr-1) == 'f')
1016 wps_data->full_line_progressbar = true;
1017 else
1018 wps_data->full_line_progressbar = false;
1020 return 0;
1022 #endif
1025 #ifdef HAVE_ALBUMART
1026 static int parse_albumart_load(const char *wps_bufptr,
1027 struct wps_token *token,
1028 struct wps_data *wps_data)
1030 const char *_pos, *newline;
1031 bool parsing;
1032 struct dim dimensions;
1033 int albumart_slot;
1034 struct skin_albumart *aa = skin_buffer_alloc(sizeof(struct skin_albumart));
1035 (void)token; /* silence warning */
1036 if (!aa)
1037 return skip_end_of_line(wps_bufptr);
1039 /* reset albumart info in wps */
1040 aa->width = -1;
1041 aa->height = -1;
1042 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1043 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
1044 aa->vp = &curr_vp->vp;
1046 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
1048 newline = strchr(wps_bufptr, '\n');
1050 /* initial validation and parsing of x and y components */
1051 if (*wps_bufptr != '|')
1052 return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
1054 _pos = wps_bufptr + 1;
1055 _pos = parse_list("dd", NULL, '|', _pos, &aa->x, &aa->y);
1057 if (!_pos || _pos > newline || *_pos != '|')
1058 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
1059 e.g. %Cl|7|59\n */
1061 /* parsing width field */
1062 parsing = true;
1063 while (parsing)
1065 /* apply each modifier in turn */
1066 ++_pos;
1067 switch (*_pos)
1069 case 'l':
1070 case 'L':
1071 case '+':
1072 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1073 break;
1074 case 'c':
1075 case 'C':
1076 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1077 break;
1078 case 'r':
1079 case 'R':
1080 case '-':
1081 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1082 break;
1083 case 'd':
1084 case 'D':
1085 case 'i':
1086 case 'I':
1087 case 's':
1088 case 'S':
1089 /* simply ignored */
1090 break;
1091 default:
1092 parsing = false;
1093 break;
1096 /* extract max width data */
1097 if (*_pos != '|')
1099 if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
1100 return WPS_ERROR_INVALID_PARAM;
1102 aa->width = atoi(_pos);
1104 _pos = strchr(_pos, '|');
1105 if (!_pos || _pos > newline)
1106 return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
1107 e.g. %Cl|7|59|200\n */
1110 /* parsing height field */
1111 parsing = true;
1112 while (parsing)
1114 /* apply each modifier in turn */
1115 ++_pos;
1116 switch (*_pos)
1118 case 't':
1119 case 'T':
1120 case '-':
1121 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1122 break;
1123 case 'c':
1124 case 'C':
1125 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1126 break;
1127 case 'b':
1128 case 'B':
1129 case '+':
1130 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1131 break;
1132 case 'd':
1133 case 'D':
1134 case 'i':
1135 case 'I':
1136 case 's':
1137 case 'S':
1138 /* simply ignored */
1139 break;
1140 default:
1141 parsing = false;
1142 break;
1145 /* extract max height data */
1146 if (*_pos != '|')
1148 if (!isdigit(*_pos))
1149 return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
1151 aa->height = atoi(_pos);
1153 _pos = strchr(_pos, '|');
1154 if (!_pos || _pos > newline)
1155 return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
1156 e.g. %Cl|7|59|200|200\n */
1159 /* if we got here, we parsed everything ok .. ! */
1160 if (aa->width < 0)
1161 aa->width = 0;
1162 else if (aa->width > LCD_WIDTH)
1163 aa->width = LCD_WIDTH;
1165 if (aa->height < 0)
1166 aa->height = 0;
1167 else if (aa->height > LCD_HEIGHT)
1168 aa->height = LCD_HEIGHT;
1170 aa->state = WPS_ALBUMART_LOAD;
1171 aa->draw = false;
1172 wps_data->albumart = aa;
1174 dimensions.width = aa->width;
1175 dimensions.height = aa->height;
1177 albumart_slot = playback_claim_aa_slot(&dimensions);
1179 if (albumart_slot < 0) /* didn't get a slot ? */
1180 return skip_end_of_line(wps_bufptr);
1181 else
1182 wps_data->playback_aa_slot = albumart_slot;
1184 /* Skip the rest of the line */
1185 return skip_end_of_line(wps_bufptr);
1188 static int parse_albumart_display(const char *wps_bufptr,
1189 struct wps_token *token,
1190 struct wps_data *wps_data)
1192 (void)wps_bufptr;
1193 struct wps_token *prev = token-1;
1194 if ((wps_data->num_tokens >= 1) && (prev->type == WPS_TOKEN_CONDITIONAL))
1196 token->type = WPS_TOKEN_ALBUMART_FOUND;
1198 else if (wps_data->albumart)
1200 wps_data->albumart->vp = &curr_vp->vp;
1202 #if 0
1203 /* the old code did this so keep it here for now...
1204 * this is to allow the posibility to showing the next tracks AA! */
1205 if (wps_bufptr+1 == 'n')
1206 return 1;
1207 #endif
1208 return 0;
1210 #endif /* HAVE_ALBUMART */
1212 #ifdef HAVE_TOUCHSCREEN
1214 struct touchaction {char* s; int action;};
1215 static struct touchaction touchactions[] = {
1216 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1217 {"prev", ACTION_WPS_SKIPPREV }, {"next", ACTION_WPS_SKIPNEXT },
1218 {"ffwd", ACTION_WPS_SEEKFWD }, {"rwd", ACTION_WPS_SEEKBACK },
1219 {"menu", ACTION_WPS_MENU }, {"browse", ACTION_WPS_BROWSE },
1220 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1221 {"quickscreen", ACTION_WPS_QUICKSCREEN },{"contextmenu", ACTION_WPS_CONTEXT },
1222 {"playlist", ACTION_WPS_VIEW_PLAYLIST }, {"pitch", ACTION_WPS_PITCHSCREEN},
1224 static int parse_touchregion(const char *wps_bufptr,
1225 struct wps_token *token, struct wps_data *wps_data)
1227 (void)token;
1228 unsigned i, imax;
1229 struct touchregion *region = NULL;
1230 const char *ptr = wps_bufptr;
1231 const char *action;
1232 const char pb_string[] = "progressbar";
1233 const char vol_string[] = "volume";
1234 int x,y,w,h;
1236 /* format: %T|x|y|width|height|action|
1237 * if action starts with & the area must be held to happen
1238 * action is one of:
1239 * play - play/pause playback
1240 * stop - stop playback, exit the wps
1241 * prev - prev track
1242 * next - next track
1243 * ffwd - seek forward
1244 * rwd - seek backwards
1245 * menu - go back to the main menu
1246 * browse - go back to the file/db browser
1247 * shuffle - toggle shuffle mode
1248 * repmode - cycle the repeat mode
1249 * quickscreen - go into the quickscreen
1250 * contextmenu - open the context menu
1251 * playlist - go into the playlist
1252 * pitch - go into the pitchscreen
1256 if (*ptr != '|')
1257 return WPS_ERROR_INVALID_PARAM;
1258 ptr++;
1260 if (!(ptr = parse_list("dddds", NULL, '|', ptr, &x, &y, &w, &h, &action)))
1261 return WPS_ERROR_INVALID_PARAM;
1263 /* Check there is a terminating | */
1264 if (*ptr != '|')
1265 return WPS_ERROR_INVALID_PARAM;
1267 region = skin_buffer_alloc(sizeof(struct touchregion));
1268 if (!region)
1269 return WPS_ERROR_INVALID_PARAM;
1271 /* should probably do some bounds checking here with the viewport... but later */
1272 region->action = ACTION_NONE;
1273 region->x = x;
1274 region->y = y;
1275 region->width = w;
1276 region->height = h;
1277 region->wvp = curr_vp;
1279 if(!strncmp(pb_string, action, sizeof(pb_string)-1)
1280 && *(action + sizeof(pb_string)-1) == '|')
1281 region->type = WPS_TOUCHREGION_SCROLLBAR;
1282 else if(!strncmp(vol_string, action, sizeof(vol_string)-1)
1283 && *(action + sizeof(vol_string)-1) == '|')
1284 region->type = WPS_TOUCHREGION_VOLUME;
1285 else
1287 region->type = WPS_TOUCHREGION_ACTION;
1289 if (*action == '&')
1291 action++;
1292 region->repeat = true;
1294 else
1295 region->repeat = false;
1297 i = 0;
1298 imax = ARRAYLEN(touchactions);
1299 while ((region->action == ACTION_NONE) &&
1300 (i < imax))
1302 /* try to match with one of our touchregion screens */
1303 int len = strlen(touchactions[i].s);
1304 if (!strncmp(touchactions[i].s, action, len)
1305 && *(action+len) == '|')
1306 region->action = touchactions[i].action;
1307 i++;
1309 if (region->action == ACTION_NONE)
1310 return WPS_ERROR_INVALID_PARAM;
1312 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1313 if (!item)
1314 return WPS_ERROR_INVALID_PARAM;
1315 add_to_ll_chain(&wps_data->touchregions, item);
1316 return skip_end_of_line(wps_bufptr);
1318 #endif
1320 /* Parse a generic token from the given string. Return the length read */
1321 static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
1323 int skip = 0, taglen = 0, ret;
1324 struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
1325 const struct wps_tag *tag;
1326 memset(token, 0, sizeof(*token));
1328 switch(*wps_bufptr)
1331 case '%':
1332 case '<':
1333 case '|':
1334 case '>':
1335 case ';':
1336 case '#':
1337 /* escaped characters */
1338 token->type = WPS_TOKEN_CHARACTER;
1339 token->value.c = *wps_bufptr;
1340 taglen = 1;
1341 wps_data->num_tokens++;
1342 break;
1344 case '?':
1345 /* conditional tag */
1346 token->type = WPS_TOKEN_CONDITIONAL;
1347 level++;
1348 condindex[level] = wps_data->num_tokens;
1349 numoptions[level] = 1;
1350 wps_data->num_tokens++;
1351 ret = parse_token(wps_bufptr + 1, wps_data);
1352 if (ret < 0) return ret;
1353 taglen = 1 + ret;
1354 break;
1356 default:
1357 /* find what tag we have */
1358 for (tag = all_tags;
1359 strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
1360 tag++) ;
1362 taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
1363 token->type = tag->type;
1364 curr_line->curr_subline->line_type |= tag->refresh_type;
1366 /* if the tag has a special parsing function, we call it */
1367 if (tag->parse_func)
1369 ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
1370 if (ret < 0) return ret;
1371 skip += ret;
1374 /* Some tags we don't want to save as tokens */
1375 if (tag->type == WPS_NO_TOKEN)
1376 break;
1378 /* tags that start with 'F', 'I' or 'D' are for the next file */
1379 if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
1380 *(tag->name) == 'D')
1381 token->next = true;
1383 wps_data->num_tokens++;
1384 break;
1387 skip += taglen;
1388 return skip;
1391 /* Parses the WPS.
1392 data is the pointer to the structure where the parsed WPS should be stored.
1393 It is initialised.
1394 wps_bufptr points to the string containing the WPS tags */
1395 #define TOKEN_BLOCK_SIZE 128
1396 static bool wps_parse(struct wps_data *data, const char *wps_bufptr, bool debug)
1398 if (!data || !wps_bufptr || !*wps_bufptr)
1399 return false;
1400 enum wps_parse_error fail = PARSE_OK;
1401 int ret;
1402 int max_tokens = TOKEN_BLOCK_SIZE;
1403 size_t buf_free = 0;
1404 line_number = 1;
1405 level = -1;
1407 /* allocate enough RAM for a reasonable skin, grow as needed.
1408 * Free any used RAM before loading the images to be 100% RAM efficient */
1409 data->tokens = (struct wps_token *)skin_buffer_grab(&buf_free);
1410 if (sizeof(struct wps_token)*max_tokens >= buf_free)
1411 return false;
1412 skin_buffer_increment(max_tokens * sizeof(struct wps_token), false);
1413 data->num_tokens = 0;
1415 while (*wps_bufptr && !fail)
1417 /* first make sure there is enough room for tokens */
1418 if (max_tokens <= data->num_tokens + 5)
1420 int extra_tokens = TOKEN_BLOCK_SIZE;
1421 size_t needed = extra_tokens * sizeof(struct wps_token);
1422 /* do some smarts here to grow the array a bit */
1423 if (skin_buffer_freespace() < needed)
1425 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1426 break;
1428 skin_buffer_increment(needed, false);
1429 max_tokens += extra_tokens;
1432 switch(*wps_bufptr++)
1435 /* Regular tag */
1436 case '%':
1437 if ((ret = parse_token(wps_bufptr, data)) < 0)
1439 fail = PARSE_FAIL_COND_INVALID_PARAM;
1440 break;
1442 else if (level >= WPS_MAX_COND_LEVEL - 1)
1444 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1445 break;
1447 wps_bufptr += ret;
1448 break;
1450 /* Alternating sublines separator */
1451 case ';':
1452 if (level >= 0) /* there are unclosed conditionals */
1454 fail = PARSE_FAIL_UNCLOSED_COND;
1455 break;
1458 if (!skin_start_new_subline(curr_line, data->num_tokens))
1459 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1461 break;
1463 /* Conditional list start */
1464 case '<':
1465 if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
1467 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1468 break;
1471 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
1472 lastcond[level] = data->num_tokens++;
1473 break;
1475 /* Conditional list end */
1476 case '>':
1477 if (level < 0) /* not in a conditional, invalid char */
1479 fail = PARSE_FAIL_INVALID_CHAR;
1480 break;
1483 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
1484 if (lastcond[level])
1485 data->tokens[lastcond[level]].value.i = data->num_tokens;
1486 else
1488 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1489 break;
1492 lastcond[level] = 0;
1493 data->num_tokens++;
1494 data->tokens[condindex[level]].value.i = numoptions[level];
1495 level--;
1496 break;
1498 /* Conditional list option */
1499 case '|':
1500 if (level < 0) /* not in a conditional, invalid char */
1502 fail = PARSE_FAIL_INVALID_CHAR;
1503 break;
1506 data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
1507 if (lastcond[level])
1508 data->tokens[lastcond[level]].value.i = data->num_tokens;
1509 else
1511 fail = PARSE_FAIL_COND_SYNTAX_ERROR;
1512 break;
1515 lastcond[level] = data->num_tokens;
1516 numoptions[level]++;
1517 data->num_tokens++;
1518 break;
1520 /* Comment */
1521 case '#':
1522 if (level >= 0) /* there are unclosed conditionals */
1524 fail = PARSE_FAIL_UNCLOSED_COND;
1525 break;
1528 wps_bufptr += skip_end_of_line(wps_bufptr);
1529 break;
1531 /* End of this line */
1532 case '\n':
1533 if (level >= 0) /* there are unclosed conditionals */
1535 fail = PARSE_FAIL_UNCLOSED_COND;
1536 break;
1538 /* add a new token for the \n so empty lines are correct */
1539 data->tokens[data->num_tokens].type = WPS_TOKEN_CHARACTER;
1540 data->tokens[data->num_tokens].value.c = '\n';
1541 data->tokens[data->num_tokens].next = false;
1542 data->num_tokens++;
1544 if (!skin_start_new_line(curr_vp, data->num_tokens))
1546 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1547 break;
1550 break;
1552 /* String */
1553 default:
1555 unsigned int len = 1;
1556 const char *string_start = wps_bufptr - 1;
1558 /* find the length of the string */
1559 while (*wps_bufptr && *wps_bufptr != '#' &&
1560 *wps_bufptr != '%' && *wps_bufptr != ';' &&
1561 *wps_bufptr != '<' && *wps_bufptr != '>' &&
1562 *wps_bufptr != '|' && *wps_bufptr != '\n')
1564 wps_bufptr++;
1565 len++;
1568 /* look if we already have that string */
1569 char *str;
1570 bool found = false;
1571 struct skin_token_list *list = data->strings;
1572 while (list)
1574 str = (char*)list->token->value.data;
1575 found = (strlen(str) == len &&
1576 strncmp(string_start, str, len) == 0);
1577 if (found)
1578 break; /* break here because the list item is
1579 used if its found */
1580 list = list->next;
1582 /* If a matching string is found, found is true and i is
1583 the index of the string. If not, found is false */
1585 if (!found)
1587 /* new string */
1588 str = (char*)skin_buffer_alloc(len+1);
1589 if (!str)
1591 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1592 break;
1594 strlcpy(str, string_start, len+1);
1595 struct skin_token_list *item =
1596 new_skin_token_list_item(&data->tokens[data->num_tokens], str);
1597 if(!item)
1599 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1600 break;
1602 add_to_ll_chain(&data->strings, item);
1604 else
1606 /* another occurrence of an existing string */
1607 data->tokens[data->num_tokens].value.data = list->token->value.data;
1609 data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
1610 data->num_tokens++;
1612 break;
1616 if (!fail && level >= 0) /* there are unclosed conditionals */
1617 fail = PARSE_FAIL_UNCLOSED_COND;
1619 if (*wps_bufptr && !fail)
1620 /* one of the limits of the while loop was exceeded */
1621 fail = PARSE_FAIL_LIMITS_EXCEEDED;
1623 /* Success! */
1624 curr_line->curr_subline->last_token_idx = data->num_tokens;
1625 data->tokens[data->num_tokens++].type = WPS_NO_TOKEN;
1626 /* freeup unused tokens */
1627 skin_buffer_free_from_front(sizeof(struct wps_token)
1628 * (max_tokens - data->num_tokens));
1630 #if defined(DEBUG) || defined(SIMULATOR)
1631 if (debug)
1632 print_debug_info(data, fail, line_number);
1633 #else
1634 (void)debug;
1635 #endif
1637 return (fail == 0);
1642 * initial setup of wps_data; does reset everything
1643 * except fields which need to survive, i.e.
1645 * wps_data->remote_wps
1647 void skin_data_reset(struct wps_data *wps_data)
1649 #ifdef HAVE_LCD_BITMAP
1650 wps_data->images = NULL;
1651 wps_data->progressbars = NULL;
1652 #endif
1653 #ifdef HAVE_TOUCHSCREEN
1654 wps_data->touchregions = NULL;
1655 #endif
1656 wps_data->viewports = NULL;
1657 wps_data->strings = NULL;
1658 #ifdef HAVE_ALBUMART
1659 wps_data->albumart = NULL;
1660 if (wps_data->playback_aa_slot >= 0)
1662 playback_release_aa_slot(wps_data->playback_aa_slot);
1663 wps_data->playback_aa_slot = -1;
1665 #endif
1666 wps_data->tokens = NULL;
1667 wps_data->num_tokens = 0;
1669 #ifdef HAVE_LCD_BITMAP
1670 wps_data->peak_meter_enabled = false;
1671 wps_data->wps_sb_tag = false;
1672 wps_data->show_sb_on_wps = false;
1673 #else /* HAVE_LCD_CHARCELLS */
1674 /* progress bars */
1675 int i;
1676 for (i = 0; i < 8; i++)
1678 wps_data->wps_progress_pat[i] = 0;
1680 wps_data->full_line_progressbar = false;
1681 #endif
1682 wps_data->wps_loaded = false;
1685 #ifdef HAVE_LCD_BITMAP
1686 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1688 (void)wps_data; /* only needed for remote targets */
1689 bool loaded = false;
1690 char img_path[MAX_PATH];
1691 get_image_filename(bitmap->data, bmpdir,
1692 img_path, sizeof(img_path));
1694 /* load the image */
1695 int format;
1696 #ifdef HAVE_REMOTE_LCD
1697 if (wps_data->remote_wps)
1698 format = FORMAT_ANY|FORMAT_REMOTE;
1699 else
1700 #endif
1701 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1703 size_t max_buf;
1704 char* imgbuf = (char*)skin_buffer_grab(&max_buf);
1705 bitmap->data = imgbuf;
1706 int ret = read_bmp_file(img_path, bitmap, max_buf, format, NULL);
1708 if (ret > 0)
1710 skin_buffer_increment(ret, true);
1711 loaded = true;
1713 else
1715 /* Abort if we can't load an image */
1716 loaded = false;
1718 return loaded;
1721 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1723 struct skin_token_list *list;
1724 /* do the progressbars */
1725 list = wps_data->progressbars;
1726 while (list)
1728 struct progressbar *pb = (struct progressbar*)list->token->value.data;
1729 if (pb->bm.data)
1731 pb->have_bitmap_pb = load_skin_bmp(wps_data, &pb->bm, bmpdir);
1733 list = list->next;
1735 /* regular images */
1736 list = wps_data->images;
1737 while (list)
1739 struct gui_img *img = (struct gui_img*)list->token->value.data;
1740 if (img->bm.data)
1742 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1743 if (img->loaded)
1744 img->subimage_height = img->bm.height / img->num_subimages;
1746 list = list->next;
1749 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1750 if (bmp_names[BACKDROP_BMP])
1752 int screen = SCREEN_MAIN;
1753 char img_path[MAX_PATH];
1754 get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
1755 img_path, sizeof(img_path));
1756 #if defined(HAVE_REMOTE_LCD)
1757 /* We only need to check LCD type if there is a remote LCD */
1758 if (wps_data->remote_wps)
1759 screen = SCREEN_REMOTE;
1760 #endif
1761 screens[screen].backdrop_load(BACKDROP_SKIN_WPS, img_path);
1763 #endif /* has backdrop support */
1765 /* If we got here, everything was OK */
1766 return true;
1769 #endif /* HAVE_LCD_BITMAP */
1771 /* to setup up the wps-data from a format-buffer (isfile = false)
1772 from a (wps-)file (isfile = true)*/
1773 bool skin_data_load(struct wps_data *wps_data,
1774 const char *buf,
1775 bool isfile)
1778 if (!wps_data || !buf)
1779 return false;
1780 #ifdef HAVE_ALBUMART
1781 int status;
1782 struct mp3entry *curtrack;
1783 long offset;
1784 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1785 if (wps_data->albumart)
1787 old_aa.state = wps_data->albumart->state;
1788 old_aa.height = wps_data->albumart->height;
1789 old_aa.width = wps_data->albumart->width;
1791 #endif
1793 skin_data_reset(wps_data);
1795 /* alloc default viewport, will be fixed up later */
1796 curr_vp = skin_buffer_alloc(sizeof(struct skin_viewport));
1797 if (!curr_vp)
1798 return false;
1799 struct skin_token_list *list = new_skin_token_list_item(NULL, curr_vp);
1800 if (!list)
1801 return false;
1802 add_to_ll_chain(&wps_data->viewports, list);
1805 /* Initialise the first (default) viewport */
1806 curr_vp->label = VP_DEFAULT_LABEL;
1807 curr_vp->pb = NULL;
1808 curr_vp->hidden_flags = 0;
1809 curr_vp->lines = NULL;
1811 curr_line = NULL;
1812 if (!skin_start_new_line(curr_vp, 0))
1813 return false;
1815 if (!isfile)
1817 return wps_parse(wps_data, buf, false);
1819 else
1821 int fd = open_utf8(buf, O_RDONLY);
1823 if (fd < 0)
1824 return false;
1826 /* get buffer space from the plugin buffer */
1827 size_t buffersize = 0;
1828 char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
1830 if (!wps_buffer)
1831 return false;
1833 /* copy the file's content to the buffer for parsing,
1834 ensuring that every line ends with a newline char. */
1835 unsigned int start = 0;
1836 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1838 start += strlen(wps_buffer + start);
1839 if (start < buffersize - 1)
1841 wps_buffer[start++] = '\n';
1842 wps_buffer[start] = 0;
1846 close(fd);
1848 if (start <= 0)
1849 return false;
1851 #ifdef HAVE_LCD_BITMAP
1852 /* Set all filename pointers to NULL */
1853 memset(bmp_names, 0, sizeof(bmp_names));
1854 #endif
1856 /* parse the WPS source */
1857 if (!wps_parse(wps_data, wps_buffer, true)) {
1858 skin_data_reset(wps_data);
1859 return false;
1862 wps_data->wps_loaded = true;
1864 #ifdef HAVE_LCD_BITMAP
1865 /* get the bitmap dir */
1866 char bmpdir[MAX_PATH];
1867 char *dot = strrchr(buf, '.');
1869 strlcpy(bmpdir, buf, dot - buf + 1);
1871 /* load the bitmaps that were found by the parsing */
1872 if (!load_skin_bitmaps(wps_data, bmpdir)) {
1873 skin_data_reset(wps_data);
1874 return false;
1876 #endif
1877 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1878 status = audio_status();
1879 if (status & AUDIO_STATUS_PLAY)
1881 struct skin_albumart *aa = wps_data->albumart;
1882 if (aa && ((aa->state && !old_aa.state) ||
1883 (aa->state &&
1884 (((old_aa.height != aa->height) ||
1885 (old_aa.width != aa->width))))))
1887 curtrack = audio_current_track();
1888 offset = curtrack->offset;
1889 audio_stop();
1890 if (!(status & AUDIO_STATUS_PAUSE))
1891 audio_play(offset);
1894 #endif
1895 #if defined(DEBUG) || defined(SIMULATOR)
1896 debug_skin_usage();
1897 #endif
1898 return true;