This is a dump of Sage of Mirrors and I's experimental work in reading DZB Files. Putting it here until I get time to re-write it properly. Some information is incorrect/oudated. Sorry!

Fun Fact: DZB probably stands for "Zelda Butsukaru Data", where Butsukaru is Romaji for Bump Into/Collide With.

    Version 2, December 2004

    Copyright (C) 2004 Sam Hocevar <>

    Everyone is permitted to copy and distribute verbatim or modified
    copies of this license document, and changing it is allowed as long
    as the name is changed.




#include "datatypes.h"
#include "geometry.h"
#include <intrin.h>

// Length:  2 bytes
// Description:
//            Discovered thanks to Sage of Mirror causing Wind Waker to throw
//            an exception while he was modifying *.dzb files. Credit goes to
//            Devbug for the final piece of the puzzle.
//            SpatialList_t::Index is an index into the Face_t array. It it
//            goes through a form of compression, resulting in a pseudo triangle
//            strip. You look at the SpatialList_t array as pairs and take
//            the delta between the next index and the current. This gives
//            you the number of Face_t triangles to draw starting at
//            the current index and drawing up that many. Then you go onto
//            the next pair.
// Purpose: 
//            Until custom collision files are working in game there is no
//            definite purpose for this. It appears to be that each pair
//            of indexes specifies a small group of triangles which never
//            repeat and are grouped together physically.
struct SpatialList_t
    U16    Index;

    inline void Byteswap()
        Index = _byteswap_ushort(Index);

// Purpose: Unknown struct. Length: 14 bytes
//            Appears to be related to the SpatialList_t
struct FaceGroupData_t
    U16        Type; //Either 01 01 or 01 00 - "Data" or "Guides"
    U16        Unknown2; //Appears to be a "group" identifier for both 01 01 and 01 00 groups. Marking them as zero doesn't seem to crash the game! //On "Data" it's used to group FaceGroupData_t's together. Can be completely 00'd out and doesn't seem to break. Sweet!
    U16        SpatialListIndex; //For a 01 01 entry, this is the index into the Spatial List of which triangle it is.
    U16        Unknown4;
    U16        Unknown5;
    U16        Unknown6;
    U16        Unknown7;
    U16        Unknown8;
    U16        Unknown9;
    U16        Unknown10;

    inline void Byteswap()
        Unknown1 = _byteswap_ushort(Unknown1);
        Unknown2 = _byteswap_ushort(Unknown2);
        Unknown3 = _byteswap_ushort(Unknown3);
        Unknown4 = _byteswap_ushort(Unknown4);
        Unknown5 = _byteswap_ushort(Unknown5);
        Unknown6 = _byteswap_ushort(Unknown6);
        Unknown7 = _byteswap_ushort(Unknown7);
        Unknown8 = _byteswap_ushort(Unknown8);
        Unknown9 = _byteswap_ushort(Unknown9);
        Unknown10 = _byteswap_ushort(Unknown10);

// Length:    16 bytes
// Description:
//            Discovered by Sage of Mirrors. The following byte patterns are known:
//            07 FF xx FF 00 yy yy FF FF FF FF 00 00 00 00 00
//            Where "xx" defines the walking sound effect (water, sand, etc.)
//            and item interaction (sword collision, arrows, etc.)
//            yy yy defines collision type. Known values:
//            03 10 Results in Link sliding down a slope, but still standing.
// Purpose:
//            Defines the surface property of each triangle. The way these
//            are indexed is not entirely known at this time.
struct SurfaceProperty_t
    U16        Unknown1;
    byte    hitEffect; //See "xx" above
    byte    Unknown2;
    U16        collisionType; //See "yy yy" above
    U16        Unknown3; //FF FF
    U16        Unknown4; //FF FF
    U16        Unknown5; //Sometimes Different
    U16        Unknown6;
    U16        Unknown7;

    inline void ConvertToLE()
        Unknown1 = _byteswap_ushort(Unknown1);
        collisionType = _byteswap_ushort(collisionType);

        Unknown3 = _byteswap_ushort(Unknown3);
        Unknown4 = _byteswap_ushort(Unknown4);
        Unknown5 = _byteswap_ushort(Unknown5);
        Unknown6 = _byteswap_ushort(Unknown6);
        Unknown7 = _byteswap_ushort(Unknown7);

// Length:  10 bytes
// Description:
//            3 indexes into the Vertex array, followed by two indexes into
//            what is theorized as the Type_t array. The values are never the
//            same, and on simple maps Unknown1 will be "0" for every entry,
//            while in the same map Unknown2 points from anything from 1-5.
// Purpose: 
//            Appears to link triangles to information about the surface type.
struct Face_t
    U16 verts[3];

    U16 SurfacePropertyIndex;
    U16 StringGroupIndex; //Can be any value with no ill effects. Seems to just be used by Nintendo's tool to easily map to which String Group it is but isn't used by the game.

    inline void Byteswap()
        verts[0] = _byteswap_ushort(verts[0]);
        verts[1] = _byteswap_ushort(verts[1]);
        verts[2] = _byteswap_ushort(verts[2]);

        SurfacePropertyIndex = _byteswap_ushort(SurfacePropertyIndex);
        StringGroupIndex = _byteswap_ushort(StringGroupIndex);

