/* * ID3 Tag identifier * 04 00 2 bytes version * xx Tag flags * xx xx xx xx Tag size excluding header and footer as syncsafe int * [Extended header] * {Frames} * [Padding] * [Footer] */ private static void EncodeID3v2_4Tag(Stream s, ID3v2Tag tag) { byte flagByte = 0; long paddingLen, size; const long TagHeaderSize = 10L; if ((tag.Flags & ID3v2TagFlags.Unsync) == ID3v2TagFlags.Unsync) { flagByte |= 0x80; } if (tag.ExtendedHeader != null) { flagByte |= 0x40; } if ((tag.Flags & ID3v2TagFlags.Experimental) == ID3v2TagFlags.Experimental) { flagByte |= 0x20; } if ((tag.Flags & ID3v2TagFlags.Footer) == ID3v2TagFlags.Footer) { flagByte |= 0x10; } StringEncoder.WriteLatin1String(s, "ID3", 3); s.WriteByte(0x04); s.WriteByte(0x00); s.WriteByte(flagByte); /* skip size */ s.Position += 4; if (tag.ExtendedHeader != null) { byte[] frameData = GetFrameRawData(tag); long crc = -1L; if ((tag.ExtendedHeader.Flags & ExtendedHeaderFlags.CrcPresent) == ExtendedHeaderFlags.CrcPresent) { /* CRC is calculated on frame data and padding */ Crc32 crcCalc = new Crc32(); crcCalc.UpdateCrc(frameData); crcCalc.UpdateCrcWithZero(ComputePaddingSize(frameData.Length + TagHeaderSize, tag)); crc = crcCalc.Crc; } EncodeID3v2_4ExtendedHeader(s, tag.ExtendedHeader, crc); s.Write(frameData, 0, frameData.Length); } else { foreach (Frame f in tag.Frames) { FrameEncoder.EncodeFrame(s, f, tag); } } paddingLen = ComputePaddingSize(s.Length, tag); s.SetLength(s.Length + paddingLen); size = s.Length - TagHeaderSize; if ((tag.Flags & ID3v2TagFlags.Footer) == ID3v2TagFlags.Footer) { s.Position = s.Length - 1L; StringEncoder.WriteLatin1String(s, "3DI", 3); s.WriteByte(0x04); s.WriteByte(0x00); s.WriteByte(flagByte); NumberConverter.WriteInt(size, s, 4, true); } s.Position = 6L; NumberConverter.WriteInt(size, s, 4, true); }
/* * xx xx xx xx Extended Header size, excluding itself; currently 6 or 10 bytes; not sync safe. * xx xx Flags * xx xx xx xx Size of padding; not sync safe * [xx xx xx xx] 4 bytes of CRC Data; not sync safe * */ private static int DecodeID3v2_3ExtendedHeader(byte[] tagContent, ExtendedHeader extHeader) { int extHeaderSize, paddingSize; CheckLength(tagContent, 10); extHeaderSize = NumberConverter.ReadInt32(tagContent, 0, 4, false); paddingSize = NumberConverter.ReadInt32(tagContent, 6, 4, false); if ((tagContent[4] & 0x7F) != 0 || tagContent[5] != 0x00) { throw new ID3ParseException("Unknown extended header flag set."); } if (tagContent[4] == 0x80) { extHeader.Flags = ExtendedHeaderFlags.CrcPresent; if (extHeaderSize != 10) { throw new ID3ParseException("Invalid extended header size."); } CheckLength(tagContent, 14); long savedCrc = NumberConverter.ReadInt64(tagContent, 10, 4, false); int dataLength = tagContent.Length - 14 - paddingSize; Crc32 crcCalculator = new Crc32(); if (dataLength < 0) { throw new ID3ParseException("Padding size mismatch in extended header."); } crcCalculator.UpdateCrc(tagContent, 14, dataLength); if (savedCrc != crcCalculator.Crc) { throw new ID3CrcMismatchException(savedCrc, crcCalculator.Crc); } extHeader.CrcChecksum = savedCrc; } else { extHeader.Flags = ExtendedHeaderFlags.None; if (extHeaderSize != 6) { throw new ID3ParseException("Invalid extended header size."); } } return extHeaderSize; }
/* * xx xx xx xx Extended header size(min 6) as syncsafe int * xx Number of flag bytes(currently 1) * xx Flags * {sizeByte [flagdata]} For every set bit in flag one entry * */ private static int DecodeID3v2_4ExtendedHeader(byte[] tagContent, ExtendedHeader extHeader) { int extHeaderSize, index; CheckLength(tagContent, 6); extHeaderSize = NumberConverter.ReadInt32(tagContent, 0, 4, true); if (extHeaderSize < 6) { throw new ID3ParseException("Size mismatch in extended header."); } if (tagContent[4] != 0x01) { throw new ID3ParseException("Flag byte count in extended header does not match."); } CheckLength(tagContent, extHeaderSize); /* parse extended header flags */ extHeader.Flags = ExtendedHeaderFlags.None; index = 6; if ((tagContent[5] & 0x40) != 0) { extHeader.Flags |= ExtendedHeaderFlags.UpdateTag; if (index >= extHeaderSize || tagContent[index] != 0x00) { throw new ID3ParseException("Error while parsing extended header flag data."); } index += 1; } if ((tagContent[5] & 0x20) != 0) { extHeader.Flags |= ExtendedHeaderFlags.CrcPresent; if (index + 5 >= extHeaderSize || tagContent[index] != 0x05) { throw new ID3ParseException("Error while parsing extended header flag data."); } long savedCrc = NumberConverter.ReadInt64(tagContent, index + 1, 5, true); Crc32 crcCalculator = new Crc32(); crcCalculator.UpdateCrc(tagContent, extHeaderSize, tagContent.Length - extHeaderSize); if (savedCrc != crcCalculator.Crc) { throw new ID3CrcMismatchException(savedCrc, crcCalculator.Crc); } extHeader.CrcChecksum = savedCrc; index += 6; } if ((tagContent[5] & 0x10) != 0) { extHeader.Flags |= ExtendedHeaderFlags.RestrictTag; if (index + 1 >= extHeaderSize || tagContent[index] != 0x01) { throw new ID3ParseException("Error while parsing extended header flag data."); } index += 1; extHeader.TagSizeRestriction = (TagSizeRestriction)(tagContent[index] & 0xC0); extHeader.TextEncodingRestriction = (TextEncodingRestriction)(tagContent[index] & 0x20); extHeader.TextFieldSizeRestriction = (TextFieldSizeRestriction)(tagContent[index] & 0x18); extHeader.ImageEncodingRestriction = (ImageEncodingRestriction)(tagContent[index] & 0x4); extHeader.ImageSizeRestriction = (ImageSizeRestriction)(tagContent[index] & 0x3); index += 1; } if ((tagContent[5] & 0x8F) != 0) { throw new ID3ParseException("Invalid entended header flag set."); } if (index != extHeaderSize) { throw new ID3ParseException("Size mismatch in extended header."); } return extHeaderSize; }
public static long ComputeCrc(byte[] buffer, int offset, int count) { Crc32 tmp = new Crc32(); tmp.UpdateCrc(buffer, offset, count); return tmp.Crc; }