예제 #1
0
        public override void Read(BinaryReader reader, Resource resource)
        {
            // NTRO only in version 0?
            if (resource.IntrospectionManifest == null)
            {
                var block = new ResourceIntrospectionManifest.ResourceDiskStruct();

                var field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName = "m_bitpackedsoundinfo",
                    Type      = DataType.UInt32,
                };
                block.FieldIntrospection.Add(field);

                field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName    = "m_loopStart",
                    Type         = DataType.Int32,
                    OnDiskOffset = 4,
                };
                block.FieldIntrospection.Add(field);

                field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName    = "m_flDuration",
                    Type         = DataType.Float,
                    OnDiskOffset = 12,
                };
                block.FieldIntrospection.Add(field);

                resource.Blocks[BlockType.NTRO] = new ResourceIntrospectionManifest();
                resource.IntrospectionManifest.ReferencedStructs.Add(block);
            }

            reader.BaseStream.Position = Offset;
            base.Read(reader, resource);

            LoopStart = ((NTROValue <int>)Output["m_loopStart"]).Value;
            Duration  = ((NTROValue <float>)Output["m_flDuration"]).Value;

            var bitpackedSoundInfo = ((NTROValue <uint>)Output["m_bitpackedsoundinfo"]).Value;

            Type        = (AudioFileType)ExtractSub(bitpackedSoundInfo, 0, 2);
            Bits        = ExtractSub(bitpackedSoundInfo, 2, 5);
            Channels    = ExtractSub(bitpackedSoundInfo, 7, 2);
            SampleSize  = ExtractSub(bitpackedSoundInfo, 9, 3);
            AudioFormat = ExtractSub(bitpackedSoundInfo, 12, 2);
            SampleRate  = ExtractSub(bitpackedSoundInfo, 14, 17);

            if (Type > AudioFileType.MP3)
            {
                throw new NotImplementedException($"Unknown audio file format '{Type}', please report this on GitHub.");
            }
        }
예제 #2
0
        private const int SIGNATURE = 55987030; // "VKV\x03" aka valve keyvalue, version 3

        public override void Read(BinaryReader reader, Resource resource)
        {
            if (resource.IntrospectionManifest == null)
            {
                var block = new ResourceIntrospectionManifest.ResourceDiskStruct();

                var field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName = "m_Signature",
                    Count     = 1,
                    Type      = DataType.Int32
                };
                block.FieldIntrospection.Add(field);

                field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName    = "m_Encoding",
                    Count        = 4,
                    OnDiskOffset = 4,
                    Type         = DataType.Boolean
                };
                block.FieldIntrospection.Add(field);

                field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName    = "m_Format",
                    Count        = 4,
                    OnDiskOffset = 20,
                    Type         = DataType.Boolean
                };
                block.FieldIntrospection.Add(field);

                resource.Blocks[BlockType.NTRO] = new ResourceIntrospectionManifest();
                resource.IntrospectionManifest.ReferencedStructs.Add(block);
            }

            base.Read(reader, resource);

            reader.BaseStream.Position = Offset;

            // TODO: Use parsed NTRO data
            if (reader.ReadUInt32() != SIGNATURE)
            {
                throw new InvalidDataException("Wrong signature.");
            }

            reader.BaseStream.Position += 32; // encoding + format (guids?)

            // TODO
        }
예제 #3
0
        private void ReadFieldIntrospection(ResourceIntrospectionManifest.ResourceDiskStruct.Field field, ref NTROStruct structEntry)
        {
            var count   = (uint)field.Count;
            var pointer = false; // TODO: get rid of this

            if (count == 0)
            {
                count = 1;
            }

            long prevOffset = 0;

            if (field.Indirections.Count > 0)
            {
                // TODO
                if (field.Indirections.Count > 1)
                {
                    throw new NotImplementedException("More than one indirection, not yet handled.");
                }

                // TODO
                if (field.Count > 0)
                {
                    throw new NotImplementedException("Indirection.Count > 0 && field.Count > 0");
                }

                var indirection = field.Indirections[0]; // TODO: depth needs fixing?

                var offset = Reader.ReadUInt32();

                if (indirection == 0x03)
                {
                    pointer = true;

                    if (offset == 0)
                    {
                        structEntry.Add(field.FieldName, new NTROValue <byte?>(field.Type, null, true)); //being byte shouldn't matter

                        return;
                    }

                    prevOffset = Reader.BaseStream.Position;

                    Reader.BaseStream.Position += offset - 4;
                }
                else if (indirection == 0x04)
                {
                    count = Reader.ReadUInt32();

                    prevOffset = Reader.BaseStream.Position;

                    if (count > 0)
                    {
                        Reader.BaseStream.Position += offset - 8;
                    }
                }
                else
                {
                    throw new NotImplementedException(string.Format("Unknown indirection. ({0})", indirection));
                }
            }

            //if (pointer)
            //{
            //    Writer.Write("{0} {1}* = (ptr) ->", ValveDataType(field.Type), field.FieldName);
            //}
            if (field.Count > 0 || field.Indirections.Count > 0)
            {
                var ntroValues = new NTROArray(field.Type, (int)count, pointer, field.Indirections.Count > 0);

                for (var i = 0; i < count; i++)
                {
                    ntroValues[i] = ReadField(field, pointer);
                }

                structEntry.Add(field.FieldName, ntroValues);
            }
            else
            {
                for (var i = 0; i < count; i++)
                {
                    structEntry.Add(field.FieldName, ReadField(field, pointer));
                }
            }

            if (prevOffset > 0)
            {
                Reader.BaseStream.Position = prevOffset;
            }
        }