// Purpose: Appears to define properties about the surface material.
//            Length: 52 bytes.
struct StringGroup_t
    //Offset from start of file. This contains Romanji names for objects, things like
    //"floor" "wall" "water" "surfacePoly" etc. My guess is these were the original
    //names in Nintendo's editor and can be reused to re-create them in the OBJ.
    U32        nameOffset;

    //This value is 1,1,1 for every Type_t on some maps, or ~94% of them on others
    //When the map does have some non-1,1,1 the values normally range between 0.5 and 2
    Vector3    Scale; //Unknown1 might be a scale, but doesn't make a lot of sense.

    U16        Unknown1;
    U16        Unknown2;
    U16        Unknown3;
    U16        Padding;

    //This value is 0,0,0 for every Type_t on some maps, or ~98% of them on others
    //When the map does have some non-0,0,0 the values range between -5000 and 5000
    Vector3 Translation;

    7:05 PM - Zeal of 12000BC: "Top/Upper level" = string group entries with no actual faces assigned to them
    7:06 PM - Zeal of 12000BC: "Bottom/Lower level" = string groups with faces assigned to them    */
    U16        ChildOf; //Ex: Child of Entry 0, Sibling of Entry 3, Parent of Entry 32
    U16        SiblingOf; //These are indexes into other Type_t groups. If it doesn't
    U16        ParentOf; //have one then it's a null byte (FF FF)
    U16        Unknown8; //Seem to always be FF FF - Padding

    U16        Unknown9; //Possibly a Flags Field? Seems to not do anything, maybe it's something else for Nintendo's tools.

    7:06 PM - Zeal of 12000BC: The faces are assigned through a 01 00 entry, whose index is given in the two bytes at 46 dec
    7:07 PM - Zeal of 12000BC: The 01 00 entry in turn has the indicies of all the 01 01 entries that, IN TURN, give the indicies of the appropriate spatial list faces
    7:08 PM - Zeal of 12000BC: It looks like sometimes the 01 00 entries might give the index of another 01 00 entry, but I'm not sure if that's a fact*/
    U16        FaceGroupIndex;

    //Either 256, 'Default' is 0
    U32        IsWater; //If it's set to 256 IT'S FUCKING WATER JESUS CHRIST. If it's not it's FUCKING SOLID. CHRIST!

    //Unknown1/Unknown2 are curious. They don't seem to correspond to anything. If they're positions
    //in the world, they aren't near where their associated triangles are. If they're some sort of
    //physics property override, they don't make much sense. It's my belief that it'd be best to just
    //write them in as 1,1,1 and 0,0,0 since some maps have these values so the engine can obviously
    //load them like that. In theory at least.

    inline void Byteswap()
        nameOffset = _byteswap_ulong(nameOffset);
        Unknown2 = _byteswap_ushort(Unknown2);
        Unknown3 = _byteswap_ushort(Unknown3);
        Unknown4 = _byteswap_ushort(Unknown4);


        Unknown5 = _byteswap_ushort(Unknown5);
        Unknown6 = _byteswap_ushort(Unknown6);
        Unknown7 = _byteswap_ushort(Unknown7);
        Unknown8 = _byteswap_ushort(Unknown8);
        Unknown9 = _byteswap_ushort(Unknown9);
        Unknown2GroupIndex = _byteswap_ushort(Unknown2GroupIndex);
        Unknown10 = _byteswap_ushort(Unknown10);

// Purpose: dzbHdr_t is 48 bytes in length, contains no MAGIC, ID, or other
//            identifying information, just a pure count/offset gig.
struct dzbHdr_t
    U32 vertexCount;
    U32 vertexOffset;
    U32 triangleCount;
    U32 triangleOffset;

    U32 SpatialListCount;
    U32 SpatialListOffset;
    U32 FaceGroupDataCount;
    U32 FaceGroupDataOffset;

    U32 TypeCount;
    U32 TypeOffset;

    U32 SurfacePropertyCount;
    U32 SurfacePropertyOffset;

    //Is always set to 0
    U32    padding;

    Vector3        *Vertex(U32 i);
    Face_t        *Face(U32 i);
    Type_t        *Type(U32 i);

    SpatialList_t    *SpatialList(U32 i);
    FaceGroupData_t    *FaceGroupData(U32 i);
    SurfaceProperty_t    *SurfaceProperty(U32 i);

    inline void Byteswap()
        vertexCount = _byteswap_ulong(vertexCount);
        vertexOffset = _byteswap_ulong(vertexOffset);
        triangleCount = _byteswap_ulong(triangleCount);
        triangleOffset = _byteswap_ulong(triangleOffset);

        SpatialListCount = _byteswap_ulong(SpatialListCount);
        SpatialListOffset = _byteswap_ulong(SpatialListOffset);
        FaceGroupDataCount = _byteswap_ulong(FaceGroupDataCount);
        FaceGroupDataOffset = _byteswap_ulong(FaceGroupDataOffset);
        SurfacePropertyCount = _byteswap_ulong(SurfacePropertyCount);
        SurfacePropertyOffset = _byteswap_ulong(SurfacePropertyOffset);

        TypeCount = _byteswap_ulong(TypeCount);
        TypeOffset = _byteswap_ulong(TypeOffset);

inline Vector3 *dzbHdr_t::Vertex(U32 i)
    return (Vector3 *)(((byte *)this) + vertexOffset) + i;

inline Face_t *dzbHdr_t::Face(U32 i)
    return (Face_t *)(((byte *)this) + triangleOffset) + i;

inline Type_t *dzbHdr_t::Type(U32 i)
    return (Type_t *)(((byte *)this) + TypeOffset) + i;

inline SpatialList_t *dzbHdr_t::SpatialList(U32 i)
    return (SpatialList_t*)(((byte *)this) + SpatialListOffset) + i;

inline FaceGroupData_t *dzbHdr_t::FaceGroupData(U32 i)
    return (FaceGroupData_t *)(((byte *)this) + FaceGroupDataOffset) + i;

inline SurfaceProperty_t *dzbHdr_t::SurfaceProperty(U32 i)
    return (SurfaceProperty_t *)(((byte *)this) + SurfacePropertyOffset) + i;

#endif //DZBFORMAT_H

2DMA holds the settings for the map display in the bottom left-hand corner of the screen. They are 0x38/56 dec bytes long.

0x00 float fullMapImageScaleX;
0x04 float fullMapImageScaleY;
0x08 float fullMapSpaceScaleX;
0x0C float fullMapSpaceScaleY;
0x10 float fullMapXCoord;
0x14 float fullMapYCoord;
0x18 float zoomedMapXScrolling1; //Something with scrolling, but that's also defined below?
0x1C float zoomedMapYScrolling1; //Does something like scrolling on y-axis
0x20 float zoomedMapXScrolling2;
0x24 float zoomedMapYScrolling2;
0x28 float zoomedMapXCoord;
0x2C float zoomedMapYCoord;
0x30 float zoomedMapScale; //That's what it appeared to affect, anyway
0x34 int8 unknown1; //Always 0x80?
0x35 int8 mapIndex; //number of the map image to use. For instance, using the first image would be 80, the second 81, and so on.
0x36 int8 unknown2; //variable, but changing it has no immediate result
0x37 int8 padding;

