Hocus Pocus Map Format

From ModdingWiki
Jump to navigation Jump to search
Hocus Pocus Map Format
Hocus Pocus Map Format.png
Format typeMap/level
Map type2D tile-based
Layer count4
Tile size (pixels)16×16
Viewport (pixels)320×160

Hocus Pocus maps consist of 13 individual files which will be referred to using the extensions .000 to .012 for convenience (as neither filenames nor extensions are stored in HOCUS.DAT). Each map uses four layers: a background layer, two foreground layers and an "event" layer containing enemies, treasures and such. The background and foreground layers are 14400 bytes long, corresponding to a level sized 240x60 tiles. Maps are stored left-to-right, top-to-bottom. The event layer is 28800 bytes long, stored in the same way but with 2 bytes per tile.

File Formats

*.000 (8 bytes)

  0 UINT16LE iNull
  2 UINT16LE iPlayerX
  4 UINT16LE iPlayerY
  6 UINT16LE iShootDelay	The lower value, the more frequently enemies shoot projectiles (45 = standard value, 0 crashes game)

*.001 (724 bytes)

This stores animation setings for the tileset. The file contains this 4-byte header:

  0 UINT8 iBackgroundTile
  1 UINT8 iSwitchDownTile
  2 UINT8 iSwitchUpTile
  3 UINT8 iShootableTile

The header is followed by 240 entries of the following structure:

TAnimData (3 bytes):

  0 UINT8 iFirstIndex	
  1 UINT8 iLastIndex
  2 UINT8 iAnimType	0 = no animation, 1 = permanent animation, 2 = animation starts at random

*.002 (5040 bytes)

This file stores 10 entries of the following structure:

TMessageData (504 bytes):

  0 UINT16LE iPosX
  2 UINT16LE iPosY
  4 CHAR[50] cLine0
 54 CHAR[50] cLine1
104 CHAR[50] cLine2
154 CHAR[50] cLine3
204 CHAR[50] cLine4
254 CHAR[50] cLine5
304 CHAR[50] cLine6
354 CHAR[50] cLine7
404 CHAR[50] cLine8
454 CHAR[50] cLine9

*.003 (40 bytes)

This file contains data for the teleporting potions. The file stores 10 entries of the following structure:

TTeleportCoordinates (4 bytes):

  0 UINT16LE iStartOff		index into linear map layer data
  2 UINT16LE iEndOff

*.004 (506 bytes)

This file stores 23 entries of the following structure:

TSwitchCoordinates (22 bytes):

  0 UINT16LE iSwitchType	0 = remove tiles from map layer, 1 = fill with tiles from additional map layer
  2 UINT16LE iSwitchOff0	offset into background layer; $FFFF if unused
  4 UINT16LE iSwitchOff1
  6 UINT16LE iSwitchOff2
  8 UINT16LE iSwitchOff3
 10 BYTE     bDesiredTile0	this data must be in the background layer at the offset given above for the switch to be activated
 11 BYTE     bDesiredTile1
 12 BYTE     bDesiredTile2
 13 BYTE     bDesiredTile3
 14 UINT16LE iUpperLeftX
 16 UINT16LE iUpperLeftY
 18 UINT16LE iLowerRightX
 20 UINT16LE iLowerRightY

*.005 (300 bytes)

This file stores 25 entries of the following structure:

TToggleCoordinates (12 bytes):

  0 UINT16LE iRequiredKey	always 0 in the original files, but the game does check this value and behave accordingly
  2 UINT16LE iRequiredTile
  4 UINT16LE iUpperLeftX
  6 UINT16LE iUpperLeftY
  8 UINT16LE iLowerRightX
 10 UINT16LE iLowerRightY

*.006 (300 bytes)

This file stores 25 entries of the following structure:

TToggleCoordinates (12 bytes):

  0 UINT16LE iRequiredKey	0 = no key required, 1 = silver key, 2 = gold key
  2 UINT16LE iRequiredTile	tile number in backgrund layer
  4 UINT16LE iUpperLeftX
  6 UINT16LE iUpperLeftY
  8 UINT16LE iLowerRightX
 10 UINT16LE iLowerRightY

