LEV File Format

This is the format used by levels in Jazz Jackrabbit 2 versions 1.00g and 1.00h. It is not a direct analogue of j2l files in 1.10 and higher, since it also contains the tileset data found in j2t files. The files are also not compressed, resulting in much larger filesizes.

Section Hierarchy

.LEV files contain a number of nestable sections headed by FourCCs. Each section begins with a long encoding the length of that section minus the FourCC and the long. Thus the DDCF section, which contains all the other sections, has a length long that is the length of the file minus eight.

  • DDCF
    • EDIT
    • EDI2
    • LINF
    • HSTR
    • TILE
      • INFO
      • DATA
      • EMSK
      • MASK
      • ANIM
      • FLIP
    • LAYR
      • INFO
      • DATA
      • EVNT
    • TMAP
    • CMAP

EDIT

This section is mostly useless. Here and throughout this article, a “string” is a single length byte, followed by that number of character bytes. For instance, the characters “foo” would be preceded by a byte with value three, and the whole thing would be called a string.
struct EDIT
{
    long SectionLength;
    byte MLLEFocusedLayer; // the current layer at time of saving
    string TilesetImageFilename; // the original .anm file
    long NumberOfTiles;
    byte unknown = 0;
    string LayerName[8];
    byte Padding[]; // pad the section length out to 196
}

EDI2

This section has exactly the same contents in every .LEV file. Presumably it was used by MLLE for some sort of storage during editing or saving.
struct EDI2
{
    long SectionLength = 4108;
    long One = 1;
    long TenTwentyFour = 1024;
    byte Zeroes[4100]; // all zeroes
}

LINF

Level Information.
struct LINF
{
    long SectionLength;
    short ChunkVersion = 258; // JJ2 gives error otherwise
    string LevelName;
    string BonusLevel;
    string MusicFile;
    string NextLevel; // minus the file extension
    string SecretLevel;
    long MinLight; // multiply by 1.5625 to get Set Light-compatible value
    long StartLight; // same
    long unknown3[8];
    byte Padding[]; // pad the section length out to a multiple of four
}

HSTR

Help Strings.
struct HSTR
{
    long SectionLength;
    short MaxLength = 256; // maybe?
    string HelpString[16];
    byte Padding[]; // pad the section length out to a multiple of four
}

TILE

Tileset-specific sections.
struct TILE
{
    long SectionLength;
    long ChunkVersion = 263; // JJ2 gives error otherwise
    section INFO;
    section DATA;
    section EMSK;
    section MASK;
    section ANIM;
    section FLIP;
}

INFO

The “tile types,” essentially whether each tile is Translucent or not. Caption is also possible (05), but since 1.00 doesn’t run in windowed mode, it just turns the tiles invisible. Values 01, 02, and 03 all work for Translucent; all other values seem to give access violations.
struct INFO
{
    long SectionLength;
    long NumberOfTiles;
    byte TileType[NumberOfTiles-1]; // starts at tile 1, not 0
    byte Padding[]; // pad the section length out to a multiple of four
}

DATA

The image data itself.
struct DATA
{
    long SectionLength;
    Tile TileType[NumberOfTiles-1]; //tile 0 is always blank
                                    //and therefore is not recorded
}
struct Tile
{
    byte	TrasparencyFlags; // bit4: Part0 is nontransparent
                                  // bit5: Part1 is nontransparent
                                  // bit6: Part2 is nontransparent
                                  // bit7: Part3 is nontransparent
    Part	TilePart[4];
}
struct Part (nontransparent)
{
    byte PaletteColor[16][16];
}
struct Part (transparent)
{
    short Size;  //Length of following data
    short Width = 16;
    short Height = 16;
    byte Data[Size-4];  //RLE encoded data
}
the Data starts with an infobyte of the structure: F00COUNT where the F bit indicates transparency (0 if transparent). the COUNT bits is the amout of pixels with the F attribute. If not transparent then the following COUNT bytes are nontransparent pixel data then comes another infobyte. If transparent, the pixels are not stored of course, and the next byte is another infobyte. If the COUNT bits = 0 (and F=1) then the row is finished and the rest of the line should be filled with transparent pixels.

EMSK

An additional encoding of the transparency mask of the tileset. Pretty similar in style to the clipping masks in JJ1: each tile is broken down into sixty-four 4×4 sections, and each bit specifies the transparency of that section: 1 if any of the pixels in it are transparent, 0 if the whole section is opaque.

