mirror of
https://github.com/libretro/blastem.git
synced 2026-11-11 17:21:46 -10:00
195 lines
4.3 KiB
C
195 lines
4.3 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <limits.h>
|
|
#include "../util.h"
|
|
#include "../paths.h"
|
|
#include "sfnt.h"
|
|
|
|
typedef enum {
|
|
STATE_DEFAULT,
|
|
STATE_DECL,
|
|
STATE_COMMENT,
|
|
STATE_TAG,
|
|
STATE_PRE_ATTRIB,
|
|
STATE_ATTRIB,
|
|
STATE_PRE_VALUE,
|
|
STATE_VALUE
|
|
} parse_state;
|
|
|
|
#define DEFAULT_WEIGHT 400
|
|
|
|
char *default_font_path(void)
|
|
{
|
|
//Would probably be better to call into Java for this, but this should do for now
|
|
FILE *f = fopen("/system/etc/fonts.xml", "rb");
|
|
if (!f) {
|
|
return NULL;
|
|
}
|
|
long size = file_size(f);
|
|
char *font_xml = malloc(size+1);
|
|
if (size != fread(font_xml, 1, size, f)) {
|
|
free(font_xml);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
fclose(f);
|
|
font_xml[size] = 0;
|
|
|
|
char *last_tag = NULL, *last_attrib = NULL, *last_value = NULL;
|
|
uint8_t last_style_was_normal = 0;
|
|
char *capture_best = NULL;
|
|
char *best = NULL;
|
|
int best_weight_diff = INT_MAX;
|
|
int last_weight = INT_MAX;
|
|
parse_state state = STATE_DEFAULT;
|
|
for(char *cur = font_xml; *cur; ++cur) {
|
|
switch (state)
|
|
{
|
|
case STATE_DEFAULT:
|
|
if (*cur == '<' && cur[1]) {
|
|
cur++;
|
|
switch(*cur)
|
|
{
|
|
case '?':
|
|
state = STATE_DECL;
|
|
break;
|
|
case '!':
|
|
if (cur[1] == '-' && cur[2] == '-') {
|
|
state = STATE_COMMENT;
|
|
cur++;
|
|
} else {
|
|
debug_message("Invalid comment\n");
|
|
cur = font_xml + size - 1;
|
|
}
|
|
break;
|
|
default:
|
|
if (capture_best) {
|
|
cur[-1] = 0;
|
|
best = strip_ws(capture_best);
|
|
capture_best = NULL;
|
|
best_weight_diff = abs(last_weight - DEFAULT_WEIGHT);
|
|
debug_message("Found candidate %s with weight %d\n", best, last_weight);
|
|
}
|
|
state = STATE_TAG;
|
|
last_tag = cur;
|
|
last_attrib = NULL;
|
|
last_value = NULL;
|
|
last_weight = INT_MAX;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case STATE_DECL:
|
|
if (*cur == '?' && cur[1] == '>') {
|
|
cur++;
|
|
state = STATE_DEFAULT;
|
|
}
|
|
break;
|
|
case STATE_COMMENT:
|
|
if (*cur == '-' && cur[1] == '-' && cur[2] == '>') {
|
|
cur += 2;
|
|
state = STATE_DEFAULT;
|
|
}
|
|
break;
|
|
case STATE_TAG:
|
|
if (*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r') {
|
|
*cur = 0;
|
|
state = STATE_PRE_ATTRIB;
|
|
} else if (*cur == '>') {
|
|
*cur = 0;
|
|
state = STATE_DEFAULT;
|
|
}
|
|
break;
|
|
case STATE_PRE_ATTRIB:
|
|
if (!(*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r')) {
|
|
if (*cur == '>') {
|
|
state = STATE_DEFAULT;
|
|
if (last_style_was_normal && abs(last_weight - DEFAULT_WEIGHT) < best_weight_diff) {
|
|
capture_best = cur + 1;
|
|
} else if (best && !strcmp("/family", last_tag)) {
|
|
debug_message("found family close tag, stopping search\n");
|
|
cur = font_xml + size - 1;
|
|
}
|
|
} else {
|
|
last_attrib = cur;
|
|
state = STATE_ATTRIB;
|
|
}
|
|
}
|
|
break;
|
|
case STATE_ATTRIB:
|
|
if (*cur == '=') {
|
|
*cur = 0;
|
|
state = STATE_PRE_VALUE;
|
|
} else if (*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r') {
|
|
*cur = 0;
|
|
}
|
|
break;
|
|
case STATE_PRE_VALUE:
|
|
if (*cur == '"') {
|
|
state = STATE_VALUE;
|
|
last_value = cur + 1;
|
|
}
|
|
break;
|
|
case STATE_VALUE:
|
|
if (*cur == '"') {
|
|
*cur = 0;
|
|
state = STATE_PRE_ATTRIB;
|
|
if (!strcmp("weight", last_attrib)) {
|
|
last_weight = atoi(last_value);
|
|
} else if (!strcmp("style", last_attrib)) {
|
|
last_style_was_normal = !strcmp("normal", last_value);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (best) {
|
|
best = path_append("/system/fonts", best);
|
|
}
|
|
free(font_xml);
|
|
return best;
|
|
}
|
|
|
|
static uint8_t *try_load_font(char *path, uint32_t *size_out)
|
|
{
|
|
debug_message("Trying to load font %s\n", path);
|
|
FILE *f = fopen(path, "rb");
|
|
free(path);
|
|
if (!f) {
|
|
return NULL;
|
|
}
|
|
long size = file_size(f);
|
|
uint8_t *buffer = malloc(size);
|
|
if (size != fread(buffer, 1, size, f)) {
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
fclose(f);
|
|
sfnt_container *sfnt = load_sfnt(buffer, size);
|
|
if (!sfnt) {
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
return sfnt_flatten(sfnt->tables, size_out);
|
|
}
|
|
|
|
uint8_t *default_font(uint32_t *size_out)
|
|
{
|
|
char *path = default_font_path();
|
|
if (!path) {
|
|
goto error;
|
|
}
|
|
uint8_t *ret = try_load_font(path, size_out);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
error:
|
|
//try some likely suspects if we failed to parse fonts.xml or failed to find the indicated font
|
|
ret = try_load_font("/system/fonts/Roboto-Regular.ttf", size_out);
|
|
if (!ret) {
|
|
ret = try_load_font("/system/fonts/DroidSans.ttf", size_out);
|
|
}
|
|
return ret;
|
|
}
|