08-25-2012, 07:11 PM
Here is a modified PCSX version: http://minus.com/lb0rni5snRrf00
It is geared towards sprite ripping. Using it is very easy. I will again use Beyond the Beyond as a typical example (just because it is one of the best jRPGs ever made!).
While Beyond the Beyond stores most graphics uncompressed 8-bit images, easy to get sheets, unfortunately *.DAT files (containing these big isometric sprites used in battle) are seems to be compressed.
So we have no choice but to reverse engineer the game. Play the game till the battle starts (save a state before it). Now hit V to view game RAM, you will see something like this:
Obviously this is the place where game code unpacks the sheet, so shift-click right mouse button inside this sheet to set a write breakpoint. Now load the saved state we made just before this battle and go into battle again. In the console window you will notice a bunch of "WB: PC=XXXXXX" messages, that werent there before:
Yay! We have found the unpacking routine and done with playing the game. Time to run IDA Pro (AFAIK, free version doesnt support MIPS). Open a game inside IDA (usually it is called like SCUS_947.02 or has an EXE extension). Do Jump -> Jump to Address and enter 0x80011DB0.
You will end in the middle of unpacking subroutine (which in our case starts at 0x80011CE0):
After decompiling it into C/C++, you will get the following code, which is a simple LZ unpacker, you have probably already seen a thousand times:
Now time for ImageMagic, which assembles all these small 256x256 battle sheets into one huge:
[MOD NOTE: Spoiler tagged the absurdly huge image for the sake of others' convenience. Try to do this for future images of similar size.]
It is geared towards sprite ripping. Using it is very easy. I will again use Beyond the Beyond as a typical example (just because it is one of the best jRPGs ever made!).
While Beyond the Beyond stores most graphics uncompressed 8-bit images, easy to get sheets, unfortunately *.DAT files (containing these big isometric sprites used in battle) are seems to be compressed.
So we have no choice but to reverse engineer the game. Play the game till the battle starts (save a state before it). Now hit V to view game RAM, you will see something like this:
Obviously this is the place where game code unpacks the sheet, so shift-click right mouse button inside this sheet to set a write breakpoint. Now load the saved state we made just before this battle and go into battle again. In the console window you will notice a bunch of "WB: PC=XXXXXX" messages, that werent there before:
Code:
WB: PC=0x80011DB0
WB: PC=0x80011DB0
WB: PC=0x80011DB0
WB: PC=0x80011D3C
WB: PC=0x80011DA4
WB: PC=0x80011D88
WB: PC=0x80011DA4
WB: PC=0x80011D88
WB: PC=0x80011D88
WB: PC=0x80011DA4
...
Yay! We have found the unpacking routine and done with playing the game. Time to run IDA Pro (AFAIK, free version doesnt support MIPS). Open a game inside IDA (usually it is called like SCUS_947.02 or has an EXE extension). Do Jump -> Jump to Address and enter 0x80011DB0.
You will end in the middle of unpacking subroutine (which in our case starts at 0x80011CE0):
Code:
TEXT:80011CE0 # =============== S U B R O U T I N E =======================================
TEXT:80011CE0
TEXT:80011CE0
TEXT:80011CE0 unpack: # CODE XREF: sub_800356D4+A4p
TEXT:80011CE0 # sub_80035A4C+40p ...
TEXT:80011CE0
TEXT:80011CE0 var_6 = -6
TEXT:80011CE0
TEXT:80011CE0 addiu $sp, -0x18
TEXT:80011CE4 move $a2, $a1
TEXT:80011CE8 lhu $a1, 0x18+var_6($sp)
TEXT:80011CEC move $v0, $zero
TEXT:80011CF0 move $v1, $a0
TEXT:80011CF4
TEXT:80011CF4 for_loop: # CODE XREF: unpack+D4j
TEXT:80011CF4 srl $t6, $v0, 1
TEXT:80011CF8
TEXT:80011CF8 for_loop2: # CODE XREF: unpack:if2j
TEXT:80011CF8 # unpack+C8j
TEXT:80011CF8 andi $v0, $t6, 0xFFFF
TEXT:80011CFC bnez $v0, if1
TEXT:80011D00 nop
TEXT:80011D04 lhu $a1, 0($a2)
TEXT:80011D08 addiu $a2, 2
TEXT:80011D0C li $v0, 0x8000
TEXT:80011D10
TEXT:80011D10 if1: # CODE XREF: unpack+1Cj
TEXT:80011D10 lhu $a3, 0($a2)
TEXT:80011D14 and $t8, $a1, $v0
TEXT:80011D18 beqz $t8, break
TEXT:80011D1C addiu $a2, 2
TEXT:80011D20 beqz $a3, return
TEXT:80011D24 andi $t1, $a3, 0x1F
TEXT:80011D28 srl $t9, $a3, 5
TEXT:80011D2C sll $t5, $t9, 1
TEXT:80011D30 subu $t3, $v1, $t5
TEXT:80011D34 lhu $t6, 0($t3)
TEXT:80011D38 andi $t4, $t1, 0xFFFF
TEXT:80011D3C sh $t6, 0($v1)
TEXT:80011D40 lhu $t7, 2($t3)
TEXT:80011D44 andi $t8, $t4, 1
TEXT:80011D48 andi $t0, $t1, 0xFFFF
TEXT:80011D4C addiu $v1, 4
TEXT:80011D50 addiu $t2, $t3, 4
TEXT:80011D54 beqz $t8, if2
TEXT:80011D58 sh $t7, -2($v1)
TEXT:80011D5C lhu $t9, 0($t2)
TEXT:80011D60 addiu $t0, $t4, -1
TEXT:80011D64 andi $t5, $t0, 0xFFFF
TEXT:80011D68 addiu $v1, 2
TEXT:80011D6C addiu $t2, 2
TEXT:80011D70 move $t0, $t5
TEXT:80011D74 sh $t9, -2($v1)
TEXT:80011D78
TEXT:80011D78 if2: # CODE XREF: unpack+74j
TEXT:80011D78 beqz $t0, for_loop2
TEXT:80011D7C srl $t6, $v0, 1
TEXT:80011D80
TEXT:80011D80 do_while: # CODE XREF: unpack+C0j
TEXT:80011D80 lhu $t6, 0($t2)
TEXT:80011D84 addiu $t0, -2
TEXT:80011D88 sh $t6, 0($v1)
TEXT:80011D8C lhu $t7, 2($t2)
TEXT:80011D90 andi $t8, $t0, 0xFFFF
TEXT:80011D94 addiu $v1, 4
TEXT:80011D98 addiu $t2, 4
TEXT:80011D9C move $t0, $t8
TEXT:80011DA0 bnez $t8, do_while
TEXT:80011DA4 sh $t7, -2($v1)
TEXT:80011DA8 b for_loop2
TEXT:80011DAC srl $t6, $v0, 1
TEXT:80011DB0 # ---------------------------------------------------------------------------
TEXT:80011DB0
TEXT:80011DB0 break: # CODE XREF: unpack+38j
TEXT:80011DB0 sh $a3, 0($v1)
TEXT:80011DB4 b for_loop
TEXT:80011DB8 addiu $v1, 2
TEXT:80011DBC # ---------------------------------------------------------------------------
TEXT:80011DBC
TEXT:80011DBC return: # CODE XREF: unpack+40j
TEXT:80011DBC subu $v0, $v1, $a0
TEXT:80011DC0 jr $ra
TEXT:80011DC4 addiu $sp, 0x18
TEXT:80011DC4 # End of function unpack
After decompiling it into C/C++, you will get the following code, which is a simple LZ unpacker, you have probably already seen a thousand times:
Code:
// Typical LZ with mask word, that says which of the following words are backrefs
static int unpack(u2 *D, u2 *S) {
u2 C, M, B, *R, *P = D;
for (B=0; ; B>>=1) {
if (!B) { // done this mask?
M = *S++;
B = 0x8000;
}
C = *S++;
if (!(M&B)) { //non backref?
*P++ = C;
continue;
}
if (!C) break; // zero backref ends decoding
R = P - (C>>5);
C &= 0x1f;
C+=2;
while (C-- > 0) *P++ = *R++;
}
return (u1*)P - (u1*)D;
Now time for ImageMagic, which assembles all these small 256x256 battle sheets into one huge:
[MOD NOTE: Spoiler tagged the absurdly huge image for the sake of others' convenience. Try to do this for future images of similar size.]