Files
perfect-dark/src/game/text.c
T
2025-02-04 15:55:21 +10:00

2728 lines
68 KiB
C

#include <ultra64.h>
#include "constants.h"
#include "game/game_006900.h"
#include "game/gfxmemory.h"
#include "game/savebuffer.h"
#include "game/text.h"
#include "game/file.h"
#include "game/lang.h"
#include "bss.h"
#include "lib/vi.h"
#include "lib/dma.h"
#include "lib/main.h"
#include "lib/memp.h"
#include "data.h"
#include "types.h"
#define SPACE_WIDTH 5
#define BLENDTYPE_DIAGONAL 0x01
#define BLENDTYPE_VERTICAL 0x02
#define BLENDTYPE_WAVE 0x04
#define BLENDTYPE_MENU 0x08
#define BLENDTYPE_HORIZONTAL 0x10
struct blendsettings {
/*0x00*/ u8 types;
/*0x04*/ u32 colour04;
/*0x08*/ u32 colour08;
/*0x0c*/ s32 diagrefx;
/*0x10*/ s32 diagrefy;
/*0x14*/ f32 diagtimer;
/*0x18*/ u8 diagmode;
/*0x1c*/ s32 backupdiagrefx;
/*0x20*/ s32 backupdiagrefy;
/*0x24*/ f32 backupdiagtimer;
/*0x28*/ u8 backupdiagmode;
/*0x29*/ u8 backupdiagtypes;
/*0x2a*/ u8 backuptypes;
/*0x2c*/ s32 vertrefy1;
/*0x30*/ s32 vertrefy2;
/*0x34*/ s32 vert34;
/*0x38*/ s32 horizrefx1;
/*0x3c*/ s32 horizrefx2;
/*0x40*/ s32 horiz40;
/*0x44*/ u32 colour44;
/*0x48*/ u32 colour48;
/*0x4c*/ s32 wave4c;
/*0x50*/ s32 wave50;
/*0x54*/ s32 wave54;
/*0x58*/ u32 wavecolour1;
/*0x5c*/ u32 wavecolour2;
/*0x60*/ u32 menuweight;
};
#if VERSION == VERSION_JPN_FINAL
s32 g_JpnKerning[13 * 13];
#endif
struct blendsettings g_Blend;
Gfx *g_TextHoloRayGdl;
Gfx *g_TextHoloRayGdlEnd;
#if VERSION == VERSION_JPN_FINAL
struct fontchar g_TmpJpnChar;
#endif
u32 var800a463c;
#if VERSION == VERSION_JPN_FINAL
s32 var800800f0jf = 0;
s32 g_UiScaleX = 1;
bool var80080104jf = false;
s32 g_TextCharOverlap = 0;
bool g_TextRotated90 = false;
s32 g_WrapIndentCount = 0;
s32 g_TextScaleX = 1;
s32 var80080108jf = 1;
#else
s32 g_UiScaleX = 1;
s32 g_TextCharOverlap = 0;
bool g_TextRotated90 = false;
s32 g_WrapIndentCount = 0;
s32 g_TextScaleX = 1;
#endif
s32 g_TextParaOverlap = -1; // higher value reduces margin between paragraphs
bool g_TextUseAverageSampling = false;
s32 var8007fadc = 0;
s32 var8007fae0 = 0;
u32 var8007fae4 = 0;
u32 var8007fae8 = 0;
u32 var8007faec = 0;
u32 var8007faf0 = 0;
struct font *g_FontTahoma2 = NULL;
struct fontchar *g_FontTahoma1 = NULL;
struct font *g_FontNumeric = NULL;
struct fontchar *g_CharsNumeric = NULL;
struct font *g_FontHandelGothicXs = NULL;
struct fontchar *g_CharsHandelGothicXs = NULL;
struct font *g_FontHandelGothicSm = NULL;
struct fontchar *g_CharsHandelGothicSm = NULL;
struct font *g_FontHandelGothicMd = NULL;
struct fontchar *g_CharsHandelGothicMd = NULL;
struct font *g_FontHandelGothicLg = NULL;
struct fontchar *g_CharsHandelGothicLg = NULL;
u32 var8007fb24 = 0;
u32 var8007fb28 = 0;
u32 var8007fb2c = 0;
u32 var8007fb30 = 0;
u32 var8007fb34 = 0;
u32 var8007fb38 = 0;
u16 var8007fb3c[] = {
0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00,
0xff00, 0xff24, 0xff48, 0xff6c, 0xff90, 0xffb4, 0xffd8, 0xffff,
};
u16 var8007fb5c[] = {
0xff00, 0xff58, 0xff74, 0xff90, 0xffac, 0xffc8, 0xffe4, 0xffff,
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00, 0xff00,
0xff00, 0xff18, 0xff30, 0xff5c, 0xff88, 0xffb4, 0xffd8, 0xffff,
};
#if VERSION == VERSION_JPN_FINAL
u16 var800801d8jf[] = {
0xff00, 0xff11, 0xff22, 0xff33, 0xff44, 0xff55, 0xff66, 0xff77,
0xff88, 0xff99, 0xffaa, 0xffbb, 0xffcc, 0xffdd, 0xffee, 0xffff,
};
#endif
bool g_TextHoloRayEnabled = false;
s32 g_TextBlendDistance = 0;
s32 g_TextLastBlendY = -1;
s32 var8007fba8 = 0;
u32 g_TextLAlpha = 1;
u32 g_TextLFade = 100;
u32 g_TextLLimbo = 44;
u32 g_TextSubleTY = 128;
u32 g_TextSubtleTX = 60;
u32 g_TextColourX = 0x44444400;
u32 g_TextColourY = 0xffffff00;
Gfx *text_render_char_v1_part2(Gfx *gdl, s32 x, s32 y, struct fontchar *char1, s32 arg4, s32 arg5, s32 arg6, s32 arg7);
void text_init(void)
{
// empty
}
void text_set_default_kerning(s32 kerning)
{
g_TextCharOverlap = -kerning;
}
void text_set_rotation90(bool rotated)
{
g_TextRotated90 = rotated;
}
void text_set_wrap_indent(s32 count)
{
g_WrapIndentCount = count;
}
void text_set_paragraph_overlap(s32 margin)
{
g_TextParaOverlap = margin;
}
void text_set_hires(bool hires)
{
if (hires) {
g_TextScaleX = 2;
} else {
g_TextScaleX = 1;
}
#if VERSION == VERSION_JPN_FINAL
var80080108jf = 1;
#endif
}
void text_load_font(u8 *romstart, u8 *romend, struct font **fontptr, struct fontchar **charsptr, bool monospace)
{
extern u8 _fonthandelgothicsmSegmentRomStart;
extern u8 _fonthandelgothicxsSegmentRomStart;
extern u8 _fonthandelgothicmdSegmentRomStart;
u32 len;
s32 maxwidth;
s32 i;
struct font *font;
struct fontchar *chars;
#if VERSION >= VERSION_PAL_BETA
s32 numchars = 94;
#if PAL
// PAL has more characters in these fonts
if (romstart == &_fonthandelgothicsmSegmentRomStart
|| romstart == &_fonthandelgothicxsSegmentRomStart
|| romstart == &_fonthandelgothicmdSegmentRomStart) {
numchars = 135;
}
#endif
#define NUMCHARS() numchars
#else
#define NUMCHARS() 94
#endif
len = (romptr_t)romend - (romptr_t)romstart;
font = memp_alloc(len, MEMPOOL_STAGE);
chars = font->chars;
dma_exec(font, (romptr_t) romstart, len);
// Convert pointers
for (i = 0; i < NUMCHARS(); i++) {
chars[i].pixeldata += (uintptr_t)font;
}
#if VERSION >= VERSION_JPN_FINAL
if (romstart == &_fonthandelgothicsmSegmentRomStart) {
for (i = 0; i < NUMCHARS(); i++) {
chars[i].baseline++;
}
}
#endif
// If requested monospace, set all widths to the max, minus 1 for some reason
if (monospace) {
maxwidth = 0;
for (i = 0; i < NUMCHARS(); i++) {
if (chars[i].width > maxwidth) {
maxwidth = chars[i].width;
}
}
maxwidth--;
for (i = 0; i < NUMCHARS(); i++) {
chars[i].width = maxwidth;
}
}
*fontptr = font;
*charsptr = chars;
#if PAL
if (romstart == &_fonthandelgothicsmSegmentRomStart
|| romstart == &_fonthandelgothicxsSegmentRomStart
|| romstart == &_fonthandelgothicmdSegmentRomStart) {
// Increment the baseline of the pipe character.
// It is suspected that the pipe character is used as a reference to set
// the size for all other text, so changing this increases the line
// height of these fonts.
(*charsptr)['|' - 0x21].baseline++;
}
#endif
}
void text_reset(void)
{
extern u8 _fontbankgothicSegmentRomStart, _fontbankgothicSegmentRomEnd;
extern u8 _fontzurichSegmentRomStart, _fontzurichSegmentRomEnd;
extern u8 _fonttahomaSegmentRomStart, _fonttahomaSegmentRomEnd;
extern u8 _fontnumericSegmentRomStart, _fontnumericSegmentRomEnd;
extern u8 _fonthandelgothicsmSegmentRomStart, _fonthandelgothicsmSegmentRomEnd;
extern u8 _fonthandelgothicxsSegmentRomStart, _fonthandelgothicxsSegmentRomEnd;
extern u8 _fonthandelgothicmdSegmentRomStart, _fonthandelgothicmdSegmentRomEnd;
extern u8 _fonthandelgothiclgSegmentRomStart, _fonthandelgothiclgSegmentRomEnd;
extern u8 _fontocramdSegmentRomStart, _fontocramdSegmentRomEnd;
extern u8 _fontocralgSegmentRomStart, _fontocralgSegmentRomEnd;
var8007faec = 0;
g_FontTahoma2 = NULL;
g_FontNumeric = NULL;
g_FontHandelGothicXs = NULL;
g_FontHandelGothicSm = NULL;
g_FontHandelGothicMd = NULL;
g_FontHandelGothicLg = NULL;
var8007fb24 = 0;
var8007fb2c = 0;
var8007fb34 = 0;
var8007faf0 = 0;
g_FontTahoma1 = NULL;
g_CharsNumeric = NULL;
g_CharsHandelGothicXs = NULL;
g_CharsHandelGothicSm = NULL;
g_CharsHandelGothicMd = NULL;
g_CharsHandelGothicLg = NULL;
var8007fb28 = 0;
var8007fb30 = 0;
var8007fb38 = 0;
g_TextCharOverlap = 0;
g_TextRotated90 = false;
g_WrapIndentCount = 0;
g_TextParaOverlap = -1;
g_TextUseAverageSampling = 0;
var8007fadc = 0;
var8007fae0 = 0;
var8007fae4 = 0;
var8007fae8 = 0;
if (g_Vars.stagenum == STAGE_TITLE) {
text_load_font(&_fonthandelgothicsmSegmentRomStart, &_fonthandelgothicsmSegmentRomEnd, &g_FontHandelGothicSm, &g_CharsHandelGothicSm, false);
text_load_font(&_fonthandelgothicmdSegmentRomStart, &_fonthandelgothicmdSegmentRomEnd, &g_FontHandelGothicMd, &g_CharsHandelGothicMd, false);
text_load_font(&_fonthandelgothiclgSegmentRomStart, &_fonthandelgothiclgSegmentRomEnd, &g_FontHandelGothicLg, &g_CharsHandelGothicLg, false);
} else if (g_Vars.stagenum == STAGE_CREDITS) {
text_load_font(&_fonthandelgothicxsSegmentRomStart, &_fonthandelgothicxsSegmentRomEnd, &g_FontHandelGothicXs, &g_CharsHandelGothicXs, false);
text_load_font(&_fonthandelgothicsmSegmentRomStart, &_fonthandelgothicsmSegmentRomEnd, &g_FontHandelGothicSm, &g_CharsHandelGothicSm, false);
text_load_font(&_fonthandelgothicmdSegmentRomStart, &_fonthandelgothicmdSegmentRomEnd, &g_FontHandelGothicMd, &g_CharsHandelGothicMd, false);
text_load_font(&_fonthandelgothiclgSegmentRomStart, &_fonthandelgothiclgSegmentRomEnd, &g_FontHandelGothicLg, &g_CharsHandelGothicLg, false);
} else {
#if VERSION >= VERSION_JPN_FINAL
text_load_font(&_fontnumericSegmentRomStart, &_fontnumericSegmentRomEnd, &g_FontNumeric, &g_CharsNumeric, false);
if (g_Vars.normmplayerisrunning) {
if (IS4MB()) {
s32 i;
for (i = 0; i < ARRAYCOUNT(g_JpnKerning); i++) {
g_JpnKerning[i] = 0;
}
g_FontHandelGothicXs = g_FontHandelGothicSm = (struct font *) g_JpnKerning;
return;
}
}
text_load_font(&_fonthandelgothicsmSegmentRomStart, &_fonthandelgothicsmSegmentRomEnd, &g_FontHandelGothicSm, &g_CharsHandelGothicSm, false);
if (!g_Vars.normmplayerisrunning || IS8MB()) {
text_load_font(&_fonthandelgothicmdSegmentRomStart, &_fonthandelgothicmdSegmentRomEnd, &g_FontHandelGothicMd, &g_CharsHandelGothicMd, false);
}
if (g_Vars.stagenum == STAGE_TEST_OLD) {
text_load_font(&_fonthandelgothiclgSegmentRomStart, &_fonthandelgothiclgSegmentRomEnd, &g_FontHandelGothicLg, &g_CharsHandelGothicLg, false);
}
text_load_font(&_fonthandelgothicxsSegmentRomStart, &_fonthandelgothicxsSegmentRomEnd, &g_FontHandelGothicXs, &g_CharsHandelGothicXs, false);
#elif VERSION >= VERSION_PAL_BETA
text_load_font(&_fontnumericSegmentRomStart, &_fontnumericSegmentRomEnd, &g_FontNumeric, &g_CharsNumeric, false);
text_load_font(&_fonthandelgothicxsSegmentRomStart, &_fonthandelgothicxsSegmentRomEnd, &g_FontHandelGothicXs, &g_CharsHandelGothicXs, false);
text_load_font(&_fonthandelgothicsmSegmentRomStart, &_fonthandelgothicsmSegmentRomEnd, &g_FontHandelGothicSm, &g_CharsHandelGothicSm, false);
if (!g_Vars.normmplayerisrunning) {
text_load_font(&_fonthandelgothicmdSegmentRomStart, &_fonthandelgothicmdSegmentRomEnd, &g_FontHandelGothicMd, &g_CharsHandelGothicMd, false);
}
if (g_Vars.stagenum == STAGE_TEST_OLD) {
text_load_font(&_fonthandelgothiclgSegmentRomStart, &_fonthandelgothiclgSegmentRomEnd, &g_FontHandelGothicLg, &g_CharsHandelGothicLg, false);
}
#else
// This unused GE font exists in NTSC but was removed in the PAL version
text_load_font(&_fonttahomaSegmentRomStart, &_fonttahomaSegmentRomEnd, &g_FontTahoma2, &g_FontTahoma1, false);
text_load_font(&_fontnumericSegmentRomStart, &_fontnumericSegmentRomEnd, &g_FontNumeric, &g_CharsNumeric, false);
text_load_font(&_fonthandelgothicxsSegmentRomStart, &_fonthandelgothicxsSegmentRomEnd, &g_FontHandelGothicXs, &g_CharsHandelGothicXs, false);
text_load_font(&_fonthandelgothicsmSegmentRomStart, &_fonthandelgothicsmSegmentRomEnd, &g_FontHandelGothicSm, &g_CharsHandelGothicSm, false);
text_load_font(&_fonthandelgothicmdSegmentRomStart, &_fonthandelgothicmdSegmentRomEnd, &g_FontHandelGothicMd, &g_CharsHandelGothicMd, false);
if (g_Vars.stagenum == STAGE_TEST_OLD) {
text_load_font(&_fonthandelgothiclgSegmentRomStart, &_fonthandelgothiclgSegmentRomEnd, &g_FontHandelGothicLg, &g_CharsHandelGothicLg, false);
}
#endif
}
}
Gfx *text_begin(Gfx *gdl)
{
gDPPipeSync(gdl++);
gDPSetCycleType(gdl++, G_CYC_1CYCLE);
gDPSetColorDither(gdl++, G_CD_DISABLE);
gDPSetRenderMode(gdl++, G_RM_XLU_SURF, G_RM_XLU_SURF2);
gDPSetCombineLERP(gdl++,
0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0,
0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0);
gDPSetTexturePersp(gdl++, G_TP_NONE);
gDPSetAlphaCompare(gdl++, G_AC_NONE);
gDPSetTextureLOD(gdl++, G_TL_TILE);
gDPSetTextureConvert(gdl++, G_TC_FILT);
gDPSetTextureLUT(gdl++, G_TT_NONE);
if (g_TextUseAverageSampling) {
gDPSetTextureFilter(gdl++, G_TF_AVERAGE);
}
#if VERSION == VERSION_JPN_FINAL
else if (var80080108jf == 2) {
gDPSetTextureFilter(gdl++, G_TF_POINT);
}
#endif
else {
gDPSetTextureFilter(gdl++, G_TF_BILERP);
}
return gdl;
}
Gfx *text_end(Gfx *gdl)
{
gDPPipeSync(gdl++);
gDPSetColorDither(gdl++, G_CD_BAYER);
gDPSetTexturePersp(gdl++, G_TP_PERSP);
gDPSetTextureLOD(gdl++, G_TL_LOD);
return gdl;
}
Gfx *text_begin_boxmode(Gfx *gdl, u32 colour)
{
gDPPipeSync(gdl++);
gDPSetRenderMode(gdl++, G_RM_XLU_SURF, G_RM_XLU_SURF2);
gDPSetCombineMode(gdl++, G_CC_PRIMITIVE, G_CC_PRIMITIVE);
gDPSetPrimColorViaWord(gdl++, 0, 0, colour);
return gdl;
}
Gfx *text_end_boxmode(Gfx *gdl)
{
gDPSetCombineMode(gdl++, G_CC_CUSTOM_02, G_CC_CUSTOM_02);
return gdl;
}
Gfx *text_draw_black_box(Gfx *gdl, s32 *x1, s32 *y1, s32 *x2, s32 *y2)
{
gdl = text_begin_boxmode(gdl, 0x00000000);
gDPFillRectangle(gdl++, *x1, *y1, *x2, *y2);
gdl = text_end_boxmode(gdl);
return gdl;
}
Gfx *text_draw_black_uibox(Gfx *gdl, s32 *x1, s32 *y1, s32 *x2, s32 *y2)
{
gdl = text_begin_boxmode(gdl, 0x00000000);
gDPFillRectangleScaled(gdl++, *x1, *y1, *x2, *y2);
gdl = text_end_boxmode(gdl);
return gdl;
}
#if VERSION >= VERSION_NTSC_1_0
Gfx *text_draw_black_textbox(Gfx *gdl, s32 left, s32 top, s32 width, s32 height)
{
gdl = text_begin_boxmode(gdl, 0x00000000);
#if VERSION >= VERSION_JPN_FINAL
gDPFillRectangle(gdl++, left - 1, top - 1, width * g_TextScaleX + left + 1, top + height * var80080108jf + 1);
#else
gDPFillRectangle(gdl++, left - 1, top - 1, width * g_TextScaleX + left + 1, top + height + 1);
#endif
gdl = text_end_boxmode(gdl);
return gdl;
}
#endif
Gfx *text_draw_box(Gfx *gdl, s32 x1, s32 y1, s32 x2, s32 y2, u32 colour)
{
gdl = text_begin_boxmode(gdl, colour);
gDPFillRectangle(gdl++, x1, y1, x2, y2);
gdl = text_end_boxmode(gdl);
return gdl;
}
Gfx *text_enable_holo_ray(Gfx *gdl)
{
Gfx *allocation;
g_TextHoloRayEnabled = true;
allocation = gfx_allocate(sizeof(Gfx) * 530);
g_TextHoloRayGdl = allocation;
g_TextHoloRayGdlEnd = allocation + 530;
gSPDisplayList(gdl++, g_TextHoloRayGdl);
gdl = ortho_holoray_end(gdl);
g_TextHoloRayGdl = ortho_holoray_begin(g_TextHoloRayGdl, 0);
g_TextLastBlendY = -1;
return gdl;
}
void text_disable_holo_ray(void)
{
g_TextHoloRayEnabled = false;
gSPEndDisplayList(g_TextHoloRayGdl++);
}
void text_calculate_blend_distance(s32 y)
{
if (y != g_TextLastBlendY) {
f32 sqdist = g_Blend.diagtimer * g_Blend.diagtimer - (f32)((y - g_Blend.diagrefy) * (y - g_Blend.diagrefy));
if (sqdist > 0.0f) {
g_TextBlendDistance = g_Blend.diagrefx + sqrtf(sqdist);
} else {
g_TextBlendDistance = 0;
}
g_TextLastBlendY = y;
}
}
void text_set_diagonal_blend(s32 x, s32 y, f32 timer, u8 mode)
{
g_Blend.types |= BLENDTYPE_DIAGONAL;
g_Blend.diagrefx = x;
g_Blend.diagrefy = y;
g_Blend.diagtimer = timer;
g_Blend.diagmode = mode;
}
void text_backup_diagonal_blend_settings(void)
{
g_Blend.backupdiagrefx = g_Blend.diagrefx;
g_Blend.backupdiagrefy = g_Blend.diagrefy;
g_Blend.backupdiagtimer = g_Blend.diagtimer;
g_Blend.backupdiagmode = g_Blend.diagmode;
g_Blend.backupdiagtypes = g_Blend.types & BLENDTYPE_DIAGONAL;
}
void text_restore_diagonal_blend_settings(void)
{
g_Blend.diagrefx = g_Blend.backupdiagrefx;
g_Blend.diagrefy = g_Blend.backupdiagrefy;
g_Blend.diagtimer = g_Blend.backupdiagtimer;
g_Blend.diagmode = g_Blend.backupdiagmode;
g_Blend.types |= g_Blend.backupdiagtypes;
}
void text_set_vertical_blend(s32 y1, s32 y2, u32 arg2)
{
g_Blend.types |= BLENDTYPE_VERTICAL;
g_Blend.vertrefy1 = y1;
g_Blend.vertrefy2 = y2;
g_Blend.vert34 = arg2;
}
void text_set_horizontal_blend(s32 x1, s32 x2, u32 arg2)
{
g_Blend.types |= BLENDTYPE_HORIZONTAL;
g_Blend.horizrefx1 = x1;
g_Blend.horizrefx2 = x2;
g_Blend.horiz40 = arg2;
}
void text_reset_blends2(void)
{
g_Blend.types = 0;
}
void text_reset_blends3(void)
{
g_Blend.types = 0;
}
void text_backup_and_reset_blends(void)
{
g_Blend.backuptypes = g_Blend.types;
g_Blend.types = 0;
}
void text_restore_blends(void)
{
g_Blend.types = g_Blend.backuptypes;
}
void text_set_wave_blend(s32 arg0, s32 arg1, s32 cthresh)
{
g_Blend.types |= BLENDTYPE_WAVE;
g_Blend.wave4c = arg0;
g_Blend.wave50 = arg1;
g_Blend.wave54 = cthresh;
g_Blend.wavecolour1 = 0x44444400;
g_Blend.wavecolour2 = 0xffffff00;
}
void text_set_menu_blend(f32 arg0)
{
g_Blend.types |= BLENDTYPE_MENU;
g_Blend.menuweight = arg0 * arg0 * 110.0f;
}
void text_set_wave_colours(u32 colour1, u32 colour2)
{
g_Blend.wavecolour1 = colour1;
g_Blend.wavecolour2 = colour2;
}
void text_reset_blends(void)
{
g_Blend.types = 0;
}
bool text_has_diagonal_blend(void)
{
return (g_Blend.types & BLENDTYPE_DIAGONAL)
&& (g_Blend.diagmode == DIAGMODE_FADEIN || g_Blend.diagmode == DIAGMODE_FADEOUT);
}
u32 text_apply_projection_colour(s32 x, s32 y, u32 colour)
{
u32 stack[3];
u32 result = colour;
if (g_Blend.types & BLENDTYPE_DIAGONAL) {
f32 weightf;
f32 f12;
f32 f14;
f32 f16;
f32 f18;
if (x - g_Blend.diagrefx > -3000 && x - g_Blend.diagrefx < 3000
&& y - g_Blend.diagrefy > -3000 && y - g_Blend.diagrefy < 3000) {
f12 = sqrtf((x - g_Blend.diagrefx) * (x - g_Blend.diagrefx) + (y - g_Blend.diagrefy) * (y - g_Blend.diagrefy));
} else {
f12 = 3000.0f;
}
f14 = g_TextLAlpha;
f18 = g_TextLFade;
f16 = g_TextLLimbo;
if (g_Blend.diagmode == 0) {
if (g_Blend.diagtimer < f12) {
result = 0;
} else if (g_Blend.diagtimer - f14 < f12) {
u32 intensity;
weightf = (f12 - (g_Blend.diagtimer - f14)) / f14 * 255.0f;
intensity = 255 - (u32) weightf;
result = intensity << 8 | intensity | intensity << 16 | intensity << 24;
} else if (g_Blend.diagtimer - (f14 + f16) < f12) {
result = (((colour & 0xff) + 0xff) >> 1) | (colour & 0xffffff00);
} else if ((g_Blend.diagtimer - (f14 + f18 + f16)) < f12) {
u32 colour2 = (((colour & 0xff) + 0xff) / 2) | (colour & 0xffffff00);
weightf = (f12 - (g_Blend.diagtimer - (f14 + f18 + f16))) / f18 * 255.0f;
result = colour_blend(colour, colour2, 0xff - (u32) weightf);
}
} else if (g_Blend.diagmode == 2) {
f16 = 0.0f;
if (g_Blend.diagtimer < f12) {
result = 0x00000000;
} else if (g_Blend.diagtimer - f14 < f12) {
weightf = (f12 - (g_Blend.diagtimer - f14)) / f14 * 255.0f;
result = colour_blend(0x00000000, colour & 0xff, weightf);
} else if (g_Blend.diagtimer - (f14 + f16) < f12) {
result = colour & 0xff;
} else if ((g_Blend.diagtimer - (f14 + f18 + f16)) < f12) {
weightf = (f12 - (g_Blend.diagtimer - (f14 + f18 + f16))) / f18 * 255.0f;
result = colour_blend(colour & 0xff, colour, weightf);
}
}
}
return result;
}
u32 text_get_colour_at_pos(s32 x, s32 y, u32 colourarg)
{
f32 f14;
f32 f18;
f32 f16;
u32 colour = colourarg;
if (g_Blend.types & BLENDTYPE_MENU) {
colour = (colour_blend(0x00000000, colour, g_Blend.menuweight) & 0xffffff00) | (colour & 0xff);
}
if (g_Blend.types & BLENDTYPE_VERTICAL) {
s32 v0 = y - g_Blend.vertrefy1;
s32 v1 = y - g_Blend.vertrefy2;
if (v0 < 0) {
v0 = -v0;
}
if (v1 < 0) {
v1 = -v1;
}
if (v1 < v0) {
v0 = v1;
}
if (g_Blend.vert34 >= v0) {
colour = colour_blend(colour, 0x00000000, v0 * 255 / g_Blend.vert34);
}
}
if (g_Blend.types & BLENDTYPE_HORIZONTAL) {
s32 v0 = x - g_Blend.horizrefx1;
s32 v1 = x - g_Blend.horizrefx2;
if (v0 < 0) {
v0 = 0;
}
if (v1 < 0) {
v1 = -v1;
}
if (v1 < v0) {
v0 = v1;
}
if (g_Blend.horiz40 >= v0) {
colour = colour_blend(colour, 0x00000000, v0 * 255 / g_Blend.horiz40);
}
}
if (g_Blend.types & BLENDTYPE_DIAGONAL) {
f32 f12;
u32 stack[3];
f32 weightf;
if (x - g_Blend.diagrefx > -3000 && x - g_Blend.diagrefx < 3000
&& y - g_Blend.diagrefy > -3000 && y - g_Blend.diagrefy < 3000) {
f12 = sqrtf((x - g_Blend.diagrefx) * (x - g_Blend.diagrefx) + (y - g_Blend.diagrefy) * (y - g_Blend.diagrefy));
} else {
f12 = 3000.0f;
}
f14 = g_TextLAlpha;
f18 = g_TextLFade;
f16 = g_TextLLimbo;
if (g_Blend.diagmode == 0) {
if (g_Blend.diagtimer < f12) {
colour = 0;
} else if (g_Blend.diagtimer - f14 < f12) {
u32 intensity;
weightf = (f12 - (g_Blend.diagtimer - f14)) / f14 * 255.0f;
intensity = 255 - (u32) weightf;
colour = intensity << 8 | intensity | intensity << 16 | intensity << 24;
} else if (g_Blend.diagtimer - (f14 + f16) < f12) {
colour = 0xffffffff;
} else if (g_Blend.diagtimer - (f14 + f18 + f16) < f12) {
u32 add;
u32 mult;
weightf = (f12 - (g_Blend.diagtimer - (f14 + f18 + f16))) / f18 * 255.0f;
add = (u32) weightf * 255;
mult = 255 - (u32) weightf;
colour = ((((colour >> 24) & 0xff) * mult + add) >> 8) << 24
| ((((colour >> 16) & 0xff) * mult + add) >> 8) << 16
| ((((colour >> 8) & 0xff) * mult + add) >> 8) << 8
| ((colour & 0xff) * mult + add) >> 8;
}
} else if (g_Blend.diagmode == 2) {
f14 = 0.00f;
f18 = 66.0f;
f16 = 0.0f;
if (g_Blend.diagtimer < f12) {
colour = 0x00000000;
} else if (g_Blend.diagtimer - f14 < f12) {
f32 weightf = (f12 - (g_Blend.diagtimer - f14)) / f14 * 255.0f;
colour = colour_blend(0x00000000, colour & 0xff, weightf);
} else if (g_Blend.diagtimer - (f14 + f16) < f12) {
colour &= 0xff;
} else if (g_Blend.diagtimer - (f14 + f18 + f16) < f12) {
f32 weightf = (f12 - (g_Blend.diagtimer - (f14 + f18 + f16))) / f18 * 255.0f;
colour = colour_blend(0x00000000, colour, weightf);
}
} else {
u32 alpha[4];
static s32 burncol = 0xffffff00;
alpha[0] = colour & 0xff;
f18 = 50.0f;
f16 = 22.0f;
main_override_variable("burncol", &burncol);
if (g_Blend.diagtimer < f12) {
colour = colour_blend(alpha[0], colour, 110);
} else if (g_Blend.diagtimer - f14 < f12) {
f32 weightf = (f12 - (g_Blend.diagtimer - f14)) / f14 * 255.0f;
colour = colour_blend(
colour_blend(burncol | (colour & 0xff), colour, 0xc0),
colour_blend(alpha[0], colour, 110),
255 - (u32) weightf);
} else if (g_Blend.diagtimer - (f14 + f16) < f12) {
u32 stack;
colour = colour_blend(burncol | (colour & 0xff), colour, 0xc0);
} else if (g_Blend.diagtimer - (f14 + f18 + f16) < f12) {
f32 weightf = (f12 - (g_Blend.diagtimer - (f14 + f18 + f16))) / f18 * 255.0f;
colour = colour_blend(
colour,
colour_blend(burncol | (colour & 0xff), colour, 0xc0),
255 - (u32) weightf);
}
}
}
if (g_Blend.types & BLENDTYPE_WAVE) {
u32 stack[2];
f32 f0 = (s32)(g_Blend.wave4c - x + g_Blend.wave50 - y + 800);
f0 = 4.0f * f0 / g_Blend.wave54;
f0 -= (s32) (f0 * 0.25f) * 4.0f;
f0 -= 1.0f;
if (f0 > 1.0f) { \
f0 = 2.0f - f0;
}
if (f0 < 0.0f) {
s32 weight = g_TextSubtleTX * (0 - f0);
colour = colour_blend(g_Blend.wavecolour1 | (colour & 0xff), colour, weight);
} else {
s32 weight = g_TextSubleTY * f0;
colour = colour_blend(g_Blend.wavecolour2 | (colour & 0xff), colour, weight);
}
}
return colour;
}
Gfx *text_configure_colours_v2(Gfx *gdl, s32 x, s32 y)
{
u32 colour = text_get_colour_at_pos(x, y, g_Blend.colour04);
if (colour != g_Blend.colour44) {
gDPSetPrimColorViaWord(gdl++, 0, 0, colour);
}
g_Blend.colour44 = colour;
return gdl;
}
#if VERSION >= VERSION_PAL_BETA
void text_latin1_to_ascii(u8 *c)
{
switch (*c) {
case 0xc0: // À
case 0xc1: // Á
case 0xc4: // Ä
*c = 'A';
break;
case 0xc8: // È
case 0xc9: // É
*c = 'E';
break;
case 0xcc: // Ì
case 0xcd: // Í
*c = 'I';
break;
case 0xd2: // Ò
case 0xd3: // Ó
case 0xd6: // Ö
*c = 'O';
break;
case 0xd9: // Ù
case 0xda: // Ú
case 0xdc: // Ü
*c = 'U';
break;
case 0xe0: // à
case 0xe1: // á
case 0xe2: // â
case 0xe4: // ä
*c = 'a';
break;
case 0xe8: // è
case 0xe9: // é
case 0xea: // ê
case 0xeb: // ë
*c = 'e';
break;
case 0xec: // ì
case 0xed: // í
case 0xee: // î
case 0xef: // ï
*c = 'i';
break;
case 0xf2: // ò
case 0xf3: // ó
case 0xf4: // ô
case 0xf6: // ö
*c = 'o';
break;
case 0xf9: // ù
case 0xfa: // ú
case 0xfb: // û
case 0xfc: // ü
*c = 'u';
break;
case 0xd1: // Ñ
*c = 'N';
break;
case 0xf1: // ñ
*c = 'n';
break;
case 0xe7: // ç
*c = 'c';
break;
case 0xdf: // ß
*c = 'B';
break;
case 0xa1: // ¡
case 0xaa: // ª
case 0xb0: // °
case 0xbf: // ¿
*c = ' ';
break;
}
}
#endif
#if VERSION >= VERSION_JPN_FINAL
char text_codepoint_to_sbchar(u16 codepoint)
{
char c = '\0';
u16 codepoint16 = codepoint;
u8 codepoint8 = codepoint;
if (codepoint >= 0x10 && codepoint < 0x1a) { // 0-9
c = codepoint16;
c += 0x20;
}
if (codepoint >= 0x1a && codepoint < 0x34) { // A-Z
c = codepoint16;
c += 0x27;
}
if (codepoint >= 0x95 && codepoint < 0xaf) { // a-z
c = codepoint16;
c -= 0x34;
}
if (codepoint == 0x3fe || codepoint == 0x3ff) {
c = '-';
}
if (codepoint < 0xff) {
switch (codepoint8) {
case 0x00: c = ' '; break;
case 0x01: c = ','; break;
case 0x02: c = '$'; break;
case 0x03: c = '('; break;
case 0x04: c = ')'; break;
case 0x05: c = '.'; break;
case 0x06: c = '%'; break;
case 0x07: c = '['; break;
case 0x08: c = ']'; break;
case 0x09: c = '"'; break;
case 0x0a: c = '<'; break;
case 0x0b: c = '>'; break;
case 0x0c: c = '&'; break;
case 0x0d: c = '~'; break;
case 0x0e: c = '.'; break;
case 0x0f: c = ' '; break;
case 0x34: c = '!'; break;
case 0x35: c = '"'; break;
case 0x36: c = '#'; break;
case 0x37: c = '"'; break;
case 0x38: c = '*'; break;
case 0x39: c = '+'; break;
case 0x3a: c = ','; break;
case 0x3b: c = '-'; break;
case 0x3c: c = '.'; break;
case 0x3d: c = '/'; break;
case 0x3e: c = ':'; break;
case 0x3f: c = '='; break;
case 0x40: c = '?'; break;
case 0x41: c = '@'; break;
}
}
return c;
}
#endif
#if VERSION >= VERSION_JPN_FINAL
u16 text_sbchar_to_codepoint(u8 c)
{
u16 codepoint = 0;
if (c >= '0' && c <= '9') {
codepoint = c - 0x20;
}
if (c >= 'A' && c <= 'Z') {
codepoint = c - 0x27;
}
if (c >= 'a' && c <= 'z') {
codepoint = c + 0x34;
}
switch (c) {
case ' ': codepoint = 0x00; break;
case ',': codepoint = 0x01; break;
case '$': codepoint = 0x02; break;
case '(': codepoint = 0x03; break;
case ')': codepoint = 0x04; break;
case '.': codepoint = 0x05; break;
case '%': codepoint = 0x06; break;
case '[': codepoint = 0x07; break;
case ']': codepoint = 0x08; break;
case '"': codepoint = 0x09; break;
case '<': codepoint = 0x0a; break;
case '>': codepoint = 0x0b; break;
case '&': codepoint = 0x0c; break;
case '~': codepoint = 0x0d; break;
case '!': codepoint = 0x34; break;
case '#': codepoint = 0x36; break;
case '*': codepoint = 0x38; break;
case '+': codepoint = 0x39; break;
case '-': codepoint = 0x3b; break;
case '/': codepoint = 0x3d; break;
case ':': codepoint = 0x3e; break;
case '=': codepoint = 0x3f; break;
case '?': codepoint = 0x40; break;
case '@': codepoint = 0x41; break;
}
return codepoint;
}
#endif
#if VERSION == VERSION_JPN_FINAL
void text_parse_char(char **text, struct fontchar **thischarptr, struct fontchar **prevcharptr, struct fontchar *chars, u8 *prevchar)
{
u8 c;
u8 c1;
u8 c2;
u16 codepoint;
u8 sbchar;
static u32 ope = 0;
if (**text < 0x80) {
if (chars == NULL || var800800f0jf) {
c = **text;
g_TmpJpnChar.index = 0;
g_TmpJpnChar.baseline = 0;
g_TmpJpnChar.height = 12;
g_TmpJpnChar.width = 12;
g_TmpJpnChar.kerningindex = 0;
g_TmpJpnChar.pixeldata = NULL;
g_TmpJpnChar.index = 0x80 + text_sbchar_to_codepoint(c);
*thischarptr = &g_TmpJpnChar;
*prevcharptr = &g_TmpJpnChar;
} else {
*thischarptr = &chars[**text - 0x21];
*prevcharptr = &chars[*prevchar - 0x21];
}
*prevchar = **text;
*text += 1;
} else {
g_TmpJpnChar.index = 0;
g_TmpJpnChar.baseline = 0;
g_TmpJpnChar.height = 11;
g_TmpJpnChar.width = 11;
g_TmpJpnChar.kerningindex = 0;
g_TmpJpnChar.pixeldata = NULL;
c1 = **text;
*text = *text + 1;
c2 = **text;
*text = *text + 1;
codepoint = ((c1 & 0x7f) << 7) | (c2 & 0x7f);
sbchar = '\0';
main_override_variable("ope", &ope);
if (ope) {
sbchar = text_codepoint_to_sbchar(codepoint);
}
if (sbchar == '\0' || chars == NULL) {
if ((codepoint & 0x1fff) >= 0x400) {
codepoint = 2;
}
g_TmpJpnChar.index = codepoint + 0x80;
*thischarptr = &g_TmpJpnChar;
*prevcharptr = &g_TmpJpnChar;
} else {
*thischarptr = &chars[sbchar - 0x21];
*prevcharptr = &g_TmpJpnChar;
}
}
}
#elif VERSION >= VERSION_PAL_BETA
void text_parse_char(char **text, struct fontchar **thischarptr, struct fontchar **prevcharptr, struct fontchar *chars, u8 *prevchar)
{
u8 c;
u8 index;
if (g_Jpn) {
if (**text < 0x80) {
*thischarptr = &chars[**text - 0x21];
*prevcharptr = &chars[*prevchar - 0x21];
*prevchar = **text;
*text += 1;
return;
}
*thischarptr = &chars[*prevchar - 0x21];
*prevcharptr = &chars[*prevchar - 0x21];
return;
}
index = 0;
c = **text;
if (chars == g_CharsHandelGothicSm || chars == g_CharsHandelGothicMd || chars == g_CharsHandelGothicXs) {
switch (c) {
case 0xc4 /*Ä*/: index = 94; break;
case 0xc1 /*Á*/: index = 102; break;
case 0xc0 /*À*/: index = 112; break;
case 0xc9 /*É*/: index = 103; break;
case 0xc8 /*È*/: index = 113; break;
case 0xcd /*Í*/: index = 104; break;
case 0xcc /*Ì*/: index = 114; break;
case 0xd6 /*Ö*/: index = 95; break;
case 0xd3 /*Ó*/: index = 105; break;
case 0xd2 /*Ò*/: index = 115; break;
case 0xdc /*Ü*/: index = 96; break;
case 0xda /*Ú*/: index = 106; break;
case 0xd9 /*Ù*/: index = 116; break;
case 0xe4 /*ä*/: index = 97; break;
case 0xe1 /*á*/: index = 107; break;
case 0xe0 /*à*/: index = 117; break;
case 0xe2 /*â*/: index = 122; break;
case 0xea /*ê*/: index = 123; break;
case 0xe9 /*é*/: index = 108; break;
case 0xeb /*ë*/: index = 98; break;
case 0xe8 /*è*/: index = 118; break;
case 0xec /*ì*/: index = 119; break;
case 0xef /*ï*/: index = 99; break;
case 0xed /*í*/: index = 109; break;
case 0xee /*î*/: index = 124; break;
case 0xf4 /*ô*/: index = 125; break;
case 0xf6 /*ö*/: index = 100; break;
case 0xf3 /*ó*/: index = 110; break;
case 0xf2 /*ò*/: index = 120; break;
case 0xf9 /*ù*/: index = 121; break;
case 0xfc /*ü*/: index = 101; break;
case 0xfa /*ú*/: index = 111; break;
case 0xfb /*û*/: index = 126; break;
case 0xd1 /*Ñ*/: index = 127; break;
case 0xf1 /*ñ*/: index = 128; break;
case 0xe7 /*ç*/: index = 129; break;
case 0xdf /*ß*/: index = 130; break;
case 0xbf /*¿*/: index = 131; break;
case 0xa1 /*¡*/: index = 132; break;
case 0xb0 /*°*/: index = 133; break;
case 0xaa /*ª*/: index = 134; break;
}
} else {
text_latin1_to_ascii(&c);
}
if (index > 0) {
*thischarptr = &chars[index];
} else {
if (c < 0x21) {
c = '!';
}
if (c > 0x7e) {
c = '!';
}
*thischarptr = &chars[c - 0x21];
}
*prevcharptr = &chars[*prevchar - 0x21];
if (index > 0) {
text_latin1_to_ascii(&c);
}
*prevchar = c;
*text += 1;
}
#endif
Gfx *text_render_credits_char(Gfx *gdl, s32 *arg1, struct fontchar *curchar, struct fontchar *prevchar,
struct font *font, f32 widthscale, f32 heightscale, f32 x, f32 y)
{
s32 tmp1;
s32 tmp2;
s16 sp3e;
s16 sp3c;
s16 sp3a;
s16 sp38;
s16 sp36;
s16 sp34;
s16 sp32;
s16 sp30;
Vtx *vertices;
Col *colours;
tmp2 = font->kerning[prevchar->kerningindex * 13 + curchar->kerningindex] + g_TextCharOverlap;
*arg1 = *arg1 - tmp2 + 1;
#if VERSION >= VERSION_JPN_FINAL
if (curchar->pixeldata == NULL) {
curchar->pixeldata = (void *) lang_get_jpn_char_pixels(curchar->index - 0x80);
}
if (curchar->index >= 0x80) {
if (!var80080104jf) {
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(var800801d8jf));
var80080104jf = true;
gDPLoadSync(gdl++);
gDPLoadTLUTCmd(gdl++, 6, 15);
}
} else {
if (var80080104jf) {
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(var8007fb3c));
var80080104jf = false;
gDPLoadSync(gdl++);
gDPLoadTLUTCmd(gdl++, 6, 15);
}
}
#endif
gDPSetTextureImage(gdl++, G_IM_FMT_CI, G_IM_SIZ_16b, 1, curchar->pixeldata);
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, ((curchar->height * 8 + 17) >> 1) - 1, 2048);
gDPPipeSync(gdl++);
tmp1 = var8007fadc;
tmp2 = var8007fae0;
sp3e = (var8007fadc + *arg1 * 4) * widthscale * 10.0f + 40.0f * x;
sp3c = (var8007fae0 + curchar->baseline * 4) * heightscale * 10.0f + 40.0f * y;
sp3a = (var8007fadc + (*arg1 + (curchar->width + 1)) * 4) * widthscale * 10.0f + 40.0f * x;
sp38 = (var8007fae0 + (curchar->baseline + (curchar->height + 1)) * 4) * heightscale * 10.0f + 40.0f * y;
sp36 = 1;
sp32 = 1;
sp34 = curchar->width;
sp30 = curchar->height;
sp34 = sp36 + ((sp34 + 1) << 6);
sp30 = sp32 + ((sp30 + 1) << 6);
vertices = gfx_allocate_vertices(4);
colours = gfx_allocate_colours(1);
colours[0].word = 0xff0000ff;
vertices[0].z = -10;
vertices[1].z = -10;
vertices[2].z = -10;
vertices[3].z = -10;
vertices[0].colour = 0;
vertices[1].colour = 0;
vertices[2].colour = 0;
vertices[3].colour = 0;
vertices[0].x = sp3e / 4;
vertices[0].y = sp3c / 4;
vertices[0].s = sp36;
vertices[0].t = sp32;
vertices[1].x = sp3a / 4;
vertices[1].y = sp3c / 4;
vertices[1].s = sp34;
vertices[1].t = sp32;
vertices[2].x = sp3a / 4;
vertices[2].y = sp38 / 4;
vertices[2].s = sp34;
vertices[2].t = sp30;
vertices[3].x = sp3e / 4;
vertices[3].y = sp38 / 4;
vertices[3].s = sp36;
vertices[3].t = sp30;
gSPColor(gdl++, colours, 1);
gSPVertex(gdl++, vertices, 4, 0);
gSPTri2(gdl++, 0, 1, 2, 2, 3, 0);
*arg1 += curchar->width;
return gdl;
}
Gfx *text_render_credits(Gfx *gdl, f32 x, f32 y, f32 widthscale, f32 heightscale,
char *text, struct fontchar *chars, struct font *font, u32 colour, s32 hdir, s32 vdir)
{
s32 totalheight;
u8 prevchar;
s32 textwidth;
s32 textheight;
s32 lineheight;
s32 relx;
f32 *ptr;
f32 fx;
f32 fy;
totalheight = 0;
prevchar = 'H';
relx = 0;
#if VERSION >= VERSION_JPN_FINAL
if (1);
lineheight = 13;
#else
lineheight = chars['['].height + chars['['].baseline;
if (g_Jpn && lineheight < 14) {
lineheight = 14;
}
#endif
text_measure(&textheight, &textwidth, text, chars, font, 0);
ptr = &x;
fx = *ptr - (widthscale - 1.0f) * textwidth * 0.5f * hdir;
fy = y - (heightscale - 1.0f) * lineheight * 0.5f * vdir;
if (fx);
if (fy);
gDPPipeSync(gdl++);
gDPSetTextureLUT(gdl++, G_TT_IA16);
#if VERSION >= VERSION_JPN_FINAL
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(var800801d8jf));
var80080104jf = true;
#else
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(var8007fb3c));
#endif
gDPLoadSync(gdl++);
gDPLoadTLUTCmd(gdl++, 6, 15);
gDPSetTile(gdl++, G_IM_FMT_CI, G_IM_SIZ_4b, 1, 0x0000, G_TX_RENDERTILE, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
gDPSetTileSize(gdl++, G_TX_RENDERTILE, 0, 0, 0x007c, 0x007c);
gDPSetPrimColorViaWord(gdl++, 0, 0, colour);
gDPPipeSync(gdl++);
#if VERSION >= VERSION_PAL_BETA
if (text != NULL) {
while (*text != '\0') {
if (*text == ' ') {
relx = relx + g_TextScaleX * 5;
prevchar = 'H';
text += 1;
} else if (*text == '\n') {
if (g_TextParaOverlap >= 0 && relx == 0) {
totalheight += g_TextParaOverlap;
relx = 0;
} else {
relx = 0;
#if VERSION >= VERSION_JPN_FINAL
totalheight = totalheight + lineheight * var80080108jf;
#else
totalheight += lineheight;
#endif
}
prevchar = 'H';
text += 1;
} else {
struct fontchar *sp84;
struct fontchar *sp80;
text_parse_char(&text, &sp84, &sp80, chars, &prevchar);
gdl = text_render_credits_char(gdl, &relx, sp84, sp80, font, widthscale, heightscale, fx, fy);
}
}
}
#else
if (text != NULL) {
while (*text != '\0') {
if (*text == ' ') {
prevchar = 'H';
text += 1;
relx = relx + g_TextScaleX * 5;
} else if (*text == '\n') {
prevchar = 'H';
text += 1;
if (g_TextParaOverlap >= 0 && relx == 0) {
totalheight += g_TextParaOverlap;
relx = 0;
} else {
totalheight += lineheight;
relx = 0;
}
} else if (*text < 0x80) {
gdl = text_render_credits_char(gdl, &relx, &chars[*text - 0x21], &chars[prevchar - 0x21], font,
widthscale, heightscale, fx, fy);
prevchar = *text;
text += 1;
} else {
u16 codepoint = (text[0] & 0x7f) << 7 | (text[1] & 0x7f);
struct fontchar tmpchar = {0, 0, 12, 11};
if (1);
if (codepoint & 0x2000) {
tmpchar.width = 15;
tmpchar.height = 16;
}
if ((codepoint & 0x1fff) >= 0x3c8) {
codepoint = 2;
}
tmpchar.index = codepoint + 0x80;
tmpchar.pixeldata = (void *) lang_get_jpn_char_pixels(codepoint);
text += 2;
}
}
}
#endif
return gdl;
}
Gfx *text_render_char_v2(Gfx *gdl, s32 *x, s32 *y, struct fontchar *curchar, struct fontchar *prevchar,
struct font *font, s32 savedx, s32 savedy, s32 width, s32 height, s32 shadowoffset)
{
#if VERSION >= VERSION_JPN_FINAL
s32 tmp;
s32 sp90;
s32 xscale = g_TextScaleX;
s32 yscale = var80080108jf;
savedy -= 2;
if (g_TextRotated90) {
xscale = 1;
}
sp90 = *y + shadowoffset;
tmp = g_TextCharOverlap + font->kerning[prevchar->kerningindex * 13 + curchar->kerningindex];
*x -= (tmp - 1) * xscale;
width *= xscale;
height *= yscale;
if (g_TextRotated90 || (*x > 0 && *x <= vi_get_width() && sp90 + curchar->baseline <= vi_get_height())) {
if (savedx + width >= *x
&& savedy + height >= curchar->baseline + sp90
&& *x >= savedx
&& curchar->baseline + sp90 + curchar->height >= savedy) {
if (curchar->pixeldata == NULL) {
curchar->pixeldata = (void *)lang_get_jpn_char_pixels(curchar->index - 0x80);
}
if (curchar->index >= 0x80) {
if (!var80080104jf) {
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(var800801d8jf));
var80080104jf = true;
gDPLoadSync(gdl++);
gDPLoadTLUTCmd(gdl++, 6, 15);
}
} else {
if (var80080104jf) {
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(var8007fb3c));
var80080104jf = false;
gDPLoadSync(gdl++);
gDPLoadTLUTCmd(gdl++, 6, 15);
}
}
gDPSetTextureImage(gdl++, G_IM_FMT_CI, G_IM_SIZ_16b, 1, curchar->pixeldata);
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, ((curchar->height * 8 + 17) >> 1) - 1, 2048);
gDPPipeSync(gdl++);
if (g_Blend.types) {
gdl = text_configure_colours_v2(gdl, *x / g_UiScaleX, *y + shadowoffset);
}
if (1);
if (*x + xscale * curchar->width <= savedx + width) {
if (savedy <= curchar->baseline * yscale + sp90) {
if (curchar->baseline * yscale + sp90 + curchar->height * yscale <= savedy + height) {
if (g_TextRotated90) {
gSPTextureRectangleFlip(gdl++,
(sp90 - curchar->baseline - curchar->height * g_TextScaleX) * 4 + var8007fae0,
*x * 4 + var8007fadc,
(sp90 - curchar->baseline) * 4 + var8007fae0,
(*x + curchar->width * var80080108jf) * 4 + var8007fadc,
G_TX_RENDERTILE,
var8007fae8 + 32,
((curchar->height - 1) << 5) + var8007fae4 + 32,
1024 / var80080108jf,
65536 - 1024 / g_TextScaleX);
} else {
gSPTextureRectangle(gdl++,
*x * 4 + var8007fadc,
(sp90 + curchar->baseline * var80080108jf) * 4 + var8007fae0,
(*x + curchar->width * g_TextScaleX) * 4 + var8007fadc,
(sp90 + curchar->baseline * var80080108jf + curchar->height * var80080108jf) * 4 + var8007fae0 - (var80080108jf - 1) * 4,
G_TX_RENDERTILE,
var8007fae4 + 32,
var8007fae8 + 32,
1024 / g_TextScaleX,
1024 / var80080108jf);
if (g_TextHoloRayEnabled) {
text_calculate_blend_distance(*y + shadowoffset);
if (g_TextBlendDistance >= *x / g_UiScaleX && *x / g_UiScaleX + curchar->width * g_TextScaleX >= g_TextBlendDistance) {
g_TextHoloRayGdl = ortho_draw_holoray(g_TextHoloRayGdl,
g_TextBlendDistance,
curchar->baseline * var80080108jf + sp90,
g_TextBlendDistance,
curchar->baseline * var80080108jf + sp90 + curchar->height * var80080108jf,
g_Blend.colour04,
g_Blend.colour04,
MENUPLANE_00);
}
if (g_TextBlendDistance - 3 >= *x / g_UiScaleX && *x / g_UiScaleX + curchar->width * g_TextScaleX >= g_TextBlendDistance - 3) {
g_TextHoloRayGdl = ortho_draw_holoray(g_TextHoloRayGdl,
g_TextBlendDistance,
curchar->baseline * var80080108jf + sp90,
g_TextBlendDistance,
curchar->baseline * var80080108jf + sp90 + curchar->height * var80080108jf,
g_Blend.colour04,
g_Blend.colour04,
MENUPLANE_00);
}
}
}
} else if (savedy + height >= curchar->baseline * var80080108jf + sp90) {
gSPTextureRectangle(gdl++,
*x * 4 + var8007fadc,
(sp90 + curchar->baseline * var80080108jf) * 4 + var8007fae0,
(*x + curchar->width * g_TextScaleX) * 4 + var8007fadc,
(savedy + height) * 4 + var8007fae0,
G_TX_RENDERTILE,
var8007fae4 + 32,
var8007fae8 + 32,
1024 / g_TextScaleX,
1024 / var80080108jf);
}
} else {
if (curchar->baseline * var80080108jf + sp90 + curchar->height * var80080108jf >= savedy) {
gSPTextureRectangle(gdl++,
*x * 4 + var8007fadc,
savedy * 4 + var8007fae0,
(*x + curchar->width * g_TextScaleX) * 4 + var8007fadc,
(curchar->baseline * var80080108jf + sp90 + curchar->height * var80080108jf) * 4 + var8007fae0,
G_TX_RENDERTILE,
var8007fae4 + 32,
((savedy - sp90 - curchar->baseline * var80080108jf) << 5) + var8007fae8 + 32,
1024 / g_TextScaleX,
1024 / var80080108jf);
}
}
}
}
}
#else
s32 tmp;
s32 sp90;
s32 xscale = g_TextScaleX;
#if VERSION >= VERSION_PAL_BETA
savedy -= 2;
#endif
if (g_TextRotated90) {
xscale = 1;
}
sp90 = *y + shadowoffset;
tmp = g_TextCharOverlap + font->kerning[prevchar->kerningindex * 13 + curchar->kerningindex];
*x -= (tmp - 1) * xscale;
width *= xscale;
if (g_TextRotated90 || (*x > 0 && *x <= vi_get_width() && sp90 + curchar->baseline <= vi_get_height())) {
if (savedx + width >= *x
&& savedy + height >= curchar->baseline + sp90
&& *x >= savedx
&& curchar->baseline + sp90 + curchar->height >= savedy) {
gDPSetTextureImage(gdl++, G_IM_FMT_CI, G_IM_SIZ_16b, 1, curchar->pixeldata);
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, ((curchar->height * 8 + 17) >> 1) - 1, 2048);
gDPPipeSync(gdl++);
if (g_Blend.types) {
gdl = text_configure_colours_v2(gdl, *x / g_UiScaleX, *y + shadowoffset);
}
if (1);
if (*x + xscale * curchar->width <= savedx + width) {
if (savedy <= curchar->baseline + sp90) {
if (curchar->baseline + sp90 + curchar->height <= savedy + height) {
if (g_TextRotated90) {
gSPTextureRectangleFlip(gdl++,
(sp90 - curchar->baseline - curchar->height * g_TextScaleX) * 4 + var8007fae0,
*x * 4 + var8007fadc,
(sp90 - curchar->baseline) * 4 + var8007fae0,
(*x + curchar->width) * 4 + var8007fadc,
G_TX_RENDERTILE,
var8007fae8 + 32,
((curchar->height - 1) << 5) + var8007fae4 + 32,
1024,
65536 - 1024 / g_TextScaleX);
} else {
gSPTextureRectangle(gdl++,
*x * 4 + var8007fadc,
(sp90 + curchar->baseline) * 4 + var8007fae0,
(*x + curchar->width * g_TextScaleX) * 4 + var8007fadc,
(sp90 + curchar->baseline + curchar->height) * 4 + var8007fae0,
G_TX_RENDERTILE,
var8007fae4 + 32,
var8007fae8 + 32,
1024 / g_TextScaleX,
1024);
if (g_TextHoloRayEnabled) {
text_calculate_blend_distance(*y + shadowoffset);
if (g_TextBlendDistance >= *x / g_UiScaleX && *x / g_UiScaleX + curchar->width * g_TextScaleX >= g_TextBlendDistance) {
g_TextHoloRayGdl = ortho_draw_holoray(g_TextHoloRayGdl,
g_TextBlendDistance,
curchar->baseline + sp90,
g_TextBlendDistance,
curchar->baseline + sp90 + curchar->height,
g_Blend.colour04,
g_Blend.colour04,
MENUPLANE_00);
}
if (g_TextBlendDistance - 3 >= *x / g_UiScaleX && *x / g_UiScaleX + curchar->width * g_TextScaleX >= g_TextBlendDistance - 3) {
g_TextHoloRayGdl = ortho_draw_holoray(g_TextHoloRayGdl,
g_TextBlendDistance,
curchar->baseline + sp90,
g_TextBlendDistance,
curchar->baseline + sp90 + curchar->height,
g_Blend.colour04,
g_Blend.colour04,
MENUPLANE_00);
}
}
}
} else if (savedy + height >= curchar->baseline + sp90) {
gSPTextureRectangle(gdl++,
*x * 4 + var8007fadc,
(sp90 + curchar->baseline) * 4 + var8007fae0,
(*x + curchar->width * g_TextScaleX) * 4 + var8007fadc,
(savedy + height) * 4 + var8007fae0,
G_TX_RENDERTILE,
var8007fae4 + 32,
var8007fae8 + 32,
1024 / g_TextScaleX,
1024);
}
} else {
if (curchar->baseline + sp90 + curchar->height >= savedy) {
gSPTextureRectangle(gdl++,
*x * 4 + var8007fadc,
savedy * 4 + var8007fae0,
(*x + curchar->width * g_TextScaleX) * 4 + var8007fadc,
(curchar->baseline + sp90 + curchar->height) * 4 + var8007fae0,
G_TX_RENDERTILE,
var8007fae4 + 32,
((savedy - sp90 - curchar->baseline) << 5) + var8007fae8 + 32,
1024 / g_TextScaleX,
1024);
}
}
}
}
}
#endif
*x += curchar->width * xscale;
return gdl;
}
bool g_TextShadowEnabled = false;
void text_set_shadow_enabled(bool enabled)
{
g_TextShadowEnabled = enabled;
}
void text_set_shadow_colour(u32 colour)
{
var800a463c = colour;
}
/**
* The v2 renderer has full support for all of PD's text rendering features.
* - Text can be rotated 90 degrees.
* - Shadows can be enabled/disabled and colour chosen using the above functions.
* - The hologram refresh line can be enabled and disabled using their functions.
* - In versions prior to JPN, shadows are drawn using the v1 renderer which
* means they don't use the special effects. In JPN they are drawn using a
* recursive call to v2 so they have all the special effects.
* - JPN draws the shadows 4 times, each offset in each direction.
* - Blend settings are supported, such as the fading out hologram effect and
* the wavy pattern in gun and function names during gameplay.
*/
Gfx *text_render_v2(Gfx *gdl, s32 *x, s32 *y, char *text, struct fontchar *chars, struct font *font,
u32 textcolour, s32 width, s32 height, s32 shadowoffset, s32 lineheight)
{
s32 savedx;
s32 savedy;
u8 prevchar;
s32 scalex;
u32 textcolourforshadow;
u32 shadowcolour;
s32 shadowx;
s32 shadowy;
#if VERSION >= VERSION_JPN_FINAL
s32 savedmode;
s32 savedtypes;
#endif
f32 alpha;
static u32 sbrd = 0x00000000;
scalex = g_TextScaleX;
if (g_TextRotated90) {
*y *= g_UiScaleX;
scalex = 1;
} else {
*x *= g_UiScaleX;
}
if (g_TextShadowEnabled) {
alpha = (1.0f - menu_get_sin_osc_frac(40.0f)) * 100.0f + 150.0f;
shadowx = *x / g_UiScaleX;
shadowy = *y;
shadowcolour = var800a463c;
#if VERSION >= VERSION_JPN_FINAL
savedmode = g_Blend.diagmode;
savedtypes = g_Blend.types;
if (!g_TextRotated90) {
shadowy--;
}
main_override_variable("sbrd", &sbrd);
if (sbrd) {
shadowcolour = sbrd;
}
g_Blend.types &= ~BLENDTYPE_WAVE;
if (g_Blend.diagmode != DIAGMODE_FADEIN) {
g_Blend.types &= ~BLENDTYPE_DIAGONAL;
} else {
g_Blend.diagmode = DIAGMODE_FADEOUT;
}
g_TextShadowEnabled = false;
shadowx = *x / g_UiScaleX + 1;
shadowy = *y - 1;
gdl = text_render_v2(gdl, &shadowx, &shadowy, text, chars, font, shadowcolour, width, height, shadowoffset, lineheight);
shadowx = *x / g_UiScaleX + 1;
shadowy = *y + 1;
gdl = text_render_v2(gdl, &shadowx, &shadowy, text, chars, font, shadowcolour, width, height, shadowoffset, lineheight);
shadowx = *x / g_UiScaleX - 1;
shadowy = *y - 1;
gdl = text_render_v2(gdl, &shadowx, &shadowy, text, chars, font, shadowcolour, width, height, shadowoffset, lineheight);
shadowx = *x / g_UiScaleX - 1;
shadowy = *y + 1;
gdl = text_render_v2(gdl, &shadowx, &shadowy, text, chars, font, shadowcolour, width, height, shadowoffset, lineheight);
g_TextShadowEnabled = true;
g_Blend.types = savedtypes;
g_Blend.diagmode = savedmode;
#else
textcolourforshadow = (textcolour & 0xffffff00) | (u32) alpha;
main_override_variable("sbrd", &sbrd);
if (sbrd) {
shadowcolour = sbrd;
}
gdl = text_render_v1(gdl, &shadowx, &shadowy, text, chars, font, textcolourforshadow, shadowcolour, width, height, shadowoffset, lineheight);
#endif
}
#if VERSION >= VERSION_JPN_FINAL
if (!g_TextRotated90) {
*y = *y - 1;
}
#endif
main_override_variable("lalpha", &g_TextLAlpha);
main_override_variable("subtlety", &g_TextSubleTY);
main_override_variable("subtletx", &g_TextSubtleTX);
main_override_variable("coly", &g_TextColourY);
main_override_variable("colx", &g_TextColourX);
main_override_variable("lfade", &g_TextLFade);
main_override_variable("llimbo", &g_TextLLimbo);
savedx = *x;
savedy = *y;
prevchar = 'H';
#if VERSION >= VERSION_JPN_FINAL
if (lineheight == -1) {
lineheight = chars['['].baseline + chars['['].height;
} else {
lineheight = 13;
}
#else
if (lineheight == 0) {
lineheight = chars['['].height + chars['['].baseline;
}
if (g_Jpn && lineheight < 14) {
lineheight = 14;
}
#endif
gDPPipeSync(gdl++);
gDPSetTextureLUT(gdl++, G_TT_IA16);
#if VERSION >= VERSION_JPN_FINAL
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(var800801d8jf));
var80080104jf = true;
#else
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(var8007fb3c));
#endif
gDPLoadSync(gdl++);
gDPLoadTLUTCmd(gdl++, 6, 15);
gDPSetTile(gdl++, G_IM_FMT_CI, G_IM_SIZ_4b, 1, 0x0000, G_TX_RENDERTILE, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
gDPSetTileSize(gdl++, G_TX_RENDERTILE, 0, 0, 0x007c, 0x007c);
gDPSetPrimColorViaWord(gdl++, 0, 0, textcolour);
gDPPipeSync(gdl++);
g_Blend.colour04 = textcolour;
g_Blend.colour44 = textcolour;
#if VERSION >= VERSION_PAL_BETA
if (text != NULL) {
while (*text != '\0') {
if (*text == ' ') {
*x += scalex * 5;
prevchar = 'H';
text++;
} else if (*text == '\n') {
if (g_TextParaOverlap >= 0 && savedx == *x) {
*y += g_TextParaOverlap;
} else {
*y += lineheight;
}
*x = savedx;
prevchar = 'H';
text++;
} else {
struct fontchar *thischardata;
struct fontchar *prevchardata;
text_parse_char(&text, &thischardata, &prevchardata, chars, &prevchar);
gdl = text_render_char_v2(gdl, x, y, thischardata, prevchardata, font, savedx, savedy, width, height, shadowoffset);
}
}
}
#else
if (text != NULL) {
while (*text != '\0') {
if (*text == ' ') {
prevchar = 'H';
*x += scalex * 5;
text++;
} else if (*text == '\n') {
prevchar = 'H';
text++;
if (g_TextParaOverlap >= 0 && savedx == *x) {
*y += g_TextParaOverlap;
} else {
*y += lineheight;
}
*x = savedx;
} else if (*text < 0x80) {
gdl = text_render_char_v2(gdl, x, y, &chars[*text - 0x21], &chars[prevchar - 0x21], font, savedx, savedy, width, height, shadowoffset);
prevchar = *text;
text++;
} else {
u16 codepoint = ((*text & 0x7f) << 7) | (text[1] & 0x7f);
struct fontchar tmpchar = {0, 0, 12, 11};
if (codepoint & 0x2000) {
tmpchar.width = 15;
tmpchar.height = 16;
}
if ((codepoint & 0x1fff) >= 0x3c8) {
codepoint = 2;
}
tmpchar.index = codepoint + 0x80;
tmpchar.pixeldata = (void *)lang_get_jpn_char_pixels(codepoint);
gdl = text_render_char_v2(gdl, x, y, &tmpchar, &tmpchar, font, savedx, savedy, width, height, shadowoffset);
text += 2;
}
}
}
#endif
if (g_TextRotated90) {
*y = *y / g_UiScaleX;
} else {
*x = *x / g_UiScaleX;
}
return gdl;
}
Gfx *text_configure_colours_v1(Gfx *gdl, s32 x, s32 y)
{
u32 colour = text_get_colour_at_pos(x, y, g_Blend.colour04);
if (colour != g_Blend.colour44) {
gDPSetColor(gdl++, G_SETENVCOLOR, colour);
}
g_Blend.colour44 = colour;
colour = (g_Blend.colour08 & 0xffffff00) | (text_get_colour_at_pos(x, y, g_Blend.colour08) & 0xff);
if (colour != g_Blend.colour48) {
gDPSetPrimColorViaWord(gdl++, 0, 0, colour);
}
g_Blend.colour48 = colour;
return gdl;
}
Gfx *text_render_char_v1(Gfx *gdl, s32 *x, s32 *y, struct fontchar *thischardata, struct fontchar *prevchardata,
struct font *font, s32 left, s32 top, s32 width, s32 height, s32 shadowoffset)
{
s32 tmp;
s32 sp38;
sp38 = *y + shadowoffset;
tmp = font->kerning[prevchardata->kerningindex * 13 + thischardata->kerningindex] + g_TextCharOverlap;
*x -= (tmp - 1) * g_TextScaleX;
if (*x > 0
&& *x <= vi_get_width()
&& sp38 + thischardata->baseline <= vi_get_height()
&& *x <= left + width
&& thischardata->baseline + sp38 <= top + height
&& *x >= left
&& sp38 + thischardata->baseline + thischardata->height >= top) {
#if VERSION >= VERSION_JPN_FINAL
if (thischardata->pixeldata == NULL) {
thischardata->pixeldata = (void *)lang_get_jpn_char_pixels(thischardata->index - 0x80);
}
#else
if (g_Blend.types) {
gdl = text_configure_colours_v1(gdl, *x / g_UiScaleX, *y + shadowoffset);
}
#endif
gDPSetTextureImage(gdl++, G_IM_FMT_CI, G_IM_SIZ_16b, 1, thischardata->pixeldata);
gDPLoadSync(gdl++);
gDPLoadBlock(gdl++, G_TX_LOADTILE, 0, 0, ((thischardata->height * 8 + 17) >> 1) - 1, 2048);
gDPPipeSync(gdl++);
gdl = text_render_char_v1_part2(gdl, *x - g_TextScaleX, sp38 - 1, thischardata, left, top - 1, width, height);
}
*x += thischardata->width * g_TextScaleX;
return gdl;
}
Gfx *text_render_char_v1_part2(Gfx *gdl, s32 x, s32 y, struct fontchar *char1, s32 arg4, s32 arg5, s32 arg6, s32 arg7)
{
if (arg4 + arg6 >= char1->width + x + 2) {
if (y + char1->baseline >= arg5) {
if (arg5 + arg7 >= y + char1->baseline + char1->height + 2) {
if (g_TextRotated90) {
gSPTextureRectangleFlip(gdl++,
((y - char1->baseline) - ((char1->height + 2) * g_TextScaleX)) * 4,
x * 4,
(y - char1->baseline) * 4,
(x + char1->width + 2) * 4,
G_TX_RENDERTILE,
0,
(char1->height + 1) << 5,
1024 / g_TextScaleX,
-1024);
} else {
gSPTextureRectangle(gdl++,
x * 4,
(y + char1->baseline) * 4,
(x + char1->width * g_TextScaleX + 2) * 4,
(y + char1->baseline + char1->height + 2) * 4,
G_TX_RENDERTILE,
0,
0,
1024 / g_TextScaleX,
1024);
}
} else {
if (arg5 + arg7 >= y + char1->baseline) {
gSPTextureRectangle(gdl++,
x * 4,
(y + char1->baseline) * 4,
(x + char1->width * g_TextScaleX + 2) * 4,
(arg5 + arg7) * 4,
G_TX_RENDERTILE,
0,
0,
1024 / g_TextScaleX,
1024);
}
}
} else {
if (y + char1->baseline + char1->height + 2 >= arg5) {
gSPTextureRectangle(gdl++,
x * 4,
arg5 * 4,
(x + char1->width * g_TextScaleX + 2) * 4,
(y + char1->baseline + char1->height + 2) * 4,
G_TX_RENDERTILE,
0,
(arg5 - char1->baseline - y) << 5,
1024 / g_TextScaleX,
1024);
}
}
}
return gdl;
}
/**
* The v1 renderer is likely very similar, if not the same as GE's.
*
* It has support for shadows and implements them using the environment colour.
*
* It does not support the hologram refresh effect, oscillating focus colour
* or blend effects.
*/
Gfx *text_render_v1(Gfx *gdl, s32 *x, s32 *y, char *text,
struct fontchar *chars, struct font *font, u32 textcolour, u32 glowcolour,
s32 width, s32 height, s32 shadowoffset, s32 lineheight)
{
s32 savedx;
s32 savedy;
#if VERSION >= VERSION_PAL_BETA
u8 prevchar;
#else
s32 prevchar;
#endif
*x *= g_UiScaleX;
savedx = *x;
savedy = *y;
prevchar = 'H';
#if VERSION >= VERSION_JPN_FINAL
if (lineheight == -1) {
lineheight = chars['['].baseline + chars['['].height;
} else {
lineheight = 13;
}
#else
if (lineheight == 0) {
lineheight = chars['['].height + chars['['].baseline;
}
if (g_Jpn && lineheight < 14) {
lineheight = 14;
}
#endif
gDPPipeSync(gdl++);
gDPSetTextureLUT(gdl++, G_TT_IA16);
gDPSetTextureImage(gdl++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, osVirtualToPhysical(&var8007fb5c));
gDPLoadSync(gdl++);
gDPLoadTLUTCmd(gdl++, 6, 31);
#if VERSION >= VERSION_JPN_FINAL
// Use clamp
gDPSetTile(gdl++, G_IM_FMT_CI, G_IM_SIZ_4b, 1, 0x0000, G_TX_RENDERTILE, 0, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOLOD);
gDPSetTileSize(gdl++, G_TX_RENDERTILE, 0, 0, 0x007c, 0x007c);
gDPSetTile(gdl++, G_IM_FMT_CI, G_IM_SIZ_4b, 1, 0x0000, 1, 1, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOLOD);
#else
// Use wrap
gDPSetTile(gdl++, G_IM_FMT_CI, G_IM_SIZ_4b, 1, 0x0000, G_TX_RENDERTILE, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
gDPSetTileSize(gdl++, G_TX_RENDERTILE, 0, 0, 0x007c, 0x007c);
gDPSetTile(gdl++, G_IM_FMT_CI, G_IM_SIZ_4b, 1, 0x0000, 1, 1, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD);
#endif
gDPSetTileSize(gdl++, 1, 0, 0, 0x007c, 0x007c);
gDPSetCycleType(gdl++, G_CYC_2CYCLE);
gDPSetCombineLERP(gdl++,
ENVIRONMENT, PRIMITIVE, TEXEL1_ALPHA, PRIMITIVE, 0, 0, 0, TEXEL0,
0, 0, 0, COMBINED, COMBINED, 0, ENVIRONMENT, 0);
gDPSetPrimColorViaWord(gdl++, 0, 0, glowcolour);
gDPSetEnvColorViaWord(gdl++, textcolour);
gDPPipeSync(gdl++);
g_Blend.colour08 = glowcolour;
g_Blend.colour48 = glowcolour;
g_Blend.colour04 = textcolour;
g_Blend.colour44 = textcolour;
#if VERSION >= VERSION_PAL_BETA
while (*text != '\0') {
if (*text == ' ') {
*x += g_TextScaleX * 5;
prevchar = 'H';
text++;
} else if (*text == '\n') {
*x = savedx;
*y += lineheight;
prevchar = 'H';
text++;
} else {
struct fontchar *thischardata;
struct fontchar *prevchardata;
text_parse_char(&text, &thischardata, &prevchardata, chars, &prevchar);
gdl = text_render_char_v1(gdl, x, y, thischardata, prevchardata,
font, savedx, savedy, width * g_TextScaleX, height, shadowoffset);
}
}
#else
while (*text != '\0') {
if (*text == ' ') {
*x += g_TextScaleX * 5;
prevchar = 'H';
text++;
} else if (*text == '\n') {
*x = savedx;
*y += lineheight;
prevchar = 'H';
text++;
} else if (*text < 0x80) {
gdl = text_render_char_v1(gdl, x, y, &chars[*text - 0x21], &chars[prevchar - 0x21],
font, savedx, savedy, width * g_TextScaleX, height, shadowoffset);
prevchar = *text;
text++;
} else {
u16 codepoint = ((*text & 0x7f) << 7) | (text[1] & 0x7f);
struct fontchar chardata = {0, 0, 12, 11};
if (codepoint & 0x2000) {
chardata.width = 15;
chardata.height = 16;
}
if ((codepoint & 0x1fff) >= 0x3c8) {
codepoint = 2;
}
chardata.index = codepoint + 0x80;
chardata.pixeldata = (void *)lang_get_jpn_char_pixels(codepoint);
gdl = text_render_char_v1(gdl, x, y, &chardata, &chardata, font, savedx, savedy, width * g_TextScaleX, height, shadowoffset);
text += 2;
}
}
#endif
gDPPipeSync(gdl++);
gDPSetCycleType(gdl++, G_CYC_1CYCLE);
gDPSetCombineLERP(gdl++,
0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0,
0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0);
*x = *x / g_UiScaleX;
return gdl;
}
#if VERSION == VERSION_JPN_FINAL
/**
* In all versions prior to JPN, there are two functions to render text:
* text_render_v1 and text_render_v2.
*
* For the Japanese version, Rare wanted to switch many of the calls from
* text_render_v1 to text_render_v2. They introduced this helper function which
* has the same arguments as text_render_v1 but wraps text_render_v2. This means
* they only had to replace the function name in all the places where they
* wanted to switch to the other function rather than change the arguments too.
*
* In decomp, to avoid ifdefs everywhere where it was changed, we call
* text_render_vx. In versions prior to JPN this is redefined to text_render_v1
* in text.h. In JPN it's Rare's wrapper function.
*/
Gfx *text_render_vx(Gfx *gdl, s32 *x, s32 *y, char *text, struct fontchar *font1, struct font *font2, u32 textcolour, u32 shadowcolour, s32 width, s32 height, s32 shadowoffset, s32 lineheight)
{
return text_render_v2(gdl, x, y, text, font1, font2, textcolour, width, height, shadowoffset, lineheight);
}
#endif
void text_measure(s32 *textheight, s32 *textwidth, char *text, struct fontchar *font1, struct font *font2, s32 lineheight)
{
char prevchar;
char thischar;
s32 longest;
#if VERSION == VERSION_JPN_FINAL
s32 overlap = 0;
struct fontchar *tmp2 = font1;
struct fontchar *thischardata;
struct fontchar *prevchardata;
#endif
s32 tmp;
#if VERSION < VERSION_PAL_BETA
char hack1 = 1;
#endif
prevchar = 'H';
tmp = 0;
longest = 0;
*textheight = 0;
*textwidth = 0;
#if VERSION == VERSION_JPN_FINAL
if (lineheight == -1) {
lineheight = font1['['].baseline + font1['['].height;
} else {
lineheight = 13;
}
#else
if (lineheight == 0) {
lineheight = font1['['].baseline + font1['['].height;
}
if (g_Jpn) {
if (lineheight < 14) {
lineheight = 14;
}
}
#endif
if (text) {
while (*text != '\0') {
if (*text == ' ') {
// Space
if (text[1] != '\n') {
*textwidth += SPACE_WIDTH;
}
prevchar = 'H';
text++;
} else if (*text == '\n') {
// Line break
#if VERSION >= VERSION_JPN_FINAL
*textwidth -= overlap;
overlap = 0;
#endif
if (*textwidth > longest) {
longest = *textwidth;
}
*textwidth = 0;
*textheight += lineheight;
text++;
} else {
#if VERSION >= VERSION_JPN_FINAL
text_parse_char(&text, &thischardata, &prevchardata, font1, &prevchar);
overlap = 0;
if (thischardata->index == 0xbe) {
overlap = 4;
}
if (thischardata->index == 0x84) {
overlap = 7;
}
if (font2) {
tmp = font2->kerning[prevchardata->kerningindex * 13 + thischardata->kerningindex] + g_TextCharOverlap - 1;
} else {
tmp = 0;
}
*textwidth += thischardata->width - tmp;
#elif VERSION >= VERSION_PAL_BETA
struct fontchar *thischardata;
struct fontchar *prevchardata;
text_parse_char(&text, &thischardata, &prevchardata, font1, &prevchar);
tmp = font2->kerning[prevchardata->kerningindex * 13 + thischardata->kerningindex] + g_TextCharOverlap - 1;
*textwidth += thischardata->width - tmp;
#else
s32 hack2 = hack1;
if (*text < 0x80) {
// Normal single-byte character
thischar = *text;
if (hack2);
tmp = font2->kerning[font1[prevchar - 0x21].kerningindex * 13 + font1[thischar - 0x21].kerningindex] + g_TextCharOverlap - 1;
*textwidth += font1[thischar - 0x21].width - tmp;
prevchar = *text;
text++;
} else if (*text < 0xc0) {
// Multi-byte character
tmp = font2->kerning[0] + g_TextCharOverlap - 1;
*textwidth -= tmp - 11;
text += 2;
} else {
// Multi-byte character
tmp = font2->kerning[0] + g_TextCharOverlap - 1;
*textwidth -= tmp - 15;
text += 2;
}
#endif
}
}
}
// @bug? Shouldn't this go at the very end of the function?
if (g_UiScaleX == 1u) {
*textwidth *= g_TextScaleX;
}
#if VERSION >= VERSION_JPN_FINAL
*textwidth -= overlap;
#endif
if (longest > *textwidth) {
*textwidth = longest;
}
}
#if VERSION == VERSION_JPN_FINAL
bool text_stub(s32 arg0, s32 arg1)
{
return false;
}
#endif
void text_wrap(s32 wrapwidth, char *src, char *dst, struct fontchar *chars, struct font *font)
{
#if VERSION >= VERSION_JPN_FINAL
s32 curlinewidth = 0;
s32 i = 0;
s32 wordlen;
s32 wordwidth;
s32 wordheight = 0;
bool more = true;
bool itfits;
bool keepchar;
s32 unusedwordwidth;
char curword[32];
bool forcespace;
while (more == true) {
wordlen = 0;
wordwidth = 0;
unusedwordwidth = 0;
forcespace = false;
keepchar = true;
while (keepchar) {
u16 codepoint = 0;
bool multibyte = false;
s32 charwidth = 0;
u8 c;
u8 c1;
u8 c2;
if (*src < 0x80) {
c = *src;
} else {
multibyte = true;
c1 = src[0];
c2 = src[1];
codepoint = ((c1 & 0x7f) << 7) | (c2 & 0x7f);
c = text_codepoint_to_sbchar(codepoint);
if (c2 == '\0') {
multibyte = false;
}
}
if (c == '\0') {
keepchar = false;
if (multibyte) {
if (codepoint >= 0x34 && codepoint < 0x4e) {
keepchar = true;
}
if (codepoint >= 0xaf && codepoint < 0xb8) {
keepchar = true;
}
if (codepoint == 0x104) {
keepchar = true;
}
if (codepoint == 0x105) {
keepchar = true;
}
if (wordlen == 0) {
keepchar = true;
}
charwidth = 11;
}
} else {
if (c > ' ') {
keepchar = true;
} else {
keepchar = false;
}
charwidth = chars[c - 0x21].width;
if (!keepchar && wordlen == 0) {
if (c != '\0' && c != ' ') {
keepchar = true;
}
if (c == ' ') {
forcespace = true;
}
}
if (wordlen != 0
&& ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))) {
if (curword[wordlen - 1] >= 0x80) {
u8 c1 = curword[wordlen - 2];
u8 c2 = curword[wordlen - 1];
u16 codepoint = ((c1 & 0x7f) << 7) | (c2 & 0x7f);
u8 c = text_codepoint_to_sbchar(codepoint);
if (c == '\0') {
keepchar = false;
}
}
}
}
if (keepchar) {
curword[wordlen] = *src;
src++;
wordlen++;
if (multibyte) {
curword[wordlen] = *src;
src++;
wordlen++;
}
unusedwordwidth += charwidth;
}
}
curword[wordlen] = '\0';
text_measure(&wordheight, &wordwidth, curword, chars, font, 0);
curlinewidth += wordwidth;
if (curlinewidth <= wrapwidth) {
itfits = true;
} else {
itfits = false;
}
if (*src == '\n') {
if (!itfits) {
*dst = '\n';
dst++;
for (i = 0; i < g_WrapIndentCount; i++) {
*dst = ' ';
dst++;
}
}
for (i = 0; i < wordlen; i++) {
*dst = curword[i];
dst++;
}
src++;
curlinewidth = 0;
*dst = '\n';
dst++;
} else if (*src == '\0') {
if (!itfits) {
*dst = '\n';
dst++;
for (i = 0; i < g_WrapIndentCount; i++) {
*dst = ' ';
dst++;
}
}
src++;
for (i = 0; i < wordlen; i++) {
*dst = curword[i];
dst++;
}
*dst = '\0';
more = false;
} else {
if (!itfits) {
*dst = '\n';
dst++;
for (i = 0; i < g_WrapIndentCount; i++) {
*dst = ' ';
dst++;
}
curlinewidth = wordwidth + g_WrapIndentCount * SPACE_WIDTH;
}
for (i = 0; i < wordlen; i++) {
*dst = curword[i];
dst++;
}
if (*src == ' ' || forcespace) {
if (*src >= 0x80) {
src++;
}
src++;
curlinewidth += SPACE_WIDTH;
*dst = ' ';
dst++;
}
}
}
#else
s32 curlinewidth = 0;
s32 i;
s32 wordlen;
s32 wordwidth;
s32 wordheight = 0;
bool more = true;
bool itfits;
bool keepchar;
s32 unusedwordwidth;
char curword[32];
while (more == true) {
// Load the next word
wordwidth = 0;
wordlen = 0;
unusedwordwidth = 0;
while (*src > ' ') {
curword[wordlen] = *src;
unusedwordwidth += chars[*src - 0x21].width;
src++;
wordlen++;
#if VERSION >= VERSION_PAL_FINAL
if (g_Jpn)
#endif
{
if (curword[wordlen - 1] >= 0x80) {
curword[wordlen] = *src;
unusedwordwidth += chars[*src - 0x21].width;
src++;
wordlen++;
}
}
}
curword[wordlen] = '\0';
text_measure(&wordheight, &wordwidth, curword, chars, font, 0);
curlinewidth += wordwidth;
if (curlinewidth <= wrapwidth) {
itfits = true;
} else {
itfits = false;
}
if (*src == '\n') {
// Write a new line and indent
if (!itfits) {
*dst = '\n';
dst++;
for (i = 0; i < g_WrapIndentCount; i++) {
*dst = ' ';
dst++;
}
}
curlinewidth = 0;
// Write the current word
for (i = 0; i < wordlen; i++) {
*dst = curword[i];
dst++;
}
// Write the original new line that was in src
*dst = '\n';
dst++;
} else if (*src == ' ') {
if (!itfits) {
// Write a new line and indent
*dst = '\n';
dst++;
for (i = 0; i < g_WrapIndentCount; i++) {
*dst = ' ';
dst++;
}
curlinewidth = wordwidth + g_WrapIndentCount * SPACE_WIDTH;
}
curlinewidth += SPACE_WIDTH;
// Write the current word
for (i = 0; i < wordlen; i++) {
*dst = curword[i];
dst++;
}
// Write the trailing space
*dst = ' ';
dst++;
} else if (*src == '\0') {
more = false;
if (!itfits) {
// Write a new line and indent
*dst = '\n';
dst++;
for (i = 0; i < g_WrapIndentCount; i++) {
*dst = ' ';
dst++;
}
}
// Write the current word
for (i = 0; i < wordlen; i++) {
*dst = curword[i];
dst++;
}
// Write the null terminator
*dst = '\0';
}
src++;
}
#endif
}