LGHT is used for an interior light source. Since LGTV shares both its first two letters and its structure with LGHT, it is assumed that they are similar in purpose. Both are 0x1C/28 dec bytes in length.

0x00: Pos X (float)
0x04: Pos Y (float)
0x08: Poz Z (float)
0x0C: Scale/Intensity (float)
0x10: Scale/Intensity (float)
0x14: Scale/Intensity (float)
0x18: R (byte)
0x19: G (byte)
0x1A: B (byte)
0x1B: Unknown (byte) - Presumably padding, 0x00.

The PLYR chunk and its entries define spawn points where Link enters maps. It is laid out like this:

0x08 int8 evntIndex; //Specifies an event from the DZS file to play upon spawning. FF means no event<br>
0x09 int8 unknown1; //Padding?<br>
0x0A int8 spawnType; //How Link enters the room<br>
0x0B int8 roomNum; //room number of the room the spawn is in<br>
0x0C int spawnXCoord;<br>
0x10 int spawnYCoord;<br>
0x14 int spawnZCoord;<br>
0x18 int16 xRotation;<br>
0x1A int16 yRotation;<br>
0x1C int16 zRotation;<br>

Below are some valid values for spawnType.

00 - Spawns Link at the entry's exact location
A0 - Link opens a door
50 - Link spawns and walks forward a few steps. If there isn't a large space behind Link, the camera will be scrunched up directly behind him.
F0 - Gliding down a column of light
D0 - Coming up from a hole
10 - Seems the same as 00
20 - Seems the same as 00
90 - Hangs with the camera in a cut scene-like stance
B0 - Sort of odd version of A0. Might be for Forsaken Fortress doors?
C0 - Can't tell. Camera starts at a door, then pivots around to Link, who spawned as if it was 00.

RARO and AROB are two chunk names for the same function. They are points to which the camera goes to when RCAM or CAMR so needs.

0x0 vector3 pointCoords;
0xC xyzRotation; //each is a short. Apparently you divide the short by 182.04444444444444 to get the actual value but I dunno how it works
0x12 int16 padding;

RPAT and PATH are chunks that put RPPN and PPNT entries, respectively, into groups. RPAT and RPPN are found only in DZRs, and PATH and PPNT are only found in DZSs. An entry is 0xC (12 dec) bytes long.

0x0 int16 numPoints; //Number of entries in this group
0x2 int16 unknown1; //Can't tell what it does, we'll have to find out later
0x4 int8 unknown2; //Ditto
0x5 int8 unknown3; //Usually this is 00, but it can also be 01
0x6 int16 padding;
0x8 in32 firstEntryOffset; //Offset to the first entry of this group

NOTE: With the exception of unknown3, the unknowns are almost always null (FF) bytes. They probably have purposes, but they are unclear at this time. A workable RPAT/PATH entry can be made with the unknowns set to FF, but also remember to set unknown3 to 00.

SCLS holds the exit data that the engine uses to switch between worldspaces when prompted to do so. Entries are 0xC (12 dec) bytes long.

0x0 string destName; //Name of destination worldspace
0x8 int8 spwnNum; //Spawn index to use to enter the destination map
0x9 int8 destRoomNum; //Number of destination room
0xA int8 unknown1; //The old doc says this is "exitType," but I messed with it and found no changes?
0xB int8 padding;

SCOB (possibly an abbreviation SCalable OBject) chunks are similar to ACTR chunks, but the types of objects found here are different. SCOB has properties for scaling objects, which also works with those that are only usually found in ACTR (though with glitches, of course).

0x00 string objectName; //8 bytes long
0x08 byte param0;
0x09 byte param1;
0x0A byte param2;
0x0B byte param3; //These four fields are context-sensitive. They differ between objects
0x0C vector3 objectCoords;
0x18 int16 auxiliaryParam1; //only objects that call up text use this, and it's for the text ID
0x1A int16 yRotation;
0x1C int16 unknown1;
0x1E int16 unknown2; //Could be padding, seems to always be FF FF
0x20 byte scaleX;
0x21 byte scaleY;
0x22 byte scaleZ;
0x23 byte padding;

TGDR and its oddball cousins (DOOR, for instance) place doors that Link can open, whether they're for dungeons or going inside regular buildings. Interestingly, doors can be placed in an ACTR chunk, as well (see the map res/Stage/Orichh/Room0).

TGDR entries are 0x24/36 dec bytes long.

  • 0x00 string name;
  • 0x08 int16 unknown0; //mostly 0F FF?
  • 0x0A int16 doorType; //This is odd. The values are all weird. Better just read out the individual bytes instead of the value for now
  • 0x0C vector3 xyzCoords;
  • 0x18 int16 unknown1;
  • 0x1A int16 yRot;
  • 0x1C int16 unknown2;
  • 0x1E int16 padding;
  • 0x20 int unknown3; //Looks like SCOB's scale properties, but changing them did nothing. Placeholder scale data that isn't used in this chunk?

FILI describes misc. data that applies only to the room it is found in.

0x00 int8 timePassage; //At least, makes it so that you can use the Song of Passing. Dunno if it's actually if time passes
0x01 int8 wind;
0x02 int8 unknown1; //Not the Tingle Tuner. Couldn't figure out what it could do
0x03 int8 lightingType; //value of 04 is normal, value of 05 darkens the room and puts a spotlight on Link, like in some rooms of the Savage Labyrinth or Dragon Roost Cavern
0x04 float unknown2; //yet another value that changes nothing apparent...

LBNK probably stands for Left BlaNK, and is typically a set of all null values, except for once in a blue moon when there is an odd byte mixed in. They are 0xC/12 dec bytes long.

SOND serves as a source for ambient noises not found in the current location's music.

0x00 string "sndpath";
0x08 vector3 sourceCoords;
0x14 int8 unknown1; //typically 00, but one example had 08
0x15 int8 padding;
0x16 int8 unknown2; //typically FF, but Outset's entries have the room number, 0x2C, here
0x17 int8 soundID;
0x18 int8 soundRadius;
0x19 int8 padding;
0x1A int8 padding;
0x1B int8 padding;

Header Format

The .dzs and .dzr format is composed of a header and then multiple chunks with differing data. The file header is only 4 bytes long and identifies how many chunks there are. The next section of the file is the chunk headers which have the format as follows:

