76d2dd9c0e
an example use: %?mo<one|two|three> meaning that when the WPS is first opened "one" will be displayed, pressing select will change it to showing two, pressing it again will show three, and once more will go back to showing one. The text there could be any wps tags (conditional viewports for example...) There is no real limit on the amount of modes, but remember that if you create a WPS which uses this tag more than once that every use HAS to have the same amount of choices or bad things will happen. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19110 a1c6a512-1295-4272-9138-f99709370657
1739 lines
55 KiB
C
1739 lines
55 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "gwps.h"
|
|
#include "file.h"
|
|
#include "misc.h"
|
|
#include "plugin.h"
|
|
|
|
#ifdef __PCTOOL__
|
|
#ifdef WPSEDITOR
|
|
#include "proxy.h"
|
|
#include "settings.h"
|
|
#include "sysfont.h"
|
|
#include "gwps.h"
|
|
#include "font.h"
|
|
#include "bmp.h"
|
|
#include "backdrop.h"
|
|
#include "ctype.h"
|
|
#else
|
|
#include "checkwps.h"
|
|
#define SYSFONT_HEIGHT 8
|
|
#define DEBUGF printf
|
|
#endif /*WPSEDITOR*/
|
|
#define FONT_SYSFIXED 0
|
|
#define FONT_UI 1
|
|
#else
|
|
#include "debug.h"
|
|
#endif /*__PCTOOL__*/
|
|
|
|
#ifndef __PCTOOL__
|
|
#include <ctype.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "font.h"
|
|
|
|
#include "gwps.h"
|
|
#include "settings.h"
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
#include "bmp.h"
|
|
#endif
|
|
|
|
#include "backdrop.h"
|
|
|
|
#endif
|
|
|
|
#define WPS_DEFAULTCFG WPS_DIR "/rockbox_default.wps"
|
|
#define RWPS_DEFAULTCFG WPS_DIR "/rockbox_default.rwps"
|
|
|
|
#define WPS_ERROR_INVALID_PARAM -1
|
|
|
|
/* 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];
|
|
|
|
/* the current line in the file */
|
|
static int line;
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
|
|
#if LCD_DEPTH > 1
|
|
#define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS+1) /* WPS images + pbar bitmap + backdrop */
|
|
#else
|
|
#define MAX_BITMAPS (MAX_IMAGES+MAX_PROGRESSBARS) /* WPS images + pbar bitmap */
|
|
#endif
|
|
|
|
#define PROGRESSBAR_BMP MAX_IMAGES
|
|
#define BACKDROP_BMP (MAX_BITMAPS-1)
|
|
|
|
/* pointers to the bitmap filenames in the WPS source */
|
|
static const char *bmp_names[MAX_BITMAPS];
|
|
|
|
#endif /* HAVE_LCD_BITMAP */
|
|
|
|
#ifdef DEBUG
|
|
/* debugging function */
|
|
extern void print_debug_info(struct wps_data *data, int fail, int line);
|
|
#endif
|
|
|
|
static void wps_reset(struct wps_data *data);
|
|
|
|
/* 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;
|
|
};
|
|
|
|
/* 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);
|
|
|
|
#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_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_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);
|
|
#endif /*HAVE_LCD_BITMAP */
|
|
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
|
|
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_conditional(const char *wps_bufptr,
|
|
struct wps_token *token, struct wps_data *wps_data);
|
|
#endif /* HAVE_ALBUMART */
|
|
static int parse_viewmode(const char *wps_bufptr,
|
|
struct wps_token *token, struct wps_data *wps_data);
|
|
|
|
#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_RIGHT, "ar", 0, NULL },
|
|
|
|
{ WPS_TOKEN_BATTERY_PERCENT, "bl", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ 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
|
|
|
|
{ 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_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_CODEC, "Fc", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_FREQUENCY, "Ff", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_FREQUENCY_KHZ, "Fk", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_NAME_WITH_EXTENSION, "Fm", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_NAME, "Fn", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_PATH, "Fp", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_SIZE, "Fs", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_VBR, "Fv", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_FILE_DIRECTORY, "D", WPS_REFRESH_DYNAMIC,
|
|
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_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_COMPOSER, "Ic", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_ALBUM, "Id", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_ALBUM_ARTIST, "IA", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_GROUPING, "IG", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_GENRE, "Ig", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_DISC_NUMBER, "Ik", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_TRACK_NUMBER, "In", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_TRACK_TITLE, "It", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_VERSION, "Iv", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_YEAR, "Iy", WPS_REFRESH_DYNAMIC, NULL },
|
|
{ WPS_TOKEN_METADATA_COMMENT, "IC", WPS_REFRESH_DYNAMIC, NULL },
|
|
|
|
#if (CONFIG_CODEC != MAS3507D)
|
|
{ WPS_TOKEN_SOUND_PITCH, "Sp", 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 },
|
|
{ WPS_TOKEN_VIEWMODE, "mo", WPS_REFRESH_STATIC,
|
|
parse_viewmode },
|
|
|
|
#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, NULL },
|
|
|
|
{ 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_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_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_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 },
|
|
#ifdef HAVE_ALBUMART
|
|
{ WPS_NO_TOKEN, "Cl", 0, parse_albumart_load },
|
|
{ WPS_TOKEN_ALBUMART_DISPLAY, "C", WPS_REFRESH_STATIC,
|
|
parse_albumart_conditional },
|
|
#endif
|
|
|
|
{ WPS_VIEWPORT_ENABLE, "Vd", WPS_REFRESH_DYNAMIC,
|
|
parse_viewport_display },
|
|
{ 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_UNKNOWN, "", 0, NULL }
|
|
/* the array MUST end with an empty string (first char is \0) */
|
|
};
|
|
|
|
/* 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++;
|
|
int skip = 0;
|
|
while(*(wps_bufptr + skip) != '\n')
|
|
skip++;
|
|
return ++skip;
|
|
}
|
|
|
|
/* Starts a new subline in the current line during parsing */
|
|
static void wps_start_new_subline(struct wps_data *data)
|
|
{
|
|
data->num_sublines++;
|
|
data->sublines[data->num_sublines].first_token_idx = data->num_tokens;
|
|
data->lines[data->num_lines].num_sublines++;
|
|
}
|
|
|
|
#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;
|
|
if (wps_data->viewports[0].vp.y == 0)
|
|
{
|
|
wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
|
|
wps_data->viewports[0].vp.height -= STATUSBAR_HEIGHT;
|
|
}
|
|
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;
|
|
if (wps_data->viewports[0].vp.y == STATUSBAR_HEIGHT)
|
|
{
|
|
wps_data->viewports[0].vp.y = 0;
|
|
wps_data->viewports[0].vp.height += STATUSBAR_HEIGHT;
|
|
}
|
|
return skip_end_of_line(wps_bufptr);
|
|
}
|
|
|
|
static bool load_bitmap(struct wps_data *wps_data,
|
|
char* filename,
|
|
struct bitmap *bm)
|
|
{
|
|
int format;
|
|
#ifdef HAVE_REMOTE_LCD
|
|
if (wps_data->remote_wps)
|
|
format = FORMAT_ANY|FORMAT_REMOTE;
|
|
else
|
|
#endif
|
|
format = FORMAT_ANY|FORMAT_TRANSPARENT;
|
|
|
|
int ret = read_bmp_file(filename, bm,
|
|
wps_data->img_buf_free,
|
|
format);
|
|
|
|
if (ret > 0)
|
|
{
|
|
#if LCD_DEPTH == 16
|
|
if (ret % 2) ret++;
|
|
/* Always consume an even number of bytes */
|
|
#endif
|
|
wps_data->img_buf_ptr += ret;
|
|
wps_data->img_buf_free -= ret;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static char *get_image_filename(const char *start, const char* bmpdir,
|
|
char *buf, int buf_size)
|
|
{
|
|
const char *end = strchr(start, '|');
|
|
|
|
if ( !end || (end - start) >= (buf_size - ROCKBOX_DIR_LEN - 2) )
|
|
{
|
|
buf = "\0";
|
|
return NULL;
|
|
}
|
|
|
|
int bmpdirlen = strlen(bmpdir);
|
|
|
|
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)
|
|
{
|
|
(void)wps_data;
|
|
int n = get_image_id(wps_bufptr[0]);
|
|
int subimage;
|
|
|
|
if (n == -1)
|
|
{
|
|
/* invalid picture display tag */
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
}
|
|
|
|
if ((subimage = get_image_id(wps_bufptr[1])) != -1)
|
|
{
|
|
/* Sanity check */
|
|
if (subimage >= wps_data->img[n].num_subimages)
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
/* Store sub-image number to display in high bits */
|
|
token->value.i = n | (subimage << 8);
|
|
return 2; /* We have consumed 2 bytes */
|
|
} else {
|
|
token->value.i = n;
|
|
return 1; /* We have consumed 1 byte */
|
|
}
|
|
}
|
|
|
|
static int parse_image_load(const char *wps_bufptr,
|
|
struct wps_token *token,
|
|
struct wps_data *wps_data)
|
|
{
|
|
int n;
|
|
const char *ptr = wps_bufptr;
|
|
const char *pos;
|
|
const char* filename;
|
|
const char* id;
|
|
const char *newline;
|
|
int x,y;
|
|
|
|
/* 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 != '|')
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
/* get the image ID */
|
|
n = get_image_id(*id);
|
|
|
|
/* check the image number and load state */
|
|
if(n < 0 || n >= MAX_IMAGES || wps_data->img[n].loaded)
|
|
{
|
|
/* Invalid image ID */
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
}
|
|
|
|
/* save a pointer to the filename */
|
|
bmp_names[n] = filename;
|
|
|
|
wps_data->img[n].x = x;
|
|
wps_data->img[n].y = y;
|
|
|
|
/* save current viewport */
|
|
wps_data->img[n].vp = &wps_data->viewports[wps_data->num_viewports].vp;
|
|
|
|
if (token->type == WPS_TOKEN_IMAGE_DISPLAY)
|
|
{
|
|
wps_data->img[n].always_display = true;
|
|
}
|
|
else
|
|
{
|
|
/* Parse the (optional) number of sub-images */
|
|
ptr++;
|
|
newline = strchr(ptr, '\n');
|
|
pos = strchr(ptr, '|');
|
|
if (pos && pos < newline)
|
|
wps_data->img[n].num_subimages = atoi(ptr);
|
|
|
|
if (wps_data->img[n].num_subimages <= 0)
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
}
|
|
|
|
/* Skip the rest of the line */
|
|
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[0];
|
|
|
|
if (letter < 'a' || letter > 'z')
|
|
{
|
|
/* invalid viewport tag */
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
}
|
|
token->value.i = letter;
|
|
return 1;
|
|
}
|
|
|
|
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 viewport* vp;
|
|
int depth;
|
|
uint32_t set = 0;
|
|
enum {
|
|
PL_X = 0,
|
|
PL_Y,
|
|
PL_WIDTH,
|
|
PL_HEIGHT,
|
|
PL_FONT,
|
|
PL_FG,
|
|
PL_BG,
|
|
};
|
|
int lcd_width = LCD_WIDTH, lcd_height = LCD_HEIGHT;
|
|
#ifdef HAVE_REMOTE_LCD
|
|
if (wps_data->remote_wps)
|
|
{
|
|
lcd_width = LCD_REMOTE_WIDTH;
|
|
lcd_height = LCD_REMOTE_HEIGHT;
|
|
}
|
|
#endif
|
|
|
|
if (wps_data->num_viewports >= WPS_MAX_VIEWPORTS)
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
wps_data->num_viewports++;
|
|
/* check for the optional letter to signify its a hideable viewport */
|
|
/* %Vl|<label>|<rest of tags>| */
|
|
wps_data->viewports[wps_data->num_viewports].hidden_flags = 0;
|
|
|
|
if (*ptr == 'l')
|
|
{
|
|
if (*(ptr+1) == '|')
|
|
{
|
|
char label = *(ptr+2);
|
|
if (label >= 'a' && label <= 'z')
|
|
{
|
|
wps_data->viewports[wps_data->num_viewports].hidden_flags = VP_DRAW_HIDEABLE;
|
|
wps_data->viewports[wps_data->num_viewports].label = label;
|
|
}
|
|
else
|
|
return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
|
|
ptr += 3;
|
|
}
|
|
}
|
|
if (*ptr != '|')
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
ptr++;
|
|
vp = &wps_data->viewports[wps_data->num_viewports].vp;
|
|
/* format: %V|x|y|width|height|font|fg_pattern|bg_pattern| */
|
|
|
|
/* Set the defaults for fields not user-specified */
|
|
vp->drawmode = DRMODE_SOLID;
|
|
|
|
/* Work out the depth of this display */
|
|
#ifdef HAVE_REMOTE_LCD
|
|
depth = (wps_data->remote_wps ? LCD_REMOTE_DEPTH : LCD_DEPTH);
|
|
#else
|
|
depth = LCD_DEPTH;
|
|
#endif
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
if (depth == 16)
|
|
{
|
|
if (!(ptr = parse_list("dddddcc", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
|
|
&vp->height, &vp->font, &vp->fg_pattern,&vp->bg_pattern)))
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
}
|
|
else
|
|
#endif
|
|
#if (LCD_DEPTH == 2) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 2)
|
|
if (depth == 2) {
|
|
/* Default to black on white */
|
|
vp->fg_pattern = 0;
|
|
vp->bg_pattern = 3;
|
|
if (!(ptr = parse_list("dddddgg", &set, '|', ptr, &vp->x, &vp->y, &vp->width,
|
|
&vp->height, &vp->font, &vp->fg_pattern, &vp->bg_pattern)))
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
}
|
|
else
|
|
#endif
|
|
#if (LCD_DEPTH == 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH == 1)
|
|
if (depth == 1)
|
|
{
|
|
if (!(ptr = parse_list("ddddd", &set, '|', ptr, &vp->x, &vp->y,
|
|
&vp->width, &vp->height, &vp->font)))
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
}
|
|
else
|
|
#endif
|
|
{}
|
|
|
|
/* Check for trailing | */
|
|
if (*ptr != '|')
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
if (!LIST_VALUE_PARSED(set, PL_X) || !LIST_VALUE_PARSED(set, PL_Y))
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
/* fix defaults */
|
|
if (!LIST_VALUE_PARSED(set, PL_WIDTH))
|
|
vp->width = lcd_width - vp->x;
|
|
if (!LIST_VALUE_PARSED(set, PL_HEIGHT))
|
|
vp->height = lcd_height - vp->y;
|
|
|
|
/* Default to using the user font if the font was an invalid number */
|
|
if (!LIST_VALUE_PARSED(set, PL_FONT) ||
|
|
((vp->font != FONT_SYSFIXED) && (vp->font != FONT_UI)))
|
|
vp->font = FONT_UI;
|
|
|
|
/* Validate the viewport dimensions - we know that the numbers are
|
|
non-negative integers */
|
|
if ((vp->x >= lcd_width) ||
|
|
((vp->x + vp->width) > lcd_width) ||
|
|
(vp->y >= lcd_height) ||
|
|
((vp->y + vp->height) > lcd_height))
|
|
{
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
}
|
|
|
|
#ifdef HAVE_LCD_COLOR
|
|
if (depth == 16)
|
|
{
|
|
if (!LIST_VALUE_PARSED(set, PL_FG))
|
|
vp->fg_pattern = global_settings.fg_color;
|
|
if (!LIST_VALUE_PARSED(set, PL_BG))
|
|
vp->bg_pattern = global_settings.bg_color;
|
|
}
|
|
#endif
|
|
|
|
wps_data->viewports[wps_data->num_viewports-1].last_line = wps_data->num_lines - 1;
|
|
|
|
wps_data->viewports[wps_data->num_viewports].first_line = wps_data->num_lines;
|
|
|
|
if (wps_data->num_sublines < WPS_MAX_SUBLINES)
|
|
{
|
|
wps_data->lines[wps_data->num_lines].first_subline_idx =
|
|
wps_data->num_sublines;
|
|
|
|
wps_data->sublines[wps_data->num_sublines].first_token_idx =
|
|
wps_data->num_tokens;
|
|
}
|
|
|
|
/* Skip the rest of the line */
|
|
return skip_end_of_line(wps_bufptr);
|
|
}
|
|
|
|
|
|
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
|
|
static int parse_image_special(const char *wps_bufptr,
|
|
struct wps_token *token,
|
|
struct wps_data *wps_data)
|
|
{
|
|
(void)wps_data; /* kill warning */
|
|
(void)token;
|
|
const char *pos = NULL;
|
|
const char *newline;
|
|
|
|
pos = strchr(wps_bufptr + 1, '|');
|
|
newline = strchr(wps_bufptr, '\n');
|
|
|
|
if (pos > newline)
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
#if LCD_DEPTH > 1
|
|
if (token->type == WPS_TOKEN_IMAGE_BACKDROP)
|
|
{
|
|
/* format: %X|filename.bmp| */
|
|
bmp_names[BACKDROP_BMP] = wps_bufptr + 1;
|
|
}
|
|
#endif
|
|
|
|
/* Skip the rest of the line */
|
|
return skip_end_of_line(wps_bufptr);
|
|
}
|
|
#endif
|
|
|
|
#endif /* HAVE_LCD_BITMAP */
|
|
|
|
static int parse_dir_level(const char *wps_bufptr,
|
|
struct wps_token *token,
|
|
struct wps_data *wps_data)
|
|
{
|
|
char val[] = { *wps_bufptr, '\0' };
|
|
token->value.i = atoi(val);
|
|
(void)wps_data; /* Kill warnings */
|
|
return 1;
|
|
}
|
|
|
|
static int parse_timeout(const char *wps_bufptr,
|
|
struct wps_token *token,
|
|
struct wps_data *wps_data)
|
|
{
|
|
int skip = 0;
|
|
int val = 0;
|
|
bool have_point = false;
|
|
bool have_tenth = false;
|
|
|
|
(void)wps_data; /* Kill the warning */
|
|
|
|
while ( isdigit(*wps_bufptr) || *wps_bufptr == '.' )
|
|
{
|
|
if (*wps_bufptr != '.')
|
|
{
|
|
val *= 10;
|
|
val += *wps_bufptr - '0';
|
|
if (have_point)
|
|
{
|
|
have_tenth = true;
|
|
wps_bufptr++;
|
|
skip++;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
have_point = true;
|
|
|
|
wps_bufptr++;
|
|
skip++;
|
|
}
|
|
|
|
if (have_tenth == false)
|
|
val *= 10;
|
|
|
|
if (val == 0 && skip == 0)
|
|
{
|
|
/* decide what to do if no value was specified */
|
|
switch (token->type)
|
|
{
|
|
case WPS_TOKEN_SUBLINE_TIMEOUT:
|
|
return -1;
|
|
case WPS_TOKEN_BUTTON_VOLUME:
|
|
val = HZ;
|
|
break;
|
|
}
|
|
}
|
|
token->value.i = val;
|
|
|
|
return skip;
|
|
}
|
|
|
|
static int parse_progressbar(const char *wps_bufptr,
|
|
struct wps_token *token,
|
|
struct wps_data *wps_data)
|
|
{
|
|
(void)token; /* Kill warnings */
|
|
/* %pb or %pb|filename|x|y|width|height|
|
|
using - for any of the params uses "sane" values */
|
|
#ifdef HAVE_LCD_BITMAP
|
|
enum {
|
|
PB_FILENAME = 0,
|
|
PB_X,
|
|
PB_Y,
|
|
PB_WIDTH,
|
|
PB_HEIGHT
|
|
};
|
|
const char *filename;
|
|
int x, y, height, width;
|
|
uint32_t set = 0;
|
|
const char *ptr = wps_bufptr;
|
|
struct progressbar *pb;
|
|
struct viewport *vp = &wps_data->viewports[wps_data->num_viewports].vp;
|
|
#ifndef __PCTOOL__
|
|
int font_height = font_get(vp->font)->height;
|
|
#else
|
|
int font_height = 8;
|
|
#endif
|
|
int line_y_pos = font_height*(wps_data->num_lines -
|
|
wps_data->viewports[wps_data->num_viewports].first_line);
|
|
|
|
if (wps_data->progressbar_count >= MAX_PROGRESSBARS)
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
pb = &wps_data->progressbar[wps_data->progressbar_count];
|
|
pb->have_bitmap_pb = false;
|
|
|
|
if (*wps_bufptr != '|') /* regular old style */
|
|
{
|
|
pb->x = 0;
|
|
pb->width = vp->width;
|
|
pb->height = SYSFONT_HEIGHT-2;
|
|
pb->y = line_y_pos + (font_height-pb->height)/2;
|
|
|
|
wps_data->viewports[wps_data->num_viewports].pb = pb;
|
|
wps_data->progressbar_count++;
|
|
return 0;
|
|
}
|
|
ptr = wps_bufptr + 1;
|
|
|
|
if (!(ptr = parse_list("sdddd", &set, '|', ptr, &filename,
|
|
&x, &y, &width, &height)))
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
if (LIST_VALUE_PARSED(set, PB_FILENAME)) /* filename */
|
|
bmp_names[PROGRESSBAR_BMP+wps_data->progressbar_count] = filename;
|
|
|
|
if (LIST_VALUE_PARSED(set, PB_X)) /* x */
|
|
pb->x = x;
|
|
else
|
|
pb->x = vp->x;
|
|
|
|
if (LIST_VALUE_PARSED(set, PB_WIDTH)) /* width */
|
|
{
|
|
/* A zero width causes a divide-by-zero error later, so reject it */
|
|
if (width == 0)
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
pb->width = width;
|
|
}
|
|
else
|
|
pb->width = vp->width - pb->x;
|
|
|
|
if (LIST_VALUE_PARSED(set, PB_HEIGHT)) /* height, default to font height */
|
|
{
|
|
/* A zero height makes no sense - reject it */
|
|
if (height == 0)
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
pb->height = height;
|
|
}
|
|
else
|
|
pb->height = font_height;
|
|
|
|
if (LIST_VALUE_PARSED(set, PB_Y)) /* y */
|
|
pb->y = y;
|
|
else
|
|
pb->y = line_y_pos + (font_height-pb->height)/2;
|
|
|
|
wps_data->viewports[wps_data->num_viewports].pb = pb;
|
|
wps_data->progressbar_count++;
|
|
|
|
/* Skip the rest of the line */
|
|
return skip_end_of_line(wps_bufptr)-1;
|
|
#else
|
|
|
|
if (*(wps_bufptr-1) == 'f')
|
|
wps_data->full_line_progressbar = true;
|
|
else
|
|
wps_data->full_line_progressbar = false;
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
}
|
|
|
|
#ifdef HAVE_ALBUMART
|
|
static int parse_albumart_load(const char *wps_bufptr,
|
|
struct wps_token *token,
|
|
struct wps_data *wps_data)
|
|
{
|
|
const char *_pos, *newline;
|
|
bool parsing;
|
|
const short xalign_mask = WPS_ALBUMART_ALIGN_LEFT |
|
|
WPS_ALBUMART_ALIGN_CENTER |
|
|
WPS_ALBUMART_ALIGN_RIGHT;
|
|
const short yalign_mask = WPS_ALBUMART_ALIGN_TOP |
|
|
WPS_ALBUMART_ALIGN_CENTER |
|
|
WPS_ALBUMART_ALIGN_BOTTOM;
|
|
|
|
(void)token; /* silence warning */
|
|
|
|
/* reset albumart info in wps */
|
|
wps_data->wps_uses_albumart = WPS_ALBUMART_NONE;
|
|
wps_data->albumart_max_width = -1;
|
|
wps_data->albumart_max_height = -1;
|
|
wps_data->albumart_xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
|
|
wps_data->albumart_yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
|
|
|
|
/* format: %Cl|x|y|[[l|c|r][d|i|s]mwidth]|[[t|c|b][d|i|s]mheight]| */
|
|
|
|
newline = strchr(wps_bufptr, '\n');
|
|
|
|
/* initial validation and parsing of x and y components */
|
|
if (*wps_bufptr != '|')
|
|
return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl7 */
|
|
|
|
_pos = wps_bufptr + 1;
|
|
if (!isdigit(*_pos))
|
|
return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|@ */
|
|
wps_data->albumart_x = atoi(_pos);
|
|
|
|
_pos = strchr(_pos, '|');
|
|
if (!_pos || _pos > newline || !isdigit(*(++_pos)))
|
|
return WPS_ERROR_INVALID_PARAM; /* malformed token: e.g. %Cl|7\n or %Cl|7|@ */
|
|
|
|
wps_data->albumart_y = atoi(_pos);
|
|
|
|
_pos = strchr(_pos, '|');
|
|
if (!_pos || _pos > newline)
|
|
return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after y coordinate
|
|
e.g. %Cl|7|59\n */
|
|
|
|
/* parsing width field */
|
|
parsing = true;
|
|
while (parsing)
|
|
{
|
|
/* apply each modifier in turn */
|
|
++_pos;
|
|
switch (*_pos)
|
|
{
|
|
case 'l':
|
|
case 'L':
|
|
case '+':
|
|
wps_data->albumart_xalign =
|
|
(wps_data->albumart_xalign & xalign_mask) |
|
|
WPS_ALBUMART_ALIGN_LEFT;
|
|
break;
|
|
case 'c':
|
|
case 'C':
|
|
wps_data->albumart_xalign =
|
|
(wps_data->albumart_xalign & xalign_mask) |
|
|
WPS_ALBUMART_ALIGN_CENTER;
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
case '-':
|
|
wps_data->albumart_xalign =
|
|
(wps_data->albumart_xalign & xalign_mask) |
|
|
WPS_ALBUMART_ALIGN_RIGHT;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
wps_data->albumart_xalign |= WPS_ALBUMART_DECREASE;
|
|
break;
|
|
case 'i':
|
|
case 'I':
|
|
wps_data->albumart_xalign |= WPS_ALBUMART_INCREASE;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
wps_data->albumart_xalign |=
|
|
(WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
|
|
break;
|
|
default:
|
|
parsing = false;
|
|
break;
|
|
}
|
|
}
|
|
/* extract max width data */
|
|
if (*_pos != '|')
|
|
{
|
|
if (!isdigit(*_pos)) /* malformed token: e.g. %Cl|7|59|# */
|
|
return WPS_ERROR_INVALID_PARAM;
|
|
|
|
wps_data->albumart_max_width = atoi(_pos);
|
|
|
|
_pos = strchr(_pos, '|');
|
|
if (!_pos || _pos > newline)
|
|
return WPS_ERROR_INVALID_PARAM; /* malformed token: no | after width field
|
|
e.g. %Cl|7|59|200\n */
|
|
}
|
|
|
|
/* parsing height field */
|
|
parsing = true;
|
|
while (parsing)
|
|
{
|
|
/* apply each modifier in turn */
|
|
++_pos;
|
|
switch (*_pos)
|
|
{
|
|
case 't':
|
|
case 'T':
|
|
case '-':
|
|
wps_data->albumart_yalign =
|
|
(wps_data->albumart_yalign & yalign_mask) |
|
|
WPS_ALBUMART_ALIGN_TOP;
|
|
break;
|
|
case 'c':
|
|
case 'C':
|
|
wps_data->albumart_yalign =
|
|
(wps_data->albumart_yalign & yalign_mask) |
|
|
WPS_ALBUMART_ALIGN_CENTER;
|
|
break;
|
|
case 'b':
|
|
case 'B':
|
|
case '+':
|
|
wps_data->albumart_yalign =
|
|
(wps_data->albumart_yalign & yalign_mask) |
|
|
WPS_ALBUMART_ALIGN_BOTTOM;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
wps_data->albumart_yalign |= WPS_ALBUMART_DECREASE;
|
|
break;
|
|
case 'i':
|
|
case 'I':
|
|
wps_data->albumart_yalign |= WPS_ALBUMART_INCREASE;
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
wps_data->albumart_yalign |=
|
|
(WPS_ALBUMART_DECREASE | WPS_ALBUMART_INCREASE);
|
|
break;
|
|
default:
|
|
parsing = false;
|
|
break;
|
|
}
|
|
}
|
|
/* extract max height data */
|
|
if (*_pos != '|')
|
|
{
|
|
if (!isdigit(*_pos))
|
|
return WPS_ERROR_INVALID_PARAM; /* malformed token e.g. %Cl|7|59|200|@ */
|
|
|
|
wps_data->albumart_max_height = atoi(_pos);
|
|
|
|
_pos = strchr(_pos, '|');
|
|
if (!_pos || _pos > newline)
|
|
return WPS_ERROR_INVALID_PARAM; /* malformed token: no closing |
|
|
e.g. %Cl|7|59|200|200\n */
|
|
}
|
|
|
|
/* if we got here, we parsed everything ok .. ! */
|
|
if (wps_data->albumart_max_width < 0)
|
|
wps_data->albumart_max_width = 0;
|
|
else if (wps_data->albumart_max_width > LCD_WIDTH)
|
|
wps_data->albumart_max_width = LCD_WIDTH;
|
|
|
|
if (wps_data->albumart_max_height < 0)
|
|
wps_data->albumart_max_height = 0;
|
|
else if (wps_data->albumart_max_height > LCD_HEIGHT)
|
|
wps_data->albumart_max_height = LCD_HEIGHT;
|
|
|
|
wps_data->wps_uses_albumart = WPS_ALBUMART_LOAD;
|
|
|
|
/* Skip the rest of the line */
|
|
return skip_end_of_line(wps_bufptr);
|
|
}
|
|
|
|
static int parse_albumart_conditional(const char *wps_bufptr,
|
|
struct wps_token *token,
|
|
struct wps_data *wps_data)
|
|
{
|
|
struct wps_token *prevtoken = token;
|
|
--prevtoken;
|
|
if (wps_data->num_tokens >= 1 && prevtoken->type == WPS_TOKEN_CONDITIONAL)
|
|
{
|
|
/* This %C is part of a %?C construct.
|
|
It's either %?C<blah> or %?Cn<blah> */
|
|
token->type = WPS_TOKEN_ALBUMART_FOUND;
|
|
if (*wps_bufptr == 'n' && *(wps_bufptr + 1) == '<')
|
|
{
|
|
token->next = true;
|
|
return 1;
|
|
}
|
|
else if (*wps_bufptr == '<')
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
token->type = WPS_NO_TOKEN;
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This %C tag is in a conditional construct. */
|
|
wps_data->albumart_cond_index = condindex[level];
|
|
return 0;
|
|
}
|
|
};
|
|
#endif /* HAVE_ALBUMART */
|
|
|
|
static int parse_viewmode(const char *wps_bufptr,
|
|
struct wps_token *token,
|
|
struct wps_data *wps_data)
|
|
{
|
|
(void)wps_bufptr; (void)token;
|
|
wps_data->current_mode = 1;
|
|
/* are we going to add parameters? */
|
|
return 0;
|
|
}
|
|
/* Parse a generic token from the given string. Return the length read */
|
|
static int parse_token(const char *wps_bufptr, struct wps_data *wps_data)
|
|
{
|
|
int skip = 0, taglen = 0, ret;
|
|
struct wps_token *token = wps_data->tokens + wps_data->num_tokens;
|
|
const struct wps_tag *tag;
|
|
|
|
switch(*wps_bufptr)
|
|
{
|
|
|
|
case '%':
|
|
case '<':
|
|
case '|':
|
|
case '>':
|
|
case ';':
|
|
case '#':
|
|
/* escaped characters */
|
|
token->type = WPS_TOKEN_CHARACTER;
|
|
token->value.c = *wps_bufptr;
|
|
taglen = 1;
|
|
wps_data->num_tokens++;
|
|
break;
|
|
|
|
case '?':
|
|
/* conditional tag */
|
|
token->type = WPS_TOKEN_CONDITIONAL;
|
|
level++;
|
|
condindex[level] = wps_data->num_tokens;
|
|
numoptions[level] = 1;
|
|
wps_data->num_tokens++;
|
|
ret = parse_token(wps_bufptr + 1, wps_data);
|
|
if (ret < 0) return ret;
|
|
taglen = 1 + ret;
|
|
break;
|
|
|
|
default:
|
|
/* find what tag we have */
|
|
for (tag = all_tags;
|
|
strncmp(wps_bufptr, tag->name, strlen(tag->name)) != 0;
|
|
tag++) ;
|
|
|
|
taglen = (tag->type != WPS_TOKEN_UNKNOWN) ? strlen(tag->name) : 2;
|
|
token->type = tag->type;
|
|
wps_data->sublines[wps_data->num_sublines].line_type |=
|
|
tag->refresh_type;
|
|
|
|
/* if the tag has a special parsing function, we call it */
|
|
if (tag->parse_func)
|
|
{
|
|
ret = tag->parse_func(wps_bufptr + taglen, token, wps_data);
|
|
if (ret < 0) return ret;
|
|
skip += ret;
|
|
}
|
|
|
|
/* Some tags we don't want to save as tokens */
|
|
if (tag->type == WPS_NO_TOKEN)
|
|
break;
|
|
|
|
/* tags that start with 'F', 'I' or 'D' are for the next file */
|
|
if ( *(tag->name) == 'I' || *(tag->name) == 'F' ||
|
|
*(tag->name) == 'D')
|
|
token->next = true;
|
|
|
|
wps_data->num_tokens++;
|
|
break;
|
|
}
|
|
|
|
skip += taglen;
|
|
return skip;
|
|
}
|
|
|
|
/* Parses the WPS.
|
|
data is the pointer to the structure where the parsed WPS should be stored.
|
|
It is initialised.
|
|
wps_bufptr points to the string containing the WPS tags */
|
|
static bool wps_parse(struct wps_data *data, const char *wps_bufptr)
|
|
{
|
|
if (!data || !wps_bufptr || !*wps_bufptr)
|
|
return false;
|
|
|
|
char *stringbuf = data->string_buffer;
|
|
int stringbuf_used = 0;
|
|
enum wps_parse_error fail = PARSE_OK;
|
|
int ret;
|
|
line = 1;
|
|
level = -1;
|
|
|
|
while(*wps_bufptr && !fail && data->num_tokens < WPS_MAX_TOKENS - 1
|
|
&& data->num_viewports < WPS_MAX_VIEWPORTS
|
|
&& data->num_lines < WPS_MAX_LINES)
|
|
{
|
|
switch(*wps_bufptr++)
|
|
{
|
|
|
|
/* Regular tag */
|
|
case '%':
|
|
if ((ret = parse_token(wps_bufptr, data)) < 0)
|
|
{
|
|
fail = PARSE_FAIL_COND_INVALID_PARAM;
|
|
break;
|
|
}
|
|
else if (level >= WPS_MAX_COND_LEVEL - 1)
|
|
{
|
|
fail = PARSE_FAIL_LIMITS_EXCEEDED;
|
|
break;
|
|
}
|
|
wps_bufptr += ret;
|
|
break;
|
|
|
|
/* Alternating sublines separator */
|
|
case ';':
|
|
if (level >= 0) /* there are unclosed conditionals */
|
|
{
|
|
fail = PARSE_FAIL_UNCLOSED_COND;
|
|
break;
|
|
}
|
|
|
|
if (data->num_sublines+1 < WPS_MAX_SUBLINES)
|
|
wps_start_new_subline(data);
|
|
else
|
|
fail = PARSE_FAIL_LIMITS_EXCEEDED;
|
|
|
|
break;
|
|
|
|
/* Conditional list start */
|
|
case '<':
|
|
if (data->tokens[data->num_tokens-2].type != WPS_TOKEN_CONDITIONAL)
|
|
{
|
|
fail = PARSE_FAIL_COND_SYNTAX_ERROR;
|
|
break;
|
|
}
|
|
|
|
data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_START;
|
|
lastcond[level] = data->num_tokens++;
|
|
break;
|
|
|
|
/* Conditional list end */
|
|
case '>':
|
|
if (level < 0) /* not in a conditional, invalid char */
|
|
{
|
|
fail = PARSE_FAIL_INVALID_CHAR;
|
|
break;
|
|
}
|
|
|
|
data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_END;
|
|
if (lastcond[level])
|
|
data->tokens[lastcond[level]].value.i = data->num_tokens;
|
|
else
|
|
{
|
|
fail = PARSE_FAIL_COND_SYNTAX_ERROR;
|
|
break;
|
|
}
|
|
|
|
lastcond[level] = 0;
|
|
data->num_tokens++;
|
|
data->tokens[condindex[level]].value.i = numoptions[level];
|
|
level--;
|
|
break;
|
|
|
|
/* Conditional list option */
|
|
case '|':
|
|
if (level < 0) /* not in a conditional, invalid char */
|
|
{
|
|
fail = PARSE_FAIL_INVALID_CHAR;
|
|
break;
|
|
}
|
|
|
|
data->tokens[data->num_tokens].type = WPS_TOKEN_CONDITIONAL_OPTION;
|
|
if (lastcond[level])
|
|
data->tokens[lastcond[level]].value.i = data->num_tokens;
|
|
else
|
|
{
|
|
fail = PARSE_FAIL_COND_SYNTAX_ERROR;
|
|
break;
|
|
}
|
|
|
|
lastcond[level] = data->num_tokens;
|
|
numoptions[level]++;
|
|
data->num_tokens++;
|
|
break;
|
|
|
|
/* Comment */
|
|
case '#':
|
|
if (level >= 0) /* there are unclosed conditionals */
|
|
{
|
|
fail = PARSE_FAIL_UNCLOSED_COND;
|
|
break;
|
|
}
|
|
|
|
wps_bufptr += skip_end_of_line(wps_bufptr);
|
|
break;
|
|
|
|
/* End of this line */
|
|
case '\n':
|
|
if (level >= 0) /* there are unclosed conditionals */
|
|
{
|
|
fail = PARSE_FAIL_UNCLOSED_COND;
|
|
break;
|
|
}
|
|
|
|
line++;
|
|
wps_start_new_subline(data);
|
|
data->num_lines++; /* Start a new line */
|
|
|
|
if ((data->num_lines < WPS_MAX_LINES) &&
|
|
(data->num_sublines < WPS_MAX_SUBLINES))
|
|
{
|
|
data->lines[data->num_lines].first_subline_idx =
|
|
data->num_sublines;
|
|
|
|
data->sublines[data->num_sublines].first_token_idx =
|
|
data->num_tokens;
|
|
}
|
|
|
|
break;
|
|
|
|
/* String */
|
|
default:
|
|
{
|
|
unsigned int len = 1;
|
|
const char *string_start = wps_bufptr - 1;
|
|
|
|
/* find the length of the string */
|
|
while (*wps_bufptr && *wps_bufptr != '#' &&
|
|
*wps_bufptr != '%' && *wps_bufptr != ';' &&
|
|
*wps_bufptr != '<' && *wps_bufptr != '>' &&
|
|
*wps_bufptr != '|' && *wps_bufptr != '\n')
|
|
{
|
|
wps_bufptr++;
|
|
len++;
|
|
}
|
|
|
|
/* look if we already have that string */
|
|
char **str;
|
|
int i;
|
|
bool found;
|
|
for (i = 0, str = data->strings, found = false;
|
|
i < data->num_strings &&
|
|
!(found = (strlen(*str) == len &&
|
|
strncmp(string_start, *str, len) == 0));
|
|
i++, str++);
|
|
/* If a matching string is found, found is true and i is
|
|
the index of the string. If not, found is false */
|
|
|
|
if (!found)
|
|
{
|
|
/* new string */
|
|
|
|
if (stringbuf_used + len > STRING_BUFFER_SIZE - 1
|
|
|| data->num_strings >= WPS_MAX_STRINGS)
|
|
{
|
|
/* too many strings or characters */
|
|
fail = PARSE_FAIL_LIMITS_EXCEEDED;
|
|
break;
|
|
}
|
|
|
|
strncpy(stringbuf, string_start, len);
|
|
*(stringbuf + len) = '\0';
|
|
|
|
data->strings[data->num_strings] = stringbuf;
|
|
stringbuf += len + 1;
|
|
stringbuf_used += len + 1;
|
|
data->tokens[data->num_tokens].value.i =
|
|
data->num_strings;
|
|
data->num_strings++;
|
|
}
|
|
else
|
|
{
|
|
/* another occurrence of an existing string */
|
|
data->tokens[data->num_tokens].value.i = i;
|
|
}
|
|
data->tokens[data->num_tokens].type = WPS_TOKEN_STRING;
|
|
data->num_tokens++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!fail && level >= 0) /* there are unclosed conditionals */
|
|
fail = PARSE_FAIL_UNCLOSED_COND;
|
|
|
|
if (*wps_bufptr && !fail)
|
|
/* one of the limits of the while loop was exceeded */
|
|
fail = PARSE_FAIL_LIMITS_EXCEEDED;
|
|
|
|
data->viewports[data->num_viewports].last_line = data->num_lines - 1;
|
|
|
|
/* We have finished with the last viewport, so increment count */
|
|
data->num_viewports++;
|
|
|
|
#ifdef DEBUG
|
|
print_debug_info(data, fail, line);
|
|
#endif
|
|
|
|
return (fail == 0);
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
/* Clear the WPS image cache */
|
|
static void wps_images_clear(struct wps_data *data)
|
|
{
|
|
int i;
|
|
/* set images to unloaded and not displayed */
|
|
for (i = 0; i < MAX_IMAGES; i++)
|
|
{
|
|
data->img[i].loaded = false;
|
|
data->img[i].display = -1;
|
|
data->img[i].always_display = false;
|
|
data->img[i].num_subimages = 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* initial setup of wps_data */
|
|
void wps_data_init(struct wps_data *wps_data)
|
|
{
|
|
#ifdef HAVE_LCD_BITMAP
|
|
wps_images_clear(wps_data);
|
|
wps_data->wps_sb_tag = false;
|
|
wps_data->show_sb_on_wps = false;
|
|
wps_data->img_buf_ptr = wps_data->img_buf; /* where in image buffer */
|
|
wps_data->img_buf_free = IMG_BUFSIZE; /* free space in image buffer */
|
|
wps_data->peak_meter_enabled = false;
|
|
/* progress bars */
|
|
wps_data->progressbar_count = 0;
|
|
#else /* HAVE_LCD_CHARCELLS */
|
|
int i;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
wps_data->wps_progress_pat[i] = 0;
|
|
}
|
|
wps_data->full_line_progressbar = false;
|
|
#endif
|
|
wps_data->button_time_volume = 0;
|
|
wps_data->current_mode = -1;
|
|
wps_data->wps_loaded = false;
|
|
}
|
|
|
|
static void wps_reset(struct wps_data *data)
|
|
{
|
|
#ifdef HAVE_REMOTE_LCD
|
|
bool rwps = data->remote_wps; /* remember whether the data is for a RWPS */
|
|
#endif
|
|
memset(data, 0, sizeof(*data));
|
|
#ifdef HAVE_ALBUMART
|
|
data->wps_uses_albumart = WPS_ALBUMART_NONE;
|
|
#endif
|
|
wps_data_init(data);
|
|
#ifdef HAVE_REMOTE_LCD
|
|
data->remote_wps = rwps;
|
|
#endif
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
|
|
static bool load_wps_bitmaps(struct wps_data *wps_data, char *bmpdir)
|
|
{
|
|
char img_path[MAX_PATH];
|
|
struct bitmap *bitmap;
|
|
bool *loaded;
|
|
int n;
|
|
for (n = 0; n < BACKDROP_BMP; n++)
|
|
{
|
|
if (bmp_names[n])
|
|
{
|
|
get_image_filename(bmp_names[n], bmpdir,
|
|
img_path, sizeof(img_path));
|
|
|
|
if (n >= PROGRESSBAR_BMP ) {
|
|
/* progressbar bitmap */
|
|
bitmap = &wps_data->progressbar[n-PROGRESSBAR_BMP].bm;
|
|
loaded = &wps_data->progressbar[n-PROGRESSBAR_BMP].have_bitmap_pb;
|
|
} else {
|
|
/* regular bitmap */
|
|
bitmap = &wps_data->img[n].bm;
|
|
loaded = &wps_data->img[n].loaded;
|
|
}
|
|
|
|
/* load the image */
|
|
bitmap->data = wps_data->img_buf_ptr;
|
|
if (load_bitmap(wps_data, img_path, bitmap))
|
|
{
|
|
*loaded = true;
|
|
|
|
/* Calculate and store height if this image has sub-images */
|
|
if (n < MAX_IMAGES)
|
|
wps_data->img[n].subimage_height = wps_data->img[n].bm.height /
|
|
wps_data->img[n].num_subimages;
|
|
}
|
|
else
|
|
{
|
|
/* Abort if we can't load an image */
|
|
DEBUGF("ERR: Failed to load image %d - %s\n",n,img_path);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
|
|
if (bmp_names[BACKDROP_BMP])
|
|
{
|
|
get_image_filename(bmp_names[BACKDROP_BMP], bmpdir,
|
|
img_path, sizeof(img_path));
|
|
|
|
#if defined(HAVE_REMOTE_LCD)
|
|
/* We only need to check LCD type if there is a remote LCD */
|
|
if (!wps_data->remote_wps)
|
|
#endif
|
|
{
|
|
/* Load backdrop for the main LCD */
|
|
if (!load_wps_backdrop(img_path))
|
|
return false;
|
|
}
|
|
#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
|
|
else
|
|
{
|
|
/* Load backdrop for the remote LCD */
|
|
if (!load_remote_wps_backdrop(img_path))
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
#endif /* has backdrop support */
|
|
|
|
/* If we got here, everything was OK */
|
|
return true;
|
|
}
|
|
|
|
#endif /* HAVE_LCD_BITMAP */
|
|
|
|
/* to setup up the wps-data from a format-buffer (isfile = false)
|
|
from a (wps-)file (isfile = true)*/
|
|
bool wps_data_load(struct wps_data *wps_data,
|
|
struct screen *display,
|
|
const char *buf,
|
|
bool isfile)
|
|
{
|
|
if (!wps_data || !buf)
|
|
return false;
|
|
|
|
wps_reset(wps_data);
|
|
|
|
/* Initialise the first (default) viewport */
|
|
wps_data->viewports[0].vp.x = 0;
|
|
wps_data->viewports[0].vp.width = display->getwidth();
|
|
if (!global_settings.statusbar)
|
|
{
|
|
wps_data->viewports[0].vp.y = 0;
|
|
wps_data->viewports[0].vp.height = display->getheight();
|
|
}
|
|
else
|
|
{
|
|
wps_data->viewports[0].vp.y = STATUSBAR_HEIGHT;
|
|
wps_data->viewports[0].vp.height = display->getheight() -
|
|
STATUSBAR_HEIGHT;
|
|
}
|
|
#ifdef HAVE_LCD_BITMAP
|
|
wps_data->viewports[0].vp.font = FONT_UI;
|
|
wps_data->viewports[0].vp.drawmode = DRMODE_SOLID;
|
|
#endif
|
|
#if LCD_DEPTH > 1
|
|
if (display->depth > 1)
|
|
{
|
|
wps_data->viewports[0].vp.fg_pattern = display->get_foreground();
|
|
wps_data->viewports[0].vp.bg_pattern = display->get_background();
|
|
}
|
|
#endif
|
|
if (!isfile)
|
|
{
|
|
return wps_parse(wps_data, buf);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Hardcode loading WPS_DEFAULTCFG to cause a reset ideally this
|
|
* wants to be a virtual file. Feel free to modify dirbrowse()
|
|
* if you're feeling brave.
|
|
*/
|
|
#ifndef __PCTOOL__
|
|
if (! strcmp(buf, WPS_DEFAULTCFG) )
|
|
{
|
|
global_settings.wps_file[0] = 0;
|
|
return false;
|
|
}
|
|
|
|
#ifdef HAVE_REMOTE_LCD
|
|
if (! strcmp(buf, RWPS_DEFAULTCFG) )
|
|
{
|
|
global_settings.rwps_file[0] = 0;
|
|
return false;
|
|
}
|
|
#endif
|
|
#endif /* __PCTOOL__ */
|
|
|
|
int fd = open_utf8(buf, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
/* get buffer space from the plugin buffer */
|
|
size_t buffersize = 0;
|
|
char *wps_buffer = (char *)plugin_get_buffer(&buffersize);
|
|
|
|
if (!wps_buffer)
|
|
return false;
|
|
|
|
/* copy the file's content to the buffer for parsing,
|
|
ensuring that every line ends with a newline char. */
|
|
unsigned int start = 0;
|
|
while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
|
|
{
|
|
start += strlen(wps_buffer + start);
|
|
if (start < buffersize - 1)
|
|
{
|
|
wps_buffer[start++] = '\n';
|
|
wps_buffer[start] = 0;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
if (start <= 0)
|
|
return false;
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
/* Set all filename pointers to NULL */
|
|
memset(bmp_names, 0, sizeof(bmp_names));
|
|
#endif
|
|
|
|
/* parse the WPS source */
|
|
if (!wps_parse(wps_data, wps_buffer)) {
|
|
wps_reset(wps_data);
|
|
return false;
|
|
}
|
|
|
|
wps_data->wps_loaded = true;
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
/* get the bitmap dir */
|
|
char bmpdir[MAX_PATH];
|
|
size_t bmpdirlen;
|
|
char *dot = strrchr(buf, '.');
|
|
bmpdirlen = dot - buf;
|
|
strncpy(bmpdir, buf, dot - buf);
|
|
bmpdir[bmpdirlen] = 0;
|
|
|
|
/* load the bitmaps that were found by the parsing */
|
|
if (!load_wps_bitmaps(wps_data, bmpdir)) {
|
|
wps_reset(wps_data);
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
}
|
|
|
|
int wps_subline_index(struct wps_data *data, int line, int subline)
|
|
{
|
|
return data->lines[line].first_subline_idx + subline;
|
|
}
|
|
|
|
int wps_first_token_index(struct wps_data *data, int line, int subline)
|
|
{
|
|
int first_subline_idx = data->lines[line].first_subline_idx;
|
|
return data->sublines[first_subline_idx + subline].first_token_idx;
|
|
}
|
|
|
|
int wps_last_token_index(struct wps_data *data, int line, int subline)
|
|
{
|
|
int first_subline_idx = data->lines[line].first_subline_idx;
|
|
int idx = first_subline_idx + subline;
|
|
if (idx < data->num_sublines - 1)
|
|
{
|
|
/* This subline ends where the next begins */
|
|
return data->sublines[idx+1].first_token_idx - 1;
|
|
}
|
|
else
|
|
{
|
|
/* The last subline goes to the end */
|
|
return data->num_tokens - 1;
|
|
}
|
|
}
|