XenonRecomp icon indicating copy to clipboard operation
XenonRecomp copied to clipboard

Getting Tekken Tag Tournament 2 to work with the tools

Open DennisStanistan opened this issue 9 months ago • 5 comments

Documenting my process for getting ttt2 recompiled for others to see, it might be of help for other games (i know nothing about x360 modding and it's my first time reading powerpc instructions, i come from an x86 background.) If you know what you're doing you'll get further with this project than me, feel free to use this as a reference

anyway, the analyzer tries to scan for switch tables using the following instructions (sonic unleashed uses this pattern before the tables):

PPC_INST_LIS,
PPC_INST_ADDI,
PPC_INST_RLWINM,   // SLWI alias
PPC_INST_LWZX,
PPC_INST_MTCTR,
PPC_INST_BCTR

where as in tag 2 (and probably other games) it's:

PPC_INST_LIS,
PPC_INST_RLWINM,   // SLWI alias
PPC_INST_ADDI,
PPC_INST_LWZX,
PPC_INST_MTCTR,
PPC_INST_BCTR

ADDI and RLWINM are swapped, so changing the values in absoluteSwitch to:

uint32_t absoluteSwitch[] =
    {
        PPC_INST_LIS,
        PPC_INST_RLWINM,   // (slwi alias)
        PPC_INST_ADDI,
        PPC_INST_LWZX,
        PPC_INST_MTCTR,
        PPC_INST_BCTR
    };

and adjusting the ReadTable function to properly offset the instructions from:

 auto* code = (uint32_t*)image.Find(table.base);
    ppc::Disassemble(code, table.base, insn);
    pOffset = insn.operands[1] << 16;

    ppc::Disassemble(code + 1, table.base + 4, insn);
    pOffset += insn.operands[2];

to

auto* code = (uint32_t*)image.Find(table.base);
    ppc::Disassemble(code, table.base, insn);           // lis
    pOffset = insn.operands[1] << 16;                   // Upper 16 bits

    ppc::Disassemble(code + 2, table.base + 8, insn);   // addi (skip rlwinm at +4)
    pOffset += insn.operands[2];                        // Lower 16 bits

now we're getting a proper switch table, here's an example:

[[switch]]
base = 0x822537A4
r = 11
default = 0x82253868
labels = [
    0x82253854,
    0x82253868,
    0x82253854,
    0x82253854,
    0x82253868,
    0x82253854,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253854,
    0x82253868,
    0x82253868,
    0x82253854,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253854,
    0x82253868,
    0x82253868,
    0x82253868,
    0x82253854,
    0x82253868,
    0x82253854,
    0x82253868,
    0x82253854,
    0x82253868,
    0x82253868,
    0x82253854,
]

...

looks perfect to what we see in r2:

[0x822400ec]> /x 7c0903a6 @ 0x82240000..0x82da0000
0x82253fb4 hit9_0 7c0903a6
0x82254214 hit9_1 7c0903a6
0x822545b0 hit9_2 7c0903a6
0x822550b0 hit9_3 7c0903a6
0x82255890 hit9_4 7c0903a6
0x82268f1c hit9_5 7c0903a6
0x8226c000 hit9_6 7c0903a6
...
[0x822400ec]> s hit9_0
[0x82253fb4]> pd 10
│           ;-- hit0_0:
│           ;-- hit9_0:
│           0x82253fb4      7c0903a6       mtctr r0
│           0x82253fb8      4e800420       bctr
            0x82253fbc      .dword 0x82253854 ; aav.0x82253854 # Wow!
            0x82253fc0      .dword 0x82253868 ; aav.0x82253868
            0x82253fc4      .dword 0x82253854 ; aav.0x82253854
            0x82253fc8      .dword 0x82253854 ; aav.0x82253854
            0x82253fcc      .dword 0x82253868 ; aav.0x82253868
            0x82253fd0      .dword 0x82253854 ; aav.0x82253854
            0x82253fd4      .dword 0x82253868 ; aav.0x82253868
            0x82253fd8      .dword 0x82253868 ; aav.0x82253868
[0x82253fb4]> 

Now for the config toml file, here's the bare minimum I got to make it generate the recompiled files without getting a segfault:

[main]
file_path = "Default_unpacked.xex"
out_directory_path = "ppc"
switch_table_file_path = "jump_tables.toml"

restgprlr_14_address = 0x82bafea0
savegprlr_14_address = 0x82bafe50
restfpr_14_address = 0x82bb0b3c
savefpr_14_address = 0x82bb0af0
restvmx_14_address = 0x82bb1d58
savevmx_14_address = 0x82bb1ac0
savevmx_64_address = 0x82bb1b54
restvmx_64_address = 0x82bb1dec

Getting the rest/save addresses is pretty straight forward: https://github.com/hedge-dev/XenonRecomp/tree/main?tab=readme-ov-file#register-restore--save-functions

Right now i'm stuck at trying to get it compiled without anything special, just the raw ppc files themselves as a test. i'm getting errors for: "error: use of undeclared label 'loc_82BAFE5C'" in lots of places, XenonRecomp did warn me about switch cases jumping out of functions like:

ERROR: Switch case at 822FA970 is trying to jump outside function: 822FAC64
ERROR: Switch case at 822FA970 is trying to jump outside function: 822FAC64
ERROR: Switch case at 822FA970 is trying to jump outside function: 822FAC64
ERROR: Switch case at 822FA970 is trying to jump outside function: 822FAC64
ERROR: Switch case at 822FA970 is trying to jump outside function: 822FAB58
ERROR: Switch case at 822FA970 is trying to jump outside function: 822FAC64
ERROR: Switch case at 822FA970 is trying to jump outside function: 822FAAA8
ERROR: Switch case at 822FA970 is trying to jump outside function: 822FAAC4

and also some unrecognized instructions:

Unrecognized instruction at 0x82D6E3B8: lhzu
Unrecognized instruction at 0x82D6E3BC: sthu
Unrecognized instruction at 0x82D6E3DC: lhzu
Unrecognized instruction at 0x82D6E3E0: sthu
Unrecognized instruction at 0x82D6E3FC: lhzu
Unrecognized instruction at 0x82D6E400: sthu
Unrecognized instruction at 0x82D6E41C: lhzu
Unrecognized instruction at 0x82D6E420: sthu
Unrecognized instruction at 0x82D6E43C: lhzu
Unrecognized instruction at 0x82D6E440: sthu
Unrecognized instruction at 0x82D6E45C: lhzu
Unrecognized instruction at 0x82D6E460: sthu
Unrecognized instruction at 0x82D6E47C: lhzu
Unrecognized instruction at 0x82D6E480: sthu
Unrecognized instruction at 0x82D6E49C: lhzu
Unrecognized instruction at 0x82D6E4A0: sthu

For now I gotta edit the analyzer to account for the function boundaries because I really don't wanna do it manually so i'll update here if I get anything working

DennisStanistan avatar Mar 04 '25 02:03 DennisStanistan

https://github.com/hedge-dev/XenonRecomp/issues/38#issuecomment-2698394064

matty45 avatar Mar 04 '25 17:03 matty45

Following this, I'm attempting to do the same for Silent Hill Downpour, and using the BBB_MoreInstructions branch, I'm down to a handful of unique errors when running XenonRecomp.

XenonAnalyse didn't generate the switch tables using the default pattern, so I've only got the absolute table to show up from using your guide here. I also had to modify the compression of the .xex using XeXTool for XenonAnalyse to accept it.

Unrecognized instruction at 0x823588B0: mulhd
Unrecognized instruction at 0x823AF9B8: vpkuwus128
Unrecognized instruction at 0x825256F8: frsqrte
Unrecognized instruction at 0x831AF414: vnor128

vcmpbfp128. at 8324F068 has RC bit enabled but no comparison was generated
ERROR: Switch case at 8312944C is trying to jump outside function: 8312948C

Here's my full log:

output.txt

efigr avatar Mar 04 '25 19:03 efigr

Following this, I'm attempting to do the same for Silent Hill Downpour, and using the BBB_MoreInstructions branch, I'm down to a handful of unique errors when running XenonRecomp.

XenonAnalyse didn't generate the switch tables using the default pattern, so I've only got the absolute table to show up from using your guide here. I also had to modify the compression of the .xex using XeXTool for XenonAnalyse to accept it.

Unrecognized instruction at 0x823588B0: mulhd
Unrecognized instruction at 0x823AF9B8: vpkuwus128
Unrecognized instruction at 0x825256F8: frsqrte
Unrecognized instruction at 0x831AF414: vnor128

vcmpbfp128. at 8324F068 has RC bit enabled but no comparison was generated
ERROR: Switch case at 8312944C is trying to jump outside function: 8312948C

Here's my full log:

output.txt

case PPC_INST_VNOR128:
        println("\t_mm_store_si128((__m128i*){}.u8, _mm_xor_si128(_mm_or_si128(_mm_load_si128((__m128i*){}.u8), _mm_load_si128((__m128i*){}.u8)), _mm_set1_epi32(0xFFFFFFFF)));", v(insn.operands[0]), v(insn.operands[1]), v(insn.operands[2]));
        break;

case PPC_INST_MULHD:
        println("\t{}.s64 = (int64_t({}.s32) * int64_t({}.s32)) >> 32;", r(insn.operands[0]), r(insn.operands[1]), r(insn.operands[2]));
        break;

I haven't looked into frsqrte and vpkuwus128 but i added other instructions (including the BBB ones) in my fork: https://github.com/DennisStanistan/XenonRecomp/commit/3f02123e7e773418113650bc31f8c9cc12f59b75 - use it with a grain of salt, i had to resort to LLMs that read powerpc pdfs to figure out anything after PPC_INST_MULHD in the code (including PPC_INST_MULHD itself) lol

I'm still trying to figure out function boundaries to prevent goto's to labels that are outside of function scopes but i'm having powerpc comprehension issues atm

DennisStanistan avatar Mar 05 '25 07:03 DennisStanistan

Can confirm, mulhd and vnor128 logs have disappeared with your fork.

output.txt

I did implement the others yesterday on a local branch like you have, but I think the biggest blockade right now is the jumping issue. I can't figure that one out at all sadly.

efigr avatar Mar 05 '25 09:03 efigr

I've added a bunch of function boundary checks which eliminated the ERROR: Switch case at { } is trying to jump outside function: { } I also had some problematic loc_ variables, so I just used the function boundary technique on them too, and they're also gone - meaning I can compile all of my .cpp files. Here's how mine look like for reference:

functions = [
        { address = 0x8240AF58, size = 0xD8 },
	{ address = 0x82428808, size = 0x84 },
	{ address = 0x82488D70, size = 0x10C },
	{ address = 0x827757C0, size = 0x13C },
	{ address = 0x829CC508, size = 0xDC },
	{ address = 0x829E3828, size = 0x258 },
	{ address = 0x82CF2DE8, size = 0x1A8 },
	{ address = 0x82D96F90, size = 0x120 },
	{ address = 0x82D971E8, size = 0xCC },
	{ address = 0x82DCC668, size = 0xDC },
	{ address = 0x83129430, size = 0x10C },
	{ address = 0x83081BF8, size = 0x25C },
	
	{ address = 0x830522E8, size = 0xB0 },
	{ address = 0x82D972B8, size = 0x258 },
]

invalid_instructions = [
    { data = 0x00000000, size = 4 }, # Padding
]

Now I'm getting linker errors everywhere, all pointing to stuff like this:

1>lld-link : error : undefined symbol: void __cdecl __imp__RtlInitializeCriticalSection(struct PPCContext &__restrict, unsigned char *)
1>>>> referenced by SH8_PC\x64\Debug\ppc_func_mapping.obj:(struct PPCFuncMapping *PPCFuncMappings)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.102.cpp:6369
1>>>>               SH8_PC\x64\Debug\ppc_recomp.102.obj:(__declspec(dllimport) _sub_82A66228)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.102.cpp:6404
1>>>>               SH8_PC\x64\Debug\ppc_recomp.102.obj:(__declspec(dllimport) _sub_82A66228)
1>>>> referenced 49 more times
1>
1>lld-link : error : undefined symbol: void __cdecl __imp__RtlEnterCriticalSection(struct PPCContext &__restrict, unsigned char *)
1>>>> referenced by SH8_PC\x64\Debug\ppc_func_mapping.obj:(struct PPCFuncMapping *PPCFuncMappings)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.0.cpp:1595
1>>>>               SH8_PC\x64\Debug\ppc_recomp.0.obj:(__declspec(dllimport) _sub_82300A78)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.0.cpp:20833
1>>>>               SH8_PC\x64\Debug\ppc_recomp.0.obj:(__declspec(dllimport) _sub_82308C80)
1>>>> referenced 385 more times
1>
1>lld-link : error : undefined symbol: void __cdecl __imp__RtlTryEnterCriticalSection(struct PPCContext &__restrict, unsigned char *)
1>>>> referenced by SH8_PC\x64\Debug\ppc_func_mapping.obj:(struct PPCFuncMapping *PPCFuncMappings)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.0.cpp:1586
1>>>>               SH8_PC\x64\Debug\ppc_recomp.0.obj:(__declspec(dllimport) _sub_82300A78)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.0.cpp:20824
1>>>>               SH8_PC\x64\Debug\ppc_recomp.0.obj:(__declspec(dllimport) _sub_82308C80)
1>>>> referenced 94 more times
1>
1>lld-link : error : undefined symbol: void __cdecl __imp__RtlLeaveCriticalSection(struct PPCContext &__restrict, unsigned char *)
1>>>> referenced by SH8_PC\x64\Debug\ppc_func_mapping.obj:(struct PPCFuncMapping *PPCFuncMappings)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.0.cpp:1626
1>>>>               SH8_PC\x64\Debug\ppc_recomp.0.obj:(__declspec(dllimport) _sub_82300A78)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.0.cpp:20847
1>>>>               SH8_PC\x64\Debug\ppc_recomp.0.obj:(__declspec(dllimport) _sub_82308C80)
1>>>> referenced 433 more times
1>
1>lld-link : error : undefined symbol: void __cdecl __imp__KeTlsAlloc(struct PPCContext &__restrict, unsigned char *)
1>>>> referenced by SH8_PC\x64\Debug\ppc_func_mapping.obj:(struct PPCFuncMapping *PPCFuncMappings)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.189.cpp:18253
1>>>>               SH8_PC\x64\Debug\ppc_recomp.189.obj:(__declspec(dllimport) _sub_82FEDC58)
1>>>> referenced by E:\Projects\Xenon\XenonRecomp\out\build\x64-Clang-Debug\XenonRecomp\ppc\ppc_recomp.199.cpp:4097
1>>>>               SH8_PC\x64\Debug\ppc_recomp.199.obj:(__declspec(dllimport) _sub_830845B8)
1>>>> referenced 4 more times

I think it might be related to this particular thing? - not sure how to proceed from here, tbh.

efigr avatar Mar 05 '25 17:03 efigr