rockbox/tools/checkwps/checkwps.c
Solomon Peachy 9d3d2c6e67 checkwps: validate themes against builtin cabbiev2 assets
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
2026-10-06 23:24:34 -04:00

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;
}