rpcs3 icon indicating copy to clipboard operation
rpcs3 copied to clipboard

[TESTERS NEEDED] cellAtracXdec implementation

Open capriots opened this issue 1 year ago • 2 comments

  • Full implementation of cellAtracXdec using FFmpeg
  • One limitation: FFmpeg doesn't provide detailed errors on invalid input, so I'm just using a generic error code if decoding fails
  • Downmixing is completely broken on LLE because the PPU thread sends a wrong command to the SPU thread, so I didn't bother implementing this
  • There are no memory allocations aside from FFmpeg, I'm using the buffer provided by the game. FFmpeg is also setup to read from and write to the same memory locations the SPU thread would.
  • Savestate compatible
  • I set it to HLE by default, but if any issue is found in testing that I am unable to fix then I will revert this for now

Known issues

  • #13168 is heavily amplified. With HLE, music stops playing within seconds of reaching the main menu. The issue is caused by the game sending invalid data to the decoder. The thread "bnusCoreDecoderAT3PThread" then just exits when it receives the error. I assume this is because of a race condition in the game. I tried slowing down the decoder thread to match realhw as closely as possible, but that didn't improve anything. I was able to trigger this issue in Tales of Xillia 1+2 and Tekken 6 as well, it's likely other NU engine games are also affected. Setting PPU Thread Count to 1 still works around the issue with HLE.

capriots avatar Apr 30 '24 20:04 capriots

Haven't even attempted to look at the assembly yet...

If you are going to do that, you'll want to replace the contents of Ghidra/Processors/PowerPC/data/languages/ppc_64_32.cspec with this, otherwise some of the decompilation will be broken:

ppc_64_32.cspec

<?xml version="1.0" encoding="UTF-8"?>
<!-- This cspec describes the 32-bit ABI for PowerPC as it is implemented for 64-bit code.
     Presumably this ABI allows binary compatibility of 64-bit code with existing 32-bit code.
     The ABI assumes 32-bit registers and addresses, in particular the maximum sized integer value
     that can be passed in a single register is 4 bytes (even though the register is 8 bytes long).
     The cspec currently has a limited ability to model this: the maxsize attribute must still be
     set to 8 for parameter passing registers r3 - r10.
-->
<compiler_spec>
  <global>
    <range space="ram"/>
  </global>
  <data_organization>
	<pointer_size value="4"/>
  </data_organization>
  <aggressivetrim signext="true"/>  <!-- Pointers are 4-bytes but are held in 8-byte registers -->
  <stackpointer register="r1" space="ram"/>
  <default_proto>
    <prototype name="__stdcall" extrapop="0" stackshift="0">
      <input>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f1"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f2"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f3"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f4"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f5"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f6"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f7"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f8"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f9"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f10"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f11"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f12"/>
        </pentry>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f13"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r3"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r4"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r5"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r6"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r7"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r8"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r9"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r10"/>
        </pentry>
        <pentry minsize="1" maxsize="500" align="8">
          <addr offset="112" space="stack"/>
        </pentry>
      </input>
      <output>
        <pentry minsize="1" maxsize="8" metatype="float" extension="float">
          <register name="f1"/>
        </pentry>
        <pentry minsize="1" maxsize="8" extension="sign">
          <register name="r3"/>
        </pentry>
      </output>
      <unaffected>
        <register name="r14"/>
        <register name="r15"/>
        <register name="r16"/>
        <register name="r17"/>
        <register name="r18"/>
        <register name="r19"/>
        <register name="r20"/>
        <register name="r21"/>
        <register name="r22"/>
        <register name="r23"/>
        <register name="r24"/>
        <register name="r25"/>
        <register name="r26"/>
        <register name="r27"/>
        <register name="r28"/>
        <register name="r29"/>
        <register name="r30"/>
        <register name="r31"/>
        <register name="r1"/>
        <register name="r2"/>
        <register name="r2Save"/>
        <register name="cr4"/>
      </unaffected>
      <pcode inject="uponreturn">
      	<body>
      		# Inject pcode when returning from a function call to place the r2Save
      		# value into 0x28(r1) which should be restored by the "ld r2,0x28(r1)" 
      		# which immediately follows calls which comply with the PPC64 ABI spec.
      		local saveR2ptr = r1 + 0x28;
      		*:8 saveR2ptr = r2Save; 
      	</body>
      </pcode>
    </prototype>
  </default_proto>

  <callfixup name="get_pc_thunk_lr">
    <pcode>
      <body><![CDATA[
      LR = inst_dest + 4;
      ]]></body>
    </pcode>
  </callfixup>
  <deadcodedelay space="stack" delay="17"/>
</compiler_spec>

This fixes stack arguments, Ghidra getting confused about the TOC pointer and some reads and writes to the stack just completely missing from the decompilation.

Also, you can import my function signatures, variable names, data types, etc. if you want (unfortunately it won't import everything without including the actual binary): libatxdec.prx.zip After importing your libatxdec.prx and before running any script or analysis: go to File -> Add To Program... then navigate to the xml file inside the zip.

capriots avatar Jun 11 '24 18:06 capriots

Just letting you know that I haven't forgotten this. I simply didn't have the time to sit down and look at the lib disasm yet. Otherwise it looks fine I guess.

Megamouse avatar Jul 19 '24 20:07 Megamouse