FileHeader - (0x4/4 Bytes in Length)

  • 0x0: NumChunks (UInt32)

ChunkHeader - (0xC/12 Bytes in Length)

  • 0x0: Tag - ASCII (4 bytes)
  • 0x4: ElementCount (UInt32)
  • 0x8: Offset (From start of File) (UInt32)

Known List of Chunks:

  • 2DMA - Onscreen Mini-Map Properties
  • ACTR - Actor Info
  • AROB - Camera waypoint, like RARO
  • CAMR - Sets a camera behavior for certain events and points to a RARO or AROB entry if needed
  • Colo - Part of Environment Lighting
  • DMAP - Dungeon Map Properties
  • EnvR - Part of Environment Lighting
  • Evnt - Worldspace Events
  • FILI - Room Properties
  • FLOR - Floor definitions for multi-floor worldspaces
  • LGHT - Interior Lightsource
  • LBNK - Padding?
  • MECO - Assigns rooms to an entry in MEMA
  • MEMA - Gives a room the amount of free memory specified by the entry indexed in MECO
  • MULT - Room model X/Z translation
  • PATH - Waypoint series definition
  • PLYR - Player Spawn Points
  • Pale - Part of Environment Lighting
  • PPNT - Path Waypoint Coordinates
  • RARO - Camera Reference Point for Event_List.dat - Identical in structure to AROB
  • RCAM - Sets camera behavior
  • RPAT - Waypoint Series Definition
  • RPPN - Path Waypoint Coordinates
  • RTBL - Defines what rooms are loaded from the room the player is currently in
  • SCLS - Exit List
  • SCOB - Environmental Actors (ie: Light)
  • SHIP - King of Red Lions spawn points.
  • SOND - Ambient Sound Settings
  • STAG - Worldspace Properties
  • TGDR - Doors
  • TGOB - Another Actor info (eg: Dragon Roost Cavern staircase)
  • TRES - Treasure Chests
  • Virt - Part of Environment Lighting
  • LGTV - Assumed to be an interior light source; shares the same structure as LGHT

Chunk Formats

###STAG Room Properties (0x14/20 Bytes in Length)

  • 0x00 float depthMin;
  • 0x04 float depthMax;
  • 0x08 int16 keyCounterDisplay; //has different values. For different settings?
  • 0x0A int16 loadedParticleBank; //particle bank to load for the worldspace. Method of IDing a certain particle bank is currently unknown.
  • 0x0C int16 itemUsageAndMinimap; //items Link can use, what color the mini-map's background is
  • 0x0E int8 padding;
  • 0x0F int8 unknown1;
  • 0x10 int8 unknown2; //RGB data? Nothing seems to change when these are changed
  • 0x11 int8 unknown3;
  • 0x12 int16 drawRange;

###RTBL Unfinished. See the RTBL Wiki Entry for the most up to date information.

###SCLS Exit List (0xC/12 Bytes in Length)

  • 0x0: Folder of Destination Map in ASCII (0x8/8 bytes) (ie: "kaze")
  • 0x8: Spawn Point # (byte)
  • 0x9: Room Number (in hex?) (byte)
  • 0xA: Exit Type (door, upwards teleport, etc.)
  • 0xB: Padding? 0xFF

###ACTR Actor definitions (0x20/32 Bytes in Length)

  • 0x00: string Name //0x8/8 bytes total space
  • 0x08: int8 unknown1;
  • 0x09: int8 rpatIndex;
  • 0x0A: int8 unknown2;
  • 0x0B: int8 behaviorType;
  • 0x0C: float xPos
  • 0x10: float yPos
  • 0x14: float zPos
  • 0x18: float xRot
  • 0x1A: float yRot
  • 0x1C: float zRot
  • 0x1E: int16 enemyNumber; //Purpose unknown. Enemies are given a number here based on their position in the actor list

PLYR Chunks and TRES (Treasure) Chunks appear to be similar.

###EVNT Event Format - (0x18/24 Bytes in Length)

  • 0x0: Unknown (byte) - Mostly 0xFF
  • 0x1: Event Name (see /stage/dat/event_list.dat) (0xF/15 Bytes long)
  • 0x10: Unknown (byte)
  • 0x11: Unknown (byte)
  • 0x12: Unknown (byte)
  • 0x13: Unknown (byte)
  • 0x14: Room Number (byte) - Matches in file names, (ie: Room44.arc) - not always set
  • 0x15: Unknown (3 bytes) - Padding? Often 0xFFFFFF

###RARO and AROB### These two chunks share identical layouts. They seem to call identical functions within Windwaker's executable. They look to be identical other than in name.

Usage: RARO is apparently used by the Event_List.dat as a Camera Reference Point. Camera moves to the position specified for events.

Event Format - (0x14/20 Bytes in Length)

  • 0x0: Pos X (float)
  • 0x4: Pos Y (float)
  • 0x8: Poz Z (float)
  • 0xC: Unknown (0x8/8 bytes) - Always "00 00 80 00 00 00 FF FF";

###LGHT### This chunk appears to be indoor lighting (to affect shading on character model). Chunk length is assumed due to not enough test cases to be entirely assured. 95% likelyhood this is correct.

Light Format - (0x1C/28 Bytes in Length (assumed))

  • 0x0: Pos X (float)
  • 0x4: Pos Y (float)
  • 0x8: Poz Z (float)
  • 0xC: Scale/Intensity (float)
  • 0x10: Scale/Intensity (float)
  • 0x14: Scale/Intensity (float)
  • 0x18: R (byte)
  • 0x19: G (byte)
  • 0x1A: B (byte)
  • 0x1B: Unknown (byte) - Presumably padding, 0x00.

###MULT### This is used by the Stage to translate rooms. Only used on two maps in the game that I'm aware of.

Mult Format - (0xC/12 Bytes in Length)

  • 0x0: Translation X (float)
  • 0x4: Translation Z (float)
  • 0x8: Rotation on Y axis (short)
  • 0xA: Room number (byte)
  • 0xB: Unknown (byte)

###RPPN### Some form of waypoints for pathing.

Rppn Format - (0x10/16 Bytes in Length)

  • 0x0: Unknown (4 bytes in length)
  • 0x4: Pos X (float)
  • 0x8: Pos Y (float)
  • 0xC: Pos Z (float)

###SHIP### Spawn points for the King of Red Lions

