1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
32 #ifdef HAVE_LCD_BITMAP
36 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
42 #define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
43 #define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
45 #define WPS_ERROR_INVALID_PARAM -1
47 #define PARSE_FAIL_UNCLOSED_COND 1
48 #define PARSE_FAIL_INVALID_CHAR 2
49 #define PARSE_FAIL_COND_SYNTAX_ERROR 3
50 #define PARSE_FAIL_COND_INVALID_PARAM 4
52 /* level of current conditional.
53 -1 means we're not in a conditional. */
54 static int level
= -1;
56 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
57 or WPS_TOKEN_CONDITIONAL_START in current level */
58 static int lastcond
[WPS_MAX_COND_LEVEL
];
60 /* index of the WPS_TOKEN_CONDITIONAL in current level */
61 static int condindex
[WPS_MAX_COND_LEVEL
];
63 /* number of condtional options in current level */
64 static int numoptions
[WPS_MAX_COND_LEVEL
];
66 /* the current line in the file */
69 #ifdef HAVE_LCD_BITMAP
72 #define MAX_BITMAPS MAX_IMAGES+2 /* WPS images + pbar bitmap + backdrop */
74 #define MAX_BITMAPS MAX_IMAGES+1 /* WPS images + pbar bitmap */
77 #define PROGRESSBAR_BMP MAX_IMAGES
78 #define BACKDROP_BMP MAX_IMAGES+1
80 /* pointers to the bitmap filenames in the WPS source */
81 static const char *bmp_names
[MAX_BITMAPS
];
83 #endif /* HAVE_LCD_BITMAP */
86 /* debugging function */
87 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
90 static void wps_reset(struct wps_data
*data
);
92 /* Function for parsing of details for a token. At the moment the
93 function is called, the token type has already been set. The
94 function must fill in the details and possibly add more tokens
95 to the token array. It should return the number of chars that
98 wps_bufptr points to the char following the tag (i.e. where
100 token is the pointer to the 'main' token being parsed
102 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
103 struct wps_token
*token
, struct wps_data
*wps_data
);
106 enum wps_token_type type
;
108 unsigned char refresh_type
;
109 const wps_tag_parse_func parse_func
;
112 /* prototypes of all special parse functions : */
113 static int parse_subline_timeout(const char *wps_bufptr
,
114 struct wps_token
*token
, struct wps_data
*wps_data
);
115 static int parse_progressbar(const char *wps_bufptr
,
116 struct wps_token
*token
, struct wps_data
*wps_data
);
117 static int parse_dir_level(const char *wps_bufptr
,
118 struct wps_token
*token
, struct wps_data
*wps_data
);
120 #ifdef HAVE_LCD_BITMAP
121 static int parse_leftmargin(const char *wps_bufptr
,
122 struct wps_token
*token
, struct wps_data
*wps_data
);
123 static int parse_image_special(const char *wps_bufptr
,
124 struct wps_token
*token
, struct wps_data
*wps_data
);
125 static int parse_statusbar_enable(const char *wps_bufptr
,
126 struct wps_token
*token
, struct wps_data
*wps_data
);
127 static int parse_statusbar_disable(const char *wps_bufptr
,
128 struct wps_token
*token
, struct wps_data
*wps_data
);
129 static int parse_image_display(const char *wps_bufptr
,
130 struct wps_token
*token
, struct wps_data
*wps_data
);
131 static int parse_image_load(const char *wps_bufptr
,
132 struct wps_token
*token
, struct wps_data
*wps_data
);
133 #endif /*HAVE_LCD_BITMAP */
136 static int parse_albumart_load(const char *wps_bufptr
,
137 struct wps_token
*token
, struct wps_data
*wps_data
);
138 static int parse_albumart_conditional(const char *wps_bufptr
,
139 struct wps_token
*token
, struct wps_data
*wps_data
);
140 #endif /* HAVE_ALBUMART */
143 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
145 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
148 /* array of available tags - those with more characters have to go first
149 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
150 static const struct wps_tag all_tags
[] = {
152 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
153 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
154 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
156 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
157 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
158 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
159 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
160 #if CONFIG_CHARGING >= CHARGING_MONITOR
161 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
164 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
167 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
168 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
169 { WPS_TOKEN_RTC_12HOUR_CFG
, "cf", WPS_RTC_REFRESH
, NULL
},
170 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
171 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
172 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
173 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
174 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
175 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
176 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
177 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
178 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
179 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
180 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
181 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
182 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
183 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
184 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
187 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
188 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
189 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
190 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
191 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
192 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
193 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
194 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
195 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
196 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
200 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_DYNAMIC
, NULL
},
201 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_DYNAMIC
, NULL
},
202 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_DYNAMIC
, NULL
},
203 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_DYNAMIC
, NULL
},
204 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_DYNAMIC
, NULL
},
205 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_DYNAMIC
, NULL
},
206 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_DYNAMIC
, NULL
},
207 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_DYNAMIC
, NULL
},
208 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_DYNAMIC
, NULL
},
209 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_DYNAMIC
,
212 /* current metadata */
213 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
214 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
215 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
216 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
217 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
218 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
219 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
220 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
221 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
222 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
223 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
224 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
227 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_DYNAMIC
, NULL
},
228 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_DYNAMIC
, NULL
},
229 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_DYNAMIC
, NULL
},
230 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_DYNAMIC
, NULL
},
231 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_DYNAMIC
, NULL
},
232 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_DYNAMIC
, NULL
},
233 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_DYNAMIC
, NULL
},
234 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_DYNAMIC
, NULL
},
235 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_DYNAMIC
, NULL
},
236 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_DYNAMIC
, NULL
},
237 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_DYNAMIC
, NULL
},
238 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_DYNAMIC
, NULL
},
240 #if (CONFIG_CODEC != MAS3507D)
241 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
244 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
245 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
248 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
250 #ifdef HAS_REMOTE_BUTTON_HOLD
251 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
253 { WPS_TOKEN_UNKNOWN
, "mr", 0, NULL
},
256 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
257 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
259 #ifdef HAVE_LCD_BITMAP
260 { WPS_TOKEN_LEFTMARGIN
, "m", 0, parse_leftmargin
},
263 #ifdef HAVE_LCD_BITMAP
264 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
266 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
267 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
269 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
272 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
274 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
275 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
276 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
277 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
279 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
280 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
281 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
282 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
285 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
286 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
287 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
290 #if CONFIG_CODEC == SWCODEC
291 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
292 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
295 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
296 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_subline_timeout
},
298 #ifdef HAVE_LCD_BITMAP
299 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
300 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
302 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
304 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
305 parse_image_display
},
307 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
308 { WPS_TOKEN_IMAGE_PROGRESS_BAR
, "P", 0, parse_image_special
},
310 { WPS_NO_TOKEN
, "Cl", 0, parse_albumart_load
},
311 { WPS_TOKEN_ALBUMART_DISPLAY
, "C", WPS_REFRESH_STATIC
,
312 parse_albumart_conditional
},
314 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
315 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
319 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
320 /* the array MUST end with an empty string (first char is \0) */
323 /* Returns the number of chars that should be skipped to jump
324 immediately after the first eol, i.e. to the start of the next line */
325 static int skip_end_of_line(const char *wps_bufptr
)
329 while(*(wps_bufptr
+ skip
) != '\n')
334 /* Starts a new subline in the current line during parsing */
335 static void wps_start_new_subline(struct wps_data
*data
)
337 data
->num_sublines
++;
338 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
339 data
->lines
[data
->num_lines
].num_sublines
++;
342 #ifdef HAVE_LCD_BITMAP
344 static int parse_statusbar_enable(const char *wps_bufptr
,
345 struct wps_token
*token
,
346 struct wps_data
*wps_data
)
348 (void)token
; /* Kill warnings */
349 wps_data
->wps_sb_tag
= true;
350 wps_data
->show_sb_on_wps
= true;
351 return skip_end_of_line(wps_bufptr
);
354 static int parse_statusbar_disable(const char *wps_bufptr
,
355 struct wps_token
*token
,
356 struct wps_data
*wps_data
)
358 (void)token
; /* Kill warnings */
359 wps_data
->wps_sb_tag
= true;
360 wps_data
->show_sb_on_wps
= false;
361 return skip_end_of_line(wps_bufptr
);
364 static bool load_bitmap(struct wps_data
*wps_data
,
369 #ifdef HAVE_REMOTE_LCD
370 if (wps_data
->remote_wps
)
371 format
= FORMAT_ANY
|FORMAT_REMOTE
;
374 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
376 int ret
= read_bmp_file(filename
, bm
,
377 wps_data
->img_buf_free
,
384 /* Always consume an even number of bytes */
386 wps_data
->img_buf_ptr
+= ret
;
387 wps_data
->img_buf_free
-= ret
;
395 static int get_image_id(int c
)
397 if(c
>= 'a' && c
<= 'z')
399 else if(c
>= 'A' && c
<= 'Z')
405 static char *get_image_filename(const char *start
, const char* bmpdir
,
406 char *buf
, int buf_size
)
408 const char *end
= strchr(start
, '|');
410 if ( !end
|| (end
- start
) >= (buf_size
- ROCKBOX_DIR_LEN
- 2) )
416 int bmpdirlen
= strlen(bmpdir
);
419 buf
[bmpdirlen
] = '/';
420 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
421 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
426 static int parse_image_display(const char *wps_bufptr
,
427 struct wps_token
*token
,
428 struct wps_data
*wps_data
)
431 int n
= get_image_id(*wps_bufptr
);
435 /* invalid picture display tag */
436 return WPS_ERROR_INVALID_PARAM
;
444 static int parse_image_load(const char *wps_bufptr
,
445 struct wps_token
*token
,
446 struct wps_data
*wps_data
)
449 const char *ptr
= wps_bufptr
;
450 const char *pos
= NULL
;
453 /* format: %x|n|filename.bmp|x|y|
454 or %xl|n|filename.bmp|x|y| */
456 ptr
= strchr(ptr
, '|') + 1;
457 pos
= strchr(ptr
, '|');
458 newline
= strchr(ptr
, '\n');
460 if (!pos
|| pos
> newline
)
463 /* get the image ID */
464 n
= get_image_id(*ptr
);
466 /* check the image number and load state */
467 if(n
< 0 || n
>= MAX_IMAGES
|| wps_data
->img
[n
].loaded
)
469 /* Invalid image ID */
470 return WPS_ERROR_INVALID_PARAM
;
478 pos
= strchr(ptr
, '|');
482 pos
= strchr(ptr
, '|');
483 if (pos
&& pos
< newline
)
484 wps_data
->img
[n
].x
= atoi(ptr
);
487 /* weird syntax, bail out */
488 return WPS_ERROR_INVALID_PARAM
;
493 pos
= strchr(ptr
, '|');
494 if (pos
&& pos
< newline
)
495 wps_data
->img
[n
].y
= atoi(ptr
);
498 /* weird syntax, bail out */
499 return WPS_ERROR_INVALID_PARAM
;
502 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
503 wps_data
->img
[n
].always_display
= true;
505 /* Skip the rest of the line */
506 return skip_end_of_line(wps_bufptr
);
509 static int parse_image_special(const char *wps_bufptr
,
510 struct wps_token
*token
,
511 struct wps_data
*wps_data
)
513 (void)wps_data
; /* kill warning */
514 const char *pos
= NULL
;
517 pos
= strchr(wps_bufptr
+ 1, '|');
518 newline
= strchr(wps_bufptr
, '\n');
521 return WPS_ERROR_INVALID_PARAM
;
523 if (token
->type
== WPS_TOKEN_IMAGE_PROGRESS_BAR
)
525 /* format: %P|filename.bmp| */
526 bmp_names
[PROGRESSBAR_BMP
] = wps_bufptr
+ 1;
529 else if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
531 /* format: %X|filename.bmp| */
532 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
536 /* Skip the rest of the line */
537 return skip_end_of_line(wps_bufptr
);
540 #endif /* HAVE_LCD_BITMAP */
542 static int parse_dir_level(const char *wps_bufptr
,
543 struct wps_token
*token
,
544 struct wps_data
*wps_data
)
546 char val
[] = { *wps_bufptr
, '\0' };
547 token
->value
.i
= atoi(val
);
548 (void)wps_data
; /* Kill warnings */
552 static int parse_subline_timeout(const char *wps_bufptr
,
553 struct wps_token
*token
,
554 struct wps_data
*wps_data
)
558 bool have_point
= false;
559 bool have_tenth
= false;
561 (void)wps_data
; /* Kill the warning */
563 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
565 if (*wps_bufptr
!= '.')
568 val
+= *wps_bufptr
- '0';
584 if (have_tenth
== false)
587 token
->value
.i
= val
;
592 static int parse_progressbar(const char *wps_bufptr
,
593 struct wps_token
*token
,
594 struct wps_data
*wps_data
)
596 (void)token
; /* Kill warnings */
597 #ifdef HAVE_LCD_BITMAP
600 &wps_data
->progress_height
,
601 &wps_data
->progress_start
,
602 &wps_data
->progress_end
,
603 &wps_data
->progress_top
};
605 /* default values : */
606 wps_data
->progress_height
= 6;
607 wps_data
->progress_start
= 0;
608 wps_data
->progress_end
= 0;
609 wps_data
->progress_top
= -1;
612 char *newline
= strchr(wps_bufptr
, '\n');
613 char *prev
= strchr(wps_bufptr
, '|');
614 if (prev
&& prev
< newline
) {
615 char *next
= strchr(prev
+1, '|');
616 while (i
< 4 && next
&& next
< newline
)
618 *(vals
[i
++]) = atoi(++prev
);
619 prev
= strchr(prev
, '|');
620 next
= strchr(++next
, '|');
623 if (wps_data
->progress_height
< 3)
624 wps_data
->progress_height
= 3;
625 if (wps_data
->progress_end
< wps_data
->progress_start
+ 3)
626 wps_data
->progress_end
= 0;
629 return newline
- wps_bufptr
;
633 if (*(wps_bufptr
-1) == 'f')
634 wps_data
->full_line_progressbar
= true;
636 wps_data
->full_line_progressbar
= false;
644 static int parse_albumart_load(const char *wps_bufptr
,
645 struct wps_token
*token
,
646 struct wps_data
*wps_data
)
648 const char *_pos
, *newline
;
650 const short xalign_mask
= WPS_ALBUMART_ALIGN_LEFT
|
651 WPS_ALBUMART_ALIGN_CENTER
|
652 WPS_ALBUMART_ALIGN_RIGHT
;
653 const short yalign_mask
= WPS_ALBUMART_ALIGN_TOP
|
654 WPS_ALBUMART_ALIGN_CENTER
|
655 WPS_ALBUMART_ALIGN_BOTTOM
;
657 (void)token
; /* silence warning */
659 /* reset albumart info in wps */
660 wps_data
->wps_uses_albumart
= WPS_ALBUMART_NONE
;
661 wps_data
->albumart_max_width
= -1;
662 wps_data
->albumart_max_height
= -1;
663 wps_data
->albumart_xalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
664 wps_data
->albumart_yalign
= WPS_ALBUMART_ALIGN_CENTER
; /* default */
666 /* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
668 newline
= strchr(wps_bufptr
, '\n');
670 /* initial validation and parsing of x and y components */
671 if (*wps_bufptr
!= '|')
672 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl7 */
674 _pos
= wps_bufptr
+ 1;
676 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|@ */
677 wps_data
->albumart_x
= atoi(_pos
);
679 _pos
= strchr(_pos
, '|');
680 if (!_pos
|| _pos
> newline
|| !isdigit(*(++_pos
)))
681 return WPS_ERROR_INVALID_PARAM
; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
683 wps_data
->albumart_y
= atoi(_pos
);
685 _pos
= strchr(_pos
, '|');
686 if (!_pos
|| _pos
> newline
)
687 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after y coordinate
690 /* parsing width field */
694 /* apply each modifier in turn */
701 wps_data
->albumart_xalign
=
702 (wps_data
->albumart_xalign
& xalign_mask
) |
703 WPS_ALBUMART_ALIGN_LEFT
;
707 wps_data
->albumart_xalign
=
708 (wps_data
->albumart_xalign
& xalign_mask
) |
709 WPS_ALBUMART_ALIGN_CENTER
;
714 wps_data
->albumart_xalign
=
715 (wps_data
->albumart_xalign
& xalign_mask
) |
716 WPS_ALBUMART_ALIGN_RIGHT
;
720 wps_data
->albumart_xalign
|= WPS_ALBUMART_DECREASE
;
724 wps_data
->albumart_xalign
|= WPS_ALBUMART_INCREASE
;
728 wps_data
->albumart_xalign
|=
729 (WPS_ALBUMART_DECREASE
| WPS_ALBUMART_INCREASE
);
736 /* extract max width data */
739 if (!isdigit(*_pos
)) /* malformed token: e.g. %Cl|7|59|# */
740 return WPS_ERROR_INVALID_PARAM
;
742 wps_data
->albumart_max_width
= atoi(_pos
);
744 _pos
= strchr(_pos
, '|');
745 if (!_pos
|| _pos
> newline
)
746 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no | after width field
747 e.g. %Cl|7|59|200\n */
750 /* parsing height field */
754 /* apply each modifier in turn */
761 wps_data
->albumart_yalign
=
762 (wps_data
->albumart_yalign
& yalign_mask
) |
763 WPS_ALBUMART_ALIGN_TOP
;
767 wps_data
->albumart_yalign
=
768 (wps_data
->albumart_yalign
& yalign_mask
) |
769 WPS_ALBUMART_ALIGN_CENTER
;
774 wps_data
->albumart_yalign
=
775 (wps_data
->albumart_yalign
& yalign_mask
) |
776 WPS_ALBUMART_ALIGN_BOTTOM
;
780 wps_data
->albumart_yalign
|= WPS_ALBUMART_DECREASE
;
784 wps_data
->albumart_yalign
|= WPS_ALBUMART_INCREASE
;
788 wps_data
->albumart_yalign
|=
789 (WPS_ALBUMART_DECREASE
| WPS_ALBUMART_INCREASE
);
796 /* extract max height data */
800 return WPS_ERROR_INVALID_PARAM
; /* malformed token e.g. %Cl|7|59|200|@ */
802 wps_data
->albumart_max_height
= atoi(_pos
);
804 _pos
= strchr(_pos
, '|');
805 if (!_pos
|| _pos
> newline
)
806 return WPS_ERROR_INVALID_PARAM
; /* malformed token: no closing |
807 e.g. %Cl|7|59|200|200\n */
810 /* if we got here, we parsed everything ok .. ! */
811 if (wps_data
->albumart_max_width
< 0)
812 wps_data
->albumart_max_width
= 0;
813 else if (wps_data
->albumart_max_width
> LCD_WIDTH
)
814 wps_data
->albumart_max_width
= LCD_WIDTH
;
816 if (wps_data
->albumart_max_height
< 0)
817 wps_data
->albumart_max_height
= 0;
818 else if (wps_data
->albumart_max_height
> LCD_HEIGHT
)
819 wps_data
->albumart_max_height
= LCD_HEIGHT
;
821 wps_data
->wps_uses_albumart
= WPS_ALBUMART_LOAD
;
823 /* Skip the rest of the line */
824 return skip_end_of_line(wps_bufptr
);
827 static int parse_albumart_conditional(const char *wps_bufptr
,
828 struct wps_token
*token
,
829 struct wps_data
*wps_data
)
831 struct wps_token
*prevtoken
= token
;
833 if (wps_data
->num_tokens
>= 1 && prevtoken
->type
== WPS_TOKEN_CONDITIONAL
)
835 /* This %C is part of a %?C construct.
836 It's either %?C<blah> or %?Cn<blah> */
837 token
->type
= WPS_TOKEN_ALBUMART_FOUND
;
838 if (*wps_bufptr
== 'n' && *(wps_bufptr
+ 1) == '<')
843 else if (*wps_bufptr
== '<')
849 token
->type
= WPS_NO_TOKEN
;
855 /* This %C tag is in a conditional construct. */
856 wps_data
->albumart_cond_index
= condindex
[level
];
860 #endif /* HAVE_ALBUMART */
862 #ifdef HAVE_LCD_BITMAP
863 static int parse_leftmargin(const char *wps_bufptr
, struct wps_token
*token
,
864 struct wps_data
*wps_data
)
870 (void)wps_data
; /* Kill the warning */
872 /* valid tag looks like %m|12| */
873 if(*wps_bufptr
== '|')
876 newline
= strchr(wps_bufptr
, '\n');
877 if(isdigit(*p
) && (pend
= strchr(p
, '|')) && pend
< newline
)
879 token
->value
.i
= atoi(p
);
880 return pend
- wps_bufptr
+ 1;
884 /* invalid tag syntax */
885 return WPS_ERROR_INVALID_PARAM
;
890 /* Parse a generic token from the given string. Return the length read */
891 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
893 int skip
= 0, taglen
= 0, ret
;
894 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
895 const struct wps_tag
*tag
;
906 /* escaped characters */
907 token
->type
= WPS_TOKEN_CHARACTER
;
908 token
->value
.c
= *wps_bufptr
;
910 wps_data
->num_tokens
++;
914 /* conditional tag */
915 token
->type
= WPS_TOKEN_CONDITIONAL
;
917 condindex
[level
] = wps_data
->num_tokens
;
918 numoptions
[level
] = 1;
919 wps_data
->num_tokens
++;
920 ret
= parse_token(wps_bufptr
+ 1, wps_data
);
921 if (ret
< 0) return ret
;
926 /* find what tag we have */
928 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
931 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
932 token
->type
= tag
->type
;
933 wps_data
->sublines
[wps_data
->num_sublines
].line_type
|=
936 /* if the tag has a special parsing function, we call it */
939 ret
= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
940 if (ret
< 0) return ret
;
944 /* Some tags we don't want to save as tokens */
945 if (tag
->type
== WPS_NO_TOKEN
)
948 /* tags that start with 'F', 'I' or 'D' are for the next file */
949 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
953 wps_data
->num_tokens
++;
962 data is the pointer to the structure where the parsed WPS should be stored.
964 wps_bufptr points to the string containing the WPS tags */
965 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
)
967 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
970 char *stringbuf
= data
->string_buffer
;
971 int stringbuf_used
= 0;
977 while(*wps_bufptr
&& !fail
&& data
->num_tokens
< WPS_MAX_TOKENS
- 1
978 && data
->num_lines
< WPS_MAX_LINES
)
980 switch(*wps_bufptr
++)
985 if ((ret
= parse_token(wps_bufptr
, data
)) < 0)
987 fail
= PARSE_FAIL_COND_INVALID_PARAM
;
993 /* Alternating sublines separator */
995 if (level
>= 0) /* there are unclosed conditionals */
997 fail
= PARSE_FAIL_UNCLOSED_COND
;
1001 if (data
->num_sublines
+1 < WPS_MAX_SUBLINES
)
1002 wps_start_new_subline(data
);
1004 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1008 /* Conditional list start */
1010 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
1012 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1016 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
1017 lastcond
[level
] = data
->num_tokens
++;
1020 /* Conditional list end */
1022 if (level
< 0) /* not in a conditional, invalid char */
1024 fail
= PARSE_FAIL_INVALID_CHAR
;
1028 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
1029 if (lastcond
[level
])
1030 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1033 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1037 lastcond
[level
] = 0;
1039 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
1043 /* Conditional list option */
1045 if (level
< 0) /* not in a conditional, invalid char */
1047 fail
= PARSE_FAIL_INVALID_CHAR
;
1051 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
1052 if (lastcond
[level
])
1053 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
1056 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
1060 lastcond
[level
] = data
->num_tokens
;
1061 numoptions
[level
]++;
1067 if (level
>= 0) /* there are unclosed conditionals */
1069 fail
= PARSE_FAIL_UNCLOSED_COND
;
1073 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
1076 /* End of this line */
1078 if (level
>= 0) /* there are unclosed conditionals */
1080 fail
= PARSE_FAIL_UNCLOSED_COND
;
1085 wps_start_new_subline(data
);
1086 data
->num_lines
++; /* Start a new line */
1088 if ((data
->num_lines
< WPS_MAX_LINES
) &&
1089 (data
->num_sublines
< WPS_MAX_SUBLINES
))
1091 data
->lines
[data
->num_lines
].first_subline_idx
=
1094 data
->sublines
[data
->num_sublines
].first_token_idx
=
1103 unsigned int len
= 1;
1104 const char *string_start
= wps_bufptr
- 1;
1106 /* find the length of the string */
1107 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
1108 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
1109 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
1110 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
1116 /* look if we already have that string */
1120 for (i
= 0, str
= data
->strings
, found
= false;
1121 i
< data
->num_strings
&&
1122 !(found
= (strlen(*str
) == len
&&
1123 strncmp(string_start
, *str
, len
) == 0));
1125 /* If a matching string is found, found is true and i is
1126 the index of the string. If not, found is false */
1128 /* If it's NOT a duplicate, do nothing if we already have
1129 too many unique strings */
1131 (stringbuf_used
< STRING_BUFFER_SIZE
- 1 &&
1132 data
->num_strings
< WPS_MAX_STRINGS
))
1139 if (stringbuf_used
+ len
> STRING_BUFFER_SIZE
- 1)
1140 len
= STRING_BUFFER_SIZE
- stringbuf_used
- 1;
1142 strncpy(stringbuf
, string_start
, len
);
1143 *(stringbuf
+ len
) = '\0';
1145 data
->strings
[data
->num_strings
] = stringbuf
;
1146 stringbuf
+= len
+ 1;
1147 stringbuf_used
+= len
+ 1;
1148 data
->tokens
[data
->num_tokens
].value
.i
=
1150 data
->num_strings
++;
1154 /* another ocurrence of an existing string */
1155 data
->tokens
[data
->num_tokens
].value
.i
= i
;
1157 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
1165 if (!fail
&& level
>= 0) /* there are unclosed conditionals */
1166 fail
= PARSE_FAIL_UNCLOSED_COND
;
1169 print_debug_info(data
, fail
, line
);
1178 #ifdef HAVE_LCD_BITMAP
1179 /* Clear the WPS image cache */
1180 static void wps_images_clear(struct wps_data
*data
)
1183 /* set images to unloaded and not displayed */
1184 for (i
= 0; i
< MAX_IMAGES
; i
++)
1186 data
->img
[i
].loaded
= false;
1187 data
->img
[i
].display
= false;
1188 data
->img
[i
].always_display
= false;
1190 data
->progressbar
.have_bitmap_pb
= false;
1194 /* initial setup of wps_data */
1195 void wps_data_init(struct wps_data
*wps_data
)
1197 #ifdef HAVE_LCD_BITMAP
1198 wps_images_clear(wps_data
);
1199 wps_data
->wps_sb_tag
= false;
1200 wps_data
->show_sb_on_wps
= false;
1201 wps_data
->img_buf_ptr
= wps_data
->img_buf
; /* where in image buffer */
1202 wps_data
->img_buf_free
= IMG_BUFSIZE
; /* free space in image buffer */
1203 wps_data
->peak_meter_enabled
= false;
1204 #else /* HAVE_LCD_CHARCELLS */
1206 for (i
= 0; i
< 8; i
++)
1208 wps_data
->wps_progress_pat
[i
] = 0;
1210 wps_data
->full_line_progressbar
= false;
1212 wps_data
->wps_loaded
= false;
1215 static void wps_reset(struct wps_data
*data
)
1217 #ifdef HAVE_REMOTE_LCD
1218 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
1220 memset(data
, 0, sizeof(*data
));
1221 #ifdef HAVE_ALBUMART
1222 data
->wps_uses_albumart
= WPS_ALBUMART_NONE
;
1224 wps_data_init(data
);
1225 #ifdef HAVE_REMOTE_LCD
1226 data
->remote_wps
= rwps
;
1230 #ifdef HAVE_LCD_BITMAP
1233 static void clear_bmp_names(void)
1236 for (n
= 0; n
< MAX_BITMAPS
; n
++)
1238 bmp_names
[n
] = NULL
;
1242 static void load_wps_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
1244 char img_path
[MAX_PATH
];
1245 struct bitmap
*bitmap
;
1249 for (n
= 0; n
< BACKDROP_BMP
; n
++)
1253 get_image_filename(bmp_names
[n
], bmpdir
,
1254 img_path
, sizeof(img_path
));
1256 if (n
== PROGRESSBAR_BMP
) {
1257 /* progressbar bitmap */
1258 bitmap
= &wps_data
->progressbar
.bm
;
1259 loaded
= &wps_data
->progressbar
.have_bitmap_pb
;
1261 /* regular bitmap */
1262 bitmap
= &wps_data
->img
[n
].bm
;
1263 loaded
= &wps_data
->img
[n
].loaded
;
1266 /* load the image */
1267 bitmap
->data
= wps_data
->img_buf_ptr
;
1268 if (load_bitmap(wps_data
, img_path
, bitmap
))
1275 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
1276 if (bmp_names
[BACKDROP_BMP
])
1278 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
1279 img_path
, sizeof(img_path
));
1280 #ifdef HAVE_REMOTE_LCD
1281 if (wps_data
->remote_wps
)
1282 #if LCD_REMOTE_DEPTH > 1
1283 load_remote_wps_backdrop(img_path
)
1287 #endif /* HAVE_REMOTE_LCD */
1288 load_wps_backdrop(img_path
);
1290 #endif /* has backdrop support */
1293 #endif /* HAVE_LCD_BITMAP */
1295 /* Skip leading UTF-8 BOM, if present. */
1296 static char *skip_utf8_bom(char *buf
)
1298 unsigned char *s
= (unsigned char *)buf
;
1300 if(s
[0] == 0xef && s
[1] == 0xbb && s
[2] == 0xbf)
1308 /* to setup up the wps-data from a format-buffer (isfile = false)
1309 from a (wps-)file (isfile = true)*/
1310 bool wps_data_load(struct wps_data
*wps_data
,
1314 if (!wps_data
|| !buf
)
1317 wps_reset(wps_data
);
1321 return wps_parse(wps_data
, buf
);
1326 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1327 * wants to be a virtual file. Feel free to modify dirbrowse()
1328 * if you're feeling brave.
1331 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1333 global_settings
.wps_file
[0] = 0;
1337 #ifdef HAVE_REMOTE_LCD
1338 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1340 global_settings
.rwps_file
[0] = 0;
1344 #endif /* __PCTOOL__ */
1346 int fd
= open(buf
, O_RDONLY
);
1351 /* get buffer space from the plugin buffer */
1352 size_t buffersize
= 0;
1353 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1358 /* copy the file's content to the buffer for parsing,
1359 ensuring that every line ends with a newline char. */
1360 unsigned int start
= 0;
1361 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1363 start
+= strlen(wps_buffer
+ start
);
1364 if (start
< buffersize
- 1)
1366 wps_buffer
[start
++] = '\n';
1367 wps_buffer
[start
] = 0;
1376 #ifdef HAVE_LCD_BITMAP
1380 /* Skip leading UTF-8 BOM, if present. */
1381 wps_buffer
= skip_utf8_bom(wps_buffer
);
1383 /* parse the WPS source */
1384 if (!wps_parse(wps_data
, wps_buffer
))
1387 wps_data
->wps_loaded
= true;
1389 #ifdef HAVE_LCD_BITMAP
1390 /* get the bitmap dir */
1391 char bmpdir
[MAX_PATH
];
1393 char *dot
= strrchr(buf
, '.');
1394 bmpdirlen
= dot
- buf
;
1395 strncpy(bmpdir
, buf
, dot
- buf
);
1396 bmpdir
[bmpdirlen
] = 0;
1398 /* load the bitmaps that were found by the parsing */
1399 load_wps_bitmaps(wps_data
, bmpdir
);
1405 int wps_subline_index(struct wps_data
*data
, int line
, int subline
)
1407 return data
->lines
[line
].first_subline_idx
+ subline
;
1410 int wps_first_token_index(struct wps_data
*data
, int line
, int subline
)
1412 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1413 return data
->sublines
[first_subline_idx
+ subline
].first_token_idx
;
1416 int wps_last_token_index(struct wps_data
*data
, int line
, int subline
)
1418 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1419 int idx
= first_subline_idx
+ subline
;
1420 if (idx
< data
->num_sublines
- 1)
1422 /* This subline ends where the next begins */
1423 return data
->sublines
[idx
+1].first_token_idx
- 1;
1427 /* The last subline goes to the end */
1428 return data
->num_tokens
- 1;