[OnFrame]
$Ultimate Codes Animal Crossing: Fix broken font
0x80010A50:dword:0x5520073E:0x5520E13E
0x80010A58:dword:0x5529E13E:0x5529073E
0x80010A74:dword:0x5520073E:0x5520E13E
0x80010A7C:dword:0x5529E13E:0x5529073E
-
-
Save Pokechu22/f3c104368833ba3d6662ad7d27efb19b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Looking through my old stuff in ghidra (which I've added here), UC animal crossing uses
FontAlphaTable[*pbVar4 >> 4]
followed byFontAlphaTable[*pbVar4 & 0xf]
, while UC Zelda usesFontAlphaTable[bVar1 & 0xf]
followed byFontAlphaTable[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