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 ****************************************************************************/
47 #include "wps_internals.h"
48 #include "skin_engine.h"
50 #include "settings_list.h"
52 #ifdef HAVE_LCD_BITMAP
58 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
59 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
61 #define WPS_ERROR_INVALID_PARAM -1
63 /* level of current conditional.
64 -1 means we're not in a conditional. */
65 static int level
= -1;
67 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
68 or WPS_TOKEN_CONDITIONAL_START in current level */
69 static int lastcond
[WPS_MAX_COND_LEVEL
];
71 /* index of the WPS_TOKEN_CONDITIONAL in current level */
72 static int condindex
[WPS_MAX_COND_LEVEL
];
74 /* number of condtional options in current level */
75 static int numoptions
[WPS_MAX_COND_LEVEL
];
77 /* the current line in the file */
80 /* the current viewport */
81 static struct skin_viewport
*curr_vp
;
83 #ifdef HAVE_LCD_BITMAP
86 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
88 #define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
91 #define PROGRESSBAR_BMP MAX_IMAGES
92 #define BACKDROP_BMP (MAX_BITMAPS-1)
94 /* pointers to the bitmap filenames in the WPS source */
95 static const char *bmp_names
[MAX_BITMAPS
];
97 #endif /* HAVE_LCD_BITMAP */
99 #if defined(DEBUG) || defined(SIMULATOR)
100 /* debugging function */
101 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
102 extern void debug_skin_usage(void);
105 static void wps_reset(struct wps_data
*data
);
107 /* Function for parsing of details for a token. At the moment the
108 function is called, the token type has already been set. The
109 function must fill in the details and possibly add more tokens
110 to the token array. It should return the number of chars that
113 wps_bufptr points to the char following the tag (i.e. where
115 token is the pointer to the 'main' token being parsed
117 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
118 struct wps_token
*token
, struct wps_data
*wps_data
);
121 enum wps_token_type type
;
123 unsigned char refresh_type
;
124 const wps_tag_parse_func parse_func
;
126 static int skip_end_of_line(const char *wps_bufptr
);
127 /* prototypes of all special parse functions : */
128 static int parse_timeout(const char *wps_bufptr
,
129 struct wps_token
*token
, struct wps_data
*wps_data
);
130 static int parse_progressbar(const char *wps_bufptr
,
131 struct wps_token
*token
, struct wps_data
*wps_data
);
132 static int parse_dir_level(const char *wps_bufptr
,
133 struct wps_token
*token
, struct wps_data
*wps_data
);
134 static int parse_setting(const char *wps_bufptr
,
135 struct wps_token
*token
, struct wps_data
*wps_data
);
137 #ifdef HAVE_LCD_BITMAP
138 static int parse_viewport_display(const char *wps_bufptr
,
139 struct wps_token
*token
, struct wps_data
*wps_data
);
140 static int parse_viewport(const char *wps_bufptr
,
141 struct wps_token
*token
, struct wps_data
*wps_data
);
142 static int parse_statusbar_enable(const char *wps_bufptr
,
143 struct wps_token
*token
, struct wps_data
*wps_data
);
144 static int parse_statusbar_disable(const char *wps_bufptr
,
145 struct wps_token
*token
, struct wps_data
*wps_data
);
146 static int parse_image_display(const char *wps_bufptr
,
147 struct wps_token
*token
, struct wps_data
*wps_data
);
148 static int parse_image_load(const char *wps_bufptr
,
149 struct wps_token
*token
, struct wps_data
*wps_data
);
150 #endif /*HAVE_LCD_BITMAP */
151 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
152 static int parse_image_special(const char *wps_bufptr
,
153 struct wps_token
*token
, struct wps_data
*wps_data
);
156 static int parse_albumart_load(const char *wps_bufptr
,
157 struct wps_token
*token
, struct wps_data
*wps_data
);
158 static int parse_albumart_conditional(const char *wps_bufptr
,
159 struct wps_token
*token
, struct wps_data
*wps_data
);
160 #endif /* HAVE_ALBUMART */
161 #ifdef HAVE_TOUCHSCREEN
162 static int parse_touchregion(const char *wps_bufptr
,
163 struct wps_token
*token
, struct wps_data
*wps_data
);
165 static int fulline_tag_not_supported(const char *wps_bufptr
,
166 struct wps_token
*token
, struct wps_data
*wps_data
)
168 (void)token
; (void)wps_data
;
169 return skip_end_of_line(wps_bufptr
);
171 #define parse_touchregion fulline_tag_not_supported
174 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
176 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
179 /* array of available tags - those with more characters have to go first
180 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
181 static const struct wps_tag all_tags
[] = {
183 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
184 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
185 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
187 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
188 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
189 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
190 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
191 #if CONFIG_CHARGING >= CHARGING_MONITOR
192 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
195 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
198 { WPS_TOKEN_RTC_PRESENT
, "cc", WPS_REFRESH_STATIC
, NULL
},
199 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
200 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
201 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
202 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
203 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
204 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
205 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
206 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
207 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
208 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
209 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
210 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
211 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
212 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
213 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
214 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
215 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
216 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
219 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
224 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
225 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
226 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
228 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
232 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_STATIC
, NULL
},
233 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_STATIC
, NULL
},
234 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_STATIC
, NULL
},
235 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_STATIC
, NULL
},
236 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_STATIC
, NULL
},
237 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_STATIC
, NULL
},
238 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_STATIC
, NULL
},
239 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_STATIC
, NULL
},
240 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_STATIC
, NULL
},
241 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_STATIC
,
244 /* current metadata */
245 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
246 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
247 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
248 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
249 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
250 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
251 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
252 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
253 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
254 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
255 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
256 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_STATIC
, NULL
},
263 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_STATIC
, NULL
},
264 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_STATIC
, NULL
},
265 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_STATIC
, NULL
},
266 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_STATIC
, NULL
},
267 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_STATIC
, NULL
},
268 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_STATIC
, NULL
},
270 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_STATIC
, NULL
},
272 #if (CONFIG_CODEC != MAS3507D)
273 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
276 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
277 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
280 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
282 #ifdef HAS_REMOTE_BUTTON_HOLD
283 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
285 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
288 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
289 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
290 { WPS_TOKEN_BUTTON_VOLUME
, "mv", WPS_REFRESH_DYNAMIC
,
293 #ifdef HAVE_LCD_BITMAP
294 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
296 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
297 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
299 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
302 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
304 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
305 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
306 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
307 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
309 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
310 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
311 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
312 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
315 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
316 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
317 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
320 #if CONFIG_CODEC == SWCODEC
321 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
322 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
325 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
326 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_timeout
},
328 #ifdef HAVE_LCD_BITMAP
329 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
330 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
332 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
334 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
335 parse_image_display
},
337 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
339 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
340 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
,
341 parse_albumart_conditional
},
344 { WPS_VIEWPORT_ENABLE
, "Vd", WPS_REFRESH_DYNAMIC
,
345 parse_viewport_display
},
346 { WPS_NO_TOKEN
, "V", 0, parse_viewport
},
348 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
349 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
353 { WPS_TOKEN_SETTING
, "St", WPS_REFRESH_DYNAMIC
, parse_setting
},
355 { WPS_TOKEN_LASTTOUCH
, "Tl", WPS_REFRESH_DYNAMIC
, parse_timeout
},
356 { WPS_NO_TOKEN
, "T", 0, parse_touchregion
},
358 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
359 /* the array MUST end with an empty string (first char is \0) */
363 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
364 * chains require the order to be kept.
366 static void add_to_ll_chain(struct skin_token_list
**list
, struct skin_token_list
*item
)
372 struct skin_token_list
*t
= *list
;
378 /* create and init a new wpsll item.
379 * passing NULL to token will alloc a new one.
380 * You should only pass NULL for the token when the token type (table above)
381 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
383 static struct skin_token_list
*new_skin_token_list_item(struct wps_token
*token
,
386 struct skin_token_list
*llitem
= skin_buffer_alloc(sizeof(struct skin_token_list
));
388 token
= skin_buffer_alloc(sizeof(struct wps_token
));
389 if (!llitem
|| !token
)
392 llitem
->token
= token
;
394 llitem
->token
->value
.data
= token_data
;
398 /* Returns the number of chars that should be skipped to jump
399 immediately after the first eol, i.e. to the start of the next line */
400 static int skip_end_of_line(const char *wps_bufptr
)
404 while(*(wps_bufptr
+ skip
) != '\n')
409 /* Starts a new subline in the current line during parsing */
410 static void wps_start_new_subline(struct wps_data
*data
)
412 data
->num_sublines
++;
413 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
414 data
->lines
[data
->num_lines
].num_sublines
++;
417 #ifdef HAVE_LCD_BITMAP
419 static int parse_statusbar_enable(const char *wps_bufptr
,
420 struct wps_token
*token
,
421 struct wps_data
*wps_data
)
423 (void)token
; /* Kill warnings */
424 wps_data
->wps_sb_tag
= true;
425 wps_data
->show_sb_on_wps
= true;
426 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
427 if (default_vp
->vp
.y
== 0)
429 default_vp
->vp
.y
= STATUSBAR_HEIGHT
;
430 default_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
432 return skip_end_of_line(wps_bufptr
);
435 static int parse_statusbar_disable(const char *wps_bufptr
,
436 struct wps_token
*token
,
437 struct wps_data
*wps_data
)
439 (void)token
; /* Kill warnings */
440 wps_data
->wps_sb_tag
= true;
441 wps_data
->show_sb_on_wps
= false;
442 struct skin_viewport
*default_vp
= find_viewport(VP_DEFAULT_LABEL
, wps_data
);
443 if (default_vp
->vp
.y
== STATUSBAR_HEIGHT
)
445 default_vp
->vp
.y
= 0;
446 default_vp
->vp
.height
+= STATUSBAR_HEIGHT
;
448 return skip_end_of_line(wps_bufptr
);
451 static int get_image_id(int c
)
453 if(c
>= 'a' && c
<= 'z')
455 else if(c
>= 'A' && c
<= 'Z')
461 static char *get_image_filename(const char *start
, const char* bmpdir
,
462 char *buf
, int buf_size
)
464 const char *end
= strchr(start
, '|');
466 if ( !end
|| (end
- start
) >= (buf_size
- (int)ROCKBOX_DIR_LEN
- 2) )
472 int bmpdirlen
= strlen(bmpdir
);
475 buf
[bmpdirlen
] = '/';
476 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
477 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
482 static int parse_image_display(const char *wps_bufptr
,
483 struct wps_token
*token
,
484 struct wps_data
*wps_data
)
486 char label
= wps_bufptr
[0];
488 struct gui_img
*img
;;
491 img
= find_image(label
, wps_data
);
494 token
->value
.i
= label
; /* do debug works */
495 return WPS_ERROR_INVALID_PARAM
;
498 if ((subimage
= get_image_id(wps_bufptr
[1])) != -1)
500 if (subimage
>= img
->num_subimages
)
501 return WPS_ERROR_INVALID_PARAM
;
503 /* Store sub-image number to display in high bits */
504 token
->value
.i
= label
| (subimage
<< 8);
505 return 2; /* We have consumed 2 bytes */
507 token
->value
.i
= label
;
508 return 1; /* We have consumed 1 byte */
512 static int parse_image_load(const char *wps_bufptr
,
513 struct wps_token
*token
,
514 struct wps_data
*wps_data
)
516 const char *ptr
= wps_bufptr
;
518 const char* filename
;
524 /* format: %x|n|filename.bmp|x|y|
525 or %xl|n|filename.bmp|x|y|
526 or %xl|n|filename.bmp|x|y|num_subimages|
530 return WPS_ERROR_INVALID_PARAM
;
534 if (!(ptr
= parse_list("ssdd", NULL
, '|', ptr
, &id
, &filename
, &x
, &y
)))
535 return WPS_ERROR_INVALID_PARAM
;
537 /* Check there is a terminating | */
539 return WPS_ERROR_INVALID_PARAM
;
541 /* check the image number and load state */
542 if(find_image(*id
, wps_data
))
544 /* Invalid image ID */
545 return WPS_ERROR_INVALID_PARAM
;
547 img
= skin_buffer_alloc(sizeof(struct gui_img
));
549 return WPS_ERROR_INVALID_PARAM
;
550 /* save a pointer to the filename */
551 img
->bm
.data
= (char*)filename
;
555 img
->num_subimages
= 1;
556 img
->always_display
= false;
558 /* save current viewport */
559 img
->vp
= &curr_vp
->vp
;
561 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
563 img
->always_display
= true;
567 /* Parse the (optional) number of sub-images */
569 newline
= strchr(ptr
, '\n');
570 pos
= strchr(ptr
, '|');
571 if (pos
&& pos
< newline
)
572 img
->num_subimages
= atoi(ptr
);
574 if (img
->num_subimages
<= 0)
575 return WPS_ERROR_INVALID_PARAM
;
577 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, img
);
579 return WPS_ERROR_INVALID_PARAM
;
580 add_to_ll_chain(&wps_data
->images
, item
);
582 /* Skip the rest of the line */
583 return skip_end_of_line(wps_bufptr
);
586 static int parse_viewport_display(const char *wps_bufptr
,
587 struct wps_token
*token
,
588 struct wps_data
*wps_data
)
591 char letter
= wps_bufptr
[0];
593 if (letter
< 'a' || letter
> 'z')
595 /* invalid viewport tag */
596 return WPS_ERROR_INVALID_PARAM
;
598 token
->value
.i
= letter
;
602 static int parse_viewport(const char *wps_bufptr
,
603 struct wps_token
*token
,
604 struct wps_data
*wps_data
)
606 (void)token
; /* Kill warnings */
607 const char *ptr
= wps_bufptr
;
610 #ifdef HAVE_REMOTE_LCD
611 wps_data
->remote_wps
? SCREEN_REMOTE
:
615 struct skin_viewport
*skin_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
617 /* check for the optional letter to signify its a hideable viewport */
618 /* %Vl|<label>|<rest of tags>| */
619 skin_vp
->hidden_flags
= 0;
620 skin_vp
->label
= VP_NO_LABEL
;
627 char label
= *(ptr
+2);
628 if (label
>= 'a' && label
<= 'z')
630 skin_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
631 skin_vp
->label
= label
;
634 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
639 return WPS_ERROR_INVALID_PARAM
;
642 struct viewport
*vp
= &skin_vp
->vp
;
643 /* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
645 if (!(ptr
= viewport_parse_viewport(vp
, screen
, ptr
, '|')))
646 return WPS_ERROR_INVALID_PARAM
;
648 /* Check for trailing | */
650 return WPS_ERROR_INVALID_PARAM
;
652 curr_vp
->last_line
= wps_data
->num_lines
- 1;
654 skin_vp
->first_line
= wps_data
->num_lines
;
656 if (wps_data
->num_sublines
< WPS_MAX_SUBLINES
)
658 wps_data
->lines
[wps_data
->num_lines
].first_subline_idx
=
659 wps_data
->num_sublines
;
661 wps_data
->sublines
[wps_data
->num_sublines
].first_token_idx
=
662 wps_data
->num_tokens
;
665 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, skin_vp
);
667 return WPS_ERROR_INVALID_PARAM
;
668 add_to_ll_chain(&wps_data
->viewports
, list
);
670 /* Skip the rest of the line */
671 return skip_end_of_line(wps_bufptr
);
674 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
675 static int parse_image_special(const char *wps_bufptr
,
676 struct wps_token
*token
,
677 struct wps_data
*wps_data
)
679 (void)wps_data
; /* kill warning */
681 const char *pos
= NULL
;
684 pos
= strchr(wps_bufptr
+ 1, '|');
685 newline
= strchr(wps_bufptr
, '\n');
688 return WPS_ERROR_INVALID_PARAM
;
690 if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
692 /* format: %X|filename.bmp| */
693 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
697 /* Skip the rest of the line */
698 return skip_end_of_line(wps_bufptr
);
702 #endif /* HAVE_LCD_BITMAP */
704 static int parse_setting(const char *wps_bufptr
,
705 struct wps_token
*token
,
706 struct wps_data
*wps_data
)
709 const char *ptr
= wps_bufptr
;
713 /* Find the setting's cfg_name */
715 return WPS_ERROR_INVALID_PARAM
;
717 end
= strchr(ptr
,'|');
719 return WPS_ERROR_INVALID_PARAM
;
721 /* Find the setting */
722 for (i
=0; i
<nb_settings
; i
++)
723 if (settings
[i
].cfg_name
&&
724 !strncmp(settings
[i
].cfg_name
,ptr
,end
-ptr
) &&
725 /* prevent matches on cfg_name prefixes */
726 strlen(settings
[i
].cfg_name
)==(size_t)(end
-ptr
))
728 if (i
== nb_settings
)
729 return WPS_ERROR_INVALID_PARAM
;
731 /* Store the setting number */
734 /* Skip the rest of the line */
739 static int parse_dir_level(const char *wps_bufptr
,
740 struct wps_token
*token
,
741 struct wps_data
*wps_data
)
743 char val
[] = { *wps_bufptr
, '\0' };
744 token
->value
.i
= atoi(val
);
745 (void)wps_data
; /* Kill warnings */
749 static int parse_timeout(const char *wps_bufptr
,
750 struct wps_token
*token
,
751 struct wps_data
*wps_data
)
755 bool have_point
= false;
756 bool have_tenth
= false;
758 (void)wps_data
; /* Kill the warning */
760 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
762 if (*wps_bufptr
!= '.')
765 val
+= *wps_bufptr
- '0';
781 if (have_tenth
== false)
784 if (val
== 0 && skip
== 0)
786 /* decide what to do if no value was specified */
789 case WPS_TOKEN_SUBLINE_TIMEOUT
:
791 case WPS_TOKEN_BUTTON_VOLUME
:
796 token
->value
.i
= val
;
801 static int parse_progressbar(const char *wps_bufptr
,
802 struct wps_token
*token
,
803 struct wps_data
*wps_data
)
805 /* %pb or %pb|filename|x|y|width|height|
806 using - for any of the params uses "sane" values */
807 #ifdef HAVE_LCD_BITMAP
815 const char *filename
;
816 int x
, y
, height
, width
;
818 const char *ptr
= wps_bufptr
;
819 struct progressbar
*pb
= skin_buffer_alloc(sizeof(struct progressbar
));
820 struct skin_token_list
*item
= new_skin_token_list_item(token
, pb
);
823 return WPS_ERROR_INVALID_PARAM
;
825 struct viewport
*vp
= &curr_vp
->vp
;
827 int font_height
= font_get(vp
->font
)->height
;
831 int line_num
= wps_data
->num_lines
- curr_vp
->first_line
;
833 pb
->have_bitmap_pb
= false;
834 pb
->bm
.data
= NULL
; /* no bitmap specified */
836 if (*wps_bufptr
!= '|') /* regular old style */
839 pb
->width
= vp
->width
;
840 pb
->height
= SYSFONT_HEIGHT
-2;
841 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
844 add_to_ll_chain(&wps_data
->progressbars
, item
);
847 ptr
= wps_bufptr
+ 1;
849 if (!(ptr
= parse_list("sdddd", &set
, '|', ptr
, &filename
,
850 &x
, &y
, &width
, &height
)))
851 return WPS_ERROR_INVALID_PARAM
;
853 if (LIST_VALUE_PARSED(set
, PB_FILENAME
)) /* filename */
854 pb
->bm
.data
= (char*)filename
;
856 if (LIST_VALUE_PARSED(set
, PB_X
)) /* x */
861 if (LIST_VALUE_PARSED(set
, PB_WIDTH
)) /* width */
863 /* A zero width causes a divide-by-zero error later, so reject it */
865 return WPS_ERROR_INVALID_PARAM
;
870 pb
->width
= vp
->width
- pb
->x
;
872 if (LIST_VALUE_PARSED(set
, PB_HEIGHT
)) /* height, default to font height */
874 /* A zero height makes no sense - reject it */
876 return WPS_ERROR_INVALID_PARAM
;
881 pb
->height
= font_height
;
883 if (LIST_VALUE_PARSED(set
, PB_Y
)) /* y */
886 pb
->y
= -line_num
- 1; /* Will be computed during the rendering */
889 add_to_ll_chain(&wps_data
->progressbars
, item
);
891 /* Skip the rest of the line */
892 return skip_end_of_line(wps_bufptr
)-1;
896 if (*(wps_bufptr
-1) == 'f')
897 wps_data
->full_line_progressbar
= true;
899 wps_data
->full_line_progressbar
= false;
907 static int parse_albumart_load(const char *wps_bufptr
,
908 struct wps_token
*token
,
909 struct wps_data
*wps_data
)
911 const char *_pos
, *newline
;
913 (void)token
; /* silence warning */
915 /* reset albumart info in wps */
916 wps_data
->albumart_max_width
= -1;
917 wps_data
->albumart_max_height
= -1;
918 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
919 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
921 /* format: %Cl|x|y|[[l|c|r]mwidth]|[[t|c|b]mheight]| */
923 newline
= strchr(wps_bufptr
, '\n');
925 /* initial validation and parsing of x and y components */
926 if (*wps_bufptr
!= '|')
927 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
929 _pos
= wps_bufptr
+ 1;
931 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|@ */
932 wps_data
->albumart_x
= atoi(_pos
);
934 _pos
= strchr(_pos
, '|');
935 if (!_pos
|| _pos
> newline
|| !isdigit(*(++_pos
)))
936 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
938 wps_data
->albumart_y
= atoi(_pos
);
940 _pos
= strchr(_pos
, '|');
941 if (!_pos
|| _pos
> newline
)
942 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
945 /* parsing width field */
949 /* apply each modifier in turn */
956 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_LEFT
;
960 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
;
965 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_RIGHT
;
980 /* extract max width data */
983 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
984 return WPS_ERROR_INVALID_PARAM
;
986 wps_data
->albumart_max_width
= atoi(_pos
);
988 _pos
= strchr(_pos
, '|');
989 if (!_pos
|| _pos
> newline
)
990 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
991 e.g. %Cl|7|59|200\n */
994 /* parsing height field */
998 /* apply each modifier in turn */
1005 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_TOP
;
1009 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
;
1014 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_BOTTOM
;
1022 /* simply ignored */
1029 /* extract max height data */
1032 if (!isdigit(*_pos
))
1033 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
1035 wps_data
->albumart_max_height
= atoi(_pos
);
1037 _pos
= strchr(_pos
, '|');
1038 if (!_pos
|| _pos
> newline
)
1039 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
1040 e.g. %Cl|7|59|200|200\n */
1043 /* if we got here, we parsed everything ok .. ! */
1044 if (wps_data
->albumart_max_width
< 0)
1045 wps_data
->albumart_max_width
= 0;
1046 else if (wps_data
->albumart_max_width
> LCD_WIDTH
)
1047 wps_data
->albumart_max_width
= LCD_WIDTH
;
1049 if (wps_data
->albumart_max_height
< 0)
1050 wps_data
->albumart_max_height
= 0;
1051 else if (wps_data
->albumart_max_height
> LCD_HEIGHT
)
1052 wps_data
->albumart_max_height
= LCD_HEIGHT
;
1054 wps_data
->wps_uses_albumart
= WPS_ALBUMART_LOAD
;
1056 /* Skip the rest of the line */
1057 return skip_end_of_line(wps_bufptr
);
1060 static int parse_albumart_conditional(const char *wps_bufptr
,
1061 struct wps_token
*token
,
1062 struct wps_data
*wps_data
)
1064 struct wps_token
*prevtoken
= token
;
1066 if (wps_data
->num_tokens
>= 1 && prevtoken
->type
== WPS_TOKEN_CONDITIONAL
)
1068 /* This %C is part of a %?C construct.
1069 It's either %?C<blah> or %?Cn<blah> */
1070 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
1071 if (*wps_bufptr
== 'n' && *(wps_bufptr
+ 1) == '<')
1076 else if (*wps_bufptr
== '<')
1082 token
->type
= WPS_NO_TOKEN
;
1088 /* This %C tag is in a conditional construct. */
1089 wps_data
->albumart_cond_index
= condindex
[level
];
1093 #endif /* HAVE_ALBUMART */
1095 #ifdef HAVE_TOUCHSCREEN
1097 struct touchaction
{char* s
; int action
;};
1098 static struct touchaction touchactions
[] = {
1099 {"play", ACTION_WPS_PLAY
}, {"stop", ACTION_WPS_STOP
},
1100 {"prev", ACTION_WPS_SKIPPREV
}, {"next", ACTION_WPS_SKIPNEXT
},
1101 {"ffwd", ACTION_WPS_SEEKFWD
}, {"rwd", ACTION_WPS_SEEKBACK
},
1102 {"menu", ACTION_WPS_MENU
}, {"browse", ACTION_WPS_BROWSE
},
1103 {"shuffle", ACTION_TOUCH_SHUFFLE
}, {"repmode", ACTION_TOUCH_REPMODE
},
1104 {"quickscreen", ACTION_WPS_QUICKSCREEN
},{"contextmenu", ACTION_WPS_CONTEXT
},
1105 {"playlist", ACTION_WPS_VIEW_PLAYLIST
}, {"pitch", ACTION_WPS_PITCHSCREEN
},
1107 static int parse_touchregion(const char *wps_bufptr
,
1108 struct wps_token
*token
, struct wps_data
*wps_data
)
1112 struct touchregion
*region
= NULL
;
1113 const char *ptr
= wps_bufptr
;
1115 const char pb_string
[] = "progressbar";
1116 const char vol_string
[] = "volume";
1119 /* format: %T|x|y|width|height|action|
1120 * if action starts with & the area must be held to happen
1122 * play - play/pause playback
1123 * stop - stop playback, exit the wps
1126 * ffwd - seek forward
1127 * rwd - seek backwards
1128 * menu - go back to the main menu
1129 * browse - go back to the file/db browser
1130 * shuffle - toggle shuffle mode
1131 * repmode - cycle the repeat mode
1132 * quickscreen - go into the quickscreen
1133 * contextmenu - open the context menu
1134 * playlist - go into the playlist
1135 * pitch - go into the pitchscreen
1140 return WPS_ERROR_INVALID_PARAM
;
1143 if (!(ptr
= parse_list("dddds", NULL
, '|', ptr
, &x
, &y
, &w
, &h
, &action
)))
1144 return WPS_ERROR_INVALID_PARAM
;
1146 /* Check there is a terminating | */
1148 return WPS_ERROR_INVALID_PARAM
;
1150 region
= skin_buffer_alloc(sizeof(struct touchregion
));
1152 return WPS_ERROR_INVALID_PARAM
;
1154 /* should probably do some bounds checking here with the viewport... but later */
1155 region
->action
= ACTION_NONE
;
1160 region
->wvp
= curr_vp
;
1162 if(!strncmp(pb_string
, action
, sizeof(pb_string
)-1)
1163 && *(action
+ sizeof(pb_string
)-1) == '|')
1164 region
->type
= WPS_TOUCHREGION_SCROLLBAR
;
1165 else if(!strncmp(vol_string
, action
, sizeof(vol_string
)-1)
1166 && *(action
+ sizeof(vol_string
)-1) == '|')
1167 region
->type
= WPS_TOUCHREGION_VOLUME
;
1170 region
->type
= WPS_TOUCHREGION_ACTION
;
1175 region
->repeat
= true;
1178 region
->repeat
= false;
1181 imax
= ARRAYLEN(touchactions
);
1182 while ((region
->action
== ACTION_NONE
) &&
1185 /* try to match with one of our touchregion screens */
1186 int len
= strlen(touchactions
[i
].s
);
1187 if (!strncmp(touchactions
[i
].s
, action
, len
)
1188 && *(action
+len
) == '|')
1189 region
->action
= touchactions
[i
].action
;
1192 if (region
->action
== ACTION_NONE
)
1193 return WPS_ERROR_INVALID_PARAM
;
1195 struct skin_token_list
*item
= new_skin_token_list_item(NULL
, region
);
1197 return WPS_ERROR_INVALID_PARAM
;
1198 add_to_ll_chain(&wps_data
->touchregions
, item
);
1199 return skip_end_of_line(wps_bufptr
);
1203 /* Parse a generic token from the given string. Return the length read */
1204 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
1206 int skip
= 0, taglen
= 0, ret
;
1207 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
1208 const struct wps_tag
*tag
;
1219 /* escaped characters */
1220 token
->type
= WPS_TOKEN_CHARACTER
;
1221 token
->value
.c
= *wps_bufptr
;
1223 wps_data
->num_tokens
++;
1227 /* conditional tag */
1228 token
->type
= WPS_TOKEN_CONDITIONAL
;
1230 condindex
[level
] = wps_data
->num_tokens
;
1231 numoptions
[level
] = 1;
1232 wps_data
->num_tokens
++;
1233 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
1234 if (ret
< 0) return ret
;
1239 /* find what tag we have */
1240 for (tag
= all_tags
;
1241 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
1244 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
1245 token
->type
= tag
->type
;
1246 wps_data
->sublines
[wps_data
->num_sublines
].line_type
|=
1249 /* if the tag has a special parsing function, we call it */
1250 if (tag
->parse_func
)
1252 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
1253 if (ret
< 0) return ret
;
1257 /* Some tags we don't want to save as tokens */
1258 if (tag
->type
== WPS_NO_TOKEN
)
1261 /* tags that start with 'F', 'I' or 'D' are for the next file */
1262 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
1263 *(tag
->name
) == 'D')
1266 wps_data
->num_tokens
++;
1275 data is the pointer to the structure where the parsed WPS should be stored.
1277 wps_bufptr points to the string containing the WPS tags */
1278 #define TOKEN_BLOCK_SIZE 128
1279 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
, bool debug
)
1281 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
1283 enum wps_parse_error fail
= PARSE_OK
;
1285 int max_tokens
= TOKEN_BLOCK_SIZE
;
1286 size_t buf_free
= 0;
1290 /* allocate enough RAM for a reasonable skin, grow as needed.
1291 * Free any used RAM before loading the images to be 100% RAM efficient */
1292 data
->tokens
= (struct wps_token
*)skin_buffer_grab(&buf_free
);
1293 if (sizeof(struct wps_token
)*max_tokens
>= buf_free
)
1295 skin_buffer_increment(max_tokens
* sizeof(struct wps_token
), false);
1296 data
->num_tokens
= 0;
1298 while(*wps_bufptr
&& !fail
1299 && data
->num_lines
< WPS_MAX_LINES
)
1301 /* first make sure there is enough room for tokens */
1302 if (max_tokens
-1 == data
->num_tokens
)
1304 int extra_tokens
= TOKEN_BLOCK_SIZE
;
1305 size_t needed
= extra_tokens
* sizeof(struct wps_token
);
1306 /* do some smarts here to grow the array a bit */
1307 if (skin_buffer_freespace() < needed
)
1309 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1312 skin_buffer_increment(needed
, false);
1313 max_tokens
+= extra_tokens
;
1316 switch(*wps_bufptr
++)
1321 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
1323 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
1326 else if (level
>= WPS_MAX_COND_LEVEL
- 1)
1328 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1334 /* Alternating sublines separator */
1336 if (level
>= 0) /* there are unclosed conditionals */
1338 fail
= PARSE_FAIL_UNCLOSED_COND
;
1342 if (data
->num_sublines
+1 < WPS_MAX_SUBLINES
)
1343 wps_start_new_subline(data
);
1345 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1349 /* Conditional list start */
1351 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1353 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1357 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1358 lastcond
[level
] = data
->num_tokens
++;
1361 /* Conditional list end */
1363 if (level
< 0) /* not in a conditional, invalid char */
1365 fail
= PARSE_FAIL_INVALID_CHAR
;
1369 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1370 if (lastcond
[level
])
1371 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1374 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1378 lastcond
[level
] = 0;
1380 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1384 /* Conditional list option */
1386 if (level
< 0) /* not in a conditional, invalid char */
1388 fail
= PARSE_FAIL_INVALID_CHAR
;
1392 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1393 if (lastcond
[level
])
1394 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1397 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1401 lastcond
[level
] = data
->num_tokens
;
1402 numoptions
[level
]++;
1408 if (level
>= 0) /* there are unclosed conditionals */
1410 fail
= PARSE_FAIL_UNCLOSED_COND
;
1414 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1417 /* End of this line */
1419 if (level
>= 0) /* there are unclosed conditionals */
1421 fail
= PARSE_FAIL_UNCLOSED_COND
;
1426 wps_start_new_subline(data
);
1427 data
->num_lines
++; /* Start a new line */
1429 if ((data
->num_lines
< WPS_MAX_LINES
) &&
1430 (data
->num_sublines
< WPS_MAX_SUBLINES
))
1432 data
->lines
[data
->num_lines
].first_subline_idx
=
1435 data
->sublines
[data
->num_sublines
].first_token_idx
=
1444 unsigned int len
= 1;
1445 const char *string_start
= wps_bufptr
- 1;
1447 /* find the length of the string */
1448 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1449 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1450 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1451 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1457 /* look if we already have that string */
1460 struct skin_token_list
*list
= data
->strings
;
1463 str
= (char*)list
->token
->value
.data
;
1464 found
= (strlen(str
) == len
&&
1465 strncmp(string_start
, str
, len
) == 0);
1467 break; /* break here because the list item is
1468 used if its found */
1471 /* If a matching string is found, found is true and i is
1472 the index of the string. If not, found is false */
1477 str
= (char*)skin_buffer_alloc(len
+1);
1480 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1483 strlcpy(str
, string_start
, len
+1);
1484 struct skin_token_list
*item
=
1485 new_skin_token_list_item(&data
->tokens
[data
->num_tokens
], str
);
1488 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1491 add_to_ll_chain(&data
->strings
, item
);
1495 /* another occurrence of an existing string */
1496 data
->tokens
[data
->num_tokens
].value
.data
= list
->token
->value
.data
;
1498 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1505 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1506 fail
= PARSE_FAIL_UNCLOSED_COND
;
1508 if (*wps_bufptr
&& !fail
)
1509 /* one of the limits of the while loop was exceeded */
1510 fail
= PARSE_FAIL_LIMITS_EXCEEDED
;
1513 /* freeup unused tokens */
1514 skin_buffer_free_from_front(sizeof(struct wps_token
)
1515 * (max_tokens
- data
->num_tokens
));
1516 curr_vp
->last_line
= data
->num_lines
- 1;
1518 #if defined(DEBUG) || defined(SIMULATOR)
1520 print_debug_info(data
, fail
, line
);
1528 static void wps_reset(struct wps_data
*data
)
1530 #ifdef HAVE_REMOTE_LCD
1531 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1533 memset(data
, 0, sizeof(*data
));
1534 skin_data_init(data
);
1535 #ifdef HAVE_REMOTE_LCD
1536 data
->remote_wps
= rwps
;
1540 #ifdef HAVE_LCD_BITMAP
1541 static bool load_skin_bmp(struct wps_data
*wps_data
, struct bitmap
*bitmap
, char* bmpdir
)
1543 (void)wps_data
; /* only needed for remote targets */
1544 bool loaded
= false;
1545 char img_path
[MAX_PATH
];
1546 get_image_filename(bitmap
->data
, bmpdir
,
1547 img_path
, sizeof(img_path
));
1549 /* load the image */
1551 #ifdef HAVE_REMOTE_LCD
1552 if (wps_data
->remote_wps
)
1553 format
= FORMAT_ANY
|FORMAT_REMOTE
;
1556 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
1559 char* imgbuf
= (char*)skin_buffer_grab(&max_buf
);
1560 bitmap
->data
= imgbuf
;
1561 int ret
= read_bmp_file(img_path
, bitmap
, max_buf
, format
, NULL
);
1565 skin_buffer_increment(ret
, true);
1570 /* Abort if we can't load an image */
1571 DEBUGF("ERR: Failed to load image - %s\n",img_path
);
1577 static bool load_skin_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1579 struct skin_token_list
*list
;
1580 /* do the progressbars */
1581 list
= wps_data
->progressbars
;
1584 struct progressbar
*pb
= (struct progressbar
*)list
->token
->value
.data
;
1587 pb
->have_bitmap_pb
= load_skin_bmp(wps_data
, &pb
->bm
, bmpdir
);
1591 /* regular images */
1592 list
= wps_data
->images
;
1595 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
1598 img
->loaded
= load_skin_bmp(wps_data
, &img
->bm
, bmpdir
);
1600 img
->subimage_height
= img
->bm
.height
/ img
->num_subimages
;
1605 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1606 if (bmp_names
[BACKDROP_BMP
])
1608 int screen
= SCREEN_MAIN
;
1609 char img_path
[MAX_PATH
];
1610 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1611 img_path
, sizeof(img_path
));
1612 #if defined(HAVE_REMOTE_LCD)
1613 /* We only need to check LCD type if there is a remote LCD */
1614 if (wps_data
->remote_wps
)
1615 screen
= SCREEN_REMOTE
;
1617 screens
[screen
].backdrop_load(BACKDROP_SKIN_WPS
, img_path
);
1619 #endif /* has backdrop support */
1621 /* If we got here, everything was OK */
1625 #endif /* HAVE_LCD_BITMAP */
1627 /* to setup up the wps-data from a format-buffer (isfile = false)
1628 from a (wps-)file (isfile = true)*/
1629 bool skin_data_load(struct wps_data
*wps_data
,
1630 struct screen
*display
,
1634 #ifdef HAVE_ALBUMART
1635 struct mp3entry
*curtrack
;
1638 int wps_uses_albumart
= wps_data
->wps_uses_albumart
;
1639 int albumart_max_height
= wps_data
->albumart_max_height
;
1640 int albumart_max_width
= wps_data
->albumart_max_width
;
1642 if (!wps_data
|| !buf
)
1645 wps_reset(wps_data
);
1647 curr_vp
= skin_buffer_alloc(sizeof(struct skin_viewport
));
1650 struct skin_token_list
*list
= new_skin_token_list_item(NULL
, curr_vp
);
1653 add_to_ll_chain(&wps_data
->viewports
, list
);
1656 /* Initialise the first (default) viewport */
1657 curr_vp
->label
= VP_DEFAULT_LABEL
;
1659 curr_vp
->vp
.width
= display
->getwidth();
1660 curr_vp
->vp
.height
= display
->getheight();
1662 curr_vp
->hidden_flags
= 0;
1663 switch (statusbar_position(display
->screen_type
))
1669 curr_vp
->vp
.y
= STATUSBAR_HEIGHT
;
1670 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1672 case STATUSBAR_BOTTOM
:
1674 curr_vp
->vp
.height
-= STATUSBAR_HEIGHT
;
1677 #ifdef HAVE_LCD_BITMAP
1678 curr_vp
->vp
.font
= FONT_UI
;
1679 curr_vp
->vp
.drawmode
= DRMODE_SOLID
;
1682 if (display
->depth
> 1)
1684 curr_vp
->vp
.fg_pattern
= display
->get_foreground();
1685 curr_vp
->vp
.bg_pattern
= display
->get_background();
1690 return wps_parse(wps_data
, buf
, false);
1695 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1696 * wants to be a virtual file. Feel free to modify dirbrowse()
1697 * if you're feeling brave.
1700 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1702 global_settings
.wps_file
[0] = 0;
1706 #ifdef HAVE_REMOTE_LCD
1707 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1709 global_settings
.rwps_file
[0] = 0;
1713 #endif /* __PCTOOL__ */
1715 int fd
= open_utf8(buf
, O_RDONLY
);
1720 /* get buffer space from the plugin buffer */
1721 size_t buffersize
= 0;
1722 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1727 /* copy the file's content to the buffer for parsing,
1728 ensuring that every line ends with a newline char. */
1729 unsigned int start
= 0;
1730 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1732 start
+= strlen(wps_buffer
+ start
);
1733 if (start
< buffersize
- 1)
1735 wps_buffer
[start
++] = '\n';
1736 wps_buffer
[start
] = 0;
1745 #ifdef HAVE_LCD_BITMAP
1746 /* Set all filename pointers to NULL */
1747 memset(bmp_names
, 0, sizeof(bmp_names
));
1750 /* parse the WPS source */
1751 if (!wps_parse(wps_data
, wps_buffer
, true)) {
1752 wps_reset(wps_data
);
1756 wps_data
->wps_loaded
= true;
1758 #ifdef HAVE_LCD_BITMAP
1759 /* get the bitmap dir */
1760 char bmpdir
[MAX_PATH
];
1761 char *dot
= strrchr(buf
, '.');
1763 strlcpy(bmpdir
, buf
, dot
- buf
+ 1);
1765 /* load the bitmaps that were found by the parsing */
1766 if (!load_skin_bitmaps(wps_data
, bmpdir
)) {
1767 wps_reset(wps_data
);
1771 #ifdef HAVE_ALBUMART
1772 status
= audio_status();
1773 if (((!wps_uses_albumart
&& wps_data
->wps_uses_albumart
) ||
1774 (wps_data
->wps_uses_albumart
&&
1775 (albumart_max_height
!= wps_data
->albumart_max_height
||
1776 albumart_max_width
!= wps_data
->albumart_max_width
))) &&
1777 status
& AUDIO_STATUS_PLAY
)
1779 curtrack
= audio_current_track();
1780 offset
= curtrack
->offset
;
1782 if (!(status
& AUDIO_STATUS_PAUSE
))
1786 #if defined(DEBUG) || defined(SIMULATOR)