Skip to content

DZB File Format (Collision)

pho edited this page Jan 13, 2014 · 5 revisions

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.

/*-------------------------------------------------------------------------
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
	Version 2, December 2004

	Copyright (C) 2004 Sam Hocevar <[email protected]>

	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.

	DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
	TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

	0. You just DO WHAT THE FUCK YOU WANT TO.
--------------------------------------------------------------------------*/

#ifndef DZBFORMAT_H
#define DZBFORMAT_H

#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);
		UnknownVec3_1.ConvertToLE();
		Unknown2 = _byteswap_ushort(Unknown2);
		Unknown3 = _byteswap_ushort(Unknown3);
		Unknown4 = _byteswap_ushort(Unknown4);

		UnknownVec3_2.ConvertToLE();

		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