*.007 (240 bytes)

This contains 10 entries of the following structure:

 0 UINT16LE iSpriteSet	$FFFF = unused entry. Used iSpriteSet values are: 0x04-0x15, 0x17-0x1C, 0x1E-0x27.
 2 UINT16LE iHealth	if this value plus the current difficulty setting is 20 or higher, enables boss health bar and kill-on-touch.
 4 UINT16LE iProjectileHSpeed
 6 UINT16LE iProjectileVSpeed
 8 UINT16LE iProjectileXOffset	Valid for vertical projectiles
10 UINT16LE iTargetPlayer	If 1, vertical projectiles will move horizontally towards the player.
12 UINT16LE iUnknown2
14 UINT16LE iShootProjectiles	If 1, enemy will fire projectiles.
16 UINT16LE iUnknown3
18 UINT16LE iWobblyProjectiles	If 1, projectiles will have a wobbly motion.
20 UINT16LE iUnknown4		Always 0
22 UINT16LE iBehavior

Valid iBehavior values are:

  • 0: Walk around, stop when shooting
  • 1: Walk around, dash towards player, don't stop when shooting
  • 2: Fly, facing one direction
  • 3: Fly, facing towards player
  • 4: Stand still, animated, firing rapidly (1st episode boss)
  • 5: Stand still, animated, firing normally (flower)
  • 6: 3, but faster (ghost)
  • 7: Bugged
  • 8: Dragon (3rd episode boss)
  • 99: Trolodon (4th episode boss)

*.008 (8000 bytes)

This file stored data required for spawning enemies. It consists of 250 entries of the following format:

TEnemyEntry (32 bytes):

  0 UINT16LE[8] iEnemyType	enemy type, $FFFF = unused entry
 16 UINT16LE[8] iOffsets	index into info layer (info layer is an array of 16 bit values!) or $FFFF for unused slots

*.009 (14400 bytes)

Background Layer (array of 240*60 BYTE values)

$FF means blank space (backdrop is visible), other values are tileset indices

*.010 (14400 bytes)

Map Layer (array of 240*60 BYTE values)

$FF means blank space (backdrop or background layer are visible), other values are tileset indices

*.011 (14400 bytes)

Additional Map Layer (array of 240*60 BYTE values)

$FF means blank space (backdrop or background layer are visible), other values are tileset indices

This layer is almost identical to the main map layer but it is never used directly to draw the maps in the game. It contains all the blocks that will be pasted into the main map layer when a switch (type 1) or an "insert wall" trigger is activated in the game. This layer is NOT used when a switch or a trigger makes walls disappear. In those cases, the area in the main map layer is simply filled with non-tile values ($FF).

*.012 (28800 bytes)

Info/Event Layer (array of 240*60 UINT16LE values)

A value of 30000 ($7530) indicates an empty cell, other values have the following meaning:

0000	red gem		(100)
0001	grey gem	(250)
0002	goblet		(500)
0003	crown		(1000)
0004	green potion	(healing)
0005	magic crystal
0006	spikes		(hazard)
0007	zapper		(increase firepower)
0008	-- UNUSED --	(invisibility)
0009	golden potion	(jumping power)
000A	white potion	(rapid fire)
000B	-- UNUSED --
000C	silver key
000D	gold key
000E	lava		(hazard)
000F	wizard		("hint" messages)
0010	grey potion	(3 fireballs)

0011-0016	-- UNUSED --

0017-001D	teleport tags
001E-0020	-- UNUSED -- (teleport tags)

0021-0031	switches
0032-0037	-- UNUSED -- (switches)

0038-003A	trigger: insert wall
003B-0050	-- UNUSED -- (trigger: insert wall)

0051-0058	trigger: remove wall
0059-0069	-- UNUSED -- (trigger: remove wall)

006A-006E	enemies
006F-0073	-- UNUSED -- (enemies?)

0074-008C	trigger: enemies
008D-016D	-- UNUSED -- (trigger: enemies)

Tile mapping

The background and foreground layers each store one byte per tile. This is a direct index into the tileset, with the value 0xFF being used as 'no tile' (to see through to the layer below, or in the case of the background layer, to the backdrop image.)

