Copy Link
Add to Bookmark
Report

First Playstation asm doc for hacking

PS_2's profile picture
Published in 
Playstation
 · 21 Jun 2019

First PSs asm doc for hacking
(Wow! ;-) )

PART I : Getting Started

In this doc, I'll explain how to start to hack your little sweet PS games, but on the asm side. I won't explain to you how to make a patch, how to translate, or how to copy PS games. Okay? I've made this doc to help people who are interested in PS hacking. If you don't like it, don't read it! If you think "this is bullshit, you're an asshole," then fuck you, and leave me alone! Okay?


Hardware needed

  • A Playstation ;-) : It's better to have the real thing, instead of an emulator.
  • A Playstation game (hum...).
  • A Pro Action Replay, flashed with Caetla.
  • A PC Comm Link : It's an ISA card for the PC, with which you can communicate between a PC and PS (connected to the AR).


Software needed

  • Windows 95 or 98, maybe ME. (never tried it)
  • AdisasM, by Antiloop : A very good R3K disassembler.
  • PSDebug : This is the main tool used. It works only with a Comm Link (not with an Xplorer), and is used to trace the code of the running game.
  • GMSCCC : Used to search code, but also used to view/download/upload/change the PS memory, while running => Very useful!
  • PsxMultiRip: Rips TIM and TMD files from inside bigger data files.
  • UltraEdit32: A very good text editor!


Where to find these tools?

GMSCCC can be found at at http://www.gscc2000.com/
PsxMultiRip can be found at http://www.loser-console.org/
UltraEdit32 can be found as http://www.ultraedit.com
and everything else is at http://dev.paradogs.com


Knowledge required

