/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include #include #include "config.h" #include "file.h" #include "misc.h" #include "plugin.h" #include "viewport.h" #ifdef __PCTOOL__ #ifdef WPSEDITOR #include "proxy.h" #include "sysfont.h" #else #include "action.h" #include "checkwps.h" #include "audio.h" #define lang_is_rtl() (false) #define DEBUGF printf #endif /*WPSEDITOR*/ #else #include "debug.h" #include "language.h" #endif /*__PCTOOL__*/ #include #include #include "font.h" #include "wps_internals.h" #include "skin_engine.h" #include "settings.h" #include "settings_list.h" #if CONFIG_TUNER #include "radio.h" #include "tuner.h" #endif #include "skin_fonts.h" #ifdef HAVE_LCD_BITMAP #include "bmp.h" #endif #ifdef HAVE_ALBUMART #include "playback.h" #endif #include "backdrop.h" #include "statusbar-skinned.h" #define WPS_ERROR_INVALID_PARAM -1 /* which screen are we parsing for? */ static enum screen_type curr_screen; /* level of current conditional. -1 means we're not in a conditional. */ static int level = -1; /* index of the last WPS_TOKEN_CONDITIONAL_OPTION or WPS_TOKEN_CONDITIONAL_START in current level */ static int lastcond[WPS_MAX_COND_LEVEL]; /* index of the WPS_TOKEN_CONDITIONAL in current level */ static int condindex[WPS_MAX_COND_LEVEL]; /* number of condtional options in current level */ static int numoptions[WPS_MAX_COND_LEVEL]; /* line number, debug only */ static int line_number; /* the current viewport */ static struct skin_viewport *curr_vp; /* the current line, linked to the above viewport */ static struct skin_line *curr_line; static int follow_lang_direction = 0; #if defined(DEBUG) || defined(SIMULATOR) /* debugging function */ extern void print_debug_info(struct wps_data *data, int fail, int line); extern void debug_skin_usage(void); #endif /* Function for parsing of details for a token. At the moment the function is called, the token type has already been set. The function must fill in the details and possibly add more tokens to the token array. It should return the number of chars that has been consumed. wps_bufptr points to the char following the tag (i.e. where details begin). token is the pointer to the 'main' token being parsed */ typedef int (*wps_tag_parse_func)(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); struct wps_tag { enum wps_token_type type; const char name[3]; unsigned char refresh_type; const wps_tag_parse_func parse_func; }; static int skip_end_of_line(const char *wps_bufptr); /* prototypes of all special parse functions : */ static int parse_timeout(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_progressbar(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_dir_level(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_setting_and_lang(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_languagedirection(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)wps_bufptr; (void)token; (void)wps_data; follow_lang_direction = 2; /* 2 because it is decremented immediatly after this token is parsed, after the next token it will be 0 again. */ return 0; } #ifdef HAVE_LCD_BITMAP static int parse_viewport_display(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_playlistview(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_viewport(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_statusbar_enable(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_statusbar_disable(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_statusbar_inbuilt(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_image_display(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_image_load(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_font_load(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); #endif /*HAVE_LCD_BITMAP */ #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) static int parse_viewportcolour(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_image_special(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); #endif #ifdef HAVE_ALBUMART static int parse_albumart_load(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); static int parse_albumart_display(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); #endif /* HAVE_ALBUMART */ #ifdef HAVE_TOUCHSCREEN static int parse_touchregion(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data); #else static int fulline_tag_not_supported(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)token; (void)wps_data; return skip_end_of_line(wps_bufptr); } #define parse_touchregion fulline_tag_not_supported #endif #ifdef CONFIG_RTC #define WPS_RTC_REFRESH WPS_REFRESH_DYNAMIC #else #define WPS_RTC_REFRESH WPS_REFRESH_STATIC #endif /* array of available tags - those with more characters have to go first (e.g. "xl" and "xd" before "x"). It needs to end with the unknown token. */ static const struct wps_tag all_tags[] = { { WPS_TOKEN_ALIGN_CENTER, "ac", 0, NULL }, { WPS_TOKEN_ALIGN_LEFT, "al", 0, NULL }, { WPS_TOKEN_ALIGN_LEFT_RTL, "aL", 0, NULL }, { WPS_TOKEN_ALIGN_RIGHT, "ar", 0, NULL }, { WPS_TOKEN_ALIGN_RIGHT_RTL, "aR", 0, NULL }, { WPS_NO_TOKEN, "ax", 0, parse_languagedirection }, { WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, parse_progressbar }, { WPS_TOKEN_BATTERY_VOLTS, "bv", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_BATTERY_TIME, "bt", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_BATTERY_SLEEPTIME, "bs", WPS_REFRESH_DYNAMIC, NULL }, #if CONFIG_CHARGING >= CHARGING_MONITOR { WPS_TOKEN_BATTERY_CHARGING, "bc", WPS_REFRESH_DYNAMIC, NULL }, #endif #if CONFIG_CHARGING { WPS_TOKEN_BATTERY_CHARGER_CONNECTED,"bp", WPS_REFRESH_DYNAMIC, NULL }, #endif #ifdef HAVE_USB_POWER { WPS_TOKEN_USB_POWERED, "bu", WPS_REFRESH_DYNAMIC, NULL }, #endif { WPS_TOKEN_RTC_PRESENT , "cc", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_RTC_DAY_OF_MONTH, "cd", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED,"ce", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_12HOUR_CFG, "cf", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED, "cH", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_HOUR_24, "ck", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED, "cI", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_HOUR_12, "cl", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_MONTH, "cm", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_MINUTE, "cM", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_SECOND, "cS", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_YEAR_2_DIGITS, "cy", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_YEAR_4_DIGITS, "cY", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_AM_PM_UPPER, "cP", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_AM_PM_LOWER, "cp", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_WEEKDAY_NAME, "ca", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_MONTH_NAME, "cb", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON, "cu", WPS_RTC_REFRESH, NULL }, { WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN, "cw", WPS_RTC_REFRESH, NULL }, /* current file */ { WPS_TOKEN_FILE_BITRATE, "fb", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_CODEC, "fc", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_FREQUENCY, "ff", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_FREQUENCY_KHZ, "fk", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "fm", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_NAME, "fn", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_PATH, "fp", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_SIZE, "fs", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_VBR, "fv", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_DIRECTORY, "d", WPS_REFRESH_STATIC, parse_dir_level }, /* next file */ { WPS_TOKEN_FILE_BITRATE, "Fb", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_STATIC, parse_dir_level }, /* current metadata */ { WPS_TOKEN_METADATA_ARTIST, "ia", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_COMPOSER, "ic", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_ALBUM, "id", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_ALBUM_ARTIST, "iA", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_GROUPING, "iG", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_GENRE, "ig", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_DISC_NUMBER, "ik", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_TRACK_NUMBER, "in", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_TRACK_TITLE, "it", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_VERSION, "iv", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_YEAR, "iy", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_COMMENT, "iC", WPS_REFRESH_STATIC, NULL }, /* next metadata */ { WPS_TOKEN_METADATA_ARTIST, "Ia", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_STATIC, NULL }, #if (CONFIG_CODEC != MAS3507D) { WPS_TOKEN_SOUND_PITCH, "Sp", WPS_REFRESH_DYNAMIC, NULL }, #endif #if (CONFIG_CODEC == SWCODEC) { WPS_TOKEN_SOUND_SPEED, "Ss", WPS_REFRESH_DYNAMIC, NULL }, #endif #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) { WPS_TOKEN_VLED_HDD, "lh", WPS_REFRESH_DYNAMIC, NULL }, #endif { WPS_TOKEN_MAIN_HOLD, "mh", WPS_REFRESH_DYNAMIC, NULL }, #ifdef HAS_REMOTE_BUTTON_HOLD { WPS_TOKEN_REMOTE_HOLD, "mr", WPS_REFRESH_DYNAMIC, NULL }, #else { WPS_TOKEN_UNKNOWN, "mr", 0, NULL }, #endif { WPS_TOKEN_REPEAT_MODE, "mm", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_PLAYBACK_STATUS, "mp", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_BUTTON_VOLUME, "mv", WPS_REFRESH_DYNAMIC, parse_timeout }, #ifdef HAVE_LCD_BITMAP { WPS_TOKEN_PEAKMETER, "pm", WPS_REFRESH_PEAK_METER, NULL }, #else { WPS_TOKEN_PLAYER_PROGRESSBAR, "pf", WPS_REFRESH_DYNAMIC | WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar }, #endif { WPS_TOKEN_PROGRESSBAR, "pb", WPS_REFRESH_PLAYER_PROGRESS, parse_progressbar }, { WPS_TOKEN_VOLUME, "pv", WPS_REFRESH_DYNAMIC, parse_progressbar }, { WPS_TOKEN_TRACK_ELAPSED_PERCENT, "px", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_TRACK_TIME_ELAPSED, "pc", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_TRACK_TIME_REMAINING, "pr", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_TRACK_LENGTH, "pt", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_TRACK_STARTING, "pS", WPS_REFRESH_DYNAMIC, parse_timeout }, { WPS_TOKEN_TRACK_ENDING, "pE", WPS_REFRESH_DYNAMIC, parse_timeout }, { WPS_TOKEN_PLAYLIST_POSITION, "pp", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_PLAYLIST_ENTRIES, "pe", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_PLAYLIST_NAME, "pn", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_PLAYLIST_SHUFFLE, "ps", WPS_REFRESH_DYNAMIC, NULL }, #ifdef HAVE_TAGCACHE { WPS_TOKEN_DATABASE_PLAYCOUNT, "rp", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_DATABASE_RATING, "rr", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_DATABASE_AUTOSCORE, "ra", WPS_REFRESH_DYNAMIC, NULL }, #endif #if CONFIG_CODEC == SWCODEC { WPS_TOKEN_REPLAYGAIN, "rg", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_CROSSFADE, "xf", WPS_REFRESH_DYNAMIC, NULL }, #endif { WPS_TOKEN_HAVE_TUNER, "tp", WPS_REFRESH_STATIC, NULL }, #if CONFIG_TUNER /* Re-uses the 't' and 'T' prefixes, be careful about doubleups */ { WPS_TOKEN_TUNER_TUNED, "tt", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_TUNER_SCANMODE, "tm", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_TUNER_STEREO, "ts", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_TUNER_MINFREQ, "ta", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_TUNER_MAXFREQ, "tb", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_TUNER_CURFREQ, "tf", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_PRESET_ID, "Ti", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_PRESET_NAME, "Tn", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_PRESET_FREQ, "Tf", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_PRESET_COUNT, "Tc", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_HAVE_RDS, "tx", WPS_REFRESH_STATIC, NULL }, #ifdef HAVE_RDS_CAP { WPS_TOKEN_RDS_NAME, "ty", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_RDS_TEXT, "tz", WPS_REFRESH_DYNAMIC, NULL }, #endif #endif /* CONFIG_TUNER */ { WPS_NO_TOKEN, "s", WPS_REFRESH_SCROLL, NULL }, { WPS_TOKEN_SUBLINE_TIMEOUT, "t", 0, parse_timeout }, #ifdef HAVE_LCD_BITMAP { WPS_NO_TOKEN, "we", 0, parse_statusbar_enable }, { WPS_NO_TOKEN, "wd", 0, parse_statusbar_disable }, { WPS_TOKEN_DRAW_INBUILTBAR, "wi", WPS_REFRESH_DYNAMIC, parse_statusbar_inbuilt }, { WPS_NO_TOKEN, "xl", 0, parse_image_load }, { WPS_TOKEN_IMAGE_PRELOAD_DISPLAY, "xd", WPS_REFRESH_STATIC, parse_image_display }, { WPS_TOKEN_IMAGE_DISPLAY, "x", 0, parse_image_load }, { WPS_NO_TOKEN, "Fl", 0, parse_font_load }, #ifdef HAVE_ALBUMART { WPS_NO_TOKEN, "Cl", 0, parse_albumart_load }, { WPS_TOKEN_ALBUMART_DISPLAY, "Cd", WPS_REFRESH_STATIC, parse_albumart_display }, { WPS_TOKEN_ALBUMART_FOUND, "C", WPS_REFRESH_STATIC, NULL }, #endif { WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC, parse_viewport_display }, { WPS_TOKEN_UIVIEWPORT_ENABLE, "VI", WPS_REFRESH_STATIC, parse_viewport_display }, #ifdef HAVE_LCD_BITMAP { WPS_VIEWPORT_CUSTOMLIST, "Vp", WPS_REFRESH_STATIC, parse_playlistview }, { WPS_TOKEN_LIST_TITLE_TEXT, "Lt", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_LIST_TITLE_ICON, "Li", WPS_REFRESH_DYNAMIC, NULL }, #endif #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) { WPS_TOKEN_VIEWPORT_FGCOLOUR, "Vf", WPS_REFRESH_STATIC, parse_viewportcolour }, { WPS_TOKEN_VIEWPORT_BGCOLOUR, "Vb", WPS_REFRESH_STATIC, parse_viewportcolour }, #endif { WPS_NO_TOKEN, "V", 0, parse_viewport }, #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1)) { WPS_TOKEN_IMAGE_BACKDROP, "X", 0, parse_image_special }, #endif #endif { WPS_TOKEN_SETTING, "St", WPS_REFRESH_DYNAMIC, parse_setting_and_lang }, { WPS_TOKEN_TRANSLATEDSTRING, "Sx", WPS_REFRESH_STATIC, parse_setting_and_lang }, { WPS_TOKEN_LANG_IS_RTL , "Sr", WPS_REFRESH_STATIC, NULL }, { WPS_TOKEN_LASTTOUCH, "Tl", WPS_REFRESH_DYNAMIC, parse_timeout }, { WPS_TOKEN_CURRENT_SCREEN, "cs", WPS_REFRESH_DYNAMIC, NULL }, { WPS_NO_TOKEN, "T", 0, parse_touchregion }, /* Recording Tokens */ { WPS_TOKEN_HAVE_RECORDING, "Rp", WPS_REFRESH_STATIC, NULL }, #ifdef HAVE_RECORDING { WPS_TOKEN_IS_RECORDING, "Rr", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_REC_FREQ, "Rf", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_REC_ENCODER, "Re", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_REC_BITRATE, "Rb", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_REC_MONO, "Rm", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_REC_SECONDS, "Rs", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_REC_MINUTES, "Rn", WPS_REFRESH_DYNAMIC, NULL }, { WPS_TOKEN_REC_HOURS, "Rh", WPS_REFRESH_DYNAMIC, NULL }, #endif { WPS_TOKEN_UNKNOWN, "", 0, NULL } /* the array MUST end with an empty string (first char is \0) */ }; /* add a skin_token_list item to the list chain. ALWAYS appended because some of the * chains require the order to be kept. */ static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item) { if (*list == NULL) *list = item; else { struct skin_token_list *t = *list; while (t->next) t = t->next; t->next = item; } } /* traverse the image linked-list for an image */ #ifdef HAVE_LCD_BITMAP struct gui_img* find_image(char label, struct wps_data *data) { struct skin_token_list *list = data->images; while (list) { struct gui_img *img = (struct gui_img *)list->token->value.data; if (img->label == label) return img; list = list->next; } return NULL; } #endif /* traverse the viewport linked list for a viewport */ struct skin_viewport* find_viewport(char label, struct wps_data *data) { struct skin_token_list *list = data->viewports; while (list) { struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data; if (vp->label == label) return vp; list = list->next; } return NULL; } /* create and init a new wpsll item. * passing NULL to token will alloc a new one. * You should only pass NULL for the token when the token type (table above) * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array */ static struct skin_token_list *new_skin_token_list_item(struct wps_token *token, void* token_data) { struct skin_token_list *llitem = skin_buffer_alloc(sizeof(struct skin_token_list)); if (!token) token = skin_buffer_alloc(sizeof(struct wps_token)); if (!llitem || !token) return NULL; llitem->next = NULL; llitem->token = token; if (token_data) llitem->token->value.data = token_data; return llitem; } /* Returns the number of chars that should be skipped to jump immediately after the first eol, i.e. to the start of the next line */ static int skip_end_of_line(const char *wps_bufptr) { line_number++; int skip = 0; while(*(wps_bufptr + skip) != '\n') skip++; return ++skip; } /* Starts a new subline in the current line during parsing */ static bool skin_start_new_subline(struct skin_line *line, int curr_token) { struct skin_subline *subline = skin_buffer_alloc(sizeof(struct skin_subline)); if (!subline) return false; subline->first_token_idx = curr_token; subline->next = NULL; subline->line_type = 0; subline->time_mult = 0; line->curr_subline->last_token_idx = curr_token-1; line->curr_subline->next = subline; line->curr_subline = subline; return true; } static bool skin_start_new_line(struct skin_viewport *vp, int curr_token) { struct skin_line *line = skin_buffer_alloc(sizeof(struct skin_line)); struct skin_subline *subline = NULL; if (!line) return false; /* init the subline */ subline = &line->sublines; subline->first_token_idx = curr_token; subline->next = NULL; subline->line_type = 0; subline->time_mult = 0; /* init the new line */ line->curr_subline = &line->sublines; line->next = NULL; line->subline_expire_time = 0; /* connect to curr_line and vp pointers. * 1) close the previous lines subline * 2) connect to vp pointer * 3) connect to curr_line global pointer */ if (curr_line) { curr_line->curr_subline->last_token_idx = curr_token - 1; curr_line->next = line; curr_line->curr_subline = NULL; } curr_line = line; if (!vp->lines) vp->lines = line; return true; } #ifdef HAVE_LCD_BITMAP static int parse_statusbar_enable(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)token; /* Kill warnings */ wps_data->wps_sb_tag = true; wps_data->show_sb_on_wps = true; struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data); viewport_set_defaults(&default_vp->vp, curr_screen); default_vp->vp.font = FONT_UI; return skip_end_of_line(wps_bufptr); } static int parse_statusbar_disable(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)token; /* Kill warnings */ wps_data->wps_sb_tag = true; wps_data->show_sb_on_wps = false; struct skin_viewport *default_vp = find_viewport(VP_DEFAULT_LABEL, wps_data); viewport_set_fullscreen(&default_vp->vp, curr_screen); default_vp->vp.font = FONT_UI; return skip_end_of_line(wps_bufptr); } static int parse_statusbar_inbuilt(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)wps_data; token->value.data = (void*)&curr_vp->vp; return skip_end_of_line(wps_bufptr); } static int get_image_id(int c) { if(c >= 'a' && c <= 'z') return c - 'a'; else if(c >= 'A' && c <= 'Z') return c - 'A' + 26; else return -1; } char *get_image_filename(const char *start, const char* bmpdir, char *buf, int buf_size) { const char *end = start; int bmpdirlen = strlen(bmpdir); while (*end && *end != ',' && *end != ')') end++; if ( !end || (end - start) >= (buf_size - bmpdirlen - 2) ) { buf[0] = '\0'; return NULL; } strcpy(buf, bmpdir); buf[bmpdirlen] = '/'; memcpy( &buf[bmpdirlen + 1], start, end - start); buf[bmpdirlen + 1 + end - start] = 0; return buf; } static int parse_image_display(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { char label = wps_bufptr[1]; int subimage; struct gui_img *img;; /* sanity check */ img = find_image(label, wps_data); if (!img) { token->value.i = label; /* so debug works */ return WPS_ERROR_INVALID_PARAM; } if ((subimage = get_image_id(wps_bufptr[2])) != -1) { if (subimage >= img->num_subimages) return WPS_ERROR_INVALID_PARAM; /* Store sub-image number to display in high bits */ token->value.i = label | (subimage << 8); return 4; /* We have consumed 2 bytes */ } else { token->value.i = label; return 3; /* We have consumed 1 byte */ } } static int parse_image_load(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { const char *ptr = wps_bufptr; const char* filename; const char* id; int x,y; struct gui_img *img; /* format: %x|n|filename.bmp|x|y| or %xl|n|filename.bmp|x|y| or %xl|n|filename.bmp|x|y|num_subimages| */ if (*ptr != '(') return WPS_ERROR_INVALID_PARAM; ptr++; if (!(ptr = parse_list("ssdd", NULL, ',', ptr, &id, &filename, &x, &y))) return WPS_ERROR_INVALID_PARAM; /* Check there is a terminating ) */ if (*ptr != ')' && *ptr != ',') return WPS_ERROR_INVALID_PARAM; /* check the image number and load state */ if(find_image(*id, wps_data)) { /* Invalid image ID */ return WPS_ERROR_INVALID_PARAM; } img = skin_buffer_alloc(sizeof(struct gui_img)); if (!img) return WPS_ERROR_INVALID_PARAM; /* save a pointer to the filename */ img->bm.data = (char*)filename; img->label = *id; img->x = x; img->y = y; img->num_subimages = 1; img->always_display = false; /* save current viewport */ img->vp = &curr_vp->vp; if (token->type == WPS_TOKEN_IMAGE_DISPLAY) { img->always_display = true; } else if (*ptr == ',') { /* Parse the (optional) number of sub-images */ ptr++; img->num_subimages = atoi(ptr); if (img->num_subimages <= 0) return WPS_ERROR_INVALID_PARAM; /* Check there is a terminating ) */ while(isdigit(*ptr)) ptr++; if (*ptr != ')') return WPS_ERROR_INVALID_PARAM; } struct skin_token_list *item = new_skin_token_list_item(NULL, img); if (!item) return WPS_ERROR_INVALID_PARAM; add_to_ll_chain(&wps_data->images, item); /* Skip the rest of the line */ return skip_end_of_line(wps_bufptr); } struct skin_font { int id; /* the id from font_load */ char *name; /* filename without path and extension */ }; static struct skin_font skinfonts[MAXUSERFONTS]; static int parse_font_load(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)wps_data; (void)token; const char *ptr = wps_bufptr; int id; char *filename; if (*ptr != '(') return WPS_ERROR_INVALID_PARAM; ptr++; if (!(ptr = parse_list("ds", NULL, ',', ptr, &id, &filename))) return WPS_ERROR_INVALID_PARAM; /* Check there is a terminating ) */ if (*ptr != ')') return WPS_ERROR_INVALID_PARAM; if (id <= FONT_UI || id >= MAXFONTS-1) return WPS_ERROR_INVALID_PARAM; #if defined(DEBUG) || defined(SIMULATOR) if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL) { DEBUGF("font id %d already being used\n", id); } #endif /* make sure the filename contains .fnt, * we dont actually use it, but require it anyway */ ptr = strchr(filename, '.'); if (!ptr || strncmp(ptr, ".fnt)", 5)) return WPS_ERROR_INVALID_PARAM; skinfonts[id-FONT_FIRSTUSERFONT].id = -1; skinfonts[id-FONT_FIRSTUSERFONT].name = filename; return skip_end_of_line(wps_bufptr); } static int parse_viewport_display(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)wps_data; char letter = wps_bufptr[1]; if (letter < 'a' || letter > 'z') { /* invalid viewport tag */ return WPS_ERROR_INVALID_PARAM; } token->value.i = letter; return 3; } #ifdef HAVE_LCD_BITMAP static int parse_playlistview_text(struct playlistviewer *viewer, enum info_line_type line, char* text) { int cur_string = 0; const struct wps_tag *tag; int taglen = 0; const char *start = text; if (*text != ',') return -1; text++; viewer->lines[line].count = 0; viewer->lines[line].scroll = false; while (*text != ',' && *text != ')') { if (*text == '%') /* it is a token of some type */ { text++; taglen = 0; switch(*text) { case '%': case '<': case '|': case '>': case ';': case '#': case '(': case ')': case ',': /* escaped characters */ viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_CHARACTER; viewer->lines[line].strings[cur_string][0] = *text; viewer->lines[line].strings[cur_string++][1] = '\0'; text++; break; default: for (tag = all_tags; strncmp(text, tag->name, strlen(tag->name)) != 0; tag++) ; /* %s isnt stored as a tag so manually check for it */ if (tag->type == WPS_NO_TOKEN) { if (!strncmp(tag->name, "s", 1)) { viewer->lines[line].scroll = true; taglen = 1; } } else if (tag->type == WPS_TOKEN_UNKNOWN) { int i = 0; /* just copy the string */ viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING; while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != ',' && text[i] != ')' && text[i] != '%') { viewer->lines[line].strings[cur_string][i] = text[i]; i++; } viewer->lines[line].strings[cur_string][i] = '\0'; cur_string++; taglen = i; } else { if (tag->parse_func) { /* unsupported tag, reject */ return -1; } taglen = strlen(tag->name); viewer->lines[line].tokens[viewer->lines[line].count++] = tag->type; } text += taglen; } } else { /* regular string */ int i = 0; /* just copy the string */ viewer->lines[line].tokens[viewer->lines[line].count++] = WPS_TOKEN_STRING; while (i<(MAX_PLAYLISTLINE_STRLEN-1) && text[i] != ',' && text[i] != ')' && text[i] != '%') { viewer->lines[line].strings[cur_string][i] = text[i]; i++; } viewer->lines[line].strings[cur_string][i] = '\0'; cur_string++; text += i; } } return text - start; } static int parse_playlistview(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)wps_data; /* %Vp|||info line text|no info text| */ struct playlistviewer *viewer = skin_buffer_alloc(sizeof(struct playlistviewer)); char *ptr = strchr(wps_bufptr, '('); int length; if (!viewer || !ptr) return WPS_ERROR_INVALID_PARAM; viewer->vp = &curr_vp->vp; viewer->show_icons = true; viewer->start_offset = atoi(ptr+1); token->value.data = (void*)viewer; ptr = strchr(ptr+1, ','); length = parse_playlistview_text(viewer, TRACK_HAS_INFO, ptr); if (length < 0) return WPS_ERROR_INVALID_PARAM; length = parse_playlistview_text(viewer, TRACK_HAS_NO_INFO, ptr+length); if (length < 0) return WPS_ERROR_INVALID_PARAM; return skip_end_of_line(wps_bufptr); } #endif static int parse_viewport(const char *wps_bufptr, struct wps_token *token, struct wps_data *wps_data) { (void)token; /* Kill warnings */ const char *ptr = wps_bufptr; struct skin_viewport *skin_vp = skin_buffer_alloc(sizeof(struct skin_viewport)); /* check for the optional letter to signify its a hideable viewport */ /* %Vl|