예제 #4
0
        private NTROValue ReadField(ResourceIntrospectionManifest.ResourceDiskStruct.Field field, bool pointer)
        {
            switch (field.Type)
            {
            case DataType.Struct:
                var newStruct = Resource.IntrospectionManifest.ReferencedStructs.First(x => x.Id == field.TypeData);
                return(new NTROValue <NTROStruct>(field.Type, ReadStructure(newStruct, Reader.BaseStream.Position), pointer));

            case DataType.Enum:
                // TODO: Lookup in ReferencedEnums
                return(new NTROValue <uint>(field.Type, Reader.ReadUInt32(), pointer));

            case DataType.SByte:
                return(new NTROValue <sbyte>(field.Type, Reader.ReadSByte(), pointer));

            case DataType.Byte:
                return(new NTROValue <byte>(field.Type, Reader.ReadByte(), pointer));

            case DataType.Boolean:
                return(new NTROValue <bool>(field.Type, Reader.ReadByte() == 1 ? true : false, pointer));

            case DataType.Int16:
                return(new NTROValue <short>(field.Type, Reader.ReadInt16(), pointer));

            case DataType.UInt16:
                return(new NTROValue <ushort>(field.Type, Reader.ReadUInt16(), pointer));

            case DataType.Int32:
                return(new NTROValue <int>(field.Type, Reader.ReadInt32(), pointer));

            case DataType.UInt32:
                return(new NTROValue <uint>(field.Type, Reader.ReadUInt32(), pointer));

            case DataType.Float:
                return(new NTROValue <float>(field.Type, Reader.ReadSingle(), pointer));

            case DataType.Int64:
                return(new NTROValue <long>(field.Type, Reader.ReadInt64(), pointer));

            case DataType.ExternalReference:
                var id    = Reader.ReadUInt64();
                var value = id > 0
                        ? Resource.ExternalReferences?.ResourceRefInfoList.FirstOrDefault(c => c.Id == id)
                        : null;

                return(new NTROValue <ResourceExtRefList.ResourceReferenceInfo>(field.Type, value, pointer));

            case DataType.UInt64:
                return(new NTROValue <ulong>(field.Type, Reader.ReadUInt64(), pointer));

            case DataType.Vector:
                var vector3 = new Vector3(
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle());

                return(new NTROValue <Vector3>(field.Type, vector3, pointer));

            case DataType.Quaternion:
            case DataType.Color:
            case DataType.Fltx4:
            case DataType.Vector4D:
            case DataType.Vector4D_44:
                var vector4 = new Vector4(
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle());

                return(new NTROValue <Vector4>(field.Type, vector4, pointer));

            case DataType.String4:
            case DataType.String:
                return(new NTROValue <string>(field.Type, Reader.ReadOffsetString(Encoding.UTF8), pointer));

            case DataType.Matrix3x4:
            case DataType.Matrix3x4a:
                var matrix3x4a = new Matrix3x4(
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle());

                return(new NTROValue <Matrix3x4>(field.Type, matrix3x4a, pointer));

            case DataType.CTransform:
                var transform = new CTransform(
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle());

                return(new NTROValue <CTransform>(field.Type, transform, pointer));

            default:
                throw new NotImplementedException($"Unknown data type: {field.Type} (name: {field.FieldName})");
            }
        }
예제 #5
0
        private NTROSerialization.NTROValue ReadField(ResourceIntrospectionManifest.ResourceDiskStruct.Field field, bool pointer)
        {
            switch (field.Type)
            {
            case DataType.Struct:
                var newStruct = Resource.IntrospectionManifest.ReferencedStructs.First(x => x.Id == field.TypeData);
                return(new NTROSerialization.NTROValue <NTROSerialization.NTROStruct>(field.Type, ReadStructure(newStruct, Reader.BaseStream.Position), pointer));

            case DataType.Enum:
                // TODO: Lookup in ReferencedEnums
                return(new NTROSerialization.NTROValue <uint>(field.Type, Reader.ReadUInt32(), pointer));

            case DataType.SByte:
                return(new NTROSerialization.NTROValue <sbyte>(field.Type, Reader.ReadSByte(), pointer));

            case DataType.Byte:
                return(new NTROSerialization.NTROValue <byte>(field.Type, Reader.ReadByte(), pointer));

            case DataType.Boolean:
                return(new NTROSerialization.NTROValue <bool>(field.Type, Reader.ReadByte() == 1 ? true : false, pointer));

            case DataType.Int16:
                return(new NTROSerialization.NTROValue <short>(field.Type, Reader.ReadInt16(), pointer));

            case DataType.UInt16:
                return(new NTROSerialization.NTROValue <ushort>(field.Type, Reader.ReadUInt16(), pointer));

            case DataType.Int32:
                return(new NTROSerialization.NTROValue <int>(field.Type, Reader.ReadInt32(), pointer));

            case DataType.UInt32:
                return(new NTROSerialization.NTROValue <uint>(field.Type, Reader.ReadUInt32(), pointer));

            case DataType.Float:
                return(new NTROSerialization.NTROValue <float>(field.Type, Reader.ReadSingle(), pointer));

            case DataType.Int64:
                return(new NTROSerialization.NTROValue <long>(field.Type, Reader.ReadInt64(), pointer));

            case DataType.ExternalReference:     // Handled elsewhere
            case DataType.UInt64:
                return(new NTROSerialization.NTROValue <ulong>(field.Type, Reader.ReadUInt64(), pointer));

            case DataType.Vector:
                var vector3 = new NTROSerialization.Vector3(
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle()
                    );

                return(new NTROSerialization.NTROValue <NTROSerialization.Vector3>(field.Type, vector3, pointer));

            case DataType.Quaternion:
            case DataType.Color:
            case DataType.Fltx4:
            case DataType.Vector4D:
                var vector4 = new NTROSerialization.Vector4(
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle()
                    );

                return(new NTROSerialization.NTROValue <NTROSerialization.Vector4>(field.Type, vector4, pointer));

            case DataType.String4:
            case DataType.String:
                return(new NTROSerialization.NTROValue <string>(field.Type, Reader.ReadOffsetString(Encoding.UTF8), pointer));

            case DataType.Matrix3x4:
            case DataType.Matrix3x4a:
                var matrix3x4a = new NTROSerialization.Matrix3x4(
                    Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle(),
                    Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle(),
                    Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle()
                    );

                return(new NTROSerialization.NTROValue <NTROSerialization.Matrix3x4>(field.Type, matrix3x4a, pointer));

            case DataType.CTransform:
                var transform = new NTROSerialization.CTransform(
                    Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle(),
                    Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle()
                    );

                return(new NTROSerialization.NTROValue <NTROSerialization.CTransform>(field.Type, transform, pointer));

            default:
                throw new NotImplementedException(string.Format("Unknown data type: {0}", field.Type));
            }
        }
예제 #6
0
        private void ReadFieldIntrospection(ResourceIntrospectionManifest.ResourceDiskStruct.Field field)
        {
            uint count    = (uint)field.Count;
            bool multiple = false; // TODO: get rid of this
            bool pointer  = false; // TODO: get rid of this

            if (count == 0)
            {
                count = 1;
            }

            long prevOffset = 0;

            if (field.Indirections.Count > 0)
            {
                // TODO
                if (field.Indirections.Count > 1)
                {
                    throw new NotImplementedException("More than one indirection, not yet handled.");
                }

                // TODO
                if (field.Count > 0)
                {
                    throw new NotImplementedException("Indirection.Count > 0 && field.Count > 0");
                }

                var indirection = field.Indirections[0]; // TODO: depth needs fixing?

                var offset = Reader.ReadUInt32();

                if (indirection == 0x03)
                {
                    pointer = true;

                    if (offset == 0)
                    {
                        Writer.WriteLine("{0} {1}* = (ptr) ->NULL", ValveDataType(field.Type), field.FieldName);

                        return;
                    }

                    prevOffset = Reader.BaseStream.Position;

                    Reader.BaseStream.Position += offset - 4;
                }
                else if (indirection == 0x04)
                {
                    count = Reader.ReadUInt32();

                    prevOffset = Reader.BaseStream.Position;

                    if (count > 0)
                    {
                        multiple = true;

                        Reader.BaseStream.Position += offset - 8;
                    }
                }
                else
                {
                    throw new NotImplementedException(string.Format("Unknown indirection. ({0})", indirection));
                }
            }

            if (pointer)
            {
                Writer.Write("{0} {1}* = (ptr) ->", ValveDataType(field.Type), field.FieldName);
            }
            else if (field.Count > 0 || field.Indirections.Count > 0)
            {
                // TODO: This is matching Valve's incosistency
                if (field.Type == DataType.Byte && field.Indirections.Count > 0)
                {
                    Writer.WriteLine("{0}[{2}] {1} =", ValveDataType(field.Type), field.FieldName, count);
                }
                else
                {
                    Writer.WriteLine("{0} {1}[{2}] =", ValveDataType(field.Type), field.FieldName, count);
                }

                Writer.WriteLine("[");
                Writer.Indent++;
            }
            else
            {
                Writer.Write("{0} {1} = ", ValveDataType(field.Type), field.FieldName);
            }

            for (var i = 0; i < count; i++)
            {
                ReadField(field, multiple);
            }

            if (!pointer && (field.Count > 0 || field.Indirections.Count > 0))
            {
                Writer.Indent--;
                Writer.WriteLine("]");
            }

            if (prevOffset > 0)
            {
                Reader.BaseStream.Position = prevOffset;
            }
        }
예제 #7
0
        private void ReadField(ResourceIntrospectionManifest.ResourceDiskStruct.Field field, bool multiple)
        {
            switch (field.Type)
            {
            case DataType.Struct:
                var newStruct = Resource.IntrospectionManifest.ReferencedStructs.First(x => x.Id == field.TypeData);

                ReadStructure(newStruct, Reader.BaseStream.Position);

                break;

            case DataType.Enum:
                // TODO: Lookup in ReferencedEnums
                Writer.WriteLine("{0}", Reader.ReadUInt32());
                break;

            case DataType.SByte:
                Writer.WriteLine("{0}", Reader.ReadSByte());
                break;

            case DataType.Byte:     // TODO: Valve print it as hex, why?
                // TODO: if there are more than one uint8's, valve prints them without 0x, and on a single line
                if (multiple)
                {
                    Writer.WriteLine("{0:X2}", Reader.ReadByte());
                }
                else
                {
                    Writer.WriteLine("0x{0:X2}", Reader.ReadByte());
                }
                break;

            case DataType.Boolean:
                Writer.WriteLine("{0}", Reader.ReadByte() == 1 ? "true" : "false");
                break;

            case DataType.Int16:
                Writer.WriteLine("{0}", Reader.ReadInt16());
                break;

            case DataType.UInt16:     // TODO: Valve print it as hex, why?
                Writer.WriteLine("0x{0:X4}", Reader.ReadUInt16());
                break;

            case DataType.Int32:
                Writer.WriteLine("{0}", Reader.ReadInt32());
                break;

            case DataType.UInt32:     // TODO: Valve print it as hex, why?
                Writer.WriteLine("0x{0:X8}", Reader.ReadUInt32());
                break;

            case DataType.Float:
                Writer.WriteLine("{0:F6}", Reader.ReadSingle());
                break;

            case DataType.Int64:
                Writer.WriteLine("{0}", Reader.ReadInt64());
                break;

            case DataType.UInt64:     // TODO: Valve print it as hex, why?
                Writer.WriteLine("0x{0:X16}", Reader.ReadUInt64());
                break;

            case DataType.ExternalReference:
                Writer.WriteLine("ID: {0:X16}", Reader.ReadUInt64());
                break;

            case DataType.Vector:
                var vector3 = new []
                {
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle()
                };

                Writer.WriteLine("({0:F6}, {1:F6}, {2:F6})", vector3[0], vector3[1], vector3[2]);

                break;

            case DataType.Quaternion:
            case DataType.Color:
            case DataType.Fltx4:
            case DataType.Vector4D:
                var vector4 = new []
                {
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle()
                };

                if (field.Type == DataType.Quaternion)
                {
                    Writer.WriteLine("{{x: {0:F}, y: {1:F}, z: {2:F}, w: {3}}}", vector4[0], vector4[1], vector4[2], vector4[3].ToString("F"));
                }
                else
                {
                    Writer.WriteLine("({0:F6}, {1:F6}, {2:F6}, {3:F6})", vector4[0], vector4[1], vector4[2], vector4[3]);
                }

                break;

            case DataType.String4:
            case DataType.String:
                Writer.WriteLine("\"{0}\"", Reader.ReadOffsetString(Encoding.UTF8));

                break;

            case DataType.Matrix3x4:
            case DataType.Matrix3x4a:
                var matrix3x4a = new []
                {
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),

                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),

                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle()
                };

                Writer.WriteLine();
                Writer.WriteLine("{0:F4} {1:F4} {2:F4} {3:F4}", matrix3x4a[0], matrix3x4a[1], matrix3x4a[2], matrix3x4a[3]);
                Writer.WriteLine("{0:F4} {1:F4} {2:F4} {3:F4}", matrix3x4a[4], matrix3x4a[5], matrix3x4a[6], matrix3x4a[7]);
                Writer.WriteLine("{0:F4} {1:F4} {2:F4} {3:F4}", matrix3x4a[8], matrix3x4a[9], matrix3x4a[10], matrix3x4a[11]);

                break;

            case DataType.CTransform:
                var transform = new []
                {
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle(),
                    Reader.ReadSingle()     // TODO: unused?
                };

                // http://stackoverflow.com/a/15085178/2200891
                Writer.WriteLine("q={{{0:F}, {1:F}, {2:F}; w={3}}} p={{{4:F}, {5:F}, {6}}}", transform[4], transform[5], transform[6], transform[7].ToString("F"), transform[0], transform[1], transform[2].ToString("F"));

                break;

            default:
                throw new NotImplementedException(string.Format("Unknown data type: {0}", field.Type));
            }
        }