Ship Format - (0x10/16 bytes in Length)

  • 0x0: Pos X (float)
  • 0x4: Pos Y (float)
  • 0x8: Pos Z (float)
  • 0xC: Unknown

###EnvR, Colo, Pale, Virt### All of these chunks have to do with environmental lighting. They have their own Wiki articles here which I am too lazy to reformat into this list.

###TRES### Treasure! Well, treasure chests. That's basically the same thing right?

Tres Format - (1C/28 Bytes in Length)

  • 0x0: Name in ASCII. Usually "takara". (0x8 bytes + null terminator = 0x9 total)
  • 0x9: Chest Type (big key, common wooden, etc.) - (Short)
  • 0xB: X Pos (float)
  • 0xF: Y Pos (float)
  • 0x13: Z Pos (float)
  • 0x17: Unknown (short)
  • 0x19: Rotation on Y Axis (short)
  • 0x1B: Chest Contents (Rupees, Hookshot, Power bracelets, etc.) (Byte)

###2DMA### Theoretically related to the Minimap. Some Stages have entries which are all zeros. Looks like a lot of single-byte values. Thoughts: Maybe its minimap offset/rotation? Doesn't seem to be tied to #rooms. M_NewD2_Stage has two entries, others tested (Kaze_Stage, Orich_Stage) have one. Maybe it's related to Tingle Tuner and the gameboy? Maybe it's related to the in-game sea-chart map and not the minimap? Either way, looks like you can shove it full of zeros and the game likes that so...

2DMa Format - (0x10/16 Bytes in Length)

  • 0x0: Unknown (0x4 Bytes)
  • 0x4: Unknown (0x8 Bytes?)
  • 0xC: Unknown (0x4 Bytes)

###MEMA### Unknown. May be related to the function: "GetMemoryMap"

MEMA Format - (0x4 bytes length)

  • 0x0: Unknown

###MECO### Unknown. May be related to the function: "GetMemoryConfig"

MECO Format - (0x2 bytes in Length)

  • 0x0: Index (?) Ordered from 00 to Num-of-entries
  • 0x1: ???

##RCAM & CAMR## Seems to correspond to camera behavior. ASCII name of behaviors match up with strings inside the executable. Both RCAM and CAMR have identical layouts and call the same code within the executable.

RCam Format - (0x14/20 Bytes in Length)

  • 0x0: CameraType (ASCII String) (0x10/16 Bytes)
  • 0x16: Null Terminator (4 bytes, pattern: 00 FF FF FF)

Possible CameraType Values (Untested): Field, Dungeon, Plain, DungeonDown, DungeonUp, DungeonCorner, Jump, DungeonWide, Room, FieldCushion, OverLook, Corridor, Subject, DungeonPassage, Cliff, Cliff2, MajTower, Boss01, Boss02, Gamoss, MiniIsland, Amoss, Cafe, P_Ganon1, P_Ganon2, WindBoss, P_Ganon3, G_BedRoom, G_Roof, G_BedRoom2, Boss04, WindHall, BigBird, DStairs.

DMAP holds some basic settings for the dungeon maps that you can view in dungeons by pressing up on the D-Pad.

0x00 float mapSpaceX;
0x04 float mapSpaceY; //The rectangular door sprites on the screen move, but the actual map image does not
0x08 float mapSpaceScale; //Larger values make the scale smaller. Fractional scale?
0x0C float unknown1; //always 00 00 00 00? Could be filler or a terminator

While these are all 0x10/16 dec bytes long everywhere else, the DMAP chunk in Dragon Roost Cavern (folder name M_NewD2) seems to have extra data attached to it. Corrupting all of this data doesn't cause any errors, and since it isn't present in any other maps, it's safe to assume that this isn't used. The extra data could be the remnant of earlier map coding.

EnvR is the first node on a tree determining the lighting situation of a worldspace. These settings are global, which means that they apply to all the rooms within a worldspace. One entry accounts for two such situations. It seems to go like this:

0x00 int8 firstClearColoIndex; //Index of the Colo entry to use for clear weather
0x01 int8 firstRainingColoIndex; //Index of the Colo entry to use for rainy weather
0x02 int8 firstSnowingColoIndex; //Index of the Colo entry to use for snowy weather (This is just a guess)
0x03 int8 firstUnknown2ColoIndex; //Index of the Colo entry to use for an unknown condition

0x04 int8 secondClearColoIndex;
0x05 int8 secondRainingColoIndex;
0x06 int8 secondSnowingColoIndex;
0x07 int8 secondUnknown2ColoIndex;

Colo. This branches lighting based on the time of day. Each entry is made up of these fields:

0x00 int8 dawnPaleIndex; //Index of Pale entry to use for dawn
0x01 int8 morningPaleIndex; //Index of Pale entry to use for morning
0x02 int8 noonPaleIndex; //Index of Pale entry to use for noon
0x03 int8 afternoonPaleIndex; //Index of Pale entry to use for the evening
0x04 int8 duskPaleIndex; //Index of Pale entry to use for dusk
0x05 int8 nightPaleIndex; //Index of Pale entry to use for night

Pale, with a length of 0x2C, contains misc. RGB data for worldspaces. The fields for one entry are defined as follows:

0x00 int8 actAmbientRValue;
0x01 int8 actAmbientGValue;
0x02 int8 actAmbientBValue;

0x03 int8 shadowRValue;
0x04 int8 shadowGValue;
0x05 int8 shadowBValue;

0x06 int8 fillLightRValue;
0x07 int8 fillLightGValue;
0x08 int8 fillLightBValue;

0x09 int8 roomAmbientRValue;
0x0A int8 roomAmbientGValue;
0x0B int8 roomAmbientBValue;

0x0C int8 waveRValue;
0x0D int8 waveGValue;
0x0E int8 waveBValue;

0x0F int8 oceanRValue;
0x10 int8 oceanGValue;
0x11 int8 oceanBvalue;

0x12 int8 unknownWhite1RValue;
0x13 int8 unknownWhite1GValue; //Typically this is pure white or just off of it
0x14 int8 unknownWhite1BValue;

0x15 int8 unknownWhite2RValue;
0x16 int8 unknownWhite2GValue; //Ditto here
0x17 int8 unknownWhite2BValue;

