M-27: D143 null-string text guard + D144 bad-matrix-ptr fast3d guard

D143 (src/game/textrelated.c): langGet() returns NULL for a string slot the
current flow has not loaded (non-DEBUG builds; DEBUG substitutes a
placeholder). The N64 retail menu flow guarantees every front-end string is
resident before it is drawn; the PC flow does not yet, so a missing
briefing/objective/mode-select string reached textRender/textMeasure/
textWrap as NULL and faulted in the `*text` scan. Added #ifdef PORT NULL
guards at all four entry points -> degrade to "draw nothing" (blank text)
instead of crashing. User verified: front-end now navigable folder -> mode
-> difficulty -> level -> in-game.

D144 (port/fast3d/gfx_pc.cpp gfx_sp_matrix): a front-end 3D model
(MISSION COMPLETE dossier / mode-select wallets - D75 family) emits a
gSPMatrix whose pointer field was never resolved; w1 arrives as
0xFFFFFFFFFFFFFFFF, seg_addr() passes it through, and gfx_sp_matrix faults
reading addr[]. Guard: an unmapped/non-canonical addr -> substitute identity
(model draws with wrong transform, already a parked D75 cosmetic) so the
screen and its "Next" -> menu transition survive.

Verified: -level_09 unregressed (framediff 2/2 ok w/ HUD mask, no crash).
This commit is contained in:
James Dansereau committed 2026-08-30 14:47:37 -04:00
1 parent 94f677584a
commit 70793eba4f
2 files changed
+50 -2

No files matched your search

+15
View File
@@ -1039,6 +1039,21 @@ static void gfx_matrix_mul(float res[4][4], const float a[4][4], const float b[4
static void gfx_sp_matrix(uint8_t parameters, const int32_t* addr) {
float matrix[4][4];
/* D144: a front-end 3D model (MISSION COMPLETE dossier, mode-select
* wallets - the D75 family) can emit a gSPMatrix whose pointer field was
* never resolved: w1 comes through as 0xFFFF... or another non-canonical
* value, and seg_addr() then hands us a wild pointer -> AV reading addr[].
* A real N64 matrix pointer always lands in mapped memory. Rather than
* crash the menu transition, substitute identity for an unmapped source
* (the model draws with the wrong transform - already a parked D75
* cosmetic - but the screen and its "Next" -> menu path work). */
const bool addr_bad = ((uintptr_t)addr < 0x10000 ||
(uintptr_t)addr >= 0x0000800000000000ULL);
if (addr_bad) {
memset(matrix, 0, sizeof(matrix));
matrix[0][0] = matrix[1][1] = matrix[2][2] = matrix[3][3] = 1.0f;
} else
#ifndef GBI_FLOATS
// Original GBI where fixed point matrices are used
for (int i = 0; i < 4; i++) {
+35 -2
View File
@@ -420,7 +420,19 @@ Gfx *textRender(Gfx *gdl, s32 *x, s32 *y, char *text,
s32 stack3;
s32 stack4;
//s32 stack5;
#ifdef PORT
/* D143: langGet() returns NULL for a string slot the current flow has not
* loaded (non-DEBUG builds; DEBUG substitutes a placeholder). The N64
* retail flow guarantees every front-end string is resident before it is
* drawn; the PC menu flow does not yet, so a missing briefing/objective
* string reaches textRender as NULL and faults in the `*text` scan.
* Degrade to "draw nothing" like an empty string would. */
if (text == NULL) {
return gdl;
}
#endif
g_JpnTextTlutNeedsLoad = 1;
savedx = *x;
@@ -683,7 +695,13 @@ Gfx *textRenderOutlined(Gfx *gdl, s32 *x, s32 *y,
s32 savedx;
struct fontchar sp74;
s32 prevchar;
#ifdef PORT
if (text == NULL) { /* D143 - see textRender */
return gdl;
}
#endif
g_JpnTextTlutNeedsLoad = 1;
savedx = *x;
@@ -757,6 +775,12 @@ void textMeasure(s32 *textheight, s32 *textwidth, char *text, struct fontchar *f
*textheight = 0;
*textwidth = 0;
#ifdef PORT
if (text == NULL) { /* D143 - see textRender */
return;
}
#endif
if (lineheight == 0)
{
lineheight = font1['['].baseline + font1['['].height;
@@ -834,6 +858,15 @@ void textWrap(s32 wrapwidth, char *src, char *dst, struct fontchar *chars, struc
u32 stack;
char curword[32];
#ifdef PORT
if (src == NULL) { /* D143 - see textRender */
if (dst != NULL) {
dst[0] = '\0';
}
return;
}
#endif
while (more == 1) {
// Load the next word
wordwidth = 0;