Levels 1 and 2 use the first tileset image in the DAT file, Levels 3 and 4 use the second and levels 5 and 6 use the third tileset image. The last three levels of each episode use the same tileset, which will be the fourth tileset file for Episode 1. The same pattern is repeated for episodes 2 to 4 of the registered version, using tileset images 5 to 16. The same scheme is used for the backdrop images, which are the 4 PCX files (or 16 for the registered version) located directly before the 4 (or 16) PCX files containing the tileset images.

The mapping can be calculated using the following formula (assuming Episode ranges from 0 to 3 and Level ranges from 0 to 8):

TilesetNumber  = FirstTilesetNumber  + 4*Episode + MIN(Level/2, 3)
BackdropNumber = FirstBackdropNumber + 4*Episode + MIN(Level/2, 3)

Note: The game does not calculate the numbers linke this. It has lookup tables in the EXE that define which tileset and backdrop images to use for each level.

The tilesets are in PCX format, as a 320×200 image made up of 16×16 images. Tile 0 is at (0,0) and tile 1 is at (16,0) for example.

Level time limit

Level time limit hardcoded into "HOCUS.EXE" executable file as an array of UINT16LE. Absolute offset to the value of each level time limit can be calculated by formula (taken directly from the disassembled game code):

ArrayOffs + (EpisodeNumber*18) + (LevelNumber*2)

Note that EpisodeNumber and LevelNumber must be zero-based values (starts from 0, not from 1).

Known ArrayOffs values:

0001FAAA: shareware 1.0
0001FDA0: shareware 1.1
00020794: registered 1.0
00020A9A: registered 1.1

Note that ArrayOffs can be found by sequences of bytes 96 00 2C 01 90 01 which are represent time limit (150, 300 and 400) for the first three levels of first episode.

Elevator Tiles

There are two elevator tiles (left and right) which move up or down when the player presses up or down arrow while standing upon one of these tiles (an elevator tile always links with the respective adjacent tile, even if that tile is not an elevator tile).

The tile numbers are stored as an array of 80 SINT16LE values in the executable (at offset 0x21C26 for v1.1 registered). The first two values are the left and right elevator tiles for Episode 1 Level 1. Note that this array actually stores information for 10 levels per episode even though the final game only uses 9 levels per episode.

If the left elevator tile is -1 then the level has no elevators. In that case, the right tile is also set to -1, but the game only checks the left tile to see if the level has any elevators.

Other stuff stored in HOCUS.EXE

! Maybe this should be moved elsewhere. I'm just putting it here to share the knowledge --K1n9 Duk3 (talk) 06:58, 12 April 2016 (UTC)

As you can see from the FAT offset, all these values are for v1.1 registered.

0x1F1A4 - FAT (offset, size)

0x20680 - sound entries (16) (vocFile, pcFile, priority)

0x20A22 - pics for story (10) (picfile, x, y)
0x20A5E - pics for end1 (2)
0x20A6A - pics for end2 (2)
0x20A76 - pics for end3 (4)
0x20A8E - pics for end4 (2)
0x20A9A - par times (9 levels per episode)

0x21714 - item infos
0x21ADA - tileset numbers
0x21B2A - level numbers
0x21B7A - backdrop numbers

0x21BDE - music numbers (9 levels per episode)
0x21C26 - elevator tiles (10 levels per episode)
0x21CC6 - x coords for boss 4 (4 stages)
0x21CCE - y coords for boss 4 (4 stages)
0x21CD6 - tile offsets for boss 4 (= x + 240*y)
0x21CDE - directions for boss 4
0x21CE6 - health thresholds for boss 4
0x21CEE - y increments for super jump (25)
0x21D20 - y increments for normal jump (19)
0x21D46 - health increments for hurting player (all <= 0)


typedef struct {
	char name[36];
	Sint16 score;
	Uint8 heal;
	Uint8 firepower;
	Uint8 type;
	Uint8 _padding;
} TItemInfo;


This file format was reverse engineered by Malvineous, K1n9 Duk3, MainMemory and some clarifications were added by Hisymak. 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!)