0x18 int8 doorwayColorRValue;
0x19 int8 doorwayColorGValue;
0x1A int8 doorwayColorBValue;

0x1E int8 fogRValue;
0x1F int8 fogGValue;
0x20 int8 fogBValue;

0x21 int8 virtIndex; //Index of Virt entry to use for skybox colors

0x22 int8 padding;
0x23 int8 padding;

0x24 int8 oceanFadeIntoRValue;
0x25 int8 oceanFadeIntoGValue;
0x26 int8 oceanFadeIntoBValue;
0x27 int8 oceanFadeIntoUnknown; //Alpha?

0x28 int8 shoreFadeIntoRValue;
0x29 int8 shoreFadeIntoGValue;
0x2A int8 shoreFadeIntoBValue;
0x2B int8 shoreFadeIntoUnknown; //Alpha?

Virt contains color data for the skybox. One entry consists of:

0x10 int8 horizonCloudRValue;
0x11 int8 horizonCloudGValue;
0x12 int8 horizonCloudBValue;
0x13 int8 horizonCloudUnknown; //Alpha?

0x14 int8 centerCloudRValue;
0x15 int8 centerCloudGValue;
0x16 int8 centerCloudBValue;
0x17 int8 centerCloudUnknown; //Alpha?

0x18 int8 centerSkyRValue;
0x19 int8 centerSkyGValue;
0x1A int8 centerSkyBValue;

0x1B int8 horizonRValue;
0x1C int8 horizonGValue;
0x1D int8 horizonBValue;

0x1E int8 skyFadeToRValue;
0x1F int8 skyFadeToGValue; //Color to fade to from centerSky
0x20 int8 skyFadeToBValue;

0x21 int8 padding;
0x22 int8 padding; //Entries would be 0x21 bytes long without these. Ick.
0x23 int8 padding;

Theorized Editor Mockup:

FLOR defines the floors that appear in a dungeon map, and what rooms are included in one.

0x0 vector lowerBoundaryYCoord; //Y value of the lower boundary of a floor. When Link crosses that coord, the map switches him to being on that floor
0x4 int8 floorID; //8x, with x starting at 0
0x5 includedRooms; //This is a field 0xF/15 dec bytes long. Each byte is a room number, and rooms in sequence are stored together. If there is a break in the sequence, there are some null FF values between one sequence and the other. Whether this is necessary or not is unknown.

Here is an example entry:

(1) C5 CB 20 00 (2) 7F (3) FF 02 FF FF FF 06 FF FF 09 0A 0B 0C FF FF FF

  1. lowerBoundaryYCoord
  2. floorID
  3. includedRooms

MECO and MEMA deal with how the engine allocates data. Note that this doc needs further investigation. The info here should be relatively viable, though.

MECO's length is dependent on how many rooms there are in the worldspace, as it seems to give each room a memory pool to fill. This two-byte structure is repeated however many times is needed:

0x00 int8 roomNum;
0x01 int8 memaIndex;

A MEMA entry appears to give the engine a memory size to allocate for a room with that entry's index. One entry is just four bytes long:

0x00 int32 memSize;

RCAM and CAMR carry the same data, and the reason for the differing names is unknown. They hold data for how the camera should behave during certain events. Their length is 0x14/20 dec bytes long.

0x0 string cameraType; //String, from a list in the .dol, giving the camera type
0x10 raroIndex; //index of the RARO this entry uses
0x11 padding;
0x12 padding; //to the nearest 4
0x13 padding;

RTBL is a chunk found in a DZS file. It defines what rooms are loaded into memory along with the one Link is currently in. This is used in dungeons to allow movement between rooms without loading screens.

This chunk is made up of three parts - offsets, entry definitions, and entries. The number of each of these is the same as the chunkCount specified in RTBL's chunk header entry.

Offsets are simply that - 4 bytes that give the offset of an entry definition within the DZS file. Note that the offset is from the start of the file itself, NOT from the start of the RTBL chunk.

Entry definitions are a bit more complex. They are set up like this:

+0x00 int8 entrySize;
+0x01 int8 unknown1;
+0x04 int entryOffset;

In situations where there is only one room in the worldspace, entrySize will be 1, to show that there are no other rooms to be loaded. However, if there are other rooms to be loaded, entrySize will vary depending on how many rooms will be put into memory.

An example would be:

04 78 00 00 00 00 03 45

This is a definition for a 4-byte long entry which begins at 0x345 in the DZS file.

Entries themselves are made up of at least one byte, which is a combo of C plus the number of the room Link is currently in. For instance, an entry defining other rooms to load from a Room0 would be C0, from Room1 it would be C1, and so on. If there are room numbers that cannot be expressed in hex with one digit, such as 16, which is 10 hex, C changes to D. D0 would thus represent a Room16, D1 would be Room17, and so on. The other bytes are what room numbers to load, in ascending order. Each room is given one full byte. Take this example:

C0 01 03 04

C0 is the room Link will be in, and Rooms1, 3 and 4 will be loaded along with it.

STAG holds some misc. information about a worldspace.

0x00 float depthMin;
0x04 float depthMax;
0x08 int16 keyCounterDisplay; //has different values. For different settings?
0x0A int16 loadedParticleBank; //particle bank to load for the worldspace. Method of IDing a certain particle bank is currently unknown.
0x0C int16 itemUsageAndMinimap; //dictates whether Link can use his items, what color fills the background of the minimap display (pre-determined elsewhere), and whether you have access to a map
0x0E int8 padding;
0x0F int8 unknown1;
0x10 int8 unknown2; //RGB data? I don't know for what though, nothing changed when I changed this. These three unknowns are usually set to 00 00 FF. That can be the default value to use for file creation, since it works
0x11 int8 unknown3;
0x12 int16 drawRange;

##Overview## Lighting consists of four separate parts. The EnvR section contains indexes into the Colo section for different lighting effects. The Colo section contains indexes into the Pale section for different times of day. The Pale section contains the actual color values used by the game. The Virt section (which is indexed by Pale) points to Skybox color settings.

It is important to note that the "Save" button works differently than expected! Pressing "Save" will only overwrite the EnvR, Colo, Pale and Virt chunks in their respective ZArchive that exists in Wind Viewer's memory. It is not until you save this archive (via File->Save All) that these changes are actually written to disk!

