Mortal Kombat 1 Trainer

mk1_trainer.zip ( cheat offsets ) ( Mobygames )

For the DOS floppy patched UltraTech release ( mk.exe should be dated 1/3/94, some *.GRA files should be dated 12/30/93 )

The trainer is a win32 program, designed to manipulate the process memory of a DOS emulator that is running MK1.
( The DosBox Emulator must be using core=normal )

Features:
Select and Lock the Map
Select and Lock the Difficulty
Select and Lock the Match
Relax the requirments for obtaining a fight with Reptile
Moon Silhouettes always on map THE PIT
Change Reptile Hint Elapse
One Rounds Matches
Free Play Mode
AI always performs Fatalitles
Disable the Fatality Timer
Disable the Round Timer
Disable the HUD
Change Break Elapse for 2 player mode
Force The AI to always be a certain Fighter
Change fighters between rounds
Set starting health
Set Break material
God Mode
Start Round Blitz ( currently broken )

Don't select Goro or TSUNG for a human player.
Don't select a match before starting your first round.
Don't select a 'Test Your Might' match.
Once you've reached the 'Test Your Might' stage, you can lock the match and do an infinte number of breaks.

If running the trainer without admin privileges you may see a messagebox about not having the SE_DEBUG_NAME token. AFAIK the token is not needed but if things don't work then try running it as admin :-)


The "Pit Fall Always" cheat is probably the most interesting hack in this trainer. One of the fight environments is a narrow bridge over a pit of spikes. Normally an uppercut knocks a fighter off the bridge only during the finishing period. This hack allows an uppercut to knock a victim off the bridge at any time. The first fighter to land an uppercut instantly wins the current round, by way of impalement. I spent quite a lot of time squeezing this patch into the available space.

// orginal: F605CE5B1D00FF751A803DBC5B1D00037511E87BA8FFFF750AE8F1A8FFFF83F8027451
// patched: B8BC5B1D00803803751981480B01010100B818631B003B3874020464041CC60000EB51
// original: if uppercut occurs code
F605CE5B1D00FF test byte [001D5BCE],FF         // check if fatalities disabled
751A           jne  0017A218 ($+1a)            // jump out if disabled
803DBC5B1D0003 cmp  byte [001D5BBC],03         // check if map == 'the pit'
7511           jne  0017A218 ($+11)            // jump out if not 'the pit'
E87BA8FFFF     call 00174A87 ($-5785)          // set jump flag if the victim is dead
750A           jne  0017A218 ($+a)             // jump out if victim is not dead
E8F1A8FFFF     call 00174B04 ($-570f)          // returns the winning player's number of rounds won...
83F802         cmp  eax,0002                   // check if rounds won == 2
7451           je   0017A269 ($+51)            // jump if someone is going to be knocked into the pit
// patched: if uppercut occurs code
B8BC5B1D00     mov  eax,001D5BBC               // load pointer to current map id
803803         cmp  byte ptr [eax],03          // check for map "the pit"
7519           jne  ($+19)                     // jump out if map is not "the pit"
81480B01010100 or   dword ptr [eax+B],00010101 // set no finishing period, finishing period over, and round over flags
B818631B00     mov  eax,001B6318               // ptr to id for player1
3B38           cmp  edi,[eax]                  // edi is the id for the victim
7402           je   ($+2)                      // jump over next instruction if player1 is the victim
0464           add  eax,64                     // load up player2 (eax + 64)
041C           add  eax,1C                     // get player->health (eax + 1C)
C60000         mov  byte ptr [eax],00          // kill that player
EB51           jmp  ($+51)                     // goto "yes someone is falling into the pit" code

Wait, that's not going to work! What if there was a relocation? What if the user closed then re-opened the game or trainer at some point? I didn't want to do a round of fix-ups on the patch bytes, so back to the drawing board assembler. In the end not everything would fit. I had to patch the IsVictimDead function to return a pointer to the victim's hit points in the EBX register, after a check that the original value returned in EBX was not used anywhere.

// Original: IsVictimDead function ( edi is the victim Id )
A134631D00     mov  eax,[001D6334]             // health P1
8B1D38631D00   mov  ebx,[001D6338]             // damage not yet dec from health P1
3B3D18631D00   cmp  edi,[001D6318]             // check if victim is player1
740B           je   00194AA5 ($+B)
A198631D00     mov  eax,[001D6398]             // p2 health
8B1D9C631D00   mov  ebx,[001D639C]             // p2 damage
2BC3           sub  eax,ebx                    // subtract dmg from hp
7902           jns  00194AAB ($+2)
33C0           xor  eax,eax                    // dead
C3             ret                             // return hp remaining & zero flag set if dead

// Patched: IsVictimDead function ( edi is the victim Id )
// changed function so ebx points to the victims health upon return...
BBxxxxxxxx     mov  ebx,xxxxxxxx               // don't touch, ptr to P1 health
3B7BE4         cmp  edi,dword ptr [ebx-1C]     // check if P1 is the victim
7403           je   $+3
83C364         add  ebx,64                     // ptr to p2 health
8B03           mov  eax,dword ptr [ebx]
2B4304         sub  eax,dword ptr [ebx+4]      // subtract dmg from hp
7902           jns  $+2
31C0           xor  eax,eax                    // dead
C3             retn                            // return hp remaining & zero flag set if dead

The pit fall check became:

// enabled
90             nop
B8 xxxxxxxx    mov  eax, xxxxxxxx              // don't touch the [fatality setting] address
F600FF         test byte ptr ds:[eax],FF       // check if fatalities disabled
7518           jne  0017A218 ($+18)            // jump out if disabled
8078EE03       cmp  byte ptr ds:[eax-12],03    // check for map "the pit"
7512           jne  0017A218 ($+12)            // jump out if map is not "the pit"
8148F901010100 or   dword ptr [eax-7],00010101 // set no finishing period, finishing period over, and round over flags
E8 75A8FFFF    call 00174A87                   // returns pointer to victim health in ebx
C60300         mov  byte ptr ds:[ebx],0        // kill the victim
90             nop
EB51           jmp  ($+51)                     // jump if someone is going to be knocked into the pit

// disabled
90             nop
B8 xxxxxxxx    mov  eax, xxxxxxxx
F600FF         test byte ptr ds:[eax],FF       // check if fatalities disabled
7518           jne  0017A218 ($+18)            // jump out if disabled
8078EE03       cmp  byte ptr ds:[eax-12],03    // check for map "the pit"
7512           jne  0017A218 ($+12)            // jump out if map is not "the pit"
90             nop
E87BA8FFFF     call 00174A87 ($-5785)          // set jump flag if the victim is dead
750A           jne  0017A218 ($+A)             // jump out if victim is not dead
E8F1A8FFFF     call 00174B04 ($-570F)          // returns the winning player's number of rounds won...
83F802         cmp  eax,0002                   // check if rounds won == 2
7451           je   0017A269 ($+51)            // jump if someone is going to be knocked into the pit

bitpatch.com