First, get the Joshua Walker's doc on PS : "Everything You Have Always Wanted to Know about the Playstation But Were Afraid to Ask.". Inside, you'll learn everything about the "beast" : memory, processor, and R3000's opcodes.
Then, a minimal programming knowledge is good.
Also, some luck! (even if it's not "knowledge" ;-) )


What will I do?

I'll show you how to find compressed data (here, a font), then, how to get the routine, and finally how to understand it. I'll take, for example, Legend Of Mana.
The method can also be used to find other things, like text, images, etc...

PART II : Let's start hacking!

Switch on the PS, connected to the PC, and launch GMSCCC. Start the game (start game with comm link), and, when you are on a screen the game's dialogs, go to "RAM EDIT", and then press the "download ram" button. Save the ram somewhere in your HD. Now, take your favorite tile editor, like naga, or tilemod, and open the saved binary (2 Mb). At this point two things should have occurred:

  • You haven't found any font, so maybe it is a TIM font. Take psx multi rip, and search inside the ram bin. If there is something, good! If not, sorry, I can't do anything for now...
  • You've found the font : Great! For LOM, it is a basic 12x16 font.

LOM's font
Pin it
LOM's font

Then, take your favorite hex editor, and find the beginning of the font, inside the ram bin. For LOM, it is at 1D1200.


What's next?

We want to find the game's decompression routine for this font. We have the location of the font in file format, but we need to convert it to a PS address. The process for doing this is very easy:

  1. First, open the ram editor inside GMSCC. PS addresses for ram are offset by 80010000, so, in order to convert the file address to a PS address, we simply have to add 80010000. In our case the font is at 1D1200, so that gives us the PS address of 801E1200.
  2. Next, while still in GMSCC, go to this address. If the data there is the same as the font in your hex editor, great! If not, hum.... too bad!
  3. Now, reset your PS, close GMSCC, and open PSDebug. Choose debug, set HB. In "address", type 801E1200 (the font's address), and select "break on write" and "break on execute". Why? Since it is the location of the uncompressed font, we want to know WHEN the game writes here.
  4. Then, select "start game with AR" on the PS. Let the game play until "PS controlling" appears in the PSDebug's title window. In Dissassembly, we should be at 800144E4.
  5. Open Memory, and go to 801E1200. Then, trace the code for about a minute and look at the memory again. If something new has appeared, and it is the same as the uncompressed font, then you've found the right routine!

It's better to have a complete routine, right? So, take AdisasM, and, under a DOS box, type "adisasm ram.bin code.asm -c", "ram.bin" is your ram bin file, and code.asm will be the output asm code. Then, search for 800144E4. We find this:

loop144e4: ; 800144E4

Kewl. Then search the beginning of the routine :

func14448: ; 80014448

Great. The end of the routine is:

jr ra 
sw t1, 0x0000(a1)

as most of the routines exit by a "jr ra".

Now, we have the decompression routine, and the uncompressed data. End of Part II, let's go to the Part III!

PART III : How To Understand This Mess!!

To understand the code, it's better to trace it with the debugger PSDebug. To save you some time, I've done it for you ;-)
I'll comment most of the main code; but not all of the routine.
The asm routine is in "decompression.asm."
HINT for tracing : When you trace, in "debug," select "Registers". You'll be able to see all the data inside the registers.

func14448: ; 80014448            ; location of the routine. 

addu t6, a0, zero
lw t4, 0x0000(t6) ; t4 is the address of the compressed data. t6 is the location where the address of the compressed data is
lw t1, 0x0000(a1) ; reads 801E1200, and puts it in t1
sltu v0, t4, a2 ; if t4 < a2 then v0 = 1
beq v0, zero, L1487c (0x8001487c) ; and check if it is the end of the decompression
lui v0, 0x8001 ; else..
addiu t7, v0, 0x0250 ; ...t7 = 80010250
addiu t2, t4, 0x0001 ; t2 = t4 + 1 : increase reading address.
sltu v0, t1, a3 ; if t1 < a3 then...

L1446c: ; 8001446C
beq v0, zero, L1487c (0x8001487c) ; ..check if it is the end of the decompression
nop
lbu a0, 0x0000(t4) ; read compressed data
nop
addiu v1, a0, 0xff10 (=-0xf0) ; only want to have the "type of compression" byte : eg, for F1, 1 is the compression byte
sltiu v0, v1, 0x0010 ; if it is bigger than 10
beq v0, zero, L14844 (0x80014844) ; branch
sll v0, v1, 0x02 ; else, thanks to the compression byte...
addu v0, v0, t7 ; ...
lw v0, 0x0000(v0) ; ...read in a address the location of the appropriate routine (very weird! Explained better after)
nop
jr v0 ; in v0, there is the address of the decompression for the byte (explained after)
nop

So, this routine reads a table to get the location of the compressed data, and then, after some checks (if it is the end, etc..), it reads a byte (8 bits) from the location. The first nibble (4 bits) of this byte tells the type of compression used to compress the data. Knowing the type, the routine then looks up in a table the location of the uncompression subroutine used for that type of compression.

Still alive? Okay, we'll try to understand how a subroutine works. Follow the steps below:

  1. First, load the file 'decompression.asm' under UltraEdit32 (very good text editor!)
  2. Look at the routine from line 27 to line 285. See anything? (A little hint : "lbu t3, 0x000")
    Still no? Okay, remember the format of the compression string, X1X2 Y1Y2 where Y1Y is the size of the uncompressed data (!!Not in EVERY sort of compression!!), and X2 is the type of the compression.
  3. Since 4 bits are used to store the type, there are 15 types of compression (not including zero). Go back to UltraEdit, and search how many 'lbu t3, 0x000' there are. You should have found 15. Now, you should be able to "arrange" the routine a bit better ;-).

Each subroutine starts with 'lbu t3, 0x000X' and ends with 'j LXXXXX'. The routines are also ordered. F1 is for the first one F2, for the second, etc..
I won't explain ALL the subroutines (I don't have the time, and you'll see it's easy to understand!).

Now, all you need a brain, and some knowledge of the R3000 opcodes.

Let's try with F1 :

lbu t3, 0x0001(t2)                  ; read after the 'F1 Y1Y2' the compressed data, and puts it into t3(the 'byte written') 
lbu v0, 0x0000(t2) ; read the size of the uncompressed data and puts it into v0
addiu t2, t2, 0x0003 ; increase the reading address by 3, because in F1, 3 bytes are used (F1 Y1Y2 and ZZ, the 'byte written', but more about it later)
addiu t4, t4, 0x0003 ; same for t4 ...
addiu t0, v0, 0x0004 ; increase the size by 4, and puts it into t0

loop144e4: ; 800144E4 ; starts the writing stuff
sb t3, 0x0000(t1) ; writes at t1 (which is where the uncompressed datas are written), t3 (the 'byte written', remember?)
addiu t0, t0, 0xffff (=-0x1) ; t0 = t0-1 a counter.
bne t0, zero, loop144e4 (0x800144e4) ; if it's not over, write again, and again, and again.... ;-)
addiu t1, t1, 0x0001 ; increase the writing address (of course!)
j L14870 (0x80014870) ; if it's over, go here to check if it is the end of the decompression.

Easy isn't it?
Let's try with F2 :

