Bionic Commando

Bionic Commando

 

Software Creations
(Capcom) 1988

 

Required
items

 

1 Bionic_Commando_(1988)(Capcom)(US)[1074]

2 An Amiga or WinUAE (Im using WinUAE)

3 X-Copy or your
favorite copier.

4 Action Replay Cartridge
or
your favorite monitor.

5 ASM-One

6 StoneCracker

7 Pencil and paper

8 Blanks, if you use a real
Amiga.

 

Given  that  this
 game  was  cracked
 over  twenty  years
 ago  and the
 cracks/SPS
 originals  are
 readily available on the Internet, we are not doing a disservice to anyone by analyzing the code.   This is for
educational purposes only and at your own
risk
and volition.

 

Make a copy of the disk using X-Copy to see what we are
facing.

 

 

This looks interesting
From the error codes below, it looks like checksum errors.

 

The list of XCopy Errors from the XCopy Shrine WebSite (http://jope.fi/xcopy/index.html).



 

Booting the disk results in the game loading to the title screen and
then freezing.

 

Directory the disk
and see that it’s
Amiga DOS. Type the startup-sequence.

 

 

Load the file
boot (it’s in the C directory) and disassemble.  One thing to note is that the addresses are offsets from the end of Amiga
DOS header(e.g. LEA 74,A1 is
actually loading the start address + 20, which
is 30020+74 when the file is loaded
to
30000, into A1).



 

Check the ASCII

 

 

LEA $74,A1 points to the library name dos.library

 

 

CLR.L D0 sets the version to 0 (any version)
MOVE
$4,A6 sets the
pointer
to
Execbase

JSR –228(A6) Opens the library and returns
the library base in D0

MOVEA.L D0,56 – Saves library base pointer

MOVE.L #4E,D1 – points to file bionic

MOVE.L 56,A6 set the pointer
to
dos.library

JSR –96(A6) LoadSeg scatter loads a program into memory

MOVE.L DO,D3 Saves the segment list
pointer

MOVE.L #4E,D1
set pointer to file bionic (call
is CreateProc(name,pri,seglist,stacksize)
(D1,D2,D3,D4)
CLR.L D2 sets the priority



MOVE.L #7D0,D4
set stack size

JSR –8A(A6) CreateProc call

It closes the dos.library (JSR 19E(A6)). Let’s
disassemble bionic and
see
what’s happening.

 

 

This
is all standard open
library, allocate memory etc. The reference manuals (Amiga Machine Language book has
library listings in
t
he appendix) show the
details of the library calls. After it
allocates memory (JSR –C6(a6)), it then
stores the memory location returned
in D0 for the allocated memory in an address
e.g. B48 (move.l
d0,
B48). Search for uses of that
address as the program may load a file there. You can also
search for
the
Amiga DOS open and read
commands
(e.g.
F 4E
AE FF D6 which corresponds
to
JSR

2A(A6)).

 

 

The last address
is the most interesting as the code at 3D07A reads the file.



 

The read call
(JSR
2A(A6) has
file(D1), buffer(D2) and length(D3) as parameters) is
shown below

 

 

We could play the game and rip the
files each time they load or write a program to read each file and write
it out again. The routines are
as follows (the open file (JSR $1E(A6) is at
3D
05C)) and this
is called by a number of routines which pass the file name to be opened.

 

Load charset.dat at 3CEB0

 

Load level?.pi1 at 3CED8 where ? is the level
number. This
is derived from the
base in A0 and the offset for
the
level in d0. This
points to the address of the
file
name

 

 

The first address at 3d142(30020+D122) gives D136 which is 3d156 (30020+d136) and points to level1.pi1.

 

Load map?.dat at 3CF04 where ? is the level number. Uses the same method as level to point to the
correct level.

 

Load mansp.spr at 3CF28

 

Load bigsol.spr at 3CF48

 

Load Level345.spr
at 3CF6E

 

Load level1.spr at 3CF9(This
also loads level2.spr, level3.spr
and heli.spr
[note there are two pointers
to heli.spr] using the offset method)

 

Load panel.pi1 at 3CFD2

 

Load spic.pi1 at 3CFF2

 

Load Level2.spr
at 3D030



So we could use the
games own code to read the
files and add our code to write them straight out again to another disk. However, given it’s Amiga DOS, there’s a good
chance that a
file copy program or
rudimentary disk copier will
work.
 You can copy the files with
the Amiga DOS copy command (copy df0: df1: all). Both
the built in dcopy in the
Action Replay and the standard utility diskcopy will copy the disk
and
correct the errors. Also, you can copy the files using Directory Opus.

 

 

The left panel is the IPF and the right panel is the ADF.
 The copy is identical.

 

 

Boot the copy



 

The game boots and plays. Let’s
train the game and see if theres any
m
ore protection.
 Also, search for
any
loader related addresses (DFF024, DFF07E, BFE001, BFD100).  There doesn’t look like there are any
custom loaders.

 

Load bionic into memory
at
30000 and disassemble.

 

 

After
scanning the code down to 30268,
you’ll see a lot of moves.  From the game, the life
counter starts

at 6 and the time counter starts at 200.  So 30268 could be the
initialization of the
life counter and

302b6 could be the
initialization of the timer.
 Search for
the
t
wo address initialized



 

Disassemble just in front of the address. The JSR
CCAE call a routine which does a subtract (SBCD
subtract binary coded
decimal). The subtract can be replaced with a NOP For a trainer, the offset from the DOS
header is CCBA.

 

 

Search for the timer address

 

 

The code at 3CC9C decrements the timer. NOP out the two SBCD.B
at
3CCAE and 3CCB0.  For
a trainer, the offset for the CC8E.

 



The test for the time at 3CC9A
is the countdown of
the time during play. The
test at 3CD54 is the
routine to
add the remaining time to the player’s
score.  This can be discerned by the following. If
the
two
SBCDs are replaced
at 3CCAE the
in game timer stops
decrementing. Disassemble
back
from the
address 3cd54 by disassembling until
the d command passes off
the
top of the screen and
then hit the up cursor. Keep scrolling up and the start of the routine appears to be 3CCDE (NOTE: remember to
subtract 30020 from the address when
searching using faq)

 

 

Disassemble from
3c996

 



 

 

By comparing the two sets of code at cdee and ce0e it looks like a
level
setting.  When
you finish the first level it runs the
code at 3CE0E and sets the addresses 8d36 and ba0 to 1.
From our previous
work with
the files it is most likely that the
address at BA0 is the level point for
loading the file. The starting code

for level 1 is at
3CDF0 (offset CDD0) and searching for CDD0 reveals
the two
calls.

The first one is the setup when starting up the game. The second one is when continuing the game.
By changing the branches
to
t
his address to point to another
level
start address e.g.

CDEE the game will start at that level.
 This is done by adding a move.w #$CE,(A0) after setting A0 to point to the base load address plus $1F2 (30212 is where the word $CDD0 is held and the difference between
$30212 and $30020 is $1F2). The level offsets are at 3C99C
and are as follows

Level 2 $CDEE, Level 3 $CE0E, Level 4 $CE2E and Level 5 $CE4E.

There doesnt appear to
be any code working with the invalid
checksums when I played through to the end.



 

 

 

 

Let’s add a trainer menu. Get the trainer source from the Chuck Rock 2 trainer tutorial. Set the number
of lines to three as the start level, lives and time will be trained. Set the
lines, levelsel and levels as follows.

 

LINES=3                    
 ; COUNT OF ON/OFF
LINES

; (WITHOUT
!START GAME! OPTION)

 

LEVELSEL=3                 
 ; WHICH ON/OFF LINE REMAINS

; TO THE LEVELSELECTOR

; 0 = NO LEVELSELECTOR
LEVELS=5                   
 ; MAXIMUM NUMBER OF LEVELS

 

In the section ENDE,
add the line

 

MOVE.L A1,$80 – this moves the patch address into the TRAP #0 vector. Set the PATCHADR to
an unused piece of memory such
as 200.

 

ENDE: 
 LEA

PATCH(PC),A0

 

MOVE.L

#PATCHADR,A1

MOVE.L

$80,OLD80

/Save the old trap
#0
register

MOVE.L

A1,$80

/load our patch
into the trap #0 register

MOVE.L

PATCHENDEPATCH,D0

 



Add the following code in the patch section. The MOVE.L A(A7),A0 puts the return address in the game code in A0 which allow the position of the life counter and time counter to be calculated.

 



PATCH:

 

MOVEM.L A0/A1,-(A7)              
 /save A0 and A1 on the
stack before we start

MOVE.L A(A7),A0                       
 /Save the return address

SUB.L    #2,A0                               
 /subtract 2 from the address to get the start of the
code

MOVE.L A0,A(A7)                        
 /write the return address back to the stack

MOVE.W #$23CF,(A0)+              
 /overwrite our TRAP #0

 

LIVES:                
 CMPI.B #$01,$c0.W                    
 /Check whether the user
s
elected infinite lives
BNE
TIME                                      
 /if not then branch to the check for infinite time
MOVE.L A(A7),A0                  
       /move the
base of the bionic
code to A0

ADD.L #$CCAC,A0                      
 /add the offset for the
subtract from the life counter

MOVE.W #$4E71,(A0)                
 /NOP out the subtract

TIME:                
 CMPI.B #$01,$c1.W                
     / Check whether the user selected infinite lives

BNE LEVEL1                                  
 / if not then branch to the return
to the level set code

MOVE.L A(A7),A0

ADD.L     #$CC80,A0                   
 / add the offset for the subtract from the time counter

MOVE.L  #$4E714E71,(A0)       
 /NOP out the subtracts

 

LEVEL1:            
 CMPI.B #$01,$c2.W                    
 /Check the level
selected by the user

BEQ RETURN
LEVEL2:            
 CMPI.B #$02,$c2.W

BNE LEVEL3

MOVE.L $A(A7),A0

ADD.L #$CDEE,A0                       
 /load the JSR
address for the level

MOVE.L $A(A7),A1

ADD.L #$1f0,A1                          
 /load the location of the address part of the
JSR inst. MOVE.L A0,(A1)                           
 /patch the JSR to point to the selected level

BRA
RETURN LEVEL3:            
 CMPI.B #$03,$c2.W

BNE LEVEL4

MOVE.L $A(A7),A0

ADD.L #$CE0E,A0

MOVE.L $A(A7),A1

ADD.L #$1F0,A1

MOVE.L A0,(A1)
BRA RETURN

LEVEL4:            
 CMPI.B #$04,$c2.W BNE LEVEL5

MOVE.L $A(A7),A0

ADD.L #$CE2E,A0

MOVE.L $A(A7),A1

ADD.L #$1F0,A1

MOVE.L A0,(A1)

BRA
RETURN LEVEL5:            
 CMPI.B #$05,$c2.W



BNE RETURN
MOVE.L $A(A7),A0

ADD.L #$CE4E,A0

MOVE.L $A(A7),A1

ADD.L #$1F0,A1

MOVE.L A0,(A1)

 

 

GAME:

RETURN:          
 MOVE.L A(A7),A0

ADD.L     #$24A,A0             
         / add the offset for the
instruction that sets life counter

MOVE.W  #$0009,(A0)              
 /change 0006 to 0009 so we have 9 lives

MOVE.L A(A7),A0

ADD.L   
 #$298,A0                     
 / add offset for the instruction that sets time counter MOVE.W #$0999,(A0)               
 /change 0200 to 0999 so we have 999 in
the time
MOVE.L   OLD80,$80                 
 /restore the TRAP #0 vector

MOVEM.L (A7)+,A0/A1              
 /restore A0 and A1

RTE                                                 
 /return to the exception we caused with trap #0

OLD80              
 dc.l        0                                       
 /location for the old TRAP #0 vector

PATCHENDE:

 

In the life section, the MOVE.W #$4E71,(A0) just NOPs out the subtract.  In the time section, the
similar
command removes the two subtracts. Compile with ASM-One (v1.20 as v1.48 exe does not work in WinUAE) and crunch with
StoneCracker using the default
settings.

 

Load the file
bionic into memory at 30000.  Replace the MOVE.L A7,B5C with TRAP #0. You could also
use
JSR $200 and then RTS
back in the patch.

 

 

When the game runs the first line of the code will restored removing our TRAP instruction. The return address is changed so that the game runs the MOVE.L A7,B5C code.



0

Publication author

offline 19 years

xyzzy

0
Comments: 1Publics: 6Registration: 28-11-2004

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
morpa
7 years ago

Just a note that the link for the download doesn’t seem to work anymore.

0
-TCB!-
-TCB!-
11 years ago

Back in the day, that protection would have been enough to stop 97% of the non-creative Amiga users that went to bed and woke up with the "X-Copy Boooooing!".

0
aLpHa oNe
11 years ago

This is really a strange "protection". 😉

0
WayneK
11 years ago

So the original has loads of checksum errors to make it "uncopyable", but a file copy of the disk gives you a working game?! *facepalm*
Nice training tho, and good to see a tutorial on Flashtro again – it’s been a while since the last one!

0
Authorization
*
*

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Registration
*
*
*

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Password generation

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

0
Would love your thoughts, please comment.x
()
x