internal static T ParseChunk <T>( BinaryReader reader, W3dParseContext context, Func <W3dChunkHeader, T> parseCallback) where T : W3dChunk { var chunkHeader = W3dChunkHeader.Parse(reader); var startPosition = reader.BaseStream.Position; var endPosition = startPosition + chunkHeader.ChunkSize; context.PushChunk(typeof(T).Name, endPosition); var result = parseCallback(chunkHeader); result.StartPosition = startPosition; result.EndPosition = endPosition; context.PopAsset(); if (reader.BaseStream.Position != endPosition) { throw new InvalidDataException($"Error while parsing asset '{typeof(T).Name}'. Expected reader to be at position {endPosition}, but was at {reader.BaseStream.Position}."); } return(result); }
internal static W3dMeshAabTree Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk <W3dMeshAabTree>(reader, context, header => { var result = new W3dMeshAabTree(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_AABTREE_HEADER: result.Header = W3dMeshAabTreeHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_AABTREE_POLYINDICES: result.PolygonIndices = W3dMeshAabTreePolyIndices.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_AABTREE_NODES: result.Nodes = W3dMeshAabTreeNodes.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dMorphAnimation Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dMorphAnimation(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_MORPHANIM_HEADER: result.Header = W3dMorphAnimHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_MORPHANIM_CHANNEL: result.AnimChannel = W3dMorphAnimChannel.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_MORPHANIM_PIVOTCHANNELDATA: result.PivotChannelData = W3dMorphAnimPivotChannelData.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dSoundRObjDefinition Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dSoundRObjDefinition { UnknownBytes = reader.ReadBytes((int)context.CurrentEndPosition - (int)reader.BaseStream.Position) }; // TODO: Determine W3dSoundRObjDefinition Chunk Structure (Currently Unknown) /* * var chunkA = reader.ReadUInt32() >> 8; * var chunkASize = reader.ReadUInt32(); * var chunkAArray = reader.ReadBytes((int) chunkASize); * * var Flag2 = reader.ReadUInt32() >> 8; * var tmp = reader.ReadBytes(4); // unknown * * var chunkB = reader.ReadUInt32() >> 8; * var chunkBSize = reader.ReadUInt32(); * var chunkBArray = reader.ReadBytes((int) chunkBSize); */ return result; })); }
internal static W3dAdaptiveDeltaAnimationChannel Parse(BinaryReader reader, W3dParseContext context, W3dAdaptiveDeltaBitCount bitCount) { return ParseChunk(reader, context, header => { var result = new W3dAdaptiveDeltaAnimationChannel { NumTimeCodes = reader.ReadUInt32(), Pivot = reader.ReadUInt16(), VectorLength = reader.ReadByte(), ChannelType = reader.ReadByteAsEnum<W3dAnimationChannelType>(), Scale = reader.ReadSingle(), }; W3dAnimationChannel.ValidateChannelDataSize(result.ChannelType, result.VectorLength); result.Data = W3dAdaptiveDeltaData.Parse( reader, result.NumTimeCodes, result.ChannelType, result.VectorLength, bitCount); // Skip 3 unknown bytes at chunk end. reader.BaseStream.Seek(3, SeekOrigin.Current); return result; }); }
internal static W3dHModelAuxData Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dHModelAuxData { Attributes = reader.ReadUInt32(), MeshCount = reader.ReadUInt32(), CollisionCount = reader.ReadUInt32(), SkinCount = reader.ReadUInt32(), FutureCounts = new uint[8] }; for (int i = 0; i < 8; i++) { result.FutureCounts[i] = reader.ReadUInt32(); } result.LodMin = reader.ReadSingle(); result.LodMax = reader.ReadSingle(); result.FutureUse = reader.ReadBytes((int)context.CurrentEndPosition - (int)reader.BaseStream.Position); // TODO: Determine If FutureUse are used anywhere or are different between games? return result; })); }
internal static W3dEmitterInfo Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { return new W3dEmitterInfo { TextureFileName = reader.ReadFixedLengthString(TextureFileNameLength), StartSize = reader.ReadSingle(), EndSize = reader.ReadSingle(), Lifetime = reader.ReadSingle(), EmissionRate = reader.ReadSingle(), MaxEmissions = reader.ReadSingle(), VelocityRandom = reader.ReadSingle(), PositionRandom = reader.ReadSingle(), FadeTime = reader.ReadSingle(), Gravity = reader.ReadSingle(), Elasticity = reader.ReadSingle(), Velocity = reader.ReadVector3(), Acceleration = reader.ReadVector3(), StartColor = reader.ReadColorRgba(), EndColor = reader.ReadColorRgba() }; })); }
internal static W3dHierarchyDef Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dHierarchyDef(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_HIERARCHY_HEADER: result.Header = W3dHierarchy.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_PIVOTS: result.Pivots = W3dPivots.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_PIVOT_FIXUPS: result.PivotFixups = W3dPivotFixups.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dTimeCodedAnimationChannel Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dTimeCodedAnimationChannel { NumTimeCodes = reader.ReadUInt32(), Pivot = reader.ReadUInt16(), VectorLength = reader.ReadByte(), ChannelType = reader.ReadByteAsEnum <W3dAnimationChannelType>() }; W3dAnimationChannel.ValidateChannelDataSize(result.ChannelType, result.VectorLength); var data = new W3dTimeCodedDatum[result.NumTimeCodes]; for (var i = 0; i < result.NumTimeCodes; i++) { data[i] = W3dTimeCodedDatum.Parse(reader, result.ChannelType); } result.Data = data; return result; })); }
internal static W3dAnimation Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dAnimation(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_ANIMATION_HEADER: result.Header = W3dAnimationHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_ANIMATION_CHANNEL: result.Channels.Add(W3dAnimationChannel.Parse(reader, context)); break; case W3dChunkType.W3D_CHUNK_BIT_CHANNEL: result.Channels.Add(W3dBitChannel.Parse(reader, context)); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
} // Bounding sphere radius internal static W3dMeshHeader3 Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { return new W3dMeshHeader3 { Version = reader.ReadUInt32(), Attributes = reader.ReadUInt32AsEnumFlags <W3dMeshFlags>(), MeshName = reader.ReadFixedLengthString(W3dConstants.NameLength), ContainerName = reader.ReadFixedLengthString(W3dConstants.NameLength), NumTris = reader.ReadUInt32(), NumVertices = reader.ReadUInt32(), NumMaterials = reader.ReadUInt32(), NumDamageStages = reader.ReadUInt32(), SortLevel = reader.ReadUInt32(), PrelitVersion = reader.ReadUInt32(), FutureCounts = reader.ReadUInt32(), VertexChannels = reader.ReadUInt32AsEnumFlags <W3dVertexChannels>(), FaceChannels = reader.ReadUInt32AsEnum <W3dFaceChannels>(), Min = reader.ReadVector3(), Max = reader.ReadVector3(), SphCenter = reader.ReadVector3(), SphRadius = reader.ReadSingle() }; })); }
internal static W3dVertexMaterial Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dVertexMaterial(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_VERTEX_MATERIAL_NAME: result.Name = W3dVertexMaterialName.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_VERTEX_MAPPER_ARGS0: result.MapperArgs0 = W3dVertexMapperArgs.Parse(reader, context, chunkType); break; case W3dChunkType.W3D_CHUNK_VERTEX_MAPPER_ARGS1: result.MapperArgs1 = W3dVertexMapperArgs.Parse(reader, context, chunkType); break; case W3dChunkType.W3D_CHUNK_VERTEX_MATERIAL_INFO: result.Info = W3dVertexMaterialInfo.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dCollection Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dCollection(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_COLLECTION_HEADER: result.Header = W3dCollectionHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_COLLECTION_OBJ_NAME: result.ObjName = W3dCollectionObjName.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_PLACEHOLDER: result.Placeholder = W3dCollectionPlaceholder.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_TRANSFORM_NODE: result.TransformNode = W3dCollectionTransformNode.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dAnimationChannel Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var startPosition = reader.BaseStream.Position; var result = new W3dAnimationChannel { FirstFrame = reader.ReadUInt16(), LastFrame = reader.ReadUInt16(), VectorLength = reader.ReadUInt16(), ChannelType = reader.ReadUInt16AsEnum <W3dAnimationChannelType>(), Pivot = reader.ReadUInt16(), Unknown = reader.ReadUInt16() }; ValidateChannelDataSize(result.ChannelType, result.VectorLength); var numElements = result.LastFrame - result.FirstFrame + 1; var data = new W3dAnimationChannelDatum[numElements]; for (var i = 0; i < numElements; i++) { data[i] = W3dAnimationChannelDatum.Parse(reader, result.ChannelType); } result.Data = data; result.NumPadBytes = (uint)(context.CurrentEndPosition - reader.BaseStream.Position); reader.BaseStream.Seek((int)result.NumPadBytes, SeekOrigin.Current); return result; })); }
internal static W3dMorphAnimChannel Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dMorphAnimChannel(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_MORPHANIM_POSENAME: result.PoseName = W3dMorphAnimPoseName.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_MORPHANIM_KEYDATA: result.KeyData = W3dMorphAnimKeyData.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dShaderMaterial Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dShaderMaterial(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_SHADER_MATERIAL_HEADER: result.Header = W3dShaderMaterialHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_SHADER_MATERIAL_PROPERTY: result.Properties.Add(W3dShaderMaterialProperty.Parse(reader, context)); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dSoundRObj Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dSoundRObj(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_SOUNDROBJ_HEADER: result.Header = W3dSoundRObjHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_SOUNDROBJ_DEFINITION: result.Definition = W3dSoundRObjDefinition.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dTextureStage Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dTextureStage(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_TEXTURE_IDS: result.TextureIds = W3dTextureIds.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_PER_FACE_TEXCOORD_IDS: result.PerFaceTexCoordIds = new W3dVectorUInt32[header.ChunkSize / W3dVectorUInt32.SizeInBytes]; for (var count = 0; count < result.PerFaceTexCoordIds.Length; count++) { result.PerFaceTexCoordIds[count] = W3dVectorUInt32.Parse(reader); } break; case W3dChunkType.W3D_CHUNK_STAGE_TEXCOORDS: result.TexCoords = W3dVector2List.Parse(reader, context, chunkType); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dDazzle Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dDazzle(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_DAZZLE_NAME: result.Name = W3dDazzleName.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_DAZZLE_TYPENAME: result.TypeName = W3dDazzleTypeName.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dVector3List Parse(BinaryReader reader, W3dParseContext context, W3dChunkType chunkType) { var result = ParseList(reader, context, r => r.ReadVector3()); result._chunkType = chunkType; return(result); }
internal static TDerived Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new TDerived(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_HLOD_SUB_OBJECT_ARRAY_HEADER: result.Header = W3dHLodArrayHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_HLOD_SUB_OBJECT: result.SubObjects.Add(W3dHLodSubObject.Parse(reader, context)); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dHLod Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dHLod(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_HLOD_HEADER: result.Header = W3dHLodHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_HLOD_LOD_ARRAY: result.Lods.Add(W3dHLodArray.Parse(reader, context)); break; case W3dChunkType.W3D_CHUNK_HLOD_AGGREGATE_ARRAY: result.Aggregate = W3dHLodAggregateArray.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_HLOD_PROXY_ARRAY: result.Proxy = W3dHLodProxyArray.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dEmitter Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { var result = new W3dEmitter(); ParseChunks(reader, context.CurrentEndPosition, chunkType => { switch (chunkType) { case W3dChunkType.W3D_CHUNK_EMITTER_HEADER: result.Header = W3dEmitterHeader.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_USER_DATA: result.UserData = W3dEmitterUserData.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_INFO: result.Info = W3dEmitterInfo.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_INFOV2: result.InfoV2 = W3dEmitterInfoV2.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_PROPS: result.Properties = W3dEmitterProperties.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_ROTATION_KEYFRAMES: result.RotationKeyframes = W3dEmitterRotationKeyframes.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_FRAME_KEYFRAMES: result.FrameKeyframes = W3dEmitterFrameKeyframes.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_LINE_PROPERTIES: result.LineProperties = W3dEmitterLineProperties.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_BLUR_TIME_KEYFRAMES: result.BlurTimeKeyframes = W3dEmitterBlurTimeKeyframes.Parse(reader, context); break; case W3dChunkType.W3D_CHUNK_EMITTER_EXTRA_INFO: result.ExtraInfo = W3dEmitterExtraInfo.Parse(reader, context); break; default: throw CreateUnknownChunkException(chunkType); } }); return result; })); }
internal static W3dEmitterExtraInfo Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { return new W3dEmitterExtraInfo { Unknown = reader.ReadBytes((int)header.ChunkSize) }; })); }
internal static W3dTextureIds Parse(BinaryReader reader, W3dParseContext context) { return(ParseList(reader, context, r => { var textureId = r.ReadInt32(); return textureId != -1 ? (uint)textureId : (uint?)null; })); }
internal static W3dMeshUserText Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { return new W3dMeshUserText { Value = reader.ReadFixedLengthString((int)header.ChunkSize) }; })); }
internal static W3dVertexMaterialName Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { return new W3dVertexMaterialName { Value = reader.ReadFixedLengthString((int)header.ChunkSize) }; })); }
internal static W3dHLodSubObject Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { return new W3dHLodSubObject { BoneIndex = reader.ReadUInt32(), Name = reader.ReadFixedLengthString(W3dConstants.NameLength * 2) }; })); }
internal static W3dHLodArrayHeader Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { return new W3dHLodArrayHeader { ModelCount = reader.ReadUInt32(), MaxScreenSize = reader.ReadSingle() }; })); }
internal static W3dEmitterHeader Parse(BinaryReader reader, W3dParseContext context) { return(ParseChunk(reader, context, header => { return new W3dEmitterHeader { Version = reader.ReadUInt32(), Name = reader.ReadFixedLengthString(W3dConstants.NameLength) }; })); }