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 PARSE_FAIL_UNCLOSED_COND 1
46 #define PARSE_FAIL_INVALID_CHAR 2
47 #define PARSE_FAIL_COND_SYNTAX_ERROR 3
49 /* level of current conditional.
50 -1 means we're not in a conditional. */
51 static int level
= -1;
53 /* index of the last WPS_TOKEN_CONDITIONAL_OPTION
54 or WPS_TOKEN_CONDITIONAL_START in current level */
55 static int lastcond
[WPS_MAX_COND_LEVEL
];
57 /* index of the WPS_TOKEN_CONDITIONAL in current level */
58 static int condindex
[WPS_MAX_COND_LEVEL
];
60 /* number of condtional options in current level */
61 static int numoptions
[WPS_MAX_COND_LEVEL
];
63 /* the current line in the file */
66 #ifdef HAVE_LCD_BITMAP
69 #define MAX_BITMAPS MAX_IMAGES+2 /* WPS images + pbar bitmap + backdrop */
71 #define MAX_BITMAPS MAX_IMAGES+1 /* WPS images + pbar bitmap */
74 #define PROGRESSBAR_BMP MAX_IMAGES
75 #define BACKDROP_BMP MAX_IMAGES+1
77 /* pointers to the bitmap filenames in the WPS source */
78 static const char *bmp_names
[MAX_BITMAPS
];
80 #endif /* HAVE_LCD_BITMAP */
83 /* debugging function */
84 extern void print_debug_info(struct wps_data
*data
, int fail
, int line
);
87 static void wps_reset(struct wps_data
*data
);
89 /* Function for parsing of details for a token. At the moment the
90 function is called, the token type has already been set. The
91 function must fill in the details and possibly add more tokens
92 to the token array. It should return the number of chars that
95 wps_bufptr points to the char following the tag (i.e. where
97 token is the pointer to the 'main' token being parsed
99 typedef int (*wps_tag_parse_func
)(const char *wps_bufptr
,
100 struct wps_token
*token
, struct wps_data
*wps_data
);
103 enum wps_token_type type
;
105 unsigned char refresh_type
;
106 const wps_tag_parse_func parse_func
;
109 /* prototypes of all special parse functions : */
110 static int parse_subline_timeout(const char *wps_bufptr
,
111 struct wps_token
*token
, struct wps_data
*wps_data
);
112 static int parse_progressbar(const char *wps_bufptr
,
113 struct wps_token
*token
, struct wps_data
*wps_data
);
114 static int parse_dir_level(const char *wps_bufptr
,
115 struct wps_token
*token
, struct wps_data
*wps_data
);
116 #ifdef HAVE_LCD_BITMAP
117 static int parse_image_special(const char *wps_bufptr
,
118 struct wps_token
*token
, struct wps_data
*wps_data
);
119 static int parse_statusbar_enable(const char *wps_bufptr
,
120 struct wps_token
*token
, struct wps_data
*wps_data
);
121 static int parse_statusbar_disable(const char *wps_bufptr
,
122 struct wps_token
*token
, struct wps_data
*wps_data
);
123 static int parse_image_display(const char *wps_bufptr
,
124 struct wps_token
*token
, struct wps_data
*wps_data
);
125 static int parse_image_load(const char *wps_bufptr
,
126 struct wps_token
*token
, struct wps_data
*wps_data
);
127 #endif /*HAVE_LCD_BITMAP */
130 #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC
132 #define WPS_RTC_REFRESH WPS_REFRESH_STATIC
135 /* array of available tags - those with more characters have to go first
136 (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */
137 static const struct wps_tag all_tags
[] = {
139 { WPS_TOKEN_ALIGN_CENTER
, "ac", 0, NULL
},
140 { WPS_TOKEN_ALIGN_LEFT
, "al", 0, NULL
},
141 { WPS_TOKEN_ALIGN_RIGHT
, "ar", 0, NULL
},
143 { WPS_TOKEN_BATTERY_PERCENT
, "bl", WPS_REFRESH_DYNAMIC
, NULL
},
144 { WPS_TOKEN_BATTERY_VOLTS
, "bv", WPS_REFRESH_DYNAMIC
, NULL
},
145 { WPS_TOKEN_BATTERY_TIME
, "bt", WPS_REFRESH_DYNAMIC
, NULL
},
146 { WPS_TOKEN_BATTERY_SLEEPTIME
, "bs", WPS_REFRESH_DYNAMIC
, NULL
},
147 #if CONFIG_CHARGING >= CHARGING_MONITOR
148 { WPS_TOKEN_BATTERY_CHARGING
, "bc", WPS_REFRESH_DYNAMIC
, NULL
},
151 { WPS_TOKEN_BATTERY_CHARGER_CONNECTED
,"bp", WPS_REFRESH_DYNAMIC
, NULL
},
154 { WPS_TOKEN_RTC_DAY_OF_MONTH
, "cd", WPS_RTC_REFRESH
, NULL
},
155 { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
,"ce", WPS_RTC_REFRESH
, NULL
},
156 { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
, "cH", WPS_RTC_REFRESH
, NULL
},
157 { WPS_TOKEN_RTC_HOUR_24
, "ck", WPS_RTC_REFRESH
, NULL
},
158 { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
, "cI", WPS_RTC_REFRESH
, NULL
},
159 { WPS_TOKEN_RTC_HOUR_12
, "cl", WPS_RTC_REFRESH
, NULL
},
160 { WPS_TOKEN_RTC_MONTH
, "cm", WPS_RTC_REFRESH
, NULL
},
161 { WPS_TOKEN_RTC_MINUTE
, "cM", WPS_RTC_REFRESH
, NULL
},
162 { WPS_TOKEN_RTC_SECOND
, "cS", WPS_RTC_REFRESH
, NULL
},
163 { WPS_TOKEN_RTC_YEAR_2_DIGITS
, "cy", WPS_RTC_REFRESH
, NULL
},
164 { WPS_TOKEN_RTC_YEAR_4_DIGITS
, "cY", WPS_RTC_REFRESH
, NULL
},
165 { WPS_TOKEN_RTC_AM_PM_UPPER
, "cP", WPS_RTC_REFRESH
, NULL
},
166 { WPS_TOKEN_RTC_AM_PM_LOWER
, "cp", WPS_RTC_REFRESH
, NULL
},
167 { WPS_TOKEN_RTC_WEEKDAY_NAME
, "ca", WPS_RTC_REFRESH
, NULL
},
168 { WPS_TOKEN_RTC_MONTH_NAME
, "cb", WPS_RTC_REFRESH
, NULL
},
169 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
, "cu", WPS_RTC_REFRESH
, NULL
},
170 { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
, "cw", WPS_RTC_REFRESH
, NULL
},
173 { WPS_TOKEN_FILE_BITRATE
, "fb", WPS_REFRESH_STATIC
, NULL
},
174 { WPS_TOKEN_FILE_CODEC
, "fc", WPS_REFRESH_STATIC
, NULL
},
175 { WPS_TOKEN_FILE_FREQUENCY
, "ff", WPS_REFRESH_STATIC
, NULL
},
176 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "fk", WPS_REFRESH_STATIC
, NULL
},
177 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "fm", WPS_REFRESH_STATIC
, NULL
},
178 { WPS_TOKEN_FILE_NAME
, "fn", WPS_REFRESH_STATIC
, NULL
},
179 { WPS_TOKEN_FILE_PATH
, "fp", WPS_REFRESH_STATIC
, NULL
},
180 { WPS_TOKEN_FILE_SIZE
, "fs", WPS_REFRESH_STATIC
, NULL
},
181 { WPS_TOKEN_FILE_VBR
, "fv", WPS_REFRESH_STATIC
, NULL
},
182 { WPS_TOKEN_FILE_DIRECTORY
, "d", WPS_REFRESH_STATIC
,
186 { WPS_TOKEN_FILE_BITRATE
, "Fb", WPS_REFRESH_DYNAMIC
, NULL
},
187 { WPS_TOKEN_FILE_CODEC
, "Fc", WPS_REFRESH_DYNAMIC
, NULL
},
188 { WPS_TOKEN_FILE_FREQUENCY
, "Ff", WPS_REFRESH_DYNAMIC
, NULL
},
189 { WPS_TOKEN_FILE_FREQUENCY_KHZ
, "Fk", WPS_REFRESH_DYNAMIC
, NULL
},
190 { WPS_TOKEN_FILE_NAME_WITH_EXTENSION
, "Fm", WPS_REFRESH_DYNAMIC
, NULL
},
191 { WPS_TOKEN_FILE_NAME
, "Fn", WPS_REFRESH_DYNAMIC
, NULL
},
192 { WPS_TOKEN_FILE_PATH
, "Fp", WPS_REFRESH_DYNAMIC
, NULL
},
193 { WPS_TOKEN_FILE_SIZE
, "Fs", WPS_REFRESH_DYNAMIC
, NULL
},
194 { WPS_TOKEN_FILE_VBR
, "Fv", WPS_REFRESH_DYNAMIC
, NULL
},
195 { WPS_TOKEN_FILE_DIRECTORY
, "D", WPS_REFRESH_DYNAMIC
,
198 /* current metadata */
199 { WPS_TOKEN_METADATA_ARTIST
, "ia", WPS_REFRESH_STATIC
, NULL
},
200 { WPS_TOKEN_METADATA_COMPOSER
, "ic", WPS_REFRESH_STATIC
, NULL
},
201 { WPS_TOKEN_METADATA_ALBUM
, "id", WPS_REFRESH_STATIC
, NULL
},
202 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "iA", WPS_REFRESH_STATIC
, NULL
},
203 { WPS_TOKEN_METADATA_GROUPING
, "iG", WPS_REFRESH_STATIC
, NULL
},
204 { WPS_TOKEN_METADATA_GENRE
, "ig", WPS_REFRESH_STATIC
, NULL
},
205 { WPS_TOKEN_METADATA_DISC_NUMBER
, "ik", WPS_REFRESH_STATIC
, NULL
},
206 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "in", WPS_REFRESH_STATIC
, NULL
},
207 { WPS_TOKEN_METADATA_TRACK_TITLE
, "it", WPS_REFRESH_STATIC
, NULL
},
208 { WPS_TOKEN_METADATA_VERSION
, "iv", WPS_REFRESH_STATIC
, NULL
},
209 { WPS_TOKEN_METADATA_YEAR
, "iy", WPS_REFRESH_STATIC
, NULL
},
210 { WPS_TOKEN_METADATA_COMMENT
, "iC", WPS_REFRESH_STATIC
, NULL
},
213 { WPS_TOKEN_METADATA_ARTIST
, "Ia", WPS_REFRESH_DYNAMIC
, NULL
},
214 { WPS_TOKEN_METADATA_COMPOSER
, "Ic", WPS_REFRESH_DYNAMIC
, NULL
},
215 { WPS_TOKEN_METADATA_ALBUM
, "Id", WPS_REFRESH_DYNAMIC
, NULL
},
216 { WPS_TOKEN_METADATA_ALBUM_ARTIST
, "IA", WPS_REFRESH_DYNAMIC
, NULL
},
217 { WPS_TOKEN_METADATA_GROUPING
, "IG", WPS_REFRESH_DYNAMIC
, NULL
},
218 { WPS_TOKEN_METADATA_GENRE
, "Ig", WPS_REFRESH_DYNAMIC
, NULL
},
219 { WPS_TOKEN_METADATA_DISC_NUMBER
, "Ik", WPS_REFRESH_DYNAMIC
, NULL
},
220 { WPS_TOKEN_METADATA_TRACK_NUMBER
, "In", WPS_REFRESH_DYNAMIC
, NULL
},
221 { WPS_TOKEN_METADATA_TRACK_TITLE
, "It", WPS_REFRESH_DYNAMIC
, NULL
},
222 { WPS_TOKEN_METADATA_VERSION
, "Iv", WPS_REFRESH_DYNAMIC
, NULL
},
223 { WPS_TOKEN_METADATA_YEAR
, "Iy", WPS_REFRESH_DYNAMIC
, NULL
},
224 { WPS_TOKEN_METADATA_COMMENT
, "IC", WPS_REFRESH_DYNAMIC
, NULL
},
226 #if (CONFIG_CODEC != MAS3507D)
227 { WPS_TOKEN_SOUND_PITCH
, "Sp", WPS_REFRESH_DYNAMIC
, NULL
},
230 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
231 { WPS_TOKEN_VLED_HDD
, "lh", WPS_REFRESH_DYNAMIC
, NULL
},
234 { WPS_TOKEN_MAIN_HOLD
, "mh", WPS_REFRESH_DYNAMIC
, NULL
},
236 #ifdef HAS_REMOTE_BUTTON_HOLD
237 { WPS_TOKEN_REMOTE_HOLD
, "mr", WPS_REFRESH_DYNAMIC
, NULL
},
240 { WPS_TOKEN_REPEAT_MODE
, "mm", WPS_REFRESH_DYNAMIC
, NULL
},
241 { WPS_TOKEN_PLAYBACK_STATUS
, "mp", WPS_REFRESH_DYNAMIC
, NULL
},
243 #ifdef HAVE_LCD_BITMAP
244 { WPS_TOKEN_PEAKMETER
, "pm", WPS_REFRESH_PEAK_METER
, NULL
},
246 { WPS_TOKEN_PLAYER_PROGRESSBAR
, "pf",
247 WPS_REFRESH_DYNAMIC
| WPS_REFRESH_PLAYER_PROGRESS
, parse_progressbar
},
249 { WPS_TOKEN_PROGRESSBAR
, "pb", WPS_REFRESH_PLAYER_PROGRESS
,
252 { WPS_TOKEN_VOLUME
, "pv", WPS_REFRESH_DYNAMIC
, NULL
},
254 { WPS_TOKEN_TRACK_ELAPSED_PERCENT
, "px", WPS_REFRESH_DYNAMIC
, NULL
},
255 { WPS_TOKEN_TRACK_TIME_ELAPSED
, "pc", WPS_REFRESH_DYNAMIC
, NULL
},
256 { WPS_TOKEN_TRACK_TIME_REMAINING
, "pr", WPS_REFRESH_DYNAMIC
, NULL
},
257 { WPS_TOKEN_TRACK_LENGTH
, "pt", WPS_REFRESH_STATIC
, NULL
},
259 { WPS_TOKEN_PLAYLIST_POSITION
, "pp", WPS_REFRESH_STATIC
, NULL
},
260 { WPS_TOKEN_PLAYLIST_ENTRIES
, "pe", WPS_REFRESH_STATIC
, NULL
},
261 { WPS_TOKEN_PLAYLIST_NAME
, "pn", WPS_REFRESH_STATIC
, NULL
},
262 { WPS_TOKEN_PLAYLIST_SHUFFLE
, "ps", WPS_REFRESH_DYNAMIC
, NULL
},
264 { WPS_TOKEN_DATABASE_PLAYCOUNT
, "rp", WPS_REFRESH_DYNAMIC
, NULL
},
265 { WPS_TOKEN_DATABASE_RATING
, "rr", WPS_REFRESH_DYNAMIC
, NULL
},
266 { WPS_TOKEN_DATABASE_AUTOSCORE
, "ra", WPS_REFRESH_DYNAMIC
, NULL
},
267 #if CONFIG_CODEC == SWCODEC
268 { WPS_TOKEN_REPLAYGAIN
, "rg", WPS_REFRESH_STATIC
, NULL
},
269 { WPS_TOKEN_CROSSFADE
, "xf", WPS_REFRESH_DYNAMIC
, NULL
},
272 { WPS_NO_TOKEN
, "s", WPS_REFRESH_SCROLL
, NULL
},
273 { WPS_TOKEN_SUBLINE_TIMEOUT
, "t", 0, parse_subline_timeout
},
275 #ifdef HAVE_LCD_BITMAP
276 { WPS_NO_TOKEN
, "we", 0, parse_statusbar_enable
},
277 { WPS_NO_TOKEN
, "wd", 0, parse_statusbar_disable
},
279 { WPS_NO_TOKEN
, "xl", 0, parse_image_load
},
281 { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
, "xd", WPS_REFRESH_STATIC
,
282 parse_image_display
},
284 { WPS_TOKEN_IMAGE_DISPLAY
, "x", 0, parse_image_load
},
285 { WPS_TOKEN_IMAGE_PROGRESS_BAR
, "P", 0, parse_image_special
},
286 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
287 { WPS_TOKEN_IMAGE_BACKDROP
, "X", 0, parse_image_special
},
291 { WPS_TOKEN_UNKNOWN
, "", 0, NULL
}
292 /* the array MUST end with an empty string (first char is \0) */
295 /* Returns the number of chars that should be skipped to jump
296 immediately after the first eol, i.e. to the start of the next line */
297 static int skip_end_of_line(const char *wps_bufptr
)
301 while(*(wps_bufptr
+ skip
) != '\n')
306 /* Starts a new subline in the current line during parsing */
307 static void wps_start_new_subline(struct wps_data
*data
)
309 data
->num_sublines
++;
310 data
->sublines
[data
->num_sublines
].first_token_idx
= data
->num_tokens
;
311 data
->lines
[data
->num_lines
].num_sublines
++;
314 #ifdef HAVE_LCD_BITMAP
316 static int parse_statusbar_enable(const char *wps_bufptr
,
317 struct wps_token
*token
,
318 struct wps_data
*wps_data
)
320 (void)token
; /* Kill warnings */
321 wps_data
->wps_sb_tag
= true;
322 wps_data
->show_sb_on_wps
= true;
323 return skip_end_of_line(wps_bufptr
);
326 static int parse_statusbar_disable(const char *wps_bufptr
,
327 struct wps_token
*token
,
328 struct wps_data
*wps_data
)
330 (void)token
; /* Kill warnings */
331 wps_data
->wps_sb_tag
= true;
332 wps_data
->show_sb_on_wps
= false;
333 return skip_end_of_line(wps_bufptr
);
336 static bool load_bitmap(struct wps_data
*wps_data
,
341 #ifdef HAVE_REMOTE_LCD
342 if (wps_data
->remote_wps
)
343 format
= FORMAT_ANY
|FORMAT_REMOTE
;
346 format
= FORMAT_ANY
|FORMAT_TRANSPARENT
;
348 int ret
= read_bmp_file(filename
, bm
,
349 wps_data
->img_buf_free
,
356 /* Always consume an even number of bytes */
358 wps_data
->img_buf_ptr
+= ret
;
359 wps_data
->img_buf_free
-= ret
;
367 static int get_image_id(int c
)
369 if(c
>= 'a' && c
<= 'z')
371 else if(c
>= 'A' && c
<= 'Z')
377 static char *get_image_filename(const char *start
, const char* bmpdir
,
378 char *buf
, int buf_size
)
380 const char *end
= strchr(start
, '|');
382 if ( !end
|| (end
- start
) >= (buf_size
- ROCKBOX_DIR_LEN
- 2) )
388 int bmpdirlen
= strlen(bmpdir
);
391 buf
[bmpdirlen
] = '/';
392 memcpy( &buf
[bmpdirlen
+ 1], start
, end
- start
);
393 buf
[bmpdirlen
+ 1 + end
- start
] = 0;
398 static int parse_image_display(const char *wps_bufptr
,
399 struct wps_token
*token
,
400 struct wps_data
*wps_data
)
402 int n
= get_image_id(*wps_bufptr
);
406 /* invalid picture display tag */
407 token
->type
= WPS_TOKEN_UNKNOWN
;
413 /* if the image is in a conditional, remember it */
415 wps_data
->img
[n
].cond_index
= condindex
[level
];
420 static int parse_image_load(const char *wps_bufptr
,
421 struct wps_token
*token
,
422 struct wps_data
*wps_data
)
425 const char *ptr
= wps_bufptr
;
428 /* format: %x|n|filename.bmp|x|y|
429 or %xl|n|filename.bmp|x|y| */
431 ptr
= strchr(ptr
, '|') + 1;
432 pos
= strchr(ptr
, '|');
437 /* get the image ID */
438 n
= get_image_id(*ptr
);
440 /* check the image number and load state */
441 if(n
< 0 || n
>= MAX_IMAGES
|| wps_data
->img
[n
].loaded
)
443 /* Skip the rest of the line */
452 pos
= strchr(ptr
, '|');
456 pos
= strchr(ptr
, '|');
458 wps_data
->img
[n
].x
= atoi(ptr
);
461 /* weird syntax, bail out */
467 pos
= strchr(ptr
, '|');
469 wps_data
->img
[n
].y
= atoi(ptr
);
472 /* weird syntax, bail out */
476 if (token
->type
== WPS_TOKEN_IMAGE_DISPLAY
)
477 wps_data
->img
[n
].always_display
= true;
479 /* Skip the rest of the line */
480 return skip_end_of_line(wps_bufptr
);
483 static int parse_image_special(const char *wps_bufptr
,
484 struct wps_token
*token
,
485 struct wps_data
*wps_data
)
487 if (token
->type
== WPS_TOKEN_IMAGE_PROGRESS_BAR
)
489 /* format: %P|filename.bmp| */
490 bmp_names
[PROGRESSBAR_BMP
] = wps_bufptr
+ 1;
493 else if (token
->type
== WPS_TOKEN_IMAGE_BACKDROP
)
495 /* format: %X|filename.bmp| */
496 bmp_names
[BACKDROP_BMP
] = wps_bufptr
+ 1;
500 (void)wps_data
; /* to avoid a warning */
502 /* Skip the rest of the line */
503 return skip_end_of_line(wps_bufptr
);
506 #endif /* HAVE_LCD_BITMAP */
508 static int parse_dir_level(const char *wps_bufptr
,
509 struct wps_token
*token
,
510 struct wps_data
*wps_data
)
512 char val
[] = { *wps_bufptr
, '\0' };
513 token
->value
.i
= atoi(val
);
514 (void)wps_data
; /* Kill warnings */
518 static int parse_subline_timeout(const char *wps_bufptr
,
519 struct wps_token
*token
,
520 struct wps_data
*wps_data
)
524 bool have_point
= false;
525 bool have_tenth
= false;
527 (void)wps_data
; /* Kill the warning */
529 while ( isdigit(*wps_bufptr
) || *wps_bufptr
== '.' )
531 if (*wps_bufptr
!= '.')
534 val
+= *wps_bufptr
- '0';
550 if (have_tenth
== false)
553 token
->value
.i
= val
;
558 static int parse_progressbar(const char *wps_bufptr
,
559 struct wps_token
*token
,
560 struct wps_data
*wps_data
)
562 (void)token
; /* Kill warnings */
563 #ifdef HAVE_LCD_BITMAP
566 &wps_data
->progress_height
,
567 &wps_data
->progress_start
,
568 &wps_data
->progress_end
,
569 &wps_data
->progress_top
};
571 /* default values : */
572 wps_data
->progress_height
= 6;
573 wps_data
->progress_start
= 0;
574 wps_data
->progress_end
= 0;
575 wps_data
->progress_top
= -1;
578 char *newline
= strchr(wps_bufptr
, '\n');
579 char *prev
= strchr(wps_bufptr
, '|');
580 if (prev
&& prev
< newline
) {
581 char *next
= strchr(prev
+1, '|');
582 while (i
< 4 && next
&& next
< newline
)
584 *(vals
[i
++]) = atoi(++prev
);
585 prev
= strchr(prev
, '|');
586 next
= strchr(++next
, '|');
589 if (wps_data
->progress_height
< 3)
590 wps_data
->progress_height
= 3;
591 if (wps_data
->progress_end
< wps_data
->progress_start
+ 3)
592 wps_data
->progress_end
= 0;
595 return newline
- wps_bufptr
;
599 if (*(wps_bufptr
-1) == 'f')
600 wps_data
->full_line_progressbar
= true;
602 wps_data
->full_line_progressbar
= false;
609 /* Parse a generic token from the given string. Return the length read */
610 static int parse_token(const char *wps_bufptr
, struct wps_data
*wps_data
)
612 int skip
= 0, taglen
= 0;
613 struct wps_token
*token
= wps_data
->tokens
+ wps_data
->num_tokens
;
614 const struct wps_tag
*tag
;
625 /* escaped characters */
626 token
->type
= WPS_TOKEN_CHARACTER
;
627 token
->value
.c
= *wps_bufptr
;
629 wps_data
->num_tokens
++;
633 /* conditional tag */
634 token
->type
= WPS_TOKEN_CONDITIONAL
;
636 condindex
[level
] = wps_data
->num_tokens
;
637 numoptions
[level
] = 1;
638 wps_data
->num_tokens
++;
639 taglen
= 1 + parse_token(wps_bufptr
+ 1, wps_data
);
643 /* find what tag we have */
645 strncmp(wps_bufptr
, tag
->name
, strlen(tag
->name
)) != 0;
648 taglen
= (tag
->type
!= WPS_TOKEN_UNKNOWN
) ? strlen(tag
->name
) : 2;
649 token
->type
= tag
->type
;
650 wps_data
->sublines
[wps_data
->num_sublines
].line_type
|=
653 /* if the tag has a special parsing function, we call it */
655 skip
+= tag
->parse_func(wps_bufptr
+ taglen
, token
, wps_data
);
657 /* Some tags we don't want to save as tokens */
658 if (tag
->type
== WPS_NO_TOKEN
)
661 /* tags that start with 'F', 'I' or 'D' are for the next file */
662 if ( *(tag
->name
) == 'I' || *(tag
->name
) == 'F' ||
666 wps_data
->num_tokens
++;
675 data is the pointer to the structure where the parsed WPS should be stored.
677 wps_bufptr points to the string containing the WPS tags */
678 static bool wps_parse(struct wps_data
*data
, const char *wps_bufptr
)
680 if (!data
|| !wps_bufptr
|| !*wps_bufptr
)
683 char *stringbuf
= data
->string_buffer
;
684 int stringbuf_used
= 0;
689 while(*wps_bufptr
&& !fail
&& data
->num_tokens
< WPS_MAX_TOKENS
- 1
690 && data
->num_lines
< WPS_MAX_LINES
)
692 switch(*wps_bufptr
++)
697 wps_bufptr
+= parse_token(wps_bufptr
, data
);
700 /* Alternating sublines separator */
702 if (level
>= 0) /* there are unclosed conditionals */
704 fail
= PARSE_FAIL_UNCLOSED_COND
;
708 if (data
->num_sublines
+1 < WPS_MAX_SUBLINES
)
709 wps_start_new_subline(data
);
711 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
715 /* Conditional list start */
717 if (data
->tokens
[data
->num_tokens
-2].type
!= WPS_TOKEN_CONDITIONAL
)
719 fail
= PARSE_FAIL_COND_SYNTAX_ERROR
;
723 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_START
;
724 lastcond
[level
] = data
->num_tokens
++;
727 /* Conditional list end */
729 if (level
< 0) /* not in a conditional, invalid char */
731 fail
= PARSE_FAIL_INVALID_CHAR
;
735 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_END
;
737 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
741 data
->tokens
[condindex
[level
]].value
.i
= numoptions
[level
];
745 /* Conditional list option */
747 if (level
< 0) /* not in a conditional, invalid char */
749 fail
= PARSE_FAIL_INVALID_CHAR
;
753 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_CONDITIONAL_OPTION
;
755 data
->tokens
[lastcond
[level
]].value
.i
= data
->num_tokens
;
757 lastcond
[level
] = data
->num_tokens
;
764 if (level
>= 0) /* there are unclosed conditionals */
766 fail
= PARSE_FAIL_UNCLOSED_COND
;
770 wps_bufptr
+= skip_end_of_line(wps_bufptr
);
773 /* End of this line */
775 if (level
>= 0) /* there are unclosed conditionals */
777 fail
= PARSE_FAIL_UNCLOSED_COND
;
782 wps_start_new_subline(data
);
783 data
->num_lines
++; /* Start a new line */
785 if ((data
->num_lines
< WPS_MAX_LINES
) &&
786 (data
->num_sublines
< WPS_MAX_SUBLINES
))
788 data
->lines
[data
->num_lines
].first_subline_idx
=
791 data
->sublines
[data
->num_sublines
].first_token_idx
=
800 unsigned int len
= 1;
801 const char *string_start
= wps_bufptr
- 1;
803 /* find the length of the string */
804 while (*wps_bufptr
&& *wps_bufptr
!= '#' &&
805 *wps_bufptr
!= '%' && *wps_bufptr
!= ';' &&
806 *wps_bufptr
!= '<' && *wps_bufptr
!= '>' &&
807 *wps_bufptr
!= '|' && *wps_bufptr
!= '\n')
813 /* look if we already have that string */
817 for (i
= 0, str
= data
->strings
, found
= false;
818 i
< data
->num_strings
&&
819 !(found
= (strlen(*str
) == len
&&
820 strncmp(string_start
, *str
, len
) == 0));
822 /* If a matching string is found, found is true and i is
823 the index of the string. If not, found is false */
825 /* If it's NOT a duplicate, do nothing if we already have
826 too many unique strings */
828 (stringbuf_used
< STRING_BUFFER_SIZE
- 1 &&
829 data
->num_strings
< WPS_MAX_STRINGS
))
836 if (stringbuf_used
+ len
> STRING_BUFFER_SIZE
- 1)
837 len
= STRING_BUFFER_SIZE
- stringbuf_used
- 1;
839 strncpy(stringbuf
, string_start
, len
);
840 *(stringbuf
+ len
) = '\0';
842 data
->strings
[data
->num_strings
] = stringbuf
;
843 stringbuf
+= len
+ 1;
844 stringbuf_used
+= len
+ 1;
845 data
->tokens
[data
->num_tokens
].value
.i
=
851 /* another ocurrence of an existing string */
852 data
->tokens
[data
->num_tokens
].value
.i
= i
;
854 data
->tokens
[data
->num_tokens
].type
= WPS_TOKEN_STRING
;
862 if (level
>= 0) /* there are unclosed conditionals */
863 fail
= PARSE_FAIL_UNCLOSED_COND
;
866 print_debug_info(data
, fail
, line
);
875 #ifdef HAVE_LCD_BITMAP
876 /* Clear the WPS image cache */
877 static void wps_images_clear(struct wps_data
*data
)
880 /* set images to unloaded and not displayed */
881 for (i
= 0; i
< MAX_IMAGES
; i
++)
883 data
->img
[i
].loaded
= false;
884 data
->img
[i
].display
= false;
885 data
->img
[i
].always_display
= false;
887 data
->progressbar
.have_bitmap_pb
= false;
891 /* initial setup of wps_data */
892 void wps_data_init(struct wps_data
*wps_data
)
894 #ifdef HAVE_LCD_BITMAP
895 wps_images_clear(wps_data
);
896 wps_data
->wps_sb_tag
= false;
897 wps_data
->show_sb_on_wps
= false;
898 wps_data
->img_buf_ptr
= wps_data
->img_buf
; /* where in image buffer */
899 wps_data
->img_buf_free
= IMG_BUFSIZE
; /* free space in image buffer */
900 wps_data
->peak_meter_enabled
= false;
901 #else /* HAVE_LCD_CHARCELLS */
903 for (i
= 0; i
< 8; i
++)
905 wps_data
->wps_progress_pat
[i
] = 0;
907 wps_data
->full_line_progressbar
= false;
909 wps_data
->wps_loaded
= false;
912 static void wps_reset(struct wps_data
*data
)
914 #ifdef HAVE_REMOTE_LCD
915 bool rwps
= data
->remote_wps
; /* remember whether the data is for a RWPS */
917 memset(data
, 0, sizeof(*data
));
919 #ifdef HAVE_REMOTE_LCD
920 data
->remote_wps
= rwps
;
924 #ifdef HAVE_LCD_BITMAP
927 static void clear_bmp_names(void)
930 for (n
= 0; n
< MAX_BITMAPS
; n
++)
936 static void load_wps_bitmaps(struct wps_data
*wps_data
, char *bmpdir
)
938 char img_path
[MAX_PATH
];
939 struct bitmap
*bitmap
;
943 for (n
= 0; n
< BACKDROP_BMP
; n
++)
947 get_image_filename(bmp_names
[n
], bmpdir
,
948 img_path
, sizeof(img_path
));
950 if (n
== PROGRESSBAR_BMP
) {
951 /* progressbar bitmap */
952 bitmap
= &wps_data
->progressbar
.bm
;
953 loaded
= &wps_data
->progressbar
.have_bitmap_pb
;
956 bitmap
= &wps_data
->img
[n
].bm
;
957 loaded
= &wps_data
->img
[n
].loaded
;
961 bitmap
->data
= wps_data
->img_buf_ptr
;
962 if (load_bitmap(wps_data
, img_path
, bitmap
))
969 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
970 if (bmp_names
[BACKDROP_BMP
])
972 get_image_filename(bmp_names
[BACKDROP_BMP
], bmpdir
,
973 img_path
, sizeof(img_path
));
974 #ifdef HAVE_REMOTE_LCD
975 if (wps_data
->remote_wps
)
976 #if LCD_REMOTE_DEPTH > 1
977 load_remote_wps_backdrop(img_path
)
981 #endif /* HAVE_REMOTE_LCD */
982 load_wps_backdrop(img_path
);
984 #endif /* has backdrop support */
987 #endif /* HAVE_LCD_BITMAP */
989 /* Skip leading UTF-8 BOM, if present. */
990 static char *skip_utf8_bom(char *buf
)
992 unsigned char *s
= (unsigned char *)buf
;
994 if(s
[0] == 0xef && s
[1] == 0xbb && s
[2] == 0xbf)
1002 /* to setup up the wps-data from a format-buffer (isfile = false)
1003 from a (wps-)file (isfile = true)*/
1004 bool wps_data_load(struct wps_data
*wps_data
,
1008 if (!wps_data
|| !buf
)
1011 wps_reset(wps_data
);
1015 return wps_parse(wps_data
, buf
);
1020 * Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
1021 * wants to be a virtual file. Feel free to modify dirbrowse()
1022 * if you're feeling brave.
1025 if (! strcmp(buf
, WPS_DEFAULTCFG
) )
1027 global_settings
.wps_file
[0] = 0;
1031 #ifdef HAVE_REMOTE_LCD
1032 if (! strcmp(buf
, RWPS_DEFAULTCFG
) )
1034 global_settings
.rwps_file
[0] = 0;
1038 #endif /* __PCTOOL__ */
1040 int fd
= open(buf
, O_RDONLY
);
1045 /* get buffer space from the plugin buffer */
1046 size_t buffersize
= 0;
1047 char *wps_buffer
= (char *)plugin_get_buffer(&buffersize
);
1052 /* copy the file's content to the buffer for parsing,
1053 ensuring that every line ends with a newline char. */
1054 unsigned int start
= 0;
1055 while(read_line(fd
, wps_buffer
+ start
, buffersize
- start
) > 0)
1057 start
+= strlen(wps_buffer
+ start
);
1058 if (start
< buffersize
- 1)
1060 wps_buffer
[start
++] = '\n';
1061 wps_buffer
[start
] = 0;
1070 #ifdef HAVE_LCD_BITMAP
1074 /* Skip leading UTF-8 BOM, if present. */
1075 wps_buffer
= skip_utf8_bom(wps_buffer
);
1077 /* parse the WPS source */
1078 if (!wps_parse(wps_data
, wps_buffer
))
1081 wps_data
->wps_loaded
= true;
1083 #ifdef HAVE_LCD_BITMAP
1084 /* get the bitmap dir */
1085 char bmpdir
[MAX_PATH
];
1087 char *dot
= strrchr(buf
, '.');
1088 bmpdirlen
= dot
- buf
;
1089 strncpy(bmpdir
, buf
, dot
- buf
);
1090 bmpdir
[bmpdirlen
] = 0;
1092 /* load the bitmaps that were found by the parsing */
1093 load_wps_bitmaps(wps_data
, bmpdir
);
1099 int wps_subline_index(struct wps_data
*data
, int line
, int subline
)
1101 return data
->lines
[line
].first_subline_idx
+ subline
;
1104 int wps_first_token_index(struct wps_data
*data
, int line
, int subline
)
1106 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1107 return data
->sublines
[first_subline_idx
+ subline
].first_token_idx
;
1110 int wps_last_token_index(struct wps_data
*data
, int line
, int subline
)
1112 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
1113 int idx
= first_subline_idx
+ subline
;
1114 if (idx
< data
->num_sublines
- 1)
1116 /* This subline ends where the next begins */
1117 return data
->sublines
[idx
+1].first_token_idx
- 1;
1121 /* The last subline goes to the end */
1122 return data
->num_tokens
- 1;