mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
Themes for a given device often reference the assets of the default cabbiev2 theme. Record a list of what is bundled, and check anything not included with the theme against that list. If something is still missing, then that's an error. This applies to fonts, wps files, backdrops, icons, and so forth. Change-Id: I5ff7feaae892c8b9a81c2cb3035879672f3e6c29
629 lines
15 KiB
C
629 lines
15 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
*
|
|
* Copyright (C) 2008 by Dave Chapman
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include "config.h"
|
|
#include "checkwps.h"
|
|
#include "resize.h"
|
|
#include "wps.h"
|
|
#include "skin_buffer.h"
|
|
#include "skin_debug.h"
|
|
#include "skin_engine.h"
|
|
#include "wps_internals.h"
|
|
#include "settings.h"
|
|
#include "viewport.h"
|
|
#include "file.h"
|
|
#include "font.h"
|
|
#include "misc.h"
|
|
|
|
bool debug_wps = false;
|
|
int wps_verbose_level = 0;
|
|
char *skin_buffer;
|
|
|
|
#ifdef SIMULATOR
|
|
#error beep beep
|
|
#endif
|
|
|
|
/* static endianness conversion */
|
|
#define SWAP_16(x) ((typeof(x))(unsigned short)(((unsigned short)(x) >> 8) | \
|
|
((unsigned short)(x) << 8)))
|
|
|
|
#define SWAP_32(x) ((typeof(x))(unsigned long)( ((unsigned long)(x) >> 24) | \
|
|
(((unsigned long)(x) & 0xff0000ul) >> 8) | \
|
|
(((unsigned long)(x) & 0xff00ul) << 8) | \
|
|
((unsigned long)(x) << 24)))
|
|
|
|
#ifndef letoh16
|
|
unsigned short letoh16(unsigned short x)
|
|
{
|
|
unsigned short n = 0x1234;
|
|
unsigned char* ch = (unsigned char*)&n;
|
|
|
|
if (*ch == 0x34)
|
|
{
|
|
/* Little-endian */
|
|
return x;
|
|
} else {
|
|
return SWAP_16(x);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef letoh32
|
|
unsigned short letoh32(unsigned short x)
|
|
{
|
|
unsigned short n = 0x1234;
|
|
unsigned char* ch = (unsigned char*)&n;
|
|
|
|
if (*ch == 0x34)
|
|
{
|
|
/* Little-endian */
|
|
return x;
|
|
} else {
|
|
return SWAP_32(x);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifndef htole32
|
|
unsigned int htole32(unsigned int x)
|
|
{
|
|
unsigned short n = 0x1234;
|
|
unsigned char* ch = (unsigned char*)&n;
|
|
|
|
if (*ch == 0x34)
|
|
{
|
|
/* Little-endian */
|
|
return x;
|
|
} else {
|
|
return SWAP_32(x);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int recalc_dimension(struct dim *dst, struct dim *src)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_ALBUMART
|
|
int playback_claim_aa_slot(struct dim *dim)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void playback_release_aa_slot(int slot)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
int resize_on_load(struct bitmap *bm, bool dither,
|
|
struct dim *src, struct rowset *tmp_row,
|
|
unsigned char *buf, unsigned int len,
|
|
const struct custom_format *cformat,
|
|
IF_PIX_FMT(int format_index,)
|
|
struct img_part* (*store_part)(void *args),
|
|
void *args)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static char pluginbuf[PLUGIN_BUFFER_SIZE];
|
|
|
|
static unsigned dummy_func2(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void* plugin_get_buffer(size_t *buffer_size)
|
|
{
|
|
*buffer_size = PLUGIN_BUFFER_SIZE;
|
|
return pluginbuf;
|
|
}
|
|
|
|
static struct viewport* init_viewport(struct viewport* vp)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
struct user_settings global_settings = {
|
|
.statusbar = STATUSBAR_TOP,
|
|
#ifdef HAVE_LCD_COLOR
|
|
.bg_color = LCD_DEFAULT_BG,
|
|
.fg_color = LCD_DEFAULT_FG,
|
|
#endif
|
|
};
|
|
|
|
struct system_status global_status;
|
|
|
|
int getwidth(void) { return LCD_WIDTH; }
|
|
int getheight(void) { return LCD_HEIGHT; }
|
|
int getuifont(void) { return 0; }
|
|
#ifdef HAVE_REMOTE_LCD
|
|
int remote_getwidth(void) { return LCD_REMOTE_WIDTH; }
|
|
int remote_getheight(void) { return LCD_REMOTE_HEIGHT; }
|
|
#endif
|
|
|
|
static inline bool backdrop_load(const char *filename, char* backdrop_buffer)
|
|
{
|
|
(void)filename; (void)backdrop_buffer; return true;
|
|
}
|
|
|
|
struct screen screens[NB_SCREENS] =
|
|
{
|
|
{
|
|
.screen_type=SCREEN_MAIN,
|
|
.lcdwidth=LCD_WIDTH,
|
|
.lcdheight=LCD_HEIGHT,
|
|
.depth=LCD_DEPTH,
|
|
#ifdef HAVE_LCD_COLOR
|
|
.is_color=true,
|
|
#else
|
|
.is_color=false,
|
|
#endif
|
|
.init_viewport=init_viewport,
|
|
.getwidth = getwidth,
|
|
.getheight = getheight,
|
|
.getuifont = getuifont,
|
|
#if LCD_DEPTH > 1
|
|
.get_foreground=dummy_func2,
|
|
.get_background=dummy_func2,
|
|
.backdrop_load=backdrop_load,
|
|
#endif
|
|
},
|
|
#ifdef HAVE_REMOTE_LCD
|
|
{
|
|
.screen_type=SCREEN_REMOTE,
|
|
.lcdwidth=LCD_REMOTE_WIDTH,
|
|
.lcdheight=LCD_REMOTE_HEIGHT,
|
|
.depth=LCD_REMOTE_DEPTH,
|
|
.getuifont = getuifont,
|
|
.is_color=false,/* No color remotes yet */
|
|
.init_viewport=init_viewport,
|
|
.getwidth=remote_getwidth,
|
|
.getheight=remote_getheight,
|
|
#if LCD_REMOTE_DEPTH > 1
|
|
.get_foreground=dummy_func2,
|
|
.get_background=dummy_func2,
|
|
.backdrop_load=backdrop_load,
|
|
#endif
|
|
}
|
|
#endif
|
|
};
|
|
|
|
void screen_clear_area(struct screen * display, int xstart, int ystart,
|
|
int width, int height)
|
|
{
|
|
display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
|
|
display->fillrect(xstart, ystart, width, height);
|
|
display->set_drawmode(DRMODE_SOLID);
|
|
}
|
|
|
|
#if CONFIG_TUNER
|
|
bool radio_hardware_present(void)
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#include "fontbundle.h"
|
|
#include "themeassets.h"
|
|
|
|
static int loaded_fonts = 0;
|
|
static struct font _font;
|
|
|
|
/* mode 0 is for fonts in the WPS
|
|
mode 1 is for fonts in the cfg file
|
|
mode 2 is everything else in the cfg file
|
|
|
|
search is an optional location to search for relative paths
|
|
*/
|
|
static int checkwps_loader(const char *path, const char *search, int mode)
|
|
{
|
|
if (!path)
|
|
return -1;
|
|
if (!path[0])
|
|
return mode? 0 : -2; /* Empty is ok for non-WPS */
|
|
if (path[0] == '-' && !path[1])
|
|
return mode? 0 : -3; /* Also ok for non-WPS */
|
|
if (path[0] == '/')
|
|
path++; /* Absolute path, skip leading / */
|
|
|
|
/* First see if it exists in the theme */
|
|
int fd = open(path, O_RDONLY);
|
|
if (fd >= 0)
|
|
goto done;
|
|
|
|
{
|
|
char buf[1024];
|
|
if (!mode) {
|
|
sprintf(buf, ".rockbox/%s", path);
|
|
fd = open(buf, O_RDONLY);
|
|
if (fd >= 0)
|
|
goto done;
|
|
}
|
|
|
|
if (search)
|
|
{
|
|
sprintf(buf, ".rockbox/%s/%s", search, path);
|
|
fd = open(buf, O_RDONLY);
|
|
if (fd >= 0)
|
|
goto done;
|
|
}
|
|
|
|
if (mode == 0 || mode == 1) /* not found and it's a font */
|
|
{
|
|
char *first = strrchr(buf, '/');
|
|
char *final = strrchr(buf, '.');
|
|
int missing = 1;
|
|
if (final) *final = 0;
|
|
|
|
/* Check if font is a default theme asset */
|
|
for (int i = 0 ; themeassets[i] != NULL; i++)
|
|
{
|
|
if (first && !strcmp(first+1, themeassets[i]))
|
|
{
|
|
missing = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check if font is included in the bundle */
|
|
if (missing)
|
|
{
|
|
for (int i = 0 ; bundledfonts[i] != NULL ; i++)
|
|
{
|
|
if (first && !strcmp(first+1, bundledfonts[i]))
|
|
{
|
|
missing = 2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (missing == 1)
|
|
{
|
|
printf("Font missing >%s<\n", first+1);
|
|
return -4;
|
|
} else if (missing == 2) {
|
|
printf("INFO: Theme requires rockbox font bundle\n");
|
|
}
|
|
}
|
|
else if (mode == 2) /* Not found and it's an asset */
|
|
{
|
|
char *first = strrchr(buf, '/');
|
|
char *final = strrchr(buf, '.');
|
|
int missing = 1;
|
|
if (final) *final = 0;
|
|
|
|
for (int i = 0 ; themeassets[i] != NULL; i++)
|
|
{
|
|
if (first && !strcmp(first+1, themeassets[i]))
|
|
{
|
|
missing = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (missing)
|
|
{
|
|
printf("ERROR: File missing: %s\n", path);
|
|
return -5;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (fd >= 0)
|
|
close(fd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int font_load(const char *path)
|
|
{
|
|
int rval = checkwps_loader(path, FONT_DIR, 0);
|
|
if (rval)
|
|
return rval;
|
|
|
|
rval = 2 + loaded_fonts;
|
|
loaded_fonts++;
|
|
return rval;
|
|
}
|
|
|
|
void font_unload(int font_id)
|
|
{
|
|
(void)font_id;
|
|
}
|
|
|
|
struct font* font_get(int font)
|
|
{
|
|
return &_font;
|
|
}
|
|
|
|
bool settings_load_config(const char* file, bool apply)
|
|
{
|
|
int fd;
|
|
char line[128];
|
|
bool theme_changed = false;
|
|
fd = open_utf8(file, O_RDONLY);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
while (read_line(fd, line, sizeof line) > 0)
|
|
{
|
|
char *name, *value;
|
|
if (!settings_parseline(line, &name, &value))
|
|
continue;
|
|
if (!strcmp(name, "font"))
|
|
{
|
|
if (checkwps_loader(value, FONT_DIR, 1))
|
|
return false;
|
|
}
|
|
#ifdef HAVE_REMOTE_LCD
|
|
if (!strcmp(name, "remote font"))
|
|
{
|
|
if (checkwps_loader(value, FONT_DIR, 1))
|
|
return false;
|
|
}
|
|
else
|
|
#endif
|
|
#if LCD_DEPTH > 1
|
|
if (!strcmp(name, "backdrop"))
|
|
{
|
|
if (checkwps_loader(value, NULL, 2)) /* No default path */
|
|
return false;
|
|
}
|
|
else
|
|
#endif
|
|
if (!strcmp(name, "wps"))
|
|
{
|
|
if (checkwps_loader(value, WPS_DIR, 2))
|
|
return false;
|
|
}
|
|
else if (!strcmp(name, "sbs"))
|
|
{
|
|
if (checkwps_loader(value, SBS_DIR, 2))
|
|
return false;
|
|
}
|
|
else if (!strcmp(name, "fms"))
|
|
{
|
|
if (checkwps_loader(value, SBS_DIR, 2))
|
|
return false;
|
|
}
|
|
else if (!strcmp(name, "rwps"))
|
|
{
|
|
if (checkwps_loader(value, WPS_DIR, 2))
|
|
return false;
|
|
}
|
|
else if (!strcmp(name, "rsbs"))
|
|
{
|
|
if (checkwps_loader(value, SBS_DIR, 2))
|
|
return false;
|
|
}
|
|
else if (!strcmp(name, "rfms"))
|
|
{
|
|
if (checkwps_loader(value, SBS_DIR, 2))
|
|
return false;
|
|
}
|
|
else if (!strcmp(name, "iconset"))
|
|
{
|
|
if (checkwps_loader(value, ICON_DIR, 2))
|
|
return false;
|
|
}
|
|
else if (!strcmp(name, "viewers iconset"))
|
|
{
|
|
if (checkwps_loader(value, ICON_DIR, 2))
|
|
return false;
|
|
}
|
|
#ifdef HAVE_REMOTE_LCD
|
|
else if (!strcmp(name, "remote iconset"))
|
|
{
|
|
if (checkwps_loader(value, ICON_DIR, 2))
|
|
return false;
|
|
}
|
|
#endif
|
|
#ifdef HAVE_LCD_COLOR
|
|
else if (!strcmp(name, "filetype colours"))
|
|
{
|
|
if (checkwps_loader(value, THEME_DIR, 2))
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if (!string_to_cfg(name, value, &theme_changed))
|
|
{
|
|
if (strcmp(name, "fms") && strcmp(name, "rfms")) {
|
|
/* Unknown settings are ignored by firmware */
|
|
printf("WARNING: Invalid setting: %s = %s\n", name, value);
|
|
continue;
|
|
}
|
|
}
|
|
} /* while(...) */
|
|
|
|
close(fd);
|
|
|
|
(void)apply;
|
|
return true;
|
|
}
|
|
|
|
/* This is no longer defined in ROCKBOX builds so just use a huge value */
|
|
#define SKIN_BUFFER_SIZE (200*1024)
|
|
|
|
int check_filetype(const char *ext, enum skinnable_screens *skin,
|
|
enum screen_type *screen)
|
|
{
|
|
if (!strcmp(ext, "sbs"))
|
|
{
|
|
*skin = CUSTOM_STATUSBAR;
|
|
*screen = SCREEN_MAIN;
|
|
}
|
|
else if (!strcmp(ext, "wps"))
|
|
{
|
|
*skin = WPS;
|
|
*screen = SCREEN_MAIN;
|
|
}
|
|
else if (!strcmp(ext, "fms"))
|
|
{
|
|
#if CONFIG_TUNER
|
|
*skin = FM_SCREEN;
|
|
*screen = SCREEN_MAIN;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
else if (!strcmp(ext, "rsbs"))
|
|
{
|
|
#ifdef HAVE_REMOTE_LCD
|
|
*skin = CUSTOM_STATUSBAR;
|
|
*screen = SCREEN_REMOTE;
|
|
#else
|
|
return 1; /* unsupported, but not an error */
|
|
#endif
|
|
}
|
|
else if (!strcmp(ext, "rwps"))
|
|
{
|
|
#ifdef HAVE_REMOTE_LCD
|
|
*skin = WPS;
|
|
*screen = SCREEN_REMOTE;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
else if (!strcmp(ext, "rfms"))
|
|
{
|
|
#if defined(HAVE_REMOTE_LCD) && CONFIG_TUNER
|
|
*skin = FM_SCREEN;
|
|
*screen = SCREEN_REMOTE;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
else if (!strcmp(ext, "cfg"))
|
|
{
|
|
return 2;
|
|
}
|
|
else
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int ret = 0;
|
|
int res;
|
|
int filearg = 1;
|
|
|
|
struct wps_data wps={0};
|
|
enum screen_type screen = SCREEN_MAIN;
|
|
enum skinnable_screens skin;
|
|
|
|
/* No arguments -> print the help text
|
|
* Also print the help text upon -h or --help */
|
|
if( (argc < 2) ||
|
|
strcmp(argv[1],"-h") == 0 ||
|
|
strcmp(argv[1],"--help") == 0 )
|
|
{
|
|
printf("Usage: checkwps [OPTIONS] [filename1] [filename2] ...\n");
|
|
printf(" supported file types: wps, sbs, fms, cfg, rwps, rsbs, rfms\n");
|
|
printf("\nOPTIONS:\n");
|
|
printf("\t-v\t\tverbose\n");
|
|
printf("\t-vv\t\tmore verbose\n");
|
|
printf("\t-vvv\t\tvery verbose\n");
|
|
printf("\t-h,\t--help\tshow this message\n");
|
|
return 1;
|
|
}
|
|
|
|
if (argv[1][0] == '-') {
|
|
filearg++;
|
|
int i = 1;
|
|
while (argv[1][i] && argv[1][i] == 'v') {
|
|
i++;
|
|
wps_verbose_level++;
|
|
debug_wps = true;
|
|
}
|
|
}
|
|
skin_buffer = malloc(SKIN_BUFFER_SIZE);
|
|
if (!skin_buffer)
|
|
{
|
|
printf("mallloc fail!\n");
|
|
return 1;
|
|
}
|
|
|
|
skin_buffer_init(skin_buffer, SKIN_BUFFER_SIZE);
|
|
|
|
/* Go through every skin that was thrown at us, error out at the first
|
|
* flawed wps */
|
|
while (argv[filearg]) {
|
|
const char* name = argv[filearg++];
|
|
const char *ext = strrchr(name, '.');
|
|
struct skin_stats stats;
|
|
printf("Checking %s...\n", name);
|
|
if (!ext)
|
|
{
|
|
printf("Invalid extension\n");
|
|
ret = 2;
|
|
goto done;
|
|
}
|
|
ext++;
|
|
|
|
int valid = check_filetype(ext, &skin, &screen);
|
|
if (valid < 0)
|
|
{
|
|
printf("Invalid extension\n");
|
|
ret = 2;
|
|
goto done;
|
|
}
|
|
else if (valid == 2)
|
|
{
|
|
if (!settings_load_config(name, false))
|
|
{
|
|
printf("Bad configuration\n");
|
|
ret = 3;
|
|
goto done;
|
|
}
|
|
printf("%s parsed OK\n\n", ext);
|
|
continue;
|
|
}
|
|
else if (valid > 0)
|
|
continue; /* skip (unsupported by this target but not an error) */
|
|
|
|
res = skin_data_load(skin, screen, &wps, name, true, &stats);
|
|
|
|
if (!res) {
|
|
printf("%s parsing failure\n", ext);
|
|
skin_error_format_message();
|
|
ret = 4;
|
|
goto done;
|
|
}
|
|
|
|
printf("%s parsed OK\n\n", ext);
|
|
if (wps_verbose_level>2)
|
|
skin_debug_tree(SKINOFFSETTOPTR(skin_buffer, wps.tree));
|
|
}
|
|
|
|
done:
|
|
if (skin_buffer)
|
|
free(skin_buffer);
|
|
return ret;
|
|
}
|