view nuklear_ui/font_android.c @ 1863:d60f2d7c02a5

Differentiate between the full Sega mapper and the SRAM only one. Fixes crash on save state load for Phantasy Star IV and other games that use the same mapper
author Michael Pavone <pavone@retrodev.com>
date Tue, 14 May 2019 23:20:35 -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;
}