lbu t3, 0x0001(t2)                  ; read after the 'F1 Y1Y2' the compressed data, and puts it into t3 
lbu v0, 0x0000(t2) ; read the size of the uncompressed data and puts it into v0
addiu t2, t2, 0x0003 ; increase the reading address by 3
addiu t4, t4, 0x0003 ; same for t4
addiu t0, v0, 0x0002 ; the size is increased by 2
srl a0, t3, 0x04 ; shift t3 4 times, and put the result into a0
andi t3, t3, 0x000f ; we only want the first 0xF's bytes ;we have 'reversed' the bytes. Ex: ABCC became CC in a0, and B in t3

loop14518: ; 80014518
sb t3, 0x0000(t1) ; writes at t1 the 'transformed' byte (B, in our example)
sb a0, 0x0001(t1) ; writes at t1+1 the second 'transformed byte' (CC in our example)
addiu t0, t0, 0xffff (=-0x1) ; subtract the size
bne t0, zero, loop14518 (0x80014518) ; loop until this is the end of the decompression
addiu t1, t1, 0x0002 ; increase writing address

j L14870 (0x80014870)

Understood? I won't do the other routines, as they can be understood in the same way described above.

Now, you may want to build a decompressor for the data, even though the uncompressed data is in the ram.
You have 2 solutions :

  1. Try to understand the compression scheme, and write your own decompressor.
  2. Or just "translate" the R3000 asm code into C code (idea from _demo_. Thanks!)

Solution 2 is the easier method. It can be easily done for this decompression routine, since the compression is not too complex!

PART IV : Conclusion

That's the end for this tutorial.
Now you can try the methods outlined above on your own game and attempt to decompress the data.

Little hints and advice:

  1. With GMSCCC, when you change text in the ram view window, it also changes the text in the game's screen! (This doesn't work with all games...)
  2. In PSDebug, when you trace, modified data is in red. This is useful to know if the routine is the decompression routine (Do not use "trace into," but "trace over").
  3. Sometimes, the decompression routine is in the main exe (slesXXX), but it won't have the same address (800144E4 is the beginning of a routine in the ram, but in the exe, it won't be the same). And sometimes, the main routines aren't in the exe, but in different files (as for LOM).

Special Thanks

I'd like to thank the people who ever helped me (in no special order):

English Thanks:

  • Jay: Thank you forever! You've always helped me when I needed! Thank you again!
  • LordTech: Always willing to help newbies! That's great! Thank you also!!And thank you too for correcting it, and for the formatting!
  • Klarth: For rewriting this tutorial in standard english ;-) (English isn't my mothertongue), and for your work on TOP!
  • Cless: For your advices, and for TOP.
  • CzarDragon: Even if you're busy, you've always answered to me!Thanks!
  • Lavospawn: For your work on LOM, and your sources!
  • Bongo`/Stealth: For your snes asm sources, and your decompression doc!
  • _Demo_: For your TOP decompression sources!(And also for zsnes and epsxe)
  • Gabriela Robin: For your great songs ;-)

Thank you to all people who write docs in order to help newbies! (And fuck you to the others, who hide their knowledge. You know who you are!!).

French Thanks:

  • Elfe_noire: Merci d'etre là ;)
  • Meradrin: plus gland, tu meurs! Merci pour tes -trop rares- sources, et -encore plus rares- conseils!
  • Lag: Good old' friend :-)
  • Yazoo: Pour les chats, les conseils, et le partage de sources pour CC (même si il manquait le principal ;-) )
  • Kenshin: Pour ton enôôrme boulot sur UHR
  • CreatSkaori: French translation power!
  • Copernic: Heuu, ché pas :-), je joue pas à Bahamut Lagoon :-p
  • Readiosys: la hache de guerre est enterée ;-)
  • En vrac: Jesaisplus (l'ignorant), Bes, Ariakas, Bitos, les glandeurs du forum de FT (ils préfèrent que l'on les appelle les 'clowns' :-) ), les traducteurs de FT, Biglo, Djoul(y'a longtemps ke j'tai pas vu!), et à tous mes amis 'de la vie', qui ignorent ce que je fait ;-)
  • Et désolé à tous les autres que j'ai plus oublier! Désolé! (un ptit chèque, et j'arrange ça à la prochaine release ;-p)


That's All Folks!

Thank you for reading this tutorial so far!
This tutorial was written by Jean Dumoignou, alias Skeud, in half a year (I was very busy!), so sorry if there are mistakes!
It was also written listening to songs by Yoko Kanno, and X japan (this band Rocks! or Rocked... :-( )

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT