Beispiel #1
0
        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;
            }));
        }
Beispiel #2
0
        internal static W3dRgbaList Parse(BinaryReader reader, W3dParseContext context, W3dChunkType chunkType)
        {
            var result = ParseList(reader, context, W3dRgba.Parse);

            result._chunkType = chunkType;
            return(result);
        }
Beispiel #3
0
        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;
            }));
        }
Beispiel #4
0
        internal static W3dVector3List Parse(BinaryReader reader, W3dParseContext context, W3dChunkType chunkType)
        {
            var result = ParseList(reader, context, r => r.ReadVector3());

            result._chunkType = chunkType;
            return(result);
        }
Beispiel #5
0
        internal static W3dTexture Parse(BinaryReader reader, W3dParseContext context)
        {
            return(ParseChunk(reader, context, header =>
            {
                var result = new W3dTexture();

                ParseChunks(reader, context.CurrentEndPosition, chunkType =>
                {
                    switch (chunkType)
                    {
                    case W3dChunkType.W3D_CHUNK_TEXTURE_NAME:
                        result.Name = W3dTextureName.Parse(reader, context);
                        break;

                    case W3dChunkType.W3D_CHUNK_TEXTURE_INFO:
                        result.TextureInfo = W3dTextureInfo.Parse(reader, context);
                        break;

                    default:
                        throw CreateUnknownChunkException(chunkType);
                    }
                });

                return result;
            }));
        }
Beispiel #6
0
        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;

                    default:
                        throw CreateUnknownChunkException(chunkType);
                    }
                });

                return result;
            }));
        }
Beispiel #7
0
        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;
            }));
        }
Beispiel #8
0
        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 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;
            }));
        }
Beispiel #10
0
        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 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;
            }));
        }
Beispiel #12
0
        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;
            }));
        }
Beispiel #13
0
        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;
            }));
        }
Beispiel #14
0
        }                                                               // 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()
                };
            }));
        }
Beispiel #15
0
        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 = W3dRgba.Parse(reader),
                    EndColor = W3dRgba.Parse(reader)
                };
            }));
        }
Beispiel #16
0
        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);
        }
Beispiel #17
0
        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;
            }));
        }
Beispiel #18
0
        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;
            }));
        }
Beispiel #19
0
 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 W3dVertexMaterialName Parse(BinaryReader reader, W3dParseContext context)
 {
     return(ParseChunk(reader, context, header =>
     {
         return new W3dVertexMaterialName
         {
             Value = reader.ReadFixedLengthString((int)header.ChunkSize)
         };
     }));
 }
Beispiel #21
0
 internal static W3dEmitterExtraInfo Parse(BinaryReader reader, W3dParseContext context)
 {
     return(ParseChunk(reader, context, header =>
     {
         return new W3dEmitterExtraInfo
         {
             Unknown = reader.ReadBytes((int)header.ChunkSize)
         };
     }));
 }
Beispiel #22
0
 internal static W3dMeshUserText Parse(BinaryReader reader, W3dParseContext context)
 {
     return(ParseChunk(reader, context, header =>
     {
         return new W3dMeshUserText
         {
             Value = reader.ReadFixedLengthString((int)header.ChunkSize)
         };
     }));
 }
Beispiel #23
0
 internal static W3dHLodArrayHeader Parse(BinaryReader reader, W3dParseContext context)
 {
     return(ParseChunk(reader, context, header =>
     {
         return new W3dHLodArrayHeader
         {
             ModelCount = reader.ReadUInt32(),
             MaxScreenSize = reader.ReadSingle()
         };
     }));
 }
Beispiel #24
0
 internal static W3dEmitterHeader Parse(BinaryReader reader, W3dParseContext context)
 {
     return(ParseChunk(reader, context, header =>
     {
         return new W3dEmitterHeader
         {
             Version = reader.ReadUInt32(),
             Name = reader.ReadFixedLengthString(W3dConstants.NameLength)
         };
     }));
 }
 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)
         };
     });
 }
Beispiel #26
0
 internal static TList ParseList(BinaryReader reader, W3dParseContext context, Func <BinaryReader, TItem> parseItem)
 {
     return(ParseChunk(reader, context, header =>
     {
         var result = new TList();
         while (reader.BaseStream.Position < context.CurrentEndPosition)
         {
             result.Items.Add(parseItem(reader));
         }
         return result;
     }));
 }
Beispiel #27
0
        internal static W3dMaterialPass Parse(BinaryReader reader, W3dParseContext context)
        {
            return(ParseChunk(reader, context, header =>
            {
                var result = new W3dMaterialPass();

                ParseChunks(reader, context.CurrentEndPosition, chunkType =>
                {
                    switch (chunkType)
                    {
                    case W3dChunkType.W3D_CHUNK_VERTEX_MATERIAL_IDS:
                        result.VertexMaterialIds = W3dUInt32List.Parse(reader, context, chunkType);
                        break;

                    case W3dChunkType.W3D_CHUNK_SHADER_IDS:
                        result.ShaderIds = W3dUInt32List.Parse(reader, context, chunkType);
                        break;

                    case W3dChunkType.W3D_CHUNK_DCG:
                        result.Dcg = W3dRgbaList.Parse(reader, context, chunkType);
                        break;

                    case W3dChunkType.W3D_CHUNK_DIG:
                        result.Dig = W3dRgbaList.Parse(reader, context, chunkType);
                        break;

                    case W3dChunkType.W3D_CHUNK_SCG:
                        result.Scg = W3dRgbaList.Parse(reader, context, chunkType);
                        break;

                    case W3dChunkType.W3D_CHUNK_TEXTURE_STAGE:
                        result.TextureStages.Add(W3dTextureStage.Parse(reader, context));
                        break;

                    case W3dChunkType.W3D_CHUNK_SHADER_MATERIAL_ID:
                        result.ShaderMaterialIds = W3dUInt32List.Parse(reader, context, chunkType);
                        break;

                    // Normally this appears inside W3dTextureStage, but it can also
                    // appear directly under W3dMaterialPass if using shader materials.
                    case W3dChunkType.W3D_CHUNK_STAGE_TEXCOORDS:
                        result.TexCoords = W3dVector2List.Parse(reader, context, chunkType);
                        break;

                    default:
                        throw CreateUnknownChunkException(chunkType);
                    }
                });

                return result;
            }));
        }
Beispiel #28
0
        private static W3dFile Parse(BinaryReader reader, string filePath)
        {
            var context = new W3dParseContext();

            context.PushChunk(nameof(W3dFile), reader.BaseStream.Length);

            var result = new W3dFile
            {
                FilePath = filePath
            };

            W3dContainerChunk.ParseChunks(reader, reader.BaseStream.Length, chunkType =>
            {
                switch (chunkType)
                {
                case W3dChunkType.W3D_CHUNK_MESH:
                    result.Chunks.Add(W3dMesh.Parse(reader, context));
                    break;

                case W3dChunkType.W3D_CHUNK_BOX:
                    result.Chunks.Add(W3dBox.Parse(reader, context));
                    break;

                case W3dChunkType.W3D_CHUNK_HIERARCHY:
                    result.Chunks.Add(W3dHierarchyDef.Parse(reader, context));
                    break;

                case W3dChunkType.W3D_CHUNK_HLOD:
                    result.Chunks.Add(W3dHLod.Parse(reader, context));
                    break;

                case W3dChunkType.W3D_CHUNK_ANIMATION:
                    result.Chunks.Add(W3dAnimation.Parse(reader, context));
                    break;

                case W3dChunkType.W3D_CHUNK_COMPRESSED_ANIMATION:
                    result.Chunks.Add(W3dCompressedAnimation.Parse(reader, context));
                    break;

                case W3dChunkType.W3D_CHUNK_EMITTER:
                    result.Chunks.Add(W3dEmitter.Parse(reader, context));
                    break;

                default:
                    throw W3dContainerChunk.CreateUnknownChunkException(chunkType);
                }
            });

            context.PopAsset();

            return(result);
        }
Beispiel #29
0
 internal static W3dMaterialInfo Parse(BinaryReader reader, W3dParseContext context)
 {
     return(ParseChunk(reader, context, header =>
     {
         return new W3dMaterialInfo
         {
             PassCount = reader.ReadUInt32(),
             VertexMaterialCount = reader.ReadUInt32(),
             ShaderCount = reader.ReadUInt32(),
             TextureCount = reader.ReadUInt32()
         };
     }));
 }
Beispiel #30
0
 internal static W3dTextureInfo Parse(BinaryReader reader, W3dParseContext context)
 {
     return(ParseChunk(reader, context, header =>
     {
         return new W3dTextureInfo
         {
             Attributes = reader.ReadUInt16AsEnumFlags <W3dTextureFlags>(),
             AnimationType = reader.ReadUInt16AsEnum <W3dTextureAnimation>(),
             FrameCount = reader.ReadUInt32(),
             FrameRate = reader.ReadSingle(),
         };
     }));
 }