/// <summary> /// Reads the extended header. /// </summary> /// <param name="streamBuffer">The stream buffer.</param> /// <param name="tag">The tag.</param> /// <param name="crcData">The CRC data.</param> /// <returns> /// The extended header if used; otherwise, null. /// </returns> /// <exception cref="System.IO.InvalidDataException">Thrown if the extended header size does not match the amount of bytes read for the extended header.</exception> private static Id3v2ExtendedHeader ReadExtendedHeader(StreamBuffer streamBuffer, Id3v2Tag tag, out int crcData) { crcData = 0; if (!tag.UseExtendedHeader) return null; // The extended header contains information that can provide further insight in the structure of the tag, // but is not vital to the correct parsing of the tag information; hence the extended header is optional. int extendedHeaderSize = 0; long startPosition = streamBuffer.Position; Id3v2ExtendedHeader extendedHeader = null; if ((tag.Version >= Id3v2Version.Id3v230) && (tag.Version < Id3v2Version.Id3v240)) { // Where the 'Extended header size', currently 6 or 10 bytes, excludes itself. extendedHeaderSize = streamBuffer.ReadBigEndianInt32() + 4; int extendedFlags = streamBuffer.ReadBigEndianInt16(); extendedHeader = Id3v2ExtendedHeader.InitExtendedHeader(tag.Version, extendedFlags); extendedHeader.PaddingSize = streamBuffer.ReadBigEndianInt32(); if (extendedHeader.CrcDataPresent) crcData = streamBuffer.ReadBigEndianInt32(); } else if (tag.Version >= Id3v2Version.Id3v240) { // Where the 'Extended header size' is the size of the whole extended header, stored as a 32 bit synchsafe integer. // An extended header can thus never have a size of fewer than six bytes. extendedHeaderSize = streamBuffer.ReadBigEndianInt32(); extendedHeaderSize = Id3v2Tag.GetUnsynchedValue(extendedHeaderSize); int extendedFlagsFieldLength = streamBuffer.ReadByte(); // The extended flags field, with its size described by 'number of flag bytes', is defined as: %0bcd0000 int extendedFlags = streamBuffer.ReadInt(extendedFlagsFieldLength); extendedHeader = Id3v2ExtendedHeader.InitExtendedHeader(tag.Version, extendedFlags); extendedHeader.SetExtendedFlagsFieldLength(extendedFlagsFieldLength); // Each flag that is set in the extended header has data attached, // which comes in the order in which the flags are encountered (i.e. the data for flag 'b' comes before the data for flag 'c'). // Unset flags cannot have any attached data. All unknown flags MUST be unset and their corresponding data removed when a tag is modified. // // Every set flag's data starts with a length byte, which contains a value between 0 and 127 ($00 - $7f), // followed by data that has the field length indicated by the length byte. // If a flag has no attached data, the value $00 is used as length byte. // If this flag is set, the present tag is an update of a tag found earlier in the present file or stream. // If frames defined as unique are found in the present tag, they are to override any corresponding ones found in the earlier tag. // This flag has no corresponding data. if (extendedHeader.TagIsUpdate) { // If a flag has no attached data, the value $00 is used as length byte. streamBuffer.ReadByte(); } // If this flag is set, a CRC-32 [ISO-3309] data is included in the extended header. // The CRC is calculated on all the data between the header and footer as indicated by the header's tag length field, minus the extended header. // Note that this includes the padding (if there is any), but excludes the footer. // The CRC-32 is stored as an 35 bit synchsafe integer, leaving the upper four bits always zeroed. if (extendedHeader.CrcDataPresent) { long crcBytes = streamBuffer.ReadBigEndianInt64(5); crcData = (int)Id3v2Tag.GetUnsynchedValue(crcBytes, 5); } // For some applications it might be desired to restrict a tag in more ways than imposed by the ID3v2 specification. // Note that the presence of these restrictions does not affect how the tag is decoded, merely how it was restricted before encoding. // If this flag is set the tag is restricted as follows: if (extendedHeader.TagIsRestricted) { // Restrictions: %ppqrrstt byte tagRestrictions = (byte)streamBuffer.ReadByte(); extendedHeader.TagRestrictions = new Id3v2TagRestrictions { // p - Tag size restrictions TagSizeRestriction = (Id3v2TagSizeRestriction)(tagRestrictions & Id3v2TagRestrictions.TagSizeRestrictionFlags), // q - Text encoding restrictions TextEncodingRestriction = (Id3v2TextEncodingRestriction)(tagRestrictions & Id3v2TagRestrictions.TextEncodingRestrictionFlags), // r - Text fields size restrictions TextFieldsSizeRestriction = (Id3v2TextFieldsSizeRestriction)(tagRestrictions & Id3v2TagRestrictions.TextFieldsSizeRestrictionFlags), // s - Image encoding restrictions ImageEncodingRestriction = (Id3v2ImageEncodingRestriction)(tagRestrictions & Id3v2TagRestrictions.ImageEncodingRestrictionFlags), // t - Image size restrictions ImageSizeRestriction = (Id3v2ImageSizeRestriction)(tagRestrictions & Id3v2TagRestrictions.ImageSizeRestrictionFlags) }; } } if ((streamBuffer.Position - startPosition) != extendedHeaderSize) { throw new InvalidDataException( string.Format( "ExtendedHeaderSize does not match amount of bytes read: expected {0} but got {1} bytes.", extendedHeaderSize, streamBuffer.Position - startPosition)); } return extendedHeader; }
public void ReadBigEndianInt64Test7Bytes() { const long Value = 0x12345678901234; const long Expected = 0x34129078563412; StreamBuffer target = new StreamBuffer(BitConverter.GetBytes(Value)) { Position = 0 }; long actual = target.ReadBigEndianInt64(7); Assert.AreEqual(Expected, actual); }