___\    /___
 :_)          (_:
 |              |             .
 |   +{N64}+    |             |
 |              |            _.  . thE LiGhtFoRCE - FAStER thAN LiGht .
 |         ____ |        .___)|                siNce 1987
 :  ______ \__/ :______  |    |____ ____ _________ __ ________ _____ ______
 .  \    /_____./   __/__|    |   _|    \.)    __/ _  \_     /    _/    __/_
 . /    /     |    _)    |    _   \_     |    _/   \   /    /.    \_.  _/   |
 : \__________|____\  ___|____|    |_____|____|________\_____|______|_______|
 |              :   \/ -Mo!/aL|____|[C O N S O L E   D I V I S I O N]
 |              |
 |              |       How to make a N64 Trainer Documentation by:
 |_            _|                   >>>>> Icarus <<<<<
 : )___    ___( :
      /_  _\

Ladies and gentleman,
Welcome to my "How to make a N64-Trainer" documentation.

The reason why i did this doc is very simple: i am quitting
all my activities in the "n64-scene" for many reasons, i think
you don't care for so i won't go into that any further.
Furthermore it would be nice to see more groups doing trainers.
So i hope this will help some dudes to start!

So however, let's come to the real documentation.

First i want to explain how my trainers are working.. 
I think you all know the hardware game-enhancers like
"Action Replay", "Gameshark", "Gamebuster" and so on...
with this hardware stuff you can enter codes like this e.g.:

8003567C 0067

If YES i will explain what the fuck this codes mean:
8003567C 0067 <-- Value to be written
~~~~~~~~ ****
Memorylocation in N64 RAM

If you enter such a code the Gameshark hardware or whatever
will do the following: It updates the MEMORYLOCATION with
the VALUE very often (don't know exactly how often i a second
but this doesn't matter now! :-) )

And exactly this is how my trainers work. So in a strictly
definition my trainers are no REAL trainers, like i did them
on e.g. the Amiga. On Amiga for example it was better to
find the ROUTINE in the GAMECODE which was responsible for
subtracting your Lives or Health etc.. and once you found 
this routine, you had the possibility to kill it with a NOP
or something..

On N64 you can say its emulating a Gameshark.
Unfortunately I must do it like this way but the fact is that
i just dont have this much possiblities with hard and software
like on my good ol' Amiga;) NMI-shit and stuff..

But the result is the same so... its not a clean way but it
works also.

So basically what we have to emulate a Gameshark and this is
very easy!:

1. We need a routine which is called VERY OFTEN to replace
   VALUES in N64-MEMORY while the user is playing the game.

Okay, if you still know what i am talking about, CONTINUE READING!
If you don't know what i am talking about, QUIT READING NOW:)

So first step:

We must try to find a routine in the GAMEROM which is called
very often.. here we can hook up our own UPDATE-CODE which will
do the rest (=the "trainer" itself).

Personally i use "OsViSwap" but you also can use
"WriteBackDCacheAll", or "__osRestoreInt"... i just tested my
trainers with the above mentioned three functions and it works

To find this routines get the tool "LFE" from www.dextorse.com
(LFE = [Libary-Function-Extractor] (c) Ravemax of Dextrose)

Then get the library "libultra.lib" from the PSY-Q-N64-Devkit.. 

Swap your gamerom now to Z64/CD64 format and make a backup of
the rom... now RESIZE the ROM to 8 Megabit and use LFE to find
out where the functions are in the ROM.

The commandline for this is:

LFE.EXE [ROM.Z64] libultra.lib 

This will take a while now.. - then LFE gives u back a ".COD"

Here i included such a .COD file just for example:

[============BEGIN TEST.COD==============]
0x8005e980,osWritebackDCacheAll   <--- THIS ONE MAYBE
0x80066170,__osRestoreInt         <--- THIS ONE MAYBE, TOO
[=============END TEST.COD===============]

So now search for one of the above mentioned functions.
Hmm... in this game we dont have a OSViSwap.. so we could
use the "osWritebackDCacheAll"-Function for our example.

Now take the resized 8-Mbit-ROM again and disassemble it with
N64PSX by NAGRA! (get it from www.blackbag.org or 

The commandline will look like this:

N64PSX.EXE -n64 [ROM.Z64] >ROM.ASM

This nice tool will use the .COD file created by LFE
and will mix it together with the disassembled code so you can 
exactly see where the functions begin.. my example will bring
ROM.ASM as a result.

Now, open "ROM.ASM" in your favorite editor and search for
the function we want to use ("osWritebackDCacheAll").

[============BEGIN ROM.ASM==============]
8005e974: 00000000 .... nop 
8005e978: 00000000 .... nop 
8005e97c: 00000000 .... nop 
8005e980: 3c088000 <... lui $t0,32768 ; osWritebackDCacheAll
8005e984: 240a2000 $. . li $t2,0x2000
8005e988: 010a4821 ..H! addu $t1,$t0,$t2
8005e98c: 2529fff0 %).. addiu $t1,$t1,0xfffffff0
8005e990: bd010000 .... cache 0x1,0x0000($t0)
8005e994: 0109082b ...+ sltu $at,$t0,$t1
8005e998: 1420fffd . .. bnez $at,0x8005e990
8005e99c: 25080010 %... addiu $t0,$t0,0x0010
8005e9a0: 03e00008 .... jr $ra  <--THIS IS THE END OF THE FUNCTION!
8005e9a4: 00000000 .... nop 
8005e9a8: 00000000 .... nop 
[=============END ROM.ASM===============]

Now, we must search for the end of the function, this must
be a "jr $ra". Once you found it, keep the memory-address
where it is located. (In our example its: $8005e9a0)
At this address we will hook up our trainer-routine.
Let's move to the next chapter!


Okay, here we go with the main-routine which is responsible for
updating the values in N64 RAM!

This routine is very simple

Just Imagine you got a Gameshark-Code like this:

8003567F 0064

As i said above this means for the Gameshark nothing else than:

So here a neat code which will do this for us in ASSEMBLER and
this will be the code which will be hooked to the Function we
found before, to go sure our routine will be executed every
time the function osWriteBackDCacheAll is called.

	        sub  	sp,28		;Get som Stackspace
	        sw   	s0,0(sp)	;Better save this regs
	        sw   	t0,4(sp)	;coz our routine below
	        sw   	t1,8(sp)	;will trash them!
	        sw   	t2,12(sp)	;t1 and t2 is for additional stuff

		li	t0,$8003567F    ;Memory-Address
		li	s0,$64          ;Value
		sb	s0,(t0)         ;Write it into Memory.

	        lw   	s0,0(sp)	;Okay.. lets restore the
	        lw   	t0,4(sp)	;regs..!
	        lw   	t1,8(sp)
	        lw   	t2,12(sp)
	        add  	sp,28		;Free our Stackspace!
		jr	ra              ;This will jump back
		nop                     ;to osWriteBackDCacheAll!

That's IT!


Above we just coded our trainer-routine. But now we
must attach this routine to the "osWritebackDCacheAll"-Function
we found in the gamerom in order to get the trainer working.

This is a very simple routine, too so take a look at the
following ASM code:

		org	$BXXXXXXX	;Orged in ROM see explanation

		la	t0,patchstart	;patchstart
		li	t1,$A0000200  	;This is place our Trainerroutine
					;will stay
		la	t2,patchend-patchstart+4	;Lenght to copy
		lw	t3,(t0)		;this loop will copy our
		sw	t3,(t1)		;patchroutine to $A0000200
		add	t0,t0,4		;so easy.
		add	t1,t1,4
		bne	t2,zero,loop
		sub	t2,t2,4

		li	t0,$A005E9A0    ; our osWritebackDCacheAll function!!

		li	t1,$3c088000    ; Patch the function
		sw	t1,(t0)
		li	t1,$35080200    ; Patch the function
		sw	t1,4(t0)
		li	t1,$01000008
		sw	t1,8(t0)

		li	ra,$b0000008	;This is the universial backjump
		lw	ra,(ra)		;routine.. explanation follows.
		add	ra,ra,$10
		jr	ra		;Back to the game!!

		;here is the patchroutine (see above) :)

Okay, what does this routine now?

First of all i have to say something about the "org". Before you assemble
this little loader you must search in the Gamerom for a nice unused
place. Take your favorite hex-editor and load up your gamerom again
and take a look.. - if you found freespace at e.g. $7FC000 in the
rom and want to place your loader there, you have to ORG it to $B07FC000.

Then our neat code will copy our "Trainerroutine" (patchstart) to a
safe memory-location. (its $A0000200)

After that it will patch the routine located at $A005E9A0. Yeees, you
are right.. this $A005E9A0 is our osWritebackDCacheAll, we found
some chapters ago! but be sure to replace the "8" with an "A".
(We found $8005E9A0 and we will patch $A005E9A0)!!!

Yeah, all done!, our trainerroutine is in a safe-memoryloaction where it
can operate from, the osWritebackDCacheAll points now to our
trainer-routine, so it will be called everytime the Function is called.

So we can jump back to Gamecode: I have made a nice routine, which will
do it universial, so no need to change something for every game.


So.. finally we made it! we have the Trainer. The only thing you have
to do now is to patch the ROM-Start at $1000, save the orig-code from
this location, jump to your loaderroutine ($B07FC000) and before you
jump back, execute the orig-code you saved from $1000. And OF COZ, doing
a nice trainermenu in Assembler (get Titaniks excellent sources from 
www.dextrose.com or get my trainermenu-source at dextrose.com, too)
Thats it.. get movin'!!!!!!!!!!

I hope i helped you with this document a little bit. Maybe this method
will be nonsense, if new hardware appeares, where you can set Breakpoints
and other usefull stuff.. But in this current point of time i think its
a good way to do trainers.. - not the best way... but WORKS:)

All infos, n64-sources etc can be obtained from www.paradogs.com, too.

To make trainers is an ART not a RACE! :D

Signed, Icarus of Paradox/Lightforce/Crystal/TRSi/Vision/LSD/Hellfire

AUGUST - 1999!

Greetings to all my friends in the scene.. stay cool.
To all the others... FUCK OFF!