###EnvR### There are two modes here, called A and B. The default color settings used are A but some maps have values stored in mode B. The usage of mode B is unknown at the moment.

  • Clear Skies Index: Index of which Color section to use when the weather is clear (ie: Not raining/snowing)
  • Raining Index: Index of which Color section to use when it is raining.
  • Snowing Index: Index of which Color section to use when it is snowing.
  • Unknown Index: It is unknown as to when these colors are currently used.

###Colo### Colo, short for Color contains different indexes that the game blends between based on the time of day. These are indexes into the Pale section for different times of day.

###Pale### Pale, short for Palette contains the actual RGB values used by the game engine.

  • Actor Ambient Color - This is ambient lighting for Actors.
  • Shadow Fill Color - This is the color of shadows in the room.
  • Room Fill Color - This is a Fill Light used by the room.
  • Room Ambient Color - Similar to Actor Ambient Color but for the Room model.
  • Wave Color - Color of the wave caps.
  • Ocean Color - Color of the Ocean.
  • Unknown Color 1 - Unknown.
  • Unknown Color 2 - Unknown.
  • Doorway Color - This is the color that the mesh behind the door gets tinted. Used to show if it is bright/dark outside a room.
  • Unknown Color 3 - Unknown.
  • Fog Color - This is the color of the fog.
  • Virt Index - Index into the Virt Array. Contains skybox color settings.
  • Ocean Fade Into Color - This is a RGBA color. ToDo: Where is this fade?
  • Shore Fade Into Color - RGBA - See Above.

###Virt### Virt, short for Virtual Box(?) contains settings for the Skybox on the level.

  • Horizon Cloud Color - RGBA - Color of the Clouds on the Horizon
  • Center Cloud Color - RGBA - Color of the clouds above the player.
  • Center Sky Color - Color of the sky above the player. Blends into Horizon Color
  • Horizon Color - Color of the sky at the horizon.
  • Sky Fade To Color - ToDo: What is this?

##Trouble Shooting## There is no index-limiting placed on controls at this time. This means you can specify an invalid index in the controls. It is untested what invalid indexes do (crash? Revert to zero?) so if you're running into trouble start by checking all of the indexes. Be aware that the indexes are zero-indexed, meaning they start count at zero and not one.

The game will probably crash if there's not at least one EnvR/Color/Virt/Pale chunk specified (there's an untested correlation between EnvR and Colo groups and number of stages on a map. It seems that there's often at least as many EnvR and Colo groups as there are rooms associated with the stage of this map.), though Rooms can omit all four of these chunks without issue.

The Environment Lighting Editor was Wind Viewer's first functional editor that supported the addition/deletion of existing items!

Welcome to the WindViewer wiki!

There's not much here yet (we're too busy changing bytes to write stuff!) but head on over to the Pages tab (until we get a better index) to see what we have written down.

##Program Usage:## Camera Modifiers: Space increases speed, Shift slows it. Ctrl fixes the depth (2d movement). Saving Capabilities: Limited, In Progress.

###Wind Viewer Editor Documentation### This section includes documentation for Wind Viewer specific topics. This is not documentation of Wind Waker formats but more of a "How To" guide for using different parts of Wind Viewer.

What we've got here now:

##DZS and DZR File Formats




##What can you do to help? Grab a file format and figure out what any bit of it does! Or merge information in from other sources on the web (ie: Zelda GCN Wiki, The Cutting Room Floor, The-GCN, etc!)

This file is an archive file and contains several other files. This document describes the structure of these RARC files (note that I got all this information by staring at these files in a hex editor, so it may not be entirely seems to work, though).

The file starts with an RarcHeader:

struct RarcHeader
  char type[4]; //'RARC'
  u32 size; //size of the file
  u32 unknown;
  u32 dataStartOffset; // You have to add 0x20 to this value.
  u32 unknown2[4];

  u32 numNodes;
  u32 unknown3[2];
  u32 fileEntriesOffset; // You have to add 0x20 to this value.
  u32 unknown4;
  u32 stringTableOffset; // You have to add 0x20 to this value.
  u32 unknown5[2];

Next are RarcHeader.numNodes Node structures:

struct Node
  char type[4];
  u32 filenameOffset; //directory name, offset into string table
  u16 unknown;
  u16 numFileEntries; //how many files belong to this node?
  u32 firstFileEntryOffset;

Each RARC file contains at least one Node, the 'ROOT' node. For each subdirectory in the archive, there's another Node (so each Node represents a directory). Each Node contains files and directories, represented by FileEntry structures:

struct FileEntry
  u16 id; //file id. If this is 0xFFFF, then this entry is a subdirectory link
  u16 unknown;
  u16 unknown2;
  u16 filenameOffset; //file/subdir name, offset into string table
  u32 dataOffset; //offset to file data (for subdirs: index of Node representing the subdir)
  u32 dataSize; //size of data
  u32 zero; //seems to be always '0'

To read the archive, you read the root node and its file entries. For each subdir in the root node's fileentries, you read the corresponding node and its file entries. For each file in the fileentries, you dump its data.

