Skip to content

Instantly share code, notes, and snippets.

@Pokechu22
Last active October 24, 2022 02:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pokechu22/f3c104368833ba3d6662ad7d27efb19b to your computer and use it in GitHub Desktop.
Save Pokechu22/f3c104368833ba3d6662ad7d27efb19b to your computer and use it in GitHub Desktop.
[OnFrame]
$Ultimate Codes Animal Crossing: Fix broken font
0x80010A50:dword:0x5520073E:0x5520E13E
0x80010A58:dword:0x5529E13E:0x5529073E
0x80010A74:dword:0x5520073E:0x5520E13E
0x80010A7C:dword:0x5529E13E:0x5529073E
500a0f6c
500aaee8 - right before a big 01234567
501157e8
5011f5d8
50129568
=> first 9df4 bytes match (for the second group).
500a0f6c-500aad60
5011f5d8-501293cc
dd if=GNHE5d_zelda.iso of=font_zelda.bin skip=$((0x5011f5d8)) count=$((0x9df4)) iflag=skip_bytes,count_bytes
dd if=GNHE5d_animalcrossing.iso of=font_ac.bin skip=$((0x500a0f6c)) count=$((0x9df4)) iflag=skip_bytes,count_bytes
Conclusion: the font is the same.
I also did some testing with the hardware fifo player, and a fifolog from UC Zelda renders correctly, but a fifolog from UC Animal Crossing fails.
The easiest way to experiment with these is not to try and extract the normal system data, but to run them and then record a memory dump. That's because Datel doesn't use the normal disc layout (also present with the lack of an FST for font data).
In Dolphin, launch the game, wait for it to load, and then go to the memory window and select "Dump MRAM". Then copy `Dolphin Emulator/Dump/mem1.raw` into a different location and rename it to something more useful.
~~In Ghidra, import it as raw binary (not GameCube binary), with the language set to PowerPC (Gekko/Broadway), and under options set the base address to 80000000. Open the imported file, but select no when prompted to analyze it.~~
Ghidra gamecube loader 1.1.4 supports Ghidra 10.1, and ALSO supports loading RAM dumps directly. The languages are also now built-in. Easy!
As a third neat feature, Ghidra automatically recognises and displays PNG images inline. Since the images are embedded in RAM (and in fact they're in the data that's loaded from the disc with the code, not loaded separately), Ghidra can automatically format them.
This seems to be a game bug.
80010a50: 55 20 e1 3e -> 5520073e
80010a58: 5529e13e
80010a74: 5520073e
80010a7c: 5529e13e
// 80010988
void LoadFontPage(FontPage *page,byte *data,int width,int height)
{
byte bVar1;
int iVar2;
int iVar3;
byte *pData;
byte *pbVar4;
byte *pbVar5;
int iVar6;
int iVar7;
int iVar8;
int iVar9;
iVar2 = width * height * 4;
iVar8 = iVar2 + 0x40;
/* no null check; only one item allocated (pData, not pRGBA, due to 0x40) */
pData = (byte *)malloc(iVar8);
iVar6 = 0;
/* Trying to encode the font? */
page->dataSize = iVar8;
pbVar5 = (byte *)((uint)(pData + 0x1f) & 0xffffffe0);
if (0 < height) {
do {
iVar8 = 0;
iVar7 = iVar6 + 4;
if (0 < width) {
do {
iVar3 = iVar8 / 2;
iVar9 = 4;
iVar8 = iVar8 + 4;
pbVar4 = data + iVar6 * (width / 2) + iVar3;
do {
/* This seems to be backwards? */
bVar1 = FontAlphaTable[*pbVar4 & 0xf];
*pbVar5 = FontAlphaTable[*pbVar4 >> 4];
pbVar5[1] = 0xff;
pbVar5[2] = bVar1;
pbVar5[3] = 0xff;
bVar1 = FontAlphaTable[pbVar4[1] & 0xf];
pbVar5[4] = FontAlphaTable[pbVar4[1] >> 4];
pbVar5[5] = 0xff;
pbVar5[6] = bVar1;
pbVar5[7] = 0xff;
pbVar5 = pbVar5 + 8;
iVar9 = iVar9 + -1;
pbVar4 = pbVar4 + width / 2;
} while (iVar9 != 0);
iVar3 = 4;
do {
*pbVar5 = 0xff;
pbVar5[1] = 0xff;
pbVar5[2] = 0xff;
pbVar5[3] = 0xff;
pbVar5[4] = 0xff;
pbVar5[5] = 0xff;
pbVar5[6] = 0xff;
pbVar5[7] = 0xff;
pbVar5 = pbVar5 + 8;
iVar3 = iVar3 + -1;
} while (iVar3 != 0);
} while (iVar8 < width);
}
iVar6 = iVar7;
} while (iVar7 < height);
}
DCStoreRange(pData,iVar2 + 0x40);
CreateTexture(page,pData,width,height,6);
return;
}
// 8001276c
void LoadFontPage(FontPage *page,byte *data,int width,int height)
{
byte bVar1;
int size;
byte *pData;
byte *pRGBA;
int iVar2;
int iVar3;
byte *pRGBA_tmp;
size = width * height * 4;
page->dataSize = size + 0x40;
pData = (byte *)malloc(size + 0x40);
if (pData == (byte *)0x0) {
FatalError(1,s_pData_==_NULL_80037cf8);
}
pRGBA = (byte *)malloc(size);
if (pData == (byte *)0x0) {
/* Note that they compare the wrong thing here
The compiler is also smart and optimises that to a single `or.` instruction
the first time with no `.` indicating to update the condition flags the
second time, so pData isn't even checked a second time. */
FatalError(2,s_pRGBA_==_NULL_80037d08);
}
iVar3 = 0;
pRGBA_tmp = pRGBA;
if (0 < height) {
do {
iVar2 = 0;
iVar3 = iVar3 + 1;
if (0 < width) {
do {
bVar1 = *data;
*pRGBA_tmp = 0xff;
pRGBA_tmp[1] = 0xff;
pRGBA_tmp[2] = 0xff;
pRGBA_tmp[3] = FontAlphaTable[bVar1 & 0xf];
pRGBA_tmp[4] = 0xff;
pRGBA_tmp[5] = 0xff;
pRGBA_tmp[6] = 0xff;
iVar2 = iVar2 + 2;
pRGBA_tmp[7] = FontAlphaTable[bVar1 >> 4];
data = data + 1;
pRGBA_tmp = pRGBA_tmp + 8;
} while (iVar2 < width);
}
} while (iVar3 < height);
}
EncodeRGBATexture(pRGBA,pData,width,height);
/* 6 is definitely RGBA8 (as in the bp TexImage0's format field) */
CreateTexture(page,pData,width,height,6);
Free(pRGBA,size);
return;
}
// 800122cc
void EncodeRGBATexture(byte *pRGBA,byte *pData,int width,int height)
{
int iVar1;
byte *pbVar2;
int iVar3;
byte *pbVar4;
int iVar5;
int iVar6;
int iVar7;
pbVar4 = (byte *)((uint)(pData + 0x1f) & 0xffffffe0);
iVar3 = 0;
if (0 < height) {
do {
iVar5 = 0;
iVar6 = iVar3 + 4;
if (0 < width) {
do {
iVar1 = (iVar3 * width + iVar5) * 4;
iVar7 = 4;
iVar5 = iVar5 + 4;
pbVar2 = pRGBA + iVar1 + 3;
do {
*pbVar4 = *pbVar2;
pbVar4[1] = pbVar2[-3];
pbVar4[2] = pbVar2[4];
pbVar4[3] = pbVar2[1];
pbVar4[4] = pbVar2[8];
pbVar4[5] = pbVar2[5];
pbVar4[6] = pbVar2[0xc];
pbVar4[7] = pbVar2[9];
pbVar4 = pbVar4 + 8;
iVar7 = iVar7 + -1;
pbVar2 = pbVar2 + width * 4;
} while (iVar7 != 0);
iVar7 = 4;
pbVar2 = pRGBA + iVar1 + 1;
do {
*pbVar4 = *pbVar2;
pbVar4[1] = pbVar2[1];
pbVar4[2] = pbVar2[4];
pbVar4[3] = pbVar2[5];
pbVar4[4] = pbVar2[8];
pbVar4[5] = pbVar2[9];
pbVar4[6] = pbVar2[0xc];
pbVar4[7] = pbVar2[0xd];
pbVar4 = pbVar4 + 8;
iVar7 = iVar7 + -1;
pbVar2 = pbVar2 + width * 4;
} while (iVar7 != 0);
} while (iVar5 < width);
}
iVar3 = iVar6;
} while (iVar6 < height);
}
StoreData(pData,width * height * 4 + 0x40);
return;
}
@Pokechu22
Copy link
Author

Looking through my old stuff in ghidra (which I've added here), UC animal crossing uses FontAlphaTable[*pbVar4 >> 4] followed by FontAlphaTable[*pbVar4 & 0xf], while UC Zelda uses FontAlphaTable[bVar1 & 0xf] followed by FontAlphaTable[bVar1 >> 4], meaning pairs of columns of pixels are swapped. I have no idea why this works on real hardware (assuming it actually does). For context: https://bugs.dolphin-emu.org/issues/9378

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment