OV.CHUNK3DS =
{
	MAIN3DS : 0x4D4D,
	EDIT3DS : 0x3D3D,
	EDIT_MATERIAL : 0xAFFF,
	MAT_NAME : 0xA000,
	MAT_AMBIENT : 0xA010,
	MAT_DIFFUSE : 0xA020,
	MAT_SPECULAR : 0xA030,
	MAT_SHININESS : 0xA040,
	MAT_SHININESS_STRENGTH : 0xA041,
	MAT_TRANSPARENCY : 0xA050,
	MAT_COLOR_F : 0x0010,
	MAT_COLOR : 0x0011,
	MAT_LIN_COLOR : 0x0012,
	MAT_LIN_COLOR_F : 0x0013,
	MAT_TEXMAP : 0xA200,
	MAT_TEXMAP_NAME : 0xA300,
	MAT_TEXMAP_UOFFSET : 0xA358,
	MAT_TEXMAP_VOFFSET : 0xA35A,
	MAT_TEXMAP_USCALE : 0xA354,
	MAT_TEXMAP_VSCALE : 0xA356,
	MAT_TEXMAP_ROTATION : 0xA35C,
	PERCENTAGE : 0x0030,
	PERCENTAGE_F : 0x0031,
	EDIT_OBJECT : 0x4000,
	OBJ_TRIMESH : 0x4100,
	OBJ_LIGHT : 0x4600,
	OBJ_CAMERA : 0x4700,
	TRI_VERTEX : 0x4110,
	TRI_TEXVERTEX : 0x4140,
	TRI_FACE : 0x4120,
	TRI_TRANSFORMATION : 0x4160,
	TRI_MATERIAL : 0x4130,
	TRI_SMOOTH : 0x4150,
	KF3DS : 0xB000,
	OBJECT_NODE : 0xB002,
	OBJECT_HIERARCHY : 0xB010,
	OBJECT_INSTANCE_NAME : 0xB011,
	OBJECT_PIVOT : 0xB013,
	OBJECT_POSITION : 0xB020,
	OBJECT_ROTATION : 0xB021,
	OBJECT_SCALE : 0xB022,
	OBJECT_ID : 0xB030
};

