Everybody's Golf: Hook the player portrait copy too

The first game builds its portrait like Golf 2 and Tennis, but its copy
function keeps y and h in different registers and loads the framebuffer pointer
at a different offset. Move the readbacks into a helper and add a hook for this
layout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-11 15:06:24 +02:00
1 parent 6f56798c44
commit 94d943664e
2 files changed
+24 -14

No files matched your search

+23 -14
View File
@@ -1406,32 +1406,40 @@ static int Hook_kingdomhearts_download_frame() {
return 0;
}
static int Hook_everybodys_copy_portrait() {
// Everybody's Golf 2 and Everybody's Tennis build player portraits the same way.
// copy_portrait(dst, x, y, w, h) memcpys a rect of the 512-stride 5551 framebuffer into a texture,
// one row at a time, which memcpy's replacement can't match to a framebuffer when it's a partial row.
// The caller (0x0887C060 in Golf 2, 0x0889B13C in Tennis) then sets each texel's alpha bit from the
// depth buffer, read straight from the linear mirror at 0x04288000. So both are read back here,
// whole rows at a time.
// Hooked after the GE sync at the start, where s4 = y and s1 = h, and the framebuffer pointer is
// loaded 3 instructions later in both versions.
// The Everybody's Golf games and Everybody's Tennis build player portraits the same way.
// copy_portrait(dst, x, y, w, h) memcpys a rect of the 512-stride 5551 framebuffer into a texture,
// one row at a time, which memcpy's replacement can't match to a framebuffer when it's a partial row.
// The caller then sets each texel's alpha bit from the depth buffer, read straight from the linear
// mirror at 0x04288000. So both are read back here, whole rows at a time.
// Each version is hooked after the GE sync at the start, where y and h are in registers and the
// framebuffer pointer is loaded from a global soon after.
static void EverybodysCopyPortrait(u32 y, u32 h, s32 luiOffset, s32 lwOffset) {
u32 fb_info;
if (!GetMIPSStaticAddress(fb_info, 0x0C, 0x10) || !Memory::IsValid4AlignedAddress(fb_info)) {
return 0;
if (!GetMIPSStaticAddress(fb_info, luiOffset, lwOffset) || !Memory::IsValid4AlignedAddress(fb_info)) {
return;
}
const u32 fb_address = Memory::ReadUnchecked_U32(fb_info);
const u32 y = currentMIPS->r[MIPS_REG_S4];
const u32 h = currentMIPS->r[MIPS_REG_S1];
const u32 colorAddr = fb_address + y * 0x400;
const u32 depthAddr = 0x04288000 + y * 0x400;
const u32 size = h * 0x400;
if (!Memory::IsVRAMAddress(colorAddr) || !Memory::IsValidRange(colorAddr, size) || !Memory::IsValidRange(depthAddr, size)) {
return 0;
return;
}
gpu->PerformReadbackToMemory(colorAddr, size);
NotifyMemInfo(MemBlockFlags::WRITE, colorAddr, size, "everybodys_copy_portrait");
gpu->PerformMemoryCopy(depthAddr, depthAddr, size, GPUCopyFlag::FORCE_DST_MATCH_MEM | GPUCopyFlag::DEPTH_REQUESTED);
NotifyMemInfo(MemBlockFlags::WRITE, depthAddr, size, "everybodys_copy_portrait");
}
// Golf 2 (caller 0x0887C060) and Tennis (caller 0x0889B13C).
static int Hook_everybodys_copy_portrait() {
EverybodysCopyPortrait(currentMIPS->r[MIPS_REG_S4], currentMIPS->r[MIPS_REG_S1], 0x0C, 0x10);
return 0;
}
// Golf (caller 0x088A1FEC).
static int Hook_everybodysgolf_copy_portrait() {
EverybodysCopyPortrait(currentMIPS->r[MIPS_REG_S0], currentMIPS->r[MIPS_REG_S4], 0x00, 0x08);
return 0;
}
@@ -1770,6 +1778,7 @@ static const ReplacementTableEntry entries[] = {
{ "kingdomhearts_download_frame", &Hook_kingdomhearts_download_frame, 0, REPFLAG_HOOKENTER, 0},
{ "everybodysgolf2_copy_portrait", &Hook_everybodys_copy_portrait, 0, REPFLAG_HOOKENTER, 0x50 },
{ "everybodystennis_copy_portrait", &Hook_everybodys_copy_portrait, 0, REPFLAG_HOOKENTER, 0x54 },
{ "everybodysgolf_copy_portrait", &Hook_everybodysgolf_copy_portrait, 0, REPFLAG_HOOKENTER, 0x50 },
{}
};
+1
View File
@@ -121,6 +121,7 @@ static const HardHashTableEntry hardcodedHashes[] = {
{ 0x0f1e7533a546f6a1, 228, "dl_write_bone_matrix_4", },
{ 0x0f2a1106ad84fb74, 52, "strcmp", },
{ 0x0ffa5db8396d4274, 64, "memcpy_jak", }, // CRUSH
{ 0x11624abe9542fb01, 180, "everybodysgolf_copy_portrait", }, // Everybody's Golf (EUR)
{ 0x1252e902d0b49bfb, 44, "vector_sub_q_2", },
{ 0x12df3d33a58d0298, 52, "sceVfpuMatrix3Unit", },
{ 0x12feef7b017d3431, 700, "memmove", },