Ruff'n'Tumble Map Ripping and Tile Replacement
In this tutorial, you will learn how to locate the tiles from level 1-1 of Ruff'n'Tumble, locate the map, then perform some tile replacements to alter the map.
Ruff'n'Tumble Save State used in this tutorial
Part 1: Tile Ripping
Load Maptapper and select Open from the File menu. Open the Ruff'n'Tumble_Level1-1.uss file.
The save-state was created in WinUAE by collecting a couple of objects on the first screen, destroying all the baddies, then moving to the far left to have a clean screen. A screenshot was also created and that will be used later to match the tiles.
Maptapper will open the WinUAE savestate, decompress it (if required), copy the ECS colour palette, select a default 16x16 tiles and position the tile ripper at address $0:
Start pressing the Page Down button on your keyboard. We're hunting for anything resembling graphics. Address $20800 looks interesting:
Horizontal bands through the graphics are a good indicator that you are in the wrong mode. It looks like this game uses ILBM tiles instead of ACBM. Press M to switch modes and increase the bitplane count to 5 by pressing the E key 4 times. You will start to see some tiles appear:
The tiles are not quite aligned correctly, so do some fine adjustments by pressing the Right Arrow key. Notice that in ILBM mode, the colour palette changes (and wraps) every 5 arrow key presses. This is because you are operating in 5 bitplane mode.
Once you get to address $2083a, the tile graphics look correct, but the colour palette is wrong:
Press the Copperlist button to trace the active copperlist rather than using the colour entries in the WinUAE save state. Hoorah! The tiles now appear, and don't they look awesome? Robin Levy did a magnificent job on them!
Now you need to find the first tile. Let's guess that the first completely black tile is the start of the tiles, so press Shift and Right Arrow until the tile appears in the top left corner.
The current rip width has defaulted to 320 x 256, but there may be a lot more tiles in the tileset, so increase the width and/or height that you are ripping by using the T/G and Y/H keys. It will make your life easier if you make the tile width an even multiple of 256 pixels, as this is 16 tiles across (or $10 in hexadecimal). I have chosen a rip width of 512 pixels in this case, or 32 tiles across ($20 in hex). These numbers will help you later when dealing with masks!
Part 2: Map Ripping
Now you have all the tiles, we need to locate the map. You could manually adjust settings looking for the map, but it's far easier to use the built-in tools. Click on the Map Search tab:
Now you need to load a screenshot from the game. Click the ... button and select the file Ruff'n'Tumble_Level1-1.png.
At this stage it is best to check the palette matches nicely, so click the Replace colours that do not match the tile palette button (which is mainly purple with a magic wand on it). The status bar at the top of the screen will change to purple, indicating some kind of copperlist tricks are changing the palette for that section.
Click inside the selection and drag it over the top left corner of the tree in the middle of the screen, then expand it out as shown:
Press the Match Tiles button and wait a few seconds. If everything goes to plan, Maptapper will locate the tiles, re-align your selection perfectly and display all the tile indexes:
Now you have the tile indexes for that area, press the Search for Map button to automatically search the memory dump for a map:
Now this is interesting. Maptapper has found 3 possible matches, one with a width of 25 tiles, and 2 matches with a width of 130 tiles.
When you see a particularly small width, it is often a screen buffer that contains just enough tiles to display a screen, plus a few extras around the edges. The other common possibility is that the game uses smaller tiles to make larger blocks.
Switch to the Map Ripper and select the first choice from the match list at $e5ce4. You will see that this is not the entire level:
Now try the next match at $e9a7e. This one looks much better:
Finally, check the 3rd match at $f2130. This one is almost identical to the previous one, but if you look closely, you will see that the final map has a few of the collectable items missing. The most logical explanation is that the game stores a large map in memory of each level complete with bonus objects, and a second duplicate map is altered by replacing tiles when objects are collected.
It's up to you which map you wish to use. If you want to rip the level with all objects on it, the second match will be just what you need.
Select the second map and locate the top left corner using the arrow keys. Adjust the height using the W and S keys. The map appears to be 24 tiles high:
At this point, you can save the entire level as a PNG by pressing the Save Map button and you are done. Or are you?
Part 3: Replacement tiles
Anyone familiar with the game will know that there are generators that spawn baddies. Perhaps you would like to show the deactivated generators on the map instead?
Hover the mouse over the left generator tile in the map, and notice the status bar at the bottom of the screen. It says:
Map: $EA342: (94, 14) = $8414 = tile 20 ($14)
This is showing that the mouse is over address $ea342 in memory, which is co-ordinate (94, 14). The value in memory is $8414, and when combined with the shift value (0) and mask ($3ff), the output tile is 20 (or $14 in hexadecimal).
If you switch back to the Tile Ripper tab, hover over the deactivated generator tile:
Check the status bar and notice the text:
Tile Ripper: 84 ($54)
This tells you that tile 84 (or $54 in hex) is the left tile of a deactivated generator. Tile 85 ($55 in hex) is the right tile.
Switch back to the Map Ripper tab and click in the Replacement Tile section. Type in the following:
Now hit F5 to refresh the map. Suddenly the generators will change everywhere in the level:
By hovering over sections of the map, you can find out a huge amount of information about how the levels are constructed. Solid areas appear to have $400 set. Platforms you can jump up onto have $800 set. Hills have $4000 set. $8000 is some kind of special code too. By playing around with the mask value you can gain a great deal of understanding about how the level is made.
If you want a clean level, you can paste the following in:
2,4-5,8-$c,$e=0 $56-$5d=0 $8000-$8004,$8008-$800d=0 $8414=$54 ;Generator left $8415=$55 ;Generator right
This will remove all the collectable items, replacing them with a blank tile. Enemy spawn positions will be hidden. Weapon collectables will be hidden. Dead generators will replace the existing tiles. You can alter the map quite significantly if required!
Tile Replacement Assembly Code
For those that are interested, here is the commented routine that is called when one of the special tiles is touched. This allows the game to call a routine such as adding to the score, increasing firepower or triggering baddies etc.
_db43c move.w (a1),d0 ;Read tile in memory and.w #$83ff,d0 ;Mask off useful bits beq _rts ;If tile index is 0, bail out cmp.w #$5e,d0 ;If the tile index is between 1 and $5d blt _db44c ;check for a replacement tile in the bpl _rts ;table, otherwise bail out _db44c lea _Tiles(pc),a2 ;Load tile replacement table into a2 _db450 tst.w (a2) ;Check if we're at the end and if so, beq _rts ;bail out cmp.w (a2),d0 ;See if the tile index we read matches the beq _db45e ;lookup table. If so, continue! lea (6,a2),a2 ;Skip 6 bytes ahead to the next entry bne _db450 _db45e move.w (2,a2),(a1) ;Write replacement tile index into memory move.w (4,a2),d0 ;Check for routine to call beq _db472 ;If not, skip ahead movem.l d2/a1,-(a7) ;Save registers onto the stack and call jsr (d0.w,a6) ;the routine for this tile bra _db476 ;And skip ahead _db472 movem.l d2/a1,-(a7) ;Save registers onto the stack _db476 bsr _dd6ba ;Do something movem.l (a7)+,d2/a1 ;Restore registers from the stack _rts rts ;And exit! ; Replacement tile table. (1) = Original tile index ; At $db70a on level 1-1. (2) = Replacement tile index ; (3) = Routine to call upon replacement ; ; (1) (2) (3) _Tiles dc.w $0002,$0000,$ebfa dc.w $0004,$0000,$ec12 dc.w $0005,$0000,$ec1e dc.w $0008,$0000,$ec48 dc.w $0009,$0000,$ecb8 dc.w $000b,$0000,$ee7a dc.w $000e,$0000,$ed00 dc.w $000a,$0000,$ed14 dc.w $8009,$0000,$ec92 dc.w $8008,$0000,$ec56 dc.w $8000,$0000,$eb34 dc.w $8001,$0000,$eb74 dc.w $8002,$0000,$eb54 dc.w $800a,$0000,$ebac dc.w $800b,$0000,$ebe0 dc.w $800c,$0000,$ebc6 dc.w $8003,$0000,$eb94 dc.w $8004,$0000,$eba0 dc.w $800d,$0000,$ec78 dc.w $000c,$0000,$ec62 dc.w $0056,$0000,$ed24 dc.w $0057,$0000,$ed24 dc.w $0058,$0000,$ed24 dc.w $0059,$0000,$ed24 dc.w $005a,$0000,$ed24 dc.w $005b,$0000,$ed24 dc.w $005c,$0000,$ed24 dc.w $005d,$0000,$ed24 dc.w $0000
You may enjoy these articles...
If you look inside many Amiga games, secret messages have been hidden by the programmers. Richard Aplin was the king of hiding messages in the startup-sequence file, and his Line of Fire and Final Fight startup-sequences have become legendary! The Sensible Software team were also prolific at hiding messages in their games.
A collection of technical interviews with Amiga programmers that worked on commercial software in the glory days of the Amiga (late 1980s to early 1990s!)
The Ultimate Amiga Graphics, Level and Map Ripper!
A random assortment of rants!
An explanation of how many famous Amiga games utilised sprites in weird and interesting ways