Dangerous Dave Level format
Dangerous Dave stores its levels consecutively in the executable starting at offset 0x26E0A (after UNLZEXE'ing.) Each level is 1280 bytes long. There is also a small 10×7 level shown on the title screen, located at offset 0x25ea4. This small level omits the path data and padding and only contains the tiles field shown in the next section.
Each level is stored in the following structure:
|BYTE[100*10]||tiles||Tile data, left to right, top to bottom|
This block of data is used to specify a path for creatures to follow. It is an array of up to 128 X,Y coordinate pairs, with each pair being a signed 8-bit integer (so two bytes * 128 pairs == 256 bytes max.) Each value dictates the number of pixels the creature should move in both the X and Y direction. The end of the path is signified by storing 0xEA in both coordinates, and upon reaching this point the path repeats from the beginning. An example path might be
05 03 FB FD EA EA
05 03 # Move five pixels to the right and three pixels down FB FD # Move five pixels left and three pixels up, i.e. return to the original position EA EA # End of path
This would cause the creature to oscillate between two points. (Since the values are signed, 0xFF == -1, 0xFE == -2, etc.)
Since the paths consist of relative movement instructions, a path that does not return the creature to its starting point at termination will cause the creature to gradually migrate off the edge of the level, as the misalignment accumulates after each loop iteration. However this could be used for creative effect, as the creature will then reappear at the opposite edge of the level.
It appears that only one path can be defined in each level, and all creatures follow it the same (although as the path consists of relative movement instructions, the creatures can trace out the same path at different locations in the level.)
The level consists of 1000 bytes of tile data, one byte per tile arranged in a 100x10 array.
The byte values are indices into the tileset, so a byte of value 0x02 means tile number 2 should be drawn in that location (with tile number 0 being the first image in the tileset.) Tile number 0 is actually a black square, so a byte value of 0x00 is used for empty space the player can walk in.
Both the player starting point and the creatures which appear in each level are hard coded in the .exe file as follows, with (0,0) being the top-left corner of the level and (99,9) being the lower right tile. See below for where these values are stored and how to change them.
|3||3||(2,5)||Creature 1 (spiders) at (44,4) and (59,4) - each spider is ~2x2 tiles in size|
|4||4||(1,5)||Creature 2 (spiky ball) at (32, 2)|
|5||5||(2,8)||3x Creature 3 (sun) at (15,3) (33,3) (49,3)|
|6||6||(2,8)||4x Creature 4 (bones) at (10,8) (28,8) (45,2) (40,8)|
|7||7||(1,2)||4x Creature 5 (UFOs) at (5,2) (16,1) (46,2) (56,3)|
|8||8||(2,8)||3x Creature 6 at (53,5) (72,2) (84,1)|
|9||9||(6,1)||4x Creature 7 at (35, 8) (41,8) (49,8) (65,8)|
|10||10||(2,8)||4x Creature 8 at (48,5) (51,2) (65,3) (82,5)|
|8 warp||6||(71,0)||None / shares with level 6|
The "spiders" in level 3 are the first creature found in the tileset, with each subsequent level using the next creature in the tileset, so the order the creatures appear in the tileset is the same order as they appear across all the levels. Each level can have between zero and four creatures, but they will always be the same type of creature. Two different creatures cannot appear in the same level.
The level chunk in the list above is the "file" the level appears in (because some levels share the same files.)
Initial level state
The initial state for each level (such as player start position) is hard-coded in the .exe at offset 0x257E8. Each value is repeated 10 times, once for each level.
|UINT8||motionFlags||Initial flags to set for player motion, primarily to set the 'falling' flag if the start point is in the air. Valid values are 0x24 (stationary) and 0x28 (falling). This is a bit field and the other flags generally are not useful, with the possible exception of 0x01 which will start with the jetpack on. If 0x20 is omitted the game will glitch badly as it seems this flag may cause the game to scroll to that position, so without it the map does not get scrolled into position properly.|
|UINT16LE||startX||X-coordinate of player's initial start position, in pixels|
|UINT16LE||startY||Y-coordinate of player's initial start position, in pixels|
The warp zones are handled differently, and things like the player starting Y-coordinate is hard-coded for all warp zones at the following locations. The 10-entry arrays use the main level number, so entering a warp zone from level 6 will use the 6th entry in the array (which has index 5 as they start from index 0).
|.exe offset||Data type||Name||Description|
|0x1710||UINT16LE||warpZoneStartY||Y-coordinate of player's start position for ALL warp zones, in pixels|
|0x1716||UINT16LE||warpZoneMotionFlags||Player motion flags for ALL warp zones, see motionFlags as above|
|0x25862||UINT16LE||warpZoneScrollOffset||Number of tiles to scroll the map horizontally|
|UINT16LE||warpZoneStartX||X-coordinate of player's initial start position, in pixels, relative to the screen|
The warpZoneScrollOffset controls which part of the map is drawn on the screen, and the warpZoneStartX controls where the player appears on the screen, so the two must work in tandem to get the player to appear at the desired point within the level.
Since in the original game entering a warp zone involves jumping off the edge of a level, followed by a "falling down a hole" animation, by default all warp zones start with the player continuing this fall. This is why there is only one player state and motion flag for all warp zones.
At offset 0x25b66 in the .exe file the following structure is repeated 10 times, once for each level. Note that each element within the structure is further repeated four times, once for each monster within the level.
|UINT16LE||monsterEnabled||Set to 0x0001 to enable this monster, 0x0000 to omit it from the level|
|UINT16LE||pixelX||X-coordinate of each monster's start point, in pixels|
|UINT16LE||pixelY||Y-coordinate of each monster's start point, in pixels|
|UINT16LE||offset||Start the path this many points in. Not used in the original levels. When more than one monster is on the screen at the same time, this can help disguise the fact that they are all following the same path. Do not set this beyond the end of the path array or the monster will fly off the screen and possibly the game may crash. Could this be used to fit more than one path into a level? It would depend on where it loops back to at the end of the path. It should at least be possible to use it to provide an initial path followed as a one-off, before the monster then returns to looping through the main path.|
|INT16LE||calmness||Lower numbers cause more frequent firing, higher numbers less frequent. 0x7FFF is the max which all but prevents firing. Negative numbers seem to act the same as 0, firing at the maximum rate.|
|UINT16LE[4 * 5]||padding||40 bytes of padding. Although these look like an additional five properties for each level's monster, changing these values appears to have no effect. The game appears to use this area as scratch space when the .exe is in memory, writing current monster state here.|
At offset 0x2590A in the .exe there is an array of 10 UINT16LE values. These are map tile numbers of ten collectable items. The first entry in this array is 0x0002, which is the map tile of the level exit door.
Immediately following this is an array of another 10 UINT16LE values. These contain the number of points awarded when each of the 10 items are collected. The first entry in this list is 2000, which will award the player 2000 points for "collecting" the end-level door.
The initial investigation and tile data documentation was done by Levellass, with Malvineous discovering how the initial level info, tileinfo, path and monster data works. MaiZure worked out most of the creature locations. If you find this information helpful in a project you're working on, please give credit where credit is due. (A link back to this wiki would be nice too!)