Files
nunuhara_xsystem4/src/hacks.c
T
Nunuhara Cabbage eca92c4f7e Add compatibility hack for English Daibanchou
Gpx2Plus.CopyStretchReduceAMap behaves differently in the English
version.

Fixes an issue with text being squashed horizontally on the regional
phase menus.
2022-04-09 11:42:17 -07:00

132 lines
4.0 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 "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;
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.
//
// This causes quite a bit of text overflow, but there's not much that can be
// done about it. Using TTF_SizeUTF8 to calculate the width actually makes it
// worse.
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];
// 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);
write_instruction0(&out, RETURN);
// 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);
write_instruction0(&out, RETURN);
write_instruction1(&out, ENDFUNC, fno);
game_rance02_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);
}
free(game_name);
}