+==========================================================================================+ |Dangerous Dave in the Haunted Mansion graphics format: | +==========================================================================================+ This covers the files EGAHEAD, EGALATCH and EGATILES, the header, unmasked and masked (sprite) files respectively. The EGALATCH and EGAHEAD files are stored in the executable. Dave uses EGA type graphics, in which an image is stored as 4 (or 5) 'planes' of data. (4 for unmasked graphics, blue, green, red and intensity, 5 for unmasked graphics, which also has a 'masked' plane.) In each plane 8 pixels are stored per byte, (this is why graphic sizes tend to be multiples of 8 pixels wide.) giving in total an approximately 4-bit form of storage. When uncompressed the files are usually 'raw data'; much like a bitmap without a header. The egahead file tells Dave where in the files to read from, and how much. A pixel (Or rather 8 pixels) is composed by adding all the values from the four planes for each pixel. So black is 0000, and dark blue is 1000 (Light blue is 1001) etc. In summary: GRAPHICS -> 4 PLANES -> 8 PIXEL LINES -> 0\1 BITS BGRL: COLOR 0000: Black 0001: Dark grey 0010: Red 0011: Light red 0100: Green 0101: Light green 0110: Brown 0111: Light brown (Yellow) 1000: Dark blue 1001: Light blue 1010: Purple 1011: Light purple (Pink) 1100: Turqoise 1101: Light Turquoise 1110: Grey 1111: White +------------------------------------------------------------------------------------------+ |Dave II Graphics Header ('EGAHEAD.DD2'): | +------------------------------------------------------------------------------------------+ Dangerous Dave has this file stored internally; An important note is that it can only retrieve data from 'rounded ($X0) addresses; this means that all pointers end in 0 ($00030, $00150...) and that any data not stored starting at a rounded address will be 'clipped' (Though the sprites have a way around this...) The graphic names are not required and do little. Many of the values in Dave are zero and so can be ignored, unless you want to do something fancy with your files. Notable differences from Keen are that the unmasked bitmaps and font are stored internally (And the actual FONT font is stored as a monochrome bitmap elsewhere) These start in the PATCHED executable at $12290, and continues between $12290 - $188E7. (26'200 bytes) ------------------------------------------------------------------------------- EGAHEAD STRUCTURE: HEADER MASKED ENTRIES ... .. . ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- HEADER: 0 4 Egaplansiz Size of unmasked graphics plane, in an HAUNTED.EXE file 4 4 Sprplansiz Size of EGASPRIT plane; this is blank since DD doesn't have one. 8 4 Bmpdatstart Where in the EGAHEAD the entries for unmasked graphics (Excluding font and tiles.) start. Should always be byte 64. 12 4 Sprdatstart Where in the EGAHEAD the entries for masked graphics (Sprites) start; by default this is right after the unmasked graphics. 16 2 Fontnum Number of 8x8 font entries are in the font; since font is written first in EGALATCH, this x 8 in bytes defines the offset into each plane that the unmasked bitmaps start It is always even. 18 4 Fontloc Offset in INTERNAL EGALATCH file where font data starts. By default, zero 22 12 Blank Kept blank due to redundancy. 34 2 Tilenum Number of 16x16 tiles 36 4 Egaplansiz Same as value at 0 40 2 Bmpnum Number of unmasked bitmaps 42 4 Bmploc Offset in plane where unmasked bitmap data starts, = fontnum * 8 46 2 Spritenum Number of sprite images IN TOTAL 48 4 Spriteloc Offset in sprite plane of start of sprite data. Is zero 52 12 ??? Unknown, three variables and some blanks 64 16x Unmsk Ent Unmasked sprite entries of 16 bytes each ? 128x Msk Ent Masked graphic entries of 128 bytes each. ------------------------------------------------ UNMASKED BITMAP ENTRIES: ? 2 Size h The width of the graphic divided by 8 +2 2 Size v The height of the graphic in pixels; if this cannot be divided into neat 16 byte pieces, the extra data, usually 8 bytes, is added to the size. +4 4 Loc When added to the graphic offset in the header, gives the location of the start of the graphic data in the plane. For the first graphic this is thus zero. +8 8 Name Name of the graphic, padded with nuls ------------------------------------------------ ------------------------------------------------ MASKED BITMAP ENTRIES: (SPRITES) ? 2 Size h The width of the graphic divided by 8 +2 2 Size v The height of the graphic in pixels; the same rule applies as for unmasked graphics except now we have: +4 2 Loc offset Usually 8, this is the number of bytes 'extra' that must be added to the location to reach the start of the sprite dats. This appears when a sprite is so small, usually 8x8 pixels, that it doesn't fill a multiple of 16 bytes. This will affect ALL sprites after the abberation until another one occurs to fix the shortfall. +6 2 Location Multiplying this by 16 bytes gives the location of the start of the sprite data in the EGA plane. +8 4 Hitbox ul Location of the upper-left corner of the sprite's hitbox or colision rectangle, in pixels, starting from 0,0. First two bytes are the h location and the next two are the v location. Values are the actual value multiplied by 16 +12 4 Hitbox br Same as above, for the bottom left corner. +16 12 Name The sprite name, usually includes a number and is usually only 10 bytes long. +28 2 Offs h H offset of the animation from the sprite, like in Keen 4-6 Often zero. +30 2 Offs v V offset, note that offsets, like the hitbox are the value * 16 +32 3*32 Copies Keen uses these entries for smooth movement; each 32 byte entry is a copy of the inital sprite shifted 2 pixels left. Keen automatically generates these copies, but needs the size bytes (The width is always 8 pixels wider than initial.), everything else can be blank. ------------------------------------------------ ------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------+ |Unmasked Graphics (HAUNTED.EXE): | +------------------------------------------------------------------------------------------+ This file stores the 8x8 font and unmasked bitmap images in Dave 2. These start in the PATCHED executable at $188E8, and continues between $188E8 - $2579F. (52928 bytes) The file is raw uncompressed data, as for Keen 1-3 read using the EGAHEAD ------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------+ |Unmasked Graphics (EGATILE.DD2): | +------------------------------------------------------------------------------------------+ This stores the 16x16 tiles in Dave as uncompressed raw EGA data, as for Keen 1-3 Notable however is the ega plane size; instead of the entire tileset being divided into four color planes, each TILE is instead, so the overall structure is Tile 1 blue, Tile 1 green, Tile 1 red, Tile 1 intensity, Tile 2 blue, Tile 2 green... This is because unlike Keen, where the whole tileset is loaded, in Dave only the tiles used in the level are loaded, each one seperately. ------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------+ |Sprite Graphics (S_*.DD2): | +------------------------------------------------------------------------------------------+ There are a variety of sprite files, each of which contains one 'set' of sprites. These are assigned sprite numbers depending on what sprites are actually present in the level, but the ABSOLUTE value given is assigned by the game by loading the files into memory in this order: S_DAVE; S_CHUNK1; S_CHUNK2; S_FRANK; and S_MASTER By default there are 199 sprites. Each file is of the same format, huffman compressed raw EGA data as for Keen 1-3 ------------------------------------------------------------------------------- COMPRESSED: 0 4 Sig Signature 'HUFF' 4 4 Dec size Size of decompressed file, in bytes 8 1020 Bin tree Huffman binary tree for decompression, 255x4 byte entries 1028 ? Data Huffman compressed data ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- UNCOMPRESSED: 0 x Plane 1 Raw data for the BLUE plane x x Plane 2 Raw data for the GREEN plane 2x x Plane 3 Raw data for the RED plane 3x x Plane 4 Raw data for the LIGHT plane 4x x Plane 5 Raw data for the MASK plane ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- COMPRESSION: I hope I don't need to explain Huffman compression, it's common enough. Huffman involves making a 'dictionary' of 256 entries, one for each possible 1-byte character. This is organized in the form of a 'binary tree' which is basically formed by taking the two lowest frequency entries and combining them into a new entry with thier added frequencies and repeating until all entries are reduced to one. After the dictionary is created, we can replace every character of data with the BIT representation from the tree An example is as follows: Compress the word 'HUFFMAN' 1.) Frequencies are 1: H,U,M,A,N 2: F 2.) Make a binary tree: (Here left = 0 and right = 1) 'Root node' * / \ / * / / \ * * * / \ / \ / \ F N M A H U 3.) The first letter is 'H'; this on the tree is '100' The next letter is 'U' whic is '000' The third letter is 'F' which is '11' (Notice common letters have shorter strings?) and so on. The final output in bits is '100000111111001001' This is not of course the optimum huffman tree, but that doesn't matter, ANY tree will do. As bytes the output s thus: '10000011 11110010 01000000' (We pad the end bit with nuls.) or $83 $F2 $40 We have a reduction from 7 bytes to 3, over 50%! Note that the number of node (*) is the number of different characters minus one, and that the maximum path length (3 nodes) is n, where 2^n is the SMALLEST power of two LARGER than the number of different characters (2^3 = 8, > 6) ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- NODE STRUCTURE: Since Dave is compressing data possibly containing all possible 255 characters, it will need 254 nodes, which are stored as 4-bit entries. Each node can be thought of as consisting of two 'branches' of two bytes. The second byte is either 0 or 1, and says whether that branch goes to a character (0) or another node (1) The first byte is the value of either that character or that node. In the huffman table then we can expect to see each possible character TWICE, once as a character, once as a node reference. (Oh and 256 $01s and 258 $00s) Note that the nodes are numbered from 0-253, NOT 1-254. The root node is ALWAYS node 254 (Number 253!) and should always be something like $xx $yy $00 $00 (Since 0 is the most common character it is closest to the root.) ------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------+ |Dave II Screens | +------------------------------------------------------------------------------------------+ These are the 320x200 files diisplayed at the game start. There are four of then; PROGPIC, TITLE1, TITLE2 and STARTPIC. These are Huffman compressed, the same as the sprites. The uncompressed file is .PIC format, an apparently common EGA screen dump. It is very similar to the Keen 1-3 screens, with just a few minor changes. ------------------------------------------------------------------------------- COMPRESSED: 0 4 Sig Signature 'HUFF' 4 4 Dec size Size of decompressed file, in bytes 8 1024 Egadict Huffman dictionary for decompression, 256x4 byte entries 1032 ? Data Huffman compressed data ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- UNCOMPRESSED: 0 4 Sig 'PIC' + $00; indicates a 'picture' file 4 2 Width Width of image \ 8 6 2 Height Image height 8 x Pane 1 Raw data for the BLUE plane +x x Pane 2 Raw data for the GREEN plane +2x x Pane 3 Raw data for the RED plane +3x x Pane 4 Raw data for the LIGHT plane ------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------+ |Dave II RLE-NW Image: (INTRO.DD2) | +------------------------------------------------------------------------------------------+ There is only one example of this; INTRO.DD2 displayed as the GAMER'S EDGE bitmap. It is RLE-NW compressed, a modification of the usual RLE format, and a very efficient one. The image itself contains only 4 planes of data, like an unmasked bitmap, but the color 'dark blue' is used as a transparent color, much like in GIF images. This means the picture can have only 15 colors, but is 4/5 the expected size for a masked image. The image size is fixed at 256x64 ------------------------------------------------------------------------------- COMPRESSED: 0 4 Size Size of uncompressed file. 4 ? Data RLE-NW compressed data. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- UNCOMPRESSED: 0 4 Size Size of file 4 ? Data Raw data ? 5 Sig 'MsDos' ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- COMPRESSION: RLE-NW is like RLE-W and a slight modification of standard RLE. Whereas in normal RLE there are two types of control byte, one to say 'repeat the next character x times' and the other to say 'copy the next y characters' RLE-NW however has only one. It copies everything to output until it finds the character $FE. When it does, it copies the x + 2 character x + 1 times. A summary is as follows for decompression: 1.) Get a byte -> If at end of file, stop. 2.) Is this byte $FE (254)? -> No, then copy it to file and goto 1.) -> If yes then... -> Get the next byte, this is our COUNT -> Get the next next byte, this is our CHARACTER, now goto 3.) 3.) Copy CHARACTER to file COUNT times, then goto 1.) -------------------------------------------------------------------------------