예제 #8
0
        public override void Read(BinaryReader reader, Resource resource)
        {
            // NTRO only in version 0?
            if (resource.IntrospectionManifest == null)
            {
                var block = new ResourceIntrospectionManifest.ResourceDiskStruct();

                var field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName = "m_bitpackedsoundinfo",
                    Type      = DataType.UInt32,
                };
                block.FieldIntrospection.Add(field);

                field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName    = "m_loopStart",
                    Type         = DataType.Int32,
                    OnDiskOffset = 4,
                };
                block.FieldIntrospection.Add(field);

                field = new ResourceIntrospectionManifest.ResourceDiskStruct.Field
                {
                    FieldName    = "m_flDuration",
                    Type         = DataType.Float,
                    OnDiskOffset = 12,
                };
                block.FieldIntrospection.Add(field);

                resource.Blocks.Add(new ResourceIntrospectionManifest());
                resource.IntrospectionManifest.ReferencedStructs.Add(block);
            }

            reader.BaseStream.Position = Offset;
            base.Read(reader, resource);

            LoopStart = ((NTROValue <int>)Output["m_loopStart"]).Value;
            Duration  = ((NTROValue <float>)Output["m_flDuration"]).Value;

            var bitpackedSoundInfo = ((NTROValue <uint>)Output["m_bitpackedsoundinfo"]).Value;

            // If these 5 bits are 0, it is the new format instead of the old
            if (ExtractSub(bitpackedSoundInfo, 27, 5) == 0)
            {
                // New format
                SampleRate = ExtractSub(bitpackedSoundInfo, 0, 16);
                SoundType  = GetTypeFromNewFormat(ExtractSub(bitpackedSoundInfo, 16, 2));
                // unknown = ExtractSub(bitpackedSoundInfo, 18, 2);
                Bits = ExtractSub(bitpackedSoundInfo, 20, 7);

                SampleSize  = Bits / 8;
                Channels    = 1;
                AudioFormat = 1;
            }
            else
            {
                // Old format
                SoundType   = (AudioFileType)ExtractSub(bitpackedSoundInfo, 0, 2);
                Bits        = ExtractSub(bitpackedSoundInfo, 2, 5);
                Channels    = ExtractSub(bitpackedSoundInfo, 7, 2);
                SampleSize  = ExtractSub(bitpackedSoundInfo, 9, 3);
                AudioFormat = ExtractSub(bitpackedSoundInfo, 12, 2);
                SampleRate  = ExtractSub(bitpackedSoundInfo, 14, 17);
            }

            if (SoundType > AudioFileType.MP3)
            {
                throw new NotImplementedException($"Unknown audio file format '{SoundType}', please report this on GitHub.");
            }
        }