struct EMSK
{
    long SectionLength;
    byte FourXFour[NumberOfTiles][8];
}

MASK

The clipping masks for the various tiles used in the level. However, not every tile has a mask, and trying to touch a tile without a mask assigned crashes JJ2.

Following the standard length long is a series of every masked tile in the level. A tile begins with a short giving its number (zero-indexed). If the sixteenth bit of this short is 0, then the next 128 bytes are the clipping mask of that tile, one bit per pixel, four bytes per row. If the sixteenth bit is 1, then the next two bytes are a short giving the number of another tile with the same mask. So 02 80 01 00 means that the third tile (02 00) has the same mask as the second tile (01 00).

At the end of the section is an 0xFFFF short as an instruction to stop reading, since there is no explicit statement of how many masked tiles there are.

ANIM

The animated tiles.
struct ANIM
{
    long SectionLength;
    long NumberOfAnimations;
    Animated_Tile Animation[NumberOfAnimations];
}
struct Animated_Tile
{
    short FrameWait;
    short RandomWait;
    short PingPongWait;
    bool PingPong;
    char Speed;
    char FrameCount;
    short Frame[FrameCount]
}

FLIP

A list of every tile that appears flipped somewhere in the level, excluding tile 0 even if otherwise applicable.
struct FLIP
{
    long SectionLength;
    short FlippedTile[SectionLength/2-2];
    short SectionTerminator = 0xFF;
}

LAYR

The layout-specific sections,
struct LAYR
{
    long SectionLength;
    long ChunkVersion = 263; // JJ2 gives error otherwise
    section INFO;
    section DATA;
    section EVNT;
}

INFO

Layer properties. Unlike in .j2l files, properties are sorted layer by layer, rather than property by property.
struct INFO
{
    long SectionLength;
    LayerInfo LayerProperties[8];
}
struct LayerInfo
{
    long LayerFlags; // bit0: Tile Width
                     // bit1: Tile Height
                     // bit2: Layer has tiles
                     // bit3: Limit Visible Region
                     // bit4: Texture mode
    short LayerWidth;
    short LayerHeight;
    short ZAxis; // nothing happens when you change these
    byte Type; // No known effect. Name from Michiel.
    long SpeedSettings; // bit0: include unknown byte
                        // bit1: include X/Y Speeds
                        // bit2: include Auto X/Y Speeds
    byte DetailLevel; //if SpeedSettings bit0.
                      //Only appears in battle1 and battle3, for
                      //layer 5 only, where it is 02 both times.
                      //No known effect. Name from Michiel.
    long LayerXSpeed; //if SpeedSettings bit1
    long LayerYSpeed; //if SpeedSettings bit1
    long LayerAutoXSpeed; //if SpeedSettings bit2
    long LayerAutoYSpeed; //if SpeedSettings bit2
}

DATA

Layout data for each layer with tiles. Words are sixteen tiles wide, instead of four like in .j2l files.
struct DATA
{
    long SectionLength;
    short AnimOffset = 1024 - NumberOfAnimations;
    short NumberOfWords;
    short unknown = 0; // always zero?
    short Word[NumberOfWords][16];
    short LayerLayout[8][Ceiling(LayerWidth/16)*LayerHeight];
}

EVNT

The event map. Just the same as in .j2l files: the first eight bits of each long are the event ID, followed by two bits for Difficulty, one bit for Illuminate Surroundings, one bit that’s always zero, and then twenty bits of parameters.
struct EVNT
{
    long SectionLength;
    long Event[Layer4Width*Layer4Height];
}

TMAP

struct TMAP (without a textured background)
{
    long SectionLength;
    short ChunkVersion = 257; // JJ2 gives error otherwise
    byte Zeroes6; //all zeroes
}

struct TMAP (with a textured background)
{
    long SectionLength;
    short ChunkVersion = 257; // JJ2 gives error otherwise
    long One = 1;
    long Seven = 7;
    long Zero = 0;
    byte TexturedBackground65536; //every pixel in layer 8
}

CMAP

The palette, or “color map.” Pretty straightforward; if you’re used to .j2t palettes, note that there’s no alpha value here.
struct CMAP
{
    long SectionLength;
    long Colorcount = 256; // seems always to be 256?
    byte unknown = 1;  // always one?
    byte Color[256][3]; // channels are in the order R,G,B
    byte Padding[]; // pad the section length out to a multiple of four
}