https://moddingwiki.shikadi.net/w/api.php?action=feedcontributions&user=Lethal+guitar&feedformat=atomModdingWiki - User contributions [en-gb]2024-03-29T12:54:49ZUser contributionsMediaWiki 1.35.1https://moddingwiki.shikadi.net/w/index.php?title=Raptor&diff=11349Raptor2023-10-08T07:51:01Z<p>Lethal guitar: Add link to source code release</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Game Infobox<br />
| Levels = No<br />
| Tiles = No<br />
| Sprites = No<br />
| Fullscreen = Edit<br />
| Sound = No<br />
| Music = Edit<br />
| Text = Edit<br />
| Story = No<br />
| Interface = No<br />
}}<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=game}}<br />
{{FileFormatTool<br />
| Name = [[Camoto]]<br />
| Platform = Linux/Windows<br />
| grp = Edit<br />
| map = No<br />
| gfx = Some<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Camoto Online#gamearchive.js|Camoto/gamearchive.js]]<br />
| Platform = Any<br />
| grp = Edit<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
== Related links ==<br />
<br />
* Raptor's creators have [http://mking.com/raptor_game.html ported it to other platforms] including iPhone and Windows<br />
* The [https://github.com/skynettx/dosraptor original source code] of the DOS version has been released<br />
<br />
{{BeginGameFileList}}<br />
{{GameFile<br />
| Name = *.glb<br />
| Format = [[GLB Format (Raptor)]]<br />
| KnownFormat = Yes<br />
| Desc = Archive file storing most of the game data files<br />
}}<br />
{{GameFile<br />
| Name = *.ini<br />
| Format = ASCII text<br />
| KnownFormat = Yes<br />
| Desc = Configuration file storing game settings<br />
}}<br />
{{GameFile<br />
| Name = *_agx<br />
| Format = [[Raptor Animated Graphics]]<br />
| KnownFormat = Yes<br />
| Desc = Files used for Raptor fullscreen cutscenes.<br />
}}<br />
{{GameFile<br />
| Name = genmidi_op2<br />
| Format = [[OP2 Bank Format]]<br />
| KnownFormat = Yes<br />
| Desc = [[OPL]] FM patches for MIDI instruments<br />
}}<br />
{{GameFile<br />
| Name = lastscr?_txt<br />
| Format = [[B800 Text]]<br />
| KnownFormat = Yes<br />
| Desc = Text-mode exit screens<br />
}}<br />
{{GameFile<br />
| Name = *_dat<br />
| Format = [[VGA Palette]]<br />
| KnownFormat = Yes<br />
| Desc = 6-bit VGA palette<br />
}}<br />
{{GameFile<br />
| Name = *_fx<br />
| Format = 8-bit PCM, unknown header<br />
| KnownFormat = Partial<br />
| Desc = Sound effects<br />
}}<br />
{{GameFile<br />
| Name = *_itm<br />
| Format = ?<br />
| KnownFormat = No<br />
| Desc = Unknown<br />
}}<br />
{{GameFile<br />
| Name = *_map<br />
| Format = [[Raptor Level Format]]<br />
| KnownFormat = No<br />
| Desc = Game levels<br />
}}<br />
{{GameFile<br />
| Name = *_mus<br />
| Format = [[MUS Format]]<br />
| KnownFormat = Yes<br />
| Desc = Background music<br />
}}<br />
{{GameFile<br />
| Name = *_pic, *_blk<br />
| Format = [[Raptor PIC Format]]<br />
| KnownFormat = Yes<br />
| Desc = Various images and full-screen graphics<br />
}}<br />
{{GameFile<br />
| Name = *_rec<br />
| Format = [[Raptor Demo Format]]<br />
| KnownFormat = No<br />
| Desc = Pre-recorded demo macros<br />
}}<br />
{{GameFile<br />
| Name = startg?tiles .. endg?tiles<br />
| Format = [[Raptor Tileset Format]]<br />
| KnownFormat = Yes<br />
| Desc = Tiles used to draw game levels<br />
}}<br />
{{GameFile<br />
| Name = *_txt<br />
| Format = ASCII text<br />
| KnownFormat = Yes<br />
| Desc = Text for various game screens<br />
}}<br />
{{EndGameFileList}}<br />
<br />
[[Category:Apogee]]<br />
[[Category:Cygnus]]<br />
[[Category:Overhead]]<br />
[[Category:Space Invaders]]</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11040Major Stryker Level Format2023-05-14T19:22:32Z<p>Lethal guitar: Add info on tile animation</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated (see below)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile animation ==<br />
<br />
Animated tiles display as a loop of 4 different images, consisting of the tile in question plus the 3 tiles following it in the tileset. For example, let's say that tile index 50 has its animated flag set in the attributes array. At locations where that tile is placed, the game actually displays a sequence of indices 50, 51, 52, 53, repeated.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers" (see below).<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Modifiers act on the actor that appears right before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Formations of multiple ships are spawned via a combination of at least two actors: First, a formation type describes the number and position of ships and their movement. After that, the next actor describes the type of ships to use in the formation. This pair of actors may be followed by additional modifiers. It seems that some (all?) formations can also appear without a "ship selector", in which case they will default to a certain ship type (varies for each formation).<br />
<br />
Actors can be "foreground" (same plane as the player) or "background" (behind the player, no collision). Which actor appears on which layer is hardcoded based on the actor ID.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
=== Foreground actors ===<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 16 || Shield ship (shoot to reveal a shield pickup)<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 34 || Formation - Flight of 10, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation - Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation. TODO describe<br />
|-<br />
| 37 || Formation - Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation - Like 34?<br />
|-<br />
| 39 || Formation - Diagonal of 3 large ships, which split up into 4 smaller ones each.<br />
|-<br />
| 40 || Formation - Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 41 || Formation - Flight of 10 in from lower left - split up/down around an oval of space, then back together to leave on the right in a line. Seems to be the same for 42 and possibly onwards? There could well be duplicate formations given the sheer number of them.<br />
|-<br />
| 91 || Pickup M<br />
|-<br />
| 93 || Pickup S<br />
|-<br />
| 94 || Pickup R<br />
|-<br />
| 96 || Trigger - End of level<br />
|-<br />
| 97 || Modifier - unknown<br />
|-<br />
| 116 || Single small white ship, flying towards player. Same as the one spawned using ship selector 20 in a formation.<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 122-124 || Trigger - Adjust flying speed (used in speed levels like E1L2, 122 = normal speed)<br />
|-<br />
| 133 || Trigger - Turns on forward lights (in dark parts of levels)<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || Formation ship selector - 1x1 white space ship (same as 116)<br />
|-<br />
| 208 || Formation ship selector - 1x1 pair of red & blue spheres<br />
|-<br />
| 238 || Prisoner capsule<br />
|-<br />
| 258 || Modifier - Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Modifier - Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
=== Background actors ===<br />
<br />
'''Note:''' background actors have X coordinates greater than 16 in the level files. To make them show up correctly, their X coordinate needs to be adjusted by -16 (the game does that when loading the actors).<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
|10 || Stationary green tank<br />
|-<br />
|11 || ???<br />
|-<br />
|12 || Stationary green tank, animated<br />
|-<br />
|13 || ???<br />
|-<br />
|22 || Stationary red tank<br />
|-<br />
|23 || ???<br />
|-<br />
|24 || Stationary red/blue tank/turret<br />
|-<br />
|25 || ???<br />
|-<br />
|211 || Blue spaceship flying in from below<br />
|-<br />
|212 || Blue spaceship flying in from above<br />
|-<br />
|213 || Same as 211 (TODO: Verify there's really no difference)<br />
|-<br />
|214 || White-blue spaceship flying in from above<br />
|-<br />
|223 || Red spaceship flying in from below<br />
|-<br />
|224 || Red spaceship flying in from above<br />
|-<br />
|225 || Green spaceship flying in from below<br />
|-<br />
|226 || Green spaceship flying in from above<br />
|-<br />
|232 || ???<br />
|-<br />
|237 || ???<br />
|}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk, and [[User:Timixretroplays|Timixretroplays]] are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11023Major Stryker Level Format2023-05-08T14:57:21Z<p>Lethal guitar: Remove 270 since it's not actually used and ignored by the code</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers" (see below).<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Modifiers act on the actor that appears right before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Formations of multiple ships are spawned via a combination of at least two actors: First, a formation type describes the number and position of ships and their movement. After that, the next actor describes the type of ships to use in the formation. This pair of actors may be followed by additional modifiers. It seems that some (all?) formations can also appear without a "ship selector", in which case they will default to a certain ship type (varies for each formation).<br />
<br />
Actors can be "foreground" (same plane as the player) or "background" (behind the player, no collision). Which actor appears on which layer is hardcoded based on the actor ID.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
=== Foreground actors ===<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 16 || Shield ship (shoot to reveal a shield pickup)<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 34 || Formation - Flight of 10, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation - Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation. TODO describe<br />
|-<br />
| 37 || Formation - Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation - Like 34?<br />
|-<br />
| 39 || Formation - Diagonal of 3 large ships, which split up into 4 smaller ones each.<br />
|-<br />
| 40 || Formation - Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 41 || Formation - Flight of 10 in from lower left - split up/down around an oval of space, then back together to leave on the right in a line. Seems to be the same for 42 and possibly onwards? There could well be duplicate formations given the sheer number of them.<br />
|-<br />
| 91 || Pickup M<br />
|-<br />
| 93 || Pickup S<br />
|-<br />
| 94 || Pickup R<br />
|-<br />
| 96 || Trigger - End of level<br />
|-<br />
| 97 || Modifier - unknown<br />
|-<br />
| 116 || Single small white ship, flying towards player. Same as the one spawned using ship selector 20 in a formation.<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 122-124 || Trigger - Adjust flying speed (used in speed levels like E1L2, 122 = normal speed)<br />
|-<br />
| 133 || Trigger - Turns on forward lights (in dark parts of levels)<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || Formation ship selector - 1x1 white space ship (same as 116)<br />
|-<br />
| 208 || Formation ship selector - 1x1 pair of red & blue spheres<br />
|-<br />
| 238 || Prisoner capsule<br />
|-<br />
| 258 || Modifier - Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Modifier - Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
=== Background actors ===<br />
<br />
'''Note:''' background actors have X coordinates greater than 16 in the level files. To make them show up correctly, their X coordinate needs to be adjusted by -16 (the game does that when loading the actors).<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
|10 || Stationary green tank<br />
|-<br />
|11 || ???<br />
|-<br />
|12 || Stationary green tank, animated<br />
|-<br />
|13 || ???<br />
|-<br />
|22 || Stationary red tank<br />
|-<br />
|23 || ???<br />
|-<br />
|24 || Stationary red/blue tank/turret<br />
|-<br />
|25 || ???<br />
|-<br />
|211 || Blue spaceship flying in from below<br />
|-<br />
|212 || Blue spaceship flying in from above<br />
|-<br />
|213 || Same as 211 (TODO: Verify there's really no difference)<br />
|-<br />
|214 || White-blue spaceship flying in from above<br />
|-<br />
|223 || Red spaceship flying in from below<br />
|-<br />
|224 || Red spaceship flying in from above<br />
|-<br />
|225 || Green spaceship flying in from below<br />
|-<br />
|226 || Green spaceship flying in from above<br />
|-<br />
|232 || ???<br />
|-<br />
|237 || ???<br />
|}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk, and [[User:Timixretroplays|Timixretroplays]] are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11022Major Stryker Level Format2023-05-08T14:19:16Z<p>Lethal guitar: Fix last bg actor id</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers" (see below).<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Modifiers act on the actor that appears right before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Formations of multiple ships are spawned via a combination of at least two actors: First, a formation type describes the number and position of ships and their movement. After that, the next actor describes the type of ships to use in the formation. This pair of actors may be followed by additional modifiers. It seems that some (all?) formations can also appear without a "ship selector", in which case they will default to a certain ship type (varies for each formation).<br />
<br />
Actors can be "foreground" (same plane as the player) or "background" (behind the player, no collision). Which actor appears on which layer is hardcoded based on the actor ID.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
=== Foreground actors ===<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 16 || Shield ship (shoot to reveal a shield pickup)<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 34 || Formation - Flight of 10, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation - Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation. TODO describe<br />
|-<br />
| 37 || Formation - Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation - Like 34?<br />
|-<br />
| 39 || Formation - Diagonal of 3 large ships, which split up into 4 smaller ones each.<br />
|-<br />
| 40 || Formation - Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 41 || Formation - Flight of 10 in from lower left - split up/down around an oval of space, then back together to leave on the right in a line. Seems to be the same for 42 and possibly onwards? There could well be duplicate formations given the sheer number of them.<br />
|-<br />
| 91 || Pickup M<br />
|-<br />
| 93 || Pickup S<br />
|-<br />
| 94 || Pickup R<br />
|-<br />
| 96 || Trigger - End of level<br />
|-<br />
| 97 || Modifier - unknown<br />
|-<br />
| 116 || Single small white ship, flying towards player. Same as the one spawned using ship selector 20 in a formation.<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 122-124 || Trigger - Adjust flying speed (used in speed levels like E1L2, 122 = normal speed)<br />
|-<br />
| 133 || Trigger - Turns on forward lights (in dark parts of levels)<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || Formation ship selector - 1x1 white space ship (same as 116)<br />
|-<br />
| 208 || Formation ship selector - 1x1 pair of red & blue spheres<br />
|-<br />
| 238 || Prisoner capsule<br />
|-<br />
| 258 || Modifier - Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Modifier - Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
=== Background actors ===<br />
<br />
'''Note:''' background actors have X coordinates greater than 16 in the level files. To make them show up correctly, their X coordinate needs to be adjusted by -16 (the game does that when loading the actors).<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
|10 || Stationary green tank<br />
|-<br />
|11 || ???<br />
|-<br />
|12 || Stationary green tank, animated<br />
|-<br />
|13 || ???<br />
|-<br />
|22 || Stationary red tank<br />
|-<br />
|23 || ???<br />
|-<br />
|24 || Stationary red/blue tank/turret<br />
|-<br />
|25 || ???<br />
|-<br />
|211 || Blue spaceship flying in from below<br />
|-<br />
|212 || Blue spaceship flying in from above<br />
|-<br />
|213 || Same as 211 (TODO: Verify there's really no difference)<br />
|-<br />
|214 || White-blue spaceship flying in from above<br />
|-<br />
|223 || Red spaceship flying in from below<br />
|-<br />
|224 || Red spaceship flying in from above<br />
|-<br />
|225 || Green spaceship flying in from below<br />
|-<br />
|226 || Green spaceship flying in from above<br />
|-<br />
|232 || ???<br />
|-<br />
|237 || ???<br />
|-<br />
|270 || ???<br />
|}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk, and [[User:Timixretroplays|Timixretroplays]] are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11019Major Stryker Level Format2023-05-08T06:02:06Z<p>Lethal guitar: Correct description of 16</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers" (see below).<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Modifiers act on the actor that appears right before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Formations of multiple ships are spawned via a combination of at least two actors: First, a formation type describes the number and position of ships and their movement. After that, the next actor describes the type of ships to use in the formation. This pair of actors may be followed by additional modifiers. It seems that some (all?) formations can also appear without a "ship selector", in which case they will default to a certain ship type (varies for each formation).<br />
<br />
Actors can be "foreground" (same plane as the player) or "background" (behind the player, no collision). Which actor appears on which layer is hardcoded based on the actor ID.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
=== Foreground actors ===<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 16 || Shield ship (shoot to reveal a shield pickup)<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 34 || Formation - Flight of 10, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation - Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation. TODO describe<br />
|-<br />
| 37 || Formation - Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation - Like 34?<br />
|-<br />
| 39 || Formation - Diagonal of 3 large ships, which split up into 4 smaller ones each.<br />
|-<br />
| 40 || Formation - Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 41 || Formation - Flight of 10 in from lower left - split up/down around an oval of space, then back together to leave on the right in a line. Seems to be the same for 42 and possibly onwards? There could well be duplicate formations given the sheer number of them.<br />
|-<br />
| 91 || Pickup M<br />
|-<br />
| 93 || Pickup S<br />
|-<br />
| 94 || Pickup R<br />
|-<br />
| 96 || Trigger - End of level<br />
|-<br />
| 97 || Modifier - unknown<br />
|-<br />
| 116 || Single small white ship, flying towards player. Same as the one spawned using ship selector 20 in a formation.<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 122-124 || Trigger - Adjust flying speed (used in speed levels like E1L2, 122 = normal speed)<br />
|-<br />
| 133 || Trigger - Turns on forward lights (in dark parts of levels)<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || Formation ship selector - 1x1 white space ship (same as 116)<br />
|-<br />
| 208 || Formation ship selector - 1x1 pair of red & blue spheres<br />
|-<br />
| 238 || Prisoner capsule<br />
|-<br />
| 258 || Modifier - Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Modifier - Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
=== Background actors ===<br />
<br />
'''Note:''' background actors have X coordinates greater than 16 in the level files. To make them show up correctly, their X coordinate needs to be adjusted by -16 (the game does that when loading the actors).<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
|10 || Stationary green tank<br />
|-<br />
|11 || ???<br />
|-<br />
|12 || Stationary green tank, animated<br />
|-<br />
|13 || ???<br />
|-<br />
|22 || Stationary red tank<br />
|-<br />
|23 || ???<br />
|-<br />
|24 || Stationary red/blue tank/turret<br />
|-<br />
|25 || ???<br />
|-<br />
|211 || Blue spaceship flying in from below<br />
|-<br />
|212 || Blue spaceship flying in from above<br />
|-<br />
|213 || Same as 211 (TODO: Verify there's really no difference)<br />
|-<br />
|214 || White-blue spaceship flying in from above<br />
|-<br />
|223 || Red spaceship flying in from below<br />
|-<br />
|224 || Red spaceship flying in from above<br />
|-<br />
|225 || Green spaceship flying in from below<br />
|-<br />
|226 || Green spaceship flying in from above<br />
|-<br />
|232 || ???<br />
|-<br />
|237 || ???<br />
|-<br />
|271 || ???<br />
|}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk, and [[User:Timixretroplays|Timixretroplays]] are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11017Major Stryker Level Format2023-05-07T21:12:41Z<p>Lethal guitar: Add new actor findings</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers" (see below).<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Modifiers act on the actor that appears right before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Formations of multiple ships are spawned via a combination of at least two actors: First, a formation type describes the number and position of ships and their movement. After that, the next actor describes the type of ships to use in the formation. This pair of actors may be followed by additional modifiers. It seems that some (all?) formations can also appear without a "ship selector", in which case they will default to a certain ship type (varies for each formation).<br />
<br />
Actors can be "foreground" (same plane as the player) or "background" (behind the player, no collision). Which actor appears on which layer is hardcoded based on the actor ID.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
=== Foreground actors ===<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 16 || Event with a big ship that comes in from the right to left and zig-zags towards player. After some time, two rows of red spaceships appear from left and right.<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 34 || Formation - Flight of 10, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation - Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation. TODO describe - I don't think this is used in the game<br />
|-<br />
| 37 || Formation - Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation - Like 34?<br />
|-<br />
| 39 || Formation - Diagonal of 3 large ships, which split up into 4 smaller ones each.<br />
|-<br />
| 40 || Formation - Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 41 || Formation - Flight of 10 in from lower left - split up/down around an oval of space, then back together to leave on the right in a line. Seems to be the same for 42 and possibly onwards?<br />
|-<br />
| 91 || Pickup M<br />
|-<br />
| 93 || Pickup S<br />
|-<br />
| 94 || Pickup R<br />
|-<br />
| 96 || Trigger - End of level<br />
|-<br />
| 97 || Modifier - unknown<br />
|-<br />
| 116 || Single small white ship, flying towards player. Same as the one spawned using ship selector 20 in a formation.<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 122-124 || Trigger - Adjust flying speed (used in speed levels like E1L2, 122 = normal speed)<br />
|-<br />
| 133 || Trigger - Turns on forward lights (in dark parts of levels)<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || Formation ship selector - 1x1 white space ship (same as 116)<br />
|-<br />
| 208 || Formation ship selector - 1x1 pair of red & blue spheres<br />
|-<br />
| 238 || Prisoner capsule<br />
|-<br />
| 258 || Modifier - Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Modifier - Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
<br />
=== Background actors ===<br />
<br />
'''Note:''' background actors have X coordinates greater than 16 in the level files. To make them show up correctly, their X coordinate needs to be adjusted by -16 (the game does that when loading the actors).<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
|10 || Stationary green tank<br />
|-<br />
|11 || ???<br />
|-<br />
|12 || Stationary green tank, animated<br />
|-<br />
|13 || ???<br />
|-<br />
|22 || Stationary red tank<br />
|-<br />
|23 || ???<br />
|-<br />
|24 || Stationary red/blue tank/turret<br />
|-<br />
|25 || ???<br />
|-<br />
|211 || Blue spaceship flying in from below<br />
|-<br />
|212 || Blue spaceship flying in from above<br />
|-<br />
|213 || Same as 211 (TODO: Verify there's really no difference)<br />
|-<br />
|214 || White-blue spaceship flying in from above<br />
|-<br />
|223 || Red spaceship flying in from below<br />
|-<br />
|224 || Red spaceship flying in from above<br />
|-<br />
|225 || Green spaceship flying in from below<br />
|-<br />
|226 || Green spaceship flying in from above<br />
|-<br />
|232 || ???<br />
|-<br />
|237 || ???<br />
|-<br />
|271 || ???<br />
|}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk, and [[User:Timixretroplays|Timixretroplays]] are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11015Major Stryker Level Format2023-05-06T18:26:44Z<p>Lethal guitar: Add info on background actors</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers" (see below).<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Modifiers act on the actor that appears right before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Formations of multiple ships are spawned via a combination of at least two actors: First, a formation type describes the number and position of ships and their movement. After that, the next actor describes the type of ships to use in the formation. This pair of actors may be followed by additional modifiers.<br />
<br />
Actors can be "foreground" (same plane as the player) or "background" (behind the player, no collision). Which actor appears on which layer is hardcoded based on the actor ID.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
=== Foreground actors ===<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 34 || Formation - Flight of 10, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation - Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation. TODO describe - I don't think this is used in the game<br />
|-<br />
| 37 || Formation - Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation - Like 34?<br />
|-<br />
| 39 || Formation - Diagonal of 3 large ships, which split up into 4 smaller ones each.<br />
|-<br />
| 40 || Formation - Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 41 || Formation - Flight of 10 in from lower left - split up/down around an oval of space, then back together to leave on the right in a line. Seems to be the same for 42 and possibly onwards?<br />
|-<br />
| 96 || Trigger - End of level<br />
|-<br />
| 97 || Modifier - unknown<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 133 || Trigger - Turns on forward lights (in dark parts of levels)<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || Formation ship selector - 1x1 white space ship<br />
|-<br />
| 208 || Formation ship selector - 1x1 pair of red & blue spheres<br />
|-<br />
| 258 || Modifier - Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Modifier - Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
<br />
=== Background actors ===<br />
<br />
'''Note:''' background actors have X coordinates greater than 16 in the level files. To make them show up correctly, their X coordinate needs to be adjusted by -16 (the game does that when loading the actors).<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
|10 || Stationary green tank<br />
|-<br />
|11 || ???<br />
|-<br />
|12 || Stationary green tank, animated<br />
|-<br />
|13 || ???<br />
|-<br />
|22 || Stationary red tank<br />
|-<br />
|23 || ???<br />
|-<br />
|24 || Stationary red/blue tank/turret<br />
|-<br />
|25 || ???<br />
|-<br />
|211 || Blue spaceship flying in from below<br />
|-<br />
|212 || Blue spaceship flying in from above<br />
|-<br />
|213 || Same as 211 (TODO: Verify there's really no difference)<br />
|-<br />
|214 || White-blue spaceship flying in from above<br />
|-<br />
|223 || Red spaceship flying in from below<br />
|-<br />
|224 || Red spaceship flying in from above<br />
|-<br />
|225 || Green spaceship flying in from below<br />
|-<br />
|226 || Green spaceship flying in from above<br />
|-<br />
|232 || ???<br />
|-<br />
|237 || ???<br />
|-<br />
|271 || ???<br />
|}<br />
<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk, and [[User:Timixretroplays|Timixretroplays]] are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11014Major Stryker Level Format2023-05-06T17:34:46Z<p>Lethal guitar: Add link to Timix profile</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers".<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Modifiers act on the actor that appears right before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Formations of multiple ships are spawned via a combination of at least two actors: First, a formation type describes the number and position of ships and their movement. After that, the next actor describes the type of ships to use in the formation. This pair of actors may be followed by additional modifiers.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 34 || Formation - Flight of 10, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation - Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation. TODO describe - I don't think this is used in the game<br />
|-<br />
| 37 || Formation - Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation - Like 34?<br />
|-<br />
| 39 || Formation - Diagonal of 3 large ships, which split up into 4 smaller ones each.<br />
|-<br />
| 40 || Formation - Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 41 || Formation - Flight of 10 in from lower left - split up/down around an oval of space, then back together to leave on the right in a line. Seems to be the same for 42 and possibly onwards?<br />
|-<br />
| 96 || Trigger - End of level<br />
|-<br />
| 97 || Modifier - unknown<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 133 || Trigger - Turns on forward lights (in dark parts of levels)<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || Formation ship selector - 1x1 white space ship<br />
|-<br />
| 208 || Formation ship selector - 1x1 pair of red & blue spheres<br />
|-<br />
| 258 || Modifier - Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Modifier - Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk, and [[User:Timixretroplays|Timixretroplays]] are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11013Major Stryker Level Format2023-05-06T08:48:07Z<p>Lethal guitar: Some additions and clarifications for actors</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers".<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Modifiers act on the actor that appears right before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Formations of multiple ships are spawned via a combination of at least two actors: First, a formation type describes the number and position of ships and their movement. After that, the next actor describes the type of ships to use in the formation. This pair of actors may be followed by additional modifiers.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 34 || Formation - Flight of 10, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation - Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation. TODO describe - I don't think this is used in the game<br />
|-<br />
| 37 || Formation - Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation - Like 34?<br />
|-<br />
| 39 || Formation - Diagonal of 3 large ships, which split up into 4 smaller ones each.<br />
|-<br />
| 40 || Formation - Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 41 || Formation - Flight of 10 in from lower left - split up/down around an oval of space, then back together to leave on the right in a line. Seems to be the same for 42 and possibly onwards?<br />
|-<br />
| 96 || Trigger - End of level<br />
|-<br />
| 97 || Modifier - unknown<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 133 || Trigger - Turns on forward lights (in dark parts of levels)<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || Formation ship selector - 1x1 white space ship<br />
|-<br />
| 208 || Formation ship selector - 1x1 pair of red & blue spheres<br />
|-<br />
| 258 || Modifier - Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Modifier - Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk and timixretroplays are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11006Major Stryker Level Format2023-05-05T13:10:02Z<p>Lethal guitar: Add & tweak actor info</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers".<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Some modifiers act on the actor that appears right afterwards, but many also affect the one that came before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray rotating turret<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 27 || 2x2 turret<br />
|-<br />
| 26 || 2x1 turret<br />
|-<br />
| 34 || Formation modifier for following actor. A flight of 16, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation modifier for following actor. Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation modifier for following actor. TODO describe<br />
|-<br />
| 37 || Formation modifier for following actor. Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation modifier for following actor. Like 34?<br />
|-<br />
| 39 || Formation modifier for following actor. Diagonal of 3 large ships, which split up into 3 smaller ones each.<br />
|-<br />
| 40 || Formation modifier for following actor. Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 96 || End of level trigger<br />
|-<br />
| 97 || Unknown modifier for preceding actor<br />
|-<br />
| 118 || Big blue spaceship flying left and right<br />
|-<br />
| 199 || Blue space ship<br />
|-<br />
| 203 || 1x1 white space ship<br />
|-<br />
| 208 || 1x1 pair of red & blue spheres<br />
|-<br />
| 258 || Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk and timixretroplays are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11005Major Stryker Level Format2023-05-05T11:39:46Z<p>Lethal guitar: Add more actor details</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers".<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Some modifiers act on the actor that appears right afterwards, but many also affect the one that came before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray turret, shooting at the player<br />
|-<br />
| 19 || Asteroid<br />
|-<br />
| 20 || Asteroid, moving faster<br />
|-<br />
| 34 || Formation modifier for following actor. A flight of 16, flying in a straight line then splitting up left & right.<br />
|-<br />
| 35 || Formation modifier for following actor. Flight of 6 large ships in a diagonal spanning width of the screen.<br />
|-<br />
| 36 || Formation modifier for following actor. TODO describe<br />
|-<br />
| 37 || Formation modifier for following actor. Two groups flying in from both edges of the screen, then meet in the middle and fly up.<br />
|-<br />
| 38 || Formation modifier for following actor. Like 34?<br />
|-<br />
| 39 || Formation modifier for following actor. Diagonal of 3 large ships, which split up into 3 smaller ones each.<br />
|-<br />
| 40 || Formation modifier for following actor. Diagonal of 3 large ships swaying side-to-side, split up into 4 smaller ones each.<br />
|-<br />
| 96 || End of level trigger<br />
|-<br />
| 97 || Unknown modifier for preceding actor<br />
|-<br />
| 199 || 4x4 blue space ship<br />
|-<br />
| 203 || 1x1 white space ship<br />
|-<br />
| 208 || 1x1 pair of red & blue spheres<br />
|-<br />
| 258 || Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk and timixretroplays are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Major_Stryker_Level_Format&diff=11004Major Stryker Level Format2023-05-05T10:59:37Z<p>Lethal guitar: Add initial actor ID explanation (ongoing research, will update this)</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Major Stryker}}<br />
}}<br />
<br />
== File Format ==<br />
<br />
The basic layout of a level file is as follows:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[Char]][12] cMaskTiles || filename of the masked tile graphics<br />
|-<br />
| [[Char]][12] cSolidTiles || filename of the solid tile graphics<br />
|-<br />
| [[Char]][12] cBackdrop || filename of the backdrop tile graphics<br />
|-<br />
| [[Char]][12] cUnused1 || unused filename (always "dropa.dr1")<br />
|-<br />
| [[Char]][12] cUnused2 || unused filename (always "attr00.dr1")<br />
|-<br />
| [[Char]][12] cMusic || filename of the music file<br />
|-<br />
| [[Char]][12] cUnused3 || unused filename (always "mboss.dr1")<br />
|-<br />
| [[UINT16LE]] iFlags || Flags for lights etc (see below)<br />
|-<br />
| [[UINT16LE]] iWidth || Width of map, in tiles (always 32)<br />
|-<br />
| [[UINT16LE]] iActorSize || Number of UINT16LE values in the actor data<br />
|-<br />
| [[UINT16LE]][iActorSize] iActorData || Actor entries (3 values per actor, see below)<br />
|-<br />
| [[BYTE]][240] bMaskAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[BYTE]][240] bTileAttributes || Tile attributes (one byte per tile, see below)<br />
|-<br />
| [[UINT16LE]][16767] iTileData || Tile codes for the entire level<br />
|}<br />
<br />
The bits in the iFlags value have the following meaning:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x0001 || none (this is ALWAYS set but has no effects at all)<br />
|-<br />
| 0x0002 || lightning (only used in E2M1)<br />
|-<br />
| 0x0004 || scroll bottom plane faster (for bosses in E1M12, E2M12, E3M12)<br />
|}<br />
<br />
The tile attribute bits have the following effects:<br />
<br />
{|class="wikitable"<br />
! Bit mask !! Effect<br />
|-<br />
| 0x10 || road<br />
|-<br />
| 0x20 || animated)<br />
|-<br />
| 0x40 || shootable<br />
|-<br />
| 0x80 || blocking (any direction)<br />
|}<br />
<br />
Actor entries use the following data structure:<br />
<br />
{|class="wikitable"<br />
! Data type !! Description<br />
|-<br />
| [[UINT16LE]] iType || ID number of this actor<br />
|-<br />
| [[UINT16LE]] iPosX || X coordinate of this actor, in tile units<br />
|-<br />
| [[UINT16LE]] iPosY || Y coordinate of this actor, in tile units<br />
|}<br />
<br />
The game ignores the <tt>iWidth</tt> value read from the file and sets it back to 32, thus the level's height will always be 523, leaving 31 UINT16LE values unused at the end of the tile data.<br />
<br />
== Tile Data ==<br />
<br />
The range of valid tile data goes from 0 to 0x1ED0. Some levels also contain the invalid values 0x22C0 and 0x22E0 in the 31 unused tiles at the end, which must be handled separately.<br />
<br />
* Values smaller than $F0 are tile indices for the masked tileset.<br />
<br />
* Subtract $F0 from any other valid value to get an offset into the solid tileset in memory (divide the offset by 32 to get a tile index).<br />
<br />
The map layout is special because the game uses parallax scrolling. The first 16 tiles of each row make up the actual map, the last 16 tiles of each row make up the layer that scrolls below the actual map (at half the speed). Actors can be placed in both of these "layers".<br />
<br />
== Actor IDs==<br />
<br />
Some actors spawn objects into the level, but some are actually modifiers that affect another actor in some way. Some modifiers act on the actor that appears right afterwards, but many also affect the one that came before.<br />
In some cases, a single in-game entity can be described by up to 4 different actors in a row due to modifiers.<br />
<br />
Finally, it's worth noting that some actors don't have a sprite-based graphical representation, but rely on the level's tile map to have an appropriate tile at the actor's location. When these actors are destroyed, they draw a "destroyed" image on top of the map tiles.<br />
<br />
Here's what's known so far:<br />
<br />
{|class="wikitable"<br />
! ID !! Description<br />
|-<br />
| 1 || 1x1 gray turret, shooting at the player<br />
|-<br />
| 96 || End of level trigger<br />
|-<br />
| 97 || Unknown modifier for preceding actor<br />
|-<br />
| 199 || 4x4 blue space ship<br />
|-<br />
| 203 || 1x1 white space ship<br />
|-<br />
| 208 || 1x1 pair of red & blue spheres<br />
|-<br />
| 258 || Make preceding actor appear only in medium or hard difficulty<br />
|-<br />
| 259 || Make preceding actor appear only in hard difficulty<br />
|}<br />
<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. [[User:Lethal_guitar|Lethal_guitar]], Primož Vovk and timixretroplays are working on figuring out the meaning of the actor IDs. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_1_Level_Format&diff=10879Duke 1 Level Format2023-02-21T23:05:14Z<p>Lethal guitar: Correct fan blades description</p>
<hr />
<div>{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 1<br />
| Tile size = 16&times;16<br />
| Viewport = 208&times;160<br />
| Games = <br />
{{Game|Duke Nukem}}<br />
}}<br />
<br />
The levels in [[Duke Nukem]] 1 are stored in the files <tt>WORLDALx.DNy</tt>, where y is the episode number, and x is the level number, going from 1-9 then A-C. Oddly, level 2 is the intermediate level and level C is the demo level.<br />
<br />
Level structure is very simple, each level is 128x90 tiles in size and consists of 11520 UINT16LE words (23040 bytes) of data. Each word controls what appears on one tile in the level. (The level has only one plane, containing background, foreground and enemies. Thus each word is sometimes called a ShapeThing. Because of this limit, the game has some interesting shortcuts to allow things to appear more naturally, such as an enemy tile using the tile before it as its background.) The following values control what appears:<br />
<br />
{|class="wikitable"<br />
! Range (hex) !! Class !! Description<br />
|-<br />
| 0000-05FF || Flashers || Animated backgrounds or stationary backdrops, Duke can pass through these.<br />
|-<br />
| 0600-17FF || Backgrounds || Static background objects that Duke can pass through.<br />
|-<br />
| 1800-2FFF || Solids || Static foreground objects that Duke cannot pass through.<br />
|-<br />
| 3000-3059 || Active || This is the fun stuff, all the things that move, such as techbots, bonuses, items, Duke's starting location, hazards (spikes, force fields), doors, keyholes/access slots, conveyor belts and some special effect items (such as mirror floor and transparent windows).<br />
|-<br />
| 306A-FFFF || (Hacks) || Static foreground objects that Duke cannot pass through. The first tiles use tile images from BORDER.DN? and most of the later tiles are displayed as random junk. All of these tiles are clingable.<br />
|}<br />
<br />
== Tile mapping ==<br />
<br />
The Level's tiles use tile graphics from 8 files: <tt>BACK0.DN?</tt> through <tt>BACK3.DN?</tt> and <tt>SOLID0.DN?</tt> through <tt>SOLID3.DN?</tt>. Tiles from <tt>BORDER.DN?</tt> can also be used, though they are not "officially" supported.<br />
<br />
Each file contains 48 tiles (at least the game loads 48 tiles from each file), so there are 384 "actual" tiles. Due to the way the tile graphics are stored in memory, you need to divide the tile value by 32 (EGA plane size of a 16x16 pixel tile) to get a tile index from the data stored in the level file.<br />
<br />
{|class="wikitable"<br />
! Range (hex) !! File<br />
|-<br />
| 0000-05FF || BACK0.DN?<br />
|-<br />
| 0600-0BFF || BACK1.DN?<br />
|-<br />
| 0C00-11FF || BACK2.DN?<br />
|-<br />
| 1200-17FF || BACK3.DN?<br />
|-<br />
| 1800-1DFF || SOLID0.DN?<br />
|-<br />
| 1E00-23FF || SOLID1.DN?<br />
|-<br />
| 2400-29FF || SOLID2.DN?<br />
|-<br />
| 2A00-2FFF || SOLID3.DN?<br />
|-<br />
| 3000-367F || Varies, see below<br />
|-<br />
| 4000-? || current backdrop 1 (hack)<br />
|-<br />
| 8100-? || current backdrop 2 (hack)<br />
|}<br />
<br />
=== Active ===<br />
<br />
The follow tiles appear to be manually mapped. These tile codes from the map do not need to be divided by 32, unlike the other tilecodes.<br />
<br />
{|class="wikitable"<br />
! Tile code (hex) !! File !! Tile index !! Type !! Description<br />
|-<br />
| 3000 || OBJECT0.DN1 || 0 || Bonus || Grey box, empty<br />
|-<br />
| 3001 || OBJECT0.DN1 || 5 || Interactive || Elevator<br />
|-<br />
| 3002 || {{TODO|?}} || ? || Interactive || Left-moving conveyor belt start, must be left of end tile<br />
|-<br />
| 3003 || {{TODO|?}} || ? || Interactive || Left-moving conveyor belt end, must be on right of start tile<br />
|-<br />
| 3004 || {{TODO|?}} || ? || Interactive || Right-moving conveyor belt start, must be left of end tile<br />
|-<br />
| 3005 || {{TODO|?}} || ? || Interactive || Right-moving conveyor belt end, must be on right of start tile<br />
|-<br />
| 3006 || OBJECT0.DN1 || 10 || Bonus || Grey box (0 @ OBJECT0.DN1), contains shoes<br />
|-<br />
| 3007 || OBJECT0.DN1 || 11-17 || Interactive || Rocket, tile below is replaced with default tile after rocket takeoff<br />
|-<br />
| 3008 || OBJECT0.DN1 || 18 || Bonus || Grey box (0 @ OBJECT0.DN1), contains ceiling-clinger<br />
|-<br />
| 3009 || OBJECT0.DN1 || 24-28 || Hazard || Flamethrower, firing to the right (three tiles wide including placement tile)<br />
|-<br />
| 300A || OBJECT0.DN1 || 29-33 || Hazard || Flamethrower, firing to the left (three tiles wide including placement tile)<br />
|-<br />
| 300B || ANIM0.DN1 || 0-5 || Enemy || Floating 1x1 robot with purple antenna<br />
|-<br />
| 300C || ANIM0.DN1 || 10-33 || Enemy || Jumping 2x2 robot<br />
|-<br />
| 300D || ANIM0.DN1 || 34-41 || Enemy || Wheeled 2x1 robot with blue wheels<br />
|-<br />
| 300E || ANIM1.DN1 || 0-31 || Enemy || 2x2 rotating wheel with flames<br />
|-<br />
| 300F || OBJECT0.DN1 || 43 || Bonus || Grey box (0 @ OBJECT0.DN1), contains raygun (ammo)<br />
|-<br />
| 3010 || ANIM1.DN1 || 32 || Enemy || 1x1 robot<br />
|-<br />
| 3011 || - || - || - || Exit door (only 1 instance allowed!)<br />
|-<br />
| 3012 || ANIM2.DN1 || 17 || Bonus || Grey box (0 @ OBJECT0.DN1), contains dynamite<br />
|-<br />
| 3013 || ANIM2.DN1 || 24-31 || Enemy || Flying orb monster, each shot removes one orb<br />
|-<br />
| 3014 || - || - || Decorative || Shimmering water effect, also affects tile below this one<br />
|-<br />
| 3015 || ANIM2.DN1 || 32-35 || Bonus || Red box (1 @ OBJECT2.DN1), contains soda can<br />
|-<br />
| 3016 || ANIM2.DN1 || 40-43 || Enemy || Green bitey creature walking up and down walls, facing right<br />
|-<br />
| 3017 || ANIM2.DN1 || 44-47 || Enemy || Green bitey creature walking up and down walls, facing left<br />
|-<br />
| 3018 || OBJECT0.DN1 || 44 || Bonus || Red box (1 @ OBJECT2.DN1), contains turkey drumstick<br />
|-<br />
| 3019 || ANIM3.DN1 || 0 || - || Bridge that explodes when stepped on twice (only 1 instance allowed!)<br />
|-<br />
| 301A || OBJECT1.DN1 || 0-3 || Hazard || Blue force field, horizontal<br />
|-<br />
| 301B || ANIM3.DN1 || 12-19 || Hazard || Rotating fan blades, blowing left<br />
|-<br />
| 301C || ANIM3.DN1 || 12-19 || Hazard || Rotating fan blades, blowing right<br />
|-<br />
| 301D || OBJECT1.DN1 || 8 || Bonus || Blue box (0 @ OBJECT2.DN1), contains football<br />
|-<br />
| 301E || OBJECT1.DN1 || 9 || Bonus || Blue box (0 @ OBJECT2.DN1), contains joystick<br />
|-<br />
| 301F || OBJECT1.DN1 || 10 || Bonus || Blue box (0 @ OBJECT2.DN1), contains "DN" floppy<br />
|-<br />
| 3020 || OBJECT1.DN1 || 13 || Bonus || Grey box (0 @ OBJECT2.DN1), contains robohand<br />
|-<br />
| 3021 || OBJECT1.DN1 || 15-18 || Hazard || White/blue force field, vertical<br />
|-<br />
| 3022 || ANIM3.DN1 || 20-47 || Enemy || Helicopter<br />
|-<br />
| 3023 || OBJECT1.DN1 || 19-23 || Bonus || Blue box (0 @ OBJECT2.DN1), contains balloon<br />
|-<br />
| 3024 || ANIM4.DN1 || 8-10 || Bonus || Security camera<br />
|-<br />
| 3025 || ANIM4.DN1 || 11 || Decorative || Brown spikes<br />
|-<br />
| 3026 || ANIM4.DN1 || 12 || Decorative || Rock, left half<br />
|-<br />
| 3027 || ANIM4.DN1 || 13 || Decorative || Rock, right half<br />
|-<br />
| 3028 || ANIM4.DN1 || 14 || Decorative || Grey rounded window<br />
|-<br />
| 3029 || OBJECT1.DN1 || 24 || Bonus || Grey box (0 @ OBJECT0.DN1), contains nuclear molecule<br />
|-<br />
| 302A || OBJECT1.DN1 || 33 || Hazard || Loose/falling ACME sign<br />
|-<br />
| 302B || OBJECT1.DN1 || 34 || Hazard || Blue reactor<br />
|-<br />
| 302C || OBJECT1.DN1 || 45-46 || Hazard || Spike, pokes up when walking over<br />
|-<br />
| 302D || OBJECT1.DN1 || 47-49 || Bonus || Blue box (0 @ OBJECT2.DN1), contains magenta flag<br />
|-<br />
| 302E || OBJECT2.DN1 || 2-4 || Bonus || Blue box (0 @ OBJECT2.DN1), contains radio<br />
|-<br />
| 302F || ANIM4.DN1 || 20-28 || Interactive || Teleporter, transports to tilecode 3030<br />
|-<br />
| 3030 || ANIM4.DN1 || 20-28 || Interactive || Teleporter, transports to tilecode 302F<br />
|-<br />
| 3031 || ANIM4.DN1 || 31 || Hazard || Spiky round red bomb, bouncing<br />
|-<br />
| 3032 || - || - || Interactive || Player start point<br />
|-<br />
| 3033 || OBJECT1.DN1 || 14 || Bonus || Grey box (0 @ OBJECT0.DN1), contains access card<br />
|-<br />
| 3034 || OBJECT2.DN1 || 5-12 || Interactive || Access card activation point<br />
|-<br />
| 3035 || OBJECT2.DN1 || 14-15 || Interactive || Robohand activation point<br />
|-<br />
| 3036 || {{TODO|?}} || ? || ? || Red girder, creates a bridge when Robohand activation point is used<br />
|-<br />
| 3037 || OBJECT2.DN1 || 21 || Bonus || Grey box (0 @ OBJECT0.DN1), contains "D"<br />
|-<br />
| 3038 || OBJECT2.DN1 || 21 || Bonus || Grey box (0 @ OBJECT0.DN1), contains "U"<br />
|-<br />
| 3039 || OBJECT2.DN1 || 21 || Bonus || Grey box (0 @ OBJECT0.DN1), contains "K"<br />
|-<br />
| 303A || OBJECT2.DN1 || 21 || Bonus || Grey box (0 @ OBJECT0.DN1), contains "E"<br />
|-<br />
| 303B || ANIM2.DN1 || 18-23 || Bonus || Purple rabbit thing, 5000 bonus points<br />
|-<br />
| 303C || ANIM5.DN1 || 1 || Enemy || Fire monster<br />
|-<br />
| 303D || ANIM5.DN1 || 12 || Decorative || Diamond-shaped wire mesh<br />
|-<br />
| 303E || ANIM5.DN1 || 13 || Decorative || Window showing through to backdrop, left half<br />
|-<br />
| 303F || ANIM5.DN1 || 14 || Decorative || Window showing through to backdrop, right half<br />
|-<br />
| 3040 || OBJECT2.DN1 || 23 || Interactive || Message, press up to read (blank message for most levels?)<br />
|-<br />
| 3041 || - || - || - || Screen display with Dr. Proton<br />
|-<br />
| 3042 || ANIM5.DN1 || 30-42 || Enemy || Dr Proton, E3 boss, killable ("I won it all!", game ends)<br />
|-<br />
| 3043 || ANIM5.DN1 || 30-42 || Enemy || Dr Proton, E1 boss, not killable (escapes to moon, game ends)<br />
|-<br />
| 3044 || OBJECT2.DN1 || 24 || Interactive || Door key, red<br />
|-<br />
| 3045 || OBJECT2.DN1 || 25 || Interactive || Door key, green<br />
|-<br />
| 3046 || OBJECT2.DN1 || 26 || Interactive || Door key, blue<br />
|-<br />
| 3047 || OBJECT2.DN1 || 27 || Interactive || Door key, magenta<br />
|-<br />
| 3048 || OBJECT2.DN1 || 37 || Interactive || Door lock, operated by red key<br />
|-<br />
| 3049 || OBJECT2.DN1 || 38 || Interactive || Door lock, operated by green key<br />
|-<br />
| 304A || OBJECT2.DN1 || 39 || Interactive || Door lock, operated by blue key<br />
|-<br />
| 304B || OBJECT2.DN1 || 40 || Interactive || Door lock, operated by magenta key<br />
|-<br />
| 304C || OBJECT2.DN1 || 28 || Interactive || Door, opened by red lock<br />
|-<br />
| 304D || OBJECT2.DN1 || 28 || Interactive || Door, opened by green lock<br />
|-<br />
| 304E || OBJECT2.DN1 || 28 || Interactive || Door, opened by blue lock<br />
|-<br />
| 304F || OBJECT2.DN1 || 28 || Interactive || Door, opened by magenta lock<br />
|-<br />
| 3050 || OBJECT1.DN1 || 8 || Bonus || Football<br />
|-<br />
| 3051 || OBJECT0.DN1 || 44 || Bonus || Turkey drumstick<br />
|-<br />
| 3052 || ANIM2.DN1 || 32-35 || Bonus || Soda can<br />
|-<br />
| 3053 || OBJECT1.DN1 || 10 || Bonus || "DN" floppy disk<br />
|-<br />
| 3054 || OBJECT1.DN1 || 9 || Bonus || Joystick<br />
|-<br />
| 3055 || OBJECT1.DN1 || 47-49 || Bonus || Purple flag<br />
|-<br />
| 3056 || OBJECT2.DN1 || 2-4 || Bonus || Radio<br />
|-<br />
| 3057 || ANIM4.DN1 || 31 || Hazard || Spiky round red bomb, stationary, explodes on touch<br />
|-<br />
| 3058 || OBJECT2.DN1 || 48 || Hazard || Spikes, poking up<br />
|-<br />
| 3059 || OBJECT2.DN1 || 49 || Hazard || Spikes, poking down<br />
|}<br />
<br />
== Links ==<br />
<br />
There are numerous level editors available, from the basic original ASCII one, to rather advanced ones capable of rendering animations.<br />
<br />
* Maps can be found at [http://vgmaps.com/Atlas/PC/index.htm#DukeNukem vgmaps.com]<br />
* The original level editor is at [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dook Nookim Klassik]<br />
* A reasonable level editor can be found at [http://crystalshard.net/?p=8&s=51 crystalshard.net]<br />
* Possibly the best editor is DN1MOD, which renders levels as the game does, by loading the game's graphics files [http://archive.shikadi.net/sites/members.iinet.net.au/~markim/admiral_bob/files/]</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Map_Format&diff=10768Duke Nukem II Map Format2022-10-26T21:46:01Z<p>Lethal guitar: Correct note about using a single image for solid & masked tiles</p>
<hr />
<div>{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 3<br />
| Tile size = 8&times;8<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
[[Duke Nukem II]] stores its levels in an evolution of the level format used by [[Cosmo's Cosmic Adventures]]. One considerable change is the addition of a second map layer, allowing foreground and background tiles to be placed in the same cell. Surprisingly, given the number of other changes to the file format, the layer data was not restructured to handle this new feature. Instead, some of the new data was shoehorned to fit in the existing space, with the rest of the data tacked on to the end of the file in a somewhat clunky manner. This resulted in some unusual limitations, such as a small number of tiles that can only be placed in the foreground layer if there is no background tile in the same cell.<br />
<br />
== File format ==<br />
The file is in this basic layout:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iBackgroundOffset||Offset where the background layer starts<br />
|-<br />
|char cCZoneName[13]||CZone filename (tileset)<br />
|-<br />
|char cBackdropName[13]||Name of the graphic to use as the level background (one of the <tt>BD*.MNI</tt> files)<br />
|-<br />
|char cMusicName[13]||Name of the [[IMF Format|IMF file]] to use as background music<br />
|-<br />
|BYTE bFlags||Level flags (define type of parallax scrolling and more)<br />
|-<br />
|BYTE bAltBackdrop||Number of alternate backdrop file, zero if no alternate backdrop is used<br />
|-<br />
|BYTE iUnused[2]||Ignored. Likely a holdover from the game's development<br />
|-<br />
|UINT16LE iActorSize||Number of UINT16 values in the actor block<br />
|-<br />
|ACTORDATA actorData[]||Variable-length array of all the actors in the level<br />
|-<br />
|GRIDLAYER gridData||see [[#Foreground and Background Layers]] below for what this contains<br />
|-<br />
|UINT16LE iExtraFGLength||Length of the additional mask/foreground manipulation data<br />
|-<br />
|BYTE iExtraFGData[iExtraFGLength]||See [[#Supplemental foreground data]] below<br />
|-<br />
|char cAttrName[13]||Zone attribute filename<br />
|-<br />
|char cTileName[13]||Zone tile filename<br />
|-<br />
|char cMaskName[13]||Zone masked tiles filename<br />
|}<br />
<br />
The three filenames at the end of the level file never appear to be used. In fact, the level files can end directly after the supplemental foreground data and the game will still load them without reporting any errors. These are probably the names of Cosmo-style tileset files ("zones") that were used before the CZone format (the "c" probably stands for "compound") was introduced.<br />
<br />
For all level files of the full version, the iActorSize value is reliable and the gridData begins directly after the last actor, leaving no padding between actor data and grid data.<br />
<br />
== Level Flags ==<br />
<br />
This is what each bit in the bFlags value indicates:<br />
<br />
Bit | Description<br />
----+------------<br />
7 | switch backdrop when using teleporter (used in L1)<br />
6 | switch backdrop when destroying force field (used in L5)<br />
5 | earthquake<br />
4 | automatic backdrop scrolling ^^<br />
3 | automatic backdrop scrolling <<<br />
2 | - (ignored/unused)<br />
1 | x-scrolling backdrop<br />
0 | x- and y-scrolling backdrop<br />
<br />
Bits 6 and 7 require an alternate backdrop index. The files where these bits are set are also the only files that have a non-zero alternate backdrop number.<br />
<br />
Note that the backdrop/scrolling related bits can NOT be combined! For example, combining bits 3 and 4 will not lead to combined movement (it results in a somewhat jerky/broken horizontal parallax scrolling instead). Combining one of bits 3 or 4 with any of the bits 1 or 0 also leads to broken/wonky results.<br />
<br />
The earthquake flag (bit 5) can be freely combined with any other bits.<br />
<br />
Combining the backdrop switch flags sort of works, but isn't really practical since there can only be two different backdrops. If both bits are set and Duke steps into a teleporter, the backdrop will switch and additionally flash as if a reactor was destroyed. If Duke destroys a reactor before stepping into a teleporter, the backdrop will switch, but will not switch a 2nd time when using a teleporter afterwards. Teleporting back again will switch, and restore the normal backdrop switching behavior.<br />
<br />
In order for either of the backdrop switch flags to work, the backdrop scroll mode needs to be set to x parallax (bit 1). For any other backdrop scroll mode, the backdrop will not switch.<br />
<br />
== Foreground and Background Layers ==<br />
<br />
At the offset indicated by <tt>iBackgroundOffset</tt> in the header, the grid/cell data begins:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iMapWidth||Map width (in tiles)<br />
|-<br />
|UINT16LE iMapData[32750]||Actual map data<br />
|}<br />
<br />
Each "element" in <tt>iMapData</tt> refers to the foreground and/or background tile used in a single grid cell. The grids are arranged left to right, top to bottom, so the index can be calculated by this formula:<br />
<br />
int iIndex = (y * iMapWidth) + x;<br />
iMapData[iIndex] = <new value to set at x,y><br />
<br />
The map data is a constant 32750 UINT16LE cells long (65500 bytes), so the map height can be calculated from this:<br />
<br />
int iMapHeight = 32750 / iMapWidth;<br />
<br />
=== Mapping cell values to tiles ===<br />
<br />
The method of mapping elements in the <tt>iMapData</tt> structure into tiles is a little complicated. There are two different methods of storing values:<br />
<br />
# If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell contains both a foreground and background tile.<br />
# Otherwise the cell value is a memory offset into the tilemap.<br />
<br />
==== Most significant bit set ====<br />
<br />
If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell value contains two indices - one for the solid/background tile, and one for the masked/foreground tile. Unlike the other type of cell value, these are actual tile indices as opposed to memory offsets (i.e. a value of 2 refers to the third tile.)<br />
<br />
The lower ten bits are the index of the background tile (this can provide a value between 0 and 1023, however since there are only 1000 tiles this value should always be less than 1000.) The lower ten bits can be isolated like this:<br />
<br />
iSolid = iMapData[x] & 0x03FF;<br />
<br />
The ignoring the most significant bit (which is used to indicate this type of tile value) the remaining five most significant bits are used as the index of the foreground/mask tile. These bits can be isolated as follows:<br />
<br />
iMask = (iMapData[x] >> 10) & 0x1F;<br />
<br />
This only provides a value between 0 and 31 - but since there are 160 masked tiles, this value needs to be further manipulated to produce an index into the masked tileset. An additional two bits per tile are stored in the [[#Supplemental foreground data]] which are used for this purpose, but this will still only allow the first 127 tiles to be used.<br />
<br />
==== Most significant bit NOT set ====<br />
<br />
If the most significant bit is not set, then the cell value is a memory offset into the tileset images as loaded by the game. It can either refer to a solid or a masked tile, and converting the value to a tile index is slightly different for each case. Values greater than or equal to 8000 refer to masked tiles, smaller values refer to solid tiles. The cell value will be zero for the first solid tile, it will be eight for the second solid tile, it will be 7992 for the last solid tile, it will be 8000 for the first masked tile, 8040 for the second masked tile, and it will be 14,360 for the last masked tile.<br />
<br />
To convert the value to an index into the tileset, first check if it's solid or masked. If the former, divide the value by eight. If the latter, subtract 8000, then divide by 40. This will give you a zero-based index into either the solid or masked tileset, with 0 referring to the very first tile of each tileset. If you'd like to combine solid and masked tiles into a single image instead, the easiest would be to still use the same approach, but then add 1000 afterwards in case it's a masked tile index.<br />
<br />
Why are the cell values like this? This is because of the way the game draws the tiles. Note that in the CZone tileset file, the solid tiles are made up of 1000 4-plane (16-colour) images, and these are followed by 160 5-plane images (16-colour + transparency.) The game loads the solid tile images into video memory, and then copies from video memory to video memory when drawing (using a technique called "latch copy" which allows the graphics card to copy 4 planes of data in one go). But the masked tile image data is kept in system memory, and copied from there to video memory when drawing. As a consequence of this, solid tiles are 8 bytes apart in memory, since only one plane of EGA/VGA memory is visible to the CPU at any one time, and the size of a single plane of an 8x8-pixel image is 8 bytes. The masked tiles on the other hand are 40 bytes apart in memory, since each tile occupies 5 planes of 8 bytes each. Cell values which reference solid tiles are directly used as memory offset into the solid tile data living in video memory. References to masked tiles are first subtracted by 8000, and then used as memory offset into the masked tile data living in system RAM. During drawing, the game compares each cell value against 8000 to determine if it's a solid or masked tile.<br />
<br />
Note that while the cell value cannot be out of range for the solid tiles/background layer (since any values larger than 8000 will be loaded from the masked tileset into the foreground layer) the cell values for the masked tileset have no such restriction.<br />
<br />
=== Supplemental foreground data ===<br />
<br />
Since the tiles containing combined foreground+background data only provide five bits for the foreground tile, this block of data is used to store an additional two bits for each tile, allowing a seven-bit tile number to be specified.<br />
<br />
The data is stored in a form of run length encoding (RLE) to reduce the size, given that a relatively small number of tiles need the extra two bits. Because there are eight bits in a byte, four lots of two-bit-chunks can be stored in every data byte. This means each data byte contains the data for four tiles. The rest of this section uses the term "tile cluster" to refer to this grouping of four tiles.<br />
<br />
==== Data layout ====<br />
<br />
The data is read one byte at a time, and the initial byte ("length-byte") indicates how many subsequent bytes need to be read. The length-bytes are ''signed'' byte values with the absolute value giving the length to be used for the following data. If the length-byte is positive (high bit is NOT set), then it is a count of the number of affected tile clusters. The following byte is the data byte containing the extra tile data. For example: (brackets inserted for clarity)<br />
<br />
[7F 00] [7F 00] [05 FF] [7F 00]<br />
<br />
This means apply value 00 to the first 254 (0x7F * 2) tile clusters, followed by value FF to the next five tile clusters, followed by 00 to the next 127 tile clusters. The format of these "values" is described below.<br />
<br />
If the length-byte is negative (high bit IS set), then a number of data values follow, depending on the absolute value of the length-byte. If the bytes are arranged like this:<br />
<br />
[FF 12] [FE 34 56] [FF 78] [FD 9A BC DE] [00 00]<br />
<br />
Then it would be interpreted as follows:<br />
<br />
# FF == one - apply value 12 to one tile cluster<br />
# FE == two - apply 34 to one tile cluster, then 56 to the following tile cluster<br />
# FF == one - apply 78 to one tile cluster<br />
# FD == three - apply 9A to one tile cluster, BC to the next, and DE to the third tile cluster<br />
# 00 == end of data<br />
<br />
An easy way to work out how many bytes are affected is this formula:<br />
<br />
iCount = 0x100 - iInitialByte<br />
<br />
This will return one for 0xFF, two for 0xFE, three for 0xFD, etc. If your variables are signed bytes, you can just use <tt>iCount = -iInitialByte</tt> instead. A loop can then be used to apply the change to the correct number of tile clusters.<br />
<br />
The last two bytes of the compressed data are always zero, which would always be read when the RLE algorithm expects to read a length-byte. So it should be safe to stop decompressing when you read 0 as length value.<br />
<br />
Note that since the length is the absolute value of a signed byte, the maximum length for both kinds of RLE flag is 127. A length-byte of 0x80 indicates an error. This is because 0x80 (-128) can't have a positive absolute value as the largest positive value in a signed byte is 127. If the game encounters a length-byte of 0x80 upon loading a level file, it will freeze after fading to black from the loading screen.<br />
<br />
==== Data values ====<br />
<br />
Each of the data values used above affects a tile cluster made up of four tiles. If the tiles are arranged from left to right, the least-significant bits affect the first (left-most) tile, and the most-significant bits affect the last (right-most) tile. For example:<br />
<br />
tile1 = (iChange & 0x03);<br />
tile2 = ((iChange >> 2) & 0x03);<br />
tile3 = ((iChange >> 4) & 0x03);<br />
tile4 = (iChange >> 6);<br />
<br />
This will give tileX a value between 0 and 3, which when multiplied by 32 can be directly added to the value of the foreground tile. For the computer scientists out there, a more efficient alternative is this:<br />
<br />
tile1 = (iChange << 5) & 0x60;<br />
tile2 = (iChange << 3) & 0x60;<br />
tile3 = (iChange << 1) & 0x60;<br />
tile4 = (iChange >> 1) & 0x60;<br />
<br />
The values here are already multiplied out, and can be OR'd with the foreground tile numbers.<br />
<br />
One major thing to remember is that these extra values ONLY apply to those tiles where a foreground and a background tile is specified in the map data. If the tile only has a background tile, or it only has a foreground tile, then it's possible to specify the full range of tile numbers and the values stored here (if any) should be ignored for those tiles. It's only when a tile has a foreground *and* a background tile that these extra bits must be used.<br />
<br />
==== Example ====<br />
<br />
This is some example code to read in all the tile values into an array, which the map code can later reference as necessary:<br />
<br />
int *iExtraValues = new int[65500 / 2]; // one element per tile, maps are fixed length<br />
int *pNextByte = iExtraValues; // running counter<br />
<br />
// Read through all the extra data<br />
for (int i = 0; i < iExtraFGLength; i++) {<br />
unsigned char iVal = readNextByte(); // must be 8-bit for logic below to work<br />
<br />
if (iVal & 0x80) {<br />
// Multiple bytes concatenated together<br />
// iVal == 0xFF for one byte, 0xFE for two bytes, etc.<br />
while (iVal++ > 0) { // should eventually wrap from 0xFF to 0x00 when we're done - only works with an 8-bit variable though<br />
applyChange(&pNextByte, readNextByte());<br />
i++;<br />
assert(iVal < 0x100); // prevent headaches if someone uses an int instead<br />
}<br />
} else {<br />
// iVal <= 0x7F<br />
UINT8 iChange = readNextByte(); i++;<br />
<br />
if (iChange > 0) {<br />
// Apply this change iVal times<br />
while (iVal-- > 0) applyChange(&pNextByte, iChange);<br />
} else {<br />
// No changes to the tiles, just skip over them (faster than applying a<br />
// change of 0x00 to all these tiles!)<br />
pNextByte += iVal * 4;<br />
// NOTE: This will only work if you zero the array first - if you don't,<br />
// use a loop here to zero out the tiles instead.<br />
}<br />
}<br />
}<br />
<br />
inline void applyChange(int **ppNextByte, int iChange)<br />
throw ()<br />
{<br />
// Grab each lot of 2-bits and shift into bits 5&6<br />
**ppNextByte = (iChange << 5) & 0x60; (*ppNextByte)++; // 10 -> 65<br />
**ppNextByte = (iChange << 3) & 0x60; (*ppNextByte)++; // 32 -> 65<br />
**ppNextByte = (iChange << 1) & 0x60; (*ppNextByte)++; // 54 -> 65<br />
**ppNextByte = (iChange >> 1) & 0x60; (*ppNextByte)++; // 76 -> 65<br />
return;<br />
}<br />
<br />
=== Example ===<br />
<br />
The following code converts a cell value into two tile indices for that cell - an index into the solid/background tilemap, and another index into the mask/foreground tileset.<br />
<br />
#define DN2_NUM_SOLID_TILES 1000<br />
#define DN2_NUM_MASKED_TILES 160<br />
#define DN2_TILEWIDTH 8 // Tiles are 8x8<br />
<br />
int iSolid, iMasked; // Index into respective tilesets<br />
int iValue = iMapData[x]; // This is the cell to convert<br />
int iExtra = iExtraData[x]; // Extra data for this cell<br />
<br />
<b>if (iValue & 0x8000) {</b><br />
// Most significant bit is set (see [[#Most significant bit set]] above),<br />
// so this cell has a foreground *and* a background tile.<br />
<br />
// First 10 bits are the index of the solid tile<br />
iSolid = iValue & 0x3FF;<br />
<br />
// Remaining five bits (not counting the sixth bit which was used as<br />
// the 0x8000 flag above) are the index of the mask tile.<br />
iMasked = ((iValue >> 10) & 0x1F);<br />
<br />
// Add the extra two bits (see [[#Supplemental foreground data]] above)<br />
iMasked |= iExtra; // assumes iExtra has already been multiplied out<br />
//iMasked = iMasked + (iExtra * 32); // if it hasn't been multiplied out<br />
<br />
<b>} else if (iValue < DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) {</b><br />
// Background only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from memory offset into a tile index.<br />
iSolid = iValue / 8;<br />
<br />
iMasked = -1; // No mask tile in this cell<br />
<br />
<b>} else {</b><br />
// Foreground only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a memory offset into a tile index.<br />
// Make the first masked tile start at zero.<br />
int iRawIndex = (iValue - DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) / 40;<br />
<br />
// No solid tile in this cell, so using zero will make the solid cell<br />
// transparent, allowing the map backdrop to show through.<br />
iSolid = 0;<br />
}<br />
<br />
=== Gotchas ===<br />
<br />
* The masked tileset has no "blank" tile, so if the foreground layer is being loaded into an array a value will need to be used to indicate that no foreground tile occupies that cell. Zero can't be used without tweaking, as masked tile zero is an actual graphic tile.<br />
<br />
* The first tile in the solid tileset is used as a transparent tile. It will appear as a red square if drawn (e.g. in a map editor) however the game does not draw this tile, so any cells with this as the background cell will be where the map backdrop shows through.<br />
<br />
* Even with the supplemental foreground data, there are only seven bits available for the foreground tile. This means that only 127 of the 160 available tiles can be used!<br />
<br />
== Actor data ==<br />
<br />
The <tt>actorData</tt> block is in the following format:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iType||Type of actor<br />
|-<br />
|UINT16LE iX||X-coordinate of actor (in tile units)<br />
|-<br />
|UINT16LE iY||Y-coordinate of actor (in tile units)<br />
|}<br />
<br />
Because the <tt>iActorSize</tt> value in the header is in UINT16s and there are three UINT16s per actor, the number of actors can be obtained quite simply:<br />
<br />
iNumActors = iActorSize / 3<br />
<br />
The <tt>iType</tt> field can conveniently be used as an index into the [[Duke Nukem II Actor Info|ACTRINFO.MNI]] sprite file. If <tt>iType</tt> is zero, the first actor image is used.<br />
<br />
Not all actors have images however, some point to actor sprites with zero frames. It appears to be that some of these "actors" are actually flags, indicating that the next actor (in the level, not in the <tt>ACTRINFO.MNI</tt>) will only appear when the level is played at a certain difficulty level.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=map}}<br />
{{FileFormatTool<br />
| Name = [http://chidesters.org/scottchidester.com/index.html Nukem2Print]<br />
| Platform = Qt<br />
| canView = Yes<br />
| canCreate = No<br />
| canModify = No<br />
| editHidden = N/A<br />
| editMetadata = N/A<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [http://www.szevvy.com Szevvy] figured out how to map the cell values back to the tilesets. [[User:Malvineous|Malvineous]] figured out the format of the supplemental foreground data. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Map_Format&diff=10767Duke Nukem II Map Format2022-10-25T14:06:34Z<p>Lethal guitar: Fix a typo</p>
<hr />
<div>{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 3<br />
| Tile size = 8&times;8<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
[[Duke Nukem II]] stores its levels in an evolution of the level format used by [[Cosmo's Cosmic Adventures]]. One considerable change is the addition of a second map layer, allowing foreground and background tiles to be placed in the same cell. Surprisingly, given the number of other changes to the file format, the layer data was not restructured to handle this new feature. Instead, some of the new data was shoehorned to fit in the existing space, with the rest of the data tacked on to the end of the file in a somewhat clunky manner. This resulted in some unusual limitations, such as a small number of tiles that can only be placed in the foreground layer if there is no background tile in the same cell.<br />
<br />
== File format ==<br />
The file is in this basic layout:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iBackgroundOffset||Offset where the background layer starts<br />
|-<br />
|char cCZoneName[13]||CZone filename (tileset)<br />
|-<br />
|char cBackdropName[13]||Name of the graphic to use as the level background (one of the <tt>BD*.MNI</tt> files)<br />
|-<br />
|char cMusicName[13]||Name of the [[IMF Format|IMF file]] to use as background music<br />
|-<br />
|BYTE bFlags||Level flags (define type of parallax scrolling and more)<br />
|-<br />
|BYTE bAltBackdrop||Number of alternate backdrop file, zero if no alternate backdrop is used<br />
|-<br />
|BYTE iUnused[2]||Ignored. Likely a holdover from the game's development<br />
|-<br />
|UINT16LE iActorSize||Number of UINT16 values in the actor block<br />
|-<br />
|ACTORDATA actorData[]||Variable-length array of all the actors in the level<br />
|-<br />
|GRIDLAYER gridData||see [[#Foreground and Background Layers]] below for what this contains<br />
|-<br />
|UINT16LE iExtraFGLength||Length of the additional mask/foreground manipulation data<br />
|-<br />
|BYTE iExtraFGData[iExtraFGLength]||See [[#Supplemental foreground data]] below<br />
|-<br />
|char cAttrName[13]||Zone attribute filename<br />
|-<br />
|char cTileName[13]||Zone tile filename<br />
|-<br />
|char cMaskName[13]||Zone masked tiles filename<br />
|}<br />
<br />
The three filenames at the end of the level file never appear to be used. In fact, the level files can end directly after the supplemental foreground data and the game will still load them without reporting any errors. These are probably the names of Cosmo-style tileset files ("zones") that were used before the CZone format (the "c" probably stands for "compound") was introduced.<br />
<br />
For all level files of the full version, the iActorSize value is reliable and the gridData begins directly after the last actor, leaving no padding between actor data and grid data.<br />
<br />
== Level Flags ==<br />
<br />
This is what each bit in the bFlags value indicates:<br />
<br />
Bit | Description<br />
----+------------<br />
7 | switch backdrop when using teleporter (used in L1)<br />
6 | switch backdrop when destroying force field (used in L5)<br />
5 | earthquake<br />
4 | automatic backdrop scrolling ^^<br />
3 | automatic backdrop scrolling <<<br />
2 | - (ignored/unused)<br />
1 | x-scrolling backdrop<br />
0 | x- and y-scrolling backdrop<br />
<br />
Bits 6 and 7 require an alternate backdrop index. The files where these bits are set are also the only files that have a non-zero alternate backdrop number.<br />
<br />
Note that the backdrop/scrolling related bits can NOT be combined! For example, combining bits 3 and 4 will not lead to combined movement (it results in a somewhat jerky/broken horizontal parallax scrolling instead). Combining one of bits 3 or 4 with any of the bits 1 or 0 also leads to broken/wonky results.<br />
<br />
The earthquake flag (bit 5) can be freely combined with any other bits.<br />
<br />
Combining the backdrop switch flags sort of works, but isn't really practical since there can only be two different backdrops. If both bits are set and Duke steps into a teleporter, the backdrop will switch and additionally flash as if a reactor was destroyed. If Duke destroys a reactor before stepping into a teleporter, the backdrop will switch, but will not switch a 2nd time when using a teleporter afterwards. Teleporting back again will switch, and restore the normal backdrop switching behavior.<br />
<br />
In order for either of the backdrop switch flags to work, the backdrop scroll mode needs to be set to x parallax (bit 1). For any other backdrop scroll mode, the backdrop will not switch.<br />
<br />
== Foreground and Background Layers ==<br />
<br />
At the offset indicated by <tt>iBackgroundOffset</tt> in the header, the grid/cell data begins:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iMapWidth||Map width (in tiles)<br />
|-<br />
|UINT16LE iMapData[32750]||Actual map data<br />
|}<br />
<br />
Each "element" in <tt>iMapData</tt> refers to the foreground and/or background tile used in a single grid cell. The grids are arranged left to right, top to bottom, so the index can be calculated by this formula:<br />
<br />
int iIndex = (y * iMapWidth) + x;<br />
iMapData[iIndex] = <new value to set at x,y><br />
<br />
The map data is a constant 32750 UINT16LE cells long (65500 bytes), so the map height can be calculated from this:<br />
<br />
int iMapHeight = 32750 / iMapWidth;<br />
<br />
=== Mapping cell values to tiles ===<br />
<br />
The method of mapping elements in the <tt>iMapData</tt> structure into tiles is a little complicated. There are two different methods of storing values:<br />
<br />
# If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell contains both a foreground and background tile.<br />
# Otherwise the cell value is a memory offset into the tilemap.<br />
<br />
==== Most significant bit set ====<br />
<br />
If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell value contains two indices - one for the solid/background tile, and one for the masked/foreground tile. Unlike the other type of cell value, these are actual tile indices as opposed to memory offsets (i.e. a value of 2 refers to the third tile.)<br />
<br />
The lower ten bits are the index of the background tile (this can provide a value between 0 and 1023, however since there are only 1000 tiles this value should always be less than 1000.) The lower ten bits can be isolated like this:<br />
<br />
iSolid = iMapData[x] & 0x03FF;<br />
<br />
The ignoring the most significant bit (which is used to indicate this type of tile value) the remaining five most significant bits are used as the index of the foreground/mask tile. These bits can be isolated as follows:<br />
<br />
iMask = (iMapData[x] >> 10) & 0x1F;<br />
<br />
This only provides a value between 0 and 31 - but since there are 160 masked tiles, this value needs to be further manipulated to produce an index into the masked tileset. An additional two bits per tile are stored in the [[#Supplemental foreground data]] which are used for this purpose, but this will still only allow the first 127 tiles to be used.<br />
<br />
==== Most significant bit NOT set ====<br />
<br />
If the most significant bit is not set, then the cell value is a memory offset into the tileset images as loaded by the game. It can either refer to a solid or a masked tile, and converting the value to a tile index is slightly different for each case. Values greater than or equal to 8000 refer to masked tiles, smaller values refer to solid tiles. The cell value will be zero for the first solid tile, it will be eight for the second solid tile, it will be 7992 for the last solid tile, it will be 8000 for the first masked tile, 8040 for the second masked tile, and it will be 14,360 for the last masked tile.<br />
<br />
To convert the value to an index into the tileset, first check if it's solid or masked. If the former, divide the value by eight. If the latter, subtract 8000, then divide by 40. This will give you a zero-based index into either the solid or masked tileset, with 0 referring to the very first tile of each tileset. Alternatively, if you'd like to combine solid and masked tiles into a single image, divide by eight instead of subtracting 8000 for the masked tile case.<br />
<br />
Why are the cell values like this? This is because of the way the game draws the tiles. Note that in the CZone tileset file, the solid tiles are made up of 1000 4-plane (16-colour) images, and these are followed by 160 5-plane images (16-colour + transparency.) The game loads the solid tile images into video memory, and then copies from video memory to video memory when drawing (using a technique called "latch copy" which allows the graphics card to copy 4 planes of data in one go). But the masked tile image data is kept in system memory, and copied from there to video memory when drawing. As a consequence of this, solid tiles are 8 bytes apart in memory, since only one plane of EGA/VGA memory is visible to the CPU at any one time, and the size of a single plane of an 8x8-pixel image is 8 bytes. The masked tiles on the other hand are 40 bytes apart in memory, since each tile occupies 5 planes of 8 bytes each. Cell values which reference solid tiles are directly used as memory offset into the solid tile data living in video memory. References to masked tiles are first subtracted by 8000, and then used as memory offset into the masked tile data living in system RAM. During drawing, the game compares each cell value against 8000 to determine if it's a solid or masked tile.<br />
<br />
Note that while the cell value cannot be out of range for the solid tiles/background layer (since any values larger than 8000 will be loaded from the masked tileset into the foreground layer) the cell values for the masked tileset have no such restriction.<br />
<br />
=== Supplemental foreground data ===<br />
<br />
Since the tiles containing combined foreground+background data only provide five bits for the foreground tile, this block of data is used to store an additional two bits for each tile, allowing a seven-bit tile number to be specified.<br />
<br />
The data is stored in a form of run length encoding (RLE) to reduce the size, given that a relatively small number of tiles need the extra two bits. Because there are eight bits in a byte, four lots of two-bit-chunks can be stored in every data byte. This means each data byte contains the data for four tiles. The rest of this section uses the term "tile cluster" to refer to this grouping of four tiles.<br />
<br />
==== Data layout ====<br />
<br />
The data is read one byte at a time, and the initial byte ("length-byte") indicates how many subsequent bytes need to be read. The length-bytes are ''signed'' byte values with the absolute value giving the length to be used for the following data. If the length-byte is positive (high bit is NOT set), then it is a count of the number of affected tile clusters. The following byte is the data byte containing the extra tile data. For example: (brackets inserted for clarity)<br />
<br />
[7F 00] [7F 00] [05 FF] [7F 00]<br />
<br />
This means apply value 00 to the first 254 (0x7F * 2) tile clusters, followed by value FF to the next five tile clusters, followed by 00 to the next 127 tile clusters. The format of these "values" is described below.<br />
<br />
If the length-byte is negative (high bit IS set), then a number of data values follow, depending on the absolute value of the length-byte. If the bytes are arranged like this:<br />
<br />
[FF 12] [FE 34 56] [FF 78] [FD 9A BC DE] [00 00]<br />
<br />
Then it would be interpreted as follows:<br />
<br />
# FF == one - apply value 12 to one tile cluster<br />
# FE == two - apply 34 to one tile cluster, then 56 to the following tile cluster<br />
# FF == one - apply 78 to one tile cluster<br />
# FD == three - apply 9A to one tile cluster, BC to the next, and DE to the third tile cluster<br />
# 00 == end of data<br />
<br />
An easy way to work out how many bytes are affected is this formula:<br />
<br />
iCount = 0x100 - iInitialByte<br />
<br />
This will return one for 0xFF, two for 0xFE, three for 0xFD, etc. If your variables are signed bytes, you can just use <tt>iCount = -iInitialByte</tt> instead. A loop can then be used to apply the change to the correct number of tile clusters.<br />
<br />
The last two bytes of the compressed data are always zero, which would always be read when the RLE algorithm expects to read a length-byte. So it should be safe to stop decompressing when you read 0 as length value.<br />
<br />
Note that since the length is the absolute value of a signed byte, the maximum length for both kinds of RLE flag is 127. A length-byte of 0x80 indicates an error. This is because 0x80 (-128) can't have a positive absolute value as the largest positive value in a signed byte is 127. If the game encounters a length-byte of 0x80 upon loading a level file, it will freeze after fading to black from the loading screen.<br />
<br />
==== Data values ====<br />
<br />
Each of the data values used above affects a tile cluster made up of four tiles. If the tiles are arranged from left to right, the least-significant bits affect the first (left-most) tile, and the most-significant bits affect the last (right-most) tile. For example:<br />
<br />
tile1 = (iChange & 0x03);<br />
tile2 = ((iChange >> 2) & 0x03);<br />
tile3 = ((iChange >> 4) & 0x03);<br />
tile4 = (iChange >> 6);<br />
<br />
This will give tileX a value between 0 and 3, which when multiplied by 32 can be directly added to the value of the foreground tile. For the computer scientists out there, a more efficient alternative is this:<br />
<br />
tile1 = (iChange << 5) & 0x60;<br />
tile2 = (iChange << 3) & 0x60;<br />
tile3 = (iChange << 1) & 0x60;<br />
tile4 = (iChange >> 1) & 0x60;<br />
<br />
The values here are already multiplied out, and can be OR'd with the foreground tile numbers.<br />
<br />
One major thing to remember is that these extra values ONLY apply to those tiles where a foreground and a background tile is specified in the map data. If the tile only has a background tile, or it only has a foreground tile, then it's possible to specify the full range of tile numbers and the values stored here (if any) should be ignored for those tiles. It's only when a tile has a foreground *and* a background tile that these extra bits must be used.<br />
<br />
==== Example ====<br />
<br />
This is some example code to read in all the tile values into an array, which the map code can later reference as necessary:<br />
<br />
int *iExtraValues = new int[65500 / 2]; // one element per tile, maps are fixed length<br />
int *pNextByte = iExtraValues; // running counter<br />
<br />
// Read through all the extra data<br />
for (int i = 0; i < iExtraFGLength; i++) {<br />
unsigned char iVal = readNextByte(); // must be 8-bit for logic below to work<br />
<br />
if (iVal & 0x80) {<br />
// Multiple bytes concatenated together<br />
// iVal == 0xFF for one byte, 0xFE for two bytes, etc.<br />
while (iVal++ > 0) { // should eventually wrap from 0xFF to 0x00 when we're done - only works with an 8-bit variable though<br />
applyChange(&pNextByte, readNextByte());<br />
i++;<br />
assert(iVal < 0x100); // prevent headaches if someone uses an int instead<br />
}<br />
} else {<br />
// iVal <= 0x7F<br />
UINT8 iChange = readNextByte(); i++;<br />
<br />
if (iChange > 0) {<br />
// Apply this change iVal times<br />
while (iVal-- > 0) applyChange(&pNextByte, iChange);<br />
} else {<br />
// No changes to the tiles, just skip over them (faster than applying a<br />
// change of 0x00 to all these tiles!)<br />
pNextByte += iVal * 4;<br />
// NOTE: This will only work if you zero the array first - if you don't,<br />
// use a loop here to zero out the tiles instead.<br />
}<br />
}<br />
}<br />
<br />
inline void applyChange(int **ppNextByte, int iChange)<br />
throw ()<br />
{<br />
// Grab each lot of 2-bits and shift into bits 5&6<br />
**ppNextByte = (iChange << 5) & 0x60; (*ppNextByte)++; // 10 -> 65<br />
**ppNextByte = (iChange << 3) & 0x60; (*ppNextByte)++; // 32 -> 65<br />
**ppNextByte = (iChange << 1) & 0x60; (*ppNextByte)++; // 54 -> 65<br />
**ppNextByte = (iChange >> 1) & 0x60; (*ppNextByte)++; // 76 -> 65<br />
return;<br />
}<br />
<br />
=== Example ===<br />
<br />
The following code converts a cell value into two tile indices for that cell - an index into the solid/background tilemap, and another index into the mask/foreground tileset.<br />
<br />
#define DN2_NUM_SOLID_TILES 1000<br />
#define DN2_NUM_MASKED_TILES 160<br />
#define DN2_TILEWIDTH 8 // Tiles are 8x8<br />
<br />
int iSolid, iMasked; // Index into respective tilesets<br />
int iValue = iMapData[x]; // This is the cell to convert<br />
int iExtra = iExtraData[x]; // Extra data for this cell<br />
<br />
<b>if (iValue & 0x8000) {</b><br />
// Most significant bit is set (see [[#Most significant bit set]] above),<br />
// so this cell has a foreground *and* a background tile.<br />
<br />
// First 10 bits are the index of the solid tile<br />
iSolid = iValue & 0x3FF;<br />
<br />
// Remaining five bits (not counting the sixth bit which was used as<br />
// the 0x8000 flag above) are the index of the mask tile.<br />
iMasked = ((iValue >> 10) & 0x1F);<br />
<br />
// Add the extra two bits (see [[#Supplemental foreground data]] above)<br />
iMasked |= iExtra; // assumes iExtra has already been multiplied out<br />
//iMasked = iMasked + (iExtra * 32); // if it hasn't been multiplied out<br />
<br />
<b>} else if (iValue < DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) {</b><br />
// Background only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from memory offset into a tile index.<br />
iSolid = iValue / 8;<br />
<br />
iMasked = -1; // No mask tile in this cell<br />
<br />
<b>} else {</b><br />
// Foreground only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a memory offset into a tile index.<br />
// Make the first masked tile start at zero.<br />
int iRawIndex = (iValue - DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) / 40;<br />
<br />
// No solid tile in this cell, so using zero will make the solid cell<br />
// transparent, allowing the map backdrop to show through.<br />
iSolid = 0;<br />
}<br />
<br />
=== Gotchas ===<br />
<br />
* The masked tileset has no "blank" tile, so if the foreground layer is being loaded into an array a value will need to be used to indicate that no foreground tile occupies that cell. Zero can't be used without tweaking, as masked tile zero is an actual graphic tile.<br />
<br />
* The first tile in the solid tileset is used as a transparent tile. It will appear as a red square if drawn (e.g. in a map editor) however the game does not draw this tile, so any cells with this as the background cell will be where the map backdrop shows through.<br />
<br />
* Even with the supplemental foreground data, there are only seven bits available for the foreground tile. This means that only 127 of the 160 available tiles can be used!<br />
<br />
== Actor data ==<br />
<br />
The <tt>actorData</tt> block is in the following format:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iType||Type of actor<br />
|-<br />
|UINT16LE iX||X-coordinate of actor (in tile units)<br />
|-<br />
|UINT16LE iY||Y-coordinate of actor (in tile units)<br />
|}<br />
<br />
Because the <tt>iActorSize</tt> value in the header is in UINT16s and there are three UINT16s per actor, the number of actors can be obtained quite simply:<br />
<br />
iNumActors = iActorSize / 3<br />
<br />
The <tt>iType</tt> field can conveniently be used as an index into the [[Duke Nukem II Actor Info|ACTRINFO.MNI]] sprite file. If <tt>iType</tt> is zero, the first actor image is used.<br />
<br />
Not all actors have images however, some point to actor sprites with zero frames. It appears to be that some of these "actors" are actually flags, indicating that the next actor (in the level, not in the <tt>ACTRINFO.MNI</tt>) will only appear when the level is played at a certain difficulty level.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=map}}<br />
{{FileFormatTool<br />
| Name = [http://chidesters.org/scottchidester.com/index.html Nukem2Print]<br />
| Platform = Qt<br />
| canView = Yes<br />
| canCreate = No<br />
| canModify = No<br />
| editHidden = N/A<br />
| editMetadata = N/A<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [http://www.szevvy.com Szevvy] figured out how to map the cell values back to the tilesets. [[User:Malvineous|Malvineous]] figured out the format of the supplemental foreground data. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Map_Format&diff=10766Duke Nukem II Map Format2022-10-25T14:03:56Z<p>Lethal guitar: Update instructions for converting cell values to take into account how the game actually functions</p>
<hr />
<div>{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 3<br />
| Tile size = 8&times;8<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
[[Duke Nukem II]] stores its levels in an evolution of the level format used by [[Cosmo's Cosmic Adventures]]. One considerable change is the addition of a second map layer, allowing foreground and background tiles to be placed in the same cell. Surprisingly, given the number of other changes to the file format, the layer data was not restructured to handle this new feature. Instead, some of the new data was shoehorned to fit in the existing space, with the rest of the data tacked on to the end of the file in a somewhat clunky manner. This resulted in some unusual limitations, such as a small number of tiles that can only be placed in the foreground layer if there is no background tile in the same cell.<br />
<br />
== File format ==<br />
The file is in this basic layout:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iBackgroundOffset||Offset where the background layer starts<br />
|-<br />
|char cCZoneName[13]||CZone filename (tileset)<br />
|-<br />
|char cBackdropName[13]||Name of the graphic to use as the level background (one of the <tt>BD*.MNI</tt> files)<br />
|-<br />
|char cMusicName[13]||Name of the [[IMF Format|IMF file]] to use as background music<br />
|-<br />
|BYTE bFlags||Level flags (define type of parallax scrolling and more)<br />
|-<br />
|BYTE bAltBackdrop||Number of alternate backdrop file, zero if no alternate backdrop is used<br />
|-<br />
|BYTE iUnused[2]||Ignored. Likely a holdover from the game's development<br />
|-<br />
|UINT16LE iActorSize||Number of UINT16 values in the actor block<br />
|-<br />
|ACTORDATA actorData[]||Variable-length array of all the actors in the level<br />
|-<br />
|GRIDLAYER gridData||see [[#Foreground and Background Layers]] below for what this contains<br />
|-<br />
|UINT16LE iExtraFGLength||Length of the additional mask/foreground manipulation data<br />
|-<br />
|BYTE iExtraFGData[iExtraFGLength]||See [[#Supplemental foreground data]] below<br />
|-<br />
|char cAttrName[13]||Zone attribute filename<br />
|-<br />
|char cTileName[13]||Zone tile filename<br />
|-<br />
|char cMaskName[13]||Zone masked tiles filename<br />
|}<br />
<br />
The three filenames at the end of the level file never appear to be used. In fact, the level files can end directly after the supplemental foreground data and the game will still load them without reporting any errors. These are probably the names of Cosmo-style tileset files ("zones") that were used before the CZone format (the "c" probably stands for "compound") was introduced.<br />
<br />
For all level files of the full version, the iActorSize value is reliable and the gridData begins directly after the last actor, leaving no padding between actor data and grid data.<br />
<br />
== Level Flags ==<br />
<br />
This is what each bit in the bFlags value indicates:<br />
<br />
Bit | Description<br />
----+------------<br />
7 | switch backdrop when using teleporter (used in L1)<br />
6 | switch backdrop when destroying force field (used in L5)<br />
5 | earthquake<br />
4 | automatic backdrop scrolling ^^<br />
3 | automatic backdrop scrolling <<<br />
2 | - (ignored/unused)<br />
1 | x-scrolling backdrop<br />
0 | x- and y-scrolling backdrop<br />
<br />
Bits 6 and 7 require an alternate backdrop index. The files where these bits are set are also the only files that have a non-zero alternate backdrop number.<br />
<br />
Note that the backdrop/scrolling related bits can NOT be combined! For example, combining bits 3 and 4 will not lead to combined movement (it results in a somewhat jerky/broken horizontal parallax scrolling instead). Combining one of bits 3 or 4 with any of the bits 1 or 0 also leads to broken/wonky results.<br />
<br />
The earthquake flag (bit 5) can be freely combined with any other bits.<br />
<br />
Combining the backdrop switch flags sort of works, but isn't really practical since there can only be two different backdrops. If both bits are set and Duke steps into a teleporter, the backdrop will switch and additionally flash as if a reactor was destroyed. If Duke destroys a reactor before stepping into a teleporter, the backdrop will switch, but will not switch a 2nd time when using a teleporter afterwards. Teleporting back again will switch, and restore the normal backdrop switching behavior.<br />
<br />
In order for either of the backdrop switch flags to work, the backdrop scroll mode needs to be set to x parallax (bit 1). For any other backdrop scroll mode, the backdrop will not switch.<br />
<br />
== Foreground and Background Layers ==<br />
<br />
At the offset indicated by <tt>iBackgroundOffset</tt> in the header, the grid/cell data begins:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iMapWidth||Map width (in tiles)<br />
|-<br />
|UINT16LE iMapData[32750]||Actual map data<br />
|}<br />
<br />
Each "element" in <tt>iMapData</tt> refers to the foreground and/or background tile used in a single grid cell. The grids are arranged left to right, top to bottom, so the index can be calculated by this formula:<br />
<br />
int iIndex = (y * iMapWidth) + x;<br />
iMapData[iIndex] = <new value to set at x,y><br />
<br />
The map data is a constant 32750 UINT16LE cells long (65500 bytes), so the map height can be calculated from this:<br />
<br />
int iMapHeight = 32750 / iMapWidth;<br />
<br />
=== Mapping cell values to tiles ===<br />
<br />
The method of mapping elements in the <tt>iMapData</tt> structure into tiles is a little complicated. There are two different methods of storing values:<br />
<br />
# If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell contains both a foreground and background tile.<br />
# Otherwise the cell value is a memory offset into the tilemap.<br />
<br />
==== Most significant bit set ====<br />
<br />
If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell value contains two indices - one for the solid/background tile, and one for the masked/foreground tile. Unlike the other type of cell value, these are actual tile indices as opposed to memory offsets (i.e. a value of 2 refers to the third tile.)<br />
<br />
The lower ten bits are the index of the background tile (this can provide a value between 0 and 1023, however since there are only 1000 tiles this value should always be less than 1000.) The lower ten bits can be isolated like this:<br />
<br />
iSolid = iMapData[x] & 0x03FF;<br />
<br />
The ignoring the most significant bit (which is used to indicate this type of tile value) the remaining five most significant bits are used as the index of the foreground/mask tile. These bits can be isolated as follows:<br />
<br />
iMask = (iMapData[x] >> 10) & 0x1F;<br />
<br />
This only provides a value between 0 and 31 - but since there are 160 masked tiles, this value needs to be further manipulated to produce an index into the masked tileset. An additional two bits per tile are stored in the [[#Supplemental foreground data]] which are used for this purpose, but this will still only allow the first 127 tiles to be used.<br />
<br />
==== Most significant bit NOT set ====<br />
<br />
If the most significant bit is not set, then the cell value is a memory offset into the tileset images as loaded by the game. It can either refer to a solid or a masked tile, and converting the value to a tile index is slightly different for each case. Values greater than or equal to 8000 refer to masked tiles, smaller values refer to solid tiles. The cell value will be zero for the first solid tile, it will be eight for the second solid tile, it will be 7992 for the last solid tile, it will be 8000 for the first masked tile, 8040 for the second masked tile, and it will be 14,360 for the last masked tile.<br />
<br />
To convert the value to an index into the tileset, first check if it's solid or masked. If the former, divide the value by eight. If the latter, subtract 8000, then divide by 40. This will give you a zero-based index into either the solid or masked tileset, with 0 referring to the very first tile of each tileset. Alternatively, if you'd like to combine solid and masked tiles into a single image, divide by eight instead of subtracting 8000 for the masked tile case.<br />
<br />
Why are the cell values like this? This is because of the way the game draws the tiles. Note that in the CZone tileset file, the solid tiles are made up of 1000 4-plane (16-colour) images, and these are followed by 160 5-plane images (16-colour + transparency.) The game loads the solid tile images into video memory, and then copies from video memory to video memory when drawing (using a technique called "latch copy" which allows the graphics card to copy 4 planes of data in one go). But the masked tile image data is kept in system memory, and copied from there to video memory when drawing. As a consequence of this, solid tiles are 8 bytes apart in memory, since only one plane of EGA/VGA memory is visible to the CPU at any on time, and the size of a single plane of an 8x8-pixel image is 8 bytes. The masked tiles on the other hand are 40 bytes apart in memory, since each tile occupies 5 planes of 8 bytes each. Cell values which reference solid tiles are directly used as memory offset into the solid tile data living in video memory. References to masked tiles are first subtracted by 8000, and then used as memory offset into the masked tile data living in system RAM. During drawing, the game compares each cell value against 8000 to determine if it's a solid or masked tile.<br />
<br />
Note that while the cell value cannot be out of range for the solid tiles/background layer (since any values larger than 8000 will be loaded from the masked tileset into the foreground layer) the cell values for the masked tileset have no such restriction.<br />
<br />
=== Supplemental foreground data ===<br />
<br />
Since the tiles containing combined foreground+background data only provide five bits for the foreground tile, this block of data is used to store an additional two bits for each tile, allowing a seven-bit tile number to be specified.<br />
<br />
The data is stored in a form of run length encoding (RLE) to reduce the size, given that a relatively small number of tiles need the extra two bits. Because there are eight bits in a byte, four lots of two-bit-chunks can be stored in every data byte. This means each data byte contains the data for four tiles. The rest of this section uses the term "tile cluster" to refer to this grouping of four tiles.<br />
<br />
==== Data layout ====<br />
<br />
The data is read one byte at a time, and the initial byte ("length-byte") indicates how many subsequent bytes need to be read. The length-bytes are ''signed'' byte values with the absolute value giving the length to be used for the following data. If the length-byte is positive (high bit is NOT set), then it is a count of the number of affected tile clusters. The following byte is the data byte containing the extra tile data. For example: (brackets inserted for clarity)<br />
<br />
[7F 00] [7F 00] [05 FF] [7F 00]<br />
<br />
This means apply value 00 to the first 254 (0x7F * 2) tile clusters, followed by value FF to the next five tile clusters, followed by 00 to the next 127 tile clusters. The format of these "values" is described below.<br />
<br />
If the length-byte is negative (high bit IS set), then a number of data values follow, depending on the absolute value of the length-byte. If the bytes are arranged like this:<br />
<br />
[FF 12] [FE 34 56] [FF 78] [FD 9A BC DE] [00 00]<br />
<br />
Then it would be interpreted as follows:<br />
<br />
# FF == one - apply value 12 to one tile cluster<br />
# FE == two - apply 34 to one tile cluster, then 56 to the following tile cluster<br />
# FF == one - apply 78 to one tile cluster<br />
# FD == three - apply 9A to one tile cluster, BC to the next, and DE to the third tile cluster<br />
# 00 == end of data<br />
<br />
An easy way to work out how many bytes are affected is this formula:<br />
<br />
iCount = 0x100 - iInitialByte<br />
<br />
This will return one for 0xFF, two for 0xFE, three for 0xFD, etc. If your variables are signed bytes, you can just use <tt>iCount = -iInitialByte</tt> instead. A loop can then be used to apply the change to the correct number of tile clusters.<br />
<br />
The last two bytes of the compressed data are always zero, which would always be read when the RLE algorithm expects to read a length-byte. So it should be safe to stop decompressing when you read 0 as length value.<br />
<br />
Note that since the length is the absolute value of a signed byte, the maximum length for both kinds of RLE flag is 127. A length-byte of 0x80 indicates an error. This is because 0x80 (-128) can't have a positive absolute value as the largest positive value in a signed byte is 127. If the game encounters a length-byte of 0x80 upon loading a level file, it will freeze after fading to black from the loading screen.<br />
<br />
==== Data values ====<br />
<br />
Each of the data values used above affects a tile cluster made up of four tiles. If the tiles are arranged from left to right, the least-significant bits affect the first (left-most) tile, and the most-significant bits affect the last (right-most) tile. For example:<br />
<br />
tile1 = (iChange & 0x03);<br />
tile2 = ((iChange >> 2) & 0x03);<br />
tile3 = ((iChange >> 4) & 0x03);<br />
tile4 = (iChange >> 6);<br />
<br />
This will give tileX a value between 0 and 3, which when multiplied by 32 can be directly added to the value of the foreground tile. For the computer scientists out there, a more efficient alternative is this:<br />
<br />
tile1 = (iChange << 5) & 0x60;<br />
tile2 = (iChange << 3) & 0x60;<br />
tile3 = (iChange << 1) & 0x60;<br />
tile4 = (iChange >> 1) & 0x60;<br />
<br />
The values here are already multiplied out, and can be OR'd with the foreground tile numbers.<br />
<br />
One major thing to remember is that these extra values ONLY apply to those tiles where a foreground and a background tile is specified in the map data. If the tile only has a background tile, or it only has a foreground tile, then it's possible to specify the full range of tile numbers and the values stored here (if any) should be ignored for those tiles. It's only when a tile has a foreground *and* a background tile that these extra bits must be used.<br />
<br />
==== Example ====<br />
<br />
This is some example code to read in all the tile values into an array, which the map code can later reference as necessary:<br />
<br />
int *iExtraValues = new int[65500 / 2]; // one element per tile, maps are fixed length<br />
int *pNextByte = iExtraValues; // running counter<br />
<br />
// Read through all the extra data<br />
for (int i = 0; i < iExtraFGLength; i++) {<br />
unsigned char iVal = readNextByte(); // must be 8-bit for logic below to work<br />
<br />
if (iVal & 0x80) {<br />
// Multiple bytes concatenated together<br />
// iVal == 0xFF for one byte, 0xFE for two bytes, etc.<br />
while (iVal++ > 0) { // should eventually wrap from 0xFF to 0x00 when we're done - only works with an 8-bit variable though<br />
applyChange(&pNextByte, readNextByte());<br />
i++;<br />
assert(iVal < 0x100); // prevent headaches if someone uses an int instead<br />
}<br />
} else {<br />
// iVal <= 0x7F<br />
UINT8 iChange = readNextByte(); i++;<br />
<br />
if (iChange > 0) {<br />
// Apply this change iVal times<br />
while (iVal-- > 0) applyChange(&pNextByte, iChange);<br />
} else {<br />
// No changes to the tiles, just skip over them (faster than applying a<br />
// change of 0x00 to all these tiles!)<br />
pNextByte += iVal * 4;<br />
// NOTE: This will only work if you zero the array first - if you don't,<br />
// use a loop here to zero out the tiles instead.<br />
}<br />
}<br />
}<br />
<br />
inline void applyChange(int **ppNextByte, int iChange)<br />
throw ()<br />
{<br />
// Grab each lot of 2-bits and shift into bits 5&6<br />
**ppNextByte = (iChange << 5) & 0x60; (*ppNextByte)++; // 10 -> 65<br />
**ppNextByte = (iChange << 3) & 0x60; (*ppNextByte)++; // 32 -> 65<br />
**ppNextByte = (iChange << 1) & 0x60; (*ppNextByte)++; // 54 -> 65<br />
**ppNextByte = (iChange >> 1) & 0x60; (*ppNextByte)++; // 76 -> 65<br />
return;<br />
}<br />
<br />
=== Example ===<br />
<br />
The following code converts a cell value into two tile indices for that cell - an index into the solid/background tilemap, and another index into the mask/foreground tileset.<br />
<br />
#define DN2_NUM_SOLID_TILES 1000<br />
#define DN2_NUM_MASKED_TILES 160<br />
#define DN2_TILEWIDTH 8 // Tiles are 8x8<br />
<br />
int iSolid, iMasked; // Index into respective tilesets<br />
int iValue = iMapData[x]; // This is the cell to convert<br />
int iExtra = iExtraData[x]; // Extra data for this cell<br />
<br />
<b>if (iValue & 0x8000) {</b><br />
// Most significant bit is set (see [[#Most significant bit set]] above),<br />
// so this cell has a foreground *and* a background tile.<br />
<br />
// First 10 bits are the index of the solid tile<br />
iSolid = iValue & 0x3FF;<br />
<br />
// Remaining five bits (not counting the sixth bit which was used as<br />
// the 0x8000 flag above) are the index of the mask tile.<br />
iMasked = ((iValue >> 10) & 0x1F);<br />
<br />
// Add the extra two bits (see [[#Supplemental foreground data]] above)<br />
iMasked |= iExtra; // assumes iExtra has already been multiplied out<br />
//iMasked = iMasked + (iExtra * 32); // if it hasn't been multiplied out<br />
<br />
<b>} else if (iValue < DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) {</b><br />
// Background only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from memory offset into a tile index.<br />
iSolid = iValue / 8;<br />
<br />
iMasked = -1; // No mask tile in this cell<br />
<br />
<b>} else {</b><br />
// Foreground only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a memory offset into a tile index.<br />
// Make the first masked tile start at zero.<br />
int iRawIndex = (iValue - DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) / 40;<br />
<br />
// No solid tile in this cell, so using zero will make the solid cell<br />
// transparent, allowing the map backdrop to show through.<br />
iSolid = 0;<br />
}<br />
<br />
=== Gotchas ===<br />
<br />
* The masked tileset has no "blank" tile, so if the foreground layer is being loaded into an array a value will need to be used to indicate that no foreground tile occupies that cell. Zero can't be used without tweaking, as masked tile zero is an actual graphic tile.<br />
<br />
* The first tile in the solid tileset is used as a transparent tile. It will appear as a red square if drawn (e.g. in a map editor) however the game does not draw this tile, so any cells with this as the background cell will be where the map backdrop shows through.<br />
<br />
* Even with the supplemental foreground data, there are only seven bits available for the foreground tile. This means that only 127 of the 160 available tiles can be used!<br />
<br />
== Actor data ==<br />
<br />
The <tt>actorData</tt> block is in the following format:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iType||Type of actor<br />
|-<br />
|UINT16LE iX||X-coordinate of actor (in tile units)<br />
|-<br />
|UINT16LE iY||Y-coordinate of actor (in tile units)<br />
|}<br />
<br />
Because the <tt>iActorSize</tt> value in the header is in UINT16s and there are three UINT16s per actor, the number of actors can be obtained quite simply:<br />
<br />
iNumActors = iActorSize / 3<br />
<br />
The <tt>iType</tt> field can conveniently be used as an index into the [[Duke Nukem II Actor Info|ACTRINFO.MNI]] sprite file. If <tt>iType</tt> is zero, the first actor image is used.<br />
<br />
Not all actors have images however, some point to actor sprites with zero frames. It appears to be that some of these "actors" are actually flags, indicating that the next actor (in the level, not in the <tt>ACTRINFO.MNI</tt>) will only appear when the level is played at a certain difficulty level.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=map}}<br />
{{FileFormatTool<br />
| Name = [http://chidesters.org/scottchidester.com/index.html Nukem2Print]<br />
| Platform = Qt<br />
| canView = Yes<br />
| canCreate = No<br />
| canModify = No<br />
| editHidden = N/A<br />
| editMetadata = N/A<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [http://www.szevvy.com Szevvy] figured out how to map the cell values back to the tilesets. [[User:Malvineous|Malvineous]] figured out the format of the supplemental foreground data. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_CZone_Format&diff=10764Duke Nukem II CZone Format2022-10-25T09:39:41Z<p>Lethal guitar: Explain why masked tile attributes have 5 values per tile</p>
<hr />
<div>{{Tileset Infobox<br />
| Hardware1 = EGA<br />
| MaxTiles = Fixed<br />
| Palette = External<br />
| Names = N<br />
| TileMinSize = 8&times;8<br />
| TileMaxSize = 8&times;8<br />
| NumPlanes = 4-5<br />
| PlaneArrangement = [[Raw EGA data#Byte-planar EGA data|Byte-planar]]<br />
| HasTransparency = Y<br />
| HasHitmap = N<br />
| Metadata = None<br />
| Subtilesets = N<br />
| Compressed = N<br />
| Hidden = N<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The CZone format stores three blocks of data - the behaviour of each tile in a tileset, a collection of images for the "solid" background tiles, and a collection of images for the "masked" foreground tiles.<br />
<br />
== File format ==<br />
<br />
{|class="wikitable"<br />
! Data type !! Name !! Description<br />
|-<br />
| BYTE[3600] || attributeData || Attribute data<br />
|-<br />
| BYTE[32000] || solidTileset || Graphics for the solid tileset<br />
|-<br />
| BYTE[6400] || maskedTileset || Graphics for the masked tileset<br />
|}<br />
<br />
=== Attribute Data ===<br />
<br />
The attribute data consists of 1000 [[UINT16LE]] values (settings for the solid tiles), followed by 800 [[UINT16LE]] values (five values for each masked tile). ''Only the first value'' for each masked tile is actually used by the game. The remaining 4 values are padding, which simplifies looking up tile attributes in the game's code. Tile references specified in the level files are actually memory offsets, with solid tile references being multiples of 8, and masked tile references being multiples of 40. By padding out the masked tile attributes to 5 values per tile, a tile reference can simply be divided by 8 to get a correct index into the tile attribute array for either solid or masked tiles (since 8/8 is 1, and 40/8 is 5).<br />
<br />
Each individual attribute value is a bitmask. The meaning of the bits is as follows:<br />
<br />
{|class="wikitable"<br />
! Bit !! Description<br />
|-<br />
| 15 || -<br />
|-<br />
| 14 || climbable (chains/ladders)<br />
|-<br />
| 13 || -<br />
|-<br />
| 12 || -<br />
|-<br />
| 11 || -<br />
|-<br />
| 10 || fast animation<br />
|-<br />
| 9 || conveyor belt -><br />
|-<br />
| 8 || conveyor belt <-<br />
|-<br />
| 7 || can hang on it (pipes, vines etc.)<br />
|-<br />
| 6 || burns away when shot<br />
|-<br />
| 5 || draw in front<br />
|-<br />
| 4 || animate (uses current tile and next 3 tiles)<br />
|-<br />
| 3 || solid/blocking right<br />
|-<br />
| 2 || solid/blocking left<br />
|-<br />
| 1 || solid/blocking up<br />
|-<br />
| 0 || solid/blocking down<br />
|}<br />
<br />
Bits 11, 12, 13, and 15 are not used by the game, and simply ignored if they are set. The tileset "czone9" has bit 15 set on some of its tiles (horns), but it has no effect. Most likely a leftover from an earlier point in the game's development.<br />
<br />
=== Solid Tileset ===<br />
<br />
The solid tileset is comprised of 1000 tiles. Each tile is 8x8 pixels, and for best effect when presenting it to a user/designer it should be displayed in a grid 40 tiles wide by 25 tiles high.<br />
<br />
The tiles are stored in [[Cosmo Tileset Format]]. The first plane is blue, followed by green, red and then intensity.<br />
<br />
Since the tiles are 8x8, each plane's scanline fits into one byte. At eight pixels tall that makes each plane occupy eight bytes, and with four planes the whole tile takes up 32 bytes. This means you can seek to any tile with a simple formula:<br />
<br />
iOffset = iTileIndex * 32<br />
<br />
=== Masked Tileset ===<br />
<br />
The masked tileset is almost identical to the solid tileset, except that there are only 160 tiles (so only four rows high, but still 40 tiles wide) and of course being a masked tileset, there are five planes. The first plane is transparency, followed by blue, green, red and intensity as with the solid tiles.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [[User:Lethal_guitar|lethal guitar]] verified that unknown/unused tile attribute bits are not used by the game, and added some details. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II&diff=10708Duke Nukem II2022-09-26T14:54:38Z<p>Lethal guitar: </p>
<hr />
<div>{{Game Infobox<br />
| Levels = Edit<br />
| Tiles = Edit<br />
| Sprites = Edit<br />
| Fullscreen = Edit<br />
| Sound = Edit<br />
| Music = Edit<br />
| Text = Some<br />
| Story = Edit<br />
| Interface = Edit<br />
| Demo = No<br />
}}<br />
<br />
[[Duke Nukem II]] is a 256-colour VGA game based on the [[Cosmo's Cosmic Adventures]] engine. Although the cutscenes are 256-colour, the game itself is only 16-colour and runs in an EGA video mode. It does however change the VGA palette such that the 16 colours in use are very different to those normally used by EGA games, successfully tricking most people into believing the game itself is 256 colour.<br />
<br />
Reverse engineered source code is available, see below.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=game}}<br />
{{FileFormatTool<br />
| Name = [[Camoto]]<br />
| Platform = Linux/Windows<br />
| grp = Edit<br />
| map = Edit<br />
| gfx = No<br />
| mus = Edit<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Camoto Online#gamearchive.js|Camoto/gamearchive.js]]<br />
| Platform = Any<br />
| grp = Edit<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [http://forums.3drealms.com/vb/showthread.php?t=38429 Duke2Edit]<br />
| Platform = Windows GUI<br />
| grp = Read<br />
| map = Edit<br />
| gfx = Edit<br />
| mus = Edit<br />
| sfx = Edit<br />
| txt = No<br />
| sav = Edit<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [http://sfprod.shikadi.net/old/games/duke2.htm#extractor Textract]<br />
| Platform = DOS<br />
| grp = Read<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Wombat]]<br />
| Platform = Windows GUI<br />
| grp = Read<br />
| map = Read<br />
| gfx = Read<br />
| mus = Read<br />
| sfx = Read<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
{{BeginGameFileList}}<br />
{{GameFile<br />
| Name = *.bin<br />
| Format = [[B800 Text]]<br />
| KnownFormat = Yes<br />
| Desc = Text screens shown at exit<br />
}}<br />
{{GameFile<br />
| Name = *.cmp<br />
| Format = [[CMP Format]]<br />
| KnownFormat = Yes<br />
| Desc = Archive file storing most of the game data<br />
}}<br />
{{GameFile<br />
| Name = actrinfo.mni<br />
| Format = [[Duke Nukem II Actor Info]]<br />
| KnownFormat = Yes<br />
| Desc = Describes how the tiles in <tt>actors.mni</tt> should be arranged to create sprites.<br />
}}<br />
{{GameFile<br />
| Name = actors.mni<br/>drop*.mni<br />
| Format = [[Cosmo Tileset Format]]<br />
| KnownFormat = Yes<br />
| Desc = Sprites & background images stored as tilesets. The 16-color VGA palette found in the game's executable at offset <tt>0x1B068</tt> should be used when displaying these images. The palette in <tt>gamepal.pal</tt> can be used as an approximation, but is not 100% like the one used in-game. Also note that some of the images are used in story cutscenes or menus, and need different palettes to look correct.<br />
}}<br />
{{GameFile<br />
| Name = audiohed.mni<br/>audiot.mni<br />
| Format = [[AudioT Format]]<br />
| KnownFormat = Yes<br />
| Desc = PC Speaker & AdLib sounds. '''Warning:''' The game uses fixed-size buffers to read these files, so modded files must not exceed the original file sizes.<br />
}}<br />
{{GameFile<br />
| Name = czone?.mni<br />
| Format = [[Duke Nukem II CZone Format]]<br />
| KnownFormat = Yes<br />
| Desc = Level-specific tilesets<br />
}}<br />
{{GameFile<br />
| Name = [lmno]?.mni<br />
| Format = [[Duke Nukem II Map Format]]<br />
| KnownFormat = Yes<br />
| Desc = Game levels (LMNO for episode 1, 2, 3 and 4 respectively)<br />
}}<br />
{{GameFile<br />
| Name = *.imf<br />
| Format = [[IMF Format]]<br />
| KnownFormat = Yes<br />
| Desc = Background music<br />
}}<br />
{{GameFile<br />
| Name = sb_*.mni<br/>intro*.mni<br />
| Format = [[VOC Format]]<br />
| KnownFormat = Yes<br />
| Desc = Digitised sound effects<br />
}}<br />
{{GameFile<br />
| Name = *.pal<br />
| Format = [[Duke Nukem II Palette Formats]]<br />
| KnownFormat = Yes<br />
| Desc = Palettes<br />
}}<br />
{{GameFile<br />
| Name = *.mni (32048 bytes)<br />
| Format = [[Duke Nukem II Full-screen Images]]<br />
| KnownFormat = Yes<br />
| Desc = 4bpp 320&times;200 images with 16-colour VGA palette<br />
}}<br />
{{GameFile<br />
| Name = lcr.mni<br />
| Format = [[Duke Nukem II Full-screen Images]]<br />
| KnownFormat = Yes<br />
| Desc = 8bpp 320&times;200 images with 256-colour VGA palette<br />
}}<br />
{{GameFile<br />
| Name = nukem2.mni<br/>nukum2.mni<br />
| Format = [[Duke Nukem II Demo Format]]<br />
| KnownFormat = Yes<br />
| Desc = Demo macros<br />
}}<br />
{{GameFile<br />
| Name = nukem2.f?<br />
| Format = [[Duke Nukem II Animation Format]]<br />
| KnownFormat = Yes<br />
| Desc = Intro animations in FLIC format<br />
}}<br />
{{GameFile<br />
| Name = nukem2.-??<br />
| Format = [[Duke Nukem II Misc Files]]<br />
| KnownFormat = Yes<br />
| Desc = Files that are generated by the game<br />
}}<br />
{{EndGameFileList}}<br />
Many of these files are stored inside <tt>[[CMP Format|nukem2.cmp]]</tt>.<br />
<br />
== See also ==<br />
<br />
* [https://github.com/lethal-guitar/RigelEngine Rigel Engine] - an open source reimplementation of the game using the original data files<br />
* [https://github.com/lethal-guitar/Duke2Reconstructed Duke2Reconstructed] - reverse engineered source code which compiles into a 100%-identical EXE file</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II&diff=10707Duke Nukem II2022-09-26T14:53:38Z<p>Lethal guitar: /* See also */</p>
<hr />
<div>{{Game Infobox<br />
| Levels = Edit<br />
| Tiles = Edit<br />
| Sprites = Edit<br />
| Fullscreen = Edit<br />
| Sound = Edit<br />
| Music = Edit<br />
| Text = Some<br />
| Story = Edit<br />
| Interface = Edit<br />
| Demo = No<br />
}}<br />
<br />
[[Duke Nukem II]] is a 256-colour VGA game based on the [[Cosmo's Cosmic Adventures]] engine. Although the cutscenes are 256-colour, the game itself is only 16-colour and runs in an EGA video mode. It does however change the VGA palette such that the 16 colours in use are very different to those normally used by EGA games, successfully tricking most people into believing the game itself is 256 colour.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=game}}<br />
{{FileFormatTool<br />
| Name = [[Camoto]]<br />
| Platform = Linux/Windows<br />
| grp = Edit<br />
| map = Edit<br />
| gfx = No<br />
| mus = Edit<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Camoto Online#gamearchive.js|Camoto/gamearchive.js]]<br />
| Platform = Any<br />
| grp = Edit<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [http://forums.3drealms.com/vb/showthread.php?t=38429 Duke2Edit]<br />
| Platform = Windows GUI<br />
| grp = Read<br />
| map = Edit<br />
| gfx = Edit<br />
| mus = Edit<br />
| sfx = Edit<br />
| txt = No<br />
| sav = Edit<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [http://sfprod.shikadi.net/old/games/duke2.htm#extractor Textract]<br />
| Platform = DOS<br />
| grp = Read<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Wombat]]<br />
| Platform = Windows GUI<br />
| grp = Read<br />
| map = Read<br />
| gfx = Read<br />
| mus = Read<br />
| sfx = Read<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
{{BeginGameFileList}}<br />
{{GameFile<br />
| Name = *.bin<br />
| Format = [[B800 Text]]<br />
| KnownFormat = Yes<br />
| Desc = Text screens shown at exit<br />
}}<br />
{{GameFile<br />
| Name = *.cmp<br />
| Format = [[CMP Format]]<br />
| KnownFormat = Yes<br />
| Desc = Archive file storing most of the game data<br />
}}<br />
{{GameFile<br />
| Name = actrinfo.mni<br />
| Format = [[Duke Nukem II Actor Info]]<br />
| KnownFormat = Yes<br />
| Desc = Describes how the tiles in <tt>actors.mni</tt> should be arranged to create sprites.<br />
}}<br />
{{GameFile<br />
| Name = actors.mni<br/>drop*.mni<br />
| Format = [[Cosmo Tileset Format]]<br />
| KnownFormat = Yes<br />
| Desc = Sprites & background images stored as tilesets. The 16-color VGA palette found in the game's executable at offset <tt>0x1B068</tt> should be used when displaying these images. The palette in <tt>gamepal.pal</tt> can be used as an approximation, but is not 100% like the one used in-game. Also note that some of the images are used in story cutscenes or menus, and need different palettes to look correct.<br />
}}<br />
{{GameFile<br />
| Name = audiohed.mni<br/>audiot.mni<br />
| Format = [[AudioT Format]]<br />
| KnownFormat = Yes<br />
| Desc = PC Speaker & AdLib sounds. '''Warning:''' The game uses fixed-size buffers to read these files, so modded files must not exceed the original file sizes.<br />
}}<br />
{{GameFile<br />
| Name = czone?.mni<br />
| Format = [[Duke Nukem II CZone Format]]<br />
| KnownFormat = Yes<br />
| Desc = Level-specific tilesets<br />
}}<br />
{{GameFile<br />
| Name = [lmno]?.mni<br />
| Format = [[Duke Nukem II Map Format]]<br />
| KnownFormat = Yes<br />
| Desc = Game levels (LMNO for episode 1, 2, 3 and 4 respectively)<br />
}}<br />
{{GameFile<br />
| Name = *.imf<br />
| Format = [[IMF Format]]<br />
| KnownFormat = Yes<br />
| Desc = Background music<br />
}}<br />
{{GameFile<br />
| Name = sb_*.mni<br/>intro*.mni<br />
| Format = [[VOC Format]]<br />
| KnownFormat = Yes<br />
| Desc = Digitised sound effects<br />
}}<br />
{{GameFile<br />
| Name = *.pal<br />
| Format = [[Duke Nukem II Palette Formats]]<br />
| KnownFormat = Yes<br />
| Desc = Palettes<br />
}}<br />
{{GameFile<br />
| Name = *.mni (32048 bytes)<br />
| Format = [[Duke Nukem II Full-screen Images]]<br />
| KnownFormat = Yes<br />
| Desc = 4bpp 320&times;200 images with 16-colour VGA palette<br />
}}<br />
{{GameFile<br />
| Name = lcr.mni<br />
| Format = [[Duke Nukem II Full-screen Images]]<br />
| KnownFormat = Yes<br />
| Desc = 8bpp 320&times;200 images with 256-colour VGA palette<br />
}}<br />
{{GameFile<br />
| Name = nukem2.mni<br/>nukum2.mni<br />
| Format = [[Duke Nukem II Demo Format]]<br />
| KnownFormat = Yes<br />
| Desc = Demo macros<br />
}}<br />
{{GameFile<br />
| Name = nukem2.f?<br />
| Format = [[Duke Nukem II Animation Format]]<br />
| KnownFormat = Yes<br />
| Desc = Intro animations in FLIC format<br />
}}<br />
{{GameFile<br />
| Name = nukem2.-??<br />
| Format = [[Duke Nukem II Misc Files]]<br />
| KnownFormat = Yes<br />
| Desc = Files that are generated by the game<br />
}}<br />
{{EndGameFileList}}<br />
Many of these files are stored inside <tt>[[CMP Format|nukem2.cmp]]</tt>.<br />
<br />
== See also ==<br />
<br />
* [https://github.com/lethal-guitar/RigelEngine Rigel Engine] - an open source reimplementation of the game using the original data files<br />
* [https://github.com/lethal-guitar/Duke2Reconstructed Duke2Reconstructed] - reverse engineered source code which compiles into a 100%-identical EXE file</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Palette_Formats&diff=10556Duke Nukem II Palette Formats2022-06-15T14:31:05Z<p>Lethal guitar: Redoing my previously reverted edit, now with corrected color table values after sorting out a bug in my script.</p>
<hr />
<div>There are two different palette formats used by [[Duke Nukem II]].<br />
<br />
== 16-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 16<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The 16-color palette is the most common palette format. It can be found in:<br />
<br />
* [[Duke Nukem II Full-screen Images#16-Color Images|16-color images]]<br />
* individual palette files (<tt>*.PAL</tt>)<br />
* the game's main executable (offsets are for the decompressed registered version executable):<br />
** in-game palette at offset <tt>0x1B068</tt><br />
** "The End ...until Duke 3-D" message (actor no. 175) palette at offset <tt>0x1B098</tt><br />
<br />
The palette stores each entry's red, green and blue value (in that order) each as one byte value, resulting in a total size of 48 bytes. However, the color values are not standard 6-bit VGA values - they range from 0 to 68 inclusive!<br />
This is because the game doesn't directly submit these values to the VGA hardware, but always in conjunction with a fade-in effect. During the fade, the game repeatedly adds the palette values to an accumulator buffer (of 16-bit values) over the course of 15 steps, and divides the accumulated values by 16 before sending the result to the hardware. Combined with a short delay after each step, this results in progressive brighter colors, until the fade-in stops.<br />
<br />
Consequently, to convert the palette values to standard 6-bit VGA values, you can use this formula:<br />
<br />
val6 = val*15 / 16<br />
<br />
The following table lists all the palette values and the corresponding 8-bit values.<br />
<br />
{|class="wikitable"<br />
! !! 0 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 !! 11 !! 12 !! 13 !! 14 !! 15 <br />
|-<br />
| '''0''' || 0 || 0 || 4 || 8 || 12 || 16 || 20 || 24 || 28 || 32 || 36 || 40 || 44 || 48 || 52 || 56<br />
|-<br />
| '''16''' || 60 || 60 || 65 || 69 || 73 || 77 || 81 || 85 || 89 || 93 || 97 || 101 || 105 || 109 || 113 || 117<br />
|-<br />
| '''32''' || 121 || 121 || 125 || 130 || 134 || 138 || 142 || 146 || 150 || 154 || 158 || 162 || 166 || 170 || 174 || 178<br />
|-<br />
| '''48''' || 182 || 182 || 186 || 190 || 195 || 199 || 203 || 207 || 211 || 215 || 219 || 223 || 227 || 231 || 235 || 239<br />
|-<br />
| '''64''' || 243 || 243 || 247 || 251 || 255<br />
|}<br />
<br />
It is possible to use palette values greater than 68, but that will result in color errors during fade-in and fade-out.<br />
<br />
See [[VGA Palette]] for details on converting between 6-bit and 8-bit values.<br />
<br />
== 256-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 256<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
"Real" VGA palettes are stored in:<br />
<br />
* <tt>NUKEM2.F*</tt> (see [[Duke Nukem II Animation Format]])<br />
* <tt>LCR.MNI</tt><br />
<br />
These are 768-byte files in classic (6-bit) [[VGA Palette]] format.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. <br />
[[User:Lethal_guitar|lethal-guitar]] added some additional information regarding conversion of the 6-bit palette entries.<br />
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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Palette_Formats&diff=10555Duke Nukem II Palette Formats2022-06-15T14:08:04Z<p>Lethal guitar: Undo revision 10553 by Lethal guitar (talk)</p>
<hr />
<div>There are two different palette formats used by [[Duke Nukem II]].<br />
<br />
== 16-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 16<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The 16-color palette is the most common palette format. It can be found in:<br />
<br />
* [[Duke Nukem II Full-screen Images#16-Color Images|16-color images]]<br />
* individual palette files (<tt>*.PAL</tt>)<br />
* the game's main executable (offsets are for the decompressed registered version executable):<br />
** in-game palette at offset <tt>0x1B068</tt><br />
** "The End ...until Duke 3-D" message (actor no. 175) palette at offset <tt>0x1B098</tt><br />
<br />
The palette stores each entry's red, green and blue value (in that order) each as one byte value, resulting in a total size of 48 bytes. However, the color values are not standard 6-bit VGA values - they range from 0 to 68 inclusive! The following table lists all the palette values and the corresponding 8-bit values.<br />
<br />
{|class="wikitable"<br />
! !! 0 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 !! 11 !! 12 !! 13 !! 14 !! 15 <br />
|-<br />
| '''0''' || 0 || 0 || 4 || 8 || 12 || 16 || 20 || 24 || 28 || 32 || 36 || 40 || 44 || 48 || 52 || 56<br />
|-<br />
| '''16''' || 60 || 60 || 65 || 69 || 73 || 77 || 81 || 85 || 89 || 93 || 97 || 101 || 105 || 109 || 113 || 117<br />
|-<br />
| '''32''' || 121 || 121 || 125 || 130 || 134 || 138 || 142 || 146 || 150 || 154 || 158 || 162 || 166 || 170 || 174 || 178<br />
|-<br />
| '''48''' || 182 || 182 || 186 || 190 || 195 || 199 || 203 || 207 || 211 || 215 || 219 || 223 || 227 || 231 || 235 || 239<br />
|-<br />
| '''64''' || 243 || 243 || 247 || 251 || 255<br />
|}<br />
<br />
It is possible to use palette values greater than 68, but that will result in color errors during fade-in and fade-out.<br />
<br />
Despite the 0-68 range, the hardware palette can only store 64 different color values. To convert the palette values to standard 6-bit VGA values, you can use this formula:<br />
<br />
val6 = Max(0, (val-1) - (val-1)/16)<br />
<br />
A more efficient version might be:<br />
<br />
IF val = 0<br />
val6 = 0<br />
ELSE<br />
val6 = val-1<br />
val6 = val6 - val6/16<br />
ENDIF<br />
<br />
See [[VGA Palette]] for details on converting between 6-bit and 8-bit values.<br />
<br />
== 256-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 256<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
"Real" VGA palettes are stored in:<br />
<br />
* <tt>NUKEM2.F*</tt> (see [[Duke Nukem II Animation Format]])<br />
* <tt>LCR.MNI</tt><br />
<br />
These are 768-byte files in classic (6-bit) [[VGA Palette]] format.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Palette_Formats&diff=10554Duke Nukem II Palette Formats2022-06-15T13:59:19Z<p>Lethal guitar: Add additional in-executable palette offset</p>
<hr />
<div>There are two different palette formats used by [[Duke Nukem II]].<br />
<br />
== 16-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 16<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The 16-color palette is the most common palette format. It can be found in:<br />
<br />
* [[Duke Nukem II Full-screen Images#16-Color Images|16-color images]]<br />
* individual palette files (<tt>*.PAL</tt>)<br />
* the game's main executable (offsets are for the decompressed registered version executable):<br />
** in-game palette at offset <tt>0x1B068</tt><br />
** "The End ...until Duke 3-D" message (actor no. 175) palette at offset <tt>0x1B098</tt><br />
<br />
The palette stores each entry's red, green and blue value (in that order) each as one byte value, resulting in a total size of 48 bytes. However, the color values are not standard 6-bit VGA values - they range from 0 to 68 inclusive!<br />
This is because the game doesn't directly submit these values to the VGA hardware, but always in conjunction with a fade-in effect. During the fade, the game repeatedly adds the palette values to an accumulator buffer (of 16-bit values) over the course of 15 steps, and divides the accumulated values by 16 before sending the result to the hardware. Combined with a short delay after each step, this results in progressive brighter colors, until the fade-in stops.<br />
<br />
Consequently, to convert the palette values to standard 6-bit VGA values, you can use this formula:<br />
<br />
val6 = val*15 / 16<br />
<br />
The following table lists all the palette values and the corresponding 8-bit values.<br />
<br />
{|class="wikitable"<br />
! !! 0 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 !! 11 !! 12 !! 13 !! 14 !! 15 <br />
|-<br />
| '''0''' || 0 || 0 || 4 || 8 || 12 || 16 || 20 || 24 || 28 || 32 || 36 || 40 || 44 || 48 || 52 || 56<br />
|-<br />
| '''16''' || 60 || 60 || 64 || 68 || 72 || 76 || 80 || 85 || 89 || 93 || 97 || 101 || 105 || 109 || 113 || 117<br />
|-<br />
| '''32''' || 121 || 121 || 125 || 129 || 133 || 137 || 141 || 145 || 149 || 153 || 157 || 161 || 165 || 170 || 174 || 178<br />
|-<br />
| '''48''' || 182 || 182 || 186 || 190 || 194 || 198 || 202 || 206 || 210 || 214 || 218 || 222 || 226 || 230 || 234 || 238<br />
|-<br />
| '''64''' || 242 || 242 || 246 || 250 || 255<br />
|}<br />
<br />
It is possible to use palette values greater than 68, but that will result in color errors during fade-in and fade-out.<br />
<br />
See [[VGA Palette]] for details on converting between 6-bit and 8-bit values.<br />
<br />
== 256-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 256<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
"Real" VGA palettes are stored in:<br />
<br />
* <tt>NUKEM2.F*</tt> (see [[Duke Nukem II Animation Format]])<br />
* <tt>LCR.MNI</tt><br />
<br />
These are 768-byte files in classic (6-bit) [[VGA Palette]] format.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. <br />
[[User:Lethal_guitar|lethal-guitar]] added some additional information regarding conversion of the 6-bit palette entries.<br />
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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Palette_Formats&diff=10553Duke Nukem II Palette Formats2022-06-15T13:52:29Z<p>Lethal guitar: Update and correct the information on palette conversion based on findings from disassembly</p>
<hr />
<div>There are two different palette formats used by [[Duke Nukem II]].<br />
<br />
== 16-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 16<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The 16-color palette is the most common palette format. It can be found in:<br />
<br />
* [[Duke Nukem II Full-screen Images#16-Color Images|16-color images]]<br />
* individual palette files (<tt>*.PAL</tt>)<br />
* the game's main executable (at offset <tt>0x1B068</tt> in the decompressed registered version executable)<br />
<br />
The palette stores each entry's red, green and blue value (in that order) each as one byte value, resulting in a total size of 48 bytes. However, the color values are not standard 6-bit VGA values - they range from 0 to 68 inclusive!<br />
This is because the game doesn't directly submit these values to the VGA hardware, but always in conjunction with a fade-in effect. During the fade, the game repeatedly adds the palette values to an accumulator buffer (of 16-bit values) over the course of 15 steps, and divides the accumulated values by 16 before sending the result to the hardware. Combined with a short delay after each step, this results in progressive brighter colors, until the fade-in stops.<br />
<br />
Consequently, to convert the palette values to standard 6-bit VGA values, you can use this formula:<br />
<br />
val6 = val*15 / 16<br />
<br />
The following table lists all the palette values and the corresponding 8-bit values.<br />
<br />
{|class="wikitable"<br />
! !! 0 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 !! 11 !! 12 !! 13 !! 14 !! 15 <br />
|-<br />
| '''0''' || 0 || 0 || 4 || 8 || 12 || 16 || 20 || 24 || 28 || 32 || 36 || 40 || 44 || 48 || 52 || 56<br />
|-<br />
| '''16''' || 60 || 60 || 64 || 68 || 72 || 76 || 80 || 85 || 89 || 93 || 97 || 101 || 105 || 109 || 113 || 117<br />
|-<br />
| '''32''' || 121 || 121 || 125 || 129 || 133 || 137 || 141 || 145 || 149 || 153 || 157 || 161 || 165 || 170 || 174 || 178<br />
|-<br />
| '''48''' || 182 || 182 || 186 || 190 || 194 || 198 || 202 || 206 || 210 || 214 || 218 || 222 || 226 || 230 || 234 || 238<br />
|-<br />
| '''64''' || 242 || 242 || 246 || 250 || 255<br />
|}<br />
<br />
It is possible to use palette values greater than 68, but that will result in color errors during fade-in and fade-out.<br />
<br />
See [[VGA Palette]] for details on converting between 6-bit and 8-bit values.<br />
<br />
== 256-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 256<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
"Real" VGA palettes are stored in:<br />
<br />
* <tt>NUKEM2.F*</tt> (see [[Duke Nukem II Animation Format]])<br />
* <tt>LCR.MNI</tt><br />
<br />
These are 768-byte files in classic (6-bit) [[VGA Palette]] format.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. <br />
[[User:Lethal_guitar|lethal-guitar]] added some additional information regarding conversion of the 6-bit palette entries.<br />
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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Palette_Formats&diff=10552Duke Nukem II Palette Formats2022-06-15T13:18:47Z<p>Lethal guitar: Give correct offset for in-game palette, and adjust formatting</p>
<hr />
<div>There are two different palette formats used by [[Duke Nukem II]].<br />
<br />
== 16-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 16<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The 16-color palette is the most common palette format. It can be found in:<br />
<br />
* [[Duke Nukem II Full-screen Images#16-Color Images|16-color images]]<br />
* individual palette files (<tt>*.PAL</tt>)<br />
* the game's main executable (at offset <tt>0x1B068</tt> in the decompressed registered version executable)<br />
<br />
The palette stores each entry's red, green and blue value (in that order) each as one byte value, resulting in a total size of 48 bytes. However, the color values are not standard 6-bit VGA values - they range from 0 to 68 inclusive! The following table lists all the palette values and the corresponding 8-bit values.<br />
<br />
{|class="wikitable"<br />
! !! 0 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 !! 11 !! 12 !! 13 !! 14 !! 15 <br />
|-<br />
| '''0''' || 0 || 0 || 4 || 8 || 12 || 16 || 20 || 24 || 28 || 32 || 36 || 40 || 44 || 48 || 52 || 56<br />
|-<br />
| '''16''' || 60 || 60 || 65 || 69 || 73 || 77 || 81 || 85 || 89 || 93 || 97 || 101 || 105 || 109 || 113 || 117<br />
|-<br />
| '''32''' || 121 || 121 || 125 || 130 || 134 || 138 || 142 || 146 || 150 || 154 || 158 || 162 || 166 || 170 || 174 || 178<br />
|-<br />
| '''48''' || 182 || 182 || 186 || 190 || 195 || 199 || 203 || 207 || 211 || 215 || 219 || 223 || 227 || 231 || 235 || 239<br />
|-<br />
| '''64''' || 243 || 243 || 247 || 251 || 255<br />
|}<br />
<br />
It is possible to use palette values greater than 68, but that will result in color errors during fade-in and fade-out.<br />
<br />
Despite the 0-68 range, the hardware palette can only store 64 different color values. To convert the palette values to standard 6-bit VGA values, you can use this formula:<br />
<br />
val6 = Max(0, (val-1) - (val-1)/16)<br />
<br />
A more efficient version might be:<br />
<br />
IF val = 0<br />
val6 = 0<br />
ELSE<br />
val6 = val-1<br />
val6 = val6 - val6/16<br />
ENDIF<br />
<br />
See [[VGA Palette]] for details on converting between 6-bit and 8-bit values.<br />
<br />
== 256-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 256<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
"Real" VGA palettes are stored in:<br />
<br />
* <tt>NUKEM2.F*</tt> (see [[Duke Nukem II Animation Format]])<br />
* <tt>LCR.MNI</tt><br />
<br />
These are 768-byte files in classic (6-bit) [[VGA Palette]] format.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II&diff=10551Duke Nukem II2022-06-15T13:17:25Z<p>Lethal guitar: Correct offset to ingame palette</p>
<hr />
<div>{{Game Infobox<br />
| Levels = Edit<br />
| Tiles = Edit<br />
| Sprites = Edit<br />
| Fullscreen = Edit<br />
| Sound = Edit<br />
| Music = Edit<br />
| Text = Some<br />
| Story = Edit<br />
| Interface = Edit<br />
| Demo = No<br />
}}<br />
<br />
[[Duke Nukem II]] is a 256-colour VGA game based on the [[Cosmo's Cosmic Adventures]] engine. Although the cutscenes are 256-colour, the game itself is only 16-colour and runs in an EGA video mode. It does however change the VGA palette such that the 16 colours in use are very different to those normally used by EGA games, successfully tricking most people into believing the game itself is 256 colour.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=game}}<br />
{{FileFormatTool<br />
| Name = [[Camoto]]<br />
| Platform = Linux/Windows<br />
| grp = Edit<br />
| map = Edit<br />
| gfx = No<br />
| mus = Edit<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Camoto Online#gamearchive.js|Camoto/gamearchive.js]]<br />
| Platform = Any<br />
| grp = Edit<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [http://forums.3drealms.com/vb/showthread.php?t=38429 Duke2Edit]<br />
| Platform = Windows GUI<br />
| grp = Read<br />
| map = Edit<br />
| gfx = Edit<br />
| mus = Edit<br />
| sfx = Edit<br />
| txt = No<br />
| sav = Edit<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [http://sfprod.shikadi.net/old/games/duke2.htm#extractor Textract]<br />
| Platform = DOS<br />
| grp = Read<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Wombat]]<br />
| Platform = Windows GUI<br />
| grp = Read<br />
| map = Read<br />
| gfx = Read<br />
| mus = Read<br />
| sfx = Read<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
{{BeginGameFileList}}<br />
{{GameFile<br />
| Name = *.bin<br />
| Format = [[B800 Text]]<br />
| KnownFormat = Yes<br />
| Desc = Text screens shown at exit<br />
}}<br />
{{GameFile<br />
| Name = *.cmp<br />
| Format = [[CMP Format]]<br />
| KnownFormat = Yes<br />
| Desc = Archive file storing most of the game data<br />
}}<br />
{{GameFile<br />
| Name = actrinfo.mni<br />
| Format = [[Duke Nukem II Actor Info]]<br />
| KnownFormat = Yes<br />
| Desc = Describes how the tiles in <tt>actors.mni</tt> should be arranged to create sprites.<br />
}}<br />
{{GameFile<br />
| Name = actors.mni<br/>drop*.mni<br />
| Format = [[Cosmo Tileset Format]]<br />
| KnownFormat = Yes<br />
| Desc = Sprites & background images stored as tilesets. The 16-color VGA palette found in the game's executable at offset <tt>0x1B068</tt> should be used when displaying these images. The palette in <tt>gamepal.pal</tt> can be used as an approximation, but is not 100% like the one used in-game. Also note that some of the images are used in story cutscenes or menus, and need different palettes to look correct.<br />
}}<br />
{{GameFile<br />
| Name = audiohed.mni<br/>audiot.mni<br />
| Format = [[AudioT Format]]<br />
| KnownFormat = Yes<br />
| Desc = PC Speaker & AdLib sounds. '''Warning:''' The game uses fixed-size buffers to read these files, so modded files must not exceed the original file sizes.<br />
}}<br />
{{GameFile<br />
| Name = czone?.mni<br />
| Format = [[Duke Nukem II CZone Format]]<br />
| KnownFormat = Yes<br />
| Desc = Level-specific tilesets<br />
}}<br />
{{GameFile<br />
| Name = [lmno]?.mni<br />
| Format = [[Duke Nukem II Map Format]]<br />
| KnownFormat = Yes<br />
| Desc = Game levels (LMNO for episode 1, 2, 3 and 4 respectively)<br />
}}<br />
{{GameFile<br />
| Name = *.imf<br />
| Format = [[IMF Format]]<br />
| KnownFormat = Yes<br />
| Desc = Background music<br />
}}<br />
{{GameFile<br />
| Name = sb_*.mni<br/>intro*.mni<br />
| Format = [[VOC Format]]<br />
| KnownFormat = Yes<br />
| Desc = Digitised sound effects<br />
}}<br />
{{GameFile<br />
| Name = *.pal<br />
| Format = [[Duke Nukem II Palette Formats]]<br />
| KnownFormat = Yes<br />
| Desc = Palettes<br />
}}<br />
{{GameFile<br />
| Name = *.mni (32048 bytes)<br />
| Format = [[Duke Nukem II Full-screen Images]]<br />
| KnownFormat = Yes<br />
| Desc = 4bpp 320&times;200 images with 16-colour VGA palette<br />
}}<br />
{{GameFile<br />
| Name = lcr.mni<br />
| Format = [[Duke Nukem II Full-screen Images]]<br />
| KnownFormat = Yes<br />
| Desc = 8bpp 320&times;200 images with 256-colour VGA palette<br />
}}<br />
{{GameFile<br />
| Name = nukem2.mni<br/>nukum2.mni<br />
| Format = [[Duke Nukem II Demo Format]]<br />
| KnownFormat = Yes<br />
| Desc = Demo macros<br />
}}<br />
{{GameFile<br />
| Name = nukem2.f?<br />
| Format = [[Duke Nukem II Animation Format]]<br />
| KnownFormat = Yes<br />
| Desc = Intro animations in FLIC format<br />
}}<br />
{{GameFile<br />
| Name = nukem2.-??<br />
| Format = [[Duke Nukem II Misc Files]]<br />
| KnownFormat = Yes<br />
| Desc = Files that are generated by the game<br />
}}<br />
{{EndGameFileList}}<br />
Many of these files are stored inside <tt>[[CMP Format|nukem2.cmp]]</tt>.<br />
<br />
== See also ==<br />
<br />
* [https://github.com/lethal-guitar/RigelEngine Rigel Engine] - an open source reimplementation of the game using the original data files</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Palette_Formats&diff=10211Duke Nukem II Palette Formats2021-12-08T06:16:07Z<p>Lethal guitar: Add offset for palette stored inside executable</p>
<hr />
<div>There are two different palette formats used by [[Duke Nukem II]].<br />
<br />
== 16-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 16<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The 16-color palette is the most common palette format. It can be found in:<br />
<br />
* [[Duke Nukem II Full-screen Images#16-Color Images|16-color images]]<br />
* individual palette files (<tt>*.PAL</tt>)<br />
* the game's main executable (at offset 0x1B038 in the decompressed registered version executable)<br />
<br />
The palette stores each entry's red, green and blue value (in that order) each as one byte value, resulting in a total size of 48 bytes. However, the color values are not standard 6-bit VGA values - they range from 0 to 68 inclusive! The following table lists all the palette values and the corresponding 8-bit values.<br />
<br />
{|class="wikitable"<br />
! !! 0 !! 1 !! 2 !! 3 !! 4 !! 5 !! 6 !! 7 !! 8 !! 9 !! 10 !! 11 !! 12 !! 13 !! 14 !! 15 <br />
|-<br />
| '''0''' || 0 || 0 || 4 || 8 || 12 || 16 || 20 || 24 || 28 || 32 || 36 || 40 || 44 || 48 || 52 || 56<br />
|-<br />
| '''16''' || 60 || 60 || 65 || 69 || 73 || 77 || 81 || 85 || 89 || 93 || 97 || 101 || 105 || 109 || 113 || 117<br />
|-<br />
| '''32''' || 121 || 121 || 125 || 130 || 134 || 138 || 142 || 146 || 150 || 154 || 158 || 162 || 166 || 170 || 174 || 178<br />
|-<br />
| '''48''' || 182 || 182 || 186 || 190 || 195 || 199 || 203 || 207 || 211 || 215 || 219 || 223 || 227 || 231 || 235 || 239<br />
|-<br />
| '''64''' || 243 || 243 || 247 || 251 || 255<br />
|}<br />
<br />
It is possible to use palette values greater than 68, but that will result in color errors during fade-in and fade-out.<br />
<br />
Despite the 0-68 range, the hardware palette can only store 64 different color values. To convert the palette values to standard 6-bit VGA values, you can use this formula:<br />
<br />
val6 = Max(0, (val-1) - (val-1)/16)<br />
<br />
A more efficient version might be:<br />
<br />
IF val = 0<br />
val6 = 0<br />
ELSE<br />
val6 = val-1<br />
val6 = val6 - val6/16<br />
ENDIF<br />
<br />
See [[VGA Palette]] for details on converting between 6-bit and 8-bit values.<br />
<br />
== 256-Color Palette ==<br />
{{Palette Infobox<br />
| Hardware = VGA<br />
| Depth = 18-bit<br />
| Count = 256<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
"Real" VGA palettes are stored in:<br />
<br />
* <tt>NUKEM2.F*</tt> (see [[Duke Nukem II Animation Format]])<br />
* <tt>LCR.MNI</tt><br />
<br />
These are 768-byte files in classic (6-bit) [[VGA Palette]] format.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]]. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II&diff=10210Duke Nukem II2021-12-08T06:12:08Z<p>Lethal guitar: Correct and extend information about ACTORS.MNI</p>
<hr />
<div>{{Game Infobox<br />
| Levels = Edit<br />
| Tiles = Edit<br />
| Sprites = Edit<br />
| Fullscreen = Edit<br />
| Sound = Edit<br />
| Music = Edit<br />
| Text = Some<br />
| Story = Edit<br />
| Interface = Edit<br />
| Demo = No<br />
}}<br />
<br />
[[Duke Nukem II]] is a 256-colour VGA game based on the [[Cosmo's Cosmic Adventures]] engine. Although the cutscenes are 256-colour, the game itself is only 16-colour and runs in an EGA video mode. It does however change the VGA palette such that the 16 colours in use are very different to those normally used by EGA games, successfully tricking most people into believing the game itself is 256 colour.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=game}}<br />
{{FileFormatTool<br />
| Name = [[Camoto]]<br />
| Platform = Linux/Windows<br />
| grp = Edit<br />
| map = Edit<br />
| gfx = No<br />
| mus = Edit<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Camoto Online#gamearchive.js|Camoto/gamearchive.js]]<br />
| Platform = Any<br />
| grp = Edit<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [http://forums.3drealms.com/vb/showthread.php?t=38429 Duke2Edit]<br />
| Platform = Windows GUI<br />
| grp = Read<br />
| map = Edit<br />
| gfx = Edit<br />
| mus = Edit<br />
| sfx = Edit<br />
| txt = No<br />
| sav = Edit<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [http://sfprod.shikadi.net/old/games/duke2.htm#extractor Textract]<br />
| Platform = DOS<br />
| grp = Read<br />
| map = No<br />
| gfx = No<br />
| mus = No<br />
| sfx = No<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{FileFormatTool<br />
| Name = [[Wombat]]<br />
| Platform = Windows GUI<br />
| grp = Read<br />
| map = Read<br />
| gfx = Read<br />
| mus = Read<br />
| sfx = Read<br />
| txt = No<br />
| sav = No<br />
| exe = No<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
{{BeginGameFileList}}<br />
{{GameFile<br />
| Name = *.bin<br />
| Format = [[B800 Text]]<br />
| KnownFormat = Yes<br />
| Desc = Text screens shown at exit<br />
}}<br />
{{GameFile<br />
| Name = *.cmp<br />
| Format = [[CMP Format]]<br />
| KnownFormat = Yes<br />
| Desc = Archive file storing most of the game data<br />
}}<br />
{{GameFile<br />
| Name = actrinfo.mni<br />
| Format = [[Duke Nukem II Actor Info]]<br />
| KnownFormat = Yes<br />
| Desc = Describes how the tiles in <tt>actors.mni</tt> should be arranged to create sprites.<br />
}}<br />
{{GameFile<br />
| Name = actors.mni<br/>drop*.mni<br />
| Format = [[Cosmo Tileset Format]]<br />
| KnownFormat = Yes<br />
| Desc = Sprites & background images stored as tilesets. The 16-color VGA palette found in the game's executable at offset <tt>0x1B038</tt> should be used when displaying these images. The palette in <tt>gamepal.pal</tt> can be used as an approximation, but is not 100% like the one used in-game. Also note that some of the images are used in story cutscenes or menus, and need different palettes to look correct.<br />
}}<br />
{{GameFile<br />
| Name = audiohed.mni<br/>audiot.mni<br />
| Format = [[AudioT Format]]<br />
| KnownFormat = Yes<br />
| Desc = PC Speaker & AdLib sounds. '''Warning:''' The game uses fixed-size buffers to read these files, so modded files must not exceed the original file sizes.<br />
}}<br />
{{GameFile<br />
| Name = czone?.mni<br />
| Format = [[Duke Nukem II CZone Format]]<br />
| KnownFormat = Yes<br />
| Desc = Level-specific tilesets<br />
}}<br />
{{GameFile<br />
| Name = [lmno]?.mni<br />
| Format = [[Duke Nukem II Map Format]]<br />
| KnownFormat = Yes<br />
| Desc = Game levels (LMNO for episode 1, 2, 3 and 4 respectively)<br />
}}<br />
{{GameFile<br />
| Name = *.imf<br />
| Format = [[IMF Format]]<br />
| KnownFormat = Yes<br />
| Desc = Background music<br />
}}<br />
{{GameFile<br />
| Name = sb_*.mni<br/>intro*.mni<br />
| Format = [[VOC Format]]<br />
| KnownFormat = Yes<br />
| Desc = Digitised sound effects<br />
}}<br />
{{GameFile<br />
| Name = *.pal<br />
| Format = [[Duke Nukem II Palette Formats]]<br />
| KnownFormat = Yes<br />
| Desc = Palettes<br />
}}<br />
{{GameFile<br />
| Name = *.mni (32048 bytes)<br />
| Format = [[Duke Nukem II Full-screen Images]]<br />
| KnownFormat = Yes<br />
| Desc = 4bpp 320&times;200 images with 16-colour VGA palette<br />
}}<br />
{{GameFile<br />
| Name = lcr.mni<br />
| Format = [[Duke Nukem II Full-screen Images]]<br />
| KnownFormat = Yes<br />
| Desc = 8bpp 320&times;200 images with 256-colour VGA palette<br />
}}<br />
{{GameFile<br />
| Name = nukem2.mni<br/>nukum2.mni<br />
| Format = [[Duke Nukem II Demo Format]]<br />
| KnownFormat = Yes<br />
| Desc = Demo macros<br />
}}<br />
{{GameFile<br />
| Name = nukem2.f?<br />
| Format = [[Duke Nukem II Animation Format]]<br />
| KnownFormat = Yes<br />
| Desc = Intro animations in FLIC format<br />
}}<br />
{{GameFile<br />
| Name = nukem2.-??<br />
| Format = [[Duke Nukem II Misc Files]]<br />
| KnownFormat = Yes<br />
| Desc = Files that are generated by the game<br />
}}<br />
{{EndGameFileList}}<br />
Many of these files are stored inside <tt>[[CMP Format|nukem2.cmp]]</tt>.<br />
<br />
== See also ==<br />
<br />
* [https://github.com/lethal-guitar/RigelEngine Rigel Engine] - an open source reimplementation of the game using the original data files</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=File:Duke_Nukem_II_Full-screen_Images.png&diff=10209File:Duke Nukem II Full-screen Images.png2021-12-01T14:06:08Z<p>Lethal guitar: One of the in-game help screens</p>
<hr />
<div>== Summary ==<br />
One of the in-game help screens</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Map_Format&diff=9746Duke Nukem II Map Format2021-05-07T14:36:59Z<p>Lethal guitar: Correct level flags info after doing some more testing.</p>
<hr />
<div>{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 3<br />
| Tile size = 8&times;8<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
[[Duke Nukem II]] stores its levels in an evolution of the level format used by [[Cosmo's Cosmic Adventures]]. One considerable change is the addition of a second map layer, allowing foreground and background tiles to be placed in the same cell. Surprisingly, given the number of other changes to the file format, the layer data was not restructured to handle this new feature. Instead, some of the new data was shoehorned to fit in the existing space, with the rest of the data tacked on to the end of the file in a somewhat clunky manner. This resulted in some unusual limitations, such as a small number of tiles that can only be placed in the foreground layer if there is no background tile in the same cell.<br />
<br />
== File format ==<br />
The file is in this basic layout:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iBackgroundOffset||Offset where the background layer starts<br />
|-<br />
|char cCZoneName[13]||CZone filename (tileset)<br />
|-<br />
|char cBackdropName[13]||Name of the graphic to use as the level background (one of the <tt>BD*.MNI</tt> files)<br />
|-<br />
|char cMusicName[13]||Name of the [[IMF Format|IMF file]] to use as background music<br />
|-<br />
|BYTE bFlags||Level flags (define type of parallax scrolling and more)<br />
|-<br />
|BYTE bAltBackdrop||Number of alternate backdrop file, zero if no alternate backdrop is used<br />
|-<br />
|BYTE iUnused[2]||Ignored. Likely a holdover from the game's development<br />
|-<br />
|UINT16LE iActorSize||Number of UINT16 values in the actor block<br />
|-<br />
|ACTORDATA actorData[]||Variable-length array of all the actors in the level<br />
|-<br />
|GRIDLAYER gridData||see [[#Foreground and Background Layers]] below for what this contains<br />
|-<br />
|UINT16LE iExtraFGLength||Length of the additional mask/foreground manipulation data<br />
|-<br />
|BYTE iExtraFGData[iExtraFGLength]||See [[#Supplemental foreground data]] below<br />
|-<br />
|char cAttrName[13]||Zone attribute filename<br />
|-<br />
|char cTileName[13]||Zone tile filename<br />
|-<br />
|char cMaskName[13]||Zone masked tiles filename<br />
|}<br />
<br />
The three filenames at the end of the level file never appear to be used. In fact, the level files can end directly after the supplemental foreground data and the game will still load them without reporting any errors. These are probably the names of Cosmo-style tileset files ("zones") that were used before the CZone format (the "c" probably stands for "compound") was introduced.<br />
<br />
For all level files of the full version, the iActorSize value is reliable and the gridData begins directly after the last actor, leaving no padding between actor data and grid data.<br />
<br />
== Level Flags ==<br />
<br />
This is what each bit in the bFlags value indicates:<br />
<br />
Bit | Description<br />
----+------------<br />
7 | switch backdrop when using teleporter (used in L1)<br />
6 | switch backdrop when destroying force field (used in L5)<br />
5 | earthquake<br />
4 | automatic backdrop scrolling ^^<br />
3 | automatic backdrop scrolling <<<br />
2 | - (ignored/unused)<br />
1 | x-scrolling backdrop<br />
0 | x- and y-scrolling backdrop<br />
<br />
Bits 6 and 7 require an alternate backdrop index. The files where these bits are set are also the only files that have a non-zero alternate backdrop number.<br />
<br />
Note that the backdrop/scrolling related bits can NOT be combined! For example, combining bits 3 and 4 will not lead to combined movement (it results in a somewhat jerky/broken horizontal parallax scrolling instead). Combining one of bits 3 or 4 with any of the bits 1 or 0 also leads to broken/wonky results.<br />
<br />
The earthquake flag (bit 5) can be freely combined with any other bits.<br />
<br />
Combining the backdrop switch flags sort of works, but isn't really practical since there can only be two different backdrops. If both bits are set and Duke steps into a teleporter, the backdrop will switch and additionally flash as if a reactor was destroyed. If Duke destroys a reactor before stepping into a teleporter, the backdrop will switch, but will not switch a 2nd time when using a teleporter afterwards. Teleporting back again will switch, and restore the normal backdrop switching behavior.<br />
<br />
In order for either of the backdrop switch flags to work, the backdrop scroll mode needs to be set to x parallax (bit 1). For any other backdrop scroll mode, the backdrop will not switch.<br />
<br />
== Foreground and Background Layers ==<br />
<br />
At the offset indicated by <tt>iBackgroundOffset</tt> in the header, the grid/cell data begins:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iMapWidth||Map width (in tiles)<br />
|-<br />
|UINT16LE iMapData[32750]||Actual map data<br />
|}<br />
<br />
Each "element" in <tt>iMapData</tt> refers to the foreground and/or background tile used in a single grid cell. The grids are arranged left to right, top to bottom, so the index can be calculated by this formula:<br />
<br />
int iIndex = (y * iMapWidth) + x;<br />
iMapData[iIndex] = <new value to set at x,y><br />
<br />
The map data is a constant 32750 UINT16LE cells long (65500 bytes), so the map height can be calculated from this:<br />
<br />
int iMapHeight = 32750 / iMapWidth;<br />
<br />
=== Mapping cell values to tiles ===<br />
<br />
The method of mapping elements in the <tt>iMapData</tt> structure into tiles is a little complicated. There are two different methods of storing values:<br />
<br />
# If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell contains both a foreground and background tile.<br />
# Otherwise the cell value is a raw pixel-based index into the tilemap.<br />
<br />
==== Most significant bit set ====<br />
<br />
If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell value contains two indices - one for the solid/background tile, and one for the masked/foreground tile. Unlike the other type of cell value, these are actual tile indices as opposed to pixel indexes (i.e. a value of 2 refers to the third tile.)<br />
<br />
The lower ten bits are the index of the background tile (this can provide a value between 0 and 1023, however since there are only 1000 tiles this value should always be less than 1000.) The lower ten bits can be isolated like this:<br />
<br />
iSolid = iMapData[x] & 0x03FF;<br />
<br />
The ignoring the most significant bit (which is used to indicate this type of tile value) the remaining five most significant bits are used as the index of the foreground/mask tile. These bits can be isolated as follows:<br />
<br />
iMask = (iMapData[x] >> 10) & 0x1F;<br />
<br />
This only provides a value between 0 and 31 - but since there are 160 masked tiles, this value needs to be further manipulated to produce an index into the masked tileset. An additional two bits per tile are stored in the [[#Supplemental foreground data]] which are used for this purpose, but this will still only allow the first 127 tiles to be used.<br />
<br />
==== Most significant bit NOT set ====<br />
<br />
If the most significant bit is not set, then the cell value is a pixel index into the tilemap. Imagine the tileset laid out left to right all on the one row. Since each tile is 8x8 pixels, at 16 pixels into the image, the third tile will begin (x = 0 for first tile, x = 8 for second tile, x = 16 for third tile.) The cell value is essentially x-coordinate in the previous value (if it's 16, then the third tile should be displayed in that cell.)<br />
<br />
Note that in the CZone tileset file, the solid tiles are made up of 1000 4-plane (16-colour) images, and these are followed by 160 5-plane images (16-colour + transparency.) Although seperate images, they appear to be concatenated when the game loads them, as the cell value will be zero for the first solid tile, it will be eight for the second solid tile, it will be 7992 for the last solid tile, and it will be 8000 for the first masked tile.<br />
<br />
This means that if the solid and masked tiles have been treated as separate images, a check will need to be performed so that any cell value below 8000 is loaded from the solid tileset, and any value larger than this is loaded from the masked tileset (after subtracting 8000 from the cell value, to put it at zero for the first masked tile.) Having said that, for some reason the values for the masked tiles are at multiples of five tiles (tile #0 (x=0) is the first tile, tile #5 (x=40) is the second tile.)<br />
<br />
Note that while the cell value cannot be out of range for the solid tiles/background layer (since any values larger than 8000 will be loaded from the masked tileset into the foreground layer) the cell values for the masked tileset have no such restriction.<br />
<br />
=== Supplemental foreground data ===<br />
<br />
Since the tiles containing combined foreground+background data only provide five bits for the foreground tile, this block of data is used to store an additional two bits for each tile, allowing a seven-bit tile number to be specified.<br />
<br />
The data is stored in a form of run length encoding (RLE) to reduce the size, given that a relatively small number of tiles need the extra two bits. Because there are eight bits in a byte, four lots of two-bit-chunks can be stored in every data byte. This means each data byte contains the data for four tiles. The rest of this section uses the term "tile cluster" to refer to this grouping of four tiles.<br />
<br />
==== Data layout ====<br />
<br />
The data is read one byte at a time, and the initial byte ("length-byte") indicates how many subsequent bytes need to be read. The length-bytes are ''signed'' byte values with the absolute value giving the length to be used for the following data. If the length-byte is positive (high bit is NOT set), then it is a count of the number of affected tile clusters. The following byte is the data byte containing the extra tile data. For example: (brackets inserted for clarity)<br />
<br />
[7F 00] [7F 00] [05 FF] [7F 00]<br />
<br />
This means apply value 00 to the first 254 (0x7F * 2) tile clusters, followed by value FF to the next five tile clusters, followed by 00 to the next 127 tile clusters. The format of these "values" is described below.<br />
<br />
If the length-byte is negative (high bit IS set), then a number of data values follow, depending on the absolute value of the length-byte. If the bytes are arranged like this:<br />
<br />
[FF 12] [FE 34 56] [FF 78] [FD 9A BC DE] [00 00]<br />
<br />
Then it would be interpreted as follows:<br />
<br />
# FF == one - apply value 12 to one tile cluster<br />
# FE == two - apply 34 to one tile cluster, then 56 to the following tile cluster<br />
# FF == one - apply 78 to one tile cluster<br />
# FD == three - apply 9A to one tile cluster, BC to the next, and DE to the third tile cluster<br />
# 00 == end of data<br />
<br />
An easy way to work out how many bytes are affected is this formula:<br />
<br />
iCount = 0x100 - iInitialByte<br />
<br />
This will return one for 0xFF, two for 0xFE, three for 0xFD, etc. If your variables are signed bytes, you can just use <tt>iCount = -iInitialByte</tt> instead. A loop can then be used to apply the change to the correct number of tile clusters.<br />
<br />
The last two bytes of the compressed data are always zero, which would always be read when the RLE algorithm expects to read a length-byte. So it should be safe to stop decompressing when you read 0 as length value.<br />
<br />
Note that since the length is the absolute value of a signed byte, the maximum length for both kinds of RLE flag is 127. A length-byte of 0x80 indicates an error. This is because 0x80 (-128) can't have a positive absolute value as the largest positive value in a signed byte is 127. If the game encounters a length-byte of 0x80 upon loading a level file, it will freeze after fading to black from the loading screen.<br />
<br />
==== Data values ====<br />
<br />
Each of the data values used above affects a tile cluster made up of four tiles. If the tiles are arranged from left to right, the least-significant bits affect the first (left-most) tile, and the most-significant bits affect the last (right-most) tile. For example:<br />
<br />
tile1 = (iChange & 0x03);<br />
tile2 = ((iChange >> 2) & 0x03);<br />
tile3 = ((iChange >> 4) & 0x03);<br />
tile4 = (iChange >> 6);<br />
<br />
This will give tileX a value between 0 and 3, which when multiplied by 32 can be directly added to the value of the foreground tile. For the computer scientists out there, a more efficient alternative is this:<br />
<br />
tile1 = (iChange << 5) & 0x60;<br />
tile2 = (iChange << 3) & 0x60;<br />
tile3 = (iChange << 1) & 0x60;<br />
tile4 = (iChange >> 1) & 0x60;<br />
<br />
The values here are already multiplied out, and can be OR'd with the foreground tile numbers.<br />
<br />
One major thing to remember is that these extra values ONLY apply to those tiles where a foreground and a background tile is specified in the map data. If the tile only has a background tile, or it only has a foreground tile, then it's possible to specify the full range of tile numbers and the values stored here (if any) should be ignored for those tiles. It's only when a tile has a foreground *and* a background tile that these extra bits must be used.<br />
<br />
==== Example ====<br />
<br />
This is some example code to read in all the tile values into an array, which the map code can later reference as necessary:<br />
<br />
int *iExtraValues = new int[65500 / 2]; // one element per tile, maps are fixed length<br />
int *pNextByte = iExtraValues; // running counter<br />
<br />
// Read through all the extra data<br />
for (int i = 0; i < iExtraFGLength; i++) {<br />
unsigned char iVal = readNextByte(); // must be 8-bit for logic below to work<br />
<br />
if (iVal & 0x80) {<br />
// Multiple bytes concatenated together<br />
// iVal == 0xFF for one byte, 0xFE for two bytes, etc.<br />
while (iVal++ > 0) { // should eventually wrap from 0xFF to 0x00 when we're done - only works with an 8-bit variable though<br />
applyChange(&pNextByte, readNextByte());<br />
i++;<br />
assert(iVal < 0x100); // prevent headaches if someone uses an int instead<br />
}<br />
} else {<br />
// iVal <= 0x7F<br />
UINT8 iChange = readNextByte(); i++;<br />
<br />
if (iChange > 0) {<br />
// Apply this change iVal times<br />
while (iVal-- > 0) applyChange(&pNextByte, iChange);<br />
} else {<br />
// No changes to the tiles, just skip over them (faster than applying a<br />
// change of 0x00 to all these tiles!)<br />
pNextByte += iVal * 4;<br />
// NOTE: This will only work if you zero the array first - if you don't,<br />
// use a loop here to zero out the tiles instead.<br />
}<br />
}<br />
}<br />
<br />
inline void applyChange(int **ppNextByte, int iChange)<br />
throw ()<br />
{<br />
// Grab each lot of 2-bits and shift into bits 5&6<br />
**ppNextByte = (iChange << 5) & 0x60; (*ppNextByte)++; // 10 -> 65<br />
**ppNextByte = (iChange << 3) & 0x60; (*ppNextByte)++; // 32 -> 65<br />
**ppNextByte = (iChange << 1) & 0x60; (*ppNextByte)++; // 54 -> 65<br />
**ppNextByte = (iChange >> 1) & 0x60; (*ppNextByte)++; // 76 -> 65<br />
return;<br />
}<br />
<br />
=== Example ===<br />
<br />
The following code converts a cell value into two tile indices for that cell - an index into the solid/background tilemap, and another index into the mask/foreground tileset.<br />
<br />
#define DN2_NUM_SOLID_TILES 1000<br />
#define DN2_NUM_MASKED_TILES 160<br />
#define DN2_TILEWIDTH 8 // Tiles are 8x8<br />
<br />
int iSolid, iMasked; // Index into respective tilesets<br />
int iValue = iMapData[x]; // This is the cell to convert<br />
int iExtra = iExtraData[x]; // Extra data for this cell<br />
<br />
<b>if (iValue & 0x8000) {</b><br />
// Most significant bit is set (see [[#Most significant bit set]] above),<br />
// so this cell has a foreground *and* a background tile.<br />
<br />
// First 10 bits are the index of the solid tile<br />
iSolid = iValue & 0x3FF;<br />
<br />
// Remaining five bits (not counting the sixth bit which was used as<br />
// the 0x8000 flag above) are the index of the mask tile.<br />
iMasked = ((iValue >> 10) & 0x1F);<br />
<br />
// Add the extra two bits (see [[#Supplemental foreground data]] above)<br />
iMasked |= iExtra; // assumes iExtra has already been multiplied out<br />
//iMasked = iMasked + (iExtra * 32); // if it hasn't been multiplied out<br />
<br />
<b>} else if (iValue < DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) {</b><br />
// Background only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a pixel coordinate into a tile unit.<br />
iSolid = iValue / 8;<br />
<br />
iMasked = -1; // No mask tile in this cell<br />
<br />
<b>} else {</b><br />
// Foreground only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a pixel coordinate into a tile unit.<br />
int iRawIndex = iValue / 8;<br />
<br />
// Make the first masked tile start at zero, instead of following on<br />
// from the solid tiles.<br />
int iMaskIndex = iRawIndex - DN2_NUM_SOLID_TILES;<br />
<br />
// For some reason the mask index has five images per tile...or something.<br />
iMasked = iMaskIndex / 5;<br />
<br />
// No solid tile in this cell, so using zero will make the solid cell<br />
// transparent, allowing the map backdrop to show through.<br />
iSolid = 0;<br />
}<br />
<br />
=== Gotchas ===<br />
<br />
* The masked tileset has no "blank" tile, so if the foreground layer is being loaded into an array a value will need to be used to indicate that no foreground tile occupies that cell. Zero can't be used without tweaking, as masked tile zero is an actual graphic tile.<br />
<br />
* The first tile in the solid tileset is used as a transparent tile. It will appear as a red square if drawn (e.g. in a map editor) however the game does not draw this tile, so any cells with this as the background cell will be where the map backdrop shows through.<br />
<br />
* Even with the supplemental foreground data, there are only seven bits available for the foreground tile. This means that only 127 of the 160 available tiles can be used!<br />
<br />
== Actor data ==<br />
<br />
The <tt>actorData</tt> block is in the following format:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iType||Type of actor<br />
|-<br />
|UINT16LE iX||X-coordinate of actor (in tile units)<br />
|-<br />
|UINT16LE iY||Y-coordinate of actor (in tile units)<br />
|}<br />
<br />
Because the <tt>iActorSize</tt> value in the header is in UINT16s and there are three UINT16s per actor, the number of actors can be obtained quite simply:<br />
<br />
iNumActors = iActorSize / 3<br />
<br />
The <tt>iType</tt> field can conveniently be used as an index into the [[Duke Nukem II Actor Info|ACTRINFO.MNI]] sprite file. If <tt>iType</tt> is zero, the first actor image is used.<br />
<br />
Not all actors have images however, some point to actor sprites with zero frames. It appears to be that some of these "actors" are actually flags, indicating that the next actor (in the level, not in the <tt>ACTRINFO.MNI</tt>) will only appear when the level is played at a certain difficulty level.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=map}}<br />
{{FileFormatTool<br />
| Name = [http://chidesters.org/scottchidester.com/index.html Nukem2Print]<br />
| Platform = Qt<br />
| canView = Yes<br />
| canCreate = No<br />
| canModify = No<br />
| editHidden = N/A<br />
| editMetadata = N/A<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [http://www.szevvy.com Szevvy] figured out how to map the cell values back to the tilesets. [[User:Malvineous|Malvineous]] figured out the format of the supplemental foreground data. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Map_Format&diff=9745Duke Nukem II Map Format2021-05-07T13:51:31Z<p>Lethal guitar: Extend level flag bits info</p>
<hr />
<div>{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 3<br />
| Tile size = 8&times;8<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
[[Duke Nukem II]] stores its levels in an evolution of the level format used by [[Cosmo's Cosmic Adventures]]. One considerable change is the addition of a second map layer, allowing foreground and background tiles to be placed in the same cell. Surprisingly, given the number of other changes to the file format, the layer data was not restructured to handle this new feature. Instead, some of the new data was shoehorned to fit in the existing space, with the rest of the data tacked on to the end of the file in a somewhat clunky manner. This resulted in some unusual limitations, such as a small number of tiles that can only be placed in the foreground layer if there is no background tile in the same cell.<br />
<br />
== File format ==<br />
The file is in this basic layout:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iBackgroundOffset||Offset where the background layer starts<br />
|-<br />
|char cCZoneName[13]||CZone filename (tileset)<br />
|-<br />
|char cBackdropName[13]||Name of the graphic to use as the level background (one of the <tt>BD*.MNI</tt> files)<br />
|-<br />
|char cMusicName[13]||Name of the [[IMF Format|IMF file]] to use as background music<br />
|-<br />
|BYTE bFlags||Level flags (define type of parallax scrolling and more)<br />
|-<br />
|BYTE bAltBackdrop||Number of alternate backdrop file, zero if no alternate backdrop is used<br />
|-<br />
|BYTE iUnused[2]||Ignored. Likely a holdover from the game's development<br />
|-<br />
|UINT16LE iActorSize||Number of UINT16 values in the actor block<br />
|-<br />
|ACTORDATA actorData[]||Variable-length array of all the actors in the level<br />
|-<br />
|GRIDLAYER gridData||see [[#Foreground and Background Layers]] below for what this contains<br />
|-<br />
|UINT16LE iExtraFGLength||Length of the additional mask/foreground manipulation data<br />
|-<br />
|BYTE iExtraFGData[iExtraFGLength]||See [[#Supplemental foreground data]] below<br />
|-<br />
|char cAttrName[13]||Zone attribute filename<br />
|-<br />
|char cTileName[13]||Zone tile filename<br />
|-<br />
|char cMaskName[13]||Zone masked tiles filename<br />
|}<br />
<br />
The three filenames at the end of the level file never appear to be used. In fact, the level files can end directly after the supplemental foreground data and the game will still load them without reporting any errors. These are probably the names of Cosmo-style tileset files ("zones") that were used before the CZone format (the "c" probably stands for "compound") was introduced.<br />
<br />
For all level files of the full version, the iActorSize value is reliable and the gridData begins directly after the last actor, leaving no padding between actor data and grid data.<br />
<br />
== Level Flags ==<br />
<br />
This is what each bit in the bFlags value indicates:<br />
<br />
Bit | Description<br />
----+------------<br />
7 | switch backdrop when using teleporter (used in L1)<br />
6 | switch backdrop when destroying force field (used in L5)<br />
5 | earthquake<br />
4 | automatic backdrop scrolling ^^<br />
3 | automatic backdrop scrolling <<<br />
2 | - (ignored/unused)<br />
1 | x-scrolling backdrop<br />
0 | x- and y-scrolling backdrop<br />
<br />
Bits 6 and 7 require an alternate backdrop index. The files where these bits are set are also the only files that have a non-zero alternate backdrop number.<br />
<br />
Note that the backdrop/scrolling related bits can NOT be combined! For example, combining bits 3 and 4 will not lead to combined movement. It only enables vertical auto-scrolling, albeit at twice the speed compared to having just bit 3 set on its own.<br />
<br />
The game evaluates these bits one after another, and the last evaluated bit always overrides any previously set bit. The evaluation order is as follows:<br />
<br />
# horizontal auto scrolling (bit 4)<br />
# vertical auto scrolling (bit 3)<br />
# x- and y-scrolling (bit 0)<br />
# x-scrolling (bit 1)<br />
<br />
In other words, if bit 1 is set, bits 3/4/0 will be ignored. If bit 0 is set, bits 3/4 will be ignored. Etc.<br />
<br />
The earthquake flag (bit 5) can be freely combined with any other bits.<br />
<br />
The backdrop switch flags can be combined, but the result isn't really useful, since there can only be two different backdrops.<br />
<br />
== Foreground and Background Layers ==<br />
<br />
At the offset indicated by <tt>iBackgroundOffset</tt> in the header, the grid/cell data begins:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iMapWidth||Map width (in tiles)<br />
|-<br />
|UINT16LE iMapData[32750]||Actual map data<br />
|}<br />
<br />
Each "element" in <tt>iMapData</tt> refers to the foreground and/or background tile used in a single grid cell. The grids are arranged left to right, top to bottom, so the index can be calculated by this formula:<br />
<br />
int iIndex = (y * iMapWidth) + x;<br />
iMapData[iIndex] = <new value to set at x,y><br />
<br />
The map data is a constant 32750 UINT16LE cells long (65500 bytes), so the map height can be calculated from this:<br />
<br />
int iMapHeight = 32750 / iMapWidth;<br />
<br />
=== Mapping cell values to tiles ===<br />
<br />
The method of mapping elements in the <tt>iMapData</tt> structure into tiles is a little complicated. There are two different methods of storing values:<br />
<br />
# If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell contains both a foreground and background tile.<br />
# Otherwise the cell value is a raw pixel-based index into the tilemap.<br />
<br />
==== Most significant bit set ====<br />
<br />
If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell value contains two indices - one for the solid/background tile, and one for the masked/foreground tile. Unlike the other type of cell value, these are actual tile indices as opposed to pixel indexes (i.e. a value of 2 refers to the third tile.)<br />
<br />
The lower ten bits are the index of the background tile (this can provide a value between 0 and 1023, however since there are only 1000 tiles this value should always be less than 1000.) The lower ten bits can be isolated like this:<br />
<br />
iSolid = iMapData[x] & 0x03FF;<br />
<br />
The ignoring the most significant bit (which is used to indicate this type of tile value) the remaining five most significant bits are used as the index of the foreground/mask tile. These bits can be isolated as follows:<br />
<br />
iMask = (iMapData[x] >> 10) & 0x1F;<br />
<br />
This only provides a value between 0 and 31 - but since there are 160 masked tiles, this value needs to be further manipulated to produce an index into the masked tileset. An additional two bits per tile are stored in the [[#Supplemental foreground data]] which are used for this purpose, but this will still only allow the first 127 tiles to be used.<br />
<br />
==== Most significant bit NOT set ====<br />
<br />
If the most significant bit is not set, then the cell value is a pixel index into the tilemap. Imagine the tileset laid out left to right all on the one row. Since each tile is 8x8 pixels, at 16 pixels into the image, the third tile will begin (x = 0 for first tile, x = 8 for second tile, x = 16 for third tile.) The cell value is essentially x-coordinate in the previous value (if it's 16, then the third tile should be displayed in that cell.)<br />
<br />
Note that in the CZone tileset file, the solid tiles are made up of 1000 4-plane (16-colour) images, and these are followed by 160 5-plane images (16-colour + transparency.) Although seperate images, they appear to be concatenated when the game loads them, as the cell value will be zero for the first solid tile, it will be eight for the second solid tile, it will be 7992 for the last solid tile, and it will be 8000 for the first masked tile.<br />
<br />
This means that if the solid and masked tiles have been treated as separate images, a check will need to be performed so that any cell value below 8000 is loaded from the solid tileset, and any value larger than this is loaded from the masked tileset (after subtracting 8000 from the cell value, to put it at zero for the first masked tile.) Having said that, for some reason the values for the masked tiles are at multiples of five tiles (tile #0 (x=0) is the first tile, tile #5 (x=40) is the second tile.)<br />
<br />
Note that while the cell value cannot be out of range for the solid tiles/background layer (since any values larger than 8000 will be loaded from the masked tileset into the foreground layer) the cell values for the masked tileset have no such restriction.<br />
<br />
=== Supplemental foreground data ===<br />
<br />
Since the tiles containing combined foreground+background data only provide five bits for the foreground tile, this block of data is used to store an additional two bits for each tile, allowing a seven-bit tile number to be specified.<br />
<br />
The data is stored in a form of run length encoding (RLE) to reduce the size, given that a relatively small number of tiles need the extra two bits. Because there are eight bits in a byte, four lots of two-bit-chunks can be stored in every data byte. This means each data byte contains the data for four tiles. The rest of this section uses the term "tile cluster" to refer to this grouping of four tiles.<br />
<br />
==== Data layout ====<br />
<br />
The data is read one byte at a time, and the initial byte ("length-byte") indicates how many subsequent bytes need to be read. The length-bytes are ''signed'' byte values with the absolute value giving the length to be used for the following data. If the length-byte is positive (high bit is NOT set), then it is a count of the number of affected tile clusters. The following byte is the data byte containing the extra tile data. For example: (brackets inserted for clarity)<br />
<br />
[7F 00] [7F 00] [05 FF] [7F 00]<br />
<br />
This means apply value 00 to the first 254 (0x7F * 2) tile clusters, followed by value FF to the next five tile clusters, followed by 00 to the next 127 tile clusters. The format of these "values" is described below.<br />
<br />
If the length-byte is negative (high bit IS set), then a number of data values follow, depending on the absolute value of the length-byte. If the bytes are arranged like this:<br />
<br />
[FF 12] [FE 34 56] [FF 78] [FD 9A BC DE] [00 00]<br />
<br />
Then it would be interpreted as follows:<br />
<br />
# FF == one - apply value 12 to one tile cluster<br />
# FE == two - apply 34 to one tile cluster, then 56 to the following tile cluster<br />
# FF == one - apply 78 to one tile cluster<br />
# FD == three - apply 9A to one tile cluster, BC to the next, and DE to the third tile cluster<br />
# 00 == end of data<br />
<br />
An easy way to work out how many bytes are affected is this formula:<br />
<br />
iCount = 0x100 - iInitialByte<br />
<br />
This will return one for 0xFF, two for 0xFE, three for 0xFD, etc. If your variables are signed bytes, you can just use <tt>iCount = -iInitialByte</tt> instead. A loop can then be used to apply the change to the correct number of tile clusters.<br />
<br />
The last two bytes of the compressed data are always zero, which would always be read when the RLE algorithm expects to read a length-byte. So it should be safe to stop decompressing when you read 0 as length value.<br />
<br />
Note that since the length is the absolute value of a signed byte, the maximum length for both kinds of RLE flag is 127. A length-byte of 0x80 indicates an error. This is because 0x80 (-128) can't have a positive absolute value as the largest positive value in a signed byte is 127. If the game encounters a length-byte of 0x80 upon loading a level file, it will freeze after fading to black from the loading screen.<br />
<br />
==== Data values ====<br />
<br />
Each of the data values used above affects a tile cluster made up of four tiles. If the tiles are arranged from left to right, the least-significant bits affect the first (left-most) tile, and the most-significant bits affect the last (right-most) tile. For example:<br />
<br />
tile1 = (iChange & 0x03);<br />
tile2 = ((iChange >> 2) & 0x03);<br />
tile3 = ((iChange >> 4) & 0x03);<br />
tile4 = (iChange >> 6);<br />
<br />
This will give tileX a value between 0 and 3, which when multiplied by 32 can be directly added to the value of the foreground tile. For the computer scientists out there, a more efficient alternative is this:<br />
<br />
tile1 = (iChange << 5) & 0x60;<br />
tile2 = (iChange << 3) & 0x60;<br />
tile3 = (iChange << 1) & 0x60;<br />
tile4 = (iChange >> 1) & 0x60;<br />
<br />
The values here are already multiplied out, and can be OR'd with the foreground tile numbers.<br />
<br />
One major thing to remember is that these extra values ONLY apply to those tiles where a foreground and a background tile is specified in the map data. If the tile only has a background tile, or it only has a foreground tile, then it's possible to specify the full range of tile numbers and the values stored here (if any) should be ignored for those tiles. It's only when a tile has a foreground *and* a background tile that these extra bits must be used.<br />
<br />
==== Example ====<br />
<br />
This is some example code to read in all the tile values into an array, which the map code can later reference as necessary:<br />
<br />
int *iExtraValues = new int[65500 / 2]; // one element per tile, maps are fixed length<br />
int *pNextByte = iExtraValues; // running counter<br />
<br />
// Read through all the extra data<br />
for (int i = 0; i < iExtraFGLength; i++) {<br />
unsigned char iVal = readNextByte(); // must be 8-bit for logic below to work<br />
<br />
if (iVal & 0x80) {<br />
// Multiple bytes concatenated together<br />
// iVal == 0xFF for one byte, 0xFE for two bytes, etc.<br />
while (iVal++ > 0) { // should eventually wrap from 0xFF to 0x00 when we're done - only works with an 8-bit variable though<br />
applyChange(&pNextByte, readNextByte());<br />
i++;<br />
assert(iVal < 0x100); // prevent headaches if someone uses an int instead<br />
}<br />
} else {<br />
// iVal <= 0x7F<br />
UINT8 iChange = readNextByte(); i++;<br />
<br />
if (iChange > 0) {<br />
// Apply this change iVal times<br />
while (iVal-- > 0) applyChange(&pNextByte, iChange);<br />
} else {<br />
// No changes to the tiles, just skip over them (faster than applying a<br />
// change of 0x00 to all these tiles!)<br />
pNextByte += iVal * 4;<br />
// NOTE: This will only work if you zero the array first - if you don't,<br />
// use a loop here to zero out the tiles instead.<br />
}<br />
}<br />
}<br />
<br />
inline void applyChange(int **ppNextByte, int iChange)<br />
throw ()<br />
{<br />
// Grab each lot of 2-bits and shift into bits 5&6<br />
**ppNextByte = (iChange << 5) & 0x60; (*ppNextByte)++; // 10 -> 65<br />
**ppNextByte = (iChange << 3) & 0x60; (*ppNextByte)++; // 32 -> 65<br />
**ppNextByte = (iChange << 1) & 0x60; (*ppNextByte)++; // 54 -> 65<br />
**ppNextByte = (iChange >> 1) & 0x60; (*ppNextByte)++; // 76 -> 65<br />
return;<br />
}<br />
<br />
=== Example ===<br />
<br />
The following code converts a cell value into two tile indices for that cell - an index into the solid/background tilemap, and another index into the mask/foreground tileset.<br />
<br />
#define DN2_NUM_SOLID_TILES 1000<br />
#define DN2_NUM_MASKED_TILES 160<br />
#define DN2_TILEWIDTH 8 // Tiles are 8x8<br />
<br />
int iSolid, iMasked; // Index into respective tilesets<br />
int iValue = iMapData[x]; // This is the cell to convert<br />
int iExtra = iExtraData[x]; // Extra data for this cell<br />
<br />
<b>if (iValue & 0x8000) {</b><br />
// Most significant bit is set (see [[#Most significant bit set]] above),<br />
// so this cell has a foreground *and* a background tile.<br />
<br />
// First 10 bits are the index of the solid tile<br />
iSolid = iValue & 0x3FF;<br />
<br />
// Remaining five bits (not counting the sixth bit which was used as<br />
// the 0x8000 flag above) are the index of the mask tile.<br />
iMasked = ((iValue >> 10) & 0x1F);<br />
<br />
// Add the extra two bits (see [[#Supplemental foreground data]] above)<br />
iMasked |= iExtra; // assumes iExtra has already been multiplied out<br />
//iMasked = iMasked + (iExtra * 32); // if it hasn't been multiplied out<br />
<br />
<b>} else if (iValue < DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) {</b><br />
// Background only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a pixel coordinate into a tile unit.<br />
iSolid = iValue / 8;<br />
<br />
iMasked = -1; // No mask tile in this cell<br />
<br />
<b>} else {</b><br />
// Foreground only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a pixel coordinate into a tile unit.<br />
int iRawIndex = iValue / 8;<br />
<br />
// Make the first masked tile start at zero, instead of following on<br />
// from the solid tiles.<br />
int iMaskIndex = iRawIndex - DN2_NUM_SOLID_TILES;<br />
<br />
// For some reason the mask index has five images per tile...or something.<br />
iMasked = iMaskIndex / 5;<br />
<br />
// No solid tile in this cell, so using zero will make the solid cell<br />
// transparent, allowing the map backdrop to show through.<br />
iSolid = 0;<br />
}<br />
<br />
=== Gotchas ===<br />
<br />
* The masked tileset has no "blank" tile, so if the foreground layer is being loaded into an array a value will need to be used to indicate that no foreground tile occupies that cell. Zero can't be used without tweaking, as masked tile zero is an actual graphic tile.<br />
<br />
* The first tile in the solid tileset is used as a transparent tile. It will appear as a red square if drawn (e.g. in a map editor) however the game does not draw this tile, so any cells with this as the background cell will be where the map backdrop shows through.<br />
<br />
* Even with the supplemental foreground data, there are only seven bits available for the foreground tile. This means that only 127 of the 160 available tiles can be used!<br />
<br />
== Actor data ==<br />
<br />
The <tt>actorData</tt> block is in the following format:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iType||Type of actor<br />
|-<br />
|UINT16LE iX||X-coordinate of actor (in tile units)<br />
|-<br />
|UINT16LE iY||Y-coordinate of actor (in tile units)<br />
|}<br />
<br />
Because the <tt>iActorSize</tt> value in the header is in UINT16s and there are three UINT16s per actor, the number of actors can be obtained quite simply:<br />
<br />
iNumActors = iActorSize / 3<br />
<br />
The <tt>iType</tt> field can conveniently be used as an index into the [[Duke Nukem II Actor Info|ACTRINFO.MNI]] sprite file. If <tt>iType</tt> is zero, the first actor image is used.<br />
<br />
Not all actors have images however, some point to actor sprites with zero frames. It appears to be that some of these "actors" are actually flags, indicating that the next actor (in the level, not in the <tt>ACTRINFO.MNI</tt>) will only appear when the level is played at a certain difficulty level.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=map}}<br />
{{FileFormatTool<br />
| Name = [http://chidesters.org/scottchidester.com/index.html Nukem2Print]<br />
| Platform = Qt<br />
| canView = Yes<br />
| canCreate = No<br />
| canModify = No<br />
| editHidden = N/A<br />
| editMetadata = N/A<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [http://www.szevvy.com Szevvy] figured out how to map the cell values back to the tilesets. [[User:Malvineous|Malvineous]] figured out the format of the supplemental foreground data. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_CZone_Format&diff=9744Duke Nukem II CZone Format2021-05-07T12:29:08Z<p>Lethal guitar: Remove "needs more info" now that we have a tileset image and full format info</p>
<hr />
<div>{{Tileset Infobox<br />
| Hardware1 = EGA<br />
| MaxTiles = Fixed<br />
| Palette = External<br />
| Names = N<br />
| TileMinSize = 8&times;8<br />
| TileMaxSize = 8&times;8<br />
| NumPlanes = 4-5<br />
| PlaneArrangement = [[Raw EGA data#Byte-planar EGA data|Byte-planar]]<br />
| HasTransparency = Y<br />
| HasHitmap = N<br />
| Metadata = None<br />
| Subtilesets = N<br />
| Compressed = N<br />
| Hidden = N<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The CZone format stores three blocks of data - the behaviour of each tile in a tileset, a collection of images for the "solid" background tiles, and a collection of images for the "masked" foreground tiles.<br />
<br />
== File format ==<br />
<br />
{|class="wikitable"<br />
! Data type !! Name !! Description<br />
|-<br />
| BYTE[3600] || attributeData || Attribute data<br />
|-<br />
| BYTE[32000] || solidTileset || Graphics for the solid tileset<br />
|-<br />
| BYTE[6400] || maskedTileset || Graphics for the masked tileset<br />
|}<br />
<br />
=== Attribute Data ===<br />
<br />
The attribute data consists of 1000 [[UINT16LE]] values (settings for the solid tiles), followed by 800 [[UINT16LE]] values (five values for each masked tile). It seems like ''only the first value'' for each masked tile is actually used by the game.<br />
<br />
Each individual attribute value is a bitmask. The meaning of the bits is as follows:<br />
<br />
{|class="wikitable"<br />
! Bit !! Description<br />
|-<br />
| 15 || -<br />
|-<br />
| 14 || climbable (chains/ladders)<br />
|-<br />
| 13 || -<br />
|-<br />
| 12 || -<br />
|-<br />
| 11 || -<br />
|-<br />
| 10 || fast animation<br />
|-<br />
| 9 || conveyor belt -><br />
|-<br />
| 8 || conveyor belt <-<br />
|-<br />
| 7 || can hang on it (pipes, vines etc.)<br />
|-<br />
| 6 || burns away when shot<br />
|-<br />
| 5 || draw in front<br />
|-<br />
| 4 || animate (uses current tile and next 3 tiles)<br />
|-<br />
| 3 || solid/blocking right<br />
|-<br />
| 2 || solid/blocking left<br />
|-<br />
| 1 || solid/blocking up<br />
|-<br />
| 0 || solid/blocking down<br />
|}<br />
<br />
Bits 11, 12, 13, and 15 are not used by the game, and simply ignored if they are set. The tileset "czone9" has bit 15 set on some of its tiles (horns), but it has no effect. Most likely a leftover from an earlier point in the game's development.<br />
<br />
=== Solid Tileset ===<br />
<br />
The solid tileset is comprised of 1000 tiles. Each tile is 8x8 pixels, and for best effect when presenting it to a user/designer it should be displayed in a grid 40 tiles wide by 25 tiles high.<br />
<br />
The tiles are stored in [[Cosmo Tileset Format]]. The first plane is blue, followed by green, red and then intensity.<br />
<br />
Since the tiles are 8x8, each plane's scanline fits into one byte. At eight pixels tall that makes each plane occupy eight bytes, and with four planes the whole tile takes up 32 bytes. This means you can seek to any tile with a simple formula:<br />
<br />
iOffset = iTileIndex * 32<br />
<br />
=== Masked Tileset ===<br />
<br />
The masked tileset is almost identical to the solid tileset, except that there are only 160 tiles (so only four rows high, but still 40 tiles wide) and of course being a masked tileset, there are five planes. The first plane is transparency, followed by blue, green, red and intensity as with the solid tiles.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [[User:Lethal_guitar|lethal guitar]] verified that unknown/unused tile attribute bits are not used by the game. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=File:Duke_Nukem_II_CZone_Format.png&diff=9743File:Duke Nukem II CZone Format.png2021-05-07T12:28:07Z<p>Lethal guitar: </p>
<hr />
<div>[[Duke Nukem II]] tileset "CZone1"<br />
[[Category:Tileset images]]<br />
<br />
== Summary ==<br />
One of the game's tilesets, aspect-ratio corrected to appear as it would when playing the game on a CRT monitor.</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=File:Duke_Nukem_II_CZone_Format.png&diff=9742File:Duke Nukem II CZone Format.png2021-05-07T12:22:12Z<p>Lethal guitar: The game's first tileset (CZone1), aspect-ratio corrected to appear as it would when playing the game on a CRT monitor.</p>
<hr />
<div>== Summary ==<br />
The game's first tileset (CZone1), aspect-ratio corrected to appear as it would when playing the game on a CRT monitor.</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Map_Format&diff=9727Duke Nukem II Map Format2021-05-04T20:01:45Z<p>Lethal guitar: Clarified some previously unknown information based on insights from disassembly</p>
<hr />
<div>{{Map Infobox<br />
| Type = 2D tile-based<br />
| Layers = 3<br />
| Tile size = 8&times;8<br />
| Viewport = 256&times;160<br />
| Games = <br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
[[Duke Nukem II]] stores its levels in an evolution of the level format used by [[Cosmo's Cosmic Adventures]]. One considerable change is the addition of a second map layer, allowing foreground and background tiles to be placed in the same cell. Surprisingly, given the number of other changes to the file format, the layer data was not restructured to handle this new feature. Instead, some of the new data was shoehorned to fit in the existing space, with the rest of the data tacked on to the end of the file in a somewhat clunky manner. This resulted in some unusual limitations, such as a small number of tiles that can only be placed in the foreground layer if there is no background tile in the same cell.<br />
<br />
== File format ==<br />
The file is in this basic layout:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iBackgroundOffset||Offset where the background layer starts<br />
|-<br />
|char cCZoneName[13]||CZone filename (tileset)<br />
|-<br />
|char cBackdropName[13]||Name of the graphic to use as the level background (one of the <tt>BD*.MNI</tt> files)<br />
|-<br />
|char cMusicName[13]||Name of the [[IMF Format|IMF file]] to use as background music<br />
|-<br />
|BYTE bFlags||Level flags (define type of parallax scrolling and more)<br />
|-<br />
|BYTE bAltBackdrop||Number of alternate backdrop file, zero if no alternate backdrop is used<br />
|-<br />
|BYTE iUnused[2]||Ignored. Likely a holdover from the game's development<br />
|-<br />
|UINT16LE iActorSize||Number of UINT16 values in the actor block<br />
|-<br />
|ACTORDATA actorData[]||Variable-length array of all the actors in the level<br />
|-<br />
|GRIDLAYER gridData||see [[#Foreground and Background Layers]] below for what this contains<br />
|-<br />
|UINT16LE iExtraFGLength||Length of the additional mask/foreground manipulation data<br />
|-<br />
|BYTE iExtraFGData[iExtraFGLength]||See [[#Supplemental foreground data]] below<br />
|-<br />
|char cAttrName[13]||Zone attribute filename<br />
|-<br />
|char cTileName[13]||Zone tile filename<br />
|-<br />
|char cMaskName[13]||Zone masked tiles filename<br />
|}<br />
<br />
The three filenames at the end of the level file never appear to be used. In fact, the level files can end directly after the supplemental foreground data and the game will still load them without reporting any errors. These are probably the names of Cosmo-style tileset files ("zones") that were used before the CZone format (the "c" probably stands for "compound") was introduced.<br />
<br />
For all level files of the full version, the iActorSize value is reliable and the gridData begins directly after the last actor, leaving no padding between actor data and grid data.<br />
<br />
== Level Flags ==<br />
<br />
This is what each bit in the bFlags value indicates:<br />
<br />
Bit | Description<br />
----+------------<br />
7 | switch backdrop when using teleporter (used in L1)<br />
6 | switch backdrop when destroying force field (used in L5)<br />
5 | earthquake<br />
4 | permanent backdrop movement ^^<br />
3 | permanent backdrop movement <<<br />
2 | ignored/unused<br />
1 | x-scrolling backdrop<br />
0 | x- and y-scrolling backdrop<br />
<br />
Bits 6 and 7 require an alternate backdrop index. The files where these bits are set are also the only files that have a non-zero alternate backdrop number.<br />
<br />
Most of the bits can NOT be combinated! For example, combining bits 3 and 4 will not lead to combined movement.<br />
<br />
== Foreground and Background Layers ==<br />
<br />
At the offset indicated by <tt>iBackgroundOffset</tt> in the header, the grid/cell data begins:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iMapWidth||Map width (in tiles)<br />
|-<br />
|UINT16LE iMapData[32750]||Actual map data<br />
|}<br />
<br />
Each "element" in <tt>iMapData</tt> refers to the foreground and/or background tile used in a single grid cell. The grids are arranged left to right, top to bottom, so the index can be calculated by this formula:<br />
<br />
int iIndex = (y * iMapWidth) + x;<br />
iMapData[iIndex] = <new value to set at x,y><br />
<br />
The map data is a constant 32750 UINT16LE cells long (65500 bytes), so the map height can be calculated from this:<br />
<br />
int iMapHeight = 32750 / iMapWidth;<br />
<br />
=== Mapping cell values to tiles ===<br />
<br />
The method of mapping elements in the <tt>iMapData</tt> structure into tiles is a little complicated. There are two different methods of storing values:<br />
<br />
# If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell contains both a foreground and background tile.<br />
# Otherwise the cell value is a raw pixel-based index into the tilemap.<br />
<br />
==== Most significant bit set ====<br />
<br />
If the most significant bit is set (<tt>iMapData[x] & 0x8000</tt>) then the cell value contains two indices - one for the solid/background tile, and one for the masked/foreground tile. Unlike the other type of cell value, these are actual tile indices as opposed to pixel indexes (i.e. a value of 2 refers to the third tile.)<br />
<br />
The lower ten bits are the index of the background tile (this can provide a value between 0 and 1023, however since there are only 1000 tiles this value should always be less than 1000.) The lower ten bits can be isolated like this:<br />
<br />
iSolid = iMapData[x] & 0x03FF;<br />
<br />
The ignoring the most significant bit (which is used to indicate this type of tile value) the remaining five most significant bits are used as the index of the foreground/mask tile. These bits can be isolated as follows:<br />
<br />
iMask = (iMapData[x] >> 10) & 0x1F;<br />
<br />
This only provides a value between 0 and 31 - but since there are 160 masked tiles, this value needs to be further manipulated to produce an index into the masked tileset. An additional two bits per tile are stored in the [[#Supplemental foreground data]] which are used for this purpose, but this will still only allow the first 127 tiles to be used.<br />
<br />
==== Most significant bit NOT set ====<br />
<br />
If the most significant bit is not set, then the cell value is a pixel index into the tilemap. Imagine the tileset laid out left to right all on the one row. Since each tile is 8x8 pixels, at 16 pixels into the image, the third tile will begin (x = 0 for first tile, x = 8 for second tile, x = 16 for third tile.) The cell value is essentially x-coordinate in the previous value (if it's 16, then the third tile should be displayed in that cell.)<br />
<br />
Note that in the CZone tileset file, the solid tiles are made up of 1000 4-plane (16-colour) images, and these are followed by 160 5-plane images (16-colour + transparency.) Although seperate images, they appear to be concatenated when the game loads them, as the cell value will be zero for the first solid tile, it will be eight for the second solid tile, it will be 7992 for the last solid tile, and it will be 8000 for the first masked tile.<br />
<br />
This means that if the solid and masked tiles have been treated as separate images, a check will need to be performed so that any cell value below 8000 is loaded from the solid tileset, and any value larger than this is loaded from the masked tileset (after subtracting 8000 from the cell value, to put it at zero for the first masked tile.) Having said that, for some reason the values for the masked tiles are at multiples of five tiles (tile #0 (x=0) is the first tile, tile #5 (x=40) is the second tile.)<br />
<br />
Note that while the cell value cannot be out of range for the solid tiles/background layer (since any values larger than 8000 will be loaded from the masked tileset into the foreground layer) the cell values for the masked tileset have no such restriction.<br />
<br />
=== Supplemental foreground data ===<br />
<br />
Since the tiles containing combined foreground+background data only provide five bits for the foreground tile, this block of data is used to store an additional two bits for each tile, allowing a seven-bit tile number to be specified.<br />
<br />
The data is stored in a form of run length encoding (RLE) to reduce the size, given that a relatively small number of tiles need the extra two bits. Because there are eight bits in a byte, four lots of two-bit-chunks can be stored in every data byte. This means each data byte contains the data for four tiles. The rest of this section uses the term "tile cluster" to refer to this grouping of four tiles.<br />
<br />
==== Data layout ====<br />
<br />
The data is read one byte at a time, and the initial byte ("length-byte") indicates how many subsequent bytes need to be read. The length-bytes are ''signed'' byte values with the absolute value giving the length to be used for the following data. If the length-byte is positive (high bit is NOT set), then it is a count of the number of affected tile clusters. The following byte is the data byte containing the extra tile data. For example: (brackets inserted for clarity)<br />
<br />
[7F 00] [7F 00] [05 FF] [7F 00]<br />
<br />
This means apply value 00 to the first 254 (0x7F * 2) tile clusters, followed by value FF to the next five tile clusters, followed by 00 to the next 127 tile clusters. The format of these "values" is described below.<br />
<br />
If the length-byte is negative (high bit IS set), then a number of data values follow, depending on the absolute value of the length-byte. If the bytes are arranged like this:<br />
<br />
[FF 12] [FE 34 56] [FF 78] [FD 9A BC DE] [00 00]<br />
<br />
Then it would be interpreted as follows:<br />
<br />
# FF == one - apply value 12 to one tile cluster<br />
# FE == two - apply 34 to one tile cluster, then 56 to the following tile cluster<br />
# FF == one - apply 78 to one tile cluster<br />
# FD == three - apply 9A to one tile cluster, BC to the next, and DE to the third tile cluster<br />
# 00 == end of data<br />
<br />
An easy way to work out how many bytes are affected is this formula:<br />
<br />
iCount = 0x100 - iInitialByte<br />
<br />
This will return one for 0xFF, two for 0xFE, three for 0xFD, etc. If your variables are signed bytes, you can just use <tt>iCount = -iInitialByte</tt> instead. A loop can then be used to apply the change to the correct number of tile clusters.<br />
<br />
The last two bytes of the compressed data are always zero, which would always be read when the RLE algorithm expects to read a length-byte. So it should be safe to stop decompressing when you read 0 as length value.<br />
<br />
Note that since the length is the absolute value of a signed byte, the maximum length for both kinds of RLE flag is 127. A length-byte of 0x80 indicates an error. This is because 0x80 (-128) can't have a positive absolute value as the largest positive value in a signed byte is 127. If the game encounters a length-byte of 0x80 upon loading a level file, it will freeze after fading to black from the loading screen.<br />
<br />
==== Data values ====<br />
<br />
Each of the data values used above affects a tile cluster made up of four tiles. If the tiles are arranged from left to right, the least-significant bits affect the first (left-most) tile, and the most-significant bits affect the last (right-most) tile. For example:<br />
<br />
tile1 = (iChange & 0x03);<br />
tile2 = ((iChange >> 2) & 0x03);<br />
tile3 = ((iChange >> 4) & 0x03);<br />
tile4 = (iChange >> 6);<br />
<br />
This will give tileX a value between 0 and 3, which when multiplied by 32 can be directly added to the value of the foreground tile. For the computer scientists out there, a more efficient alternative is this:<br />
<br />
tile1 = (iChange << 5) & 0x60;<br />
tile2 = (iChange << 3) & 0x60;<br />
tile3 = (iChange << 1) & 0x60;<br />
tile4 = (iChange >> 1) & 0x60;<br />
<br />
The values here are already multiplied out, and can be OR'd with the foreground tile numbers.<br />
<br />
One major thing to remember is that these extra values ONLY apply to those tiles where a foreground and a background tile is specified in the map data. If the tile only has a background tile, or it only has a foreground tile, then it's possible to specify the full range of tile numbers and the values stored here (if any) should be ignored for those tiles. It's only when a tile has a foreground *and* a background tile that these extra bits must be used.<br />
<br />
==== Example ====<br />
<br />
This is some example code to read in all the tile values into an array, which the map code can later reference as necessary:<br />
<br />
int *iExtraValues = new int[65500 / 2]; // one element per tile, maps are fixed length<br />
int *pNextByte = iExtraValues; // running counter<br />
<br />
// Read through all the extra data<br />
for (int i = 0; i < iExtraFGLength; i++) {<br />
unsigned char iVal = readNextByte(); // must be 8-bit for logic below to work<br />
<br />
if (iVal & 0x80) {<br />
// Multiple bytes concatenated together<br />
// iVal == 0xFF for one byte, 0xFE for two bytes, etc.<br />
while (iVal++ > 0) { // should eventually wrap from 0xFF to 0x00 when we're done - only works with an 8-bit variable though<br />
applyChange(&pNextByte, readNextByte());<br />
i++;<br />
assert(iVal < 0x100); // prevent headaches if someone uses an int instead<br />
}<br />
} else {<br />
// iVal <= 0x7F<br />
UINT8 iChange = readNextByte(); i++;<br />
<br />
if (iChange > 0) {<br />
// Apply this change iVal times<br />
while (iVal-- > 0) applyChange(&pNextByte, iChange);<br />
} else {<br />
// No changes to the tiles, just skip over them (faster than applying a<br />
// change of 0x00 to all these tiles!)<br />
pNextByte += iVal * 4;<br />
// NOTE: This will only work if you zero the array first - if you don't,<br />
// use a loop here to zero out the tiles instead.<br />
}<br />
}<br />
}<br />
<br />
inline void applyChange(int **ppNextByte, int iChange)<br />
throw ()<br />
{<br />
// Grab each lot of 2-bits and shift into bits 5&6<br />
**ppNextByte = (iChange << 5) & 0x60; (*ppNextByte)++; // 10 -> 65<br />
**ppNextByte = (iChange << 3) & 0x60; (*ppNextByte)++; // 32 -> 65<br />
**ppNextByte = (iChange << 1) & 0x60; (*ppNextByte)++; // 54 -> 65<br />
**ppNextByte = (iChange >> 1) & 0x60; (*ppNextByte)++; // 76 -> 65<br />
return;<br />
}<br />
<br />
=== Example ===<br />
<br />
The following code converts a cell value into two tile indices for that cell - an index into the solid/background tilemap, and another index into the mask/foreground tileset.<br />
<br />
#define DN2_NUM_SOLID_TILES 1000<br />
#define DN2_NUM_MASKED_TILES 160<br />
#define DN2_TILEWIDTH 8 // Tiles are 8x8<br />
<br />
int iSolid, iMasked; // Index into respective tilesets<br />
int iValue = iMapData[x]; // This is the cell to convert<br />
int iExtra = iExtraData[x]; // Extra data for this cell<br />
<br />
<b>if (iValue & 0x8000) {</b><br />
// Most significant bit is set (see [[#Most significant bit set]] above),<br />
// so this cell has a foreground *and* a background tile.<br />
<br />
// First 10 bits are the index of the solid tile<br />
iSolid = iValue & 0x3FF;<br />
<br />
// Remaining five bits (not counting the sixth bit which was used as<br />
// the 0x8000 flag above) are the index of the mask tile.<br />
iMasked = ((iValue >> 10) & 0x1F);<br />
<br />
// Add the extra two bits (see [[#Supplemental foreground data]] above)<br />
iMasked |= iExtra; // assumes iExtra has already been multiplied out<br />
//iMasked = iMasked + (iExtra * 32); // if it hasn't been multiplied out<br />
<br />
<b>} else if (iValue < DN2_NUM_SOLID_TILES * DN2_TILEWIDTH) {</b><br />
// Background only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a pixel coordinate into a tile unit.<br />
iSolid = iValue / 8;<br />
<br />
iMasked = -1; // No mask tile in this cell<br />
<br />
<b>} else {</b><br />
// Foreground only tile (see [[#Most significant bit NOT set]] above)<br />
<br />
// Convert the number from a pixel coordinate into a tile unit.<br />
int iRawIndex = iValue / 8;<br />
<br />
// Make the first masked tile start at zero, instead of following on<br />
// from the solid tiles.<br />
int iMaskIndex = iRawIndex - DN2_NUM_SOLID_TILES;<br />
<br />
// For some reason the mask index has five images per tile...or something.<br />
iMasked = iMaskIndex / 5;<br />
<br />
// No solid tile in this cell, so using zero will make the solid cell<br />
// transparent, allowing the map backdrop to show through.<br />
iSolid = 0;<br />
}<br />
<br />
=== Gotchas ===<br />
<br />
* The masked tileset has no "blank" tile, so if the foreground layer is being loaded into an array a value will need to be used to indicate that no foreground tile occupies that cell. Zero can't be used without tweaking, as masked tile zero is an actual graphic tile.<br />
<br />
* The first tile in the solid tileset is used as a transparent tile. It will appear as a red square if drawn (e.g. in a map editor) however the game does not draw this tile, so any cells with this as the background cell will be where the map backdrop shows through.<br />
<br />
* Even with the supplemental foreground data, there are only seven bits available for the foreground tile. This means that only 127 of the 160 available tiles can be used!<br />
<br />
== Actor data ==<br />
<br />
The <tt>actorData</tt> block is in the following format:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iType||Type of actor<br />
|-<br />
|UINT16LE iX||X-coordinate of actor (in tile units)<br />
|-<br />
|UINT16LE iY||Y-coordinate of actor (in tile units)<br />
|}<br />
<br />
Because the <tt>iActorSize</tt> value in the header is in UINT16s and there are three UINT16s per actor, the number of actors can be obtained quite simply:<br />
<br />
iNumActors = iActorSize / 3<br />
<br />
The <tt>iType</tt> field can conveniently be used as an index into the [[Duke Nukem II Actor Info|ACTRINFO.MNI]] sprite file. If <tt>iType</tt> is zero, the first actor image is used.<br />
<br />
Not all actors have images however, some point to actor sprites with zero frames. It appears to be that some of these "actors" are actually flags, indicating that the next actor (in the level, not in the <tt>ACTRINFO.MNI</tt>) will only appear when the level is played at a certain difficulty level.<br />
<br />
== Tools ==<br />
<br />
{{BeginFileFormatTools|Type=map}}<br />
{{FileFormatTool<br />
| Name = [http://chidesters.org/scottchidester.com/index.html Nukem2Print]<br />
| Platform = Qt<br />
| canView = Yes<br />
| canCreate = No<br />
| canModify = No<br />
| editHidden = N/A<br />
| editMetadata = N/A<br />
}}<br />
{{EndFileFormatTools}}<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [http://www.szevvy.com Szevvy] figured out how to map the cell values back to the tilesets. [[User:Malvineous|Malvineous]] figured out the format of the supplemental foreground data. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=User:Lethal_guitar&diff=9718User:Lethal guitar2021-05-02T07:04:12Z<p>Lethal guitar: </p>
<hr />
<div>Hi, I'm Niko, a software engineer working in the music tech industry. Way back in 2006, I discovered Dave Bollinger's Duke Nukem II specs on the internet. This fascinated me greatly and inspired me to create a level editor for the game, based on the specs and some reverse engineering effort to fill in the gaps. I never publicly shared this level editor (and there are much better options out there nowadays), but I kept doing little game file format reverse engineering projects from time to time.<br />
<br />
Fast forward to 2016, I learned about reverse-engineered game engine recreation projects like ReDuke, Omnispeak and Commander Genius, and others. Again, I was intrigued, and I started my own project: [https://github.com/lethal-guitar/RigelEngine RigelEngine], a recreation of Duke Nukem II's engine. This wiki was a great help in getting all the file format reading code to work (and I've made sure to reference it in my source code wherever applicable :)). As I dug deeper and deeper into the original game's assembly code, I discovered the answers to a few open questions that were still on some of the wiki pages, and other information that's not here yet. Since the wiki was such a great help for my project, I'm now contributing back to it by filling in these missing gaps with what I've learned from working on my project.</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_CZone_Format&diff=9717Duke Nukem II CZone Format2021-05-01T21:50:22Z<p>Lethal guitar: Expanded information on tile attributes</p>
<hr />
<div>{{NeedMoreInfo}}<br />
{{Tileset Infobox<br />
| Hardware1 = EGA<br />
| MaxTiles = Fixed<br />
| Palette = External<br />
| Names = N<br />
| TileMinSize = 8&times;8<br />
| TileMaxSize = 8&times;8<br />
| NumPlanes = 4-5<br />
| PlaneArrangement = [[Raw EGA data#Byte-planar EGA data|Byte-planar]]<br />
| HasTransparency = Y<br />
| HasHitmap = N<br />
| Metadata = None<br />
| Subtilesets = N<br />
| Compressed = N<br />
| Hidden = N<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The CZone format stores three blocks of data - the behaviour of each tile in a tileset, a collection of images for the "solid" background tiles, and a collection of images for the "masked" foreground tiles.<br />
<br />
== File format ==<br />
<br />
{|class="wikitable"<br />
! Data type !! Name !! Description<br />
|-<br />
| BYTE[3600] || attributeData || Attribute data<br />
|-<br />
| BYTE[32000] || solidTileset || Graphics for the solid tileset<br />
|-<br />
| BYTE[6400] || maskedTileset || Graphics for the masked tileset<br />
|}<br />
<br />
=== Attribute Data ===<br />
<br />
The attribute data consists of 1000 [[UINT16LE]] values (settings for the solid tiles), followed by 800 [[UINT16LE]] values (five values for each masked tile). It seems like ''only the first value'' for each masked tile is actually used by the game.<br />
<br />
Each individual attribute value is a bitmask. The meaning of the bits is as follows:<br />
<br />
{|class="wikitable"<br />
! Bit !! Description<br />
|-<br />
| 15 || -<br />
|-<br />
| 14 || climbable (chains/ladders)<br />
|-<br />
| 13 || -<br />
|-<br />
| 12 || -<br />
|-<br />
| 11 || -<br />
|-<br />
| 10 || fast animation<br />
|-<br />
| 9 || conveyor belt -><br />
|-<br />
| 8 || conveyor belt <-<br />
|-<br />
| 7 || can hang on it (pipes, vines etc.)<br />
|-<br />
| 6 || burns away when shot<br />
|-<br />
| 5 || draw in front<br />
|-<br />
| 4 || animate (uses current tile and next 3 tiles)<br />
|-<br />
| 3 || solid/blocking right<br />
|-<br />
| 2 || solid/blocking left<br />
|-<br />
| 1 || solid/blocking up<br />
|-<br />
| 0 || solid/blocking down<br />
|}<br />
<br />
Bits 11, 12, 13, and 15 are not used by the game, and simply ignored if they are set. The tileset "czone9" has bit 15 set on some of its tiles (horns), but it has no effect. Most likely a leftover from an earlier point in the game's development.<br />
<br />
=== Solid Tileset ===<br />
<br />
The solid tileset is comprised of 1000 tiles. Each tile is 8x8 pixels, and for best effect when presenting it to a user/designer it should be displayed in a grid 40 tiles wide by 25 tiles high.<br />
<br />
The tiles are stored in [[Cosmo Tileset Format]]. The first plane is blue, followed by green, red and then intensity.<br />
<br />
Since the tiles are 8x8, each plane's scanline fits into one byte. At eight pixels tall that makes each plane occupy eight bytes, and with four planes the whole tile takes up 32 bytes. This means you can seek to any tile with a simple formula:<br />
<br />
iOffset = iTileIndex * 32<br />
<br />
=== Masked Tileset ===<br />
<br />
The masked tileset is almost identical to the solid tileset, except that there are only 160 tiles (so only four rows high, but still 40 tiles wide) and of course being a masked tileset, there are five planes. The first plane is transparency, followed by blue, green, red and intensity as with the solid tiles.<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/ Dave Bollinger]. Most of this info came from the [http://archive.shikadi.net/sites/www.geocities.com/dooknookimklassik/dn2specs.txt specs on his website]. [[User:Lethal_guitar|lethal guitar]] verified that unknown/unused tile attribute bits are not used by the game. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Misc_Files&diff=9716Duke Nukem II Misc Files2021-05-01T18:33:15Z<p>Lethal guitar: Added more information on temporary savegames</p>
<hr />
<div>{{Playerdata Infobox<br />
| Config = Yes<br />
| Savegame = Yes<br />
| Storing = Sound, Scores, Keys, Game speed<br />
| Where = Start<br />
| Elements = Level, Difficulty, Score, Ammo, Health, Weapon, Hints displayed<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
== NUKEM2.-GT ==<br />
<br />
Stores game settings (controls, sound options etc...)<br />
<br />
0 | UINT16LE | iUpKey (scan code)<br />
2 | UINT16LE | iDownKey (scan code)<br />
4 | UINT16LE | iLeftKey (scan code)<br />
6 | UINT16LE | iRightKey (scan code)<br />
8 | UINT16LE | iJumpKey (scan code)<br />
10 | UINT16LE | iFireKey (scan code)<br />
12 | UINT16LE | iDifficulty (always 1, not used by the game)<br />
14 | UINT16LE | iSound_SoundBlaster (0..1)<br />
16 | UINT16LE | iSound_AdLib (0..1)<br />
18 | UINT16LE | iSound_PCSpeaker (0..1)<br />
20 | UINT16LE | iMusic (0..1)<br />
22 | UINT16LE | iJoystickCalibrated (0..1)<br />
24 | UINT16LE | iJoystickCalibrationData1<br />
26 | UINT16LE | iJoystickCalibrationData2<br />
28 | UINT16LE | iJoystickCalibrationData3<br />
30 | UINT16LE | iJoystickCalibrationData4<br />
32 | UINT16LE | iJoystickFireButtonIndex (0..1)<br />
34 | UINT16LE | iGameSpeed (1..7)<br />
<br />
Key bindings are stored as IBM PC keyboard scan codes.<br />
<br />
<tt>iJoystickCalibrated</tt> is set to one after going through the Joystick calibration process in the menu.<br />
The game will read jostyick input in addition to key presses from then on.<br />
<br />
<tt>iJoystickFireButtonIndex</tt> determines if button 1 or button 2 on the joystick serves as the fire button (with the other button used for jumping).<br />
<br />
The joystick calibration data is needed by the game to correctly interpret joystick motion. This is closely tied to the way joysticks functioned on DOS PCs, see [https://www.epanorama.net/documents/joystick/pc_joystick.html PC analogue joystick interface] for more information.<br />
<br />
== NUKEM2.-NM ==<br />
<br />
Stores names of the saved games. Contains 8 strings with a fixed length of 18 chars each (padded with nulls, but not null-terminated).<br />
<br />
== NUKEM2.-S? ==<br />
<br />
Stores savegames (? ranges from 1 to 8, plus <tt>B</tt>, <tt>T</tt>, and <tt>Z</tt> for temporary saves, more on that below).<br />
<br />
0 | UINT16LE | iWeapon (0..3) - 0=N, 1=L, 2=R, 3=F<br />
2 | UINT16LE | iHealth (always 9, except in temporary respawn beacon savegame)<br />
4 | UINT16LE | iAmmo (1..32, 1..64 for flame thrower)<br />
6 | UINT16LE | iDifficulty (1..3)<br />
8 | UINT16LE | iEpisode (0..3)<br />
10 | UINT16LE | iLevel (0..7)<br />
12 | UINT16LE | iRespawnBeaconActivated (0..1 - always 0 except in temporary respawn beacon savegame)<br />
14 | UINT16LE | iBackdropAddressOffset (always 0 except in temporary respawn beacon savegame)<br />
16 | BYTE[30] | bHintsDisplayed (0..1)<br />
46 | UINT32LE | iScore (ignored if iRespawnBeaconActivated == 1)<br />
<br />
=== Temporary savegame files ===<br />
<br />
The game uses this savegame format for some internal purposes in addition to regular savegames.<br />
<br />
When starting a level, the game creates a <tt>.-ST</tt> file to capture the player's score, ammo etc. at the beginning of the level. When the player dies (before having reached a respawn beacon), the file is used to restore these values.<br />
<br />
When the player reaches a respawn beacon, a <tt>.-SZ</tt> file is created. The file is used to restore weapon and ammo when the player dies and respawns at the beacon. The score value is ignored in that case. If the health value is 1, it is set to 2 instead.<br />
In this case, the <tt>iBackdropAddressOffset</tt> is needed for levels with a backdrop switch, to restore the correct backdrop when respawning.<br />
<br />
<tt>.-SB</tt> files are created as part of creating regular savegames. When you create a savegame via the menu, the score, weapon, ammo etc. values from the beginning of the level are stored, regardless of what the values currently are. The game loads the <tt>.-ST</tt> file in order to restore the values from the beginning of the level, and then creates the requested savegame based on that. Since loading the temp file overwrites the actual current values, the game first creates a <tt>.-SB</tt> file with the current state, and then reloads it after the regular savegame has been written.<br />
<br />
== NUKEM2.-V? ==<br />
<br />
Stores High Scores for each episode (? ranges from 1 to 4).<br />
<br />
Contains 10 entries of the following structure:<br />
<br />
CHAR[15] | cName (NOT null-terminated!)<br />
UINT32LE | iScore<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]], with some additions by [[User:Lethal guitar|lethal guitar]]. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Misc_Files&diff=9714Duke Nukem II Misc Files2021-05-01T17:06:47Z<p>Lethal guitar: Add more information to the savegame file format</p>
<hr />
<div>{{Playerdata Infobox<br />
| Config = Yes<br />
| Savegame = Yes<br />
| Storing = Sound, Scores, Keys, Game speed<br />
| Where = Start<br />
| Elements = Level, Difficulty, Score, Ammo, Health, Weapon, Hints displayed<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
== NUKEM2.-GT ==<br />
<br />
Stores game settings (controls, sound options etc...)<br />
<br />
0 | UINT16LE | iUpKey (scan code)<br />
2 | UINT16LE | iDownKey (scan code)<br />
4 | UINT16LE | iLeftKey (scan code)<br />
6 | UINT16LE | iRightKey (scan code)<br />
8 | UINT16LE | iJumpKey (scan code)<br />
10 | UINT16LE | iFireKey (scan code)<br />
12 | UINT16LE | iDifficulty (always 1, not used by the game)<br />
14 | UINT16LE | iSound_SoundBlaster (0..1)<br />
16 | UINT16LE | iSound_AdLib (0..1)<br />
18 | UINT16LE | iSound_PCSpeaker (0..1)<br />
20 | UINT16LE | iMusic (0..1)<br />
22 | UINT16LE | iJoystickCalibrated (0..1)<br />
24 | UINT16LE | iJoystickCalibrationData1<br />
26 | UINT16LE | iJoystickCalibrationData2<br />
28 | UINT16LE | iJoystickCalibrationData3<br />
30 | UINT16LE | iJoystickCalibrationData4<br />
32 | UINT16LE | iJoystickFireButtonIndex (0..1)<br />
34 | UINT16LE | iGameSpeed (1..7)<br />
<br />
Key bindings are stored as IBM PC keyboard scan codes.<br />
<br />
<tt>iJoystickCalibrated</tt> is set to one after going through the Joystick calibration process in the menu.<br />
The game will read jostyick input in addition to key presses from then on.<br />
<br />
<tt>iJoystickFireButtonIndex</tt> determines if button 1 or button 2 on the joystick serves as the fire button (with the other button used for jumping).<br />
<br />
The joystick calibration data is needed by the game to correctly interpret joystick motion. This is closely tied to the way joysticks functioned on DOS PCs, see [https://www.epanorama.net/documents/joystick/pc_joystick.html PC analogue joystick interface] for more information.<br />
<br />
== NUKEM2.-NM ==<br />
<br />
Stores names of the saved games. Contains 8 strings with a fixed length of 18 chars each (padded with nulls, but not null-terminated).<br />
<br />
== NUKEM2.-S? ==<br />
<br />
Stores savegames (? ranges from 1 to 8, plus "b" and "t" for temporary saves).<br />
<br />
0 | UINT16LE | iWeapon (0..3) - 0=N, 1=L, 2=R, 3=F<br />
2 | UINT16LE | iHealth (always 9, except in temporary respawn beacon savegame)<br />
4 | UINT16LE | iAmmo (1..32, 1..64 for flame thrower)<br />
6 | UINT16LE | iDifficulty (1..3)<br />
8 | UINT16LE | iEpisode (0..3)<br />
10 | UINT16LE | iLevel (0..7)<br />
12 | UINT16LE | iRespawnBeaconActivated (0..1 - always 0 except in temporary respawn beacon savegame)<br />
14 | UINT16LE | iBackdropAddressOffset (always 0 except in temporary respawn beacon savegame)<br />
16 | BYTE[30] | bHintsDisplayed (0..1)<br />
46 | UINT32LE | iScore (ignored if iRespawnBeaconActivated == 1)<br />
<br />
== NUKEM2.-V? ==<br />
<br />
Stores High Scores for each episode (? ranges from 1 to 4).<br />
<br />
Contains 10 entries of the following structure:<br />
<br />
CHAR[15] | cName (NOT null-terminated!)<br />
UINT32LE | iScore<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]], with some additions by [[User:Lethal guitar|lethal guitar]]. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Misc_Files&diff=9713Duke Nukem II Misc Files2021-05-01T16:51:01Z<p>Lethal guitar: Fix incorrect data types from previous edits</p>
<hr />
<div>{{Playerdata Infobox<br />
| Config = Yes<br />
| Savegame = Yes<br />
| Storing = Sound, Scores, Keys, Game speed<br />
| Where = Start<br />
| Elements = Level, Difficulty, Score, Ammo, Health, Weapon, Hints displayed<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
== NUKEM2.-GT ==<br />
<br />
Stores game settings (controls, sound options etc...)<br />
<br />
0 | UINT16LE | iUpKey (scan code)<br />
2 | UINT16LE | iDownKey (scan code)<br />
4 | UINT16LE | iLeftKey (scan code)<br />
6 | UINT16LE | iRightKey (scan code)<br />
8 | UINT16LE | iJumpKey (scan code)<br />
10 | UINT16LE | iFireKey (scan code)<br />
12 | UINT16LE | iDifficulty (always 1, not used by the game)<br />
14 | UINT16LE | iSound_SoundBlaster (0..1)<br />
16 | UINT16LE | iSound_AdLib (0..1)<br />
18 | UINT16LE | iSound_PCSpeaker (0..1)<br />
20 | UINT16LE | iMusic (0..1)<br />
22 | UINT16LE | iJoystickCalibrated (0..1)<br />
24 | UINT16LE | iJoystickCalibrationData1<br />
26 | UINT16LE | iJoystickCalibrationData2<br />
28 | UINT16LE | iJoystickCalibrationData3<br />
30 | UINT16LE | iJoystickCalibrationData4<br />
32 | UINT16LE | iJoystickFireButtonIndex (0..1)<br />
34 | UINT16LE | iGameSpeed (1..7)<br />
<br />
Key bindings are stored as IBM PC keyboard scan codes.<br />
<br />
<tt>iJoystickCalibrated</tt> is set to one after going through the Joystick calibration process in the menu.<br />
The game will read jostyick input in addition to key presses from then on.<br />
<br />
<tt>iJoystickFireButtonIndex</tt> determines if button 1 or button 2 on the joystick serves as the fire button (with the other button used for jumping).<br />
<br />
The joystick calibration data is needed by the game to correctly interpret joystick motion. This is closely tied to the way joysticks functioned on DOS PCs, see [https://www.epanorama.net/documents/joystick/pc_joystick.html PC analogue joystick interface] for more information.<br />
<br />
== NUKEM2.-NM ==<br />
<br />
Stores names of the saved games. Contains 8 strings with a fixed length of 18 chars each (padded with nulls, but not null-terminated).<br />
<br />
== NUKEM2.-S? ==<br />
<br />
Stores savegames (? ranges from 1 to 8, plus "b" and "t" for temporary saves).<br />
<br />
0 | UINT16LE | iWeapon (0..3) - 0=N, 1=L, 2=R, 3=F<br />
2 | UINT16LE | iHealth (always 9, except for temporary respawn beacon savegame)<br />
4 | UINT16LE | iAmmo<br />
6 | UINT16LE | iDifficulty (1..3)<br />
8 | UINT16LE | iEpisode (0..3)<br />
10 | UINT16LE | iLevel (0..7)<br />
12 | BYTE[34] | bHintsDisplayed (0..1)<br />
46 | UINT32LE | iScore<br />
<br />
== NUKEM2.-V? ==<br />
<br />
Stores High Scores for each episode (? ranges from 1 to 4).<br />
<br />
Contains 10 entries of the following structure:<br />
<br />
CHAR[15] | cName (NOT null-terminated!)<br />
UINT32LE | iScore<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]], with some additions by [[User:Lethal guitar|lethal guitar]]. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Misc_Files&diff=9712Duke Nukem II Misc Files2021-05-01T13:35:18Z<p>Lethal guitar: Improve formatting, add a link for more info on joysticks</p>
<hr />
<div>{{Playerdata Infobox<br />
| Config = Yes<br />
| Savegame = Yes<br />
| Storing = Sound, Scores, Keys, Game speed<br />
| Where = Start<br />
| Elements = Level, Difficulty, Score, Ammo, Health, Weapon, Hints displayed<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
== NUKEM2.-GT ==<br />
<br />
Stores game settings (controls, sound options etc...)<br />
<br />
0 | UINT16LE | iUpKey (scan code)<br />
2 | UINT16LE | iDownKey (scan code)<br />
4 | UINT16LE | iLeftKey (scan code)<br />
6 | UINT16LE | iRightKey (scan code)<br />
8 | UINT16LE | iJumpKey (scan code)<br />
10 | UINT16LE | iFireKey (scan code)<br />
12 | UINT16LE | iDifficulty (always 1, not used by the game)<br />
14 | UINT16LE | iSound_SoundBlaster (0..1)<br />
16 | UINT16LE | iSound_AdLib (0..1)<br />
18 | UINT16LE | iSound_PCSpeaker (0..1)<br />
20 | UINT16LE | iMusic (0..1)<br />
22 | UINT8LE | iJoystickCalibrated (0..1)<br />
24 | UINT16LE | iJoystickCalibrationData1<br />
26 | UINT16LE | iJoystickCalibrationData2<br />
28 | UINT16LE | iJoystickCalibrationData3<br />
30 | UINT16LE | iJoystickCalibrationData4<br />
32 | UINT8LE | iJoystickFireButtonIndex (0..1)<br />
34 | UINT16LE | iGameSpeed (1..7)<br />
<br />
Key bindings are stored as IBM PC keyboard scan codes.<br />
<br />
<tt>iJoystickCalibrated</tt> is set to one after going through the Joystick calibration process in the menu.<br />
The game will read jostyick input in addition to key presses from then on.<br />
<br />
<tt>iJoystickFireButtonIndex</tt> determines if button 1 or button 2 on the joystick serves as the fire button (with the other button used for jumping).<br />
<br />
The joystick calibration data is needed by the game to correctly interpret joystick motion. This is closely tied to the way joysticks functioned on DOS PCs, see [https://www.epanorama.net/documents/joystick/pc_joystick.html PC analogue joystick interface] for more information.<br />
<br />
== NUKEM2.-NM ==<br />
<br />
Stores names of the saved games. Contains 8 strings with a fixed length of 18 chars each (padded with nulls, but not null-terminated).<br />
<br />
== NUKEM2.-S? ==<br />
<br />
Stores savegames (? ranges from 1 to 8, plus "b" and "t" for temporary saves).<br />
<br />
0 | UINT16LE | iWeapon (0..3) - 0=N, 1=L, 2=R, 3=F<br />
2 | UINT16LE | iHealth (always 9, except for temporary respawn beacon savegame)<br />
4 | UINT16LE | iAmmo<br />
6 | UINT16LE | iDifficulty (1..3)<br />
8 | UINT16LE | iEpisode (0..3)<br />
10 | UINT16LE | iLevel (0..7)<br />
12 | BYTE[34] | bHintsDisplayed (0..1)<br />
46 | UINT32LE | iScore<br />
<br />
== NUKEM2.-V? ==<br />
<br />
Stores High Scores for each episode (? ranges from 1 to 4).<br />
<br />
Contains 10 entries of the following structure:<br />
<br />
CHAR[15] | cName (NOT null-terminated!)<br />
UINT32LE | iScore<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]], with some additions by [[User:Lethal guitar|lethal guitar]]. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=User:Lethal_guitar&diff=9711User:Lethal guitar2021-05-01T13:25:45Z<p>Lethal guitar: </p>
<hr />
<div>I'm a software engineer working in the music tech industry. Way back in 2006, I discovered Dave Bollinger's Duke Nukem II specs on the internet. This fascinated me greatly and inspired me to create a level editor for the game, based on the specs and some reverse engineering effort to fill in the gaps. I never publicly shared this level editor (and there are much better options out there nowadays), but I kept doing little game file format reverse engineering projects from time to time.<br />
<br />
Fast forward to 2016, I learned about reverse-engineered game engine recreation projects like ReDuke, Omnispeak and Commander Genius, and others. Again, I was intrigued, and I started my own project: [https://github.com/lethal-guitar/RigelEngine RigelEngine], a recreation of Duke Nukem II's engine. This wiki was a great help in getting all the file format reading code to work (and I've made sure to reference it in my source code wherever applicable :)). As I dug deeper and deeper into the original game's assembly code, I discovered the answers to a few open questions that were still on some of the wiki pages, and other information that's not here yet. Since the wiki was such a great help for my project, I'm now contributing back to it by filling in these missing gaps with what I've learned from working on my project.</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Main_Page&diff=9710Main Page2021-05-01T13:24:26Z<p>Lethal guitar: /* Current projects */</p>
<hr />
<div>__NOTOC__ <!-- Hide the 'table of contents' box --><br />
<big>'''Welcome to the DOS Game Modding Wiki!'''</big><br />
<br />
The goal of this wiki is to assist people wishing to modify DOS games (typically those released for the PC in the early 1990s) to create entirely new games. The wiki attempts to document all the file formats used by each game to assist programmers writing editing tools, as well as listing any existing tools that can already be used to modify the game.<br />
<br />
<table border="0" width="100%" cellspacing="10"><br />
<tr><td align="center"><br />
[[Image:icon-games.png|link=:Category:Game Intro Page]]<br/><br />
[[:Category:Game Intro Page|Games]]<br />
</td><td align="center"><br />
[[Image:icon-mods.png|link=:Category:Mods by game]]<br/><br />
[[:Category:Mods by game|Mods]]<br />
</td><td align="center"><br />
[[Image:icon-settings.png|link=:Category:File Formats]]<br/><br />
[[:Category:File Formats|File formats]]<br />
</td><td align="center"><br />
[[Image:icon-info.png|link=:Category:Tutorials]]<br/><br />
[[:Category:Tutorials|Tutorials]]<br />
</td></tr><br />
</table><br />
<br />
=== About ===<br />
<br />
The wiki is structured so that each game has its own summary page, which in turn branches out to other pages explaining things in more detail such as file formats and instructions for using modding tools.<br />
<br />
If you find anything missing, incomplete or inaccurate among these pages, please fix it! To help prevent spam, you'll need to [[Special:Userlogin|log in]] before you can edit pages - if you don't have an account, it's easy to [[Special:Userlogin|create one]]. Have a quick look at the [[ModdingWiki:Contributing|editing guidelines]] before your first edit so you know what we're expecting.<br />
<br />
Don't forget that this site will only become a useful reference if all the modders out there lend a hand and contribute what they know! If you've got some info about modding a DOS game that's not yet listed, please create a new page for it!<br />
<br />
=== Help ===<br />
<br />
If you are looking for help with a mod you are working on, or if you need assistance reverse-engineering a game, please drop by the [http://www.classicdosgames.com/forum/viewforum.php?f=25 Modding section of the RGB Classic Games forum] and we'll try to help.<br />
<br />
=== Current projects ===<br />
<br />
* [[User:T-Squared|T-Squared]] is creating a mod for [[Cosmo's Cosmic Adventures]] called "HUMANIZED!!!". Take a look at [[User:T-Squared|T-Squared's user page]] for more info.<br />
* [[User:Eros|Eros]] is remaking the original [[Catacomb]] for Windows and Linux in OpenGL. Visit the [http://code.google.com/p/cataclone/ Google Code project page].<br />
* [[User:Malvineous|Malvineous]] is working on a cross-platform modding tool called [[Camoto]], which can edit a number of different games.<br />
* [[User:Nyerguds|Nyerguds]] is constantly expanding his [[Engie File Converter]] and [[Westwood Font Editor]], adding graphics formats from increasingly more non-[[:category:Westwood Studios|Westwood]] games to them, and upgrading his [[Librarian]] tool to put the edited results back into the game archives they came from.<br />
* [[User:TheAlmightyGuru|TheAlmightyGuru]] is working on a viewer for [[BGI Stroked Font]].<br />
* Scott Smitelli has reverse-engineered the original .EXE binaries of [[Cosmo's Cosmic Adventures]] to produce a [https://github.com/smitelli/cosmore 96%-accurate reconstruction]<br />
* [[User:Lethal_guitar|lethal guitar]] is working on an open-source reimplementation of [[Duke Nukem II]], called [https://github.com/lethal-guitar/RigelEngine RigelEngine].</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Misc_Files&diff=9709Duke Nukem II Misc Files2021-05-01T13:20:45Z<p>Lethal guitar: Add more information to the options file format.</p>
<hr />
<div>{{Playerdata Infobox<br />
| Config = Yes<br />
| Savegame = Yes<br />
| Storing = Sound, Scores, Keys, Game speed<br />
| Where = Start<br />
| Elements = Level, Difficulty, Score, Ammo, Health, Weapon, Hints displayed<br />
| Games =<br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
== NUKEM2.-GT ==<br />
<br />
Stores game settings (controls, sound options etc...)<br />
<br />
0 | UINT16LE | iUpKey (scan code)<br />
2 | UINT16LE | iDownKey (scan code)<br />
4 | UINT16LE | iLeftKey (scan code)<br />
6 | UINT16LE | iRightKey (scan code)<br />
8 | UINT16LE | iJumpKey (scan code)<br />
10 | UINT16LE | iFireKey (scan code)<br />
12 | UINT16LE | iDifficulty (always 1, not used by the game)<br />
14 | UINT16LE | iSound_SoundBlaster (0..1)<br />
16 | UINT16LE | iSound_AdLib (0..1)<br />
18 | UINT16LE | iSound_PCSpeaker (0..1)<br />
20 | UINT16LE | iMusic (0..1)<br />
22 | UINT8LE | iJoystickCalibrated (0..1)<br />
24 | UINT16LE | iJoystickCalibrationData1<br />
26 | UINT16LE | iJoystickCalibrationData2<br />
28 | UINT16LE | iJoystickCalibrationData3<br />
30 | UINT16LE | iJoystickCalibrationData4<br />
32 | UINT8LE | iJoystickFireButtonIndex (0..1)<br />
34 | UINT16LE | iGameSpeed (1..7)<br />
<br />
Key bindings are stored as IBM PC keyboard scan codes.<br />
iJoystickCalibrated is set to one after going through the Joystick calibration process in the menu.<br />
The game will read jostyick input in addition to key presses from then on.<br />
The joystick button index determines if button 1 or button 2 on the joystick serves as the fire button (with the other button used for jumping).<br />
Joystick calibration data is needed by the game to correctly interpret joystick motion.<br />
<br />
== NUKEM2.-NM ==<br />
<br />
Stores names of the saved games. Contains 8 strings with a fixed length of 18 chars each (padded with nulls, but not null-terminated).<br />
<br />
== NUKEM2.-S? ==<br />
<br />
Stores savegames (? ranges from 1 to 8, plus "b" and "t" for temporary saves).<br />
<br />
0 | UINT16LE | iWeapon (0..3) - 0=N, 1=L, 2=R, 3=F<br />
2 | UINT16LE | iHealth (always 9, except for temporary respawn beacon savegame)<br />
4 | UINT16LE | iAmmo<br />
6 | UINT16LE | iDifficulty (1..3)<br />
8 | UINT16LE | iEpisode (0..3)<br />
10 | UINT16LE | iLevel (0..7)<br />
12 | BYTE[34] | bHintsDisplayed (0..1)<br />
46 | UINT32LE | iScore<br />
<br />
== NUKEM2.-V? ==<br />
<br />
Stores High Scores for each episode (? ranges from 1 to 4).<br />
<br />
Contains 10 entries of the following structure:<br />
<br />
CHAR[15] | cName (NOT null-terminated!)<br />
UINT32LE | iScore<br />
<br />
== Credits ==<br />
<br />
This file format was reverse engineered by [[User:K1n9_Duk3|K1n9_Duk3]], with some additions by [[User:Lethal guitar|lethal guitar]]. 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!)</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Actor_Info&diff=9706Duke Nukem II Actor Info2021-05-01T09:04:42Z<p>Lethal guitar: Move text into notes section, and add sub-headings</p>
<hr />
<div>{{Tileset Infobox<br />
| Hardware1 = EGA<br />
| MaxTiles = 65,536<br />
| Palette = External<br />
| Names = N<br />
| TileMinSize = 0&times;0<br />
| TileMaxSize = 524,280&times;524,280<br />
| NumPlanes = 5<br />
| PlaneArrangement = Byte Planar<br />
| HasTransparency = Y<br />
| HasHitmap = N<br />
| Metadata = None<br />
| Subtilesets = N<br />
| Compressed = N<br />
| Hidden = Y<br />
| Games = <br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The tile arrangement for the [[Duke Nukem II]] actors (game characters) is stored in <tt>ACTRINFO.MNI</tt> in the main <tt>NUKEM2.CMP</tt> group file. This file describes how to arrange the bare 8&times;8 tiles found in the corresponding tileset, so that they form a frame belonging to an actor animation.<br />
<br />
== File format ==<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iNumActors||Number of actors described in the file<br />
|-<br />
|UINT16LE iOffset[iNumActors]||Array of UINT16 offsets to the actor info structure (double these values to turn them into byte offsets)<br />
|}<br />
<br />
Following this structure the first actor data should begin. The actor data runs end-to-end, and <tt>iOffset</tt> should only be required when jumping to a particular actor's offset.<br />
<br />
Note that <tt>iNumActors</tt> is technically the UINT16-offset of the first structure, however if it is treated as such it becomes difficult to find the number of actors described by the file.<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iNumFrames||Number of frames this actor has<br />
|-<br />
|INT16LE iDrawIndex||Defines when the actor will be drawn to the screen (ignored for actors spawned during gameplay)<br />
|-<br />
|ACTOR_FRAME frameInfo[iNumFrames]||Structure holding information about the frame<br />
|}<br />
<br />
The iDrawIndex value ranges from -1 to 3. Actors with a lower index will be updated and drawn first, the actors with highest index last. Any value smaller than -1 or greater than 3 is invalid and will prevent the actor from being spawned in a level. Actors with an identical draw index will be drawn in the order that they appear in the level file.<br />
<br />
<tt>ACTOR_FRAME</tt> is defined as:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|INT16LE iHotspotX||X-coord of hotspot (in tiles, can be negative)<br />
|-<br />
|INT16LE iHotspotY||Y-coord of hotspot (in tiles, can be negative)<br />
|-<br />
|UINT16LE iHeight||Frame height (in tiles)<br />
|-<br />
|UINT16LE iWidth||Frame width (in tiles)<br />
|-<br />
|UINT32LE iDataOffset||Offset into <tt>ACTORS.MNI</tt> where the tile data starts<br />
|-<br />
|UINT16LE iPadding1||never used<br />
|-<br />
|UINT16LE iPadding2||never used<br />
|}<br />
<br />
The hotspot is the location in the image which should appear at the offset given in the map layer. In other words, take the actor location, subtract iHotspotX number of tiles from the X-coordinate, subtract iHotspotY number of tiles from the Y-coordinate, and the actor will appear at the correct location.<br />
<br />
The two padding values at the end are inserted to allow the game to calculate the start of the <tt>ACTOR_FRAME</tt> data for each frame with the same formula:<br />
<br />
<tt>start = (iOffset[actor] + 8 * frame + 2) * 2</tt><br />
<br />
This means the padding values are effectively dummies for the <tt>iNumFrames</tt> and <tt>iDrawIndex</tt> values. Even though these padding values could theoretically be omitted for the last frame of each actor entry, they are always present in the original files.<br />
<br />
== Notes ==<br />
<br />
=== Draw index ===<br />
<br />
The draw index is only taken into account during level loading. For actors spawned during gameplay, the draw order instead depends on the state of the game's object list. The game uses a fixed-size array to represent actor state at run-time. When an actor is destroyed, its corresponding entry in the list is marked as "free". When a new actor is created, the game uses the first free entry to store the new actor's state. The effect of this is that a newly spawned actor may appear at the beginning of the list or at the end, depending on how many actors have been destroyed so far. And the list position determines the draw order, so the newly spawned actor may appear behind or in front of other actors.<br />
<br />
During level loading, the game does multiple passes over the actor list from the level file, only creating the actors matching the current pass' draw index on each pass.<br />
<br />
=== Unused data ===<br />
<br />
<tt>ACTORS.MNI</tt> contains some bytes that are not used by any actor frame. These bytes are found at the end of every 64KB block (exactly 65536 bytes) of data. In the original file, no sprite frame is split across these 64KB blocks. The image data is written to the end of the block to fill it up, then the new block is started, containing the entire image data of the sprite's frame.<br />
<br />
However, [[Duke Nukem II]] seems to be able to handle the image data correctly, even if it is split across two of these blocks. This might have just been a limitation in the original tool that was used to create the <tt>ACTORS.MNI</tt> file.<br />
<br />
=== Font data format ===<br />
<br />
The data for sprite number 29 is in a slightly different format. Instead of 4 colour planes and a mask plane, the image data for the frames of this sprite only consist of two planes. The frames of this sprite store the big 8*16 pixel font used in the menus. Furthermore, the game is hard-coded to draw this font at 8*16 pixles, i.e. 1 tile wide and 2 tiles high.</div>Lethal guitarhttps://moddingwiki.shikadi.net/w/index.php?title=Duke_Nukem_II_Actor_Info&diff=9705Duke Nukem II Actor Info2021-05-01T08:30:35Z<p>Lethal guitar: Added more information on how the game handles the actor draw index, corrected a typo</p>
<hr />
<div>{{Tileset Infobox<br />
| Hardware1 = EGA<br />
| MaxTiles = 65,536<br />
| Palette = External<br />
| Names = N<br />
| TileMinSize = 0&times;0<br />
| TileMaxSize = 524,280&times;524,280<br />
| NumPlanes = 5<br />
| PlaneArrangement = Byte Planar<br />
| HasTransparency = Y<br />
| HasHitmap = N<br />
| Metadata = None<br />
| Subtilesets = N<br />
| Compressed = N<br />
| Hidden = Y<br />
| Games = <br />
{{Game|Duke Nukem II}}<br />
}}<br />
<br />
The tile arrangement for the [[Duke Nukem II]] actors (game characters) is stored in <tt>ACTRINFO.MNI</tt> in the main <tt>NUKEM2.CMP</tt> group file. This file describes how to arrange the bare 8&times;8 tiles found in the corresponding tileset, so that they form a frame belonging to an actor animation.<br />
<br />
== File format ==<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iNumActors||Number of actors described in the file<br />
|-<br />
|UINT16LE iOffset[iNumActors]||Array of UINT16 offsets to the actor info structure (double these values to turn them into byte offsets)<br />
|}<br />
<br />
Following this structure the first actor data should begin. The actor data runs end-to-end, and <tt>iOffset</tt> should only be required when jumping to a particular actor's offset.<br />
<br />
Note that <tt>iNumActors</tt> is technically the UINT16-offset of the first structure, however if it is treated as such it becomes difficult to find the number of actors described by the file.<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|UINT16LE iNumFrames||Number of frames this actor has<br />
|-<br />
|INT16LE iDrawIndex||Defines when the actor will be drawn to the screen (ignored for actors spawned during gameplay)<br />
|-<br />
|ACTOR_FRAME frameInfo[iNumFrames]||Structure holding information about the frame<br />
|}<br />
<br />
The iDrawIndex value ranges from -1 to 3. Actors with a lower index will be updated and drawn first, the actors with highest index last. Any value smaller than -1 or greater than 3 is invalid and will prevent the actor from being spawned in a level. Actors with an identical draw index will be drawn in the order that they appear in the level file.<br />
<br />
Note that the draw index is only taken into account during level loading. For actors spawned during gameplay, the draw order depends on the state of the game's object list. The game uses a fixed-size array to represent actor state at run-time, and when an actor is destroyed, its corresponding entry in the list is marked as "free". When a new actor is created, the game uses the first free entry to store the actor's state. The effect of this is that a newly spawned actor may appear at the beginning of the list or at the end, depending on how many actors have been destroyed so far. And the list position determines the draw order.<br />
<br />
During level loading, the game does multiple passes over the actor list from the level file, only creating the actors matching the current pass' draw index on each pass.<br />
<br />
<tt>ACTOR_FRAME</tt> is defined as:<br />
<br />
{|class="wikitable"<br />
!Data type!!Description<br />
|-<br />
|INT16LE iHotspotX||X-coord of hotspot (in tiles, can be negative)<br />
|-<br />
|INT16LE iHotspotY||Y-coord of hotspot (in tiles, can be negative)<br />
|-<br />
|UINT16LE iHeight||Frame height (in tiles)<br />
|-<br />
|UINT16LE iWidth||Frame width (in tiles)<br />
|-<br />
|UINT32LE iDataOffset||Offset into <tt>ACTORS.MNI</tt> where the tile data starts<br />
|-<br />
|UINT16LE iPadding1||never used<br />
|-<br />
|UINT16LE iPadding2||never used<br />
|}<br />
<br />
The hotspot is the location in the image which should appear at the offset given in the map layer. In other words, take the actor location, subtract iHotspotX number of tiles from the X-coordinate, subtract iHotspotY number of tiles from the Y-coordinate, and the actor will appear at the correct location.<br />
<br />
The two padding values at the end are inserted to allow the game to calculate the start of the <tt>ACTOR_FRAME</tt> data for each frame with the same formula:<br />
<br />
<tt>start = (iOffset[actor] + 8 * frame + 2) * 2</tt><br />
<br />
This means the padding values are effectively dummies for the <tt>iNumFrames</tt> and <tt>iDrawIndex</tt> values. Even though these padding values could theoretically be omitted for the last frame of each actor entry, they are always present in the original files.<br />
<br />
== Notes ==<br />
<br />
<tt>ACTORS.MNI</tt> contains some bytes that are not used by any actor frame. These bytes are found at the end of every 64KB block (exactly 65536 bytes) of data. In the original file, no sprite frame is split across these 64KB blocks. The image data is written to the end of the block to fill it up, then the new block is started, containing the entire image data of the sprite's frame.<br />
<br />
However, [[Duke Nukem II]] seems to be able to handle the image data correctly, even if it is split across two of these blocks. This might have just been a limitation in the original tool that was used to create the <tt>ACTORS.MNI</tt> file.<br />
<br />
The data for sprite number 29 is in a slightly different format. Instead of 4 colour planes and a mask plane, the image data for the frames of this sprite only consist of two planes. The frames of this sprite store the big 8*16 pixel font used in the menus. Furthermore, the game is hard-coded to draw this font at 8*16 pixles, i.e. 1 tile wide and 2 tiles high.</div>Lethal guitar