view nuklear_ui/font_android.c @ 1925:039553703c20

Don't apply address and cd register changes to the 'live' registers until pending flag is cleared, but do preserve the upper address bits in the latch. Fixes regression in Overdrive 2 while preserving fix to Mona in 344 bytes
author Michael Pavone <pavone@retrodev.com>
date Mon, 13 Apr 2020 20:43:25 -0700
parents 78abbabfd58d
children 13abdc98379e
line wrap: on
line source

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