OV.Importer3ds = class extends OV.ImporterBase
{
	constructor ()
	{
		super ();

		this.materialNameToIndex = null;
		this.meshNameToIndex = null;
	
		this.meshTransformations = null;
		this.defaultMaterialIndex = null;
	}
	
	ResetState ()
	{
		this.materialNameToIndex = {};
		this.meshNameToIndex = {};

		this.meshTransformations = [];
		this.defaultMaterialIndex = null;
	}

	CanImportExtension (extension)
	{
		return extension === '3ds';
	}

	GetKnownFileFormats ()
	{
		return {
			'3ds' : OV.FileFormat.Binary
		};
	}
	
    GetUpDirection ()
    {
        return OV.Direction.Z;
    }

	ImportContent (fileContent)
	{
        this.ProcessBinary (fileContent);
	}

    ProcessBinary (fileContent)
	{
		let obj = this;
        let reader = new OV.BinaryReader (fileContent, true);
		let endByte = reader.GetByteLength ();
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.MAIN3DS) {
				obj.ReadMainChunk (reader, chunkLength);
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});		
    }

	ReadMainChunk (reader, length)
	{
		let obj = this;
		let endByte = this.GetChunkEnd (reader, length);
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.EDIT3DS) {
				obj.ReadEditorChunk (reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.KF3DS) {
				obj.ReadKeyFrameChunk (reader, chunkLength);
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});
	}

	ReadEditorChunk (reader, length)
	{
		let obj = this;
		let endByte = this.GetChunkEnd (reader, length);
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.EDIT_MATERIAL) {
				obj.ReadMaterialChunk (reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.EDIT_OBJECT) {
				obj.ReadObjectChunk (reader, chunkLength);
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});
	}

	ReadMaterialChunk (reader, length)
	{
		let obj = this;
		let material = new OV.Material ();
		let endByte = this.GetChunkEnd (reader, length);
		let shininess = null;
		let shininessStrength = null;
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.MAT_NAME) {
				material.name = obj.ReadName (reader);
			} else if (chunkId === OV.CHUNK3DS.MAT_AMBIENT) {
				material.ambient = obj.ReadColorChunk (reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.MAT_DIFFUSE) {
				material.diffuse = obj.ReadColorChunk (reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.MAT_SPECULAR) {
				material.specular = obj.ReadColorChunk (reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.MAT_SHININESS) {
				shininess = obj.ReadPercentageChunk (reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.MAT_SHININESS_STRENGTH) {
				shininessStrength = obj.ReadPercentageChunk (reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.MAT_TRANSPARENCY) {
				material.opacity = 1.0 - obj.ReadPercentageChunk (reader, chunkLength);
				material.transparent = OV.IsLower (material.opacity, 1.0);
			} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP) {
				material.diffuseMap = obj.ReadTextureMapChunk (reader, chunkLength);
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});

		if (shininess !== null || shininessStrength !== null) {
			material.shininess = shininess * shininessStrength;
		}
		let materialIndex = this.model.AddMaterial (material);
		this.materialNameToIndex[material.name] = materialIndex;
	}

	ReadTextureMapChunk (reader, length)
	{
		let obj = this;
		let texture = new OV.TextureMap ();
		let endByte = this.GetChunkEnd (reader, length);
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_NAME) {
				let textureName = obj.ReadName (reader);
				let textureBuffer = obj.callbacks.getFileBuffer (textureName);
				texture.name = textureName;
				if (textureBuffer !== null) {
					texture.url = textureBuffer.url;
					texture.buffer = textureBuffer.buffer;
				}
			} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_UOFFSET) {
				texture.offset.x = reader.ReadFloat32 ();
			} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_VOFFSET) {
				texture.offset.y = reader.ReadFloat32 ();
			} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_USCALE) {
				texture.scale.x = reader.ReadFloat32 ();
			} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_VSCALE) {
				texture.scale.y = reader.ReadFloat32 ();
			} else if (chunkId === OV.CHUNK3DS.MAT_TEXMAP_ROTATION) {
				texture.rotation = reader.ReadFloat32 () * OV.DegRad;
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});
		return texture;
	}	

	ReadColorChunk (reader, length)
	{
		let obj = this;
		let color = new OV.Color (0, 0, 0);
		let endByte = this.GetChunkEnd (reader, length);
		let hasLinColor = false;
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.MAT_COLOR) {
				if (!hasLinColor) {
					color.r = reader.ReadUnsignedCharacter8 ();
					color.g = reader.ReadUnsignedCharacter8 ();
					color.b = reader.ReadUnsignedCharacter8 ();
				}
			} else if (chunkId === OV.CHUNK3DS.MAT_LIN_COLOR) {
				color.r = reader.ReadUnsignedCharacter8 ();
				color.g = reader.ReadUnsignedCharacter8 ();
				color.b = reader.ReadUnsignedCharacter8 ();
				hasLinColor = true;
			} else if (chunkId === OV.CHUNK3DS.MAT_COLOR_F) {
				if (!hasLinColor) {
					color.r = parseInt (reader.ReadFloat32 () * 255.0, 10);
					color.g = parseInt (reader.ReadFloat32 () * 255.0, 10);
					color.b = parseInt (reader.ReadFloat32 () * 255.0, 10);
				}
			} else if (chunkId === OV.CHUNK3DS.MAT_LIN_COLOR_F) {
				color.r = parseInt (reader.ReadFloat32 () * 255.0, 10);
				color.g = parseInt (reader.ReadFloat32 () * 255.0, 10);
				color.b = parseInt (reader.ReadFloat32 () * 255.0, 10);
				hasLinColor = true;
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});
		return color;
	}

	ReadPercentageChunk (reader, length)
	{
		let obj = this;
		let percentage = 0.0;
		let endByte = this.GetChunkEnd (reader, length);
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.PERCENTAGE) {
				percentage = reader.ReadUnsignedInteger16 () / 100.0;
			} else if (chunkId === OV.CHUNK3DS.PERCENTAGE_F) {
				percentage = reader.ReadFloat32 ();
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});
		return percentage;
	}

	ReadObjectChunk (reader, length)
	{
		let obj = this;
		let endByte = this.GetChunkEnd (reader, length);
		let objectName = this.ReadName (reader);
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.OBJ_TRIMESH) {
				obj.ReadMeshChunk (reader, chunkLength, objectName);
			} else if (chunkId === OV.CHUNK3DS.OBJ_LIGHT) {
				obj.SkipChunk (reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.OBJ_CAMERA) {
				obj.SkipChunk (reader, chunkLength);
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});
	}

	ReadMeshChunk (reader, length, objectName)
	{
		let obj = this;

		let mesh = new OV.Mesh ();
		mesh.SetName (objectName);

		let endByte = this.GetChunkEnd (reader, length);
		let transformation = null;
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.TRI_VERTEX) {
				obj.ReadVerticesChunk (mesh, reader);
			} else if (chunkId === OV.CHUNK3DS.TRI_TEXVERTEX) {
				obj.ReadTextureVerticesChunk (mesh, reader);
			} else if (chunkId === OV.CHUNK3DS.TRI_FACE) {
				obj.ReadFacesChunk (mesh, reader, chunkLength);
			} else if (chunkId === OV.CHUNK3DS.TRI_TRANSFORMATION) {
				transformation = obj.ReadTransformationChunk (reader);
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});

		if (mesh.VertexCount () === mesh.TextureUVCount ()) {
			for (let i = 0; i < mesh.TriangleCount (); i++) {
				let triangle = mesh.GetTriangle (i);
				triangle.SetTextureUVs (
					triangle.v0,
					triangle.v1,
					triangle.v2
				);
			}
		}

		let meshIndex = this.model.AddMesh (mesh);
		this.meshNameToIndex[mesh.GetName ()] = meshIndex;
		this.meshTransformations.push (new OV.Matrix (transformation));
	}

	ReadVerticesChunk (mesh, reader)
	{
		let vertexCount = reader.ReadUnsignedInteger16 ();
		for (let i = 0; i < vertexCount; i++) {
			let x = reader.ReadFloat32 ();
			let y = reader.ReadFloat32 ();
			let z = reader.ReadFloat32 ();
			mesh.AddVertex (new OV.Coord3D (x, y, z));
		}
	}

	ReadTextureVerticesChunk (mesh, reader)
	{
		let texVertexCount = reader.ReadUnsignedInteger16 ();
		for (let i = 0; i < texVertexCount; i++) {
			let x = reader.ReadFloat32 ();
			let y = reader.ReadFloat32 ();
			mesh.AddTextureUV (new OV.Coord2D (x, y));
		}
	}

	ReadFacesChunk (mesh, reader, length)
	{
		let endByte = this.GetChunkEnd (reader, length);
		let faceCount = reader.ReadUnsignedInteger16 ();
		for (let i = 0; i < faceCount; i++) {
			let v0 = reader.ReadUnsignedInteger16 ();
			let v1 = reader.ReadUnsignedInteger16 ();
			let v2 = reader.ReadUnsignedInteger16 ();
			reader.ReadUnsignedInteger16 (); // flags
			mesh.AddTriangle (new OV.Triangle (v0, v1, v2));
		}

		let obj = this;
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.TRI_MATERIAL) {
				obj.ReadFaceMaterialsChunk (mesh, reader);
			} else if (chunkId === OV.CHUNK3DS.TRI_SMOOTH) {
				obj.ReadFaceSmoothingGroupsChunk (mesh, faceCount, reader);
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});
	}

	ReadFaceMaterialsChunk (mesh, reader)
	{
		let materialName = this.ReadName (reader);
		let materialIndex = this.materialNameToIndex[materialName];
		let faceCount = reader.ReadUnsignedInteger16 ();
		for (let i = 0; i < faceCount; i++) {
			let faceIndex = reader.ReadUnsignedInteger16 ();
			let triangle = mesh.GetTriangle (faceIndex);
			if (materialIndex !== undefined) {
				triangle.mat = materialIndex;
			}
		}
	}
	
	ReadFaceSmoothingGroupsChunk (mesh, faceCount, reader)
	{
		for (let i = 0; i < faceCount; i++) {
			let smoothingGroup = reader.ReadUnsignedInteger32 ();
			let triangle = mesh.GetTriangle (i);
			triangle.curve = smoothingGroup;
		}
	}

	ReadTransformationChunk (reader)
	{
		let matrix = [];
		for (let i = 0; i < 4; i++) {
			for (let j = 0; j < 3; j++) {
				matrix.push (reader.ReadFloat32 ());
			}
			if (i < 3) {
				matrix.push (0);
			} else {
				matrix.push (1);
			}
		}
		return matrix;
	}

	ReadKeyFrameChunk (reader, length)
	{
		let nodeHierarchy = {
			nodes : [],
			idToIndex : {},
			meshIndexToNodes : {}
		};

		let endByte = this.GetChunkEnd (reader, length);
		let obj = this;
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.OBJECT_NODE) {
				obj.ReadObjectNodeChunk (nodeHierarchy, reader, chunkLength);
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});

		this.ApplyModelTransformations (nodeHierarchy);
	}

	ApplyModelTransformations (nodeHierarchy)
	{
		function GetNodeTransformation (nodeHierarchy, node)
		{
			function GetNodePosition (node)
			{
				if (node.positions.length === 0) {
					return [0.0, 0.0, 0.0];
				}
				return node.positions[0];
			}
		
			function GetNodeRotation (node)
			{
				function GetQuaternionFromAxisAndAngle (rotation)
				{
					let result = [0.0, 0.0, 0.0, 1.0];
					let length = Math.sqrt (rotation[0] * rotation[0] + rotation[1] * rotation[1] + rotation[2] * rotation[2]);
					if (length > 0.0) {
						let omega = rotation[3] * -0.5;
						let si = Math.sin (omega) / length;
						result = [si * rotation[0], si * rotation[1], si * rotation[2], Math.cos (omega)];
					}
					return result;
				}

				if (node.rotations.length === 0) {
					return [0.0, 0.0, 0.0, 1.0];
				}
				
				let rotation = node.rotations[0];
				return GetQuaternionFromAxisAndAngle (rotation);
			}

			function GetNodeScale (node)
			{
				if (node.scales.length === 0) {
					return [1.0, 1.0, 1.0];
				}
				return node.scales[0];
			}
			
			if (node.matrix !== null) {
				return node.matrix;
			}
			
			let matrix = new OV.Matrix ();
			matrix.ComposeTRS (
				GetNodePosition (node),
				GetNodeRotation (node),
				GetNodeScale (node)
			);

			if (node.userId !== 65535) {
				let parentIndex = nodeHierarchy.idToIndex[node.userId];
				if (parentIndex !== undefined) {
					let parentNode = nodeHierarchy.nodes[parentIndex];
					let parentMatrix = GetNodeTransformation (nodeHierarchy, parentNode);
					matrix = matrix.MultiplyMatrix (parentMatrix);
				}
			}
			
			node.matrix = matrix;
			return matrix;
		}

		function ApplyMeshTransformation (model, currentMeshIndex, meshMatrix, nodeHierarchy, node)
		{
			function GetNodePivotPoint (node)
			{
				if (node === null) {
					return [0.0, 0.0, 0.0];
				}
				return node.pivot;
			}

			if (!meshMatrix.IsValid ()) {
				return;
			}

			let nodeMatrix = meshMatrix;
			if (node !== null) {
				nodeMatrix = GetNodeTransformation (nodeHierarchy, node);
			}

			let determinant = meshMatrix.Determinant ();
			if (OV.IsNegative (determinant)) {
				// Mirror by x coordinates
				let scaleMatrix = new OV.Matrix ().CreateScale (-1.0, 1.0, 1.0);
				meshMatrix = scaleMatrix.MultiplyMatrix (meshMatrix);
			}

			let invMeshMatrix = meshMatrix.Invert ();
			if (invMeshMatrix === null) {
				return;
			}
			
			let pivotPoint = GetNodePivotPoint (node);
			let pivotMatrix = new OV.Matrix ().CreateTranslation (-pivotPoint[0], -pivotPoint[1], -pivotPoint[2]);

			let matrix = nodeMatrix.Clone ();
			matrix = pivotMatrix.MultiplyMatrix (matrix);
			matrix = invMeshMatrix.MultiplyMatrix (matrix);

			let transformation = new OV.Transformation (matrix);
			let mesh = model.GetMesh (currentMeshIndex);
			OV.TransformMesh (mesh, transformation);
		}

		function AddDuplicatedMesh (model, meshIndex, toIndex)
		{
			let mesh = model.GetMesh (meshIndex);
			let clonedMesh = OV.CloneMesh (mesh);
			let clonedMeshIndex = model.AddMeshToIndex (clonedMesh, toIndex);
			return clonedMeshIndex;
		}

		let newToOldMeshIndexOffset = 0;
		for (let meshIndex = 0; meshIndex < this.model.MeshCount (); meshIndex++) {
			let currentMeshIndex = meshIndex;
			let originalMeshIndex = currentMeshIndex - newToOldMeshIndexOffset;
			let meshTransformation = this.meshTransformations[originalMeshIndex];
			let meshNodes = nodeHierarchy.meshIndexToNodes[originalMeshIndex];
			if (meshNodes === undefined) {
				ApplyMeshTransformation (this.model, currentMeshIndex, meshTransformation, nodeHierarchy, null);
			} else {
				for (let nodeIndex = 0; nodeIndex < meshNodes.length; nodeIndex++) {
					let currentNode = meshNodes[nodeIndex];
					let transformedMeshIndex = currentMeshIndex;
					if (nodeIndex > 0) {
						transformedMeshIndex = AddDuplicatedMesh (this.model, currentMeshIndex, currentMeshIndex + nodeIndex);
						newToOldMeshIndexOffset += 1;
						meshIndex += 1;
					}
					ApplyMeshTransformation (this.model, transformedMeshIndex, meshTransformation, nodeHierarchy, currentNode);
				}
			}
		}
	}

	ReadObjectNodeChunk (nodeHierarchy, reader, length)
	{
		function ReadTrackVector (obj, reader, type)
		{
			let result = [];
			reader.Skip (10);
			
			let keyNum = reader.ReadInteger32 ();
			for (let i = 0; i < keyNum; i++) {
				reader.ReadInteger32 ();
				let flags = reader.ReadUnsignedInteger16 ();
				if (flags !== 0) {
					reader.ReadFloat32 ();
				}
				
				let current = null;
				if (type === OV.CHUNK3DS.OBJECT_ROTATION) {
					let tmp = reader.ReadFloat32 ();
					current = obj.ReadVector (reader);
					current[3] = tmp;
				} else {
					current = obj.ReadVector (reader);
				}
				result.push (current);
			}

			return result;
		}
	
		let objectNode = {
			name : '',
			instanceName : '',
			nodeId : -1,
			flags : -1,
			userId : -1,
			pivot : [0.0, 0.0, 0.0],
			positions : [],
			rotations : [],
			scales : [],
			matrix : null
		};
		
		let obj = this;
		let endByte = this.GetChunkEnd (reader, length);
		this.ReadChunks (reader, endByte, function (chunkId, chunkLength) {
			if (chunkId === OV.CHUNK3DS.OBJECT_HIERARCHY) {
				objectNode.name = obj.ReadName (reader);
				objectNode.flags = reader.ReadUnsignedInteger32 ();
				objectNode.userId = reader.ReadUnsignedInteger16 ();
			} else if (chunkId === OV.CHUNK3DS.OBJECT_INSTANCE_NAME) {
				objectNode.instanceName = obj.ReadName (reader);
			} else if (chunkId === OV.CHUNK3DS.OBJECT_PIVOT) {
				objectNode.pivot = obj.ReadVector (reader);
			} else if (chunkId === OV.CHUNK3DS.OBJECT_POSITION) {
				objectNode.positions = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_POSITION);
			} else if (chunkId === OV.CHUNK3DS.OBJECT_ROTATION) {
				objectNode.rotations = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_ROTATION);
			} else if (chunkId === OV.CHUNK3DS.OBJECT_SCALE) {
				objectNode.scales = ReadTrackVector (obj, reader, OV.CHUNK3DS.OBJECT_SCALE);
			} else if (chunkId === OV.CHUNK3DS.OBJECT_ID) {
				objectNode.nodeId = reader.ReadUnsignedInteger16 ();
			} else {
				obj.SkipChunk (reader, chunkLength);
			}
		});

		let nodeIndex = nodeHierarchy.nodes.length;
		nodeHierarchy.nodes.push (objectNode);
		nodeHierarchy.idToIndex[objectNode.nodeId] = nodeIndex;

		let meshIndex = this.meshNameToIndex[objectNode.name];
		if (meshIndex !== undefined) {
			let meshNodes = nodeHierarchy.meshIndexToNodes[meshIndex];
			if (meshNodes === undefined) {
				nodeHierarchy.meshIndexToNodes[meshIndex] = [];
			}
			nodeHierarchy.meshIndexToNodes[meshIndex].push (objectNode);
		}
	}	

	ReadName (reader)
	{
		let name = '';
		let char = 0;
		let count = 0;
		while (count < 64) {
			char = reader.ReadCharacter8 ();
			if (char === 0) {
				break;
			}
			name = name + String.fromCharCode (char);
			count = count + 1;
		}
		return name;
	}

	ReadVector (reader)
	{
		let result = [
			reader.ReadFloat32 (),
			reader.ReadFloat32 (),
			reader.ReadFloat32 ()
		];
		return result;
	}

	ReadChunks (reader, endByte, onChunk)
	{
		while (reader.GetPosition () <= endByte - 6) {
			let chunkId = reader.ReadUnsignedInteger16 ();
			let chunkLength = reader.ReadUnsignedInteger32 ();
			onChunk (chunkId, chunkLength);
		}
	}
	
	GetChunkEnd (reader, length)
	{
		return reader.GetPosition () + length - 6;
	}

	SkipChunk (reader, length)
	{
		reader.Skip (length - 6);
	}	
};