Original specs by: thakis (


20050211: Initial release

20140112: fileEntriesOffset also needs +0x20

Here, we will define some terms used throughout the wiki. It will be updated as needed.

  • Room - An archive containing a single map's model, collision, DZR file, and other misc. data
  • DZR file (Zelda Room Data) - A file that defines player spawn points, actors, tags, waypoints, and other misc. data
  • Worldspace - A group of rooms in one folder, all sharing one Stage.arc
  • Stage.arc - An archive containing skybox models, cloud textures, an Event_List.dat, and a DZS file
  • Event_List.dat - A file that defines all of the events that can occur within a worldspace
  • DZS File (Zelda Stage Data) - A file that defines a worldspace's lighting, fog, and callable events; Occasionally holds actors and tags

The following is a list of known extensions used by Wind Waker Maps and what type of file they are. This list does not include file extensions used outside of map archives.

  • *.bmd - Older Model Format (used for the models of the test rooms)
  • *.bdl - Updated Model Format (used for the models of most other rooms, plus the skybox components)
  • *.bck - Bone Animation
  • *.brk - Alpha Animation
  • *.btk - Texture Animation
  • *.dzb - Collision Mesh
  • *.dzr - Entity Information for Rooms
  • *.dzs - Entity Information for Worldspaces
  • *.dat - Event data for worldspaces
  • *.bti - 2D Image Format

[Note: This is a tentative spec/work plan. This is mostly me getting the ideas written down as a form of validating them and is not how it actually behaves at the moment.]

Nintendo's framework supports the .rarc/.arc file format and it is used across many of their games. It is very similar in functionality to a *.zip file. These files can support compression (via yaz0 compression) or uncompressed. The game can load both without issues.

Because these Archives function in a manner similar to *.zip files, the first thing WindViewer does upon opening an archive is extract it to your Working Directory (By default: My Documents\WindViewer...?). It re-creates the folder structure found inside an archive and places the individual files inside their respective folders. Example Folder Structure:


Once WindViewer has extracted the contents from the *.arc, the archive is no longer kept in WindViewer's memory. Instead, it is replaced by meta-data that describes the data and holds references to it. This is the ZData structure which represents the "MiniHyo" level of data (as described above). Within it, it holds a reference to each file (model.bdl, model.btk, room.dzb, room.dzr, etc.). Each file is loaded from disk and is stored in the ZData structure.

Once the file is in the ZData structure we can now add/remove/modify the data to our hearts content. When the current working project is saved (File->Save) we read the list of loaded ZData's and write them out to disk (inside the working directory). It is only upon File->Export that we take a loaded ZData and export out a brand new *.arc that has been created from scratch which can be placed in the Game's Data and played!


Yaz0 is a hybrid of RLE compression. (ShizZie says: "This is somewhat misleading. Run length encoding is only compression on a character-to-character basis, i.e. aaabbbbcccccc encodes to a2b3c5. Yaz0 compresses string-to-string, so it's more like LZMA."

You can see if you're dealing with the following compression if the first 4 bytes of the 16 byte long header contains Y a z 0. The second 4 bytes are in big-endian(uint32) and tells you how large the decompressed data is as well has give you a frame of reference on the size of your buffer. The following 8 bytes are always zero.

"First you read a "code" byte that tells you for the next 8 "read operations" what you have to do. Each bit of the "code" byte represents one "read operation" (from left to right, that is, 0x80 first, 0x01 last). If the bit is 1, copy one byte from the input buffer to the output buffer. Easy. If the bit is 0, things are a little bit more complicated, RLE compressed data is ahead. You have to read the next two bytes to decide how long your run is and what you should write to your output buffer.

15 8 7 0

a b

The upper nibble of the first byte (a) contains the information you need to determine how many bytes you're going to write to your output buffer for this "read operation". if a == 0, then you have to read a third byte from your input buffer, and add 0x12 to it. Otherwise, you simply add 2 to a. This is the number of bytes to write ("count") in this "read operation". byte2 and the lower nibble of byte1 (b) tell you from where to copy data to your output buffer: you move (dist = (b < <8) - byte2 + 1) bytes back in your outputBuffer and copy "count" bytes from thereto the end of the buffer. Note that count could be greater than dist which means that the copy source and copy destination might overlap." ~said it better than I could've.

###Decompression code

/*src points to the yaz0 source data (to the "real" source data, not at the header!) dst points to a buffer uncompressedSize bytes large (you get uncompressedSize from the second 4 bytes in the Yaz0 header). */

void decode(u8* src, u8* dst, int uncompressedSize) 
    int srcPlace = 0, dstPlace = 0; //current read/write positions 
    u32 validBitCount = 0; //number of valid bits left in "code" byte 
    u8 currCodeByte; 
    while(dstPlace < uncompressedSize) 
        //read new "code" byte if the current one is used up 
        if(validBitCount == 0) 
            currCodeByte = src[srcPlace]; 
            validBitCount = 8; 
        if((currCodeByte & 0x80) != 0) 
            //straight copy 
            dst[dstPlace] = src[srcPlace]; 
            //RLE part 
            u8 byte1 = src[srcPlace]; 
            u8 byte2 = src[srcPlace + 1]; 
            srcPlace += 2; 
            u32 dist = ((byte1 & 0xF) < < 8) - byte2; 
            u32 copySource = dstPlace - (dist + 1); 
            u32 numBytes = byte1 > > 4;
            if (numBytes == 0)
                numBytes = src[srcPlace] + 0x12;
                numBytes += 2;

            //copy run 
            for(int i = 0; i < numBytes; ++i) 
                dst[dstPlace] = dst[copySource]; 

             //use next bit from "code" byte 
             currCodeByte < <= 1; 


This format is used to store the fonts in the BIOS/IPL. It is compressed similar to the the Zelda 64 'Yaz0' compression.

0x000 ;Yay0
0x0004 ;size of decoded data in bytes
0x0008 ;offset to link table
0x000c ;offset to non-linked chunks and count modifiers table
0x0010 ;packed data (32 bit words) with 0 being a linked chunk and 1 being non linked chunk

###Decompression code

private void Decode(void* s, void* d)
    u32 i, j, k;
    u32 p, q;
    u32 cnt;
    i = r21 = *(u32*) (s + 4); // size of decoded data 
    j = r29 = *(u32*) (s + 8); // link table 
    k = r23 = *(u32*) (s + 12); // byte chunks and count modifiers 
    q = r31 = 0; // current offset in dest buffer 
    cnt = r28 = 0; // mask bit counter 
    p = r24 = 16; // current offset in mask table 
        // if all bits are done, get next mask 
        if (cnt == 0)
            // read word from mask data block 
            r22 = *(u32*) (s + p);
            p += 4;
            cnt = 32; // bit counter 
        // if next bit is set, chunk is non-linked 
        if (r22 & 0x80000000)
            // get next byte 
            *(u8*) (d + q) = *(u8*) (s + k);
            // do copy, otherwise 
            // read 16-bit from link table 
            r26 = *(u16*) (s + j);
            j += 2;
            // 'offset' 
            r25 = q - (r26 & 0xfff);
            // 'count' 
            r30 = r26 > >
            if (r30 == 0)
                // get 'count' modifier 
                r5 = *(u8*) (s + k);
                r30 = r5 + 18;
            else r30 += 2;
            // do block copy 
            r5 = d + r25;
            for (i = 0; i < r30; i++)
                *(u8*) (d + q) = *(u8*) (r5 - 1);
        // next bit in mask 
        r22 < <=
    } while (q < i);