Files
nunuhara_xsystem4/src/hacks.c
T
Nunuhara Cabbage eaa0baad7f Handle library incompatibilities
Add a mechanism for libraries to resolve incompatibilities between
library versions before linking.

This is currently used for ChipmunkSpriteEngine and GoatGUIEngine,
where early versions used integer IDs when loading CGs (later versions
use strings).
2022-06-06 08:49:12 -07:00

177 lines
5.2 KiB
C

/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "system4.h"
#include "system4/ain.h"
#include "system4/buffer.h"
#include "system4/instructions.h"
#include "system4/string.h"
#include "system4/utfsjis.h"
#include "gfx/gfx.h"
#include "xsystem4.h"
#include "hll/hll.h"
bool game_daibanchou_en = false;
bool game_rance02_mg = false;
bool game_rance6_mg = false;
bool game_rance7_mg = false;
bool id_indexed_afa = false;
static void write_instruction0(struct buffer *out, enum opcode op)
{
buffer_write_int16(out, op);
}
static void write_instruction1(struct buffer *out, enum opcode op, int32_t arg)
{
buffer_write_int16(out, op);
buffer_write_int32(out, arg);
}
// Rance 02 (MangaGamer version)
// -----------------------------
// Uses a custom proportional font implementation which makes assumptions
// about the font. We replace the _CalculateWidth function with a font-generic
// implementation.
//
// '^' character is mapped to 'é' (Rosé)
// '}' character is mapped to double-width dash
static void apply_rance02_hacks(struct ain *ain)
{
int fno = ain_get_function(ain, "_CalculateWidth");
if (fno <= 0) {
WARNING("No '_CalculateWidth' function when applying Rance 02 compatibility hack");
return;
}
struct ain_function *f = &ain->functions[fno];
// Reduce the text scale if it wasn't set by the user
if (!config.manual_text_x_scale) {
config.text_x_scale = 0.9375;
}
// rewrite function
struct buffer out;
buffer_init(&out, ain->code, ain->code_size);
buffer_seek(&out, f->address);
// _CalculateWidth(ref string str, int nFontSize, int nWeightSize) {
// if (str[0] > 255 || str[0] == '}')
// return nFontSize + nWeightSize;
// return nFontSize / 2 + nWeightSize;
// }
// if (str[0] > 255 || str[0] == '}')
write_instruction1(&out, SH_LOCALREF, 0);
write_instruction1(&out, PUSH, 0);
write_instruction0(&out, C_REF);
write_instruction0(&out, DUP);
write_instruction1(&out, PUSH, 255);
write_instruction0(&out, GT);
write_instruction0(&out, SWAP);
write_instruction1(&out, PUSH, '}');
write_instruction0(&out, EQUALE);
write_instruction0(&out, OR);
int ifz_addr = out.index;
write_instruction1(&out, IFZ, 0);
// return nFontSize + nWeightSize
write_instruction1(&out, SH_LOCALREF, 1);
write_instruction1(&out, SH_LOCALREF, 2);
write_instruction0(&out, ADD);
int jump_addr = out.index;
write_instruction1(&out, JUMP, 0);
// return nFontSize / 2 + nWeightSize;
buffer_write_int32_at(&out, ifz_addr + 2, out.index);
write_instruction1(&out, SH_LOCALREF, 1);
write_instruction1(&out, PUSH, 2);
write_instruction0(&out, DIV);
write_instruction1(&out, SH_LOCALREF, 2);
write_instruction0(&out, ADD);
// multiply by config.text_x_scale (if not 1.0)
buffer_write_int32_at(&out, jump_addr + 2, out.index);
if (fabsf(1.0 - config.text_x_scale) > 0.01) {
union { int32_t i; float f; } cast = { .f = config.text_x_scale };
write_instruction0(&out, ITOF);
write_instruction1(&out, F_PUSH, cast.i);
write_instruction0(&out, F_MUL);
write_instruction0(&out, FTOI);
}
write_instruction0(&out, RETURN);
write_instruction1(&out, ENDFUNC, fno);
game_rance02_mg = true;
}
// Rance VI (MangaGamer version)
// -----------------------------
// 'ノ' character is mapped to 'é' (Rosé)
static void apply_rance6_hacks(struct ain *ain)
{
if (ain_get_function(ain, "GetTextWidth") < 0)
return;
game_rance6_mg = true;
}
static void apply_rance7_hacks(struct ain *ain)
{
if (ain_get_library(ain, "SengokuRanceFont") < 0)
return;
game_rance7_mg = true;
}
// Daibanchou (English fan translation)
// ------------------------------------
// Gpx2Plus.CopyStretchReduceAMap behaves differently in order to work around
// text rendering issues caused by how the game calculates text width in
// StructRegionalSelect::DrawMenuItem.
static void apply_daibanchou_hacks(struct ain *ain)
{
if (ain->nr_strings > 2 && !strcmp(ain->strings[2]->text, "Seijou Academy")) {
game_daibanchou_en = true;
}
}
void apply_game_specific_hacks(struct ain *ain)
{
char *game_name = sjis2utf(config.game_name, 0);
if (!strcmp(game_name, "大番長")) {
apply_daibanchou_hacks(ain);
} else if (!strcmp(game_name, "Rance 02")) {
apply_rance02_hacks(ain);
} else if (!strcmp(game_name, "Rance6")) {
apply_rance6_hacks(ain);
} else if (!strcmp(game_name, "Sengoku Rance")) {
apply_rance7_hacks(ain);
} else if (!strcmp(game_name, "しゃーまんず・さんくちゅあり 巫女の聖域")) {
id_indexed_afa = true;
} else if (!strcmp(game_name, "大帝国")) {
id_indexed_afa = true;
}
free(game_name);
}