1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
50 #include "wps_internals.h"
51 #include "skin_engine.h"
53 #include "settings_list.h"
55 #ifdef HAVE_LCD_BITMAP
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
92 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
94 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
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);
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
117 wps_bufptr points to the char following the tag (i.e. where
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
);
125 enum wps_token_type type
;
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
);
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
);
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
178 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
180 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
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
},
199 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
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
},
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
,
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
,
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
},
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
},
279 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
280 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
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
},
288 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
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
,
296 #ifdef HAVE_LCD_BITMAP
297 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
299 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
300 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
302 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
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
},
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
},
323 #if CONFIG_CODEC == SWCODEC
324 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
325 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
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
},
342 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
343 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
, parse_albumart_display
},
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
},
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
)
378 struct skin_token_list
*t
= *list
;
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
;
392 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
393 if (img
->label
== label
)
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
;
407 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->token
->value
.data
;
408 if (vp
->label
== label
)
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
,
424 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
426 token
= skin_buffer_alloc(sizeof(struct wps_token
));
427 if (!llitem
|| !token
)
430 llitem
->token
= token
;
432 llitem
->token
->value
.data
= token_data
;
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
)
442 while(*(wps_bufptr
+ skip
) != '\n')
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
));
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
;
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
;
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
;
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
492 curr_line
->curr_subline
->last_token_idx
= curr_token
- 1;
493 curr_line
->next
= line
;
494 curr_line
->curr_subline
= NULL
;
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')
541 else if(c
>= 'A' && c
<= 'Z')
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) )
558 int bmpdirlen
= strlen(bmpdir
);
561 buf
[bmpdirlen
] = '/';
562 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
563 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
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];
574 struct gui_img
*img
;;
577 img
= find_image(label
, wps_data
);
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 */
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
;
604 const char* filename
;
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|
616 return WPS_ERROR_INVALID_PARAM
;
620 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
621 return WPS_ERROR_INVALID_PARAM
;
623 /* Check there is a terminating | */
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
));
635 return WPS_ERROR_INVALID_PARAM
;
636 /* save a pointer to the filename */
637 img
->bm
.data
= (char*)filename
;
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;
653 /* Parse the (optional) number of sub-images */
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
);
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
)
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
;
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
;
696 #ifdef HAVE_REMOTE_LCD
697 wps_data
->remote_wps
? SCREEN_REMOTE
:
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
;
708 skin_vp
->lines
= NULL
;
711 curr_line
->curr_subline
->last_token_idx
= wps_data
->num_tokens
712 - (wps_data
->num_tokens
> 0 ? 1 : 0);
716 if (!skin_start_new_line(skin_vp
, wps_data
->num_tokens
))
717 return WPS_ERROR_INVALID_PARAM
;
722 skin_vp
->label
= VP_INFO_LABEL
;
723 skin_vp
->hidden_flags
= VP_NEVER_VISIBLE
;
726 else if (*ptr
== 'l')
730 char label
= *(ptr
+2);
731 if (label
>= 'a' && label
<= 'z')
733 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
734 skin_vp
->label
= label
;
737 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
742 return WPS_ERROR_INVALID_PARAM
;
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 | */
754 return WPS_ERROR_INVALID_PARAM
;
757 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
759 return WPS_ERROR_INVALID_PARAM
;
760 add_to_ll_chain(&wps_data
->viewports
, list
);
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 */
773 const char *pos
= NULL
;
776 pos
= strchr(wps_bufptr
+ 1, '|');
777 newline
= strchr(wps_bufptr
, '\n');
780 return WPS_ERROR_INVALID_PARAM
;
782 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
784 /* format: %X|filename.bmp| */
785 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
789 /* Skip the rest of the line */
790 return skip_end_of_line(wps_bufptr
);
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
806 const char *ptr
= wps_bufptr
;
811 /* Find the setting's cfg_name */
813 return WPS_ERROR_INVALID_PARAM
;
815 end
= strchr(ptr
,'|');
817 return WPS_ERROR_INVALID_PARAM
;
818 strlcpy(temp
, ptr
,end
-ptr
+1);
820 if (token
->type
== WPS_TOKEN_TRANSLATEDSTRING
)
823 i
= lang_english_to_id(temp
);
825 return WPS_ERROR_INVALID_PARAM
;
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
))
838 if (i
== nb_settings
)
839 return WPS_ERROR_INVALID_PARAM
;
842 /* Store the setting number */
845 /* Skip the rest of the line */
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 */
860 static int parse_timeout(const char *wps_bufptr
,
861 struct wps_token
*token
,
862 struct wps_data
*wps_data
)
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
!= '.')
876 val
+= *wps_bufptr
- '0';
892 if (have_tenth
== false)
895 if (val
== 0 && skip
== 0)
897 /* decide what to do if no value was specified */
900 case WPS_TOKEN_SUBLINE_TIMEOUT
:
902 case WPS_TOKEN_BUTTON_VOLUME
:
907 token
->value
.i
= val
;
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
926 const char *filename
;
927 int x
, y
, height
, width
;
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
);
934 return WPS_ERROR_INVALID_PARAM
;
936 struct viewport
*vp
= &curr_vp
->vp
;
938 int font_height
= font_get(vp
->font
)->height
;
942 /* we need to know what line number (viewport relative) this pb is,
943 * so count them... */
945 struct skin_line
*line
= curr_vp
->lines
;
951 pb
->have_bitmap_pb
= false;
952 pb
->bm
.data
= NULL
; /* no bitmap specified */
954 if (*wps_bufptr
!= '|') /* regular old style */
957 pb
->width
= vp
->width
;
958 pb
->height
= SYSFONT_HEIGHT
-2;
959 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
962 add_to_ll_chain(&wps_data
->progressbars
, item
);
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 */
979 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
981 /* A zero width causes a divide-by-zero error later, so reject it */
983 return WPS_ERROR_INVALID_PARAM
;
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 */
994 return WPS_ERROR_INVALID_PARAM
;
999 pb
->height
= font_height
;
1001 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
1004 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
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;
1014 if (*(wps_bufptr
-1) == 'f')
1015 wps_data
->full_line_progressbar
= true;
1017 wps_data
->full_line_progressbar
= false;
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
;
1031 struct dim dimensions
;
1033 struct skin_albumart
*aa
= skin_buffer_alloc(sizeof(struct skin_albumart
));
1034 (void)token
; /* silence warning */
1036 return skip_end_of_line(wps_bufptr
);
1038 /* reset albumart info in wps */
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
1060 /* parsing width field */
1064 /* apply each modifier in turn */
1071 aa
->xalign
= WPS_ALBUMART_ALIGN_LEFT
;
1075 aa
->xalign
= WPS_ALBUMART_ALIGN_CENTER
;
1080 aa
->xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
1088 /* simply ignored */
1095 /* extract max width data */
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 */
1113 /* apply each modifier in turn */
1120 aa
->yalign
= WPS_ALBUMART_ALIGN_TOP
;
1124 aa
->yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1129 aa
->yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1137 /* simply ignored */
1144 /* extract max height data */
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 .. ! */
1161 else if (aa
->width
> LCD_WIDTH
)
1162 aa
->width
= LCD_WIDTH
;
1166 else if (aa
->height
> LCD_HEIGHT
)
1167 aa
->height
= LCD_HEIGHT
;
1169 aa
->state
= WPS_ALBUMART_LOAD
;
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
);
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
)
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
;
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')
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
)
1228 struct touchregion
*region
= NULL
;
1229 const char *ptr
= wps_bufptr
;
1231 const char pb_string
[] = "progressbar";
1232 const char vol_string
[] = "volume";
1235 /* format: %T|x|y|width|height|action|
1236 * if action starts with & the area must be held to happen
1238 * play - play/pause playback
1239 * stop - stop playback, exit the wps
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
1256 return WPS_ERROR_INVALID_PARAM
;
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 | */
1264 return WPS_ERROR_INVALID_PARAM
;
1266 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1268 return WPS_ERROR_INVALID_PARAM
;
1270 /* should probably do some bounds checking here with the viewport... but later */
1271 region
->action
= ACTION_NONE
;
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
;
1286 region
->type
= WPS_TOUCHREGION_ACTION
;
1291 region
->repeat
= true;
1294 region
->repeat
= false;
1297 imax
= ARRAYLEN(touchactions
);
1298 while ((region
->action
== ACTION_NONE
) &&
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
;
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
);
1313 return WPS_ERROR_INVALID_PARAM
;
1314 add_to_ll_chain(&wps_data
->touchregions
, item
);
1315 return skip_end_of_line(wps_bufptr
);
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
));
1336 /* escaped characters */
1337 token
->type
= WPS_TOKEN_CHARACTER
;
1338 token
->value
.c
= *wps_bufptr
;
1340 wps_data
->num_tokens
++;
1344 /* conditional tag */
1345 token
->type
= WPS_TOKEN_CONDITIONAL
;
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
;
1356 /* find what tag we have */
1357 for (tag
= all_tags
;
1358 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
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
;
1373 /* Some tags we don't want to save as tokens */
1374 if (tag
->type
== WPS_NO_TOKEN
)
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')
1382 wps_data
->num_tokens
++;
1391 data is the pointer to the structure where the parsed WPS should be stored.
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
)
1399 enum wps_parse_error fail
= PARSE_OK
;
1401 int max_tokens
= TOKEN_BLOCK_SIZE
;
1402 size_t buf_free
= 0;
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
)
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
;
1427 skin_buffer_increment(needed
, false);
1428 max_tokens
+= extra_tokens
;
1431 switch(*wps_bufptr
++)
1436 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1438 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1441 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1443 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1449 /* Alternating sublines separator */
1451 if (level
>= 0) /* there are unclosed conditionals */
1453 fail
= PARSE_FAIL_UNCLOSED_COND
;
1457 if (!skin_start_new_subline(curr_line
, data
->num_tokens
))
1458 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1462 /* Conditional list start */
1464 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1466 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1470 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1471 lastcond
[level
] = data
->num_tokens
++;
1474 /* Conditional list end */
1476 if (level
< 0) /* not in a conditional, invalid char */
1478 fail
= PARSE_FAIL_INVALID_CHAR
;
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
;
1487 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1491 lastcond
[level
] = 0;
1493 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1497 /* Conditional list option */
1499 if (level
< 0) /* not in a conditional, invalid char */
1501 fail
= PARSE_FAIL_INVALID_CHAR
;
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
;
1510 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1514 lastcond
[level
] = data
->num_tokens
;
1515 numoptions
[level
]++;
1521 if (level
>= 0) /* there are unclosed conditionals */
1523 fail
= PARSE_FAIL_UNCLOSED_COND
;
1527 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1530 /* End of this line */
1532 if (level
>= 0) /* there are unclosed conditionals */
1534 fail
= PARSE_FAIL_UNCLOSED_COND
;
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;
1543 if (!skin_start_new_line(curr_vp
, data
->num_tokens
))
1545 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
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')
1567 /* look if we already have that string */
1570 struct skin_token_list
*list
= data
->strings
;
1573 str
= (char*)list
->token
->value
.data
;
1574 found
= (strlen(str
) == len
&&
1575 strncmp(string_start
, str
, len
) == 0);
1577 break; /* break here because the list item is
1578 used if its found */
1581 /* If a matching string is found, found is true and i is
1582 the index of the string. If not, found is false */
1587 str
= (char*)skin_buffer_alloc(len
+1);
1590 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
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
);
1598 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1601 add_to_ll_chain(&data
->strings
, item
);
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
;
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
;
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)
1631 print_debug_info(data
, fail
, line_number
);
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
;
1652 #ifdef HAVE_TOUCHSCREEN
1653 wps_data
->touchregions
= NULL
;
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;
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 */
1675 for (i
= 0; i
< 8; i
++)
1677 wps_data
->wps_progress_pat
[i
] = 0;
1679 wps_data
->full_line_progressbar
= false;
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 */
1695 #ifdef HAVE_REMOTE_LCD
1696 if (wps_data
->remote_wps
)
1697 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1700 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
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
);
1709 skin_buffer_increment(ret
, true);
1714 /* Abort if we can't load an image */
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
;
1727 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
1730 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
1734 /* regular images */
1735 list
= wps_data
->images
;
1738 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1741 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1743 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
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
;
1760 screens
[screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1762 #endif /* has backdrop support */
1764 /* If we got here, everything was OK */
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
,
1777 if (!wps_data
|| !buf
)
1779 #ifdef HAVE_ALBUMART
1781 struct mp3entry
*curtrack
;
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
;
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
));
1798 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
1801 add_to_ll_chain(&wps_data
->viewports
, list
);
1804 /* Initialise the first (default) viewport */
1805 curr_vp
->label
= VP_DEFAULT_LABEL
;
1807 curr_vp
->hidden_flags
= 0;
1808 curr_vp
->lines
= NULL
;
1811 if (!skin_start_new_line(curr_vp
, 0))
1816 return wps_parse(wps_data
, buf
, false);
1820 int fd
= open_utf8(buf
, O_RDONLY
);
1825 /* get buffer space from the plugin buffer */
1826 size_t buffersize
= 0;
1827 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
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;
1850 #ifdef HAVE_LCD_BITMAP
1851 /* Set all filename pointers to NULL */
1852 memset(bmp_names
, 0, sizeof(bmp_names
));
1855 /* parse the WPS source */
1856 if (!wps_parse(wps_data
, wps_buffer
, true)) {
1857 skin_data_reset(wps_data
);
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
);
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
) ||
1883 (((old_aa
.height
!= aa
->height
) ||
1884 (old_aa
.width
!= aa
->width
))))))
1886 curtrack
= audio_current_track();
1887 offset
= curtrack
->offset
;
1889 if (!(status
& AUDIO_STATUS_PAUSE
))
1894 #if defined(DEBUG) || defined(SIMULATOR)